Version Description
- Added Promo Code support.
Download this release
Release Info
Developer | MailChimp |
Plugin | MailChimp for WooCommerce |
Version | 2.1.0 |
Comparing to | |
See all releases |
Code changes from version 2.0.2 to 2.1.0
- LICENSE.txt +0 -0
- README.md +4 -4
- README.txt +5 -1
- admin/class-mailchimp-woocommerce-admin.php +3 -0
- admin/css/mailchimp-woocommerce-admin.css +0 -0
- admin/index.php +0 -0
- admin/js/mailchimp-woocommerce-admin.js +0 -0
- admin/partials/mailchimp-woocommerce-admin-tabs.php +2 -2
- admin/partials/tabs/api_key.php +0 -0
- admin/partials/tabs/campaign_defaults.php +0 -0
- admin/partials/tabs/errors/missing_api_key.php +0 -0
- admin/partials/tabs/errors/missing_campaign_defaults.php +0 -0
- admin/partials/tabs/errors/missing_list.php +0 -0
- admin/partials/tabs/errors/missing_store.php +0 -0
- admin/partials/tabs/errors/not_ready_for_sync.php +0 -0
- admin/partials/tabs/logs.php +1 -0
- admin/partials/tabs/newsletter_settings.php +0 -0
- admin/partials/tabs/notices.php +0 -0
- admin/partials/tabs/store_info.php +0 -0
- admin/partials/tabs/store_sync.php +0 -0
- admin/partials/tabs/success/re-sync-started.php +0 -0
- changelog.md +0 -0
- env.php +0 -10
- includes/api/assets/class-mailchimp-address.php +0 -0
- includes/api/assets/class-mailchimp-cart.php +0 -0
- includes/api/assets/class-mailchimp-customer.php +0 -0
- includes/api/assets/class-mailchimp-line-item.php +0 -0
- includes/api/assets/class-mailchimp-order.php +32 -1
- includes/api/assets/class-mailchimp-product-variation.php +0 -0
- includes/api/assets/class-mailchimp-product.php +0 -0
- includes/api/assets/class-mailchimp-promo-code.php +230 -0
- includes/api/assets/class-mailchimp-promo-rule.php +405 -0
- includes/api/assets/class-mailchimp-store.php +0 -0
- includes/api/class-mailchimp-api.php +210 -0
- includes/api/class-mailchimp-woocommerce-api.php +3 -2
- includes/api/class-mailchimp-woocommerce-create-list-submission.php +0 -0
- includes/api/class-mailchimp-woocommerce-transform-coupons.php +136 -0
- includes/api/class-mailchimp-woocommerce-transform-orders.php +11 -0
- includes/api/class-mailchimp-woocommerce-transform-products.php +2 -2
- includes/api/errors/class-mailchimp-error.php +0 -0
- includes/api/errors/class-mailchimp-server-error.php +0 -0
- includes/api/helpers/class-mailchimp-woocommerce-api-currency-codes.php +0 -0
- includes/api/helpers/class-mailchimp-woocommerce-api-locales.php +0 -0
- includes/class-mailchimp-woocommerce-activator.php +0 -0
- includes/class-mailchimp-woocommerce-deactivator.php +0 -0
- includes/class-mailchimp-woocommerce-i18n.php +0 -0
- includes/class-mailchimp-woocommerce-loader.php +0 -0
- includes/class-mailchimp-woocommerce-newsletter.php +0 -0
- includes/class-mailchimp-woocommerce-options.php +6 -0
- includes/class-mailchimp-woocommerce-service.php +57 -2
- includes/class-mailchimp-woocommerce.php +19 -3
- includes/index.php +0 -0
- includes/plugin-update-checker/README.md +0 -105
- includes/plugin-update-checker/composer.json +0 -19
- includes/plugin-update-checker/css/puc-debug-bar.css +0 -62
- includes/plugin-update-checker/debug-bar-panel.php +0 -146
- includes/plugin-update-checker/debug-bar-plugin.php +0 -102
- includes/plugin-update-checker/github-checker.php +0 -458
- includes/plugin-update-checker/js/debug-bar.js +0 -52
- includes/plugin-update-checker/languages/plugin-update-checker-fr_FR.mo +0 -0
- includes/plugin-update-checker/languages/plugin-update-checker-fr_FR.po +0 -38
- includes/plugin-update-checker/languages/plugin-update-checker-hu_HU.mo +0 -0
- includes/plugin-update-checker/languages/plugin-update-checker-hu_HU.po +0 -41
- includes/plugin-update-checker/languages/plugin-update-checker.pot +0 -39
- includes/plugin-update-checker/license.txt +0 -7
- includes/plugin-update-checker/plugin-update-checker.php +0 -1641
- includes/plugin-update-checker/vendor/Parsedown.php +0 -1538
- includes/plugin-update-checker/vendor/ParsedownLegacy.php +0 -1535
- includes/plugin-update-checker/vendor/readme-parser.php +0 -331
- includes/processes/class-mailchimp-woocommerce-abstract-sync.php +1 -1
- includes/processes/class-mailchimp-woocommerce-cart-update.php +5 -6
- includes/processes/class-mailchimp-woocommerce-process-coupons.php +69 -0
- includes/processes/class-mailchimp-woocommerce-process-orders.php +4 -4
- includes/processes/class-mailchimp-woocommerce-process-products.php +3 -3
- includes/processes/class-mailchimp-woocommerce-single-coupon.php +51 -0
- includes/processes/class-mailchimp-woocommerce-single-order.php +7 -4
- includes/processes/class-mailchimp-woocommerce-single-product.php +3 -3
- includes/processes/class-mailchimp-woocommerce-user-submit.php +1 -1
- includes/vendor/queue.php +0 -0
- includes/vendor/queue/classes/cli/queue-command.php +0 -0
- includes/vendor/queue/classes/worker/wp-http-worker.php +0 -0
- includes/vendor/queue/classes/worker/wp-worker.php +1 -1
- includes/vendor/queue/classes/wp-job.php +0 -0
- includes/vendor/queue/classes/wp-queue.php +0 -0
- index.php +0 -0
- languages/mailchimp-woocommerce.pot +0 -0
- mailchimp-woocommerce.php +33 -2
- public/class-mailchimp-woocommerce-public.php +0 -0
- public/css/mailchimp-woocommerce-public.css +0 -0
- public/index.php +0 -0
- public/js/mailchimp-woocommerce-public.js +0 -0
- public/partials/mailchimp-woocommerce-public-display.php +0 -0
- uninstall.php +0 -0
LICENSE.txt
CHANGED
File without changes
|
README.md
CHANGED
@@ -10,7 +10,7 @@ In this article, you’ll learn how to connect MailChimp for WooCommerce.
|
|
10 |
|
11 |
- This plugin requires you to have the [WooCommerce plugin](https://wordpress.org/plugins/woocommerce) already installed and activated in WordPress.
|
12 |
|
13 |
-
- Your host environment must meet [WooCommerce's minimum requirements](https://docs.woocommerce.com/document/server-requirements), including PHP
|
14 |
|
15 |
- We recommend you use this plugin in a staging environment before installing it on production servers.
|
16 |
|
@@ -59,7 +59,7 @@ After you activate the plugin, you’ll be taken to the **Settings** page, where
|
|
59 |
|
60 |
1) On the **Connect** tab, paste your MailChimp API key into the field, choose whether or not you want to send debugging logs to MailChimp, and click **Save all changes**. To learn how to generate a MailChimp API Key, read [About API Keys](http://kb.mailchimp.com/integrations/api-integrations/about-api-keys).
|
61 |
|
62 |
-
|
63 |
|
64 |
2) Navigate to the **Store Settings** tab.
|
65 |
|
@@ -67,11 +67,11 @@ After you activate the plugin, you’ll be taken to the **Settings** page, where
|
|
67 |
|
68 |
3) Enter the contact and location details for your WooCommerce Store, and click **Save all changes**.
|
69 |
|
70 |
-
![Save all changes]
|
71 |
|
72 |
4) Navigate to the **List Settings** tab.
|
73 |
|
74 |
-
![List Settings tab]
|
75 |
|
76 |
5) Choose the list you want to sync, decide whether or not you want to auto-subscribe existing customers, set the subscribe message you want customers to see at checkout, and click **Save all changes**.
|
77 |
|
10 |
|
11 |
- This plugin requires you to have the [WooCommerce plugin](https://wordpress.org/plugins/woocommerce) already installed and activated in WordPress.
|
12 |
|
13 |
+
- Your host environment must meet [WooCommerce's minimum requirements](https://docs.woocommerce.com/document/server-requirements), including PHP 7.0 or greater.
|
14 |
|
15 |
- We recommend you use this plugin in a staging environment before installing it on production servers.
|
16 |
|
59 |
|
60 |
1) On the **Connect** tab, paste your MailChimp API key into the field, choose whether or not you want to send debugging logs to MailChimp, and click **Save all changes**. To learn how to generate a MailChimp API Key, read [About API Keys](http://kb.mailchimp.com/integrations/api-integrations/about-api-keys).
|
61 |
|
62 |
+
![API key](https://cloud.githubusercontent.com/assets/19805049/18877771/3fca90e8-849c-11e6-9e3a-161a7b3936dd.png)
|
63 |
|
64 |
2) Navigate to the **Store Settings** tab.
|
65 |
|
67 |
|
68 |
3) Enter the contact and location details for your WooCommerce Store, and click **Save all changes**.
|
69 |
|
70 |
+
![Save all changes](https://cloud.githubusercontent.com/assets/6547700/18677996/a76d126c-7f28-11e6-9150-4b289d20f057.png)
|
71 |
|
72 |
4) Navigate to the **List Settings** tab.
|
73 |
|
74 |
+
![List Settings tab](https://cloud.githubusercontent.com/assets/19805049/18878446/961221d0-849e-11e6-99bb-175c22bf921e.png)
|
75 |
|
76 |
5) Choose the list you want to sync, decide whether or not you want to auto-subscribe existing customers, set the subscribe message you want customers to see at checkout, and click **Save all changes**.
|
77 |
|
README.txt
CHANGED
@@ -22,6 +22,7 @@ With MailChimp for WooCommerce, you’ll have the power to:
|
|
22 |
- View detailed data on your marketing performance in your MailChimp Dashboard.
|
23 |
- Grow your audience and sell more stuff with Facebook and Instagram Ad Campaigns in MailChimp.
|
24 |
- Automatically embed a pop-up form that converts your website visitors to subscribers.
|
|
|
25 |
|
26 |
###A note for current WooCommerce integration users
|
27 |
This plugin supports our most powerful API 3.0 features, and is intended for users who have not yet integrated their WooCommerce stores with MailChimp.
|
@@ -33,7 +34,7 @@ You can run this new integration at the same time as your current WooCommerce in
|
|
33 |
Here are some things to know before you begin this process.
|
34 |
|
35 |
- This plugin requires you to have the [WooCommerce plugin](https://woocommerce.com/) already installed and activated in WordPress.
|
36 |
-
- Your hosting environment must meet [WooCommerce's minimum requirements](https://docs.woocommerce.com/document/server-requirements), including PHP
|
37 |
- We recommend you use this plugin in a staging environment before installing it on production servers. To learn more about staging environments, [check out these related Wordpress plugins](https://wordpress.org/plugins/search.php?q=staging).
|
38 |
- MailChimp for WooCommerce syncs the customer’s first name, last name, email address, and orders.
|
39 |
- WooCommerce customers who haven't signed up for marketing emails will appear in the **Transactional** portion of your list, and cannot be exported.
|
@@ -50,6 +51,9 @@ For more information on settings and configuration, please visit our Knowledge B
|
|
50 |
|
51 |
== Changelog ==
|
52 |
|
|
|
|
|
|
|
53 |
= 2.0.2 =
|
54 |
* Added new logs feature to help troubleshoot isolated sync and data feed issues.
|
55 |
* Fixed bug with setting customers as Transactional during checkout if they had already opted in previously.
|
22 |
- View detailed data on your marketing performance in your MailChimp Dashboard.
|
23 |
- Grow your audience and sell more stuff with Facebook and Instagram Ad Campaigns in MailChimp.
|
24 |
- Automatically embed a pop-up form that converts your website visitors to subscribers.
|
25 |
+
- Add discount codes created in WooCommerce to your emails and automations with a Promo Code content block
|
26 |
|
27 |
###A note for current WooCommerce integration users
|
28 |
This plugin supports our most powerful API 3.0 features, and is intended for users who have not yet integrated their WooCommerce stores with MailChimp.
|
34 |
Here are some things to know before you begin this process.
|
35 |
|
36 |
- This plugin requires you to have the [WooCommerce plugin](https://woocommerce.com/) already installed and activated in WordPress.
|
37 |
+
- Your hosting environment must meet [WooCommerce's minimum requirements](https://docs.woocommerce.com/document/server-requirements), including PHP 7.0 or greater.
|
38 |
- We recommend you use this plugin in a staging environment before installing it on production servers. To learn more about staging environments, [check out these related Wordpress plugins](https://wordpress.org/plugins/search.php?q=staging).
|
39 |
- MailChimp for WooCommerce syncs the customer’s first name, last name, email address, and orders.
|
40 |
- WooCommerce customers who haven't signed up for marketing emails will appear in the **Transactional** portion of your list, and cannot be exported.
|
51 |
|
52 |
== Changelog ==
|
53 |
|
54 |
+
= 2.1.0 =
|
55 |
+
* Added Promo Code support.
|
56 |
+
|
57 |
= 2.0.2 =
|
58 |
* Added new logs feature to help troubleshoot isolated sync and data feed issues.
|
59 |
* Fixed bug with setting customers as Transactional during checkout if they had already opted in previously.
|
admin/class-mailchimp-woocommerce-admin.php
CHANGED
@@ -884,6 +884,9 @@ class MailChimp_Woocommerce_Admin extends MailChimp_Woocommerce_Options {
|
|
884 |
*/
|
885 |
private function startSync()
|
886 |
{
|
|
|
|
|
|
|
887 |
$job = new MailChimp_WooCommerce_Process_Products();
|
888 |
$job->flagStartSync();
|
889 |
wp_queue($job);
|
884 |
*/
|
885 |
private function startSync()
|
886 |
{
|
887 |
+
$coupon_sync = new MailChimp_WooCommerce_Process_Coupons();
|
888 |
+
wp_queue($coupon_sync);
|
889 |
+
|
890 |
$job = new MailChimp_WooCommerce_Process_Products();
|
891 |
$job->flagStartSync();
|
892 |
wp_queue($job);
|
admin/css/mailchimp-woocommerce-admin.css
CHANGED
File without changes
|
admin/index.php
CHANGED
File without changes
|
admin/js/mailchimp-woocommerce-admin.js
CHANGED
File without changes
|
admin/partials/mailchimp-woocommerce-admin-tabs.php
CHANGED
@@ -60,9 +60,9 @@ if (isset($options['mailchimp_api_key']) && $handler->hasValidApiKey()) {
|
|
60 |
}
|
61 |
</style>
|
62 |
|
63 |
-
<?php if (!defined('PHP_VERSION_ID') || (PHP_VERSION_ID <
|
64 |
<div data-dismissible="notice-php-version" class="error notice notice-error is-dismissible">
|
65 |
-
<p><?php _e('MailChimp says: Please upgrade your PHP version to a minimum of
|
66 |
</div>
|
67 |
<?php endif; ?>
|
68 |
|
60 |
}
|
61 |
</style>
|
62 |
|
63 |
+
<?php if (!defined('PHP_VERSION_ID') || (PHP_VERSION_ID < 70000)): ?>
|
64 |
<div data-dismissible="notice-php-version" class="error notice notice-error is-dismissible">
|
65 |
+
<p><?php _e('MailChimp says: Please upgrade your PHP version to a minimum of 7.0', 'mailchimp-woocommerce'); ?></p>
|
66 |
</div>
|
67 |
<?php endif; ?>
|
68 |
|
admin/partials/tabs/api_key.php
CHANGED
File without changes
|
admin/partials/tabs/campaign_defaults.php
CHANGED
File without changes
|
admin/partials/tabs/errors/missing_api_key.php
CHANGED
File without changes
|
admin/partials/tabs/errors/missing_campaign_defaults.php
CHANGED
File without changes
|
admin/partials/tabs/errors/missing_list.php
CHANGED
File without changes
|
admin/partials/tabs/errors/missing_store.php
CHANGED
File without changes
|
admin/partials/tabs/errors/not_ready_for_sync.php
CHANGED
File without changes
|
admin/partials/tabs/logs.php
CHANGED
@@ -3,6 +3,7 @@ if (!empty( $_REQUEST['handle'])) {
|
|
3 |
if (!empty($_REQUEST['_wpnonce']) && wp_verify_nonce($_REQUEST['_wpnonce'], 'remove_log')) {
|
4 |
$log_handler = new WC_Log_Handler_File();
|
5 |
$log_handler->remove($_REQUEST['handle']);
|
|
|
6 |
}
|
7 |
}
|
8 |
$files = defined('WC_LOG_DIR') ? @scandir( WC_LOG_DIR ) : array();
|
3 |
if (!empty($_REQUEST['_wpnonce']) && wp_verify_nonce($_REQUEST['_wpnonce'], 'remove_log')) {
|
4 |
$log_handler = new WC_Log_Handler_File();
|
5 |
$log_handler->remove($_REQUEST['handle']);
|
6 |
+
wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=logs');
|
7 |
}
|
8 |
}
|
9 |
$files = defined('WC_LOG_DIR') ? @scandir( WC_LOG_DIR ) : array();
|
admin/partials/tabs/newsletter_settings.php
CHANGED
File without changes
|
admin/partials/tabs/notices.php
CHANGED
File without changes
|
admin/partials/tabs/store_info.php
CHANGED
File without changes
|
admin/partials/tabs/store_sync.php
CHANGED
File without changes
|
admin/partials/tabs/success/re-sync-started.php
CHANGED
File without changes
|
changelog.md
CHANGED
File without changes
|
env.php
DELETED
@@ -1,10 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
global $wp_version;
|
3 |
-
|
4 |
-
return (object) array(
|
5 |
-
'repo' => 'master',
|
6 |
-
'environment' => 'production',
|
7 |
-
'version' => '2.0.1',
|
8 |
-
'wp_version' => (empty($wp_version) ? 'Unknown' : $wp_version),
|
9 |
-
'wc_version' => class_exists('WC') ? WC()->version : null,
|
10 |
-
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/api/assets/class-mailchimp-address.php
CHANGED
File without changes
|
includes/api/assets/class-mailchimp-cart.php
CHANGED
File without changes
|
includes/api/assets/class-mailchimp-customer.php
CHANGED
File without changes
|
includes/api/assets/class-mailchimp-line-item.php
CHANGED
File without changes
|
includes/api/assets/class-mailchimp-order.php
CHANGED
@@ -29,6 +29,7 @@ class MailChimp_WooCommerce_Order
|
|
29 |
protected $billing_address = null;
|
30 |
protected $lines = array();
|
31 |
protected $confirm_and_paid = false;
|
|
|
32 |
|
33 |
/**
|
34 |
* @return array
|
@@ -124,6 +125,31 @@ class MailChimp_WooCommerce_Order
|
|
124 |
return $this;
|
125 |
}
|
126 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
127 |
/**
|
128 |
* @return array
|
129 |
*/
|
@@ -161,7 +187,7 @@ class MailChimp_WooCommerce_Order
|
|
161 |
|
162 |
/**
|
163 |
* @param null $financial_status
|
164 |
-
* @return
|
165 |
*/
|
166 |
public function setFinancialStatus($financial_status)
|
167 |
{
|
@@ -447,6 +473,7 @@ class MailChimp_WooCommerce_Order
|
|
447 |
'updated_at_foreign' => (string) $this->getUpdatedAt(),
|
448 |
'shipping_address' => $this->getShippingAddress()->toArray(),
|
449 |
'billing_address' => $this->getBillingAddress()->toArray(),
|
|
|
450 |
'lines' => array_map(function ($item) {
|
451 |
/** @var MailChimp_WooCommerce_LineItem $item */
|
452 |
return $item->toArray();
|
@@ -482,6 +509,10 @@ class MailChimp_WooCommerce_Order
|
|
482 |
$this->billing_address = $billing->fromArray($data['billing_address']);
|
483 |
}
|
484 |
|
|
|
|
|
|
|
|
|
485 |
if (array_key_exists('lines', $data) && is_array($data['lines'])) {
|
486 |
$this->lines = array();
|
487 |
foreach ($data['lines'] as $line_item) {
|
29 |
protected $billing_address = null;
|
30 |
protected $lines = array();
|
31 |
protected $confirm_and_paid = false;
|
32 |
+
protected $promos = [];
|
33 |
|
34 |
/**
|
35 |
* @return array
|
125 |
return $this;
|
126 |
}
|
127 |
|
128 |
+
/**
|
129 |
+
* @param $code
|
130 |
+
* @param $amount
|
131 |
+
* @param bool $is_percentage
|
132 |
+
* @return $this
|
133 |
+
*/
|
134 |
+
public function addDiscount($code, $amount, $is_percentage = false)
|
135 |
+
{
|
136 |
+
$this->promos[] = [
|
137 |
+
'code' => $code,
|
138 |
+
'amount_discounted' => $amount,
|
139 |
+
'type' => $is_percentage ? 'percent' : 'fixed'
|
140 |
+
];
|
141 |
+
|
142 |
+
return $this;
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* @return array
|
147 |
+
*/
|
148 |
+
public function discounts()
|
149 |
+
{
|
150 |
+
return $this->promos;
|
151 |
+
}
|
152 |
+
|
153 |
/**
|
154 |
* @return array
|
155 |
*/
|
187 |
|
188 |
/**
|
189 |
* @param null $financial_status
|
190 |
+
* @return MailChimp_WooCommerce_Order
|
191 |
*/
|
192 |
public function setFinancialStatus($financial_status)
|
193 |
{
|
473 |
'updated_at_foreign' => (string) $this->getUpdatedAt(),
|
474 |
'shipping_address' => $this->getShippingAddress()->toArray(),
|
475 |
'billing_address' => $this->getBillingAddress()->toArray(),
|
476 |
+
'promos' => !empty($this->promos) ? $this->promos : null,
|
477 |
'lines' => array_map(function ($item) {
|
478 |
/** @var MailChimp_WooCommerce_LineItem $item */
|
479 |
return $item->toArray();
|
509 |
$this->billing_address = $billing->fromArray($data['billing_address']);
|
510 |
}
|
511 |
|
512 |
+
if (array_key_exists('promos', $data)) {
|
513 |
+
$this->promos = $data['promos'];
|
514 |
+
}
|
515 |
+
|
516 |
if (array_key_exists('lines', $data) && is_array($data['lines'])) {
|
517 |
$this->lines = array();
|
518 |
foreach ($data['lines'] as $line_item) {
|
includes/api/assets/class-mailchimp-product-variation.php
CHANGED
File without changes
|
includes/api/assets/class-mailchimp-product.php
CHANGED
File without changes
|
includes/api/assets/class-mailchimp-promo-code.php
ADDED
@@ -0,0 +1,230 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@vextras.com
|
8 |
+
* Date: 6/13/17
|
9 |
+
* Time: 1:19 PM
|
10 |
+
*/
|
11 |
+
class MailChimp_WooCommerce_PromoCode
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* @var string
|
15 |
+
* @title Promo Rule Foreign ID
|
16 |
+
* @description A unique identifier for the promo code
|
17 |
+
*/
|
18 |
+
protected $id;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @var string
|
22 |
+
* @title Code
|
23 |
+
* @required
|
24 |
+
* @description The discount code
|
25 |
+
*/
|
26 |
+
protected $code;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var string
|
30 |
+
* @title Promo Redemption Url
|
31 |
+
* @required
|
32 |
+
* @description The url that should be used in the promotion campaign. Eg. A url that applies promo code directly at checkout or a url that points to sale page. Use store url if promotion url is not available.
|
33 |
+
*/
|
34 |
+
protected $redemption_url;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* @var string
|
38 |
+
* @title Description
|
39 |
+
* @default null
|
40 |
+
* @description Number of times promo code has been used.
|
41 |
+
*/
|
42 |
+
protected $usage_count;
|
43 |
+
|
44 |
+
/**
|
45 |
+
* @var boolean
|
46 |
+
* @title Enabled
|
47 |
+
* @default true
|
48 |
+
* @description Whether the promo code is currently enabled. ***
|
49 |
+
*/
|
50 |
+
protected $enabled;
|
51 |
+
|
52 |
+
/**
|
53 |
+
* @var \DateTime
|
54 |
+
* @title Start Time
|
55 |
+
* @default null
|
56 |
+
* @description The date and time when the promotion starts in ISO 8601 format
|
57 |
+
*/
|
58 |
+
protected $created_at_foreign;
|
59 |
+
|
60 |
+
/**
|
61 |
+
* @var \DateTime
|
62 |
+
* @title Start Time
|
63 |
+
* @default null
|
64 |
+
* @description The date and time when the promotion starts in ISO 8601 format
|
65 |
+
*/
|
66 |
+
protected $updated_at_foreign;
|
67 |
+
|
68 |
+
/**
|
69 |
+
* @var MailChimp_WooCommerce_PromoRule|null
|
70 |
+
*/
|
71 |
+
protected $promo_rule;
|
72 |
+
|
73 |
+
/**
|
74 |
+
* @return array
|
75 |
+
*/
|
76 |
+
public function getValidation()
|
77 |
+
{
|
78 |
+
return [
|
79 |
+
'id' => 'required',
|
80 |
+
'code' => 'required',
|
81 |
+
'redemption_url' => 'required',
|
82 |
+
'usage_count' => 'integer',
|
83 |
+
'created_at_foreign' => 'date',
|
84 |
+
'updated_at_foreign' => 'date',
|
85 |
+
];
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* @param MailChimp_WooCommerce_PromoRule $promo
|
90 |
+
* @return MailChimp_WooCommerce_PromoCode
|
91 |
+
*/
|
92 |
+
public function attachPromoRule(MailChimp_WooCommerce_PromoRule $promo)
|
93 |
+
{
|
94 |
+
$this->promo_rule = $promo;
|
95 |
+
return $this;
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* @return MailChimp_WooCommerce_PromoRule|null
|
100 |
+
*/
|
101 |
+
public function getAttachedPromoRule()
|
102 |
+
{
|
103 |
+
return $this->promo_rule;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* @return string
|
108 |
+
*/
|
109 |
+
public function getId()
|
110 |
+
{
|
111 |
+
return $this->id;
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* @param null $id
|
116 |
+
* @return MailChimp_WooCommerce_PromoCode
|
117 |
+
*/
|
118 |
+
public function setId($id)
|
119 |
+
{
|
120 |
+
$this->id = $id;
|
121 |
+
|
122 |
+
return $this;
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* @return string
|
127 |
+
*/
|
128 |
+
public function getCode()
|
129 |
+
{
|
130 |
+
return $this->code;
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* @param string $code
|
135 |
+
* @return MailChimp_WooCommerce_PromoCode
|
136 |
+
*/
|
137 |
+
public function setCode($code)
|
138 |
+
{
|
139 |
+
$this->code = $code;
|
140 |
+
|
141 |
+
return $this;
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* @return string
|
146 |
+
*/
|
147 |
+
public function getRedemptionURL()
|
148 |
+
{
|
149 |
+
return $this->redemption_url;
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* @param string $url
|
154 |
+
* @return MailChimp_WooCommerce_PromoCode
|
155 |
+
*/
|
156 |
+
public function setRedemptionURL($url)
|
157 |
+
{
|
158 |
+
$this->redemption_url = $url;
|
159 |
+
|
160 |
+
return $this;
|
161 |
+
}
|
162 |
+
|
163 |
+
public function getUsageCount()
|
164 |
+
{
|
165 |
+
return $this->usage_count;
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* @param $count
|
170 |
+
* @return MailChimp_WooCommerce_PromoCode
|
171 |
+
*/
|
172 |
+
public function setUsageCount($count)
|
173 |
+
{
|
174 |
+
$this->usage_count = $count;
|
175 |
+
|
176 |
+
return $this;
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* @param $enabled
|
181 |
+
* @return MailChimp_WooCommerce_PromoCode
|
182 |
+
*/
|
183 |
+
public function setEnabled($enabled)
|
184 |
+
{
|
185 |
+
$this->enabled = (bool) $enabled;
|
186 |
+
return $this;
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* @return bool
|
191 |
+
*/
|
192 |
+
public function isEnabled()
|
193 |
+
{
|
194 |
+
return (bool) $this->enabled;
|
195 |
+
}
|
196 |
+
|
197 |
+
/**
|
198 |
+
* @return array
|
199 |
+
*/
|
200 |
+
public function toArray()
|
201 |
+
{
|
202 |
+
return mailchimp_array_remove_empty([
|
203 |
+
'id' => (string) $this->getId(),
|
204 |
+
'code' => (string) $this->getCode(),
|
205 |
+
'redemption_url' => (string) $this->getRedemptionURL(),
|
206 |
+
'usage_count' => $this->getUsageCount(),
|
207 |
+
'enabled' => $this->isEnabled(),
|
208 |
+
]);
|
209 |
+
}
|
210 |
+
|
211 |
+
/**
|
212 |
+
* @param array $data
|
213 |
+
* @return MailChimp_WooCommerce_PromoCode
|
214 |
+
*/
|
215 |
+
public function fromArray(array $data)
|
216 |
+
{
|
217 |
+
$singles = [
|
218 |
+
'id', 'code', 'usage_count', 'enabled',
|
219 |
+
'redemption_url', 'created_at_foreign', 'updated_at_foreign',
|
220 |
+
];
|
221 |
+
|
222 |
+
foreach ($singles as $key) {
|
223 |
+
if (array_key_exists($key, $data)) {
|
224 |
+
$this->$key = $data[$key];
|
225 |
+
}
|
226 |
+
}
|
227 |
+
|
228 |
+
return $this;
|
229 |
+
}
|
230 |
+
}
|
includes/api/assets/class-mailchimp-promo-rule.php
ADDED
@@ -0,0 +1,405 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@vextras.com
|
8 |
+
* Date: 6/13/17
|
9 |
+
* Time: 1:19 PM
|
10 |
+
*/
|
11 |
+
class MailChimp_WooCommerce_PromoRule
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* @var string
|
15 |
+
* @title Promo Rule Foreign ID
|
16 |
+
* @default = null
|
17 |
+
* @description
|
18 |
+
* A unique identifier for the promo rule. If Ecomm platform does not support promo rule,
|
19 |
+
* use promo code id as promo rule id.
|
20 |
+
* Restricted to UTF-8 characters with max length 50
|
21 |
+
*/
|
22 |
+
protected $id;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var string
|
26 |
+
* @title Title
|
27 |
+
* @default null
|
28 |
+
* @description The title that will show up in promotion campaign. Restricted to UTF-8 characters with max length 100
|
29 |
+
*/
|
30 |
+
protected $title;
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @var string
|
34 |
+
* @title Description
|
35 |
+
* @default null
|
36 |
+
* @description The description of a promotion
|
37 |
+
*/
|
38 |
+
protected $description;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @var \DateTime
|
42 |
+
* @title Start Time
|
43 |
+
* @default null
|
44 |
+
* @description The date and time when the promotion starts in ISO 8601 format
|
45 |
+
*/
|
46 |
+
protected $starts_at;
|
47 |
+
|
48 |
+
/**
|
49 |
+
* @var \DateTime
|
50 |
+
* @title Start Time
|
51 |
+
* @default null
|
52 |
+
* @description The date and time when the promotion starts in ISO 8601 format
|
53 |
+
*/
|
54 |
+
protected $ends_at;
|
55 |
+
|
56 |
+
/**
|
57 |
+
* @var float
|
58 |
+
* @title Amount
|
59 |
+
* @required
|
60 |
+
* @description The amount of discount; Positive dollar or percentage amount.
|
61 |
+
*/
|
62 |
+
protected $amount;
|
63 |
+
|
64 |
+
/**
|
65 |
+
* @var string
|
66 |
+
* @title Type
|
67 |
+
* @required
|
68 |
+
* @description One of ‘fixed’ , ‘percentage’
|
69 |
+
*/
|
70 |
+
protected $type;
|
71 |
+
|
72 |
+
/**
|
73 |
+
* @var string
|
74 |
+
* @title Target
|
75 |
+
* @required
|
76 |
+
* @description One of ‘per_item’, ‘total’, ‘shipping’
|
77 |
+
*/
|
78 |
+
protected $target;
|
79 |
+
|
80 |
+
/**
|
81 |
+
* @var boolean
|
82 |
+
* @title Enabled
|
83 |
+
* @default true
|
84 |
+
* @description Whether the promo rule is currently enabled
|
85 |
+
*/
|
86 |
+
protected $enabled = true;
|
87 |
+
|
88 |
+
/**
|
89 |
+
* @var \DateTime
|
90 |
+
* @title Start Time
|
91 |
+
* @default null
|
92 |
+
* @description The date and time when the promotion starts in ISO 8601 format
|
93 |
+
*/
|
94 |
+
protected $created_at_foreign;
|
95 |
+
|
96 |
+
/**
|
97 |
+
* @var \DateTime
|
98 |
+
* @title Start Time
|
99 |
+
* @default null
|
100 |
+
* @description The date and time when the promotion starts in ISO 8601 format
|
101 |
+
*/
|
102 |
+
protected $updated_at_foreign;
|
103 |
+
|
104 |
+
/**
|
105 |
+
* @return array
|
106 |
+
*/
|
107 |
+
public function getValidation()
|
108 |
+
{
|
109 |
+
return [
|
110 |
+
'id' => 'required',
|
111 |
+
'amount' => 'required|number',
|
112 |
+
'type' => 'required',
|
113 |
+
'target' => 'required',
|
114 |
+
'enabled' => 'boolean',
|
115 |
+
'starts_at' => 'date',
|
116 |
+
'ends_at' => 'date',
|
117 |
+
'created_at_foreign' => 'date',
|
118 |
+
'updated_at_foreign' => 'date',
|
119 |
+
];
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* @return string
|
124 |
+
*/
|
125 |
+
public function getId()
|
126 |
+
{
|
127 |
+
return $this->id;
|
128 |
+
}
|
129 |
+
|
130 |
+
/**
|
131 |
+
* @param null $id
|
132 |
+
* @return MailChimp_WooCommerce_PromoRule
|
133 |
+
*/
|
134 |
+
public function setId($id)
|
135 |
+
{
|
136 |
+
$this->id = $id;
|
137 |
+
|
138 |
+
return $this;
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* @return string
|
143 |
+
*/
|
144 |
+
public function getTitle()
|
145 |
+
{
|
146 |
+
return $this->title;
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* @param string $name
|
151 |
+
* @return MailChimp_WooCommerce_PromoRule
|
152 |
+
*/
|
153 |
+
public function setTitle($name)
|
154 |
+
{
|
155 |
+
$this->title = $name;
|
156 |
+
|
157 |
+
return $this;
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* @return string
|
162 |
+
*/
|
163 |
+
public function getDescription()
|
164 |
+
{
|
165 |
+
return $this->description;
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* @param string $description
|
170 |
+
* @return MailChimp_WooCommerce_PromoRule
|
171 |
+
*/
|
172 |
+
public function setDescription($description)
|
173 |
+
{
|
174 |
+
$this->description = $description;
|
175 |
+
|
176 |
+
return $this;
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* @param \DateTime $date
|
181 |
+
* @return $this
|
182 |
+
*/
|
183 |
+
public function setStartsAt(\DateTime $date)
|
184 |
+
{
|
185 |
+
$this->starts_at = (string) $date;
|
186 |
+
|
187 |
+
return $this;
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* @return \DateTime
|
192 |
+
*/
|
193 |
+
public function getStartsAt()
|
194 |
+
{
|
195 |
+
return $this->starts_at;
|
196 |
+
}
|
197 |
+
|
198 |
+
/**
|
199 |
+
* @param \DateTime $date
|
200 |
+
* @return $this
|
201 |
+
*/
|
202 |
+
public function setEndsAt(\DateTime $date)
|
203 |
+
{
|
204 |
+
$this->ends_at = (string) $date;
|
205 |
+
|
206 |
+
return $this;
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* @return \DateTime
|
211 |
+
*/
|
212 |
+
public function getEndsAt()
|
213 |
+
{
|
214 |
+
return $this->ends_at;
|
215 |
+
}
|
216 |
+
|
217 |
+
/**
|
218 |
+
* @param $enabled
|
219 |
+
* @return $this
|
220 |
+
*/
|
221 |
+
public function setEnabled($enabled)
|
222 |
+
{
|
223 |
+
$this->enabled = (bool) $enabled;
|
224 |
+
return $this;
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* @return bool
|
229 |
+
*/
|
230 |
+
public function isEnabled()
|
231 |
+
{
|
232 |
+
return (bool) $this->enabled;
|
233 |
+
}
|
234 |
+
|
235 |
+
/**
|
236 |
+
* @return string
|
237 |
+
*/
|
238 |
+
public function getAmount()
|
239 |
+
{
|
240 |
+
return $this->amount;
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* @param int $amount
|
245 |
+
* @return MailChimp_WooCommerce_PromoRule
|
246 |
+
*/
|
247 |
+
public function setAmount($amount)
|
248 |
+
{
|
249 |
+
$this->amount = $amount;
|
250 |
+
|
251 |
+
return $this;
|
252 |
+
}
|
253 |
+
|
254 |
+
/**
|
255 |
+
* @return string
|
256 |
+
*/
|
257 |
+
public function getType()
|
258 |
+
{
|
259 |
+
return $this->type;
|
260 |
+
}
|
261 |
+
|
262 |
+
/**
|
263 |
+
* @return string
|
264 |
+
*/
|
265 |
+
public function getTarget()
|
266 |
+
{
|
267 |
+
return $this->target;
|
268 |
+
}
|
269 |
+
|
270 |
+
/**
|
271 |
+
* @return $this
|
272 |
+
*/
|
273 |
+
public function setTypeFixed()
|
274 |
+
{
|
275 |
+
$this->type = 'fixed';
|
276 |
+
|
277 |
+
return $this;
|
278 |
+
}
|
279 |
+
|
280 |
+
/**
|
281 |
+
* @return $this
|
282 |
+
*/
|
283 |
+
public function setTypePercentage()
|
284 |
+
{
|
285 |
+
$this->type = 'percentage';
|
286 |
+
|
287 |
+
return $this;
|
288 |
+
}
|
289 |
+
|
290 |
+
/**
|
291 |
+
* @return $this
|
292 |
+
*/
|
293 |
+
public function setTargetTypePerItem()
|
294 |
+
{
|
295 |
+
$this->target = 'per_item';
|
296 |
+
|
297 |
+
return $this;
|
298 |
+
}
|
299 |
+
|
300 |
+
/**
|
301 |
+
* @return $this
|
302 |
+
*/
|
303 |
+
public function setTargetTypeShipping()
|
304 |
+
{
|
305 |
+
$this->target = 'shipping';
|
306 |
+
|
307 |
+
return $this;
|
308 |
+
}
|
309 |
+
|
310 |
+
/**
|
311 |
+
* @return $this
|
312 |
+
*/
|
313 |
+
public function setTargetTypeTotal()
|
314 |
+
{
|
315 |
+
$this->target = 'total';
|
316 |
+
|
317 |
+
return $this;
|
318 |
+
}
|
319 |
+
/**
|
320 |
+
* @param \DateTime $time
|
321 |
+
* @return $this
|
322 |
+
*/
|
323 |
+
public function setUpdatedAt(\DateTime $time)
|
324 |
+
{
|
325 |
+
$this->updated_at_foreign = (string) $time;
|
326 |
+
|
327 |
+
return $this;
|
328 |
+
}
|
329 |
+
|
330 |
+
/**
|
331 |
+
* @return null
|
332 |
+
*/
|
333 |
+
public function getUpdatedAt()
|
334 |
+
{
|
335 |
+
return $this->updated_at_foreign;
|
336 |
+
}
|
337 |
+
|
338 |
+
/**
|
339 |
+
* @param \DateTime $time
|
340 |
+
* @return $this
|
341 |
+
*/
|
342 |
+
public function setCreatedAt(\DateTime $time)
|
343 |
+
{
|
344 |
+
$this->created_at_foreign = (string) $time;
|
345 |
+
|
346 |
+
return $this;
|
347 |
+
}
|
348 |
+
|
349 |
+
/**
|
350 |
+
* @return null
|
351 |
+
*/
|
352 |
+
public function getCreatedAt()
|
353 |
+
{
|
354 |
+
return $this->created_at_foreign;
|
355 |
+
}
|
356 |
+
|
357 |
+
/**
|
358 |
+
* @return array
|
359 |
+
*/
|
360 |
+
public function toArray()
|
361 |
+
{
|
362 |
+
return mailchimp_array_remove_empty([
|
363 |
+
'id' => (string) $this->getId(),
|
364 |
+
'title' => (string) $this->getTitle(),
|
365 |
+
'description' => (string) $this->getDescription(),
|
366 |
+
'starts_at' => (string) $this->getStartsAt(),
|
367 |
+
'ends_at' => (string) $this->getEndsAt(),
|
368 |
+
'amount' => floatval($this->getAmount()),
|
369 |
+
'type' => (string) $this->getType(),
|
370 |
+
'target' => (string) $this->getTarget(),
|
371 |
+
'enabled' => (bool) $this->isEnabled(),
|
372 |
+
'created_at_foreign' => (string) $this->getCreatedAt(),
|
373 |
+
'updated_at_foreign' => (string) $this->getUpdatedAt(),
|
374 |
+
]);
|
375 |
+
}
|
376 |
+
|
377 |
+
/**
|
378 |
+
* @param array $data
|
379 |
+
* @return MailChimp_WooCommerce_PromoRule
|
380 |
+
*/
|
381 |
+
public function fromArray(array $data)
|
382 |
+
{
|
383 |
+
$singles = [
|
384 |
+
'id',
|
385 |
+
'title',
|
386 |
+
'description',
|
387 |
+
'starts_at',
|
388 |
+
'ends_at',
|
389 |
+
'amount',
|
390 |
+
'type',
|
391 |
+
'target',
|
392 |
+
'enabled',
|
393 |
+
'created_at_foreign',
|
394 |
+
'updated_at_foreign'
|
395 |
+
];
|
396 |
+
|
397 |
+
foreach ($singles as $key) {
|
398 |
+
if (array_key_exists($key, $data)) {
|
399 |
+
$this->$key = $data[$key];
|
400 |
+
}
|
401 |
+
}
|
402 |
+
|
403 |
+
return $this;
|
404 |
+
}
|
405 |
+
}
|
includes/api/assets/class-mailchimp-store.php
CHANGED
File without changes
|
includes/api/class-mailchimp-api.php
CHANGED
@@ -841,6 +841,216 @@ class MailChimp_WooCommerce_MailChimpApi
|
|
841 |
}
|
842 |
}
|
843 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
844 |
/**
|
845 |
* @param MailChimp_WooCommerce_Store|MailChimp_WooCommerce_Order|MailChimp_WooCommerce_Product|MailChimp_WooCommerce_Customer $target
|
846 |
* @return bool
|
841 |
}
|
842 |
}
|
843 |
|
844 |
+
|
845 |
+
/**
|
846 |
+
* @param $store_id
|
847 |
+
* @param MailChimp_WooCommerce_PromoRule $rule
|
848 |
+
* @param bool $throw
|
849 |
+
* @return MailChimp_WooCommerce_PromoRule|bool
|
850 |
+
* @throws MailChimp_WooCommerce_Error
|
851 |
+
*/
|
852 |
+
public function addPromoRule($store_id, MailChimp_WooCommerce_PromoRule $rule, $throw = true)
|
853 |
+
{
|
854 |
+
try {
|
855 |
+
if (($response = $this->updatePromoRule($store_id, $rule, false))) {
|
856 |
+
return $response;
|
857 |
+
}
|
858 |
+
$data = $this->post("ecommerce/stores/{$store_id}/promo-rules", $rule->toArray());
|
859 |
+
return (new MailChimp_WooCommerce_PromoRule)->fromArray($data);
|
860 |
+
} catch (MailChimp_WooCommerce_Error $e) {
|
861 |
+
if ($throw) throw $e;
|
862 |
+
return false;
|
863 |
+
}
|
864 |
+
}
|
865 |
+
|
866 |
+
/**
|
867 |
+
* @param $store_id
|
868 |
+
* @param MailChimp_WooCommerce_PromoRule $rule
|
869 |
+
* @param bool $throw
|
870 |
+
* @return MailChimp_WooCommerce_PromoRule|bool
|
871 |
+
* @throws MailChimp_WooCommerce_Error
|
872 |
+
*/
|
873 |
+
public function updatePromoRule($store_id, MailChimp_WooCommerce_PromoRule $rule, $throw = true)
|
874 |
+
{
|
875 |
+
try {
|
876 |
+
$data = $this->patch("ecommerce/stores/{$store_id}/promo-rules/{$rule->getId()}", $rule->toArray());
|
877 |
+
return (new MailChimp_WooCommerce_PromoRule)->fromArray($data);
|
878 |
+
} catch (MailChimp_WooCommerce_Error $e) {
|
879 |
+
if ($throw) throw $e;
|
880 |
+
return false;
|
881 |
+
}
|
882 |
+
}
|
883 |
+
|
884 |
+
/**
|
885 |
+
* @param $store_id
|
886 |
+
* @param MailChimp_WooCommerce_PromoRule|int $rule
|
887 |
+
* @return bool
|
888 |
+
*/
|
889 |
+
public function deletePromoRule($store_id, $rule)
|
890 |
+
{
|
891 |
+
try {
|
892 |
+
$id = $rule instanceof MailChimp_WooCommerce_PromoRule ? $rule->getId() : $rule;
|
893 |
+
//print_r(array('id' => $id, 'store' => $store_id));die();
|
894 |
+
return (bool) $this->delete("ecommerce/stores/{$store_id}/promo-rules/{$id}");
|
895 |
+
} catch (MailChimp_WooCommerce_Error $e) {
|
896 |
+
//\Log::error("MC::deletePromoRule :: {$rule->getId()} :: {$e->getMessage()} on {$e->getLine()} in {$e->getFile()}");
|
897 |
+
return false;
|
898 |
+
}
|
899 |
+
}
|
900 |
+
|
901 |
+
/**
|
902 |
+
* @param $store_id
|
903 |
+
* @param int $page
|
904 |
+
* @param int $count
|
905 |
+
* @return array
|
906 |
+
*/
|
907 |
+
public function getPromoRuleIds($store_id, $page = 1, $count = 10)
|
908 |
+
{
|
909 |
+
$result = $this->get("ecommerce/stores/{$store_id}/promo-rules", [
|
910 |
+
'start' => $page,
|
911 |
+
'count' => $count,
|
912 |
+
'offset' => $page > 1 ? (($page-1) * $count) : 0,
|
913 |
+
'include' => 'id',
|
914 |
+
]);
|
915 |
+
|
916 |
+
$ids = array();
|
917 |
+
foreach ($result['promo_rules'] as $rule) {
|
918 |
+
$id = (string) $rule['id'];
|
919 |
+
$ids[$id] = $id;
|
920 |
+
}
|
921 |
+
return $ids;
|
922 |
+
}
|
923 |
+
|
924 |
+
/**
|
925 |
+
* @param $store_id
|
926 |
+
* @param int $page
|
927 |
+
* @param int $count
|
928 |
+
* @param bool $return_original
|
929 |
+
* @return array
|
930 |
+
*/
|
931 |
+
public function getPromoRules($store_id, $page = 1, $count = 10, $return_original = false)
|
932 |
+
{
|
933 |
+
$result = $this->get("ecommerce/stores/{$store_id}/promo-rules", [
|
934 |
+
'start' => $page,
|
935 |
+
'count' => $count,
|
936 |
+
'offset' => $page > 1 ? (($page-1) * $count) : 0,
|
937 |
+
]);
|
938 |
+
|
939 |
+
if ($return_original) {
|
940 |
+
return $result;
|
941 |
+
}
|
942 |
+
|
943 |
+
$rules = array();
|
944 |
+
foreach ($result['promo_rules'] as $rule_data) {
|
945 |
+
$rule = new MailChimp_WooCommerce_PromoRule();
|
946 |
+
$rule->fromArray($rule_data);
|
947 |
+
$rules[] = $rule;
|
948 |
+
}
|
949 |
+
return $rules;
|
950 |
+
}
|
951 |
+
|
952 |
+
/**
|
953 |
+
* @param $store_id
|
954 |
+
* @param $rule_id
|
955 |
+
* @param int $page
|
956 |
+
* @param int $count
|
957 |
+
* @param bool $return_original
|
958 |
+
* @return array
|
959 |
+
*/
|
960 |
+
public function getPromoCodesForRule($store_id, $rule_id, $page = 1, $count = 10, $return_original = false)
|
961 |
+
{
|
962 |
+
$result = $this->get("ecommerce/stores/{$store_id}/promo-rules/{$rule_id}/promo_codes", [
|
963 |
+
'start' => $page,
|
964 |
+
'count' => $count,
|
965 |
+
'offset' => $page > 1 ? (($page-1) * $count) : 0,
|
966 |
+
]);
|
967 |
+
|
968 |
+
if ($return_original) {
|
969 |
+
return $result;
|
970 |
+
}
|
971 |
+
|
972 |
+
$rules = array();
|
973 |
+
foreach ($result as $rule_data) {
|
974 |
+
$rule = new MailChimp_WooCommerce_PromoCode();
|
975 |
+
$rule->fromArray($rule_data);
|
976 |
+
$rules[] = $rule;
|
977 |
+
}
|
978 |
+
return $rules;
|
979 |
+
}
|
980 |
+
|
981 |
+
/**
|
982 |
+
* @param $store_id
|
983 |
+
* @param $rule_id
|
984 |
+
* @param $code_id
|
985 |
+
*
|
986 |
+
* @return MailChimp_WooCommerce_PromoCode|bool
|
987 |
+
*/
|
988 |
+
public function getPromoCodeForRule($store_id, $rule_id, $code_id)
|
989 |
+
{
|
990 |
+
try {
|
991 |
+
$data = $this->get("ecommerce/stores/{$store_id}/promo-rules/{$rule_id}/promo-codes/{$code_id}");
|
992 |
+
return (new MailChimp_WooCommerce_PromoCode)->fromArray($data);
|
993 |
+
} catch (MailChimp_WooCommerce_Error $e) {
|
994 |
+
return false;
|
995 |
+
}
|
996 |
+
}
|
997 |
+
|
998 |
+
/**
|
999 |
+
* @param $store_id
|
1000 |
+
* @param MailChimp_WooCommerce_PromoRule $rule
|
1001 |
+
* @param MailChimp_WooCommerce_PromoCode $code
|
1002 |
+
* @param bool $throw
|
1003 |
+
* @return MailChimp_WooCommerce_PromoCode|bool
|
1004 |
+
* @throws MailChimp_WooCommerce_Error
|
1005 |
+
*/
|
1006 |
+
public function addPromoCodeForRule($store_id, MailChimp_WooCommerce_PromoRule $rule, MailChimp_WooCommerce_PromoCode $code, $throw = true)
|
1007 |
+
{
|
1008 |
+
try {
|
1009 |
+
if (($result = $this->updatePromoCodeForRule($store_id, $rule, $code, false))) {
|
1010 |
+
return $result;
|
1011 |
+
}
|
1012 |
+
$data = $this->post("ecommerce/stores/{$store_id}/promo-rules/{$rule->getId()}/promo-codes", $code->toArray());
|
1013 |
+
return (new MailChimp_WooCommerce_PromoCode)->fromArray($data);
|
1014 |
+
} catch (MailChimp_WooCommerce_Error $e) {
|
1015 |
+
if ($throw) throw $e;
|
1016 |
+
return false;
|
1017 |
+
}
|
1018 |
+
}
|
1019 |
+
|
1020 |
+
/**
|
1021 |
+
* @param $store_id
|
1022 |
+
* @param MailChimp_WooCommerce_PromoRule $rule
|
1023 |
+
* @param MailChimp_WooCommerce_PromoCode $code
|
1024 |
+
* @param bool $throw
|
1025 |
+
* @return MailChimp_WooCommerce_PromoCode|bool
|
1026 |
+
* @throws MailChimp_WooCommerce_Error
|
1027 |
+
*/
|
1028 |
+
public function updatePromoCodeForRule($store_id, MailChimp_WooCommerce_PromoRule $rule, MailChimp_WooCommerce_PromoCode $code, $throw = true)
|
1029 |
+
{
|
1030 |
+
try {
|
1031 |
+
$data = $this->patch("ecommerce/stores/{$store_id}/promo-rules/{$rule->getId()}/promo-codes/{$code->getId()}", $code->toArray());
|
1032 |
+
return (new MailChimp_WooCommerce_PromoCode)->fromArray($data);
|
1033 |
+
} catch (MailChimp_WooCommerce_Error $e) {
|
1034 |
+
if ($throw) throw $e;
|
1035 |
+
return false;
|
1036 |
+
}
|
1037 |
+
}
|
1038 |
+
|
1039 |
+
/**
|
1040 |
+
* @param $store_id
|
1041 |
+
* @param $rule_id
|
1042 |
+
* @param $code_id
|
1043 |
+
* @return bool
|
1044 |
+
*/
|
1045 |
+
public function deletePromoCodeForRule($store_id, $rule_id, $code_id)
|
1046 |
+
{
|
1047 |
+
try {
|
1048 |
+
return (bool) $this->delete("ecommerce/stores/{$store_id}/promo-rules/{$rule_id}/promo-codes/{$code_id}");
|
1049 |
+
} catch (MailChimp_WooCommerce_Error $e) {
|
1050 |
+
return false;
|
1051 |
+
}
|
1052 |
+
}
|
1053 |
+
|
1054 |
/**
|
1055 |
* @param MailChimp_WooCommerce_Store|MailChimp_WooCommerce_Order|MailChimp_WooCommerce_Product|MailChimp_WooCommerce_Customer $target
|
1056 |
* @return bool
|
includes/api/class-mailchimp-woocommerce-api.php
CHANGED
@@ -81,7 +81,7 @@ class MailChimp_WooCommerce_Api
|
|
81 |
|
82 |
/**
|
83 |
* @param $resource
|
84 |
-
* @return bool|MailChimp_WooCommerce_Transform_Orders|MailChimp_WooCommerce_Transform_Products
|
85 |
*/
|
86 |
public function engine($resource)
|
87 |
{
|
@@ -92,7 +92,8 @@ class MailChimp_WooCommerce_Api
|
|
92 |
case 'orders' :
|
93 |
return new MailChimp_WooCommerce_Transform_Orders();
|
94 |
break;
|
95 |
-
|
|
|
96 |
default:
|
97 |
return false;
|
98 |
}
|
81 |
|
82 |
/**
|
83 |
* @param $resource
|
84 |
+
* @return bool|MailChimp_WooCommerce_Transform_Orders|MailChimp_WooCommerce_Transform_Products|MailChimp_WooCommerce_Transform_Coupons
|
85 |
*/
|
86 |
public function engine($resource)
|
87 |
{
|
92 |
case 'orders' :
|
93 |
return new MailChimp_WooCommerce_Transform_Orders();
|
94 |
break;
|
95 |
+
case 'coupons':
|
96 |
+
return new MailChimp_WooCommerce_Transform_Coupons();
|
97 |
default:
|
98 |
return false;
|
99 |
}
|
includes/api/class-mailchimp-woocommerce-create-list-submission.php
CHANGED
File without changes
|
includes/api/class-mailchimp-woocommerce-transform-coupons.php
ADDED
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 10/06/17
|
9 |
+
* Time: 8:29 AM
|
10 |
+
*/
|
11 |
+
class MailChimp_WooCommerce_Transform_Coupons
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* @param int $page
|
15 |
+
* @param int $limit
|
16 |
+
* @return \stdClass
|
17 |
+
*/
|
18 |
+
public function compile($page = 1, $limit = 5)
|
19 |
+
{
|
20 |
+
$response = (object) array(
|
21 |
+
'endpoint' => 'coupons',
|
22 |
+
'page' => $page ? $page : 1,
|
23 |
+
'limit' => (int) $limit,
|
24 |
+
'count' => 0,
|
25 |
+
'stuffed' => false,
|
26 |
+
'items' => array(),
|
27 |
+
);
|
28 |
+
|
29 |
+
if ((($coupons = $this->getCouponPosts($page, $limit)) && !empty($coupons))) {
|
30 |
+
foreach ($coupons as $post) {
|
31 |
+
$response->items[] = $this->transform($post->ID);
|
32 |
+
$response->count++;
|
33 |
+
}
|
34 |
+
}
|
35 |
+
|
36 |
+
$response->stuffed = ($response->count > 0 && (int) $response->count === (int) $limit) ? true : false;
|
37 |
+
|
38 |
+
return $response;
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* @param int $post_id
|
43 |
+
* @return MailChimp_WooCommerce_PromoCode
|
44 |
+
*/
|
45 |
+
public function transform($post_id)
|
46 |
+
{
|
47 |
+
$resource = new WC_Coupon($post_id);
|
48 |
+
$valid = true;
|
49 |
+
|
50 |
+
if (($exp = $resource->get_date_expires()) && current_time('timestamp', true) > $exp->getTimestamp()) {
|
51 |
+
$valid = false;
|
52 |
+
}
|
53 |
+
|
54 |
+
$rule = new MailChimp_WooCommerce_PromoRule();
|
55 |
+
|
56 |
+
$rule->setId($resource->get_id());
|
57 |
+
$rule->setTitle($resource->get_code());
|
58 |
+
$rule->setDescription($resource->get_description());
|
59 |
+
$rule->setEnabled($valid);
|
60 |
+
$rule->setAmount($resource->get_amount('edit'));
|
61 |
+
|
62 |
+
if (!$rule->getDescription()) {
|
63 |
+
$rule->setDescription($resource->get_code());
|
64 |
+
}
|
65 |
+
|
66 |
+
switch ($resource->get_discount_type()) {
|
67 |
+
case 'fixed_product':
|
68 |
+
$rule->setTypeFixed();
|
69 |
+
$rule->setTargetTypePerItem();
|
70 |
+
break;
|
71 |
+
|
72 |
+
case 'fixed_cart':
|
73 |
+
$rule->setTypeFixed();
|
74 |
+
$rule->setTargetTypeTotal();
|
75 |
+
break;
|
76 |
+
|
77 |
+
case 'percent':
|
78 |
+
$rule->setTypePercentage();
|
79 |
+
$rule->setTargetTypeTotal();
|
80 |
+
$rule->setAmount(($resource->get_amount('edit')/100));
|
81 |
+
break;
|
82 |
+
}
|
83 |
+
|
84 |
+
if (($exp = $resource->get_date_expires())) {
|
85 |
+
$rule->setEndsAt($exp);
|
86 |
+
}
|
87 |
+
|
88 |
+
$code = new MailChimp_WooCommerce_PromoCode();
|
89 |
+
|
90 |
+
$code->setId($resource->get_id());
|
91 |
+
$code->setCode($resource->get_code());
|
92 |
+
$code->setEnabled($valid);
|
93 |
+
$code->setRedemptionURL(get_home_url());
|
94 |
+
$code->setUsageCount($resource->get_usage_count());
|
95 |
+
|
96 |
+
// attach the rule for use.
|
97 |
+
$code->attachPromoRule($rule);
|
98 |
+
|
99 |
+
return $code;
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* @param int $page
|
104 |
+
* @param int $posts
|
105 |
+
* @return array|bool
|
106 |
+
*/
|
107 |
+
public function getCouponPosts($page = 1, $posts = 5)
|
108 |
+
{
|
109 |
+
$coupons = get_posts(array(
|
110 |
+
'post_type' => array_merge(array_keys(wc_get_product_types()), array('shop_coupon')),
|
111 |
+
'posts_per_page' => $posts,
|
112 |
+
'paged' => $page,
|
113 |
+
'orderby' => 'ID',
|
114 |
+
'order' => 'ASC',
|
115 |
+
));
|
116 |
+
|
117 |
+
if (empty($coupons)) {
|
118 |
+
|
119 |
+
sleep(2);
|
120 |
+
|
121 |
+
$coupons = get_posts(array(
|
122 |
+
'post_type' => array_merge(array_keys(wc_get_product_types()), array('shop_coupon')),
|
123 |
+
'posts_per_page' => $posts,
|
124 |
+
'paged' => $page,
|
125 |
+
'orderby' => 'ID',
|
126 |
+
'order' => 'ASC',
|
127 |
+
));
|
128 |
+
|
129 |
+
if (empty($coupons)) {
|
130 |
+
return false;
|
131 |
+
}
|
132 |
+
}
|
133 |
+
|
134 |
+
return $coupons;
|
135 |
+
}
|
136 |
+
}
|
includes/api/class-mailchimp-woocommerce-transform-orders.php
CHANGED
@@ -168,6 +168,17 @@ class MailChimp_WooCommerce_Transform_Orders
|
|
168 |
$order->addItem($item);
|
169 |
}
|
170 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
171 |
//if (($refund = $woo->get_total_refunded()) && $refund > 0){
|
172 |
// this is where we would be altering the submission to tell us about the refund.
|
173 |
//}
|
168 |
$order->addItem($item);
|
169 |
}
|
170 |
|
171 |
+
// apply the coupon discounts
|
172 |
+
if (($used_coupons = $woo->get_used_coupons()) && is_array($used_coupons)) {
|
173 |
+
foreach ($used_coupons as $coupon_code) {
|
174 |
+
if (($coupon_id = wc_get_coupon_id_by_code($coupon_code))) {
|
175 |
+
$coupon = new WC_Coupon($coupon_id);
|
176 |
+
$is_percentage = $coupon->get_discount_type() === 'percent';
|
177 |
+
$order->addDiscount($coupon_code, $coupon->get_amount('edit'), $is_percentage);
|
178 |
+
}
|
179 |
+
}
|
180 |
+
}
|
181 |
+
|
182 |
//if (($refund = $woo->get_total_refunded()) && $refund > 0){
|
183 |
// this is where we would be altering the submission to tell us about the refund.
|
184 |
//}
|
includes/api/class-mailchimp-woocommerce-transform-products.php
CHANGED
@@ -44,7 +44,7 @@ class MailChimp_WooCommerce_Transform_Products
|
|
44 |
*/
|
45 |
public function transform(WP_Post $post)
|
46 |
{
|
47 |
-
$woo =
|
48 |
|
49 |
$variant_posts = $this->getProductVariantPosts($post->ID);
|
50 |
|
@@ -98,7 +98,7 @@ class MailChimp_WooCommerce_Transform_Products
|
|
98 |
if (isset($post->post_type) && $post->post_type === 'product_variation') {
|
99 |
$woo = new WC_Product_Variation($post->ID);
|
100 |
} else {
|
101 |
-
$woo =
|
102 |
}
|
103 |
}
|
104 |
|
44 |
*/
|
45 |
public function transform(WP_Post $post)
|
46 |
{
|
47 |
+
$woo = wc_get_product($post);
|
48 |
|
49 |
$variant_posts = $this->getProductVariantPosts($post->ID);
|
50 |
|
98 |
if (isset($post->post_type) && $post->post_type === 'product_variation') {
|
99 |
$woo = new WC_Product_Variation($post->ID);
|
100 |
} else {
|
101 |
+
$woo = wc_get_product($post);
|
102 |
}
|
103 |
}
|
104 |
|
includes/api/errors/class-mailchimp-error.php
CHANGED
File without changes
|
includes/api/errors/class-mailchimp-server-error.php
CHANGED
File without changes
|
includes/api/helpers/class-mailchimp-woocommerce-api-currency-codes.php
CHANGED
File without changes
|
includes/api/helpers/class-mailchimp-woocommerce-api-locales.php
CHANGED
File without changes
|
includes/class-mailchimp-woocommerce-activator.php
CHANGED
File without changes
|
includes/class-mailchimp-woocommerce-deactivator.php
CHANGED
File without changes
|
includes/class-mailchimp-woocommerce-i18n.php
CHANGED
File without changes
|
includes/class-mailchimp-woocommerce-loader.php
CHANGED
File without changes
|
includes/class-mailchimp-woocommerce-newsletter.php
CHANGED
File without changes
|
includes/class-mailchimp-woocommerce-options.php
CHANGED
@@ -28,6 +28,12 @@ abstract class MailChimp_Woocommerce_Options
|
|
28 |
$this->is_admin = current_user_can('administrator');
|
29 |
if (get_option('mailchimp_woocommerce_plugin_do_activation_redirect', false)) {
|
30 |
delete_option('mailchimp_woocommerce_plugin_do_activation_redirect');
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
if (!isset($_GET['activate-multi'])) {
|
32 |
wp_redirect("options-general.php?page=mailchimp-woocommerce");
|
33 |
}
|
28 |
$this->is_admin = current_user_can('administrator');
|
29 |
if (get_option('mailchimp_woocommerce_plugin_do_activation_redirect', false)) {
|
30 |
delete_option('mailchimp_woocommerce_plugin_do_activation_redirect');
|
31 |
+
|
32 |
+
// don't do the redirect while activating the plugin through the rest API. ( Bartosz from Woo asked for this )
|
33 |
+
if ((defined( 'REST_REQUEST' ) && REST_REQUEST)) {
|
34 |
+
return;
|
35 |
+
}
|
36 |
+
|
37 |
if (!isset($_GET['activate-multi'])) {
|
38 |
wp_redirect("options-general.php?page=mailchimp-woocommerce");
|
39 |
}
|
includes/class-mailchimp-woocommerce-service.php
CHANGED
@@ -95,10 +95,8 @@ class MailChimp_Service extends MailChimp_Woocommerce_Options
|
|
95 |
public function handleOrderStatusChanged($order_id)
|
96 |
{
|
97 |
if ($this->hasOption('mailchimp_api_key')) {
|
98 |
-
|
99 |
// register this order is already in process..
|
100 |
static::$pushed_orders[$order_id] = true;
|
101 |
-
|
102 |
// queue up the single order to be processed.
|
103 |
$handler = new MailChimp_WooCommerce_Single_Order($order_id, null, null, null);
|
104 |
$handler->is_update = true;
|
@@ -169,6 +167,33 @@ class MailChimp_Service extends MailChimp_Woocommerce_Options
|
|
169 |
return false;
|
170 |
}
|
171 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
/**
|
173 |
* Save post metadata when a post is saved.
|
174 |
*
|
@@ -187,6 +212,36 @@ class MailChimp_Service extends MailChimp_Woocommerce_Options
|
|
187 |
}
|
188 |
}
|
189 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
190 |
/**
|
191 |
* @param $user_id
|
192 |
*/
|
95 |
public function handleOrderStatusChanged($order_id)
|
96 |
{
|
97 |
if ($this->hasOption('mailchimp_api_key')) {
|
|
|
98 |
// register this order is already in process..
|
99 |
static::$pushed_orders[$order_id] = true;
|
|
|
100 |
// queue up the single order to be processed.
|
101 |
$handler = new MailChimp_WooCommerce_Single_Order($order_id, null, null, null);
|
102 |
$handler->is_update = true;
|
167 |
return false;
|
168 |
}
|
169 |
|
170 |
+
/**
|
171 |
+
* @param $post_id
|
172 |
+
*/
|
173 |
+
public function handleNewCoupon($post_id)
|
174 |
+
{
|
175 |
+
$this->handleCouponSaved($post_id, new WC_Coupon($post_id));
|
176 |
+
}
|
177 |
+
|
178 |
+
/**
|
179 |
+
* @param $post_id
|
180 |
+
* @param null $coupon
|
181 |
+
*/
|
182 |
+
public function handleCouponSaved($post_id, $coupon = null)
|
183 |
+
{
|
184 |
+
if ($coupon instanceof WC_Coupon) {
|
185 |
+
wp_queue(new MailChimp_WooCommerce_SingleCoupon($post_id));
|
186 |
+
}
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* @param $post_id
|
191 |
+
*/
|
192 |
+
public function handleCouponRestored($post_id)
|
193 |
+
{
|
194 |
+
$this->handleCouponSaved($post_id, new WC_Coupon($post_id));
|
195 |
+
}
|
196 |
+
|
197 |
/**
|
198 |
* Save post metadata when a post is saved.
|
199 |
*
|
212 |
}
|
213 |
}
|
214 |
|
215 |
+
/**
|
216 |
+
* @param $post_id
|
217 |
+
* @return bool
|
218 |
+
*/
|
219 |
+
public function handlePostTrashed($post_id)
|
220 |
+
{
|
221 |
+
if (!$this->hasOption('mailchimp_api_key')) {
|
222 |
+
return false;
|
223 |
+
}
|
224 |
+
|
225 |
+
switch (get_post_type($post_id)) {
|
226 |
+
case 'shop_coupon':
|
227 |
+
mailchimp_get_api()->deletePromoRule(mailchimp_get_store_id(), $post_id);
|
228 |
+
mailchimp_log('promo_code.deleted', "deleted promo code {$post_id}");
|
229 |
+
break;
|
230 |
+
}
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* @param $post_id
|
235 |
+
*/
|
236 |
+
public function handlePostRestored($post_id)
|
237 |
+
{
|
238 |
+
switch(get_post_type($post_id)) {
|
239 |
+
case 'shop_coupon':
|
240 |
+
return $this->handleCouponRestored($post_id);
|
241 |
+
break;
|
242 |
+
}
|
243 |
+
}
|
244 |
+
|
245 |
/**
|
246 |
* @param $user_id
|
247 |
*/
|
includes/class-mailchimp-woocommerce.php
CHANGED
@@ -174,6 +174,7 @@ class MailChimp_Woocommerce {
|
|
174 |
require_once $path . 'includes/api/class-mailchimp-woocommerce-api.php';
|
175 |
require_once $path . 'includes/api/class-mailchimp-woocommerce-create-list-submission.php';
|
176 |
require_once $path . 'includes/api/class-mailchimp-woocommerce-transform-products.php';
|
|
|
177 |
|
178 |
/** Require all the mailchimp api asset classes */
|
179 |
require_once $path . 'includes/api/assets/class-mailchimp-address.php';
|
@@ -184,6 +185,8 @@ class MailChimp_Woocommerce {
|
|
184 |
require_once $path . 'includes/api/assets/class-mailchimp-product.php';
|
185 |
require_once $path . 'includes/api/assets/class-mailchimp-product-variation.php';
|
186 |
require_once $path . 'includes/api/assets/class-mailchimp-store.php';
|
|
|
|
|
187 |
|
188 |
/** Require all the api error helpers */
|
189 |
require_once $path . 'includes/api/errors/class-mailchimp-error.php';
|
@@ -204,12 +207,14 @@ class MailChimp_Woocommerce {
|
|
204 |
// bulk data sync
|
205 |
require_once $path.'includes/processes/class-mailchimp-woocommerce-process-orders.php';
|
206 |
require_once $path.'includes/processes/class-mailchimp-woocommerce-process-products.php';
|
|
|
207 |
|
208 |
// individual item sync
|
209 |
require_once $path.'includes/processes/class-mailchimp-woocommerce-cart-update.php';
|
210 |
require_once $path.'includes/processes/class-mailchimp-woocommerce-single-order.php';
|
211 |
require_once $path.'includes/processes/class-mailchimp-woocommerce-single-product.php';
|
212 |
-
|
|
|
213 |
|
214 |
// fire up the loader
|
215 |
$this->loader = new MailChimp_Woocommerce_Loader();
|
@@ -325,7 +330,7 @@ class MailChimp_Woocommerce {
|
|
325 |
|
326 |
// order hooks
|
327 |
$this->loader->add_action('woocommerce_thankyou', $service, 'onNewOrder', 1);
|
328 |
-
$this->loader->add_action('woocommerce_api_create_order', $service, '
|
329 |
$this->loader->add_action('woocommerce_order_status_changed', $service, 'handleOrderStatusChanged', 2);
|
330 |
|
331 |
// partially refunded
|
@@ -336,8 +341,19 @@ class MailChimp_Woocommerce {
|
|
336 |
$this->loader->add_action('woocommerce_add_to_cart', $service, 'handleCartUpdated');
|
337 |
$this->loader->add_action('woocommerce_cart_item_removed', $service, 'handleCartUpdated');
|
338 |
|
339 |
-
// save post
|
340 |
$this->loader->add_action('save_post', $service, 'handlePostSaved', 10, 3);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
341 |
|
342 |
// handle the user registration hook
|
343 |
$this->loader->add_action('user_register', $service, 'handleUserRegistration');
|
174 |
require_once $path . 'includes/api/class-mailchimp-woocommerce-api.php';
|
175 |
require_once $path . 'includes/api/class-mailchimp-woocommerce-create-list-submission.php';
|
176 |
require_once $path . 'includes/api/class-mailchimp-woocommerce-transform-products.php';
|
177 |
+
require_once $path . 'includes/api/class-mailchimp-woocommerce-transform-coupons.php';
|
178 |
|
179 |
/** Require all the mailchimp api asset classes */
|
180 |
require_once $path . 'includes/api/assets/class-mailchimp-address.php';
|
185 |
require_once $path . 'includes/api/assets/class-mailchimp-product.php';
|
186 |
require_once $path . 'includes/api/assets/class-mailchimp-product-variation.php';
|
187 |
require_once $path . 'includes/api/assets/class-mailchimp-store.php';
|
188 |
+
require_once $path . 'includes/api/assets/class-mailchimp-promo-code.php';
|
189 |
+
require_once $path . 'includes/api/assets/class-mailchimp-promo-rule.php';
|
190 |
|
191 |
/** Require all the api error helpers */
|
192 |
require_once $path . 'includes/api/errors/class-mailchimp-error.php';
|
207 |
// bulk data sync
|
208 |
require_once $path.'includes/processes/class-mailchimp-woocommerce-process-orders.php';
|
209 |
require_once $path.'includes/processes/class-mailchimp-woocommerce-process-products.php';
|
210 |
+
require_once $path.'includes/processes/class-mailchimp-woocommerce-process-coupons.php';
|
211 |
|
212 |
// individual item sync
|
213 |
require_once $path.'includes/processes/class-mailchimp-woocommerce-cart-update.php';
|
214 |
require_once $path.'includes/processes/class-mailchimp-woocommerce-single-order.php';
|
215 |
require_once $path.'includes/processes/class-mailchimp-woocommerce-single-product.php';
|
216 |
+
require_once $path.'includes/processes/class-mailchimp-woocommerce-single-coupon.php';
|
217 |
+
require_once $path.'includes/processes/class-mailchimp-woocommerce-user-submit.php';
|
218 |
|
219 |
// fire up the loader
|
220 |
$this->loader = new MailChimp_Woocommerce_Loader();
|
330 |
|
331 |
// order hooks
|
332 |
$this->loader->add_action('woocommerce_thankyou', $service, 'onNewOrder', 1);
|
333 |
+
$this->loader->add_action('woocommerce_api_create_order', $service, 'onNewOrder', 10);
|
334 |
$this->loader->add_action('woocommerce_order_status_changed', $service, 'handleOrderStatusChanged', 2);
|
335 |
|
336 |
// partially refunded
|
341 |
$this->loader->add_action('woocommerce_add_to_cart', $service, 'handleCartUpdated');
|
342 |
$this->loader->add_action('woocommerce_cart_item_removed', $service, 'handleCartUpdated');
|
343 |
|
344 |
+
// save post hooks
|
345 |
$this->loader->add_action('save_post', $service, 'handlePostSaved', 10, 3);
|
346 |
+
$this->loader->add_action('wp_trash_post', $service, 'handlePostTrashed', 10);
|
347 |
+
$this->loader->add_action('untrashed_post', $service, 'handlePostRestored', 10);
|
348 |
+
|
349 |
+
//coupons
|
350 |
+
$this->loader->add_action('woocommerce_new_coupon', $service, 'handleNewCoupon', 10);
|
351 |
+
$this->loader->add_action('woocommerce_coupon_options_save', $service, 'handleCouponSaved', 10, 2);
|
352 |
+
$this->loader->add_action('woocommerce_api_create_coupon', $service, 'handleCouponSaved', 9, 2);
|
353 |
+
|
354 |
+
$this->loader->add_action('woocommerce_delete_coupon', $service, 'handleCouponTrashed', 10);
|
355 |
+
$this->loader->add_action('woocommerce_trash_coupon', $service, 'handleCouponTrashed', 10);
|
356 |
+
$this->loader->add_action('woocommerce_api_delete_coupon', $service, 'handleCouponTrashed', 9);
|
357 |
|
358 |
// handle the user registration hook
|
359 |
$this->loader->add_action('user_register', $service, 'handleUserRegistration');
|
includes/index.php
CHANGED
File without changes
|
includes/plugin-update-checker/README.md
DELETED
@@ -1,105 +0,0 @@
|
|
1 |
-
Plugin Update Checker
|
2 |
-
=====================
|
3 |
-
|
4 |
-
This is a custom update checker library for WordPress plugins. It lets you add automatic update notifications and one-click upgrades to your commercial and private plugins. All you need to do is put your plugin details in a JSON file, place the file on your server, and pass the URL to the library. The library periodically checks the URL to see if there's a new version available and displays an update notification to the user if necessary.
|
5 |
-
|
6 |
-
From the users' perspective, it works just like with plugins hosted on WordPress.org. The update checker uses the default plugin upgrade UI that will already be familiar to most WordPress users.
|
7 |
-
|
8 |
-
[See this blog post](http://w-shadow.com/blog/2010/09/02/automatic-updates-for-any-plugin/) for more information and usage instructions.
|
9 |
-
|
10 |
-
Getting Started
|
11 |
-
---------------
|
12 |
-
|
13 |
-
### Self-hosted Plugins
|
14 |
-
|
15 |
-
1. Make a JSON file that describes your plugin. Here's a minimal example:
|
16 |
-
|
17 |
-
```json
|
18 |
-
{
|
19 |
-
"name" : "My Cool Plugin",
|
20 |
-
"version" : "2.0",
|
21 |
-
"author" : "John Smith",
|
22 |
-
"download_url" : "http://example.com/plugins/my-cool-plugin.zip",
|
23 |
-
"sections" : {
|
24 |
-
"description" : "Plugin description here. You can use HTML."
|
25 |
-
}
|
26 |
-
}
|
27 |
-
```
|
28 |
-
See [this table](https://spreadsheets.google.com/pub?key=0AqP80E74YcUWdEdETXZLcXhjd2w0cHMwX2U1eDlWTHc&authkey=CK7h9toK&hl=en&single=true&gid=0&output=html) for a full list of supported fields.
|
29 |
-
2. Upload this file to a publicly accessible location.
|
30 |
-
3. Download [the update checker](https://github.com/YahnisElsts/plugin-update-checker/releases/latest), unzip the archive and copy the `plugin-update-checker` directory to your plugin.
|
31 |
-
4. Add the following code to the main plugin file:
|
32 |
-
|
33 |
-
```php
|
34 |
-
require 'plugin-update-checker/plugin-update-checker.php';
|
35 |
-
$myUpdateChecker = PucFactory::buildUpdateChecker(
|
36 |
-
'http://example.com/path/to/metadata.json',
|
37 |
-
__FILE__
|
38 |
-
);
|
39 |
-
```
|
40 |
-
|
41 |
-
#### Notes
|
42 |
-
- You could use [wp-update-server](https://github.com/YahnisElsts/wp-update-server) to automatically generate JSON metadata from ZIP packages.
|
43 |
-
- The second argument passed to `buildUpdateChecker` should be the full path to the main plugin file.
|
44 |
-
- There are more options available - see the [blog](http://w-shadow.com/blog/2010/09/02/automatic-updates-for-any-plugin/) for details.
|
45 |
-
|
46 |
-
### Plugins Hosted on GitHub
|
47 |
-
|
48 |
-
*(GitHub support is experimental.)*
|
49 |
-
|
50 |
-
1. Download [the latest release](https://github.com/YahnisElsts/plugin-update-checker/releases/latest), unzip it and copy the `plugin-update-checker` directory to your plugin.
|
51 |
-
2. Add the following code to the main file of your plugin:
|
52 |
-
|
53 |
-
```php
|
54 |
-
require 'plugin-update-checker/plugin-update-checker.php';
|
55 |
-
$className = PucFactory::getLatestClassVersion('PucGitHubChecker');
|
56 |
-
$myUpdateChecker = new $className(
|
57 |
-
'https://github.com/user-name/plugin-repo-name/',
|
58 |
-
__FILE__,
|
59 |
-
'master'
|
60 |
-
);
|
61 |
-
```
|
62 |
-
The third argument specifies the branch to use for updating your plugin. The default is `master`. If the branch name is omitted or set to `master`, the update checker will use the latest release or tag (if available). Otherwise it will use the specified branch.
|
63 |
-
3. Optional: Add a `readme.txt` file formatted according to the [WordPress.org plugin readme standard](https://wordpress.org/plugins/about/readme.txt). The contents of this file will be shown when the user clicks the "View version 1.2.3 details" link.
|
64 |
-
|
65 |
-
#### Notes
|
66 |
-
|
67 |
-
If your GitHub repository requires an access token, you can specify it like this:
|
68 |
-
```php
|
69 |
-
$myUpdateChecker->setAccessToken('your-token-here');
|
70 |
-
```
|
71 |
-
|
72 |
-
The GitHub version of the library will pull update details from the following parts of a release/tag/branch:
|
73 |
-
|
74 |
-
- Changelog
|
75 |
-
- The "Changelog" section of `readme.txt`.
|
76 |
-
- One of the following files:
|
77 |
-
CHANGES.md, CHANGELOG.md, changes.md, changelog.md
|
78 |
-
- Release notes.
|
79 |
-
- Version number
|
80 |
-
- The "Version" plugin header.
|
81 |
-
- The latest release or tag name.
|
82 |
-
- Required and tested WordPress versions
|
83 |
-
- The "Requires at least" and "Tested up to" fields in `readme.txt`.
|
84 |
-
- The following plugin headers:
|
85 |
-
`Required WP`, `Tested WP`, `Requires at least`, `Tested up to`
|
86 |
-
- "Last updated" timestamp
|
87 |
-
- The creation timestamp of the latest release.
|
88 |
-
- The latest commit of the selected tag or branch that changed the main plugin file.
|
89 |
-
- Number of downloads
|
90 |
-
- The `download_count` statistic of the latest release.
|
91 |
-
- If you're not using GitHub releases, there will be no download stats.
|
92 |
-
- Other plugin details - author, homepage URL, description
|
93 |
-
- The "Description" section of `readme.txt`.
|
94 |
-
- Remote plugin headers (i.e. the latest version on GitHub).
|
95 |
-
- Local plugin headers (i.e. the currently installed version).
|
96 |
-
- Ratings, banners, screenshots
|
97 |
-
- Not supported.
|
98 |
-
|
99 |
-
Resources
|
100 |
-
---------
|
101 |
-
|
102 |
-
- [Theme Update Checker](http://w-shadow.com/blog/2011/06/02/automatic-updates-for-commercial-themes/)
|
103 |
-
- [Debug Bar](https://wordpress.org/plugins/debug-bar/) - useful for testing and debugging the update checker.
|
104 |
-
- [Securing download links](http://w-shadow.com/blog/2013/03/19/plugin-updates-securing-download-links/) - a general overview.
|
105 |
-
- [A GUI for entering download credentials](http://open-tools.net/documentation/tutorial-automatic-updates.html#wordpress)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/plugin-update-checker/composer.json
DELETED
@@ -1,19 +0,0 @@
|
|
1 |
-
{
|
2 |
-
"name": "yahnis-elsts/plugin-update-checker",
|
3 |
-
"type": "library",
|
4 |
-
"description": "A custom update checker for WordPress plugins. Useful if you can't host your plugin in the official WP plugin repository but still want it to support automatic plugin updates.",
|
5 |
-
"keywords": ["wordpress", "plugin updates", "automatic updates"],
|
6 |
-
"homepage": "https://github.com/YahnisElsts/plugin-update-checker/",
|
7 |
-
"license": "MIT",
|
8 |
-
"authors": [
|
9 |
-
{
|
10 |
-
"name": "Yahnis Elsts",
|
11 |
-
"email": "whiteshadow@w-shadow.com",
|
12 |
-
"homepage": "http://w-shadow.com/",
|
13 |
-
"role": "Developer"
|
14 |
-
}
|
15 |
-
],
|
16 |
-
"require": {
|
17 |
-
"php": ">=5.2.0"
|
18 |
-
}
|
19 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/plugin-update-checker/css/puc-debug-bar.css
DELETED
@@ -1,62 +0,0 @@
|
|
1 |
-
.puc-debug-bar-panel pre {
|
2 |
-
margin-top: 0;
|
3 |
-
}
|
4 |
-
|
5 |
-
/* Style the debug data table to match "widefat" table style used by WordPress. */
|
6 |
-
table.puc-debug-data {
|
7 |
-
width: 100%;
|
8 |
-
clear: both;
|
9 |
-
margin: 0;
|
10 |
-
|
11 |
-
border-spacing: 0;
|
12 |
-
background-color: #f9f9f9;
|
13 |
-
|
14 |
-
border-radius: 3px;
|
15 |
-
border: 1px solid #dfdfdf;
|
16 |
-
border-collapse: separate;
|
17 |
-
}
|
18 |
-
|
19 |
-
table.puc-debug-data * {
|
20 |
-
word-wrap: break-word;
|
21 |
-
}
|
22 |
-
|
23 |
-
table.puc-debug-data th {
|
24 |
-
width: 11em;
|
25 |
-
padding: 7px 7px 8px;
|
26 |
-
text-align: left;
|
27 |
-
|
28 |
-
font-family: "Georgia", "Times New Roman", "Bitstream Charter", "Times", serif;
|
29 |
-
font-weight: 400;
|
30 |
-
font-size: 14px;
|
31 |
-
line-height: 1.3em;
|
32 |
-
text-shadow: rgba(255, 255, 255, 0.804) 0 1px 0;
|
33 |
-
}
|
34 |
-
|
35 |
-
table.puc-debug-data td, table.puc-debug-data th {
|
36 |
-
border-width: 1px 0;
|
37 |
-
border-style: solid;
|
38 |
-
|
39 |
-
border-top-color: #fff;
|
40 |
-
border-bottom-color: #dfdfdf;
|
41 |
-
|
42 |
-
text-transform: none;
|
43 |
-
}
|
44 |
-
|
45 |
-
table.puc-debug-data td {
|
46 |
-
color: #555;
|
47 |
-
font-size: 12px;
|
48 |
-
padding: 4px 7px 2px;
|
49 |
-
vertical-align: top;
|
50 |
-
}
|
51 |
-
|
52 |
-
.puc-ajax-response {
|
53 |
-
border: 1px solid #dfdfdf;
|
54 |
-
border-radius: 3px;
|
55 |
-
padding: 0.5em;
|
56 |
-
margin: 5px 0;
|
57 |
-
background-color: white;
|
58 |
-
}
|
59 |
-
|
60 |
-
.puc-ajax-nonce {
|
61 |
-
display: none;
|
62 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/plugin-update-checker/debug-bar-panel.php
DELETED
@@ -1,146 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
if ( !class_exists('PluginUpdateCheckerPanel_3_1', false) && class_exists('Debug_Bar_Panel', false) ) {
|
4 |
-
|
5 |
-
/**
|
6 |
-
* A Debug Bar panel for the plugin update checker.
|
7 |
-
*/
|
8 |
-
class PluginUpdateCheckerPanel_3_1 extends Debug_Bar_Panel {
|
9 |
-
/** @var PluginUpdateChecker_3_1 */
|
10 |
-
private $updateChecker;
|
11 |
-
|
12 |
-
private $responseBox = '<div class="puc-ajax-response" style="display: none;"></div>';
|
13 |
-
|
14 |
-
public function __construct($updateChecker) {
|
15 |
-
$this->updateChecker = $updateChecker;
|
16 |
-
$title = sprintf(
|
17 |
-
'<span id="puc-debug-menu-link-%s">PUC (%s)</span>',
|
18 |
-
esc_attr($this->updateChecker->slug),
|
19 |
-
$this->updateChecker->slug
|
20 |
-
);
|
21 |
-
parent::Debug_Bar_Panel($title);
|
22 |
-
}
|
23 |
-
|
24 |
-
public function render() {
|
25 |
-
printf(
|
26 |
-
'<div class="puc-debug-bar-panel" id="puc-debug-bar-panel_%1$s" data-slug="%1$s" data-nonce="%2$s">',
|
27 |
-
esc_attr($this->updateChecker->slug),
|
28 |
-
esc_attr(wp_create_nonce('puc-ajax'))
|
29 |
-
);
|
30 |
-
|
31 |
-
$this->displayConfiguration();
|
32 |
-
$this->displayStatus();
|
33 |
-
$this->displayCurrentUpdate();
|
34 |
-
|
35 |
-
echo '</div>';
|
36 |
-
}
|
37 |
-
|
38 |
-
private function displayConfiguration() {
|
39 |
-
echo '<h3>Configuration</h3>';
|
40 |
-
echo '<table class="puc-debug-data">';
|
41 |
-
$this->row('Plugin file', htmlentities($this->updateChecker->pluginFile));
|
42 |
-
$this->row('Slug', htmlentities($this->updateChecker->slug));
|
43 |
-
$this->row('DB option', htmlentities($this->updateChecker->optionName));
|
44 |
-
|
45 |
-
$requestInfoButton = '';
|
46 |
-
if ( function_exists('get_submit_button') ) {
|
47 |
-
$requestInfoButton = get_submit_button('Request Info', 'secondary', 'puc-request-info-button', false, array('id' => 'puc-request-info-button-' . $this->updateChecker->slug));
|
48 |
-
}
|
49 |
-
$this->row('Metadata URL', htmlentities($this->updateChecker->metadataUrl) . ' ' . $requestInfoButton . $this->responseBox);
|
50 |
-
|
51 |
-
$scheduler = $this->updateChecker->scheduler;
|
52 |
-
if ( $scheduler->checkPeriod > 0 ) {
|
53 |
-
$this->row('Automatic checks', 'Every ' . $scheduler->checkPeriod . ' hours');
|
54 |
-
} else {
|
55 |
-
$this->row('Automatic checks', 'Disabled');
|
56 |
-
}
|
57 |
-
|
58 |
-
if ( isset($scheduler->throttleRedundantChecks) ) {
|
59 |
-
if ( $scheduler->throttleRedundantChecks && ($scheduler->checkPeriod > 0) ) {
|
60 |
-
$this->row(
|
61 |
-
'Throttling',
|
62 |
-
sprintf(
|
63 |
-
'Enabled. If an update is already available, check for updates every %1$d hours instead of every %2$d hours.',
|
64 |
-
$scheduler->throttledCheckPeriod,
|
65 |
-
$scheduler->checkPeriod
|
66 |
-
)
|
67 |
-
);
|
68 |
-
} else {
|
69 |
-
$this->row('Throttling', 'Disabled');
|
70 |
-
}
|
71 |
-
}
|
72 |
-
echo '</table>';
|
73 |
-
}
|
74 |
-
|
75 |
-
private function displayStatus() {
|
76 |
-
echo '<h3>Status</h3>';
|
77 |
-
echo '<table class="puc-debug-data">';
|
78 |
-
$state = $this->updateChecker->getUpdateState();
|
79 |
-
$checkNowButton = '';
|
80 |
-
if ( function_exists('get_submit_button') ) {
|
81 |
-
$checkNowButton = get_submit_button('Check Now', 'secondary', 'puc-check-now-button', false, array('id' => 'puc-check-now-button-' . $this->updateChecker->slug));
|
82 |
-
}
|
83 |
-
|
84 |
-
if ( isset($state, $state->lastCheck) ) {
|
85 |
-
$this->row('Last check', $this->formatTimeWithDelta($state->lastCheck) . ' ' . $checkNowButton . $this->responseBox);
|
86 |
-
} else {
|
87 |
-
$this->row('Last check', 'Never');
|
88 |
-
}
|
89 |
-
|
90 |
-
$nextCheck = wp_next_scheduled($this->updateChecker->scheduler->getCronHookName());
|
91 |
-
$this->row('Next automatic check', $this->formatTimeWithDelta($nextCheck));
|
92 |
-
|
93 |
-
if ( isset($state, $state->checkedVersion) ) {
|
94 |
-
$this->row('Checked version', htmlentities($state->checkedVersion));
|
95 |
-
$this->row('Cached update', $state->update);
|
96 |
-
}
|
97 |
-
$this->row('Update checker class', htmlentities(get_class($this->updateChecker)));
|
98 |
-
echo '</table>';
|
99 |
-
}
|
100 |
-
|
101 |
-
private function displayCurrentUpdate() {
|
102 |
-
$update = $this->updateChecker->getUpdate();
|
103 |
-
if ( $update !== null ) {
|
104 |
-
echo '<h3>An Update Is Available</h3>';
|
105 |
-
echo '<table class="puc-debug-data">';
|
106 |
-
$fields = array('version', 'download_url', 'slug', 'homepage', 'upgrade_notice');
|
107 |
-
foreach($fields as $field) {
|
108 |
-
$this->row(ucwords(str_replace('_', ' ', $field)), htmlentities($update->$field));
|
109 |
-
}
|
110 |
-
echo '</table>';
|
111 |
-
} else {
|
112 |
-
echo '<h3>No updates currently available</h3>';
|
113 |
-
}
|
114 |
-
}
|
115 |
-
|
116 |
-
private function formatTimeWithDelta($unixTime) {
|
117 |
-
if ( empty($unixTime) ) {
|
118 |
-
return 'Never';
|
119 |
-
}
|
120 |
-
|
121 |
-
$delta = time() - $unixTime;
|
122 |
-
$result = human_time_diff(time(), $unixTime);
|
123 |
-
if ( $delta < 0 ) {
|
124 |
-
$result = 'after ' . $result;
|
125 |
-
} else {
|
126 |
-
$result = $result . ' ago';
|
127 |
-
}
|
128 |
-
$result .= ' (' . $this->formatTimestamp($unixTime) . ')';
|
129 |
-
return $result;
|
130 |
-
}
|
131 |
-
|
132 |
-
private function formatTimestamp($unixTime) {
|
133 |
-
return gmdate('Y-m-d H:i:s', $unixTime + (get_option('gmt_offset') * 3600));
|
134 |
-
}
|
135 |
-
|
136 |
-
private function row($name, $value) {
|
137 |
-
if ( is_object($value) || is_array($value) ) {
|
138 |
-
$value = '<pre>' . htmlentities(print_r($value, true)) . '</pre>';
|
139 |
-
} else if ($value === null) {
|
140 |
-
$value = '<code>null</code>';
|
141 |
-
}
|
142 |
-
printf('<tr><th scope="row">%1$s</th> <td>%2$s</td></tr>', $name, $value);
|
143 |
-
}
|
144 |
-
}
|
145 |
-
|
146 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/plugin-update-checker/debug-bar-plugin.php
DELETED
@@ -1,102 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
if ( !class_exists('PucDebugBarPlugin_3_1', false) ) {
|
3 |
-
|
4 |
-
class PucDebugBarPlugin_3_1 {
|
5 |
-
/** @var PluginUpdateChecker_3_1 */
|
6 |
-
private $updateChecker;
|
7 |
-
|
8 |
-
public function __construct($updateChecker) {
|
9 |
-
$this->updateChecker = $updateChecker;
|
10 |
-
|
11 |
-
add_filter('debug_bar_panels', array($this, 'addDebugBarPanel'));
|
12 |
-
add_action('debug_bar_enqueue_scripts', array($this, 'enqueuePanelDependencies'));
|
13 |
-
|
14 |
-
add_action('wp_ajax_puc_debug_check_now', array($this, 'ajaxCheckNow'));
|
15 |
-
add_action('wp_ajax_puc_debug_request_info', array($this, 'ajaxRequestInfo'));
|
16 |
-
}
|
17 |
-
|
18 |
-
/**
|
19 |
-
* Register the PUC Debug Bar panel.
|
20 |
-
*
|
21 |
-
* @param array $panels
|
22 |
-
* @return array
|
23 |
-
*/
|
24 |
-
public function addDebugBarPanel($panels) {
|
25 |
-
require_once dirname(__FILE__) . '/debug-bar-panel.php';
|
26 |
-
if ( current_user_can('update_plugins') && class_exists('PluginUpdateCheckerPanel_3_1', false) ) {
|
27 |
-
$panels[] = new PluginUpdateCheckerPanel_3_1($this->updateChecker);
|
28 |
-
}
|
29 |
-
return $panels;
|
30 |
-
}
|
31 |
-
|
32 |
-
/**
|
33 |
-
* Enqueue our Debug Bar scripts and styles.
|
34 |
-
*/
|
35 |
-
public function enqueuePanelDependencies() {
|
36 |
-
wp_enqueue_style(
|
37 |
-
'puc-debug-bar-style',
|
38 |
-
plugins_url( "/css/puc-debug-bar.css", __FILE__ ),
|
39 |
-
array('debug-bar'),
|
40 |
-
'20130927'
|
41 |
-
);
|
42 |
-
|
43 |
-
wp_enqueue_script(
|
44 |
-
'puc-debug-bar-js',
|
45 |
-
plugins_url( "/js/debug-bar.js", __FILE__ ),
|
46 |
-
array('jquery'),
|
47 |
-
'20121026'
|
48 |
-
);
|
49 |
-
}
|
50 |
-
|
51 |
-
/**
|
52 |
-
* Run an update check and output the result. Useful for making sure that
|
53 |
-
* the update checking process works as expected.
|
54 |
-
*/
|
55 |
-
public function ajaxCheckNow() {
|
56 |
-
if ( $_POST['slug'] !== $this->updateChecker->slug ) {
|
57 |
-
return;
|
58 |
-
}
|
59 |
-
$this->preAjaxReqest();
|
60 |
-
$update = $this->updateChecker->checkForUpdates();
|
61 |
-
if ( $update !== null ) {
|
62 |
-
echo "An update is available:";
|
63 |
-
echo '<pre>', htmlentities(print_r($update, true)), '</pre>';
|
64 |
-
} else {
|
65 |
-
echo 'No updates found.';
|
66 |
-
}
|
67 |
-
exit;
|
68 |
-
}
|
69 |
-
|
70 |
-
/**
|
71 |
-
* Request plugin info and output it.
|
72 |
-
*/
|
73 |
-
public function ajaxRequestInfo() {
|
74 |
-
if ( $_POST['slug'] !== $this->updateChecker->slug ) {
|
75 |
-
return;
|
76 |
-
}
|
77 |
-
$this->preAjaxReqest();
|
78 |
-
$info = $this->updateChecker->requestInfo();
|
79 |
-
if ( $info !== null ) {
|
80 |
-
echo 'Successfully retrieved plugin info from the metadata URL:';
|
81 |
-
echo '<pre>', htmlentities(print_r($info, true)), '</pre>';
|
82 |
-
} else {
|
83 |
-
echo 'Failed to retrieve plugin info from the metadata URL.';
|
84 |
-
}
|
85 |
-
exit;
|
86 |
-
}
|
87 |
-
|
88 |
-
/**
|
89 |
-
* Check access permissions and enable error display (for debugging).
|
90 |
-
*/
|
91 |
-
private function preAjaxReqest() {
|
92 |
-
if ( !current_user_can('update_plugins') ) {
|
93 |
-
die('Access denied');
|
94 |
-
}
|
95 |
-
check_ajax_referer('puc-ajax');
|
96 |
-
|
97 |
-
error_reporting(E_ALL);
|
98 |
-
@ini_set('display_errors','On');
|
99 |
-
}
|
100 |
-
}
|
101 |
-
|
102 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/plugin-update-checker/github-checker.php
DELETED
@@ -1,458 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
if ( !class_exists('PucGitHubChecker_3_1', false) ):
|
4 |
-
|
5 |
-
class PucGitHubChecker_3_1 extends PluginUpdateChecker_3_1 {
|
6 |
-
/**
|
7 |
-
* @var string GitHub username.
|
8 |
-
*/
|
9 |
-
protected $userName;
|
10 |
-
/**
|
11 |
-
* @var string GitHub repository name.
|
12 |
-
*/
|
13 |
-
protected $repositoryName;
|
14 |
-
|
15 |
-
/**
|
16 |
-
* @var string Either a fully qualified repository URL, or just "user/repo-name".
|
17 |
-
*/
|
18 |
-
protected $repositoryUrl;
|
19 |
-
|
20 |
-
/**
|
21 |
-
* @var string The branch to use as the latest version. Defaults to "master".
|
22 |
-
*/
|
23 |
-
protected $branch;
|
24 |
-
|
25 |
-
/**
|
26 |
-
* @var string GitHub authentication token. Optional.
|
27 |
-
*/
|
28 |
-
protected $accessToken;
|
29 |
-
|
30 |
-
public function __construct(
|
31 |
-
$repositoryUrl,
|
32 |
-
$pluginFile,
|
33 |
-
$branch = 'master',
|
34 |
-
$checkPeriod = 1,
|
35 |
-
$optionName = '',
|
36 |
-
$muPluginFile = ''
|
37 |
-
) {
|
38 |
-
|
39 |
-
$this->repositoryUrl = $repositoryUrl;
|
40 |
-
$this->branch = empty($branch) ? 'master' : $branch;
|
41 |
-
|
42 |
-
$path = @parse_url($repositoryUrl, PHP_URL_PATH);
|
43 |
-
if ( preg_match('@^/?(?P<username>[^/]+?)/(?P<repository>[^/#?&]+?)/?$@', $path, $matches) ) {
|
44 |
-
$this->userName = $matches['username'];
|
45 |
-
$this->repositoryName = $matches['repository'];
|
46 |
-
} else {
|
47 |
-
throw new InvalidArgumentException('Invalid GitHub repository URL: "' . $repositoryUrl . '"');
|
48 |
-
}
|
49 |
-
|
50 |
-
parent::__construct($repositoryUrl, $pluginFile, '', $checkPeriod, $optionName, $muPluginFile);
|
51 |
-
}
|
52 |
-
|
53 |
-
/**
|
54 |
-
* Retrieve details about the latest plugin version from GitHub.
|
55 |
-
*
|
56 |
-
* @param array $unusedQueryArgs Unused.
|
57 |
-
* @return PluginInfo_3_1
|
58 |
-
*/
|
59 |
-
public function requestInfo($unusedQueryArgs = array()) {
|
60 |
-
$info = new PluginInfo_3_1();
|
61 |
-
$info->filename = $this->pluginFile;
|
62 |
-
$info->slug = $this->slug;
|
63 |
-
|
64 |
-
$this->setInfoFromHeader($this->getPluginHeader(), $info);
|
65 |
-
|
66 |
-
//Figure out which reference (tag or branch) we'll use to get the latest version of the plugin.
|
67 |
-
$ref = $this->branch;
|
68 |
-
if ( $this->branch === 'master' ) {
|
69 |
-
//Use the latest release.
|
70 |
-
$release = $this->getLatestRelease();
|
71 |
-
if ( $release !== null ) {
|
72 |
-
$ref = $release->tag_name;
|
73 |
-
$info->version = ltrim($release->tag_name, 'v'); //Remove the "v" prefix from "v1.2.3".
|
74 |
-
$info->last_updated = $release->created_at;
|
75 |
-
$info->download_url = $release->zipball_url;
|
76 |
-
|
77 |
-
if ( !empty($release->body) ) {
|
78 |
-
$info->sections['changelog'] = $this->parseMarkdown($release->body);
|
79 |
-
}
|
80 |
-
if ( isset($release->assets[0]) ) {
|
81 |
-
$info->downloaded = $release->assets[0]->download_count;
|
82 |
-
}
|
83 |
-
} else {
|
84 |
-
//Failing that, use the tag with the highest version number.
|
85 |
-
$tag = $this->getLatestTag();
|
86 |
-
if ( $tag !== null ) {
|
87 |
-
$ref = $tag->name;
|
88 |
-
$info->version = $tag->name;
|
89 |
-
$info->download_url = $tag->zipball_url;
|
90 |
-
}
|
91 |
-
}
|
92 |
-
}
|
93 |
-
|
94 |
-
if ( empty($info->download_url) ) {
|
95 |
-
$info->download_url = $this->buildArchiveDownloadUrl($ref);
|
96 |
-
} else if ( !empty($this->accessToken) ) {
|
97 |
-
$info->download_url = add_query_arg('access_token', $this->accessToken, $info->download_url);
|
98 |
-
}
|
99 |
-
|
100 |
-
//Get headers from the main plugin file in this branch/tag. Its "Version" header and other metadata
|
101 |
-
//are what the WordPress install will actually see after upgrading, so they take precedence over releases/tags.
|
102 |
-
$mainPluginFile = basename($this->pluginFile);
|
103 |
-
$remotePlugin = $this->getRemoteFile($mainPluginFile, $ref);
|
104 |
-
if ( !empty($remotePlugin) ) {
|
105 |
-
$remoteHeader = $this->getFileHeader($remotePlugin);
|
106 |
-
$this->setInfoFromHeader($remoteHeader, $info);
|
107 |
-
}
|
108 |
-
|
109 |
-
//Try parsing readme.txt. If it's formatted according to WordPress.org standards, it will contain
|
110 |
-
//a lot of useful information like the required/tested WP version, changelog, and so on.
|
111 |
-
if ( $this->readmeTxtExistsLocally() ) {
|
112 |
-
$this->setInfoFromRemoteReadme($ref, $info);
|
113 |
-
}
|
114 |
-
|
115 |
-
//The changelog might be in a separate file.
|
116 |
-
if ( empty($info->sections['changelog']) ) {
|
117 |
-
$info->sections['changelog'] = $this->getRemoteChangelog($ref);
|
118 |
-
if ( empty($info->sections['changelog']) ) {
|
119 |
-
$info->sections['changelog'] = __('There is no changelog available.', 'plugin-update-checker');
|
120 |
-
}
|
121 |
-
}
|
122 |
-
|
123 |
-
if ( empty($info->last_updated) ) {
|
124 |
-
//Fetch the latest commit that changed the main plugin file and use it as the "last_updated" date.
|
125 |
-
//It's reasonable to assume that every update will change the version number in that file.
|
126 |
-
$latestCommit = $this->getLatestCommit($mainPluginFile, $ref);
|
127 |
-
if ( $latestCommit !== null ) {
|
128 |
-
$info->last_updated = $latestCommit->commit->author->date;
|
129 |
-
}
|
130 |
-
}
|
131 |
-
$info = apply_filters('puc_request_info_result-' . $this->slug, $info, null);
|
132 |
-
return $info;
|
133 |
-
}
|
134 |
-
|
135 |
-
/**
|
136 |
-
* Get the latest release from GitHub.
|
137 |
-
*
|
138 |
-
* @return StdClass|null
|
139 |
-
*/
|
140 |
-
protected function getLatestRelease() {
|
141 |
-
$releases = $this->api('/repos/:user/:repo/releases');
|
142 |
-
if ( is_wp_error($releases) || !is_array($releases) || !isset($releases[0]) ) {
|
143 |
-
return null;
|
144 |
-
}
|
145 |
-
|
146 |
-
$latestRelease = $releases[0];
|
147 |
-
return $latestRelease;
|
148 |
-
}
|
149 |
-
|
150 |
-
/**
|
151 |
-
* Get the tag that looks like the highest version number.
|
152 |
-
*
|
153 |
-
* @return StdClass|null
|
154 |
-
*/
|
155 |
-
protected function getLatestTag() {
|
156 |
-
$tags = $this->api('/repos/:user/:repo/tags');
|
157 |
-
|
158 |
-
if ( is_wp_error($tags) || empty($tags) || !is_array($tags) ) {
|
159 |
-
return null;
|
160 |
-
}
|
161 |
-
|
162 |
-
usort($tags, array($this, 'compareTagNames')); //Sort from highest to lowest.
|
163 |
-
return $tags[0];
|
164 |
-
}
|
165 |
-
|
166 |
-
/**
|
167 |
-
* Compare two GitHub tags as if they were version number.
|
168 |
-
*
|
169 |
-
* @param string $tag1
|
170 |
-
* @param string $tag2
|
171 |
-
* @return int
|
172 |
-
*/
|
173 |
-
protected function compareTagNames($tag1, $tag2) {
|
174 |
-
if ( !isset($tag1->name) ) {
|
175 |
-
return 1;
|
176 |
-
}
|
177 |
-
if ( !isset($tag2->name) ) {
|
178 |
-
return -1;
|
179 |
-
}
|
180 |
-
return -version_compare($tag1->name, $tag2->name);
|
181 |
-
}
|
182 |
-
|
183 |
-
/**
|
184 |
-
* Get the latest commit that changed the specified file.
|
185 |
-
*
|
186 |
-
* @param string $filename
|
187 |
-
* @param string $ref Reference name (e.g. branch or tag).
|
188 |
-
* @return StdClass|null
|
189 |
-
*/
|
190 |
-
protected function getLatestCommit($filename, $ref = 'master') {
|
191 |
-
$commits = $this->api(
|
192 |
-
'/repos/:user/:repo/commits',
|
193 |
-
array(
|
194 |
-
'path' => $filename,
|
195 |
-
'sha' => $ref,
|
196 |
-
)
|
197 |
-
);
|
198 |
-
if ( !is_wp_error($commits) && is_array($commits) && isset($commits[0]) ) {
|
199 |
-
return $commits[0];
|
200 |
-
}
|
201 |
-
return null;
|
202 |
-
}
|
203 |
-
|
204 |
-
protected function getRemoteChangelog($ref = '') {
|
205 |
-
$filename = $this->getChangelogFilename();
|
206 |
-
if ( empty($filename) ) {
|
207 |
-
return null;
|
208 |
-
}
|
209 |
-
|
210 |
-
$changelog = $this->getRemoteFile($filename, $ref);
|
211 |
-
if ( $changelog === null ) {
|
212 |
-
return null;
|
213 |
-
}
|
214 |
-
return $this->parseMarkdown($changelog);
|
215 |
-
}
|
216 |
-
|
217 |
-
protected function getChangelogFilename() {
|
218 |
-
$pluginDirectory = dirname($this->pluginAbsolutePath);
|
219 |
-
if ( empty($this->pluginAbsolutePath) || !is_dir($pluginDirectory) || ($pluginDirectory === '.') ) {
|
220 |
-
return null;
|
221 |
-
}
|
222 |
-
|
223 |
-
$possibleNames = array('CHANGES.md', 'CHANGELOG.md', 'changes.md', 'changelog.md');
|
224 |
-
$files = scandir($pluginDirectory);
|
225 |
-
$foundNames = array_intersect($possibleNames, $files);
|
226 |
-
|
227 |
-
if ( !empty($foundNames) ) {
|
228 |
-
return reset($foundNames);
|
229 |
-
}
|
230 |
-
return null;
|
231 |
-
}
|
232 |
-
|
233 |
-
/**
|
234 |
-
* Convert Markdown to HTML.
|
235 |
-
*
|
236 |
-
* @param string $markdown
|
237 |
-
* @return string
|
238 |
-
*/
|
239 |
-
protected function parseMarkdown($markdown) {
|
240 |
-
if ( !class_exists('Parsedown', false) ) {
|
241 |
-
require_once(dirname(__FILE__) . '/vendor/Parsedown' . (version_compare(PHP_VERSION, '5.3.0', '>=') ? '' : 'Legacy') . '.php');
|
242 |
-
}
|
243 |
-
|
244 |
-
$instance = Parsedown::instance();
|
245 |
-
return $instance->text($markdown);
|
246 |
-
}
|
247 |
-
|
248 |
-
/**
|
249 |
-
* Perform a GitHub API request.
|
250 |
-
*
|
251 |
-
* @param string $url
|
252 |
-
* @param array $queryParams
|
253 |
-
* @return mixed|WP_Error
|
254 |
-
*/
|
255 |
-
protected function api($url, $queryParams = array()) {
|
256 |
-
$variables = array(
|
257 |
-
'user' => $this->userName,
|
258 |
-
'repo' => $this->repositoryName,
|
259 |
-
);
|
260 |
-
foreach ($variables as $name => $value) {
|
261 |
-
$url = str_replace('/:' . $name, '/' . urlencode($value), $url);
|
262 |
-
}
|
263 |
-
$url = 'https://api.github.com' . $url;
|
264 |
-
|
265 |
-
if ( !empty($this->accessToken) ) {
|
266 |
-
$queryParams['access_token'] = $this->accessToken;
|
267 |
-
}
|
268 |
-
if ( !empty($queryParams) ) {
|
269 |
-
$url = add_query_arg($queryParams, $url);
|
270 |
-
}
|
271 |
-
|
272 |
-
$response = wp_remote_get($url, array('timeout' => 10));
|
273 |
-
if ( is_wp_error($response) ) {
|
274 |
-
return $response;
|
275 |
-
}
|
276 |
-
|
277 |
-
$code = wp_remote_retrieve_response_code($response);
|
278 |
-
$body = wp_remote_retrieve_body($response);
|
279 |
-
if ( $code === 200 ) {
|
280 |
-
$document = json_decode($body);
|
281 |
-
return $document;
|
282 |
-
}
|
283 |
-
|
284 |
-
return new WP_Error(
|
285 |
-
'puc-github-http-error',
|
286 |
-
'GitHub API error. HTTP status: ' . $code
|
287 |
-
);
|
288 |
-
}
|
289 |
-
|
290 |
-
/**
|
291 |
-
* Set the access token that will be used to make authenticated GitHub API requests.
|
292 |
-
*
|
293 |
-
* @param string $accessToken
|
294 |
-
*/
|
295 |
-
public function setAccessToken($accessToken) {
|
296 |
-
$this->accessToken = $accessToken;
|
297 |
-
}
|
298 |
-
|
299 |
-
/**
|
300 |
-
* Get the contents of a file from a specific branch or tag.
|
301 |
-
*
|
302 |
-
* @param string $path File name.
|
303 |
-
* @param string $ref
|
304 |
-
* @return null|string Either the contents of the file, or null if the file doesn't exist or there's an error.
|
305 |
-
*/
|
306 |
-
protected function getRemoteFile($path, $ref = 'master') {
|
307 |
-
$apiUrl = '/repos/:user/:repo/contents/' . $path;
|
308 |
-
$response = $this->api($apiUrl, array('ref' => $ref));
|
309 |
-
|
310 |
-
if ( is_wp_error($response) || !isset($response->content) || ($response->encoding !== 'base64') ) {
|
311 |
-
return null;
|
312 |
-
}
|
313 |
-
return base64_decode($response->content);
|
314 |
-
}
|
315 |
-
|
316 |
-
/**
|
317 |
-
* Parse plugin metadata from the header comment.
|
318 |
-
* This is basically a simplified version of the get_file_data() function from /wp-includes/functions.php.
|
319 |
-
*
|
320 |
-
* @param $content
|
321 |
-
* @return array
|
322 |
-
*/
|
323 |
-
protected function getFileHeader($content) {
|
324 |
-
$headers = array(
|
325 |
-
'Name' => 'Plugin Name',
|
326 |
-
'PluginURI' => 'Plugin URI',
|
327 |
-
'Version' => 'Version',
|
328 |
-
'Description' => 'Description',
|
329 |
-
'Author' => 'Author',
|
330 |
-
'AuthorURI' => 'Author URI',
|
331 |
-
'TextDomain' => 'Text Domain',
|
332 |
-
'DomainPath' => 'Domain Path',
|
333 |
-
'Network' => 'Network',
|
334 |
-
|
335 |
-
//The newest WordPress version that this plugin requires or has been tested with.
|
336 |
-
//We support several different formats for compatibility with other libraries.
|
337 |
-
'Tested WP' => 'Tested WP',
|
338 |
-
'Requires WP' => 'Requires WP',
|
339 |
-
'Tested up to' => 'Tested up to',
|
340 |
-
'Requires at least' => 'Requires at least',
|
341 |
-
);
|
342 |
-
|
343 |
-
$content = str_replace("\r", "\n", $content); //Normalize line endings.
|
344 |
-
$results = array();
|
345 |
-
foreach ($headers as $field => $name) {
|
346 |
-
$success = preg_match('/^[ \t\/*#@]*' . preg_quote($name, '/') . ':(.*)$/mi', $content, $matches);
|
347 |
-
if ( ($success === 1) && $matches[1] ) {
|
348 |
-
$results[$field] = _cleanup_header_comment($matches[1]);
|
349 |
-
} else {
|
350 |
-
$results[$field] = '';
|
351 |
-
}
|
352 |
-
}
|
353 |
-
|
354 |
-
return $results;
|
355 |
-
}
|
356 |
-
|
357 |
-
/**
|
358 |
-
* Copy plugin metadata from a file header to a PluginInfo object.
|
359 |
-
*
|
360 |
-
* @param array $fileHeader
|
361 |
-
* @param PluginInfo_3_1 $pluginInfo
|
362 |
-
*/
|
363 |
-
protected function setInfoFromHeader($fileHeader, $pluginInfo) {
|
364 |
-
$headerToPropertyMap = array(
|
365 |
-
'Version' => 'version',
|
366 |
-
'Name' => 'name',
|
367 |
-
'PluginURI' => 'homepage',
|
368 |
-
'Author' => 'author',
|
369 |
-
'AuthorName' => 'author',
|
370 |
-
'AuthorURI' => 'author_homepage',
|
371 |
-
|
372 |
-
'Requires WP' => 'requires',
|
373 |
-
'Tested WP' => 'tested',
|
374 |
-
'Requires at least' => 'requires',
|
375 |
-
'Tested up to' => 'tested',
|
376 |
-
);
|
377 |
-
foreach ($headerToPropertyMap as $headerName => $property) {
|
378 |
-
if ( isset($fileHeader[$headerName]) && !empty($fileHeader[$headerName]) ) {
|
379 |
-
$pluginInfo->$property = $fileHeader[$headerName];
|
380 |
-
}
|
381 |
-
}
|
382 |
-
|
383 |
-
if ( !empty($fileHeader['Description']) ) {
|
384 |
-
$pluginInfo->sections['description'] = $fileHeader['Description'];
|
385 |
-
}
|
386 |
-
}
|
387 |
-
|
388 |
-
/**
|
389 |
-
* Copy plugin metadata from the remote readme.txt file.
|
390 |
-
*
|
391 |
-
* @param string $ref GitHub tag or branch where to look for the readme.
|
392 |
-
* @param PluginInfo_3_1 $pluginInfo
|
393 |
-
*/
|
394 |
-
protected function setInfoFromRemoteReadme($ref, $pluginInfo) {
|
395 |
-
$readmeTxt = $this->getRemoteFile('readme.txt', $ref);
|
396 |
-
if ( empty($readmeTxt) ) {
|
397 |
-
return;
|
398 |
-
}
|
399 |
-
|
400 |
-
$readme = $this->parseReadme($readmeTxt);
|
401 |
-
|
402 |
-
if ( isset($readme['sections']) ) {
|
403 |
-
$pluginInfo->sections = array_merge($pluginInfo->sections, $readme['sections']);
|
404 |
-
}
|
405 |
-
if ( !empty($readme['tested_up_to']) ) {
|
406 |
-
$pluginInfo->tested = $readme['tested_up_to'];
|
407 |
-
}
|
408 |
-
if ( !empty($readme['requires_at_least']) ) {
|
409 |
-
$pluginInfo->requires = $readme['requires_at_least'];
|
410 |
-
}
|
411 |
-
|
412 |
-
if ( isset($readme['upgrade_notice'], $readme['upgrade_notice'][$pluginInfo->version]) ) {
|
413 |
-
$pluginInfo->upgrade_notice = $readme['upgrade_notice'][$pluginInfo->version];
|
414 |
-
}
|
415 |
-
}
|
416 |
-
|
417 |
-
protected function parseReadme($content) {
|
418 |
-
if ( !class_exists('PucReadmeParser', false) ) {
|
419 |
-
require_once(dirname(__FILE__) . '/vendor/readme-parser.php');
|
420 |
-
}
|
421 |
-
$parser = new PucReadmeParser();
|
422 |
-
return $parser->parse_readme_contents($content);
|
423 |
-
}
|
424 |
-
|
425 |
-
/**
|
426 |
-
* Check if the currently installed version has a readme.txt file.
|
427 |
-
*
|
428 |
-
* @return bool
|
429 |
-
*/
|
430 |
-
protected function readmeTxtExistsLocally() {
|
431 |
-
$pluginDirectory = dirname($this->pluginAbsolutePath);
|
432 |
-
if ( empty($this->pluginAbsolutePath) || !is_dir($pluginDirectory) || ($pluginDirectory === '.') ) {
|
433 |
-
return false;
|
434 |
-
}
|
435 |
-
return is_file($pluginDirectory . '/readme.txt');
|
436 |
-
}
|
437 |
-
|
438 |
-
/**
|
439 |
-
* Generate a URL to download a ZIP archive of the specified branch/tag/etc.
|
440 |
-
*
|
441 |
-
* @param string $ref
|
442 |
-
* @return string
|
443 |
-
*/
|
444 |
-
protected function buildArchiveDownloadUrl($ref = 'master') {
|
445 |
-
$url = sprintf(
|
446 |
-
'https://api.github.com/repos/%1$s/%2$s/zipball/%3$s',
|
447 |
-
urlencode($this->userName),
|
448 |
-
urlencode($this->repositoryName),
|
449 |
-
urlencode($ref)
|
450 |
-
);
|
451 |
-
if ( !empty($this->accessToken) ) {
|
452 |
-
$url = add_query_arg('access_token', $this->accessToken, $url);
|
453 |
-
}
|
454 |
-
return $url;
|
455 |
-
}
|
456 |
-
}
|
457 |
-
|
458 |
-
endif;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/plugin-update-checker/js/debug-bar.js
DELETED
@@ -1,52 +0,0 @@
|
|
1 |
-
jQuery(function($) {
|
2 |
-
|
3 |
-
function runAjaxAction(button, action) {
|
4 |
-
button = $(button);
|
5 |
-
var panel = button.closest('.puc-debug-bar-panel');
|
6 |
-
var responseBox = button.closest('td').find('.puc-ajax-response');
|
7 |
-
|
8 |
-
responseBox.text('Processing...').show();
|
9 |
-
$.post(
|
10 |
-
ajaxurl,
|
11 |
-
{
|
12 |
-
action : action,
|
13 |
-
slug : panel.data('slug'),
|
14 |
-
_wpnonce: panel.data('nonce')
|
15 |
-
},
|
16 |
-
function(data) {
|
17 |
-
responseBox.html(data);
|
18 |
-
},
|
19 |
-
'html'
|
20 |
-
);
|
21 |
-
}
|
22 |
-
|
23 |
-
$('.puc-debug-bar-panel input[name="puc-check-now-button"]').click(function() {
|
24 |
-
runAjaxAction(this, 'puc_debug_check_now');
|
25 |
-
return false;
|
26 |
-
});
|
27 |
-
|
28 |
-
$('.puc-debug-bar-panel input[name="puc-request-info-button"]').click(function() {
|
29 |
-
runAjaxAction(this, 'puc_debug_request_info');
|
30 |
-
return false;
|
31 |
-
});
|
32 |
-
|
33 |
-
|
34 |
-
// Debug Bar uses the panel class name as part of its link and container IDs. This means we can
|
35 |
-
// end up with multiple identical IDs if more than one plugin uses the update checker library.
|
36 |
-
// Fix it by replacing the class name with the plugin slug.
|
37 |
-
var panels = $('#debug-menu-targets').find('.puc-debug-bar-panel');
|
38 |
-
panels.each(function(index) {
|
39 |
-
var panel = $(this);
|
40 |
-
var slug = panel.data('slug');
|
41 |
-
var target = panel.closest('.debug-menu-target');
|
42 |
-
|
43 |
-
//Change the panel wrapper ID.
|
44 |
-
target.attr('id', 'debug-menu-target-puc-' + slug);
|
45 |
-
|
46 |
-
//Change the menu link ID as well and point it at the new target ID.
|
47 |
-
$('#puc-debug-menu-link-' + panel.data('slug'))
|
48 |
-
.closest('.debug-menu-link')
|
49 |
-
.attr('id', 'debug-menu-link-puc-' + slug)
|
50 |
-
.attr('href', '#' + target.attr('id'));
|
51 |
-
});
|
52 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/plugin-update-checker/languages/plugin-update-checker-fr_FR.mo
DELETED
Binary file
|
includes/plugin-update-checker/languages/plugin-update-checker-fr_FR.po
DELETED
@@ -1,38 +0,0 @@
|
|
1 |
-
msgid ""
|
2 |
-
msgstr ""
|
3 |
-
"Project-Id-Version: plugin-update-checker\n"
|
4 |
-
"POT-Creation-Date: 2016-02-17 14:21+0100\n"
|
5 |
-
"PO-Revision-Date: 2016-02-17 14:22+0100\n"
|
6 |
-
"Last-Translator: studio RVOLA <hello@rvola.com>\n"
|
7 |
-
"Language-Team: studio RVOLA <hello@rvola.com>\n"
|
8 |
-
"Language: fr_FR\n"
|
9 |
-
"MIME-Version: 1.0\n"
|
10 |
-
"Content-Type: text/plain; charset=UTF-8\n"
|
11 |
-
"Content-Transfer-Encoding: 8bit\n"
|
12 |
-
"X-Generator: Poedit 1.8.7\n"
|
13 |
-
"X-Poedit-Basepath: ..\n"
|
14 |
-
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
15 |
-
"X-Poedit-SourceCharset: UTF-8\n"
|
16 |
-
"X-Poedit-KeywordsList: __;_e\n"
|
17 |
-
"X-Poedit-SearchPath-0: .\n"
|
18 |
-
|
19 |
-
#: github-checker.php:120
|
20 |
-
msgid "There is no changelog available."
|
21 |
-
msgstr "Il n’y a aucun changelog disponible."
|
22 |
-
|
23 |
-
#: plugin-update-checker.php:637
|
24 |
-
msgid "Check for updates"
|
25 |
-
msgstr "Vérifier les mises à jour"
|
26 |
-
|
27 |
-
#: plugin-update-checker.php:681
|
28 |
-
msgid "This plugin is up to date."
|
29 |
-
msgstr "Ce plugin est à jour."
|
30 |
-
|
31 |
-
#: plugin-update-checker.php:683
|
32 |
-
msgid "A new version of this plugin is available."
|
33 |
-
msgstr "Une nouvelle version de ce plugin est disponible."
|
34 |
-
|
35 |
-
#: plugin-update-checker.php:685
|
36 |
-
#, php-format
|
37 |
-
msgid "Unknown update checker status \"%s\""
|
38 |
-
msgstr "Un problème inconnu est survenu \"%s\""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/plugin-update-checker/languages/plugin-update-checker-hu_HU.mo
DELETED
Binary file
|
includes/plugin-update-checker/languages/plugin-update-checker-hu_HU.po
DELETED
@@ -1,41 +0,0 @@
|
|
1 |
-
msgid ""
|
2 |
-
msgstr ""
|
3 |
-
"Project-Id-Version: plugin-update-checker\n"
|
4 |
-
"POT-Creation-Date: 2016-01-11 21:23+0100\n"
|
5 |
-
"PO-Revision-Date: 2016-01-11 21:25+0100\n"
|
6 |
-
"Last-Translator: Tamás András Horváth <htomy92@gmail.com>\n"
|
7 |
-
"Language-Team: \n"
|
8 |
-
"Language: hu_HU\n"
|
9 |
-
"MIME-Version: 1.0\n"
|
10 |
-
"Content-Type: text/plain; charset=UTF-8\n"
|
11 |
-
"Content-Transfer-Encoding: 8bit\n"
|
12 |
-
"X-Generator: Poedit 1.8.6\n"
|
13 |
-
"X-Poedit-Basepath: ..\n"
|
14 |
-
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
15 |
-
"X-Poedit-SourceCharset: UTF-8\n"
|
16 |
-
"X-Poedit-KeywordsList: __;_e\n"
|
17 |
-
"X-Poedit-SearchPath-0: .\n"
|
18 |
-
|
19 |
-
#: github-checker.php:137
|
20 |
-
msgid "There is no changelog available."
|
21 |
-
msgstr "Nem érhető el a changelog."
|
22 |
-
|
23 |
-
#: plugin-update-checker.php:852
|
24 |
-
msgid "Check for updates"
|
25 |
-
msgstr "Frissítés ellenőrzése"
|
26 |
-
|
27 |
-
#: plugin-update-checker.php:896
|
28 |
-
msgid "This plugin is up to date."
|
29 |
-
msgstr "Ez a plugin naprakész."
|
30 |
-
|
31 |
-
#: plugin-update-checker.php:898
|
32 |
-
msgid "A new version of this plugin is available."
|
33 |
-
msgstr "Új verzió érhető el a kiegészítőhöz"
|
34 |
-
|
35 |
-
#: plugin-update-checker.php:900
|
36 |
-
#, php-format
|
37 |
-
msgid "Unknown update checker status \"%s\""
|
38 |
-
msgstr "Ismeretlen a frissítés ellenőrző státusza \"%s\""
|
39 |
-
|
40 |
-
#~ msgid "Every %d hours"
|
41 |
-
#~ msgstr "Minden %d órában"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/plugin-update-checker/languages/plugin-update-checker.pot
DELETED
@@ -1,39 +0,0 @@
|
|
1 |
-
#, fuzzy
|
2 |
-
msgid ""
|
3 |
-
msgstr ""
|
4 |
-
"Project-Id-Version: plugin-update-checker\n"
|
5 |
-
"POT-Creation-Date: 2016-01-11 21:22+0100\n"
|
6 |
-
"PO-Revision-Date: 2016-01-10 20:59+0100\n"
|
7 |
-
"Last-Translator: Tamás András Horváth <htomy92@gmail.com>\n"
|
8 |
-
"Language-Team: \n"
|
9 |
-
"Language: en_US\n"
|
10 |
-
"MIME-Version: 1.0\n"
|
11 |
-
"Content-Type: text/plain; charset=UTF-8\n"
|
12 |
-
"Content-Transfer-Encoding: 8bit\n"
|
13 |
-
"X-Generator: Poedit 1.8.6\n"
|
14 |
-
"X-Poedit-Basepath: ..\n"
|
15 |
-
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
16 |
-
"X-Poedit-SourceCharset: UTF-8\n"
|
17 |
-
"X-Poedit-KeywordsList: __;_e\n"
|
18 |
-
"X-Poedit-SearchPath-0: .\n"
|
19 |
-
|
20 |
-
#: github-checker.php:137
|
21 |
-
msgid "There is no changelog available."
|
22 |
-
msgstr ""
|
23 |
-
|
24 |
-
#: plugin-update-checker.php:852
|
25 |
-
msgid "Check for updates"
|
26 |
-
msgstr ""
|
27 |
-
|
28 |
-
#: plugin-update-checker.php:896
|
29 |
-
msgid "This plugin is up to date."
|
30 |
-
msgstr ""
|
31 |
-
|
32 |
-
#: plugin-update-checker.php:898
|
33 |
-
msgid "A new version of this plugin is available."
|
34 |
-
msgstr ""
|
35 |
-
|
36 |
-
#: plugin-update-checker.php:900
|
37 |
-
#, php-format
|
38 |
-
msgid "Unknown update checker status \"%s\""
|
39 |
-
msgstr ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/plugin-update-checker/license.txt
DELETED
@@ -1,7 +0,0 @@
|
|
1 |
-
Copyright (c) 2014 Jānis Elsts
|
2 |
-
|
3 |
-
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:
|
4 |
-
|
5 |
-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6 |
-
|
7 |
-
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.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/plugin-update-checker/plugin-update-checker.php
DELETED
@@ -1,1641 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* Plugin Update Checker Library 3.1
|
4 |
-
* http://w-shadow.com/
|
5 |
-
*
|
6 |
-
* Copyright 2016 Janis Elsts
|
7 |
-
* Released under the MIT license. See license.txt for details.
|
8 |
-
*/
|
9 |
-
|
10 |
-
if ( !class_exists('PluginUpdateChecker_3_1', false) ):
|
11 |
-
|
12 |
-
/**
|
13 |
-
* A custom plugin update checker.
|
14 |
-
*
|
15 |
-
* @author Janis Elsts
|
16 |
-
* @copyright 2016
|
17 |
-
* @version 3.0
|
18 |
-
* @access public
|
19 |
-
*/
|
20 |
-
class PluginUpdateChecker_3_1 {
|
21 |
-
public $metadataUrl = ''; //The URL of the plugin's metadata file.
|
22 |
-
public $pluginAbsolutePath = ''; //Full path of the main plugin file.
|
23 |
-
public $pluginFile = ''; //Plugin filename relative to the plugins directory. Many WP APIs use this to identify plugins.
|
24 |
-
public $slug = ''; //Plugin slug.
|
25 |
-
public $optionName = ''; //Where to store the update info.
|
26 |
-
public $muPluginFile = ''; //For MU plugins, the plugin filename relative to the mu-plugins directory.
|
27 |
-
|
28 |
-
public $debugMode = false; //Set to TRUE to enable error reporting. Errors are raised using trigger_error()
|
29 |
-
//and should be logged to the standard PHP error log.
|
30 |
-
public $scheduler;
|
31 |
-
|
32 |
-
protected $upgraderStatus;
|
33 |
-
|
34 |
-
private $debugBarPlugin = null;
|
35 |
-
private $cachedInstalledVersion = null;
|
36 |
-
|
37 |
-
private $metadataHost = ''; //The host component of $metadataUrl.
|
38 |
-
|
39 |
-
/**
|
40 |
-
* Class constructor.
|
41 |
-
*
|
42 |
-
* @param string $metadataUrl The URL of the plugin's metadata file.
|
43 |
-
* @param string $pluginFile Fully qualified path to the main plugin file.
|
44 |
-
* @param string $slug The plugin's 'slug'. If not specified, the filename part of $pluginFile sans '.php' will be used as the slug.
|
45 |
-
* @param integer $checkPeriod How often to check for updates (in hours). Defaults to checking every 12 hours. Set to 0 to disable automatic update checks.
|
46 |
-
* @param string $optionName Where to store book-keeping info about update checks. Defaults to 'external_updates-$slug'.
|
47 |
-
* @param string $muPluginFile Optional. The plugin filename relative to the mu-plugins directory.
|
48 |
-
*/
|
49 |
-
public function __construct($metadataUrl, $pluginFile, $slug = '', $checkPeriod = 12, $optionName = '', $muPluginFile = ''){
|
50 |
-
$this->metadataUrl = $metadataUrl;
|
51 |
-
$this->pluginAbsolutePath = $pluginFile;
|
52 |
-
$this->pluginFile = plugin_basename($this->pluginAbsolutePath);
|
53 |
-
$this->muPluginFile = $muPluginFile;
|
54 |
-
$this->slug = $slug;
|
55 |
-
$this->optionName = $optionName;
|
56 |
-
$this->debugMode = (bool)(constant('WP_DEBUG'));
|
57 |
-
|
58 |
-
//If no slug is specified, use the name of the main plugin file as the slug.
|
59 |
-
//For example, 'my-cool-plugin/cool-plugin.php' becomes 'cool-plugin'.
|
60 |
-
if ( empty($this->slug) ){
|
61 |
-
$this->slug = basename($this->pluginFile, '.php');
|
62 |
-
}
|
63 |
-
|
64 |
-
if ( empty($this->optionName) ){
|
65 |
-
$this->optionName = 'external_updates-' . $this->slug;
|
66 |
-
}
|
67 |
-
|
68 |
-
//Backwards compatibility: If the plugin is a mu-plugin but no $muPluginFile is specified, assume
|
69 |
-
//it's the same as $pluginFile given that it's not in a subdirectory (WP only looks in the base dir).
|
70 |
-
if ( (strpbrk($this->pluginFile, '/\\') === false) && $this->isUnknownMuPlugin() ) {
|
71 |
-
$this->muPluginFile = $this->pluginFile;
|
72 |
-
}
|
73 |
-
|
74 |
-
$this->scheduler = $this->createScheduler($checkPeriod);
|
75 |
-
$this->upgraderStatus = new PucUpgraderStatus_3_1();
|
76 |
-
|
77 |
-
$this->installHooks();
|
78 |
-
}
|
79 |
-
|
80 |
-
/**
|
81 |
-
* Create an instance of the scheduler.
|
82 |
-
*
|
83 |
-
* This is implemented as a method to make it possible for plugins to subclass the update checker
|
84 |
-
* and substitute their own scheduler.
|
85 |
-
*
|
86 |
-
* @param int $checkPeriod
|
87 |
-
* @return PucScheduler_3_1
|
88 |
-
*/
|
89 |
-
protected function createScheduler($checkPeriod) {
|
90 |
-
return new PucScheduler_3_1($this, $checkPeriod);
|
91 |
-
}
|
92 |
-
|
93 |
-
/**
|
94 |
-
* Install the hooks required to run periodic update checks and inject update info
|
95 |
-
* into WP data structures.
|
96 |
-
*
|
97 |
-
* @return void
|
98 |
-
*/
|
99 |
-
protected function installHooks(){
|
100 |
-
//Override requests for plugin information
|
101 |
-
add_filter('plugins_api', array($this, 'injectInfo'), 20, 3);
|
102 |
-
|
103 |
-
//Insert our update info into the update array maintained by WP.
|
104 |
-
add_filter('site_transient_update_plugins', array($this,'injectUpdate')); //WP 3.0+
|
105 |
-
add_filter('transient_update_plugins', array($this,'injectUpdate')); //WP 2.8+
|
106 |
-
add_filter('site_transient_update_plugins', array($this, 'injectTranslationUpdates'));
|
107 |
-
|
108 |
-
add_filter('plugin_row_meta', array($this, 'addCheckForUpdatesLink'), 10, 2);
|
109 |
-
add_action('admin_init', array($this, 'handleManualCheck'));
|
110 |
-
add_action('all_admin_notices', array($this, 'displayManualCheckResult'));
|
111 |
-
|
112 |
-
//Clear the version number cache when something - anything - is upgraded or WP clears the update cache.
|
113 |
-
add_filter('upgrader_post_install', array($this, 'clearCachedVersion'));
|
114 |
-
add_action('delete_site_transient_update_plugins', array($this, 'clearCachedVersion'));
|
115 |
-
//Clear translation updates when WP clears the update cache.
|
116 |
-
//This needs to be done directly because the library doesn't actually remove obsolete plugin updates,
|
117 |
-
//it just hides them (see getUpdate()). We can't do that with translations - too much disk I/O.
|
118 |
-
add_action('delete_site_transient_update_plugins', array($this, 'clearCachedTranslationUpdates'));
|
119 |
-
|
120 |
-
if ( did_action('plugins_loaded') ) {
|
121 |
-
$this->initDebugBarPanel();
|
122 |
-
} else {
|
123 |
-
add_action('plugins_loaded', array($this, 'initDebugBarPanel'));
|
124 |
-
}
|
125 |
-
|
126 |
-
//Rename the update directory to be the same as the existing directory.
|
127 |
-
add_filter('upgrader_source_selection', array($this, 'fixDirectoryName'), 10, 3);
|
128 |
-
|
129 |
-
//Enable language support (i18n).
|
130 |
-
load_plugin_textdomain('plugin-update-checker', false, plugin_basename(dirname(__FILE__)) . '/languages');
|
131 |
-
|
132 |
-
//Allow HTTP requests to the metadata URL even if it's on a local host.
|
133 |
-
$this->metadataHost = @parse_url($this->metadataUrl, PHP_URL_HOST);
|
134 |
-
add_filter('http_request_host_is_external', array($this, 'allowMetadataHost'), 10, 2);
|
135 |
-
}
|
136 |
-
|
137 |
-
/**
|
138 |
-
* Explicitly allow HTTP requests to the metadata URL.
|
139 |
-
*
|
140 |
-
* WordPress has a security feature where the HTTP API will reject all requests that are sent to
|
141 |
-
* another site hosted on the same server as the current site (IP match), a local host, or a local
|
142 |
-
* IP, unless the host exactly matches the current site.
|
143 |
-
*
|
144 |
-
* This feature is opt-in (at least in WP 4.4). Apparently some people enable it.
|
145 |
-
*
|
146 |
-
* That can be a problem when you're developing your plugin and you decide to host the update information
|
147 |
-
* on the same server as your test site. Update requests will mysteriously fail.
|
148 |
-
*
|
149 |
-
* We fix that by adding an exception for the metadata host.
|
150 |
-
*
|
151 |
-
* @param bool $allow
|
152 |
-
* @param string $host
|
153 |
-
* @return bool
|
154 |
-
*/
|
155 |
-
public function allowMetadataHost($allow, $host) {
|
156 |
-
if ( strtolower($host) === strtolower($this->metadataHost) ) {
|
157 |
-
return true;
|
158 |
-
}
|
159 |
-
return $allow;
|
160 |
-
}
|
161 |
-
|
162 |
-
/**
|
163 |
-
* Retrieve plugin info from the configured API endpoint.
|
164 |
-
*
|
165 |
-
* @uses wp_remote_get()
|
166 |
-
*
|
167 |
-
* @param array $queryArgs Additional query arguments to append to the request. Optional.
|
168 |
-
* @return PluginInfo_3_1
|
169 |
-
*/
|
170 |
-
public function requestInfo($queryArgs = array()){
|
171 |
-
//Query args to append to the URL. Plugins can add their own by using a filter callback (see addQueryArgFilter()).
|
172 |
-
$installedVersion = $this->getInstalledVersion();
|
173 |
-
$queryArgs['installed_version'] = ($installedVersion !== null) ? $installedVersion : '';
|
174 |
-
$queryArgs = apply_filters('puc_request_info_query_args-'.$this->slug, $queryArgs);
|
175 |
-
|
176 |
-
//Various options for the wp_remote_get() call. Plugins can filter these, too.
|
177 |
-
$options = array(
|
178 |
-
'timeout' => 10, //seconds
|
179 |
-
'headers' => array(
|
180 |
-
'Accept' => 'application/json'
|
181 |
-
),
|
182 |
-
);
|
183 |
-
$options = apply_filters('puc_request_info_options-'.$this->slug, $options);
|
184 |
-
|
185 |
-
//The plugin info should be at 'http://your-api.com/url/here/$slug/info.json'
|
186 |
-
$url = $this->metadataUrl;
|
187 |
-
if ( !empty($queryArgs) ){
|
188 |
-
$url = add_query_arg($queryArgs, $url);
|
189 |
-
}
|
190 |
-
|
191 |
-
$result = wp_remote_get(
|
192 |
-
$url,
|
193 |
-
$options
|
194 |
-
);
|
195 |
-
|
196 |
-
//Try to parse the response
|
197 |
-
$status = $this->validateApiResponse($result);
|
198 |
-
$pluginInfo = null;
|
199 |
-
if ( !is_wp_error($status) ){
|
200 |
-
$pluginInfo = PluginInfo_3_1::fromJson($result['body']);
|
201 |
-
if ( $pluginInfo !== null ) {
|
202 |
-
$pluginInfo->filename = $this->pluginFile;
|
203 |
-
$pluginInfo->slug = $this->slug;
|
204 |
-
}
|
205 |
-
} else {
|
206 |
-
$this->triggerError(
|
207 |
-
sprintf('The URL %s does not point to a valid plugin metadata file. ', $url)
|
208 |
-
. $status->get_error_message(),
|
209 |
-
E_USER_WARNING
|
210 |
-
);
|
211 |
-
}
|
212 |
-
|
213 |
-
$pluginInfo = apply_filters('puc_request_info_result-'.$this->slug, $pluginInfo, $result);
|
214 |
-
return $pluginInfo;
|
215 |
-
}
|
216 |
-
|
217 |
-
/**
|
218 |
-
* Check if $result is a successful update API response.
|
219 |
-
*
|
220 |
-
* @param array|WP_Error $result
|
221 |
-
* @return true|WP_Error
|
222 |
-
*/
|
223 |
-
private function validateApiResponse($result) {
|
224 |
-
if ( is_wp_error($result) ) { /** @var WP_Error $result */
|
225 |
-
return new WP_Error($result->get_error_code(), 'WP HTTP Error: ' . $result->get_error_message());
|
226 |
-
}
|
227 |
-
|
228 |
-
if ( !isset($result['response']['code']) ) {
|
229 |
-
return new WP_Error('puc_no_response_code', 'wp_remote_get() returned an unexpected result.');
|
230 |
-
}
|
231 |
-
|
232 |
-
if ( $result['response']['code'] !== 200 ) {
|
233 |
-
return new WP_Error(
|
234 |
-
'puc_unexpected_response_code',
|
235 |
-
'HTTP response code is ' . $result['response']['code'] . ' (expected: 200)'
|
236 |
-
);
|
237 |
-
}
|
238 |
-
|
239 |
-
if ( empty($result['body']) ) {
|
240 |
-
return new WP_Error('puc_empty_response', 'The metadata file appears to be empty.');
|
241 |
-
}
|
242 |
-
|
243 |
-
return true;
|
244 |
-
}
|
245 |
-
|
246 |
-
/**
|
247 |
-
* Retrieve the latest update (if any) from the configured API endpoint.
|
248 |
-
*
|
249 |
-
* @uses PluginUpdateChecker::requestInfo()
|
250 |
-
*
|
251 |
-
* @return PluginUpdate_3_1 An instance of PluginUpdate, or NULL when no updates are available.
|
252 |
-
*/
|
253 |
-
public function requestUpdate(){
|
254 |
-
//For the sake of simplicity, this function just calls requestInfo()
|
255 |
-
//and transforms the result accordingly.
|
256 |
-
$pluginInfo = $this->requestInfo(array('checking_for_updates' => '1'));
|
257 |
-
if ( $pluginInfo == null ){
|
258 |
-
return null;
|
259 |
-
}
|
260 |
-
$update = PluginUpdate_3_1::fromPluginInfo($pluginInfo);
|
261 |
-
|
262 |
-
//Keep only those translation updates that apply to this site.
|
263 |
-
$update->translations = $this->filterApplicableTranslations($update->translations);
|
264 |
-
|
265 |
-
return $update;
|
266 |
-
}
|
267 |
-
|
268 |
-
/**
|
269 |
-
* Filter a list of translation updates and return a new list that contains only updates
|
270 |
-
* that apply to the current site.
|
271 |
-
*
|
272 |
-
* @param array $translations
|
273 |
-
* @return array
|
274 |
-
*/
|
275 |
-
private function filterApplicableTranslations($translations) {
|
276 |
-
$languages = array_flip(array_values(get_available_languages()));
|
277 |
-
$installedTranslations = wp_get_installed_translations('plugins');
|
278 |
-
if ( isset($installedTranslations[$this->slug]) ) {
|
279 |
-
$installedTranslations = $installedTranslations[$this->slug];
|
280 |
-
} else {
|
281 |
-
$installedTranslations = array();
|
282 |
-
}
|
283 |
-
|
284 |
-
$applicableTranslations = array();
|
285 |
-
foreach($translations as $translation) {
|
286 |
-
//Does it match one of the available core languages?
|
287 |
-
$isApplicable = array_key_exists($translation->language, $languages);
|
288 |
-
//Is it more recent than an already-installed translation?
|
289 |
-
if ( isset($installedTranslations[$translation->language]) ) {
|
290 |
-
$updateTimestamp = strtotime($translation->updated);
|
291 |
-
$installedTimestamp = strtotime($installedTranslations[$translation->language]['PO-Revision-Date']);
|
292 |
-
$isApplicable = $updateTimestamp > $installedTimestamp;
|
293 |
-
}
|
294 |
-
|
295 |
-
if ( $isApplicable ) {
|
296 |
-
$applicableTranslations[] = $translation;
|
297 |
-
}
|
298 |
-
}
|
299 |
-
|
300 |
-
return $applicableTranslations;
|
301 |
-
}
|
302 |
-
|
303 |
-
/**
|
304 |
-
* Get the currently installed version of the plugin.
|
305 |
-
*
|
306 |
-
* @return string Version number.
|
307 |
-
*/
|
308 |
-
public function getInstalledVersion(){
|
309 |
-
if ( isset($this->cachedInstalledVersion) ) {
|
310 |
-
return $this->cachedInstalledVersion;
|
311 |
-
}
|
312 |
-
|
313 |
-
$pluginHeader = $this->getPluginHeader();
|
314 |
-
if ( isset($pluginHeader['Version']) ) {
|
315 |
-
$this->cachedInstalledVersion = $pluginHeader['Version'];
|
316 |
-
return $pluginHeader['Version'];
|
317 |
-
} else {
|
318 |
-
//This can happen if the filename points to something that is not a plugin.
|
319 |
-
$this->triggerError(
|
320 |
-
sprintf(
|
321 |
-
"Can't to read the Version header for '%s'. The filename is incorrect or is not a plugin.",
|
322 |
-
$this->pluginFile
|
323 |
-
),
|
324 |
-
E_USER_WARNING
|
325 |
-
);
|
326 |
-
return null;
|
327 |
-
}
|
328 |
-
}
|
329 |
-
|
330 |
-
/**
|
331 |
-
* Get plugin's metadata from its file header.
|
332 |
-
*
|
333 |
-
* @return array
|
334 |
-
*/
|
335 |
-
protected function getPluginHeader() {
|
336 |
-
if ( !is_file($this->pluginAbsolutePath) ) {
|
337 |
-
//This can happen if the plugin filename is wrong.
|
338 |
-
$this->triggerError(
|
339 |
-
sprintf(
|
340 |
-
"Can't to read the plugin header for '%s'. The file does not exist.",
|
341 |
-
$this->pluginFile
|
342 |
-
),
|
343 |
-
E_USER_WARNING
|
344 |
-
);
|
345 |
-
return array();
|
346 |
-
}
|
347 |
-
|
348 |
-
if ( !function_exists('get_plugin_data') ){
|
349 |
-
/** @noinspection PhpIncludeInspection */
|
350 |
-
require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
|
351 |
-
}
|
352 |
-
return get_plugin_data($this->pluginAbsolutePath, false, false);
|
353 |
-
}
|
354 |
-
|
355 |
-
/**
|
356 |
-
* Check for plugin updates.
|
357 |
-
* The results are stored in the DB option specified in $optionName.
|
358 |
-
*
|
359 |
-
* @return PluginUpdate_3_1|null
|
360 |
-
*/
|
361 |
-
public function checkForUpdates(){
|
362 |
-
$installedVersion = $this->getInstalledVersion();
|
363 |
-
//Fail silently if we can't find the plugin or read its header.
|
364 |
-
if ( $installedVersion === null ) {
|
365 |
-
$this->triggerError(
|
366 |
-
sprintf('Skipping update check for %s - installed version unknown.', $this->pluginFile),
|
367 |
-
E_USER_WARNING
|
368 |
-
);
|
369 |
-
return null;
|
370 |
-
}
|
371 |
-
|
372 |
-
$state = $this->getUpdateState();
|
373 |
-
if ( empty($state) ){
|
374 |
-
$state = new stdClass;
|
375 |
-
$state->lastCheck = 0;
|
376 |
-
$state->checkedVersion = '';
|
377 |
-
$state->update = null;
|
378 |
-
}
|
379 |
-
|
380 |
-
$state->lastCheck = time();
|
381 |
-
$state->checkedVersion = $installedVersion;
|
382 |
-
$this->setUpdateState($state); //Save before checking in case something goes wrong
|
383 |
-
|
384 |
-
$state->update = $this->requestUpdate();
|
385 |
-
$this->setUpdateState($state);
|
386 |
-
|
387 |
-
return $this->getUpdate();
|
388 |
-
}
|
389 |
-
|
390 |
-
/**
|
391 |
-
* Load the update checker state from the DB.
|
392 |
-
*
|
393 |
-
* @return stdClass|null
|
394 |
-
*/
|
395 |
-
public function getUpdateState() {
|
396 |
-
$state = get_site_option($this->optionName, null);
|
397 |
-
if ( empty($state) || !is_object($state)) {
|
398 |
-
$state = null;
|
399 |
-
}
|
400 |
-
|
401 |
-
if ( isset($state, $state->update) && is_object($state->update) ) {
|
402 |
-
$state->update = PluginUpdate_3_1::fromObject($state->update);
|
403 |
-
}
|
404 |
-
return $state;
|
405 |
-
}
|
406 |
-
|
407 |
-
|
408 |
-
/**
|
409 |
-
* Persist the update checker state to the DB.
|
410 |
-
*
|
411 |
-
* @param StdClass $state
|
412 |
-
* @return void
|
413 |
-
*/
|
414 |
-
private function setUpdateState($state) {
|
415 |
-
if ( isset($state->update) && is_object($state->update) && method_exists($state->update, 'toStdClass') ) {
|
416 |
-
$update = $state->update; /** @var PluginUpdate_3_1 $update */
|
417 |
-
$state->update = $update->toStdClass();
|
418 |
-
}
|
419 |
-
update_site_option($this->optionName, $state);
|
420 |
-
}
|
421 |
-
|
422 |
-
/**
|
423 |
-
* Reset update checker state - i.e. last check time, cached update data and so on.
|
424 |
-
*
|
425 |
-
* Call this when your plugin is being uninstalled, or if you want to
|
426 |
-
* clear the update cache.
|
427 |
-
*/
|
428 |
-
public function resetUpdateState() {
|
429 |
-
delete_site_option($this->optionName);
|
430 |
-
}
|
431 |
-
|
432 |
-
/**
|
433 |
-
* Intercept plugins_api() calls that request information about our plugin and
|
434 |
-
* use the configured API endpoint to satisfy them.
|
435 |
-
*
|
436 |
-
* @see plugins_api()
|
437 |
-
*
|
438 |
-
* @param mixed $result
|
439 |
-
* @param string $action
|
440 |
-
* @param array|object $args
|
441 |
-
* @return mixed
|
442 |
-
*/
|
443 |
-
public function injectInfo($result, $action = null, $args = null){
|
444 |
-
$relevant = ($action == 'plugin_information') && isset($args->slug) && (
|
445 |
-
($args->slug == $this->slug) || ($args->slug == dirname($this->pluginFile))
|
446 |
-
);
|
447 |
-
if ( !$relevant ) {
|
448 |
-
return $result;
|
449 |
-
}
|
450 |
-
|
451 |
-
$pluginInfo = $this->requestInfo();
|
452 |
-
$pluginInfo = apply_filters('puc_pre_inject_info-' . $this->slug, $pluginInfo);
|
453 |
-
if ( $pluginInfo ) {
|
454 |
-
return $pluginInfo->toWpFormat();
|
455 |
-
}
|
456 |
-
|
457 |
-
return $result;
|
458 |
-
}
|
459 |
-
|
460 |
-
/**
|
461 |
-
* Insert the latest update (if any) into the update list maintained by WP.
|
462 |
-
*
|
463 |
-
* @param StdClass $updates Update list.
|
464 |
-
* @return StdClass Modified update list.
|
465 |
-
*/
|
466 |
-
public function injectUpdate($updates){
|
467 |
-
//Is there an update to insert?
|
468 |
-
$update = $this->getUpdate();
|
469 |
-
|
470 |
-
//No update notifications for mu-plugins unless explicitly enabled. The MU plugin file
|
471 |
-
//is usually different from the main plugin file so the update wouldn't show up properly anyway.
|
472 |
-
if ( $this->isUnknownMuPlugin() ) {
|
473 |
-
$update = null;
|
474 |
-
}
|
475 |
-
|
476 |
-
if ( !empty($update) ) {
|
477 |
-
//Let plugins filter the update info before it's passed on to WordPress.
|
478 |
-
$update = apply_filters('puc_pre_inject_update-' . $this->slug, $update);
|
479 |
-
$updates = $this->addUpdateToList($updates, $update);
|
480 |
-
} else {
|
481 |
-
//Clean up any stale update info.
|
482 |
-
$updates = $this->removeUpdateFromList($updates);
|
483 |
-
}
|
484 |
-
|
485 |
-
return $updates;
|
486 |
-
}
|
487 |
-
|
488 |
-
/**
|
489 |
-
* @param StdClass|null $updates
|
490 |
-
* @param PluginUpdate_3_1 $updateToAdd
|
491 |
-
* @return StdClass
|
492 |
-
*/
|
493 |
-
private function addUpdateToList($updates, $updateToAdd) {
|
494 |
-
if ( !is_object($updates) ) {
|
495 |
-
$updates = new stdClass();
|
496 |
-
$updates->response = array();
|
497 |
-
}
|
498 |
-
|
499 |
-
$wpUpdate = $updateToAdd->toWpFormat();
|
500 |
-
$pluginFile = $this->pluginFile;
|
501 |
-
|
502 |
-
if ( $this->isMuPlugin() ) {
|
503 |
-
//WP does not support automatic update installation for mu-plugins, but we can still display a notice.
|
504 |
-
$wpUpdate->package = null;
|
505 |
-
$pluginFile = $this->muPluginFile;
|
506 |
-
}
|
507 |
-
$updates->response[$pluginFile] = $wpUpdate;
|
508 |
-
return $updates;
|
509 |
-
}
|
510 |
-
|
511 |
-
/**
|
512 |
-
* @param stdClass|null $updates
|
513 |
-
* @return stdClass|null
|
514 |
-
*/
|
515 |
-
private function removeUpdateFromList($updates) {
|
516 |
-
if ( isset($updates, $updates->response) ) {
|
517 |
-
unset($updates->response[$this->pluginFile]);
|
518 |
-
if ( !empty($this->muPluginFile) ) {
|
519 |
-
unset($updates->response[$this->muPluginFile]);
|
520 |
-
}
|
521 |
-
}
|
522 |
-
return $updates;
|
523 |
-
}
|
524 |
-
|
525 |
-
/**
|
526 |
-
* Insert translation updates into the list maintained by WordPress.
|
527 |
-
*
|
528 |
-
* @param stdClass $updates
|
529 |
-
* @return stdClass
|
530 |
-
*/
|
531 |
-
public function injectTranslationUpdates($updates) {
|
532 |
-
$translationUpdates = $this->getTranslationUpdates();
|
533 |
-
if ( empty($translationUpdates) ) {
|
534 |
-
return $updates;
|
535 |
-
}
|
536 |
-
|
537 |
-
//Being defensive.
|
538 |
-
if ( !is_object($updates) ) {
|
539 |
-
$updates = new stdClass();
|
540 |
-
}
|
541 |
-
if ( !isset($updates->translations) ) {
|
542 |
-
$updates->translations = array();
|
543 |
-
}
|
544 |
-
|
545 |
-
//In case there's a name collision with a plugin hosted on wordpress.org,
|
546 |
-
//remove any preexisting updates that match our plugin.
|
547 |
-
$translationType = 'plugin';
|
548 |
-
$filteredTranslations = array();
|
549 |
-
foreach($updates->translations as $translation) {
|
550 |
-
if ( ($translation['type'] === $translationType) && ($translation['slug'] === $this->slug) ) {
|
551 |
-
continue;
|
552 |
-
}
|
553 |
-
$filteredTranslations[] = $translation;
|
554 |
-
}
|
555 |
-
$updates->translations = $filteredTranslations;
|
556 |
-
|
557 |
-
//Add our updates to the list.
|
558 |
-
foreach($translationUpdates as $update) {
|
559 |
-
$convertedUpdate = array_merge(
|
560 |
-
array(
|
561 |
-
'type' => $translationType,
|
562 |
-
'slug' => $this->slug,
|
563 |
-
'autoupdate' => 0,
|
564 |
-
//AFAICT, WordPress doesn't actually use the "version" field for anything.
|
565 |
-
//But lets make sure it's there, just in case.
|
566 |
-
'version' => isset($update->version) ? $update->version : ('1.' . strtotime($update->updated)),
|
567 |
-
),
|
568 |
-
(array)$update
|
569 |
-
);
|
570 |
-
|
571 |
-
$updates->translations[] = $convertedUpdate;
|
572 |
-
}
|
573 |
-
|
574 |
-
return $updates;
|
575 |
-
}
|
576 |
-
|
577 |
-
/**
|
578 |
-
* Rename the update directory to match the existing plugin directory.
|
579 |
-
*
|
580 |
-
* When WordPress installs a plugin or theme update, it assumes that the ZIP file will contain
|
581 |
-
* exactly one directory, and that the directory name will be the same as the directory where
|
582 |
-
* the plugin/theme is currently installed.
|
583 |
-
*
|
584 |
-
* GitHub and other repositories provide ZIP downloads, but they often use directory names like
|
585 |
-
* "project-branch" or "project-tag-hash". We need to change the name to the actual plugin folder.
|
586 |
-
*
|
587 |
-
* This is a hook callback. Don't call it from a plugin.
|
588 |
-
*
|
589 |
-
* @param string $source The directory to copy to /wp-content/plugins. Usually a subdirectory of $remoteSource.
|
590 |
-
* @param string $remoteSource WordPress has extracted the update to this directory.
|
591 |
-
* @param WP_Upgrader $upgrader
|
592 |
-
* @return string|WP_Error
|
593 |
-
*/
|
594 |
-
public function fixDirectoryName($source, $remoteSource, $upgrader) {
|
595 |
-
global $wp_filesystem; /** @var WP_Filesystem_Base $wp_filesystem */
|
596 |
-
|
597 |
-
//Basic sanity checks.
|
598 |
-
if ( !isset($source, $remoteSource, $upgrader, $upgrader->skin, $wp_filesystem) ) {
|
599 |
-
return $source;
|
600 |
-
}
|
601 |
-
|
602 |
-
//If WordPress is upgrading anything other than our plugin, leave the directory name unchanged.
|
603 |
-
if ( !$this->isPluginBeingUpgraded($upgrader) ) {
|
604 |
-
return $source;
|
605 |
-
}
|
606 |
-
|
607 |
-
//Rename the source to match the existing plugin directory.
|
608 |
-
$pluginDirectoryName = dirname($this->pluginFile);
|
609 |
-
if ( $pluginDirectoryName === '.' ) {
|
610 |
-
return $source;
|
611 |
-
}
|
612 |
-
$correctedSource = trailingslashit($remoteSource) . $pluginDirectoryName . '/';
|
613 |
-
if ( $source !== $correctedSource ) {
|
614 |
-
//The update archive should contain a single directory that contains the rest of plugin files. Otherwise,
|
615 |
-
//WordPress will try to copy the entire working directory ($source == $remoteSource). We can't rename
|
616 |
-
//$remoteSource because that would break WordPress code that cleans up temporary files after update.
|
617 |
-
if ( $this->isBadDirectoryStructure($remoteSource) ) {
|
618 |
-
return new WP_Error(
|
619 |
-
'puc-incorrect-directory-structure',
|
620 |
-
sprintf(
|
621 |
-
'The directory structure of the update is incorrect. All plugin files should be inside ' .
|
622 |
-
'a directory named <span class="code">%s</span>, not at the root of the ZIP file.',
|
623 |
-
htmlentities($this->slug)
|
624 |
-
)
|
625 |
-
);
|
626 |
-
}
|
627 |
-
|
628 |
-
/** @var WP_Upgrader_Skin $upgrader->skin */
|
629 |
-
$upgrader->skin->feedback(sprintf(
|
630 |
-
'Renaming %s to %s…',
|
631 |
-
'<span class="code">' . basename($source) . '</span>',
|
632 |
-
'<span class="code">' . $pluginDirectoryName . '</span>'
|
633 |
-
));
|
634 |
-
|
635 |
-
if ( $wp_filesystem->move($source, $correctedSource, true) ) {
|
636 |
-
$upgrader->skin->feedback('Plugin directory successfully renamed.');
|
637 |
-
return $correctedSource;
|
638 |
-
} else {
|
639 |
-
return new WP_Error(
|
640 |
-
'puc-rename-failed',
|
641 |
-
'Unable to rename the update to match the existing plugin directory.'
|
642 |
-
);
|
643 |
-
}
|
644 |
-
}
|
645 |
-
|
646 |
-
return $source;
|
647 |
-
}
|
648 |
-
|
649 |
-
/**
|
650 |
-
* Check for incorrect update directory structure. An update must contain a single directory,
|
651 |
-
* all other files should be inside that directory.
|
652 |
-
*
|
653 |
-
* @param string $remoteSource Directory path.
|
654 |
-
* @return bool
|
655 |
-
*/
|
656 |
-
private function isBadDirectoryStructure($remoteSource) {
|
657 |
-
global $wp_filesystem; /** @var WP_Filesystem_Base $wp_filesystem */
|
658 |
-
|
659 |
-
$sourceFiles = $wp_filesystem->dirlist($remoteSource);
|
660 |
-
if ( is_array($sourceFiles) ) {
|
661 |
-
$sourceFiles = array_keys($sourceFiles);
|
662 |
-
$firstFilePath = trailingslashit($remoteSource) . $sourceFiles[0];
|
663 |
-
return (count($sourceFiles) > 1) || (!$wp_filesystem->is_dir($firstFilePath));
|
664 |
-
}
|
665 |
-
|
666 |
-
//Assume it's fine.
|
667 |
-
return false;
|
668 |
-
}
|
669 |
-
|
670 |
-
/**
|
671 |
-
* Is there and update being installed RIGHT NOW, for this specific plugin?
|
672 |
-
*
|
673 |
-
* @param WP_Upgrader|null $upgrader The upgrader that's performing the current update.
|
674 |
-
* @return bool
|
675 |
-
*/
|
676 |
-
public function isPluginBeingUpgraded($upgrader = null) {
|
677 |
-
return $this->upgraderStatus->isPluginBeingUpgraded($this->pluginFile, $upgrader);
|
678 |
-
}
|
679 |
-
|
680 |
-
/**
|
681 |
-
* Get the details of the currently available update, if any.
|
682 |
-
*
|
683 |
-
* If no updates are available, or if the last known update version is below or equal
|
684 |
-
* to the currently installed version, this method will return NULL.
|
685 |
-
*
|
686 |
-
* Uses cached update data. To retrieve update information straight from
|
687 |
-
* the metadata URL, call requestUpdate() instead.
|
688 |
-
*
|
689 |
-
* @return PluginUpdate_3_1|null
|
690 |
-
*/
|
691 |
-
public function getUpdate() {
|
692 |
-
$state = $this->getUpdateState(); /** @var StdClass $state */
|
693 |
-
|
694 |
-
//Is there an update available?
|
695 |
-
if ( isset($state, $state->update) ) {
|
696 |
-
$update = $state->update;
|
697 |
-
//Check if the update is actually newer than the currently installed version.
|
698 |
-
$installedVersion = $this->getInstalledVersion();
|
699 |
-
if ( ($installedVersion !== null) && version_compare($update->version, $installedVersion, '>') ){
|
700 |
-
$update->filename = $this->pluginFile;
|
701 |
-
return $update;
|
702 |
-
}
|
703 |
-
}
|
704 |
-
return null;
|
705 |
-
}
|
706 |
-
|
707 |
-
/**
|
708 |
-
* Get a list of available translation updates.
|
709 |
-
*
|
710 |
-
* This method will return an empty array if there are no updates.
|
711 |
-
* Uses cached update data.
|
712 |
-
*
|
713 |
-
* @return array
|
714 |
-
*/
|
715 |
-
public function getTranslationUpdates() {
|
716 |
-
$state = $this->getUpdateState();
|
717 |
-
if ( isset($state, $state->update, $state->update->translations) ) {
|
718 |
-
return $state->update->translations;
|
719 |
-
}
|
720 |
-
return array();
|
721 |
-
}
|
722 |
-
|
723 |
-
/**
|
724 |
-
* Remove all cached translation updates.
|
725 |
-
*
|
726 |
-
* @see wp_clean_update_cache
|
727 |
-
*/
|
728 |
-
public function clearCachedTranslationUpdates() {
|
729 |
-
$state = $this->getUpdateState();
|
730 |
-
if ( isset($state, $state->update, $state->update->translations) ) {
|
731 |
-
$state->update->translations = array();
|
732 |
-
$this->setUpdateState($state);
|
733 |
-
}
|
734 |
-
}
|
735 |
-
|
736 |
-
/**
|
737 |
-
* Add a "Check for updates" link to the plugin row in the "Plugins" page. By default,
|
738 |
-
* the new link will appear after the "Visit plugin site" link.
|
739 |
-
*
|
740 |
-
* You can change the link text by using the "puc_manual_check_link-$slug" filter.
|
741 |
-
* Returning an empty string from the filter will disable the link.
|
742 |
-
*
|
743 |
-
* @param array $pluginMeta Array of meta links.
|
744 |
-
* @param string $pluginFile
|
745 |
-
* @return array
|
746 |
-
*/
|
747 |
-
public function addCheckForUpdatesLink($pluginMeta, $pluginFile) {
|
748 |
-
$isRelevant = ($pluginFile == $this->pluginFile)
|
749 |
-
|| (!empty($this->muPluginFile) && $pluginFile == $this->muPluginFile);
|
750 |
-
|
751 |
-
if ( $isRelevant && current_user_can('update_plugins') ) {
|
752 |
-
$linkUrl = wp_nonce_url(
|
753 |
-
add_query_arg(
|
754 |
-
array(
|
755 |
-
'puc_check_for_updates' => 1,
|
756 |
-
'puc_slug' => $this->slug,
|
757 |
-
),
|
758 |
-
self_admin_url('plugins.php')
|
759 |
-
),
|
760 |
-
'puc_check_for_updates'
|
761 |
-
);
|
762 |
-
|
763 |
-
$linkText = apply_filters('puc_manual_check_link-' . $this->slug, __('Check for updates', 'plugin-update-checker'));
|
764 |
-
if ( !empty($linkText) ) {
|
765 |
-
$pluginMeta[] = sprintf('<a href="%s">%s</a>', esc_attr($linkUrl), $linkText);
|
766 |
-
}
|
767 |
-
}
|
768 |
-
return $pluginMeta;
|
769 |
-
}
|
770 |
-
|
771 |
-
/**
|
772 |
-
* Check for updates when the user clicks the "Check for updates" link.
|
773 |
-
* @see self::addCheckForUpdatesLink()
|
774 |
-
*
|
775 |
-
* @return void
|
776 |
-
*/
|
777 |
-
public function handleManualCheck() {
|
778 |
-
$shouldCheck =
|
779 |
-
isset($_GET['puc_check_for_updates'], $_GET['puc_slug'])
|
780 |
-
&& $_GET['puc_slug'] == $this->slug
|
781 |
-
&& current_user_can('update_plugins')
|
782 |
-
&& check_admin_referer('puc_check_for_updates');
|
783 |
-
|
784 |
-
if ( $shouldCheck ) {
|
785 |
-
$update = $this->checkForUpdates();
|
786 |
-
$status = ($update === null) ? 'no_update' : 'update_available';
|
787 |
-
wp_redirect(add_query_arg(
|
788 |
-
array(
|
789 |
-
'puc_update_check_result' => $status,
|
790 |
-
'puc_slug' => $this->slug,
|
791 |
-
),
|
792 |
-
self_admin_url('plugins.php')
|
793 |
-
));
|
794 |
-
}
|
795 |
-
}
|
796 |
-
|
797 |
-
/**
|
798 |
-
* Display the results of a manual update check.
|
799 |
-
* @see self::handleManualCheck()
|
800 |
-
*
|
801 |
-
* You can change the result message by using the "puc_manual_check_message-$slug" filter.
|
802 |
-
*/
|
803 |
-
public function displayManualCheckResult() {
|
804 |
-
if ( isset($_GET['puc_update_check_result'], $_GET['puc_slug']) && ($_GET['puc_slug'] == $this->slug) ) {
|
805 |
-
$status = strval($_GET['puc_update_check_result']);
|
806 |
-
if ( $status == 'no_update' ) {
|
807 |
-
$message = __('This plugin is up to date.', 'plugin-update-checker');
|
808 |
-
} else if ( $status == 'update_available' ) {
|
809 |
-
$message = __('A new version of this plugin is available.', 'plugin-update-checker');
|
810 |
-
} else {
|
811 |
-
$message = sprintf(__('Unknown update checker status "%s"', 'plugin-update-checker'), htmlentities($status));
|
812 |
-
}
|
813 |
-
printf(
|
814 |
-
'<div class="updated notice is-dismissible"><p>%s</p></div>',
|
815 |
-
apply_filters('puc_manual_check_message-' . $this->slug, $message, $status)
|
816 |
-
);
|
817 |
-
}
|
818 |
-
}
|
819 |
-
|
820 |
-
/**
|
821 |
-
* Check if the plugin file is inside the mu-plugins directory.
|
822 |
-
*
|
823 |
-
* @return bool
|
824 |
-
*/
|
825 |
-
protected function isMuPlugin() {
|
826 |
-
static $cachedResult = null;
|
827 |
-
|
828 |
-
if ( $cachedResult === null ) {
|
829 |
-
//Convert both paths to the canonical form before comparison.
|
830 |
-
$muPluginDir = realpath(WPMU_PLUGIN_DIR);
|
831 |
-
$pluginPath = realpath($this->pluginAbsolutePath);
|
832 |
-
|
833 |
-
$cachedResult = (strpos($pluginPath, $muPluginDir) === 0);
|
834 |
-
}
|
835 |
-
|
836 |
-
return $cachedResult;
|
837 |
-
}
|
838 |
-
|
839 |
-
/**
|
840 |
-
* MU plugins are partially supported, but only when we know which file in mu-plugins
|
841 |
-
* corresponds to this plugin.
|
842 |
-
*
|
843 |
-
* @return bool
|
844 |
-
*/
|
845 |
-
protected function isUnknownMuPlugin() {
|
846 |
-
return empty($this->muPluginFile) && $this->isMuPlugin();
|
847 |
-
}
|
848 |
-
|
849 |
-
/**
|
850 |
-
* Clear the cached plugin version. This method can be set up as a filter (hook) and will
|
851 |
-
* return the filter argument unmodified.
|
852 |
-
*
|
853 |
-
* @param mixed $filterArgument
|
854 |
-
* @return mixed
|
855 |
-
*/
|
856 |
-
public function clearCachedVersion($filterArgument = null) {
|
857 |
-
$this->cachedInstalledVersion = null;
|
858 |
-
return $filterArgument;
|
859 |
-
}
|
860 |
-
|
861 |
-
/**
|
862 |
-
* Register a callback for filtering query arguments.
|
863 |
-
*
|
864 |
-
* The callback function should take one argument - an associative array of query arguments.
|
865 |
-
* It should return a modified array of query arguments.
|
866 |
-
*
|
867 |
-
* @uses add_filter() This method is a convenience wrapper for add_filter().
|
868 |
-
*
|
869 |
-
* @param callable $callback
|
870 |
-
* @return void
|
871 |
-
*/
|
872 |
-
public function addQueryArgFilter($callback){
|
873 |
-
add_filter('puc_request_info_query_args-'.$this->slug, $callback);
|
874 |
-
}
|
875 |
-
|
876 |
-
/**
|
877 |
-
* Register a callback for filtering arguments passed to wp_remote_get().
|
878 |
-
*
|
879 |
-
* The callback function should take one argument - an associative array of arguments -
|
880 |
-
* and return a modified array or arguments. See the WP documentation on wp_remote_get()
|
881 |
-
* for details on what arguments are available and how they work.
|
882 |
-
*
|
883 |
-
* @uses add_filter() This method is a convenience wrapper for add_filter().
|
884 |
-
*
|
885 |
-
* @param callable $callback
|
886 |
-
* @return void
|
887 |
-
*/
|
888 |
-
public function addHttpRequestArgFilter($callback){
|
889 |
-
add_filter('puc_request_info_options-'.$this->slug, $callback);
|
890 |
-
}
|
891 |
-
|
892 |
-
/**
|
893 |
-
* Register a callback for filtering the plugin info retrieved from the external API.
|
894 |
-
*
|
895 |
-
* The callback function should take two arguments. If the plugin info was retrieved
|
896 |
-
* successfully, the first argument passed will be an instance of PluginInfo. Otherwise,
|
897 |
-
* it will be NULL. The second argument will be the corresponding return value of
|
898 |
-
* wp_remote_get (see WP docs for details).
|
899 |
-
*
|
900 |
-
* The callback function should return a new or modified instance of PluginInfo or NULL.
|
901 |
-
*
|
902 |
-
* @uses add_filter() This method is a convenience wrapper for add_filter().
|
903 |
-
*
|
904 |
-
* @param callable $callback
|
905 |
-
* @return void
|
906 |
-
*/
|
907 |
-
public function addResultFilter($callback){
|
908 |
-
add_filter('puc_request_info_result-'.$this->slug, $callback, 10, 2);
|
909 |
-
}
|
910 |
-
|
911 |
-
/**
|
912 |
-
* Register a callback for one of the update checker filters.
|
913 |
-
*
|
914 |
-
* Identical to add_filter(), except it automatically adds the "puc_" prefix
|
915 |
-
* and the "-$plugin_slug" suffix to the filter name. For example, "request_info_result"
|
916 |
-
* becomes "puc_request_info_result-your_plugin_slug".
|
917 |
-
*
|
918 |
-
* @param string $tag
|
919 |
-
* @param callable $callback
|
920 |
-
* @param int $priority
|
921 |
-
* @param int $acceptedArgs
|
922 |
-
*/
|
923 |
-
public function addFilter($tag, $callback, $priority = 10, $acceptedArgs = 1) {
|
924 |
-
add_filter('puc_' . $tag . '-' . $this->slug, $callback, $priority, $acceptedArgs);
|
925 |
-
}
|
926 |
-
|
927 |
-
/**
|
928 |
-
* Initialize the update checker Debug Bar plugin/add-on thingy.
|
929 |
-
*/
|
930 |
-
public function initDebugBarPanel() {
|
931 |
-
$debugBarPlugin = dirname(__FILE__) . '/debug-bar-plugin.php';
|
932 |
-
if ( class_exists('Debug_Bar', false) && file_exists($debugBarPlugin) ) {
|
933 |
-
/** @noinspection PhpIncludeInspection */
|
934 |
-
require_once $debugBarPlugin;
|
935 |
-
$this->debugBarPlugin = new PucDebugBarPlugin_3_1($this);
|
936 |
-
}
|
937 |
-
}
|
938 |
-
|
939 |
-
/**
|
940 |
-
* Trigger a PHP error, but only when $debugMode is enabled.
|
941 |
-
*
|
942 |
-
* @param string $message
|
943 |
-
* @param int $errorType
|
944 |
-
*/
|
945 |
-
protected function triggerError($message, $errorType) {
|
946 |
-
if ( $this->debugMode ) {
|
947 |
-
trigger_error($message, $errorType);
|
948 |
-
}
|
949 |
-
}
|
950 |
-
}
|
951 |
-
|
952 |
-
endif;
|
953 |
-
|
954 |
-
if ( !class_exists('PluginInfo_3_1', false) ):
|
955 |
-
|
956 |
-
/**
|
957 |
-
* A container class for holding and transforming various plugin metadata.
|
958 |
-
*
|
959 |
-
* @author Janis Elsts
|
960 |
-
* @copyright 2016
|
961 |
-
* @version 3.0
|
962 |
-
* @access public
|
963 |
-
*/
|
964 |
-
class PluginInfo_3_1 {
|
965 |
-
//Most fields map directly to the contents of the plugin's info.json file.
|
966 |
-
//See the relevant docs for a description of their meaning.
|
967 |
-
public $name;
|
968 |
-
public $slug;
|
969 |
-
public $version;
|
970 |
-
public $homepage;
|
971 |
-
public $sections = array();
|
972 |
-
public $banners;
|
973 |
-
public $translations = array();
|
974 |
-
public $download_url;
|
975 |
-
|
976 |
-
public $author;
|
977 |
-
public $author_homepage;
|
978 |
-
|
979 |
-
public $requires;
|
980 |
-
public $tested;
|
981 |
-
public $upgrade_notice;
|
982 |
-
|
983 |
-
public $rating;
|
984 |
-
public $num_ratings;
|
985 |
-
public $downloaded;
|
986 |
-
public $active_installs;
|
987 |
-
public $last_updated;
|
988 |
-
|
989 |
-
public $id = 0; //The native WP.org API returns numeric plugin IDs, but they're not used for anything.
|
990 |
-
|
991 |
-
public $filename; //Plugin filename relative to the plugins directory.
|
992 |
-
|
993 |
-
/**
|
994 |
-
* Create a new instance of PluginInfo from JSON-encoded plugin info
|
995 |
-
* returned by an external update API.
|
996 |
-
*
|
997 |
-
* @param string $json Valid JSON string representing plugin info.
|
998 |
-
* @return PluginInfo_3_1|null New instance of PluginInfo, or NULL on error.
|
999 |
-
*/
|
1000 |
-
public static function fromJson($json){
|
1001 |
-
/** @var StdClass $apiResponse */
|
1002 |
-
$apiResponse = json_decode($json);
|
1003 |
-
if ( empty($apiResponse) || !is_object($apiResponse) ){
|
1004 |
-
trigger_error(
|
1005 |
-
"Failed to parse plugin metadata. Try validating your .json file with http://jsonlint.com/",
|
1006 |
-
E_USER_NOTICE
|
1007 |
-
);
|
1008 |
-
return null;
|
1009 |
-
}
|
1010 |
-
|
1011 |
-
$valid = self::validateMetadata($apiResponse);
|
1012 |
-
if ( is_wp_error($valid) ){
|
1013 |
-
trigger_error($valid->get_error_message(), E_USER_NOTICE);
|
1014 |
-
return null;
|
1015 |
-
}
|
1016 |
-
|
1017 |
-
$info = new self();
|
1018 |
-
foreach(get_object_vars($apiResponse) as $key => $value){
|
1019 |
-
$info->$key = $value;
|
1020 |
-
}
|
1021 |
-
|
1022 |
-
//json_decode decodes assoc. arrays as objects. We want it as an array.
|
1023 |
-
$info->sections = (array)$info->sections;
|
1024 |
-
|
1025 |
-
return $info;
|
1026 |
-
}
|
1027 |
-
|
1028 |
-
/**
|
1029 |
-
* Very, very basic validation.
|
1030 |
-
*
|
1031 |
-
* @param StdClass $apiResponse
|
1032 |
-
* @return bool|WP_Error
|
1033 |
-
*/
|
1034 |
-
protected static function validateMetadata($apiResponse) {
|
1035 |
-
if (
|
1036 |
-
!isset($apiResponse->name, $apiResponse->version)
|
1037 |
-
|| empty($apiResponse->name)
|
1038 |
-
|| empty($apiResponse->version)
|
1039 |
-
) {
|
1040 |
-
return new WP_Error(
|
1041 |
-
'puc-invalid-metadata',
|
1042 |
-
"The plugin metadata file does not contain the required 'name' and/or 'version' keys."
|
1043 |
-
);
|
1044 |
-
}
|
1045 |
-
return true;
|
1046 |
-
}
|
1047 |
-
|
1048 |
-
|
1049 |
-
/**
|
1050 |
-
* Transform plugin info into the format used by the native WordPress.org API
|
1051 |
-
*
|
1052 |
-
* @return object
|
1053 |
-
*/
|
1054 |
-
public function toWpFormat(){
|
1055 |
-
$info = new stdClass;
|
1056 |
-
|
1057 |
-
//The custom update API is built so that many fields have the same name and format
|
1058 |
-
//as those returned by the native WordPress.org API. These can be assigned directly.
|
1059 |
-
$sameFormat = array(
|
1060 |
-
'name', 'slug', 'version', 'requires', 'tested', 'rating', 'upgrade_notice',
|
1061 |
-
'num_ratings', 'downloaded', 'active_installs', 'homepage', 'last_updated',
|
1062 |
-
);
|
1063 |
-
foreach($sameFormat as $field){
|
1064 |
-
if ( isset($this->$field) ) {
|
1065 |
-
$info->$field = $this->$field;
|
1066 |
-
} else {
|
1067 |
-
$info->$field = null;
|
1068 |
-
}
|
1069 |
-
}
|
1070 |
-
|
1071 |
-
//Other fields need to be renamed and/or transformed.
|
1072 |
-
$info->download_link = $this->download_url;
|
1073 |
-
$info->author = $this->getFormattedAuthor();
|
1074 |
-
$info->sections = array_merge(array('description' => ''), $this->sections);
|
1075 |
-
|
1076 |
-
if ( !empty($this->banners) ) {
|
1077 |
-
//WP expects an array with two keys: "high" and "low". Both are optional.
|
1078 |
-
//Docs: https://wordpress.org/plugins/about/faq/#banners
|
1079 |
-
$info->banners = is_object($this->banners) ? get_object_vars($this->banners) : $this->banners;
|
1080 |
-
$info->banners = array_intersect_key($info->banners, array('high' => true, 'low' => true));
|
1081 |
-
}
|
1082 |
-
|
1083 |
-
return $info;
|
1084 |
-
}
|
1085 |
-
|
1086 |
-
protected function getFormattedAuthor() {
|
1087 |
-
if ( !empty($this->author_homepage) ){
|
1088 |
-
return sprintf('<a href="%s">%s</a>', $this->author_homepage, $this->author);
|
1089 |
-
}
|
1090 |
-
return $this->author;
|
1091 |
-
}
|
1092 |
-
}
|
1093 |
-
|
1094 |
-
endif;
|
1095 |
-
|
1096 |
-
if ( !class_exists('PluginUpdate_3_1', false) ):
|
1097 |
-
|
1098 |
-
/**
|
1099 |
-
* A simple container class for holding information about an available update.
|
1100 |
-
*
|
1101 |
-
* @author Janis Elsts
|
1102 |
-
* @copyright 2016
|
1103 |
-
* @version 3.0
|
1104 |
-
* @access public
|
1105 |
-
*/
|
1106 |
-
class PluginUpdate_3_1 {
|
1107 |
-
public $id = 0;
|
1108 |
-
public $slug;
|
1109 |
-
public $version;
|
1110 |
-
public $homepage;
|
1111 |
-
public $download_url;
|
1112 |
-
public $upgrade_notice;
|
1113 |
-
public $tested;
|
1114 |
-
public $translations = array();
|
1115 |
-
public $filename; //Plugin filename relative to the plugins directory.
|
1116 |
-
|
1117 |
-
private static $fields = array(
|
1118 |
-
'id', 'slug', 'version', 'homepage', 'tested',
|
1119 |
-
'download_url', 'upgrade_notice', 'filename',
|
1120 |
-
'translations'
|
1121 |
-
);
|
1122 |
-
|
1123 |
-
/**
|
1124 |
-
* Create a new instance of PluginUpdate from its JSON-encoded representation.
|
1125 |
-
*
|
1126 |
-
* @param string $json
|
1127 |
-
* @return PluginUpdate_3_1|null
|
1128 |
-
*/
|
1129 |
-
public static function fromJson($json){
|
1130 |
-
//Since update-related information is simply a subset of the full plugin info,
|
1131 |
-
//we can parse the update JSON as if it was a plugin info string, then copy over
|
1132 |
-
//the parts that we care about.
|
1133 |
-
$pluginInfo = PluginInfo_3_1::fromJson($json);
|
1134 |
-
if ( $pluginInfo != null ) {
|
1135 |
-
return self::fromPluginInfo($pluginInfo);
|
1136 |
-
} else {
|
1137 |
-
return null;
|
1138 |
-
}
|
1139 |
-
}
|
1140 |
-
|
1141 |
-
/**
|
1142 |
-
* Create a new instance of PluginUpdate based on an instance of PluginInfo.
|
1143 |
-
* Basically, this just copies a subset of fields from one object to another.
|
1144 |
-
*
|
1145 |
-
* @param PluginInfo_3_1 $info
|
1146 |
-
* @return PluginUpdate_3_1
|
1147 |
-
*/
|
1148 |
-
public static function fromPluginInfo($info){
|
1149 |
-
return self::fromObject($info);
|
1150 |
-
}
|
1151 |
-
|
1152 |
-
/**
|
1153 |
-
* Create a new instance of PluginUpdate by copying the necessary fields from
|
1154 |
-
* another object.
|
1155 |
-
*
|
1156 |
-
* @param StdClass|PluginInfo_3_1|PluginUpdate_3_1 $object The source object.
|
1157 |
-
* @return PluginUpdate_3_1 The new copy.
|
1158 |
-
*/
|
1159 |
-
public static function fromObject($object) {
|
1160 |
-
$update = new self();
|
1161 |
-
$fields = self::$fields;
|
1162 |
-
if ( !empty($object->slug) ) {
|
1163 |
-
$fields = apply_filters('puc_retain_fields-' . $object->slug, $fields);
|
1164 |
-
}
|
1165 |
-
foreach($fields as $field){
|
1166 |
-
if (property_exists($object, $field)) {
|
1167 |
-
$update->$field = $object->$field;
|
1168 |
-
}
|
1169 |
-
}
|
1170 |
-
return $update;
|
1171 |
-
}
|
1172 |
-
|
1173 |
-
/**
|
1174 |
-
* Create an instance of StdClass that can later be converted back to
|
1175 |
-
* a PluginUpdate. Useful for serialization and caching, as it avoids
|
1176 |
-
* the "incomplete object" problem if the cached value is loaded before
|
1177 |
-
* this class.
|
1178 |
-
*
|
1179 |
-
* @return StdClass
|
1180 |
-
*/
|
1181 |
-
public function toStdClass() {
|
1182 |
-
$object = new stdClass();
|
1183 |
-
$fields = self::$fields;
|
1184 |
-
if ( !empty($this->slug) ) {
|
1185 |
-
$fields = apply_filters('puc_retain_fields-' . $this->slug, $fields);
|
1186 |
-
}
|
1187 |
-
foreach($fields as $field){
|
1188 |
-
if (property_exists($this, $field)) {
|
1189 |
-
$object->$field = $this->$field;
|
1190 |
-
}
|
1191 |
-
}
|
1192 |
-
return $object;
|
1193 |
-
}
|
1194 |
-
|
1195 |
-
|
1196 |
-
/**
|
1197 |
-
* Transform the update into the format used by WordPress native plugin API.
|
1198 |
-
*
|
1199 |
-
* @return object
|
1200 |
-
*/
|
1201 |
-
public function toWpFormat(){
|
1202 |
-
$update = new stdClass;
|
1203 |
-
|
1204 |
-
$update->id = $this->id;
|
1205 |
-
$update->slug = $this->slug;
|
1206 |
-
$update->new_version = $this->version;
|
1207 |
-
$update->url = $this->homepage;
|
1208 |
-
$update->package = $this->download_url;
|
1209 |
-
$update->tested = $this->tested;
|
1210 |
-
$update->plugin = $this->filename;
|
1211 |
-
|
1212 |
-
if ( !empty($this->upgrade_notice) ){
|
1213 |
-
$update->upgrade_notice = $this->upgrade_notice;
|
1214 |
-
}
|
1215 |
-
|
1216 |
-
return $update;
|
1217 |
-
}
|
1218 |
-
}
|
1219 |
-
|
1220 |
-
endif;
|
1221 |
-
|
1222 |
-
if ( !class_exists('PucScheduler_3_1', false) ):
|
1223 |
-
|
1224 |
-
/**
|
1225 |
-
* The scheduler decides when and how often to check for updates.
|
1226 |
-
* It calls @see PluginUpdateChecker::checkForUpdates() to perform the actual checks.
|
1227 |
-
*
|
1228 |
-
* @version 3.0
|
1229 |
-
*/
|
1230 |
-
class PucScheduler_3_1 {
|
1231 |
-
public $checkPeriod = 12; //How often to check for updates (in hours).
|
1232 |
-
public $throttleRedundantChecks = false; //Check less often if we already know that an update is available.
|
1233 |
-
public $throttledCheckPeriod = 72;
|
1234 |
-
|
1235 |
-
/**
|
1236 |
-
* @var PluginUpdateChecker_3_1
|
1237 |
-
*/
|
1238 |
-
protected $updateChecker;
|
1239 |
-
|
1240 |
-
private $cronHook = null;
|
1241 |
-
|
1242 |
-
/**
|
1243 |
-
* Scheduler constructor.
|
1244 |
-
*
|
1245 |
-
* @param PluginUpdateChecker_3_1 $updateChecker
|
1246 |
-
* @param int $checkPeriod How often to check for updates (in hours).
|
1247 |
-
*/
|
1248 |
-
public function __construct($updateChecker, $checkPeriod) {
|
1249 |
-
$this->updateChecker = $updateChecker;
|
1250 |
-
$this->checkPeriod = $checkPeriod;
|
1251 |
-
|
1252 |
-
//Set up the periodic update checks
|
1253 |
-
$this->cronHook = 'check_plugin_updates-' . $this->updateChecker->slug;
|
1254 |
-
if ( $this->checkPeriod > 0 ){
|
1255 |
-
|
1256 |
-
//Trigger the check via Cron.
|
1257 |
-
//Try to use one of the default schedules if possible as it's less likely to conflict
|
1258 |
-
//with other plugins and their custom schedules.
|
1259 |
-
$defaultSchedules = array(
|
1260 |
-
1 => 'hourly',
|
1261 |
-
12 => 'twicedaily',
|
1262 |
-
24 => 'daily',
|
1263 |
-
);
|
1264 |
-
if ( array_key_exists($this->checkPeriod, $defaultSchedules) ) {
|
1265 |
-
$scheduleName = $defaultSchedules[$this->checkPeriod];
|
1266 |
-
} else {
|
1267 |
-
//Use a custom cron schedule.
|
1268 |
-
$scheduleName = 'every' . $this->checkPeriod . 'hours';
|
1269 |
-
add_filter('cron_schedules', array($this, '_addCustomSchedule'));
|
1270 |
-
}
|
1271 |
-
|
1272 |
-
if ( !wp_next_scheduled($this->cronHook) && !defined('WP_INSTALLING') ) {
|
1273 |
-
wp_schedule_event(time(), $scheduleName, $this->cronHook);
|
1274 |
-
}
|
1275 |
-
add_action($this->cronHook, array($this, 'maybeCheckForUpdates'));
|
1276 |
-
|
1277 |
-
register_deactivation_hook($this->updateChecker->pluginFile, array($this, '_removeUpdaterCron'));
|
1278 |
-
|
1279 |
-
//In case Cron is disabled or unreliable, we also manually trigger
|
1280 |
-
//the periodic checks while the user is browsing the Dashboard.
|
1281 |
-
add_action( 'admin_init', array($this, 'maybeCheckForUpdates') );
|
1282 |
-
|
1283 |
-
//Like WordPress itself, we check more often on certain pages.
|
1284 |
-
/** @see wp_update_plugins */
|
1285 |
-
add_action('load-update-core.php', array($this, 'maybeCheckForUpdates'));
|
1286 |
-
add_action('load-plugins.php', array($this, 'maybeCheckForUpdates'));
|
1287 |
-
add_action('load-update.php', array($this, 'maybeCheckForUpdates'));
|
1288 |
-
//This hook fires after a bulk update is complete.
|
1289 |
-
add_action('upgrader_process_complete', array($this, 'maybeCheckForUpdates'), 11, 0);
|
1290 |
-
|
1291 |
-
} else {
|
1292 |
-
//Periodic checks are disabled.
|
1293 |
-
wp_clear_scheduled_hook($this->cronHook);
|
1294 |
-
}
|
1295 |
-
}
|
1296 |
-
|
1297 |
-
/**
|
1298 |
-
* Check for updates if the configured check interval has already elapsed.
|
1299 |
-
* Will use a shorter check interval on certain admin pages like "Dashboard -> Updates" or when doing cron.
|
1300 |
-
*
|
1301 |
-
* You can override the default behaviour by using the "puc_check_now-$slug" filter.
|
1302 |
-
* The filter callback will be passed three parameters:
|
1303 |
-
* - Current decision. TRUE = check updates now, FALSE = don't check now.
|
1304 |
-
* - Last check time as a Unix timestamp.
|
1305 |
-
* - Configured check period in hours.
|
1306 |
-
* Return TRUE to check for updates immediately, or FALSE to cancel.
|
1307 |
-
*
|
1308 |
-
* This method is declared public because it's a hook callback. Calling it directly is not recommended.
|
1309 |
-
*/
|
1310 |
-
public function maybeCheckForUpdates(){
|
1311 |
-
if ( empty($this->checkPeriod) ){
|
1312 |
-
return;
|
1313 |
-
}
|
1314 |
-
|
1315 |
-
$state = $this->updateChecker->getUpdateState();
|
1316 |
-
$shouldCheck =
|
1317 |
-
empty($state) ||
|
1318 |
-
!isset($state->lastCheck) ||
|
1319 |
-
( (time() - $state->lastCheck) >= $this->getEffectiveCheckPeriod() );
|
1320 |
-
|
1321 |
-
//Let plugin authors substitute their own algorithm.
|
1322 |
-
$shouldCheck = apply_filters(
|
1323 |
-
'puc_check_now-' . $this->updateChecker->slug,
|
1324 |
-
$shouldCheck,
|
1325 |
-
(!empty($state) && isset($state->lastCheck)) ? $state->lastCheck : 0,
|
1326 |
-
$this->checkPeriod
|
1327 |
-
);
|
1328 |
-
|
1329 |
-
if ( $shouldCheck ) {
|
1330 |
-
$this->updateChecker->checkForUpdates();
|
1331 |
-
}
|
1332 |
-
}
|
1333 |
-
|
1334 |
-
/**
|
1335 |
-
* Calculate the actual check period based on the current status and environment.
|
1336 |
-
*
|
1337 |
-
* @return int Check period in seconds.
|
1338 |
-
*/
|
1339 |
-
protected function getEffectiveCheckPeriod() {
|
1340 |
-
$currentFilter = current_filter();
|
1341 |
-
if ( in_array($currentFilter, array('load-update-core.php', 'upgrader_process_complete')) ) {
|
1342 |
-
//Check more often when the user visits "Dashboard -> Updates" or does a bulk update.
|
1343 |
-
$period = 60;
|
1344 |
-
} else if ( in_array($currentFilter, array('load-plugins.php', 'load-update.php')) ) {
|
1345 |
-
//Also check more often on the "Plugins" page and /wp-admin/update.php.
|
1346 |
-
$period = 3600;
|
1347 |
-
} else if ( $this->throttleRedundantChecks && ($this->updateChecker->getUpdate() !== null) ) {
|
1348 |
-
//Check less frequently if it's already known that an update is available.
|
1349 |
-
$period = $this->throttledCheckPeriod * 3600;
|
1350 |
-
} else if ( defined('DOING_CRON') && constant('DOING_CRON') ) {
|
1351 |
-
//WordPress cron schedules are not exact, so lets do an update check even
|
1352 |
-
//if slightly less than $checkPeriod hours have elapsed since the last check.
|
1353 |
-
$cronFuzziness = 20 * 60;
|
1354 |
-
$period = $this->checkPeriod * 3600 - $cronFuzziness;
|
1355 |
-
} else {
|
1356 |
-
$period = $this->checkPeriod * 3600;
|
1357 |
-
}
|
1358 |
-
|
1359 |
-
return $period;
|
1360 |
-
}
|
1361 |
-
|
1362 |
-
/**
|
1363 |
-
* Add our custom schedule to the array of Cron schedules used by WP.
|
1364 |
-
*
|
1365 |
-
* @param array $schedules
|
1366 |
-
* @return array
|
1367 |
-
*/
|
1368 |
-
public function _addCustomSchedule($schedules){
|
1369 |
-
if ( $this->checkPeriod && ($this->checkPeriod > 0) ){
|
1370 |
-
$scheduleName = 'every' . $this->checkPeriod . 'hours';
|
1371 |
-
$schedules[$scheduleName] = array(
|
1372 |
-
'interval' => $this->checkPeriod * 3600,
|
1373 |
-
'display' => sprintf('Every %d hours', $this->checkPeriod),
|
1374 |
-
);
|
1375 |
-
}
|
1376 |
-
return $schedules;
|
1377 |
-
}
|
1378 |
-
|
1379 |
-
/**
|
1380 |
-
* Remove the scheduled cron event that the library uses to check for updates.
|
1381 |
-
*
|
1382 |
-
* @return void
|
1383 |
-
*/
|
1384 |
-
public function _removeUpdaterCron(){
|
1385 |
-
wp_clear_scheduled_hook($this->cronHook);
|
1386 |
-
}
|
1387 |
-
|
1388 |
-
/**
|
1389 |
-
* Get the name of the update checker's WP-cron hook. Mostly useful for debugging.
|
1390 |
-
*
|
1391 |
-
* @return string
|
1392 |
-
*/
|
1393 |
-
public function getCronHookName() {
|
1394 |
-
return $this->cronHook;
|
1395 |
-
}
|
1396 |
-
}
|
1397 |
-
|
1398 |
-
endif;
|
1399 |
-
|
1400 |
-
|
1401 |
-
if ( !class_exists('PucUpgraderStatus_3_1', false) ):
|
1402 |
-
|
1403 |
-
/**
|
1404 |
-
* A utility class that helps figure out which plugin WordPress is upgrading.
|
1405 |
-
*
|
1406 |
-
* It may seem strange to have an separate class just for that, but the task is surprisingly complicated.
|
1407 |
-
* Core classes like Plugin_Upgrader don't expose the plugin file name during an in-progress update (AFAICT).
|
1408 |
-
* This class uses a few workarounds and heuristics to get the file name.
|
1409 |
-
*/
|
1410 |
-
class PucUpgraderStatus_3_1 {
|
1411 |
-
private $upgradedPluginFile = null; //The plugin that is currently being upgraded by WordPress.
|
1412 |
-
|
1413 |
-
public function __construct() {
|
1414 |
-
//Keep track of which plugin WordPress is currently upgrading.
|
1415 |
-
add_filter('upgrader_pre_install', array($this, 'setUpgradedPlugin'), 10, 2);
|
1416 |
-
add_filter('upgrader_package_options', array($this, 'setUpgradedPluginFromOptions'), 10, 1);
|
1417 |
-
add_filter('upgrader_post_install', array($this, 'clearUpgradedPlugin'), 10, 1);
|
1418 |
-
add_action('upgrader_process_complete', array($this, 'clearUpgradedPlugin'), 10, 1);
|
1419 |
-
}
|
1420 |
-
|
1421 |
-
/**
|
1422 |
-
* Is there and update being installed RIGHT NOW, for a specific plugin?
|
1423 |
-
*
|
1424 |
-
* Caution: This method is unreliable. WordPress doesn't make it easy to figure out what it is upgrading,
|
1425 |
-
* and upgrader implementations are liable to change without notice.
|
1426 |
-
*
|
1427 |
-
* @param string $pluginFile The plugin to check.
|
1428 |
-
* @param WP_Upgrader|null $upgrader The upgrader that's performing the current update.
|
1429 |
-
* @return bool True if the plugin identified by $pluginFile is being upgraded.
|
1430 |
-
*/
|
1431 |
-
public function isPluginBeingUpgraded($pluginFile, $upgrader = null) {
|
1432 |
-
if ( isset($upgrader) ) {
|
1433 |
-
$upgradedPluginFile = $this->getPluginBeingUpgradedBy($upgrader);
|
1434 |
-
if ( !empty($upgradedPluginFile) ) {
|
1435 |
-
$this->upgradedPluginFile = $upgradedPluginFile;
|
1436 |
-
}
|
1437 |
-
}
|
1438 |
-
return ( !empty($this->upgradedPluginFile) && ($this->upgradedPluginFile === $pluginFile) );
|
1439 |
-
}
|
1440 |
-
|
1441 |
-
/**
|
1442 |
-
* Get the file name of the plugin that's currently being upgraded.
|
1443 |
-
*
|
1444 |
-
* @param Plugin_Upgrader|WP_Upgrader $upgrader
|
1445 |
-
* @return string|null
|
1446 |
-
*/
|
1447 |
-
private function getPluginBeingUpgradedBy($upgrader) {
|
1448 |
-
if ( !isset($upgrader, $upgrader->skin) ) {
|
1449 |
-
return null;
|
1450 |
-
}
|
1451 |
-
|
1452 |
-
//Figure out which plugin is being upgraded.
|
1453 |
-
$pluginFile = null;
|
1454 |
-
$skin = $upgrader->skin;
|
1455 |
-
if ( $skin instanceof Plugin_Upgrader_Skin ) {
|
1456 |
-
if ( isset($skin->plugin) && is_string($skin->plugin) && ($skin->plugin !== '') ) {
|
1457 |
-
$pluginFile = $skin->plugin;
|
1458 |
-
}
|
1459 |
-
} elseif ( isset($skin->plugin_info) && is_array($skin->plugin_info) ) {
|
1460 |
-
//This case is tricky because Bulk_Plugin_Upgrader_Skin (etc) doesn't actually store the plugin
|
1461 |
-
//filename anywhere. Instead, it has the plugin headers in $plugin_info. So the best we can
|
1462 |
-
//do is compare those headers to the headers of installed plugins.
|
1463 |
-
$pluginFile = $this->identifyPluginByHeaders($skin->plugin_info);
|
1464 |
-
}
|
1465 |
-
|
1466 |
-
return $pluginFile;
|
1467 |
-
}
|
1468 |
-
|
1469 |
-
/**
|
1470 |
-
* Identify an installed plugin based on its headers.
|
1471 |
-
*
|
1472 |
-
* @param array $searchHeaders The plugin file header to look for.
|
1473 |
-
* @return string|null Plugin basename ("foo/bar.php"), or NULL if we can't identify the plugin.
|
1474 |
-
*/
|
1475 |
-
private function identifyPluginByHeaders($searchHeaders) {
|
1476 |
-
if ( !function_exists('get_plugins') ){
|
1477 |
-
/** @noinspection PhpIncludeInspection */
|
1478 |
-
require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
|
1479 |
-
}
|
1480 |
-
|
1481 |
-
$installedPlugins = get_plugins();
|
1482 |
-
$matches = array();
|
1483 |
-
foreach($installedPlugins as $pluginBasename => $headers) {
|
1484 |
-
$diff1 = array_diff_assoc($headers, $searchHeaders);
|
1485 |
-
$diff2 = array_diff_assoc($searchHeaders, $headers);
|
1486 |
-
if ( empty($diff1) && empty($diff2) ) {
|
1487 |
-
$matches[] = $pluginBasename;
|
1488 |
-
}
|
1489 |
-
}
|
1490 |
-
|
1491 |
-
//It's possible (though very unlikely) that there could be two plugins with identical
|
1492 |
-
//headers. In that case, we can't unambiguously identify the plugin that's being upgraded.
|
1493 |
-
if ( count($matches) !== 1 ) {
|
1494 |
-
return null;
|
1495 |
-
}
|
1496 |
-
|
1497 |
-
return reset($matches);
|
1498 |
-
}
|
1499 |
-
|
1500 |
-
/**
|
1501 |
-
* @access private
|
1502 |
-
*
|
1503 |
-
* @param mixed $input
|
1504 |
-
* @param array $hookExtra
|
1505 |
-
* @return mixed Returns $input unaltered.
|
1506 |
-
*/
|
1507 |
-
public function setUpgradedPlugin($input, $hookExtra) {
|
1508 |
-
if (!empty($hookExtra['plugin']) && is_string($hookExtra['plugin'])) {
|
1509 |
-
$this->upgradedPluginFile = $hookExtra['plugin'];
|
1510 |
-
} else {
|
1511 |
-
$this->upgradedPluginFile = null;
|
1512 |
-
}
|
1513 |
-
return $input;
|
1514 |
-
}
|
1515 |
-
|
1516 |
-
/**
|
1517 |
-
* @access private
|
1518 |
-
*
|
1519 |
-
* @param array $options
|
1520 |
-
* @return array
|
1521 |
-
*/
|
1522 |
-
public function setUpgradedPluginFromOptions($options) {
|
1523 |
-
if (isset($options['hook_extra']['plugin']) && is_string($options['hook_extra']['plugin'])) {
|
1524 |
-
$this->upgradedPluginFile = $options['hook_extra']['plugin'];
|
1525 |
-
} else {
|
1526 |
-
$this->upgradedPluginFile = null;
|
1527 |
-
}
|
1528 |
-
return $options;
|
1529 |
-
}
|
1530 |
-
|
1531 |
-
/**
|
1532 |
-
* @access private
|
1533 |
-
*
|
1534 |
-
* @param mixed $input
|
1535 |
-
* @return mixed Returns $input unaltered.
|
1536 |
-
*/
|
1537 |
-
public function clearUpgradedPlugin($input = null) {
|
1538 |
-
$this->upgradedPluginFile = null;
|
1539 |
-
return $input;
|
1540 |
-
}
|
1541 |
-
}
|
1542 |
-
|
1543 |
-
endif;
|
1544 |
-
|
1545 |
-
|
1546 |
-
if ( !class_exists('PucFactory', false) ):
|
1547 |
-
|
1548 |
-
/**
|
1549 |
-
* A factory that builds instances of other classes from this library.
|
1550 |
-
*
|
1551 |
-
* When multiple versions of the same class have been loaded (e.g. PluginUpdateChecker 1.2
|
1552 |
-
* and 1.3), this factory will always use the latest available version. Register class
|
1553 |
-
* versions by calling {@link PucFactory::addVersion()}.
|
1554 |
-
*
|
1555 |
-
* At the moment it can only build instances of the PluginUpdateChecker class. Other classes
|
1556 |
-
* are intended mainly for internal use and refer directly to specific implementations. If you
|
1557 |
-
* want to instantiate one of them anyway, you can use {@link PucFactory::getLatestClassVersion()}
|
1558 |
-
* to get the class name and then create it with <code>new $class(...)</code>.
|
1559 |
-
*/
|
1560 |
-
class PucFactory {
|
1561 |
-
protected static $classVersions = array();
|
1562 |
-
protected static $sorted = false;
|
1563 |
-
|
1564 |
-
/**
|
1565 |
-
* Create a new instance of PluginUpdateChecker.
|
1566 |
-
*
|
1567 |
-
* @see PluginUpdateChecker::__construct()
|
1568 |
-
*
|
1569 |
-
* @param $metadataUrl
|
1570 |
-
* @param $pluginFile
|
1571 |
-
* @param string $slug
|
1572 |
-
* @param int $checkPeriod
|
1573 |
-
* @param string $optionName
|
1574 |
-
* @param string $muPluginFile
|
1575 |
-
* @return PluginUpdateChecker_3_1
|
1576 |
-
*/
|
1577 |
-
public static function buildUpdateChecker($metadataUrl, $pluginFile, $slug = '', $checkPeriod = 12, $optionName = '', $muPluginFile = '') {
|
1578 |
-
$class = self::getLatestClassVersion('PluginUpdateChecker');
|
1579 |
-
return new $class($metadataUrl, $pluginFile, $slug, $checkPeriod, $optionName, $muPluginFile);
|
1580 |
-
}
|
1581 |
-
|
1582 |
-
/**
|
1583 |
-
* Get the specific class name for the latest available version of a class.
|
1584 |
-
*
|
1585 |
-
* @param string $class
|
1586 |
-
* @return string|null
|
1587 |
-
*/
|
1588 |
-
public static function getLatestClassVersion($class) {
|
1589 |
-
if ( !self::$sorted ) {
|
1590 |
-
self::sortVersions();
|
1591 |
-
}
|
1592 |
-
|
1593 |
-
if ( isset(self::$classVersions[$class]) ) {
|
1594 |
-
return reset(self::$classVersions[$class]);
|
1595 |
-
} else {
|
1596 |
-
return null;
|
1597 |
-
}
|
1598 |
-
}
|
1599 |
-
|
1600 |
-
/**
|
1601 |
-
* Sort available class versions in descending order (i.e. newest first).
|
1602 |
-
*/
|
1603 |
-
protected static function sortVersions() {
|
1604 |
-
foreach ( self::$classVersions as $class => $versions ) {
|
1605 |
-
uksort($versions, array(__CLASS__, 'compareVersions'));
|
1606 |
-
self::$classVersions[$class] = $versions;
|
1607 |
-
}
|
1608 |
-
self::$sorted = true;
|
1609 |
-
}
|
1610 |
-
|
1611 |
-
protected static function compareVersions($a, $b) {
|
1612 |
-
return -version_compare($a, $b);
|
1613 |
-
}
|
1614 |
-
|
1615 |
-
/**
|
1616 |
-
* Register a version of a class.
|
1617 |
-
*
|
1618 |
-
* @access private This method is only for internal use by the library.
|
1619 |
-
*
|
1620 |
-
* @param string $generalClass Class name without version numbers, e.g. 'PluginUpdateChecker'.
|
1621 |
-
* @param string $versionedClass Actual class name, e.g. 'PluginUpdateChecker_1_2'.
|
1622 |
-
* @param string $version Version number, e.g. '1.2'.
|
1623 |
-
*/
|
1624 |
-
public static function addVersion($generalClass, $versionedClass, $version) {
|
1625 |
-
if ( !isset(self::$classVersions[$generalClass]) ) {
|
1626 |
-
self::$classVersions[$generalClass] = array();
|
1627 |
-
}
|
1628 |
-
self::$classVersions[$generalClass][$version] = $versionedClass;
|
1629 |
-
self::$sorted = false;
|
1630 |
-
}
|
1631 |
-
}
|
1632 |
-
|
1633 |
-
endif;
|
1634 |
-
|
1635 |
-
require_once(dirname(__FILE__) . '/github-checker.php');
|
1636 |
-
|
1637 |
-
//Register classes defined in this file with the factory.
|
1638 |
-
PucFactory::addVersion('PluginUpdateChecker', 'PluginUpdateChecker_3_1', '3.1');
|
1639 |
-
PucFactory::addVersion('PluginUpdate', 'PluginUpdate_3_1', '3.1');
|
1640 |
-
PucFactory::addVersion('PluginInfo', 'PluginInfo_3_1', '3.1');
|
1641 |
-
PucFactory::addVersion('PucGitHubChecker', 'PucGitHubChecker_3_1', '3.1');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/plugin-update-checker/vendor/Parsedown.php
DELETED
@@ -1,1538 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
#
|
4 |
-
#
|
5 |
-
# Parsedown
|
6 |
-
# http://parsedown.org
|
7 |
-
#
|
8 |
-
# (c) Emanuil Rusev
|
9 |
-
# http://erusev.com
|
10 |
-
#
|
11 |
-
# For the full license information, view the LICENSE file that was distributed
|
12 |
-
# with this source code.
|
13 |
-
#
|
14 |
-
#
|
15 |
-
|
16 |
-
class Parsedown
|
17 |
-
{
|
18 |
-
# ~
|
19 |
-
|
20 |
-
const version = '1.6.0';
|
21 |
-
|
22 |
-
# ~
|
23 |
-
|
24 |
-
function text($text)
|
25 |
-
{
|
26 |
-
# make sure no definitions are set
|
27 |
-
$this->DefinitionData = array();
|
28 |
-
|
29 |
-
# standardize line breaks
|
30 |
-
$text = str_replace(array("\r\n", "\r"), "\n", $text);
|
31 |
-
|
32 |
-
# remove surrounding line breaks
|
33 |
-
$text = trim($text, "\n");
|
34 |
-
|
35 |
-
# split text into lines
|
36 |
-
$lines = explode("\n", $text);
|
37 |
-
|
38 |
-
# iterate through lines to identify blocks
|
39 |
-
$markup = $this->lines($lines);
|
40 |
-
|
41 |
-
# trim line breaks
|
42 |
-
$markup = trim($markup, "\n");
|
43 |
-
|
44 |
-
return $markup;
|
45 |
-
}
|
46 |
-
|
47 |
-
#
|
48 |
-
# Setters
|
49 |
-
#
|
50 |
-
|
51 |
-
function setBreaksEnabled($breaksEnabled)
|
52 |
-
{
|
53 |
-
$this->breaksEnabled = $breaksEnabled;
|
54 |
-
|
55 |
-
return $this;
|
56 |
-
}
|
57 |
-
|
58 |
-
protected $breaksEnabled;
|
59 |
-
|
60 |
-
function setMarkupEscaped($markupEscaped)
|
61 |
-
{
|
62 |
-
$this->markupEscaped = $markupEscaped;
|
63 |
-
|
64 |
-
return $this;
|
65 |
-
}
|
66 |
-
|
67 |
-
protected $markupEscaped;
|
68 |
-
|
69 |
-
function setUrlsLinked($urlsLinked)
|
70 |
-
{
|
71 |
-
$this->urlsLinked = $urlsLinked;
|
72 |
-
|
73 |
-
return $this;
|
74 |
-
}
|
75 |
-
|
76 |
-
protected $urlsLinked = true;
|
77 |
-
|
78 |
-
#
|
79 |
-
# Lines
|
80 |
-
#
|
81 |
-
|
82 |
-
protected $BlockTypes = array(
|
83 |
-
'#' => array('Header'),
|
84 |
-
'*' => array('Rule', 'List'),
|
85 |
-
'+' => array('List'),
|
86 |
-
'-' => array('SetextHeader', 'Table', 'Rule', 'List'),
|
87 |
-
'0' => array('List'),
|
88 |
-
'1' => array('List'),
|
89 |
-
'2' => array('List'),
|
90 |
-
'3' => array('List'),
|
91 |
-
'4' => array('List'),
|
92 |
-
'5' => array('List'),
|
93 |
-
'6' => array('List'),
|
94 |
-
'7' => array('List'),
|
95 |
-
'8' => array('List'),
|
96 |
-
'9' => array('List'),
|
97 |
-
':' => array('Table'),
|
98 |
-
'<' => array('Comment', 'Markup'),
|
99 |
-
'=' => array('SetextHeader'),
|
100 |
-
'>' => array('Quote'),
|
101 |
-
'[' => array('Reference'),
|
102 |
-
'_' => array('Rule'),
|
103 |
-
'`' => array('FencedCode'),
|
104 |
-
'|' => array('Table'),
|
105 |
-
'~' => array('FencedCode'),
|
106 |
-
);
|
107 |
-
|
108 |
-
# ~
|
109 |
-
|
110 |
-
protected $unmarkedBlockTypes = array(
|
111 |
-
'Code',
|
112 |
-
);
|
113 |
-
|
114 |
-
#
|
115 |
-
# Blocks
|
116 |
-
#
|
117 |
-
|
118 |
-
protected function lines(array $lines)
|
119 |
-
{
|
120 |
-
$CurrentBlock = null;
|
121 |
-
|
122 |
-
foreach ($lines as $line)
|
123 |
-
{
|
124 |
-
if (chop($line) === '')
|
125 |
-
{
|
126 |
-
if (isset($CurrentBlock))
|
127 |
-
{
|
128 |
-
$CurrentBlock['interrupted'] = true;
|
129 |
-
}
|
130 |
-
|
131 |
-
continue;
|
132 |
-
}
|
133 |
-
|
134 |
-
if (strpos($line, "\t") !== false)
|
135 |
-
{
|
136 |
-
$parts = explode("\t", $line);
|
137 |
-
|
138 |
-
$line = $parts[0];
|
139 |
-
|
140 |
-
unset($parts[0]);
|
141 |
-
|
142 |
-
foreach ($parts as $part)
|
143 |
-
{
|
144 |
-
$shortage = 4 - mb_strlen($line, 'utf-8') % 4;
|
145 |
-
|
146 |
-
$line .= str_repeat(' ', $shortage);
|
147 |
-
$line .= $part;
|
148 |
-
}
|
149 |
-
}
|
150 |
-
|
151 |
-
$indent = 0;
|
152 |
-
|
153 |
-
while (isset($line[$indent]) and $line[$indent] === ' ')
|
154 |
-
{
|
155 |
-
$indent ++;
|
156 |
-
}
|
157 |
-
|
158 |
-
$text = $indent > 0 ? substr($line, $indent) : $line;
|
159 |
-
|
160 |
-
# ~
|
161 |
-
|
162 |
-
$Line = array('body' => $line, 'indent' => $indent, 'text' => $text);
|
163 |
-
|
164 |
-
# ~
|
165 |
-
|
166 |
-
if (isset($CurrentBlock['continuable']))
|
167 |
-
{
|
168 |
-
$Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock);
|
169 |
-
|
170 |
-
if (isset($Block))
|
171 |
-
{
|
172 |
-
$CurrentBlock = $Block;
|
173 |
-
|
174 |
-
continue;
|
175 |
-
}
|
176 |
-
else
|
177 |
-
{
|
178 |
-
if ($this->isBlockCompletable($CurrentBlock['type']))
|
179 |
-
{
|
180 |
-
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
|
181 |
-
}
|
182 |
-
}
|
183 |
-
}
|
184 |
-
|
185 |
-
# ~
|
186 |
-
|
187 |
-
$marker = $text[0];
|
188 |
-
|
189 |
-
# ~
|
190 |
-
|
191 |
-
$blockTypes = $this->unmarkedBlockTypes;
|
192 |
-
|
193 |
-
if (isset($this->BlockTypes[$marker]))
|
194 |
-
{
|
195 |
-
foreach ($this->BlockTypes[$marker] as $blockType)
|
196 |
-
{
|
197 |
-
$blockTypes []= $blockType;
|
198 |
-
}
|
199 |
-
}
|
200 |
-
|
201 |
-
#
|
202 |
-
# ~
|
203 |
-
|
204 |
-
foreach ($blockTypes as $blockType)
|
205 |
-
{
|
206 |
-
$Block = $this->{'block'.$blockType}($Line, $CurrentBlock);
|
207 |
-
|
208 |
-
if (isset($Block))
|
209 |
-
{
|
210 |
-
$Block['type'] = $blockType;
|
211 |
-
|
212 |
-
if ( ! isset($Block['identified']))
|
213 |
-
{
|
214 |
-
$Blocks []= $CurrentBlock;
|
215 |
-
|
216 |
-
$Block['identified'] = true;
|
217 |
-
}
|
218 |
-
|
219 |
-
if ($this->isBlockContinuable($blockType))
|
220 |
-
{
|
221 |
-
$Block['continuable'] = true;
|
222 |
-
}
|
223 |
-
|
224 |
-
$CurrentBlock = $Block;
|
225 |
-
|
226 |
-
continue 2;
|
227 |
-
}
|
228 |
-
}
|
229 |
-
|
230 |
-
# ~
|
231 |
-
|
232 |
-
if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted']))
|
233 |
-
{
|
234 |
-
$CurrentBlock['element']['text'] .= "\n".$text;
|
235 |
-
}
|
236 |
-
else
|
237 |
-
{
|
238 |
-
$Blocks []= $CurrentBlock;
|
239 |
-
|
240 |
-
$CurrentBlock = $this->paragraph($Line);
|
241 |
-
|
242 |
-
$CurrentBlock['identified'] = true;
|
243 |
-
}
|
244 |
-
}
|
245 |
-
|
246 |
-
# ~
|
247 |
-
|
248 |
-
if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type']))
|
249 |
-
{
|
250 |
-
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
|
251 |
-
}
|
252 |
-
|
253 |
-
# ~
|
254 |
-
|
255 |
-
$Blocks []= $CurrentBlock;
|
256 |
-
|
257 |
-
unset($Blocks[0]);
|
258 |
-
|
259 |
-
# ~
|
260 |
-
|
261 |
-
$markup = '';
|
262 |
-
|
263 |
-
foreach ($Blocks as $Block)
|
264 |
-
{
|
265 |
-
if (isset($Block['hidden']))
|
266 |
-
{
|
267 |
-
continue;
|
268 |
-
}
|
269 |
-
|
270 |
-
$markup .= "\n";
|
271 |
-
$markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']);
|
272 |
-
}
|
273 |
-
|
274 |
-
$markup .= "\n";
|
275 |
-
|
276 |
-
# ~
|
277 |
-
|
278 |
-
return $markup;
|
279 |
-
}
|
280 |
-
|
281 |
-
protected function isBlockContinuable($Type)
|
282 |
-
{
|
283 |
-
return method_exists($this, 'block'.$Type.'Continue');
|
284 |
-
}
|
285 |
-
|
286 |
-
protected function isBlockCompletable($Type)
|
287 |
-
{
|
288 |
-
return method_exists($this, 'block'.$Type.'Complete');
|
289 |
-
}
|
290 |
-
|
291 |
-
#
|
292 |
-
# Code
|
293 |
-
|
294 |
-
protected function blockCode($Line, $Block = null)
|
295 |
-
{
|
296 |
-
if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted']))
|
297 |
-
{
|
298 |
-
return;
|
299 |
-
}
|
300 |
-
|
301 |
-
if ($Line['indent'] >= 4)
|
302 |
-
{
|
303 |
-
$text = substr($Line['body'], 4);
|
304 |
-
|
305 |
-
$Block = array(
|
306 |
-
'element' => array(
|
307 |
-
'name' => 'pre',
|
308 |
-
'handler' => 'element',
|
309 |
-
'text' => array(
|
310 |
-
'name' => 'code',
|
311 |
-
'text' => $text,
|
312 |
-
),
|
313 |
-
),
|
314 |
-
);
|
315 |
-
|
316 |
-
return $Block;
|
317 |
-
}
|
318 |
-
}
|
319 |
-
|
320 |
-
protected function blockCodeContinue($Line, $Block)
|
321 |
-
{
|
322 |
-
if ($Line['indent'] >= 4)
|
323 |
-
{
|
324 |
-
if (isset($Block['interrupted']))
|
325 |
-
{
|
326 |
-
$Block['element']['text']['text'] .= "\n";
|
327 |
-
|
328 |
-
unset($Block['interrupted']);
|
329 |
-
}
|
330 |
-
|
331 |
-
$Block['element']['text']['text'] .= "\n";
|
332 |
-
|
333 |
-
$text = substr($Line['body'], 4);
|
334 |
-
|
335 |
-
$Block['element']['text']['text'] .= $text;
|
336 |
-
|
337 |
-
return $Block;
|
338 |
-
}
|
339 |
-
}
|
340 |
-
|
341 |
-
protected function blockCodeComplete($Block)
|
342 |
-
{
|
343 |
-
$text = $Block['element']['text']['text'];
|
344 |
-
|
345 |
-
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
346 |
-
|
347 |
-
$Block['element']['text']['text'] = $text;
|
348 |
-
|
349 |
-
return $Block;
|
350 |
-
}
|
351 |
-
|
352 |
-
#
|
353 |
-
# Comment
|
354 |
-
|
355 |
-
protected function blockComment($Line)
|
356 |
-
{
|
357 |
-
if ($this->markupEscaped)
|
358 |
-
{
|
359 |
-
return;
|
360 |
-
}
|
361 |
-
|
362 |
-
if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!')
|
363 |
-
{
|
364 |
-
$Block = array(
|
365 |
-
'markup' => $Line['body'],
|
366 |
-
);
|
367 |
-
|
368 |
-
if (preg_match('/-->$/', $Line['text']))
|
369 |
-
{
|
370 |
-
$Block['closed'] = true;
|
371 |
-
}
|
372 |
-
|
373 |
-
return $Block;
|
374 |
-
}
|
375 |
-
}
|
376 |
-
|
377 |
-
protected function blockCommentContinue($Line, array $Block)
|
378 |
-
{
|
379 |
-
if (isset($Block['closed']))
|
380 |
-
{
|
381 |
-
return;
|
382 |
-
}
|
383 |
-
|
384 |
-
$Block['markup'] .= "\n" . $Line['body'];
|
385 |
-
|
386 |
-
if (preg_match('/-->$/', $Line['text']))
|
387 |
-
{
|
388 |
-
$Block['closed'] = true;
|
389 |
-
}
|
390 |
-
|
391 |
-
return $Block;
|
392 |
-
}
|
393 |
-
|
394 |
-
#
|
395 |
-
# Fenced Code
|
396 |
-
|
397 |
-
protected function blockFencedCode($Line)
|
398 |
-
{
|
399 |
-
if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches))
|
400 |
-
{
|
401 |
-
$Element = array(
|
402 |
-
'name' => 'code',
|
403 |
-
'text' => '',
|
404 |
-
);
|
405 |
-
|
406 |
-
if (isset($matches[1]))
|
407 |
-
{
|
408 |
-
$class = 'language-'.$matches[1];
|
409 |
-
|
410 |
-
$Element['attributes'] = array(
|
411 |
-
'class' => $class,
|
412 |
-
);
|
413 |
-
}
|
414 |
-
|
415 |
-
$Block = array(
|
416 |
-
'char' => $Line['text'][0],
|
417 |
-
'element' => array(
|
418 |
-
'name' => 'pre',
|
419 |
-
'handler' => 'element',
|
420 |
-
'text' => $Element,
|
421 |
-
),
|
422 |
-
);
|
423 |
-
|
424 |
-
return $Block;
|
425 |
-
}
|
426 |
-
}
|
427 |
-
|
428 |
-
protected function blockFencedCodeContinue($Line, $Block)
|
429 |
-
{
|
430 |
-
if (isset($Block['complete']))
|
431 |
-
{
|
432 |
-
return;
|
433 |
-
}
|
434 |
-
|
435 |
-
if (isset($Block['interrupted']))
|
436 |
-
{
|
437 |
-
$Block['element']['text']['text'] .= "\n";
|
438 |
-
|
439 |
-
unset($Block['interrupted']);
|
440 |
-
}
|
441 |
-
|
442 |
-
if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text']))
|
443 |
-
{
|
444 |
-
$Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1);
|
445 |
-
|
446 |
-
$Block['complete'] = true;
|
447 |
-
|
448 |
-
return $Block;
|
449 |
-
}
|
450 |
-
|
451 |
-
$Block['element']['text']['text'] .= "\n".$Line['body'];;
|
452 |
-
|
453 |
-
return $Block;
|
454 |
-
}
|
455 |
-
|
456 |
-
protected function blockFencedCodeComplete($Block)
|
457 |
-
{
|
458 |
-
$text = $Block['element']['text']['text'];
|
459 |
-
|
460 |
-
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
461 |
-
|
462 |
-
$Block['element']['text']['text'] = $text;
|
463 |
-
|
464 |
-
return $Block;
|
465 |
-
}
|
466 |
-
|
467 |
-
#
|
468 |
-
# Header
|
469 |
-
|
470 |
-
protected function blockHeader($Line)
|
471 |
-
{
|
472 |
-
if (isset($Line['text'][1]))
|
473 |
-
{
|
474 |
-
$level = 1;
|
475 |
-
|
476 |
-
while (isset($Line['text'][$level]) and $Line['text'][$level] === '#')
|
477 |
-
{
|
478 |
-
$level ++;
|
479 |
-
}
|
480 |
-
|
481 |
-
if ($level > 6)
|
482 |
-
{
|
483 |
-
return;
|
484 |
-
}
|
485 |
-
|
486 |
-
$text = trim($Line['text'], '# ');
|
487 |
-
|
488 |
-
$Block = array(
|
489 |
-
'element' => array(
|
490 |
-
'name' => 'h' . min(6, $level),
|
491 |
-
'text' => $text,
|
492 |
-
'handler' => 'line',
|
493 |
-
),
|
494 |
-
);
|
495 |
-
|
496 |
-
return $Block;
|
497 |
-
}
|
498 |
-
}
|
499 |
-
|
500 |
-
#
|
501 |
-
# List
|
502 |
-
|
503 |
-
protected function blockList($Line)
|
504 |
-
{
|
505 |
-
list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]');
|
506 |
-
|
507 |
-
if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches))
|
508 |
-
{
|
509 |
-
$Block = array(
|
510 |
-
'indent' => $Line['indent'],
|
511 |
-
'pattern' => $pattern,
|
512 |
-
'element' => array(
|
513 |
-
'name' => $name,
|
514 |
-
'handler' => 'elements',
|
515 |
-
),
|
516 |
-
);
|
517 |
-
|
518 |
-
$Block['li'] = array(
|
519 |
-
'name' => 'li',
|
520 |
-
'handler' => 'li',
|
521 |
-
'text' => array(
|
522 |
-
$matches[2],
|
523 |
-
),
|
524 |
-
);
|
525 |
-
|
526 |
-
$Block['element']['text'] []= & $Block['li'];
|
527 |
-
|
528 |
-
return $Block;
|
529 |
-
}
|
530 |
-
}
|
531 |
-
|
532 |
-
protected function blockListContinue($Line, array $Block)
|
533 |
-
{
|
534 |
-
if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches))
|
535 |
-
{
|
536 |
-
if (isset($Block['interrupted']))
|
537 |
-
{
|
538 |
-
$Block['li']['text'] []= '';
|
539 |
-
|
540 |
-
unset($Block['interrupted']);
|
541 |
-
}
|
542 |
-
|
543 |
-
unset($Block['li']);
|
544 |
-
|
545 |
-
$text = isset($matches[1]) ? $matches[1] : '';
|
546 |
-
|
547 |
-
$Block['li'] = array(
|
548 |
-
'name' => 'li',
|
549 |
-
'handler' => 'li',
|
550 |
-
'text' => array(
|
551 |
-
$text,
|
552 |
-
),
|
553 |
-
);
|
554 |
-
|
555 |
-
$Block['element']['text'] []= & $Block['li'];
|
556 |
-
|
557 |
-
return $Block;
|
558 |
-
}
|
559 |
-
|
560 |
-
if ($Line['text'][0] === '[' and $this->blockReference($Line))
|
561 |
-
{
|
562 |
-
return $Block;
|
563 |
-
}
|
564 |
-
|
565 |
-
if ( ! isset($Block['interrupted']))
|
566 |
-
{
|
567 |
-
$text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
|
568 |
-
|
569 |
-
$Block['li']['text'] []= $text;
|
570 |
-
|
571 |
-
return $Block;
|
572 |
-
}
|
573 |
-
|
574 |
-
if ($Line['indent'] > 0)
|
575 |
-
{
|
576 |
-
$Block['li']['text'] []= '';
|
577 |
-
|
578 |
-
$text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
|
579 |
-
|
580 |
-
$Block['li']['text'] []= $text;
|
581 |
-
|
582 |
-
unset($Block['interrupted']);
|
583 |
-
|
584 |
-
return $Block;
|
585 |
-
}
|
586 |
-
}
|
587 |
-
|
588 |
-
#
|
589 |
-
# Quote
|
590 |
-
|
591 |
-
protected function blockQuote($Line)
|
592 |
-
{
|
593 |
-
if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
|
594 |
-
{
|
595 |
-
$Block = array(
|
596 |
-
'element' => array(
|
597 |
-
'name' => 'blockquote',
|
598 |
-
'handler' => 'lines',
|
599 |
-
'text' => (array) $matches[1],
|
600 |
-
),
|
601 |
-
);
|
602 |
-
|
603 |
-
return $Block;
|
604 |
-
}
|
605 |
-
}
|
606 |
-
|
607 |
-
protected function blockQuoteContinue($Line, array $Block)
|
608 |
-
{
|
609 |
-
if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
|
610 |
-
{
|
611 |
-
if (isset($Block['interrupted']))
|
612 |
-
{
|
613 |
-
$Block['element']['text'] []= '';
|
614 |
-
|
615 |
-
unset($Block['interrupted']);
|
616 |
-
}
|
617 |
-
|
618 |
-
$Block['element']['text'] []= $matches[1];
|
619 |
-
|
620 |
-
return $Block;
|
621 |
-
}
|
622 |
-
|
623 |
-
if ( ! isset($Block['interrupted']))
|
624 |
-
{
|
625 |
-
$Block['element']['text'] []= $Line['text'];
|
626 |
-
|
627 |
-
return $Block;
|
628 |
-
}
|
629 |
-
}
|
630 |
-
|
631 |
-
#
|
632 |
-
# Rule
|
633 |
-
|
634 |
-
protected function blockRule($Line)
|
635 |
-
{
|
636 |
-
if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text']))
|
637 |
-
{
|
638 |
-
$Block = array(
|
639 |
-
'element' => array(
|
640 |
-
'name' => 'hr'
|
641 |
-
),
|
642 |
-
);
|
643 |
-
|
644 |
-
return $Block;
|
645 |
-
}
|
646 |
-
}
|
647 |
-
|
648 |
-
#
|
649 |
-
# Setext
|
650 |
-
|
651 |
-
protected function blockSetextHeader($Line, array $Block = null)
|
652 |
-
{
|
653 |
-
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
|
654 |
-
{
|
655 |
-
return;
|
656 |
-
}
|
657 |
-
|
658 |
-
if (chop($Line['text'], $Line['text'][0]) === '')
|
659 |
-
{
|
660 |
-
$Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
|
661 |
-
|
662 |
-
return $Block;
|
663 |
-
}
|
664 |
-
}
|
665 |
-
|
666 |
-
#
|
667 |
-
# Markup
|
668 |
-
|
669 |
-
protected function blockMarkup($Line)
|
670 |
-
{
|
671 |
-
if ($this->markupEscaped)
|
672 |
-
{
|
673 |
-
return;
|
674 |
-
}
|
675 |
-
|
676 |
-
if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
|
677 |
-
{
|
678 |
-
$element = strtolower($matches[1]);
|
679 |
-
|
680 |
-
if (in_array($element, $this->textLevelElements))
|
681 |
-
{
|
682 |
-
return;
|
683 |
-
}
|
684 |
-
|
685 |
-
$Block = array(
|
686 |
-
'name' => $matches[1],
|
687 |
-
'depth' => 0,
|
688 |
-
'markup' => $Line['text'],
|
689 |
-
);
|
690 |
-
|
691 |
-
$length = strlen($matches[0]);
|
692 |
-
|
693 |
-
$remainder = substr($Line['text'], $length);
|
694 |
-
|
695 |
-
if (trim($remainder) === '')
|
696 |
-
{
|
697 |
-
if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
|
698 |
-
{
|
699 |
-
$Block['closed'] = true;
|
700 |
-
|
701 |
-
$Block['void'] = true;
|
702 |
-
}
|
703 |
-
}
|
704 |
-
else
|
705 |
-
{
|
706 |
-
if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
|
707 |
-
{
|
708 |
-
return;
|
709 |
-
}
|
710 |
-
|
711 |
-
if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder))
|
712 |
-
{
|
713 |
-
$Block['closed'] = true;
|
714 |
-
}
|
715 |
-
}
|
716 |
-
|
717 |
-
return $Block;
|
718 |
-
}
|
719 |
-
}
|
720 |
-
|
721 |
-
protected function blockMarkupContinue($Line, array $Block)
|
722 |
-
{
|
723 |
-
if (isset($Block['closed']))
|
724 |
-
{
|
725 |
-
return;
|
726 |
-
}
|
727 |
-
|
728 |
-
if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open
|
729 |
-
{
|
730 |
-
$Block['depth'] ++;
|
731 |
-
}
|
732 |
-
|
733 |
-
if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close
|
734 |
-
{
|
735 |
-
if ($Block['depth'] > 0)
|
736 |
-
{
|
737 |
-
$Block['depth'] --;
|
738 |
-
}
|
739 |
-
else
|
740 |
-
{
|
741 |
-
$Block['closed'] = true;
|
742 |
-
}
|
743 |
-
}
|
744 |
-
|
745 |
-
if (isset($Block['interrupted']))
|
746 |
-
{
|
747 |
-
$Block['markup'] .= "\n";
|
748 |
-
|
749 |
-
unset($Block['interrupted']);
|
750 |
-
}
|
751 |
-
|
752 |
-
$Block['markup'] .= "\n".$Line['body'];
|
753 |
-
|
754 |
-
return $Block;
|
755 |
-
}
|
756 |
-
|
757 |
-
#
|
758 |
-
# Reference
|
759 |
-
|
760 |
-
protected function blockReference($Line)
|
761 |
-
{
|
762 |
-
if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
|
763 |
-
{
|
764 |
-
$id = strtolower($matches[1]);
|
765 |
-
|
766 |
-
$Data = array(
|
767 |
-
'url' => $matches[2],
|
768 |
-
'title' => null,
|
769 |
-
);
|
770 |
-
|
771 |
-
if (isset($matches[3]))
|
772 |
-
{
|
773 |
-
$Data['title'] = $matches[3];
|
774 |
-
}
|
775 |
-
|
776 |
-
$this->DefinitionData['Reference'][$id] = $Data;
|
777 |
-
|
778 |
-
$Block = array(
|
779 |
-
'hidden' => true,
|
780 |
-
);
|
781 |
-
|
782 |
-
return $Block;
|
783 |
-
}
|
784 |
-
}
|
785 |
-
|
786 |
-
#
|
787 |
-
# Table
|
788 |
-
|
789 |
-
protected function blockTable($Line, array $Block = null)
|
790 |
-
{
|
791 |
-
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
|
792 |
-
{
|
793 |
-
return;
|
794 |
-
}
|
795 |
-
|
796 |
-
if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '')
|
797 |
-
{
|
798 |
-
$alignments = array();
|
799 |
-
|
800 |
-
$divider = $Line['text'];
|
801 |
-
|
802 |
-
$divider = trim($divider);
|
803 |
-
$divider = trim($divider, '|');
|
804 |
-
|
805 |
-
$dividerCells = explode('|', $divider);
|
806 |
-
|
807 |
-
foreach ($dividerCells as $dividerCell)
|
808 |
-
{
|
809 |
-
$dividerCell = trim($dividerCell);
|
810 |
-
|
811 |
-
if ($dividerCell === '')
|
812 |
-
{
|
813 |
-
continue;
|
814 |
-
}
|
815 |
-
|
816 |
-
$alignment = null;
|
817 |
-
|
818 |
-
if ($dividerCell[0] === ':')
|
819 |
-
{
|
820 |
-
$alignment = 'left';
|
821 |
-
}
|
822 |
-
|
823 |
-
if (substr($dividerCell, - 1) === ':')
|
824 |
-
{
|
825 |
-
$alignment = $alignment === 'left' ? 'center' : 'right';
|
826 |
-
}
|
827 |
-
|
828 |
-
$alignments []= $alignment;
|
829 |
-
}
|
830 |
-
|
831 |
-
# ~
|
832 |
-
|
833 |
-
$HeaderElements = array();
|
834 |
-
|
835 |
-
$header = $Block['element']['text'];
|
836 |
-
|
837 |
-
$header = trim($header);
|
838 |
-
$header = trim($header, '|');
|
839 |
-
|
840 |
-
$headerCells = explode('|', $header);
|
841 |
-
|
842 |
-
foreach ($headerCells as $index => $headerCell)
|
843 |
-
{
|
844 |
-
$headerCell = trim($headerCell);
|
845 |
-
|
846 |
-
$HeaderElement = array(
|
847 |
-
'name' => 'th',
|
848 |
-
'text' => $headerCell,
|
849 |
-
'handler' => 'line',
|
850 |
-
);
|
851 |
-
|
852 |
-
if (isset($alignments[$index]))
|
853 |
-
{
|
854 |
-
$alignment = $alignments[$index];
|
855 |
-
|
856 |
-
$HeaderElement['attributes'] = array(
|
857 |
-
'style' => 'text-align: '.$alignment.';',
|
858 |
-
);
|
859 |
-
}
|
860 |
-
|
861 |
-
$HeaderElements []= $HeaderElement;
|
862 |
-
}
|
863 |
-
|
864 |
-
# ~
|
865 |
-
|
866 |
-
$Block = array(
|
867 |
-
'alignments' => $alignments,
|
868 |
-
'identified' => true,
|
869 |
-
'element' => array(
|
870 |
-
'name' => 'table',
|
871 |
-
'handler' => 'elements',
|
872 |
-
),
|
873 |
-
);
|
874 |
-
|
875 |
-
$Block['element']['text'] []= array(
|
876 |
-
'name' => 'thead',
|
877 |
-
'handler' => 'elements',
|
878 |
-
);
|
879 |
-
|
880 |
-
$Block['element']['text'] []= array(
|
881 |
-
'name' => 'tbody',
|
882 |
-
'handler' => 'elements',
|
883 |
-
'text' => array(),
|
884 |
-
);
|
885 |
-
|
886 |
-
$Block['element']['text'][0]['text'] []= array(
|
887 |
-
'name' => 'tr',
|
888 |
-
'handler' => 'elements',
|
889 |
-
'text' => $HeaderElements,
|
890 |
-
);
|
891 |
-
|
892 |
-
return $Block;
|
893 |
-
}
|
894 |
-
}
|
895 |
-
|
896 |
-
protected function blockTableContinue($Line, array $Block)
|
897 |
-
{
|
898 |
-
if (isset($Block['interrupted']))
|
899 |
-
{
|
900 |
-
return;
|
901 |
-
}
|
902 |
-
|
903 |
-
if ($Line['text'][0] === '|' or strpos($Line['text'], '|'))
|
904 |
-
{
|
905 |
-
$Elements = array();
|
906 |
-
|
907 |
-
$row = $Line['text'];
|
908 |
-
|
909 |
-
$row = trim($row);
|
910 |
-
$row = trim($row, '|');
|
911 |
-
|
912 |
-
preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches);
|
913 |
-
|
914 |
-
foreach ($matches[0] as $index => $cell)
|
915 |
-
{
|
916 |
-
$cell = trim($cell);
|
917 |
-
|
918 |
-
$Element = array(
|
919 |
-
'name' => 'td',
|
920 |
-
'handler' => 'line',
|
921 |
-
'text' => $cell,
|
922 |
-
);
|
923 |
-
|
924 |
-
if (isset($Block['alignments'][$index]))
|
925 |
-
{
|
926 |
-
$Element['attributes'] = array(
|
927 |
-
'style' => 'text-align: '.$Block['alignments'][$index].';',
|
928 |
-
);
|
929 |
-
}
|
930 |
-
|
931 |
-
$Elements []= $Element;
|
932 |
-
}
|
933 |
-
|
934 |
-
$Element = array(
|
935 |
-
'name' => 'tr',
|
936 |
-
'handler' => 'elements',
|
937 |
-
'text' => $Elements,
|
938 |
-
);
|
939 |
-
|
940 |
-
$Block['element']['text'][1]['text'] []= $Element;
|
941 |
-
|
942 |
-
return $Block;
|
943 |
-
}
|
944 |
-
}
|
945 |
-
|
946 |
-
#
|
947 |
-
# ~
|
948 |
-
#
|
949 |
-
|
950 |
-
protected function paragraph($Line)
|
951 |
-
{
|
952 |
-
$Block = array(
|
953 |
-
'element' => array(
|
954 |
-
'name' => 'p',
|
955 |
-
'text' => $Line['text'],
|
956 |
-
'handler' => 'line',
|
957 |
-
),
|
958 |
-
);
|
959 |
-
|
960 |
-
return $Block;
|
961 |
-
}
|
962 |
-
|
963 |
-
#
|
964 |
-
# Inline Elements
|
965 |
-
#
|
966 |
-
|
967 |
-
protected $InlineTypes = array(
|
968 |
-
'"' => array('SpecialCharacter'),
|
969 |
-
'!' => array('Image'),
|
970 |
-
'&' => array('SpecialCharacter'),
|
971 |
-
'*' => array('Emphasis'),
|
972 |
-
':' => array('Url'),
|
973 |
-
'<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'),
|
974 |
-
'>' => array('SpecialCharacter'),
|
975 |
-
'[' => array('Link'),
|
976 |
-
'_' => array('Emphasis'),
|
977 |
-
'`' => array('Code'),
|
978 |
-
'~' => array('Strikethrough'),
|
979 |
-
'\\' => array('EscapeSequence'),
|
980 |
-
);
|
981 |
-
|
982 |
-
# ~
|
983 |
-
|
984 |
-
protected $inlineMarkerList = '!"*_&[:<>`~\\';
|
985 |
-
|
986 |
-
#
|
987 |
-
# ~
|
988 |
-
#
|
989 |
-
|
990 |
-
public function line($text)
|
991 |
-
{
|
992 |
-
$markup = '';
|
993 |
-
|
994 |
-
# $excerpt is based on the first occurrence of a marker
|
995 |
-
|
996 |
-
while ($excerpt = strpbrk($text, $this->inlineMarkerList))
|
997 |
-
{
|
998 |
-
$marker = $excerpt[0];
|
999 |
-
|
1000 |
-
$markerPosition = strpos($text, $marker);
|
1001 |
-
|
1002 |
-
$Excerpt = array('text' => $excerpt, 'context' => $text);
|
1003 |
-
|
1004 |
-
foreach ($this->InlineTypes[$marker] as $inlineType)
|
1005 |
-
{
|
1006 |
-
$Inline = $this->{'inline'.$inlineType}($Excerpt);
|
1007 |
-
|
1008 |
-
if ( ! isset($Inline))
|
1009 |
-
{
|
1010 |
-
continue;
|
1011 |
-
}
|
1012 |
-
|
1013 |
-
# makes sure that the inline belongs to "our" marker
|
1014 |
-
|
1015 |
-
if (isset($Inline['position']) and $Inline['position'] > $markerPosition)
|
1016 |
-
{
|
1017 |
-
continue;
|
1018 |
-
}
|
1019 |
-
|
1020 |
-
# sets a default inline position
|
1021 |
-
|
1022 |
-
if ( ! isset($Inline['position']))
|
1023 |
-
{
|
1024 |
-
$Inline['position'] = $markerPosition;
|
1025 |
-
}
|
1026 |
-
|
1027 |
-
# the text that comes before the inline
|
1028 |
-
$unmarkedText = substr($text, 0, $Inline['position']);
|
1029 |
-
|
1030 |
-
# compile the unmarked text
|
1031 |
-
$markup .= $this->unmarkedText($unmarkedText);
|
1032 |
-
|
1033 |
-
# compile the inline
|
1034 |
-
$markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']);
|
1035 |
-
|
1036 |
-
# remove the examined text
|
1037 |
-
$text = substr($text, $Inline['position'] + $Inline['extent']);
|
1038 |
-
|
1039 |
-
continue 2;
|
1040 |
-
}
|
1041 |
-
|
1042 |
-
# the marker does not belong to an inline
|
1043 |
-
|
1044 |
-
$unmarkedText = substr($text, 0, $markerPosition + 1);
|
1045 |
-
|
1046 |
-
$markup .= $this->unmarkedText($unmarkedText);
|
1047 |
-
|
1048 |
-
$text = substr($text, $markerPosition + 1);
|
1049 |
-
}
|
1050 |
-
|
1051 |
-
$markup .= $this->unmarkedText($text);
|
1052 |
-
|
1053 |
-
return $markup;
|
1054 |
-
}
|
1055 |
-
|
1056 |
-
#
|
1057 |
-
# ~
|
1058 |
-
#
|
1059 |
-
|
1060 |
-
protected function inlineCode($Excerpt)
|
1061 |
-
{
|
1062 |
-
$marker = $Excerpt['text'][0];
|
1063 |
-
|
1064 |
-
if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
|
1065 |
-
{
|
1066 |
-
$text = $matches[2];
|
1067 |
-
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
1068 |
-
$text = preg_replace("/[ ]*\n/", ' ', $text);
|
1069 |
-
|
1070 |
-
return array(
|
1071 |
-
'extent' => strlen($matches[0]),
|
1072 |
-
'element' => array(
|
1073 |
-
'name' => 'code',
|
1074 |
-
'text' => $text,
|
1075 |
-
),
|
1076 |
-
);
|
1077 |
-
}
|
1078 |
-
}
|
1079 |
-
|
1080 |
-
protected function inlineEmailTag($Excerpt)
|
1081 |
-
{
|
1082 |
-
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches))
|
1083 |
-
{
|
1084 |
-
$url = $matches[1];
|
1085 |
-
|
1086 |
-
if ( ! isset($matches[2]))
|
1087 |
-
{
|
1088 |
-
$url = 'mailto:' . $url;
|
1089 |
-
}
|
1090 |
-
|
1091 |
-
return array(
|
1092 |
-
'extent' => strlen($matches[0]),
|
1093 |
-
'element' => array(
|
1094 |
-
'name' => 'a',
|
1095 |
-
'text' => $matches[1],
|
1096 |
-
'attributes' => array(
|
1097 |
-
'href' => $url,
|
1098 |
-
),
|
1099 |
-
),
|
1100 |
-
);
|
1101 |
-
}
|
1102 |
-
}
|
1103 |
-
|
1104 |
-
protected function inlineEmphasis($Excerpt)
|
1105 |
-
{
|
1106 |
-
if ( ! isset($Excerpt['text'][1]))
|
1107 |
-
{
|
1108 |
-
return;
|
1109 |
-
}
|
1110 |
-
|
1111 |
-
$marker = $Excerpt['text'][0];
|
1112 |
-
|
1113 |
-
if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
|
1114 |
-
{
|
1115 |
-
$emphasis = 'strong';
|
1116 |
-
}
|
1117 |
-
elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
|
1118 |
-
{
|
1119 |
-
$emphasis = 'em';
|
1120 |
-
}
|
1121 |
-
else
|
1122 |
-
{
|
1123 |
-
return;
|
1124 |
-
}
|
1125 |
-
|
1126 |
-
return array(
|
1127 |
-
'extent' => strlen($matches[0]),
|
1128 |
-
'element' => array(
|
1129 |
-
'name' => $emphasis,
|
1130 |
-
'handler' => 'line',
|
1131 |
-
'text' => $matches[1],
|
1132 |
-
),
|
1133 |
-
);
|
1134 |
-
}
|
1135 |
-
|
1136 |
-
protected function inlineEscapeSequence($Excerpt)
|
1137 |
-
{
|
1138 |
-
if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
|
1139 |
-
{
|
1140 |
-
return array(
|
1141 |
-
'markup' => $Excerpt['text'][1],
|
1142 |
-
'extent' => 2,
|
1143 |
-
);
|
1144 |
-
}
|
1145 |
-
}
|
1146 |
-
|
1147 |
-
protected function inlineImage($Excerpt)
|
1148 |
-
{
|
1149 |
-
if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[')
|
1150 |
-
{
|
1151 |
-
return;
|
1152 |
-
}
|
1153 |
-
|
1154 |
-
$Excerpt['text']= substr($Excerpt['text'], 1);
|
1155 |
-
|
1156 |
-
$Link = $this->inlineLink($Excerpt);
|
1157 |
-
|
1158 |
-
if ($Link === null)
|
1159 |
-
{
|
1160 |
-
return;
|
1161 |
-
}
|
1162 |
-
|
1163 |
-
$Inline = array(
|
1164 |
-
'extent' => $Link['extent'] + 1,
|
1165 |
-
'element' => array(
|
1166 |
-
'name' => 'img',
|
1167 |
-
'attributes' => array(
|
1168 |
-
'src' => $Link['element']['attributes']['href'],
|
1169 |
-
'alt' => $Link['element']['text'],
|
1170 |
-
),
|
1171 |
-
),
|
1172 |
-
);
|
1173 |
-
|
1174 |
-
$Inline['element']['attributes'] += $Link['element']['attributes'];
|
1175 |
-
|
1176 |
-
unset($Inline['element']['attributes']['href']);
|
1177 |
-
|
1178 |
-
return $Inline;
|
1179 |
-
}
|
1180 |
-
|
1181 |
-
protected function inlineLink($Excerpt)
|
1182 |
-
{
|
1183 |
-
$Element = array(
|
1184 |
-
'name' => 'a',
|
1185 |
-
'handler' => 'line',
|
1186 |
-
'text' => null,
|
1187 |
-
'attributes' => array(
|
1188 |
-
'href' => null,
|
1189 |
-
'title' => null,
|
1190 |
-
),
|
1191 |
-
);
|
1192 |
-
|
1193 |
-
$extent = 0;
|
1194 |
-
|
1195 |
-
$remainder = $Excerpt['text'];
|
1196 |
-
|
1197 |
-
if (preg_match('/\[((?:[^][]|(?R))*)\]/', $remainder, $matches))
|
1198 |
-
{
|
1199 |
-
$Element['text'] = $matches[1];
|
1200 |
-
|
1201 |
-
$extent += strlen($matches[0]);
|
1202 |
-
|
1203 |
-
$remainder = substr($remainder, $extent);
|
1204 |
-
}
|
1205 |
-
else
|
1206 |
-
{
|
1207 |
-
return;
|
1208 |
-
}
|
1209 |
-
|
1210 |
-
if (preg_match('/^[(]((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|\'[^\']*\'))?[)]/', $remainder, $matches))
|
1211 |
-
{
|
1212 |
-
$Element['attributes']['href'] = $matches[1];
|
1213 |
-
|
1214 |
-
if (isset($matches[2]))
|
1215 |
-
{
|
1216 |
-
$Element['attributes']['title'] = substr($matches[2], 1, - 1);
|
1217 |
-
}
|
1218 |
-
|
1219 |
-
$extent += strlen($matches[0]);
|
1220 |
-
}
|
1221 |
-
else
|
1222 |
-
{
|
1223 |
-
if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
|
1224 |
-
{
|
1225 |
-
$definition = strlen($matches[1]) ? $matches[1] : $Element['text'];
|
1226 |
-
$definition = strtolower($definition);
|
1227 |
-
|
1228 |
-
$extent += strlen($matches[0]);
|
1229 |
-
}
|
1230 |
-
else
|
1231 |
-
{
|
1232 |
-
$definition = strtolower($Element['text']);
|
1233 |
-
}
|
1234 |
-
|
1235 |
-
if ( ! isset($this->DefinitionData['Reference'][$definition]))
|
1236 |
-
{
|
1237 |
-
return;
|
1238 |
-
}
|
1239 |
-
|
1240 |
-
$Definition = $this->DefinitionData['Reference'][$definition];
|
1241 |
-
|
1242 |
-
$Element['attributes']['href'] = $Definition['url'];
|
1243 |
-
$Element['attributes']['title'] = $Definition['title'];
|
1244 |
-
}
|
1245 |
-
|
1246 |
-
$Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']);
|
1247 |
-
|
1248 |
-
return array(
|
1249 |
-
'extent' => $extent,
|
1250 |
-
'element' => $Element,
|
1251 |
-
);
|
1252 |
-
}
|
1253 |
-
|
1254 |
-
protected function inlineMarkup($Excerpt)
|
1255 |
-
{
|
1256 |
-
if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false)
|
1257 |
-
{
|
1258 |
-
return;
|
1259 |
-
}
|
1260 |
-
|
1261 |
-
if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches))
|
1262 |
-
{
|
1263 |
-
return array(
|
1264 |
-
'markup' => $matches[0],
|
1265 |
-
'extent' => strlen($matches[0]),
|
1266 |
-
);
|
1267 |
-
}
|
1268 |
-
|
1269 |
-
if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?[^-])*-->/s', $Excerpt['text'], $matches))
|
1270 |
-
{
|
1271 |
-
return array(
|
1272 |
-
'markup' => $matches[0],
|
1273 |
-
'extent' => strlen($matches[0]),
|
1274 |
-
);
|
1275 |
-
}
|
1276 |
-
|
1277 |
-
if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches))
|
1278 |
-
{
|
1279 |
-
return array(
|
1280 |
-
'markup' => $matches[0],
|
1281 |
-
'extent' => strlen($matches[0]),
|
1282 |
-
);
|
1283 |
-
}
|
1284 |
-
}
|
1285 |
-
|
1286 |
-
protected function inlineSpecialCharacter($Excerpt)
|
1287 |
-
{
|
1288 |
-
if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text']))
|
1289 |
-
{
|
1290 |
-
return array(
|
1291 |
-
'markup' => '&',
|
1292 |
-
'extent' => 1,
|
1293 |
-
);
|
1294 |
-
}
|
1295 |
-
|
1296 |
-
$SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot');
|
1297 |
-
|
1298 |
-
if (isset($SpecialCharacter[$Excerpt['text'][0]]))
|
1299 |
-
{
|
1300 |
-
return array(
|
1301 |
-
'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';',
|
1302 |
-
'extent' => 1,
|
1303 |
-
);
|
1304 |
-
}
|
1305 |
-
}
|
1306 |
-
|
1307 |
-
protected function inlineStrikethrough($Excerpt)
|
1308 |
-
{
|
1309 |
-
if ( ! isset($Excerpt['text'][1]))
|
1310 |
-
{
|
1311 |
-
return;
|
1312 |
-
}
|
1313 |
-
|
1314 |
-
if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches))
|
1315 |
-
{
|
1316 |
-
return array(
|
1317 |
-
'extent' => strlen($matches[0]),
|
1318 |
-
'element' => array(
|
1319 |
-
'name' => 'del',
|
1320 |
-
'text' => $matches[1],
|
1321 |
-
'handler' => 'line',
|
1322 |
-
),
|
1323 |
-
);
|
1324 |
-
}
|
1325 |
-
}
|
1326 |
-
|
1327 |
-
protected function inlineUrl($Excerpt)
|
1328 |
-
{
|
1329 |
-
if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/')
|
1330 |
-
{
|
1331 |
-
return;
|
1332 |
-
}
|
1333 |
-
|
1334 |
-
if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
|
1335 |
-
{
|
1336 |
-
$Inline = array(
|
1337 |
-
'extent' => strlen($matches[0][0]),
|
1338 |
-
'position' => $matches[0][1],
|
1339 |
-
'element' => array(
|
1340 |
-
'name' => 'a',
|
1341 |
-
'text' => $matches[0][0],
|
1342 |
-
'attributes' => array(
|
1343 |
-
'href' => $matches[0][0],
|
1344 |
-
),
|
1345 |
-
),
|
1346 |
-
);
|
1347 |
-
|
1348 |
-
return $Inline;
|
1349 |
-
}
|
1350 |
-
}
|
1351 |
-
|
1352 |
-
protected function inlineUrlTag($Excerpt)
|
1353 |
-
{
|
1354 |
-
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
|
1355 |
-
{
|
1356 |
-
$url = str_replace(array('&', '<'), array('&', '<'), $matches[1]);
|
1357 |
-
|
1358 |
-
return array(
|
1359 |
-
'extent' => strlen($matches[0]),
|
1360 |
-
'element' => array(
|
1361 |
-
'name' => 'a',
|
1362 |
-
'text' => $url,
|
1363 |
-
'attributes' => array(
|
1364 |
-
'href' => $url,
|
1365 |
-
),
|
1366 |
-
),
|
1367 |
-
);
|
1368 |
-
}
|
1369 |
-
}
|
1370 |
-
|
1371 |
-
# ~
|
1372 |
-
|
1373 |
-
protected function unmarkedText($text)
|
1374 |
-
{
|
1375 |
-
if ($this->breaksEnabled)
|
1376 |
-
{
|
1377 |
-
$text = preg_replace('/[ ]*\n/', "<br />\n", $text);
|
1378 |
-
}
|
1379 |
-
else
|
1380 |
-
{
|
1381 |
-
$text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "<br />\n", $text);
|
1382 |
-
$text = str_replace(" \n", "\n", $text);
|
1383 |
-
}
|
1384 |
-
|
1385 |
-
return $text;
|
1386 |
-
}
|
1387 |
-
|
1388 |
-
#
|
1389 |
-
# Handlers
|
1390 |
-
#
|
1391 |
-
|
1392 |
-
protected function element(array $Element)
|
1393 |
-
{
|
1394 |
-
$markup = '<'.$Element['name'];
|
1395 |
-
|
1396 |
-
if (isset($Element['attributes']))
|
1397 |
-
{
|
1398 |
-
foreach ($Element['attributes'] as $name => $value)
|
1399 |
-
{
|
1400 |
-
if ($value === null)
|
1401 |
-
{
|
1402 |
-
continue;
|
1403 |
-
}
|
1404 |
-
|
1405 |
-
$markup .= ' '.$name.'="'.$value.'"';
|
1406 |
-
}
|
1407 |
-
}
|
1408 |
-
|
1409 |
-
if (isset($Element['text']))
|
1410 |
-
{
|
1411 |
-
$markup .= '>';
|
1412 |
-
|
1413 |
-
if (isset($Element['handler']))
|
1414 |
-
{
|
1415 |
-
$markup .= $this->{$Element['handler']}($Element['text']);
|
1416 |
-
}
|
1417 |
-
else
|
1418 |
-
{
|
1419 |
-
$markup .= $Element['text'];
|
1420 |
-
}
|
1421 |
-
|
1422 |
-
$markup .= '</'.$Element['name'].'>';
|
1423 |
-
}
|
1424 |
-
else
|
1425 |
-
{
|
1426 |
-
$markup .= ' />';
|
1427 |
-
}
|
1428 |
-
|
1429 |
-
return $markup;
|
1430 |
-
}
|
1431 |
-
|
1432 |
-
protected function elements(array $Elements)
|
1433 |
-
{
|
1434 |
-
$markup = '';
|
1435 |
-
|
1436 |
-
foreach ($Elements as $Element)
|
1437 |
-
{
|
1438 |
-
$markup .= "\n" . $this->element($Element);
|
1439 |
-
}
|
1440 |
-
|
1441 |
-
$markup .= "\n";
|
1442 |
-
|
1443 |
-
return $markup;
|
1444 |
-
}
|
1445 |
-
|
1446 |
-
# ~
|
1447 |
-
|
1448 |
-
protected function li($lines)
|
1449 |
-
{
|
1450 |
-
$markup = $this->lines($lines);
|
1451 |
-
|
1452 |
-
$trimmedMarkup = trim($markup);
|
1453 |
-
|
1454 |
-
if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '<p>')
|
1455 |
-
{
|
1456 |
-
$markup = $trimmedMarkup;
|
1457 |
-
$markup = substr($markup, 3);
|
1458 |
-
|
1459 |
-
$position = strpos($markup, "</p>");
|
1460 |
-
|
1461 |
-
$markup = substr_replace($markup, '', $position, 4);
|
1462 |
-
}
|
1463 |
-
|
1464 |
-
return $markup;
|
1465 |
-
}
|
1466 |
-
|
1467 |
-
#
|
1468 |
-
# Deprecated Methods
|
1469 |
-
#
|
1470 |
-
|
1471 |
-
function parse($text)
|
1472 |
-
{
|
1473 |
-
$markup = $this->text($text);
|
1474 |
-
|
1475 |
-
return $markup;
|
1476 |
-
}
|
1477 |
-
|
1478 |
-
#
|
1479 |
-
# Static Methods
|
1480 |
-
#
|
1481 |
-
|
1482 |
-
static function instance($name = 'default')
|
1483 |
-
{
|
1484 |
-
if (isset(self::$instances[$name]))
|
1485 |
-
{
|
1486 |
-
return self::$instances[$name];
|
1487 |
-
}
|
1488 |
-
|
1489 |
-
$instance = new static();
|
1490 |
-
|
1491 |
-
self::$instances[$name] = $instance;
|
1492 |
-
|
1493 |
-
return $instance;
|
1494 |
-
}
|
1495 |
-
|
1496 |
-
private static $instances = array();
|
1497 |
-
|
1498 |
-
#
|
1499 |
-
# Fields
|
1500 |
-
#
|
1501 |
-
|
1502 |
-
protected $DefinitionData;
|
1503 |
-
|
1504 |
-
#
|
1505 |
-
# Read-Only
|
1506 |
-
|
1507 |
-
protected $specialCharacters = array(
|
1508 |
-
'\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|',
|
1509 |
-
);
|
1510 |
-
|
1511 |
-
protected $StrongRegex = array(
|
1512 |
-
'*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
|
1513 |
-
'_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us',
|
1514 |
-
);
|
1515 |
-
|
1516 |
-
protected $EmRegex = array(
|
1517 |
-
'*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
|
1518 |
-
'_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
|
1519 |
-
);
|
1520 |
-
|
1521 |
-
protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?';
|
1522 |
-
|
1523 |
-
protected $voidElements = array(
|
1524 |
-
'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source',
|
1525 |
-
);
|
1526 |
-
|
1527 |
-
protected $textLevelElements = array(
|
1528 |
-
'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont',
|
1529 |
-
'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
|
1530 |
-
'i', 'rp', 'del', 'code', 'strike', 'marquee',
|
1531 |
-
'q', 'rt', 'ins', 'font', 'strong',
|
1532 |
-
's', 'tt', 'sub', 'mark',
|
1533 |
-
'u', 'xm', 'sup', 'nobr',
|
1534 |
-
'var', 'ruby',
|
1535 |
-
'wbr', 'span',
|
1536 |
-
'time',
|
1537 |
-
);
|
1538 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/plugin-update-checker/vendor/ParsedownLegacy.php
DELETED
@@ -1,1535 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
#
|
4 |
-
#
|
5 |
-
# Parsedown
|
6 |
-
# http://parsedown.org
|
7 |
-
#
|
8 |
-
# (c) Emanuil Rusev
|
9 |
-
# http://erusev.com
|
10 |
-
#
|
11 |
-
# For the full license information, view the LICENSE file that was distributed
|
12 |
-
# with this source code.
|
13 |
-
#
|
14 |
-
#
|
15 |
-
|
16 |
-
class Parsedown
|
17 |
-
{
|
18 |
-
# ~
|
19 |
-
|
20 |
-
const version = '1.5.0';
|
21 |
-
|
22 |
-
# ~
|
23 |
-
|
24 |
-
function text($text)
|
25 |
-
{
|
26 |
-
# make sure no definitions are set
|
27 |
-
$this->DefinitionData = array();
|
28 |
-
|
29 |
-
# standardize line breaks
|
30 |
-
$text = str_replace(array("\r\n", "\r"), "\n", $text);
|
31 |
-
|
32 |
-
# remove surrounding line breaks
|
33 |
-
$text = trim($text, "\n");
|
34 |
-
|
35 |
-
# split text into lines
|
36 |
-
$lines = explode("\n", $text);
|
37 |
-
|
38 |
-
# iterate through lines to identify blocks
|
39 |
-
$markup = $this->lines($lines);
|
40 |
-
|
41 |
-
# trim line breaks
|
42 |
-
$markup = trim($markup, "\n");
|
43 |
-
|
44 |
-
return $markup;
|
45 |
-
}
|
46 |
-
|
47 |
-
#
|
48 |
-
# Setters
|
49 |
-
#
|
50 |
-
|
51 |
-
function setBreaksEnabled($breaksEnabled)
|
52 |
-
{
|
53 |
-
$this->breaksEnabled = $breaksEnabled;
|
54 |
-
|
55 |
-
return $this;
|
56 |
-
}
|
57 |
-
|
58 |
-
protected $breaksEnabled;
|
59 |
-
|
60 |
-
function setMarkupEscaped($markupEscaped)
|
61 |
-
{
|
62 |
-
$this->markupEscaped = $markupEscaped;
|
63 |
-
|
64 |
-
return $this;
|
65 |
-
}
|
66 |
-
|
67 |
-
protected $markupEscaped;
|
68 |
-
|
69 |
-
function setUrlsLinked($urlsLinked)
|
70 |
-
{
|
71 |
-
$this->urlsLinked = $urlsLinked;
|
72 |
-
|
73 |
-
return $this;
|
74 |
-
}
|
75 |
-
|
76 |
-
protected $urlsLinked = true;
|
77 |
-
|
78 |
-
#
|
79 |
-
# Lines
|
80 |
-
#
|
81 |
-
|
82 |
-
protected $BlockTypes = array(
|
83 |
-
'#' => array('Header'),
|
84 |
-
'*' => array('Rule', 'List'),
|
85 |
-
'+' => array('List'),
|
86 |
-
'-' => array('SetextHeader', 'Table', 'Rule', 'List'),
|
87 |
-
'0' => array('List'),
|
88 |
-
'1' => array('List'),
|
89 |
-
'2' => array('List'),
|
90 |
-
'3' => array('List'),
|
91 |
-
'4' => array('List'),
|
92 |
-
'5' => array('List'),
|
93 |
-
'6' => array('List'),
|
94 |
-
'7' => array('List'),
|
95 |
-
'8' => array('List'),
|
96 |
-
'9' => array('List'),
|
97 |
-
':' => array('Table'),
|
98 |
-
'<' => array('Comment', 'Markup'),
|
99 |
-
'=' => array('SetextHeader'),
|
100 |
-
'>' => array('Quote'),
|
101 |
-
'[' => array('Reference'),
|
102 |
-
'_' => array('Rule'),
|
103 |
-
'`' => array('FencedCode'),
|
104 |
-
'|' => array('Table'),
|
105 |
-
'~' => array('FencedCode'),
|
106 |
-
);
|
107 |
-
|
108 |
-
# ~
|
109 |
-
|
110 |
-
protected $DefinitionTypes = array(
|
111 |
-
'[' => array('Reference'),
|
112 |
-
);
|
113 |
-
|
114 |
-
# ~
|
115 |
-
|
116 |
-
protected $unmarkedBlockTypes = array(
|
117 |
-
'Code',
|
118 |
-
);
|
119 |
-
|
120 |
-
#
|
121 |
-
# Blocks
|
122 |
-
#
|
123 |
-
|
124 |
-
private function lines(array $lines)
|
125 |
-
{
|
126 |
-
$CurrentBlock = null;
|
127 |
-
|
128 |
-
foreach ($lines as $line)
|
129 |
-
{
|
130 |
-
if (chop($line) === '')
|
131 |
-
{
|
132 |
-
if (isset($CurrentBlock))
|
133 |
-
{
|
134 |
-
$CurrentBlock['interrupted'] = true;
|
135 |
-
}
|
136 |
-
|
137 |
-
continue;
|
138 |
-
}
|
139 |
-
|
140 |
-
if (strpos($line, "\t") !== false)
|
141 |
-
{
|
142 |
-
$parts = explode("\t", $line);
|
143 |
-
|
144 |
-
$line = $parts[0];
|
145 |
-
|
146 |
-
unset($parts[0]);
|
147 |
-
|
148 |
-
foreach ($parts as $part)
|
149 |
-
{
|
150 |
-
$shortage = 4 - mb_strlen($line, 'utf-8') % 4;
|
151 |
-
|
152 |
-
$line .= str_repeat(' ', $shortage);
|
153 |
-
$line .= $part;
|
154 |
-
}
|
155 |
-
}
|
156 |
-
|
157 |
-
$indent = 0;
|
158 |
-
|
159 |
-
while (isset($line[$indent]) and $line[$indent] === ' ')
|
160 |
-
{
|
161 |
-
$indent ++;
|
162 |
-
}
|
163 |
-
|
164 |
-
$text = $indent > 0 ? substr($line, $indent) : $line;
|
165 |
-
|
166 |
-
# ~
|
167 |
-
|
168 |
-
$Line = array('body' => $line, 'indent' => $indent, 'text' => $text);
|
169 |
-
|
170 |
-
# ~
|
171 |
-
|
172 |
-
if (isset($CurrentBlock['incomplete']))
|
173 |
-
{
|
174 |
-
$Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock);
|
175 |
-
|
176 |
-
if (isset($Block))
|
177 |
-
{
|
178 |
-
$CurrentBlock = $Block;
|
179 |
-
|
180 |
-
continue;
|
181 |
-
}
|
182 |
-
else
|
183 |
-
{
|
184 |
-
if (method_exists($this, 'block'.$CurrentBlock['type'].'Complete'))
|
185 |
-
{
|
186 |
-
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
|
187 |
-
}
|
188 |
-
|
189 |
-
unset($CurrentBlock['incomplete']);
|
190 |
-
}
|
191 |
-
}
|
192 |
-
|
193 |
-
# ~
|
194 |
-
|
195 |
-
$marker = $text[0];
|
196 |
-
|
197 |
-
# ~
|
198 |
-
|
199 |
-
$blockTypes = $this->unmarkedBlockTypes;
|
200 |
-
|
201 |
-
if (isset($this->BlockTypes[$marker]))
|
202 |
-
{
|
203 |
-
foreach ($this->BlockTypes[$marker] as $blockType)
|
204 |
-
{
|
205 |
-
$blockTypes []= $blockType;
|
206 |
-
}
|
207 |
-
}
|
208 |
-
|
209 |
-
#
|
210 |
-
# ~
|
211 |
-
|
212 |
-
foreach ($blockTypes as $blockType)
|
213 |
-
{
|
214 |
-
$Block = $this->{'block'.$blockType}($Line, $CurrentBlock);
|
215 |
-
|
216 |
-
if (isset($Block))
|
217 |
-
{
|
218 |
-
$Block['type'] = $blockType;
|
219 |
-
|
220 |
-
if ( ! isset($Block['identified']))
|
221 |
-
{
|
222 |
-
$Blocks []= $CurrentBlock;
|
223 |
-
|
224 |
-
$Block['identified'] = true;
|
225 |
-
}
|
226 |
-
|
227 |
-
if (method_exists($this, 'block'.$blockType.'Continue'))
|
228 |
-
{
|
229 |
-
$Block['incomplete'] = true;
|
230 |
-
}
|
231 |
-
|
232 |
-
$CurrentBlock = $Block;
|
233 |
-
|
234 |
-
continue 2;
|
235 |
-
}
|
236 |
-
}
|
237 |
-
|
238 |
-
# ~
|
239 |
-
|
240 |
-
if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted']))
|
241 |
-
{
|
242 |
-
$CurrentBlock['element']['text'] .= "\n".$text;
|
243 |
-
}
|
244 |
-
else
|
245 |
-
{
|
246 |
-
$Blocks []= $CurrentBlock;
|
247 |
-
|
248 |
-
$CurrentBlock = $this->paragraph($Line);
|
249 |
-
|
250 |
-
$CurrentBlock['identified'] = true;
|
251 |
-
}
|
252 |
-
}
|
253 |
-
|
254 |
-
# ~
|
255 |
-
|
256 |
-
if (isset($CurrentBlock['incomplete']) and method_exists($this, 'block'.$CurrentBlock['type'].'Complete'))
|
257 |
-
{
|
258 |
-
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
|
259 |
-
}
|
260 |
-
|
261 |
-
# ~
|
262 |
-
|
263 |
-
$Blocks []= $CurrentBlock;
|
264 |
-
|
265 |
-
unset($Blocks[0]);
|
266 |
-
|
267 |
-
# ~
|
268 |
-
|
269 |
-
$markup = '';
|
270 |
-
|
271 |
-
foreach ($Blocks as $Block)
|
272 |
-
{
|
273 |
-
if (isset($Block['hidden']))
|
274 |
-
{
|
275 |
-
continue;
|
276 |
-
}
|
277 |
-
|
278 |
-
$markup .= "\n";
|
279 |
-
$markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']);
|
280 |
-
}
|
281 |
-
|
282 |
-
$markup .= "\n";
|
283 |
-
|
284 |
-
# ~
|
285 |
-
|
286 |
-
return $markup;
|
287 |
-
}
|
288 |
-
|
289 |
-
#
|
290 |
-
# Code
|
291 |
-
|
292 |
-
protected function blockCode($Line, $Block = null)
|
293 |
-
{
|
294 |
-
if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted']))
|
295 |
-
{
|
296 |
-
return;
|
297 |
-
}
|
298 |
-
|
299 |
-
if ($Line['indent'] >= 4)
|
300 |
-
{
|
301 |
-
$text = substr($Line['body'], 4);
|
302 |
-
|
303 |
-
$Block = array(
|
304 |
-
'element' => array(
|
305 |
-
'name' => 'pre',
|
306 |
-
'handler' => 'element',
|
307 |
-
'text' => array(
|
308 |
-
'name' => 'code',
|
309 |
-
'text' => $text,
|
310 |
-
),
|
311 |
-
),
|
312 |
-
);
|
313 |
-
|
314 |
-
return $Block;
|
315 |
-
}
|
316 |
-
}
|
317 |
-
|
318 |
-
protected function blockCodeContinue($Line, $Block)
|
319 |
-
{
|
320 |
-
if ($Line['indent'] >= 4)
|
321 |
-
{
|
322 |
-
if (isset($Block['interrupted']))
|
323 |
-
{
|
324 |
-
$Block['element']['text']['text'] .= "\n";
|
325 |
-
|
326 |
-
unset($Block['interrupted']);
|
327 |
-
}
|
328 |
-
|
329 |
-
$Block['element']['text']['text'] .= "\n";
|
330 |
-
|
331 |
-
$text = substr($Line['body'], 4);
|
332 |
-
|
333 |
-
$Block['element']['text']['text'] .= $text;
|
334 |
-
|
335 |
-
return $Block;
|
336 |
-
}
|
337 |
-
}
|
338 |
-
|
339 |
-
protected function blockCodeComplete($Block)
|
340 |
-
{
|
341 |
-
$text = $Block['element']['text']['text'];
|
342 |
-
|
343 |
-
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
344 |
-
|
345 |
-
$Block['element']['text']['text'] = $text;
|
346 |
-
|
347 |
-
return $Block;
|
348 |
-
}
|
349 |
-
|
350 |
-
#
|
351 |
-
# Comment
|
352 |
-
|
353 |
-
protected function blockComment($Line)
|
354 |
-
{
|
355 |
-
if ($this->markupEscaped)
|
356 |
-
{
|
357 |
-
return;
|
358 |
-
}
|
359 |
-
|
360 |
-
if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!')
|
361 |
-
{
|
362 |
-
$Block = array(
|
363 |
-
'markup' => $Line['body'],
|
364 |
-
);
|
365 |
-
|
366 |
-
if (preg_match('/-->$/', $Line['text']))
|
367 |
-
{
|
368 |
-
$Block['closed'] = true;
|
369 |
-
}
|
370 |
-
|
371 |
-
return $Block;
|
372 |
-
}
|
373 |
-
}
|
374 |
-
|
375 |
-
protected function blockCommentContinue($Line, array $Block)
|
376 |
-
{
|
377 |
-
if (isset($Block['closed']))
|
378 |
-
{
|
379 |
-
return;
|
380 |
-
}
|
381 |
-
|
382 |
-
$Block['markup'] .= "\n" . $Line['body'];
|
383 |
-
|
384 |
-
if (preg_match('/-->$/', $Line['text']))
|
385 |
-
{
|
386 |
-
$Block['closed'] = true;
|
387 |
-
}
|
388 |
-
|
389 |
-
return $Block;
|
390 |
-
}
|
391 |
-
|
392 |
-
#
|
393 |
-
# Fenced Code
|
394 |
-
|
395 |
-
protected function blockFencedCode($Line)
|
396 |
-
{
|
397 |
-
if (preg_match('/^(['.$Line['text'][0].']{3,})[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches))
|
398 |
-
{
|
399 |
-
$Element = array(
|
400 |
-
'name' => 'code',
|
401 |
-
'text' => '',
|
402 |
-
);
|
403 |
-
|
404 |
-
if (isset($matches[2]))
|
405 |
-
{
|
406 |
-
$class = 'language-'.$matches[2];
|
407 |
-
|
408 |
-
$Element['attributes'] = array(
|
409 |
-
'class' => $class,
|
410 |
-
);
|
411 |
-
}
|
412 |
-
|
413 |
-
$Block = array(
|
414 |
-
'char' => $Line['text'][0],
|
415 |
-
'element' => array(
|
416 |
-
'name' => 'pre',
|
417 |
-
'handler' => 'element',
|
418 |
-
'text' => $Element,
|
419 |
-
),
|
420 |
-
);
|
421 |
-
|
422 |
-
return $Block;
|
423 |
-
}
|
424 |
-
}
|
425 |
-
|
426 |
-
protected function blockFencedCodeContinue($Line, $Block)
|
427 |
-
{
|
428 |
-
if (isset($Block['complete']))
|
429 |
-
{
|
430 |
-
return;
|
431 |
-
}
|
432 |
-
|
433 |
-
if (isset($Block['interrupted']))
|
434 |
-
{
|
435 |
-
$Block['element']['text']['text'] .= "\n";
|
436 |
-
|
437 |
-
unset($Block['interrupted']);
|
438 |
-
}
|
439 |
-
|
440 |
-
if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text']))
|
441 |
-
{
|
442 |
-
$Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1);
|
443 |
-
|
444 |
-
$Block['complete'] = true;
|
445 |
-
|
446 |
-
return $Block;
|
447 |
-
}
|
448 |
-
|
449 |
-
$Block['element']['text']['text'] .= "\n".$Line['body'];;
|
450 |
-
|
451 |
-
return $Block;
|
452 |
-
}
|
453 |
-
|
454 |
-
protected function blockFencedCodeComplete($Block)
|
455 |
-
{
|
456 |
-
$text = $Block['element']['text']['text'];
|
457 |
-
|
458 |
-
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
459 |
-
|
460 |
-
$Block['element']['text']['text'] = $text;
|
461 |
-
|
462 |
-
return $Block;
|
463 |
-
}
|
464 |
-
|
465 |
-
#
|
466 |
-
# Header
|
467 |
-
|
468 |
-
protected function blockHeader($Line)
|
469 |
-
{
|
470 |
-
if (isset($Line['text'][1]))
|
471 |
-
{
|
472 |
-
$level = 1;
|
473 |
-
|
474 |
-
while (isset($Line['text'][$level]) and $Line['text'][$level] === '#')
|
475 |
-
{
|
476 |
-
$level ++;
|
477 |
-
}
|
478 |
-
|
479 |
-
if ($level > 6)
|
480 |
-
{
|
481 |
-
return;
|
482 |
-
}
|
483 |
-
|
484 |
-
$text = trim($Line['text'], '# ');
|
485 |
-
|
486 |
-
$Block = array(
|
487 |
-
'element' => array(
|
488 |
-
'name' => 'h' . min(6, $level),
|
489 |
-
'text' => $text,
|
490 |
-
'handler' => 'line',
|
491 |
-
),
|
492 |
-
);
|
493 |
-
|
494 |
-
return $Block;
|
495 |
-
}
|
496 |
-
}
|
497 |
-
|
498 |
-
#
|
499 |
-
# List
|
500 |
-
|
501 |
-
protected function blockList($Line)
|
502 |
-
{
|
503 |
-
list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]');
|
504 |
-
|
505 |
-
if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches))
|
506 |
-
{
|
507 |
-
$Block = array(
|
508 |
-
'indent' => $Line['indent'],
|
509 |
-
'pattern' => $pattern,
|
510 |
-
'element' => array(
|
511 |
-
'name' => $name,
|
512 |
-
'handler' => 'elements',
|
513 |
-
),
|
514 |
-
);
|
515 |
-
|
516 |
-
$Block['li'] = array(
|
517 |
-
'name' => 'li',
|
518 |
-
'handler' => 'li',
|
519 |
-
'text' => array(
|
520 |
-
$matches[2],
|
521 |
-
),
|
522 |
-
);
|
523 |
-
|
524 |
-
$Block['element']['text'] []= & $Block['li'];
|
525 |
-
|
526 |
-
return $Block;
|
527 |
-
}
|
528 |
-
}
|
529 |
-
|
530 |
-
protected function blockListContinue($Line, array $Block)
|
531 |
-
{
|
532 |
-
if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches))
|
533 |
-
{
|
534 |
-
if (isset($Block['interrupted']))
|
535 |
-
{
|
536 |
-
$Block['li']['text'] []= '';
|
537 |
-
|
538 |
-
unset($Block['interrupted']);
|
539 |
-
}
|
540 |
-
|
541 |
-
unset($Block['li']);
|
542 |
-
|
543 |
-
$text = isset($matches[1]) ? $matches[1] : '';
|
544 |
-
|
545 |
-
$Block['li'] = array(
|
546 |
-
'name' => 'li',
|
547 |
-
'handler' => 'li',
|
548 |
-
'text' => array(
|
549 |
-
$text,
|
550 |
-
),
|
551 |
-
);
|
552 |
-
|
553 |
-
$Block['element']['text'] []= & $Block['li'];
|
554 |
-
|
555 |
-
return $Block;
|
556 |
-
}
|
557 |
-
|
558 |
-
if ($Line['text'][0] === '[' and $this->blockReference($Line))
|
559 |
-
{
|
560 |
-
return $Block;
|
561 |
-
}
|
562 |
-
|
563 |
-
if ( ! isset($Block['interrupted']))
|
564 |
-
{
|
565 |
-
$text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
|
566 |
-
|
567 |
-
$Block['li']['text'] []= $text;
|
568 |
-
|
569 |
-
return $Block;
|
570 |
-
}
|
571 |
-
|
572 |
-
if ($Line['indent'] > 0)
|
573 |
-
{
|
574 |
-
$Block['li']['text'] []= '';
|
575 |
-
|
576 |
-
$text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
|
577 |
-
|
578 |
-
$Block['li']['text'] []= $text;
|
579 |
-
|
580 |
-
unset($Block['interrupted']);
|
581 |
-
|
582 |
-
return $Block;
|
583 |
-
}
|
584 |
-
}
|
585 |
-
|
586 |
-
#
|
587 |
-
# Quote
|
588 |
-
|
589 |
-
protected function blockQuote($Line)
|
590 |
-
{
|
591 |
-
if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
|
592 |
-
{
|
593 |
-
$Block = array(
|
594 |
-
'element' => array(
|
595 |
-
'name' => 'blockquote',
|
596 |
-
'handler' => 'lines',
|
597 |
-
'text' => (array) $matches[1],
|
598 |
-
),
|
599 |
-
);
|
600 |
-
|
601 |
-
return $Block;
|
602 |
-
}
|
603 |
-
}
|
604 |
-
|
605 |
-
protected function blockQuoteContinue($Line, array $Block)
|
606 |
-
{
|
607 |
-
if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
|
608 |
-
{
|
609 |
-
if (isset($Block['interrupted']))
|
610 |
-
{
|
611 |
-
$Block['element']['text'] []= '';
|
612 |
-
|
613 |
-
unset($Block['interrupted']);
|
614 |
-
}
|
615 |
-
|
616 |
-
$Block['element']['text'] []= $matches[1];
|
617 |
-
|
618 |
-
return $Block;
|
619 |
-
}
|
620 |
-
|
621 |
-
if ( ! isset($Block['interrupted']))
|
622 |
-
{
|
623 |
-
$Block['element']['text'] []= $Line['text'];
|
624 |
-
|
625 |
-
return $Block;
|
626 |
-
}
|
627 |
-
}
|
628 |
-
|
629 |
-
#
|
630 |
-
# Rule
|
631 |
-
|
632 |
-
protected function blockRule($Line)
|
633 |
-
{
|
634 |
-
if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text']))
|
635 |
-
{
|
636 |
-
$Block = array(
|
637 |
-
'element' => array(
|
638 |
-
'name' => 'hr'
|
639 |
-
),
|
640 |
-
);
|
641 |
-
|
642 |
-
return $Block;
|
643 |
-
}
|
644 |
-
}
|
645 |
-
|
646 |
-
#
|
647 |
-
# Setext
|
648 |
-
|
649 |
-
protected function blockSetextHeader($Line, array $Block = null)
|
650 |
-
{
|
651 |
-
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
|
652 |
-
{
|
653 |
-
return;
|
654 |
-
}
|
655 |
-
|
656 |
-
if (chop($Line['text'], $Line['text'][0]) === '')
|
657 |
-
{
|
658 |
-
$Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
|
659 |
-
|
660 |
-
return $Block;
|
661 |
-
}
|
662 |
-
}
|
663 |
-
|
664 |
-
#
|
665 |
-
# Markup
|
666 |
-
|
667 |
-
protected function blockMarkup($Line)
|
668 |
-
{
|
669 |
-
if ($this->markupEscaped)
|
670 |
-
{
|
671 |
-
return;
|
672 |
-
}
|
673 |
-
|
674 |
-
if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
|
675 |
-
{
|
676 |
-
if (in_array($matches[1], $this->textLevelElements))
|
677 |
-
{
|
678 |
-
return;
|
679 |
-
}
|
680 |
-
|
681 |
-
$Block = array(
|
682 |
-
'name' => $matches[1],
|
683 |
-
'depth' => 0,
|
684 |
-
'markup' => $Line['text'],
|
685 |
-
);
|
686 |
-
|
687 |
-
$length = strlen($matches[0]);
|
688 |
-
|
689 |
-
$remainder = substr($Line['text'], $length);
|
690 |
-
|
691 |
-
if (trim($remainder) === '')
|
692 |
-
{
|
693 |
-
if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
|
694 |
-
{
|
695 |
-
$Block['closed'] = true;
|
696 |
-
|
697 |
-
$Block['void'] = true;
|
698 |
-
}
|
699 |
-
}
|
700 |
-
else
|
701 |
-
{
|
702 |
-
if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
|
703 |
-
{
|
704 |
-
return;
|
705 |
-
}
|
706 |
-
|
707 |
-
if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder))
|
708 |
-
{
|
709 |
-
$Block['closed'] = true;
|
710 |
-
}
|
711 |
-
}
|
712 |
-
|
713 |
-
return $Block;
|
714 |
-
}
|
715 |
-
}
|
716 |
-
|
717 |
-
protected function blockMarkupContinue($Line, array $Block)
|
718 |
-
{
|
719 |
-
if (isset($Block['closed']))
|
720 |
-
{
|
721 |
-
return;
|
722 |
-
}
|
723 |
-
|
724 |
-
if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open
|
725 |
-
{
|
726 |
-
$Block['depth'] ++;
|
727 |
-
}
|
728 |
-
|
729 |
-
if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close
|
730 |
-
{
|
731 |
-
if ($Block['depth'] > 0)
|
732 |
-
{
|
733 |
-
$Block['depth'] --;
|
734 |
-
}
|
735 |
-
else
|
736 |
-
{
|
737 |
-
$Block['closed'] = true;
|
738 |
-
}
|
739 |
-
|
740 |
-
$Block['markup'] .= $matches[1];
|
741 |
-
}
|
742 |
-
|
743 |
-
if (isset($Block['interrupted']))
|
744 |
-
{
|
745 |
-
$Block['markup'] .= "\n";
|
746 |
-
|
747 |
-
unset($Block['interrupted']);
|
748 |
-
}
|
749 |
-
|
750 |
-
$Block['markup'] .= "\n".$Line['body'];
|
751 |
-
|
752 |
-
return $Block;
|
753 |
-
}
|
754 |
-
|
755 |
-
#
|
756 |
-
# Reference
|
757 |
-
|
758 |
-
protected function blockReference($Line)
|
759 |
-
{
|
760 |
-
if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
|
761 |
-
{
|
762 |
-
$id = strtolower($matches[1]);
|
763 |
-
|
764 |
-
$Data = array(
|
765 |
-
'url' => $matches[2],
|
766 |
-
'title' => null,
|
767 |
-
);
|
768 |
-
|
769 |
-
if (isset($matches[3]))
|
770 |
-
{
|
771 |
-
$Data['title'] = $matches[3];
|
772 |
-
}
|
773 |
-
|
774 |
-
$this->DefinitionData['Reference'][$id] = $Data;
|
775 |
-
|
776 |
-
$Block = array(
|
777 |
-
'hidden' => true,
|
778 |
-
);
|
779 |
-
|
780 |
-
return $Block;
|
781 |
-
}
|
782 |
-
}
|
783 |
-
|
784 |
-
#
|
785 |
-
# Table
|
786 |
-
|
787 |
-
protected function blockTable($Line, array $Block = null)
|
788 |
-
{
|
789 |
-
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
|
790 |
-
{
|
791 |
-
return;
|
792 |
-
}
|
793 |
-
|
794 |
-
if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '')
|
795 |
-
{
|
796 |
-
$alignments = array();
|
797 |
-
|
798 |
-
$divider = $Line['text'];
|
799 |
-
|
800 |
-
$divider = trim($divider);
|
801 |
-
$divider = trim($divider, '|');
|
802 |
-
|
803 |
-
$dividerCells = explode('|', $divider);
|
804 |
-
|
805 |
-
foreach ($dividerCells as $dividerCell)
|
806 |
-
{
|
807 |
-
$dividerCell = trim($dividerCell);
|
808 |
-
|
809 |
-
if ($dividerCell === '')
|
810 |
-
{
|
811 |
-
continue;
|
812 |
-
}
|
813 |
-
|
814 |
-
$alignment = null;
|
815 |
-
|
816 |
-
if ($dividerCell[0] === ':')
|
817 |
-
{
|
818 |
-
$alignment = 'left';
|
819 |
-
}
|
820 |
-
|
821 |
-
if (substr($dividerCell, - 1) === ':')
|
822 |
-
{
|
823 |
-
$alignment = $alignment === 'left' ? 'center' : 'right';
|
824 |
-
}
|
825 |
-
|
826 |
-
$alignments []= $alignment;
|
827 |
-
}
|
828 |
-
|
829 |
-
# ~
|
830 |
-
|
831 |
-
$HeaderElements = array();
|
832 |
-
|
833 |
-
$header = $Block['element']['text'];
|
834 |
-
|
835 |
-
$header = trim($header);
|
836 |
-
$header = trim($header, '|');
|
837 |
-
|
838 |
-
$headerCells = explode('|', $header);
|
839 |
-
|
840 |
-
foreach ($headerCells as $index => $headerCell)
|
841 |
-
{
|
842 |
-
$headerCell = trim($headerCell);
|
843 |
-
|
844 |
-
$HeaderElement = array(
|
845 |
-
'name' => 'th',
|
846 |
-
'text' => $headerCell,
|
847 |
-
'handler' => 'line',
|
848 |
-
);
|
849 |
-
|
850 |
-
if (isset($alignments[$index]))
|
851 |
-
{
|
852 |
-
$alignment = $alignments[$index];
|
853 |
-
|
854 |
-
$HeaderElement['attributes'] = array(
|
855 |
-
'style' => 'text-align: '.$alignment.';',
|
856 |
-
);
|
857 |
-
}
|
858 |
-
|
859 |
-
$HeaderElements []= $HeaderElement;
|
860 |
-
}
|
861 |
-
|
862 |
-
# ~
|
863 |
-
|
864 |
-
$Block = array(
|
865 |
-
'alignments' => $alignments,
|
866 |
-
'identified' => true,
|
867 |
-
'element' => array(
|
868 |
-
'name' => 'table',
|
869 |
-
'handler' => 'elements',
|
870 |
-
),
|
871 |
-
);
|
872 |
-
|
873 |
-
$Block['element']['text'] []= array(
|
874 |
-
'name' => 'thead',
|
875 |
-
'handler' => 'elements',
|
876 |
-
);
|
877 |
-
|
878 |
-
$Block['element']['text'] []= array(
|
879 |
-
'name' => 'tbody',
|
880 |
-
'handler' => 'elements',
|
881 |
-
'text' => array(),
|
882 |
-
);
|
883 |
-
|
884 |
-
$Block['element']['text'][0]['text'] []= array(
|
885 |
-
'name' => 'tr',
|
886 |
-
'handler' => 'elements',
|
887 |
-
'text' => $HeaderElements,
|
888 |
-
);
|
889 |
-
|
890 |
-
return $Block;
|
891 |
-
}
|
892 |
-
}
|
893 |
-
|
894 |
-
protected function blockTableContinue($Line, array $Block)
|
895 |
-
{
|
896 |
-
if (isset($Block['interrupted']))
|
897 |
-
{
|
898 |
-
return;
|
899 |
-
}
|
900 |
-
|
901 |
-
if ($Line['text'][0] === '|' or strpos($Line['text'], '|'))
|
902 |
-
{
|
903 |
-
$Elements = array();
|
904 |
-
|
905 |
-
$row = $Line['text'];
|
906 |
-
|
907 |
-
$row = trim($row);
|
908 |
-
$row = trim($row, '|');
|
909 |
-
|
910 |
-
preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches);
|
911 |
-
|
912 |
-
foreach ($matches[0] as $index => $cell)
|
913 |
-
{
|
914 |
-
$cell = trim($cell);
|
915 |
-
|
916 |
-
$Element = array(
|
917 |
-
'name' => 'td',
|
918 |
-
'handler' => 'line',
|
919 |
-
'text' => $cell,
|
920 |
-
);
|
921 |
-
|
922 |
-
if (isset($Block['alignments'][$index]))
|
923 |
-
{
|
924 |
-
$Element['attributes'] = array(
|
925 |
-
'style' => 'text-align: '.$Block['alignments'][$index].';',
|
926 |
-
);
|
927 |
-
}
|
928 |
-
|
929 |
-
$Elements []= $Element;
|
930 |
-
}
|
931 |
-
|
932 |
-
$Element = array(
|
933 |
-
'name' => 'tr',
|
934 |
-
'handler' => 'elements',
|
935 |
-
'text' => $Elements,
|
936 |
-
);
|
937 |
-
|
938 |
-
$Block['element']['text'][1]['text'] []= $Element;
|
939 |
-
|
940 |
-
return $Block;
|
941 |
-
}
|
942 |
-
}
|
943 |
-
|
944 |
-
#
|
945 |
-
# ~
|
946 |
-
#
|
947 |
-
|
948 |
-
protected function paragraph($Line)
|
949 |
-
{
|
950 |
-
$Block = array(
|
951 |
-
'element' => array(
|
952 |
-
'name' => 'p',
|
953 |
-
'text' => $Line['text'],
|
954 |
-
'handler' => 'line',
|
955 |
-
),
|
956 |
-
);
|
957 |
-
|
958 |
-
return $Block;
|
959 |
-
}
|
960 |
-
|
961 |
-
#
|
962 |
-
# Inline Elements
|
963 |
-
#
|
964 |
-
|
965 |
-
protected $InlineTypes = array(
|
966 |
-
'"' => array('SpecialCharacter'),
|
967 |
-
'!' => array('Image'),
|
968 |
-
'&' => array('SpecialCharacter'),
|
969 |
-
'*' => array('Emphasis'),
|
970 |
-
':' => array('Url'),
|
971 |
-
'<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'),
|
972 |
-
'>' => array('SpecialCharacter'),
|
973 |
-
'[' => array('Link'),
|
974 |
-
'_' => array('Emphasis'),
|
975 |
-
'`' => array('Code'),
|
976 |
-
'~' => array('Strikethrough'),
|
977 |
-
'\\' => array('EscapeSequence'),
|
978 |
-
);
|
979 |
-
|
980 |
-
# ~
|
981 |
-
|
982 |
-
protected $inlineMarkerList = '!"*_&[:<>`~\\';
|
983 |
-
|
984 |
-
#
|
985 |
-
# ~
|
986 |
-
#
|
987 |
-
|
988 |
-
public function line($text)
|
989 |
-
{
|
990 |
-
$markup = '';
|
991 |
-
|
992 |
-
$unexaminedText = $text;
|
993 |
-
|
994 |
-
$markerPosition = 0;
|
995 |
-
|
996 |
-
while ($excerpt = strpbrk($unexaminedText, $this->inlineMarkerList))
|
997 |
-
{
|
998 |
-
$marker = $excerpt[0];
|
999 |
-
|
1000 |
-
$markerPosition += strpos($unexaminedText, $marker);
|
1001 |
-
|
1002 |
-
$Excerpt = array('text' => $excerpt, 'context' => $text);
|
1003 |
-
|
1004 |
-
foreach ($this->InlineTypes[$marker] as $inlineType)
|
1005 |
-
{
|
1006 |
-
$Inline = $this->{'inline'.$inlineType}($Excerpt);
|
1007 |
-
|
1008 |
-
if ( ! isset($Inline))
|
1009 |
-
{
|
1010 |
-
continue;
|
1011 |
-
}
|
1012 |
-
|
1013 |
-
if (isset($Inline['position']) and $Inline['position'] > $markerPosition) # position is ahead of marker
|
1014 |
-
{
|
1015 |
-
continue;
|
1016 |
-
}
|
1017 |
-
|
1018 |
-
if ( ! isset($Inline['position']))
|
1019 |
-
{
|
1020 |
-
$Inline['position'] = $markerPosition;
|
1021 |
-
}
|
1022 |
-
|
1023 |
-
$unmarkedText = substr($text, 0, $Inline['position']);
|
1024 |
-
|
1025 |
-
$markup .= $this->unmarkedText($unmarkedText);
|
1026 |
-
|
1027 |
-
$markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']);
|
1028 |
-
|
1029 |
-
$text = substr($text, $Inline['position'] + $Inline['extent']);
|
1030 |
-
|
1031 |
-
$unexaminedText = $text;
|
1032 |
-
|
1033 |
-
$markerPosition = 0;
|
1034 |
-
|
1035 |
-
continue 2;
|
1036 |
-
}
|
1037 |
-
|
1038 |
-
$unexaminedText = substr($excerpt, 1);
|
1039 |
-
|
1040 |
-
$markerPosition ++;
|
1041 |
-
}
|
1042 |
-
|
1043 |
-
$markup .= $this->unmarkedText($text);
|
1044 |
-
|
1045 |
-
return $markup;
|
1046 |
-
}
|
1047 |
-
|
1048 |
-
#
|
1049 |
-
# ~
|
1050 |
-
#
|
1051 |
-
|
1052 |
-
protected function inlineCode($Excerpt)
|
1053 |
-
{
|
1054 |
-
$marker = $Excerpt['text'][0];
|
1055 |
-
|
1056 |
-
if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
|
1057 |
-
{
|
1058 |
-
$text = $matches[2];
|
1059 |
-
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
1060 |
-
$text = preg_replace("/[ ]*\n/", ' ', $text);
|
1061 |
-
|
1062 |
-
return array(
|
1063 |
-
'extent' => strlen($matches[0]),
|
1064 |
-
'element' => array(
|
1065 |
-
'name' => 'code',
|
1066 |
-
'text' => $text,
|
1067 |
-
),
|
1068 |
-
);
|
1069 |
-
}
|
1070 |
-
}
|
1071 |
-
|
1072 |
-
protected function inlineEmailTag($Excerpt)
|
1073 |
-
{
|
1074 |
-
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches))
|
1075 |
-
{
|
1076 |
-
$url = $matches[1];
|
1077 |
-
|
1078 |
-
if ( ! isset($matches[2]))
|
1079 |
-
{
|
1080 |
-
$url = 'mailto:' . $url;
|
1081 |
-
}
|
1082 |
-
|
1083 |
-
return array(
|
1084 |
-
'extent' => strlen($matches[0]),
|
1085 |
-
'element' => array(
|
1086 |
-
'name' => 'a',
|
1087 |
-
'text' => $matches[1],
|
1088 |
-
'attributes' => array(
|
1089 |
-
'href' => $url,
|
1090 |
-
),
|
1091 |
-
),
|
1092 |
-
);
|
1093 |
-
}
|
1094 |
-
}
|
1095 |
-
|
1096 |
-
protected function inlineEmphasis($Excerpt)
|
1097 |
-
{
|
1098 |
-
if ( ! isset($Excerpt['text'][1]))
|
1099 |
-
{
|
1100 |
-
return;
|
1101 |
-
}
|
1102 |
-
|
1103 |
-
$marker = $Excerpt['text'][0];
|
1104 |
-
|
1105 |
-
if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
|
1106 |
-
{
|
1107 |
-
$emphasis = 'strong';
|
1108 |
-
}
|
1109 |
-
elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
|
1110 |
-
{
|
1111 |
-
$emphasis = 'em';
|
1112 |
-
}
|
1113 |
-
else
|
1114 |
-
{
|
1115 |
-
return;
|
1116 |
-
}
|
1117 |
-
|
1118 |
-
return array(
|
1119 |
-
'extent' => strlen($matches[0]),
|
1120 |
-
'element' => array(
|
1121 |
-
'name' => $emphasis,
|
1122 |
-
'handler' => 'line',
|
1123 |
-
'text' => $matches[1],
|
1124 |
-
),
|
1125 |
-
);
|
1126 |
-
}
|
1127 |
-
|
1128 |
-
protected function inlineEscapeSequence($Excerpt)
|
1129 |
-
{
|
1130 |
-
if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
|
1131 |
-
{
|
1132 |
-
return array(
|
1133 |
-
'markup' => $Excerpt['text'][1],
|
1134 |
-
'extent' => 2,
|
1135 |
-
);
|
1136 |
-
}
|
1137 |
-
}
|
1138 |
-
|
1139 |
-
protected function inlineImage($Excerpt)
|
1140 |
-
{
|
1141 |
-
if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[')
|
1142 |
-
{
|
1143 |
-
return;
|
1144 |
-
}
|
1145 |
-
|
1146 |
-
$Excerpt['text']= substr($Excerpt['text'], 1);
|
1147 |
-
|
1148 |
-
$Link = $this->inlineLink($Excerpt);
|
1149 |
-
|
1150 |
-
if ($Link === null)
|
1151 |
-
{
|
1152 |
-
return;
|
1153 |
-
}
|
1154 |
-
|
1155 |
-
$Inline = array(
|
1156 |
-
'extent' => $Link['extent'] + 1,
|
1157 |
-
'element' => array(
|
1158 |
-
'name' => 'img',
|
1159 |
-
'attributes' => array(
|
1160 |
-
'src' => $Link['element']['attributes']['href'],
|
1161 |
-
'alt' => $Link['element']['text'],
|
1162 |
-
),
|
1163 |
-
),
|
1164 |
-
);
|
1165 |
-
|
1166 |
-
$Inline['element']['attributes'] += $Link['element']['attributes'];
|
1167 |
-
|
1168 |
-
unset($Inline['element']['attributes']['href']);
|
1169 |
-
|
1170 |
-
return $Inline;
|
1171 |
-
}
|
1172 |
-
|
1173 |
-
protected function inlineLink($Excerpt)
|
1174 |
-
{
|
1175 |
-
$Element = array(
|
1176 |
-
'name' => 'a',
|
1177 |
-
'handler' => 'line',
|
1178 |
-
'text' => null,
|
1179 |
-
'attributes' => array(
|
1180 |
-
'href' => null,
|
1181 |
-
'title' => null,
|
1182 |
-
),
|
1183 |
-
);
|
1184 |
-
|
1185 |
-
$extent = 0;
|
1186 |
-
|
1187 |
-
$remainder = $Excerpt['text'];
|
1188 |
-
|
1189 |
-
if (preg_match('/\[((?:[^][]|(?R))*)\]/', $remainder, $matches))
|
1190 |
-
{
|
1191 |
-
$Element['text'] = $matches[1];
|
1192 |
-
|
1193 |
-
$extent += strlen($matches[0]);
|
1194 |
-
|
1195 |
-
$remainder = substr($remainder, $extent);
|
1196 |
-
}
|
1197 |
-
else
|
1198 |
-
{
|
1199 |
-
return;
|
1200 |
-
}
|
1201 |
-
|
1202 |
-
if (preg_match('/^[(]((?:[^ (]|[(][^ )]+[)])+)(?:[ ]+("[^"]+"|\'[^\']+\'))?[)]/', $remainder, $matches))
|
1203 |
-
{
|
1204 |
-
$Element['attributes']['href'] = $matches[1];
|
1205 |
-
|
1206 |
-
if (isset($matches[2]))
|
1207 |
-
{
|
1208 |
-
$Element['attributes']['title'] = substr($matches[2], 1, - 1);
|
1209 |
-
}
|
1210 |
-
|
1211 |
-
$extent += strlen($matches[0]);
|
1212 |
-
}
|
1213 |
-
else
|
1214 |
-
{
|
1215 |
-
if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
|
1216 |
-
{
|
1217 |
-
$definition = $matches[1] ? $matches[1] : $Element['text'];
|
1218 |
-
$definition = strtolower($definition);
|
1219 |
-
|
1220 |
-
$extent += strlen($matches[0]);
|
1221 |
-
}
|
1222 |
-
else
|
1223 |
-
{
|
1224 |
-
$definition = strtolower($Element['text']);
|
1225 |
-
}
|
1226 |
-
|
1227 |
-
if ( ! isset($this->DefinitionData['Reference'][$definition]))
|
1228 |
-
{
|
1229 |
-
return;
|
1230 |
-
}
|
1231 |
-
|
1232 |
-
$Definition = $this->DefinitionData['Reference'][$definition];
|
1233 |
-
|
1234 |
-
$Element['attributes']['href'] = $Definition['url'];
|
1235 |
-
$Element['attributes']['title'] = $Definition['title'];
|
1236 |
-
}
|
1237 |
-
|
1238 |
-
$Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']);
|
1239 |
-
|
1240 |
-
return array(
|
1241 |
-
'extent' => $extent,
|
1242 |
-
'element' => $Element,
|
1243 |
-
);
|
1244 |
-
}
|
1245 |
-
|
1246 |
-
protected function inlineMarkup($Excerpt)
|
1247 |
-
{
|
1248 |
-
if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false)
|
1249 |
-
{
|
1250 |
-
return;
|
1251 |
-
}
|
1252 |
-
|
1253 |
-
if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches))
|
1254 |
-
{
|
1255 |
-
return array(
|
1256 |
-
'markup' => $matches[0],
|
1257 |
-
'extent' => strlen($matches[0]),
|
1258 |
-
);
|
1259 |
-
}
|
1260 |
-
|
1261 |
-
if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?[^-])*-->/s', $Excerpt['text'], $matches))
|
1262 |
-
{
|
1263 |
-
return array(
|
1264 |
-
'markup' => $matches[0],
|
1265 |
-
'extent' => strlen($matches[0]),
|
1266 |
-
);
|
1267 |
-
}
|
1268 |
-
|
1269 |
-
if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches))
|
1270 |
-
{
|
1271 |
-
return array(
|
1272 |
-
'markup' => $matches[0],
|
1273 |
-
'extent' => strlen($matches[0]),
|
1274 |
-
);
|
1275 |
-
}
|
1276 |
-
}
|
1277 |
-
|
1278 |
-
protected function inlineSpecialCharacter($Excerpt)
|
1279 |
-
{
|
1280 |
-
if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text']))
|
1281 |
-
{
|
1282 |
-
return array(
|
1283 |
-
'markup' => '&',
|
1284 |
-
'extent' => 1,
|
1285 |
-
);
|
1286 |
-
}
|
1287 |
-
|
1288 |
-
$SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot');
|
1289 |
-
|
1290 |
-
if (isset($SpecialCharacter[$Excerpt['text'][0]]))
|
1291 |
-
{
|
1292 |
-
return array(
|
1293 |
-
'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';',
|
1294 |
-
'extent' => 1,
|
1295 |
-
);
|
1296 |
-
}
|
1297 |
-
}
|
1298 |
-
|
1299 |
-
protected function inlineStrikethrough($Excerpt)
|
1300 |
-
{
|
1301 |
-
if ( ! isset($Excerpt['text'][1]))
|
1302 |
-
{
|
1303 |
-
return;
|
1304 |
-
}
|
1305 |
-
|
1306 |
-
if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches))
|
1307 |
-
{
|
1308 |
-
return array(
|
1309 |
-
'extent' => strlen($matches[0]),
|
1310 |
-
'element' => array(
|
1311 |
-
'name' => 'del',
|
1312 |
-
'text' => $matches[1],
|
1313 |
-
'handler' => 'line',
|
1314 |
-
),
|
1315 |
-
);
|
1316 |
-
}
|
1317 |
-
}
|
1318 |
-
|
1319 |
-
protected function inlineUrl($Excerpt)
|
1320 |
-
{
|
1321 |
-
if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/')
|
1322 |
-
{
|
1323 |
-
return;
|
1324 |
-
}
|
1325 |
-
|
1326 |
-
if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
|
1327 |
-
{
|
1328 |
-
$Inline = array(
|
1329 |
-
'extent' => strlen($matches[0][0]),
|
1330 |
-
'position' => $matches[0][1],
|
1331 |
-
'element' => array(
|
1332 |
-
'name' => 'a',
|
1333 |
-
'text' => $matches[0][0],
|
1334 |
-
'attributes' => array(
|
1335 |
-
'href' => $matches[0][0],
|
1336 |
-
),
|
1337 |
-
),
|
1338 |
-
);
|
1339 |
-
|
1340 |
-
return $Inline;
|
1341 |
-
}
|
1342 |
-
}
|
1343 |
-
|
1344 |
-
protected function inlineUrlTag($Excerpt)
|
1345 |
-
{
|
1346 |
-
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
|
1347 |
-
{
|
1348 |
-
$url = str_replace(array('&', '<'), array('&', '<'), $matches[1]);
|
1349 |
-
|
1350 |
-
return array(
|
1351 |
-
'extent' => strlen($matches[0]),
|
1352 |
-
'element' => array(
|
1353 |
-
'name' => 'a',
|
1354 |
-
'text' => $url,
|
1355 |
-
'attributes' => array(
|
1356 |
-
'href' => $url,
|
1357 |
-
),
|
1358 |
-
),
|
1359 |
-
);
|
1360 |
-
}
|
1361 |
-
}
|
1362 |
-
|
1363 |
-
#
|
1364 |
-
# ~
|
1365 |
-
|
1366 |
-
protected $unmarkedInlineTypes = array("\n" => 'Break', '://' => 'Url');
|
1367 |
-
|
1368 |
-
# ~
|
1369 |
-
|
1370 |
-
protected function unmarkedText($text)
|
1371 |
-
{
|
1372 |
-
if ($this->breaksEnabled)
|
1373 |
-
{
|
1374 |
-
$text = preg_replace('/[ ]*\n/', "<br />\n", $text);
|
1375 |
-
}
|
1376 |
-
else
|
1377 |
-
{
|
1378 |
-
$text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "<br />\n", $text);
|
1379 |
-
$text = str_replace(" \n", "\n", $text);
|
1380 |
-
}
|
1381 |
-
|
1382 |
-
return $text;
|
1383 |
-
}
|
1384 |
-
|
1385 |
-
#
|
1386 |
-
# Handlers
|
1387 |
-
#
|
1388 |
-
|
1389 |
-
protected function element(array $Element)
|
1390 |
-
{
|
1391 |
-
$markup = '<'.$Element['name'];
|
1392 |
-
|
1393 |
-
if (isset($Element['attributes']))
|
1394 |
-
{
|
1395 |
-
foreach ($Element['attributes'] as $name => $value)
|
1396 |
-
{
|
1397 |
-
if ($value === null)
|
1398 |
-
{
|
1399 |
-
continue;
|
1400 |
-
}
|
1401 |
-
|
1402 |
-
$markup .= ' '.$name.'="'.$value.'"';
|
1403 |
-
}
|
1404 |
-
}
|
1405 |
-
|
1406 |
-
if (isset($Element['text']))
|
1407 |
-
{
|
1408 |
-
$markup .= '>';
|
1409 |
-
|
1410 |
-
if (isset($Element['handler']))
|
1411 |
-
{
|
1412 |
-
$markup .= $this->$Element['handler']($Element['text']);
|
1413 |
-
}
|
1414 |
-
else
|
1415 |
-
{
|
1416 |
-
$markup .= $Element['text'];
|
1417 |
-
}
|
1418 |
-
|
1419 |
-
$markup .= '</'.$Element['name'].'>';
|
1420 |
-
}
|
1421 |
-
else
|
1422 |
-
{
|
1423 |
-
$markup .= ' />';
|
1424 |
-
}
|
1425 |
-
|
1426 |
-
return $markup;
|
1427 |
-
}
|
1428 |
-
|
1429 |
-
protected function elements(array $Elements)
|
1430 |
-
{
|
1431 |
-
$markup = '';
|
1432 |
-
|
1433 |
-
foreach ($Elements as $Element)
|
1434 |
-
{
|
1435 |
-
$markup .= "\n" . $this->element($Element);
|
1436 |
-
}
|
1437 |
-
|
1438 |
-
$markup .= "\n";
|
1439 |
-
|
1440 |
-
return $markup;
|
1441 |
-
}
|
1442 |
-
|
1443 |
-
# ~
|
1444 |
-
|
1445 |
-
protected function li($lines)
|
1446 |
-
{
|
1447 |
-
$markup = $this->lines($lines);
|
1448 |
-
|
1449 |
-
$trimmedMarkup = trim($markup);
|
1450 |
-
|
1451 |
-
if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '<p>')
|
1452 |
-
{
|
1453 |
-
$markup = $trimmedMarkup;
|
1454 |
-
$markup = substr($markup, 3);
|
1455 |
-
|
1456 |
-
$position = strpos($markup, "</p>");
|
1457 |
-
|
1458 |
-
$markup = substr_replace($markup, '', $position, 4);
|
1459 |
-
}
|
1460 |
-
|
1461 |
-
return $markup;
|
1462 |
-
}
|
1463 |
-
|
1464 |
-
#
|
1465 |
-
# Deprecated Methods
|
1466 |
-
#
|
1467 |
-
|
1468 |
-
function parse($text)
|
1469 |
-
{
|
1470 |
-
$markup = $this->text($text);
|
1471 |
-
|
1472 |
-
return $markup;
|
1473 |
-
}
|
1474 |
-
|
1475 |
-
#
|
1476 |
-
# Static Methods
|
1477 |
-
#
|
1478 |
-
|
1479 |
-
static function instance($name = 'default')
|
1480 |
-
{
|
1481 |
-
if (isset(self::$instances[$name]))
|
1482 |
-
{
|
1483 |
-
return self::$instances[$name];
|
1484 |
-
}
|
1485 |
-
|
1486 |
-
$instance = new self();
|
1487 |
-
|
1488 |
-
self::$instances[$name] = $instance;
|
1489 |
-
|
1490 |
-
return $instance;
|
1491 |
-
}
|
1492 |
-
|
1493 |
-
private static $instances = array();
|
1494 |
-
|
1495 |
-
#
|
1496 |
-
# Fields
|
1497 |
-
#
|
1498 |
-
|
1499 |
-
protected $DefinitionData;
|
1500 |
-
|
1501 |
-
#
|
1502 |
-
# Read-Only
|
1503 |
-
|
1504 |
-
protected $specialCharacters = array(
|
1505 |
-
'\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|',
|
1506 |
-
);
|
1507 |
-
|
1508 |
-
protected $StrongRegex = array(
|
1509 |
-
'*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
|
1510 |
-
'_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us',
|
1511 |
-
);
|
1512 |
-
|
1513 |
-
protected $EmRegex = array(
|
1514 |
-
'*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
|
1515 |
-
'_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
|
1516 |
-
);
|
1517 |
-
|
1518 |
-
protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?';
|
1519 |
-
|
1520 |
-
protected $voidElements = array(
|
1521 |
-
'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source',
|
1522 |
-
);
|
1523 |
-
|
1524 |
-
protected $textLevelElements = array(
|
1525 |
-
'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont',
|
1526 |
-
'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
|
1527 |
-
'i', 'rp', 'del', 'code', 'strike', 'marquee',
|
1528 |
-
'q', 'rt', 'ins', 'font', 'strong',
|
1529 |
-
's', 'tt', 'sub', 'mark',
|
1530 |
-
'u', 'xm', 'sup', 'nobr',
|
1531 |
-
'var', 'ruby',
|
1532 |
-
'wbr', 'span',
|
1533 |
-
'time',
|
1534 |
-
);
|
1535 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/plugin-update-checker/vendor/readme-parser.php
DELETED
@@ -1,331 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* This is a slightly modified version of github.com/markjaquith/WordPress-Plugin-Readme-Parser
|
5 |
-
* It uses Parsedown instead of the "Markdown Extra" parser.
|
6 |
-
*/
|
7 |
-
|
8 |
-
Class PucReadmeParser {
|
9 |
-
|
10 |
-
function __construct() {
|
11 |
-
// This space intentially blank
|
12 |
-
}
|
13 |
-
|
14 |
-
function parse_readme( $file ) {
|
15 |
-
$file_contents = @implode('', @file($file));
|
16 |
-
return $this->parse_readme_contents( $file_contents );
|
17 |
-
}
|
18 |
-
|
19 |
-
function parse_readme_contents( $file_contents ) {
|
20 |
-
$file_contents = str_replace(array("\r\n", "\r"), "\n", $file_contents);
|
21 |
-
$file_contents = trim($file_contents);
|
22 |
-
if ( 0 === strpos( $file_contents, "\xEF\xBB\xBF" ) )
|
23 |
-
$file_contents = substr( $file_contents, 3 );
|
24 |
-
|
25 |
-
// Markdown transformations
|
26 |
-
$file_contents = preg_replace( "|^###([^#]+)#*?\s*?\n|im", '=$1='."\n", $file_contents );
|
27 |
-
$file_contents = preg_replace( "|^##([^#]+)#*?\s*?\n|im", '==$1=='."\n", $file_contents );
|
28 |
-
$file_contents = preg_replace( "|^#([^#]+)#*?\s*?\n|im", '===$1==='."\n", $file_contents );
|
29 |
-
|
30 |
-
// === Plugin Name ===
|
31 |
-
// Must be the very first thing.
|
32 |
-
if ( !preg_match('|^===(.*)===|', $file_contents, $_name) )
|
33 |
-
return array(); // require a name
|
34 |
-
$name = trim($_name[1], '=');
|
35 |
-
$name = $this->sanitize_text( $name );
|
36 |
-
|
37 |
-
$file_contents = $this->chop_string( $file_contents, $_name[0] );
|
38 |
-
|
39 |
-
|
40 |
-
// Requires at least: 1.5
|
41 |
-
if ( preg_match('|Requires at least:(.*)|i', $file_contents, $_requires_at_least) )
|
42 |
-
$requires_at_least = $this->sanitize_text($_requires_at_least[1]);
|
43 |
-
else
|
44 |
-
$requires_at_least = NULL;
|
45 |
-
|
46 |
-
|
47 |
-
// Tested up to: 2.1
|
48 |
-
if ( preg_match('|Tested up to:(.*)|i', $file_contents, $_tested_up_to) )
|
49 |
-
$tested_up_to = $this->sanitize_text( $_tested_up_to[1] );
|
50 |
-
else
|
51 |
-
$tested_up_to = NULL;
|
52 |
-
|
53 |
-
|
54 |
-
// Stable tag: 10.4-ride-the-fire-eagle-danger-day
|
55 |
-
if ( preg_match('|Stable tag:(.*)|i', $file_contents, $_stable_tag) )
|
56 |
-
$stable_tag = $this->sanitize_text( $_stable_tag[1] );
|
57 |
-
else
|
58 |
-
$stable_tag = NULL; // we assume trunk, but don't set it here to tell the difference between specified trunk and default trunk
|
59 |
-
|
60 |
-
|
61 |
-
// Tags: some tag, another tag, we like tags
|
62 |
-
if ( preg_match('|Tags:(.*)|i', $file_contents, $_tags) ) {
|
63 |
-
$tags = preg_split('|,[\s]*?|', trim($_tags[1]));
|
64 |
-
foreach ( array_keys($tags) as $t )
|
65 |
-
$tags[$t] = $this->sanitize_text( $tags[$t] );
|
66 |
-
} else {
|
67 |
-
$tags = array();
|
68 |
-
}
|
69 |
-
|
70 |
-
|
71 |
-
// Contributors: markjaquith, mdawaffe, zefrank
|
72 |
-
$contributors = array();
|
73 |
-
if ( preg_match('|Contributors:(.*)|i', $file_contents, $_contributors) ) {
|
74 |
-
$temp_contributors = preg_split('|,[\s]*|', trim($_contributors[1]));
|
75 |
-
foreach ( array_keys($temp_contributors) as $c ) {
|
76 |
-
$tmp_sanitized = $this->user_sanitize( $temp_contributors[$c] );
|
77 |
-
if ( strlen(trim($tmp_sanitized)) > 0 )
|
78 |
-
$contributors[$c] = $tmp_sanitized;
|
79 |
-
unset($tmp_sanitized);
|
80 |
-
}
|
81 |
-
}
|
82 |
-
|
83 |
-
|
84 |
-
// Donate Link: URL
|
85 |
-
if ( preg_match('|Donate link:(.*)|i', $file_contents, $_donate_link) )
|
86 |
-
$donate_link = esc_url( $_donate_link[1] );
|
87 |
-
else
|
88 |
-
$donate_link = NULL;
|
89 |
-
|
90 |
-
|
91 |
-
// togs, conts, etc are optional and order shouldn't matter. So we chop them only after we've grabbed their values.
|
92 |
-
foreach ( array('tags', 'contributors', 'requires_at_least', 'tested_up_to', 'stable_tag', 'donate_link') as $chop ) {
|
93 |
-
if ( $$chop ) {
|
94 |
-
$_chop = '_' . $chop;
|
95 |
-
$file_contents = $this->chop_string( $file_contents, ${$_chop}[0] );
|
96 |
-
}
|
97 |
-
}
|
98 |
-
|
99 |
-
$file_contents = trim($file_contents);
|
100 |
-
|
101 |
-
|
102 |
-
// short-description fu
|
103 |
-
if ( !preg_match('/(^(.*?))^[\s]*=+?[\s]*.+?[\s]*=+?/ms', $file_contents, $_short_description) )
|
104 |
-
$_short_description = array( 1 => &$file_contents, 2 => &$file_contents );
|
105 |
-
$short_desc_filtered = $this->sanitize_text( $_short_description[2] );
|
106 |
-
$short_desc_length = strlen($short_desc_filtered);
|
107 |
-
$short_description = substr($short_desc_filtered, 0, 150);
|
108 |
-
if ( $short_desc_length > strlen($short_description) )
|
109 |
-
$truncated = true;
|
110 |
-
else
|
111 |
-
$truncated = false;
|
112 |
-
if ( $_short_description[1] )
|
113 |
-
$file_contents = $this->chop_string( $file_contents, $_short_description[1] ); // yes, the [1] is intentional
|
114 |
-
|
115 |
-
// == Section ==
|
116 |
-
// Break into sections
|
117 |
-
// $_sections[0] will be the title of the first section, $_sections[1] will be the content of the first section
|
118 |
-
// the array alternates from there: title2, content2, title3, content3... and so forth
|
119 |
-
$_sections = preg_split('/^[\s]*==[\s]*(.+?)[\s]*==/m', $file_contents, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
|
120 |
-
|
121 |
-
$sections = array();
|
122 |
-
for ( $i=1; $i <= count($_sections); $i +=2 ) {
|
123 |
-
$_sections[$i] = preg_replace('/^[\s]*=[\s]+(.+?)[\s]+=/m', '<h4>$1</h4>', $_sections[$i]);
|
124 |
-
$_sections[$i] = $this->filter_text( $_sections[$i], true );
|
125 |
-
$title = $this->sanitize_text( $_sections[$i-1] );
|
126 |
-
$sections[str_replace(' ', '_', strtolower($title))] = array('title' => $title, 'content' => $_sections[$i]);
|
127 |
-
}
|
128 |
-
|
129 |
-
|
130 |
-
// Special sections
|
131 |
-
// This is where we nab our special sections, so we can enforce their order and treat them differently, if needed
|
132 |
-
// upgrade_notice is not a section, but parse it like it is for now
|
133 |
-
$final_sections = array();
|
134 |
-
foreach ( array('description', 'installation', 'frequently_asked_questions', 'screenshots', 'changelog', 'change_log', 'upgrade_notice') as $special_section ) {
|
135 |
-
if ( isset($sections[$special_section]) ) {
|
136 |
-
$final_sections[$special_section] = $sections[$special_section]['content'];
|
137 |
-
unset($sections[$special_section]);
|
138 |
-
}
|
139 |
-
}
|
140 |
-
if ( isset($final_sections['change_log']) && empty($final_sections['changelog']) )
|
141 |
-
$final_sections['changelog'] = $final_sections['change_log'];
|
142 |
-
|
143 |
-
|
144 |
-
$final_screenshots = array();
|
145 |
-
if ( isset($final_sections['screenshots']) ) {
|
146 |
-
preg_match_all('|<li>(.*?)</li>|s', $final_sections['screenshots'], $screenshots, PREG_SET_ORDER);
|
147 |
-
if ( $screenshots ) {
|
148 |
-
foreach ( (array) $screenshots as $ss )
|
149 |
-
$final_screenshots[] = $ss[1];
|
150 |
-
}
|
151 |
-
}
|
152 |
-
|
153 |
-
// Parse the upgrade_notice section specially:
|
154 |
-
// 1.0 => blah, 1.1 => fnord
|
155 |
-
$upgrade_notice = array();
|
156 |
-
if ( isset($final_sections['upgrade_notice']) ) {
|
157 |
-
$split = preg_split( '#<h4>(.*?)</h4>#', $final_sections['upgrade_notice'], -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
|
158 |
-
for ( $i = 0; $i < count( $split ); $i += 2 )
|
159 |
-
$upgrade_notice[$this->sanitize_text( $split[$i] )] = substr( $this->sanitize_text( $split[$i + 1] ), 0, 300 );
|
160 |
-
unset( $final_sections['upgrade_notice'] );
|
161 |
-
}
|
162 |
-
|
163 |
-
// No description?
|
164 |
-
// No problem... we'll just fall back to the old style of description
|
165 |
-
// We'll even let you use markup this time!
|
166 |
-
$excerpt = false;
|
167 |
-
if ( !isset($final_sections['description']) ) {
|
168 |
-
$final_sections = array_merge(array('description' => $this->filter_text( $_short_description[2], true )), $final_sections);
|
169 |
-
$excerpt = true;
|
170 |
-
}
|
171 |
-
|
172 |
-
|
173 |
-
// dump the non-special sections into $remaining_content
|
174 |
-
// their order will be determined by their original order in the readme.txt
|
175 |
-
$remaining_content = '';
|
176 |
-
foreach ( $sections as $s_name => $s_data ) {
|
177 |
-
$remaining_content .= "\n<h3>{$s_data['title']}</h3>\n{$s_data['content']}";
|
178 |
-
}
|
179 |
-
$remaining_content = trim($remaining_content);
|
180 |
-
|
181 |
-
|
182 |
-
// All done!
|
183 |
-
// $r['tags'] and $r['contributors'] are simple arrays
|
184 |
-
// $r['sections'] is an array with named elements
|
185 |
-
$r = array(
|
186 |
-
'name' => $name,
|
187 |
-
'tags' => $tags,
|
188 |
-
'requires_at_least' => $requires_at_least,
|
189 |
-
'tested_up_to' => $tested_up_to,
|
190 |
-
'stable_tag' => $stable_tag,
|
191 |
-
'contributors' => $contributors,
|
192 |
-
'donate_link' => $donate_link,
|
193 |
-
'short_description' => $short_description,
|
194 |
-
'screenshots' => $final_screenshots,
|
195 |
-
'is_excerpt' => $excerpt,
|
196 |
-
'is_truncated' => $truncated,
|
197 |
-
'sections' => $final_sections,
|
198 |
-
'remaining_content' => $remaining_content,
|
199 |
-
'upgrade_notice' => $upgrade_notice
|
200 |
-
);
|
201 |
-
|
202 |
-
return $r;
|
203 |
-
}
|
204 |
-
|
205 |
-
function chop_string( $string, $chop ) { // chop a "prefix" from a string: Agressive! uses strstr not 0 === strpos
|
206 |
-
if ( $_string = strstr($string, $chop) ) {
|
207 |
-
$_string = substr($_string, strlen($chop));
|
208 |
-
return trim($_string);
|
209 |
-
} else {
|
210 |
-
return trim($string);
|
211 |
-
}
|
212 |
-
}
|
213 |
-
|
214 |
-
function user_sanitize( $text, $strict = false ) { // whitelisted chars
|
215 |
-
if ( function_exists('user_sanitize') ) // bbPress native
|
216 |
-
return user_sanitize( $text, $strict );
|
217 |
-
|
218 |
-
if ( $strict ) {
|
219 |
-
$text = preg_replace('/[^a-z0-9-]/i', '', $text);
|
220 |
-
$text = preg_replace('|-+|', '-', $text);
|
221 |
-
} else {
|
222 |
-
$text = preg_replace('/[^a-z0-9_-]/i', '', $text);
|
223 |
-
}
|
224 |
-
return $text;
|
225 |
-
}
|
226 |
-
|
227 |
-
function sanitize_text( $text ) { // not fancy
|
228 |
-
$text = strip_tags($text);
|
229 |
-
$text = esc_html($text);
|
230 |
-
$text = trim($text);
|
231 |
-
return $text;
|
232 |
-
}
|
233 |
-
|
234 |
-
function filter_text( $text, $markdown = false ) { // fancy, Markdown
|
235 |
-
$text = trim($text);
|
236 |
-
|
237 |
-
$text = call_user_func( array( __CLASS__, 'code_trick' ), $text, $markdown ); // A better parser than Markdown's for: backticks -> CODE
|
238 |
-
|
239 |
-
if ( $markdown ) { // Parse markdown.
|
240 |
-
if ( !class_exists('Parsedown', false) ) {
|
241 |
-
require_once(dirname(__FILE__) . '/Parsedown' . (version_compare(PHP_VERSION, '5.3.0', '>=') ? '' : 'Legacy') . '.php');
|
242 |
-
}
|
243 |
-
$instance = Parsedown::instance();
|
244 |
-
$text = $instance->text($text);
|
245 |
-
}
|
246 |
-
|
247 |
-
$allowed = array(
|
248 |
-
'a' => array(
|
249 |
-
'href' => array(),
|
250 |
-
'title' => array(),
|
251 |
-
'rel' => array()),
|
252 |
-
'blockquote' => array('cite' => array()),
|
253 |
-
'br' => array(),
|
254 |
-
'p' => array(),
|
255 |
-
'code' => array(),
|
256 |
-
'pre' => array(),
|
257 |
-
'em' => array(),
|
258 |
-
'strong' => array(),
|
259 |
-
'ul' => array(),
|
260 |
-
'ol' => array(),
|
261 |
-
'li' => array(),
|
262 |
-
'h3' => array(),
|
263 |
-
'h4' => array()
|
264 |
-
);
|
265 |
-
|
266 |
-
$text = balanceTags($text);
|
267 |
-
|
268 |
-
$text = wp_kses( $text, $allowed );
|
269 |
-
$text = trim($text);
|
270 |
-
return $text;
|
271 |
-
}
|
272 |
-
|
273 |
-
function code_trick( $text, $markdown ) { // Don't use bbPress native function - it's incompatible with Markdown
|
274 |
-
// If doing markdown, first take any user formatted code blocks and turn them into backticks so that
|
275 |
-
// markdown will preserve things like underscores in code blocks
|
276 |
-
if ( $markdown )
|
277 |
-
$text = preg_replace_callback("!(<pre><code>|<code>)(.*?)(</code></pre>|</code>)!s", array( __CLASS__,'decodeit'), $text);
|
278 |
-
|
279 |
-
$text = str_replace(array("\r\n", "\r"), "\n", $text);
|
280 |
-
if ( !$markdown ) {
|
281 |
-
// This gets the "inline" code blocks, but can't be used with Markdown.
|
282 |
-
$text = preg_replace_callback("|(`)(.*?)`|", array( __CLASS__, 'encodeit'), $text);
|
283 |
-
// This gets the "block level" code blocks and converts them to PRE CODE
|
284 |
-
$text = preg_replace_callback("!(^|\n)`(.*?)`!s", array( __CLASS__, 'encodeit'), $text);
|
285 |
-
} else {
|
286 |
-
// Markdown can do inline code, we convert bbPress style block level code to Markdown style
|
287 |
-
$text = preg_replace_callback("!(^|\n)([ \t]*?)`(.*?)`!s", array( __CLASS__, 'indent'), $text);
|
288 |
-
}
|
289 |
-
return $text;
|
290 |
-
}
|
291 |
-
|
292 |
-
function indent( $matches ) {
|
293 |
-
$text = $matches[3];
|
294 |
-
$text = preg_replace('|^|m', $matches[2] . ' ', $text);
|
295 |
-
return $matches[1] . $text;
|
296 |
-
}
|
297 |
-
|
298 |
-
function encodeit( $matches ) {
|
299 |
-
if ( function_exists('encodeit') ) // bbPress native
|
300 |
-
return encodeit( $matches );
|
301 |
-
|
302 |
-
$text = trim($matches[2]);
|
303 |
-
$text = htmlspecialchars($text, ENT_QUOTES);
|
304 |
-
$text = str_replace(array("\r\n", "\r"), "\n", $text);
|
305 |
-
$text = preg_replace("|\n\n\n+|", "\n\n", $text);
|
306 |
-
$text = str_replace('&lt;', '<', $text);
|
307 |
-
$text = str_replace('&gt;', '>', $text);
|
308 |
-
$text = "<code>$text</code>";
|
309 |
-
if ( "`" != $matches[1] )
|
310 |
-
$text = "<pre>$text</pre>";
|
311 |
-
return $text;
|
312 |
-
}
|
313 |
-
|
314 |
-
function decodeit( $matches ) {
|
315 |
-
if ( function_exists('decodeit') ) // bbPress native
|
316 |
-
return decodeit( $matches );
|
317 |
-
|
318 |
-
$text = $matches[2];
|
319 |
-
$trans_table = array_flip(get_html_translation_table(HTML_ENTITIES));
|
320 |
-
$text = strtr($text, $trans_table);
|
321 |
-
$text = str_replace('<br />', '', $text);
|
322 |
-
$text = str_replace('&', '&', $text);
|
323 |
-
$text = str_replace(''', "'", $text);
|
324 |
-
if ( '<pre><code>' == $matches[1] )
|
325 |
-
$text = "\n$text\n";
|
326 |
-
return "`$text`";
|
327 |
-
}
|
328 |
-
|
329 |
-
} // end class
|
330 |
-
|
331 |
-
Class Automattic_Readme extends PucReadmeParser {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/processes/class-mailchimp-woocommerce-abstract-sync.php
CHANGED
@@ -102,7 +102,7 @@ abstract class MailChimp_WooCommerce_Abtstract_Sync extends WP_Job
|
|
102 |
// if we've got a 0 count, that means we're done.
|
103 |
if ($page->count <= 0) {
|
104 |
|
105 |
-
mailchimp_debug(get_called_class().'@handle', $this->getResourceType().' ::
|
106 |
|
107 |
// reset the resource page back to 1
|
108 |
$this->resourceComplete($this->getResourceType());
|
102 |
// if we've got a 0 count, that means we're done.
|
103 |
if ($page->count <= 0) {
|
104 |
|
105 |
+
mailchimp_debug(get_called_class().'@handle', $this->getResourceType().' :: completing now!');
|
106 |
|
107 |
// reset the resource page back to 1
|
108 |
$this->resourceComplete($this->getResourceType());
|
includes/processes/class-mailchimp-woocommerce-cart-update.php
CHANGED
@@ -125,7 +125,7 @@ class MailChimp_WooCommerce_Cart_Update extends WP_Job
|
|
125 |
|
126 |
} catch (\Exception $e) {
|
127 |
|
128 |
-
|
129 |
|
130 |
// if we have an error it's most likely due to a product not being found.
|
131 |
// let's loop through each item, verify that we have the product or not.
|
@@ -138,8 +138,6 @@ class MailChimp_WooCommerce_Cart_Update extends WP_Job
|
|
138 |
}
|
139 |
}
|
140 |
|
141 |
-
mailchimp_log('abandoned_cart.submitting', "email: {$customer->getEmailAddress()}");
|
142 |
-
|
143 |
// if the post is successful we're all good.
|
144 |
$api->addCart($store_id, $cart, false);
|
145 |
|
@@ -149,7 +147,7 @@ class MailChimp_WooCommerce_Cart_Update extends WP_Job
|
|
149 |
|
150 |
} catch (\Exception $e) {
|
151 |
update_option('mailchimp-woocommerce-cart-error', $e->getMessage());
|
152 |
-
|
153 |
}
|
154 |
|
155 |
return false;
|
@@ -162,7 +160,8 @@ class MailChimp_WooCommerce_Cart_Update extends WP_Job
|
|
162 |
*/
|
163 |
protected function transformLineItem($hash, $item)
|
164 |
{
|
165 |
-
$product =
|
|
|
166 |
|
167 |
$line = new MailChimp_WooCommerce_LineItem();
|
168 |
$line->setId($hash);
|
@@ -175,7 +174,7 @@ class MailChimp_WooCommerce_Cart_Update extends WP_Job
|
|
175 |
}
|
176 |
|
177 |
$line->setQuantity($item['quantity']);
|
178 |
-
$line->setPrice($
|
179 |
|
180 |
return $line;
|
181 |
}
|
125 |
|
126 |
} catch (\Exception $e) {
|
127 |
|
128 |
+
mailchimp_error('abandoned_cart.error', "email: {$customer->getEmailAddress()} :: attempting product update :: {$e->getMessage()}");
|
129 |
|
130 |
// if we have an error it's most likely due to a product not being found.
|
131 |
// let's loop through each item, verify that we have the product or not.
|
138 |
}
|
139 |
}
|
140 |
|
|
|
|
|
141 |
// if the post is successful we're all good.
|
142 |
$api->addCart($store_id, $cart, false);
|
143 |
|
147 |
|
148 |
} catch (\Exception $e) {
|
149 |
update_option('mailchimp-woocommerce-cart-error', $e->getMessage());
|
150 |
+
mailchimp_error('abandoned_cart.error', $e);
|
151 |
}
|
152 |
|
153 |
return false;
|
160 |
*/
|
161 |
protected function transformLineItem($hash, $item)
|
162 |
{
|
163 |
+
$product = wc_get_product($item['product_id']);
|
164 |
+
$price = $product ? $product->get_price() : 0;
|
165 |
|
166 |
$line = new MailChimp_WooCommerce_LineItem();
|
167 |
$line->setId($hash);
|
174 |
}
|
175 |
|
176 |
$line->setQuantity($item['quantity']);
|
177 |
+
$line->setPrice($price);
|
178 |
|
179 |
return $line;
|
180 |
}
|
includes/processes/class-mailchimp-woocommerce-process-coupons.php
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 10/06/17
|
9 |
+
* Time: 10:57 AM
|
10 |
+
*/
|
11 |
+
class MailChimp_WooCommerce_Process_Coupons extends MailChimp_WooCommerce_Abtstract_Sync
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* @var string
|
15 |
+
*/
|
16 |
+
protected $action = 'mailchimp_woocommerce_process_coupons';
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @return string
|
20 |
+
*/
|
21 |
+
public function getResourceType()
|
22 |
+
{
|
23 |
+
return 'coupons';
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @param WC_Coupon $item
|
28 |
+
*
|
29 |
+
* @return mixed
|
30 |
+
*/
|
31 |
+
protected function iterate($item)
|
32 |
+
{
|
33 |
+
if ($item instanceof MailChimp_WooCommerce_PromoCode) {
|
34 |
+
|
35 |
+
mailchimp_debug('promo_code_sync', "#{$item->getId()}", $item->toArray());
|
36 |
+
|
37 |
+
try {
|
38 |
+
$this->mailchimp()->addPromoRule($this->store_id, $item->getAttachedPromoRule(), true);
|
39 |
+
$response = $this->mailchimp()->addPromoCodeForRule($this->store_id, $item->getAttachedPromoRule(), $item, true);
|
40 |
+
mailchimp_log('coupon_sync.success', "update promo rule :: #{$item->getCode()}");
|
41 |
+
return $response;
|
42 |
+
} catch (MailChimp_WooCommerce_ServerError $e) {
|
43 |
+
mailchimp_error('order_submit.error', mailchimp_error_trace($e, "update promo rule :: {$item->getCode()}"));
|
44 |
+
return false;
|
45 |
+
} catch (MailChimp_WooCommerce_Error $e) {
|
46 |
+
mailchimp_error('order_submit.error', mailchimp_error_trace($e, "update promo rule :: {$item->getCode()}"));
|
47 |
+
return false;
|
48 |
+
} catch (Exception $e) {
|
49 |
+
mailchimp_error('order_submit.error', mailchimp_error_trace($e, "update promo rule :: {$item->getCode()}"));
|
50 |
+
return false;
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
mailchimp_debug('coupon_sync', 'no coupon found', $item);
|
55 |
+
|
56 |
+
return false;
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* After the resources have been loaded and pushed
|
61 |
+
*/
|
62 |
+
protected function complete()
|
63 |
+
{
|
64 |
+
mailchimp_log('coupon_sync.completed', 'Done with the coupon sync.');
|
65 |
+
|
66 |
+
// add a timestamp for the orders sync completion
|
67 |
+
$this->setResourceCompleteTime();
|
68 |
+
}
|
69 |
+
}
|
includes/processes/class-mailchimp-woocommerce-process-orders.php
CHANGED
@@ -56,7 +56,7 @@ class MailChimp_WooCommerce_Process_Orders extends MailChimp_WooCommerce_Abtstra
|
|
56 |
$response = $this->mailchimp()->$call($this->store_id, $item, false);
|
57 |
|
58 |
if (empty($response)) {
|
59 |
-
|
60 |
return $response;
|
61 |
}
|
62 |
|
@@ -67,13 +67,13 @@ class MailChimp_WooCommerce_Process_Orders extends MailChimp_WooCommerce_Abtstra
|
|
67 |
return $response;
|
68 |
|
69 |
} catch (MailChimp_WooCommerce_ServerError $e) {
|
70 |
-
|
71 |
return false;
|
72 |
} catch (MailChimp_WooCommerce_Error $e) {
|
73 |
-
|
74 |
return false;
|
75 |
} catch (Exception $e) {
|
76 |
-
|
77 |
return false;
|
78 |
}
|
79 |
}
|
56 |
$response = $this->mailchimp()->$call($this->store_id, $item, false);
|
57 |
|
58 |
if (empty($response)) {
|
59 |
+
mailchimp_error('order_submit.failure', "$call :: #{$item->getId()} :: email: {$item->getCustomer()->getEmailAddress()} produced a blank response from MailChimp");
|
60 |
return $response;
|
61 |
}
|
62 |
|
67 |
return $response;
|
68 |
|
69 |
} catch (MailChimp_WooCommerce_ServerError $e) {
|
70 |
+
mailchimp_error('order_submit.error', mailchimp_error_trace($e, "$call :: {$item->getId()}"));
|
71 |
return false;
|
72 |
} catch (MailChimp_WooCommerce_Error $e) {
|
73 |
+
mailchimp_error('order_submit.error', mailchimp_error_trace($e, "$call :: {$item->getId()}"));
|
74 |
return false;
|
75 |
} catch (Exception $e) {
|
76 |
+
mailchimp_error('order_submit.error', mailchimp_error_trace($e, "$call :: {$item->getId()}"));
|
77 |
return false;
|
78 |
}
|
79 |
}
|
includes/processes/class-mailchimp-woocommerce-process-products.php
CHANGED
@@ -56,11 +56,11 @@ class MailChimp_WooCommerce_Process_Products extends MailChimp_WooCommerce_Abtst
|
|
56 |
return $response;
|
57 |
|
58 |
} catch (MailChimp_WooCommerce_ServerError $e) {
|
59 |
-
|
60 |
} catch (MailChimp_WooCommerce_Error $e) {
|
61 |
-
|
62 |
} catch (Exception $e) {
|
63 |
-
|
64 |
}
|
65 |
}
|
66 |
|
56 |
return $response;
|
57 |
|
58 |
} catch (MailChimp_WooCommerce_ServerError $e) {
|
59 |
+
mailchimp_error('product_sync.error', mailchimp_error_trace($e, "addStoreProduct :: {$item->getId()}"));
|
60 |
} catch (MailChimp_WooCommerce_Error $e) {
|
61 |
+
mailchimp_error('product_sync.error', mailchimp_error_trace($e, "addStoreProduct :: {$item->getId()}"));
|
62 |
} catch (Exception $e) {
|
63 |
+
mailchimp_error('product_sync.error', mailchimp_error_trace($e, "addStoreProduct :: {$item->getId()}"));
|
64 |
}
|
65 |
}
|
66 |
|
includes/processes/class-mailchimp-woocommerce-single-coupon.php
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@vextras.com
|
8 |
+
* Date: 10/6/17
|
9 |
+
* Time: 11:14 AM
|
10 |
+
*/
|
11 |
+
class MailChimp_WooCommerce_SingleCoupon extends WP_Job
|
12 |
+
{
|
13 |
+
public $coupon_data;
|
14 |
+
public $post_id;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* MailChimp_WooCommerce_Coupon_Sync constructor.
|
18 |
+
* @param $post_id
|
19 |
+
*/
|
20 |
+
public function __construct($post_id = null)
|
21 |
+
{
|
22 |
+
$this->post_id = $post_id;
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @return null
|
27 |
+
*/
|
28 |
+
public function handle()
|
29 |
+
{
|
30 |
+
try {
|
31 |
+
$api = mailchimp_get_api();
|
32 |
+
$store_id = mailchimp_get_store_id();
|
33 |
+
|
34 |
+
if (empty($this->post_id)) {
|
35 |
+
mailchimp_error('promo_code.failure', "could not process coupon {$this->post_id}");
|
36 |
+
return;
|
37 |
+
}
|
38 |
+
|
39 |
+
$transformer = new MailChimp_WooCommerce_Transform_Coupons();
|
40 |
+
$code = $transformer->transform($this->post_id);
|
41 |
+
|
42 |
+
$api->addPromoRule($store_id, $code->getAttachedPromoRule(), true);
|
43 |
+
$api->addPromoCodeForRule($store_id, $code->getAttachedPromoRule(), $code, true);
|
44 |
+
|
45 |
+
mailchimp_log('promo_code.update', "updated promo code {$code->getCode()}");
|
46 |
+
} catch (\Exception $e) {
|
47 |
+
$promo_code = isset($code) ? "code {$code->getCode()}" : "id {$this->post_id}";
|
48 |
+
mailchimp_error('promo_code.error', mailchimp_error_trace($e, "error updating promo {$promo_code}"));
|
49 |
+
}
|
50 |
+
}
|
51 |
+
}
|
includes/processes/class-mailchimp-woocommerce-single-order.php
CHANGED
@@ -51,6 +51,7 @@ class MailChimp_WooCommerce_Single_Order extends WP_Job
|
|
51 |
if (!empty($store_id) && is_array($options) && isset($options['mailchimp_api_key'])) {
|
52 |
|
53 |
if (!($woo_order_number = $this->getRealOrderNumber())) {
|
|
|
54 |
return false;
|
55 |
}
|
56 |
|
@@ -117,7 +118,7 @@ class MailChimp_WooCommerce_Single_Order extends WP_Job
|
|
117 |
$api_response = $api->$call($store_id, $order, false);
|
118 |
|
119 |
if (empty($api_response)) {
|
120 |
-
|
121 |
return $api_response;
|
122 |
}
|
123 |
|
@@ -133,7 +134,9 @@ class MailChimp_WooCommerce_Single_Order extends WP_Job
|
|
133 |
|
134 |
} catch (\Exception $e) {
|
135 |
|
136 |
-
|
|
|
|
|
137 |
|
138 |
if (!isset($order)) {
|
139 |
// transform the order
|
@@ -170,7 +173,7 @@ class MailChimp_WooCommerce_Single_Order extends WP_Job
|
|
170 |
return $api_response;
|
171 |
|
172 |
} catch (\Exception $e) {
|
173 |
-
|
174 |
}
|
175 |
}
|
176 |
}
|
@@ -192,7 +195,7 @@ class MailChimp_WooCommerce_Single_Order extends WP_Job
|
|
192 |
return $this->woo_order_number = $woo->get_order_number();
|
193 |
} catch (\Exception $e) {
|
194 |
$this->woo_order_number = false;
|
195 |
-
|
196 |
return false;
|
197 |
}
|
198 |
}
|
51 |
if (!empty($store_id) && is_array($options) && isset($options['mailchimp_api_key'])) {
|
52 |
|
53 |
if (!($woo_order_number = $this->getRealOrderNumber())) {
|
54 |
+
mailchimp_log('order_submit.failure', "There is no real order number to use.");
|
55 |
return false;
|
56 |
}
|
57 |
|
118 |
$api_response = $api->$call($store_id, $order, false);
|
119 |
|
120 |
if (empty($api_response)) {
|
121 |
+
mailchimp_error('order_submit.failure', "$call :: #{$order->getId()} :: email: {$order->getCustomer()->getEmailAddress()} produced a blank response from MailChimp");
|
122 |
return $api_response;
|
123 |
}
|
124 |
|
134 |
|
135 |
} catch (\Exception $e) {
|
136 |
|
137 |
+
$message = strtolower($e->getMessage());
|
138 |
+
|
139 |
+
mailchimp_error('order_submit.tracing_error', $e);
|
140 |
|
141 |
if (!isset($order)) {
|
142 |
// transform the order
|
173 |
return $api_response;
|
174 |
|
175 |
} catch (\Exception $e) {
|
176 |
+
mailchimp_error('order_submit.error', mailchimp_error_trace($e, 'deleting-customer-re-add :: #'.$this->order_id));
|
177 |
}
|
178 |
}
|
179 |
}
|
195 |
return $this->woo_order_number = $woo->get_order_number();
|
196 |
} catch (\Exception $e) {
|
197 |
$this->woo_order_number = false;
|
198 |
+
mailchimp_error('order_sync.failure', mailchimp_error_trace($e, "{$this->order_id} could not be loaded"));
|
199 |
return false;
|
200 |
}
|
201 |
}
|
includes/processes/class-mailchimp-woocommerce-single-product.php
CHANGED
@@ -68,11 +68,11 @@ class MailChimp_WooCommerce_Single_Product extends WP_Job
|
|
68 |
return $product;
|
69 |
|
70 |
} catch (MailChimp_WooCommerce_ServerError $e) {
|
71 |
-
|
72 |
} catch (MailChimp_WooCommerce_Error $e) {
|
73 |
-
mailchimp_log('product_submit.error', "addStoreProduct :: #{$this->product_id}
|
74 |
} catch (Exception $e) {
|
75 |
-
mailchimp_log('product_submit.error', "addStoreProduct :: #{$this->product_id}
|
76 |
}
|
77 |
|
78 |
return false;
|
68 |
return $product;
|
69 |
|
70 |
} catch (MailChimp_WooCommerce_ServerError $e) {
|
71 |
+
mailchimp_error('product_submit.error', mailchimp_error_trace($e, "addStoreProduct :: #{$this->product_id}"));
|
72 |
} catch (MailChimp_WooCommerce_Error $e) {
|
73 |
+
mailchimp_log('product_submit.error', mailchimp_error_trace($e, "addStoreProduct :: #{$this->product_id}"));
|
74 |
} catch (Exception $e) {
|
75 |
+
mailchimp_log('product_submit.error', mailchimp_error_trace($e, "addStoreProduct :: #{$this->product_id}"));
|
76 |
}
|
77 |
|
78 |
return false;
|
includes/processes/class-mailchimp-woocommerce-user-submit.php
CHANGED
@@ -135,7 +135,7 @@ class MailChimp_WooCommerce_User_Submit extends WP_Job
|
|
135 |
return false;
|
136 |
}
|
137 |
|
138 |
-
|
139 |
}
|
140 |
|
141 |
return false;
|
135 |
return false;
|
136 |
}
|
137 |
|
138 |
+
mailchimp_error('member.sync', mailchimp_error_trace($e, $user->user_email));
|
139 |
}
|
140 |
|
141 |
return false;
|
includes/vendor/queue.php
CHANGED
File without changes
|
includes/vendor/queue/classes/cli/queue-command.php
CHANGED
File without changes
|
includes/vendor/queue/classes/worker/wp-http-worker.php
CHANGED
File without changes
|
includes/vendor/queue/classes/worker/wp-worker.php
CHANGED
@@ -54,7 +54,7 @@ if ( ! class_exists( 'WP_Worker' ) ) {
|
|
54 |
$this->payload->set_job( $job );
|
55 |
|
56 |
try {
|
57 |
-
|
58 |
|
59 |
if ( $this->payload->is_released() ) {
|
60 |
// Job manually released, release back onto queue
|
54 |
$this->payload->set_job( $job );
|
55 |
|
56 |
try {
|
57 |
+
$this->payload->handle();
|
58 |
|
59 |
if ( $this->payload->is_released() ) {
|
60 |
// Job manually released, release back onto queue
|
includes/vendor/queue/classes/wp-job.php
CHANGED
File without changes
|
includes/vendor/queue/classes/wp-queue.php
CHANGED
File without changes
|
index.php
CHANGED
File without changes
|
languages/mailchimp-woocommerce.pot
CHANGED
File without changes
|
mailchimp-woocommerce.php
CHANGED
@@ -16,7 +16,7 @@
|
|
16 |
* Plugin Name: MailChimp for WooCommerce
|
17 |
* Plugin URI: https://mailchimp.com/connect-your-store/
|
18 |
* Description: MailChimp - WooCommerce plugin
|
19 |
-
* Version: 2.0
|
20 |
* Author: MailChimp
|
21 |
* Author URI: https://mailchimp.com
|
22 |
* License: GPL-2.0+
|
@@ -43,7 +43,7 @@ function mailchimp_environment_variables() {
|
|
43 |
return (object) array(
|
44 |
'repo' => 'master',
|
45 |
'environment' => 'production',
|
46 |
-
'version' => '2.0
|
47 |
'wp_version' => (empty($wp_version) ? 'Unknown' : $wp_version),
|
48 |
'wc_version' => class_exists('WC') ? WC()->version : null,
|
49 |
'logging' => ($o && is_array($o) && isset($o['mailchimp_logging'])) ? $o['mailchimp_logging'] : 'none',
|
@@ -268,6 +268,31 @@ function mailchimp_log($action, $message, $data = array()) {
|
|
268 |
}
|
269 |
}
|
270 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
271 |
/**
|
272 |
* Determine if a given string contains a given substring.
|
273 |
*
|
@@ -340,6 +365,12 @@ function mailchimp_update_connected_site_script() {
|
|
340 |
// if we have a store
|
341 |
if (($store = $api->getStore($store_id))) {
|
342 |
|
|
|
|
|
|
|
|
|
|
|
|
|
343 |
// see if we have a connected site script url/fragment
|
344 |
$url = $store->getConnectedSiteScriptUrl();
|
345 |
$fragment = $store->getConnectedSiteScriptFragment();
|
16 |
* Plugin Name: MailChimp for WooCommerce
|
17 |
* Plugin URI: https://mailchimp.com/connect-your-store/
|
18 |
* Description: MailChimp - WooCommerce plugin
|
19 |
+
* Version: 2.1.0
|
20 |
* Author: MailChimp
|
21 |
* Author URI: https://mailchimp.com
|
22 |
* License: GPL-2.0+
|
43 |
return (object) array(
|
44 |
'repo' => 'master',
|
45 |
'environment' => 'production',
|
46 |
+
'version' => '2.1.0',
|
47 |
'wp_version' => (empty($wp_version) ? 'Unknown' : $wp_version),
|
48 |
'wc_version' => class_exists('WC') ? WC()->version : null,
|
49 |
'logging' => ($o && is_array($o) && isset($o['mailchimp_logging'])) ? $o['mailchimp_logging'] : 'none',
|
268 |
}
|
269 |
}
|
270 |
|
271 |
+
/**
|
272 |
+
* @param $action
|
273 |
+
* @param $message
|
274 |
+
* @param array $data
|
275 |
+
* @return array|WP_Error
|
276 |
+
*/
|
277 |
+
function mailchimp_error($action, $message, $data = array()) {
|
278 |
+
if (mailchimp_environment_variables()->logging !== 'none') {
|
279 |
+
if ($message instanceof \Exception) $message = mailchimp_error_trace($message);
|
280 |
+
if (is_array($data) && !empty($data)) $message .= " :: ".wc_print_r($data, true);
|
281 |
+
wc_get_logger()->error("{$action} :: {$message}", array('source' => 'mailchimp_woocommerce'));
|
282 |
+
}
|
283 |
+
}
|
284 |
+
|
285 |
+
/**
|
286 |
+
* @param Exception $e
|
287 |
+
* @param string $wrap
|
288 |
+
* @return string
|
289 |
+
*/
|
290 |
+
function mailchimp_error_trace(\Exception $e, $wrap = "") {
|
291 |
+
$error = "{$e->getMessage()} on {$e->getLine()} in {$e->getFile()}";
|
292 |
+
if (empty($wrap)) return $error;
|
293 |
+
return "{$wrap} :: {$error}";
|
294 |
+
}
|
295 |
+
|
296 |
/**
|
297 |
* Determine if a given string contains a given substring.
|
298 |
*
|
365 |
// if we have a store
|
366 |
if (($store = $api->getStore($store_id))) {
|
367 |
|
368 |
+
// handle the coupon sync if we don't have a flag that says otherwise.
|
369 |
+
$job = new MailChimp_WooCommerce_Process_Coupons();
|
370 |
+
if ($job->getData('sync.coupons.completed_at', false) === false) {
|
371 |
+
wp_queue($job);
|
372 |
+
}
|
373 |
+
|
374 |
// see if we have a connected site script url/fragment
|
375 |
$url = $store->getConnectedSiteScriptUrl();
|
376 |
$fragment = $store->getConnectedSiteScriptFragment();
|
public/class-mailchimp-woocommerce-public.php
CHANGED
File without changes
|
public/css/mailchimp-woocommerce-public.css
CHANGED
File without changes
|
public/index.php
CHANGED
File without changes
|
public/js/mailchimp-woocommerce-public.js
CHANGED
File without changes
|
public/partials/mailchimp-woocommerce-public-display.php
CHANGED
File without changes
|
uninstall.php
CHANGED
File without changes
|