Version Description
(2017-01-06) =
- Added hook to change excerpt length
- Added hook to disable the excerpt and instead display the full review content
Download this release
Release Info
Developer | geminilabs |
Plugin | Site Reviews |
Version | 1.2.2 |
Comparing to | |
See all releases |
Version 1.2.2
- activate.php +73 -0
- assets/css/site-reviews-admin.css +1 -0
- assets/css/site-reviews.css +8 -0
- assets/css/twenty-eleven.css +8 -0
- assets/css/twenty-fifteen.css +8 -0
- assets/css/twenty-fourteen.css +8 -0
- assets/css/twenty-sixteen.css +8 -0
- assets/css/twenty-ten.css +8 -0
- assets/css/twenty-thirteen.css +8 -0
- assets/css/twenty-twelve.css +8 -0
- assets/img/booking.svg +14 -0
- assets/img/facebook.svg +11 -0
- assets/img/google.svg +15 -0
- assets/img/icon.png +0 -0
- assets/img/pinned.svg +6 -0
- assets/img/star-empty.svg +6 -0
- assets/img/star-filled.svg +6 -0
- assets/img/star-half.svg +6 -0
- assets/img/tripadvisor.svg +21 -0
- assets/img/yelp.svg +14 -0
- assets/js/site-reviews-admin.js +1 -0
- assets/js/site-reviews.js +1 -0
- autoload.php +39 -0
- languages/readme.txt +5 -0
- languages/site-reviews-en_US.mo +0 -0
- languages/site-reviews-en_US.po +896 -0
- languages/site-reviews.pot +850 -0
- license.txt +2 -0
- plugin/App.php +161 -0
- plugin/Commands/EnqueueAssets.php +27 -0
- plugin/Commands/RegisterPointers.php +23 -0
- plugin/Commands/RegisterPostType.php +43 -0
- plugin/Commands/RegisterShortcodes.php +23 -0
- plugin/Commands/RegisterWidgets.php +21 -0
- plugin/Commands/SubmitReview.php +53 -0
- plugin/Commands/TogglePinned.php +27 -0
- plugin/Container.php +218 -0
- plugin/Controllers/AjaxController.php +57 -0
- plugin/Controllers/BaseController.php +121 -0
- plugin/Controllers/MainController.php +574 -0
- plugin/Controllers/ReviewController.php +69 -0
- plugin/Database.php +535 -0
- plugin/Email.php +245 -0
- plugin/Handlers/EnqueueAssets.php +118 -0
- plugin/Handlers/RegisterPointers.php +64 -0
- plugin/Handlers/RegisterPostType.php +640 -0
- plugin/Handlers/RegisterShortcodes.php +53 -0
- plugin/Handlers/RegisterWidgets.php +55 -0
- plugin/Handlers/SubmitReview.php +223 -0
- plugin/Handlers/TogglePinned.php +50 -0
- plugin/Html.php +249 -0
- plugin/Html/Field.php +365 -0
- plugin/Html/Fields/Base.php +361 -0
- plugin/Html/Fields/Checkbox.php +48 -0
- plugin/Html/Fields/Code.php +26 -0
- plugin/Html/Fields/Colorpicker.php +58 -0
- plugin/Html/Fields/Email.php +27 -0
- plugin/Html/Fields/Heading.php +33 -0
- plugin/Html/Fields/Hidden.php +39 -0
- plugin/Html/Fields/Number.php +28 -0
- plugin/Html/Fields/Progress.php +64 -0
- plugin/Html/Fields/Radio.php +33 -0
- plugin/Html/Fields/Roles.php +26 -0
- plugin/Html/Fields/Select.php +34 -0
- plugin/Html/Fields/Submit.php +31 -0
- plugin/Html/Fields/Text.php +34 -0
- plugin/Html/Fields/Textarea.php +36 -0
- plugin/Html/Fields/Url.php +27 -0
- plugin/Html/Fields/Yesno.php +31 -0
- plugin/Html/Form.php +288 -0
- plugin/Html/Normalize.php +396 -0
- plugin/Html/Partial.php +75 -0
- plugin/Html/Partials/Addon.php +50 -0
- plugin/Html/Partials/Base.php +49 -0
- plugin/Html/Partials/Filterby.php +43 -0
- plugin/Html/Partials/Rating.php +49 -0
- plugin/Html/Partials/Reviews.php +311 -0
- plugin/Html/Partials/Subsubsub.php +54 -0
- plugin/Html/Partials/Tabs.php +48 -0
- plugin/Log/LogLevel.php +18 -0
- plugin/Log/Logger.php +305 -0
- plugin/Log/LoggerInterface.php +123 -0
- plugin/Notices.php +186 -0
- plugin/Providers/MainProvider.php +64 -0
- plugin/Providers/ProviderInterface.php +21 -0
- plugin/Router.php +123 -0
- plugin/Session.php +336 -0
- plugin/Settings.php +295 -0
- plugin/Shortcode.php +103 -0
- plugin/Shortcodes/SiteReviews.php +76 -0
- plugin/Shortcodes/SiteReviewsForm.php +95 -0
- plugin/Strings.php +65 -0
- plugin/SystemInfo.php +457 -0
- plugin/Validator.php +587 -0
- plugin/Widget.php +113 -0
- plugin/Widgets/RecentReviews.php +184 -0
- plugin/Widgets/ReviewsForm.php +146 -0
- readme.txt +146 -0
- site-reviews.php +64 -0
- uninstall.php +26 -0
- vendor/sinergi/browser-detector/LICENSE +20 -0
- vendor/sinergi/browser-detector/src/Browser.php +322 -0
- vendor/sinergi/browser-detector/src/BrowserDetector.php +1006 -0
- vendor/sinergi/browser-detector/src/DetectorInterface.php +7 -0
- vendor/sinergi/browser-detector/src/InvalidArgumentException.php +7 -0
- vendor/sinergi/browser-detector/src/UserAgent.php +56 -0
- views/base.php +12 -0
- views/edit/meta.php +63 -0
- views/edit/notice.php +3 -0
- views/edit/pinned.php +39 -0
- views/edit/review.php +9 -0
- views/email/body.php +3 -0
- views/email/footer.php +4 -0
- views/email/header.php +9 -0
- views/email/index.php +5 -0
- views/email/templates/review-notification.php +11 -0
- views/menu/addons/addons.php +25 -0
- views/menu/help/documentation/hooks.php +28 -0
- views/menu/help/documentation/shortcodes.php +33 -0
- views/menu/help/documentation/support.php +37 -0
- views/menu/help/system.php +50 -0
- views/menu/index.php +38 -0
- views/menu/settings/licenses.php +3 -0
- views/menu/settings/settings/form.php +3 -0
- views/menu/settings/settings/general.php +3 -0
- views/menu/settings/welcome.php +26 -0
- views/menu/status.php +81 -0
- views/submit/index.php +125 -0
activate.php
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
defined( 'WPINC' ) || die;
|
12 |
+
|
13 |
+
if( !function_exists( 'glsr_version_check' ) ) {
|
14 |
+
function glsr_version_check() {
|
15 |
+
global $wp_version;
|
16 |
+
return [
|
17 |
+
'php' => version_compare( PHP_VERSION, '5.4.0', '<' ),
|
18 |
+
'wordpress' => version_compare( $wp_version, '4.0', '<' ),
|
19 |
+
];
|
20 |
+
}
|
21 |
+
}
|
22 |
+
|
23 |
+
if( !function_exists( 'glsr_deactivate_plugin' ) ) {
|
24 |
+
function glsr_deactivate_plugin( $plugin )
|
25 |
+
{
|
26 |
+
$check = glsr_version_check();
|
27 |
+
|
28 |
+
if( !$check['php'] && !$check['wordpress'] )return;
|
29 |
+
|
30 |
+
$plugin_name = plugin_basename( dirname( __FILE__ ) . '/site-reviews.php' );
|
31 |
+
|
32 |
+
if( $plugin == $plugin_name ) {
|
33 |
+
$paged = filter_input( INPUT_GET, 'paged' );
|
34 |
+
$s = filter_input( INPUT_GET, 's' );
|
35 |
+
$status = filter_input( INPUT_GET, 'plugin_status' );
|
36 |
+
|
37 |
+
wp_redirect( self_admin_url( sprintf( 'plugins.php?plugin_status=%s&paged=%s&s=%s', $status, $paged, $s ) ) );
|
38 |
+
die;
|
39 |
+
}
|
40 |
+
|
41 |
+
deactivate_plugins( $plugin_name );
|
42 |
+
|
43 |
+
$title = __( 'The Site Reviews plugin was deactivated.', 'site-reviews' );
|
44 |
+
$msg_1 = '';
|
45 |
+
$msg_2 = '';
|
46 |
+
|
47 |
+
if( $check['php'] ) {
|
48 |
+
$msg_1 = __( 'Sorry, this plugin requires PHP version 5.4 or greater in order to work properly.', 'site-reviews' );
|
49 |
+
$msg_2 = __( 'Please contact your hosting provider or server administrator to upgrade the version of PHP on your server (your server is running PHP version %s), or try to find an alternative plugin.', 'site-reviews' );
|
50 |
+
$msg_2 = sprintf( $msg_2, PHP_VERSION );
|
51 |
+
}
|
52 |
+
|
53 |
+
// WordPress check overrides the PHP check
|
54 |
+
if( $check['wordpress'] ) {
|
55 |
+
$msg_1 = __( 'Sorry, this plugin requires WordPress version 4.0.0 or greater in order to work properly.', 'site-reviews' );
|
56 |
+
$msg_2 = sprintf( '<a href="%s">%s</a>', admin_url( 'update-core.php' ), __( 'Update WordPress', 'site-reviews' ) );
|
57 |
+
}
|
58 |
+
|
59 |
+
printf( '<div id="message" class="notice notice-error error is-dismissible"><p><strong>%s</strong></p><p>%s</p><p>%s</p></div>',
|
60 |
+
$title,
|
61 |
+
$msg_1,
|
62 |
+
$msg_2
|
63 |
+
);
|
64 |
+
}
|
65 |
+
}
|
66 |
+
|
67 |
+
$check = glsr_version_check();
|
68 |
+
|
69 |
+
// PHP >= 5.4.0 and WordPress version >= 4.0.0 check
|
70 |
+
if( $check['php'] || $check['wordpress'] ) {
|
71 |
+
add_action( 'activated_plugin', 'glsr_deactivate_plugin' );
|
72 |
+
add_action( 'admin_notices', 'glsr_deactivate_plugin' );
|
73 |
+
}
|
assets/css/site-reviews-admin.css
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
.glsr-addon-name,.glsr-addon-wrap,.glsr-progress *{box-sizing:border-box}.glsr-addons{margin:-20px 0 0 -20px}.glsr-addon-description p,.glsr-addon-name{text-overflow:ellipsis;margin:0;overflow:hidden}.glsr-addon-wrap{float:left;position:relative;width:100%;padding:20px 0 0 20px}@media only screen and (min-width:480px){.glsr-addon-wrap{width:50%}}@media only screen and (min-width:782px){.glsr-addon-wrap{width:33.33%}}@media only screen and (min-width:1400px){.glsr-addon-wrap{width:25%}}.glsr-addon{border:1px solid #ddd;box-shadow:0 1px 1px -1px rgba(0,0,0,.1)}.glsr-addon-description{background-color:#fff;padding:1em 15px}.glsr-addon-description p{height:calc(13px * 1.5 * 4)}.glsr-addon-name{height:48px;font-size:15px;font-weight:600;background:rgba(255,255,255,.65);box-shadow:inset 0 1px 0 rgba(0,0,0,.1);white-space:nowrap;padding:15px}.glsr-addon-screenshot{display:block;background-position:50%;background-repeat:no-repeat;background-size:100% auto;border-bottom:1px solid #ddd;padding-top:56.25%}.glsr-addon-screenshot[data-name=booking]{background-image:url(../img/booking.svg);background-color:#00347D}.glsr-addon-screenshot[data-name=facebook]{background-image:url(../img/facebook.svg);background-color:#3B5998}.glsr-addon-screenshot[data-name=google]{background-image:url(../img/google.svg);background-color:#C6DAFC}.glsr-addon-screenshot[data-name=tripadvisor]{background-color:#4b7e37;background-image:url(../img/tripadvisor.svg)}.glsr-addon-screenshot[data-name=yelp]{background-image:url(../img/yelp.svg);background-color:#d32323}.glsr-addon-link{position:absolute;right:15px;bottom:10px}.glsr-addon-link:after{display:inline-block;content:"\00a0\f504";font-family:dashicons;vertical-align:inherit;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media screen and (max-width:782px){.glsr-addon-link{bottom:5px;text-indent:-100%;padding:6px 10px!important;overflow:hidden}.glsr-addon-link:after{float:right;height:16px;width:16px;content:"\f504";font-size:18px;text-indent:0}}#dashboard_right_now li a.glsr-review-count:before,#dashboard_right_now li>span.glsr-review-count:before{content:'\f459';line-height:.8em}#titlediv #title{margin-bottom:20px}#contentdiv textarea,#titlediv #title{border:1px solid #e5e5e5;box-shadow:0 1px 1px rgba(0,0,0,.04);background-color:#fff}#contentdiv textarea,.post-type-site-review .wp-editor-area{width:100%;height:320px;font-family:Georgia,"Times New Roman","Bitstream Charter",Times,serif;font-size:16px;line-height:1.5;padding:10px;resize:vertical;-webkit-font-smoothing:antialiased!important;overflow-wrap:break-word}body.branch-4 .misc-pub-pinned:before,body.branch-4-1 .misc-pub-pinned:before,body.branch-4-2 .misc-pub-pinned:before,body.branch-4-3 .misc-pub-pinned:before{display:none}body.branch-4 .misc-pub-pinned .pinned-icon,body.branch-4-1 .misc-pub-pinned .pinned-icon,body.branch-4-2 .misc-pub-pinned .pinned-icon,body.branch-4-3 .misc-pub-pinned .pinned-icon{position:relative;display:inline-block;width:20px;padding-right:2px}body.branch-4 .misc-pub-pinned .pinned-icon svg,body.branch-4-1 .misc-pub-pinned .pinned-icon svg,body.branch-4-2 .misc-pub-pinned .pinned-icon svg,body.branch-4-3 .misc-pub-pinned .pinned-icon svg{position:absolute;top:-13px;left:-1px;width:19px;height:18px;color:#888}.misc-pub-pinned:before{display:inline-block;position:relative;content:"\f537";font:400 20px/1 dashicons;speak:none;vertical-align:top;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;top:0;left:-1px;color:#82878c;padding:0 2px 0 0}.glsr-card dt,.glsr-metabox-table td:first-child,.misc-pub-pinned .pinned-status-text{font-weight:600}.glsr-field textarea.code,.glsr-log[readonly],.post-type-site-review .code,.post-type-site-review code{font-family:"Operator Mono","Fira Code",monaco,monospace}.misc-pub-pinned .pinned-status-select{margin-top:3px}#geminilabs-site-reviews_review.postbox .inside{padding:0;margin:0}.glsr-metabox-table{width:auto;padding:0 12px 12px;margin-top:12px}.glsr-metabox-table td{line-height:1.5;vertical-align:top;padding:0 14px 8px 0}.glsr-metabox-table td:last-child{padding-bottom:0}.revert-action{line-height:23px;text-align:right;border-top:1px solid #ddd;background:#f5f5f5;padding:10px}.revert-action .spinner{float:none}body.branch-4 .notice-warning{border-color:#ffba00}.glsr-progress{position:relative;display:block;width:300px;height:24px;opacity:.65;text-align:center}@media screen and (max-width:782px){.glsr-progress{height:39px;font-size:16px}}.glsr-progress.active{opacity:1}.glsr-progress-background,.glsr-progress-bar{height:100%;background-size:40px 40px}.glsr-progress.active .glsr-progress-background,.glsr-progress.active .glsr-progress-bar{-webkit-transition:width .6s ease;transition:width .6s ease;-webkit-animation:glsr-progress-bar 1s linear infinite;animation:glsr-progress-bar 1s linear infinite}.glsr-progress-bar{position:relative;width:0;color:#fff;background-color:#616b75;background-image:-webkit-linear-gradient(135deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);box-shadow:0 1px 0 #4a5259;border-width:1px;border-style:solid;border-color:#616b75 #4a5259 #4a5259;border-radius:3px 0 0 3px;overflow:hidden;z-index:1}.glsr-progress-bar[style="width: 0%;"]{border-width:0}.glsr-progress-bar[style="width: 100%;"]{border-radius:3px}.glsr-progress-background{position:absolute;top:0;left:0;width:100%;height:25px;background-color:#f7f7f7;background-image:-webkit-linear-gradient(135deg,#fff 25%,transparent 25%,transparent 50%,#fff 50%,#fff 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,#fff 25%,transparent 25%,transparent 50%,#fff 50%,#fff 75%,transparent 75%,transparent);border-radius:3px;box-shadow:inset 0 1px 2px #e5e5e5;border:1px solid #ccc}.glsr-progress span{position:absolute;width:300px;left:0;top:0;line-height:23px;color:#555;padding:0 6px}@media screen and (max-width:782px){.glsr-progress-background{height:40px}.glsr-progress span{line-height:38px}}.glsr-progress-bar>span{color:#fff;text-shadow:0 -1px 1px #4a5259,1px 0 1px #4a5259,0 1px 1px #4a5259,-1px 0 1px #4a5259}@-webkit-keyframes glsr-progress-bar{from{background-position:0 0}to{background-position:40px 0}}@keyframes glsr-progress-bar{from{background-position:0 0}to{background-position:40px 0}}.glsr-progress.blue .glsr-progress-bar{background-color:#0073aa;box-shadow:0 1px 0 #006799;border-color:#0073aa #006799 #006799}.glsr-progress.blue .glsr-progress-bar>span{text-shadow:0 -1px 1px #006799,1px 0 1px #006799,0 1px 1px #006799,-1px 0 1px #006799}.glsr-progress.green .glsr-progress-bar{background-color:#46b44f;box-shadow:0 1px 0 #3fa247;border-color:#46b44f #3fa247 #3fa247}.glsr-progress.green .glsr-progress-bar>span{text-shadow:0 -1px 1px #3fa247,1px 0 1px #3fa247,0 1px 1px #3fa247,-1px 0 1px #3fa247}.star-rating .glsr-star{width:16px;font-size:16px;line-height:20px;color:#555}.glsr-card code,.glsr-card pre{white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}.glsr-card pre{border:1px solid #e5e5e5;border-radius:3px;position:relative;background-color:#f9f9f9;overflow:visible;padding:0}.glsr-card pre code{background:0 0;max-height:inherit;height:100%;padding:.5em;display:block;overflow:auto}.glsr-card dd,.glsr-card dt{margin:1em 0}.glsr-card code a{text-decoration:none}.wrap h2.nav-tab-wrapper{padding-top:0;margin-top:9px}.float-left{float:left;padding-right:.25em}.glsr-subsubsub{float:none;text-align:left}.glsr-log[readonly]{width:100%;background-color:rgba(255,255,255,.5);border-color:rgba(222,222,222,.75);box-shadow:inset 0 1px 2px rgba(0,0,0,.04);color:rgba(50,55,60,.75);font-weight:300;font-size:13px;-webkit-font-smoothing:antialiased!important;resize:vertical;white-space:pre;overflow:auto;padding:3px 6px}@media only screen and (max-width:782px){.glsr-card{max-width:inherit}}tr.glsr-field li,tr.glsr-field ul{margin:0 auto}.glsr-field ul.inline li{display:inline}.glsr-field ul.inline li:not(:last-child){margin-right:1em}.glsr-field{margin:1em 0;line-height:1.5}.glsr-field>label{display:block}@media screen and (max-width:782px){.glsr-field>label+input.small-text{margin-left:0}}.glsr-field>ul{margin:0}.glsr-field>ul li{margin-bottom:2px}body.branch-4 .wrap h1.page-title,body.branch-4-1 .wrap h1.page-title,body.branch-4-2 .wrap h1.page-title{font-size:23px;font-weight:400;line-height:29px;padding:9px 15px 4px 0;margin:0}body.branch-4 .wrap h2.title,body.branch-4-1 .wrap h2.title,body.branch-4-2 .wrap h2.title{color:#23282d;font-size:1.3em;font-weight:600;line-height:1.4em;padding:0;margin:1em 0}body.branch-4 .glsr-card,body.branch-4-1 .glsr-card{position:relative;margin-top:20px;padding:.7em 2em 1em;min-width:255px;max-width:520px;border:1px solid #e5e5e5;box-shadow:0 1px 1px rgba(0,0,0,.04);background:#fff}.glsr-status{max-width:1000px}.glsr-status th.site{width:25%}.glsr-status td{vertical-align:middle}.glsr-status td.site{font-weight:600}.glsr-status td.site span{font-weight:400;font-size:90%}.glsr-status .toggle-row{top:0}.glsr-status .toggle-row:before{top:6px}@media only screen and (min-width:783px){.glsr-status td.total-fetched,.glsr-status th.total-fetched{text-align:center}}@media only screen and (max-width:782px){.glsr-status th.site,.glsr-status tr:not(.inline-edit-row):not(.no-items) td.site{display:table-cell!important;width:230px!important;vertical-align:top;padding:8px 10px}.glsr-status tr:not(.inline-edit-row):not(.no-items) td.total-fetched~td:not(.site){overflow:hidden;white-space:nowrap;text-overflow:ellipsis;padding:0 10px 0 260px;margin-left:-250px;margin-bottom:8px}.glsr-status tr:not(.inline-edit-row):not(.no-items) td.total-fetched~td:before{width:240px}}@media only screen and (max-width:691px){.glsr-status th.site,.glsr-status tr:not(.inline-edit-row):not(.no-items) td.site{width:200px!important}.glsr-status tr:not(.inline-edit-row):not(.no-items) td.total-fetched~td:not(.site){padding-left:230px;margin-left:-220px}.glsr-status tr:not(.inline-edit-row):not(.no-items) td.total-fetched~td:before{width:210px}}@media only screen and (max-width:600px){.glsr-status th.site,.glsr-status tr:not(.inline-edit-row):not(.no-items) td.site{width:170px!important}.glsr-status tr:not(.inline-edit-row):not(.no-items) td.total-fetched~td:not(.site){padding-left:200px;margin-left:-190px}.glsr-status tr:not(.inline-edit-row):not(.no-items) td.total-fetched~td:before{width:180px}}@media only screen and (max-width:480px){.glsr-status th.site,.glsr-status tr:not(.inline-edit-row):not(.no-items) td.site{width:130px!important}.glsr-status tr:not(.inline-edit-row):not(.no-items) td.total-fetched~td:not(.site){padding-left:160px;margin-left:-150px}.glsr-status tr:not(.inline-edit-row):not(.no-items) td.total-fetched~td:before{width:140px}}@media only screen and (max-width:425px){.glsr-status td:before{font-weight:600}.glsr-status tr:not(.inline-edit-row):not(.no-items) td.total-fetched~td:not(.site){padding-left:10px}.glsr-status tr:not(.inline-edit-row):not(.no-items) td.total-fetched~td:before{position:static}}body.branch-4 .glsr-status.striped>tbody>:nth-child(odd),body.branch-4-1 .glsr-status.striped>tbody>:nth-child(odd){background-color:#f9f9f9}body.branch-4 .glsr-status .toggle-row,body.branch-4-1 .glsr-status .toggle-row,body.branch-4-2 .glsr-status .toggle-row{display:none;position:absolute;top:10px;right:8px;width:40px;height:40px;border:none;outline:0;background:0 0;padding:0}body.branch-4 .glsr-status .toggle-row:hover,body.branch-4-1 .glsr-status .toggle-row:hover,body.branch-4-2 .glsr-status .toggle-row:hover{cursor:pointer}body.branch-4 .glsr-status .toggle-row:focus:before,body.branch-4-1 .glsr-status .toggle-row:focus:before,body.branch-4-2 .glsr-status .toggle-row:focus:before{box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}body.branch-4 .glsr-status .toggle-row:active,body.branch-4-1 .glsr-status .toggle-row:active,body.branch-4-2 .glsr-status .toggle-row:active{box-shadow:none}body.branch-4 .glsr-status .toggle-row:before,body.branch-4-1 .glsr-status .toggle-row:before,body.branch-4-2 .glsr-status .toggle-row:before{display:block;position:absolute;top:-5px;left:10px;border-radius:50%;color:#666;content:'\f140';font:400 20px/1 dashicons;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;speak:none;padding:1px 2px 1px 0}body.branch-4 .glsr-status .is-expanded .toggle-row:before,body.branch-4-1 .glsr-status .is-expanded .toggle-row:before,body.branch-4-2 .glsr-status .is-expanded .toggle-row:before{content:'\f142'}@media screen and (max-width:782px){body.branch-4 .glsr-status .column-primary,body.branch-4-1 .glsr-status .column-primary,body.branch-4-2 .glsr-status .column-primary{width:100%}body.branch-4 .glsr-status .column-primary~td,body.branch-4 .glsr-status .column-primary~th,body.branch-4-1 .glsr-status .column-primary~td,body.branch-4-1 .glsr-status .column-primary~th,body.branch-4-2 .glsr-status .column-primary~td,body.branch-4-2 .glsr-status .column-primary~th{display:none}body.branch-4 .glsr-status .column-primary .toggle-row,body.branch-4-1 .glsr-status .column-primary .toggle-row,body.branch-4-2 .glsr-status .column-primary .toggle-row{display:block}body.branch-4 .glsr-status td,body.branch-4-1 .glsr-status td,body.branch-4-2 .glsr-status td{position:relative;clear:both;display:block;width:auto!important}body.branch-4 .glsr-status td.column-primary,body.branch-4-1 .glsr-status td.column-primary,body.branch-4-2 .glsr-status td.column-primary{padding-right:50px}body.branch-4 .glsr-status td.column-primary~td:not(.check-column),body.branch-4-1 .glsr-status td.column-primary~td:not(.check-column),body.branch-4-2 .glsr-status td.column-primary~td:not(.check-column){padding:3px 8px 3px 35%}body.branch-4 .glsr-status td:not(.column-primary)::before,body.branch-4-1 .glsr-status td:not(.column-primary)::before,body.branch-4-2 .glsr-status td:not(.column-primary)::before{position:absolute;left:10px;display:block;overflow:hidden;width:32%;content:attr(data-colname);white-space:nowrap;text-overflow:ellipsis}body.branch-4 .glsr-status .is-expanded td:not(.hidden),body.branch-4-1 .glsr-status .is-expanded td:not(.hidden),body.branch-4-2 .glsr-status .is-expanded td:not(.hidden){display:block;overflow:hidden}body.branch-4-1.post-type-review .fixed .column-rating,body.branch-4-1.post-type-review .fixed .column-reviewer,body.branch-4-1.post-type-review .fixed .column-site,body.branch-4-1.post-type-review .fixed .column-stars,body.branch-4-2.post-type-review .fixed .column-rating,body.branch-4-2.post-type-review .fixed .column-reviewer,body.branch-4-2.post-type-review .fixed .column-site,body.branch-4-2.post-type-review .fixed .column-stars,body.branch-4.post-type-review .fixed .column-rating,body.branch-4.post-type-review .fixed .column-reviewer,body.branch-4.post-type-review .fixed .column-site,body.branch-4.post-type-review .fixed .column-stars{display:none}}.post-type-site-review .wp-list-table .approve,.post-type-site-review .wp-list-table .status-pending .unapprove{display:none}.post-type-site-review .wp-list-table .spam .approve,.post-type-site-review .wp-list-table .status-pending .approve,.post-type-site-review .wp-list-table .trash .approve{display:inline}.post-type-site-review .wp-list-table .approve a{color:#006505}.post-type-site-review .wp-list-table .approve a:hover{color:#007f06}.post-type-site-review .wp-list-table .unapprove a{color:#d98500}.post-type-site-review .wp-list-table .unapprove a:hover{color:#f39500}.post-type-site-review .wp-list-table th.column-stars{width:100px}.post-type-site-review .wp-list-table td.column-sticky i{line-height:24px;text-indent:-3px;cursor:pointer}.post-type-site-review .wp-list-table td.column-sticky i:not(.pinned){color:rgba(85,85,85,.2)}.post-type-site-review .wp-list-table :not(.status-pending)+tr.status-pending th.check-column{box-shadow:inset 0 1px 0 rgba(0,0,0,.07),inset 0 -1px 0 rgba(0,0,0,.07)}.post-type-site-review .wp-list-table :not(.status-pending)+tr.status-pending td:nth-child(2){box-shadow:inset 0 1px 0 rgba(0,0,0,.07)}.post-type-site-review .wp-list-table :not(.status-pending)+tr.status-pending:not(.is-expanded) td:nth-child(2){box-shadow:inset 0 1px 0 rgba(0,0,0,.07),inset 0 -1px 0 rgba(0,0,0,.07)}.post-type-site-review .wp-list-table tr.status-pending th.check-column,.post-type-site-review .wp-list-table tr.status-pending.is-expanded td:last-child,.post-type-site-review .wp-list-table tr.status-pending:not(.is-expanded) td:nth-child(2){box-shadow:inset 0 -1px 0 rgba(0,0,0,.07)}.post-type-site-review .wp-list-table tr.status-pending td,.post-type-site-review .wp-list-table tr.status-pending th.check-column{background-color:#fef7f1}.post-type-site-review .wp-list-table tr.status-pending th.check-column{position:relative;border-left:4px solid #d54e21;z-index:1}.post-type-site-review .wp-list-table tr.status-pending th.check-column input{margin-left:4px}@media screen and (max-width:782px){body.branch-4-1.post-type-site-review .wp-list-table .column-title,body.branch-4-2.post-type-site-review .wp-list-table .column-title,body.branch-4.post-type-site-review .wp-list-table .column-title{width:auto}body.branch-4-1.post-type-site-review .wp-list-table .column-title~[class*=column-]:not(.column-stars),body.branch-4-2.post-type-site-review .wp-list-table .column-title~[class*=column-]:not(.column-stars),body.branch-4.post-type-site-review .wp-list-table .column-title~[class*=column-]:not(.column-stars){display:none}body.branch-4-1.post-type-site-review .wp-list-table :not(.status-pending)+tr.status-pending td.column-stars,body.branch-4-2.post-type-site-review .wp-list-table :not(.status-pending)+tr.status-pending td.column-stars,body.branch-4.post-type-site-review .wp-list-table :not(.status-pending)+tr.status-pending td.column-stars{box-shadow:inset 0 1px 0 rgba(0,0,0,.07)}body.branch-4-1.post-type-site-review .wp-list-table :not(.status-pending)+tr.status-pending:not(.is-expanded) td.column-stars,body.branch-4-2.post-type-site-review .wp-list-table :not(.status-pending)+tr.status-pending:not(.is-expanded) td.column-stars,body.branch-4.post-type-site-review .wp-list-table :not(.status-pending)+tr.status-pending:not(.is-expanded) td.column-stars{box-shadow:inset 0 1px 0 rgba(0,0,0,.07),inset 0 -1px 0 rgba(0,0,0,.07)}body.branch-4-1.post-type-site-review .wp-list-table tr.status-pending:not(.is-expanded) td.column-stars,body.branch-4-2.post-type-site-review .wp-list-table tr.status-pending:not(.is-expanded) td.column-stars,body.branch-4.post-type-site-review .wp-list-table tr.status-pending:not(.is-expanded) td.column-stars{box-shadow:inset 0 -1px 0 rgba(0,0,0,.07)}}@media screen and (min-width:783px){.post-type-site-review .wp-list-table th.column-sticky span.pinned-icon,.post-type-site-review .wp-list-table th.column-sticky span.sorting-indicator,body.branch-4-1.post-type-site-review .wp-list-table .dashicons-sticky:before,body.branch-4-1.post-type-site-review .wp-list-table th.column-sticky a:after,body.branch-4-2.post-type-site-review .wp-list-table .dashicons-sticky:before,body.branch-4-2.post-type-site-review .wp-list-table th.column-sticky a:after,body.branch-4-3.post-type-site-review .wp-list-table .dashicons-sticky:before,body.branch-4-3.post-type-site-review .wp-list-table th.column-sticky a:after,body.branch-4.post-type-site-review .wp-list-table .dashicons-sticky:before,body.branch-4.post-type-site-review .wp-list-table th.column-sticky a:after{display:none}body.branch-4-1.post-type-site-review .wp-list-table th.column-sticky,body.branch-4-2.post-type-site-review .wp-list-table th.column-sticky,body.branch-4-3.post-type-site-review .wp-list-table th.column-sticky,body.branch-4.post-type-site-review .wp-list-table th.column-sticky{position:relative}body.branch-4-1.post-type-site-review .wp-list-table th.column-sticky svg,body.branch-4-2.post-type-site-review .wp-list-table th.column-sticky svg,body.branch-4-3.post-type-site-review .wp-list-table th.column-sticky svg,body.branch-4.post-type-site-review .wp-list-table th.column-sticky svg{position:absolute;top:10px;width:16px;height:16px}.post-type-site-review .wp-list-table :not(.status-pending)+tr.status-pending td{box-shadow:inset 0 1px 0 rgba(0,0,0,.07),inset 0 -1px 0 rgba(0,0,0,.07)}.post-type-site-review .wp-list-table tr.status-pending td{box-shadow:inset 0 -1px 0 rgba(0,0,0,.07)}.post-type-site-review .wp-list-table th.column-reviewer{width:140px}.post-type-site-review .wp-list-table th.column-site{width:120px}.post-type-site-review .wp-list-table th.column-sticky{width:36px}.post-type-site-review .wp-list-table th.column-sticky a:after{display:inline-block;position:relative;content:"\f537";font:400 20px/1 dashicons;speak:none;vertical-align:top;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:20px;height:20px;line-height:20px;font-size:16px;text-align:center;-webkit-transition:color .1s ease-in 0;transition:color .1s ease-in 0}.post-type-site-review .wp-list-table td.column-sticky{padding:8px}.post-type-site-review .wp-list-table td.column-sticky i{text-indent:0}}@media screen and (max-width:1100px) and (min-width:782px),(max-width:480px){body.branch-4-1.post-type-site-review .wp-list-table th.column-date,body.branch-4-2.post-type-site-review .wp-list-table th.column-date,body.branch-4-3.post-type-site-review .wp-list-table th.column-date,body.branch-4.post-type-site-review .wp-list-table th.column-date{width:14%}}::placeholder{color:#72777c!important;font-size:inherit!important;line-height:inherit!important;opacity:1}::-webkit-input-placeholder{color:#72777c!important;font-size:inherit!important;line-height:inherit!important;opacity:1}:-moz-placeholder{color:#72777c!important;font-size:inherit!important;line-height:inherit!important;opacity:1}::-moz-placeholder{color:#72777c!important;font-size:inherit!important;line-height:inherit!important;opacity:1}:-ms-input-placeholder{color:#72777c!important;font-size:inherit!important;line-height:inherit!important;opacity:1}
|
assets/css/site-reviews.css
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* Star Rating
|
3 |
+
*
|
4 |
+
* Version: 1.3.0
|
5 |
+
* Author: Paul Ryley (http://geminilabs.io)
|
6 |
+
* URL: https://github.com/geminilabs/star-rating.js
|
7 |
+
* License: MIT
|
8 |
+
*/.gl-star-rating[data-star-rating]{position:relative;display:block}.gl-star-rating[data-star-rating]>select{position:absolute!important;top:0;left:0;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px);-webkit-clip-path:circle(1px at 0 0);clip-path:circle(1px at 0 0);visibility:visible}.gl-star-rating[data-star-rating] .gl-star-rating-stars{display:inline-block;height:26px;vertical-align:middle;cursor:pointer}.gl-star-rating[data-star-rating] .gl-star-rating-stars>span{display:inline-block;width:24px;height:24px;background-size:24px;background-repeat:no-repeat;background-image:url(../img/star-empty.svg);margin-right:4px}.gl-star-rating[data-star-rating] .gl-star-rating-stars.s100>span,.gl-star-rating[data-star-rating] .gl-star-rating-stars.s10>span:nth-child(1),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s20>span:nth-child(-1n+2),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s30>span:nth-child(-1n+3),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s40>span:nth-child(-1n+4),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s50>span:nth-child(-1n+5),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s60>span:nth-child(-1n+6),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s70>span:nth-child(-1n+7),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s80>span:nth-child(-1n+8),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s90>span:nth-child(-1n+9){background-image:url(../img/star-filled.svg)}.gl-star-rating[data-star-rating] .gl-star-rating-text{display:inline-block;position:relative;height:26px;line-height:26px;font-size:.8em;font-weight:600;color:#fff;background-color:#1a1a1a;white-space:nowrap;vertical-align:middle;padding:0 12px 0 6px;margin:0 0 0 12px}.gl-star-rating[data-star-rating] .gl-star-rating-text:before{position:absolute;top:0;left:-12px;width:0;height:0;content:"";border-style:solid;border-width:13px 12px 13px 0;border-color:transparent #1a1a1a transparent transparent}[id^=geminilabs-site-reviews].widget ul.glsr-reviews{list-style:none;margin-left:0}[id^=geminilabs-site-reviews].widget .glsr-review-title,[id^=geminilabs-site-reviews].widget p{margin-bottom:1em}.glsr-review{margin-bottom:2em}.glsr-review p{margin:0 0 1em}.glsr-review-date{font-style:italic}.glsr-review-meta>span:not(:last-child){margin-right:.5em}.glsr-review-link{white-space:nowrap}[class*=glsr-star-]{display:inline-block;width:16px;height:16px;font-size:16px;vertical-align:text-top;background-repeat:no-repeat;background-size:16px}.glsr-star-empty{background-image:url(../img/star-empty.svg)}.glsr-star-full{background-image:url(../img/star-filled.svg)}.glsr-star-half{background-image:url(../img/star-half.svg)}.gl-star-rating[data-star-rating]{line-height:1}.glsr-field-messages{color:#1976D2}.glsr-has-error{color:#D32F2F}.glsr-field{margin-bottom:1em}.glsr-field-error{display:block;font-size:.875em;color:#D32F2F}.glsr-field ul{list-style-type:none}.glsr-field label{display:block}.glsr-field label[for^=terms]{-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}.glsr-field label[for^=rating]:after,.glsr-field.glsr-required label:after{content:"*";color:#D32F2F;margin-left:.33em}.glsr-field input[type=checkbox]{margin-right:.25em}.glsr-field select[name=rating]{display:block;height:26px;visibility:hidden}
|
assets/css/twenty-eleven.css
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* Star Rating
|
3 |
+
*
|
4 |
+
* Version: 1.3.0
|
5 |
+
* Author: Paul Ryley (http://geminilabs.io)
|
6 |
+
* URL: https://github.com/geminilabs/star-rating.js
|
7 |
+
* License: MIT
|
8 |
+
*/.gl-star-rating[data-star-rating]{position:relative;display:block}.gl-star-rating[data-star-rating]>select{position:absolute!important;top:0;left:0;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px);-webkit-clip-path:circle(1px at 0 0);clip-path:circle(1px at 0 0);visibility:visible}.gl-star-rating[data-star-rating] .gl-star-rating-stars{display:inline-block;height:26px;vertical-align:middle;cursor:pointer}.gl-star-rating[data-star-rating] .gl-star-rating-stars>span{display:inline-block;width:24px;height:24px;background-size:24px;background-repeat:no-repeat;background-image:url(../img/star-empty.svg);margin-right:4px}.gl-star-rating[data-star-rating] .gl-star-rating-stars.s100>span,.gl-star-rating[data-star-rating] .gl-star-rating-stars.s10>span:nth-child(1),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s20>span:nth-child(-1n+2),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s30>span:nth-child(-1n+3),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s40>span:nth-child(-1n+4),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s50>span:nth-child(-1n+5),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s60>span:nth-child(-1n+6),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s70>span:nth-child(-1n+7),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s80>span:nth-child(-1n+8),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s90>span:nth-child(-1n+9){background-image:url(../img/star-filled.svg)}.gl-star-rating[data-star-rating] .gl-star-rating-text{display:inline-block;position:relative;height:26px;line-height:26px;font-size:.8em;font-weight:600;color:#fff;background-color:#1a1a1a;white-space:nowrap;vertical-align:middle;padding:0 12px 0 6px;margin:0 0 0 12px}.gl-star-rating[data-star-rating] .gl-star-rating-text:before{position:absolute;top:0;left:-12px;width:0;height:0;content:"";border-style:solid;border-width:13px 12px 13px 0;border-color:transparent #1a1a1a transparent transparent}[id^=geminilabs-site-reviews].widget ul.glsr-reviews{list-style:none;margin-left:0}[id^=geminilabs-site-reviews].widget .glsr-review-title,[id^=geminilabs-site-reviews].widget p{margin-bottom:1em}.glsr-review{margin-bottom:2em}.glsr-review p{margin:0 0 1em}.glsr-review-date{font-style:italic}.glsr-review-meta>span:not(:last-child){margin-right:.5em}.glsr-review-link{white-space:nowrap}[class*=glsr-star-]{display:inline-block;width:16px;height:16px;font-size:16px;vertical-align:text-top;background-repeat:no-repeat;background-size:16px}.glsr-star-empty{background-image:url(../img/star-empty.svg)}.glsr-star-full{background-image:url(../img/star-filled.svg)}.glsr-star-half{background-image:url(../img/star-half.svg)}.gl-star-rating[data-star-rating]{line-height:1}.glsr-field-messages{color:#1976D2}.glsr-has-error{color:#D32F2F}.glsr-field{margin-bottom:1em}.glsr-field ul{list-style-type:none}.glsr-field label[for^=terms]{-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}.glsr-field label[for^=rating]:after,.glsr-field.glsr-required label:after{content:"*";margin-left:.33em}.glsr-field input[type=checkbox]{margin-right:.25em}.glsr-field select[name=rating]{display:block;height:26px;visibility:hidden}.glsr-form-messages{color:#009781;font-size:15px}.gslr-has-errors{color:#bd3500}h3.glsr-form-title{color:#373737;font-size:24px;font-weight:700;line-height:30px;text-transform:none;letter-spacing:normal}.glsr-form-description{font-size:13px;margin:10px 0}.glsr-form-description span{color:#bd3500}.entry-content .glsr-field input[type=text],.entry-content .glsr-field input[type=email],.entry-content .glsr-field input[type=url],.entry-content .glsr-field textarea,.one .glsr-field input[type=text],.one .glsr-field input[type=email],.one .glsr-field input[type=url],.one .glsr-field textarea{padding:10px}@media (max-width:800px){.entry-content .glsr-field input[type=text],.entry-content .glsr-field input[type=email],.entry-content .glsr-field input[type=url],.entry-content .glsr-field textarea,.one .glsr-field input[type=text],.one .glsr-field input[type=email],.one .glsr-field input[type=url],.one .glsr-field textarea{padding:6px}}.glsr-field-error{display:block;color:#bd3500;font-size:13px}.glsr-field label{display:block;font-size:13px}.glsr-field label[for^=rating]:after,.glsr-field.glsr-required label:after{color:#bd3500;line-height:13px;font-size:22px;font-weight:600;vertical-align:middle}.glsr-field input[type=text],.glsr-field input[type=email],.glsr-field input[type=url],.glsr-field textarea{position:relative;display:block;width:100%;border-radius:3px;box-sizing:border-box;padding:6px}.glsr-field input[type=submit]{position:relative;background:#222;border:none;border-radius:3px;box-shadow:0 1px 2px rgba(0,0,0,.3);color:#eee;cursor:pointer;text-shadow:0 -1px 0 rgba(0,0,0,.3);padding:5px 22px}.glsr-field input[type=submit]:active{background:#1982d1;color:#bfddf3}
|
assets/css/twenty-fifteen.css
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* Star Rating
|
3 |
+
*
|
4 |
+
* Version: 1.3.0
|
5 |
+
* Author: Paul Ryley (http://geminilabs.io)
|
6 |
+
* URL: https://github.com/geminilabs/star-rating.js
|
7 |
+
* License: MIT
|
8 |
+
*/.gl-star-rating[data-star-rating]{position:relative;display:block}.gl-star-rating[data-star-rating]>select{position:absolute!important;top:0;left:0;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px);-webkit-clip-path:circle(1px at 0 0);clip-path:circle(1px at 0 0);visibility:visible}.gl-star-rating[data-star-rating] .gl-star-rating-stars{display:inline-block;height:26px;vertical-align:middle;cursor:pointer}.gl-star-rating[data-star-rating] .gl-star-rating-stars>span{display:inline-block;width:24px;height:24px;background-size:24px;background-repeat:no-repeat;background-image:url(../img/star-empty.svg);margin-right:4px}.gl-star-rating[data-star-rating] .gl-star-rating-stars.s100>span,.gl-star-rating[data-star-rating] .gl-star-rating-stars.s10>span:nth-child(1),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s20>span:nth-child(-1n+2),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s30>span:nth-child(-1n+3),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s40>span:nth-child(-1n+4),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s50>span:nth-child(-1n+5),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s60>span:nth-child(-1n+6),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s70>span:nth-child(-1n+7),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s80>span:nth-child(-1n+8),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s90>span:nth-child(-1n+9){background-image:url(../img/star-filled.svg)}.gl-star-rating[data-star-rating] .gl-star-rating-text{display:inline-block;position:relative;height:26px;line-height:26px;font-size:.8em;font-weight:600;color:#fff;background-color:#1a1a1a;white-space:nowrap;vertical-align:middle;padding:0 12px 0 6px;margin:0 0 0 12px}.gl-star-rating[data-star-rating] .gl-star-rating-text:before{position:absolute;top:0;left:-12px;width:0;height:0;content:"";border-style:solid;border-width:13px 12px 13px 0;border-color:transparent #1a1a1a transparent transparent}[id^=geminilabs-site-reviews].widget ul.glsr-reviews{list-style:none;margin-left:0}[id^=geminilabs-site-reviews].widget .glsr-review-title,[id^=geminilabs-site-reviews].widget p{margin-bottom:1em}.glsr-review{margin-bottom:2em}.glsr-review p{margin:0 0 1em}.glsr-review-date{font-style:italic}.glsr-review-meta>span:not(:last-child){margin-right:.5em}.glsr-review-link{white-space:nowrap}[class*=glsr-star-]{display:inline-block;width:16px;height:16px;font-size:16px;vertical-align:text-top;background-repeat:no-repeat;background-size:16px}.glsr-star-empty{background-image:url(../img/star-empty.svg)}.glsr-star-full{background-image:url(../img/star-filled.svg)}.glsr-star-half{background-image:url(../img/star-half.svg)}.gl-star-rating[data-star-rating]{line-height:1}.glsr-field-messages{color:#1976D2}.glsr-has-error{color:#D32F2F}.glsr-field ul{list-style-type:none}.glsr-field label[for^=terms]{-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}.glsr-field input[type=checkbox]{margin-right:.25em}.glsr-field select[name=rating]{display:block;height:26px;visibility:hidden}.glsr-form-messages{color:#21759b}.gslr-has-errors{color:#c0392b}.shortcode-reviews-form{border-top:1px solid #eaeaea;border-top:1px solid rgba(51,51,51,.1);padding-top:1.6em}.shortcode-reviews-form>:last-child{margin-bottom:0}.shortcode-reviews-form h3.glsr-form-title{font-family:"Noto Serif",serif;font-size:18px;font-size:1.8rem;line-height:1.3333333333;margin-top:0;margin-bottom:1.3333333333em}@media screen and (min-width:46.25em){.shortcode-reviews-form{padding-top:1.6470588235em}.shortcode-reviews-form h3.glsr-form-title{font-size:24px;font-size:2.4rem;line-height:1.1666666667;margin-bottom:1.1666666667em}}@media screen and (min-width:55em){.shortcode-reviews-form{padding-top:1.6842105263em}.shortcode-reviews-form h3.glsr-form-title{font-size:27px;font-size:2.7rem;line-height:1.1851851852;margin-bottom:1.1851851852em}}@media screen and (min-width:59.6875em){.shortcode-reviews-form{padding-top:1.6em}.shortcode-reviews-form h3.glsr-form-title{font-size:18px;font-size:1.8rem;line-height:1.3333333333;margin-bottom:1.3333333333em}}@media screen and (min-width:68.75em){.shortcode-reviews-form{padding-top:1.6470588235em}.shortcode-reviews-form h3.glsr-form-title{font-size:24px;font-size:2.4rem;line-height:1.1666666667;margin-bottom:1.1666666667em}}@media screen and (min-width:77.5em){.shortcode-reviews-form{padding-top:1.6842105263em}.shortcode-reviews-form h3.glsr-form-title{font-size:27px;font-size:2.7rem;line-height:1.1851851852;margin-bottom:1.1851851852em}}.glsr-field label,.glsr-field-error,.glsr-form-description{font-family:"Noto Sans",sans-serif;line-height:1.5}.glsr-form-description{color:#707070;color:rgba(51,51,51,.7);margin-bottom:2em}.glsr-form-description span{color:#c0392b}.glsr-field-error,.glsr-form-description{font-size:12px;font-size:1.2rem}@media screen and (min-width:46.25em){.glsr-field-error,.glsr-form-description{font-size:14px;font-size:1.4rem}}@media screen and (min-width:55em){.glsr-field-error,.glsr-form-description{font-size:16px;font-size:1.6rem}}@media screen and (min-width:59.6875em){.glsr-field-error,.glsr-form-description{font-size:12px;font-size:1.2rem}}@media screen and (min-width:68.75em){.glsr-field-error,.glsr-form-description{font-size:14px;font-size:1.4rem}}@media screen and (min-width:77.5em){.glsr-field-error,.glsr-form-description{font-size:16px;font-size:1.6rem}}.glsr-field{margin-bottom:1.6em}.glsr-field:last-child{margin-bottom:0}.glsr-field-error{display:block;color:#c0392b}.glsr-field-error:first-child{margin-top:2em}.glsr-field label{display:block;color:#707070;color:rgba(51,51,51,.7);font-size:12px;font-size:1.2rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase}.glsr-field label[for^=rating]:after,.glsr-field.glsr-required label:after{content:"*";margin-left:.33em;color:#c0392b}.glsr-field input[type=checkbox]:focus{outline:0}.glsr-field input[type=submit]{width:100%}@media screen and (min-width:46.25em){.glsr-field label{font-size:14px;font-size:1.4rem}.glsr-field{margin-bottom:1.6471em}}@media screen and (min-width:55em){.glsr-field label{font-size:16px;font-size:1.6rem}.glsr-field{margin-bottom:1.6842em}}@media screen and (min-width:59.6875em){.glsr-field label{font-size:12px;font-size:1.2rem}.glsr-field{margin-bottom:1.6em}}@media screen and (min-width:68.75em){.glsr-field label{font-size:14px;font-size:1.4rem}.glsr-field{margin-bottom:1.6471em}}@media screen and (min-width:77.5em){.glsr-field label{font-size:16px;font-size:1.6rem}.glsr-field{margin-bottom:1.6842em}}
|
assets/css/twenty-fourteen.css
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* Star Rating
|
3 |
+
*
|
4 |
+
* Version: 1.3.0
|
5 |
+
* Author: Paul Ryley (http://geminilabs.io)
|
6 |
+
* URL: https://github.com/geminilabs/star-rating.js
|
7 |
+
* License: MIT
|
8 |
+
*/.gl-star-rating[data-star-rating]{position:relative;display:block}.gl-star-rating[data-star-rating]>select{position:absolute!important;top:0;left:0;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px);-webkit-clip-path:circle(1px at 0 0);clip-path:circle(1px at 0 0);visibility:visible}.gl-star-rating[data-star-rating] .gl-star-rating-stars{display:inline-block;height:26px;vertical-align:middle;cursor:pointer}.gl-star-rating[data-star-rating] .gl-star-rating-stars>span{display:inline-block;width:24px;height:24px;background-size:24px;background-repeat:no-repeat;background-image:url(../img/star-empty.svg);margin-right:4px}.gl-star-rating[data-star-rating] .gl-star-rating-stars.s100>span,.gl-star-rating[data-star-rating] .gl-star-rating-stars.s10>span:nth-child(1),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s20>span:nth-child(-1n+2),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s30>span:nth-child(-1n+3),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s40>span:nth-child(-1n+4),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s50>span:nth-child(-1n+5),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s60>span:nth-child(-1n+6),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s70>span:nth-child(-1n+7),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s80>span:nth-child(-1n+8),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s90>span:nth-child(-1n+9){background-image:url(../img/star-filled.svg)}.gl-star-rating[data-star-rating] .gl-star-rating-text{display:inline-block;position:relative;height:26px;line-height:26px;font-size:.8em;font-weight:600;color:#fff;background-color:#1a1a1a;white-space:nowrap;vertical-align:middle;padding:0 12px 0 6px;margin:0 0 0 12px}.gl-star-rating[data-star-rating] .gl-star-rating-text:before{position:absolute;top:0;left:-12px;width:0;height:0;content:"";border-style:solid;border-width:13px 12px 13px 0;border-color:transparent #1a1a1a transparent transparent}[id^=geminilabs-site-reviews].widget ul.glsr-reviews{list-style:none;margin-left:0}[id^=geminilabs-site-reviews].widget .glsr-review-title,[id^=geminilabs-site-reviews].widget p{margin-bottom:1em}.glsr-review{margin-bottom:2em}.glsr-review p{margin:0 0 1em}.glsr-field,h3.glsr-form-title{margin-bottom:24px}.glsr-review-date{font-style:italic}.glsr-review-meta>span:not(:last-child){margin-right:.5em}.glsr-review-link{white-space:nowrap}[class*=glsr-star-]{display:inline-block;width:16px;height:16px;font-size:16px;vertical-align:text-top;background-repeat:no-repeat;background-size:16px}.glsr-star-empty{background-image:url(../img/star-empty.svg)}.glsr-star-full{background-image:url(../img/star-filled.svg)}.glsr-star-half{background-image:url(../img/star-half.svg)}.gl-star-rating[data-star-rating]{line-height:1}.glsr-field-messages{color:#1976D2}.glsr-has-error{color:#D32F2F}.glsr-field-error{display:block;font-size:.875em}.glsr-field ul{list-style-type:none}.glsr-field label{display:block}.glsr-field label[for^=terms]{-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}.glsr-field label[for^=rating]:after,.glsr-field.glsr-required label:after{content:"*";margin-left:.33em}.glsr-field input[type=checkbox]{margin-right:.25em}.glsr-field select[name=rating]{display:block;height:26px;visibility:hidden}.glsr-form-messages{color:#21759b}.glsr-form-description span,.gslr-has-errors{color:#c0392b}h3.glsr-form-title{font:900 16px/1.5 Lato,sans-serif;text-transform:uppercase;margin-top:0}.shortcode-reviews-form .glsr-form-description{font-size:14px;line-height:1.7142857143;color:#767676}.glsr-field label[for^=rating]:after,.glsr-field-error,.glsr-field.glsr-required label:after{color:#c0392b}.glsr-field input[type=text],.glsr-field input[type=email],.glsr-field input[type=url]{width:100%}@media screen and (max-width:1079px){#secondary .gl-star-rating[data-star-rating] .gl-star-rating-stars>span{width:21px;height:21px;background-size:21px}#secondary .gl-star-rating[data-star-rating] .gl-star-rating-stars>span:last-child{margin-right:0}}
|
assets/css/twenty-sixteen.css
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* Star Rating
|
3 |
+
*
|
4 |
+
* Version: 1.3.0
|
5 |
+
* Author: Paul Ryley (http://geminilabs.io)
|
6 |
+
* URL: https://github.com/geminilabs/star-rating.js
|
7 |
+
* License: MIT
|
8 |
+
*/.glsr-field label:not([for^=terms]),.glsr-form-messages,.glsr-form-title{font-family:Montserrat,"Helvetica Neue",sans-serif}.gl-star-rating[data-star-rating]{position:relative;display:block}.gl-star-rating[data-star-rating]>select{position:absolute!important;top:0;left:0;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px);-webkit-clip-path:circle(1px at 0 0);clip-path:circle(1px at 0 0);visibility:visible}.gl-star-rating[data-star-rating] .gl-star-rating-stars{display:inline-block;height:26px;vertical-align:middle;cursor:pointer}.gl-star-rating[data-star-rating] .gl-star-rating-stars>span{display:inline-block;width:24px;height:24px;background-size:24px;background-repeat:no-repeat;background-image:url(../img/star-empty.svg);margin-right:4px}.gl-star-rating[data-star-rating] .gl-star-rating-stars.s100>span,.gl-star-rating[data-star-rating] .gl-star-rating-stars.s10>span:nth-child(1),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s20>span:nth-child(-1n+2),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s30>span:nth-child(-1n+3),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s40>span:nth-child(-1n+4),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s50>span:nth-child(-1n+5),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s60>span:nth-child(-1n+6),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s70>span:nth-child(-1n+7),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s80>span:nth-child(-1n+8),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s90>span:nth-child(-1n+9){background-image:url(../img/star-filled.svg)}.gl-star-rating[data-star-rating] .gl-star-rating-text{display:inline-block;position:relative;height:26px;line-height:26px;font-size:.8em;font-weight:600;color:#fff;background-color:#1a1a1a;white-space:nowrap;vertical-align:middle;padding:0 12px 0 6px;margin:0 0 0 12px}.gl-star-rating[data-star-rating] .gl-star-rating-text:before{position:absolute;top:0;left:-12px;width:0;height:0;content:"";border-style:solid;border-width:13px 12px 13px 0;border-color:transparent #1a1a1a transparent transparent}[id^=geminilabs-site-reviews].widget ul.glsr-reviews{list-style:none;margin-left:0}[id^=geminilabs-site-reviews].widget .glsr-review-title,[id^=geminilabs-site-reviews].widget p{margin-bottom:1em}.glsr-review{margin-bottom:2em}.glsr-review p{margin:0 0 1em}.glsr-review-date{font-style:italic}.glsr-review-meta>span:not(:last-child){margin-right:.5em}.glsr-review-link{white-space:nowrap}[class*=glsr-star-]{display:inline-block;width:16px;height:16px;font-size:16px;vertical-align:text-top;background-repeat:no-repeat;background-size:16px}.glsr-star-empty{background-image:url(../img/star-empty.svg)}.glsr-star-full{background-image:url(../img/star-filled.svg)}.glsr-star-half{background-image:url(../img/star-half.svg)}.gl-star-rating[data-star-rating]{line-height:1}.glsr-field-messages{color:#1976D2}.glsr-has-error{color:#D32F2F}.glsr-field ul{list-style-type:none}.glsr-field label{display:block}.glsr-field label[for^=terms]{-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}.glsr-field input[type=checkbox]{margin-right:.25em}.glsr-field select[name=rating]{display:block;height:26px;visibility:hidden}.glsr-form-title{font-size:23px;font-size:1.4375rem;font-weight:700;line-height:1.3125;border-top:4px solid #1a1a1a;padding-top:1.2173913043em;margin-bottom:1.2173913043em}.glsr-form-description{color:#686868;font-size:13px;font-size:.8125rem;line-height:1.6153846154;margin-bottom:2.1538461538em}.glsr-form-description span,.glsr-form-messages{color:#007acc}.gslr-has-errors{color:#e4002e}.glsr-field{margin-bottom:1.75em}.glsr-field-error{display:block;color:#e4002e;font-size:13px;font-size:.8125rem;line-height:1.6153846154}.glsr-field-error:first-child{margin-top:.5384615385em}.glsr-field label:not([for^=terms]){color:#686868;font-size:13px;font-size:.8125rem;letter-spacing:.0769230769em;line-height:1.6153846154;margin-bottom:.5384615385em;text-transform:uppercase}.glsr-field label[for^=rating]:after,.glsr-field.glsr-required label:after{content:"*";font-family:Merriweather,Georgia,serif;color:#007acc;margin-left:5px}.glsr-field ::-webkit-input-placeholder{font-family:Merriweather,Georgia,serif}.glsr-field :-moz-placeholder{font-family:Merriweather,Georgia,serif}.glsr-field ::-moz-placeholder{font-family:Merriweather,Georgia,serif}.glsr-field :-ms-input-placeholder{font-family:Merriweather,Georgia,serif}
|
assets/css/twenty-ten.css
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* Star Rating
|
3 |
+
*
|
4 |
+
* Version: 1.3.0
|
5 |
+
* Author: Paul Ryley (http://geminilabs.io)
|
6 |
+
* URL: https://github.com/geminilabs/star-rating.js
|
7 |
+
* License: MIT
|
8 |
+
*/.gl-star-rating[data-star-rating]{position:relative;display:block}.gl-star-rating[data-star-rating]>select{position:absolute!important;top:0;left:0;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px);-webkit-clip-path:circle(1px at 0 0);clip-path:circle(1px at 0 0);visibility:visible}.gl-star-rating[data-star-rating] .gl-star-rating-stars{display:inline-block;height:26px;vertical-align:middle;cursor:pointer}.gl-star-rating[data-star-rating] .gl-star-rating-stars>span{display:inline-block;width:24px;height:24px;background-size:24px;background-repeat:no-repeat;background-image:url(../img/star-empty.svg);margin-right:4px}.gl-star-rating[data-star-rating] .gl-star-rating-stars.s100>span,.gl-star-rating[data-star-rating] .gl-star-rating-stars.s10>span:nth-child(1),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s20>span:nth-child(-1n+2),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s30>span:nth-child(-1n+3),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s40>span:nth-child(-1n+4),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s50>span:nth-child(-1n+5),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s60>span:nth-child(-1n+6),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s70>span:nth-child(-1n+7),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s80>span:nth-child(-1n+8),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s90>span:nth-child(-1n+9){background-image:url(../img/star-filled.svg)}.gl-star-rating[data-star-rating] .gl-star-rating-text{display:inline-block;position:relative;height:26px;line-height:26px;font-size:.8em;font-weight:600;color:#fff;background-color:#1a1a1a;white-space:nowrap;vertical-align:middle;padding:0 12px 0 6px;margin:0 0 0 12px}.gl-star-rating[data-star-rating] .gl-star-rating-text:before{position:absolute;top:0;left:-12px;width:0;height:0;content:"";border-style:solid;border-width:13px 12px 13px 0;border-color:transparent #1a1a1a transparent transparent}[id^=geminilabs-site-reviews].widget ul.glsr-reviews{list-style:none;margin-left:0}[id^=geminilabs-site-reviews].widget .glsr-review-title,[id^=geminilabs-site-reviews].widget p{margin-bottom:1em}.glsr-review{margin-bottom:2em}.glsr-review p{margin:0 0 1em}.glsr-review-date{font-style:italic}.glsr-review-meta>span:not(:last-child){margin-right:.5em}.glsr-review-link{white-space:nowrap}[class*=glsr-star-]{display:inline-block;width:16px;height:16px;font-size:16px;vertical-align:text-top;background-repeat:no-repeat;background-size:16px}.glsr-star-empty{background-image:url(../img/star-empty.svg)}.glsr-star-full{background-image:url(../img/star-filled.svg)}.glsr-star-half{background-image:url(../img/star-half.svg)}.gl-star-rating[data-star-rating]{line-height:1}.glsr-field-messages{color:#1976D2}.glsr-has-error{color:#D32F2F}.glsr-field{margin-bottom:1em}.glsr-field-error{display:block;font-size:.875em}.glsr-field ul{list-style-type:none}.glsr-field label{display:block}.glsr-field label[for^=terms]{-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}.glsr-field label[for^=rating]:after,.glsr-field.glsr-required label:after{content:"*";margin-left:.33em}.glsr-field select[name=rating]{display:block;height:26px;visibility:hidden}.glsr-form-messages{color:#2b54d6}.gslr-has-errors{color:#ff4b33}.shortcode-reviews-form{position:relative;border-top:1px solid #e7e7e7;margin:24px 0}#content .glsr-form-title,.glsr-form-title{color:#000;font-size:20px;font-weight:600;font-family:"Helvetica Neue",Arial,Helvetica,"Nimbus Sans L",sans-serif;margin:18px 0}.glsr-field label[for^=rating]:after,.glsr-field-error,.glsr-field.glsr-required label:after,.glsr-form-description span{color:#ff4b33}.glsr-field{margin:0 0 9px}.glsr-field input[type=text],.glsr-field input[type=email],.glsr-field input[type=url]{width:98%;margin:0}.glsr-field input[type=checkbox]{margin-right:.25em}.glsr-field textarea{width:98%}
|
assets/css/twenty-thirteen.css
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* Star Rating
|
3 |
+
*
|
4 |
+
* Version: 1.3.0
|
5 |
+
* Author: Paul Ryley (http://geminilabs.io)
|
6 |
+
* URL: https://github.com/geminilabs/star-rating.js
|
7 |
+
* License: MIT
|
8 |
+
*/.gl-star-rating[data-star-rating]{position:relative;display:block}.gl-star-rating[data-star-rating]>select{position:absolute!important;top:0;left:0;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px);-webkit-clip-path:circle(1px at 0 0);clip-path:circle(1px at 0 0);visibility:visible}.gl-star-rating[data-star-rating] .gl-star-rating-stars{display:inline-block;height:26px;vertical-align:middle;cursor:pointer}.gl-star-rating[data-star-rating] .gl-star-rating-stars>span{display:inline-block;width:24px;height:24px;background-size:24px;background-repeat:no-repeat;background-image:url(../img/star-empty.svg);margin-right:4px}.gl-star-rating[data-star-rating] .gl-star-rating-stars.s100>span,.gl-star-rating[data-star-rating] .gl-star-rating-stars.s10>span:nth-child(1),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s20>span:nth-child(-1n+2),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s30>span:nth-child(-1n+3),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s40>span:nth-child(-1n+4),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s50>span:nth-child(-1n+5),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s60>span:nth-child(-1n+6),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s70>span:nth-child(-1n+7),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s80>span:nth-child(-1n+8),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s90>span:nth-child(-1n+9){background-image:url(../img/star-filled.svg)}.gl-star-rating[data-star-rating] .gl-star-rating-text{display:inline-block;position:relative;height:26px;line-height:26px;font-size:.8em;font-weight:600;color:#fff;background-color:#1a1a1a;white-space:nowrap;vertical-align:middle;padding:0 12px 0 6px;margin:0 0 0 12px}.gl-star-rating[data-star-rating] .gl-star-rating-text:before{position:absolute;top:0;left:-12px;width:0;height:0;content:"";border-style:solid;border-width:13px 12px 13px 0;border-color:transparent #1a1a1a transparent transparent}[id^=geminilabs-site-reviews].widget ul.glsr-reviews{list-style:none;margin-left:0}[id^=geminilabs-site-reviews].widget .glsr-review-title,[id^=geminilabs-site-reviews].widget p{margin-bottom:1em}.glsr-review{margin-bottom:2em}.glsr-review p{margin:0 0 1em}.glsr-review-date{font-style:italic}.glsr-review-meta>span:not(:last-child){margin-right:.5em}.glsr-review-link{white-space:nowrap}[class*=glsr-star-]{display:inline-block;width:16px;height:16px;font-size:16px;vertical-align:text-top;background-repeat:no-repeat;background-size:16px}.glsr-star-empty{background-image:url(../img/star-empty.svg)}.glsr-star-full{background-image:url(../img/star-filled.svg)}.glsr-star-half{background-image:url(../img/star-half.svg)}.gl-star-rating[data-star-rating]{line-height:1}.glsr-field-messages{color:#1976D2}.glsr-has-error{color:#D32F2F}.glsr-field-error{display:block;font-size:.875em}.glsr-field ul{list-style-type:none}.glsr-field label{display:block}.glsr-field label[for^=terms]{-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}.glsr-field label[for^=rating]:after,.glsr-field.glsr-required label:after{content:"*";margin-left:.33em}.glsr-field input[type=checkbox]{margin-right:.25em}.glsr-field select[name=rating]{display:block;height:26px;visibility:hidden}.glsr-form-messages{color:#21759b}.glsr-field label[for^=rating]:after,.glsr-field-error,.glsr-field.glsr-required label:after,.glsr-form-description span,.gslr-has-errors{color:#ed331c}.glsr-field{margin-bottom:12px}.glsr-field input[type=text],.glsr-field input[type=email],.glsr-field input[type=url],.glsr-field textarea{width:100%}
|
assets/css/twenty-twelve.css
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* Star Rating
|
3 |
+
*
|
4 |
+
* Version: 1.3.0
|
5 |
+
* Author: Paul Ryley (http://geminilabs.io)
|
6 |
+
* URL: https://github.com/geminilabs/star-rating.js
|
7 |
+
* License: MIT
|
8 |
+
*/.gl-star-rating[data-star-rating]{position:relative;display:block}.gl-star-rating[data-star-rating]>select{position:absolute!important;top:0;left:0;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px);-webkit-clip-path:circle(1px at 0 0);clip-path:circle(1px at 0 0);visibility:visible}.gl-star-rating[data-star-rating] .gl-star-rating-stars{display:inline-block;height:26px;vertical-align:middle;cursor:pointer}.gl-star-rating[data-star-rating] .gl-star-rating-stars>span{display:inline-block;width:24px;height:24px;background-size:24px;background-repeat:no-repeat;background-image:url(../img/star-empty.svg);margin-right:4px}.gl-star-rating[data-star-rating] .gl-star-rating-stars.s100>span,.gl-star-rating[data-star-rating] .gl-star-rating-stars.s10>span:nth-child(1),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s20>span:nth-child(-1n+2),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s30>span:nth-child(-1n+3),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s40>span:nth-child(-1n+4),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s50>span:nth-child(-1n+5),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s60>span:nth-child(-1n+6),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s70>span:nth-child(-1n+7),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s80>span:nth-child(-1n+8),.gl-star-rating[data-star-rating] .gl-star-rating-stars.s90>span:nth-child(-1n+9){background-image:url(../img/star-filled.svg)}.gl-star-rating[data-star-rating] .gl-star-rating-text{display:inline-block;position:relative;height:26px;line-height:26px;font-size:.8em;font-weight:600;color:#fff;background-color:#1a1a1a;white-space:nowrap;vertical-align:middle;padding:0 12px 0 6px;margin:0 0 0 12px}.gl-star-rating[data-star-rating] .gl-star-rating-text:before{position:absolute;top:0;left:-12px;width:0;height:0;content:"";border-style:solid;border-width:13px 12px 13px 0;border-color:transparent #1a1a1a transparent transparent}[id^=geminilabs-site-reviews].widget ul.glsr-reviews{list-style:none;margin-left:0}[id^=geminilabs-site-reviews].widget .glsr-review-title,[id^=geminilabs-site-reviews].widget p{margin-bottom:1em}.glsr-review{margin-bottom:2em}.glsr-review p{margin:0 0 1em}.glsr-review-date{font-style:italic}.glsr-review-meta>span:not(:last-child){margin-right:.5em}.glsr-review-link{white-space:nowrap}[class*=glsr-star-]{display:inline-block;width:16px;height:16px;font-size:16px;vertical-align:text-top;background-repeat:no-repeat;background-size:16px}.glsr-star-empty{background-image:url(../img/star-empty.svg)}.glsr-star-full{background-image:url(../img/star-filled.svg)}.glsr-star-half{background-image:url(../img/star-half.svg)}.gl-star-rating[data-star-rating]{line-height:1}.glsr-field-messages{color:#1976D2}.glsr-has-error{color:#D32F2F}.glsr-field{margin-bottom:1em}.glsr-field-error{display:block;font-size:.875em}.glsr-field ul{list-style-type:none}.glsr-field label[for^=terms]{-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}.glsr-field label[for^=rating]:after,.glsr-field.glsr-required label:after{content:"*";margin-left:.33em}.glsr-field input[type=checkbox]{margin-right:.25em}.glsr-field select[name=rating]{display:block;height:26px;visibility:hidden}.glsr-form-messages{color:#0088d0}.glsr-field label[for^=rating]:after,.glsr-field-error,.glsr-field.glsr-required label:after,.glsr-form-description span,.gslr-has-errors{color:red}h3.glsr-form-title{margin:1.7142857143rem 0}.glsr-field,.shortcode-reviews-form p.glsr-form-description,.widget-area .widget p.glsr-form-description{line-height:1.5;margin:.7857142857rem 0}.glsr-field label{display:block;line-height:1.7142857143}.glsr-field input[type=text],.glsr-field input[type=email],.glsr-field input[type=url],.glsr-field textarea{width:100%;box-sizing:border-box;font-size:12px;font-size:.8571428571 rem;line-height:1.7142857143;padding:10px;padding:.8571428571 rem}
|
assets/img/booking.svg
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 540">
|
2 |
+
<title>booking-reviews</title>
|
3 |
+
<clipPath>
|
4 |
+
<path d="M0 0h960v540H0z"/>
|
5 |
+
</clipPath>
|
6 |
+
<g fill="none">
|
7 |
+
<path fill="#00347D" d="M0 0h960v540H0z"/>
|
8 |
+
<path fill="#003E8C" d="M676.33 566.25c2.26-.97 4.22-2.36 5.84-4.03l-4.2-28.26c-4.5-2.5-10.03-3.06-15.2-1.07-8.73 3.32-13.35 12.7-10.93 21.53l24.5 11.8zm-53.78-25.92c7.43-4.4 10.83-13.62 7.63-21.95-3.56-9.3-13.98-13.92-23.27-10.36-7.95 3.06-12.5 11.17-11.37 19.3l27.03 13zm-54.07-26.06c6.17-4.73 8.8-13.12 5.86-20.75-3.57-9.3-14-13.93-23.27-10.36-7.28 2.8-11.7 9.8-11.56 17.15l29 13.98zm95.87-71.8c-3.72 4.8-4.92 11.4-2.58 17.48 1.33 3.45 3.6 6.26 6.4 8.25l-3.82-25.74zM317.7 549.82c-.2-1.25-.52-2.5-1-3.72-3.55-9.28-13.97-13.92-23.26-10.35-9.3 3.56-13.92 13.98-10.36 23.27.83 2.17 2.04 4.1 3.53 5.7l31.1-14.9zm39.48-18.9c-.04-.1-.08-.2-.1-.3-3.58-9.3-14-13.94-23.28-10.37-9.3 3.56-13.93 13.98-10.36 23.27.4 1 .86 1.98 1.4 2.88l32.34-15.48zm38.87-18.63c-4.4-7.37-13.6-10.73-21.9-7.55-9.2 3.54-13.84 13.8-10.45 23.04l32.35-15.5zm38.25-18.33c-4.85-5.34-12.65-7.45-19.8-4.7-8.07 3.1-12.64 11.4-11.33 19.6L434.3 494zm37.56-18c-4.76-3.42-11.1-4.46-17-2.2-6.85 2.63-11.17 9-11.53 15.88L471.86 476zm42.3 12.12c5.06-4.88 7-12.5 4.33-19.46-3.6-9.28-14-13.92-23.3-10.36-6.6 2.53-10.85 8.54-11.47 15.14l30.42 14.66zm143.28-92.07c-.27.1-.53.2-.8.3-9.28 3.55-13.92 13.97-10.36 23.26 2.7 7 9.3 11.37 16.4 11.54l-5.24-35.1zm-368.9 98.64c5.6 1.33 10.46 5.3 12.68 11.07 3.56 9.3-1.07 19.7-10.36 23.27-2.65 1.03-5.4 1.38-8.03 1.14l5.72-35.47zm367.8-106.13c-.73.4-1.5.75-2.28 1.05-9.3 3.56-19.7-1.08-23.27-10.37-3.58-9.3 1.05-19.7 10.34-23.27 3.38-1.3 6.9-1.5 10.2-.8l4.98 33.38zm-359.5 54.76c1.68-1.58 3.7-2.88 5.97-3.76 9.3-3.56 19.73 1.08 23.3 10.36 3.56 9.3-1.08 19.7-10.37 23.27-8.58 3.3-18.14-.42-22.35-8.34l3.48-21.52zm390.03-137.2c-4.7-6.26-13.17-8.93-20.85-5.98-9.3 3.57-13.93 14-10.36 23.27 1.25 3.26 3.36 5.95 5.97 7.9l25.24-25.2zm67.47-67.37c-3.72-9-13.96-13.45-23.1-9.94-9.3 3.54-13.93 13.96-10.36 23.25 1.84 4.8 5.5 8.36 9.92 10.18l23.54-23.5zm-443.7 105.3c.5-.25 1-.48 1.55-.7 9.26-3.55 19.7 1.1 23.25 10.37 3.56 9.3-1.08 19.7-10.36 23.27-5.97 2.3-12.4 1.2-17.17-2.32l4.63-28.66-1.94-1.98zm459.34-170.68c4.17 1.88 7.62 5.34 9.4 9.95 3.56 9.3-1.08 19.7-10.37 23.27-9.27 3.56-19.7-1.07-23.25-10.36-3.56-9.27 1.07-19.7 10.36-23.25 1.4-.53 2.8-.88 4.23-1.06l9.66 1.46zm14.97 2.27c.13 1.7.5 3.42 1.15 5.1 2.5 6.5 8.37 10.74 14.85 11.45l12.27-12.25-28.27-4.3zM285.2 318.38c.53-6.7 4.8-12.83 11.5-15.4 9.28-3.56 19.7 1.08 23.27 10.37 3.56 9.28-1.08 19.7-10.36 23.27-1.7.65-3.43 1.03-5.17 1.15l-19.23-19.4zm439.46-151.92c-.6 6.64-4.85 12.7-11.5 15.25-9.28 3.58-19.7-1.06-23.27-10.35-1.3-3.4-1.53-6.97-.8-10.3l35.55 5.42zm-96.53-14.67c1.8 8.5-2.78 17.3-11.17 20.52-9.3 3.57-19.7-1.07-23.27-10.36-2-5.2-1.44-10.74 1.1-15.25l33.32 5.08zm30.26 4.6c-.37.14-.73.3-1.1.43-6.53 2.5-13.63.95-18.5-3.43l19.6 3zM238.5 271.3c9.12-3.66 13.64-13.97 10.1-23.17-3.56-9.3-13.97-13.93-23.26-10.36-4.82 1.85-8.4 5.54-10.2 9.96l23.36 23.57zm333.9-137.45c-1.1 5.98-5.2 11.28-11.3 13.62-9.27 3.56-19.7-1.08-23.26-10.36-3.56-9.27 1.08-19.7 10.36-23.25 5.4-2.08 11.2-1.37 15.8 1.4l8.4 18.6zm-235.72 15.9c7.5-.3 14.7 4.15 17.52 11.55 3.57 9.28-1.07 19.7-10.36 23.27-9.28 3.56-19.7-1.08-23.27-10.36-3.56-9.27 1.08-19.7 10.36-23.25.62-.24 1.23-.44 1.85-.6l3.9-.6zm49.12-13.6c3.88 1.94 7.08 5.28 8.76 9.66 3.56 9.3-1.07 19.7-10.36 23.3-9.3 3.55-19.7-1.1-23.27-10.37-1.62-4.23-1.55-8.68-.1-12.62l21.92-3.3 3.05-6.64zm170.03-39.02c-1.82 4.43-5.38 8.13-10.2 9.98-9.3 3.6-19.7-1.05-23.28-10.34-3.56-9.3 1.08-19.7 10.36-23.27 4.16-1.6 8.5-1.56 12.4-.2l10.74 23.84zM171.45 203.65c6.16-4.73 8.77-13.12 5.84-20.74-1.3-3.37-3.53-6.13-6.26-8.1l-24.55 3.7 24.94 25.16zm46.3-35.93c3.36 9.2-1.28 19.44-10.46 22.96-9.3 3.57-19.73-1.07-23.3-10.36-.92-2.4-1.3-4.9-1.17-7.32l34.92-5.28zm41.18-6.22c-1.07 6.02-5.17 11.34-11.3 13.7-8.66 3.32-18.33-.5-22.47-8.6l33.77-5.1zM411.3 80.63c3.6 1.98 6.54 5.2 8.12 9.33 3.57 9.3-1.07 19.7-10.36 23.27-4.27 1.64-8.8 1.54-12.76.05l15-32.65zm127.6-21.06c-2 3.17-5 5.74-8.77 7.2-9.28 3.55-19.7-1.1-23.27-10.37-3.56-9.3 1.08-19.7 10.36-23.27 3.08-1.2 6.28-1.47 9.3-.98l12.38 27.42zM436.78 25.14c3.3 2 6 5.08 7.5 8.97 3.57 9.3-1.06 19.73-10.35 23.3-4.04 1.54-8.28 1.54-12.1.3l14.95-32.55zm84.94-3.66c-1.86 2.12-4.26 3.84-7.08 4.92-9.3 3.56-19.7-1.07-23.27-10.36-3.56-9.3 1.07-19.7 10.36-23.27 2.14-.82 4.34-1.2 6.5-1.2l13.5 29.9zm-59.47-51.8c3.04 2 5.5 4.93 6.9 8.6 3.57 9.27-1.07 19.7-10.36 23.26-3.8 1.45-7.8 1.54-11.4.5l14.84-32.36zm42.1 13.28c-1.5 1.28-3.25 2.34-5.2 3.08-9.3 3.57-19.7-1.07-23.27-10.36-3.56-9.28 1.07-19.7 10.36-23.27 1.28-.47 2.58-.8 3.88-1l14.23 31.57zM486.83-55.9c-.98.62-2.04 1.16-3.17 1.6-3.55 1.35-7.27 1.52-10.7.68l7.5-16.38 6.37 14.1zm173.35 582.06c9.28-3.57 13.92-13.98 10.36-23.27-3.57-9.3-13.98-13.95-23.27-10.38-9.3 3.56-13.93 13.98-10.36 23.27 3.58 9.3 14 13.9 23.3 10.35zm-55.85-24.87c9.3-3.58 13.93-14 10.36-23.28-3.58-9.3-14-13.93-23.3-10.36-9.27 3.56-13.9 13.98-10.35 23.26 3.57 9.3 13.98 13.93 23.27 10.36zm40.36-15.5c9.26-3.57 13.9-14 10.34-23.28-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.57 9.3 14 13.93 23.28 10.36zm-96.23-9.38c9.3-3.56 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.92-23.26-10.36-9.3 3.56-13.93 13.97-10.37 23.26 3.58 9.3 14 13.93 23.28 10.36zm40.36-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.57-9.3-14-13.93-23.28-10.36-9.3 3.57-13.92 14-10.36 23.28 3.57 9.3 13.98 13.92 23.27 10.36zm40.36-15.48c9.27-3.57 13.9-13.98 10.35-23.27-3.57-9.3-14-13.93-23.27-10.36-9.3 3.57-13.94 14-10.37 23.28 3.57 9.27 14 13.9 23.28 10.35zm-298 68.08c9.3-3.56 13.93-13.98 10.37-23.27-3.57-9.3-14-13.92-23.27-10.36-9.28 3.56-13.92 13.97-10.35 23.26 3.56 9.3 13.98 13.92 23.26 10.36zm40.36-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.57-13.92 14-10.36 23.28 3.56 9.3 13.97 13.92 23.26 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.58-9.3-14-13.93-23.28-10.36-9.3 3.57-13.93 14-10.36 23.28 3.56 9.27 13.98 13.9 23.27 10.35zm40.35-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.94-23.27-10.37-9.26 3.6-13.9 14-10.36 23.3 3.58 9.28 14 13.92 23.28 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.58-9.3-14-13.94-23.28-10.37-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.37zm40.35-15.5c9.28-3.56 13.92-13.98 10.35-23.26-3.56-9.28-13.98-13.92-23.27-10.36-9.28 3.57-13.92 14-10.36 23.27 3.57 9.3 14 13.95 23.27 10.38zm40.35-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.57-9.27-13.98-13.9-23.27-10.35-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.48c9.3-3.57 13.93-14 10.37-23.28-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.94 23.26 10.37zm-257.62 52.58c9.3-3.56 13.92-13.98 10.36-23.27-3.57-9.28-13.98-13.9-23.27-10.36-9.3 3.57-13.92 14-10.36 23.27 3.56 9.3 14 13.94 23.27 10.37zm40.36-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.92 14-10.35 23.27 3.56 9.3 13.98 13.94 23.27 10.37zm40.35-15.48c9.28-3.56 13.92-13.98 10.35-23.27-3.56-9.3-13.98-13.9-23.27-10.35-9.3 3.57-13.92 13.98-10.36 23.27 3.56 9.3 13.97 13.93 23.26 10.36zm40.35-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.56-9.3-13.98-13.92-23.26-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.92 23.27 10.36zm40.35-15.48c9.3-3.57 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.55-13.93 13.97-10.37 23.26 3.56 9.3 14 13.92 23.26 10.36zm40.36-15.5c9.28-3.56 13.92-13.98 10.36-23.26-3.58-9.3-14-13.93-23.28-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.55 9.28 13.97 13.92 23.26 10.36zm40.35-15.48c9.3-3.57 13.94-14 10.37-23.27-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 14-10.36 23.27 3.57 9.28 14 13.92 23.27 10.36zm40.37-15.5c9.3-3.56 13.92-13.98 10.36-23.26-3.56-9.3-13.97-13.93-23.26-10.36-9.3 3.56-13.92 13.98-10.36 23.27 3.56 9.27 13.98 13.9 23.27 10.35zm-297.98 68.1c9.27-3.58 13.9-14 10.35-23.28-3.57-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.55 9.28 13.97 13.92 23.26 10.36zm40.34-15.5c9.3-3.57 13.94-14 10.37-23.27-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 14-10.36 23.27 3.57 9.28 14 13.92 23.27 10.36zm40.37-15.5c9.3-3.56 13.92-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.37 23.27 3.55 9.27 13.97 13.9 23.26 10.35zm40.36-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.58-9.28-14-13.92-23.28-10.36-9.3 3.57-13.93 14-10.36 23.27 3.55 9.28 13.97 13.92 23.26 10.35zM502 355.37c9.3-3.56 13.94-13.98 10.37-23.27-3.56-9.3-13.98-13.93-23.27-10.37-9.28 3.57-13.92 14-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.36zm40.37-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.57-9.27-14-13.9-23.27-10.35-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.94 23.27 10.37zm40.35-15.48c9.3-3.57 13.93-14 10.36-23.28-3.56-9.28-13.98-13.9-23.27-10.35-9.26 3.57-13.9 13.98-10.34 23.27 3.57 9.3 14 13.93 23.27 10.36zm40.36-15.5c9.28-3.57 13.92-13.98 10.36-23.27-3.57-9.3-13.98-13.92-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.58 9.3 14 13.92 23.3 10.36zm40.35-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.58-9.3-14-13.93-23.3-10.36-9.27 3.56-13.9 13.98-10.35 23.27 3.57 9.3 14 13.92 23.27 10.36zm40.36-15.5c9.27-3.55 13.9-13.97 10.34-23.25-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.55-13.92 13.97-10.36 23.26 3.57 9.28 14 13.92 23.28 10.36zm-338.36 83.6c9.3-3.56 13.93-13.98 10.36-23.27-3.55-9.28-13.97-13.9-23.26-10.35-9.28 3.57-13.92 13.98-10.36 23.27 3.57 9.3 14 13.93 23.27 10.36zM405.8 346c9.3-3.56 13.93-13.97 10.37-23.26-3.57-9.3-13.98-13.92-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.92 23.27 10.36zm40.36-15.48c9.3-3.57 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.93-23.26-10.36-9.3 3.55-13.93 13.97-10.37 23.26 3.55 9.28 13.97 13.92 23.25 10.36zm40.36-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.27 3.56-13.9 13.98-10.34 23.27 3.57 9.28 13.98 13.92 23.27 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.57-9.3-14-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.58 9.28 14 13.92 23.3 10.36zm40.35-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.58-9.3-14-13.93-23.3-10.36-9.27 3.56-13.9 13.98-10.35 23.26 3.57 9.3 13.98 13.93 23.27 10.37zm40.36-15.5c9.26-3.55 13.9-13.97 10.34-23.26-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.57 9.3 14 13.92 23.28 10.35zm40.33-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.57-9.3-14-13.93-23.28-10.37-9.28 3.58-13.92 14-10.36 23.28 3.57 9.3 14 13.94 23.27 10.37zm40.36-15.5c9.3-3.55 13.9-13.97 10.35-23.26-3.57-9.28-13.98-13.92-23.27-10.36-9.3 3.57-13.94 13.98-10.37 23.27 3.57 9.3 14 13.93 23.28 10.36zm40.34-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.55-9.3-13.97-13.92-23.25-10.36-9.3 3.57-13.93 13.98-10.37 23.27 3.57 9.3 14 13.94 23.27 10.37zm-378.7 99.06c9.3-3.56 13.94-13.98 10.37-23.26-3.55-9.3-13.97-13.93-23.26-10.36-9.28 3.56-13.92 13.98-10.36 23.26 3.56 9.3 13.98 13.93 23.26 10.36zm40.37-15.5c9.3-3.55 13.93-13.97 10.37-23.26-3.57-9.28-14-13.92-23.27-10.36-9.28 3.57-13.92 14-10.35 23.27 3.56 9.3 13.98 13.92 23.27 10.35zm40.36-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.93-23.27-10.37-9.28 3.58-13.92 14-10.36 23.28 3.56 9.3 14 13.94 23.26 10.37zm40.36-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.58-9.28-14-13.92-23.28-10.36-9.3 3.57-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.92-23.26-10.36-9.3 3.57-13.93 13.98-10.36 23.27 3.57 9.3 14 13.94 23.27 10.37zm40.36-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.57-9.3-14-13.93-23.28-10.36-9.3 3.56-13.92 13.98-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.37zm40.36-15.48c9.27-3.57 13.9-13.98 10.35-23.27-3.57-9.28-14-13.92-23.27-10.35-9.3 3.56-13.94 13.98-10.37 23.27 3.57 9.28 14 13.92 23.28 10.36zm40.34-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.55-9.3-13.97-13.93-23.26-10.36-9.28 3.56-13.92 13.98-10.36 23.27 3.57 9.28 14 13.92 23.27 10.36zm40.36-15.48c9.3-3.57 13.93-14 10.37-23.27-3.57-9.3-14-13.93-23.27-10.36-9.3 3.55-13.93 13.97-10.36 23.26 3.56 9.28 13.98 13.92 23.27 10.36zm-378.7 99.06c9.3-3.56 13.94-13.98 10.38-23.27-3.57-9.3-14-13.92-23.27-10.36-9.27 3.57-13.9 13.98-10.34 23.27 3.56 9.3 13.98 13.94 23.26 10.37zm40.37-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.92 13.98-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.37zm40.36-15.48c9.28-3.57 13.92-13.98 10.36-23.27-3.6-9.28-14-13.92-23.3-10.35-9.3 3.56-13.92 13.98-10.35 23.27 3.56 9.28 13.98 13.92 23.27 10.36zm40.35-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 13.98-10.36 23.27 3.6 9.28 14 13.92 23.3 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.57-9.3-13.98-13.93-23.27-10.36-9.3 3.55-13.93 13.97-10.36 23.26 3.56 9.28 13.98 13.92 23.27 10.36zm40.35-15.5c9.27-3.56 13.9-13.98 10.34-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 13.98-10.36 23.26 3.58 9.3 14 13.93 23.28 10.36zm40.34-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.56-9.28-13.97-13.92-23.26-10.36-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.48c9.3-3.56 13.92-13.98 10.36-23.27-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.94 14-10.37 23.27 3.56 9.3 13.98 13.93 23.26 10.36zM278.6 255.9c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.37-9.26 3.57-13.9 14-10.34 23.27 3.57 9.3 14 13.93 23.27 10.36zm40.36-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.56-9.28-13.98-13.92-23.27-10.36-9.3 3.57-13.92 14-10.36 23.27 3.58 9.3 14 13.93 23.3 10.36zm40.36-15.48c9.28-3.56 13.92-13.98 10.36-23.27-3.57-9.28-14-13.92-23.27-10.36-9.3 3.55-13.93 13.97-10.36 23.25 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.5c9.27-3.55 13.9-13.97 10.34-23.26-3.56-9.28-13.98-13.92-23.27-10.36-9.28 3.57-13.92 13.98-10.36 23.27 3.58 9.3 14 13.93 23.28 10.36zm40.34-15.48c9.28-3.56 13.92-13.98 10.36-23.27-3.56-9.3-13.98-13.92-23.26-10.36-9.3 3.6-13.93 14-10.36 23.3 3.56 9.3 13.98 13.92 23.27 10.35zm40.35-15.5c9.3-3.56 13.92-13.97 10.35-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.37 23.27 3.58 9.27 14 13.9 23.28 10.34zm40.35-15.48c9.28-3.57 13.92-13.98 10.36-23.27-3.56-9.3-13.97-13.95-23.26-10.38-9.3 3.56-13.93 13.98-10.36 23.27 3.55 9.27 13.97 13.9 23.26 10.35zm-297.98 68.08c9.28-3.56 13.92-13.98 10.36-23.27-3.57-9.3-14-13.92-23.27-10.36-9.3 3.6-13.93 14-10.36 23.3 3.57 9.3 14 13.9 23.28 10.35zm40.35-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.57-9.3-14-13.93-23.28-10.36-9.28 3.56-13.92 13.98-10.36 23.27 3.57 9.27 14 13.9 23.27 10.34zm40.36-15.48c9.28-3.57 13.9-14 10.35-23.27-3.57-9.3-13.98-13.95-23.27-10.38-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.27 13.98 13.9 23.27 10.35zm121.06-46.48c9.28-3.56 13.92-13.98 10.36-23.26-3.57-9.3-14-13.93-23.27-10.37-9.3 3.57-13.93 14-10.36 23.27 3.55 9.3 13.97 13.93 23.26 10.36zm40.35-15.5c9.3-3.55 13.93-13.97 10.36-23.26-3.56-9.28-13.98-13.92-23.27-10.36-9.3 3.57-13.93 14-10.37 23.27 3.57 9.3 13.98 13.93 23.27 10.36zm40.36-15.48c9.28-3.56 13.92-13.98 10.36-23.27-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.55 9.3 13.97 13.93 23.26 10.36zM449.4 97.74c9.3-3.57 13.94-14 10.37-23.27-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.58-13.9 14-10.35 23.3 3.57 9.27 13.98 13.9 23.27 10.35zm40.37-15.5c9.28-3.56 13.92-13.98 10.36-23.26-3.57-9.3-14-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.57 9.26 14 13.9 23.28 10.34zm-15.5-40.35c9.3-3.58 13.93-14 10.37-23.3-3.57-9.3-14-13.9-23.27-10.35-9.3 3.57-13.93 13.98-10.36 23.27 3.57 9.3 14 13.93 23.27 10.36z"/>
|
9 |
+
<ellipse fill="#fff" cx="414.946" cy="222.281" rx="10.624" ry="10.624"/>
|
10 |
+
<path fill="#fff" d="M556.4 238.9c-3.46 0-6.12 1.4-7.46 3.93l-.52.97-.84-.74c-2.96-2.54-8.24-5.57-16.8-5.57-17.1 0-28.6 12.8-28.6 31.9 0 19.06 11.9 32.38 28.94 32.38 5.8 0 10.4-1.35 14.04-4.12l1.4-1.04v1.76c0 8.58-5.54 13.3-15.56 13.3-4.9 0-9.32-1.18-12.3-2.26-3.86-1.17-6.12-.2-7.7 3.68l-1.44 3.58-2.06 5.24 1.26.68c6.43 3.4 14.78 5.42 22.34 5.42 15.56 0 33.7-7.95 33.7-30.37l.08-58.76h-8.47zm-22.02 46.9c-10 0-13.56-8.73-13.56-16.88 0-3.6.9-15.3 12.58-15.3 5.8 0 13.56 1.65 13.56 15.9 0 13.45-6.86 16.28-12.58 16.28zm-61.4-48.18c-9.35 0-15.3 4.15-18.64 7.67l-1.12 1.12-.4-1.54c-.98-3.78-4.3-5.84-9.3-5.84h-8.23l.05 64.06h18.25v-29.5c0-2.9.4-5.4 1.14-7.68 2.04-6.9 7.7-11.2 14.8-11.2 5.7 0 7.9 3 7.9 10.76v27.9c0 6.64 3.1 9.75 9.72 9.75h8.7l-.03-40.75c0-16.2-7.9-24.77-22.85-24.77v.02zm-57.8 1.44h-8.65l.05 49.56v14.5h9.25c.1 0 .2.03.33.03l4.3-.02h4.28v-.03h.03l.03-54.3c0-6.55-3.13-9.73-9.6-9.73h-.03zm-31.43 33.54c-.78-1.44-1.63-2.6-2.55-3.57l-.6-.63.6-.6c.92-.94 1.8-2.05 2.7-3.34l17.07-25.42h-20.72l-12.87 19.9c-.72 1.04-2.2 1.6-4.38 1.6h-2.95v-37.57c0-7.52-4.7-8.55-9.73-8.55h-8.67v88.72h18.4v-26.6h1.72c2.1 0 3.52.24 4.18 1.38l10.16 19.16c2.83 5.2 5.66 6.07 10.96 6.07h14.1l-10.5-17.36-6.95-13.18.05-.02zm-82.2-34.83c-19.4 0-33.46 13.95-33.46 33.2 0 19.2 14.04 33.16 33.43 33.16 19.42 0 33.56-13.95 33.56-33.16 0-19.23-14.14-33.18-33.57-33.18v-.03zm0 50.84c-9.16 0-15.52-7.26-15.52-17.64 0-10.4 6.36-17.67 15.5-17.67 9.2 0 15.65 7.3 15.65 17.67 0 10.56-6.3 17.66-15.64 17.66v-.02zm-71.75-50.83c-19.37 0-33.43 13.95-33.43 33.2 0 19.2 14.08 33.16 33.43 33.16 19.43 0 33.57-13.95 33.57-33.16 0-19.23-14.13-33.18-33.57-33.18v-.03zm0 50.84c-9.16 0-15.5-7.26-15.5-17.64 0-10.4 6.34-17.67 15.5-17.67 9.2 0 15.65 7.3 15.65 17.67 0 10.56-6.3 17.66-15.65 17.66v-.02zm-49-30.93l-2.72-1.52 2.35-2c2.73-2.34 7.3-7.6 7.3-16.73 0-13.95-10.82-22.95-27.55-22.95h-21.25c-4.97.2-8.96 4.2-9.02 9.22v79.1l8.8.04H160.56c18.6 0 30.64-10.14 30.64-25.8 0-8.48-3.9-15.7-10.42-19.37zm-19 30.2h-14.87v-17.8c0-3.8 1.46-5.76 4.7-6.22h10.16c7.22 0 11.9 4.56 11.9 11.94 0 7.58-4.56 12.06-11.9 12.08zm-14.87-47.94v-4.7c0-4.07 1.72-6.03 5.52-6.25h7.62c6.5 0 10.42 3.9 10.42 10.43 0 4.96-2.68 10.8-10.2 10.8h-13.37v-10.27z"/>
|
11 |
+
<ellipse fill="#009FE2" cx="582.698" cy="293.181" rx="10.624" ry="10.624"/>
|
12 |
+
<path fill="#009FE2" d="M808.2 237.83c-7.55 0-14.9 3.56-19.6 9.52l-1.34 1.67-1.04-1.88c-3.4-6.2-9.2-9.32-17.3-9.32-8.47 0-14.18 4.74-16.8 7.56l-1.75 1.86-.68-2.47c-.97-3.52-4.12-5.48-8.92-5.48h-7.65l-.08 63.8h17.44v-28.16c0-2.47.3-4.9.9-7.46 1.7-6.82 6.26-14.14 13.95-13.4 4.74.45 7.07 4.1 7.07 11.2v37.8h17.55v-28.15c0-3.1.3-5.4 1-7.7 1.4-6.5 6.17-13.16 13.6-13.16 5.37 0 7.36 3.03 7.36 11.22v28.48c0 6.45 2.86 9.32 9.32 9.32h8.2v-40.72c0-16.3-7.14-24.55-21.24-24.55zm-115.14-.05c-19.43 0-33.47 13.95-33.47 33.2 0 19.2 14.05 33.16 33.44 33.16 19.43 0 33.56-13.94 33.56-33.16 0-19.23-14.15-33.17-33.57-33.17l.03-.02zm0 50.84c-9.18 0-15.56-7.26-15.56-17.65 0-10.4 6.4-17.66 15.54-17.66 9.18 0 15.65 7.3 15.65 17.68 0 10.56-6.32 17.66-15.66 17.66l.02-.02zm-43.57-7.64c-.06.05-7.57 7.95-17.45 7.95-9 0-18.1-5.5-18.1-17.83 0-10.65 7.05-18.1 17.15-18.1 3.28 0 7 1.18 7.6 3.16l.06.33c1.35 4.47 5.4 4.7 6.22 4.7h9.56v-8.34c0-11-14.02-15-23.47-15-20.15 0-34.8 14.02-34.8 33.36s14.5 33.37 34.45 33.37c17.3 0 26.72-11.4 26.8-11.5l.52-.64-7.56-12.55-1 1.08z"/>
|
13 |
+
</g>
|
14 |
+
</svg>
|
assets/img/facebook.svg
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 540">
|
2 |
+
<title>facebook-reviews</title>
|
3 |
+
<clipPath>
|
4 |
+
<path d="M0 0h960v540H0z"/>
|
5 |
+
</clipPath>
|
6 |
+
<g fill="none">
|
7 |
+
<path fill="#3B5998" d="M0 0h960v540H0z"/>
|
8 |
+
<path fill="#4161A1" d="M676.33 566.25c2.26-.97 4.22-2.36 5.84-4.03l-4.2-28.26c-4.5-2.5-10.03-3.06-15.2-1.07-8.73 3.3-13.35 12.7-10.93 21.52l24.5 11.8zm-53.78-25.92c7.43-4.4 10.83-13.62 7.63-21.95-3.56-9.3-13.98-13.92-23.27-10.36-7.94 3.06-12.5 11.17-11.36 19.3l27.03 13zm-54.07-26.06c6.17-4.73 8.8-13.12 5.86-20.75-3.57-9.3-14-13.93-23.27-10.36-7.28 2.8-11.7 9.8-11.56 17.15l29 14zm95.87-71.8c-3.72 4.8-4.92 11.4-2.58 17.48 1.33 3.45 3.6 6.26 6.4 8.25l-3.82-25.74zM317.7 549.82c-.2-1.25-.52-2.5-1-3.72-3.55-9.28-13.97-13.92-23.26-10.35-9.3 3.56-13.92 13.98-10.36 23.27.83 2.17 2.04 4.1 3.53 5.7l31.1-14.9zm39.48-18.9c-.04-.1-.08-.2-.1-.3-3.58-9.3-14-13.94-23.28-10.37-9.3 3.56-13.93 13.98-10.36 23.27.4 1 .86 1.98 1.4 2.88l32.34-15.48zm38.87-18.63c-4.4-7.38-13.6-10.74-21.9-7.56-9.2 3.54-13.84 13.8-10.45 23.04l32.35-15.5zm38.25-18.34c-4.85-5.34-12.65-7.45-19.8-4.7-8.07 3.1-12.64 11.4-11.33 19.6L434.3 494zm37.56-18c-4.76-3.42-11.1-4.46-17-2.2-6.85 2.63-11.17 9-11.53 15.88L471.86 476zm42.3 12.12c5.06-4.88 7-12.5 4.33-19.46-3.6-9.28-14-13.92-23.3-10.36-6.6 2.53-10.86 8.54-11.48 15.14l30.42 14.66zM657.44 396c-.27.1-.53.2-.8.3-9.28 3.56-13.92 13.98-10.36 23.27 2.7 7 9.3 11.37 16.4 11.54l-5.24-35.1zm-368.9 98.65c5.6 1.33 10.46 5.3 12.68 11.07 3.56 9.3-1.07 19.7-10.36 23.27-2.65 1.02-5.4 1.37-8.03 1.13l5.72-35.47zm367.8-106.13c-.73.4-1.5.75-2.28 1.05-9.3 3.56-19.7-1.08-23.27-10.37-3.6-9.3 1.04-19.7 10.33-23.27 3.38-1.3 6.9-1.5 10.2-.8l4.98 33.38zm-359.5 54.76c1.68-1.58 3.7-2.88 5.97-3.76 9.3-3.56 19.74 1.08 23.3 10.36 3.57 9.3-1.07 19.7-10.36 23.27-8.58 3.3-18.14-.42-22.35-8.34l3.47-21.5zm390.03-137.2c-4.7-6.26-13.17-8.93-20.85-5.98-9.3 3.57-13.93 14-10.36 23.27 1.25 3.26 3.36 5.95 5.97 7.9l25.24-25.2zm67.47-67.37c-3.72-9-13.96-13.44-23.1-9.93-9.3 3.54-13.93 13.96-10.36 23.25 1.84 4.8 5.5 8.36 9.92 10.18l23.54-23.5zM310.64 344c.5-.24 1-.47 1.55-.7 9.25-3.54 19.7 1.1 23.24 10.38 3.56 9.3-1.08 19.7-10.36 23.27-5.97 2.3-12.4 1.2-17.17-2.32l4.64-28.66-1.94-1.98zm459.34-170.67c4.17 1.88 7.62 5.34 9.4 9.95 3.56 9.3-1.08 19.7-10.37 23.27-9.26 3.56-19.7-1.07-23.24-10.36-3.56-9.28 1.07-19.7 10.36-23.26 1.4-.53 2.8-.88 4.23-1.06l9.66 1.46zm14.97 2.27c.13 1.7.5 3.42 1.15 5.1 2.5 6.5 8.37 10.74 14.85 11.45l12.27-12.25-28.27-4.3zM285.2 318.38c.53-6.7 4.8-12.83 11.5-15.4 9.28-3.56 19.7 1.08 23.27 10.37 3.56 9.28-1.08 19.7-10.36 23.27-1.7.65-3.42 1.03-5.16 1.15l-19.23-19.4zm439.46-151.92c-.6 6.64-4.85 12.7-11.5 15.25-9.28 3.6-19.7-1.05-23.27-10.34-1.3-3.4-1.54-6.97-.8-10.3l35.54 5.42zm-96.53-14.67c1.8 8.5-2.78 17.3-11.17 20.5-9.3 3.58-19.7-1.06-23.27-10.35-2-5.2-1.45-10.74 1.1-15.25l33.3 5.08zm30.26 4.6c-.38.13-.74.3-1.1.42-6.54 2.5-13.64.95-18.5-3.43l19.6 3zM238.5 271.3c9.12-3.66 13.64-13.97 10.1-23.17-3.56-9.3-13.97-13.93-23.26-10.36-4.82 1.85-8.4 5.54-10.2 9.96l23.36 23.57zm333.9-137.45c-1.1 5.98-5.2 11.28-11.3 13.62-9.27 3.56-19.7-1.08-23.26-10.36-3.56-9.26 1.08-19.7 10.36-23.24 5.4-2.08 11.2-1.37 15.8 1.4l8.4 18.6zm-235.72 15.9c7.5-.3 14.7 4.15 17.52 11.55 3.57 9.28-1.07 19.7-10.36 23.27-9.28 3.56-19.7-1.08-23.27-10.36-3.56-9.26 1.08-19.7 10.36-23.24.62-.24 1.23-.44 1.85-.6l3.9-.6zm49.12-13.6c3.88 1.94 7.08 5.28 8.76 9.66 3.56 9.3-1.07 19.7-10.36 23.3-9.3 3.56-19.7-1.1-23.27-10.36-1.62-4.23-1.55-8.68-.1-12.62l21.92-3.3 3.05-6.64zm170.03-39.02c-1.82 4.43-5.38 8.13-10.2 9.98-9.3 3.6-19.7-1.04-23.28-10.33-3.56-9.3 1.08-19.7 10.36-23.27 4.17-1.6 8.5-1.56 12.4-.2l10.75 23.84zM171.45 203.65c6.16-4.73 8.77-13.12 5.84-20.74-1.3-3.36-3.54-6.12-6.27-8.1l-24.55 3.7 24.94 25.17zm46.3-35.93c3.36 9.2-1.28 19.44-10.46 22.96-9.3 3.57-19.74-1.07-23.3-10.36-.93-2.4-1.3-4.9-1.18-7.32l34.92-5.28zm41.18-6.22c-1.07 6.02-5.17 11.34-11.3 13.7-8.66 3.32-18.33-.5-22.47-8.6l33.77-5.1zM411.3 80.63c3.6 1.98 6.54 5.2 8.12 9.33 3.57 9.3-1.07 19.7-10.36 23.27-4.27 1.64-8.8 1.54-12.76.05l15-32.65zm127.6-21.06c-2 3.17-5 5.74-8.77 7.2-9.28 3.55-19.7-1.1-23.27-10.37-3.56-9.3 1.08-19.7 10.36-23.27 3.08-1.2 6.28-1.47 9.3-.98l12.38 27.42zM436.78 25.14c3.3 2 6 5.08 7.5 8.97 3.57 9.3-1.06 19.74-10.35 23.3-4.04 1.55-8.28 1.55-12.1.3l14.95-32.54zm84.94-3.66c-1.86 2.12-4.26 3.84-7.08 4.92-9.3 3.56-19.7-1.07-23.27-10.36-3.56-9.3 1.07-19.7 10.36-23.27 2.14-.82 4.34-1.2 6.5-1.2l13.5 29.9zm-59.47-51.8c3.04 2 5.5 4.93 6.9 8.6 3.57 9.27-1.07 19.7-10.36 23.26-3.8 1.45-7.8 1.54-11.4.5l14.83-32.36zm42.1 13.28c-1.5 1.28-3.25 2.34-5.2 3.08-9.3 3.57-19.7-1.07-23.27-10.36-3.56-9.28 1.07-19.7 10.36-23.27 1.28-.46 2.58-.8 3.88-1L504.35-17zM486.83-55.9c-.98.62-2.04 1.16-3.17 1.6-3.55 1.35-7.27 1.52-10.7.68l7.5-16.38 6.37 14.1zm173.35 582.06c9.28-3.57 13.92-13.98 10.36-23.27-3.57-9.3-13.98-13.96-23.27-10.4-9.3 3.57-13.93 14-10.36 23.28 3.6 9.3 14 13.9 23.3 10.35zm-55.85-24.87c9.3-3.6 13.93-14 10.36-23.3-3.6-9.3-14-13.92-23.3-10.35-9.28 3.56-13.9 13.98-10.36 23.26 3.57 9.3 13.98 13.94 23.27 10.37zm40.36-15.5c9.25-3.58 13.9-14 10.33-23.3-3.57-9.27-14-13.9-23.27-10.35-9.3 3.57-13.93 14-10.36 23.27 3.57 9.3 14 13.93 23.28 10.36zm-96.24-9.4c9.3-3.55 13.93-13.97 10.36-23.26-3.56-9.3-13.98-13.92-23.26-10.36-9.3 3.56-13.93 13.97-10.37 23.26 3.57 9.3 14 13.93 23.27 10.36zm40.36-15.5c9.3-3.55 13.93-13.96 10.36-23.25-3.57-9.3-14-13.93-23.28-10.36-9.3 3.56-13.92 14-10.36 23.27 3.57 9.3 13.98 13.92 23.27 10.36zm40.36-15.47c9.27-3.57 13.9-13.98 10.35-23.27-3.57-9.3-14-13.93-23.27-10.36-9.3 3.57-13.94 14-10.37 23.28 3.56 9.27 14 13.9 23.27 10.35zm-298 68.08c9.3-3.55 13.93-13.97 10.37-23.26-3.57-9.3-14-13.92-23.27-10.36-9.28 3.56-13.92 13.97-10.35 23.26 3.56 9.3 13.98 13.92 23.26 10.36zm40.36-15.5c9.3-3.55 13.93-13.96 10.36-23.25-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.92 14-10.36 23.27 3.56 9.3 13.97 13.92 23.26 10.36zm40.36-15.47c9.28-3.57 13.92-14 10.36-23.27-3.58-9.3-14-13.93-23.28-10.36-9.3 3.57-13.93 14-10.36 23.28 3.56 9.27 13.98 13.9 23.27 10.35zm40.35-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.55-9.3-13.97-13.94-23.26-10.37-9.26 3.6-13.9 14-10.36 23.3 3.58 9.28 14 13.92 23.28 10.36zm40.36-15.48c9.3-3.57 13.93-14 10.37-23.27-3.58-9.3-14-13.94-23.28-10.37-9.3 3.57-13.94 14-10.37 23.28 3.56 9.3 13.98 13.93 23.27 10.37zm40.36-15.5c9.28-3.56 13.92-13.98 10.35-23.26-3.55-9.3-13.97-13.93-23.26-10.37-9.28 3.57-13.92 14-10.36 23.27 3.57 9.3 14 13.95 23.27 10.38zm40.35-15.5c9.3-3.55 13.93-13.97 10.37-23.26-3.57-9.28-13.98-13.9-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.94 23.27 10.37zm40.36-15.48c9.3-3.57 13.93-14 10.37-23.28-3.57-9.3-14-13.93-23.27-10.37-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.94 23.26 10.37zm-257.62 52.58c9.3-3.56 13.92-13.98 10.36-23.27-3.57-9.28-13.98-13.9-23.27-10.36-9.3 3.57-13.92 14-10.36 23.27 3.56 9.3 14 13.93 23.27 10.36zm40.36-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.57-9.3-14-13.93-23.27-10.37-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.94 23.27 10.37zm40.35-15.48c9.28-3.56 13.92-13.98 10.35-23.27-3.56-9.3-13.98-13.9-23.27-10.35-9.3 3.57-13.92 13.98-10.36 23.27 3.56 9.3 13.97 13.93 23.26 10.36zm40.35-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.56-9.3-13.98-13.9-23.26-10.35-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.92 23.27 10.36zm40.35-15.48c9.3-3.58 13.93-14 10.36-23.28-3.55-9.3-13.97-13.93-23.26-10.36-9.3 3.55-13.93 13.97-10.37 23.26 3.56 9.3 14 13.92 23.26 10.36zm40.36-15.5c9.3-3.57 13.93-14 10.37-23.27-3.58-9.3-14-13.93-23.28-10.36-9.3 3.56-13.94 13.98-10.37 23.27 3.55 9.28 13.97 13.92 23.26 10.36zm40.36-15.5c9.3-3.56 13.94-14 10.37-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 14-10.36 23.27 3.57 9.28 14 13.92 23.27 10.36zm40.37-15.5c9.3-3.55 13.92-13.97 10.36-23.25-3.57-9.3-13.98-13.93-23.27-10.36-9.3 3.55-13.92 13.97-10.36 23.26 3.56 9.27 13.98 13.9 23.27 10.35zm-297.98 68.1c9.27-3.57 13.9-14 10.35-23.27-3.57-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.55 9.28 13.97 13.92 23.26 10.36zm40.34-15.5c9.3-3.56 13.93-14 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.93 14-10.37 23.27 3.57 9.28 14 13.92 23.27 10.36zm40.36-15.5c9.3-3.55 13.92-13.97 10.36-23.25-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.55-13.93 13.97-10.37 23.26 3.55 9.27 13.97 13.9 23.26 10.35zm40.36-15.5c9.28-3.54 13.92-13.96 10.36-23.25-3.58-9.28-14-13.92-23.28-10.36-9.3 3.56-13.93 14-10.36 23.26 3.55 9.28 13.97 13.92 23.26 10.35zM502 355.38c9.3-3.56 13.94-13.98 10.37-23.27-3.56-9.3-13.98-13.93-23.27-10.37-9.28 3.57-13.92 14-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.36zm40.37-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.57-9.26-14-13.9-23.27-10.34-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.94 23.27 10.37zm40.35-15.48c9.3-3.58 13.93-14 10.36-23.3-3.56-9.27-13.98-13.9-23.27-10.34-9.25 3.57-13.9 13.98-10.33 23.27 3.57 9.3 14 13.93 23.27 10.36zm40.36-15.5c9.28-3.58 13.92-14 10.36-23.28-3.57-9.3-13.98-13.92-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.6 9.3 14 13.92 23.3 10.36zm40.35-15.5c9.3-3.57 13.93-13.98 10.36-23.27-3.6-9.3-14-13.93-23.3-10.36-9.28 3.56-13.9 13.98-10.36 23.27 3.57 9.3 14 13.92 23.27 10.36zm40.36-15.5c9.26-3.56 13.9-13.98 10.33-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.55-13.92 13.97-10.36 23.26 3.57 9.28 14 13.92 23.28 10.36zm-338.37 83.6c9.3-3.57 13.93-14 10.36-23.28-3.56-9.28-13.98-13.9-23.27-10.35-9.28 3.57-13.92 13.98-10.36 23.27 3.57 9.3 14 13.93 23.27 10.36zM405.8 346c9.3-3.56 13.93-13.97 10.37-23.26-3.57-9.3-13.98-13.92-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.92 23.27 10.36zm40.36-15.48c9.3-3.57 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.93-23.26-10.36-9.3 3.54-13.93 13.96-10.37 23.25 3.54 9.28 13.96 13.92 23.24 10.36zm40.36-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.26 3.56-13.9 13.98-10.33 23.27 3.57 9.28 13.98 13.92 23.27 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.57-9.3-14-13.93-23.27-10.36-9.3 3.57-13.93 14-10.36 23.28 3.6 9.28 14 13.92 23.3 10.36zm40.35-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.6-9.3-14-13.93-23.3-10.36-9.28 3.56-13.9 13.98-10.36 23.26 3.57 9.3 13.98 13.93 23.27 10.37zm40.36-15.5c9.25-3.55 13.9-13.97 10.33-23.26-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.57 9.3 14 13.9 23.28 10.34zm40.32-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.57-9.3-14-13.94-23.28-10.38-9.28 3.58-13.92 14-10.36 23.28 3.57 9.3 14 13.94 23.27 10.37zm40.36-15.5c9.3-3.55 13.9-13.97 10.35-23.26-3.57-9.28-13.98-13.92-23.27-10.36-9.3 3.57-13.94 13.98-10.37 23.27 3.56 9.3 14 13.94 23.27 10.37zm40.34-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.55-9.3-13.97-13.9-23.25-10.35-9.3 3.57-13.93 13.98-10.37 23.27 3.57 9.3 14 13.94 23.27 10.37zm-378.7 99.06c9.3-3.56 13.94-13.98 10.37-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 13.98-10.36 23.26 3.56 9.3 13.98 13.93 23.26 10.36zm40.37-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.9 23.27 10.34zm40.35-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.55-9.3-13.97-13.94-23.26-10.38-9.28 3.58-13.92 14-10.36 23.28 3.56 9.3 14 13.94 23.26 10.37zm40.36-15.5c9.3-3.55 13.93-13.97 10.37-23.26-3.58-9.28-14-13.92-23.28-10.36-9.3 3.57-13.94 13.98-10.37 23.27 3.56 9.3 13.98 13.94 23.27 10.37zm40.36-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.9-23.26-10.35-9.3 3.57-13.93 13.98-10.36 23.27 3.57 9.3 14 13.94 23.27 10.37zm40.36-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.57-9.3-14-13.93-23.28-10.36-9.3 3.56-13.92 13.98-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.37zm40.36-15.48c9.27-3.57 13.9-13.98 10.35-23.27-3.57-9.28-14-13.92-23.27-10.35-9.3 3.56-13.94 13.98-10.37 23.27 3.56 9.28 14 13.92 23.27 10.36zm40.34-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.55-9.3-13.97-13.93-23.26-10.36-9.28 3.56-13.92 13.98-10.36 23.27 3.57 9.28 14 13.92 23.27 10.36zm40.36-15.48c9.3-3.57 13.93-14 10.37-23.27-3.57-9.3-14-13.93-23.27-10.36-9.3 3.54-13.93 13.96-10.36 23.25 3.56 9.28 13.98 13.92 23.27 10.36zm-378.7 99.06c9.3-3.56 13.94-13.98 10.38-23.27-3.57-9.3-14-13.9-23.27-10.35-9.28 3.57-13.9 13.98-10.35 23.27 3.56 9.3 13.98 13.94 23.26 10.37zm40.37-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.55-9.3-13.97-13.93-23.26-10.36-9.3 3.56-13.92 13.98-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.37zm40.36-15.48c9.3-3.57 13.93-13.98 10.37-23.27-3.6-9.28-14-13.92-23.3-10.35-9.3 3.56-13.92 13.98-10.35 23.27 3.56 9.28 13.98 13.92 23.27 10.36zm40.36-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 13.98-10.36 23.27 3.6 9.28 14 13.92 23.3 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.57-9.3-13.98-13.93-23.27-10.36-9.3 3.54-13.92 13.96-10.35 23.25 3.56 9.28 13.98 13.92 23.27 10.36zm40.35-15.5c9.27-3.56 13.9-13.98 10.34-23.26-3.55-9.3-13.97-13.93-23.26-10.36-9.28 3.56-13.92 13.98-10.36 23.26 3.58 9.3 14 13.93 23.28 10.36zm40.34-15.5c9.3-3.55 13.93-13.97 10.37-23.26-3.56-9.28-13.97-13.92-23.26-10.36-9.3 3.57-13.92 14-10.35 23.27 3.56 9.3 13.98 13.93 23.27 10.36zm40.36-15.48c9.3-3.56 13.92-13.98 10.36-23.27-3.57-9.28-14-13.92-23.27-10.36-9.3 3.58-13.94 14-10.37 23.28 3.56 9.3 13.98 13.93 23.26 10.36zM278.6 255.9c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.37-9.27 3.57-13.9 14-10.35 23.27 3.57 9.3 14 13.93 23.27 10.36zm40.36-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.56-9.28-13.98-13.92-23.27-10.36-9.3 3.57-13.92 14-10.36 23.27 3.57 9.3 14 13.93 23.3 10.36zm40.36-15.48c9.28-3.56 13.92-13.98 10.36-23.27-3.57-9.28-14-13.92-23.27-10.36-9.3 3.54-13.92 13.96-10.35 23.24 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.5c9.27-3.55 13.9-13.97 10.34-23.26-3.55-9.28-13.97-13.92-23.26-10.36-9.28 3.57-13.92 13.98-10.36 23.27 3.58 9.3 14 13.93 23.28 10.36zM440 193.94c9.3-3.56 13.93-13.98 10.37-23.27-3.56-9.3-13.98-13.92-23.26-10.36-9.3 3.6-13.92 14-10.35 23.3 3.56 9.3 13.98 13.93 23.27 10.36zm40.36-15.5c9.3-3.56 13.92-13.97 10.35-23.26-3.55-9.3-13.97-13.93-23.26-10.36-9.3 3.56-13.93 13.98-10.37 23.27 3.58 9.26 14 13.9 23.28 10.33zm40.35-15.48c9.3-3.57 13.93-13.98 10.37-23.27-3.56-9.3-13.97-13.96-23.26-10.4-9.3 3.57-13.92 14-10.35 23.28 3.55 9.27 13.97 13.9 23.26 10.35zm-297.97 68.08c9.28-3.56 13.92-13.98 10.36-23.27-3.58-9.3-14-13.92-23.28-10.36-9.3 3.6-13.93 14-10.36 23.3 3.57 9.3 14 13.9 23.28 10.36zm40.35-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.57-9.3-14-13.93-23.28-10.36-9.28 3.56-13.92 13.98-10.36 23.27 3.57 9.26 14 13.9 23.27 10.33zm40.36-15.48c9.28-3.57 13.9-14 10.35-23.27-3.58-9.3-14-13.96-23.28-10.4-9.3 3.57-13.93 14-10.36 23.28 3.56 9.27 13.98 13.9 23.27 10.35zm121.06-46.48c9.28-3.56 13.92-13.98 10.36-23.26-3.57-9.3-14-13.93-23.27-10.37-9.3 3.57-13.94 14-10.37 23.27 3.55 9.3 13.97 13.93 23.26 10.36zm40.35-15.5c9.3-3.55 13.93-13.97 10.36-23.26-3.55-9.28-13.97-13.92-23.26-10.36-9.3 3.57-13.93 14-10.37 23.27 3.57 9.3 13.98 13.93 23.27 10.36zm40.36-15.48c9.3-3.56 13.93-13.98 10.37-23.27-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.55 9.3 13.97 13.93 23.26 10.36zm-55.8-24.86c9.3-3.57 13.94-14 10.37-23.27-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.6-13.9 14-10.35 23.3 3.57 9.28 13.98 13.9 23.27 10.36zm40.37-15.5c9.28-3.56 13.92-13.98 10.36-23.26-3.57-9.3-14-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.57 9.25 14 13.9 23.28 10.33zm-15.5-40.35c9.3-3.6 13.93-14 10.37-23.3-3.57-9.3-14-13.9-23.27-10.36-9.3 3.57-13.93 13.98-10.36 23.27 3.58 9.3 14 13.94 23.28 10.37z"/>
|
9 |
+
<path fill="#fff" d="M500.92 246.9c-5.35 0-9.2 1.77-13.12 3.56v40.45c3.75.37 5.9.37 9.45.37 12.85 0 14.6-5.88 14.6-14.1v-19.32c0-6.06-2-10.94-10.93-10.94zm-85.3-2.2c-8.92 0-10.96 4.9-10.96 10.96v3.4h21.88v-3.4c0-6.06-2.03-10.95-10.93-10.95zm-165.28 42.12c0 4.8 2.26 7.28 7.25 7.28 5.35 0 8.52-1.74 12.43-3.54v-9.6h-11.7c-5.56 0-8 1.04-8 5.86zm333.57-39.9c-8.92 0-12 4.87-12 10.93V280c0 6.07 3.08 10.96 12 10.96 8.9 0 12.03-4.9 12.03-10.97v-22.15c0-6.06-3.12-10.94-12.02-10.94zm-377.3 64.72h-26.25v-63.52h-13.12v-21.9h13.12V213.1c0-17.85 7.4-28.47 28.43-28.47h17.52v21.9h-10.95c-8.2 0-8.73 3.06-8.73 8.77l-.03 10.96h19.82l-2.32 21.9h-17.5v63.5zm89.68.16H274.4l-.94-5.53c-10 5.53-18.9 6.43-24.78 6.43-16.04 0-24.58-10.72-24.58-25.54 0-17.48 9.96-23.72 27.8-23.72h18.13v-3.78c0-8.92-1.03-11.54-14.75-11.54h-22.44l2.2-21.9h24.52c30.12 0 36.72 9.52 36.72 33.62v51.96zm74.38-62.08c-13.6-2.33-17.52-2.84-24.07-2.84-11.78 0-15.33 2.6-15.33 12.58v18.9c0 10 3.55 12.6 15.32 12.6 6.54 0 10.45-.53 24.06-2.87v21.34c-11.92 2.67-19.7 3.38-26.26 3.38-28.17 0-39.37-14.82-39.37-36.23v-15.34c0-21.42 11.2-36.27 39.37-36.27 6.57 0 14.34.7 26.26 3.38v21.34zm82.13 26.87h-48.14v1.76c0 10 3.56 12.6 15.32 12.6 10.57 0 17.02-.53 30.6-2.87v21.34c-13.1 2.67-19.92 3.38-32.78 3.38-28.17 0-39.38-14.82-39.38-36.23v-17.54c0-18.73 8.3-34.07 37.2-34.07 28.86 0 37.17 15.16 37.17 34.06v17.53zm85.3.4c0 20.7-5.92 35.78-41.74 35.78-12.93 0-20.52-1.14-34.8-3.34V191.18l26.24-4.38v41.37c5.67-2.1 13-3.18 19.7-3.18 26.23 0 30.6 11.76 30.6 30.66V277zm84.1.45c0 17.85-7.37 35.16-38.2 35.16-30.84 0-38.35-17.3-38.35-35.15v-17.23c0-17.86 7.5-35.17 38.35-35.17 30.83 0 38.2 17.3 38.2 35.17v17.23zm84.04 0c0 17.85-7.38 35.16-38.2 35.16-30.85 0-38.35-17.3-38.35-35.15v-17.23c0-17.86 7.5-35.17 38.34-35.17 30.82 0 38.2 17.3 38.2 35.17v17.23zm86.24 34.2h-28.43L740 271.48v40.14h-26.25V191.18L740 186.8v77.55l24.05-38.12h28.43l-26.25 41.6 26.25 43.8zM667.95 246.9c-8.9 0-12 4.9-12 10.95V280c0 6.07 3.1 10.96 12 10.96S680 286.06 680 280v-22.15c0-6.06-3.15-10.94-12.05-10.94zM807.46 296.66c4.42 0 7.94 3.6 7.94 8.1 0 4.57-3.52 8.13-7.97 8.13-4.43 0-8.02-3.57-8.02-8.14 0-4.5 3.6-8.1 8.03-8.1h.03zm-.03 1.26c-3.56 0-6.47 3.06-6.47 6.84 0 3.85 2.9 6.87 6.5 6.87 3.6.04 6.47-3.02 6.47-6.84s-2.87-6.88-6.47-6.88h-.03zm-1.5 11.56h-1.45v-9.04c.75-.1 1.47-.22 2.55-.22 1.37 0 2.26.3 2.8.68.54.4.83 1 .83 1.87 0 1.2-.8 1.9-1.75 2.2v.08c.8.14 1.33.86 1.5 2.2.22 1.4.44 1.93.6 2.23h-1.52c-.22-.3-.43-1.12-.62-2.3-.2-1.16-.8-1.6-1.94-1.6h-1v3.9zm0-5h1.03c1.18 0 2.2-.44 2.2-1.56 0-.8-.58-1.58-2.2-1.58-.47 0-.8.03-1.04.07v3.07z"/>
|
10 |
+
</g>
|
11 |
+
</svg>
|
assets/img/google.svg
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 540">
|
2 |
+
<title>google-reviews</title>
|
3 |
+
<clipPath>
|
4 |
+
<path d="M0 0h960v540H0z"/>
|
5 |
+
</clipPath>
|
6 |
+
<g fill="none">
|
7 |
+
<path fill="#C6DAFC" d="M0 0h960v540H0z"/>
|
8 |
+
<path fill="#D3E3FC" d="M676.33 566.25c2.26-.97 4.22-2.36 5.84-4.03l-4.2-28.26c-4.5-2.5-10.03-3.06-15.2-1.07-8.73 3.32-13.35 12.7-10.93 21.53l24.5 11.8zm-53.78-25.92c7.43-4.4 10.83-13.62 7.63-21.95-3.56-9.3-13.98-13.92-23.27-10.36-7.95 3.06-12.5 11.17-11.37 19.3l27.03 13zm-54.07-26.06c6.17-4.73 8.8-13.12 5.86-20.75-3.57-9.3-14-13.93-23.27-10.36-7.28 2.8-11.7 9.8-11.56 17.15l29 13.98zm95.87-71.8c-3.72 4.8-4.92 11.4-2.58 17.48 1.33 3.45 3.6 6.26 6.4 8.25l-3.82-25.74zM317.7 549.82c-.2-1.25-.52-2.5-1-3.72-3.55-9.28-13.97-13.92-23.26-10.35-9.3 3.56-13.92 13.98-10.36 23.27.83 2.17 2.04 4.1 3.53 5.7l31.1-14.9zm39.48-18.9c-.04-.1-.08-.2-.1-.3-3.58-9.3-14-13.94-23.28-10.37-9.3 3.56-13.93 13.98-10.36 23.27.4 1 .86 1.98 1.4 2.88l32.34-15.48zm38.87-18.63c-4.4-7.37-13.6-10.73-21.9-7.55-9.2 3.54-13.84 13.8-10.45 23.04l32.35-15.5zm38.25-18.33c-4.85-5.34-12.65-7.45-19.8-4.7-8.07 3.1-12.64 11.4-11.33 19.6L434.3 494zm37.56-18c-4.76-3.42-11.1-4.46-17-2.2-6.85 2.63-11.17 9-11.53 15.88L471.86 476zm42.3 12.12c5.06-4.88 7-12.5 4.33-19.46-3.6-9.28-14-13.92-23.3-10.36-6.6 2.53-10.85 8.54-11.47 15.14l30.42 14.66zm143.28-92.07c-.27.1-.53.2-.8.3-9.28 3.55-13.92 13.97-10.36 23.26 2.7 7 9.3 11.37 16.4 11.54l-5.24-35.1zm-368.9 98.64c5.6 1.33 10.46 5.3 12.68 11.07 3.56 9.3-1.07 19.7-10.36 23.27-2.65 1.03-5.4 1.38-8.03 1.14l5.72-35.47zm367.8-106.13c-.73.4-1.5.75-2.28 1.05-9.3 3.56-19.7-1.08-23.27-10.37-3.58-9.3 1.05-19.7 10.34-23.27 3.38-1.3 6.9-1.5 10.2-.8l4.98 33.38zm-359.5 54.76c1.68-1.58 3.7-2.88 5.97-3.76 9.3-3.56 19.73 1.08 23.3 10.36 3.56 9.3-1.08 19.7-10.37 23.27-8.58 3.3-18.14-.42-22.35-8.34l3.48-21.52zm390.03-137.2c-4.7-6.26-13.17-8.93-20.85-5.98-9.3 3.57-13.93 14-10.36 23.27 1.25 3.26 3.36 5.95 5.97 7.9l25.24-25.2zm67.47-67.37c-3.72-9-13.96-13.45-23.1-9.94-9.3 3.54-13.93 13.96-10.36 23.25 1.84 4.8 5.5 8.36 9.92 10.18l23.54-23.5zm-443.7 105.3c.5-.25 1-.48 1.55-.7 9.26-3.55 19.7 1.1 23.25 10.37 3.56 9.3-1.08 19.7-10.36 23.27-5.97 2.3-12.4 1.2-17.17-2.32l4.63-28.66-1.94-1.98zm459.34-170.68c4.17 1.88 7.62 5.34 9.4 9.95 3.56 9.3-1.08 19.7-10.37 23.27-9.27 3.56-19.7-1.07-23.25-10.36-3.56-9.27 1.07-19.7 10.36-23.25 1.4-.53 2.8-.88 4.23-1.06l9.66 1.46zm14.97 2.27c.13 1.7.5 3.42 1.15 5.1 2.5 6.5 8.37 10.74 14.85 11.45l12.27-12.25-28.27-4.3zM285.2 318.38c.53-6.7 4.8-12.83 11.5-15.4 9.28-3.56 19.7 1.08 23.27 10.37 3.56 9.28-1.08 19.7-10.36 23.27-1.7.65-3.43 1.03-5.17 1.15l-19.23-19.4zm439.46-151.92c-.6 6.64-4.85 12.7-11.5 15.25-9.28 3.58-19.7-1.06-23.27-10.35-1.3-3.4-1.53-6.97-.8-10.3l35.55 5.42zm-96.53-14.67c1.8 8.5-2.78 17.3-11.17 20.52-9.3 3.57-19.7-1.07-23.27-10.36-2-5.2-1.44-10.74 1.1-15.25l33.32 5.08zm30.26 4.6c-.37.14-.73.3-1.1.43-6.53 2.5-13.63.95-18.5-3.43l19.6 3zM238.5 271.3c9.12-3.66 13.64-13.97 10.1-23.17-3.56-9.3-13.97-13.93-23.26-10.36-4.82 1.85-8.4 5.54-10.2 9.96l23.36 23.57zm333.9-137.45c-1.1 5.98-5.2 11.28-11.3 13.62-9.27 3.56-19.7-1.08-23.26-10.36-3.56-9.27 1.08-19.7 10.36-23.25 5.4-2.08 11.2-1.37 15.8 1.4l8.4 18.6zm-235.72 15.9c7.5-.3 14.7 4.15 17.52 11.55 3.57 9.28-1.07 19.7-10.36 23.27-9.28 3.56-19.7-1.08-23.27-10.36-3.56-9.27 1.08-19.7 10.36-23.25.62-.24 1.23-.44 1.85-.6l3.9-.6zm49.12-13.6c3.88 1.94 7.08 5.28 8.76 9.66 3.56 9.3-1.07 19.7-10.36 23.3-9.3 3.55-19.7-1.1-23.27-10.37-1.62-4.23-1.55-8.68-.1-12.62l21.92-3.3 3.05-6.64zm170.03-39.02c-1.82 4.43-5.38 8.13-10.2 9.98-9.3 3.6-19.7-1.05-23.28-10.34-3.56-9.3 1.08-19.7 10.36-23.27 4.16-1.6 8.5-1.56 12.4-.2l10.74 23.84zM171.45 203.65c6.16-4.73 8.77-13.12 5.84-20.74-1.3-3.37-3.53-6.13-6.26-8.1l-24.55 3.7 24.94 25.16zm46.3-35.93c3.36 9.2-1.28 19.44-10.46 22.96-9.3 3.57-19.73-1.07-23.3-10.36-.92-2.4-1.3-4.9-1.17-7.32l34.92-5.28zm41.18-6.22c-1.07 6.02-5.17 11.34-11.3 13.7-8.66 3.32-18.33-.5-22.47-8.6l33.77-5.1zM411.3 80.63c3.6 1.98 6.54 5.2 8.12 9.33 3.57 9.3-1.07 19.7-10.36 23.27-4.27 1.64-8.8 1.54-12.76.05l15-32.65zm127.6-21.06c-2 3.17-5 5.74-8.77 7.2-9.28 3.55-19.7-1.1-23.27-10.37-3.56-9.3 1.08-19.7 10.36-23.27 3.08-1.2 6.28-1.47 9.3-.98l12.38 27.42zM436.78 25.14c3.3 2 6 5.08 7.5 8.97 3.57 9.3-1.06 19.73-10.35 23.3-4.04 1.54-8.28 1.54-12.1.3l14.95-32.55zm84.94-3.66c-1.86 2.12-4.26 3.84-7.08 4.92-9.3 3.56-19.7-1.07-23.27-10.36-3.56-9.3 1.07-19.7 10.36-23.27 2.14-.82 4.34-1.2 6.5-1.2l13.5 29.9zm-59.47-51.8c3.04 2 5.5 4.93 6.9 8.6 3.57 9.27-1.07 19.7-10.36 23.26-3.8 1.45-7.8 1.54-11.4.5l14.84-32.36zm42.1 13.28c-1.5 1.28-3.25 2.34-5.2 3.08-9.3 3.57-19.7-1.07-23.27-10.36-3.56-9.28 1.07-19.7 10.36-23.27 1.28-.47 2.58-.8 3.88-1l14.23 31.57zM486.83-55.9c-.98.62-2.04 1.16-3.17 1.6-3.55 1.35-7.27 1.52-10.7.68l7.5-16.38 6.37 14.1zm173.35 582.06c9.28-3.57 13.92-13.98 10.36-23.27-3.57-9.3-13.98-13.95-23.27-10.38-9.3 3.56-13.93 13.98-10.36 23.27 3.58 9.3 14 13.9 23.3 10.35zm-55.85-24.87c9.3-3.58 13.93-14 10.36-23.28-3.58-9.3-14-13.93-23.3-10.36-9.27 3.56-13.9 13.98-10.35 23.26 3.57 9.3 13.98 13.93 23.27 10.36zm40.36-15.5c9.26-3.57 13.9-14 10.34-23.28-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.57 9.3 14 13.93 23.28 10.36zm-96.23-9.38c9.3-3.56 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.92-23.26-10.36-9.3 3.56-13.93 13.97-10.37 23.26 3.58 9.3 14 13.93 23.28 10.36zm40.36-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.57-9.3-14-13.93-23.28-10.36-9.3 3.57-13.92 14-10.36 23.28 3.57 9.3 13.98 13.92 23.27 10.36zm40.36-15.48c9.27-3.57 13.9-13.98 10.35-23.27-3.57-9.3-14-13.93-23.27-10.36-9.3 3.57-13.94 14-10.37 23.28 3.57 9.27 14 13.9 23.28 10.35zm-298 68.08c9.3-3.56 13.93-13.98 10.37-23.27-3.57-9.3-14-13.92-23.27-10.36-9.28 3.56-13.92 13.97-10.35 23.26 3.56 9.3 13.98 13.92 23.26 10.36zm40.36-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.57-13.92 14-10.36 23.28 3.56 9.3 13.97 13.92 23.26 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.58-9.3-14-13.93-23.28-10.36-9.3 3.57-13.93 14-10.36 23.28 3.56 9.27 13.98 13.9 23.27 10.35zm40.35-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.94-23.27-10.37-9.26 3.6-13.9 14-10.36 23.3 3.58 9.28 14 13.92 23.28 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.58-9.3-14-13.94-23.28-10.37-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.37zm40.35-15.5c9.28-3.56 13.92-13.98 10.35-23.26-3.56-9.28-13.98-13.92-23.27-10.36-9.28 3.57-13.92 14-10.36 23.27 3.57 9.3 14 13.95 23.27 10.38zm40.35-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.57-9.27-13.98-13.9-23.27-10.35-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.48c9.3-3.57 13.93-14 10.37-23.28-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.94 23.26 10.37zm-257.62 52.58c9.3-3.56 13.92-13.98 10.36-23.27-3.57-9.28-13.98-13.9-23.27-10.36-9.3 3.57-13.92 14-10.36 23.27 3.56 9.3 14 13.94 23.27 10.37zm40.36-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.92 14-10.35 23.27 3.56 9.3 13.98 13.94 23.27 10.37zm40.35-15.48c9.28-3.56 13.92-13.98 10.35-23.27-3.56-9.3-13.98-13.9-23.27-10.35-9.3 3.57-13.92 13.98-10.36 23.27 3.56 9.3 13.97 13.93 23.26 10.36zm40.35-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.56-9.3-13.98-13.92-23.26-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.92 23.27 10.36zm40.35-15.48c9.3-3.57 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.55-13.93 13.97-10.37 23.26 3.56 9.3 14 13.92 23.26 10.36zm40.36-15.5c9.28-3.56 13.92-13.98 10.36-23.26-3.58-9.3-14-13.93-23.28-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.55 9.28 13.97 13.92 23.26 10.36zm40.35-15.48c9.3-3.57 13.94-14 10.37-23.27-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 14-10.36 23.27 3.57 9.28 14 13.92 23.27 10.36zm40.37-15.5c9.3-3.56 13.92-13.98 10.36-23.26-3.56-9.3-13.97-13.93-23.26-10.36-9.3 3.56-13.92 13.98-10.36 23.27 3.56 9.27 13.98 13.9 23.27 10.35zm-297.98 68.1c9.27-3.58 13.9-14 10.35-23.28-3.57-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.55 9.28 13.97 13.92 23.26 10.36zm40.34-15.5c9.3-3.57 13.94-14 10.37-23.27-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 14-10.36 23.27 3.57 9.28 14 13.92 23.27 10.36zm40.37-15.5c9.3-3.56 13.92-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.37 23.27 3.55 9.27 13.97 13.9 23.26 10.35zm40.36-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.58-9.28-14-13.92-23.28-10.36-9.3 3.57-13.93 14-10.36 23.27 3.55 9.28 13.97 13.92 23.26 10.35zM502 355.37c9.3-3.56 13.94-13.98 10.37-23.27-3.56-9.3-13.98-13.93-23.27-10.37-9.28 3.57-13.92 14-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.36zm40.37-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.57-9.27-14-13.9-23.27-10.35-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.94 23.27 10.37zm40.35-15.48c9.3-3.57 13.93-14 10.36-23.28-3.56-9.28-13.98-13.9-23.27-10.35-9.26 3.57-13.9 13.98-10.34 23.27 3.57 9.3 14 13.93 23.27 10.36zm40.36-15.5c9.28-3.57 13.92-13.98 10.36-23.27-3.57-9.3-13.98-13.92-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.58 9.3 14 13.92 23.3 10.36zm40.35-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.58-9.3-14-13.93-23.3-10.36-9.27 3.56-13.9 13.98-10.35 23.27 3.57 9.3 14 13.92 23.27 10.36zm40.36-15.5c9.27-3.55 13.9-13.97 10.34-23.25-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.55-13.92 13.97-10.36 23.26 3.57 9.28 14 13.92 23.28 10.36zm-338.36 83.6c9.3-3.56 13.93-13.98 10.36-23.27-3.55-9.28-13.97-13.9-23.26-10.35-9.28 3.57-13.92 13.98-10.36 23.27 3.57 9.3 14 13.93 23.27 10.36zM405.8 346c9.3-3.56 13.93-13.97 10.37-23.26-3.57-9.3-13.98-13.92-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.92 23.27 10.36zm40.36-15.48c9.3-3.57 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.93-23.26-10.36-9.3 3.55-13.93 13.97-10.37 23.26 3.55 9.28 13.97 13.92 23.25 10.36zm40.36-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.27 3.56-13.9 13.98-10.34 23.27 3.57 9.28 13.98 13.92 23.27 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.57-9.3-14-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.58 9.28 14 13.92 23.3 10.36zm40.35-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.58-9.3-14-13.93-23.3-10.36-9.27 3.56-13.9 13.98-10.35 23.26 3.57 9.3 13.98 13.93 23.27 10.37zm40.36-15.5c9.26-3.55 13.9-13.97 10.34-23.26-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.57 9.3 14 13.92 23.28 10.35zm40.33-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.57-9.3-14-13.93-23.28-10.37-9.28 3.58-13.92 14-10.36 23.28 3.57 9.3 14 13.94 23.27 10.37zm40.36-15.5c9.3-3.55 13.9-13.97 10.35-23.26-3.57-9.28-13.98-13.92-23.27-10.36-9.3 3.57-13.94 13.98-10.37 23.27 3.57 9.3 14 13.93 23.28 10.36zm40.34-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.55-9.3-13.97-13.92-23.25-10.36-9.3 3.57-13.93 13.98-10.37 23.27 3.57 9.3 14 13.94 23.27 10.37zm-378.7 99.06c9.3-3.56 13.94-13.98 10.37-23.26-3.55-9.3-13.97-13.93-23.26-10.36-9.28 3.56-13.92 13.98-10.36 23.26 3.56 9.3 13.98 13.93 23.26 10.36zm40.37-15.5c9.3-3.55 13.93-13.97 10.37-23.26-3.57-9.28-14-13.92-23.27-10.36-9.28 3.57-13.92 14-10.35 23.27 3.56 9.3 13.98 13.92 23.27 10.35zm40.36-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.93-23.27-10.37-9.28 3.58-13.92 14-10.36 23.28 3.56 9.3 14 13.94 23.26 10.37zm40.36-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.58-9.28-14-13.92-23.28-10.36-9.3 3.57-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.92-23.26-10.36-9.3 3.57-13.93 13.98-10.36 23.27 3.57 9.3 14 13.94 23.27 10.37zm40.36-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.57-9.3-14-13.93-23.28-10.36-9.3 3.56-13.92 13.98-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.37zm40.36-15.48c9.27-3.57 13.9-13.98 10.35-23.27-3.57-9.28-14-13.92-23.27-10.35-9.3 3.56-13.94 13.98-10.37 23.27 3.57 9.28 14 13.92 23.28 10.36zm40.34-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.55-9.3-13.97-13.93-23.26-10.36-9.28 3.56-13.92 13.98-10.36 23.27 3.57 9.28 14 13.92 23.27 10.36zm40.36-15.48c9.3-3.57 13.93-14 10.37-23.27-3.57-9.3-14-13.93-23.27-10.36-9.3 3.55-13.93 13.97-10.36 23.26 3.56 9.28 13.98 13.92 23.27 10.36zm-378.7 99.06c9.3-3.56 13.94-13.98 10.38-23.27-3.57-9.3-14-13.92-23.27-10.36-9.27 3.57-13.9 13.98-10.34 23.27 3.56 9.3 13.98 13.94 23.26 10.37zm40.37-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.92 13.98-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.37zm40.36-15.48c9.28-3.57 13.92-13.98 10.36-23.27-3.6-9.28-14-13.92-23.3-10.35-9.3 3.56-13.92 13.98-10.35 23.27 3.56 9.28 13.98 13.92 23.27 10.36zm40.35-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 13.98-10.36 23.27 3.6 9.28 14 13.92 23.3 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.57-9.3-13.98-13.93-23.27-10.36-9.3 3.55-13.93 13.97-10.36 23.26 3.56 9.28 13.98 13.92 23.27 10.36zm40.35-15.5c9.27-3.56 13.9-13.98 10.34-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 13.98-10.36 23.26 3.58 9.3 14 13.93 23.28 10.36zm40.34-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.56-9.28-13.97-13.92-23.26-10.36-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.48c9.3-3.56 13.92-13.98 10.36-23.27-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.94 14-10.37 23.27 3.56 9.3 13.98 13.93 23.26 10.36zM278.6 255.9c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.37-9.26 3.57-13.9 14-10.34 23.27 3.57 9.3 14 13.93 23.27 10.36zm40.36-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.56-9.28-13.98-13.92-23.27-10.36-9.3 3.57-13.92 14-10.36 23.27 3.58 9.3 14 13.93 23.3 10.36zm40.36-15.48c9.28-3.56 13.92-13.98 10.36-23.27-3.57-9.28-14-13.92-23.27-10.36-9.3 3.55-13.93 13.97-10.36 23.25 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.5c9.27-3.55 13.9-13.97 10.34-23.26-3.56-9.28-13.98-13.92-23.27-10.36-9.28 3.57-13.92 13.98-10.36 23.27 3.58 9.3 14 13.93 23.28 10.36zm40.34-15.48c9.28-3.56 13.92-13.98 10.36-23.27-3.56-9.3-13.98-13.92-23.26-10.36-9.3 3.6-13.93 14-10.36 23.3 3.56 9.3 13.98 13.92 23.27 10.35zm40.35-15.5c9.3-3.56 13.92-13.97 10.35-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.37 23.27 3.58 9.27 14 13.9 23.28 10.34zm40.35-15.48c9.28-3.57 13.92-13.98 10.36-23.27-3.56-9.3-13.97-13.95-23.26-10.38-9.3 3.56-13.93 13.98-10.36 23.27 3.55 9.27 13.97 13.9 23.26 10.35zm-297.98 68.08c9.28-3.56 13.92-13.98 10.36-23.27-3.57-9.3-14-13.92-23.27-10.36-9.3 3.6-13.93 14-10.36 23.3 3.57 9.3 14 13.9 23.28 10.35zm40.35-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.57-9.3-14-13.93-23.28-10.36-9.28 3.56-13.92 13.98-10.36 23.27 3.57 9.27 14 13.9 23.27 10.34zm40.36-15.48c9.28-3.57 13.9-14 10.35-23.27-3.57-9.3-13.98-13.95-23.27-10.38-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.27 13.98 13.9 23.27 10.35zm121.06-46.48c9.28-3.56 13.92-13.98 10.36-23.26-3.57-9.3-14-13.93-23.27-10.37-9.3 3.57-13.93 14-10.36 23.27 3.55 9.3 13.97 13.93 23.26 10.36zm40.35-15.5c9.3-3.55 13.93-13.97 10.36-23.26-3.56-9.28-13.98-13.92-23.27-10.36-9.3 3.57-13.93 14-10.37 23.27 3.57 9.3 13.98 13.93 23.27 10.36zm40.36-15.48c9.28-3.56 13.92-13.98 10.36-23.27-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.55 9.3 13.97 13.93 23.26 10.36zM449.4 97.74c9.3-3.57 13.94-14 10.37-23.27-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.58-13.9 14-10.35 23.3 3.57 9.27 13.98 13.9 23.27 10.35zm40.37-15.5c9.28-3.56 13.92-13.98 10.36-23.26-3.57-9.3-14-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.57 9.26 14 13.9 23.28 10.34zm-15.5-40.35c9.3-3.58 13.93-14 10.37-23.3-3.57-9.3-14-13.9-23.27-10.35-9.3 3.57-13.93 13.98-10.36 23.27 3.57 9.3 14 13.93 23.27 10.36z"/>
|
9 |
+
<path stroke="#99BAF9" stroke-width="2" d="M606 246H483v51h70.8c-6.6 32.4-34.2 51-70.8 51-43.2 0-78-34.8-78-78s34.8-78 78-78c18.6 0 35.4 6.6 48.6 17.4L570 171c-23.4-20.4-53.4-33-87-33-73.2 0-132 58.8-132 132s58.8 132 132 132c66 0 126-48 126-132 0-7.8-1.2-16.2-3-24z"/>
|
10 |
+
<path fill="#4285F4" d="M553.8 297H483v-51h123c1.8 7.8 3 16.2 3 24 0 43.02-15.74 76.6-39.96 99.13L526.9 336.5c13.77-8.7 23.36-22.1 26.9-39.5z"/>
|
11 |
+
<path fill="#34A853" d="M526.9 336.5C515 344 499.95 348 483 348c-34.66 0-63.9-22.4-74.12-53.57l-44.38 34.02C385.96 372.12 430.82 402 483 402c32.2 0 62.96-11.42 86.04-32.87L526.9 336.5z"/>
|
12 |
+
<path fill="#FBBC05" d="M408.9 294.54c-2.53-7.7-3.9-15.96-3.9-24.54 0-8.58 1.37-16.83 3.9-24.54l-44.38-33.95C355.86 229.13 351 248.97 351 270s4.86 40.88 13.52 58.5l44.4-33.96z"/>
|
13 |
+
<path fill="#EA4335" d="M408.9 245.46C419.16 214.36 448.4 192 483 192c18.6 0 35.4 6.6 48.6 17.4L570 171c-23.4-20.4-53.4-33-87-33-52.16 0-97 29.86-118.48 73.5l44.4 33.96z"/>
|
14 |
+
</g>
|
15 |
+
</svg>
|
assets/img/icon.png
ADDED
Binary file
|
assets/img/pinned.svg
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1800 1800">
|
2 |
+
<title>pinned</title>
|
3 |
+
<g fill="none">
|
4 |
+
<path fill="currentColor" d="M450 1l900-1-1 258c-62.07 13.33-113.63 44.83-154.67 94.5-41.05 49.67-61.57 107.17-61.57 172.5v50c.67 65.33 21.36 122.67 62.07 172 40.7 49.33 92.43 80.67 155.17 94l-1 258h-341.38l-1 257c0 51.33-11.35 137.67-34.04 259-22.7 121.33-46.72 182-72.08 182-26.03 0-50.56-60.67-73.58-182-23.03-121.33-34.54-207.67-34.54-259v-256L450 1099l1-258c62.07-12.67 113.46-43.83 154.17-93.5 40.7-49.67 61.07-107.17 61.07-172.5v-50c0-65.33-20.36-122.83-61.07-172.5-40.7-49.67-92.43-81.17-155.17-94.5V1z"/>
|
5 |
+
</g>
|
6 |
+
</svg>
|
assets/img/star-empty.svg
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1800 1800">
|
2 |
+
<title>star-empty</title>
|
3 |
+
<g fill="#FFB900">
|
4 |
+
<path d="M900 0l300 600 600 75-413 462 113 663-600-300-600 300 112-663L0 675l600-75L900 0zm0 224L666 693l-465 58 318 356-87 515 468-234 468 234-87-515 318-356-465-58-234-469z"/>
|
5 |
+
</g>
|
6 |
+
</svg>
|
assets/img/star-filled.svg
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1800 1800">
|
2 |
+
<title>star-filled</title>
|
3 |
+
<g fill="#FFB900">
|
4 |
+
<path d="M900 0L600 600 0 675l412 462-112 663 600-300 600 300-113-663 413-462-600-75z"/>
|
5 |
+
</g>
|
6 |
+
</svg>
|
assets/img/star-half.svg
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1800 1800">
|
2 |
+
<title>star-half</title>
|
3 |
+
<g fill="#FFB900">
|
4 |
+
<path d="M900 0L600 600 0 675l413 462-113 663 600-300 600 300-112-663 412-462-600-75L900 0zm0 224l234 469 465 58-318 356 87 515-468-234V224z"/>
|
5 |
+
</g>
|
6 |
+
</svg>
|
assets/img/tripadvisor.svg
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 540">
|
2 |
+
<title>tripadvisor-reviews</title>
|
3 |
+
<clipPath>
|
4 |
+
<path d="M0 0h960v540H0z"/>
|
5 |
+
</clipPath>
|
6 |
+
<g fill="none">
|
7 |
+
<path fill="#4B7E37" d="M0 0h960v540H0z"/>
|
8 |
+
<path fill="#558A42" d="M676.33 566.25c2.26-.97 4.22-2.36 5.84-4.03l-4.2-28.26c-4.5-2.5-10.03-3.06-15.2-1.07-8.73 3.32-13.35 12.7-10.93 21.53l24.5 11.8zm-53.78-25.92c7.43-4.4 10.83-13.62 7.63-21.95-3.56-9.3-13.98-13.92-23.27-10.36-7.95 3.06-12.5 11.17-11.37 19.3l27.03 13zm-54.07-26.06c6.17-4.73 8.8-13.12 5.86-20.75-3.57-9.3-14-13.93-23.27-10.36-7.28 2.8-11.7 9.8-11.56 17.15l29 13.98zm95.87-71.8c-3.72 4.8-4.92 11.4-2.58 17.48 1.33 3.45 3.6 6.26 6.4 8.25l-3.82-25.74zM317.7 549.82c-.2-1.25-.52-2.5-1-3.72-3.55-9.28-13.97-13.92-23.26-10.35-9.3 3.56-13.92 13.98-10.36 23.27.83 2.17 2.04 4.1 3.53 5.7l31.1-14.9zm39.48-18.9c-.04-.1-.08-.2-.1-.3-3.58-9.3-14-13.94-23.28-10.37-9.3 3.56-13.93 13.98-10.36 23.27.4 1 .86 1.98 1.4 2.88l32.34-15.48zm38.87-18.63c-4.4-7.37-13.6-10.73-21.9-7.55-9.2 3.54-13.84 13.8-10.45 23.04l32.35-15.5zm38.25-18.33c-4.85-5.34-12.65-7.45-19.8-4.7-8.07 3.1-12.64 11.4-11.33 19.6L434.3 494zm37.56-18c-4.76-3.42-11.1-4.46-17-2.2-6.85 2.63-11.17 9-11.53 15.88L471.86 476zm42.3 12.12c5.06-4.88 7-12.5 4.33-19.46-3.6-9.28-14-13.92-23.3-10.36-6.6 2.53-10.85 8.54-11.47 15.14l30.42 14.66zm143.28-92.07c-.27.1-.53.2-.8.3-9.28 3.55-13.92 13.97-10.36 23.26 2.7 7 9.3 11.37 16.4 11.54l-5.24-35.1zm-368.9 98.64c5.6 1.33 10.46 5.3 12.68 11.07 3.56 9.3-1.07 19.7-10.36 23.27-2.65 1.03-5.4 1.38-8.03 1.14l5.72-35.47zm367.8-106.13c-.73.4-1.5.75-2.28 1.05-9.3 3.56-19.7-1.08-23.27-10.37-3.58-9.3 1.05-19.7 10.34-23.27 3.38-1.3 6.9-1.5 10.2-.8l4.98 33.38zm-359.5 54.76c1.68-1.58 3.7-2.88 5.97-3.76 9.3-3.56 19.73 1.08 23.3 10.36 3.56 9.3-1.08 19.7-10.37 23.27-8.58 3.3-18.14-.42-22.35-8.34l3.48-21.52zm390.03-137.2c-4.7-6.26-13.17-8.93-20.85-5.98-9.3 3.57-13.93 14-10.36 23.27 1.25 3.26 3.36 5.95 5.97 7.9l25.24-25.2zm67.47-67.37c-3.72-9-13.96-13.45-23.1-9.94-9.3 3.54-13.93 13.96-10.36 23.25 1.84 4.8 5.5 8.36 9.92 10.18l23.54-23.5zm-443.7 105.3c.5-.25 1-.48 1.55-.7 9.26-3.55 19.7 1.1 23.25 10.37 3.56 9.3-1.08 19.7-10.36 23.27-5.97 2.3-12.4 1.2-17.17-2.32l4.63-28.66-1.94-1.98zm459.34-170.68c4.17 1.88 7.62 5.34 9.4 9.95 3.56 9.3-1.08 19.7-10.37 23.27-9.27 3.56-19.7-1.07-23.25-10.36-3.56-9.27 1.07-19.7 10.36-23.25 1.4-.53 2.8-.88 4.23-1.06l9.66 1.46zm14.97 2.27c.13 1.7.5 3.42 1.15 5.1 2.5 6.5 8.37 10.74 14.85 11.45l12.27-12.25-28.27-4.3zM285.2 318.38c.53-6.7 4.8-12.83 11.5-15.4 9.28-3.56 19.7 1.08 23.27 10.37 3.56 9.28-1.08 19.7-10.36 23.27-1.7.65-3.43 1.03-5.17 1.15l-19.23-19.4zm439.46-151.92c-.6 6.64-4.85 12.7-11.5 15.25-9.28 3.58-19.7-1.06-23.27-10.35-1.3-3.4-1.53-6.97-.8-10.3l35.55 5.42zm-96.53-14.67c1.8 8.5-2.78 17.3-11.17 20.52-9.3 3.57-19.7-1.07-23.27-10.36-2-5.2-1.44-10.74 1.1-15.25l33.32 5.08zm30.26 4.6c-.37.14-.73.3-1.1.43-6.53 2.5-13.63.95-18.5-3.43l19.6 3zM238.5 271.3c9.12-3.66 13.64-13.97 10.1-23.17-3.56-9.3-13.97-13.93-23.26-10.36-4.82 1.85-8.4 5.54-10.2 9.96l23.36 23.57zm333.9-137.45c-1.1 5.98-5.2 11.28-11.3 13.62-9.27 3.56-19.7-1.08-23.26-10.36-3.56-9.27 1.08-19.7 10.36-23.25 5.4-2.08 11.2-1.37 15.8 1.4l8.4 18.6zm-235.72 15.9c7.5-.3 14.7 4.15 17.52 11.55 3.57 9.28-1.07 19.7-10.36 23.27-9.28 3.56-19.7-1.08-23.27-10.36-3.56-9.27 1.08-19.7 10.36-23.25.62-.24 1.23-.44 1.85-.6l3.9-.6zm49.12-13.6c3.88 1.94 7.08 5.28 8.76 9.66 3.56 9.3-1.07 19.7-10.36 23.3-9.3 3.55-19.7-1.1-23.27-10.37-1.62-4.23-1.55-8.68-.1-12.62l21.92-3.3 3.05-6.64zm170.03-39.02c-1.82 4.43-5.38 8.13-10.2 9.98-9.3 3.6-19.7-1.05-23.28-10.34-3.56-9.3 1.08-19.7 10.36-23.27 4.16-1.6 8.5-1.56 12.4-.2l10.74 23.84zM171.45 203.65c6.16-4.73 8.77-13.12 5.84-20.74-1.3-3.37-3.53-6.13-6.26-8.1l-24.55 3.7 24.94 25.16zm46.3-35.93c3.36 9.2-1.28 19.44-10.46 22.96-9.3 3.57-19.73-1.07-23.3-10.36-.92-2.4-1.3-4.9-1.17-7.32l34.92-5.28zm41.18-6.22c-1.07 6.02-5.17 11.34-11.3 13.7-8.66 3.32-18.33-.5-22.47-8.6l33.77-5.1zM411.3 80.63c3.6 1.98 6.54 5.2 8.12 9.33 3.57 9.3-1.07 19.7-10.36 23.27-4.27 1.64-8.8 1.54-12.76.05l15-32.65zm127.6-21.06c-2 3.17-5 5.74-8.77 7.2-9.28 3.55-19.7-1.1-23.27-10.37-3.56-9.3 1.08-19.7 10.36-23.27 3.08-1.2 6.28-1.47 9.3-.98l12.38 27.42zM436.78 25.14c3.3 2 6 5.08 7.5 8.97 3.57 9.3-1.06 19.73-10.35 23.3-4.04 1.54-8.28 1.54-12.1.3l14.95-32.55zm84.94-3.66c-1.86 2.12-4.26 3.84-7.08 4.92-9.3 3.56-19.7-1.07-23.27-10.36-3.56-9.3 1.07-19.7 10.36-23.27 2.14-.82 4.34-1.2 6.5-1.2l13.5 29.9zm-59.47-51.8c3.04 2 5.5 4.93 6.9 8.6 3.57 9.27-1.07 19.7-10.36 23.26-3.8 1.45-7.8 1.54-11.4.5l14.84-32.36zm42.1 13.28c-1.5 1.28-3.25 2.34-5.2 3.08-9.3 3.57-19.7-1.07-23.27-10.36-3.56-9.28 1.07-19.7 10.36-23.27 1.28-.47 2.58-.8 3.88-1l14.23 31.57zM486.83-55.9c-.98.62-2.04 1.16-3.17 1.6-3.55 1.35-7.27 1.52-10.7.68l7.5-16.38 6.37 14.1zm173.35 582.06c9.28-3.57 13.92-13.98 10.36-23.27-3.57-9.3-13.98-13.95-23.27-10.38-9.3 3.56-13.93 13.98-10.36 23.27 3.58 9.3 14 13.9 23.3 10.35zm-55.85-24.87c9.3-3.58 13.93-14 10.36-23.28-3.58-9.3-14-13.93-23.3-10.36-9.27 3.56-13.9 13.98-10.35 23.26 3.57 9.3 13.98 13.93 23.27 10.36zm40.36-15.5c9.26-3.57 13.9-14 10.34-23.28-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.57 9.3 14 13.93 23.28 10.36zm-96.23-9.38c9.3-3.56 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.92-23.26-10.36-9.3 3.56-13.93 13.97-10.37 23.26 3.58 9.3 14 13.93 23.28 10.36zm40.36-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.57-9.3-14-13.93-23.28-10.36-9.3 3.57-13.92 14-10.36 23.28 3.57 9.3 13.98 13.92 23.27 10.36zm40.36-15.48c9.27-3.57 13.9-13.98 10.35-23.27-3.57-9.3-14-13.93-23.27-10.36-9.3 3.57-13.94 14-10.37 23.28 3.57 9.27 14 13.9 23.28 10.35zm-298 68.08c9.3-3.56 13.93-13.98 10.37-23.27-3.57-9.3-14-13.92-23.27-10.36-9.28 3.56-13.92 13.97-10.35 23.26 3.56 9.3 13.98 13.92 23.26 10.36zm40.36-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.57-13.92 14-10.36 23.28 3.56 9.3 13.97 13.92 23.26 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.58-9.3-14-13.93-23.28-10.36-9.3 3.57-13.93 14-10.36 23.28 3.56 9.27 13.98 13.9 23.27 10.35zm40.35-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.94-23.27-10.37-9.26 3.6-13.9 14-10.36 23.3 3.58 9.28 14 13.92 23.28 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.58-9.3-14-13.94-23.28-10.37-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.37zm40.35-15.5c9.28-3.56 13.92-13.98 10.35-23.26-3.56-9.28-13.98-13.92-23.27-10.36-9.28 3.57-13.92 14-10.36 23.27 3.57 9.3 14 13.95 23.27 10.38zm40.35-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.57-9.27-13.98-13.9-23.27-10.35-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.48c9.3-3.57 13.93-14 10.37-23.28-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.94 23.26 10.37zm-257.62 52.58c9.3-3.56 13.92-13.98 10.36-23.27-3.57-9.28-13.98-13.9-23.27-10.36-9.3 3.57-13.92 14-10.36 23.27 3.56 9.3 14 13.94 23.27 10.37zm40.36-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.92 14-10.35 23.27 3.56 9.3 13.98 13.94 23.27 10.37zm40.35-15.48c9.28-3.56 13.92-13.98 10.35-23.27-3.56-9.3-13.98-13.9-23.27-10.35-9.3 3.57-13.92 13.98-10.36 23.27 3.56 9.3 13.97 13.93 23.26 10.36zm40.35-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.56-9.3-13.98-13.92-23.26-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.92 23.27 10.36zm40.35-15.48c9.3-3.57 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.55-13.93 13.97-10.37 23.26 3.56 9.3 14 13.92 23.26 10.36zm40.36-15.5c9.28-3.56 13.92-13.98 10.36-23.26-3.58-9.3-14-13.93-23.28-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.55 9.28 13.97 13.92 23.26 10.36zm40.35-15.48c9.3-3.57 13.94-14 10.37-23.27-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 14-10.36 23.27 3.57 9.28 14 13.92 23.27 10.36zm40.37-15.5c9.3-3.56 13.92-13.98 10.36-23.26-3.56-9.3-13.97-13.93-23.26-10.36-9.3 3.56-13.92 13.98-10.36 23.27 3.56 9.27 13.98 13.9 23.27 10.35zm-297.98 68.1c9.27-3.58 13.9-14 10.35-23.28-3.57-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.55 9.28 13.97 13.92 23.26 10.36zm40.34-15.5c9.3-3.57 13.94-14 10.37-23.27-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 14-10.36 23.27 3.57 9.28 14 13.92 23.27 10.36zm40.37-15.5c9.3-3.56 13.92-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.37 23.27 3.55 9.27 13.97 13.9 23.26 10.35zm40.36-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.58-9.28-14-13.92-23.28-10.36-9.3 3.57-13.93 14-10.36 23.27 3.55 9.28 13.97 13.92 23.26 10.35zM502 355.37c9.3-3.56 13.94-13.98 10.37-23.27-3.56-9.3-13.98-13.93-23.27-10.37-9.28 3.57-13.92 14-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.36zm40.37-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.57-9.27-14-13.9-23.27-10.35-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.94 23.27 10.37zm40.35-15.48c9.3-3.57 13.93-14 10.36-23.28-3.56-9.28-13.98-13.9-23.27-10.35-9.26 3.57-13.9 13.98-10.34 23.27 3.57 9.3 14 13.93 23.27 10.36zm40.36-15.5c9.28-3.57 13.92-13.98 10.36-23.27-3.57-9.3-13.98-13.92-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.58 9.3 14 13.92 23.3 10.36zm40.35-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.58-9.3-14-13.93-23.3-10.36-9.27 3.56-13.9 13.98-10.35 23.27 3.57 9.3 14 13.92 23.27 10.36zm40.36-15.5c9.27-3.55 13.9-13.97 10.34-23.25-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.55-13.92 13.97-10.36 23.26 3.57 9.28 14 13.92 23.28 10.36zm-338.36 83.6c9.3-3.56 13.93-13.98 10.36-23.27-3.55-9.28-13.97-13.9-23.26-10.35-9.28 3.57-13.92 13.98-10.36 23.27 3.57 9.3 14 13.93 23.27 10.36zM405.8 346c9.3-3.56 13.93-13.97 10.37-23.26-3.57-9.3-13.98-13.92-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.92 23.27 10.36zm40.36-15.48c9.3-3.57 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.93-23.26-10.36-9.3 3.55-13.93 13.97-10.37 23.26 3.55 9.28 13.97 13.92 23.25 10.36zm40.36-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.27 3.56-13.9 13.98-10.34 23.27 3.57 9.28 13.98 13.92 23.27 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.57-9.3-14-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.58 9.28 14 13.92 23.3 10.36zm40.35-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.58-9.3-14-13.93-23.3-10.36-9.27 3.56-13.9 13.98-10.35 23.26 3.57 9.3 13.98 13.93 23.27 10.37zm40.36-15.5c9.26-3.55 13.9-13.97 10.34-23.26-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.57 9.3 14 13.92 23.28 10.35zm40.33-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.57-9.3-14-13.93-23.28-10.37-9.28 3.58-13.92 14-10.36 23.28 3.57 9.3 14 13.94 23.27 10.37zm40.36-15.5c9.3-3.55 13.9-13.97 10.35-23.26-3.57-9.28-13.98-13.92-23.27-10.36-9.3 3.57-13.94 13.98-10.37 23.27 3.57 9.3 14 13.93 23.28 10.36zm40.34-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.55-9.3-13.97-13.92-23.25-10.36-9.3 3.57-13.93 13.98-10.37 23.27 3.57 9.3 14 13.94 23.27 10.37zm-378.7 99.06c9.3-3.56 13.94-13.98 10.37-23.26-3.55-9.3-13.97-13.93-23.26-10.36-9.28 3.56-13.92 13.98-10.36 23.26 3.56 9.3 13.98 13.93 23.26 10.36zm40.37-15.5c9.3-3.55 13.93-13.97 10.37-23.26-3.57-9.28-14-13.92-23.27-10.36-9.28 3.57-13.92 14-10.35 23.27 3.56 9.3 13.98 13.92 23.27 10.35zm40.36-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.93-23.27-10.37-9.28 3.58-13.92 14-10.36 23.28 3.56 9.3 14 13.94 23.26 10.37zm40.36-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.58-9.28-14-13.92-23.28-10.36-9.3 3.57-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.92-23.26-10.36-9.3 3.57-13.93 13.98-10.36 23.27 3.57 9.3 14 13.94 23.27 10.37zm40.36-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.57-9.3-14-13.93-23.28-10.36-9.3 3.56-13.92 13.98-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.37zm40.36-15.48c9.27-3.57 13.9-13.98 10.35-23.27-3.57-9.28-14-13.92-23.27-10.35-9.3 3.56-13.94 13.98-10.37 23.27 3.57 9.28 14 13.92 23.28 10.36zm40.34-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.55-9.3-13.97-13.93-23.26-10.36-9.28 3.56-13.92 13.98-10.36 23.27 3.57 9.28 14 13.92 23.27 10.36zm40.36-15.48c9.3-3.57 13.93-14 10.37-23.27-3.57-9.3-14-13.93-23.27-10.36-9.3 3.55-13.93 13.97-10.36 23.26 3.56 9.28 13.98 13.92 23.27 10.36zm-378.7 99.06c9.3-3.56 13.94-13.98 10.38-23.27-3.57-9.3-14-13.92-23.27-10.36-9.27 3.57-13.9 13.98-10.34 23.27 3.56 9.3 13.98 13.94 23.26 10.37zm40.37-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.92 13.98-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.37zm40.36-15.48c9.28-3.57 13.92-13.98 10.36-23.27-3.6-9.28-14-13.92-23.3-10.35-9.3 3.56-13.92 13.98-10.35 23.27 3.56 9.28 13.98 13.92 23.27 10.36zm40.35-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 13.98-10.36 23.27 3.6 9.28 14 13.92 23.3 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.57-9.3-13.98-13.93-23.27-10.36-9.3 3.55-13.93 13.97-10.36 23.26 3.56 9.28 13.98 13.92 23.27 10.36zm40.35-15.5c9.27-3.56 13.9-13.98 10.34-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 13.98-10.36 23.26 3.58 9.3 14 13.93 23.28 10.36zm40.34-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.56-9.28-13.97-13.92-23.26-10.36-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.48c9.3-3.56 13.92-13.98 10.36-23.27-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.94 14-10.37 23.27 3.56 9.3 13.98 13.93 23.26 10.36zM278.6 255.9c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.37-9.26 3.57-13.9 14-10.34 23.27 3.57 9.3 14 13.93 23.27 10.36zm40.36-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.56-9.28-13.98-13.92-23.27-10.36-9.3 3.57-13.92 14-10.36 23.27 3.58 9.3 14 13.93 23.3 10.36zm40.36-15.48c9.28-3.56 13.92-13.98 10.36-23.27-3.57-9.28-14-13.92-23.27-10.36-9.3 3.55-13.93 13.97-10.36 23.25 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.5c9.27-3.55 13.9-13.97 10.34-23.26-3.56-9.28-13.98-13.92-23.27-10.36-9.28 3.57-13.92 13.98-10.36 23.27 3.58 9.3 14 13.93 23.28 10.36zm40.34-15.48c9.28-3.56 13.92-13.98 10.36-23.27-3.56-9.3-13.98-13.92-23.26-10.36-9.3 3.6-13.93 14-10.36 23.3 3.56 9.3 13.98 13.92 23.27 10.35zm40.35-15.5c9.3-3.56 13.92-13.97 10.35-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.37 23.27 3.58 9.27 14 13.9 23.28 10.34zm40.35-15.48c9.28-3.57 13.92-13.98 10.36-23.27-3.56-9.3-13.97-13.95-23.26-10.38-9.3 3.56-13.93 13.98-10.36 23.27 3.55 9.27 13.97 13.9 23.26 10.35zm-297.98 68.08c9.28-3.56 13.92-13.98 10.36-23.27-3.57-9.3-14-13.92-23.27-10.36-9.3 3.6-13.93 14-10.36 23.3 3.57 9.3 14 13.9 23.28 10.35zm40.35-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.57-9.3-14-13.93-23.28-10.36-9.28 3.56-13.92 13.98-10.36 23.27 3.57 9.27 14 13.9 23.27 10.34zm40.36-15.48c9.28-3.57 13.9-14 10.35-23.27-3.57-9.3-13.98-13.95-23.27-10.38-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.27 13.98 13.9 23.27 10.35zm121.06-46.48c9.28-3.56 13.92-13.98 10.36-23.26-3.57-9.3-14-13.93-23.27-10.37-9.3 3.57-13.93 14-10.36 23.27 3.55 9.3 13.97 13.93 23.26 10.36zm40.35-15.5c9.3-3.55 13.93-13.97 10.36-23.26-3.56-9.28-13.98-13.92-23.27-10.36-9.3 3.57-13.93 14-10.37 23.27 3.57 9.3 13.98 13.93 23.27 10.36zm40.36-15.48c9.28-3.56 13.92-13.98 10.36-23.27-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.55 9.3 13.97 13.93 23.26 10.36zM449.4 97.74c9.3-3.57 13.94-14 10.37-23.27-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.58-13.9 14-10.35 23.3 3.57 9.27 13.98 13.9 23.27 10.35zm40.37-15.5c9.28-3.56 13.92-13.98 10.36-23.26-3.57-9.3-14-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.57 9.26 14 13.9 23.28 10.34zm-15.5-40.35c9.3-3.58 13.93-14 10.37-23.3-3.57-9.3-14-13.9-23.27-10.35-9.3 3.57-13.93 13.98-10.36 23.27 3.57 9.3 14 13.93 23.27 10.36z"/>
|
9 |
+
<path fill="#fff" d="M405.25 391.43c0 11.1-6.67 18.26-17 18.26-6.3 0-10.7-4.16-10.7-10.12 0-7.82 7.47-12.12 21.03-12.12h6.7v3.98h-.03zm-11.48-38.75c-8 0-15 1.5-21.4 4.6l-.4.17.13 11.14 1.03-.57c6.14-3.44 11.94-5.17 17.24-5.17 9.46 0 14.88 5.4 14.88 14.88v.56h-7.46c-21.5 0-33.33 7.78-33.33 21.92 0 11.35 8.56 19.28 20.8 19.28 9 0 15.95-3.53 20.9-10.2v9.3H418v-38.98c0-17.9-8.13-26.94-24.18-26.94h-.05zm62.68 56.12c-9.95 0-16.14-8.67-16.14-22.65 0-14 6.24-23.05 15.9-23.05 10.8 0 17.26 8.62 17.26 23.05 0 13.76-6.68 22.67-17 22.67v-.02zm17-85.38v39.52c-4.8-6.7-11.53-10.26-20.15-10.26-15.96 0-26.7 13.13-26.7 32.7 0 20.17 10.88 34.23 26.45 34.23 9.23 0 16.4-4.05 21.42-12.02v11.03h12.07v-95.2h-13.08v.02zm71.47 30.77l-18.54 37.17-17.1-37.2-14.73.06 31.56 64.42 32.47-64.4H544.9v-.05h.02zm188.23 10.4v-10.44l-11.94.03v64.4h13.08v-32.35c0-13.52 5.7-21 16.02-21 1.54 0 2.98.2 4.75.72l.84.26.43-12.73-5.6-.45c-7.6 0-13.72 4.1-17.54 11.56h-.04zm-56.1-11.88c-19.65 0-31.83 12.9-31.83 33.72s12.18 33.74 31.83 33.74c19.7 0 31.96-12.93 31.96-33.73 0-20.8-12.24-33.7-31.95-33.7v-.03zm.16 56.75c-10.87 0-18.16-9.32-18.16-23.2 0-13.93 7.3-23.3 18.17-23.3 11.02 0 18.14 9.16 18.14 23.3 0 14.13-7.12 23.22-18.14 23.22v-.03zm-83.6-38.76c0 9.08 6.24 13.84 17.72 19.47 7.26 3.58 11.05 5.66 11.05 10.68 0 5.15-4.67 8.5-11.87 8.5-5.12 0-10.78-1.47-16.4-4.23l-.96-.46-.4 11.35.47.2c5.36 2.12 11 3.18 16.85 3.18 15.98 0 26.3-7.63 26.3-19.4 0-9.83-6.54-14.83-17.85-20.1-8.3-3.9-11.77-6.17-11.77-10.3 0-3.9 3.85-6.43 9.84-6.43 5.4 0 10.67 1.2 15.65 3.58l.93.46.6-11.15-.53-.14c-5.44-1.62-10.77-2.43-15.9-2.43-14.15 0-23.68 6.94-23.68 17.27h-.02l-.02-.03zm-29.65-35.4c0 4.3 3.5 7.78 7.8 7.78 4.27 0 7.76-3.5 7.76-7.77 0-4.28-3.48-7.76-7.76-7.76-4.3 0-7.78 3.52-7.78 7.82v-.04zm13.66 18.9h-12.38v64.4h13.07v-64.4h-.68zm199.97 4.26h1.24c1.6 0 2.36-.53 2.36-1.67 0-1.24-.8-1.76-2.5-1.76h-1.05v3.42h-.03zm1.63-5.64c3.68 0 5.54 1.28 5.54 3.72 0 1.63-1 2.9-2.72 3.44l2.84 5.6H781l-2.4-5.08h-1.03v5.08h-3.52v-12.74h5.15v-.02zm-6.85-.26c-1.9 1.9-2.87 4.32-2.87 6.9 0 2.5.92 4.77 2.7 6.62 1.84 1.86 4.23 2.92 6.78 2.92 2.52 0 4.9-.98 6.76-2.78 1.83-1.73 2.83-4.15 2.83-6.77 0-2.57-.93-4.9-2.7-6.7-1.85-1.88-4.24-2.88-6.84-2.88-2.6 0-4.86.92-6.65 2.68zm15.04-1.27c2.14 2.16 3.26 5.02 3.26 8.14 0 3.24-1.2 6.14-3.45 8.3-2.3 2.2-5.22 3.38-8.22 3.38-3.14 0-6.06-1.23-8.3-3.54-2.17-2.25-3.35-5.1-3.35-8.13 0-3.07 1.24-6.1 3.55-8.35 2.18-2.15 4.96-3.32 8.1-3.32 3.23 0 6.17 1.24 8.4 3.55v-.03z"/>
|
10 |
+
<path fill="#0A0A08" d="M194.9 334.75l-11.76 4.06v15.4H170.1v10.28h12.67v33.1c0 15.74 5.37 22.14 18.52 22.14 3.35 0 6.53-.4 9.7-1.26l.55-.13-.14-10.74-.92.34c-2.57.9-4.6 1.32-6.16 1.32-6.18 0-8.5-3.47-8.5-12.86v-31.96h15.1v-10.28h-15.1v-19.72l-.92.32m40.76 29.84v-10.44l-11.94.03v64.4h13.05v-32.35c0-13.52 5.7-21 16.02-21 1.5 0 2.94.2 4.7.72l.85.26.44-12.73-5.62-.45c-7.56 0-13.7 4.1-17.5 11.56m32.75-29.3c0 4.28 3.5 7.76 7.77 7.76 4.3 0 7.78-3.48 7.78-7.77 0-4.3-3.48-7.78-7.78-7.78-4.28 0-7.77 3.53-7.77 7.83m13.63 18.85H269.7v64.4h13.06v-64.4h-.7m46.83-1.52c-9 0-16.52 4.24-21.56 11.97v-10.42H295.4v90.68h13.1v-35.17c4.7 6.4 11.46 9.92 19.9 9.92 16.5 0 27.17-13.53 27.17-34.48 0-19.42-10.74-32.47-26.7-32.47m-3.57 56.36c-10.35 0-16.78-8.83-16.78-23.04 0-13.7 6.76-22.55 17.24-22.55 10.08 0 16.1 8.38 16.1 22.4 0 14.33-6.35 23.2-16.62 23.2M343.77 175.66c-3.65-14.9-14.77-29.68-14.77-29.68l47.22-.03c28.15-18.33 63.86-28.17 103.38-28.17 38.17 0 72.68 9.57 101 27.82l50.13.03s-11.28 14.98-14.86 30c4.2 5.6 7.68 11.93 10.25 18.82 14.44 38.9-5.6 82.24-44.72 96.64-18.9 6.92-39.42 6.13-57.78-2.3-11.03-5.06-20.47-12.5-27.76-21.74l-16.2 24.1-16.1-23.88c-13.86 17.17-35.13 28.15-58.94 28.15-41.65 0-75.57-33.72-75.57-75.2 0-16.66 5.48-32.1 14.73-44.57"/>
|
11 |
+
<path fill="#E8C56C" d="M405.8 145.04c21.68-9.25 46.74-14.15 73.8-14.15 27.07 0 51.54 4.88 73.16 14.5-7.9.23-15.93 1.74-23.73 4.6-18.9 6.95-34 20.85-42.48 39.1-3.65 7.93-5.87 16.25-6.63 24.67-3.32-38.06-35.18-68.1-74.13-68.7v-.03z"/>
|
12 |
+
<ellipse fill="#fff" cx="404.583" cy="220.508" rx="60.317" ry="60.028"/>
|
13 |
+
<ellipse fill="#fff" cx="555.218" cy="220.508" rx="60.317" ry="60.028"/>
|
14 |
+
<ellipse fill="#0A0A08" cx="404.6" cy="220.508" rx="37.335" ry="37.156"/>
|
15 |
+
<ellipse fill="#0A0A08" cx="555.185" cy="220.508" rx="37.335" ry="37.156"/>
|
16 |
+
<ellipse fill="#fff" cx="404.6" cy="220.508" rx="24.475" ry="24.358"/>
|
17 |
+
<ellipse fill="#fff" cx="555.185" cy="220.508" rx="24.475" ry="24.358"/>
|
18 |
+
<ellipse fill="#922826" cx="404.6" cy="220.508" rx="12.445" ry="12.385"/>
|
19 |
+
<ellipse fill="#548C40" cx="555.185" cy="220.508" rx="12.445" ry="12.385"/>
|
20 |
+
</g>
|
21 |
+
</svg>
|
assets/img/yelp.svg
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 540">
|
2 |
+
<title>yelp-reviews</title>
|
3 |
+
<clipPath>
|
4 |
+
<path d="M0 0h960v540H0z"/>
|
5 |
+
</clipPath>
|
6 |
+
<g fill="none">
|
7 |
+
<path fill="#D32323" d="M0 0h960v540H0z"/>
|
8 |
+
<path fill="#DB4123" d="M676.33 566.25c2.26-.97 4.22-2.36 5.84-4.03l-4.2-28.26c-4.5-2.5-10.03-3.06-15.2-1.07-8.73 3.33-13.35 12.72-10.93 21.54l24.5 11.8zm-53.78-25.92c7.43-4.4 10.83-13.62 7.63-21.95-3.56-9.3-13.98-13.92-23.27-10.36-7.96 3.06-12.5 11.17-11.38 19.3l27.03 13zm-54.07-26.06c6.17-4.73 8.8-13.12 5.86-20.75-3.57-9.3-14-13.93-23.27-10.36-7.28 2.8-11.7 9.8-11.56 17.15l28.98 13.97zm95.87-71.8c-3.72 4.8-4.92 11.4-2.58 17.48 1.33 3.45 3.6 6.26 6.4 8.25l-3.82-25.74zM317.7 549.82c-.2-1.25-.52-2.5-1-3.72-3.55-9.28-13.97-13.92-23.26-10.35-9.3 3.56-13.92 13.98-10.36 23.27.83 2.17 2.04 4.1 3.53 5.7l31.1-14.9zm39.48-18.9c-.04-.1-.08-.2-.1-.3-3.58-9.3-14-13.94-23.28-10.37-9.3 3.56-13.93 13.98-10.36 23.27.4 1 .86 1.98 1.4 2.88l32.34-15.48zm38.87-18.63c-4.4-7.36-13.6-10.72-21.9-7.54-9.2 3.54-13.84 13.8-10.45 23.04l32.35-15.5zm38.25-18.32c-4.85-5.34-12.65-7.45-19.8-4.7-8.07 3.1-12.64 11.4-11.33 19.6L434.3 494zm37.56-18c-4.76-3.42-11.1-4.46-17-2.2-6.85 2.63-11.17 9-11.53 15.88L471.86 476zm42.3 12.12c5.06-4.87 7-12.48 4.33-19.45-3.58-9.28-14-13.92-23.28-10.36-6.6 2.53-10.86 8.54-11.48 15.14l30.42 14.66zm143.28-92.06c-.27.1-.53.2-.8.3-9.28 3.55-13.92 13.97-10.36 23.26 2.7 7.02 9.3 11.38 16.4 11.55l-5.24-35.1zm-368.9 98.64c5.6 1.33 10.46 5.3 12.68 11.07 3.56 9.3-1.07 19.7-10.36 23.27-2.65 1.02-5.4 1.37-8.03 1.13l5.72-35.47zm367.8-106.13c-.73.4-1.5.75-2.28 1.05-9.3 3.57-19.7-1.07-23.27-10.36-3.57-9.3 1.06-19.7 10.35-23.27 3.38-1.3 6.9-1.5 10.2-.8l4.98 33.38zm-359.5 54.76c1.68-1.58 3.7-2.88 5.97-3.76 9.3-3.56 19.72 1.08 23.28 10.36 3.57 9.3-1.07 19.7-10.36 23.27-8.58 3.3-18.14-.42-22.35-8.34l3.48-21.52zm390.03-137.2c-4.7-6.26-13.17-8.93-20.85-5.98-9.3 3.57-13.93 14-10.36 23.27 1.25 3.27 3.36 5.96 5.97 7.92l25.24-25.2zm67.47-67.37c-3.72-9-13.96-13.45-23.1-9.94-9.3 3.55-13.93 13.97-10.36 23.26 1.84 4.8 5.5 8.36 9.92 10.18l23.54-23.5zm-443.7 105.3c.5-.25 1-.48 1.55-.7 9.27-3.55 19.7 1.1 23.26 10.37 3.56 9.3-1.08 19.7-10.36 23.27-5.96 2.3-12.38 1.2-17.16-2.32l4.63-28.66-1.94-1.97zm459.34-170.68c4.17 1.88 7.62 5.34 9.4 9.95 3.56 9.3-1.08 19.7-10.37 23.27-9.28 3.56-19.7-1.07-23.26-10.36-3.56-9.28 1.07-19.7 10.36-23.26 1.4-.53 2.8-.88 4.22-1.06l9.66 1.47zm14.97 2.27c.13 1.7.5 3.42 1.15 5.1 2.5 6.5 8.37 10.74 14.85 11.45l12.27-12.25-28.27-4.3zM285.2 318.38c.53-6.7 4.8-12.83 11.5-15.4 9.28-3.56 19.7 1.08 23.27 10.37 3.56 9.28-1.08 19.7-10.36 23.27-1.7.65-3.44 1.03-5.18 1.15l-19.23-19.4zm439.46-151.92c-.6 6.64-4.85 12.7-11.5 15.25-9.28 3.57-19.7-1.07-23.27-10.36-1.3-3.4-1.52-6.97-.8-10.3l35.56 5.42zm-96.53-14.67c1.8 8.5-2.78 17.3-11.17 20.53-9.3 3.57-19.7-1.07-23.27-10.36-2-5.2-1.43-10.74 1.1-15.25l33.33 5.07zm30.26 4.6c-.36.15-.72.3-1.08.44-6.54 2.5-13.64.95-18.52-3.43l19.6 3zM238.5 271.3c9.12-3.66 13.64-13.97 10.1-23.17-3.56-9.3-13.97-13.93-23.26-10.36-4.82 1.85-8.4 5.54-10.2 9.96l23.36 23.57zM572.4 133.85c-1.1 5.98-5.2 11.28-11.3 13.62-9.27 3.56-19.7-1.08-23.26-10.36-3.56-9.28 1.08-19.7 10.36-23.26 5.4-2.08 11.2-1.37 15.8 1.4l8.4 18.6zm-235.72 15.9c7.5-.3 14.7 4.15 17.52 11.55 3.57 9.28-1.07 19.7-10.36 23.27-9.28 3.56-19.7-1.08-23.27-10.36-3.56-9.28 1.08-19.7 10.36-23.26.62-.24 1.23-.44 1.85-.6l3.9-.6zm49.12-13.6c3.88 1.94 7.08 5.28 8.76 9.66 3.56 9.3-1.07 19.7-10.36 23.28-9.3 3.56-19.7-1.08-23.27-10.36-1.62-4.23-1.55-8.68-.1-12.62l21.92-3.3 3.05-6.65zm170.03-39.02c-1.82 4.43-5.38 8.13-10.2 9.98-9.3 3.58-19.7-1.06-23.28-10.35-3.56-9.3 1.08-19.7 10.36-23.27 4.15-1.58 8.5-1.55 12.38-.2l10.75 23.85zM171.45 203.65c6.16-4.73 8.77-13.12 5.84-20.74-1.3-3.38-3.52-6.14-6.25-8.12l-24.55 3.7 24.95 25.17zm46.3-35.93c3.36 9.2-1.28 19.44-10.46 22.96-9.3 3.57-19.72-1.07-23.28-10.36-.93-2.4-1.3-4.9-1.18-7.32l34.92-5.28zm41.18-6.22c-1.07 6.02-5.17 11.34-11.3 13.7-8.66 3.32-18.33-.5-22.47-8.6l33.77-5.1zM411.3 80.63c3.6 1.98 6.54 5.2 8.12 9.33 3.57 9.3-1.07 19.7-10.36 23.27-4.27 1.64-8.8 1.54-12.76.05l15-32.65zm127.6-21.06c-2 3.17-5 5.74-8.77 7.2-9.28 3.55-19.7-1.1-23.27-10.37-3.56-9.3 1.08-19.7 10.36-23.27 3.08-1.2 6.28-1.47 9.3-.98l12.38 27.42zM436.78 25.14c3.3 2 6 5.08 7.5 8.97 3.57 9.3-1.06 19.72-10.35 23.28-4.04 1.55-8.28 1.55-12.1.3l14.95-32.54zm84.94-3.66c-1.86 2.12-4.26 3.84-7.08 4.92-9.3 3.56-19.7-1.07-23.27-10.36-3.56-9.3 1.07-19.7 10.36-23.27 2.14-.82 4.34-1.2 6.5-1.2l13.5 29.9zm-59.47-51.8c3.04 2 5.5 4.93 6.9 8.6 3.57 9.27-1.07 19.7-10.36 23.26-3.8 1.45-7.78 1.54-11.4.5l14.85-32.36zm42.1 13.28c-1.5 1.28-3.25 2.34-5.2 3.08-9.3 3.57-19.7-1.07-23.27-10.36-3.56-9.28 1.07-19.7 10.36-23.27 1.28-.48 2.58-.8 3.88-1l14.23 31.56zM486.83-55.9c-.98.62-2.04 1.16-3.17 1.6-3.55 1.35-7.27 1.52-10.7.68l7.5-16.38 6.37 14.1zm173.35 582.06c9.28-3.57 13.92-13.98 10.36-23.27-3.57-9.3-13.98-13.94-23.27-10.37-9.3 3.56-13.93 13.98-10.36 23.27 3.57 9.3 14 13.92 23.28 10.36zm-55.85-24.87c9.3-3.57 13.93-14 10.36-23.27-3.57-9.3-14-13.93-23.28-10.36-9.28 3.56-13.92 13.98-10.36 23.26 3.57 9.3 13.98 13.93 23.27 10.36zm40.36-15.5c9.27-3.56 13.9-13.98 10.35-23.27-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.36zm-96.22-9.37c9.3-3.56 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.92-23.26-10.36-9.3 3.57-13.93 13.98-10.37 23.27 3.58 9.3 14 13.93 23.28 10.36zm40.36-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.57-13.92 14-10.36 23.28 3.57 9.3 13.98 13.92 23.27 10.36zm40.36-15.48c9.28-3.57 13.92-13.98 10.36-23.27-3.57-9.3-14-13.93-23.27-10.36-9.3 3.56-13.94 13.98-10.37 23.27 3.56 9.27 13.98 13.9 23.27 10.35zm-298 68.08c9.3-3.56 13.94-13.98 10.38-23.27-3.57-9.3-14-13.92-23.27-10.36-9.28 3.57-13.92 13.98-10.35 23.27 3.56 9.3 13.98 13.92 23.26 10.36zm40.37-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.57-13.92 14-10.36 23.28 3.57 9.3 13.98 13.92 23.27 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.58-9.3-14-13.93-23.28-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.27 13.98 13.9 23.27 10.35zm40.35-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.94-23.27-10.37C430.1 437 425.45 447.4 429 456.7c3.58 9.28 14 13.92 23.28 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.57-9.3-14-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.37zm40.35-15.5c9.28-3.56 13.92-13.98 10.35-23.26-3.56-9.28-13.98-13.92-23.27-10.36-9.28 3.57-13.92 14-10.36 23.27 3.57 9.3 14 13.94 23.27 10.37zm40.35-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.56-9.27-13.97-13.9-23.26-10.35-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.48c9.3-3.57 13.93-14 10.37-23.28-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.93 23.26 10.36zm-257.62 52.58c9.3-3.56 13.92-13.98 10.36-23.27-3.57-9.27-13.98-13.9-23.27-10.35-9.3 3.57-13.92 14-10.36 23.27 3.57 9.3 14 13.93 23.28 10.36zm40.36-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.48c9.28-3.56 13.92-13.98 10.35-23.27-3.56-9.3-13.98-13.92-23.27-10.36-9.3 3.57-13.92 13.98-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.36zm40.35-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.56-9.3-13.98-13.92-23.26-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.92 23.27 10.36zm40.35-15.48c9.3-3.57 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.55-13.93 13.97-10.37 23.26 3.57 9.3 14 13.92 23.27 10.36zm40.36-15.5c9.28-3.56 13.92-13.98 10.36-23.26-3.57-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.55 9.28 13.97 13.92 23.26 10.36zm40.35-15.48c9.3-3.57 13.94-14 10.37-23.27-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.57-13.92 14-10.36 23.28 3.57 9.28 14 13.92 23.27 10.36zm40.37-15.5c9.3-3.56 13.92-13.98 10.36-23.26-3.57-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.92 13.98-10.36 23.27 3.56 9.27 13.98 13.9 23.27 10.35zm-297.98 68.1c9.27-3.58 13.9-14 10.35-23.28-3.57-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.28 13.98 13.92 23.27 10.36zm40.34-15.5c9.3-3.57 13.93-14 10.36-23.27-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.57-13.92 14-10.36 23.28 3.57 9.28 14 13.92 23.27 10.36zm40.36-15.5c9.3-3.56 13.92-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.37 23.27 3.56 9.27 13.98 13.9 23.27 10.35zm40.36-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.55 9.28 13.97 13.92 23.26 10.35zM502 355.37c9.3-3.56 13.94-13.98 10.37-23.27-3.56-9.3-13.98-13.93-23.27-10.37-9.28 3.57-13.92 14-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.36zm40.37-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.94 23.27 10.37zm40.35-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.56-9.28-13.98-13.9-23.27-10.35-9.27 3.57-13.9 13.98-10.35 23.27 3.57 9.3 14 13.93 23.27 10.36zm40.36-15.5c9.28-3.56 13.92-13.97 10.36-23.26-3.57-9.3-13.98-13.92-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.57 9.3 14 13.92 23.28 10.36zm40.35-15.48c9.3-3.57 13.93-13.98 10.36-23.27-3.57-9.3-14-13.93-23.28-10.36-9.28 3.56-13.92 13.98-10.36 23.27 3.57 9.3 14 13.92 23.27 10.36zm40.36-15.5c9.28-3.56 13.9-13.98 10.35-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.92 13.98-10.36 23.27 3.56 9.28 13.98 13.92 23.27 10.36zM365.44 361.5c9.3-3.56 13.93-13.98 10.36-23.27-3.55-9.28-13.97-13.9-23.26-10.35-9.28 3.57-13.92 13.98-10.36 23.27 3.57 9.3 14 13.93 23.27 10.36zm40.36-15.5c9.3-3.56 13.93-13.97 10.37-23.26-3.57-9.3-13.98-13.92-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.92 23.27 10.36zm40.36-15.48c9.3-3.57 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.93-23.26-10.36-9.3 3.56-13.93 13.98-10.37 23.27 3.56 9.28 13.98 13.92 23.26 10.36zm40.36-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.9 13.98-10.35 23.27 3.57 9.28 13.98 13.92 23.27 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.57-9.3-14-13.93-23.27-10.36-9.3 3.55-13.93 13.97-10.36 23.26 3.57 9.28 14 13.92 23.28 10.36zm40.35-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.57-9.3-14-13.93-23.28-10.36-9.28 3.56-13.92 13.98-10.36 23.26 3.57 9.3 13.98 13.93 23.27 10.37zm40.36-15.5c9.27-3.55 13.9-13.97 10.35-23.26-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.36zm40.34-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.56-9.28-13.98-13.92-23.27-10.36-9.28 3.58-13.92 14-10.36 23.28 3.57 9.3 14 13.93 23.27 10.36zm40.36-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.57-9.28-13.98-13.92-23.27-10.36-9.3 3.57-13.94 13.98-10.37 23.27 3.56 9.28 13.98 13.92 23.27 10.35zm40.35-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.55-9.3-13.97-13.93-23.25-10.37-9.3 3.57-13.93 13.98-10.37 23.27 3.57 9.3 14 13.93 23.27 10.36zm-378.7 99.06c9.3-3.56 13.94-13.98 10.37-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 13.98-10.36 23.26 3.56 9.3 13.98 13.93 23.26 10.36zm40.37-15.5c9.28-3.55 13.92-13.97 10.36-23.26-3.57-9.28-14-13.92-23.27-10.36-9.28 3.57-13.92 14-10.35 23.27 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.56-9.28-13.98-13.92-23.27-10.36-9.28 3.58-13.92 14-10.36 23.28 3.57 9.3 14 13.93 23.27 10.36zm40.36-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.58-9.28-14-13.92-23.28-10.36-9.3 3.57-13.93 13.98-10.36 23.27 3.56 9.28 13.98 13.92 23.27 10.35zm40.35-15.48c9.3-3.56 13.93-13.98 10.36-23.27-3.56-9.3-13.98-13.93-23.26-10.37-9.3 3.57-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.93 23.26 10.36zm40.36-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.92 13.98-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.37zm40.36-15.48c9.28-3.57 13.92-13.98 10.36-23.27-3.57-9.28-14-13.92-23.27-10.35-9.3 3.56-13.94 13.98-10.37 23.27 3.56 9.28 13.98 13.92 23.27 10.36zm40.35-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.55-9.3-13.97-13.93-23.26-10.36-9.28 3.56-13.92 13.98-10.36 23.27 3.57 9.28 14 13.92 23.27 10.36zm40.36-15.48c9.3-3.57 13.93-14 10.37-23.27-3.57-9.3-14-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.28 13.98 13.92 23.27 10.36zm-378.7 99.06c9.3-3.56 13.94-13.98 10.38-23.27-3.57-9.3-14-13.93-23.27-10.37-9.28 3.57-13.92 13.98-10.35 23.27 3.56 9.3 13.98 13.93 23.26 10.36zm40.37-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.92 13.98-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.37zm40.36-15.48c9.28-3.57 13.92-13.98 10.36-23.27-3.58-9.28-14-13.92-23.28-10.35-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.28 13.98 13.92 23.27 10.36zm40.35-15.5c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 13.98-10.36 23.27 3.58 9.28 14 13.92 23.28 10.36zm40.36-15.48c9.28-3.57 13.92-14 10.36-23.27-3.57-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.28 13.98 13.92 23.27 10.36zm40.35-15.5c9.28-3.56 13.92-13.98 10.35-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.56-13.92 13.98-10.36 23.26 3.57 9.3 14 13.93 23.27 10.36zm40.35-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.56-9.28-13.97-13.92-23.26-10.36-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.48c9.3-3.56 13.93-13.98 10.37-23.27-3.57-9.28-14-13.92-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.26 3.56 9.3 13.98 13.93 23.26 10.36zM278.62 255.9c9.3-3.56 13.93-13.98 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.37-9.27 3.57-13.9 14-10.35 23.27 3.57 9.3 14 13.93 23.27 10.36zm40.36-15.5c9.3-3.55 13.92-13.97 10.36-23.26-3.56-9.28-13.98-13.92-23.27-10.36-9.3 3.57-13.92 14-10.36 23.27 3.57 9.3 14 13.93 23.28 10.36zm40.36-15.48c9.28-3.56 13.92-13.98 10.36-23.27-3.57-9.28-14-13.92-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.26 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.5c9.28-3.55 13.92-13.97 10.35-23.26-3.56-9.28-13.98-13.92-23.27-10.36-9.28 3.57-13.92 13.98-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.36zm40.35-15.48c9.28-3.56 13.92-13.98 10.36-23.27-3.56-9.3-13.98-13.92-23.26-10.36-9.3 3.58-13.93 14-10.36 23.28 3.56 9.3 13.98 13.93 23.27 10.36zm40.35-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.56-9.3-13.98-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.37 23.27 3.57 9.28 14 13.9 23.27 10.35zm40.36-15.48c9.28-3.57 13.92-13.98 10.36-23.27-3.57-9.3-13.98-13.94-23.27-10.37-9.3 3.56-13.93 13.98-10.36 23.27 3.55 9.28 13.97 13.92 23.26 10.36zm-297.98 68.08c9.28-3.56 13.92-13.98 10.36-23.27-3.57-9.3-14-13.92-23.27-10.36-9.3 3.58-13.93 14-10.36 23.28 3.57 9.3 14 13.92 23.28 10.36zm40.35-15.5c9.3-3.56 13.93-13.97 10.36-23.26-3.57-9.3-14-13.93-23.28-10.36-9.28 3.56-13.92 13.98-10.36 23.27 3.57 9.28 14 13.9 23.27 10.35zm40.36-15.48c9.28-3.57 13.9-14 10.35-23.27-3.57-9.3-13.98-13.94-23.27-10.37-9.3 3.56-13.93 13.98-10.36 23.27 3.56 9.28 13.98 13.92 23.27 10.36zm121.06-46.48c9.28-3.56 13.92-13.98 10.36-23.26-3.57-9.3-14-13.93-23.27-10.37-9.3 3.57-13.93 14-10.36 23.27 3.55 9.3 13.97 13.93 23.26 10.36zm40.35-15.5c9.3-3.55 13.94-13.97 10.37-23.26-3.56-9.28-13.98-13.92-23.27-10.36-9.28 3.57-13.92 14-10.36 23.27 3.57 9.3 13.98 13.93 23.27 10.36zm40.37-15.48c9.28-3.56 13.92-13.98 10.36-23.27-3.57-9.28-14-13.92-23.27-10.36-9.3 3.57-13.93 14-10.36 23.27 3.56 9.3 13.98 13.93 23.27 10.36zm-55.85-24.86c9.3-3.57 13.93-14 10.36-23.27-3.56-9.3-13.98-13.93-23.27-10.36-9.28 3.57-13.9 14-10.35 23.28 3.57 9.28 13.98 13.92 23.27 10.36zm40.36-15.5c9.28-3.56 13.92-13.98 10.36-23.26-3.57-9.3-14-13.93-23.27-10.36-9.3 3.56-13.93 13.98-10.36 23.27 3.57 9.27 14 13.9 23.28 10.35zm-15.5-40.35c9.3-3.57 13.93-14 10.37-23.28-3.57-9.3-14-13.92-23.27-10.36-9.3 3.57-13.93 13.98-10.36 23.27 3.56 9.3 13.98 13.93 23.26 10.36z"/>
|
9 |
+
<path fill="#C7C6C2" d="M626.86 150c-.68 0-1.4 0-2.1.1-7.68.6-15.38 1.93-22.86 3.93-7.52 2-14.83 4.7-21.78 8.05-6.8 3.22-12 9.2-14.23 16.4-2.25 7.17-1.38 15.1 2.38 21.63l17.95 31.1c-2.44-.72-5-1.1-7.57-1.1-11.52 0-21.78 7.24-25.6 18.07-3.1-2.56-6.56-4.83-10.44-6.62-6.86-3.28-14.46-4.98-22.13-4.98-5.56 0-11 .9-16.12 2.68-3.97-1.97-8.35-2.98-12.83-2.98-5.37 0-10.67 1.5-15.2 4.32v-1.44c0-16.02-13.07-29.05-29.1-29.05-16.1 0-29.1 13.04-29.1 29.06v10.74c-.6-.54-1.26-1.07-1.9-1.55-12.22-9.7-25.64-11.16-32.5-11.16-5.37 0-19 1.1-31.62 10.73-3.58-4.38-8.35-7.6-13.87-9.3-2.84-.9-5.76-1.32-8.65-1.32-12.7 0-23.77 7.98-27.8 19.94l-1.7-3.43c-4.9-10-15.2-16.46-26.36-16.46-4.42 0-8.86 1.02-12.83 3-7.06 3.42-12.34 9.38-14.9 16.78-2.5 7.4-2.03 15.36 1.4 22.37l25.2 51.57c-7.46 3.5-12.82 9.84-15.12 18.02-2.45 8.64-.9 17.64 4.3 24.74 4.05 5.5 12.02 12.1 26.9 12.53h1.67c25.18 0 36.2-14.7 41.05-27.05.53-1.38.98-2.7 1.4-3.94v-.1l5.2-16.7c8.94 16.4 24.84 25.45 44.8 25.45h.6c16.3-.15 29.52-4.7 39.37-13.48 5.37 8.26 14.52 13.27 24.4 13.27 5.37 0 10.65-1.5 15.2-4.32v1.4c0 16.02 13.02 29.05 29.04 29.05 7.67 0 14.86-2.9 20.3-8.23 5.06-4.9 8.04-11.34 8.64-18.2 17.8-.3 28.85-7.87 35.02-14.23 9.2-9.4 14.44-22.8 15.6-39.83 1.6.36 3.28.48 4.9.48.9 0 1.7 0 2.5-.06-3.46 5.27-4.98 11.68-4.24 18.04.9 7.75 5.16 14.76 11.58 19.18 10.32 7.1 22.25 11.93 34.54 14.02 1.5.24 3.04.35 4.53.35 13.28 0 24.3-9.4 26.67-22.08 5.13 5.97 12.53 9.43 20.6 9.43 6.25 0 12.4-2.2 17.3-6.2 9.53-7.94 17.47-18.1 22.87-29.4 3.4-7 3.6-15.2.48-22.36-2.53-5.9-7.07-10.66-12.7-13.53 5.72-2.62 10.44-7.2 13.12-13 3.37-7.04 3.5-15.22.4-22.37-5.08-11.64-12.45-21.78-21.97-30.22-4.98-4.4-11.33-6.8-17.95-6.8-7.46 0-14.56 3.04-19.63 8.35v-36.18c0-14.98-12.17-27.15-27.15-27.15h.1-.04z"/>
|
10 |
+
<path fill="#fff" d="M626.86 151.85c13.96 0 25.3 11.33 25.3 25.3v41.25l.48-.75c4.7-6.98 12.52-11.12 21-11.12 6.14 0 12.08 2.24 16.7 6.26 9.3 8.25 16.56 18.2 21.48 29.58 2.9 6.7 2.77 14.32-.3 20.88-3.16 6.57-8.95 11.52-15.93 13.52l-.84.24.8.3c6.95 2.2 12.53 7.33 15.45 14 2.94 6.73 2.8 14.33-.34 20.9-5.28 11.03-13.04 20.97-22.44 28.75-4.53 3.73-10.23 5.8-16.1 5.8-8.77 0-16.8-4.5-21.48-11.94l-.45-.72v.9c-.5 13.67-11.64 24.4-25.28 24.4-1.4 0-2.8-.1-4.17-.35-12.05-2-23.72-6.74-33.8-13.66-6.05-4.13-9.96-10.66-10.83-17.9-.9-7.23 1.43-14.48 6.33-19.88l.5-.65-.83.2c-2.03.45-4.12.7-6.18.7-2.3 0-4.47-.3-6.62-.9-.6 11.63-3.34 28.7-15.2 40.86-6.04 6.27-17.02 13.63-34.7 13.63h-.7c-.38 14.7-12.43 26.55-27.2 26.55-14.97 0-27.14-12.22-27.14-27.22l-.1-4.95c-4.6 3.8-10.55 6.05-17 6.05-10.34 0-19.38-5.8-23.98-14.32-7.57 7.46-19.98 14.32-39.85 14.56h-.6c-19.6 0-35.1-8.95-43.67-25.24-.6-1.07-1.07-2.14-1.58-3.28l-6.56 21.12v.12c-.4 1.26-.84 2.5-1.34 3.88-4.63 11.75-15.22 25.8-39.32 25.8h-1.6c-14.15-.3-21.67-6.56-25.48-11.78-4.87-6.56-6.33-15-4-23.12 2.38-8.4 8.1-14.6 15.8-17.6l-25.94-53.4c-3.3-6.56-3.7-14-1.32-21 2.4-6.9 7.34-12.52 13.93-15.74 3.76-1.8 7.9-2.78 12.06-2.78 10.44 0 20.13 6.06 24.7 15.42l3.75 7.67 1.2-3.8c3.58-11.63 14.1-19.4 26.25-19.4 2.68 0 5.46.46 8.05 1.27 5.82 1.8 10.74 5.36 14.2 10.2 12.53-10.38 26.4-11.52 31.92-11.52 6.65 0 19.57 1.43 31.32 10.74 1.8 1.42 3.43 2.97 4.92 4.47V239.1c0-14.97 12.23-27.2 27.15-27.2 15 0 27.13 12.23 27.13 27.2v4.98c4.77-3.75 10.68-6.02 17.12-6.02 4.57 0 8.9 1.13 12.7 3.16 5.08-1.85 10.63-2.8 16.24-2.8 7.36 0 14.76 1.67 21.35 4.77 4.83 2.32 8.83 5.15 12.1 8.34.25-.77.5-1.55.72-2.3 3.5-10.3 13.13-17.2 24-17.2 3.8 0 7.62.9 11.08 2.56l.78.36-20.58-35.7c-3.58-6.16-4.4-13.52-2.3-20.24 2.1-6.7 6.92-12.3 13.25-15.3 6.86-3.28 14.02-5.96 21.47-7.93 7.3-2 14.92-3.3 22.46-3.9.66-.05 1.35-.08 2-.08v.06l.03-.03z"/>
|
11 |
+
<path fill="#C0282A" d="M582.03 250.35l33.56 16.34c6.43 3.16 5.42 12.64-1.5 14.37l-36.25 9.04c-4.47 1.12-8.95-2-9.54-6.6-1.1-9.9 0-19.7 3.08-28.7 1.5-4.4 6.5-6.48 10.65-4.48v.03zm13.42 69.02l24.97-27.74c4.77-5.3 13.66-1.73 13.42 5.43l-1.3 37.3c-.2 4.67-4.4 8.1-8.96 7.33-9.66-1.62-18.8-5.38-26.76-10.84-3.87-2.63-4.47-8.06-1.37-11.5zm59.15-36.92l35.5 11.63c4.48 1.4 6.57 6.4 4.63 10.6-4.18 8.73-10.2 16.5-17.75 22.75-3.58 2.98-8.95 2.1-11.46-1.88l-19.77-31.62c-3.8-6.12 2.08-13.63 8.88-11.43l-.03-.05zm36.1-22.56l-35.8 10.26c-6.92 2-12.53-5.73-8.5-11.7l20.88-30.93c2.57-3.88 7.97-4.57 11.43-1.5 7.17 6.27 13.04 14.2 17 23.33 1.8 4.3-.47 9.25-4.94 10.53h-.06zm-83.73-87.1c-6.48 1.78-12.62 4.02-18.5 6.85-4 1.88-5.52 6.86-3.28 10.74l35.04 60.72c3.94 6.8 14.32 4 14.32-3.87v-70.1c0-4.48-3.8-8.06-8.23-7.67-6.42.5-12.9 1.6-19.33 3.34l-.03-.03z"/>
|
12 |
+
<path fill="#0C0D0B" d="M532.72 301.1c-.15 18.13-4.33 23.9-13.63 23.9-8.66 0-13.14-5.3-13.82-18.78-.15-1.8-.15-3.37-.15-4.98 0-1.28 0-2.57.1-3.88.8-14.74 3.7-19.7 10.58-21.18 11.7-2.68 17.16 3.88 16.83 24.9h.08zm19.03-4.6c0-12.53-1.55-29.84-17.45-37.38-8.95-4.27-20.05-4.18-28.55.7-1.7 1.03-3.4 2.1-4.9 3.24-1-4.18-4.76-7.34-9.33-7.34-5.28 0-9.54 4.32-9.54 9.63v95.55c0 5.28 4.26 9.54 9.54 9.54 5.3 0 9.64-4.3 9.64-9.6v-25.06l.83 1.34c3.87 3.52 10.08 6.63 15.86 6.8 36.5 1.2 34.1-38.68 33.9-47.43zm-94.86 38.18c0 5.37-4.33 9.66-9.64 9.66-5.37 0-9.67-4.3-9.67-9.6V239.1c0-5.28 4.32-9.6 9.6-9.6 5.28 0 9.54 4.32 9.54 9.6v95.55h.12l.03.03zm-87.4-43.85c.6-13.3 10.73-15.5 15.2-15.5 4.57 0 14.62 2.22 15.22 15.68-.12.75-.42 1.14-.84 1.14h-28.75c-.45 0-.72-.48-.8-1.34l-.04.03zm4.17 17.12h35.3c4.76-.36 9.3-4.33 9.32-10.68 0 0 0 0 0-.06v-.47c-1.8-35.08-24.3-40.15-34.5-40.15-10.6 0-34.68 6.27-34.68 45.28.15 14.62 3.64 42.96 34.12 42.66 15.5-.12 25.3-5.37 30.9-13.27 1.08-2.18 1.74-4.18 1.7-6.42-.08-5.2-4.77-10.26-10.26-8.44-2.47.83-4.83 3.27-9.33 6.94-8.47 4.95-18.2 4.18-22.85-1.64-1.73-2.18-3.04-5.37-3.73-9.25-.12-.44-.15-.9-.15-1.28 0-2.24 1.2-3.1 4.12-3.22h.03zm-78.75 24.4l-30.04-61.48c-2.4-4.92-.36-10.83 4.53-13.2 4.93-2.5 10.84-.43 13.23 4.46l23.26 47.6 14.3-46.23c1.62-5.16 7.17-8.05 12.36-6.47 5.2 1.58 8.1 7.16 6.5 12.32l-24.76 80.03v.06c-.3.9-.6 1.8-.95 2.63-4.1 10.44-11.54 14.9-24.1 14.6-12.4-.28-14.32-7.6-12.98-12.42 1.5-5.3 5.97-6.87 10.3-6.87 4.38 0 6.67 0 8.17-1.1 2.26-1.73 4.05-6.26.23-14.08l-.08.06.02.1z"/>
|
13 |
+
</g>
|
14 |
+
</svg>
|
assets/js/site-reviews-admin.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
"use strict";var x=jQuery.noConflict(),GLSR={addons:{},pinned:{}};GLSR.colorControls=function(){"object"==typeof x.wp&&"function"==typeof x.wp.wpColorPicker&&x(document).find('input[type="text"].color-picker-hex').each(function(){var e=x(this),n=e.data("colorpicker")||{};e.wpColorPicker(n)})},GLSR.dismissNotices=function(){x(".notice.is-dismissible").each(function(){var e=x(this);e.fadeTo(100,0,function(){e.slideUp(100,function(){e.remove()})})})},GLSR.getURLParameter=function(e){return decodeURIComponent((new RegExp("[?|&]"+e+"=([^&;]+?)(&|#|;|$)").exec(location.search)||[null,""])[1].replace(/\+/g,"%20"))||null},GLSR.insertNotices=function(e){e=e||!1,e&&(x("#glsr-notices").length||(x("#message.notice").remove(),x("form#post").before('<div id="glsr-notices" />')),x("#glsr-notices").html(e),x(document).trigger("wp-updates-notice-added"))},GLSR.normalizeValue=function(e){return["true","on","1"].indexOf(e)>-1||!(["false","off","0"].indexOf(e)>-1)&&e},GLSR.normalizeValues=function(e){return e.map(GLSR.normalizeValue)},GLSR.onClearLog=function(e){e.preventDefault();var n=x(this);if(!n.is(":disabled")){var t={action:site_reviews.action,request:{action:"clear-log"}};n.prop("disabled",!0),x.post(site_reviews.ajaxurl,t,function(e){GLSR.insertNotices(e.notices),x("#log-file").val(e.log),n.prop("disabled",!1)},"json")}},GLSR.onFieldChange=function(){var e=x(this).closest("form").find("[data-depends]");if(e.length)for(var n=this.getAttribute("name"),t=this.getAttribute("type"),i=0;i<e.length;i++)try{var o,s=JSON.parse(e[i].getAttribute("data-depends"));if(s.name!==n)continue;o="checkbox"===t?!!this.checked:x.isArray(s.value)?x.inArray(GLSR.normalizeValue(this.value),GLSR.normalizeValues(s.value))!==-1:GLSR.normalizeValue(s.value)===GLSR.normalizeValue(this.value),GLSR.toggleHiddenField(e[i],o)}catch(a){}},GLSR.pointers=function(e){x(e.target).pointer({content:e.options.content,position:e.options.position,close:function(){x.post(ajaxurl,{pointer:e.id,action:"dismiss-wp-pointer"})}}).pointer("open").pointer("sendToTop"),x(document).on("wp-window-resized",function(){x(e.target).pointer("reposition")})},GLSR.textareaResize=function(e){var n=320,t=e[0];t.style.height="auto",t.style.height=t.scrollHeight>n?t.scrollHeight+"px":n+"px"},GLSR.toggleHiddenField=function(e,n){var t=x(e).closest(".glsr-field");t.length&&(n?t.removeClass("hidden"):t.addClass("hidden"))},GLSR.pinned.events=function(){var e=x("#pinned-status-select");x("a.cancel-pinned-status").on("click",function(n){n.preventDefault(),e.slideUp("fast").siblings("a.edit-pinned-status").show().focus(),e.find("select").val("0"===x("#hidden-pinned-status").val()?1:0)}),x("a.edit-pinned-status").on("click",function(n){n.preventDefault(),e.is(":hidden")&&(e.slideDown("fast",function(){e.find("select").focus()}),x(this).hide())}),x("a.save-pinned-status").on("click",function(n){n.preventDefault(),e.slideUp("fast").siblings("a.edit-pinned-status").show().focus(),GLSR.pinned.save(x(this))}),x("table").on("click","td.sticky i",GLSR.pinned.onToggle)},GLSR.pinned.onToggle=function(){var e=x(this),n={action:site_reviews.action,request:{action:"toggle-pinned",id:e[0].getAttribute("data-id")}};x.post(site_reviews.ajaxurl,n,function(n){n.pinned?e.addClass("pinned"):e.removeClass("pinned")})},GLSR.pinned.save=function(e){var n={action:site_reviews.action,request:{action:"toggle-pinned",id:x("#post_ID").val(),pinned:x("#pinned-status").val()}};x.post(site_reviews.ajaxurl,n,function(n){x("#pinned-status").val(0|!n.pinned),x("#hidden-pinned-status").val(0|n.pinned),x("#pinned-status-text").text(n.pinned?e.data("yes"):e.data("no")),GLSR.insertNotices(n.notices)})},x(function(){var e=GLSR.getURLParameter("fix"),n=x("#contentdiv > textarea");e&&x('td [data-key="'+e+'"]').focus(),n.length&&(GLSR.textareaResize(n),x(document).on("wp-window-resized.editor-expand",function(){GLSR.textareaResize(n)})),x("form").on("change",":input",GLSR.onFieldChange),x("form").on("click","#clear-log",GLSR.onClearLog),GLSR.colorControls(),GLSR.pinned.events(),x.each(site_reviews_pointers.pointers,function(e,n){GLSR.pointers(n)}),x(document).on("click",".branch-4 .toggle-row, .branch-4-1 .toggle-row, .branch-4-2 .toggle-row",function(){x(this).closest("tr").toggleClass("is-expanded")})});
|
assets/js/site-reviews.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
!function(t,e,s){"use strict";var r=function(t,e){this.el=t,this.options=e,this.metadata=this.el.getAttribute("data-options"),this.stars=0,this.init()};r.prototype={defaults:{clearable:!0,initialText:"Click to Rate",onClick:null,showText:!0},init:function(){if("SELECT"===this.el.tagName){for(var t=0;t<this.el.length;t++)if(""!==this.el[t].value){if(isNaN(parseFloat(this.el[t].value))||!isFinite(this.el[t].value))return;this.stars++}if(!(this.stars<1||this.stars>10))return this.config=this._extend({},this.defaults,this.options,this.metadata),this.build(),this._on("change",this.el,this.change.bind(this)),this._on("mouseenter",this.wrap,this.enter.bind(this)),this._on("mouseleave",this.wrap,this.leave.bind(this)),this._on("click",this.wrap,this.select.bind(this)),this._on("reset",this.el.closest("form"),this.clear.bind(this)),this.current=this.el.options[this.el.selectedIndex].value,this.selected=this.current,this.width=this.wrap.offsetWidth,this.star=Math.round(this.width/this.stars),this.select(),this}},build:function(){var t={},e={},s=this._createEl("span",{"class":"gl-star-rating","data-star-rating":""});this.el.parentNode.insertBefore(s,this.el),s.appendChild(this.el),this.wrap=this._insertAfterEl(this.el,"span",{"class":"gl-star-rating-stars"}),this.config.showText&&(this.text=this._insertAfterEl(this.wrap,"span",{"class":"gl-star-rating-text"}));for(var r=0;r<this.el.length;r++)""!==this.el[r].value&&(e[this.el[r].value]=this.el[r].text);Object.keys(e).sort().forEach(function(s){t[s]=e[s]});for(var i in t)this._appendTo(this.wrap,"span",{"data-value":i,"data-text":t[i]})},change:function(){this.show(this.el.options[this.el.selectedIndex].value)},clear:function(t){(this.config.clearable||t!==s)&&(this.el.value="",this.selected="",this.show(0))},enter:function(){var t=this.wrap.getBoundingClientRect();this._on("mousemove",this.wrap,this.move.bind(this)),this.offsetLeft=t.left+e.body.scrollLeft},getIndexFromPosition:function(t){return Math.min(Math.ceil(Math.max(t-this.offsetLeft,1)/this.star),this.stars)},leave:function(){this._off("mousemove",this.wrap,this.move),this.show(this.selected)},move:function(t){this.show(this.getIndexFromPosition(t.pageX))},select:function(t){var e=this.current;return t!==s&&(e=this.getIndexFromPosition(t.pageX),""!==this.current&&parseFloat(this.selected)===e)?void this.clear():(this.el.value=e,this.selected=e,this.show(e),void(t!==s&&"function"==typeof this.config.onClick&&this.config.onClick(this.el)))},show:function(t){(t<0||""===t)&&(t=0),t>this.stars&&(t=this.stars),this._removeClass(this.wrap,"s"+10*this.current),this._addClass(this.wrap,"s"+10*t),this.config.showText&&(this.text.textContent=t<1?this.config.initialText:this.wrap.childNodes[t-1].dataset.text),this.current=t},_addClass:function(t,e){t.classList?t.classList.add(e):this._hasClass(t,e)||(t.className+=" "+e)},_appendTo:function(t,e,s){var r=this._createEl(e,s);t.innerHTML+=r.outerHTML},_createEl:function(t,s){var r="string"==typeof t?e.createElement(t):t;s=s||{};for(var i in s)r.setAttribute(i,s[i]);return r},_extend:function(){var t=[].slice.call(arguments),e=!1;"boolean"==typeof t[0]&&(e=t.shift());for(var s=t[0],r=t.slice(1),i=r.length,n=0;n<i;n++){var a=r[n];for(var o in a){var l=a[o];if(e&&l&&"object"==typeof l){var c=Array.isArray(l)?[]:{};s[o]=this._extend(!0,c,l)}else s[o]=l}}return s},_hasClass:function(t,e){return t.classList?t.classList.contains(e):new RegExp("\\b"+e+"\\b").test(t.className)},_insertAfterEl:function(t,e,s){var r=this._createEl(e,s);return t.parentNode.insertBefore(r,t.nextSibling),r},_off:function(t,e,s){e.detachEvent?e.detachEvent("on"+t,s):e.removeEventListener(t,s)},_on:function(t,e,s){e.attachEvent?e.attachEvent("on"+t,s):e.addEventListener(t,s)},_removeClass:function(t,e){t.classList?t.classList.remove(e):t.className=t.className.replace(new RegExp("\\b"+e+"\\b","g"),"")}},r.defaults=r.prototype.defaults,t.jQuery&&(jQuery.fn.starrating=function(t){return this.each(function(){jQuery.data(this,"plugin_starrating")||jQuery.data(this,"plugin_starrating",new r(this,t))})}),t.StarRating=r}(window,document),this.Element&&!function(t){t.matches=t.matches||t.matchesSelector||t.webkitMatchesSelector||t.msMatchesSelector||function(t){for(var e=this,s=(e.parentNode||e.document).querySelectorAll(t),r=-1;s[++r]&&s[r]!==e;);return!!s[r]},t.closest=t.closest||function(t){for(var e=this;e.matches&&!e.matches(t);)e=e.parentNode;return e.matches?e:null}}(Element.prototype);var GLSR={};GLSR.addClass=function(t,e){t.classList?t.classList.add(e):GLSR.hasClass(t,e)||(t.className+=" "+e)},GLSR.convertValue=function(t){if(GLSR.isNumeric(t))return parseFloat(t);if("true"===t)return!0;if("false"===t)return!1;if(""!==t&&null!==t)return t},GLSR.hasClass=function(t,e){return t.classList?t.classList.contains(e):new RegExp("\\b"+e+"\\b").test(t.className)},GLSR.inArray=function(t,e){for(var s=e.length;s--;)if(e[s]===t)return!0;return!1},GLSR.isNumeric=function(t){return!(isNaN(parseFloat(t))||!isFinite(t))},GLSR.on=function(t,e,s){[].forEach.call(document.querySelectorAll(e),function(e){e.attachEvent?e.attachEvent("on"+t,s):e.addEventListener(t,s)})},GLSR.off=function(t,e,s){[].forEach.call(document.querySelectorAll(e),function(e){e.detachEvent?e.detachEvent("on"+t,s):e.removeEventListener(t,s)})},GLSR.parseFormData=function(t,e){e=!!e||!1;for(var s=/[^\[\]]+/g,r={},i={},n=(function(t,s,r,a){var o=r.shift();if(a=a?a+"."+o:o,r.length)s[o]||(s[o]={}),n(t,s[o],r,a);else{var l=e?GLSR.convertValue(t.value):t.value;if(a in i&&"radio"!==t.type&&!s[o].isArray()?o in s?s[o]=[s[o]]:s[o]=[]:i[a]=!0,GLSR.inArray(t.type,["radio","checkbox"])&&!t.checked)return;s[o]?s[o].push(l):s[o]=l}}),a=0;a<t.length;a++){var o=t[a];if(o.name&&!o.disabled&&!GLSR.inArray(o.type,["file","reset","submit","button"])){var l=o.name.match(s);l.length||(l=[o.name]),n(o,r,l)}}return r},GLSR.postAjax=function(t,e,s){var r="string"!=typeof e?GLSR.serialize(e):e,i=window.XMLHttpRequest?new XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP");return i.open("POST",t),i.onreadystatechange=function(){i.readyState>3&&200===i.status&&s(JSON.parse(i.responseText))},i.setRequestHeader("X-Requested-With","XMLHttpRequest"),i.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8"),i.send(r),i},GLSR.ready=function(t){"function"==typeof t&&("loading"!==document.readyState?t():document.addEventListener?document.addEventListener("DOMContentLoaded",t):document.attachEvent("onreadystatechange",function(){"complete"===document.readyState&&t()}))},GLSR.removeClass=function(t,e){t.classList?t.classList.remove(e):t.className=t.className.replace(new RegExp("\\b"+e+"\\b","g"),"")},GLSR.serialize=function(t,e){var s=[];for(var r in t)if(t.hasOwnProperty(r)){var i=e?e+"["+r+"]":r,n=t[r];s.push("object"==typeof n?GLSR.serialize(n,i):encodeURIComponent(i)+"="+encodeURIComponent(n))}return s.join("&")},GLSR.insertAfter=function(t,e,s){var r=GLSR.createEl(e,s);return t.parentNode.insertBefore(r,t.nextSibling),r},GLSR.appendTo=function(t,e,s){var r=GLSR.createEl(e,s);return t.appendChild(r),r},GLSR.createEl=function(t,e){var s="string"==typeof t?document.createElement(t):t;e=e||{};for(var r in e)s.setAttribute(r,e[r]);return s},GLSR.clearFieldError=function(t){var e=t.closest(".glsr-field");if(null!==e){var s=e.querySelector(".glsr-field-errors");GLSR.removeClass(e,"glsr-has-error"),null!==s&&s.parentNode.removeChild(s)}},GLSR.clearFormErrors=function(t){for(var e=0;e<t.length;e++)GLSR.clearFieldError(t[e])},GLSR.showFormErrors=function(t,e){var s,r;for(var i in e){s=t.querySelector('[name="'+i+'"]').closest(".glsr-field"),r=s.querySelector(".glsr-field-errors"),GLSR.addClass(s,"glsr-has-error"),null===r&&(r=GLSR.appendTo(s,"span",{"class":"glsr-field-errors"}));for(var n=0;n<e[i].errors.length;n++)null!==e[i].errors[n]&&(r.innerHTML+='<span class="glsr-field-error">'+e[i].errors[n]+"</span>")}},GLSR.showFormMessage=function(t,e){var s=t.querySelector('input[name="form_id"]'),r=t.querySelector(".glsr-form-messages");null===r&&(r=GLSR.insertAfter(s,"div",{"class":"glsr-form-messages"})),e.errors?GLSR.addClass(r,"gslr-has-errors"):GLSR.removeClass(r,"gslr-has-errors"),r.innerHTML="<p>"+e.message+"</p>"},GLSR.submitForm=function(t){var e=t.querySelector('input[type="submit"]'),s={action:site_reviews.action,request:GLSR.parseFormData(t)};GLSR.postAjax(site_reviews.ajaxurl,s,function(s){GLSR.clearFormErrors(t),GLSR.showFormMessage(t,s),e.disabled&&e.removeAttribute("disabled"),s.errors?GLSR.showFormErrors(t,s.errors):t.reset()})},GLSR.on("change","form.glsr-submit-review-form",function(t){GLSR.clearFieldError(t.target)}),GLSR.on("submit","form.glsr-submit-review-form",function(t){t.preventDefault();var e=this.querySelector('input[type="submit"]');e.disabled||e.setAttribute("disabled",""),GLSR.submitForm(this)}),GLSR.ready(function(){for(var t=document.querySelectorAll("select.glsr-star-rating"),e=0;e<t.length;e++)new StarRating(t[e],{clearable:!1,showText:!1,onClick:GLSR.clearFieldError})});
|
autoload.php
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
defined( 'WPINC' ) || die;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* PSR-4 autoloader
|
15 |
+
*/
|
16 |
+
spl_autoload_register( function( $class )
|
17 |
+
{
|
18 |
+
$namespaces = [
|
19 |
+
'GeminiLabs\\SiteReviews\\' => __DIR__ . '/plugin/',
|
20 |
+
'GeminiLabs\\SiteReviews\\Tests\\' => __DIR__ . '/tests/',
|
21 |
+
'Sinergi\\BrowserDetector\\' => __DIR__ . '/vendor/sinergi/browser-detector/src/',
|
22 |
+
];
|
23 |
+
|
24 |
+
foreach( $namespaces as $prefix => $base_dir ) {
|
25 |
+
|
26 |
+
$len = strlen( $prefix );
|
27 |
+
|
28 |
+
if( strncmp( $prefix, $class, $len ) !== 0 )continue;
|
29 |
+
|
30 |
+
$file = $base_dir . str_replace( '\\', '/', substr( $class, $len ) ) . '.php';
|
31 |
+
|
32 |
+
if( !file_exists( $file ) )continue;
|
33 |
+
|
34 |
+
require $file;
|
35 |
+
break;
|
36 |
+
}
|
37 |
+
});
|
38 |
+
|
39 |
+
require_once( ABSPATH . WPINC . '/class-phpass.php' );
|
languages/readme.txt
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
== Site Reviews i18n ==
|
2 |
+
|
3 |
+
Do not put custom translations here as they will be deleted on plugin updates. Instead, put custom translations in /wp-content/languages/site-reviews/
|
4 |
+
|
5 |
+
If you would like to translate or improve a translation, email: site-reviews@geminilabs.io
|
languages/site-reviews-en_US.mo
ADDED
Binary file
|
languages/site-reviews-en_US.po
ADDED
@@ -0,0 +1,896 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Copyright (C) 2017 site-reviews
|
2 |
+
# This file is distributed under the same license as the site-reviews package.
|
3 |
+
msgid ""
|
4 |
+
msgstr ""
|
5 |
+
"Project-Id-Version: site-reviews\n"
|
6 |
+
"Report-Msgid-Bugs-To: https://github.com/geminilabs/site-reviews/issues/new\n"
|
7 |
+
"MIME-Version: 1.0\n"
|
8 |
+
"Content-Type: text/plain; charset=utf-8\n"
|
9 |
+
"Content-Transfer-Encoding: 8bit\n"
|
10 |
+
"PO-Revision-Date: 2017-MO-DA HO:MI+ZONE\n"
|
11 |
+
"Last-Translator: Paul Ryley <paul@geminilabs.io>\n"
|
12 |
+
"Language-Team: Gemini Labs <support@geminilabs.io>\n"
|
13 |
+
"X-Poedit-Basepath: ..\n"
|
14 |
+
"X-Poedit-Sourcecharset: UTF-8\n"
|
15 |
+
"X-Poedit-Keywordslist: "
|
16 |
+
"__;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;esc_attr_e;esc_attr_x:1,"
|
17 |
+
"2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;_nx_noop:3c,1,2;__"
|
18 |
+
"ngettext_noop:1,2\n"
|
19 |
+
"X-Poedit-Searchpath-0: .\n"
|
20 |
+
"X-Poedit-Searchpathexcluded-0: *.js\n"
|
21 |
+
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
22 |
+
"Language: qps-ploc\n"
|
23 |
+
|
24 |
+
#: activate.php:43
|
25 |
+
msgid "The Site Reviews plugin was deactivated."
|
26 |
+
msgstr "The Site Reviews plugin was deactivated."
|
27 |
+
|
28 |
+
#: activate.php:48
|
29 |
+
msgid ""
|
30 |
+
"Sorry, this plugin requires PHP version 5.4 or greater in order to work "
|
31 |
+
"properly."
|
32 |
+
msgstr ""
|
33 |
+
"Sorry, this plugin requires PHP version 5.4 or greater in order to work "
|
34 |
+
"properly."
|
35 |
+
|
36 |
+
#: activate.php:49
|
37 |
+
msgid ""
|
38 |
+
"Please contact your hosting provider or server administrator to upgrade the "
|
39 |
+
"version of PHP on your server (your server is running PHP version %s), or "
|
40 |
+
"try to find an alternative plugin."
|
41 |
+
msgstr ""
|
42 |
+
"Please contact your hosting provider or server administrator to upgrade the "
|
43 |
+
"version of PHP on your server (your server is running PHP version %s), or "
|
44 |
+
"try to find an alternative plugin."
|
45 |
+
|
46 |
+
#: activate.php:55
|
47 |
+
msgid ""
|
48 |
+
"Sorry, this plugin requires WordPress version 4.0.0 or greater in order to "
|
49 |
+
"work properly."
|
50 |
+
msgstr ""
|
51 |
+
"Sorry, this plugin requires WordPress version 4.0.0 or greater in order to "
|
52 |
+
"work properly."
|
53 |
+
|
54 |
+
#: activate.php:56
|
55 |
+
msgid "Update WordPress"
|
56 |
+
msgstr "Update WordPress"
|
57 |
+
|
58 |
+
#: plugin/Commands/RegisterPostType.php:20, plugin/Widgets/RecentReviews.php:48, plugin/Widgets/ReviewsForm.php:40
|
59 |
+
msgid "Title"
|
60 |
+
msgstr "Title"
|
61 |
+
|
62 |
+
#: plugin/Commands/RegisterPostType.php:21, views/edit/meta.php:46
|
63 |
+
msgid "Date"
|
64 |
+
msgstr "Date"
|
65 |
+
|
66 |
+
#: plugin/Controllers/AjaxController.php:26
|
67 |
+
msgid "Log is empty"
|
68 |
+
msgstr "Log is empty"
|
69 |
+
|
70 |
+
#: plugin/Controllers/MainController.php:51
|
71 |
+
msgid "Log was cleared."
|
72 |
+
msgstr "Log was cleared."
|
73 |
+
|
74 |
+
#: plugin/Controllers/MainController.php:87, plugin/Controllers/MainController.php:293, plugin/Controllers/MainController.php:478
|
75 |
+
msgid "Settings"
|
76 |
+
msgstr "Settings"
|
77 |
+
|
78 |
+
#: plugin/Controllers/MainController.php:106
|
79 |
+
msgid "%s Review"
|
80 |
+
msgid_plural "%s Reviews"
|
81 |
+
msgstr[0] "%s Review"
|
82 |
+
msgstr[1] "%s Reviews"
|
83 |
+
|
84 |
+
#: plugin/Controllers/MainController.php:156
|
85 |
+
msgid "Details"
|
86 |
+
msgstr "Details"
|
87 |
+
|
88 |
+
#: plugin/Controllers/MainController.php:170
|
89 |
+
msgid "Pin Your Reviews"
|
90 |
+
msgstr "Pin Your Reviews"
|
91 |
+
|
92 |
+
#: plugin/Controllers/MainController.php:171
|
93 |
+
msgid ""
|
94 |
+
"You can pin exceptional reviews so that they are always shown first in your "
|
95 |
+
"widgets and shortcodes."
|
96 |
+
msgstr ""
|
97 |
+
"You can pin exceptional reviews so that they are always shown first in your "
|
98 |
+
"widgets and shortcodes."
|
99 |
+
|
100 |
+
#: plugin/Controllers/MainController.php:193
|
101 |
+
msgid "Review"
|
102 |
+
msgstr "Review"
|
103 |
+
|
104 |
+
#: plugin/Controllers/MainController.php:194, views/menu/status.php:17
|
105 |
+
msgid "Reviews"
|
106 |
+
msgstr "Reviews"
|
107 |
+
|
108 |
+
#: plugin/Controllers/MainController.php:195
|
109 |
+
msgid "Site Reviews"
|
110 |
+
msgstr "Site Reviews"
|
111 |
+
|
112 |
+
#: plugin/Controllers/MainController.php:203, views/edit/meta.php:50
|
113 |
+
msgid "Reviewer"
|
114 |
+
msgstr "Reviewer"
|
115 |
+
|
116 |
+
#: plugin/Controllers/MainController.php:204, views/edit/meta.php:42
|
117 |
+
msgid "Type"
|
118 |
+
msgstr "Type"
|
119 |
+
|
120 |
+
#: plugin/Controllers/MainController.php:205, views/edit/meta.php:38
|
121 |
+
msgid "Rating"
|
122 |
+
msgstr "Rating"
|
123 |
+
|
124 |
+
#: plugin/Controllers/MainController.php:206, views/edit/pinned.php:17
|
125 |
+
msgid "Pinned"
|
126 |
+
msgstr "Pinned"
|
127 |
+
|
128 |
+
#: plugin/Controllers/MainController.php:229
|
129 |
+
msgid "Approve this review"
|
130 |
+
msgstr "Approve this review"
|
131 |
+
|
132 |
+
#: plugin/Controllers/MainController.php:231, plugin/Handlers/SubmitReview.php:95
|
133 |
+
msgid "Approve"
|
134 |
+
msgstr "Approve"
|
135 |
+
|
136 |
+
#: plugin/Controllers/MainController.php:234
|
137 |
+
msgid "Unapprove this review"
|
138 |
+
msgstr "Unapprove this review"
|
139 |
+
|
140 |
+
#: plugin/Controllers/MainController.php:236
|
141 |
+
msgid "Unapprove"
|
142 |
+
msgstr "Unapprove"
|
143 |
+
|
144 |
+
#: plugin/Controllers/MainController.php:294
|
145 |
+
msgid "Get Help"
|
146 |
+
msgstr "Get Help"
|
147 |
+
|
148 |
+
#: plugin/Controllers/MainController.php:295, plugin/Controllers/MainController.php:353
|
149 |
+
msgid "Add-Ons"
|
150 |
+
msgstr "Add-Ons"
|
151 |
+
|
152 |
+
#: plugin/Controllers/MainController.php:331
|
153 |
+
msgid "Submit a Site Review"
|
154 |
+
msgstr "Submit a Site Review"
|
155 |
+
|
156 |
+
#: plugin/Controllers/MainController.php:332
|
157 |
+
msgid "A \"submit a review\" form for your site."
|
158 |
+
msgstr "A \"submit a review\" form for your site."
|
159 |
+
|
160 |
+
#: plugin/Controllers/MainController.php:336
|
161 |
+
msgid "Recent Site Reviews"
|
162 |
+
msgstr "Recent Site Reviews"
|
163 |
+
|
164 |
+
#: plugin/Controllers/MainController.php:337
|
165 |
+
msgid "Your site’s most recent Local Reviews."
|
166 |
+
msgstr "Your site’s most recent Local Reviews."
|
167 |
+
|
168 |
+
#: plugin/Controllers/MainController.php:373
|
169 |
+
msgid "Documentation"
|
170 |
+
msgstr "Documentation"
|
171 |
+
|
172 |
+
#: plugin/Controllers/MainController.php:376
|
173 |
+
msgid "System Info"
|
174 |
+
msgstr "System Info"
|
175 |
+
|
176 |
+
#: plugin/Controllers/MainController.php:459
|
177 |
+
msgid "%s reviews are read-only."
|
178 |
+
msgstr "%s reviews are read-only."
|
179 |
+
|
180 |
+
#: plugin/Controllers/MainController.php:481
|
181 |
+
msgid "Licenses"
|
182 |
+
msgstr "Licenses"
|
183 |
+
|
184 |
+
#: plugin/Controllers/MainController.php:497
|
185 |
+
msgid "Logging enabled."
|
186 |
+
msgstr "Logging enabled."
|
187 |
+
|
188 |
+
#: plugin/Controllers/MainController.php:498
|
189 |
+
msgid "Logging disabled."
|
190 |
+
msgstr "Logging disabled."
|
191 |
+
|
192 |
+
#: plugin/Controllers/MainController.php:514
|
193 |
+
msgid "Settings updated."
|
194 |
+
msgstr "Settings updated."
|
195 |
+
|
196 |
+
#: plugin/Controllers/ReviewController.php:48
|
197 |
+
msgid "Anonymous"
|
198 |
+
msgstr "Anonymous"
|
199 |
+
|
200 |
+
#: plugin/Controllers/ReviewController.php:56
|
201 |
+
msgid "No Title"
|
202 |
+
msgstr "No Title"
|
203 |
+
|
204 |
+
#: plugin/Controllers/ReviewController.php:60
|
205 |
+
msgid "Please fix the submission errors."
|
206 |
+
msgstr "Please fix the submission errors."
|
207 |
+
|
208 |
+
#: plugin/Handlers/RegisterPostType.php:298
|
209 |
+
msgid "%s review updated."
|
210 |
+
msgid_plural "%s posts updated."
|
211 |
+
msgstr[0] "%s review updated."
|
212 |
+
msgstr[1] "%s posts updated."
|
213 |
+
|
214 |
+
#: plugin/Handlers/RegisterPostType.php:299
|
215 |
+
msgid "%s review not updated, somebody is editing it."
|
216 |
+
msgid_plural "%s reviews not updated, somebody is editing them."
|
217 |
+
msgstr[0] "%s review not updated, somebody is editing it."
|
218 |
+
msgstr[1] "%s reviews not updated, somebody is editing them."
|
219 |
+
|
220 |
+
#: plugin/Handlers/RegisterPostType.php:300
|
221 |
+
msgid "%s review permanently deleted."
|
222 |
+
msgid_plural "%s reviews permanently deleted."
|
223 |
+
msgstr[0] "%s review permanently deleted."
|
224 |
+
msgstr[1] "%s reviews permanently deleted."
|
225 |
+
|
226 |
+
#: plugin/Handlers/RegisterPostType.php:301
|
227 |
+
msgid "%s review moved to the Trash."
|
228 |
+
msgid_plural "%s reviews moved to the Trash."
|
229 |
+
msgstr[0] "%s review moved to the Trash."
|
230 |
+
msgstr[1] "%s reviews moved to the Trash."
|
231 |
+
|
232 |
+
#: plugin/Handlers/RegisterPostType.php:302
|
233 |
+
msgid "%s review restored from the Trash."
|
234 |
+
msgid_plural "%s reviews restored from the Trash."
|
235 |
+
msgstr[0] "%s review restored from the Trash."
|
236 |
+
msgstr[1] "%s reviews restored from the Trash."
|
237 |
+
|
238 |
+
#: plugin/Handlers/RegisterPostType.php:593
|
239 |
+
msgid "%s star"
|
240 |
+
msgid_plural "%s stars"
|
241 |
+
msgstr[0] "%s star"
|
242 |
+
msgstr[1] "%s stars"
|
243 |
+
|
244 |
+
#: plugin/Handlers/RegisterPostType.php:597
|
245 |
+
msgid "All ratings"
|
246 |
+
msgstr "All ratings"
|
247 |
+
|
248 |
+
#: plugin/Handlers/RegisterPostType.php:615
|
249 |
+
msgid "All types"
|
250 |
+
msgstr "All types"
|
251 |
+
|
252 |
+
#: plugin/Handlers/RegisterPostType.php:618, views/edit/meta.php:7
|
253 |
+
msgid "Local Review"
|
254 |
+
msgstr "Local Review"
|
255 |
+
|
256 |
+
#: plugin/Handlers/SubmitReview.php:63
|
257 |
+
msgid "Your review has been submitted!"
|
258 |
+
msgstr "Your review has been submitted!"
|
259 |
+
|
260 |
+
#: plugin/Handlers/SubmitReview.php:96
|
261 |
+
msgid "Discard"
|
262 |
+
msgstr "Discard"
|
263 |
+
|
264 |
+
#: plugin/Handlers/SubmitReview.php:144
|
265 |
+
msgid "New %s-Star Review"
|
266 |
+
msgstr "New %s-Star Review"
|
267 |
+
|
268 |
+
#: plugin/Handlers/SubmitReview.php:197, plugin/Strings.php:28
|
269 |
+
msgid "View Review"
|
270 |
+
msgstr "View Review"
|
271 |
+
|
272 |
+
#: plugin/Handlers/TogglePinned.php:40
|
273 |
+
msgid "The review is pinned."
|
274 |
+
msgstr "The review is pinned."
|
275 |
+
|
276 |
+
#: plugin/Handlers/TogglePinned.php:41
|
277 |
+
msgid "The review is unpinned."
|
278 |
+
msgstr "The review is unpinned."
|
279 |
+
|
280 |
+
#: plugin/Html/Fields/Colorpicker.php:40
|
281 |
+
msgid "Hex Value"
|
282 |
+
msgstr "Hex Value"
|
283 |
+
|
284 |
+
#: plugin/Html/Fields/Progress.php:37
|
285 |
+
msgid "Please wait..."
|
286 |
+
msgstr "Please wait..."
|
287 |
+
|
288 |
+
#: plugin/Html/Fields/Progress.php:38
|
289 |
+
msgid "Inactive"
|
290 |
+
msgstr "Inactive"
|
291 |
+
|
292 |
+
#: plugin/Html/Fields/Yesno.php:25, views/edit/pinned.php:13
|
293 |
+
msgid "No"
|
294 |
+
msgstr "No"
|
295 |
+
|
296 |
+
#: plugin/Html/Fields/Yesno.php:26, views/edit/pinned.php:14
|
297 |
+
msgid "Yes"
|
298 |
+
msgstr "Yes"
|
299 |
+
|
300 |
+
#: plugin/Html/Form.php:119
|
301 |
+
msgid "Submit"
|
302 |
+
msgstr "Submit"
|
303 |
+
|
304 |
+
#: plugin/Html/Form.php:255
|
305 |
+
msgid "Save Changes"
|
306 |
+
msgstr "Save Changes"
|
307 |
+
|
308 |
+
#: plugin/Html/Partials/Addon.php:40
|
309 |
+
msgid "More Info"
|
310 |
+
msgstr "More Info"
|
311 |
+
|
312 |
+
#: plugin/Html/Partials/Reviews.php:77
|
313 |
+
msgid "No reviews were found."
|
314 |
+
msgstr "No reviews were found."
|
315 |
+
|
316 |
+
#: plugin/Html/Partials/Reviews.php:133
|
317 |
+
msgid "<span class=\"meta-nav\">←</span> Previous"
|
318 |
+
msgstr "<span class=\"meta-nav\">←</span> Previous"
|
319 |
+
|
320 |
+
#: plugin/Html/Partials/Reviews.php:139
|
321 |
+
msgid "Next <span class=\"meta-nav\">→</span>"
|
322 |
+
msgstr "Next <span class=\"meta-nav\">→</span>"
|
323 |
+
|
324 |
+
#: plugin/Html/Partials/Reviews.php:145
|
325 |
+
msgid "Page"
|
326 |
+
msgstr "Page"
|
327 |
+
|
328 |
+
#: plugin/Html/Partials/Reviews.php:148
|
329 |
+
msgid "Next →"
|
330 |
+
msgstr "Next →"
|
331 |
+
|
332 |
+
#: plugin/Html/Partials/Reviews.php:149
|
333 |
+
msgid "← Previous"
|
334 |
+
msgstr "← Previous"
|
335 |
+
|
336 |
+
#: plugin/Html/Partials/Reviews.php:199
|
337 |
+
msgid "Site Reviews navigation"
|
338 |
+
msgstr "Site Reviews navigation"
|
339 |
+
|
340 |
+
#: plugin/Html/Partials/Reviews.php:260
|
341 |
+
msgid "read more"
|
342 |
+
msgstr "read more"
|
343 |
+
|
344 |
+
#: plugin/Settings.php:113, plugin/Settings.php:196
|
345 |
+
msgid "Save Settings"
|
346 |
+
msgstr "Save Settings"
|
347 |
+
|
348 |
+
#: plugin/Settings.php:119
|
349 |
+
msgid "Require approval"
|
350 |
+
msgstr "Require approval"
|
351 |
+
|
352 |
+
#: plugin/Settings.php:121
|
353 |
+
msgid "Set the status of new review submissions to pending."
|
354 |
+
msgstr "Set the status of new review submissions to pending."
|
355 |
+
|
356 |
+
#: plugin/Settings.php:127
|
357 |
+
msgid "Require login"
|
358 |
+
msgstr "Require login"
|
359 |
+
|
360 |
+
#: plugin/Settings.php:128
|
361 |
+
msgid "Only allow review submissions from registered users."
|
362 |
+
msgstr "Only allow review submissions from registered users."
|
363 |
+
|
364 |
+
#: plugin/Settings.php:134
|
365 |
+
msgid "Notifications"
|
366 |
+
msgstr "Notifications"
|
367 |
+
|
368 |
+
#: plugin/Settings.php:137
|
369 |
+
msgid "Do not send review notifications"
|
370 |
+
msgstr "Do not send review notifications"
|
371 |
+
|
372 |
+
#: plugin/Settings.php:138
|
373 |
+
msgid "Send to administrator <code>%s</code>"
|
374 |
+
msgstr "Send to administrator <code>%s</code>"
|
375 |
+
|
376 |
+
#: plugin/Settings.php:139
|
377 |
+
msgid "Send to one or more email addresses"
|
378 |
+
msgstr "Send to one or more email addresses"
|
379 |
+
|
380 |
+
#: plugin/Settings.php:140
|
381 |
+
msgid "Send to <a href=\"https://slack.com/\">Slack</a>"
|
382 |
+
msgstr "Send to <a href=\"https://slack.com/\">Slack</a>"
|
383 |
+
|
384 |
+
#: plugin/Settings.php:147
|
385 |
+
msgid "Send notification emails to"
|
386 |
+
msgstr "Send notification emails to"
|
387 |
+
|
388 |
+
#: plugin/Settings.php:151
|
389 |
+
msgid "Separate multiple emails with a comma"
|
390 |
+
msgstr "Separate multiple emails with a comma"
|
391 |
+
|
392 |
+
#: plugin/Settings.php:157
|
393 |
+
msgid "Webhook URL"
|
394 |
+
msgstr "Webhook URL"
|
395 |
+
|
396 |
+
#: plugin/Settings.php:162
|
397 |
+
msgid ""
|
398 |
+
"To send notifications to Slack, <a href=\"%s\">create a new Incoming "
|
399 |
+
"WebHook</a> and then paste the provided Webhook URL in the field above."
|
400 |
+
msgstr ""
|
401 |
+
"To send notifications to Slack, <a href=\"%s\">create a new Incoming "
|
402 |
+
"WebHook</a> and then paste the provided Webhook URL in the field above."
|
403 |
+
|
404 |
+
#: plugin/Settings.php:170
|
405 |
+
msgid "Notification template"
|
406 |
+
msgstr "Notification template"
|
407 |
+
|
408 |
+
#: plugin/Settings.php:201
|
409 |
+
msgid "Form Labels"
|
410 |
+
msgstr "Form Labels"
|
411 |
+
|
412 |
+
#: plugin/Settings.php:202
|
413 |
+
msgid "Customize the label text for the review submission form fields."
|
414 |
+
msgstr "Customize the label text for the review submission form fields."
|
415 |
+
|
416 |
+
#: plugin/Settings.php:208
|
417 |
+
msgid "Rating label"
|
418 |
+
msgstr "Rating label"
|
419 |
+
|
420 |
+
#: plugin/Settings.php:209
|
421 |
+
msgid "Your overall rating"
|
422 |
+
msgstr "Your overall rating"
|
423 |
+
|
424 |
+
#: plugin/Settings.php:216
|
425 |
+
msgid "Title label"
|
426 |
+
msgstr "Title label"
|
427 |
+
|
428 |
+
#: plugin/Settings.php:217
|
429 |
+
msgid "Title of your review"
|
430 |
+
msgstr "Title of your review"
|
431 |
+
|
432 |
+
#: plugin/Settings.php:224
|
433 |
+
msgid "Content label"
|
434 |
+
msgstr "Content label"
|
435 |
+
|
436 |
+
#: plugin/Settings.php:225
|
437 |
+
msgid "Your review"
|
438 |
+
msgstr "Your review"
|
439 |
+
|
440 |
+
#: plugin/Settings.php:232
|
441 |
+
msgid "Reviewer label"
|
442 |
+
msgstr "Reviewer label"
|
443 |
+
|
444 |
+
#: plugin/Settings.php:233
|
445 |
+
msgid "Your name"
|
446 |
+
msgstr "Your name"
|
447 |
+
|
448 |
+
#: plugin/Settings.php:240
|
449 |
+
msgid "Email label"
|
450 |
+
msgstr "Email label"
|
451 |
+
|
452 |
+
#: plugin/Settings.php:241
|
453 |
+
msgid "Your email"
|
454 |
+
msgstr "Your email"
|
455 |
+
|
456 |
+
#: plugin/Settings.php:248
|
457 |
+
msgid "Terms label"
|
458 |
+
msgstr "Terms label"
|
459 |
+
|
460 |
+
#: plugin/Settings.php:249
|
461 |
+
msgid "This review is based on my own experience and is my genuine opinion."
|
462 |
+
msgstr "This review is based on my own experience and is my genuine opinion."
|
463 |
+
|
464 |
+
#: plugin/Settings.php:255
|
465 |
+
msgid "Form Placeholders"
|
466 |
+
msgstr "Form Placeholders"
|
467 |
+
|
468 |
+
#: plugin/Settings.php:256
|
469 |
+
msgid ""
|
470 |
+
"Customize the placeholder text for the review submission form fields. Use a "
|
471 |
+
"single space character to disable the placeholder text."
|
472 |
+
msgstr ""
|
473 |
+
"Customize the placeholder text for the review submission form fields. Use a "
|
474 |
+
"single space character to disable the placeholder text."
|
475 |
+
|
476 |
+
#: plugin/Settings.php:263
|
477 |
+
msgid "Title placeholder"
|
478 |
+
msgstr "Title placeholder"
|
479 |
+
|
480 |
+
#: plugin/Settings.php:264
|
481 |
+
msgid "Summarize your review or highlight an interesting detail"
|
482 |
+
msgstr "Summarize your review or highlight an interesting detail"
|
483 |
+
|
484 |
+
#: plugin/Settings.php:272
|
485 |
+
msgid "Content placeholder"
|
486 |
+
msgstr "Content placeholder"
|
487 |
+
|
488 |
+
#: plugin/Settings.php:273
|
489 |
+
msgid "Tell people your review"
|
490 |
+
msgstr "Tell people your review"
|
491 |
+
|
492 |
+
#: plugin/Settings.php:281
|
493 |
+
msgid "Reviewer placeholder"
|
494 |
+
msgstr "Reviewer placeholder"
|
495 |
+
|
496 |
+
#: plugin/Settings.php:282
|
497 |
+
msgid "Tell us your name"
|
498 |
+
msgstr "Tell us your name"
|
499 |
+
|
500 |
+
#: plugin/Settings.php:290
|
501 |
+
msgid "Email placeholder"
|
502 |
+
msgstr "Email placeholder"
|
503 |
+
|
504 |
+
#: plugin/Settings.php:291
|
505 |
+
msgid "Tell us your email"
|
506 |
+
msgstr "Tell us your email"
|
507 |
+
|
508 |
+
#: plugin/Shortcodes/SiteReviewsForm.php:28, plugin/Widgets/ReviewsForm.php:125
|
509 |
+
msgid "You must be <a href=\"%s\">logged in</a> to submit a review."
|
510 |
+
msgstr "You must be <a href=\"%s\">logged in</a> to submit a review."
|
511 |
+
|
512 |
+
#: plugin/Strings.php:18
|
513 |
+
msgid "Add New Review"
|
514 |
+
msgstr "Add New Review"
|
515 |
+
|
516 |
+
#: plugin/Strings.php:19, plugin/Widgets/RecentReviews.php:72
|
517 |
+
msgid "All Reviews"
|
518 |
+
msgstr "All Reviews"
|
519 |
+
|
520 |
+
#: plugin/Strings.php:20
|
521 |
+
msgid "Review Archives"
|
522 |
+
msgstr "Review Archives"
|
523 |
+
|
524 |
+
#: plugin/Strings.php:21
|
525 |
+
msgid "Edit Review"
|
526 |
+
msgstr "Edit Review"
|
527 |
+
|
528 |
+
#: plugin/Strings.php:22
|
529 |
+
msgid "Insert into review"
|
530 |
+
msgstr "Insert into review"
|
531 |
+
|
532 |
+
#: plugin/Strings.php:23
|
533 |
+
msgid "New Review"
|
534 |
+
msgstr "New Review"
|
535 |
+
|
536 |
+
#: plugin/Strings.php:24
|
537 |
+
msgid "No Reviews found"
|
538 |
+
msgstr "No Reviews found"
|
539 |
+
|
540 |
+
#: plugin/Strings.php:25
|
541 |
+
msgid "No Reviews found in Trash"
|
542 |
+
msgstr "No Reviews found in Trash"
|
543 |
+
|
544 |
+
#: plugin/Strings.php:26
|
545 |
+
msgid "Search Reviews"
|
546 |
+
msgstr "Search Reviews"
|
547 |
+
|
548 |
+
#: plugin/Strings.php:27
|
549 |
+
msgid "Uploaded to this review"
|
550 |
+
msgstr "Uploaded to this review"
|
551 |
+
|
552 |
+
#: plugin/Strings.php:35
|
553 |
+
msgid "Review has been approved and published."
|
554 |
+
msgstr "Review has been approved and published."
|
555 |
+
|
556 |
+
#: plugin/Strings.php:36
|
557 |
+
msgid "Review draft updated."
|
558 |
+
msgstr "Review draft updated."
|
559 |
+
|
560 |
+
#: plugin/Strings.php:37
|
561 |
+
msgid "Preview review"
|
562 |
+
msgstr "Preview review"
|
563 |
+
|
564 |
+
#: plugin/Strings.php:38
|
565 |
+
msgid "Review published."
|
566 |
+
msgstr "Review published."
|
567 |
+
|
568 |
+
#: plugin/Strings.php:39
|
569 |
+
msgid "Review restored to revision from %s."
|
570 |
+
msgstr "Review restored to revision from %s."
|
571 |
+
|
572 |
+
#: plugin/Strings.php:40
|
573 |
+
msgid "Review has been reverted to its original submission state."
|
574 |
+
msgstr "Review has been reverted to its original submission state."
|
575 |
+
|
576 |
+
#: plugin/Strings.php:41
|
577 |
+
msgid "Review saved."
|
578 |
+
msgstr "Review saved."
|
579 |
+
|
580 |
+
#: plugin/Strings.php:42
|
581 |
+
msgid "Review scheduled for: %s."
|
582 |
+
msgstr "Review scheduled for: %s."
|
583 |
+
|
584 |
+
#: plugin/Strings.php:43
|
585 |
+
msgid "Review submitted."
|
586 |
+
msgstr "Review submitted."
|
587 |
+
|
588 |
+
#: plugin/Strings.php:44
|
589 |
+
msgid "Review has been unapproved and is now pending."
|
590 |
+
msgstr "Review has been unapproved and is now pending."
|
591 |
+
|
592 |
+
#: plugin/Strings.php:45
|
593 |
+
msgid "Review updated."
|
594 |
+
msgstr "Review updated."
|
595 |
+
|
596 |
+
#: plugin/Strings.php:46
|
597 |
+
msgid "View review"
|
598 |
+
msgstr "View review"
|
599 |
+
|
600 |
+
#: plugin/Widgets/RecentReviews.php:58
|
601 |
+
msgid "Display only title"
|
602 |
+
msgstr "Display only title"
|
603 |
+
|
604 |
+
#: plugin/Widgets/RecentReviews.php:59
|
605 |
+
msgid "Display only excerpt"
|
606 |
+
msgstr "Display only excerpt"
|
607 |
+
|
608 |
+
#: plugin/Widgets/RecentReviews.php:60
|
609 |
+
msgid "Display title and excerpt"
|
610 |
+
msgstr "Display title and excerpt"
|
611 |
+
|
612 |
+
#: plugin/Widgets/RecentReviews.php:70
|
613 |
+
msgid "Which reviews would you like to display? "
|
614 |
+
msgstr "Which reviews would you like to display? "
|
615 |
+
|
616 |
+
#: plugin/Widgets/RecentReviews.php:79
|
617 |
+
msgid "What is the minimum rating to display? "
|
618 |
+
msgstr "What is the minimum rating to display? "
|
619 |
+
|
620 |
+
#: plugin/Widgets/RecentReviews.php:82
|
621 |
+
msgid "5 stars"
|
622 |
+
msgstr "5 stars"
|
623 |
+
|
624 |
+
#: plugin/Widgets/RecentReviews.php:83
|
625 |
+
msgid "4 stars"
|
626 |
+
msgstr "4 stars"
|
627 |
+
|
628 |
+
#: plugin/Widgets/RecentReviews.php:84
|
629 |
+
msgid "3 stars"
|
630 |
+
msgstr "3 stars"
|
631 |
+
|
632 |
+
#: plugin/Widgets/RecentReviews.php:85
|
633 |
+
msgid "2 stars"
|
634 |
+
msgstr "2 stars"
|
635 |
+
|
636 |
+
#: plugin/Widgets/RecentReviews.php:86
|
637 |
+
msgid "1 star"
|
638 |
+
msgstr "1 star"
|
639 |
+
|
640 |
+
#: plugin/Widgets/RecentReviews.php:93
|
641 |
+
msgid "How many reviews would you like to display? "
|
642 |
+
msgstr "How many reviews would you like to display? "
|
643 |
+
|
644 |
+
#: plugin/Widgets/RecentReviews.php:104
|
645 |
+
msgid "Show the name of the reviewer?"
|
646 |
+
msgstr "Show the name of the reviewer?"
|
647 |
+
|
648 |
+
#: plugin/Widgets/RecentReviews.php:105
|
649 |
+
msgid "Show the review date?"
|
650 |
+
msgstr "Show the review date?"
|
651 |
+
|
652 |
+
#: plugin/Widgets/RecentReviews.php:106
|
653 |
+
msgid "Show the review rating?"
|
654 |
+
msgstr "Show the review rating?"
|
655 |
+
|
656 |
+
#: plugin/Widgets/RecentReviews.php:113, plugin/Widgets/ReviewsForm.php:67
|
657 |
+
msgid "Enter any custom CSS classes here"
|
658 |
+
msgstr "Enter any custom CSS classes here"
|
659 |
+
|
660 |
+
#: plugin/Widgets/ReviewsForm.php:30
|
661 |
+
msgid "Your email address will not be published. Required fields are marked %s*%s"
|
662 |
+
msgstr "Your email address will not be published. Required fields are marked %s*%s"
|
663 |
+
|
664 |
+
#: plugin/Widgets/ReviewsForm.php:48
|
665 |
+
msgid "Description"
|
666 |
+
msgstr "Description"
|
667 |
+
|
668 |
+
#: plugin/Widgets/ReviewsForm.php:57
|
669 |
+
msgid "Hide the title field"
|
670 |
+
msgstr "Hide the title field"
|
671 |
+
|
672 |
+
#: plugin/Widgets/ReviewsForm.php:58
|
673 |
+
msgid "Hide the reviewer field"
|
674 |
+
msgstr "Hide the reviewer field"
|
675 |
+
|
676 |
+
#: plugin/Widgets/ReviewsForm.php:59
|
677 |
+
msgid "Hide the email field"
|
678 |
+
msgstr "Hide the email field"
|
679 |
+
|
680 |
+
#: plugin/Widgets/ReviewsForm.php:60
|
681 |
+
msgid "Hide the terms field"
|
682 |
+
msgstr "Hide the terms field"
|
683 |
+
|
684 |
+
#: views/edit/meta.php:27
|
685 |
+
msgid "Nothing to Revert"
|
686 |
+
msgstr "Nothing to Revert"
|
687 |
+
|
688 |
+
#: views/edit/meta.php:28
|
689 |
+
msgid "Revert Changes"
|
690 |
+
msgstr "Revert Changes"
|
691 |
+
|
692 |
+
#: views/edit/meta.php:54
|
693 |
+
msgid "Avatar"
|
694 |
+
msgstr "Avatar"
|
695 |
+
|
696 |
+
#: views/edit/pinned.php:21
|
697 |
+
msgid "Edit"
|
698 |
+
msgstr "Edit"
|
699 |
+
|
700 |
+
#: views/edit/pinned.php:22
|
701 |
+
msgid "Edit pinned status"
|
702 |
+
msgstr "Edit pinned status"
|
703 |
+
|
704 |
+
#: views/edit/pinned.php:30
|
705 |
+
msgid "Pin"
|
706 |
+
msgstr "Pin"
|
707 |
+
|
708 |
+
#: views/edit/pinned.php:31
|
709 |
+
msgid "Unpin"
|
710 |
+
msgstr "Unpin"
|
711 |
+
|
712 |
+
#: views/edit/pinned.php:34
|
713 |
+
msgid "OK"
|
714 |
+
msgstr "OK"
|
715 |
+
|
716 |
+
#: views/edit/pinned.php:35
|
717 |
+
msgid "Cancel"
|
718 |
+
msgstr "Cancel"
|
719 |
+
|
720 |
+
#: views/edit/review.php:4
|
721 |
+
msgid "no title"
|
722 |
+
msgstr "no title"
|
723 |
+
|
724 |
+
#: views/menu/addons/addons.php:3
|
725 |
+
msgid "The following Add-Ons extend the functionality of Site Reviews."
|
726 |
+
msgstr "The following Add-Ons extend the functionality of Site Reviews."
|
727 |
+
|
728 |
+
#: views/menu/addons/addons.php:12
|
729 |
+
msgid ""
|
730 |
+
"Sync your Tripadvisor business reviews with an optional minimum rating and "
|
731 |
+
"display them on your site."
|
732 |
+
msgstr ""
|
733 |
+
"Sync your Tripadvisor business reviews with an optional minimum rating and "
|
734 |
+
"display them on your site."
|
735 |
+
|
736 |
+
#: views/menu/addons/addons.php:19
|
737 |
+
msgid ""
|
738 |
+
"Sync your Yelp business reviews with an optional minimum rating and display "
|
739 |
+
"them on your site."
|
740 |
+
msgstr ""
|
741 |
+
"Sync your Yelp business reviews with an optional minimum rating and display "
|
742 |
+
"them on your site."
|
743 |
+
|
744 |
+
#: views/menu/help/system.php:9
|
745 |
+
msgid "Download System Info"
|
746 |
+
msgstr "Download System Info"
|
747 |
+
|
748 |
+
#: views/menu/help/system.php:14
|
749 |
+
msgid "Logging"
|
750 |
+
msgstr "Logging"
|
751 |
+
|
752 |
+
#: views/menu/help/system.php:15
|
753 |
+
msgid ""
|
754 |
+
"You can enable logging for debugging purposes. Log files can grow quickly, "
|
755 |
+
"so don't leave this on."
|
756 |
+
msgstr ""
|
757 |
+
"You can enable logging for debugging purposes. Log files can grow quickly, "
|
758 |
+
"so don't leave this on."
|
759 |
+
|
760 |
+
#: views/menu/help/system.php:26
|
761 |
+
msgid "Clear Log"
|
762 |
+
msgstr "Clear Log"
|
763 |
+
|
764 |
+
#: views/menu/help/system.php:32
|
765 |
+
msgid "Download Log"
|
766 |
+
msgstr "Download Log"
|
767 |
+
|
768 |
+
#: views/menu/help/system.php:44
|
769 |
+
msgid "Disable Logging"
|
770 |
+
msgstr "Disable Logging"
|
771 |
+
|
772 |
+
#: views/menu/help/system.php:48
|
773 |
+
msgid "Enable Logging"
|
774 |
+
msgstr "Enable Logging"
|
775 |
+
|
776 |
+
#: views/menu/status.php:7
|
777 |
+
msgid ""
|
778 |
+
"All dates and times shown here use the WordPress <a href='%s'>configured "
|
779 |
+
"timezone</a>."
|
780 |
+
msgstr ""
|
781 |
+
"All dates and times shown here use the WordPress <a href='%s'>configured "
|
782 |
+
"timezone</a>."
|
783 |
+
|
784 |
+
#: views/menu/status.php:16
|
785 |
+
msgid "Site"
|
786 |
+
msgstr "Site"
|
787 |
+
|
788 |
+
#: views/menu/status.php:18, views/menu/status.php:35
|
789 |
+
msgid "Last fetch"
|
790 |
+
msgstr "Last fetch"
|
791 |
+
|
792 |
+
#: views/menu/status.php:19, views/menu/status.php:38
|
793 |
+
msgid "Next scheduled fetch"
|
794 |
+
msgstr "Next scheduled fetch"
|
795 |
+
|
796 |
+
#: views/menu/status.php:36
|
797 |
+
msgid "No fetch has been completed"
|
798 |
+
msgstr "No fetch has been completed"
|
799 |
+
|
800 |
+
#: views/menu/status.php:39
|
801 |
+
msgid "Nothing currently scheduled"
|
802 |
+
msgstr "Nothing currently scheduled"
|
803 |
+
|
804 |
+
#: views/menu/status.php:59
|
805 |
+
msgid "Fetch reviews from"
|
806 |
+
msgstr "Fetch reviews from"
|
807 |
+
|
808 |
+
#: views/menu/status.php:66
|
809 |
+
msgid "Fetch status"
|
810 |
+
msgstr "Fetch status"
|
811 |
+
|
812 |
+
#: views/menu/status.php:67
|
813 |
+
msgid "Fetching reviews..."
|
814 |
+
msgstr "Fetching reviews..."
|
815 |
+
|
816 |
+
#: views/menu/status.php:79
|
817 |
+
msgid "Fetch Reviews"
|
818 |
+
msgstr "Fetch Reviews"
|
819 |
+
|
820 |
+
#: views/submit/index.php:17
|
821 |
+
msgid "Select a Rating"
|
822 |
+
msgstr "Select a Rating"
|
823 |
+
|
824 |
+
#: views/submit/index.php:18
|
825 |
+
msgid "Excellent"
|
826 |
+
msgstr "Excellent"
|
827 |
+
|
828 |
+
#: views/submit/index.php:19
|
829 |
+
msgid "Very Good"
|
830 |
+
msgstr "Very Good"
|
831 |
+
|
832 |
+
#: views/submit/index.php:20
|
833 |
+
msgid "Average"
|
834 |
+
msgstr "Average"
|
835 |
+
|
836 |
+
#: views/submit/index.php:21
|
837 |
+
msgid "Poor"
|
838 |
+
msgstr "Poor"
|
839 |
+
|
840 |
+
#: views/submit/index.php:22
|
841 |
+
msgid "Terrible"
|
842 |
+
msgstr "Terrible"
|
843 |
+
|
844 |
+
#: views/submit/index.php:121
|
845 |
+
msgid "Submit your review"
|
846 |
+
msgstr "Submit your review"
|
847 |
+
|
848 |
+
#: plugin/Strings.php:53
|
849 |
+
msgctxt ":attribute is a placeholder and should not be translated."
|
850 |
+
msgid "The :attribute must be accepted."
|
851 |
+
msgstr "The :attribute must be accepted."
|
852 |
+
|
853 |
+
#: plugin/Strings.php:56
|
854 |
+
msgctxt ":attribute is a placeholder and should not be translated."
|
855 |
+
msgid "The :attribute must be a valid email address."
|
856 |
+
msgstr "The :attribute must be a valid email address."
|
857 |
+
|
858 |
+
#: plugin/Strings.php:61
|
859 |
+
msgctxt ":attribute is a placeholder and should not be translated."
|
860 |
+
msgid "The :attribute format is invalid."
|
861 |
+
msgstr "The :attribute format is invalid."
|
862 |
+
|
863 |
+
#: plugin/Strings.php:62
|
864 |
+
msgctxt ":attribute is a placeholder and should not be translated."
|
865 |
+
msgid "The :attribute field is required."
|
866 |
+
msgstr "The :attribute field is required."
|
867 |
+
|
868 |
+
#: plugin/Strings.php:54
|
869 |
+
msgctxt ":attribute, :min, and :max are placeholders and should not be translated."
|
870 |
+
msgid "The :attribute must be between :min and :max."
|
871 |
+
msgstr "The :attribute must be between :min and :max."
|
872 |
+
|
873 |
+
#: plugin/Strings.php:55
|
874 |
+
msgctxt ":attribute, :min, and :max are placeholders and should not be translated."
|
875 |
+
msgid "The :attribute must be between :min and :max characters."
|
876 |
+
msgstr "The :attribute must be between :min and :max characters."
|
877 |
+
|
878 |
+
#: plugin/Strings.php:57
|
879 |
+
msgctxt ":attribute and :max are placeholders and should not be translated."
|
880 |
+
msgid "The :attribute may not be greater than :max."
|
881 |
+
msgstr "The :attribute may not be greater than :max."
|
882 |
+
|
883 |
+
#: plugin/Strings.php:58
|
884 |
+
msgctxt ":attribute and :max are placeholders and should not be translated."
|
885 |
+
msgid "The :attribute may not be greater than :max characters."
|
886 |
+
msgstr "The :attribute may not be greater than :max characters."
|
887 |
+
|
888 |
+
#: plugin/Strings.php:59
|
889 |
+
msgctxt ":attribute and :min are placeholders and should not be translated."
|
890 |
+
msgid "The :attribute must be at least :min."
|
891 |
+
msgstr "The :attribute must be at least :min."
|
892 |
+
|
893 |
+
#: plugin/Strings.php:60
|
894 |
+
msgctxt ":attribute and :min are placeholders and should not be translated."
|
895 |
+
msgid "The :attribute must be at least :min characters."
|
896 |
+
msgstr "The :attribute must be at least :min characters."
|
languages/site-reviews.pot
ADDED
@@ -0,0 +1,850 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Copyright (C) 2017 site-reviews
|
2 |
+
# This file is distributed under the same license as the site-reviews package.
|
3 |
+
msgid ""
|
4 |
+
msgstr ""
|
5 |
+
"Project-Id-Version: site-reviews\n"
|
6 |
+
"Report-Msgid-Bugs-To: https://github.com/geminilabs/site-reviews/issues/new\n"
|
7 |
+
"MIME-Version: 1.0\n"
|
8 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
9 |
+
"Content-Transfer-Encoding: 8bit\n"
|
10 |
+
"PO-Revision-Date: 2017-MO-DA HO:MI+ZONE\n"
|
11 |
+
"Last-Translator: Paul Ryley <paul@geminilabs.io>\n"
|
12 |
+
"Language-Team: Gemini Labs <support@geminilabs.io>\n"
|
13 |
+
"X-Poedit-Basepath: ..\n"
|
14 |
+
"X-Poedit-SourceCharset: UTF-8\n"
|
15 |
+
"X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
|
16 |
+
"X-Poedit-SearchPath-0: .\n"
|
17 |
+
"X-Poedit-SearchPathExcluded-0: *.js\n"
|
18 |
+
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
19 |
+
|
20 |
+
#: activate.php:43
|
21 |
+
msgid "The Site Reviews plugin was deactivated."
|
22 |
+
msgstr ""
|
23 |
+
|
24 |
+
#: activate.php:48
|
25 |
+
msgid "Sorry, this plugin requires PHP version 5.4 or greater in order to work properly."
|
26 |
+
msgstr ""
|
27 |
+
|
28 |
+
#: activate.php:49
|
29 |
+
msgid "Please contact your hosting provider or server administrator to upgrade the version of PHP on your server (your server is running PHP version %s), or try to find an alternative plugin."
|
30 |
+
msgstr ""
|
31 |
+
|
32 |
+
#: activate.php:55
|
33 |
+
msgid "Sorry, this plugin requires WordPress version 4.0.0 or greater in order to work properly."
|
34 |
+
msgstr ""
|
35 |
+
|
36 |
+
#: activate.php:56
|
37 |
+
msgid "Update WordPress"
|
38 |
+
msgstr ""
|
39 |
+
|
40 |
+
#: plugin/Commands/RegisterPostType.php:20, plugin/Widgets/RecentReviews.php:48, plugin/Widgets/ReviewsForm.php:40
|
41 |
+
msgid "Title"
|
42 |
+
msgstr ""
|
43 |
+
|
44 |
+
#: plugin/Commands/RegisterPostType.php:21, views/edit/meta.php:46
|
45 |
+
msgid "Date"
|
46 |
+
msgstr ""
|
47 |
+
|
48 |
+
#: plugin/Controllers/AjaxController.php:26
|
49 |
+
msgid "Log is empty"
|
50 |
+
msgstr ""
|
51 |
+
|
52 |
+
#: plugin/Controllers/MainController.php:51
|
53 |
+
msgid "Log was cleared."
|
54 |
+
msgstr ""
|
55 |
+
|
56 |
+
#: plugin/Controllers/MainController.php:87, plugin/Controllers/MainController.php:293, plugin/Controllers/MainController.php:478
|
57 |
+
msgid "Settings"
|
58 |
+
msgstr ""
|
59 |
+
|
60 |
+
#: plugin/Controllers/MainController.php:106
|
61 |
+
msgid "%s Review"
|
62 |
+
msgid_plural "%s Reviews"
|
63 |
+
msgstr[0] ""
|
64 |
+
msgstr[1] ""
|
65 |
+
|
66 |
+
#: plugin/Controllers/MainController.php:156
|
67 |
+
msgid "Details"
|
68 |
+
msgstr ""
|
69 |
+
|
70 |
+
#: plugin/Controllers/MainController.php:170
|
71 |
+
msgid "Pin Your Reviews"
|
72 |
+
msgstr ""
|
73 |
+
|
74 |
+
#: plugin/Controllers/MainController.php:171
|
75 |
+
msgid "You can pin exceptional reviews so that they are always shown first in your widgets and shortcodes."
|
76 |
+
msgstr ""
|
77 |
+
|
78 |
+
#: plugin/Controllers/MainController.php:193
|
79 |
+
msgid "Review"
|
80 |
+
msgstr ""
|
81 |
+
|
82 |
+
#: plugin/Controllers/MainController.php:194, views/menu/status.php:17
|
83 |
+
msgid "Reviews"
|
84 |
+
msgstr ""
|
85 |
+
|
86 |
+
#: plugin/Controllers/MainController.php:195
|
87 |
+
msgid "Site Reviews"
|
88 |
+
msgstr ""
|
89 |
+
|
90 |
+
#: plugin/Controllers/MainController.php:203, views/edit/meta.php:50
|
91 |
+
msgid "Reviewer"
|
92 |
+
msgstr ""
|
93 |
+
|
94 |
+
#: plugin/Controllers/MainController.php:204, views/edit/meta.php:42
|
95 |
+
msgid "Type"
|
96 |
+
msgstr ""
|
97 |
+
|
98 |
+
#: plugin/Controllers/MainController.php:205, views/edit/meta.php:38
|
99 |
+
msgid "Rating"
|
100 |
+
msgstr ""
|
101 |
+
|
102 |
+
#: plugin/Controllers/MainController.php:206, views/edit/pinned.php:17
|
103 |
+
msgid "Pinned"
|
104 |
+
msgstr ""
|
105 |
+
|
106 |
+
#: plugin/Controllers/MainController.php:229
|
107 |
+
msgid "Approve this review"
|
108 |
+
msgstr ""
|
109 |
+
|
110 |
+
#: plugin/Controllers/MainController.php:231, plugin/Handlers/SubmitReview.php:95
|
111 |
+
msgid "Approve"
|
112 |
+
msgstr ""
|
113 |
+
|
114 |
+
#: plugin/Controllers/MainController.php:234
|
115 |
+
msgid "Unapprove this review"
|
116 |
+
msgstr ""
|
117 |
+
|
118 |
+
#: plugin/Controllers/MainController.php:236
|
119 |
+
msgid "Unapprove"
|
120 |
+
msgstr ""
|
121 |
+
|
122 |
+
#: plugin/Controllers/MainController.php:294
|
123 |
+
msgid "Get Help"
|
124 |
+
msgstr ""
|
125 |
+
|
126 |
+
#: plugin/Controllers/MainController.php:295, plugin/Controllers/MainController.php:353
|
127 |
+
msgid "Add-Ons"
|
128 |
+
msgstr ""
|
129 |
+
|
130 |
+
#: plugin/Controllers/MainController.php:331
|
131 |
+
msgid "Submit a Site Review"
|
132 |
+
msgstr ""
|
133 |
+
|
134 |
+
#: plugin/Controllers/MainController.php:332
|
135 |
+
msgid "A \"submit a review\" form for your site."
|
136 |
+
msgstr ""
|
137 |
+
|
138 |
+
#: plugin/Controllers/MainController.php:336
|
139 |
+
msgid "Recent Site Reviews"
|
140 |
+
msgstr ""
|
141 |
+
|
142 |
+
#: plugin/Controllers/MainController.php:337
|
143 |
+
msgid "Your site’s most recent Local Reviews."
|
144 |
+
msgstr ""
|
145 |
+
|
146 |
+
#: plugin/Controllers/MainController.php:373
|
147 |
+
msgid "Documentation"
|
148 |
+
msgstr ""
|
149 |
+
|
150 |
+
#: plugin/Controllers/MainController.php:376
|
151 |
+
msgid "System Info"
|
152 |
+
msgstr ""
|
153 |
+
|
154 |
+
#: plugin/Controllers/MainController.php:459
|
155 |
+
msgid "%s reviews are read-only."
|
156 |
+
msgstr ""
|
157 |
+
|
158 |
+
#: plugin/Controllers/MainController.php:481
|
159 |
+
msgid "Licenses"
|
160 |
+
msgstr ""
|
161 |
+
|
162 |
+
#: plugin/Controllers/MainController.php:497
|
163 |
+
msgid "Logging enabled."
|
164 |
+
msgstr ""
|
165 |
+
|
166 |
+
#: plugin/Controllers/MainController.php:498
|
167 |
+
msgid "Logging disabled."
|
168 |
+
msgstr ""
|
169 |
+
|
170 |
+
#: plugin/Controllers/MainController.php:514
|
171 |
+
msgid "Settings updated."
|
172 |
+
msgstr ""
|
173 |
+
|
174 |
+
#: plugin/Controllers/ReviewController.php:48
|
175 |
+
msgid "Anonymous"
|
176 |
+
msgstr ""
|
177 |
+
|
178 |
+
#: plugin/Controllers/ReviewController.php:56
|
179 |
+
msgid "No Title"
|
180 |
+
msgstr ""
|
181 |
+
|
182 |
+
#: plugin/Controllers/ReviewController.php:60
|
183 |
+
msgid "Please fix the submission errors."
|
184 |
+
msgstr ""
|
185 |
+
|
186 |
+
#: plugin/Handlers/RegisterPostType.php:298
|
187 |
+
msgid "%s review updated."
|
188 |
+
msgid_plural "%s posts updated."
|
189 |
+
msgstr[0] ""
|
190 |
+
msgstr[1] ""
|
191 |
+
|
192 |
+
#: plugin/Handlers/RegisterPostType.php:299
|
193 |
+
msgid "%s review not updated, somebody is editing it."
|
194 |
+
msgid_plural "%s reviews not updated, somebody is editing them."
|
195 |
+
msgstr[0] ""
|
196 |
+
msgstr[1] ""
|
197 |
+
|
198 |
+
#: plugin/Handlers/RegisterPostType.php:300
|
199 |
+
msgid "%s review permanently deleted."
|
200 |
+
msgid_plural "%s reviews permanently deleted."
|
201 |
+
msgstr[0] ""
|
202 |
+
msgstr[1] ""
|
203 |
+
|
204 |
+
#: plugin/Handlers/RegisterPostType.php:301
|
205 |
+
msgid "%s review moved to the Trash."
|
206 |
+
msgid_plural "%s reviews moved to the Trash."
|
207 |
+
msgstr[0] ""
|
208 |
+
msgstr[1] ""
|
209 |
+
|
210 |
+
#: plugin/Handlers/RegisterPostType.php:302
|
211 |
+
msgid "%s review restored from the Trash."
|
212 |
+
msgid_plural "%s reviews restored from the Trash."
|
213 |
+
msgstr[0] ""
|
214 |
+
msgstr[1] ""
|
215 |
+
|
216 |
+
#: plugin/Handlers/RegisterPostType.php:593
|
217 |
+
msgid "%s star"
|
218 |
+
msgid_plural "%s stars"
|
219 |
+
msgstr[0] ""
|
220 |
+
msgstr[1] ""
|
221 |
+
|
222 |
+
#: plugin/Handlers/RegisterPostType.php:597
|
223 |
+
msgid "All ratings"
|
224 |
+
msgstr ""
|
225 |
+
|
226 |
+
#: plugin/Handlers/RegisterPostType.php:615
|
227 |
+
msgid "All types"
|
228 |
+
msgstr ""
|
229 |
+
|
230 |
+
#: plugin/Handlers/RegisterPostType.php:618, views/edit/meta.php:7
|
231 |
+
msgid "Local Review"
|
232 |
+
msgstr ""
|
233 |
+
|
234 |
+
#: plugin/Handlers/SubmitReview.php:63
|
235 |
+
msgid "Your review has been submitted!"
|
236 |
+
msgstr ""
|
237 |
+
|
238 |
+
#: plugin/Handlers/SubmitReview.php:96
|
239 |
+
msgid "Discard"
|
240 |
+
msgstr ""
|
241 |
+
|
242 |
+
#: plugin/Handlers/SubmitReview.php:144
|
243 |
+
msgid "New %s-Star Review"
|
244 |
+
msgstr ""
|
245 |
+
|
246 |
+
#: plugin/Handlers/SubmitReview.php:197, plugin/Strings.php:28
|
247 |
+
msgid "View Review"
|
248 |
+
msgstr ""
|
249 |
+
|
250 |
+
#: plugin/Handlers/TogglePinned.php:40
|
251 |
+
msgid "The review is pinned."
|
252 |
+
msgstr ""
|
253 |
+
|
254 |
+
#: plugin/Handlers/TogglePinned.php:41
|
255 |
+
msgid "The review is unpinned."
|
256 |
+
msgstr ""
|
257 |
+
|
258 |
+
#: plugin/Html/Fields/Colorpicker.php:40
|
259 |
+
msgid "Hex Value"
|
260 |
+
msgstr ""
|
261 |
+
|
262 |
+
#: plugin/Html/Fields/Progress.php:37
|
263 |
+
msgid "Please wait..."
|
264 |
+
msgstr ""
|
265 |
+
|
266 |
+
#: plugin/Html/Fields/Progress.php:38
|
267 |
+
msgid "Inactive"
|
268 |
+
msgstr ""
|
269 |
+
|
270 |
+
#: plugin/Html/Fields/Yesno.php:25, views/edit/pinned.php:13
|
271 |
+
msgid "No"
|
272 |
+
msgstr ""
|
273 |
+
|
274 |
+
#: plugin/Html/Fields/Yesno.php:26, views/edit/pinned.php:14
|
275 |
+
msgid "Yes"
|
276 |
+
msgstr ""
|
277 |
+
|
278 |
+
#: plugin/Html/Form.php:119
|
279 |
+
msgid "Submit"
|
280 |
+
msgstr ""
|
281 |
+
|
282 |
+
#: plugin/Html/Form.php:255
|
283 |
+
msgid "Save Changes"
|
284 |
+
msgstr ""
|
285 |
+
|
286 |
+
#: plugin/Html/Partials/Addon.php:40
|
287 |
+
msgid "More Info"
|
288 |
+
msgstr ""
|
289 |
+
|
290 |
+
#: plugin/Html/Partials/Reviews.php:77
|
291 |
+
msgid "No reviews were found."
|
292 |
+
msgstr ""
|
293 |
+
|
294 |
+
#: plugin/Html/Partials/Reviews.php:133
|
295 |
+
msgid "<span class=\"meta-nav\">←</span> Previous"
|
296 |
+
msgstr ""
|
297 |
+
|
298 |
+
#: plugin/Html/Partials/Reviews.php:139
|
299 |
+
msgid "Next <span class=\"meta-nav\">→</span>"
|
300 |
+
msgstr ""
|
301 |
+
|
302 |
+
#: plugin/Html/Partials/Reviews.php:145
|
303 |
+
msgid "Page"
|
304 |
+
msgstr ""
|
305 |
+
|
306 |
+
#: plugin/Html/Partials/Reviews.php:148
|
307 |
+
msgid "Next →"
|
308 |
+
msgstr ""
|
309 |
+
|
310 |
+
#: plugin/Html/Partials/Reviews.php:149
|
311 |
+
msgid "← Previous"
|
312 |
+
msgstr ""
|
313 |
+
|
314 |
+
#: plugin/Html/Partials/Reviews.php:199
|
315 |
+
msgid "Site Reviews navigation"
|
316 |
+
msgstr ""
|
317 |
+
|
318 |
+
#: plugin/Html/Partials/Reviews.php:260
|
319 |
+
msgid "read more"
|
320 |
+
msgstr ""
|
321 |
+
|
322 |
+
#: plugin/Settings.php:113, plugin/Settings.php:196
|
323 |
+
msgid "Save Settings"
|
324 |
+
msgstr ""
|
325 |
+
|
326 |
+
#: plugin/Settings.php:119
|
327 |
+
msgid "Require approval"
|
328 |
+
msgstr ""
|
329 |
+
|
330 |
+
#: plugin/Settings.php:121
|
331 |
+
msgid "Set the status of new review submissions to pending."
|
332 |
+
msgstr ""
|
333 |
+
|
334 |
+
#: plugin/Settings.php:127
|
335 |
+
msgid "Require login"
|
336 |
+
msgstr ""
|
337 |
+
|
338 |
+
#: plugin/Settings.php:128
|
339 |
+
msgid "Only allow review submissions from registered users."
|
340 |
+
msgstr ""
|
341 |
+
|
342 |
+
#: plugin/Settings.php:134
|
343 |
+
msgid "Notifications"
|
344 |
+
msgstr ""
|
345 |
+
|
346 |
+
#: plugin/Settings.php:137
|
347 |
+
msgid "Do not send review notifications"
|
348 |
+
msgstr ""
|
349 |
+
|
350 |
+
#: plugin/Settings.php:138
|
351 |
+
msgid "Send to administrator <code>%s</code>"
|
352 |
+
msgstr ""
|
353 |
+
|
354 |
+
#: plugin/Settings.php:139
|
355 |
+
msgid "Send to one or more email addresses"
|
356 |
+
msgstr ""
|
357 |
+
|
358 |
+
#: plugin/Settings.php:140
|
359 |
+
msgid "Send to <a href=\"https://slack.com/\">Slack</a>"
|
360 |
+
msgstr ""
|
361 |
+
|
362 |
+
#: plugin/Settings.php:147
|
363 |
+
msgid "Send notification emails to"
|
364 |
+
msgstr ""
|
365 |
+
|
366 |
+
#: plugin/Settings.php:151
|
367 |
+
msgid "Separate multiple emails with a comma"
|
368 |
+
msgstr ""
|
369 |
+
|
370 |
+
#: plugin/Settings.php:157
|
371 |
+
msgid "Webhook URL"
|
372 |
+
msgstr ""
|
373 |
+
|
374 |
+
#: plugin/Settings.php:162
|
375 |
+
msgid "To send notifications to Slack, <a href=\"%s\">create a new Incoming WebHook</a> and then paste the provided Webhook URL in the field above."
|
376 |
+
msgstr ""
|
377 |
+
|
378 |
+
#: plugin/Settings.php:170
|
379 |
+
msgid "Notification template"
|
380 |
+
msgstr ""
|
381 |
+
|
382 |
+
#: plugin/Settings.php:201
|
383 |
+
msgid "Form Labels"
|
384 |
+
msgstr ""
|
385 |
+
|
386 |
+
#: plugin/Settings.php:202
|
387 |
+
msgid "Customize the label text for the review submission form fields."
|
388 |
+
msgstr ""
|
389 |
+
|
390 |
+
#: plugin/Settings.php:208
|
391 |
+
msgid "Rating label"
|
392 |
+
msgstr ""
|
393 |
+
|
394 |
+
#: plugin/Settings.php:209
|
395 |
+
msgid "Your overall rating"
|
396 |
+
msgstr ""
|
397 |
+
|
398 |
+
#: plugin/Settings.php:216
|
399 |
+
msgid "Title label"
|
400 |
+
msgstr ""
|
401 |
+
|
402 |
+
#: plugin/Settings.php:217
|
403 |
+
msgid "Title of your review"
|
404 |
+
msgstr ""
|
405 |
+
|
406 |
+
#: plugin/Settings.php:224
|
407 |
+
msgid "Content label"
|
408 |
+
msgstr ""
|
409 |
+
|
410 |
+
#: plugin/Settings.php:225
|
411 |
+
msgid "Your review"
|
412 |
+
msgstr ""
|
413 |
+
|
414 |
+
#: plugin/Settings.php:232
|
415 |
+
msgid "Reviewer label"
|
416 |
+
msgstr ""
|
417 |
+
|
418 |
+
#: plugin/Settings.php:233
|
419 |
+
msgid "Your name"
|
420 |
+
msgstr ""
|
421 |
+
|
422 |
+
#: plugin/Settings.php:240
|
423 |
+
msgid "Email label"
|
424 |
+
msgstr ""
|
425 |
+
|
426 |
+
#: plugin/Settings.php:241
|
427 |
+
msgid "Your email"
|
428 |
+
msgstr ""
|
429 |
+
|
430 |
+
#: plugin/Settings.php:248
|
431 |
+
msgid "Terms label"
|
432 |
+
msgstr ""
|
433 |
+
|
434 |
+
#: plugin/Settings.php:249
|
435 |
+
msgid "This review is based on my own experience and is my genuine opinion."
|
436 |
+
msgstr ""
|
437 |
+
|
438 |
+
#: plugin/Settings.php:255
|
439 |
+
msgid "Form Placeholders"
|
440 |
+
msgstr ""
|
441 |
+
|
442 |
+
#: plugin/Settings.php:256
|
443 |
+
msgid "Customize the placeholder text for the review submission form fields. Use a single space character to disable the placeholder text."
|
444 |
+
msgstr ""
|
445 |
+
|
446 |
+
#: plugin/Settings.php:263
|
447 |
+
msgid "Title placeholder"
|
448 |
+
msgstr ""
|
449 |
+
|
450 |
+
#: plugin/Settings.php:264
|
451 |
+
msgid "Summarize your review or highlight an interesting detail"
|
452 |
+
msgstr ""
|
453 |
+
|
454 |
+
#: plugin/Settings.php:272
|
455 |
+
msgid "Content placeholder"
|
456 |
+
msgstr ""
|
457 |
+
|
458 |
+
#: plugin/Settings.php:273
|
459 |
+
msgid "Tell people your review"
|
460 |
+
msgstr ""
|
461 |
+
|
462 |
+
#: plugin/Settings.php:281
|
463 |
+
msgid "Reviewer placeholder"
|
464 |
+
msgstr ""
|
465 |
+
|
466 |
+
#: plugin/Settings.php:282
|
467 |
+
msgid "Tell us your name"
|
468 |
+
msgstr ""
|
469 |
+
|
470 |
+
#: plugin/Settings.php:290
|
471 |
+
msgid "Email placeholder"
|
472 |
+
msgstr ""
|
473 |
+
|
474 |
+
#: plugin/Settings.php:291
|
475 |
+
msgid "Tell us your email"
|
476 |
+
msgstr ""
|
477 |
+
|
478 |
+
#: plugin/Shortcodes/SiteReviewsForm.php:28, plugin/Widgets/ReviewsForm.php:125
|
479 |
+
msgid "You must be <a href=\"%s\">logged in</a> to submit a review."
|
480 |
+
msgstr ""
|
481 |
+
|
482 |
+
#: plugin/Strings.php:18
|
483 |
+
msgid "Add New Review"
|
484 |
+
msgstr ""
|
485 |
+
|
486 |
+
#: plugin/Strings.php:19, plugin/Widgets/RecentReviews.php:72
|
487 |
+
msgid "All Reviews"
|
488 |
+
msgstr ""
|
489 |
+
|
490 |
+
#: plugin/Strings.php:20
|
491 |
+
msgid "Review Archives"
|
492 |
+
msgstr ""
|
493 |
+
|
494 |
+
#: plugin/Strings.php:21
|
495 |
+
msgid "Edit Review"
|
496 |
+
msgstr ""
|
497 |
+
|
498 |
+
#: plugin/Strings.php:22
|
499 |
+
msgid "Insert into review"
|
500 |
+
msgstr ""
|
501 |
+
|
502 |
+
#: plugin/Strings.php:23
|
503 |
+
msgid "New Review"
|
504 |
+
msgstr ""
|
505 |
+
|
506 |
+
#: plugin/Strings.php:24
|
507 |
+
msgid "No Reviews found"
|
508 |
+
msgstr ""
|
509 |
+
|
510 |
+
#: plugin/Strings.php:25
|
511 |
+
msgid "No Reviews found in Trash"
|
512 |
+
msgstr ""
|
513 |
+
|
514 |
+
#: plugin/Strings.php:26
|
515 |
+
msgid "Search Reviews"
|
516 |
+
msgstr ""
|
517 |
+
|
518 |
+
#: plugin/Strings.php:27
|
519 |
+
msgid "Uploaded to this review"
|
520 |
+
msgstr ""
|
521 |
+
|
522 |
+
#: plugin/Strings.php:35
|
523 |
+
msgid "Review has been approved and published."
|
524 |
+
msgstr ""
|
525 |
+
|
526 |
+
#: plugin/Strings.php:36
|
527 |
+
msgid "Review draft updated."
|
528 |
+
msgstr ""
|
529 |
+
|
530 |
+
#: plugin/Strings.php:37
|
531 |
+
msgid "Preview review"
|
532 |
+
msgstr ""
|
533 |
+
|
534 |
+
#: plugin/Strings.php:38
|
535 |
+
msgid "Review published."
|
536 |
+
msgstr ""
|
537 |
+
|
538 |
+
#: plugin/Strings.php:39
|
539 |
+
msgid "Review restored to revision from %s."
|
540 |
+
msgstr ""
|
541 |
+
|
542 |
+
#: plugin/Strings.php:40
|
543 |
+
msgid "Review has been reverted to its original submission state."
|
544 |
+
msgstr ""
|
545 |
+
|
546 |
+
#: plugin/Strings.php:41
|
547 |
+
msgid "Review saved."
|
548 |
+
msgstr ""
|
549 |
+
|
550 |
+
#: plugin/Strings.php:42
|
551 |
+
msgid "Review scheduled for: %s."
|
552 |
+
msgstr ""
|
553 |
+
|
554 |
+
#: plugin/Strings.php:43
|
555 |
+
msgid "Review submitted."
|
556 |
+
msgstr ""
|
557 |
+
|
558 |
+
#: plugin/Strings.php:44
|
559 |
+
msgid "Review has been unapproved and is now pending."
|
560 |
+
msgstr ""
|
561 |
+
|
562 |
+
#: plugin/Strings.php:45
|
563 |
+
msgid "Review updated."
|
564 |
+
msgstr ""
|
565 |
+
|
566 |
+
#: plugin/Strings.php:46
|
567 |
+
msgid "View review"
|
568 |
+
msgstr ""
|
569 |
+
|
570 |
+
#: plugin/Strings.php:53
|
571 |
+
msgctxt ":attribute is a placeholder and should not be translated."
|
572 |
+
msgid "The :attribute must be accepted."
|
573 |
+
msgstr ""
|
574 |
+
|
575 |
+
#: plugin/Strings.php:54
|
576 |
+
msgctxt ":attribute, :min, and :max are placeholders and should not be translated."
|
577 |
+
msgid "The :attribute must be between :min and :max."
|
578 |
+
msgstr ""
|
579 |
+
|
580 |
+
#: plugin/Strings.php:55
|
581 |
+
msgctxt ":attribute, :min, and :max are placeholders and should not be translated."
|
582 |
+
msgid "The :attribute must be between :min and :max characters."
|
583 |
+
msgstr ""
|
584 |
+
|
585 |
+
#: plugin/Strings.php:56
|
586 |
+
msgctxt ":attribute is a placeholder and should not be translated."
|
587 |
+
msgid "The :attribute must be a valid email address."
|
588 |
+
msgstr ""
|
589 |
+
|
590 |
+
#: plugin/Strings.php:57
|
591 |
+
msgctxt ":attribute and :max are placeholders and should not be translated."
|
592 |
+
msgid "The :attribute may not be greater than :max."
|
593 |
+
msgstr ""
|
594 |
+
|
595 |
+
#: plugin/Strings.php:58
|
596 |
+
msgctxt ":attribute and :max are placeholders and should not be translated."
|
597 |
+
msgid "The :attribute may not be greater than :max characters."
|
598 |
+
msgstr ""
|
599 |
+
|
600 |
+
#: plugin/Strings.php:59
|
601 |
+
msgctxt ":attribute and :min are placeholders and should not be translated."
|
602 |
+
msgid "The :attribute must be at least :min."
|
603 |
+
msgstr ""
|
604 |
+
|
605 |
+
#: plugin/Strings.php:60
|
606 |
+
msgctxt ":attribute and :min are placeholders and should not be translated."
|
607 |
+
msgid "The :attribute must be at least :min characters."
|
608 |
+
msgstr ""
|
609 |
+
|
610 |
+
#: plugin/Strings.php:61
|
611 |
+
msgctxt ":attribute is a placeholder and should not be translated."
|
612 |
+
msgid "The :attribute format is invalid."
|
613 |
+
msgstr ""
|
614 |
+
|
615 |
+
#: plugin/Strings.php:62
|
616 |
+
msgctxt ":attribute is a placeholder and should not be translated."
|
617 |
+
msgid "The :attribute field is required."
|
618 |
+
msgstr ""
|
619 |
+
|
620 |
+
#: plugin/Widgets/RecentReviews.php:58
|
621 |
+
msgid "Display only title"
|
622 |
+
msgstr ""
|
623 |
+
|
624 |
+
#: plugin/Widgets/RecentReviews.php:59
|
625 |
+
msgid "Display only excerpt"
|
626 |
+
msgstr ""
|
627 |
+
|
628 |
+
#: plugin/Widgets/RecentReviews.php:60
|
629 |
+
msgid "Display title and excerpt"
|
630 |
+
msgstr ""
|
631 |
+
|
632 |
+
#: plugin/Widgets/RecentReviews.php:70
|
633 |
+
msgid "Which reviews would you like to display? "
|
634 |
+
msgstr ""
|
635 |
+
|
636 |
+
#: plugin/Widgets/RecentReviews.php:79
|
637 |
+
msgid "What is the minimum rating to display? "
|
638 |
+
msgstr ""
|
639 |
+
|
640 |
+
#: plugin/Widgets/RecentReviews.php:82
|
641 |
+
msgid "5 stars"
|
642 |
+
msgstr ""
|
643 |
+
|
644 |
+
#: plugin/Widgets/RecentReviews.php:83
|
645 |
+
msgid "4 stars"
|
646 |
+
msgstr ""
|
647 |
+
|
648 |
+
#: plugin/Widgets/RecentReviews.php:84
|
649 |
+
msgid "3 stars"
|
650 |
+
msgstr ""
|
651 |
+
|
652 |
+
#: plugin/Widgets/RecentReviews.php:85
|
653 |
+
msgid "2 stars"
|
654 |
+
msgstr ""
|
655 |
+
|
656 |
+
#: plugin/Widgets/RecentReviews.php:86
|
657 |
+
msgid "1 star"
|
658 |
+
msgstr ""
|
659 |
+
|
660 |
+
#: plugin/Widgets/RecentReviews.php:93
|
661 |
+
msgid "How many reviews would you like to display? "
|
662 |
+
msgstr ""
|
663 |
+
|
664 |
+
#: plugin/Widgets/RecentReviews.php:104
|
665 |
+
msgid "Show the name of the reviewer?"
|
666 |
+
msgstr ""
|
667 |
+
|
668 |
+
#: plugin/Widgets/RecentReviews.php:105
|
669 |
+
msgid "Show the review date?"
|
670 |
+
msgstr ""
|
671 |
+
|
672 |
+
#: plugin/Widgets/RecentReviews.php:106
|
673 |
+
msgid "Show the review rating?"
|
674 |
+
msgstr ""
|
675 |
+
|
676 |
+
#: plugin/Widgets/RecentReviews.php:113, plugin/Widgets/ReviewsForm.php:67
|
677 |
+
msgid "Enter any custom CSS classes here"
|
678 |
+
msgstr ""
|
679 |
+
|
680 |
+
#: plugin/Widgets/ReviewsForm.php:30
|
681 |
+
msgid "Your email address will not be published. Required fields are marked %s*%s"
|
682 |
+
msgstr ""
|
683 |
+
|
684 |
+
#: plugin/Widgets/ReviewsForm.php:48
|
685 |
+
msgid "Description"
|
686 |
+
msgstr ""
|
687 |
+
|
688 |
+
#: plugin/Widgets/ReviewsForm.php:57
|
689 |
+
msgid "Hide the title field"
|
690 |
+
msgstr ""
|
691 |
+
|
692 |
+
#: plugin/Widgets/ReviewsForm.php:58
|
693 |
+
msgid "Hide the reviewer field"
|
694 |
+
msgstr ""
|
695 |
+
|
696 |
+
#: plugin/Widgets/ReviewsForm.php:59
|
697 |
+
msgid "Hide the email field"
|
698 |
+
msgstr ""
|
699 |
+
|
700 |
+
#: plugin/Widgets/ReviewsForm.php:60
|
701 |
+
msgid "Hide the terms field"
|
702 |
+
msgstr ""
|
703 |
+
|
704 |
+
#: views/edit/meta.php:27
|
705 |
+
msgid "Nothing to Revert"
|
706 |
+
msgstr ""
|
707 |
+
|
708 |
+
#: views/edit/meta.php:28
|
709 |
+
msgid "Revert Changes"
|
710 |
+
msgstr ""
|
711 |
+
|
712 |
+
#: views/edit/meta.php:54
|
713 |
+
msgid "Avatar"
|
714 |
+
msgstr ""
|
715 |
+
|
716 |
+
#: views/edit/pinned.php:21
|
717 |
+
msgid "Edit"
|
718 |
+
msgstr ""
|
719 |
+
|
720 |
+
#: views/edit/pinned.php:22
|
721 |
+
msgid "Edit pinned status"
|
722 |
+
msgstr ""
|
723 |
+
|
724 |
+
#: views/edit/pinned.php:30
|
725 |
+
msgid "Pin"
|
726 |
+
msgstr ""
|
727 |
+
|
728 |
+
#: views/edit/pinned.php:31
|
729 |
+
msgid "Unpin"
|
730 |
+
msgstr ""
|
731 |
+
|
732 |
+
#: views/edit/pinned.php:34
|
733 |
+
msgid "OK"
|
734 |
+
msgstr ""
|
735 |
+
|
736 |
+
#: views/edit/pinned.php:35
|
737 |
+
msgid "Cancel"
|
738 |
+
msgstr ""
|
739 |
+
|
740 |
+
#: views/edit/review.php:4
|
741 |
+
msgid "no title"
|
742 |
+
msgstr ""
|
743 |
+
|
744 |
+
#: views/menu/addons/addons.php:3
|
745 |
+
msgid "The following Add-Ons extend the functionality of Site Reviews."
|
746 |
+
msgstr ""
|
747 |
+
|
748 |
+
#: views/menu/addons/addons.php:12
|
749 |
+
msgid "Sync your Tripadvisor business reviews with an optional minimum rating and display them on your site."
|
750 |
+
msgstr ""
|
751 |
+
|
752 |
+
#: views/menu/addons/addons.php:19
|
753 |
+
msgid "Sync your Yelp business reviews with an optional minimum rating and display them on your site."
|
754 |
+
msgstr ""
|
755 |
+
|
756 |
+
#: views/menu/help/system.php:9
|
757 |
+
msgid "Download System Info"
|
758 |
+
msgstr ""
|
759 |
+
|
760 |
+
#: views/menu/help/system.php:14
|
761 |
+
msgid "Logging"
|
762 |
+
msgstr ""
|
763 |
+
|
764 |
+
#: views/menu/help/system.php:15
|
765 |
+
msgid "You can enable logging for debugging purposes. Log files can grow quickly, so don't leave this on."
|
766 |
+
msgstr ""
|
767 |
+
|
768 |
+
#: views/menu/help/system.php:26
|
769 |
+
msgid "Clear Log"
|
770 |
+
msgstr ""
|
771 |
+
|
772 |
+
#: views/menu/help/system.php:32
|
773 |
+
msgid "Download Log"
|
774 |
+
msgstr ""
|
775 |
+
|
776 |
+
#: views/menu/help/system.php:44
|
777 |
+
msgid "Disable Logging"
|
778 |
+
msgstr ""
|
779 |
+
|
780 |
+
#: views/menu/help/system.php:48
|
781 |
+
msgid "Enable Logging"
|
782 |
+
msgstr ""
|
783 |
+
|
784 |
+
#: views/menu/status.php:7
|
785 |
+
msgid "All dates and times shown here use the WordPress <a href='%s'>configured timezone</a>."
|
786 |
+
msgstr ""
|
787 |
+
|
788 |
+
#: views/menu/status.php:16
|
789 |
+
msgid "Site"
|
790 |
+
msgstr ""
|
791 |
+
|
792 |
+
#: views/menu/status.php:18, views/menu/status.php:35
|
793 |
+
msgid "Last fetch"
|
794 |
+
msgstr ""
|
795 |
+
|
796 |
+
#: views/menu/status.php:19, views/menu/status.php:38
|
797 |
+
msgid "Next scheduled fetch"
|
798 |
+
msgstr ""
|
799 |
+
|
800 |
+
#: views/menu/status.php:36
|
801 |
+
msgid "No fetch has been completed"
|
802 |
+
msgstr ""
|
803 |
+
|
804 |
+
#: views/menu/status.php:39
|
805 |
+
msgid "Nothing currently scheduled"
|
806 |
+
msgstr ""
|
807 |
+
|
808 |
+
#: views/menu/status.php:59
|
809 |
+
msgid "Fetch reviews from"
|
810 |
+
msgstr ""
|
811 |
+
|
812 |
+
#: views/menu/status.php:66
|
813 |
+
msgid "Fetch status"
|
814 |
+
msgstr ""
|
815 |
+
|
816 |
+
#: views/menu/status.php:67
|
817 |
+
msgid "Fetching reviews..."
|
818 |
+
msgstr ""
|
819 |
+
|
820 |
+
#: views/menu/status.php:79
|
821 |
+
msgid "Fetch Reviews"
|
822 |
+
msgstr ""
|
823 |
+
|
824 |
+
#: views/submit/index.php:17
|
825 |
+
msgid "Select a Rating"
|
826 |
+
msgstr ""
|
827 |
+
|
828 |
+
#: views/submit/index.php:18
|
829 |
+
msgid "Excellent"
|
830 |
+
msgstr ""
|
831 |
+
|
832 |
+
#: views/submit/index.php:19
|
833 |
+
msgid "Very Good"
|
834 |
+
msgstr ""
|
835 |
+
|
836 |
+
#: views/submit/index.php:20
|
837 |
+
msgid "Average"
|
838 |
+
msgstr ""
|
839 |
+
|
840 |
+
#: views/submit/index.php:21
|
841 |
+
msgid "Poor"
|
842 |
+
msgstr ""
|
843 |
+
|
844 |
+
#: views/submit/index.php:22
|
845 |
+
msgid "Terrible"
|
846 |
+
msgstr ""
|
847 |
+
|
848 |
+
#: views/submit/index.php:121
|
849 |
+
msgid "Submit your review"
|
850 |
+
msgstr ""
|
license.txt
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
GPLv2 or later
|
2 |
+
https://www.gnu.org/licenses/gpl-2.0.html
|
plugin/App.php
ADDED
@@ -0,0 +1,161 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Container;
|
14 |
+
|
15 |
+
final class App extends Container
|
16 |
+
{
|
17 |
+
public $defaults;
|
18 |
+
public $file;
|
19 |
+
public $id;
|
20 |
+
public $name;
|
21 |
+
public $path;
|
22 |
+
public $prefix;
|
23 |
+
public $url;
|
24 |
+
public $version;
|
25 |
+
|
26 |
+
public function __construct()
|
27 |
+
{
|
28 |
+
// hardcoded path to the plugin file
|
29 |
+
$file = realpath( dirname( __DIR__ ) . '/site-reviews.php' );
|
30 |
+
|
31 |
+
$data = [
|
32 |
+
'id' => 'Text Domain',
|
33 |
+
'name' => 'Plugin Name',
|
34 |
+
'version' => 'Version',
|
35 |
+
];
|
36 |
+
|
37 |
+
$plugin = get_file_data( $file, $data, 'plugin' );
|
38 |
+
|
39 |
+
$this->id = 'geminilabs-' . $plugin['id'];
|
40 |
+
$this->file = $file;
|
41 |
+
$this->name = $plugin['name'];
|
42 |
+
$this->path = plugin_dir_path( $file );
|
43 |
+
$this->prefix = str_replace( '-', '_', $this->id );
|
44 |
+
$this->url = plugin_dir_url( $file );
|
45 |
+
$this->version = $plugin['version'];
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* This is the Application entry point
|
50 |
+
*
|
51 |
+
* @return void
|
52 |
+
*/
|
53 |
+
public function init()
|
54 |
+
{
|
55 |
+
$basename = plugin_basename( $this->file );
|
56 |
+
|
57 |
+
$controller = $this->make( 'Controllers\MainController' );
|
58 |
+
$router = $this->make( 'Router' );
|
59 |
+
|
60 |
+
// Action Hooks
|
61 |
+
add_action( 'plugins_loaded', [ $this, 'registerAddons'] );
|
62 |
+
add_action( 'admin_enqueue_scripts', [ $controller, 'enqueueAssets'] );
|
63 |
+
add_action( 'wp_enqueue_scripts', [ $controller, 'enqueueAssets'] );
|
64 |
+
add_action( 'admin_menu', [ $controller, 'registerMenuCount'] );
|
65 |
+
add_action( 'add_meta_boxes_site-review', [ $controller, 'registerMetaBox'] );
|
66 |
+
add_action( 'admin_enqueue_scripts', [ $controller, 'registerPointers'], 13 );
|
67 |
+
add_action( 'init', [ $controller, 'registerPostType'] );
|
68 |
+
add_action( 'admin_init', [ $controller, 'registerSettings'] );
|
69 |
+
add_action( 'init', [ $controller, 'registerShortcodes'] );
|
70 |
+
add_action( 'admin_menu', [ $controller, 'registerSubMenus'] );
|
71 |
+
add_action( 'init', [ $controller, 'registerTextdomain'] );
|
72 |
+
add_action( 'widgets_init', [ $controller, 'registerWidgets'] );
|
73 |
+
add_action( 'post_submitbox_misc_actions', [ $controller, 'renderMetaBoxPinned'] );
|
74 |
+
add_action( 'edit_form_after_title', [ $controller, 'renderReview'] );
|
75 |
+
add_action( 'edit_form_top', [ $controller, 'renderReviewNotice'] );
|
76 |
+
add_action( "wp_ajax_{$this->prefix}_action", [ $router, 'routeAjaxRequests'] );
|
77 |
+
add_action( "wp_ajax_nopriv_{$this->prefix}_action", [ $router, 'routeAjaxRequests'] );
|
78 |
+
add_action( 'admin_init', [ $router, 'routePostRequests'] );
|
79 |
+
add_action( 'admin_init', [ $router, 'routeWebhookRequests'] );
|
80 |
+
|
81 |
+
// Filter Hooks
|
82 |
+
add_filter( "plugin_action_links_{$basename}", [ $controller, 'registerActionLinks'] );
|
83 |
+
add_filter( 'dashboard_glance_items', [ $controller, 'registerDashboardGlanceItems'] );
|
84 |
+
add_filter( 'post_row_actions', [ $controller, 'registerRowActions'], 10, 2 );
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Runs on plugin activation
|
89 |
+
*
|
90 |
+
* @return void
|
91 |
+
*/
|
92 |
+
public function activate()
|
93 |
+
{
|
94 |
+
$current_version = get_option( "{$this->prefix}_version" );
|
95 |
+
|
96 |
+
if( $current_version ) {
|
97 |
+
update_option( "{$this->prefix}_version_upgraded_from", $current_version );
|
98 |
+
}
|
99 |
+
|
100 |
+
update_option( "{$this->prefix}_logging", 0 );
|
101 |
+
update_option( "{$this->prefix}_version", $this->version );
|
102 |
+
|
103 |
+
$this->make( 'Database' )->setDefaultSettings();
|
104 |
+
|
105 |
+
// Schedule session purge
|
106 |
+
if( !wp_next_scheduled( 'site-reviews/schedule/session/purge' ) ) {
|
107 |
+
wp_schedule_event( time(), 'twicedaily', 'site-reviews/schedule/session/purge' );
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Runs on plugin deactivation
|
113 |
+
*
|
114 |
+
* @return void
|
115 |
+
*/
|
116 |
+
public function deactivate()
|
117 |
+
{
|
118 |
+
$events = ['site-reviews/schedule/session/purge'];
|
119 |
+
|
120 |
+
foreach( $events as $event ) {
|
121 |
+
wp_unschedule_event( wp_next_scheduled( $event ), $event );
|
122 |
+
}
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Get the default settings
|
127 |
+
*
|
128 |
+
* @return array
|
129 |
+
*/
|
130 |
+
public function getDefaults()
|
131 |
+
{
|
132 |
+
if( !$this->defaults ) {
|
133 |
+
$this->defaults = $this->make( 'Settings' )->getSettings();
|
134 |
+
|
135 |
+
// Allow addons to modify the default settings
|
136 |
+
$this->defaults = apply_filters( 'site-reviews/addon/defaults', $this->defaults );
|
137 |
+
}
|
138 |
+
|
139 |
+
return $this->defaults;
|
140 |
+
}
|
141 |
+
|
142 |
+
/**
|
143 |
+
* Register available add-ons
|
144 |
+
*
|
145 |
+
* @return void
|
146 |
+
*/
|
147 |
+
public function registerAddons()
|
148 |
+
{
|
149 |
+
do_action( 'site-reviews/addon/register', $this );
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Verify permissions
|
154 |
+
*
|
155 |
+
* @return bool
|
156 |
+
*/
|
157 |
+
public function verify()
|
158 |
+
{
|
159 |
+
return current_user_can( 'customize' );
|
160 |
+
}
|
161 |
+
}
|
plugin/Commands/EnqueueAssets.php
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Commands;
|
12 |
+
|
13 |
+
class EnqueueAssets
|
14 |
+
{
|
15 |
+
public $handle;
|
16 |
+
public $path;
|
17 |
+
public $url;
|
18 |
+
public $version;
|
19 |
+
|
20 |
+
public function __construct( $input )
|
21 |
+
{
|
22 |
+
$this->handle = $input['handle'];
|
23 |
+
$this->url = $input['url'];
|
24 |
+
$this->path = $input['path'];
|
25 |
+
$this->version = $input['version'];
|
26 |
+
}
|
27 |
+
}
|
plugin/Commands/RegisterPointers.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* =
|
5 |
+
*
|
6 |
+
* @package GeminiLabs\SiteReviews
|
7 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
8 |
+
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
9 |
+
* @since 1.0.0
|
10 |
+
* -------------------------------------------------------------------------------------------------
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace GeminiLabs\SiteReviews\Commands;
|
14 |
+
|
15 |
+
class RegisterPointers
|
16 |
+
{
|
17 |
+
public $pointers;
|
18 |
+
|
19 |
+
public function __construct( $input )
|
20 |
+
{
|
21 |
+
$this->pointers = $input;
|
22 |
+
}
|
23 |
+
}
|
plugin/Commands/RegisterPostType.php
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Commands;
|
12 |
+
|
13 |
+
class RegisterPostType
|
14 |
+
{
|
15 |
+
public $args;
|
16 |
+
|
17 |
+
public function __construct( $input )
|
18 |
+
{
|
19 |
+
$columns = [
|
20 |
+
'title' => __( 'Title', 'site-reviews' ),
|
21 |
+
'date' => __( 'Date', 'site-reviews' ),
|
22 |
+
];
|
23 |
+
|
24 |
+
$defaults = [
|
25 |
+
'capability_type' => '',
|
26 |
+
'columns' => $columns,
|
27 |
+
'has_archive' => false,
|
28 |
+
'hierarchical' => false,
|
29 |
+
'map_meta_cap' => true,
|
30 |
+
'menu_icon' => null,
|
31 |
+
'menu_name' => '',
|
32 |
+
'menu_position' => 25,
|
33 |
+
'public' => true,
|
34 |
+
'query_var' => true,
|
35 |
+
'rewrite' => ['slug' => $input['slug'], 'with_front' => false ],
|
36 |
+
'show_in_menu' => true, //'edit.php?post_type=post'
|
37 |
+
'supports' => ['title', 'editor'],
|
38 |
+
'taxonomies' => [],
|
39 |
+
];
|
40 |
+
|
41 |
+
$this->args = wp_parse_args( $input, $defaults );
|
42 |
+
}
|
43 |
+
}
|
plugin/Commands/RegisterShortcodes.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* =
|
5 |
+
*
|
6 |
+
* @package GeminiLabs\SiteReviews
|
7 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
8 |
+
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
9 |
+
* @since 1.0.0
|
10 |
+
* -------------------------------------------------------------------------------------------------
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace GeminiLabs\SiteReviews\Commands;
|
14 |
+
|
15 |
+
class RegisterShortcodes
|
16 |
+
{
|
17 |
+
public $shortcodes;
|
18 |
+
|
19 |
+
public function __construct( $input )
|
20 |
+
{
|
21 |
+
$this->shortcodes = $input;
|
22 |
+
}
|
23 |
+
}
|
plugin/Commands/RegisterWidgets.php
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Commands;
|
12 |
+
|
13 |
+
class RegisterWidgets
|
14 |
+
{
|
15 |
+
public $widgets;
|
16 |
+
|
17 |
+
public function __construct( $input )
|
18 |
+
{
|
19 |
+
$this->widgets = (object) $input;
|
20 |
+
}
|
21 |
+
}
|
plugin/Commands/SubmitReview.php
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Commands;
|
12 |
+
|
13 |
+
class SubmitReview
|
14 |
+
{
|
15 |
+
public $ajaxRequest;
|
16 |
+
public $content;
|
17 |
+
public $email;
|
18 |
+
public $formId;
|
19 |
+
public $ipAddress;
|
20 |
+
public $rating;
|
21 |
+
public $reviewer;
|
22 |
+
public $terms;
|
23 |
+
public $title;
|
24 |
+
|
25 |
+
public function __construct( $input )
|
26 |
+
{
|
27 |
+
$this->ajaxRequest = isset( $input['ajax_request'] ) ? true : false;
|
28 |
+
$this->content = $input['content'];
|
29 |
+
$this->email = $input['email'];
|
30 |
+
$this->formId = $input['form_id'];
|
31 |
+
$this->ipAddress = $this->getIpAddress();
|
32 |
+
$this->rating = $input['rating'];
|
33 |
+
$this->reviewer = $input['reviewer'];
|
34 |
+
$this->title = $input['title'];
|
35 |
+
$this->terms = isset( $input['terms'] ) ? true : false;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Get the IP address and domain of the reviewer
|
40 |
+
*
|
41 |
+
* @return string|null
|
42 |
+
*/
|
43 |
+
protected function getIpAddress()
|
44 |
+
{
|
45 |
+
$ipAddress = filter_input( INPUT_SERVER, 'REMOTE_ADDR' );
|
46 |
+
|
47 |
+
if( $ipAddress ) {
|
48 |
+
$ipAddress .= ', ' . @gethostbyaddr( $ipAddress );
|
49 |
+
}
|
50 |
+
|
51 |
+
return $ipAddress;
|
52 |
+
}
|
53 |
+
}
|
plugin/Commands/TogglePinned.php
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Commands;
|
12 |
+
|
13 |
+
class TogglePinned
|
14 |
+
{
|
15 |
+
public $id;
|
16 |
+
public $pinned;
|
17 |
+
|
18 |
+
public function __construct( $input )
|
19 |
+
{
|
20 |
+
$pinned = isset( $input['pinned'] )
|
21 |
+
? wp_validate_boolean( $input['pinned'] )
|
22 |
+
: null;
|
23 |
+
|
24 |
+
$this->id = $input['id'];
|
25 |
+
$this->pinned = $pinned;
|
26 |
+
}
|
27 |
+
}
|
plugin/Container.php
ADDED
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews;
|
12 |
+
|
13 |
+
use Exception;
|
14 |
+
use GeminiLabs\SiteReviews\Providers\ProviderInterface;
|
15 |
+
use ReflectionClass;
|
16 |
+
|
17 |
+
abstract class Container
|
18 |
+
{
|
19 |
+
/**
|
20 |
+
* The current globally available container (if any).
|
21 |
+
*
|
22 |
+
* @var static
|
23 |
+
*/
|
24 |
+
protected static $instance;
|
25 |
+
|
26 |
+
protected $services = [];
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Set/get the globally available instance of the container.
|
30 |
+
*
|
31 |
+
* @return static
|
32 |
+
*/
|
33 |
+
public static function load()
|
34 |
+
{
|
35 |
+
if( is_null( static::$instance ) ) {
|
36 |
+
static::$instance = new static;
|
37 |
+
}
|
38 |
+
|
39 |
+
return static::$instance;
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* This is the Application entry point
|
44 |
+
*
|
45 |
+
* @return void
|
46 |
+
*/
|
47 |
+
abstract public function init();
|
48 |
+
|
49 |
+
/**
|
50 |
+
*
|
51 |
+
* @param string $service
|
52 |
+
*
|
53 |
+
* @return mixed
|
54 |
+
*/
|
55 |
+
public function __get( $service )
|
56 |
+
{
|
57 |
+
if( !isset( $this->services[ $service ] ) ) {
|
58 |
+
return null;
|
59 |
+
}
|
60 |
+
|
61 |
+
if( !is_callable( $this->services[ $service ] ) ) {
|
62 |
+
return $this->services[ $service ];
|
63 |
+
}
|
64 |
+
|
65 |
+
return $this->services[ $service ]( $this );
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
*
|
70 |
+
* @param string $service
|
71 |
+
* @param string $callback
|
72 |
+
*
|
73 |
+
* @return void
|
74 |
+
*/
|
75 |
+
public function __set( $service, $callback )
|
76 |
+
{
|
77 |
+
$this->services[ $service ] = $callback;
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Register a Provider.
|
82 |
+
*
|
83 |
+
* @param ProviderInterface $provider
|
84 |
+
*
|
85 |
+
* @return void
|
86 |
+
*/
|
87 |
+
public function register( ProviderInterface $provider )
|
88 |
+
{
|
89 |
+
$provider->register( $this );
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Bind a singleton instance to the container.
|
94 |
+
*
|
95 |
+
* @param string $alias
|
96 |
+
* @param string $binding
|
97 |
+
*
|
98 |
+
* @return void
|
99 |
+
*/
|
100 |
+
public function singleton( $alias, $binding )
|
101 |
+
{
|
102 |
+
$this->bind( $alias, $this->make( $binding ) );
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Bind a listener to an event.
|
107 |
+
*
|
108 |
+
* @param string $tag
|
109 |
+
* @param string $listener
|
110 |
+
* @param int $priority
|
111 |
+
* @param int $acceptedArgs
|
112 |
+
*
|
113 |
+
* @return void
|
114 |
+
*/
|
115 |
+
public function addListener( $tag, $listener, $priority = 10, $acceptedArgs = 1 )
|
116 |
+
{
|
117 |
+
$reviews = $this;
|
118 |
+
|
119 |
+
add_action( $tag, function( $event ) use ( $listener, $reviews ) {
|
120 |
+
$reviews->make( $listener )->handle( $event );
|
121 |
+
}, $priority, $acceptedArgs );
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Bind a service to the container.
|
126 |
+
*
|
127 |
+
* @param string $alias
|
128 |
+
* @param string $concrete
|
129 |
+
*
|
130 |
+
* @return mixed
|
131 |
+
*/
|
132 |
+
public function bind( $alias, $concrete )
|
133 |
+
{
|
134 |
+
$this->services[ $alias ] = $concrete;
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Request a service from the container.
|
139 |
+
*
|
140 |
+
* @param string $alias
|
141 |
+
* @param bool|string $prefixed
|
142 |
+
*
|
143 |
+
* @return mixed
|
144 |
+
*/
|
145 |
+
public function make( $alias, $prefixed = false )
|
146 |
+
{
|
147 |
+
if( isset( $this->services[ $alias ] ) && is_callable( $this->services[ $alias ] ) ) {
|
148 |
+
return call_user_func_array( $this->services[ $alias ], [ $this ] );
|
149 |
+
}
|
150 |
+
|
151 |
+
if( isset( $this->services[ $alias ] ) && is_object( $this->services[ $alias ] ) ) {
|
152 |
+
return $this->services[ $alias ];
|
153 |
+
}
|
154 |
+
|
155 |
+
if( isset( $this->services[ $alias ] ) && class_exists( $this->services[ $alias ] ) ) {
|
156 |
+
return $this->resolve( $this->services[ $alias ] );
|
157 |
+
}
|
158 |
+
|
159 |
+
// Allow unbound aliases that omit the root namespace
|
160 |
+
// i.e. 'Html\Field' translates to 'GeminiLabs\SiteReviews\Html\Field'
|
161 |
+
if( $prefixed === false && strpos( $alias, __NAMESPACE__ ) === false && !class_exists( $alias ) ) {
|
162 |
+
return $this->make( __NAMESPACE__ . "\\$alias" , 'prefix alias with the namespace' );
|
163 |
+
}
|
164 |
+
|
165 |
+
return $this->resolve( $alias );
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Resolve class dependencies automatically
|
170 |
+
*
|
171 |
+
* @param string $class
|
172 |
+
*
|
173 |
+
* @return mixed
|
174 |
+
*/
|
175 |
+
private function resolve( $class )
|
176 |
+
{
|
177 |
+
$reflector = new ReflectionClass( $class );
|
178 |
+
|
179 |
+
// If the type is not instantiable (i.e. interface/abstract class) then bail.
|
180 |
+
if( !$reflector->isInstantiable() ) {
|
181 |
+
$message = "Target [$class] is not instantiable.";
|
182 |
+
|
183 |
+
throw new Exception( $message );
|
184 |
+
}
|
185 |
+
|
186 |
+
$constructor = $reflector->getConstructor();
|
187 |
+
|
188 |
+
// No constructor means no dependencies so we can just resolve the instance right away.
|
189 |
+
if( is_null( $constructor ) ) {
|
190 |
+
return new $class;
|
191 |
+
}
|
192 |
+
|
193 |
+
$parameters = $constructor->getParameters();
|
194 |
+
|
195 |
+
$newInstanceParameters = [];
|
196 |
+
|
197 |
+
foreach( $parameters as $parameter ) {
|
198 |
+
|
199 |
+
// Allow array parameters if they have a default value
|
200 |
+
if( $parameter->isArray() && $parameter->isDefaultValueAvailable() ) {
|
201 |
+
$newInstanceParameters[] = $parameter->getDefaultValue();
|
202 |
+
continue;
|
203 |
+
}
|
204 |
+
|
205 |
+
// This will throw a TypeError if parameter is not a class
|
206 |
+
if( is_null( $parameter->getClass() ) ) {
|
207 |
+
$newInstanceParameters[] = null;
|
208 |
+
continue;
|
209 |
+
}
|
210 |
+
|
211 |
+
$newInstanceParameters[] = $this->make(
|
212 |
+
$parameter->getClass()->getName()
|
213 |
+
);
|
214 |
+
}
|
215 |
+
|
216 |
+
return $reflector->newInstanceArgs( $newInstanceParameters );
|
217 |
+
}
|
218 |
+
}
|
plugin/Controllers/AjaxController.php
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Controllers;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Controllers\BaseController;
|
14 |
+
use GeminiLabs\SiteReviews\Commands\TogglePinned;
|
15 |
+
|
16 |
+
class AjaxController extends BaseController
|
17 |
+
{
|
18 |
+
/**
|
19 |
+
* Clears the log
|
20 |
+
*/
|
21 |
+
public function ajaxClearLog()
|
22 |
+
{
|
23 |
+
$this->app->make( 'Controllers\MainController' )->postClearLog();
|
24 |
+
|
25 |
+
wp_send_json([
|
26 |
+
'log' => __( 'Log is empty', 'site-reviews' ),
|
27 |
+
'notices' => $this->notices->show( false ),
|
28 |
+
]);
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Toggle the pinned status of a review
|
33 |
+
*/
|
34 |
+
public function ajaxTogglePinned( $request )
|
35 |
+
{
|
36 |
+
$response = $this->execute( new TogglePinned( $request ) );
|
37 |
+
|
38 |
+
wp_send_json([
|
39 |
+
'notices' => $this->notices->show( false ),
|
40 |
+
'pinned' => (bool) $response,
|
41 |
+
]);
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Submit a review
|
46 |
+
*/
|
47 |
+
public function ajaxPostReview( $request )
|
48 |
+
{
|
49 |
+
$response = $this->app->make( 'Controllers\ReviewController' )->postSubmitReview( $request );
|
50 |
+
$errors = $this->app->make( 'Session' )->get( "{$request['form_id']}-errors", false, 'clear errors' );
|
51 |
+
|
52 |
+
wp_send_json([
|
53 |
+
'errors' => $errors,
|
54 |
+
'message' => $response,
|
55 |
+
]);
|
56 |
+
}
|
57 |
+
}
|
plugin/Controllers/BaseController.php
ADDED
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Controllers;
|
12 |
+
|
13 |
+
use Exception;
|
14 |
+
use GeminiLabs\SiteReviews\App;
|
15 |
+
use InvalidArgumentException;
|
16 |
+
use WP_Error;
|
17 |
+
|
18 |
+
abstract class BaseController
|
19 |
+
{
|
20 |
+
/**
|
21 |
+
* @var App
|
22 |
+
*/
|
23 |
+
protected $app;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var Database
|
27 |
+
*/
|
28 |
+
protected $db;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* @var Html
|
32 |
+
*/
|
33 |
+
protected $html;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @var Logger
|
37 |
+
*/
|
38 |
+
protected $log;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @var Notices
|
42 |
+
*/
|
43 |
+
protected $notices;
|
44 |
+
|
45 |
+
public function __construct( App $app )
|
46 |
+
{
|
47 |
+
$this->app = $app;
|
48 |
+
$this->db = $app->make( 'Database' );
|
49 |
+
$this->html = $app->make( 'Html' );
|
50 |
+
$this->log = $app->make( 'Log\Logger' );
|
51 |
+
$this->notices = $app->make( 'Notices' );
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Send a command to its handler and execute it.
|
56 |
+
*
|
57 |
+
* @param object $command
|
58 |
+
*
|
59 |
+
* @return void
|
60 |
+
*/
|
61 |
+
public function execute( $command )
|
62 |
+
{
|
63 |
+
$handlerClass = str_replace( 'Commands', 'Handlers', get_class( $command ) );
|
64 |
+
|
65 |
+
if( !class_exists( $handlerClass ) ) {
|
66 |
+
throw new InvalidArgumentException( "Handler {$handlerClass} doesn't exist.");
|
67 |
+
}
|
68 |
+
|
69 |
+
$handler = $this->app->make( $handlerClass );
|
70 |
+
|
71 |
+
try {
|
72 |
+
return $handler->handle( $command );
|
73 |
+
}
|
74 |
+
catch( Exception $e ) {
|
75 |
+
status_header( 400 );
|
76 |
+
$this->notices->addError( new WP_Error( 'reviews_error', $e->getMessage() ) );
|
77 |
+
$this->log->error( $e->getMessage() );
|
78 |
+
}
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Render a view and pass any provided data to the view
|
83 |
+
*
|
84 |
+
* @param string $view
|
85 |
+
*
|
86 |
+
* @return void
|
87 |
+
*/
|
88 |
+
public function render( $view, array $data = [] )
|
89 |
+
{
|
90 |
+
$data['db'] = $this->db;
|
91 |
+
$data['html'] = $this->html; // singleton
|
92 |
+
$data['log'] = $this->log;
|
93 |
+
$data['notices'] = $this->notices;
|
94 |
+
|
95 |
+
extract( $data );
|
96 |
+
|
97 |
+
return include $this->app->path . 'views/base.php';
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Validate form data
|
102 |
+
*
|
103 |
+
* @return bool
|
104 |
+
*/
|
105 |
+
public function validate( array $request, array $rules = [] )
|
106 |
+
{
|
107 |
+
$session = $this->app->make( 'Session' ); // singleton
|
108 |
+
$validator = $this->app->make( 'Validator' );
|
109 |
+
|
110 |
+
$errors = $validator->validate( $request, $rules );
|
111 |
+
|
112 |
+
if( empty( $errors ) ) {
|
113 |
+
return true;
|
114 |
+
}
|
115 |
+
|
116 |
+
$session->set( "{$request['form_id']}-errors", $errors );
|
117 |
+
$session->set( "{$request['form_id']}-values", $request );
|
118 |
+
|
119 |
+
return false;
|
120 |
+
}
|
121 |
+
}
|
plugin/Controllers/MainController.php
ADDED
@@ -0,0 +1,574 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Controllers;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Commands\EnqueueAssets;
|
14 |
+
use GeminiLabs\SiteReviews\Commands\RegisterPointers;
|
15 |
+
use GeminiLabs\SiteReviews\Commands\RegisterPostType;
|
16 |
+
use GeminiLabs\SiteReviews\Commands\RegisterShortcodes;
|
17 |
+
use GeminiLabs\SiteReviews\Commands\RegisterWidgets;
|
18 |
+
use GeminiLabs\SiteReviews\Commands\SubmitReview;
|
19 |
+
use GeminiLabs\SiteReviews\Controllers\BaseController;
|
20 |
+
use GeminiLabs\SiteReviews\Strings;
|
21 |
+
use WP_Admin_Bar;
|
22 |
+
use WP_Post;
|
23 |
+
|
24 |
+
class MainController extends BaseController
|
25 |
+
{
|
26 |
+
/**
|
27 |
+
* @return void
|
28 |
+
*
|
29 |
+
* @action wp_enqueue_scripts
|
30 |
+
*/
|
31 |
+
public function enqueueAssets()
|
32 |
+
{
|
33 |
+
$command = new EnqueueAssets([
|
34 |
+
'handle' => $this->app->id,
|
35 |
+
'path' => $this->app->path . 'assets/',
|
36 |
+
'url' => $this->app->url . 'assets/',
|
37 |
+
'version' => $this->app->version,
|
38 |
+
]);
|
39 |
+
|
40 |
+
$this->execute( $command );
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Clears the log
|
45 |
+
*
|
46 |
+
* @return void
|
47 |
+
*/
|
48 |
+
public function postClearLog()
|
49 |
+
{
|
50 |
+
$this->log->clear();
|
51 |
+
$this->notices->addSuccess( __( 'Log was cleared.', 'site-reviews' ) );
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Downloads the log
|
56 |
+
*
|
57 |
+
* @return void
|
58 |
+
*/
|
59 |
+
public function postDownloadLog()
|
60 |
+
{
|
61 |
+
$this->log->download();
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Downloads the system info
|
66 |
+
*
|
67 |
+
* @param string $system_info
|
68 |
+
*
|
69 |
+
* @return void
|
70 |
+
*/
|
71 |
+
public function postDownloadSystemInfo( $system_info )
|
72 |
+
{
|
73 |
+
$this->app->make( 'SystemInfo' )->download( $system_info );
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Registers the plugin action links on the plugins page
|
78 |
+
*
|
79 |
+
* @return array
|
80 |
+
*
|
81 |
+
* @filter plugin_action_links_reviews/reviews.php
|
82 |
+
*/
|
83 |
+
public function registerActionLinks( array $links )
|
84 |
+
{
|
85 |
+
$settings_url = admin_url( 'edit.php?post_type=site-review&page=settings' );
|
86 |
+
|
87 |
+
$links[] = sprintf( '<a href="%s">%s</a>', $settings_url, __( 'Settings', 'site-reviews' ) );
|
88 |
+
|
89 |
+
return $links;
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Adds the reviews count to the "At a Glance" Dashboard widget
|
94 |
+
*
|
95 |
+
* @return array
|
96 |
+
*
|
97 |
+
* @filter dashboard_glance_items
|
98 |
+
*/
|
99 |
+
public function registerDashboardGlanceItems( array $items )
|
100 |
+
{
|
101 |
+
$post_type = 'site-review';
|
102 |
+
$num_posts = wp_count_posts( $post_type );
|
103 |
+
|
104 |
+
if( $num_posts && $num_posts->publish ) {
|
105 |
+
|
106 |
+
$text = _n( '%s Review', '%s Reviews', $num_posts->publish, 'site-reviews' );
|
107 |
+
$text = sprintf( $text, number_format_i18n( $num_posts->publish ) );
|
108 |
+
|
109 |
+
$post_type_object = get_post_type_object( $post_type );
|
110 |
+
|
111 |
+
$items[] = $post_type_object && current_user_can( $post_type_object->cap->edit_posts )
|
112 |
+
? sprintf( '<a class="glsr-review-count" href="edit.php?post_type=%s">%s</a>', $post_type, $text )
|
113 |
+
: sprintf( '<span class="glsr-review-count">%s</span>', $text );
|
114 |
+
}
|
115 |
+
|
116 |
+
return $items;
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* @return void
|
121 |
+
*
|
122 |
+
* @action admin_menu
|
123 |
+
*/
|
124 |
+
public function registerMenuCount()
|
125 |
+
{
|
126 |
+
global $menu, $typenow;
|
127 |
+
|
128 |
+
$post_type = 'site-review';
|
129 |
+
|
130 |
+
foreach( $menu as $key => $value ) {
|
131 |
+
if( !( isset( $value[2] ) && $value[2] === "edit.php?post_type={$post_type}" ) )continue;
|
132 |
+
|
133 |
+
$awaiting_mod = wp_count_posts( $post_type );
|
134 |
+
$awaiting_mod = $awaiting_mod->pending;
|
135 |
+
|
136 |
+
$menu[ $key ][0] .= sprintf( ' <span class="awaiting-mod count-%d"><span class="pending-count">%s</span></span>',
|
137 |
+
absint( $awaiting_mod ),
|
138 |
+
number_format_i18n( $awaiting_mod )
|
139 |
+
);
|
140 |
+
|
141 |
+
if( $typenow === $post_type ) {
|
142 |
+
$menu[ $key ][4] .= ' current';
|
143 |
+
}
|
144 |
+
|
145 |
+
break;
|
146 |
+
}
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* @return void
|
151 |
+
*
|
152 |
+
* @action add_meta_boxes_review
|
153 |
+
*/
|
154 |
+
public function registerMetaBox()
|
155 |
+
{
|
156 |
+
add_meta_box( "{$this->app->id}_review", __( 'Details', 'site-reviews' ), [ $this, 'renderMetaBox'], null, 'side' );
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* @return void
|
161 |
+
*
|
162 |
+
* @action admin_enqueue_scripts
|
163 |
+
*/
|
164 |
+
public function registerPointers()
|
165 |
+
{
|
166 |
+
$command = new RegisterPointers([[
|
167 |
+
'id' => 'glsr-pointer-pinned',
|
168 |
+
'screen' => 'site-review',
|
169 |
+
'target' => '#misc-pub-pinned',
|
170 |
+
'title' => __( 'Pin Your Reviews', 'site-reviews' ),
|
171 |
+
'content' => __( 'You can pin exceptional reviews so that they are always shown first in your widgets and shortcodes.', 'site-reviews' ),
|
172 |
+
'position' => [
|
173 |
+
'edge' => 'right', // top, bottom, left, right
|
174 |
+
'align' => 'middle', // top, bottom, left, right, middle
|
175 |
+
],
|
176 |
+
]]);
|
177 |
+
|
178 |
+
$this->execute( $command );
|
179 |
+
}
|
180 |
+
|
181 |
+
/**
|
182 |
+
* @return void
|
183 |
+
*
|
184 |
+
* @action init
|
185 |
+
*/
|
186 |
+
public function registerPostType()
|
187 |
+
{
|
188 |
+
if( !$this->app->verify() )return;
|
189 |
+
|
190 |
+
$command = new RegisterPostType([
|
191 |
+
'post_type' => 'site-review',
|
192 |
+
'slug' => 'reviews',
|
193 |
+
'single' => __( 'Review', 'site-reviews' ),
|
194 |
+
'plural' => __( 'Reviews', 'site-reviews' ),
|
195 |
+
'menu_name' => __( 'Site Reviews', 'site-reviews' ),
|
196 |
+
'menu_icon' => 'dashicons-star-half',
|
197 |
+
'public' => false,
|
198 |
+
'has_archive' => false,
|
199 |
+
'show_ui' => false,
|
200 |
+
'labels' => (new Strings)->post_type_labels(),
|
201 |
+
'columns' => [
|
202 |
+
'title' => '', // empty values use the default label
|
203 |
+
'reviewer' => __( 'Reviewer', 'site-reviews' ),
|
204 |
+
'site' => __( 'Type', 'site-reviews' ),
|
205 |
+
'stars' => __( 'Rating', 'site-reviews' ),
|
206 |
+
'sticky' => __( 'Pinned', 'site-reviews' ),
|
207 |
+
'date' => '',
|
208 |
+
],
|
209 |
+
]);
|
210 |
+
|
211 |
+
$this->execute( $command );
|
212 |
+
}
|
213 |
+
|
214 |
+
/**
|
215 |
+
* Add Approve/Unapprove links and remove Quick-edit
|
216 |
+
*
|
217 |
+
* @return array
|
218 |
+
*
|
219 |
+
* @filter post_row_actions
|
220 |
+
*/
|
221 |
+
public function registerRowActions( array $actions, WP_Post $post )
|
222 |
+
{
|
223 |
+
if( $post->post_type !== 'site-review' || $post->post_status === 'trash' ) {
|
224 |
+
return $actions;
|
225 |
+
}
|
226 |
+
|
227 |
+
$atts = [
|
228 |
+
'approve' => [
|
229 |
+
'aria-label' => esc_attr__( 'Approve this review', 'site-reviews' ),
|
230 |
+
'href' => wp_nonce_url( admin_url( sprintf( 'post.php?post=%s&action=approve', $post->ID ) ), 'approve-review_' . $post->ID ),
|
231 |
+
'text' => __( 'Approve', 'site-reviews' ),
|
232 |
+
],
|
233 |
+
'unapprove' => [
|
234 |
+
'aria-label' => esc_attr__( 'Unapprove this review', 'site-reviews' ),
|
235 |
+
'href' => wp_nonce_url( admin_url( sprintf( 'post.php?post=%s&action=unapprove', $post->ID ) ), 'unapprove-review_' . $post->ID ),
|
236 |
+
'text' => __( 'Unapprove', 'site-reviews' ),
|
237 |
+
],
|
238 |
+
];
|
239 |
+
|
240 |
+
$newActions = [];
|
241 |
+
|
242 |
+
foreach( $atts as $key => $values ) {
|
243 |
+
$newActions[ $key ] = sprintf( '<a href="%s" aria-label="%s">%s</a>',
|
244 |
+
$values['href'],
|
245 |
+
$values['aria-label'],
|
246 |
+
$values['text']
|
247 |
+
);
|
248 |
+
}
|
249 |
+
|
250 |
+
// Remove Quick-edit
|
251 |
+
unset( $actions['inline hide-if-no-js'] );
|
252 |
+
|
253 |
+
return $newActions + $actions;
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* @return void
|
258 |
+
*
|
259 |
+
* @action admin_init
|
260 |
+
*/
|
261 |
+
public function registerSettings()
|
262 |
+
{
|
263 |
+
register_setting( "{$this->app->id}-settings", "{$this->app->prefix}_settings", [ $this, 'sanitizeSettings'] );
|
264 |
+
register_setting( "{$this->app->id}-logging", "{$this->app->prefix}_logging", [ $this, 'sanitizeLogging'] );
|
265 |
+
|
266 |
+
// register the settings form fields
|
267 |
+
$this->app->make( 'Settings' )->register();
|
268 |
+
}
|
269 |
+
|
270 |
+
/**
|
271 |
+
* @return void
|
272 |
+
*
|
273 |
+
* @action init
|
274 |
+
*/
|
275 |
+
public function registerShortcodes()
|
276 |
+
{
|
277 |
+
$command = new RegisterShortcodes([
|
278 |
+
'site_reviews',
|
279 |
+
'site_reviews_form',
|
280 |
+
]);
|
281 |
+
|
282 |
+
$this->execute( $command );
|
283 |
+
}
|
284 |
+
|
285 |
+
/**
|
286 |
+
* @return void
|
287 |
+
*
|
288 |
+
* @action admin_menu
|
289 |
+
*/
|
290 |
+
public function registerSubMenus()
|
291 |
+
{
|
292 |
+
$pages = [
|
293 |
+
'settings' => __( 'Settings', 'site-reviews' ),
|
294 |
+
'help' => __( 'Get Help', 'site-reviews' ),
|
295 |
+
'addons' => __( 'Add-Ons', 'site-reviews' ),
|
296 |
+
];
|
297 |
+
|
298 |
+
$pages = apply_filters( 'site-reviews/addon/submenu/pages', $pages );
|
299 |
+
|
300 |
+
foreach( $pages as $slug => $title ) {
|
301 |
+
|
302 |
+
$method = sprintf( 'render%sMenu', ucfirst( $slug ) );
|
303 |
+
|
304 |
+
$callback = apply_filters( 'site-reviews/addon/submenu/callback', [ $this, $method ], $slug );
|
305 |
+
|
306 |
+
if( !is_callable( $callback ) )continue;
|
307 |
+
|
308 |
+
add_submenu_page( 'edit.php?post_type=site-review', $title, $title, 'customize', $slug, $callback );
|
309 |
+
}
|
310 |
+
}
|
311 |
+
|
312 |
+
/**
|
313 |
+
* @return void
|
314 |
+
*
|
315 |
+
* @action init
|
316 |
+
*/
|
317 |
+
public function registerTextdomain()
|
318 |
+
{
|
319 |
+
load_plugin_textdomain( $this->app->id, false, "{$this->app->path}languages" );
|
320 |
+
}
|
321 |
+
|
322 |
+
/**
|
323 |
+
* @return void
|
324 |
+
*
|
325 |
+
* @action widgets_init
|
326 |
+
*/
|
327 |
+
public function registerWidgets()
|
328 |
+
{
|
329 |
+
$command = new RegisterWidgets([
|
330 |
+
'reviews_form' => [
|
331 |
+
'title' => __( 'Submit a Site Review', 'site-reviews' ),
|
332 |
+
'description' => __( 'A "submit a review" form for your site.', 'site-reviews' ),
|
333 |
+
'class' => 'glsr-widget glsr-widget-reviews-form',
|
334 |
+
],
|
335 |
+
'recent_reviews' => [
|
336 |
+
'title' => __( 'Recent Site Reviews', 'site-reviews' ),
|
337 |
+
'description' => __( 'Your site’s most recent Local Reviews.', 'site-reviews' ),
|
338 |
+
'class' => 'glsr-widget glsr-widget-recent-reviews',
|
339 |
+
],
|
340 |
+
]);
|
341 |
+
|
342 |
+
$this->execute( $command );
|
343 |
+
}
|
344 |
+
|
345 |
+
/**
|
346 |
+
* add_submenu_page() callback
|
347 |
+
*
|
348 |
+
* @return void
|
349 |
+
*/
|
350 |
+
public function renderAddonsMenu()
|
351 |
+
{
|
352 |
+
$this->renderMenu( 'addons', [
|
353 |
+
'addons' => __( 'Add-Ons', 'site-reviews' ),
|
354 |
+
]);
|
355 |
+
}
|
356 |
+
|
357 |
+
/**
|
358 |
+
* add_submenu_page() callback
|
359 |
+
*
|
360 |
+
* @return void
|
361 |
+
*/
|
362 |
+
public function renderHelpMenu()
|
363 |
+
{
|
364 |
+
// allow addons to add their own help sections
|
365 |
+
$sections = apply_filters( 'site-reviews/addon/documentation/sections', [
|
366 |
+
'support' => 'Support',
|
367 |
+
'shortcodes' => 'Shortcodes',
|
368 |
+
'hooks' => 'Hooks',
|
369 |
+
]);
|
370 |
+
|
371 |
+
$this->renderMenu( 'help', [
|
372 |
+
'documentation' => [
|
373 |
+
'title' => __( 'Documentation', 'site-reviews' ),
|
374 |
+
'sections' => $sections,
|
375 |
+
],
|
376 |
+
'system' => __( 'System Info', 'site-reviews' ),
|
377 |
+
],[
|
378 |
+
'system_info' => $this->app->make( 'SystemInfo' ),
|
379 |
+
]);
|
380 |
+
}
|
381 |
+
|
382 |
+
/**
|
383 |
+
* @return void
|
384 |
+
*/
|
385 |
+
public function renderMenu( $page, $tabs, $data = [] )
|
386 |
+
{
|
387 |
+
$tabs = $this->normalizeTabs( $tabs );
|
388 |
+
$tab = $this->filterTab( $tabs );
|
389 |
+
$section = $this->filterSection( $tabs, $tab );
|
390 |
+
|
391 |
+
$defaults = [
|
392 |
+
'page' => $page,
|
393 |
+
'tabs' => $tabs,
|
394 |
+
'tabView' => $tab,
|
395 |
+
'tabViewSection' => $section,
|
396 |
+
];
|
397 |
+
|
398 |
+
$data = apply_filters( 'site-reviews/addon/menu/data', $data, $defaults );
|
399 |
+
|
400 |
+
$this->render( 'menu/index', wp_parse_args( $data, $defaults ) );
|
401 |
+
}
|
402 |
+
|
403 |
+
/**
|
404 |
+
* add_meta_box() callback
|
405 |
+
*
|
406 |
+
* @return void
|
407 |
+
*/
|
408 |
+
public function renderMetaBox( WP_Post $post )
|
409 |
+
{
|
410 |
+
if( $post->post_type != 'site-review' )return;
|
411 |
+
|
412 |
+
$this->render( 'edit/meta', ['post' => $post ] );
|
413 |
+
}
|
414 |
+
|
415 |
+
/**
|
416 |
+
* @return void
|
417 |
+
*
|
418 |
+
* @action post_submitbox_misc_actions
|
419 |
+
*/
|
420 |
+
public function renderMetaBoxPinned()
|
421 |
+
{
|
422 |
+
global $post;
|
423 |
+
|
424 |
+
if( $post->post_type != 'site-review' )return;
|
425 |
+
|
426 |
+
$pinned = get_post_meta( $post->ID, 'pinned', true );
|
427 |
+
|
428 |
+
$this->render( 'edit/pinned', ['pinned' => $pinned ] );
|
429 |
+
}
|
430 |
+
|
431 |
+
/**
|
432 |
+
* @return void
|
433 |
+
*
|
434 |
+
* @action edit_form_after_title
|
435 |
+
*/
|
436 |
+
public function renderReview( WP_Post $post )
|
437 |
+
{
|
438 |
+
if( $post->post_type != 'site-review' )return;
|
439 |
+
if( post_type_supports( 'site-review', 'title' ) )return;
|
440 |
+
if( get_post_meta( $post->ID, 'site_name', true ) == 'local' )return;
|
441 |
+
|
442 |
+
$this->render( 'edit/review', ['post' => $post ] );
|
443 |
+
}
|
444 |
+
|
445 |
+
/**
|
446 |
+
* @return void
|
447 |
+
*
|
448 |
+
* @action edit_form_top
|
449 |
+
*/
|
450 |
+
public function renderReviewNotice( WP_Post $post )
|
451 |
+
{
|
452 |
+
if( $post->post_type != 'site-review' )return;
|
453 |
+
if( post_type_supports( 'site-review', 'title' ) )return;
|
454 |
+
|
455 |
+
$type = get_post_meta( $post->ID, 'site_name', true );
|
456 |
+
|
457 |
+
if( $type == 'local' )return;
|
458 |
+
|
459 |
+
$this->notices->addWarning( sprintf( __( '%s reviews are read-only.', 'site-reviews' ), ucfirst( $type ) ) );
|
460 |
+
$this->render( 'edit/notice' );
|
461 |
+
}
|
462 |
+
|
463 |
+
/**
|
464 |
+
* add_submenu_page() callback
|
465 |
+
*
|
466 |
+
* @return void
|
467 |
+
*/
|
468 |
+
public function renderSettingsMenu()
|
469 |
+
{
|
470 |
+
// allow addons to add their own setting sections
|
471 |
+
$sections = apply_filters( 'site-reviews/addon/settings/sections', [
|
472 |
+
'general' => 'General',
|
473 |
+
'form' => 'Submission Form',
|
474 |
+
]);
|
475 |
+
|
476 |
+
$this->renderMenu( 'settings', [
|
477 |
+
'settings' => [
|
478 |
+
'title' => __( 'Settings', 'site-reviews' ),
|
479 |
+
'sections' => $sections,
|
480 |
+
],
|
481 |
+
'licenses' => __( 'Licenses', 'site-reviews' ),
|
482 |
+
],[
|
483 |
+
'settings' => $this->app->getDefaults(),
|
484 |
+
]);
|
485 |
+
}
|
486 |
+
|
487 |
+
/**
|
488 |
+
* register_setting() callback
|
489 |
+
*
|
490 |
+
* @param int $input
|
491 |
+
*
|
492 |
+
* @return int
|
493 |
+
*/
|
494 |
+
public function sanitizeLogging( $input )
|
495 |
+
{
|
496 |
+
$message = $input
|
497 |
+
? __( 'Logging enabled.', 'site-reviews' )
|
498 |
+
: __( 'Logging disabled.', 'site-reviews' );
|
499 |
+
|
500 |
+
$this->notices->addSuccess( $message );
|
501 |
+
|
502 |
+
return $input;
|
503 |
+
}
|
504 |
+
|
505 |
+
/**
|
506 |
+
* register_setting() callback
|
507 |
+
*
|
508 |
+
* @return array
|
509 |
+
*/
|
510 |
+
public function sanitizeSettings( array $input )
|
511 |
+
{
|
512 |
+
$settings = $this->db->getOption();
|
513 |
+
|
514 |
+
$this->notices->addSuccess( __( 'Settings updated.', 'site-reviews' ) );
|
515 |
+
|
516 |
+
// Merge the settings tab section arrays
|
517 |
+
return array_merge( $settings, $input );
|
518 |
+
}
|
519 |
+
|
520 |
+
/**
|
521 |
+
* Gets the current menu page tab section
|
522 |
+
*
|
523 |
+
* @param string $tab
|
524 |
+
*
|
525 |
+
* @return string
|
526 |
+
*/
|
527 |
+
protected function filterSection( array $tabs, $tab )
|
528 |
+
{
|
529 |
+
$section = filter_input( INPUT_GET, 'section' );
|
530 |
+
|
531 |
+
if( !$section || !isset( $tabs[ $tab ]['sections'][ $section ] ) ) {
|
532 |
+
$section = isset( $tabs[ $tab ]['sections'] )
|
533 |
+
? key( $tabs[ $tab ]['sections'] )
|
534 |
+
: '';
|
535 |
+
}
|
536 |
+
|
537 |
+
return $section;
|
538 |
+
}
|
539 |
+
|
540 |
+
/**
|
541 |
+
* Gets the current menu page tab
|
542 |
+
*
|
543 |
+
* @return string
|
544 |
+
*/
|
545 |
+
protected function filterTab( array $tabs )
|
546 |
+
{
|
547 |
+
$tab = filter_input( INPUT_GET, 'tab' );
|
548 |
+
|
549 |
+
if( !$tab || !array_key_exists( $tab, $tabs ) ) {
|
550 |
+
$tab = key( $tabs );
|
551 |
+
}
|
552 |
+
|
553 |
+
return $tab;
|
554 |
+
}
|
555 |
+
|
556 |
+
/**
|
557 |
+
* Normalize the tabs array
|
558 |
+
*
|
559 |
+
* @return array
|
560 |
+
*/
|
561 |
+
protected function normalizeTabs( array $tabs )
|
562 |
+
{
|
563 |
+
foreach( $tabs as $key => $value ) {
|
564 |
+
if( !is_array( $value ) ) {
|
565 |
+
$tabs[ $key ] = ['title' => $value ];
|
566 |
+
}
|
567 |
+
if( $key == 'licenses' && !apply_filters( 'site-reviews/addon/licenses', false ) ) {
|
568 |
+
unset( $tabs[ $key ] );
|
569 |
+
}
|
570 |
+
}
|
571 |
+
|
572 |
+
return $tabs;
|
573 |
+
}
|
574 |
+
}
|
plugin/Controllers/ReviewController.php
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Controllers;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Commands\SubmitReview;
|
14 |
+
use GeminiLabs\SiteReviews\Controllers\BaseController;
|
15 |
+
|
16 |
+
class ReviewController extends BaseController
|
17 |
+
{
|
18 |
+
/**
|
19 |
+
* Submit the review form
|
20 |
+
*
|
21 |
+
* @return void
|
22 |
+
* @throws Exception
|
23 |
+
*/
|
24 |
+
public function postSubmitReview( array $request )
|
25 |
+
{
|
26 |
+
$minContentLength = apply_filters( 'site-reviews/local/review/content/minLength', '0' );
|
27 |
+
|
28 |
+
$rules = [
|
29 |
+
'content' => 'required|min:' . $minContentLength,
|
30 |
+
'email' => 'required|email|min:5',
|
31 |
+
'rating' => 'required|numeric|between:1,5',
|
32 |
+
'reviewer' => 'required',
|
33 |
+
'terms' => 'accepted',
|
34 |
+
'title' => 'required',
|
35 |
+
];
|
36 |
+
|
37 |
+
$excluded = isset( $request['excluded'] )
|
38 |
+
? json_decode( $request['excluded'] )
|
39 |
+
: [];
|
40 |
+
|
41 |
+
// only use the rules for non-excluded values
|
42 |
+
$rules = array_diff_key( $rules, array_flip( $excluded ) );
|
43 |
+
|
44 |
+
$user = wp_get_current_user();
|
45 |
+
|
46 |
+
$reviewer = $user->exists()
|
47 |
+
? $user->display_name
|
48 |
+
: __( 'Anonymous', 'site-reviews' );
|
49 |
+
|
50 |
+
$defaults = [
|
51 |
+
'content' => '',
|
52 |
+
'email' => '',
|
53 |
+
'rating' => '',
|
54 |
+
'reviewer' => $reviewer,
|
55 |
+
'terms' => '',
|
56 |
+
'title' => __( 'No Title', 'site-reviews' ),
|
57 |
+
];
|
58 |
+
|
59 |
+
if( !$this->validate( $request, $rules ) ) {
|
60 |
+
return __( 'Please fix the submission errors.', 'site-reviews' );
|
61 |
+
}
|
62 |
+
|
63 |
+
// normalize the request array
|
64 |
+
$request = array_merge( $defaults, $request );
|
65 |
+
|
66 |
+
return $this->execute( new SubmitReview( $request ) );
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
plugin/Database.php
ADDED
@@ -0,0 +1,535 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\App;
|
14 |
+
use WP_Query;
|
15 |
+
|
16 |
+
class Database
|
17 |
+
{
|
18 |
+
/**
|
19 |
+
* @var App
|
20 |
+
*/
|
21 |
+
protected $app;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var Logger
|
25 |
+
*/
|
26 |
+
protected $log;
|
27 |
+
|
28 |
+
public function __construct( App $app )
|
29 |
+
{
|
30 |
+
$this->app = $app;
|
31 |
+
$this->log = $app->make( 'Log\Logger' );
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Count all reviews
|
36 |
+
*
|
37 |
+
* @param string $siteName
|
38 |
+
*
|
39 |
+
* @return array
|
40 |
+
*/
|
41 |
+
public function countReviews( $siteName )
|
42 |
+
{
|
43 |
+
global $wpdb;
|
44 |
+
|
45 |
+
$counts = wp_cache_get( $this->app->id, 'counts' );
|
46 |
+
|
47 |
+
if( false !== $counts ) {
|
48 |
+
return isset( $counts[ $siteName ] ) ? $counts[ $siteName ] : 0;
|
49 |
+
}
|
50 |
+
|
51 |
+
$results = (array) $wpdb->get_results(
|
52 |
+
"SELECT m.meta_value AS name, COUNT( * ) num_posts " .
|
53 |
+
"FROM {$wpdb->posts} AS p " .
|
54 |
+
"INNER JOIN {$wpdb->postmeta} AS m ON p.ID = m.post_id " .
|
55 |
+
"WHERE p.post_type = 'site-review' AND m.meta_key = 'site_name' " .
|
56 |
+
"GROUP BY name"
|
57 |
+
);
|
58 |
+
|
59 |
+
$counts = [];
|
60 |
+
|
61 |
+
foreach( $results as $site ) {
|
62 |
+
$counts[ $site->name ] = $site->num_posts;
|
63 |
+
}
|
64 |
+
|
65 |
+
wp_cache_set( $this->app->id, $counts, 'counts' );
|
66 |
+
|
67 |
+
return isset( $counts[ $siteName ] ) ? $counts[ $siteName ] : 0;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Delete reviews based on an array of review_id meta values
|
72 |
+
*
|
73 |
+
* @return void
|
74 |
+
*/
|
75 |
+
public function deleteReviews( array $review_ids = [] )
|
76 |
+
{
|
77 |
+
global $wpdb;
|
78 |
+
|
79 |
+
foreach( $review_ids as $review_id ) {
|
80 |
+
|
81 |
+
$post_id = $this->getReviewPostId( $review_id );
|
82 |
+
|
83 |
+
if( !empty( $post_id ) ) {
|
84 |
+
wp_delete_post( $post_id, true );
|
85 |
+
}
|
86 |
+
}
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Get array of meta values for all of a post_type
|
91 |
+
*
|
92 |
+
* @param string $key
|
93 |
+
* @param string $status
|
94 |
+
* @param string $type
|
95 |
+
*
|
96 |
+
* @return array
|
97 |
+
*/
|
98 |
+
public function getMetaValues( $key = '', $status = 'publish', $type = 'site-review' )
|
99 |
+
{
|
100 |
+
if( empty( $key ) ) {
|
101 |
+
return [];
|
102 |
+
}
|
103 |
+
|
104 |
+
global $wpdb;
|
105 |
+
|
106 |
+
if( $status == 'all' || empty( $status ) ) {
|
107 |
+
$status = get_post_stati( ['exclude_from_search' => false ] );
|
108 |
+
}
|
109 |
+
|
110 |
+
$status = array_map( function( $ps ) { return "p.post_status = '{$ps}'"; }, (array) $status );
|
111 |
+
$status = implode( ' OR ', $status );
|
112 |
+
|
113 |
+
return $wpdb->get_col( $wpdb->prepare(
|
114 |
+
"SELECT DISTINCT pm.meta_value FROM {$wpdb->postmeta} pm " .
|
115 |
+
"LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id " .
|
116 |
+
"WHERE pm.meta_key = '%s' " .
|
117 |
+
"AND p.post_type = '%s' " .
|
118 |
+
"AND ({$status}) " .
|
119 |
+
"ORDER BY pm.meta_value",
|
120 |
+
$key,
|
121 |
+
$type
|
122 |
+
));
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Get the current page number for the global query
|
127 |
+
*
|
128 |
+
* @return int
|
129 |
+
*/
|
130 |
+
public function getCurrentPageNumber()
|
131 |
+
{
|
132 |
+
$paged = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : (
|
133 |
+
get_query_var( 'page' ) ? get_query_var( 'page' ) : 1
|
134 |
+
);
|
135 |
+
|
136 |
+
return intval( $paged );
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* Gets an option from the plugin settings array using dot notation
|
141 |
+
*
|
142 |
+
* @param string $path
|
143 |
+
* @param mixed $fallback
|
144 |
+
* @param string $suffix
|
145 |
+
*
|
146 |
+
* @return mixed
|
147 |
+
*/
|
148 |
+
public function getOption( $path = '', $fallback = '', $suffix = 'settings' )
|
149 |
+
{
|
150 |
+
$settings = get_option( "{$this->app->prefix}_{$suffix}", [] );
|
151 |
+
|
152 |
+
$option = $this->getDotNotationValue( $settings, $path, $fallback );
|
153 |
+
|
154 |
+
// fallback to setting defaults
|
155 |
+
if( $suffix == 'settings' && empty( $option ) ) {
|
156 |
+
$defaults = $this->app->getDefaults();
|
157 |
+
|
158 |
+
if( isset( $defaults[ $path ] ) ) {
|
159 |
+
$option = $defaults[ $path ];
|
160 |
+
}
|
161 |
+
}
|
162 |
+
|
163 |
+
return $option;
|
164 |
+
}
|
165 |
+
|
166 |
+
/**
|
167 |
+
* Gets an array of all saved Review IDs
|
168 |
+
*
|
169 |
+
* @param string $siteName
|
170 |
+
*
|
171 |
+
* @return array
|
172 |
+
*/
|
173 |
+
public function getReviewIds( $siteName )
|
174 |
+
{
|
175 |
+
global $wpdb;
|
176 |
+
|
177 |
+
$ids = $wpdb->get_col( $wpdb->prepare(
|
178 |
+
"SELECT m1.meta_value AS review_id " .
|
179 |
+
"FROM {$wpdb->posts} AS p " .
|
180 |
+
"INNER JOIN {$wpdb->postmeta} AS m1 ON p.ID = m1.post_id " .
|
181 |
+
"INNER JOIN {$wpdb->postmeta} AS m2 ON p.ID = m2.post_id " .
|
182 |
+
"WHERE p.post_type = 'site-review' " .
|
183 |
+
"AND m1.meta_key = 'review_id' " .
|
184 |
+
"AND m2.meta_key = 'site_name' " .
|
185 |
+
"AND m2.meta_value = '%s'",
|
186 |
+
$siteName
|
187 |
+
));
|
188 |
+
|
189 |
+
return array_keys( array_flip( $ids ) );
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* Get the review post ID from the review_id meta value
|
194 |
+
*
|
195 |
+
* @param string $review_id
|
196 |
+
*
|
197 |
+
* @return int|null
|
198 |
+
*/
|
199 |
+
public function getReviewPostId( $review_id )
|
200 |
+
{
|
201 |
+
global $wpdb;
|
202 |
+
|
203 |
+
return $wpdb->get_var( $wpdb->prepare(
|
204 |
+
"SELECT p.ID " .
|
205 |
+
"FROM {$wpdb->posts} AS p " .
|
206 |
+
"INNER JOIN {$wpdb->postmeta} AS m1 ON p.ID = m1.post_id " .
|
207 |
+
"WHERE p.post_type = 'site-review' " .
|
208 |
+
"AND m1.meta_key = 'review_id' " .
|
209 |
+
"AND m1.meta_value = '%s'",
|
210 |
+
$review_id
|
211 |
+
));
|
212 |
+
}
|
213 |
+
|
214 |
+
/**
|
215 |
+
* Gets a WP_Query object of saved Reviews
|
216 |
+
*
|
217 |
+
* @return WP_Query
|
218 |
+
*/
|
219 |
+
public function getReviews( array $args = [] )
|
220 |
+
{
|
221 |
+
$defaults = [
|
222 |
+
'max_reviews' => '10',
|
223 |
+
'min_rating' => '5',
|
224 |
+
'order_by' => 'date',
|
225 |
+
'pagination' => false,
|
226 |
+
'site_name' => '',
|
227 |
+
];
|
228 |
+
|
229 |
+
$args = shortcode_atts( $defaults, $args );
|
230 |
+
|
231 |
+
extract( $args );
|
232 |
+
|
233 |
+
if( !empty( $site_name ) ) {
|
234 |
+
$meta_query[] = [
|
235 |
+
'key' => 'site_name',
|
236 |
+
'value' => $site_name,
|
237 |
+
];
|
238 |
+
}
|
239 |
+
|
240 |
+
$meta_query[] = [
|
241 |
+
'key' => 'rating',
|
242 |
+
'value' => $min_rating,
|
243 |
+
'compare' => '>=',
|
244 |
+
];
|
245 |
+
|
246 |
+
$pagination = $pagination ? $this->getCurrentPageNumber() : 1;
|
247 |
+
|
248 |
+
return new WP_Query([
|
249 |
+
'meta_key' => 'pinned',
|
250 |
+
'meta_query' => $meta_query,
|
251 |
+
'order' => 'DESC',
|
252 |
+
'orderby' => "meta_value $order_by",
|
253 |
+
'paged' => $pagination,
|
254 |
+
'post_status' => 'publish',
|
255 |
+
'post_type' => 'site-review',
|
256 |
+
'posts_per_page' => $max_reviews ? $max_reviews : -1,
|
257 |
+
]);
|
258 |
+
}
|
259 |
+
|
260 |
+
/**
|
261 |
+
* Gets the review types (default type is "local")
|
262 |
+
*
|
263 |
+
* @return array
|
264 |
+
*/
|
265 |
+
public function getReviewTypes()
|
266 |
+
{
|
267 |
+
global $wpdb;
|
268 |
+
|
269 |
+
$types = $wpdb->get_col(
|
270 |
+
"SELECT DISTINCT(meta_value) FROM $wpdb->postmeta WHERE meta_key = 'site_name' ORDER BY meta_value ASC"
|
271 |
+
);
|
272 |
+
|
273 |
+
$types = array_flip( $types );
|
274 |
+
|
275 |
+
array_walk( $types, function( &$value, $key ) {
|
276 |
+
$value = sprintf( '%s reviews', ucfirst( $key ) );
|
277 |
+
});
|
278 |
+
|
279 |
+
return $types;
|
280 |
+
}
|
281 |
+
|
282 |
+
/**
|
283 |
+
* Gets an selector option from the plugin settings array using dot notation
|
284 |
+
*
|
285 |
+
* @param string $path
|
286 |
+
* @param mixed $fallback
|
287 |
+
*
|
288 |
+
* @return mixed
|
289 |
+
*/
|
290 |
+
public function getSelectorOption( $path = '', $fallback = '' )
|
291 |
+
{
|
292 |
+
$settings = $this->setDefaultSettings(['update' => false ]);
|
293 |
+
|
294 |
+
return $this->getDotNotationValue( $settings, $path, $fallback );
|
295 |
+
}
|
296 |
+
|
297 |
+
/**
|
298 |
+
* Save a review to the database
|
299 |
+
*
|
300 |
+
* @param string $review_id
|
301 |
+
* @param bool $update
|
302 |
+
*
|
303 |
+
* @return void
|
304 |
+
*/
|
305 |
+
public function postReview( $review_id, array $meta, $update = false )
|
306 |
+
{
|
307 |
+
$post_id = $this->getReviewPostId( $review_id );
|
308 |
+
|
309 |
+
if( !empty( $post_id ) && !$update ) {
|
310 |
+
return $post_id;
|
311 |
+
}
|
312 |
+
|
313 |
+
// make sure we set post_meta fallback defaults
|
314 |
+
$meta = wp_parse_args( $meta, [
|
315 |
+
'author' => '',
|
316 |
+
'avatar' => '',
|
317 |
+
'content' => '',
|
318 |
+
'date' => get_date_from_gmt( gmdate( 'Y-m-d H:i:s' )),
|
319 |
+
'email' => '',
|
320 |
+
'ip_address' => '',
|
321 |
+
'pinned' => false,
|
322 |
+
'rating' => '',
|
323 |
+
'review_id' => '',
|
324 |
+
'site_name' => 'local',
|
325 |
+
'title' => '',
|
326 |
+
'url' => '',
|
327 |
+
]);
|
328 |
+
|
329 |
+
$post_data = [
|
330 |
+
'comment_status' => 'closed',
|
331 |
+
'ID' => $post_id ? $post_id : 0,
|
332 |
+
'ping_status' => 'closed',
|
333 |
+
'post_content' => $meta['content'],
|
334 |
+
'post_date' => $meta['date'],
|
335 |
+
'post_name' => $meta['site_name'] . str_replace( ['review_', '_'], '-', $review_id ),
|
336 |
+
'post_status' => 'publish',
|
337 |
+
'post_title' => wp_strip_all_tags( $meta['title'] ),
|
338 |
+
'post_type' => 'site-review',
|
339 |
+
];
|
340 |
+
|
341 |
+
if( $this->getOption( 'general.require.approval', false ) && $meta['site_name'] == 'local' ) {
|
342 |
+
$post_data['post_status'] = 'pending';
|
343 |
+
}
|
344 |
+
|
345 |
+
$post_id = wp_insert_post( $post_data, true );
|
346 |
+
|
347 |
+
if( is_wp_error( $post_id ) ) {
|
348 |
+
$this->log->error( sprintf( '%s (%s)', $post_id->get_error_message(), $review_id ) );
|
349 |
+
|
350 |
+
return false;
|
351 |
+
}
|
352 |
+
|
353 |
+
// add post_meta
|
354 |
+
foreach( $meta as $field => $value ) {
|
355 |
+
update_post_meta( $post_id, $field, $value );
|
356 |
+
}
|
357 |
+
|
358 |
+
return $post_id;
|
359 |
+
}
|
360 |
+
|
361 |
+
/**
|
362 |
+
* Resets an option to the provided value and returns the old value
|
363 |
+
*
|
364 |
+
* @param mixed $value
|
365 |
+
* @param string $path
|
366 |
+
* @param string $suffix
|
367 |
+
*
|
368 |
+
* @return mixed
|
369 |
+
*/
|
370 |
+
public function resetOption( $value, $path = '', $suffix = 'settings' )
|
371 |
+
{
|
372 |
+
$option = $this->getOption( $path, '', $suffix );
|
373 |
+
|
374 |
+
$this->setOption( $value, $path, $suffix );
|
375 |
+
|
376 |
+
return $option;
|
377 |
+
}
|
378 |
+
|
379 |
+
/**
|
380 |
+
* Reverts a review title, date, and content to the originally submitted values
|
381 |
+
*
|
382 |
+
* @param string $postId
|
383 |
+
*
|
384 |
+
* @return int|false
|
385 |
+
*/
|
386 |
+
public function revertReview( $postId )
|
387 |
+
{
|
388 |
+
$post = get_post( $postId );
|
389 |
+
|
390 |
+
if( !isset( $post->post_type ) || $post->post_type != 'site-review' ) {
|
391 |
+
return false;
|
392 |
+
}
|
393 |
+
|
394 |
+
delete_post_meta( $post->ID, '_edit_last' );
|
395 |
+
|
396 |
+
return wp_update_post([
|
397 |
+
'ID' => $post->ID,
|
398 |
+
'post_content' => get_post_meta( $post->ID, 'content', true ),
|
399 |
+
'post_date' => get_post_meta( $post->ID, 'date', true ),
|
400 |
+
'post_title' => get_post_meta( $post->ID, 'title', true ),
|
401 |
+
]);
|
402 |
+
}
|
403 |
+
|
404 |
+
/**
|
405 |
+
* Sets an option to the plugin settings array using dot notation
|
406 |
+
*
|
407 |
+
* @param mixed $value
|
408 |
+
* @param string $path
|
409 |
+
* @param string $suffix
|
410 |
+
*
|
411 |
+
* @return bool
|
412 |
+
*/
|
413 |
+
public function setOption( $value, $path = '', $suffix = 'settings' )
|
414 |
+
{
|
415 |
+
$option = get_option( "{$this->app->prefix}_{$suffix}", [] );
|
416 |
+
|
417 |
+
$option = $this->convertDotNotationPath( $path, $value, $option );
|
418 |
+
|
419 |
+
return update_option( "{$this->app->prefix}_{$suffix}", $option );
|
420 |
+
}
|
421 |
+
|
422 |
+
/**
|
423 |
+
* Sets the default settings
|
424 |
+
*
|
425 |
+
* @return array
|
426 |
+
*/
|
427 |
+
public function setDefaultSettings( array $args = [] )
|
428 |
+
{
|
429 |
+
$defaults = [
|
430 |
+
'data' => null,
|
431 |
+
'merge' => true,
|
432 |
+
'update' => true,
|
433 |
+
];
|
434 |
+
|
435 |
+
$args = shortcode_atts( $defaults, $args );
|
436 |
+
|
437 |
+
$currentSettings = $args['merge']
|
438 |
+
? get_option( "{$this->app->prefix}_settings", [] )
|
439 |
+
: [];
|
440 |
+
|
441 |
+
$currentSettings = $this->removeEmptyValuesFrom( $currentSettings );
|
442 |
+
$defaultSettings = [];
|
443 |
+
|
444 |
+
$args['data'] ?: $args['data'] = $this->app->getDefaults();
|
445 |
+
|
446 |
+
foreach( $args['data'] as $path => $value ) {
|
447 |
+
// Don't save the default selector values as they are used anyway by default.
|
448 |
+
if( !!$args['update'] && strpos( $path, '.selectors.' ) !== false ) {
|
449 |
+
$value = '';
|
450 |
+
}
|
451 |
+
|
452 |
+
$defaultSettings = $this->convertDotNotationPath( $path, $value, $defaultSettings );
|
453 |
+
}
|
454 |
+
|
455 |
+
$settings = array_replace_recursive( $defaultSettings, $currentSettings );
|
456 |
+
|
457 |
+
if( $args['update'] ) {
|
458 |
+
update_option( "{$this->app->prefix}_settings", $settings );
|
459 |
+
}
|
460 |
+
|
461 |
+
return $settings;
|
462 |
+
}
|
463 |
+
|
464 |
+
/**
|
465 |
+
* Gets a value from an array using a dot-notation path
|
466 |
+
*
|
467 |
+
* @param mixed $value
|
468 |
+
* @param string $path
|
469 |
+
* @param mixed $fallback
|
470 |
+
*
|
471 |
+
* @return mixed
|
472 |
+
*/
|
473 |
+
protected function getDotNotationValue( $value, $path, $fallback )
|
474 |
+
{
|
475 |
+
if( empty( $path ) ) {
|
476 |
+
return $value;
|
477 |
+
}
|
478 |
+
|
479 |
+
$keys = explode( '.', $path );
|
480 |
+
|
481 |
+
foreach( $keys as $key ) {
|
482 |
+
if( !isset( $value[ $key ] ) ) {
|
483 |
+
return $fallback;
|
484 |
+
}
|
485 |
+
$value = $value[ $key ];
|
486 |
+
}
|
487 |
+
|
488 |
+
return $value;
|
489 |
+
}
|
490 |
+
|
491 |
+
/**
|
492 |
+
* Convert a dot-notation path to an array
|
493 |
+
*
|
494 |
+
* @param string $path
|
495 |
+
* @param mixed $value
|
496 |
+
* @param mixed $option
|
497 |
+
*
|
498 |
+
* @return array
|
499 |
+
*/
|
500 |
+
protected function convertDotNotationPath( $path, $value, $option )
|
501 |
+
{
|
502 |
+
$token = strtok( $path, '.' );
|
503 |
+
|
504 |
+
$ref = &$option;
|
505 |
+
|
506 |
+
while( $token !== false ) {
|
507 |
+
$ref = is_array( $ref ) ? $ref : [];
|
508 |
+
$ref = &$ref[ $token ];
|
509 |
+
$token = strtok( '.' );
|
510 |
+
}
|
511 |
+
|
512 |
+
$ref = $value;
|
513 |
+
|
514 |
+
return $option;
|
515 |
+
}
|
516 |
+
|
517 |
+
/**
|
518 |
+
* Removes empty values from an array
|
519 |
+
*
|
520 |
+
* @return array
|
521 |
+
*/
|
522 |
+
protected function removeEmptyValuesFrom( array $array )
|
523 |
+
{
|
524 |
+
$result = [];
|
525 |
+
|
526 |
+
foreach( $array as $key => $value ) {
|
527 |
+
if( !$value )continue;
|
528 |
+
$result[ $key ] = is_array( $value )
|
529 |
+
? $this->removeEmptyValuesFrom( $value )
|
530 |
+
: $value;
|
531 |
+
}
|
532 |
+
|
533 |
+
return $result;
|
534 |
+
}
|
535 |
+
}
|
plugin/Email.php
ADDED
@@ -0,0 +1,245 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\App;
|
14 |
+
|
15 |
+
class Email
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* @var App
|
19 |
+
*/
|
20 |
+
protected $app;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @var array
|
24 |
+
*/
|
25 |
+
protected $attachments;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* @var array
|
29 |
+
*/
|
30 |
+
protected $headers;
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @var string
|
34 |
+
*/
|
35 |
+
protected $message;
|
36 |
+
|
37 |
+
/**
|
38 |
+
* @var string
|
39 |
+
*/
|
40 |
+
protected $subject;
|
41 |
+
|
42 |
+
/**
|
43 |
+
* @var string
|
44 |
+
*/
|
45 |
+
protected $to;
|
46 |
+
|
47 |
+
public function __construct( App $app )
|
48 |
+
{
|
49 |
+
$this->app = $app;
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* @return Email
|
54 |
+
*/
|
55 |
+
public function compose( array $email )
|
56 |
+
{
|
57 |
+
$email = $this->normalize( $email );
|
58 |
+
|
59 |
+
$this->attachments = $email['attachments'];
|
60 |
+
$this->headers = $this->buildHeaders( $email );
|
61 |
+
$this->message = $this->buildHtmlMessage( $email );
|
62 |
+
$this->subject = $email['subject'];
|
63 |
+
$this->to = $email['to'];
|
64 |
+
|
65 |
+
add_action( 'phpmailer_init', [ $this, 'buildPlainTextMessage'] );
|
66 |
+
|
67 |
+
return $this;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* @return string|null
|
72 |
+
*/
|
73 |
+
public function read( $plaintext = false )
|
74 |
+
{
|
75 |
+
if( !!$plaintext ) {
|
76 |
+
$message = $this->stripHtmlTags( $this->message );
|
77 |
+
|
78 |
+
return apply_filters( 'site-reviews/email/message', $message, 'text', $this );
|
79 |
+
}
|
80 |
+
|
81 |
+
return $this->message;
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* @return bool|null
|
86 |
+
*/
|
87 |
+
public function send()
|
88 |
+
{
|
89 |
+
if( !$this->message || !$this->subject || !$this->to )return;
|
90 |
+
|
91 |
+
$sent = wp_mail(
|
92 |
+
$this->to,
|
93 |
+
$this->subject,
|
94 |
+
$this->message,
|
95 |
+
$this->headers,
|
96 |
+
$this->attachments
|
97 |
+
);
|
98 |
+
|
99 |
+
$this->reset();
|
100 |
+
|
101 |
+
return $sent;
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* @return void
|
106 |
+
*
|
107 |
+
* @action phpmailer_init
|
108 |
+
*/
|
109 |
+
public function buildPlainTextMessage( $phpmailer )
|
110 |
+
{
|
111 |
+
if( $phpmailer->ContentType === 'text/plain' || !empty( $phpmailer->AltBody ) )return;
|
112 |
+
|
113 |
+
$message = $this->stripHtmlTags( $phpmailer->Body );
|
114 |
+
|
115 |
+
$phpmailer->AltBody = apply_filters( 'site-reviews/email/message', $message, 'text', $this );
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* @return array
|
120 |
+
*/
|
121 |
+
protected function buildHeaders( $email )
|
122 |
+
{
|
123 |
+
$allowed = [
|
124 |
+
'bcc',
|
125 |
+
'cc',
|
126 |
+
'from',
|
127 |
+
'reply-to',
|
128 |
+
];
|
129 |
+
|
130 |
+
$headers = array_intersect_key( $email, array_flip( $allowed ) );
|
131 |
+
$headers = array_filter( $headers );
|
132 |
+
|
133 |
+
foreach( $headers as $key => $value ) {
|
134 |
+
unset( $headers[ $key ] );
|
135 |
+
$headers[] = "{$key}: {$value}";
|
136 |
+
}
|
137 |
+
|
138 |
+
$headers[] = 'Content-Type: text/html';
|
139 |
+
|
140 |
+
return apply_filters( 'site-reviews/email/headers', $headers, $this );
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* @return string
|
145 |
+
*/
|
146 |
+
protected function buildHtmlMessage( $email )
|
147 |
+
{
|
148 |
+
$html = $this->app->make( 'Html' );
|
149 |
+
|
150 |
+
$template = trim( $this->app->make( 'Database' )->getOption( 'general.notification_message' ) );
|
151 |
+
|
152 |
+
if( !empty( $template ) ) {
|
153 |
+
$message = $html->renderTemplateString( $template, $email['template-tags'], 'return' );
|
154 |
+
}
|
155 |
+
else if( $email['template'] ) {
|
156 |
+
$message = $html->renderTemplate( "email/templates/{$email['template']}", $email['template-tags'], 'return' );
|
157 |
+
}
|
158 |
+
|
159 |
+
if( !isset( $message ) ) {
|
160 |
+
$message = $email['message'];
|
161 |
+
}
|
162 |
+
|
163 |
+
$message = $email['before'] . $message . $email['after'];
|
164 |
+
|
165 |
+
$body = $html->renderTemplate( 'email/index', [], 'return' );
|
166 |
+
|
167 |
+
$message = strip_shortcodes( $message );
|
168 |
+
$message = wptexturize( $message );
|
169 |
+
$message = wpautop( $message );
|
170 |
+
$message = str_replace( '<> ', '', $message );
|
171 |
+
$message = str_replace( ']]>', ']]>', $message );
|
172 |
+
$message = str_replace( '{message}', $message, $body );
|
173 |
+
$message = stripslashes( $message );
|
174 |
+
|
175 |
+
return apply_filters( 'site-reviews/email/message', $message, 'html', $this );
|
176 |
+
}
|
177 |
+
|
178 |
+
/**
|
179 |
+
* @return array
|
180 |
+
*/
|
181 |
+
protected function normalize( $email )
|
182 |
+
{
|
183 |
+
$fromName = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
|
184 |
+
$fromEmail = get_option( 'admin_email' );
|
185 |
+
|
186 |
+
$defaults = [
|
187 |
+
'after' => '',
|
188 |
+
'attachments' => [],
|
189 |
+
'bcc' => '',
|
190 |
+
'before' => '',
|
191 |
+
'cc' => '',
|
192 |
+
'from' => "{$fromName} <{$fromEmail}>",
|
193 |
+
'message' => '',
|
194 |
+
'reply-to' => '',
|
195 |
+
'subject' => '',
|
196 |
+
'template' => '',
|
197 |
+
'template-tags' => [],
|
198 |
+
'to' => '',
|
199 |
+
];
|
200 |
+
|
201 |
+
$email = shortcode_atts( $defaults, $email );
|
202 |
+
|
203 |
+
!empty( $email['reply-to'] ) ?: $email['reply-to'] = $email['from'];
|
204 |
+
|
205 |
+
return apply_filters( 'site-reviews/email/compose', $email, $this );
|
206 |
+
}
|
207 |
+
|
208 |
+
/**
|
209 |
+
* @return void
|
210 |
+
*/
|
211 |
+
protected function reset()
|
212 |
+
{
|
213 |
+
$this->attachments = [];
|
214 |
+
$this->headers = [];
|
215 |
+
$this->message = null;
|
216 |
+
$this->subject = null;
|
217 |
+
$this->to = null;
|
218 |
+
}
|
219 |
+
|
220 |
+
/**
|
221 |
+
* @return string
|
222 |
+
*/
|
223 |
+
protected function stripHtmlTags( $string )
|
224 |
+
{
|
225 |
+
// remove invisible elements
|
226 |
+
$string = preg_replace( '@<(embed|head|noembed|noscript|object|script|style)[^>]*?>.*?</\\1>@siu', '', $string );
|
227 |
+
|
228 |
+
// replace certain elements with a line-break
|
229 |
+
$string = preg_replace( '@</(div|h[1-9]|p|pre|tr)@iu', "\r\n\$0", $string );
|
230 |
+
|
231 |
+
// replace other elements with a space
|
232 |
+
$string = preg_replace( '@</(td|th)@iu', " \$0", $string );
|
233 |
+
|
234 |
+
// add a placeholder for plain-text bullets to list elements
|
235 |
+
$string = preg_replace( '@<(li)[^>]*?>@siu', "\$0-o-^-o-", $string );
|
236 |
+
|
237 |
+
// strip all remaining HTML tags
|
238 |
+
$string = wp_strip_all_tags( $string );
|
239 |
+
$string = wp_specialchars_decode( $string, ENT_QUOTES );
|
240 |
+
$string = preg_replace( '/\v(?:[\v\h]+){2,}/', "\r\n\r\n", $string );
|
241 |
+
$string = str_replace( '-o-^-o-', ' - ', $string );
|
242 |
+
|
243 |
+
return html_entity_decode( $string, ENT_QUOTES, 'UTF-8' );
|
244 |
+
}
|
245 |
+
}
|
plugin/Handlers/EnqueueAssets.php
ADDED
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Handlers;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\App;
|
14 |
+
use GeminiLabs\SiteReviews\Commands\EnqueueAssets as Command;
|
15 |
+
|
16 |
+
class EnqueueAssets
|
17 |
+
{
|
18 |
+
/**
|
19 |
+
* @var App
|
20 |
+
*/
|
21 |
+
protected $app;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var array
|
25 |
+
*/
|
26 |
+
protected $dependencies;
|
27 |
+
|
28 |
+
public function __construct( App $app )
|
29 |
+
{
|
30 |
+
$this->app = $app;
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* @return void
|
35 |
+
*/
|
36 |
+
public function handle( Command $command )
|
37 |
+
{
|
38 |
+
$this->dependencies = $this->app->make( 'Html' )->getDependencies();
|
39 |
+
|
40 |
+
if( is_admin() ) {
|
41 |
+
$this->enqueueAdmin( $command );
|
42 |
+
}
|
43 |
+
else {
|
44 |
+
$this->enqueuePublic( $command );
|
45 |
+
}
|
46 |
+
|
47 |
+
wp_localize_script( $command->handle, 'site_reviews', [
|
48 |
+
'action' => $this->app->prefix . '_action',
|
49 |
+
'ajaxurl' => wp_nonce_url( admin_url( 'admin-ajax.php' ), "{$this->app->id}-ajax-nonce" ),
|
50 |
+
]);
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Enqueue admin assets
|
55 |
+
*
|
56 |
+
* @return void
|
57 |
+
*/
|
58 |
+
public function enqueueAdmin( Command $command )
|
59 |
+
{
|
60 |
+
$screen = get_current_screen();
|
61 |
+
|
62 |
+
$dependencies = array_merge( $this->dependencies, ['jquery'] );
|
63 |
+
|
64 |
+
wp_enqueue_style(
|
65 |
+
$command->handle,
|
66 |
+
$command->url . 'css/site-reviews-admin.css',
|
67 |
+
[],
|
68 |
+
$command->version
|
69 |
+
);
|
70 |
+
|
71 |
+
if( !( $screen->post_type == 'site-review'
|
72 |
+
|| $screen->base == 'post'
|
73 |
+
|| $screen->id == 'dashboard'
|
74 |
+
|| $screen->id == 'widgets'
|
75 |
+
))return;
|
76 |
+
|
77 |
+
wp_enqueue_script(
|
78 |
+
$command->handle,
|
79 |
+
$command->url . 'js/site-reviews-admin.js',
|
80 |
+
$dependencies,
|
81 |
+
$command->version,
|
82 |
+
true
|
83 |
+
);
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Enqueue public assets
|
88 |
+
*
|
89 |
+
* @return void
|
90 |
+
*/
|
91 |
+
public function enqueuePublic( Command $command )
|
92 |
+
{
|
93 |
+
$currentTheme = sanitize_title( wp_get_theme()->get( 'Name' ) );
|
94 |
+
|
95 |
+
$stylesheet = file_exists( $command->path . "css/{$currentTheme}.css" )
|
96 |
+
? $command->url . "css/{$currentTheme}.css"
|
97 |
+
: $command->url . 'css/site-reviews.css';
|
98 |
+
|
99 |
+
if( apply_filters( 'site-reviews/assets/css', true ) ) {
|
100 |
+
wp_enqueue_style(
|
101 |
+
$command->handle,
|
102 |
+
$stylesheet,
|
103 |
+
[],
|
104 |
+
$command->version
|
105 |
+
);
|
106 |
+
}
|
107 |
+
|
108 |
+
if( !apply_filters( 'site-reviews/assets/js', true ) )return;
|
109 |
+
|
110 |
+
wp_enqueue_script(
|
111 |
+
$command->handle,
|
112 |
+
$command->url . 'js/site-reviews.js',
|
113 |
+
['jquery'],
|
114 |
+
$command->version,
|
115 |
+
true
|
116 |
+
);
|
117 |
+
}
|
118 |
+
}
|
plugin/Handlers/RegisterPointers.php
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Handlers;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\App;
|
14 |
+
use GeminiLabs\SiteReviews\Commands\RegisterPointers as Command;
|
15 |
+
|
16 |
+
class RegisterPointers
|
17 |
+
{
|
18 |
+
protected $app;
|
19 |
+
|
20 |
+
public function __construct( App $app )
|
21 |
+
{
|
22 |
+
$this->app = $app;
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Add pointers to the current screen if they have not yet been dismissed
|
27 |
+
*
|
28 |
+
* return void
|
29 |
+
*/
|
30 |
+
public function handle( Command $command )
|
31 |
+
{
|
32 |
+
$screen = get_current_screen();
|
33 |
+
|
34 |
+
// Get dismissed pointers
|
35 |
+
$dismissed = get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true );
|
36 |
+
$dismissed = explode( ',', (string) $dismissed );
|
37 |
+
|
38 |
+
$pointers = [];
|
39 |
+
|
40 |
+
foreach( $command->pointers as $pointer ) {
|
41 |
+
|
42 |
+
if( $pointer['screen'] != $screen->id || in_array( $pointer['id'], $dismissed ) )continue;
|
43 |
+
|
44 |
+
$pointers[] = [
|
45 |
+
'id' => $pointer['id'],
|
46 |
+
'screen' => $pointer['screen'],
|
47 |
+
'target' => $pointer['target'],
|
48 |
+
'options' => [
|
49 |
+
'content' => sprintf( '<h3>%s</h3><p>%s</p>', $pointer['title'], $pointer['content'] ),
|
50 |
+
'position' => $pointer['position'],
|
51 |
+
],
|
52 |
+
];
|
53 |
+
}
|
54 |
+
|
55 |
+
wp_localize_script( $this->app->id, 'site_reviews_pointers', [
|
56 |
+
'pointers' => $pointers,
|
57 |
+
]);
|
58 |
+
|
59 |
+
if( empty( $pointers ) )return;
|
60 |
+
|
61 |
+
wp_enqueue_style( 'wp-pointer' );
|
62 |
+
wp_enqueue_script( 'wp-pointer' );
|
63 |
+
}
|
64 |
+
}
|
plugin/Handlers/RegisterPostType.php
ADDED
@@ -0,0 +1,640 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Handlers;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\App;
|
14 |
+
use GeminiLabs\SiteReviews\Commands\RegisterPostType as Command;
|
15 |
+
use GeminiLabs\SiteReviews\Strings;
|
16 |
+
use WP_Query;
|
17 |
+
|
18 |
+
class RegisterPostType
|
19 |
+
{
|
20 |
+
/**
|
21 |
+
* @var App
|
22 |
+
*/
|
23 |
+
protected $app;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var array
|
27 |
+
*/
|
28 |
+
protected $columns;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* @var Database
|
32 |
+
*/
|
33 |
+
protected $db;
|
34 |
+
|
35 |
+
public function __construct( App $app )
|
36 |
+
{
|
37 |
+
$this->app = $app;
|
38 |
+
$this->db = $app->make( 'Database' );
|
39 |
+
}
|
40 |
+
|
41 |
+
public function handle( Command $command )
|
42 |
+
{
|
43 |
+
extract( $command->args );
|
44 |
+
|
45 |
+
$this->columns = $columns;
|
46 |
+
|
47 |
+
if( in_array( $post_type, get_post_types(['_builtin' => true ]) ) )return;
|
48 |
+
|
49 |
+
$post_type = empty( $post_type )
|
50 |
+
? sanitize_title( $single )
|
51 |
+
: $post_type;
|
52 |
+
|
53 |
+
$slug = empty( $slug )
|
54 |
+
? sanitize_title( $plural )
|
55 |
+
: $slug;
|
56 |
+
|
57 |
+
$menu_name = empty( $menu_name )
|
58 |
+
? $plural
|
59 |
+
: $menu_name;
|
60 |
+
|
61 |
+
$show_in_nav_menus = isset( $args['show_in_nav_menus'] )
|
62 |
+
? $args['show_in_nav_menus']
|
63 |
+
: $public;
|
64 |
+
|
65 |
+
$show_ui = !isset( $args['show_ui'] ) ?: $args['show_ui'];
|
66 |
+
|
67 |
+
$exclude_from_search = isset( $args['exclude_from_search'] )
|
68 |
+
? $args['exclude_from_search']
|
69 |
+
: !$public;
|
70 |
+
|
71 |
+
$publicly_queryable = isset( $args['publicly_queryable'] )
|
72 |
+
? $args['publicly_queryable']
|
73 |
+
: $public;
|
74 |
+
|
75 |
+
$labels['singular_name'] = $single;
|
76 |
+
$labels['name'] = $plural;
|
77 |
+
$labels['menu_name'] = $menu_name;
|
78 |
+
|
79 |
+
$args = [
|
80 |
+
'description' => '',
|
81 |
+
'labels' => $labels,
|
82 |
+
'taxonomies' => $taxonomies,
|
83 |
+
'supports' => $supports,
|
84 |
+
'map_meta_cap' => $map_meta_cap,
|
85 |
+
'menu_position' => $menu_position,
|
86 |
+
'menu_icon' => $menu_icon,
|
87 |
+
'has_archive' => $has_archive,
|
88 |
+
'public' => $public,
|
89 |
+
'show_in_nav_menus' => $show_in_nav_menus,
|
90 |
+
'show_ui' => $show_ui,
|
91 |
+
'exclude_from_search' => $exclude_from_search,
|
92 |
+
'publicly_queryable' => $publicly_queryable,
|
93 |
+
'capabilities' => ['create_posts' => "create_{$slug}"],
|
94 |
+
'hierarchical' => $hierarchical,
|
95 |
+
'rewrite' => $rewrite,
|
96 |
+
'query_var' => $query_var,
|
97 |
+
];
|
98 |
+
|
99 |
+
register_post_type( $post_type, $args );
|
100 |
+
|
101 |
+
$this->performHooks( $post_type );
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Removes the autosave functionality
|
106 |
+
*
|
107 |
+
* @return void
|
108 |
+
*
|
109 |
+
* @action admin_print_scripts-post.php
|
110 |
+
*/
|
111 |
+
public function disableAutosave()
|
112 |
+
{
|
113 |
+
if( $this->isEditLocalReviewPage() )return;
|
114 |
+
|
115 |
+
global $post;
|
116 |
+
|
117 |
+
if( $post->post_type == 'site-review' ) {
|
118 |
+
wp_deregister_script( 'autosave' );
|
119 |
+
}
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Removes the slug metabox
|
124 |
+
*
|
125 |
+
* @return void
|
126 |
+
*
|
127 |
+
* @action admin_menu
|
128 |
+
*/
|
129 |
+
public function disableMetaBoxes()
|
130 |
+
{
|
131 |
+
remove_meta_box( 'slugdiv', 'site-review', 'advanced' );
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* Creates the custom post_type columns
|
136 |
+
*
|
137 |
+
* @return array
|
138 |
+
*
|
139 |
+
* @filter manage_{$post_type}_posts_columns
|
140 |
+
*/
|
141 |
+
public function manageColumns( array $columns )
|
142 |
+
{
|
143 |
+
$this->columns = ['cb' => ''] + $this->columns;
|
144 |
+
|
145 |
+
array_walk( $this->columns, function( &$value, $key ) use ( $columns ) {
|
146 |
+
if( array_key_exists( $key, $columns ) && empty( $value ) ) {
|
147 |
+
$value = $columns[ $key ];
|
148 |
+
}
|
149 |
+
else if( $key === 'sticky' ) {
|
150 |
+
// wrap in <span> so we can replace with a dashicon in CSS @media
|
151 |
+
$value = "<span class=\"pinned-icon\">{$value}</span>";
|
152 |
+
|
153 |
+
global $wp_version;
|
154 |
+
|
155 |
+
// WP < 4.4 support
|
156 |
+
if( version_compare( $wp_version, '4.4', '<' ) ) {
|
157 |
+
$value .= file_get_contents( "{$this->app->path}assets/img/pinned.svg" );
|
158 |
+
}
|
159 |
+
}
|
160 |
+
});
|
161 |
+
|
162 |
+
$sites = $this->db->getMetaValues( 'site_name' );
|
163 |
+
|
164 |
+
if( count( $sites ) < 1 || ( count( $sites ) == 1 && $sites[0] == 'local' ) ) {
|
165 |
+
unset( $this->columns['site'] );
|
166 |
+
}
|
167 |
+
|
168 |
+
// remove all keys with null, false, or empty values
|
169 |
+
return array_filter( $this->columns, 'strlen' );
|
170 |
+
}
|
171 |
+
|
172 |
+
/**
|
173 |
+
* Sets which custom post_type columns are sortable
|
174 |
+
*
|
175 |
+
* @return array
|
176 |
+
*
|
177 |
+
* @filter manage_edit-{$post_type}_sortable_columns
|
178 |
+
*/
|
179 |
+
public function manageSortableColumns( array $columns )
|
180 |
+
{
|
181 |
+
$columns['reviewer'] = 'author';
|
182 |
+
$columns['site'] = 'site_name';
|
183 |
+
$columns['stars'] = 'rating';
|
184 |
+
$columns['sticky'] = 'pinned';
|
185 |
+
|
186 |
+
return $columns;
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Modifies the WP_Query meta_query value
|
191 |
+
*
|
192 |
+
* @return void
|
193 |
+
*
|
194 |
+
* @action pre_get_posts
|
195 |
+
*/
|
196 |
+
public function modifyColumnMetaQuery( WP_Query $query )
|
197 |
+
{
|
198 |
+
global $pagenow;
|
199 |
+
|
200 |
+
if( !is_admin()
|
201 |
+
|| !$query->is_main_query()
|
202 |
+
|| $query->query['post_type'] != 'site-review'
|
203 |
+
|| $pagenow != 'edit.php'
|
204 |
+
)return;
|
205 |
+
|
206 |
+
$meta_keys = [
|
207 |
+
'rating',
|
208 |
+
'site_name',
|
209 |
+
];
|
210 |
+
|
211 |
+
foreach( $meta_keys as $key ) {
|
212 |
+
if( $value = filter_input( INPUT_GET, $key ) ) {
|
213 |
+
$query->query_vars['meta_query'][] = [
|
214 |
+
'key' => $key,
|
215 |
+
'value' => $value,
|
216 |
+
];
|
217 |
+
}
|
218 |
+
}
|
219 |
+
}
|
220 |
+
|
221 |
+
/**
|
222 |
+
* Modifies the WP_Query orderby value
|
223 |
+
*
|
224 |
+
* @return void
|
225 |
+
*
|
226 |
+
* @action pre_get_posts
|
227 |
+
*/
|
228 |
+
public function modifyColumnOrderby( WP_Query $query )
|
229 |
+
{
|
230 |
+
global $pagenow;
|
231 |
+
|
232 |
+
if( !is_admin()
|
233 |
+
|| !$query->is_main_query()
|
234 |
+
|| $query->query['post_type'] != 'site-review'
|
235 |
+
|| $pagenow != 'edit.php'
|
236 |
+
)return;
|
237 |
+
|
238 |
+
$orderby = $query->get( 'orderby' );
|
239 |
+
|
240 |
+
switch( $orderby ) {
|
241 |
+
case 'author':
|
242 |
+
case 'site_name':
|
243 |
+
case 'rating':
|
244 |
+
case 'pinned':
|
245 |
+
$query->set( 'meta_key', $orderby );
|
246 |
+
$query->set( 'orderby', 'meta_value' );
|
247 |
+
break;
|
248 |
+
}
|
249 |
+
}
|
250 |
+
|
251 |
+
/**
|
252 |
+
* Modifies the WP_Editor settings
|
253 |
+
*
|
254 |
+
* @return array
|
255 |
+
*
|
256 |
+
* @action wp_editor_settings
|
257 |
+
*/
|
258 |
+
public function modifyContentEditor( array $settings )
|
259 |
+
{
|
260 |
+
if( $this->isEditLocalReviewPage() !== true ) {
|
261 |
+
return $settings;
|
262 |
+
}
|
263 |
+
|
264 |
+
return [
|
265 |
+
'textarea_rows' => 12,
|
266 |
+
'media_buttons' => false,
|
267 |
+
'quicktags' => false,
|
268 |
+
'tinymce' => false,
|
269 |
+
];
|
270 |
+
}
|
271 |
+
|
272 |
+
/**
|
273 |
+
* Modify the WP_Editor html to allow autosizing without breaking the `editor-expand` script
|
274 |
+
*
|
275 |
+
* @param string $html
|
276 |
+
*
|
277 |
+
* @return string
|
278 |
+
*
|
279 |
+
* @action the_editor
|
280 |
+
*/
|
281 |
+
public function modifyContentEditorHtml( $html )
|
282 |
+
{
|
283 |
+
if( $this->isEditLocalReviewPage() !== true ) {
|
284 |
+
return $html;
|
285 |
+
}
|
286 |
+
|
287 |
+
return str_replace( '<textarea', '<div id="ed_toolbar"></div><textarea', $html );
|
288 |
+
}
|
289 |
+
|
290 |
+
/**
|
291 |
+
* Customize the bulk updated messages array for this post_type
|
292 |
+
*
|
293 |
+
* return array
|
294 |
+
*/
|
295 |
+
public function modifyPostTypeBulkMessages( array $messages, array $counts )
|
296 |
+
{
|
297 |
+
$messages['site-review'] = [
|
298 |
+
'updated' => _n( '%s review updated.', '%s posts updated.', $counts['updated'], 'site-reviews' ),
|
299 |
+
'locked' => _n( '%s review not updated, somebody is editing it.', '%s reviews not updated, somebody is editing them.', $counts['locked'], 'site-reviews' ),
|
300 |
+
'deleted' => _n( '%s review permanently deleted.', '%s reviews permanently deleted.', $counts['deleted'], 'site-reviews' ),
|
301 |
+
'trashed' => _n( '%s review moved to the Trash.', '%s reviews moved to the Trash.', $counts['trashed'], 'site-reviews' ),
|
302 |
+
'untrashed' => _n( '%s review restored from the Trash.', '%s reviews restored from the Trash.', $counts['untrashed'], 'site-reviews' ),
|
303 |
+
];
|
304 |
+
|
305 |
+
return $messages;
|
306 |
+
}
|
307 |
+
|
308 |
+
/**
|
309 |
+
* Customize the updated messages array for this post_type
|
310 |
+
*
|
311 |
+
* return array
|
312 |
+
*/
|
313 |
+
public function modifyPostTypeMessages( array $messages )
|
314 |
+
{
|
315 |
+
global $post;
|
316 |
+
|
317 |
+
if( !isset( $post->ID ) || !$post->ID ) {
|
318 |
+
return $messages;
|
319 |
+
}
|
320 |
+
|
321 |
+
$strings = (new Strings)->post_updated_messages();
|
322 |
+
|
323 |
+
$restored = filter_input( INPUT_GET, 'revision' );
|
324 |
+
$restored = $restored
|
325 |
+
? sprintf( $strings['restored'], wp_post_revision_title( (int) $restored, false ) )
|
326 |
+
: false;
|
327 |
+
|
328 |
+
$scheduled_date = date_i18n( 'M j, Y @ H:i', strtotime( $post->post_date ) );
|
329 |
+
|
330 |
+
$messages['site-review'] = [
|
331 |
+
1 => $strings['updated'],
|
332 |
+
4 => $strings['updated'],
|
333 |
+
5 => $restored,
|
334 |
+
6 => $strings['published'],
|
335 |
+
7 => $strings['saved'],
|
336 |
+
8 => $strings['submitted'],
|
337 |
+
9 => sprintf( $strings['scheduled'], sprintf( '<strong>%s</strong>', $scheduled_date ) ),
|
338 |
+
10 => $strings['draft_updated'],
|
339 |
+
50 => $strings['approved'],
|
340 |
+
51 => $strings['unapproved'],
|
341 |
+
52 => $strings['reverted'],
|
342 |
+
];
|
343 |
+
|
344 |
+
return $messages;
|
345 |
+
}
|
346 |
+
|
347 |
+
/**
|
348 |
+
* Prints the column filters
|
349 |
+
*
|
350 |
+
* @param string $post_type
|
351 |
+
*
|
352 |
+
* @return void
|
353 |
+
*
|
354 |
+
* @action restrict_manage_posts
|
355 |
+
*/
|
356 |
+
public function printColumnFilters( $post_type )
|
357 |
+
{
|
358 |
+
// WP < 4.4 support
|
359 |
+
if( !$post_type ) {
|
360 |
+
$screen = get_current_screen();
|
361 |
+
$post_type = $screen->post_type;
|
362 |
+
}
|
363 |
+
|
364 |
+
if( $post_type !== 'site-review' )return;
|
365 |
+
|
366 |
+
$status = filter_input( INPUT_GET, 'post_status' );
|
367 |
+
$status = $status ? $status : 'publish';
|
368 |
+
|
369 |
+
$ratings = $this->db->getMetaValues( 'rating', $status );
|
370 |
+
|
371 |
+
$this->renderRatingsFilter( $ratings );
|
372 |
+
|
373 |
+
$sites = $this->db->getMetaValues( 'site_name', $status );
|
374 |
+
|
375 |
+
if( count( $sites ) == 1 && $sites[0] == 'local' )return;
|
376 |
+
|
377 |
+
$this->renderSitesFilter( $sites );
|
378 |
+
}
|
379 |
+
|
380 |
+
/**
|
381 |
+
* Prints the custom column values
|
382 |
+
*
|
383 |
+
* @param string $column
|
384 |
+
*
|
385 |
+
* @return void
|
386 |
+
*
|
387 |
+
* @action manage_{$post_type}_posts_custom_column
|
388 |
+
*/
|
389 |
+
public function printColumnValues( $column )
|
390 |
+
{
|
391 |
+
global $post;
|
392 |
+
global $wp_version;
|
393 |
+
|
394 |
+
switch( $column ) {
|
395 |
+
|
396 |
+
case 'slug':
|
397 |
+
echo $post->post_name;
|
398 |
+
break;
|
399 |
+
|
400 |
+
case 'featured':
|
401 |
+
case 'image':
|
402 |
+
case 'thumbnail':
|
403 |
+
|
404 |
+
if( has_post_thumbnail( $post->ID ) ) {
|
405 |
+
$img = wp_get_attachment_image( get_post_thumbnail_id( $post->ID ), [96, 48] );
|
406 |
+
}
|
407 |
+
|
408 |
+
echo ( isset( $img ) && !empty( $img ) ) ? $img : '—';
|
409 |
+
break;
|
410 |
+
|
411 |
+
case 'reviewer':
|
412 |
+
echo get_post_meta( $post->ID, 'author', true );
|
413 |
+
break;
|
414 |
+
|
415 |
+
case 'stars':
|
416 |
+
$this->app->make( 'Html' )->renderPartial( 'rating', [
|
417 |
+
'stars' => get_post_meta( $post->ID, 'rating', true ),
|
418 |
+
]);
|
419 |
+
break;
|
420 |
+
|
421 |
+
case 'site':
|
422 |
+
echo ucfirst( get_post_meta( $post->ID, 'site_name', true ) );
|
423 |
+
break;
|
424 |
+
|
425 |
+
case 'sticky':
|
426 |
+
$pinned = get_post_meta( $post->ID, 'pinned', true )
|
427 |
+
? ' pinned'
|
428 |
+
: '';
|
429 |
+
|
430 |
+
// WP < 4.4 support
|
431 |
+
$fallback = version_compare( $wp_version, '4.4', '<' )
|
432 |
+
? file_get_contents( "{$this->app->path}assets/img/pinned.svg" )
|
433 |
+
: '';
|
434 |
+
|
435 |
+
echo sprintf( '<i class="dashicons dashicons-sticky%s" data-id="%s">%s</i>', $pinned, $post->ID, $fallback );
|
436 |
+
break;
|
437 |
+
|
438 |
+
default:
|
439 |
+
echo apply_filters( "_populate_column_{$column}", '', $post->ID );
|
440 |
+
break;
|
441 |
+
}
|
442 |
+
}
|
443 |
+
|
444 |
+
public function revertPost()
|
445 |
+
{
|
446 |
+
$post_id = filter_input( INPUT_GET, 'post' );
|
447 |
+
|
448 |
+
if( !$post_id )return;
|
449 |
+
|
450 |
+
check_admin_referer( 'revert-review_' . $post_id );
|
451 |
+
|
452 |
+
$this->db->revertReview( $post_id );
|
453 |
+
|
454 |
+
wp_redirect( $this->getRedirectUrl( $post_id, 52 ) );
|
455 |
+
|
456 |
+
exit();
|
457 |
+
}
|
458 |
+
|
459 |
+
public function unapprovePost()
|
460 |
+
{
|
461 |
+
$post_id = filter_input( INPUT_GET, 'post' );
|
462 |
+
|
463 |
+
if( !$post_id )return;
|
464 |
+
|
465 |
+
check_admin_referer( 'unapprove-review_' . $post_id );
|
466 |
+
|
467 |
+
$this->changePostStatus( $post_id, 'pending' );
|
468 |
+
|
469 |
+
wp_redirect( $this->getRedirectUrl( $post_id, 51 ) );
|
470 |
+
|
471 |
+
exit();
|
472 |
+
}
|
473 |
+
|
474 |
+
public function approvePost()
|
475 |
+
{
|
476 |
+
$post_id = filter_input( INPUT_GET, 'post' );
|
477 |
+
|
478 |
+
if( !$post_id )return;
|
479 |
+
|
480 |
+
check_admin_referer( 'approve-review_' . $post_id );
|
481 |
+
|
482 |
+
$this->changePostStatus( $post_id, 'publish' );
|
483 |
+
|
484 |
+
wp_redirect( $this->getRedirectUrl( $post_id, 50 ) );
|
485 |
+
|
486 |
+
exit();
|
487 |
+
}
|
488 |
+
|
489 |
+
/**
|
490 |
+
* Set/persist custom permissions for the post_type
|
491 |
+
*
|
492 |
+
* return void
|
493 |
+
*/
|
494 |
+
public function setPermissions()
|
495 |
+
{
|
496 |
+
foreach( wp_roles()->roles as $role => $value ) {
|
497 |
+
wp_roles()->remove_cap( $role, 'create_reviews' );
|
498 |
+
}
|
499 |
+
}
|
500 |
+
|
501 |
+
/**
|
502 |
+
* Remove post_type support for all non-local reviews
|
503 |
+
*
|
504 |
+
* @todo: Move this to addons
|
505 |
+
*
|
506 |
+
* return void
|
507 |
+
*/
|
508 |
+
public function setPostTypeSupport()
|
509 |
+
{
|
510 |
+
if( $this->isEditLocalReviewPage() )return;
|
511 |
+
|
512 |
+
remove_post_type_support( 'site-review', 'title' );
|
513 |
+
remove_post_type_support( 'site-review', 'editor' );
|
514 |
+
}
|
515 |
+
|
516 |
+
/**
|
517 |
+
* @return null|bool
|
518 |
+
*/
|
519 |
+
protected function isEditLocalReviewPage()
|
520 |
+
{
|
521 |
+
$screen = get_current_screen();
|
522 |
+
|
523 |
+
$action = filter_input( INPUT_GET, 'action' );
|
524 |
+
$postId = filter_input( INPUT_GET, 'post' );
|
525 |
+
|
526 |
+
if( $action != 'edit'
|
527 |
+
|| $postId < 1
|
528 |
+
|| $screen->base != 'post'
|
529 |
+
|| $screen->post_type != 'site-review'
|
530 |
+
)return;
|
531 |
+
|
532 |
+
$siteName = get_post_meta( $postId, 'site_name', true );
|
533 |
+
|
534 |
+
if( 'local' === $siteName ) {
|
535 |
+
return true;
|
536 |
+
}
|
537 |
+
}
|
538 |
+
|
539 |
+
/**
|
540 |
+
* @param string $post_type
|
541 |
+
*
|
542 |
+
* @return void
|
543 |
+
*/
|
544 |
+
protected function performHooks( $post_type = '' )
|
545 |
+
{
|
546 |
+
add_action( 'admin_action_approve', [ $this, 'approvePost'] );
|
547 |
+
add_action( 'admin_print_scripts-post.php', [ $this, 'disableAutosave'], 999 );
|
548 |
+
add_action( 'admin_menu', [ $this, 'disableMetaBoxes'] );
|
549 |
+
add_action( 'pre_get_posts', [ $this, 'modifyColumnMetaQuery'] );
|
550 |
+
add_action( 'pre_get_posts', [ $this, 'modifyColumnOrderby'] );
|
551 |
+
add_action( 'restrict_manage_posts', [ $this, 'printColumnFilters'] );
|
552 |
+
add_action( "manage_{$post_type}_posts_custom_column", [ $this, 'printColumnValues'] );
|
553 |
+
add_action( 'admin_action_revert', [ $this, 'revertPost'] );
|
554 |
+
add_action( 'admin_init', [ $this, 'setPermissions'], 999 );
|
555 |
+
add_action( 'current_screen', [ $this, 'setPostTypeSupport'] );
|
556 |
+
add_action( 'admin_action_unapprove', [ $this, 'unapprovePost'] );
|
557 |
+
|
558 |
+
add_filter( "manage_{$post_type}_posts_columns", [ $this, 'manageColumns'] );
|
559 |
+
add_filter( "manage_edit-{$post_type}_sortable_columns", [ $this, 'manageSortableColumns'] );
|
560 |
+
add_filter( 'wp_editor_settings', [ $this, 'modifyContentEditor' ] );
|
561 |
+
add_filter( 'the_editor', [ $this, 'modifyContentEditorHtml'] );
|
562 |
+
add_filter( 'bulk_post_updated_messages', [ $this, 'modifyPostTypeBulkMessages'], 10, 2 );
|
563 |
+
add_filter( 'post_updated_messages', [ $this, 'modifyPostTypeMessages'] );
|
564 |
+
}
|
565 |
+
|
566 |
+
/**
|
567 |
+
* @param int $post_id
|
568 |
+
* @param int $message_index
|
569 |
+
*
|
570 |
+
* @return string
|
571 |
+
*/
|
572 |
+
protected function getRedirectUrl( $post_id, $message_index )
|
573 |
+
{
|
574 |
+
$referer = wp_get_referer();
|
575 |
+
|
576 |
+
return !$referer || strpos( $referer, 'post.php' ) !== false || strpos( $referer, 'post-new.php' ) !== false
|
577 |
+
? add_query_arg( ['message' => $message_index ], get_edit_post_link( $post_id, false ) )
|
578 |
+
: add_query_arg( ['message' => $message_index ], remove_query_arg( ['trashed', 'untrashed', 'deleted', 'ids'], $referer ) );
|
579 |
+
}
|
580 |
+
|
581 |
+
/**
|
582 |
+
* @param array $ratings
|
583 |
+
*
|
584 |
+
* @return void
|
585 |
+
*/
|
586 |
+
protected function renderRatingsFilter( $ratings )
|
587 |
+
{
|
588 |
+
if( empty( $ratings ) )return;
|
589 |
+
|
590 |
+
$ratings = array_flip( array_reverse( $ratings ) );
|
591 |
+
|
592 |
+
array_walk( $ratings, function( &$value, $key ) {
|
593 |
+
$label = _n( '%s star', '%s stars', $key, 'site-reviews' );
|
594 |
+
$value = sprintf( $label, $key );
|
595 |
+
});
|
596 |
+
|
597 |
+
$ratings = [ __( 'All ratings', 'site-reviews' ) ] + $ratings;
|
598 |
+
|
599 |
+
$this->app->make( 'Html' )->renderPartial( 'filterby', [
|
600 |
+
'name' => 'rating',
|
601 |
+
'values' => $ratings,
|
602 |
+
]);
|
603 |
+
}
|
604 |
+
|
605 |
+
/**
|
606 |
+
* @param array $sites
|
607 |
+
*
|
608 |
+
* @return void
|
609 |
+
*/
|
610 |
+
protected function renderSitesFilter( $sites )
|
611 |
+
{
|
612 |
+
if( empty( $sites ) )return;
|
613 |
+
|
614 |
+
$sites = array_combine( $sites, array_map( 'ucfirst', $sites ) );
|
615 |
+
$sites = [ __( 'All types', 'site-reviews' ) ] + $sites;
|
616 |
+
|
617 |
+
if( isset( $sites['local'] ) ) {
|
618 |
+
$sites['local'] = __( 'Local Review', 'site-reviews' );
|
619 |
+
}
|
620 |
+
|
621 |
+
$this->app->make( 'Html' )->renderPartial( 'filterby', [
|
622 |
+
'name' => 'site_name',
|
623 |
+
'values' => $sites,
|
624 |
+
]);
|
625 |
+
}
|
626 |
+
|
627 |
+
/**
|
628 |
+
* @param int $post_id
|
629 |
+
* @param string $status
|
630 |
+
*
|
631 |
+
* @return void
|
632 |
+
*/
|
633 |
+
protected function changePostStatus( $post_id, $status )
|
634 |
+
{
|
635 |
+
return wp_update_post([
|
636 |
+
'ID' => $post_id,
|
637 |
+
'post_status' => $status,
|
638 |
+
]);
|
639 |
+
}
|
640 |
+
}
|
plugin/Handlers/RegisterShortcodes.php
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Handlers;
|
12 |
+
|
13 |
+
use Exception;
|
14 |
+
use GeminiLabs\SiteReviews\App;
|
15 |
+
use GeminiLabs\SiteReviews\Commands\RegisterShortcodes as Command;
|
16 |
+
use ReflectionException;
|
17 |
+
|
18 |
+
class RegisterShortcodes
|
19 |
+
{
|
20 |
+
/**
|
21 |
+
* @var App
|
22 |
+
*/
|
23 |
+
protected $app;
|
24 |
+
|
25 |
+
public function __construct( App $app )
|
26 |
+
{
|
27 |
+
$this->app = $app;
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* return void
|
32 |
+
*/
|
33 |
+
public function handle( Command $command )
|
34 |
+
{
|
35 |
+
foreach( $command->shortcodes as $key ) {
|
36 |
+
|
37 |
+
$shortcodeClass = implode( '', array_map( 'ucfirst', explode( '_', $key ) ) );
|
38 |
+
$shortcodeClass = "GeminiLabs\SiteReviews\Shortcodes\\$shortcodeClass";
|
39 |
+
|
40 |
+
try {
|
41 |
+
$shortcode = $this->app->make( $shortcodeClass );
|
42 |
+
add_shortcode( $key, [ $shortcode, 'printShortcode'] );
|
43 |
+
}
|
44 |
+
catch( Exception $e ) {
|
45 |
+
$this->app->make( 'Log\Logger' )->error( sprintf( 'Error registering shortcode. Message: %s "(%s:%s)"',
|
46 |
+
$e->getMessage(),
|
47 |
+
$e->getFile(),
|
48 |
+
$e->getLine()
|
49 |
+
));
|
50 |
+
}
|
51 |
+
}
|
52 |
+
}
|
53 |
+
}
|
plugin/Handlers/RegisterWidgets.php
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Handlers;
|
12 |
+
|
13 |
+
use Exception;
|
14 |
+
use GeminiLabs\SiteReviews\App;
|
15 |
+
use GeminiLabs\SiteReviews\Commands\RegisterWidgets as Command;
|
16 |
+
|
17 |
+
class RegisterWidgets
|
18 |
+
{
|
19 |
+
/**
|
20 |
+
* @var App
|
21 |
+
*/
|
22 |
+
protected $app;
|
23 |
+
|
24 |
+
public function __construct( App $app )
|
25 |
+
{
|
26 |
+
$this->app = $app;
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* return void
|
31 |
+
*/
|
32 |
+
public function handle( Command $command )
|
33 |
+
{
|
34 |
+
global $wp_widget_factory;
|
35 |
+
|
36 |
+
foreach( $command->widgets as $key => $values ) {
|
37 |
+
|
38 |
+
$widgetClass = implode( '', array_map( 'ucfirst', explode( '_', $key ) ) );
|
39 |
+
$widgetClass = "GeminiLabs\SiteReviews\Widgets\\$widgetClass";
|
40 |
+
|
41 |
+
try {
|
42 |
+
// bypass register_widget() in order to pass our custom values to the widget
|
43 |
+
$widget = new $widgetClass( "{$this->app->id}_{$key}", $values['title'], $values );
|
44 |
+
$wp_widget_factory->widgets[ $widgetClass ] = $widget;
|
45 |
+
}
|
46 |
+
catch( Exception $e ) {
|
47 |
+
$this->app->make( 'Log\Logger' )->error( sprintf( 'Error registering widget. Message: %s "(%s:%s)"',
|
48 |
+
$e->getMessage(),
|
49 |
+
$e->getFile(),
|
50 |
+
$e->getLine()
|
51 |
+
));
|
52 |
+
}
|
53 |
+
}
|
54 |
+
}
|
55 |
+
}
|
plugin/Handlers/SubmitReview.php
ADDED
@@ -0,0 +1,223 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Handlers;
|
12 |
+
|
13 |
+
use Exception;
|
14 |
+
use GeminiLabs\SiteReviews\App;
|
15 |
+
use GeminiLabs\SiteReviews\Commands\SubmitReview as Command;
|
16 |
+
use ReflectionException;
|
17 |
+
|
18 |
+
class SubmitReview
|
19 |
+
{
|
20 |
+
/**
|
21 |
+
* @var App
|
22 |
+
*/
|
23 |
+
protected $app;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var Database
|
27 |
+
*/
|
28 |
+
protected $db;
|
29 |
+
|
30 |
+
public function __construct( App $app )
|
31 |
+
{
|
32 |
+
$this->app = $app;
|
33 |
+
$this->db = $app->make( 'Database' );
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* @todo log any negative result of the sent notification
|
38 |
+
*
|
39 |
+
* return void
|
40 |
+
*/
|
41 |
+
public function handle( Command $command )
|
42 |
+
{
|
43 |
+
$reviewId = 'local_' . md5( serialize( $command ) );
|
44 |
+
|
45 |
+
$review = [
|
46 |
+
'author' => $command->reviewer,
|
47 |
+
'avatar' => get_avatar_url( $command->email ),
|
48 |
+
'content' => $command->content,
|
49 |
+
'email' => $command->email,
|
50 |
+
'ip_address' => $command->ipAddress,
|
51 |
+
'rating' => $command->rating,
|
52 |
+
'review_id' => $reviewId,
|
53 |
+
'site_name' => 'local',
|
54 |
+
'title' => $command->title,
|
55 |
+
];
|
56 |
+
|
57 |
+
$review = apply_filters( 'site-reviews/local/review', $review, $command );
|
58 |
+
|
59 |
+
$post_id = $this->db->postReview( $reviewId, $review );
|
60 |
+
|
61 |
+
$this->sendNotification( $post_id, $command );
|
62 |
+
|
63 |
+
$message = __( 'Your review has been submitted!', 'site-reviews' );
|
64 |
+
|
65 |
+
if( $command->ajaxRequest ) {
|
66 |
+
|
67 |
+
$this->app->make( 'Session' )->clear();
|
68 |
+
|
69 |
+
return $message;
|
70 |
+
}
|
71 |
+
else {
|
72 |
+
// set message
|
73 |
+
$this->app->make( 'Session' )->set( "{$command->formId}-message", $message );
|
74 |
+
|
75 |
+
wp_redirect( $_SERVER['PHP_SELF'] );
|
76 |
+
exit;
|
77 |
+
}
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* @param int $post_id
|
82 |
+
*
|
83 |
+
* @return array
|
84 |
+
*/
|
85 |
+
protected function addNotificationLinks( $post_id, array $args )
|
86 |
+
{
|
87 |
+
if( $this->db->getOption( 'general.require.approval', false ) ) {
|
88 |
+
|
89 |
+
$review_approve_link = wp_nonce_url( admin_url( sprintf( 'post.php?post=%s&action=approve', $post_id ) ), 'approve-review_' . $post_id );
|
90 |
+
$review_discard_link = wp_nonce_url( admin_url( sprintf( 'post.php?post=%s&action=trash', $post_id ) ), 'trash-post_' . $post_id );
|
91 |
+
// $review_discard_link = esc_url( get_delete_post_link( $post_id, false ) );
|
92 |
+
|
93 |
+
$after = [];
|
94 |
+
|
95 |
+
$after[] = sprintf( '%1$s: <a href="%2$s">%2$s</a>', __( 'Approve', 'site-reviews' ), $review_approve_link );
|
96 |
+
$after[] = sprintf( '%1$s: <a href="%2$s">%2$s</a>', __( 'Discard', 'site-reviews' ), $review_discard_link );
|
97 |
+
|
98 |
+
$args['after'] = "\r\n\r\n" . implode( "\r\n\r\n", $after ); // makes each line a paragraph
|
99 |
+
}
|
100 |
+
|
101 |
+
return $args;
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* @return GeminiLabs\SiteReviews\Email
|
106 |
+
*/
|
107 |
+
protected function createNotification( Command $command, array $args = [] )
|
108 |
+
{
|
109 |
+
$email = [
|
110 |
+
'to' => $args['recipient'],
|
111 |
+
'subject' => $args['notification_title'],
|
112 |
+
'template' => 'review-notification',
|
113 |
+
'template-tags' => [
|
114 |
+
'review_author' => $command->reviewer,
|
115 |
+
'review_content' => $command->content,
|
116 |
+
'review_email' => $command->email,
|
117 |
+
'review_ip' => $command->ipAddress,
|
118 |
+
'review_link' => sprintf( '<a href="%1$s">%1$s</a>', $args['notification_link'] ),
|
119 |
+
'review_rating' => $command->rating,
|
120 |
+
'review_title' => $command->title,
|
121 |
+
],
|
122 |
+
];
|
123 |
+
|
124 |
+
// $email = $this->addNotificationLinks( $post_id, $email );
|
125 |
+
|
126 |
+
return $this->app->make( 'Email' )->compose( $email );
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* @param int $post_id
|
131 |
+
*
|
132 |
+
* @return null|bool|array|WP_Error
|
133 |
+
*/
|
134 |
+
protected function sendNotification( $post_id, Command $command )
|
135 |
+
{
|
136 |
+
$notificationType = $this->db->getOption( 'general.notification', 'none' );
|
137 |
+
|
138 |
+
if( !in_array( $notificationType, ['default', 'custom', 'webhook'] ) )return;
|
139 |
+
|
140 |
+
$blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
|
141 |
+
|
142 |
+
$notificationTitle = sprintf( '[%s] %s',
|
143 |
+
$blogname,
|
144 |
+
sprintf( __( 'New %s-Star Review', 'site-reviews' ), $command->rating )
|
145 |
+
);
|
146 |
+
|
147 |
+
$args = [
|
148 |
+
'notification_link' => esc_url( admin_url( sprintf( 'post.php?post=%s&action=edit', $post_id ) ) ),
|
149 |
+
'notification_title' => $notificationTitle,
|
150 |
+
'notification_type' => $notificationType,
|
151 |
+
];
|
152 |
+
|
153 |
+
return $args['notification_type'] == 'webhook'
|
154 |
+
? $this->sendNotificationWebhook( $command, $args )
|
155 |
+
: $this->sendNotificationEmail( $command, $args );
|
156 |
+
}
|
157 |
+
|
158 |
+
/**
|
159 |
+
* @return bool|null
|
160 |
+
*/
|
161 |
+
protected function sendNotificationEmail( Command $command, array $args )
|
162 |
+
{
|
163 |
+
$args['recipient'] = 'default' === $args['notification_type']
|
164 |
+
? get_option( 'admin_email' )
|
165 |
+
: $this->db->getOption( 'general.notification_email' );
|
166 |
+
|
167 |
+
// no email address has been set
|
168 |
+
if( empty( $args['recipient'] ) )return;
|
169 |
+
|
170 |
+
return $this->createNotification( $command, $args )->send();
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* @return null|array|WP_Error
|
175 |
+
*/
|
176 |
+
protected function sendNotificationWebhook( Command $command, array $args )
|
177 |
+
{
|
178 |
+
if( !( $endpoint = $this->db->getOption( 'general.webhook_url' ) ) )return;
|
179 |
+
|
180 |
+
$fields = [];
|
181 |
+
|
182 |
+
$fields[] = ['title' => str_repeat( ':star:', $command->rating ) ];
|
183 |
+
|
184 |
+
if( $command->title ) {
|
185 |
+
$fields[] = ['title' => $command->title ];
|
186 |
+
}
|
187 |
+
|
188 |
+
if( $command->content ) {
|
189 |
+
$fields[] = ['value' => $command->content ];
|
190 |
+
}
|
191 |
+
|
192 |
+
if( $command->reviewer ) {
|
193 |
+
!$command->email ?: $command->email = sprintf( ' <%s>', $command->email );
|
194 |
+
$fields[] = ['value' => trim( sprintf( '%s%s - %s', $command->reviewer, $command->email, $command->ipAddress ) ) ];
|
195 |
+
}
|
196 |
+
|
197 |
+
$fields[] = ['value' => sprintf( '<%s|%s>', $args['notification_link'], __( 'View Review', 'site-reviews' ) ) ];
|
198 |
+
|
199 |
+
$notification = json_encode([
|
200 |
+
'icon_url' => $this->app->url . 'assets/img/icon.png',
|
201 |
+
'username' => $this->app->name,
|
202 |
+
'attachments' => [
|
203 |
+
[
|
204 |
+
'pretext' => $args['notification_title'],
|
205 |
+
'color' => '#665068',
|
206 |
+
'fallback' => $this->createNotification( $command, $args )->read( 'plaintext' ),
|
207 |
+
'fields' => $fields,
|
208 |
+
],
|
209 |
+
],
|
210 |
+
]);
|
211 |
+
|
212 |
+
return wp_remote_post( $endpoint, [
|
213 |
+
'method' => 'POST',
|
214 |
+
'timeout' => 45,
|
215 |
+
'redirection' => 5,
|
216 |
+
'httpversion' => '1.0',
|
217 |
+
'blocking' => false,
|
218 |
+
'sslverify' => false,
|
219 |
+
'headers' => ['Content-Type' => 'application/json'],
|
220 |
+
'body' => apply_filters( 'site-reviews/webhook/notification', $notification, $command ),
|
221 |
+
]);
|
222 |
+
}
|
223 |
+
}
|
plugin/Handlers/TogglePinned.php
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Handlers;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Commands\TogglePinned as Command;
|
14 |
+
use GeminiLabs\SiteReviews\Notices;
|
15 |
+
|
16 |
+
class TogglePinned
|
17 |
+
{
|
18 |
+
/**
|
19 |
+
* @var Notices
|
20 |
+
*/
|
21 |
+
protected $notices;
|
22 |
+
|
23 |
+
public function __construct( Notices $notices )
|
24 |
+
{
|
25 |
+
$this->notices = $notices;
|
26 |
+
}
|
27 |
+
|
28 |
+
public function handle( Command $command )
|
29 |
+
{
|
30 |
+
if( !get_post( $command->id ) ) {
|
31 |
+
return false;
|
32 |
+
}
|
33 |
+
|
34 |
+
if( is_null( $command->pinned ) ) {
|
35 |
+
$meta = get_post_meta( $command->id, 'pinned', true );
|
36 |
+
$command->pinned = !wp_validate_boolean( $meta );
|
37 |
+
}
|
38 |
+
else {
|
39 |
+
$notice = $command->pinned
|
40 |
+
? __( 'The review is pinned.', 'site-reviews' )
|
41 |
+
: __( 'The review is unpinned.', 'site-reviews' );
|
42 |
+
|
43 |
+
$this->notices->addSuccess( $notice );
|
44 |
+
}
|
45 |
+
|
46 |
+
update_post_meta( $command->id, 'pinned', $command->pinned );
|
47 |
+
|
48 |
+
return $command->pinned;
|
49 |
+
}
|
50 |
+
}
|
plugin/Html.php
ADDED
@@ -0,0 +1,249 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews;
|
12 |
+
|
13 |
+
use Exception;
|
14 |
+
use GeminiLabs\SiteReviews\App;
|
15 |
+
|
16 |
+
class Html
|
17 |
+
{
|
18 |
+
/**
|
19 |
+
* @var App
|
20 |
+
*/
|
21 |
+
protected $app;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var array
|
25 |
+
*/
|
26 |
+
protected $forms;
|
27 |
+
|
28 |
+
public function __construct( App $app )
|
29 |
+
{
|
30 |
+
$this->app = $app;
|
31 |
+
$this->forms = [];
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Add a field to an existing form
|
36 |
+
*
|
37 |
+
* @param string $formId
|
38 |
+
*
|
39 |
+
* @return Form|false
|
40 |
+
*/
|
41 |
+
public function addField( $formId, array $args = [] )
|
42 |
+
{
|
43 |
+
if( !$this->isForm( $formId ) )return;
|
44 |
+
|
45 |
+
return $this->forms[ $formId ]->addField( $args );
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Create a new form
|
50 |
+
*
|
51 |
+
* @param string $formId
|
52 |
+
*
|
53 |
+
* @return Form
|
54 |
+
*/
|
55 |
+
public function createForm( $formId, array $args = [] )
|
56 |
+
{
|
57 |
+
if( !$this->isForm( $formId ) ) {
|
58 |
+
|
59 |
+
$form = $this->app->make( 'Html\Form' )->normalize( $args );
|
60 |
+
|
61 |
+
$this->forms[ $formId ] = $form;
|
62 |
+
}
|
63 |
+
|
64 |
+
return $this->forms[ $formId ];
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Return dependencies from all forms
|
69 |
+
*
|
70 |
+
* @return array
|
71 |
+
*/
|
72 |
+
public function getDependencies()
|
73 |
+
{
|
74 |
+
$dependencies = [];
|
75 |
+
|
76 |
+
foreach( $this->forms as $form ) {
|
77 |
+
$dependencies = array_unique(
|
78 |
+
array_merge( $dependencies, $form->dependencies )
|
79 |
+
);
|
80 |
+
}
|
81 |
+
|
82 |
+
return $dependencies;
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Return dependencies from a specific form
|
87 |
+
*
|
88 |
+
* @return array
|
89 |
+
*/
|
90 |
+
public function getFormDependencies( $formId )
|
91 |
+
{
|
92 |
+
return $this->isForm( $formId )
|
93 |
+
? $this->forms[ $formId ]->dependencies
|
94 |
+
: [];
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Render a field (outside of a form)
|
99 |
+
*
|
100 |
+
* @param string|bool $print
|
101 |
+
*
|
102 |
+
* @return void|string
|
103 |
+
*/
|
104 |
+
public function renderField( array $args = [], $print = 'print' )
|
105 |
+
{
|
106 |
+
$field = $this->app->make( 'Html\Field' )->normalize( $args );
|
107 |
+
|
108 |
+
return $field->render( $print );
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Render a form then remove it
|
113 |
+
*
|
114 |
+
* @param string $formId
|
115 |
+
* @param string|bool $print
|
116 |
+
*
|
117 |
+
* @return void|string
|
118 |
+
*/
|
119 |
+
public function renderForm( $formId, $print = 'print' )
|
120 |
+
{
|
121 |
+
if( !$this->isForm( $formId ) )return;
|
122 |
+
|
123 |
+
$form = $this->forms[ $formId ];
|
124 |
+
|
125 |
+
unset( $this->forms[ $formId ] );
|
126 |
+
|
127 |
+
return $form->render( $print );
|
128 |
+
}
|
129 |
+
|
130 |
+
/**
|
131 |
+
* Render a partial
|
132 |
+
*
|
133 |
+
* @param string $partialName
|
134 |
+
* @param string|bool $print
|
135 |
+
*
|
136 |
+
* @return void|string
|
137 |
+
*/
|
138 |
+
public function renderPartial( $partialName, array $args = [], $print = 'print' )
|
139 |
+
{
|
140 |
+
$partial = $this->app->make( 'Html\Partial' )->normalize( $partialName, $args );
|
141 |
+
|
142 |
+
return $partial->render( $print );
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Render a template
|
147 |
+
*
|
148 |
+
* @param string $templatePath
|
149 |
+
* @param string|bool $print
|
150 |
+
*
|
151 |
+
* @return void|string
|
152 |
+
*/
|
153 |
+
public function renderTemplate( $templatePath, array $args = [], $print = 'print' )
|
154 |
+
{
|
155 |
+
$file = $this->app->path . "views/{$templatePath}.php";
|
156 |
+
|
157 |
+
if( !file_exists( $file ) )return;
|
158 |
+
|
159 |
+
ob_start();
|
160 |
+
include $file;
|
161 |
+
$template = ob_get_clean();
|
162 |
+
|
163 |
+
return $this->renderTemplateString( $template, $args, $print );
|
164 |
+
}
|
165 |
+
|
166 |
+
/**
|
167 |
+
* Render a template string
|
168 |
+
*
|
169 |
+
* @param string $template
|
170 |
+
* @param string|bool $print
|
171 |
+
*
|
172 |
+
* @return void|string
|
173 |
+
*/
|
174 |
+
public function renderTemplateString( $template, array $args = [], $print = 'print' )
|
175 |
+
{
|
176 |
+
if( !empty( $args ) ) {
|
177 |
+
foreach( $args as $key => $value ) {
|
178 |
+
$template = str_replace( '{' . $key . '}', $value, $template );
|
179 |
+
}
|
180 |
+
}
|
181 |
+
|
182 |
+
$template = trim( $template );
|
183 |
+
|
184 |
+
if( !!$print && $print !== 'return' ) {
|
185 |
+
echo $template;
|
186 |
+
}
|
187 |
+
|
188 |
+
return $template;
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* Reset the forms array
|
193 |
+
*
|
194 |
+
* @return Html
|
195 |
+
*/
|
196 |
+
public function reset()
|
197 |
+
{
|
198 |
+
$this->forms = [];
|
199 |
+
|
200 |
+
return $this;
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* Set dependencies of a form
|
205 |
+
*
|
206 |
+
* @param string $formId
|
207 |
+
* @param bool|string $overwrite
|
208 |
+
*
|
209 |
+
* @return void
|
210 |
+
*/
|
211 |
+
public function setFormDependencies( $formId, array $dependencies, $overwrite = false )
|
212 |
+
{
|
213 |
+
if( $this->isForm( $formId ) )return;
|
214 |
+
|
215 |
+
if( !$overwrite || $overwrite == 'merge' ) {
|
216 |
+
$dependencies = array_unique(
|
217 |
+
array_merge( $this->forms[ $formId ]->dependencies, $dependencies )
|
218 |
+
);
|
219 |
+
}
|
220 |
+
|
221 |
+
$this->forms[ $formId ]->dependencies = $dependencies;
|
222 |
+
}
|
223 |
+
|
224 |
+
/**
|
225 |
+
* Return a stored form
|
226 |
+
*
|
227 |
+
* @return Form|false
|
228 |
+
*/
|
229 |
+
public function switchForm( $formId )
|
230 |
+
{
|
231 |
+
if( $this->isForm( $formId ) ) {
|
232 |
+
return $this->forms[ $formId ];
|
233 |
+
}
|
234 |
+
|
235 |
+
return false;
|
236 |
+
}
|
237 |
+
|
238 |
+
/**
|
239 |
+
* Check if a form exists
|
240 |
+
*
|
241 |
+
* @param string $formId
|
242 |
+
*
|
243 |
+
* @return bool
|
244 |
+
*/
|
245 |
+
protected function isForm( $formId )
|
246 |
+
{
|
247 |
+
return isset( $this->forms[ $formId ] );
|
248 |
+
}
|
249 |
+
}
|
plugin/Html/Field.php
ADDED
@@ -0,0 +1,365 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html;
|
12 |
+
|
13 |
+
use Exception;
|
14 |
+
use GeminiLabs\SiteReviews\App;
|
15 |
+
use ReflectionException;
|
16 |
+
|
17 |
+
class Field
|
18 |
+
{
|
19 |
+
/**
|
20 |
+
* @var App
|
21 |
+
*/
|
22 |
+
protected $app;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var array
|
26 |
+
*/
|
27 |
+
protected $args;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @var array
|
31 |
+
*/
|
32 |
+
protected $dependencies;
|
33 |
+
|
34 |
+
public function __construct( App $app )
|
35 |
+
{
|
36 |
+
$this->app = $app;
|
37 |
+
$this->args = [];
|
38 |
+
$this->dependencies = [];
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* @param string $property
|
43 |
+
*
|
44 |
+
* @return mixed
|
45 |
+
* @throws Exception
|
46 |
+
*/
|
47 |
+
public function __get( $property )
|
48 |
+
{
|
49 |
+
switch( $property ) {
|
50 |
+
case 'args';
|
51 |
+
case 'dependencies';
|
52 |
+
return $this->$property;
|
53 |
+
}
|
54 |
+
|
55 |
+
throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $property );
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Get a specific Field
|
60 |
+
*
|
61 |
+
* @return mixed GeminiLabs\SiteReviews\Html\Fields\*
|
62 |
+
*/
|
63 |
+
public function getField( array $args = [] )
|
64 |
+
{
|
65 |
+
if( empty( $args ) ) {
|
66 |
+
$args = $this->args;
|
67 |
+
}
|
68 |
+
|
69 |
+
$className = sprintf( 'GeminiLabs\SiteReviews\Html\Fields\%s', ucfirst( $args['type'] ) );
|
70 |
+
|
71 |
+
if( !class_exists( $className ) ) {
|
72 |
+
throw new ReflectionException( "Class does not exist: {$className}" );
|
73 |
+
}
|
74 |
+
|
75 |
+
return (new $className( $args ));
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Normalize the field arguments
|
80 |
+
*
|
81 |
+
* @return $this
|
82 |
+
*/
|
83 |
+
public function normalize( array $args = [] )
|
84 |
+
{
|
85 |
+
$defaults = [
|
86 |
+
'after' => '',
|
87 |
+
'attributes' => '',
|
88 |
+
'before' => '',
|
89 |
+
'class' => '',
|
90 |
+
'default' => null,
|
91 |
+
'depends' => null,
|
92 |
+
'desc' => '',
|
93 |
+
'errors' => [],
|
94 |
+
'inline' => false,
|
95 |
+
'label' => '',
|
96 |
+
'name' => '',
|
97 |
+
'options' => [],
|
98 |
+
'placeholder' => '',
|
99 |
+
'prefix' => '',
|
100 |
+
'render' => true,
|
101 |
+
'suffix' => null,
|
102 |
+
'type' => 'text',
|
103 |
+
'value' => '',
|
104 |
+
];
|
105 |
+
|
106 |
+
$args = $atts = wp_parse_args( $args, $defaults );
|
107 |
+
|
108 |
+
$args['attributes'] = $this->parseAttributes( $atts );
|
109 |
+
$args['id'] = $this->parseId( $atts );
|
110 |
+
$args['inline'] = $this->parseInline( $atts );
|
111 |
+
$args['type'] = $this->parseType( $atts );
|
112 |
+
$args['name'] = $this->parseName( $atts );
|
113 |
+
$args['options'] = (array) $atts['options']; // make sure this is always an array
|
114 |
+
$args['prefix'] = $this->parsePrefix( $atts );
|
115 |
+
$args['value'] = $this->parseValue( $atts );
|
116 |
+
|
117 |
+
$this->args = $args;
|
118 |
+
|
119 |
+
$this->dependencies = $this->getField( $args )->dependencies;
|
120 |
+
|
121 |
+
$this->setDataDepends();
|
122 |
+
|
123 |
+
$this->checkForErrors( $atts );
|
124 |
+
|
125 |
+
return $this;
|
126 |
+
}
|
127 |
+
|
128 |
+
/**
|
129 |
+
* Render the field
|
130 |
+
*
|
131 |
+
* @param mixed $print
|
132 |
+
*
|
133 |
+
* @return string|void
|
134 |
+
*/
|
135 |
+
public function render( $print = true )
|
136 |
+
{
|
137 |
+
if( $this->args['render'] === false )return;
|
138 |
+
|
139 |
+
$field = $this->getField();
|
140 |
+
|
141 |
+
$class = 'glsr-field';
|
142 |
+
$class .= $this->args['errors'] ? ' glsr-has-error' : '';
|
143 |
+
|
144 |
+
$renderedString = '%s';
|
145 |
+
|
146 |
+
if( ( isset( $field->args['required'] ) && $field->args['required'] )
|
147 |
+
|| ( isset( $field->args['attributes']['required'] ) || in_array( 'required', $field->args['attributes'] ) ) ) {
|
148 |
+
$class .= ' glsr-required';
|
149 |
+
}
|
150 |
+
|
151 |
+
if( $field->args['type'] !== 'hidden' ) {
|
152 |
+
$renderedString = sprintf( '<div class="%s">%%s</div>', $class );
|
153 |
+
}
|
154 |
+
|
155 |
+
$rendered = sprintf( $renderedString,
|
156 |
+
$this->args['before'] .
|
157 |
+
$field->generateLabel() .
|
158 |
+
$field->render() .
|
159 |
+
$this->args['after'] .
|
160 |
+
$this->args['errors']
|
161 |
+
);
|
162 |
+
|
163 |
+
$rendered = apply_filters( 'site-reviews/rendered/field', $rendered, $field->args['type'] );
|
164 |
+
|
165 |
+
if( !!$print && $print !== 'return' ) {
|
166 |
+
echo $rendered;
|
167 |
+
}
|
168 |
+
|
169 |
+
return $rendered;
|
170 |
+
}
|
171 |
+
|
172 |
+
/**
|
173 |
+
* Reset the Field
|
174 |
+
*
|
175 |
+
* @return self
|
176 |
+
*/
|
177 |
+
public function reset()
|
178 |
+
{
|
179 |
+
$this->args = [];
|
180 |
+
|
181 |
+
return $this;
|
182 |
+
}
|
183 |
+
|
184 |
+
/**
|
185 |
+
* Check for form submission field errors
|
186 |
+
*
|
187 |
+
* @return void
|
188 |
+
*/
|
189 |
+
protected function checkForErrors( array $atts )
|
190 |
+
{
|
191 |
+
$args = $this->args;
|
192 |
+
|
193 |
+
if( !array_key_exists( $atts['name'], $args['errors'] ) ) {
|
194 |
+
$this->args['errors'] = ''; // set to an empty string
|
195 |
+
return;
|
196 |
+
}
|
197 |
+
|
198 |
+
$field_errors = $args['errors'][ $atts['name'] ];
|
199 |
+
|
200 |
+
$errors = array_reduce( $field_errors['errors'], function( $carry, $error ) {
|
201 |
+
return $carry . sprintf( '<span>%s</span> ', $error );
|
202 |
+
});
|
203 |
+
|
204 |
+
$this->args['errors'] = sprintf( '<span class="glsr-field-errors">%s</span>', $errors );
|
205 |
+
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* Parse the field attributes and convert to an array if needed
|
209 |
+
*
|
210 |
+
* @return array
|
211 |
+
*/
|
212 |
+
protected function parseAttributes( array $args )
|
213 |
+
{
|
214 |
+
if( empty( $args['attributes'] ) ) {
|
215 |
+
return [];
|
216 |
+
}
|
217 |
+
|
218 |
+
$attributes = (array) $args['attributes'];
|
219 |
+
|
220 |
+
foreach( $attributes as $key => $value ) {
|
221 |
+
if( is_string( $key ) )continue;
|
222 |
+
unset( $attributes[ $key ] );
|
223 |
+
if( !isset( $attributes[ $value ] ) ) {
|
224 |
+
$attributes[ $value ] = '';
|
225 |
+
}
|
226 |
+
}
|
227 |
+
|
228 |
+
return $attributes;
|
229 |
+
}
|
230 |
+
|
231 |
+
/**
|
232 |
+
* Parse the field ID from the field path
|
233 |
+
*
|
234 |
+
* @return null|string
|
235 |
+
*/
|
236 |
+
protected function parseId( array $args )
|
237 |
+
{
|
238 |
+
if( isset( $args['id'] ) && !$args['id'] )return;
|
239 |
+
|
240 |
+
!$args['suffix'] ?: $args['suffix'] = "-{$args['suffix']}";
|
241 |
+
|
242 |
+
return str_replace( ['[]','[',']','.'], ['','-','','-'], $this->parseName( $args ) . $args['suffix'] );
|
243 |
+
}
|
244 |
+
|
245 |
+
/**
|
246 |
+
* Parse the field inline
|
247 |
+
*
|
248 |
+
* @return bool
|
249 |
+
*/
|
250 |
+
protected function parseInline( array $args )
|
251 |
+
{
|
252 |
+
return false !== stripos( $args['type'], '_inline' )
|
253 |
+
? true
|
254 |
+
: $args['inline'];
|
255 |
+
}
|
256 |
+
|
257 |
+
/**
|
258 |
+
* Parse the field name
|
259 |
+
*
|
260 |
+
* @return string
|
261 |
+
*/
|
262 |
+
protected function parseName( array $args )
|
263 |
+
{
|
264 |
+
$name = $args['name'];
|
265 |
+
$prefix = $this->parsePrefix( $args );
|
266 |
+
|
267 |
+
if( $prefix === false ) {
|
268 |
+
return $name;
|
269 |
+
}
|
270 |
+
|
271 |
+
$paths = explode( '.', $name );
|
272 |
+
|
273 |
+
return array_reduce( $paths, function( $result, $value ) {
|
274 |
+
return $result .= "[$value]";
|
275 |
+
}, $prefix );
|
276 |
+
}
|
277 |
+
|
278 |
+
/**
|
279 |
+
* Parse the field prefix
|
280 |
+
*
|
281 |
+
* @return string|false
|
282 |
+
*/
|
283 |
+
protected function parsePrefix( array $args )
|
284 |
+
{
|
285 |
+
$prefix = $args['prefix'];
|
286 |
+
|
287 |
+
return ( '' === trim( $prefix ) && $prefix !== false )
|
288 |
+
? "{$this->app->prefix}_settings"
|
289 |
+
: $prefix;
|
290 |
+
}
|
291 |
+
|
292 |
+
/**
|
293 |
+
* Parse the field type
|
294 |
+
*
|
295 |
+
* @return string
|
296 |
+
*/
|
297 |
+
protected function parseType( array $args )
|
298 |
+
{
|
299 |
+
$type = $args['type'];
|
300 |
+
|
301 |
+
return false !== stripos( $type, '_inline' )
|
302 |
+
? str_replace( '_inline', '', $type )
|
303 |
+
: $type;
|
304 |
+
}
|
305 |
+
|
306 |
+
/**
|
307 |
+
* Parse the field value
|
308 |
+
*
|
309 |
+
* @return string
|
310 |
+
*/
|
311 |
+
protected function parseValue( array $args )
|
312 |
+
{
|
313 |
+
$default = $args['default'];
|
314 |
+
$name = $args['name'];
|
315 |
+
$prefix = $args['prefix'];
|
316 |
+
$value = $args['value'];
|
317 |
+
|
318 |
+
if( $default == ':placeholder' ) {
|
319 |
+
$default = '';
|
320 |
+
}
|
321 |
+
|
322 |
+
return ( !empty( $value ) || !$name || $prefix === false )
|
323 |
+
? $value
|
324 |
+
: $this->app->make( 'Database' )->getOption( $name, $default );
|
325 |
+
}
|
326 |
+
|
327 |
+
/**
|
328 |
+
* Get the [data-depends] attribute
|
329 |
+
*
|
330 |
+
* @return array|null
|
331 |
+
*/
|
332 |
+
public function getDataDepends()
|
333 |
+
{
|
334 |
+
return $this->setDataDepends();
|
335 |
+
}
|
336 |
+
|
337 |
+
/**
|
338 |
+
* Set the [data-depends] attribute
|
339 |
+
*
|
340 |
+
* @return null|array
|
341 |
+
*/
|
342 |
+
protected function setDataDepends()
|
343 |
+
{
|
344 |
+
if( !( $depends = $this->args['depends'] ) )return;
|
345 |
+
|
346 |
+
$name = $depends;
|
347 |
+
$value = true;
|
348 |
+
|
349 |
+
if( is_array( $depends ) ) {
|
350 |
+
reset( $depends );
|
351 |
+
$name = key( $depends );
|
352 |
+
$value = $depends[ $name ];
|
353 |
+
}
|
354 |
+
|
355 |
+
$name = $this->parseName([
|
356 |
+
'name' => $name,
|
357 |
+
'prefix' => $this->args['prefix'],
|
358 |
+
]);
|
359 |
+
|
360 |
+
return $this->args['attributes']['data-depends'] = [
|
361 |
+
'name' => $name,
|
362 |
+
'value' => $value,
|
363 |
+
];
|
364 |
+
}
|
365 |
+
}
|
plugin/Html/Fields/Base.php
ADDED
@@ -0,0 +1,361 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Fields;
|
12 |
+
|
13 |
+
use Exception;
|
14 |
+
use GeminiLabs\SiteReviews\Html\Normalize;
|
15 |
+
|
16 |
+
abstract class Base
|
17 |
+
{
|
18 |
+
/**
|
19 |
+
* @var array
|
20 |
+
*/
|
21 |
+
protected $args;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var array
|
25 |
+
*/
|
26 |
+
protected $dependencies = [];
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Whether the field has multiple values
|
30 |
+
*
|
31 |
+
* @var bool
|
32 |
+
*/
|
33 |
+
protected $multi = false;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Whether the field is rendered outside of the form table
|
37 |
+
*
|
38 |
+
* @var bool
|
39 |
+
*/
|
40 |
+
protected $outside = false;
|
41 |
+
|
42 |
+
/**
|
43 |
+
* The field element tag (i.e. "input")
|
44 |
+
*
|
45 |
+
* @var string
|
46 |
+
*/
|
47 |
+
protected $element;
|
48 |
+
|
49 |
+
public function __construct( array $args = [] )
|
50 |
+
{
|
51 |
+
$this->args = $args;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* @param string $property
|
56 |
+
*
|
57 |
+
* @return mixed
|
58 |
+
* @throws Exception
|
59 |
+
*/
|
60 |
+
public function __get( $property )
|
61 |
+
{
|
62 |
+
switch( $property ) {
|
63 |
+
case 'args';
|
64 |
+
case 'dependencies';
|
65 |
+
case 'element';
|
66 |
+
case 'multi';
|
67 |
+
case 'outside';
|
68 |
+
return $this->$property;
|
69 |
+
}
|
70 |
+
|
71 |
+
throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $property );
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Generate the field description
|
76 |
+
*
|
77 |
+
* @param bool $paragraph
|
78 |
+
*
|
79 |
+
* @return null|string
|
80 |
+
*/
|
81 |
+
public function generateDescription( $paragraph = true )
|
82 |
+
{
|
83 |
+
if( !isset( $this->args['desc'] ) || !$this->args['desc'] )return;
|
84 |
+
|
85 |
+
$tag = ( !!$paragraph || $paragraph == 'p' ) ? 'p' : 'span';
|
86 |
+
|
87 |
+
return sprintf( '<%1$s class="description">%2$s</%1$s>', $tag, $this->args['desc'] );
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Generate the field label
|
92 |
+
*
|
93 |
+
* @return null|string
|
94 |
+
*/
|
95 |
+
public function generateLabel()
|
96 |
+
{
|
97 |
+
if( empty( $this->args['label'] ) )return;
|
98 |
+
|
99 |
+
$for = !!$this->args['id']
|
100 |
+
? " for=\"{$this->args['id']}\""
|
101 |
+
: '';
|
102 |
+
|
103 |
+
return sprintf( '<label%s>%s</label>', $for, $this->args['label'] );
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Render this field type
|
108 |
+
*
|
109 |
+
* @return string
|
110 |
+
*/
|
111 |
+
abstract public function render();
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Convert a value to camel case.
|
115 |
+
*
|
116 |
+
* @param string $value
|
117 |
+
*
|
118 |
+
* @return string
|
119 |
+
*/
|
120 |
+
protected function camelCase( $value )
|
121 |
+
{
|
122 |
+
$value = ucwords( str_replace( ['-', '_'], ' ', $value ) );
|
123 |
+
|
124 |
+
return lcfirst( str_replace( ' ', '', $value ) );
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Implode the field attributes
|
129 |
+
*
|
130 |
+
* @return array
|
131 |
+
*/
|
132 |
+
protected function implodeAttributes( $defaults = [] )
|
133 |
+
{
|
134 |
+
return $this->normalize( $defaults, 'implode' );
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Implode multi-field items
|
139 |
+
*
|
140 |
+
* @return null|string
|
141 |
+
*/
|
142 |
+
protected function implodeOptions( $method = 'select_option', $default = null )
|
143 |
+
{
|
144 |
+
$this->args['default'] ?: $this->args['default'] = $default;
|
145 |
+
|
146 |
+
$method = $this->camelCase( $method );
|
147 |
+
|
148 |
+
$method = method_exists( $this, $method )
|
149 |
+
? $method
|
150 |
+
: 'selectOption';
|
151 |
+
|
152 |
+
$i = 0;
|
153 |
+
|
154 |
+
if( $method === 'singleInput' ) {
|
155 |
+
|
156 |
+
if( !isset( $this->args['options'] ) || empty( $this->args['options'] ) )return;
|
157 |
+
|
158 |
+
// hack to make sure unset single checkbox values start at 1 instead of 0
|
159 |
+
if( key( $this->args['options'] ) === 0 ) {
|
160 |
+
$options = ['1' => $this->args['options'][0]];
|
161 |
+
$this->args['options'] = $options;
|
162 |
+
}
|
163 |
+
|
164 |
+
return $this->singleInput();
|
165 |
+
}
|
166 |
+
|
167 |
+
return array_reduce( array_keys( $this->args['options'] ), function( $carry, $key ) use ( &$i, $method ) {
|
168 |
+
return $carry .= $this->$method( $key, $i++ );
|
169 |
+
});
|
170 |
+
}
|
171 |
+
|
172 |
+
/**
|
173 |
+
* Normalize attributes for this specific field type
|
174 |
+
*
|
175 |
+
* @param bool|string $implode
|
176 |
+
*
|
177 |
+
* @return array
|
178 |
+
*/
|
179 |
+
protected function normalize( array $defaults = [], $implode = false )
|
180 |
+
{
|
181 |
+
$args = $this->mergeAttributesWith( $defaults );
|
182 |
+
|
183 |
+
$normalize = new Normalize;
|
184 |
+
|
185 |
+
return ( $this->element && method_exists( $normalize, $this->element ) )
|
186 |
+
? $normalize->{$this->element}( $args, $implode )
|
187 |
+
: ( !!$implode ? '' : [] );
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* Merge and overwrite empty $this->args values with $defaults
|
192 |
+
*
|
193 |
+
* @return array
|
194 |
+
*/
|
195 |
+
protected function mergeAttributesWith( array $defaults )
|
196 |
+
{
|
197 |
+
// similar to array_merge except overwrite empty values
|
198 |
+
foreach( $defaults as $key => $value ) {
|
199 |
+
if( isset( $this->args[ $key ] ) && !empty( $this->args[ $key ] ) )continue;
|
200 |
+
$this->args[ $key ] = $value;
|
201 |
+
}
|
202 |
+
|
203 |
+
$attributes = $this->args['attributes'];
|
204 |
+
|
205 |
+
// prioritize $attributes over $this->args, don't worry about duplicates
|
206 |
+
return array_merge( $this->args, $attributes );
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* Generate checkboxes and radios
|
211 |
+
*
|
212 |
+
* @param string $optionKey
|
213 |
+
* @param string $number
|
214 |
+
* @param string $type
|
215 |
+
*
|
216 |
+
* @return null|string
|
217 |
+
*/
|
218 |
+
protected function multiInput( $optionKey, $number, $type = 'radio' )
|
219 |
+
{
|
220 |
+
$args = $this->multiInputArgs( $type, $optionKey, $number );
|
221 |
+
|
222 |
+
if( !$args )return;
|
223 |
+
|
224 |
+
$attributes = '';
|
225 |
+
|
226 |
+
foreach( $args['attributes'] as $key => $val ) {
|
227 |
+
$attributes .= sprintf( '%s="%s" ', $key, $val );
|
228 |
+
}
|
229 |
+
|
230 |
+
return sprintf( '<li><label for="%s"><input %s%s/> %s</label></li>',
|
231 |
+
$args['attributes']['id'],
|
232 |
+
$attributes,
|
233 |
+
checked( $args['value'], $args['attributes']['value'], false ),
|
234 |
+
$args['label']
|
235 |
+
);
|
236 |
+
}
|
237 |
+
|
238 |
+
/**
|
239 |
+
* Build the checkbox/radio args
|
240 |
+
*
|
241 |
+
* @param string $type
|
242 |
+
* @param string $optionName
|
243 |
+
* @param string $number
|
244 |
+
*
|
245 |
+
* @return array|null
|
246 |
+
*/
|
247 |
+
protected function multiInputArgs( $type, $optionName, $number )
|
248 |
+
{
|
249 |
+
$defaults = [
|
250 |
+
'class' => '',
|
251 |
+
'name' => '',
|
252 |
+
'type' => $type,
|
253 |
+
'value' => '',
|
254 |
+
];
|
255 |
+
|
256 |
+
$args = [];
|
257 |
+
|
258 |
+
$value = $this->args['options'][ $optionName ];
|
259 |
+
|
260 |
+
if( is_array( $value ) ) {
|
261 |
+
$args = $value;
|
262 |
+
}
|
263 |
+
|
264 |
+
if( is_string( $value ) ) {
|
265 |
+
$label = $value;
|
266 |
+
}
|
267 |
+
|
268 |
+
isset( $args['name'] ) ?: $args['name'] = $optionName;
|
269 |
+
isset( $args['value'] ) ?: $args['value'] = $optionName;
|
270 |
+
|
271 |
+
$args = wp_parse_args( $args, $defaults );
|
272 |
+
|
273 |
+
if( !isset( $label ) || $args['name'] === '' )return;
|
274 |
+
|
275 |
+
$args['id'] = $this->args['id'] . "-{$number}";
|
276 |
+
$args['name'] = $this->args['name'] . ( $type === 'checkbox' && $this->multi ? '[]' : '' );
|
277 |
+
|
278 |
+
$args = array_filter( $args, function( $value ) {
|
279 |
+
return $value !== '';
|
280 |
+
});
|
281 |
+
|
282 |
+
if( is_array( $this->args['value'] ) ) {
|
283 |
+
if( in_array( $args['value'], $this->args['value'] ) ) {
|
284 |
+
$this->args['default'] = $args['value'];
|
285 |
+
}
|
286 |
+
}
|
287 |
+
else if( $this->args['value'] ) {
|
288 |
+
$this->args['default'] = $this->args['value'];
|
289 |
+
}
|
290 |
+
else if( $type == 'radio' && !$this->args['default'] ) {
|
291 |
+
$this->args['default'] = 0;
|
292 |
+
}
|
293 |
+
|
294 |
+
return [
|
295 |
+
'attributes' => $args,
|
296 |
+
'label' => $label,
|
297 |
+
'value' => $this->args['default'],
|
298 |
+
];
|
299 |
+
}
|
300 |
+
|
301 |
+
/**
|
302 |
+
* Generate checkboxes
|
303 |
+
*
|
304 |
+
* @param string $optionKey
|
305 |
+
* @param string $number
|
306 |
+
*
|
307 |
+
* @return null|string
|
308 |
+
*/
|
309 |
+
protected function multiInputCheckbox( $optionKey, $number )
|
310 |
+
{
|
311 |
+
return $this->multiInput( $optionKey, $number, 'checkbox' );
|
312 |
+
}
|
313 |
+
|
314 |
+
/**
|
315 |
+
* Generate select options
|
316 |
+
*
|
317 |
+
* @param string $optionKey
|
318 |
+
*
|
319 |
+
* @return string
|
320 |
+
*/
|
321 |
+
protected function selectOption( $optionKey )
|
322 |
+
{
|
323 |
+
return sprintf( '<option value="%s"%s>%s</option>',
|
324 |
+
$optionKey,
|
325 |
+
selected( $this->args['value'], $optionKey, false ),
|
326 |
+
$this->args['options'][ $optionKey ]
|
327 |
+
);
|
328 |
+
}
|
329 |
+
|
330 |
+
/**
|
331 |
+
* Generate a single checkbox
|
332 |
+
*
|
333 |
+
* @param string $type
|
334 |
+
*
|
335 |
+
* @return null|string
|
336 |
+
*/
|
337 |
+
protected function singleInput( $type = 'checkbox' )
|
338 |
+
{
|
339 |
+
$optionKey = key( $this->args['options'] );
|
340 |
+
|
341 |
+
$args = $this->multiInputArgs( $type, $optionKey, 1 );
|
342 |
+
|
343 |
+
if( !$args )return;
|
344 |
+
|
345 |
+
$atts = $this->normalize();
|
346 |
+
$atts = wp_parse_args( $args['attributes'], $atts );
|
347 |
+
|
348 |
+
$attributes = '';
|
349 |
+
|
350 |
+
foreach( $atts as $key => $val ) {
|
351 |
+
$attributes .= sprintf( '%s="%s" ', $key, $val );
|
352 |
+
}
|
353 |
+
|
354 |
+
return sprintf( '<label for="%s"><input %s%s/> %s</label>',
|
355 |
+
$atts['id'],
|
356 |
+
$attributes,
|
357 |
+
checked( $args['value'], $atts['value'], false ),
|
358 |
+
$args['label']
|
359 |
+
);
|
360 |
+
}
|
361 |
+
}
|
plugin/Html/Fields/Checkbox.php
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Fields;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Fields\Base;
|
14 |
+
|
15 |
+
class Checkbox extends Base
|
16 |
+
{
|
17 |
+
protected $element = 'input';
|
18 |
+
|
19 |
+
public function __construct( array $args = [] )
|
20 |
+
{
|
21 |
+
parent::__construct( $args );
|
22 |
+
|
23 |
+
if( count( $args['options'] ) > 1 ) {
|
24 |
+
$this->multi = true;
|
25 |
+
}
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @return string
|
30 |
+
*/
|
31 |
+
public function render()
|
32 |
+
{
|
33 |
+
$inline = $this->args['inline'] ? ' class="inline"' : '';
|
34 |
+
|
35 |
+
if( $this->multi ) {
|
36 |
+
return sprintf( '<ul%s>%s</ul>%s',
|
37 |
+
$inline,
|
38 |
+
$this->implodeOptions( 'multi_input_checkbox' ),
|
39 |
+
$this->generateDescription()
|
40 |
+
);
|
41 |
+
}
|
42 |
+
|
43 |
+
return sprintf( '%s%s',
|
44 |
+
$this->implodeOptions( 'single_input' ),
|
45 |
+
$this->generateDescription()
|
46 |
+
);
|
47 |
+
}
|
48 |
+
}
|
plugin/Html/Fields/Code.php
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Fields;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Fields\Textarea;
|
14 |
+
|
15 |
+
class Code extends Textarea
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* @return string
|
19 |
+
*/
|
20 |
+
public function render()
|
21 |
+
{
|
22 |
+
return parent::render([
|
23 |
+
'class' => 'large-text code',
|
24 |
+
]);
|
25 |
+
}
|
26 |
+
}
|
plugin/Html/Fields/Colorpicker.php
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Fields;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Fields\Base;
|
14 |
+
|
15 |
+
class Colorpicker extends Text
|
16 |
+
{
|
17 |
+
protected $dependencies = ['wp-color-picker'];
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @return string
|
21 |
+
*/
|
22 |
+
public function render()
|
23 |
+
{
|
24 |
+
$value = $this->args['value'];
|
25 |
+
|
26 |
+
$hex_color = '(([a-fA-F0-9]){3}){1,2}$';
|
27 |
+
|
28 |
+
// Prepend "#" if it's missing
|
29 |
+
if( preg_match( '/^' . $hex_color . '/i', $value ) ) {
|
30 |
+
$this->args['value'] = "#{$value}";
|
31 |
+
}
|
32 |
+
// Reset value if it's bad
|
33 |
+
else if( !preg_match( '/^#' . $hex_color . '/i', $value ) ) {
|
34 |
+
$this->args['value'] = '';
|
35 |
+
}
|
36 |
+
|
37 |
+
return parent::render([
|
38 |
+
'class' => 'color-picker-hex',
|
39 |
+
'maxlength' => 7,
|
40 |
+
'placeholder' => __( 'Hex Value', 'site-reviews' ),
|
41 |
+
'attributes' => [
|
42 |
+
'data-colorpicker' => json_encode([
|
43 |
+
'palettes' => [
|
44 |
+
'#F44336',
|
45 |
+
'#E91E63',
|
46 |
+
'#9C27B0',
|
47 |
+
'#2196F3',
|
48 |
+
'#4CAF50',
|
49 |
+
'#FFEB3B',
|
50 |
+
'#FF9800',
|
51 |
+
'#795548',
|
52 |
+
'#9E9E9E',
|
53 |
+
],
|
54 |
+
]),
|
55 |
+
],
|
56 |
+
]);
|
57 |
+
}
|
58 |
+
}
|
plugin/Html/Fields/Email.php
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Fields;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Fields\Text;
|
14 |
+
|
15 |
+
class Email extends Text
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* @return string
|
19 |
+
*/
|
20 |
+
public function render()
|
21 |
+
{
|
22 |
+
return parent::render([
|
23 |
+
'class' => 'regular-text ltr',
|
24 |
+
'type' => 'email',
|
25 |
+
]);
|
26 |
+
}
|
27 |
+
}
|
plugin/Html/Fields/Heading.php
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Fields;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Fields\Base;
|
14 |
+
|
15 |
+
class Heading extends Base
|
16 |
+
{
|
17 |
+
protected $outside = true;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @return string
|
21 |
+
*/
|
22 |
+
public function render()
|
23 |
+
{
|
24 |
+
isset( $this->args['desc'] ) ?: $this->args['desc'] = '';
|
25 |
+
|
26 |
+
// WP version check
|
27 |
+
|
28 |
+
return sprintf( '<h2 class="title">%s</h2>%s',
|
29 |
+
$this->args['value'],
|
30 |
+
wpautop( $this->args['desc'] )
|
31 |
+
);
|
32 |
+
}
|
33 |
+
}
|
plugin/Html/Fields/Hidden.php
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Fields;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Fields\Text;
|
14 |
+
|
15 |
+
class Hidden extends Text
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* @return string
|
19 |
+
*/
|
20 |
+
public function render()
|
21 |
+
{
|
22 |
+
if( isset( $this->args['label'] ) ) {
|
23 |
+
unset( $this->args['label'] );
|
24 |
+
}
|
25 |
+
|
26 |
+
if( isset( $this->args['desc'] ) ) {
|
27 |
+
unset( $this->args['desc'] );
|
28 |
+
}
|
29 |
+
|
30 |
+
if( isset( $this->args['id'] ) ) {
|
31 |
+
unset( $this->args['id'] );
|
32 |
+
}
|
33 |
+
|
34 |
+
return parent::render([
|
35 |
+
'class' => '',
|
36 |
+
'type' => 'hidden',
|
37 |
+
]);
|
38 |
+
}
|
39 |
+
}
|
plugin/Html/Fields/Number.php
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Fields;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Fields\Text;
|
14 |
+
|
15 |
+
class Number extends Text
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* @return string
|
19 |
+
*/
|
20 |
+
public function render()
|
21 |
+
{
|
22 |
+
return parent::render([
|
23 |
+
'class' => 'small-text',
|
24 |
+
'min' => '0',
|
25 |
+
'type' => 'number',
|
26 |
+
]);
|
27 |
+
}
|
28 |
+
}
|
plugin/Html/Fields/Progress.php
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Fields;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Fields\Base;
|
14 |
+
|
15 |
+
class Progress extends Base
|
16 |
+
{
|
17 |
+
protected $value;
|
18 |
+
|
19 |
+
public function __construct( array $args = [] )
|
20 |
+
{
|
21 |
+
$value = intval( $args['value'] );
|
22 |
+
|
23 |
+
$this->value = ( 0 <= $value && $value <= 100 ) ? $value : 0; // 0-100
|
24 |
+
|
25 |
+
$args['name'] = '';
|
26 |
+
$args['value'] = '';
|
27 |
+
|
28 |
+
parent::__construct( $args );
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @return string
|
33 |
+
*/
|
34 |
+
public function render()
|
35 |
+
{
|
36 |
+
$defaults = [
|
37 |
+
'data-active-text' => __( 'Please wait...', 'site-reviews' ),
|
38 |
+
'data-inactive-text' => __( 'Inactive', 'site-reviews' ),
|
39 |
+
];
|
40 |
+
|
41 |
+
$args = $this->mergeAttributesWith( $defaults );
|
42 |
+
|
43 |
+
preg_match( '/\bactive(?=$|\s)/', $args['class'], $matches );
|
44 |
+
|
45 |
+
$initialText = isset( $matches[0] )
|
46 |
+
? $args['data-active-text']
|
47 |
+
: $args['data-inactive-text'];
|
48 |
+
|
49 |
+
$progressBar = '' .
|
50 |
+
'<div class="glsr-progress %5$s" data-inactive-text="%2$s" data-active-text="%3$s">' .
|
51 |
+
'<div class="glsr-progress-bar" style="width: %4$d%%;"><span>%1$s</span></div>' .
|
52 |
+
'<div class="glsr-progress-background"><span>%1$s</span></div>' .
|
53 |
+
'</div>';
|
54 |
+
|
55 |
+
return sprintf( $progressBar,
|
56 |
+
$initialText,
|
57 |
+
$args['data-inactive-text'],
|
58 |
+
$args['data-active-text'],
|
59 |
+
$this->value,
|
60 |
+
$args['class'],
|
61 |
+
$this->generateDescription()
|
62 |
+
);
|
63 |
+
}
|
64 |
+
}
|
plugin/Html/Fields/Radio.php
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Fields;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Fields\Base;
|
14 |
+
|
15 |
+
class Radio extends Base
|
16 |
+
{
|
17 |
+
protected $multi = true;
|
18 |
+
protected $element = 'input';
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @return string
|
22 |
+
*/
|
23 |
+
public function render( $default = null )
|
24 |
+
{
|
25 |
+
$inline = $this->args['inline'] ? ' class="inline"' : '';
|
26 |
+
|
27 |
+
return sprintf( '<ul%s>%s</ul>%s',
|
28 |
+
$inline,
|
29 |
+
$this->implodeOptions( 'multi_input', $default ),
|
30 |
+
$this->generateDescription()
|
31 |
+
);
|
32 |
+
}
|
33 |
+
}
|
plugin/Html/Fields/Roles.php
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Fields;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Fields\Select;
|
14 |
+
|
15 |
+
class Roles extends Select
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* @return string
|
19 |
+
*/
|
20 |
+
public function render()
|
21 |
+
{
|
22 |
+
return parent::render([
|
23 |
+
'options' => wp_roles()->get_names(),
|
24 |
+
]);
|
25 |
+
}
|
26 |
+
}
|
plugin/Html/Fields/Select.php
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Fields;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Fields\Base;
|
14 |
+
|
15 |
+
class Select extends Base
|
16 |
+
{
|
17 |
+
protected $element = 'select';
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @return string
|
21 |
+
*/
|
22 |
+
public function render( array $defaults = [] )
|
23 |
+
{
|
24 |
+
$defaults = wp_parse_args( $defaults, [
|
25 |
+
'type' => 'select',
|
26 |
+
]);
|
27 |
+
|
28 |
+
return sprintf( '<select %s>%s</select>%s',
|
29 |
+
$this->implodeAttributes( $defaults ),
|
30 |
+
$this->implodeOptions( 'select_option' ),
|
31 |
+
$this->generateDescription()
|
32 |
+
);
|
33 |
+
}
|
34 |
+
}
|
plugin/Html/Fields/Submit.php
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Fields;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Fields\Text;
|
14 |
+
|
15 |
+
class Submit extends Text
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* @return string
|
19 |
+
*/
|
20 |
+
public function render()
|
21 |
+
{
|
22 |
+
if( isset( $this->args['name'] ) ) {
|
23 |
+
$this->args['name'] = 'submit';
|
24 |
+
}
|
25 |
+
|
26 |
+
return parent::render([
|
27 |
+
'class' => 'button button-primary',
|
28 |
+
'type' => 'submit',
|
29 |
+
]);
|
30 |
+
}
|
31 |
+
}
|
plugin/Html/Fields/Text.php
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Fields;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Fields\Base;
|
14 |
+
|
15 |
+
class Text extends Base
|
16 |
+
{
|
17 |
+
protected $element = 'input';
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @return string
|
21 |
+
*/
|
22 |
+
public function render( array $defaults = [] )
|
23 |
+
{
|
24 |
+
$defaults = wp_parse_args( $defaults, [
|
25 |
+
'class' => 'regular-text',
|
26 |
+
'type' => 'text',
|
27 |
+
]);
|
28 |
+
|
29 |
+
return sprintf( '<input %s/>%s',
|
30 |
+
$this->implodeAttributes( $defaults ),
|
31 |
+
$this->generateDescription()
|
32 |
+
);
|
33 |
+
}
|
34 |
+
}
|
plugin/Html/Fields/Textarea.php
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Fields;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Fields\Base;
|
14 |
+
|
15 |
+
class Textarea extends Base
|
16 |
+
{
|
17 |
+
protected $element = 'textarea';
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @return string
|
21 |
+
*/
|
22 |
+
public function render( array $defaults = [] )
|
23 |
+
{
|
24 |
+
$defaults = wp_parse_args( $defaults, [
|
25 |
+
'class' => 'large-text',
|
26 |
+
'rows' => 3,
|
27 |
+
'type' => 'textarea',
|
28 |
+
]);
|
29 |
+
|
30 |
+
return sprintf( '<textarea %s>%s</textarea>%s',
|
31 |
+
$this->implodeAttributes( $defaults ),
|
32 |
+
$this->args['value'],
|
33 |
+
$this->generateDescription()
|
34 |
+
);
|
35 |
+
}
|
36 |
+
}
|
plugin/Html/Fields/Url.php
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Fields;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Fields\Text;
|
14 |
+
|
15 |
+
class Url extends Text
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* @return string
|
19 |
+
*/
|
20 |
+
public function render()
|
21 |
+
{
|
22 |
+
return parent::render([
|
23 |
+
'class' => 'regular-text code',
|
24 |
+
'type' => 'url',
|
25 |
+
]);
|
26 |
+
}
|
27 |
+
}
|
plugin/Html/Fields/Yesno.php
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Fields;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Fields\Radio;
|
14 |
+
|
15 |
+
class Yesno extends Radio
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* @return string
|
19 |
+
*/
|
20 |
+
public function render()
|
21 |
+
{
|
22 |
+
$defaultValue = 0;
|
23 |
+
|
24 |
+
$this->args['options'] = [
|
25 |
+
__( 'No', 'site-reviews' ),
|
26 |
+
__( 'Yes', 'site-reviews' ),
|
27 |
+
];
|
28 |
+
|
29 |
+
return parent::render( $defaultValue );
|
30 |
+
}
|
31 |
+
}
|
plugin/Html/Form.php
ADDED
@@ -0,0 +1,288 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\App;
|
14 |
+
|
15 |
+
use Exception;
|
16 |
+
|
17 |
+
class Form
|
18 |
+
{
|
19 |
+
/**
|
20 |
+
* @var App
|
21 |
+
*/
|
22 |
+
protected $app;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var array
|
26 |
+
*/
|
27 |
+
protected $args;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @var array
|
31 |
+
*/
|
32 |
+
protected $dependencies;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* @var array
|
36 |
+
*/
|
37 |
+
protected $fields;
|
38 |
+
|
39 |
+
public function __construct( App $app )
|
40 |
+
{
|
41 |
+
$this->app = $app;
|
42 |
+
$this->args = [];
|
43 |
+
$this->dependencies = [];
|
44 |
+
$this->fields = [];
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* @param string $property
|
49 |
+
*
|
50 |
+
* @return mixed
|
51 |
+
* @throws Exception
|
52 |
+
*/
|
53 |
+
public function __get( $property )
|
54 |
+
{
|
55 |
+
switch( $property ) {
|
56 |
+
case 'args';
|
57 |
+
case 'dependencies';
|
58 |
+
case 'fields';
|
59 |
+
return $this->$property;
|
60 |
+
}
|
61 |
+
|
62 |
+
throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $property );
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* @param string $property
|
67 |
+
* @param string $value
|
68 |
+
*
|
69 |
+
* @return void
|
70 |
+
* @throws Exception
|
71 |
+
*/
|
72 |
+
public function __set( $property, $value )
|
73 |
+
{
|
74 |
+
switch( $property ) {
|
75 |
+
case 'args':
|
76 |
+
case 'dependencies':
|
77 |
+
case 'fields':
|
78 |
+
$this->$property = $value;
|
79 |
+
break;
|
80 |
+
default:
|
81 |
+
throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $property );
|
82 |
+
}
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Add a field to the form
|
87 |
+
*
|
88 |
+
* @return Form
|
89 |
+
*/
|
90 |
+
public function addField( array $args = [] )
|
91 |
+
{
|
92 |
+
$field = $this->app->make( 'Html\Field' )->normalize( $args );
|
93 |
+
|
94 |
+
if( $field->args['render'] !== false ) {
|
95 |
+
$this->dependencies = array_unique(
|
96 |
+
array_merge( $field->dependencies, $this->dependencies )
|
97 |
+
);
|
98 |
+
$this->fields[] = $field;
|
99 |
+
}
|
100 |
+
|
101 |
+
return $this;
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Normalize the form arguments
|
106 |
+
*
|
107 |
+
* @return Form
|
108 |
+
*/
|
109 |
+
public function normalize( array $args = [] )
|
110 |
+
{
|
111 |
+
$defaults = [
|
112 |
+
'action' => '',
|
113 |
+
'attributes' => '',
|
114 |
+
'id' => '',
|
115 |
+
'class' => '',
|
116 |
+
'enctype' => 'multipart/form-data',
|
117 |
+
'method' => 'post',
|
118 |
+
'nonce' => '',
|
119 |
+
'submit' => __( 'Submit', 'site-reviews' ),
|
120 |
+
];
|
121 |
+
|
122 |
+
$this->args = array_merge( $defaults, $args );
|
123 |
+
|
124 |
+
$attributes = $this->app->make( 'Html\Normalize' )->form( $this->args, 'implode' );
|
125 |
+
|
126 |
+
$this->args['attributes'] = $attributes;
|
127 |
+
|
128 |
+
return $this;
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Render the form
|
133 |
+
*
|
134 |
+
* @param mixed $print
|
135 |
+
*
|
136 |
+
* @return string|void
|
137 |
+
*/
|
138 |
+
public function render( $print = true )
|
139 |
+
{
|
140 |
+
$rendered = sprintf( '<form %s>%s%s</form>',
|
141 |
+
$this->args['attributes'],
|
142 |
+
$this->generateFields(),
|
143 |
+
$this->generateSubmitButton()
|
144 |
+
);
|
145 |
+
|
146 |
+
if( !!$print && $print !== 'return' ) {
|
147 |
+
echo $rendered;
|
148 |
+
}
|
149 |
+
|
150 |
+
return $rendered;
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
* Reset the Form
|
155 |
+
*
|
156 |
+
* @return Form
|
157 |
+
*/
|
158 |
+
public function reset()
|
159 |
+
{
|
160 |
+
$this->args = [];
|
161 |
+
$this->dependencies = [];
|
162 |
+
$this->fields = [];
|
163 |
+
|
164 |
+
return $this;
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Generate the hidden fields of a wp-admin form
|
169 |
+
*
|
170 |
+
* @return string
|
171 |
+
*/
|
172 |
+
protected function generateAdminFields()
|
173 |
+
{
|
174 |
+
ob_start();
|
175 |
+
|
176 |
+
settings_fields( $this->args['nonce'] );
|
177 |
+
do_settings_sections( $this->args['nonce'] );
|
178 |
+
|
179 |
+
return ob_get_clean();
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Generate the form fields
|
184 |
+
*
|
185 |
+
* @return string
|
186 |
+
*/
|
187 |
+
protected function generateFields()
|
188 |
+
{
|
189 |
+
$hiddenFields = '';
|
190 |
+
|
191 |
+
$fields = array_reduce( $this->fields, function( $carry, $formField ) use ( &$hiddenFields ) {
|
192 |
+
|
193 |
+
$stringLegend = '<legend class="screen-reader-text"><span>%s</span></legend>';
|
194 |
+
$stringFieldset = '<fieldset%s>%s%s</fieldset>';
|
195 |
+
$stringRendered = '<tr class="glsr-field %s"><th scope="row">%s</th><td>%s</td></tr>';
|
196 |
+
$outsideRendered = '</tbody></table>%s<table class="form-table"><tbody>';
|
197 |
+
|
198 |
+
$field = $formField->getField();
|
199 |
+
$multi = $field->multi === true;
|
200 |
+
$label = $field->generateLabel();
|
201 |
+
$rendered = $field->render();
|
202 |
+
|
203 |
+
// render hidden inputs outside the table
|
204 |
+
if( $field->args['type'] === 'hidden' ) {
|
205 |
+
$hiddenFields .= $rendered;
|
206 |
+
return $carry;
|
207 |
+
}
|
208 |
+
|
209 |
+
$hiddenClass = $this->isFieldHidden( $formField ) ? 'hidden' : '';
|
210 |
+
|
211 |
+
if( $multi ) {
|
212 |
+
if( $depends = $formField->getDataDepends() ) {
|
213 |
+
$depends = sprintf( ' data-depends=\'%s\'', json_encode( $depends ) );
|
214 |
+
}
|
215 |
+
|
216 |
+
$legend = $label ? sprintf( $stringLegend, $label ) : '';
|
217 |
+
$rendered = sprintf( $stringFieldset, $depends, $legend, $rendered );
|
218 |
+
}
|
219 |
+
|
220 |
+
$renderedField = $field->outside
|
221 |
+
? sprintf( $outsideRendered, $rendered )
|
222 |
+
: sprintf( $stringRendered, $hiddenClass, $label, $rendered );
|
223 |
+
|
224 |
+
return $carry . $renderedField;
|
225 |
+
});
|
226 |
+
|
227 |
+
$hiddenFields .= is_admin()
|
228 |
+
? $this->generateAdminFields()
|
229 |
+
: $this->generatePublicFields();
|
230 |
+
|
231 |
+
return sprintf( '<table class="form-table"><tbody>%s</tbody></table>%s', $fields, $hiddenFields );
|
232 |
+
}
|
233 |
+
|
234 |
+
/**
|
235 |
+
* Generate the hidden fields of a front-end form
|
236 |
+
*
|
237 |
+
* @return null|string
|
238 |
+
*/
|
239 |
+
protected function generatePublicFields()
|
240 |
+
{
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* Generate the form submit button
|
245 |
+
*
|
246 |
+
* @return null|string
|
247 |
+
*/
|
248 |
+
protected function generateSubmitButton()
|
249 |
+
{
|
250 |
+
$args = $this->args['submit'];
|
251 |
+
|
252 |
+
is_array( $args ) ?: $args = ['text' => $args ];
|
253 |
+
|
254 |
+
$args = shortcode_atts([
|
255 |
+
'text' => __( 'Save Changes', 'site-reviews' ),
|
256 |
+
'type' => 'primary',
|
257 |
+
'name' => 'submit',
|
258 |
+
'wrap' => true,
|
259 |
+
'other_attributes' => null,
|
260 |
+
], $args );
|
261 |
+
|
262 |
+
if( is_admin() ) {
|
263 |
+
ob_start();
|
264 |
+
submit_button( $args['text'], $args['type'], $args['name'], $args['wrap'], $args['other_attributes'] );
|
265 |
+
return ob_get_clean();
|
266 |
+
}
|
267 |
+
}
|
268 |
+
|
269 |
+
/**
|
270 |
+
* @param object $field GeminiLabs\SiteReviews\Html\Fields\*
|
271 |
+
*
|
272 |
+
* @return bool|null
|
273 |
+
*/
|
274 |
+
protected function isFieldHidden( $field )
|
275 |
+
{
|
276 |
+
if( !( $dependsOn = $field->getDataDepends() ) )return;
|
277 |
+
|
278 |
+
foreach( $this->fields as $formField ) {
|
279 |
+
if( $dependsOn['name'] !== $formField->args['name'] )continue;
|
280 |
+
|
281 |
+
if( is_array( $dependsOn['value'] ) ) {
|
282 |
+
return !in_array( $formField->args['value'], $dependsOn['value'] );
|
283 |
+
}
|
284 |
+
|
285 |
+
return $dependsOn['value'] != $formField->args['value'];
|
286 |
+
}
|
287 |
+
}
|
288 |
+
}
|
plugin/Html/Normalize.php
ADDED
@@ -0,0 +1,396 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html;
|
12 |
+
|
13 |
+
class Normalize
|
14 |
+
{
|
15 |
+
/**
|
16 |
+
* @var array
|
17 |
+
*/
|
18 |
+
protected $args;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @var array
|
22 |
+
*/
|
23 |
+
protected $booleanAttributes;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var array
|
27 |
+
*/
|
28 |
+
protected $formAttributes;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* @var array
|
32 |
+
*/
|
33 |
+
protected $globalAttributes;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @var array
|
37 |
+
*/
|
38 |
+
protected $globalWildcardAttributes;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @var array
|
42 |
+
*/
|
43 |
+
protected $inputAttributes;
|
44 |
+
|
45 |
+
/**
|
46 |
+
* @var array
|
47 |
+
*/
|
48 |
+
protected $inputTypes;
|
49 |
+
|
50 |
+
/**
|
51 |
+
* @var array
|
52 |
+
*/
|
53 |
+
protected $selectAttributes;
|
54 |
+
|
55 |
+
/**
|
56 |
+
* @var array
|
57 |
+
*/
|
58 |
+
protected $textareaAttributes;
|
59 |
+
|
60 |
+
public function __construct( array $args = [] )
|
61 |
+
{
|
62 |
+
$this->args = $args;
|
63 |
+
|
64 |
+
$this->booleanAttributes = apply_filters( 'site-reviews/normalize/boolean/attributes', [
|
65 |
+
'autofocus',
|
66 |
+
'capture',
|
67 |
+
'checked',
|
68 |
+
'disabled',
|
69 |
+
'draggable',
|
70 |
+
'formnovalidate',
|
71 |
+
'hidden',
|
72 |
+
'multiple',
|
73 |
+
'novalidate',
|
74 |
+
'readonly',
|
75 |
+
'required',
|
76 |
+
'selected',
|
77 |
+
'spellcheck',
|
78 |
+
'webkitdirectory',
|
79 |
+
]);
|
80 |
+
|
81 |
+
$this->formAttributes = apply_filters( 'site-reviews/normalize/form/attributes', [
|
82 |
+
'accept',
|
83 |
+
'accept-charset',
|
84 |
+
'action',
|
85 |
+
'autocapitalize',
|
86 |
+
'autocomplete',
|
87 |
+
'enctype',
|
88 |
+
'method',
|
89 |
+
'name',
|
90 |
+
'novalidate',
|
91 |
+
'target',
|
92 |
+
]);
|
93 |
+
|
94 |
+
$this->globalAttributes = apply_filters( 'site-reviews/normalize/global/attributes', [
|
95 |
+
'accesskey',
|
96 |
+
'class',
|
97 |
+
'contenteditable',
|
98 |
+
'contextmenu',
|
99 |
+
'dir',
|
100 |
+
'draggable',
|
101 |
+
'dropzone',
|
102 |
+
'hidden',
|
103 |
+
'id',
|
104 |
+
'lang',
|
105 |
+
'spellcheck',
|
106 |
+
'style',
|
107 |
+
'tabindex',
|
108 |
+
'title',
|
109 |
+
]);
|
110 |
+
|
111 |
+
$this->globalWildcardAttributes = apply_filters( 'site-reviews/normalize/global/wildcard/attributes', [
|
112 |
+
'aria-',
|
113 |
+
'data-',
|
114 |
+
'item',
|
115 |
+
'on',
|
116 |
+
]);
|
117 |
+
|
118 |
+
$this->inputAttributes = apply_filters( 'site-reviews/normalize/input/attributes', [
|
119 |
+
'accept',
|
120 |
+
'autocapitalize',
|
121 |
+
'autocomplete',
|
122 |
+
'autocorrect',
|
123 |
+
'autofocus',
|
124 |
+
'capture',
|
125 |
+
'checked',
|
126 |
+
'disabled',
|
127 |
+
'form',
|
128 |
+
'formaction',
|
129 |
+
'formenctype',
|
130 |
+
'formmethod',
|
131 |
+
'formnovalidate',
|
132 |
+
'formtarget',
|
133 |
+
'height',
|
134 |
+
'incremental',
|
135 |
+
'inputmode',
|
136 |
+
'list',
|
137 |
+
'max',
|
138 |
+
'maxlength',
|
139 |
+
'min',
|
140 |
+
'minlength',
|
141 |
+
'mozactionhint',
|
142 |
+
'multiple',
|
143 |
+
'name',
|
144 |
+
'pattern',
|
145 |
+
'placeholder',
|
146 |
+
'readonly',
|
147 |
+
'required',
|
148 |
+
'results',
|
149 |
+
'selectionDirection',
|
150 |
+
'size',
|
151 |
+
'src',
|
152 |
+
'step',
|
153 |
+
'type',
|
154 |
+
'value',
|
155 |
+
'webkitdirectory',
|
156 |
+
'width',
|
157 |
+
'x-moz-errormessage',
|
158 |
+
]);
|
159 |
+
|
160 |
+
$this->inputTypes = apply_filters( 'site-reviews/normalize/input/types', [
|
161 |
+
'button',
|
162 |
+
'checkbox',
|
163 |
+
'color',
|
164 |
+
'date',
|
165 |
+
'datetime',
|
166 |
+
'datetime-local',
|
167 |
+
'email',
|
168 |
+
'file',
|
169 |
+
'hidden',
|
170 |
+
'image',
|
171 |
+
'max',
|
172 |
+
'min',
|
173 |
+
'month',
|
174 |
+
'number',
|
175 |
+
'password',
|
176 |
+
'radio',
|
177 |
+
'range',
|
178 |
+
'reset',
|
179 |
+
'search',
|
180 |
+
'step',
|
181 |
+
'submit',
|
182 |
+
'tel',
|
183 |
+
'text',
|
184 |
+
'time',
|
185 |
+
'url',
|
186 |
+
'value',
|
187 |
+
'week',
|
188 |
+
]);
|
189 |
+
|
190 |
+
$this->selectAttributes = apply_filters( 'site-reviews/normalize/select/attributes', [
|
191 |
+
'autofocus',
|
192 |
+
'disabled',
|
193 |
+
'form',
|
194 |
+
'multiple',
|
195 |
+
'name',
|
196 |
+
'required',
|
197 |
+
'size',
|
198 |
+
]);
|
199 |
+
|
200 |
+
$this->textareaAttributes = apply_filters( 'site-reviews/normalize/textarea/attributes', [
|
201 |
+
'autocapitalize',
|
202 |
+
'autocomplete',
|
203 |
+
'autofocus',
|
204 |
+
'cols',
|
205 |
+
'disabled',
|
206 |
+
'form',
|
207 |
+
'maxlength',
|
208 |
+
'minlength',
|
209 |
+
'name',
|
210 |
+
'placeholder',
|
211 |
+
'readonly',
|
212 |
+
'required',
|
213 |
+
'rows',
|
214 |
+
'selectionDirection',
|
215 |
+
'selectionEnd',
|
216 |
+
'selectionStart',
|
217 |
+
'wrap',
|
218 |
+
]);
|
219 |
+
}
|
220 |
+
|
221 |
+
/**
|
222 |
+
* Normalize form attributes
|
223 |
+
*
|
224 |
+
* @return array|string
|
225 |
+
*/
|
226 |
+
public function form( array $args = [], $implode = false )
|
227 |
+
{
|
228 |
+
$attributes = $this->parseAttributes( $this->formAttributes, $args );
|
229 |
+
|
230 |
+
return $this->maybeImplode( $attributes, $implode );
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Normalize input attributes
|
235 |
+
*
|
236 |
+
* @return array|string
|
237 |
+
*/
|
238 |
+
public function input( array $args = [], $implode = false )
|
239 |
+
{
|
240 |
+
$this->filterInputType();
|
241 |
+
|
242 |
+
$attributes = $this->parseAttributes( $this->inputAttributes, $args );
|
243 |
+
|
244 |
+
return $this->maybeImplode( $attributes, $implode );
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* Possibly implode attributes into a string
|
249 |
+
*
|
250 |
+
* @param bool|string $implode
|
251 |
+
*
|
252 |
+
* @return array|string
|
253 |
+
*/
|
254 |
+
public function maybeImplode( array $attributes, $implode = true )
|
255 |
+
{
|
256 |
+
if( !$implode || $implode !== 'implode' ) {
|
257 |
+
return $attributes;
|
258 |
+
}
|
259 |
+
|
260 |
+
$results = [];
|
261 |
+
|
262 |
+
foreach( $attributes as $key => $value ) {
|
263 |
+
|
264 |
+
// if data attributes, use single quotes in case of json encoded values
|
265 |
+
$quotes = false !== stripos( $key, 'data-' ) ? "'" : '"';
|
266 |
+
|
267 |
+
if( is_array( $value ) ) {
|
268 |
+
$value = json_encode( $value );
|
269 |
+
$quotes = "'";
|
270 |
+
}
|
271 |
+
|
272 |
+
$results[] = is_string( $key )
|
273 |
+
? sprintf( '%1$s=%3$s%2$s%3$s', $key, $value, $quotes )
|
274 |
+
: $value;
|
275 |
+
}
|
276 |
+
|
277 |
+
return implode( ' ', $results );
|
278 |
+
}
|
279 |
+
|
280 |
+
/**
|
281 |
+
* Normalize select attributes
|
282 |
+
*
|
283 |
+
* @return array|string
|
284 |
+
*/
|
285 |
+
public function select( array $args = [], $implode = false )
|
286 |
+
{
|
287 |
+
$attributes = $this->parseAttributes( $this->selectAttributes, $args );
|
288 |
+
|
289 |
+
return $this->maybeImplode( $attributes, $implode );
|
290 |
+
}
|
291 |
+
|
292 |
+
/**
|
293 |
+
* Normalize textarea attributes
|
294 |
+
*
|
295 |
+
* @return array|string
|
296 |
+
*/
|
297 |
+
public function textarea( array $args = [], $implode = false )
|
298 |
+
{
|
299 |
+
$attributes = $this->parseAttributes( $this->textareaAttributes, $args );
|
300 |
+
|
301 |
+
return $this->maybeImplode( $attributes, $implode );
|
302 |
+
}
|
303 |
+
|
304 |
+
/**
|
305 |
+
* Filter attributes by the provided attrribute keys and remove any non-boolean keys
|
306 |
+
* with empty values
|
307 |
+
*
|
308 |
+
* @return array
|
309 |
+
*/
|
310 |
+
protected function filterAttributes( array $attributeKeys )
|
311 |
+
{
|
312 |
+
$filtered = array_intersect_key( $this->args, array_flip( $attributeKeys ));
|
313 |
+
|
314 |
+
// normalize truthy boolean attributes
|
315 |
+
foreach( $filtered as $key => $value ) {
|
316 |
+
if( !in_array( $key, $this->booleanAttributes ) )continue;
|
317 |
+
|
318 |
+
if( $value === false ) {
|
319 |
+
unset( $filtered[ $key ] );
|
320 |
+
}
|
321 |
+
else {
|
322 |
+
$filtered[ $key ] = '';
|
323 |
+
}
|
324 |
+
}
|
325 |
+
|
326 |
+
$filteredKeys = array_filter( array_keys( $filtered ), function( $key ) use ( $filtered ) {
|
327 |
+
return !(
|
328 |
+
empty( $filtered[ $key ] )
|
329 |
+
&& !is_numeric( $filtered[ $key ] )
|
330 |
+
&& !in_array( $key, $this->booleanAttributes )
|
331 |
+
);
|
332 |
+
});
|
333 |
+
|
334 |
+
return array_intersect_key( $filtered, array_flip( $filteredKeys ) );
|
335 |
+
}
|
336 |
+
|
337 |
+
/**
|
338 |
+
* @return array
|
339 |
+
*/
|
340 |
+
protected function filterGlobalAttributes()
|
341 |
+
{
|
342 |
+
$global = $this->filterAttributes( $this->globalAttributes );
|
343 |
+
|
344 |
+
$wildcards = [];
|
345 |
+
|
346 |
+
foreach( $this->globalWildcardAttributes as $wildcard ) {
|
347 |
+
|
348 |
+
foreach( $this->args as $key => $value ) {
|
349 |
+
|
350 |
+
$length = strlen( $wildcard );
|
351 |
+
$result = substr( $key, 0, $length) === $wildcard;
|
352 |
+
|
353 |
+
if( $result ) {
|
354 |
+
// only allow data attributes to have an empty value
|
355 |
+
if( $wildcard != 'data-' && empty( $value ) )continue;
|
356 |
+
|
357 |
+
if( is_array( $value ) ) {
|
358 |
+
|
359 |
+
if( $wildcard != 'data-' )continue;
|
360 |
+
|
361 |
+
$value = json_encode( $value );
|
362 |
+
}
|
363 |
+
|
364 |
+
$wildcards[ $key ] = $value;
|
365 |
+
}
|
366 |
+
}
|
367 |
+
}
|
368 |
+
|
369 |
+
return array_merge( $global, $wildcards );
|
370 |
+
}
|
371 |
+
|
372 |
+
/**
|
373 |
+
* @return void
|
374 |
+
*/
|
375 |
+
protected function filterInputType()
|
376 |
+
{
|
377 |
+
if( !isset( $this->args['type'] ) || !in_array( $this->args['type'], $this->inputTypes ) ) {
|
378 |
+
$this->args['type'] = 'text';
|
379 |
+
}
|
380 |
+
}
|
381 |
+
|
382 |
+
/**
|
383 |
+
* @return array
|
384 |
+
*/
|
385 |
+
protected function parseAttributes( array $attributes, array $args = [] )
|
386 |
+
{
|
387 |
+
if( !empty( $args ) ) {
|
388 |
+
$this->args = array_change_key_case( $args );
|
389 |
+
}
|
390 |
+
|
391 |
+
$global = $this->filterGlobalAttributes();
|
392 |
+
$local = $this->filterAttributes( $attributes );
|
393 |
+
|
394 |
+
return array_merge( $global, $local );
|
395 |
+
}
|
396 |
+
}
|
plugin/Html/Partial.php
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\App;
|
14 |
+
use ReflectionException;
|
15 |
+
|
16 |
+
class Partial
|
17 |
+
{
|
18 |
+
/**
|
19 |
+
* @var App
|
20 |
+
*/
|
21 |
+
protected $app;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var array
|
25 |
+
*/
|
26 |
+
protected $args;
|
27 |
+
|
28 |
+
public function __construct( App $app )
|
29 |
+
{
|
30 |
+
$this->app = $app;
|
31 |
+
$this->args = [];
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Normalize the partial arguments
|
36 |
+
*
|
37 |
+
* @return $this
|
38 |
+
*/
|
39 |
+
public function normalize( $name, array $args = [] )
|
40 |
+
{
|
41 |
+
$defaults = [
|
42 |
+
'partial' => $name,
|
43 |
+
];
|
44 |
+
|
45 |
+
$this->args = wp_parse_args( $args, $defaults );
|
46 |
+
|
47 |
+
return $this;
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Render the partial
|
52 |
+
*
|
53 |
+
* @param mixed $print
|
54 |
+
*
|
55 |
+
* @return string|void
|
56 |
+
*/
|
57 |
+
public function render( $print = true )
|
58 |
+
{
|
59 |
+
$className = sprintf( 'GeminiLabs\SiteReviews\Html\Partials\%s', ucfirst( $this->args['partial'] ) );
|
60 |
+
|
61 |
+
$instance = $this->app->make( $className );
|
62 |
+
|
63 |
+
$instance->args = $this->args;
|
64 |
+
|
65 |
+
$rendered = $instance->render();
|
66 |
+
|
67 |
+
$rendered = apply_filters( 'site-reviews/rendered/partial', $rendered, $this->args['partial'] );
|
68 |
+
|
69 |
+
if( !!$print && $print !== 'return' ) {
|
70 |
+
echo $rendered;
|
71 |
+
}
|
72 |
+
|
73 |
+
return $rendered;
|
74 |
+
}
|
75 |
+
}
|
plugin/Html/Partials/Addon.php
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Partials;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Partials\Base;
|
14 |
+
|
15 |
+
class Addon extends Base
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* Generate an add-on element
|
19 |
+
*
|
20 |
+
* @return string
|
21 |
+
*/
|
22 |
+
public function render()
|
23 |
+
{
|
24 |
+
$defaults = [
|
25 |
+
'name' => '',
|
26 |
+
'title' => '',
|
27 |
+
'description' => '',
|
28 |
+
'link' => '',
|
29 |
+
];
|
30 |
+
|
31 |
+
$args = shortcode_atts( $defaults, $this->args );
|
32 |
+
|
33 |
+
extract( $args );
|
34 |
+
|
35 |
+
$addonPoster = $link
|
36 |
+
? sprintf( '<a href="%s" class="glsr-addon-screenshot" data-name="%s"></a>', $link, $name )
|
37 |
+
: sprintf( '<div class="glsr-addon-screenshot" data-name="%s"></div>', $name );
|
38 |
+
|
39 |
+
$addonLink = $link
|
40 |
+
? sprintf( '<a href="%s" class="glsr-addon-link button button-secondary">%s</a>', $link, __( 'More Info', 'site-reviews' ) )
|
41 |
+
: '';
|
42 |
+
|
43 |
+
return '' .
|
44 |
+
'<div class="glsr-addon-wrap"><div class="glsr-addon">' .
|
45 |
+
$addonPoster .
|
46 |
+
sprintf( '<div class="glsr-addon-description"><p>%s</p></div>', $description ) .
|
47 |
+
sprintf( '<h3 class="glsr-addon-name">%s</h3>%s', $title, $addonLink ) .
|
48 |
+
'</div></div>';
|
49 |
+
}
|
50 |
+
}
|
plugin/Html/Partials/Base.php
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Partials;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\App;
|
14 |
+
|
15 |
+
abstract class Base
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* @var App
|
19 |
+
*/
|
20 |
+
public $app;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @var array
|
24 |
+
*/
|
25 |
+
public $args = [];
|
26 |
+
|
27 |
+
public function __construct( App $app )
|
28 |
+
{
|
29 |
+
$this->app = $app;
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Generate select option
|
34 |
+
*
|
35 |
+
* @param string $value
|
36 |
+
* @param string $title
|
37 |
+
* @param string $selected
|
38 |
+
*
|
39 |
+
* @return string
|
40 |
+
*/
|
41 |
+
protected function selectOption( $value, $title, $selected )
|
42 |
+
{
|
43 |
+
return sprintf( '<option value="%s" %s>%s</option>',
|
44 |
+
$value,
|
45 |
+
selected( $selected, $value, false ),
|
46 |
+
$title
|
47 |
+
);
|
48 |
+
}
|
49 |
+
}
|
plugin/Html/Partials/Filterby.php
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Partials;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Partials\Base;
|
14 |
+
|
15 |
+
class Filterby extends Base
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* Generate a filter dropdown for the admin post_type table
|
19 |
+
*
|
20 |
+
* @return string|null
|
21 |
+
*/
|
22 |
+
public function render()
|
23 |
+
{
|
24 |
+
$defaults = [
|
25 |
+
'name' => '',
|
26 |
+
'values' => [],
|
27 |
+
];
|
28 |
+
|
29 |
+
$args = shortcode_atts( $defaults, $this->args );
|
30 |
+
|
31 |
+
extract( $args );
|
32 |
+
|
33 |
+
if( !$name || empty( $values ) )return;
|
34 |
+
|
35 |
+
$options = '';
|
36 |
+
|
37 |
+
foreach( $values as $value => $title ) {
|
38 |
+
$options .= $this->selectOption( $value, $title, filter_input( INPUT_GET, $name ) );
|
39 |
+
}
|
40 |
+
|
41 |
+
printf( '<select name="%s">%s</select>', $name, $options );
|
42 |
+
}
|
43 |
+
}
|
plugin/Html/Partials/Rating.php
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Partials;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Partials\Base;
|
14 |
+
|
15 |
+
class Rating extends Base
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* Generate a star rating
|
19 |
+
*
|
20 |
+
* @return string
|
21 |
+
*/
|
22 |
+
public function render()
|
23 |
+
{
|
24 |
+
$defaults = [
|
25 |
+
'stars' => 5,
|
26 |
+
];
|
27 |
+
|
28 |
+
$args = shortcode_atts( $defaults, $this->args );
|
29 |
+
|
30 |
+
extract( $args );
|
31 |
+
|
32 |
+
$rating = '';
|
33 |
+
|
34 |
+
$star = is_admin() ? ' star star%s' : '%s';
|
35 |
+
$star = sprintf( '<span class="glsr-star%s"></span>', $star );
|
36 |
+
|
37 |
+
$class = 'glsr-review-rating' . ( is_admin() ? ' star-rating' : '' );
|
38 |
+
|
39 |
+
for( $i = 0; $i < $stars; $i++ ) {
|
40 |
+
$rating .= sprintf( $star, '-full' );
|
41 |
+
}
|
42 |
+
|
43 |
+
for( $i = 5; $i > $stars; $i-- ) {
|
44 |
+
$rating .= sprintf( $star, '-empty' );
|
45 |
+
}
|
46 |
+
|
47 |
+
return sprintf( '<span class="%s">%s</span>', $class, $rating );
|
48 |
+
}
|
49 |
+
}
|
plugin/Html/Partials/Reviews.php
ADDED
@@ -0,0 +1,311 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Partials;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Partials\Base;
|
14 |
+
|
15 |
+
class Reviews extends Base
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* Generate a review
|
19 |
+
*
|
20 |
+
* @return string
|
21 |
+
*/
|
22 |
+
public function render()
|
23 |
+
{
|
24 |
+
global $post;
|
25 |
+
|
26 |
+
$defaults = [
|
27 |
+
'class' => '',
|
28 |
+
'display' => 'title',
|
29 |
+
'max_reviews' => '',
|
30 |
+
'min_rating' => '',
|
31 |
+
'order_by' => '',
|
32 |
+
'pagination' => false,
|
33 |
+
'show_author' => false,
|
34 |
+
'show_date' => false,
|
35 |
+
'show_link' => false,
|
36 |
+
'show_rating' => false,
|
37 |
+
'site_name' => '',
|
38 |
+
'word_limit' => 55,
|
39 |
+
];
|
40 |
+
|
41 |
+
$args = shortcode_atts( $defaults, $this->args );
|
42 |
+
|
43 |
+
$reviews = $this->app->make( 'Database' )->getReviews( $args );
|
44 |
+
|
45 |
+
if( $reviews->have_posts() ) {
|
46 |
+
|
47 |
+
$html = '';
|
48 |
+
|
49 |
+
while( $reviews->have_posts() ) :
|
50 |
+
|
51 |
+
$reviews->the_post();
|
52 |
+
|
53 |
+
$meta = get_post_meta( $post->ID );
|
54 |
+
$meta = (object) array_map( 'array_shift', $meta );
|
55 |
+
|
56 |
+
$review_author = $this->reviewAuthor( $args['show_author'], $meta->author );
|
57 |
+
$review_excerpt = $this->reviewExcerpt( $args, $meta, $post->ID );
|
58 |
+
$review_meta = $this->reviewMeta( $args, $meta, $post->ID );
|
59 |
+
$review_title = $this->reviewTitle( $args, $meta, $post->ID );
|
60 |
+
|
61 |
+
$print_both = $review_title . $review_meta . $review_excerpt;
|
62 |
+
$print_excerpt = $review_meta . $review_excerpt;
|
63 |
+
$print_title = $review_title . $review_meta;
|
64 |
+
|
65 |
+
$html .= sprintf( '<div class="glsr-review">%s%s</div>', ${"print_{$args['display']}"}, $review_author );
|
66 |
+
|
67 |
+
endwhile;
|
68 |
+
|
69 |
+
if( $args['pagination'] ) {
|
70 |
+
$html .= $this->buildPagination( $reviews->max_num_pages );
|
71 |
+
}
|
72 |
+
|
73 |
+
wp_reset_postdata();
|
74 |
+
}
|
75 |
+
else {
|
76 |
+
$html = sprintf( '<p class="glsr-review glsr-no-reviews">%s</p>',
|
77 |
+
__( 'No reviews were found.', 'site-reviews' )
|
78 |
+
);
|
79 |
+
}
|
80 |
+
|
81 |
+
return sprintf( '<div class="glsr-reviews-wrap"><div class="glsr-reviews %s">%s</div></div>',
|
82 |
+
$args['class'],
|
83 |
+
$html
|
84 |
+
);
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Build an excerpt from a string
|
89 |
+
*
|
90 |
+
* @param string $text
|
91 |
+
* @param int $wordCount
|
92 |
+
* @param string $more
|
93 |
+
*
|
94 |
+
* @return string
|
95 |
+
*/
|
96 |
+
protected function buildExcerpt( $text, $wordCount = 55, $more = null )
|
97 |
+
{
|
98 |
+
$text = strip_shortcodes( $text );
|
99 |
+
$text = wptexturize( $text );
|
100 |
+
$text = convert_smilies( $text );
|
101 |
+
$text = wpautop( $text );
|
102 |
+
$text = str_replace( ']]>', ']]>', $text );
|
103 |
+
|
104 |
+
if( apply_filters( 'site-reviews/reviews/use_excerpt', true ) ) {
|
105 |
+
$wordCount = apply_filters( 'site-reviews/reviews/excerpt_length', $wordCount );
|
106 |
+
$text = wp_trim_words( $text, $wordCount, $more );
|
107 |
+
}
|
108 |
+
|
109 |
+
return $text;
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Build the reviews pagination
|
114 |
+
*
|
115 |
+
* @param int $maxPageNum
|
116 |
+
*
|
117 |
+
* @return string|null
|
118 |
+
*/
|
119 |
+
protected function buildPagination( $maxPageNum )
|
120 |
+
{
|
121 |
+
if( $maxPageNum < 2 )return;
|
122 |
+
|
123 |
+
$paged = $this->app->make( 'Database' )->getCurrentPageNumber();
|
124 |
+
$theme = wp_get_theme()->get( 'TextDomain' );
|
125 |
+
|
126 |
+
if( in_array( $theme, ['twentyten','twentyeleven','twentytwelve','twentythirteen'] ) ) {
|
127 |
+
|
128 |
+
$links = '';
|
129 |
+
|
130 |
+
if( $paged > 1 ) {
|
131 |
+
$links .= sprintf( '<div class="nav-previous"><a href="%s">%s</a></div>',
|
132 |
+
get_pagenum_link( $paged - 1 ),
|
133 |
+
__( '<span class="meta-nav">←</span> Previous', 'site-reviews' )
|
134 |
+
);
|
135 |
+
}
|
136 |
+
if( $paged < $maxPageNum ) {
|
137 |
+
$links .= sprintf( '<div class="nav-next"><a href="%s">%s</a></div>',
|
138 |
+
get_pagenum_link( $paged + 1 ),
|
139 |
+
__( 'Next <span class="meta-nav">→</span>', 'site-reviews' )
|
140 |
+
);
|
141 |
+
}
|
142 |
+
}
|
143 |
+
else {
|
144 |
+
$links = paginate_links([
|
145 |
+
'before_page_number' => '<span class="meta-nav screen-reader-text">' . __( 'Page', 'site-reviews' ) . ' </span>',
|
146 |
+
'current' => $paged,
|
147 |
+
'mid_size' => 1,
|
148 |
+
'next_text' => __( 'Next →', 'site-reviews' ),
|
149 |
+
'prev_text' => __( '← Previous', 'site-reviews' ),
|
150 |
+
'total' => $maxPageNum,
|
151 |
+
]);
|
152 |
+
}
|
153 |
+
|
154 |
+
$links = apply_filters( 'site-reviews/reviews/navigation_links', $links, $paged, $maxPageNum );
|
155 |
+
|
156 |
+
if( !$links )return;
|
157 |
+
|
158 |
+
return $this->paginationTemplate( $links );
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* Get the correct pagination template
|
163 |
+
*
|
164 |
+
* @param string $links
|
165 |
+
*
|
166 |
+
* @return string
|
167 |
+
*/
|
168 |
+
protected function paginationTemplate( $links )
|
169 |
+
{
|
170 |
+
$class = '';
|
171 |
+
$theme = wp_get_theme()->get( 'TextDomain' );
|
172 |
+
|
173 |
+
switch( $theme ) {
|
174 |
+
|
175 |
+
case 'twentyten':
|
176 |
+
case 'twentyeleven':
|
177 |
+
case 'twentytwelve':
|
178 |
+
$template = '<nav class="navigation" role="navigation">%3$s</nav>';
|
179 |
+
break;
|
180 |
+
case 'twentyfourteen':
|
181 |
+
$class = 'paging-navigation';
|
182 |
+
$template = '' .
|
183 |
+
'<nav class="navigation %1$s" role="navigation">' .
|
184 |
+
'<h2 class="screen-reader-text">%2$s</h2>' .
|
185 |
+
'<div class="pagination loop-pagination">%3$s</div>' .
|
186 |
+
'</nav>';
|
187 |
+
break;
|
188 |
+
default:
|
189 |
+
$class = 'pagination';
|
190 |
+
$template = '' .
|
191 |
+
'<nav class="navigation %1$s" role="navigation">' .
|
192 |
+
'<h2 class="screen-reader-text">%2$s</h2>' .
|
193 |
+
'<div class="nav-links">%3$s</div>' .
|
194 |
+
'</nav>';
|
195 |
+
}
|
196 |
+
|
197 |
+
$template = apply_filters( 'navigation_markup_template', $template, $class );
|
198 |
+
|
199 |
+
$screenReaderText = __( 'Site Reviews navigation', 'site-reviews' );
|
200 |
+
|
201 |
+
return sprintf( $template, $class, $screenReaderText, $links );
|
202 |
+
}
|
203 |
+
|
204 |
+
/**
|
205 |
+
* Build the review author string
|
206 |
+
*
|
207 |
+
* @param string $author
|
208 |
+
* @param string $metaAuthor
|
209 |
+
*
|
210 |
+
* @return string
|
211 |
+
*/
|
212 |
+
protected function reviewAuthor( $author, $metaAuthor )
|
213 |
+
{
|
214 |
+
return wp_validate_boolean( $author )
|
215 |
+
? sprintf( '<p class="glsr-review-author">—%s</p> ', $metaAuthor )
|
216 |
+
: '';
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Build the review excerpt string
|
221 |
+
*
|
222 |
+
* @param object $meta
|
223 |
+
* @param string $postId
|
224 |
+
*
|
225 |
+
* @return string
|
226 |
+
*/
|
227 |
+
protected function reviewExcerpt( array $args, $meta, $postId )
|
228 |
+
{
|
229 |
+
$excerpt = $this->buildExcerpt( get_the_content( $postId ), $args['word_limit'] );
|
230 |
+
$review_link = $this->reviewLink( $args['show_link'], $meta->url );
|
231 |
+
|
232 |
+
$use_excerpt_as_link = apply_filters( 'site-reviews/reviews/use_excerpt_as_link', false )
|
233 |
+
&& in_array( $args['display'], ['both', 'excerpt'] )
|
234 |
+
&& !empty( $review_link );
|
235 |
+
|
236 |
+
$show_excerpt_read_more = !apply_filters( 'site-reviews/reviews/hide_excerpt_read_more', false )
|
237 |
+
? $review_link
|
238 |
+
: '';
|
239 |
+
|
240 |
+
$review_excerpt = $use_excerpt_as_link
|
241 |
+
? sprintf( '<a href="%s" target="_blank">%s</a>', esc_url( $meta->url ), $excerpt )
|
242 |
+
: "{$excerpt} {$show_excerpt_read_more}";
|
243 |
+
|
244 |
+
return !empty( $review_excerpt )
|
245 |
+
? sprintf( '<p class="glsr-review-excerpt">%s</p>', $review_excerpt )
|
246 |
+
: '';
|
247 |
+
}
|
248 |
+
|
249 |
+
/**
|
250 |
+
* Build the review url
|
251 |
+
*
|
252 |
+
* @param string $link
|
253 |
+
* @param string $metaLink
|
254 |
+
*
|
255 |
+
* @return string
|
256 |
+
*/
|
257 |
+
protected function reviewLink( $link, $metaLink )
|
258 |
+
{
|
259 |
+
return wp_validate_boolean( $link )
|
260 |
+
? sprintf( '<span class="glsr-review-link">[<a href="%s" target="_blank">%s</a>]</span>', esc_url( $metaLink ), __( 'read more', 'site-reviews' ) )
|
261 |
+
: '';
|
262 |
+
}
|
263 |
+
|
264 |
+
/**
|
265 |
+
* Build the review meta string
|
266 |
+
*
|
267 |
+
* @param object $meta
|
268 |
+
* @param string $postId
|
269 |
+
*
|
270 |
+
* @return string
|
271 |
+
*/
|
272 |
+
protected function reviewMeta( array $args, $meta, $postId )
|
273 |
+
{
|
274 |
+
$rating = wp_validate_boolean( $args['show_rating'] )
|
275 |
+
? $this->app->make( 'Html' )->renderPartial( 'rating', ['stars' => $meta->rating ], 'return' )
|
276 |
+
: '';
|
277 |
+
|
278 |
+
$date = wp_validate_boolean( $args['show_date'] )
|
279 |
+
? sprintf( '<span class="glsr-review-date">%s</span> ', get_the_date( 'M j, Y', $postId ) )
|
280 |
+
: '';
|
281 |
+
|
282 |
+
return ( $rating || $date )
|
283 |
+
? sprintf( '<p class="glsr-review-meta">%s%s</p>', $rating, $date )
|
284 |
+
: '';
|
285 |
+
}
|
286 |
+
|
287 |
+
/**
|
288 |
+
* Build the review title string
|
289 |
+
*
|
290 |
+
* @param object $meta
|
291 |
+
* @param string $postId
|
292 |
+
*
|
293 |
+
* @return string
|
294 |
+
*/
|
295 |
+
protected function reviewTitle( array $args, $meta, $postId )
|
296 |
+
{
|
297 |
+
$review_link = $this->reviewLink( $args['show_link'], $meta->url );
|
298 |
+
|
299 |
+
$use_title_as_link = apply_filters( 'site-reviews/widget/use_title_as_link', false )
|
300 |
+
&& in_array( $args['display'], ['both', 'excerpt'] )
|
301 |
+
&& !empty( $review_link );
|
302 |
+
|
303 |
+
$review_title = $use_title_as_link
|
304 |
+
? sprintf( '<a href="%s" target="_blank">%s</a>', esc_url( $meta->url ), get_the_title( $postId ) )
|
305 |
+
: get_the_title( $postId );
|
306 |
+
|
307 |
+
return !empty( $review_title )
|
308 |
+
? sprintf( '<h3 class="glsr-review-title">%s</h3>', $review_title )
|
309 |
+
: '';
|
310 |
+
}
|
311 |
+
}
|
plugin/Html/Partials/Subsubsub.php
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Partials;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Partials\Base;
|
14 |
+
|
15 |
+
class Subsubsub extends Base
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* Generate tabs
|
19 |
+
*
|
20 |
+
* @return null|string
|
21 |
+
*/
|
22 |
+
public function render()
|
23 |
+
{
|
24 |
+
$defaults = [
|
25 |
+
'page' => '',
|
26 |
+
'tabs' => [],
|
27 |
+
'tab' => '',
|
28 |
+
'section' => '',
|
29 |
+
];
|
30 |
+
|
31 |
+
$args = shortcode_atts( $defaults, $this->args );
|
32 |
+
|
33 |
+
extract( $args );
|
34 |
+
|
35 |
+
if( !isset( $tabs[ $tab ]['sections'] ) || count( $tabs[ $tab ]['sections'] ) < 2 )return;
|
36 |
+
|
37 |
+
$linkEls = array_reduce( array_keys( $tabs[ $tab ]['sections'] ),
|
38 |
+
function( $result, $key ) use ( $page, $tabs, $tab, $section ) {
|
39 |
+
|
40 |
+
$sections = $tabs[ $tab ]['sections'];
|
41 |
+
|
42 |
+
return $result . sprintf( '<li><a href="?post_type=site-review&page=%s&tab=%s§ion=%s"%s>%s</a>%s</li>',
|
43 |
+
$page,
|
44 |
+
$tab,
|
45 |
+
$key,
|
46 |
+
$section == $key ? ' class="current"' : null,
|
47 |
+
$sections[ $key ],
|
48 |
+
end( $sections ) !== $sections[ $key ] ? ' | ' : ''
|
49 |
+
);
|
50 |
+
});
|
51 |
+
|
52 |
+
return sprintf( '<ul class="subsubsub glsr-subsubsub">%s</ul>', $linkEls );
|
53 |
+
}
|
54 |
+
}
|
plugin/Html/Partials/Tabs.php
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Html\Partials;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\Html\Partials\Base;
|
14 |
+
|
15 |
+
class Tabs extends Base
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* Generate tabs
|
19 |
+
*
|
20 |
+
* @return null|string
|
21 |
+
*/
|
22 |
+
public function render()
|
23 |
+
{
|
24 |
+
$defaults = [
|
25 |
+
'page' => '',
|
26 |
+
'tabs' => [],
|
27 |
+
'tab' => '',
|
28 |
+
];
|
29 |
+
|
30 |
+
$args = shortcode_atts( $defaults, $this->args );
|
31 |
+
|
32 |
+
extract( $args );
|
33 |
+
|
34 |
+
if( count( $tabs ) < 2 )return;
|
35 |
+
|
36 |
+
$links = array_reduce( array_keys( $tabs ),
|
37 |
+
function( $result, $key ) use ( $page, $tabs, $tab ) {
|
38 |
+
return $result . sprintf( '<a href="?post_type=site-review&page=%s&tab=%s" class="nav-tab%s">%s</a>',
|
39 |
+
$page,
|
40 |
+
$key,
|
41 |
+
(0 === strpos( $tab, $key )) ? ' nav-tab-active' : null,
|
42 |
+
$tabs[ $key ]['title']
|
43 |
+
);
|
44 |
+
});
|
45 |
+
|
46 |
+
return sprintf( '<h2 class="nav-tab-wrapper">%s</h2>', $links );
|
47 |
+
}
|
48 |
+
}
|
plugin/Log/LogLevel.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace GeminiLabs\SiteReviews\Log;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Describes log levels
|
7 |
+
*/
|
8 |
+
class LogLevel
|
9 |
+
{
|
10 |
+
const EMERGENCY = 'emergency';
|
11 |
+
const ALERT = 'alert';
|
12 |
+
const CRITICAL = 'critical';
|
13 |
+
const ERROR = 'error';
|
14 |
+
const WARNING = 'warning';
|
15 |
+
const NOTICE = 'notice';
|
16 |
+
const INFO = 'info';
|
17 |
+
const DEBUG = 'debug';
|
18 |
+
}
|
plugin/Log/Logger.php
ADDED
@@ -0,0 +1,305 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace GeminiLabs\SiteReviews\Log;
|
4 |
+
|
5 |
+
use GeminiLabs\SiteReviews\Log\LoggerInterface;
|
6 |
+
use GeminiLabs\SiteReviews\Log\LogLevel;
|
7 |
+
|
8 |
+
class Logger implements LoggerInterface
|
9 |
+
{
|
10 |
+
protected $file;
|
11 |
+
protected $id;
|
12 |
+
protected $log;
|
13 |
+
protected $prefix;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Load/Create the log file
|
17 |
+
*
|
18 |
+
* @param string $filename
|
19 |
+
* @param string $prefix
|
20 |
+
*
|
21 |
+
* @return Logger
|
22 |
+
*/
|
23 |
+
public static function file( $filename, $prefix )
|
24 |
+
{
|
25 |
+
$logger = new static;
|
26 |
+
|
27 |
+
$logger->file = $filename;
|
28 |
+
$logger->log = file_exists( $filename ) ? file_get_contents( $filename ) : 'No log';
|
29 |
+
$logger->prefix = $prefix;
|
30 |
+
|
31 |
+
return $logger;
|
32 |
+
}
|
33 |
+
|
34 |
+
public function __toString()
|
35 |
+
{
|
36 |
+
return $this->log;
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Download the log file
|
41 |
+
*
|
42 |
+
* @return void
|
43 |
+
*/
|
44 |
+
public function download()
|
45 |
+
{
|
46 |
+
if( !current_user_can( 'install_plugins' ) )return;
|
47 |
+
|
48 |
+
nocache_headers();
|
49 |
+
|
50 |
+
header( 'Content-Type: text/plain' );
|
51 |
+
header( 'Content-Disposition: attachment; filename="log.txt"' );
|
52 |
+
|
53 |
+
echo wp_strip_all_tags( $this->log );
|
54 |
+
|
55 |
+
exit;
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* System is unusable.
|
60 |
+
*
|
61 |
+
* @param string $message
|
62 |
+
* @param array $context
|
63 |
+
*
|
64 |
+
* @return void
|
65 |
+
*/
|
66 |
+
public function emergency( $message, array $context = [] )
|
67 |
+
{
|
68 |
+
$this->log( LogLevel::EMERGENCY, $message, $context );
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Action must be taken immediately.
|
73 |
+
*
|
74 |
+
* Example: Entire website down, database unavailable, etc. This should
|
75 |
+
* trigger the SMS alerts and wake you up.
|
76 |
+
*
|
77 |
+
* @param string $message
|
78 |
+
* @param array $context
|
79 |
+
*
|
80 |
+
* @return void
|
81 |
+
*/
|
82 |
+
public function alert( $message, array $context = [] )
|
83 |
+
{
|
84 |
+
$this->log( LogLevel::ALERT, $message, $context );
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Critical conditions.
|
89 |
+
*
|
90 |
+
* Example: Application component unavailable, unexpected exception.
|
91 |
+
*
|
92 |
+
* @param string $message
|
93 |
+
* @param array $context
|
94 |
+
*
|
95 |
+
* @return void
|
96 |
+
*/
|
97 |
+
public function critical( $message, array $context = [] )
|
98 |
+
{
|
99 |
+
$this->log( LogLevel::CRITICAL, $message, $context );
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Runtime errors that do not require immediate action but should typically
|
104 |
+
* be logged and monitored.
|
105 |
+
*
|
106 |
+
* @param string $message
|
107 |
+
* @param array $context
|
108 |
+
*
|
109 |
+
* @return void
|
110 |
+
*/
|
111 |
+
public function error( $message, array $context = [] )
|
112 |
+
{
|
113 |
+
$this->log( LogLevel::ERROR, $message, $context );
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Exceptional occurrences that are not errors.
|
118 |
+
*
|
119 |
+
* Example: Use of deprecated APIs, poor use of an API, undesirable things
|
120 |
+
* that are not necessarily wrong.
|
121 |
+
*
|
122 |
+
* @param string $message
|
123 |
+
* @param array $context
|
124 |
+
*
|
125 |
+
* @return void
|
126 |
+
*/
|
127 |
+
public function warning( $message, array $context = [] )
|
128 |
+
{
|
129 |
+
$this->log( LogLevel::WARNING, $message, $context );
|
130 |
+
}
|
131 |
+
|
132 |
+
/**
|
133 |
+
* Normal but significant events.
|
134 |
+
*
|
135 |
+
* @param string $message
|
136 |
+
* @param array $context
|
137 |
+
*
|
138 |
+
* @return void
|
139 |
+
*/
|
140 |
+
public function notice( $message, array $context = [] )
|
141 |
+
{
|
142 |
+
$this->log( LogLevel::NOTICE, $message, $context );
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Interesting events.
|
147 |
+
*
|
148 |
+
* Example: User logs in, SQL logs.
|
149 |
+
*
|
150 |
+
* @param string $message
|
151 |
+
* @param array $context
|
152 |
+
*
|
153 |
+
* @return void
|
154 |
+
*/
|
155 |
+
public function info( $message, array $context = [] )
|
156 |
+
{
|
157 |
+
$this->log( LogLevel::INFO, $message, $context );
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Detailed debug information.
|
162 |
+
*
|
163 |
+
* @param string $message
|
164 |
+
* @param array $context
|
165 |
+
*
|
166 |
+
* @return void
|
167 |
+
*/
|
168 |
+
public function debug( $message, array $context = [] )
|
169 |
+
{
|
170 |
+
$this->log( LogLevel::DEBUG, $message, $context );
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* Logs with an arbitrary level.
|
175 |
+
*
|
176 |
+
* @param mixed $level
|
177 |
+
* @param string $message
|
178 |
+
* @param array $context
|
179 |
+
*
|
180 |
+
* @return void
|
181 |
+
*/
|
182 |
+
public function log( $level, $message, array $context = [] )
|
183 |
+
{
|
184 |
+
// Check if logging is enabled
|
185 |
+
if( get_option( "{$this->prefix}_logging" ) != 1 )return;
|
186 |
+
|
187 |
+
$reflection = new \ReflectionClass( __NAMESPACE__ . '\LogLevel' );
|
188 |
+
|
189 |
+
$levels = $reflection->getConstants();
|
190 |
+
|
191 |
+
$custom_levels = array_intersect( apply_filters( 'site-reviews/logger/levels', $levels ), $levels );
|
192 |
+
$custom_levels = array_keys( array_flip( $custom_levels ) );
|
193 |
+
|
194 |
+
$levels = empty( $custom_levels ) ? $levels : $custom_levels;
|
195 |
+
|
196 |
+
// Check if log level is allowed.
|
197 |
+
if( !in_array( $level, $levels, true ) )return;
|
198 |
+
|
199 |
+
$date = get_date_from_gmt( gmdate('Y-m-d H:i:s') );
|
200 |
+
$level = strtoupper( $level );
|
201 |
+
$message = $this->interpolate( $message, $context );
|
202 |
+
$entry = "[{$date}] {$level}: $message" . PHP_EOL;
|
203 |
+
|
204 |
+
if( $this->log == 'Log is empty' ) {
|
205 |
+
file_put_contents( $this->file, '' );
|
206 |
+
}
|
207 |
+
|
208 |
+
file_put_contents( $this->file, $entry, FILE_APPEND|LOCK_EX );
|
209 |
+
}
|
210 |
+
|
211 |
+
/**
|
212 |
+
* Wrapper for print_r()
|
213 |
+
*
|
214 |
+
* @return void
|
215 |
+
*/
|
216 |
+
public function display()
|
217 |
+
{
|
218 |
+
call_user_func_array( [ $this, 'print_r' ], func_get_args() );
|
219 |
+
}
|
220 |
+
|
221 |
+
/**
|
222 |
+
* Capture print_r() output as variable
|
223 |
+
*
|
224 |
+
* @return string
|
225 |
+
*/
|
226 |
+
public function capture()
|
227 |
+
{
|
228 |
+
ob_start();
|
229 |
+
call_user_func_array( [ $this, 'display' ], func_get_args() );
|
230 |
+
return ob_get_clean();
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Wrapper for print_r()
|
235 |
+
*
|
236 |
+
* @param mixed $value ...
|
237 |
+
*
|
238 |
+
* @return void
|
239 |
+
*/
|
240 |
+
public function print_r( $value )
|
241 |
+
{
|
242 |
+
if( func_num_args() == 1 ) {
|
243 |
+
printf( '<div class="print__r"><pre>%s</pre></div>',
|
244 |
+
htmlspecialchars( print_r( $value, true ), ENT_QUOTES, 'UTF-8' )
|
245 |
+
);
|
246 |
+
}
|
247 |
+
else {
|
248 |
+
echo '<div class="print__r_group">';
|
249 |
+
foreach( func_get_args() as $param ) {
|
250 |
+
$this->print_r( $param );
|
251 |
+
}
|
252 |
+
echo '</div>';
|
253 |
+
}
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Clear the log file
|
258 |
+
*
|
259 |
+
* @return void
|
260 |
+
*/
|
261 |
+
public function clear()
|
262 |
+
{
|
263 |
+
$log = 'Log is empty';
|
264 |
+
|
265 |
+
file_put_contents( $this->file, $log );
|
266 |
+
|
267 |
+
$this->log = $log;
|
268 |
+
}
|
269 |
+
|
270 |
+
/**
|
271 |
+
* Interpolates context values into the message placeholders.
|
272 |
+
*
|
273 |
+
* @param string $message
|
274 |
+
* @param array $context
|
275 |
+
*
|
276 |
+
* @return array|string
|
277 |
+
*/
|
278 |
+
protected function interpolate( $message, array $context = [] )
|
279 |
+
{
|
280 |
+
if( is_array( $message ) ) {
|
281 |
+
return $message;
|
282 |
+
}
|
283 |
+
|
284 |
+
// build a replacement array with braces around the context keys
|
285 |
+
$replace = [];
|
286 |
+
|
287 |
+
foreach( $context as $key => $val ) {
|
288 |
+
|
289 |
+
if( is_object( $val ) && get_class( $val ) === 'DateTime' ) {
|
290 |
+
$val = $val->format( 'Y-m-d H:i:s' );
|
291 |
+
}
|
292 |
+
else if( is_object( $val ) || is_array( $val ) ) {
|
293 |
+
$val = json_encode( $val );
|
294 |
+
}
|
295 |
+
else if( is_resource( $val ) ) {
|
296 |
+
$val = (string) $val;
|
297 |
+
}
|
298 |
+
|
299 |
+
$replace['{' . $key . '}'] = $val;
|
300 |
+
}
|
301 |
+
|
302 |
+
// interpolate replacement values into the message and return
|
303 |
+
return strtr( $message, $replace );
|
304 |
+
}
|
305 |
+
}
|
plugin/Log/LoggerInterface.php
ADDED
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace GeminiLabs\SiteReviews\Log;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Describes a logger instance
|
7 |
+
*
|
8 |
+
* The message MUST be a string or object implementing __toString().
|
9 |
+
*
|
10 |
+
* The message MAY contain placeholders in the form: {foo} where foo
|
11 |
+
* will be replaced by the context data in key "foo".
|
12 |
+
*
|
13 |
+
* The context array can contain arbitrary data, the only assumption that
|
14 |
+
* can be made by implementors is that if an Exception instance is given
|
15 |
+
* to produce a stack trace, it MUST be in a key named "exception".
|
16 |
+
*
|
17 |
+
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
|
18 |
+
* for the full interface specification.
|
19 |
+
*/
|
20 |
+
interface LoggerInterface
|
21 |
+
{
|
22 |
+
/**
|
23 |
+
* System is unusable.
|
24 |
+
*
|
25 |
+
* @param string $message
|
26 |
+
* @param array $context
|
27 |
+
*
|
28 |
+
* @return void
|
29 |
+
*/
|
30 |
+
public function emergency( $message, array $context = [] );
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Action must be taken immediately.
|
34 |
+
*
|
35 |
+
* Example: Entire website down, database unavailable, etc. This should
|
36 |
+
* trigger the SMS alerts and wake you up.
|
37 |
+
*
|
38 |
+
* @param string $message
|
39 |
+
* @param array $context
|
40 |
+
*
|
41 |
+
* @return void
|
42 |
+
*/
|
43 |
+
public function alert( $message, array $context = [] );
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Critical conditions.
|
47 |
+
*
|
48 |
+
* Example: Application component unavailable, unexpected exception.
|
49 |
+
*
|
50 |
+
* @param string $message
|
51 |
+
* @param array $context
|
52 |
+
*
|
53 |
+
* @return void
|
54 |
+
*/
|
55 |
+
public function critical( $message, array $context = [] );
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Runtime errors that do not require immediate action but should typically
|
59 |
+
* be logged and monitored.
|
60 |
+
*
|
61 |
+
* @param string $message
|
62 |
+
* @param array $context
|
63 |
+
*
|
64 |
+
* @return void
|
65 |
+
*/
|
66 |
+
public function error( $message, array $context = [] );
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Exceptional occurrences that are not errors.
|
70 |
+
*
|
71 |
+
* Example: Use of deprecated APIs, poor use of an API, undesirable things
|
72 |
+
* that are not necessarily wrong.
|
73 |
+
*
|
74 |
+
* @param string $message
|
75 |
+
* @param array $context
|
76 |
+
*
|
77 |
+
* @return void
|
78 |
+
*/
|
79 |
+
public function warning( $message, array $context = [] );
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Normal but significant events.
|
83 |
+
*
|
84 |
+
* @param string $message
|
85 |
+
* @param array $context
|
86 |
+
*
|
87 |
+
* @return void
|
88 |
+
*/
|
89 |
+
public function notice( $message, array $context = [] );
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Interesting events.
|
93 |
+
*
|
94 |
+
* Example: User logs in, SQL logs.
|
95 |
+
*
|
96 |
+
* @param string $message
|
97 |
+
* @param array $context
|
98 |
+
*
|
99 |
+
* @return void
|
100 |
+
*/
|
101 |
+
public function info( $message, array $context = [] );
|
102 |
+
|
103 |
+
/**
|
104 |
+
* Detailed debug information.
|
105 |
+
*
|
106 |
+
* @param string $message
|
107 |
+
* @param array $context
|
108 |
+
*
|
109 |
+
* @return void
|
110 |
+
*/
|
111 |
+
public function debug( $message, array $context = [] );
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Logs with an arbitrary level.
|
115 |
+
*
|
116 |
+
* @param mixed $level
|
117 |
+
* @param string $message
|
118 |
+
* @param array $context
|
119 |
+
*
|
120 |
+
* @return void
|
121 |
+
*/
|
122 |
+
public function log( $level, $message, array $context = [] );
|
123 |
+
}
|
plugin/Notices.php
ADDED
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\App;
|
14 |
+
|
15 |
+
class Notices
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* @var App
|
19 |
+
*/
|
20 |
+
protected $app;
|
21 |
+
|
22 |
+
public function __construct( App $app )
|
23 |
+
{
|
24 |
+
$this->app = $app;
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Add a notice
|
29 |
+
*
|
30 |
+
* @param string $type
|
31 |
+
* @param string|array $message
|
32 |
+
*/
|
33 |
+
public function add( $type, $message, array $args = [] )
|
34 |
+
{
|
35 |
+
if( empty( $type ) || empty( $message ) )return;
|
36 |
+
|
37 |
+
$defaults = [
|
38 |
+
'dismissible' => true,
|
39 |
+
'inline' => true,
|
40 |
+
'messages' => [],
|
41 |
+
'type' => '',
|
42 |
+
];
|
43 |
+
|
44 |
+
$args = (object) shortcode_atts( $defaults, $args );
|
45 |
+
|
46 |
+
$args->messages = is_wp_error( $message )
|
47 |
+
? (array) $message->get_error_message()
|
48 |
+
: (array) $message;
|
49 |
+
|
50 |
+
$args->type = in_array( $type, ['error','warning','success'] )
|
51 |
+
? $type
|
52 |
+
: 'success';
|
53 |
+
|
54 |
+
add_settings_error( $this->app->id, '', $args );
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Add an error notice
|
59 |
+
*
|
60 |
+
* @param string|array $message
|
61 |
+
*/
|
62 |
+
public function addError( $message, array $args = [] )
|
63 |
+
{
|
64 |
+
$this->add( 'error', $message, $args );
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Add a success notice
|
69 |
+
*
|
70 |
+
* @param string|array $message
|
71 |
+
*/
|
72 |
+
public function addSuccess( $message, array $args = [] )
|
73 |
+
{
|
74 |
+
$this->add( 'success', $message, $args );
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Add a warning notice
|
79 |
+
*
|
80 |
+
* @param string|array $message
|
81 |
+
*/
|
82 |
+
public function addWarning( $message, array $args = [] )
|
83 |
+
{
|
84 |
+
$this->add( 'warning', $message, $args );
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Show all notices
|
89 |
+
*
|
90 |
+
* @param bool $sanitize
|
91 |
+
* @param bool $hide_on_update
|
92 |
+
*/
|
93 |
+
public function show( $print = true, $sanitize = false, $hide_on_update = false )
|
94 |
+
{
|
95 |
+
global $wp_settings_errors;
|
96 |
+
|
97 |
+
if( $hide_on_update && !empty( $_GET['settings-updated'] ) )return;
|
98 |
+
|
99 |
+
$settings_errors = get_settings_errors( $this->app->id, $sanitize );
|
100 |
+
|
101 |
+
// make sure each notice is unique
|
102 |
+
$unique_notices = array_map( 'unserialize', array_unique( array_map( 'serialize', $settings_errors ) ) );
|
103 |
+
|
104 |
+
if( empty( $unique_notices ) )return;
|
105 |
+
|
106 |
+
// Empty $wp_settings_errors in case ajax is being used (we don't want to keep old notices)
|
107 |
+
$wp_settings_errors = [];
|
108 |
+
$notices = [];
|
109 |
+
|
110 |
+
foreach( $unique_notices as $key => $notice ) {
|
111 |
+
if( is_string( $notice['message'] ) ) {
|
112 |
+
$notices[] = $this->returnNotice( $notice['type'], true, true, $notice['message'] );
|
113 |
+
}
|
114 |
+
else {
|
115 |
+
$notices[] = $this->returnNotice(
|
116 |
+
$notice['message']->type,
|
117 |
+
$notice['message']->inline,
|
118 |
+
$notice['message']->dismissible,
|
119 |
+
$notice['message']->messages
|
120 |
+
);
|
121 |
+
}
|
122 |
+
}
|
123 |
+
|
124 |
+
$notices = implode( '', $notices );
|
125 |
+
|
126 |
+
if( $print ) {
|
127 |
+
echo $notices;
|
128 |
+
}
|
129 |
+
else {
|
130 |
+
return $notices;
|
131 |
+
}
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* Print a notice to the page
|
136 |
+
*
|
137 |
+
* @param string $type
|
138 |
+
* @param bool $inline
|
139 |
+
* @param bool $dismissible
|
140 |
+
* @param mixed $messages
|
141 |
+
*/
|
142 |
+
public function printNotice( $type, $inline, $dismissible, $messages )
|
143 |
+
{
|
144 |
+
global $wp_version;
|
145 |
+
|
146 |
+
$type = $type == 'updated' ? 'success' : $type;
|
147 |
+
|
148 |
+
// WP 4.0 support
|
149 |
+
if( version_compare( $wp_version, '4.1', '<' ) ) {
|
150 |
+
$type = $type == 'error' ? 'error error' : "$type updated";
|
151 |
+
}
|
152 |
+
|
153 |
+
if( is_string( $messages ) ) {
|
154 |
+
$messages = (array) $messages;
|
155 |
+
}
|
156 |
+
|
157 |
+
array_walk( $messages, function( &$message ) {
|
158 |
+
$value = is_wp_error( $message )
|
159 |
+
? $message->get_error_message()
|
160 |
+
: $message;
|
161 |
+
$message = sprintf( '<p>%s</p>', $value );
|
162 |
+
});
|
163 |
+
|
164 |
+
printf( '<div class="notice notice-%s%s%s">%s</div>',
|
165 |
+
$type,
|
166 |
+
$inline ? ' inline' : '',
|
167 |
+
$dismissible ? ' is-dismissible' : '',
|
168 |
+
implode( '', $messages )
|
169 |
+
);
|
170 |
+
}
|
171 |
+
|
172 |
+
/**
|
173 |
+
* Return a notice
|
174 |
+
*
|
175 |
+
* @param string $type
|
176 |
+
* @param bool $inline
|
177 |
+
* @param bool $dismissible
|
178 |
+
* @param mixed $messages
|
179 |
+
*/
|
180 |
+
public function returnNotice( $type, $inline, $dismissible, $messages )
|
181 |
+
{
|
182 |
+
ob_start();
|
183 |
+
$this->printNotice( $type, $inline, $dismissible, $messages );
|
184 |
+
return ob_get_clean();
|
185 |
+
}
|
186 |
+
}
|
plugin/Providers/MainProvider.php
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Providers;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\App;
|
14 |
+
use GeminiLabs\SiteReviews\Log\Logger;
|
15 |
+
use GeminiLabs\SiteReviews\Providers\ProviderInterface;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Note: We're using the full "namespace\classname" because "::class" isn't supported in PHP 5.4
|
19 |
+
*/
|
20 |
+
class MainProvider implements ProviderInterface
|
21 |
+
{
|
22 |
+
public function register( App $app )
|
23 |
+
{
|
24 |
+
// Bind the Reviews instance itself to the container
|
25 |
+
$app->bind( 'GeminiLabs\SiteReviews\App', $app );
|
26 |
+
|
27 |
+
// Initialise logger from log file
|
28 |
+
$app->bind( 'GeminiLabs\SiteReviews\Log\Logger', function( App $app )
|
29 |
+
{
|
30 |
+
return Logger::file( trailingslashit( $app->path ) . 'debug.log', $app->prefix );
|
31 |
+
});
|
32 |
+
|
33 |
+
// Singletons must be last since they call "make()"
|
34 |
+
$app->singleton(
|
35 |
+
'GeminiLabs\SiteReviews\Controllers\AjaxController',
|
36 |
+
'GeminiLabs\SiteReviews\Controllers\AjaxController'
|
37 |
+
);
|
38 |
+
|
39 |
+
$app->singleton(
|
40 |
+
'GeminiLabs\SiteReviews\Html',
|
41 |
+
'GeminiLabs\SiteReviews\Html'
|
42 |
+
);
|
43 |
+
|
44 |
+
$app->singleton(
|
45 |
+
'GeminiLabs\SiteReviews\Controllers\MainController',
|
46 |
+
'GeminiLabs\SiteReviews\Controllers\MainController'
|
47 |
+
);
|
48 |
+
|
49 |
+
$app->singleton(
|
50 |
+
'GeminiLabs\SiteReviews\Controllers\ReviewController',
|
51 |
+
'GeminiLabs\SiteReviews\Controllers\ReviewController'
|
52 |
+
);
|
53 |
+
|
54 |
+
$app->singleton(
|
55 |
+
'GeminiLabs\SiteReviews\Session',
|
56 |
+
'GeminiLabs\SiteReviews\Session'
|
57 |
+
);
|
58 |
+
|
59 |
+
$app->singleton(
|
60 |
+
'GeminiLabs\SiteReviews\Settings',
|
61 |
+
'GeminiLabs\SiteReviews\Settings'
|
62 |
+
);
|
63 |
+
}
|
64 |
+
}
|
plugin/Providers/ProviderInterface.php
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews\Providers;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\App;
|
14 |
+
|
15 |
+
interface ProviderInterface
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* @return void
|
19 |
+
*/
|
20 |
+
public function register( App $app );
|
21 |
+
}
|
plugin/Router.php
ADDED
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\App;
|
14 |
+
|
15 |
+
class Router
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* @var App
|
19 |
+
*/
|
20 |
+
protected $app;
|
21 |
+
|
22 |
+
protected $id;
|
23 |
+
protected $prefix;
|
24 |
+
|
25 |
+
public function __construct( App $app )
|
26 |
+
{
|
27 |
+
$this->app = $app;
|
28 |
+
$this->id = $app->id;
|
29 |
+
$this->prefix = $app->prefix;
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @param string $prefix
|
34 |
+
* @param string $action
|
35 |
+
*
|
36 |
+
* @return string
|
37 |
+
*/
|
38 |
+
public function getMethodName( $prefix, $action )
|
39 |
+
{
|
40 |
+
$callback = function( $matches ) { return strtoupper( $matches[1] ); };
|
41 |
+
return $prefix . preg_replace_callback( '/[-_](.)/', $callback, strtolower( $action ) );
|
42 |
+
}
|
43 |
+
|
44 |
+
public function routeAjaxRequests()
|
45 |
+
{
|
46 |
+
$request = $_REQUEST['request'];
|
47 |
+
|
48 |
+
if( isset( $request[ $this->prefix ]['action'] ) ) {
|
49 |
+
$request = $request[ $this->prefix ];
|
50 |
+
}
|
51 |
+
|
52 |
+
// All ajax requests are triggered by a single action hook,
|
53 |
+
// each route is determined by the request["action"].
|
54 |
+
if( !isset( $request['action'] ) ) {
|
55 |
+
wp_die();
|
56 |
+
}
|
57 |
+
|
58 |
+
// Nonce url is localized in "GeminiLabs\SiteReviews\Handlers\EnqueueAssets"
|
59 |
+
check_ajax_referer( sprintf( '%s-ajax-nonce', $this->id ) );
|
60 |
+
|
61 |
+
$request['ajax_request'] = true;
|
62 |
+
|
63 |
+
// undo damage done by javascript: encodeURIComponent()
|
64 |
+
array_walk_recursive( $request, function( &$value ) {
|
65 |
+
$value = stripslashes( $value );
|
66 |
+
});
|
67 |
+
|
68 |
+
$ajaxController = $this->app->make( 'Controllers\AjaxController' );
|
69 |
+
$method = $this->getMethodName( 'ajax', $request['action'] );
|
70 |
+
|
71 |
+
if( is_callable([ $ajaxController, $method ]) ) {
|
72 |
+
$ajaxController->$method( $request );
|
73 |
+
}
|
74 |
+
else {
|
75 |
+
do_action( 'site-reviews/router/ajax/request', $method, $request );
|
76 |
+
}
|
77 |
+
|
78 |
+
wp_die();
|
79 |
+
}
|
80 |
+
|
81 |
+
public function routePostRequests()
|
82 |
+
{
|
83 |
+
// get the request data that is prefixed with the app prefix
|
84 |
+
$request = filter_input( INPUT_POST, $this->prefix, FILTER_DEFAULT , FILTER_REQUIRE_ARRAY );
|
85 |
+
|
86 |
+
if( !isset( $request['action'] ) )return;
|
87 |
+
|
88 |
+
$nonce = ( filter_input( INPUT_POST, 'option_page' ) == $request['action'] && filter_input( INPUT_POST, 'action' ) == 'update' )
|
89 |
+
? $request['action'] . '-options'
|
90 |
+
: $request['action'];
|
91 |
+
|
92 |
+
check_admin_referer( $nonce );
|
93 |
+
|
94 |
+
switch( $request['action'] ) {
|
95 |
+
case 'clear-log':
|
96 |
+
$this->app->make( 'Controllers\MainController' )->postClearLog();
|
97 |
+
break;
|
98 |
+
|
99 |
+
case 'download-log':
|
100 |
+
$this->app->make( 'Controllers\MainController' )->postDownloadLog();
|
101 |
+
break;
|
102 |
+
|
103 |
+
case 'download-system-info':
|
104 |
+
$this->app->make( 'Controllers\MainController' )->postDownloadSystemInfo( $request['system-info'] );
|
105 |
+
break;
|
106 |
+
|
107 |
+
case 'post-review':
|
108 |
+
$this->app->make( 'Controllers\ReviewController' )->postSubmitReview( $request );
|
109 |
+
break;
|
110 |
+
|
111 |
+
default:
|
112 |
+
do_action( 'site-reviews/router/post/request', $request['action'], $request );
|
113 |
+
break;
|
114 |
+
}
|
115 |
+
}
|
116 |
+
|
117 |
+
public function routeWebhookRequests()
|
118 |
+
{
|
119 |
+
$request = filter_input( INPUT_GET, sprintf( '%s-hook', $this->id ) );
|
120 |
+
|
121 |
+
if( !$request )return;
|
122 |
+
}
|
123 |
+
}
|
plugin/Session.php
ADDED
@@ -0,0 +1,336 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
*
|
9 |
+
* Much of the code in this class is derived from WP Session Manager (1.2.0)
|
10 |
+
* Copyright (c) <Eric Mann>
|
11 |
+
* -------------------------------------------------------------------------------------------------
|
12 |
+
*/
|
13 |
+
|
14 |
+
namespace GeminiLabs\SiteReviews;
|
15 |
+
|
16 |
+
use GeminiLabs\SiteReviews\App;
|
17 |
+
use PasswordHash;
|
18 |
+
|
19 |
+
class Session
|
20 |
+
{
|
21 |
+
/**
|
22 |
+
* Unix timestamp when session expires.
|
23 |
+
*
|
24 |
+
* @var int
|
25 |
+
*/
|
26 |
+
protected $expiry;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Unix timestamp indicating when the expiration time needs to be reset.
|
30 |
+
*
|
31 |
+
* @var int
|
32 |
+
*/
|
33 |
+
protected $expiryReset;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @var string
|
37 |
+
*/
|
38 |
+
protected $prefix;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @var App
|
42 |
+
*/
|
43 |
+
protected $app;
|
44 |
+
|
45 |
+
/**
|
46 |
+
* The current session data.
|
47 |
+
*
|
48 |
+
* @var array
|
49 |
+
*/
|
50 |
+
protected $session;
|
51 |
+
|
52 |
+
/**
|
53 |
+
* ID of the current session.
|
54 |
+
*
|
55 |
+
* @var string
|
56 |
+
*/
|
57 |
+
protected $sessionId;
|
58 |
+
|
59 |
+
public function __construct( App $app )
|
60 |
+
{
|
61 |
+
$this->app = $app;
|
62 |
+
|
63 |
+
$this->prefix = "_{$this->app->prefix}_session";
|
64 |
+
|
65 |
+
if( isset( $_COOKIE[ $this->app->id ] ) ) {
|
66 |
+
|
67 |
+
$cookie = explode( '||', stripslashes( $_COOKIE[ $this->app->id ] ) );
|
68 |
+
|
69 |
+
$this->sessionId = $cookie[0];
|
70 |
+
$this->expiry = $cookie[1];
|
71 |
+
$this->expiryReset = $cookie[2];
|
72 |
+
|
73 |
+
// Update the session expiration if we're past the reset time
|
74 |
+
if( time() > $this->expiryReset ) {
|
75 |
+
$this->resetCookieExpiration();
|
76 |
+
$this->resetSessionExpiration();
|
77 |
+
}
|
78 |
+
}
|
79 |
+
else {
|
80 |
+
$this->sessionId = $this->generateSessionId();
|
81 |
+
$this->resetCookieExpiration();
|
82 |
+
}
|
83 |
+
|
84 |
+
$this->getSession();
|
85 |
+
$this->setCookie();
|
86 |
+
|
87 |
+
add_action( 'site-reviews/schedule/session/purge', [ $this, 'purgeSessions'] );
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Alias for delete()
|
92 |
+
*
|
93 |
+
* @return void
|
94 |
+
*/
|
95 |
+
public function clear()
|
96 |
+
{
|
97 |
+
$this->delete();
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Delete the current session
|
102 |
+
*
|
103 |
+
* @return void
|
104 |
+
*/
|
105 |
+
public function delete()
|
106 |
+
{
|
107 |
+
$this->resetCookieExpiration();
|
108 |
+
$this->regenerateSessionId( 'and delete session!' );
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Get a variable from the current session
|
113 |
+
*
|
114 |
+
* @param string $key
|
115 |
+
* @param string|array $fallback
|
116 |
+
* @param mixed $unset
|
117 |
+
*
|
118 |
+
* @return string
|
119 |
+
*/
|
120 |
+
public function get( $key, $fallback = '', $unset = false )
|
121 |
+
{
|
122 |
+
$key = sanitize_key( $key );
|
123 |
+
|
124 |
+
$value = isset( $this->session[ $key ] )
|
125 |
+
? maybe_unserialize( $this->session[ $key ] )
|
126 |
+
: $fallback;
|
127 |
+
|
128 |
+
if( !!$unset ) {
|
129 |
+
unset( $this->session[ $key ] );
|
130 |
+
$this->updateSession();
|
131 |
+
}
|
132 |
+
|
133 |
+
return $value;
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Reset the current session to an empty array, does not touch the database
|
138 |
+
*
|
139 |
+
* @return self
|
140 |
+
*/
|
141 |
+
public function reset()
|
142 |
+
{
|
143 |
+
$this->session = [];
|
144 |
+
|
145 |
+
return $this;
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* Set a variable to the current session
|
150 |
+
*
|
151 |
+
* @param string $key
|
152 |
+
* @param mixed $value
|
153 |
+
*
|
154 |
+
* @return string
|
155 |
+
*/
|
156 |
+
public function set( $key, $value )
|
157 |
+
{
|
158 |
+
$key = sanitize_key( $key );
|
159 |
+
|
160 |
+
$this->session[ $key ] = maybe_serialize( $value );
|
161 |
+
|
162 |
+
$this->updateSession();
|
163 |
+
|
164 |
+
return $this->session[ $key ];
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* @return string
|
169 |
+
*/
|
170 |
+
protected function generateSessionId()
|
171 |
+
{
|
172 |
+
return md5((new PasswordHash( 8, false ))->get_random_bytes( 32 ));
|
173 |
+
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* @param mixed $purge
|
177 |
+
*
|
178 |
+
* @return void
|
179 |
+
*/
|
180 |
+
protected function regenerateSessionId( $purge = false )
|
181 |
+
{
|
182 |
+
$this->sessionId = $this->generateSessionId();
|
183 |
+
$this->setCookie();
|
184 |
+
|
185 |
+
if( !!$purge ) {
|
186 |
+
$this->deleteSession();
|
187 |
+
}
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* @return void
|
192 |
+
*/
|
193 |
+
protected function resetCookieExpiration()
|
194 |
+
{
|
195 |
+
$this->expiry = time() + (30 * 60); // 30 minutes
|
196 |
+
$this->expiryReset = time() + (24 * 60); // 24 minutes
|
197 |
+
}
|
198 |
+
|
199 |
+
/**
|
200 |
+
* @return void
|
201 |
+
*/
|
202 |
+
protected function setCookie()
|
203 |
+
{
|
204 |
+
$cookie = "{$this->sessionId}||{$this->expiry}||{$this->expiryReset}";
|
205 |
+
|
206 |
+
$secure = apply_filters( 'site-reviews/session/cookie/secure', false );
|
207 |
+
$httponly = apply_filters( 'site-reviews/session/cookie/httponly', false );
|
208 |
+
|
209 |
+
if( !defined( 'COOKIEPATH' ) ) {
|
210 |
+
define( 'COOKIEPATH', preg_replace( '|https?://[^/]+|i', '', get_option( 'home' ) . '/' ) );
|
211 |
+
}
|
212 |
+
|
213 |
+
if( !defined( 'COOKIE_DOMAIN' ) ) {
|
214 |
+
define( 'COOKIE_DOMAIN', false );
|
215 |
+
}
|
216 |
+
|
217 |
+
if( !headers_sent() ) {
|
218 |
+
setcookie( $this->app->id, $cookie, $this->expiry, COOKIEPATH, COOKIE_DOMAIN, $secure, $httponly );
|
219 |
+
}
|
220 |
+
}
|
221 |
+
|
222 |
+
// DATABASE
|
223 |
+
// ---------------------------------------------------------------------------------------------
|
224 |
+
|
225 |
+
/**
|
226 |
+
* Delete ALL sessions from the database.
|
227 |
+
*
|
228 |
+
* @return int
|
229 |
+
*/
|
230 |
+
public function deleteSessions()
|
231 |
+
{
|
232 |
+
global $wpdb;
|
233 |
+
|
234 |
+
$count = $wpdb->query(
|
235 |
+
"DELETE FROM $wpdb->options " .
|
236 |
+
"WHERE option_name LIKE '{$this->prefix}_%'"
|
237 |
+
);
|
238 |
+
|
239 |
+
return (int) ( $count / 2 );
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* Delete old sessions from the database.
|
244 |
+
*
|
245 |
+
* @param int $limit
|
246 |
+
*
|
247 |
+
* @return int
|
248 |
+
*/
|
249 |
+
public function purgeSessions( $limit = 1000 )
|
250 |
+
{
|
251 |
+
global $wpdb;
|
252 |
+
|
253 |
+
$sessions = $wpdb->get_results( $wpdb->prepare(
|
254 |
+
"SELECT option_name AS name, option_value AS expiry " .
|
255 |
+
"FROM $wpdb->options " .
|
256 |
+
"WHERE option_name LIKE '{$this->prefix}_expires_%' " .
|
257 |
+
"ORDER BY option_value ASC " .
|
258 |
+
"LIMIT 0, %d", absint( $limit )
|
259 |
+
));
|
260 |
+
|
261 |
+
if( empty( $sessions ) ) {
|
262 |
+
return 0;
|
263 |
+
}
|
264 |
+
|
265 |
+
$now = time();
|
266 |
+
$expired = [];
|
267 |
+
$count = 0;
|
268 |
+
|
269 |
+
foreach( $sessions as $session ) {
|
270 |
+
if( $now <= $session->expiry )continue;
|
271 |
+
|
272 |
+
$session_id = addslashes( substr( $session->name, 20 ) );
|
273 |
+
|
274 |
+
$expired[] = $session->name;
|
275 |
+
$expired[] = "{$this->prefix}_{$session_id}";
|
276 |
+
|
277 |
+
$count++;
|
278 |
+
}
|
279 |
+
|
280 |
+
// Delete expired sessions
|
281 |
+
if( !empty( $expired ) ) {
|
282 |
+
$session_names = implode( "','", $expired );
|
283 |
+
|
284 |
+
$wpdb->query(
|
285 |
+
"DELETE FROM $wpdb->options " .
|
286 |
+
"WHERE option_name IN ('{$session_names}')"
|
287 |
+
);
|
288 |
+
}
|
289 |
+
|
290 |
+
return $count;
|
291 |
+
}
|
292 |
+
|
293 |
+
/**
|
294 |
+
* @return bool
|
295 |
+
*/
|
296 |
+
protected function createSession()
|
297 |
+
{
|
298 |
+
return add_option( "{$this->prefix}_{$this->sessionId}", $this->session, '', false );
|
299 |
+
}
|
300 |
+
|
301 |
+
/**
|
302 |
+
* @return bool
|
303 |
+
*/
|
304 |
+
protected function deleteSession()
|
305 |
+
{
|
306 |
+
return delete_option( "{$this->prefix}_{$this->sessionId}" );
|
307 |
+
}
|
308 |
+
|
309 |
+
/**
|
310 |
+
* @return array
|
311 |
+
*/
|
312 |
+
protected function getSession()
|
313 |
+
{
|
314 |
+
return $this->session = get_option( "{$this->prefix}_{$this->sessionId}", [] );
|
315 |
+
}
|
316 |
+
|
317 |
+
/**
|
318 |
+
* @return bool
|
319 |
+
*/
|
320 |
+
protected function resetSessionExpiration()
|
321 |
+
{
|
322 |
+
return update_option( "{$this->prefix}_expires_{$this->sessionId}", $this->expiry, false );
|
323 |
+
}
|
324 |
+
|
325 |
+
/**
|
326 |
+
* @return bool
|
327 |
+
*/
|
328 |
+
protected function updateSession()
|
329 |
+
{
|
330 |
+
if( false === get_option( "{$this->prefix}_{$this->sessionId}" ) ) {
|
331 |
+
$this->resetSessionExpiration();
|
332 |
+
}
|
333 |
+
|
334 |
+
return update_option( "{$this->prefix}_{$this->sessionId}", $this->session, false );
|
335 |
+
}
|
336 |
+
}
|
plugin/Settings.php
ADDED
@@ -0,0 +1,295 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\App;
|
14 |
+
use ReflectionClass;
|
15 |
+
use ReflectionMethod;
|
16 |
+
|
17 |
+
class Settings
|
18 |
+
{
|
19 |
+
/**
|
20 |
+
* @var App
|
21 |
+
*/
|
22 |
+
protected $app;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var Html
|
26 |
+
*/
|
27 |
+
protected $html;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @var array
|
31 |
+
*/
|
32 |
+
protected $settings;
|
33 |
+
|
34 |
+
public function __construct( App $app )
|
35 |
+
{
|
36 |
+
$this->app = $app;
|
37 |
+
$this->html = $app->make( 'Html' );
|
38 |
+
$this->settings = [];
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Add a setting default
|
43 |
+
*
|
44 |
+
* @param string $formId
|
45 |
+
*
|
46 |
+
* @return void
|
47 |
+
*/
|
48 |
+
public function addSetting( $formId, array $args )
|
49 |
+
{
|
50 |
+
if( isset( $args['name'] ) ) {
|
51 |
+
$this->settings[ $args['name'] ] = $this->getDefault( $args );
|
52 |
+
}
|
53 |
+
|
54 |
+
$this->html->addfield( $formId, $args );
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Get the default field value
|
59 |
+
*
|
60 |
+
* @return string
|
61 |
+
*/
|
62 |
+
public function getDefault( array $args )
|
63 |
+
{
|
64 |
+
isset( $args['default'] ) ?: $args['default'] = '';
|
65 |
+
isset( $args['placeholder'] ) ?: $args['placeholder'] = '';
|
66 |
+
|
67 |
+
if( $args['default'] === ':placeholder' ) {
|
68 |
+
$args['default'] = $args['placeholder'];
|
69 |
+
}
|
70 |
+
|
71 |
+
return $args['default'];
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Get the default settings
|
76 |
+
*
|
77 |
+
* @return array
|
78 |
+
*/
|
79 |
+
public function getSettings()
|
80 |
+
{
|
81 |
+
$this->register();
|
82 |
+
|
83 |
+
return $this->settings;
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Register the settings for each form
|
88 |
+
*
|
89 |
+
* @return void
|
90 |
+
*
|
91 |
+
* @action admin_init
|
92 |
+
*/
|
93 |
+
public function register()
|
94 |
+
{
|
95 |
+
if( !empty( $this->settings ) )return;
|
96 |
+
|
97 |
+
$methods = (new ReflectionClass( __CLASS__ ))->getMethods( ReflectionMethod::IS_PROTECTED );
|
98 |
+
|
99 |
+
foreach( $methods as $method ) {
|
100 |
+
if( substr( $method->name, 0, 3 ) === 'set' ) {
|
101 |
+
$this->{$method->name}();
|
102 |
+
}
|
103 |
+
}
|
104 |
+
}
|
105 |
+
|
106 |
+
protected function setGeneral()
|
107 |
+
{
|
108 |
+
$formId = 'settings/general';
|
109 |
+
|
110 |
+
$this->html->createForm( $formId, [
|
111 |
+
'action' => admin_url( 'options.php' ),
|
112 |
+
'nonce' => $this->app->id . '-settings',
|
113 |
+
'submit' => __( 'Save Settings', 'site-reviews' ),
|
114 |
+
]);
|
115 |
+
|
116 |
+
$this->addSetting( $formId, [
|
117 |
+
'type' => 'yesno_inline',
|
118 |
+
'name' => 'general.require.approval',
|
119 |
+
'label' => __( 'Require approval', 'site-reviews' ),
|
120 |
+
'default' => true,
|
121 |
+
'desc' => __( 'Set the status of new review submissions to pending.', 'site-reviews' ),
|
122 |
+
]);
|
123 |
+
|
124 |
+
$this->addSetting( $formId, [
|
125 |
+
'type' => 'yesno_inline',
|
126 |
+
'name' => 'general.require.login',
|
127 |
+
'label' => __( 'Require login', 'site-reviews' ),
|
128 |
+
'desc' => __( 'Only allow review submissions from registered users.', 'site-reviews' ),
|
129 |
+
]);
|
130 |
+
|
131 |
+
$this->addSetting( $formId, [
|
132 |
+
'type' => 'radio',
|
133 |
+
'name' => 'general.notification',
|
134 |
+
'label' => __( 'Notifications', 'site-reviews' ),
|
135 |
+
'default' => 'none',
|
136 |
+
'options' => [
|
137 |
+
'none' => __( 'Do not send review notifications', 'site-reviews' ),
|
138 |
+
'default' => sprintf( __( 'Send to administrator <code>%s</code>', 'site-reviews' ), get_option( 'admin_email' ) ),
|
139 |
+
'custom' => __( 'Send to one or more email addresses', 'site-reviews' ),
|
140 |
+
'webhook' => __( 'Send to <a href="https://slack.com/">Slack</a>', 'site-reviews' ),
|
141 |
+
],
|
142 |
+
]);
|
143 |
+
|
144 |
+
$this->addSetting( $formId, [
|
145 |
+
'type' => 'text',
|
146 |
+
'name' => 'general.notification_email',
|
147 |
+
'label' => __( 'Send notification emails to', 'site-reviews' ),
|
148 |
+
'depends' => [
|
149 |
+
'general.notification' => 'custom',
|
150 |
+
],
|
151 |
+
'placeholder' => __( 'Separate multiple emails with a comma', 'site-reviews' ),
|
152 |
+
]);
|
153 |
+
|
154 |
+
$this->addSetting( $formId, [
|
155 |
+
'type' => 'url',
|
156 |
+
'name' => 'general.webhook_url',
|
157 |
+
'label' => __( 'Webhook URL', 'site-reviews' ),
|
158 |
+
'depends' => [
|
159 |
+
'general.notification' => 'webhook',
|
160 |
+
],
|
161 |
+
'desc' => sprintf(
|
162 |
+
__( 'To send notifications to Slack, <a href="%s">create a new Incoming WebHook</a> and then paste the provided Webhook URL in the field above.', 'site-reviews' ),
|
163 |
+
esc_url( 'https://slack.com/apps/new/A0F7XDUAZ-incoming-webhooks' )
|
164 |
+
),
|
165 |
+
]);
|
166 |
+
|
167 |
+
$this->addSetting( $formId, [
|
168 |
+
'type' => 'code',
|
169 |
+
'name' => 'general.notification_message',
|
170 |
+
'label' => __( 'Notification template', 'site-reviews' ),
|
171 |
+
'rows' => 9,
|
172 |
+
'depends' => [
|
173 |
+
'general.notification' => ['custom', 'default', 'webhook'],
|
174 |
+
],
|
175 |
+
'default' => $this->html->renderTemplate( 'email/templates/review-notification', [], 'return' ),
|
176 |
+
'desc' => 'To restore the default text, save an empty template.
|
177 |
+
If you are sending notifications to Slack then this template will only be used as a fallback in the event that <a href="https://api.slack.com/docs/attachments">Message Attachments</a> have been disabled.<br>
|
178 |
+
Available template tags:<br>
|
179 |
+
<code>{review_rating}</code> - The review rating number (1-5)<br>
|
180 |
+
<code>{review_title}</code> - The review title<br>
|
181 |
+
<code>{review_content}</code> - The review content<br>
|
182 |
+
<code>{review_author}</code> - The review author<br>
|
183 |
+
<code>{review_email}</code> - The email of the review author<br>
|
184 |
+
<code>{review_ip}</code> - The IP address of the review author<br>
|
185 |
+
<code>{review_link}</code> - The link to edit/view a review',
|
186 |
+
]);
|
187 |
+
}
|
188 |
+
|
189 |
+
protected function setForm()
|
190 |
+
{
|
191 |
+
$formId = 'settings/form';
|
192 |
+
|
193 |
+
$this->html->createForm( $formId, [
|
194 |
+
'action' => admin_url( 'options.php' ),
|
195 |
+
'nonce' => $this->app->id . '-settings',
|
196 |
+
'submit' => __( 'Save Settings', 'site-reviews' ),
|
197 |
+
]);
|
198 |
+
|
199 |
+
$this->html->addfield( $formId, [
|
200 |
+
'type' => 'heading',
|
201 |
+
'value' => __( 'Form Labels', 'site-reviews' ),
|
202 |
+
'desc' => __( 'Customize the label text for the review submission form fields.', 'site-reviews' ),
|
203 |
+
]);
|
204 |
+
|
205 |
+
$this->addSetting( $formId, [
|
206 |
+
'type' => 'text',
|
207 |
+
'name' => 'form.rating.label',
|
208 |
+
'label' => __( 'Rating label', 'site-reviews' ),
|
209 |
+
'placeholder' => __( 'Your overall rating', 'site-reviews' ),
|
210 |
+
'default' => ':placeholder',
|
211 |
+
]);
|
212 |
+
|
213 |
+
$this->addSetting( $formId, [
|
214 |
+
'type' => 'text',
|
215 |
+
'name' => 'form.title.label',
|
216 |
+
'label' => __( 'Title label', 'site-reviews' ),
|
217 |
+
'placeholder' => __( 'Title of your review', 'site-reviews' ),
|
218 |
+
'default' => ':placeholder',
|
219 |
+
]);
|
220 |
+
|
221 |
+
$this->addSetting( $formId, [
|
222 |
+
'type' => 'text',
|
223 |
+
'name' => 'form.content.label',
|
224 |
+
'label' => __( 'Content label', 'site-reviews' ),
|
225 |
+
'placeholder' => __( 'Your review', 'site-reviews' ),
|
226 |
+
'default' => ':placeholder',
|
227 |
+
]);
|
228 |
+
|
229 |
+
$this->addSetting( $formId, [
|
230 |
+
'type' => 'text',
|
231 |
+
'name' => 'form.reviewer.label',
|
232 |
+
'label' => __( 'Reviewer label', 'site-reviews' ),
|
233 |
+
'placeholder' => __( 'Your name', 'site-reviews' ),
|
234 |
+
'default' => ':placeholder',
|
235 |
+
]);
|
236 |
+
|
237 |
+
$this->addSetting( $formId, [
|
238 |
+
'type' => 'text',
|
239 |
+
'name' => 'form.email.label',
|
240 |
+
'label' => __( 'Email label', 'site-reviews' ),
|
241 |
+
'placeholder' => __( 'Your email', 'site-reviews' ),
|
242 |
+
'default' => ':placeholder',
|
243 |
+
]);
|
244 |
+
|
245 |
+
$this->addSetting( $formId, [
|
246 |
+
'type' => 'textarea',
|
247 |
+
'name' => 'form.terms.label',
|
248 |
+
'label' => __( 'Terms label', 'site-reviews' ),
|
249 |
+
'placeholder' => __( 'This review is based on my own experience and is my genuine opinion.', 'site-reviews' ),
|
250 |
+
'default' => ':placeholder',
|
251 |
+
]);
|
252 |
+
|
253 |
+
$this->html->addfield( $formId, [
|
254 |
+
'type' => 'heading',
|
255 |
+
'value' => __( 'Form Placeholders', 'site-reviews' ),
|
256 |
+
'desc' => __( 'Customize the placeholder text for the review submission form fields. Use a single space character to disable the placeholder text.', 'site-reviews' ),
|
257 |
+
]);
|
258 |
+
|
259 |
+
$this->addSetting( $formId, [
|
260 |
+
'type' => 'text',
|
261 |
+
'name' => 'form.title.placeholder',
|
262 |
+
'class' => 'large-text',
|
263 |
+
'label' => __( 'Title placeholder', 'site-reviews' ),
|
264 |
+
'placeholder' => __( 'Summarize your review or highlight an interesting detail', 'site-reviews' ),
|
265 |
+
'default' => ':placeholder',
|
266 |
+
]);
|
267 |
+
|
268 |
+
$this->addSetting( $formId, [
|
269 |
+
'type' => 'text',
|
270 |
+
'name' => 'form.content.placeholder',
|
271 |
+
'class' => 'large-text',
|
272 |
+
'label' => __( 'Content placeholder', 'site-reviews' ),
|
273 |
+
'placeholder' => __( 'Tell people your review', 'site-reviews' ),
|
274 |
+
'default' => ':placeholder',
|
275 |
+
]);
|
276 |
+
|
277 |
+
$this->addSetting( $formId, [
|
278 |
+
'type' => 'text',
|
279 |
+
'name' => 'form.reviewer.placeholder',
|
280 |
+
'class' => 'large-text',
|
281 |
+
'label' => __( 'Reviewer placeholder', 'site-reviews' ),
|
282 |
+
'placeholder' => __( 'Tell us your name', 'site-reviews' ),
|
283 |
+
'default' => ':placeholder',
|
284 |
+
]);
|
285 |
+
|
286 |
+
$this->addSetting( $formId, [
|
287 |
+
'type' => 'text',
|
288 |
+
'name' => 'form.email.placeholder',
|
289 |
+
'class' => 'large-text',
|
290 |
+
'label' => __( 'Email placeholder', 'site-reviews' ),
|
291 |
+
'placeholder' => __( 'Tell us your email', 'site-reviews' ),
|
292 |
+
'default' => ':placeholder',
|
293 |
+
]);
|
294 |
+
}
|
295 |
+
}
|
plugin/Shortcode.php
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* = Shortcode Boilerplate
|
5 |
+
*
|
6 |
+
* @package GeminiLabs\SiteReviews
|
7 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
8 |
+
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
9 |
+
* @since 1.0.0
|
10 |
+
* -------------------------------------------------------------------------------------------------
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace Geminilabs\SiteReviews;
|
14 |
+
|
15 |
+
use GeminiLabs\SiteReviews\App;
|
16 |
+
|
17 |
+
abstract class Shortcode
|
18 |
+
{
|
19 |
+
/**
|
20 |
+
* @var App
|
21 |
+
*/
|
22 |
+
protected $app;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var Database
|
26 |
+
*/
|
27 |
+
protected $db;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @var Html
|
31 |
+
*/
|
32 |
+
protected $html;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* @var Session
|
36 |
+
*/
|
37 |
+
protected $session;
|
38 |
+
|
39 |
+
public function __construct( App $app )
|
40 |
+
{
|
41 |
+
$this->app = $app;
|
42 |
+
$this->db = $app->make( 'Database' );
|
43 |
+
$this->html = $app->make( 'Html' );
|
44 |
+
$this->session = $app->make( 'Session' );
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Add a help page for the shortcodes
|
49 |
+
*
|
50 |
+
* @return void
|
51 |
+
*/
|
52 |
+
public function addHelpPage()
|
53 |
+
{
|
54 |
+
add_action( 'load-post.php', [ $this, 'helpTabsHook'] );
|
55 |
+
add_action( 'load-post-new.php', [ $this, 'helpTabsHook'] );
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Add the shortcode help tabs and content
|
60 |
+
*
|
61 |
+
* @return void
|
62 |
+
*/
|
63 |
+
public function addHelpTabs()
|
64 |
+
{
|
65 |
+
$screen = get_current_screen();
|
66 |
+
|
67 |
+
$screen->add_help_tab([
|
68 |
+
'id' => $this->app->id . '-shortcodes',
|
69 |
+
'title' => $this->app->name . ' Shortcodes',
|
70 |
+
'content' => $this->helpContent(),
|
71 |
+
]);
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* The shortcode help content
|
76 |
+
*
|
77 |
+
* @return string
|
78 |
+
*/
|
79 |
+
public function helpContent()
|
80 |
+
{
|
81 |
+
return '';
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Hook to insert help tabs at the end of existing tabs
|
86 |
+
*
|
87 |
+
* @return void
|
88 |
+
*/
|
89 |
+
public function helpTabsHook()
|
90 |
+
{
|
91 |
+
add_action( 'in_admin_header', [ $this, 'addHelpTabs'] );
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Generate a unique ID string from the Shortcode attributes array
|
96 |
+
*
|
97 |
+
* @return string
|
98 |
+
*/
|
99 |
+
protected function generate_id( array $atts )
|
100 |
+
{
|
101 |
+
return substr( md5( serialize( $atts ) ), 0, 8 );
|
102 |
+
}
|
103 |
+
}
|
plugin/Shortcodes/SiteReviews.php
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* = Reviews Form shortcode
|
5 |
+
*
|
6 |
+
* @package GeminiLabs\SiteReviews
|
7 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
8 |
+
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
9 |
+
* @since 1.0.0
|
10 |
+
* -------------------------------------------------------------------------------------------------
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace GeminiLabs\SiteReviews\Shortcodes;
|
14 |
+
|
15 |
+
use GeminiLabs\SiteReviews\Shortcode;
|
16 |
+
|
17 |
+
class SiteReviews extends Shortcode
|
18 |
+
{
|
19 |
+
/**
|
20 |
+
* @return string
|
21 |
+
*/
|
22 |
+
public function printShortcode( $atts = [] )
|
23 |
+
{
|
24 |
+
$defaults = [
|
25 |
+
'class' => '',
|
26 |
+
'count' => 10,
|
27 |
+
'display' => 'title,excerpt,author,date,rating,url',
|
28 |
+
'pagination' => false,
|
29 |
+
'rating' => 5,
|
30 |
+
'title' => '',
|
31 |
+
'type' => '',
|
32 |
+
];
|
33 |
+
|
34 |
+
$args = shortcode_atts( $defaults, $atts );
|
35 |
+
|
36 |
+
extract( $args );
|
37 |
+
|
38 |
+
$display = array_map( 'trim', explode( ',', $display ) );
|
39 |
+
|
40 |
+
$author = in_array( 'author', $display );
|
41 |
+
$date = in_array( 'date', $display );
|
42 |
+
$link = in_array( 'link', $display );
|
43 |
+
$rating = in_array( 'rating', $display );
|
44 |
+
|
45 |
+
$display = array_intersect( ['title', 'excerpt'], $display );
|
46 |
+
$display = $display
|
47 |
+
? ( count($display) > 1 ? 'both' : array_shift( $display ) )
|
48 |
+
: 'both';
|
49 |
+
|
50 |
+
ob_start();
|
51 |
+
|
52 |
+
echo '<div class="shortcode-site-reviews">';
|
53 |
+
|
54 |
+
if( !empty( $title ) ) {
|
55 |
+
printf( '<h2>%s</h2>', $title );
|
56 |
+
}
|
57 |
+
|
58 |
+
$this->html->renderPartial( 'reviews', [
|
59 |
+
'class' => $class,
|
60 |
+
'display' => $display,
|
61 |
+
'max_reviews' => $count,
|
62 |
+
'min_rating' => $rating,
|
63 |
+
'order_by' => 'date',
|
64 |
+
'pagination' => $pagination,
|
65 |
+
'show_author' => $author,
|
66 |
+
'show_date' => $date,
|
67 |
+
'show_link' => $link,
|
68 |
+
'show_rating' => $rating,
|
69 |
+
'site_name' => $type,
|
70 |
+
]);
|
71 |
+
|
72 |
+
echo '</div>';
|
73 |
+
|
74 |
+
return ob_get_clean();
|
75 |
+
}
|
76 |
+
}
|
plugin/Shortcodes/SiteReviewsForm.php
ADDED
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* = Reviews Form shortcode
|
5 |
+
*
|
6 |
+
* @package GeminiLabs\SiteReviews
|
7 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
8 |
+
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
9 |
+
* @since 1.0.0
|
10 |
+
* -------------------------------------------------------------------------------------------------
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace GeminiLabs\SiteReviews\Shortcodes;
|
14 |
+
|
15 |
+
use GeminiLabs\SiteReviews\Shortcode;
|
16 |
+
|
17 |
+
class SiteReviewsForm extends Shortcode
|
18 |
+
{
|
19 |
+
/**
|
20 |
+
* @return null|string
|
21 |
+
*/
|
22 |
+
public function printShortcode( $atts = [] )
|
23 |
+
{
|
24 |
+
$requireUser = glsr_resolve( 'Database' )->getOption( 'general.require.login', false );
|
25 |
+
|
26 |
+
if( $requireUser && !is_user_logged_in() ) {
|
27 |
+
$message = sprintf(
|
28 |
+
__( 'You must be <a href="%s">logged in</a> to submit a review.', 'site-reviews' ),
|
29 |
+
wp_login_url( get_permalink() )
|
30 |
+
);
|
31 |
+
echo wpautop( $message );
|
32 |
+
return;
|
33 |
+
}
|
34 |
+
|
35 |
+
$defaults = [
|
36 |
+
'class' => '',
|
37 |
+
'description' => '',
|
38 |
+
'hide' => '',
|
39 |
+
'title' => '',
|
40 |
+
];
|
41 |
+
|
42 |
+
$atts = shortcode_atts( $defaults, $atts );
|
43 |
+
|
44 |
+
$fields = explode( ',', $atts['hide'] );
|
45 |
+
$fields = array_filter( $fields, function( $value ) {
|
46 |
+
return in_array( $value, [
|
47 |
+
'title',
|
48 |
+
'reviewer',
|
49 |
+
'email',
|
50 |
+
'terms'
|
51 |
+
]);
|
52 |
+
});
|
53 |
+
|
54 |
+
$formId = $this->generate_id( $atts );
|
55 |
+
|
56 |
+
$errors = $this->session->get( "{$formId}-errors", [], 'and then remove errors' );
|
57 |
+
$message = $this->session->get( "{$formId}-message", [], 'and then remove message' );
|
58 |
+
|
59 |
+
$values = !empty( $errors )
|
60 |
+
? $this->session->get( "{$formId}-values", [], 'and then remove values' )
|
61 |
+
: [];
|
62 |
+
|
63 |
+
ob_start();
|
64 |
+
|
65 |
+
echo '<div class="shortcode-reviews-form">';
|
66 |
+
|
67 |
+
if( !empty( $atts['title'] ) ) {
|
68 |
+
printf( '<h3 class="glsr-form-title">%s</h3>', $atts['title'] );
|
69 |
+
}
|
70 |
+
|
71 |
+
if( !empty( $atts['description'] ) ) {
|
72 |
+
printf( '<p class="glsr-form-description">%s</p>', $atts['description'] );
|
73 |
+
}
|
74 |
+
|
75 |
+
$this->app->make( 'Controllers\ReviewController' )->render( 'submit/index', [
|
76 |
+
'class' => trim( 'glsr-submit-review-form ' . $atts['class'] ),
|
77 |
+
'errors' => $errors,
|
78 |
+
'exclude' => $fields,
|
79 |
+
'form_id' => $formId,
|
80 |
+
'message' => $message,
|
81 |
+
'values' => shortcode_atts([
|
82 |
+
'rating' => '',
|
83 |
+
'title' => '',
|
84 |
+
'content' => '',
|
85 |
+
'reviewer' => '',
|
86 |
+
'email' => '',
|
87 |
+
'terms' => '',
|
88 |
+
], $values ),
|
89 |
+
]);
|
90 |
+
|
91 |
+
echo '</div>';
|
92 |
+
|
93 |
+
return ob_get_clean();
|
94 |
+
}
|
95 |
+
}
|
plugin/Strings.php
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews;
|
12 |
+
|
13 |
+
class Strings
|
14 |
+
{
|
15 |
+
public function post_type_labels()
|
16 |
+
{
|
17 |
+
return [
|
18 |
+
'add_new_item' => __( 'Add New Review', 'site-reviews' ),
|
19 |
+
'all_items' => __( 'All Reviews', 'site-reviews' ),
|
20 |
+
'archives' => __( 'Review Archives', 'site-reviews' ),
|
21 |
+
'edit_item' => __( 'Edit Review', 'site-reviews' ),
|
22 |
+
'insert_into_item' => __( 'Insert into review', 'site-reviews' ),
|
23 |
+
'new_item' => __( 'New Review', 'site-reviews' ),
|
24 |
+
'not_found' => __( 'No Reviews found', 'site-reviews' ),
|
25 |
+
'not_found_in_trash' => __( 'No Reviews found in Trash', 'site-reviews' ),
|
26 |
+
'search_items' => __( 'Search Reviews', 'site-reviews' ),
|
27 |
+
'uploaded_to_this_item' => __( 'Uploaded to this review', 'site-reviews' ),
|
28 |
+
'view_item' => __( 'View Review', 'site-reviews' ),
|
29 |
+
];
|
30 |
+
}
|
31 |
+
|
32 |
+
public function post_updated_messages()
|
33 |
+
{
|
34 |
+
return [
|
35 |
+
'approved' => __( 'Review has been approved and published.', 'site-reviews' ),
|
36 |
+
'draft_updated' => __( 'Review draft updated.', 'site-reviews' ),
|
37 |
+
'preview' => __( 'Preview review', 'site-reviews' ),
|
38 |
+
'published' => __( 'Review published.', 'site-reviews' ),
|
39 |
+
'restored' => __( 'Review restored to revision from %s.', 'site-reviews' ),
|
40 |
+
'reverted' => __( 'Review has been reverted to its original submission state.', 'site-reviews' ),
|
41 |
+
'saved' => __( 'Review saved.', 'site-reviews' ),
|
42 |
+
'scheduled' => __( 'Review scheduled for: %s.', 'site-reviews' ),
|
43 |
+
'submitted' => __( 'Review submitted.', 'site-reviews' ),
|
44 |
+
'unapproved' => __( 'Review has been unapproved and is now pending.', 'site-reviews' ),
|
45 |
+
'updated' => __( 'Review updated.', 'site-reviews' ),
|
46 |
+
'view' => __( 'View review', 'site-reviews' ),
|
47 |
+
];
|
48 |
+
}
|
49 |
+
|
50 |
+
public function validation()
|
51 |
+
{
|
52 |
+
return [
|
53 |
+
'accepted' => _x( 'The :attribute must be accepted.', ':attribute is a placeholder and should not be translated.', 'site-reviews' ),
|
54 |
+
'between.numeric' => _x( 'The :attribute must be between :min and :max.', ':attribute, :min, and :max are placeholders and should not be translated.', 'site-reviews' ),
|
55 |
+
'between.string' => _x( 'The :attribute must be between :min and :max characters.', ':attribute, :min, and :max are placeholders and should not be translated.', 'site-reviews' ),
|
56 |
+
'email' => _x( 'The :attribute must be a valid email address.', ':attribute is a placeholder and should not be translated.', 'site-reviews' ),
|
57 |
+
'max.numeric' => _x( 'The :attribute may not be greater than :max.', ':attribute and :max are placeholders and should not be translated.', 'site-reviews' ),
|
58 |
+
'max.string' => _x( 'The :attribute may not be greater than :max characters.', ':attribute and :max are placeholders and should not be translated.', 'site-reviews' ),
|
59 |
+
'min.numeric' => _x( 'The :attribute must be at least :min.', ':attribute and :min are placeholders and should not be translated.', 'site-reviews' ),
|
60 |
+
'min.string' => _x( 'The :attribute must be at least :min characters.', ':attribute and :min are placeholders and should not be translated.', 'site-reviews' ),
|
61 |
+
'regex' => _x( 'The :attribute format is invalid.', ':attribute is a placeholder and should not be translated.', 'site-reviews' ),
|
62 |
+
'required' => _x( 'The :attribute field is required.', ':attribute is a placeholder and should not be translated.', 'site-reviews' ),
|
63 |
+
];
|
64 |
+
}
|
65 |
+
}
|
plugin/SystemInfo.php
ADDED
@@ -0,0 +1,457 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace GeminiLabs\SiteReviews;
|
12 |
+
|
13 |
+
use GeminiLabs\SiteReviews\App;
|
14 |
+
use Sinergi\BrowserDetector\Browser;
|
15 |
+
|
16 |
+
class SystemInfo
|
17 |
+
{
|
18 |
+
/**
|
19 |
+
* @var int
|
20 |
+
*/
|
21 |
+
public $pad = 30;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var App
|
25 |
+
*/
|
26 |
+
protected $app;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var array
|
30 |
+
*/
|
31 |
+
protected $sysinfo = [];
|
32 |
+
|
33 |
+
public function __construct( App $app )
|
34 |
+
{
|
35 |
+
$this->app = $app;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Downloads the system info as a text file
|
40 |
+
*
|
41 |
+
* @param string $system_info
|
42 |
+
*
|
43 |
+
* @return void
|
44 |
+
*/
|
45 |
+
public function download( $system_info )
|
46 |
+
{
|
47 |
+
if( !current_user_can( 'install_plugins' ) )return;
|
48 |
+
|
49 |
+
nocache_headers();
|
50 |
+
|
51 |
+
header( 'Content-Type: text/plain' );
|
52 |
+
header( 'Content-Disposition: attachment; filename="system-info.txt"' );
|
53 |
+
|
54 |
+
echo wp_strip_all_tags( $system_info );
|
55 |
+
|
56 |
+
exit;
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Get all system info
|
61 |
+
*
|
62 |
+
* @return string
|
63 |
+
*/
|
64 |
+
public function getAll()
|
65 |
+
{
|
66 |
+
return trim(
|
67 |
+
$this->getPlugin() .
|
68 |
+
$this->getBrowser() .
|
69 |
+
$this->getWebserver() .
|
70 |
+
$this->getPHPConfig() .
|
71 |
+
$this->getPHPExtensions() .
|
72 |
+
$this->getWordpress() .
|
73 |
+
$this->getWordpressMuplugins() .
|
74 |
+
$this->getWordpressPlugins() .
|
75 |
+
$this->getWordpressMultisitePlugins()
|
76 |
+
);
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Get browser info
|
81 |
+
*
|
82 |
+
* @param string $title
|
83 |
+
*
|
84 |
+
* @return null|string
|
85 |
+
*/
|
86 |
+
public function getBrowser( $title = 'Browser Details' )
|
87 |
+
{
|
88 |
+
if( !$this->title( $title ) )return;
|
89 |
+
|
90 |
+
$browser = new Browser;
|
91 |
+
$userAgent = $browser->getUserAgent();
|
92 |
+
|
93 |
+
$this->sysinfo[ $title ]['Browser Name'] = sprintf( '%s v%s', $browser->getName(), $browser->getVersion() );
|
94 |
+
$this->sysinfo[ $title ]['Browser UA'] = $userAgent->getUserAgentString();
|
95 |
+
|
96 |
+
return $this->implode( $title );
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Get plugin info
|
101 |
+
*
|
102 |
+
* @param string $title
|
103 |
+
*
|
104 |
+
* @return null|string
|
105 |
+
*/
|
106 |
+
public function getPlugin( $title = 'Plugin Details' )
|
107 |
+
{
|
108 |
+
if( !$this->title( $title ) )return;
|
109 |
+
|
110 |
+
$this->sysinfo[ $title ]['Current version'] = $this->app->version;
|
111 |
+
$this->sysinfo[ $title ]['Previous version'] = 'None';
|
112 |
+
|
113 |
+
return $this->implode( $title );
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Get WordPress configuration info
|
118 |
+
*
|
119 |
+
* @param string $title
|
120 |
+
*
|
121 |
+
* @return null|string
|
122 |
+
*/
|
123 |
+
public function getWordpress( $title = 'WordPress Configuration' )
|
124 |
+
{
|
125 |
+
if( !$this->title( $title ) )return;
|
126 |
+
|
127 |
+
global $wpdb;
|
128 |
+
|
129 |
+
$theme = wp_get_theme();
|
130 |
+
$response = wp_remote_post( 'https://api.wordpress.org/stats/php/1.0/' );
|
131 |
+
|
132 |
+
$wp_prefix_status = strlen( $wpdb->prefix ) > 16 ? 'ERROR: Too long' : 'Acceptable';
|
133 |
+
|
134 |
+
$wp_remote_post = !is_wp_error( $response )
|
135 |
+
&& $response['response']['code'] >= 200
|
136 |
+
&& $response['response']['code'] < 300;
|
137 |
+
|
138 |
+
$this->sysinfo[ $title ]['Active Theme'] = sprintf( '%s v%s', $theme->Name, $theme->Version );
|
139 |
+
$this->sysinfo[ $title ]['Home URL'] = home_url();
|
140 |
+
$this->sysinfo[ $title ]['Language'] = ( defined( 'WPLANG' ) && WPLANG ) ? WPLANG : 'en_US';
|
141 |
+
$this->sysinfo[ $title ]['Memory Limit'] = WP_MEMORY_LIMIT;
|
142 |
+
$this->sysinfo[ $title ]['Multisite'] = is_multisite() ? 'Yes' : 'No';
|
143 |
+
$this->sysinfo[ $title ]['Page For Posts ID'] = get_option( 'page_for_posts' );
|
144 |
+
$this->sysinfo[ $title ]['Page On Front ID'] = get_option( 'page_on_front' );
|
145 |
+
$this->sysinfo[ $title ]['Permalink Structure'] = get_option( 'permalink_structure', 'Default' );
|
146 |
+
$this->sysinfo[ $title ]['Post Stati'] = implode( ', ', get_post_stati() );
|
147 |
+
$this->sysinfo[ $title ]['Remote Post'] = $wp_remote_post ? 'Works' : 'Does not work';
|
148 |
+
$this->sysinfo[ $title ]['Show On Front'] = get_option( 'show_on_front' );
|
149 |
+
$this->sysinfo[ $title ]['Site URL'] = site_url();
|
150 |
+
$this->sysinfo[ $title ]['Table Prefix'] = sprintf( '%s (%d)', $wp_prefix_status, strlen( $wpdb->prefix ) );
|
151 |
+
$this->sysinfo[ $title ]['Timezone'] = get_option( 'timezone_string' );
|
152 |
+
$this->sysinfo[ $title ]['Version'] = get_bloginfo( 'version' );
|
153 |
+
$this->sysinfo[ $title ]['WP Debug'] = defined( 'WP_DEBUG' ) ? WP_DEBUG ? 'Enabled' : 'Disabled' : 'Not set';
|
154 |
+
$this->sysinfo[ $title ]['WP Max Upload Size'] = size_format( wp_max_upload_size() );
|
155 |
+
$this->sysinfo[ $title ]['WP Memory Limit'] = WP_MEMORY_LIMIT;
|
156 |
+
|
157 |
+
return $this->implode( $title );
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Get all active WordPress multisite plugins
|
162 |
+
*
|
163 |
+
* @param string $title
|
164 |
+
*
|
165 |
+
* @return null|string
|
166 |
+
*/
|
167 |
+
public function getWordpressMultisitePlugins( $title = 'Network Active Plugins' )
|
168 |
+
{
|
169 |
+
if( !is_multisite() )return;
|
170 |
+
|
171 |
+
$active = get_site_option( 'active_sitewide_plugins', [] );
|
172 |
+
|
173 |
+
if( !$this->title( $title ) || !count( $active ) )return;
|
174 |
+
|
175 |
+
$plugins = wp_get_active_network_plugins();
|
176 |
+
|
177 |
+
foreach( $plugins as $plugin_path ) {
|
178 |
+
|
179 |
+
if( !in_array( plugin_basename( $plugin_path ), $active ) )continue;
|
180 |
+
|
181 |
+
$plugin = get_plugin_data( $plugin_path );
|
182 |
+
|
183 |
+
$this->sysinfo[ $title ][] = sprintf( '%s v%s', $plugin['Name'], $plugin['Version'] );
|
184 |
+
}
|
185 |
+
|
186 |
+
return $this->implode( $title );
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Get all WordPress mu-plugins
|
191 |
+
*
|
192 |
+
* @param string $title
|
193 |
+
*
|
194 |
+
* @return null|string
|
195 |
+
*/
|
196 |
+
public function getWordpressMuplugins( $title = 'Must-Use Plugins' )
|
197 |
+
{
|
198 |
+
$auto_plugins = get_plugins( '/../' . basename( WPMU_PLUGIN_DIR ) );
|
199 |
+
$mu_plugins = get_mu_plugins();
|
200 |
+
|
201 |
+
$plugins = $this->formatPlugins( array_merge( $mu_plugins, $auto_plugins ) );
|
202 |
+
|
203 |
+
return $this->wordpressPlugins( $title, $plugins );
|
204 |
+
}
|
205 |
+
|
206 |
+
/**
|
207 |
+
* Get all active/inactive WordPress plugins
|
208 |
+
*
|
209 |
+
* @param string $active_title
|
210 |
+
* @param string $inactive_title
|
211 |
+
*
|
212 |
+
* @return null|string
|
213 |
+
*/
|
214 |
+
public function getWordpressPlugins( $active_title = 'Active Plugins', $inactive_title = 'Inactive Plugins' )
|
215 |
+
{
|
216 |
+
$plugins = get_plugins();
|
217 |
+
|
218 |
+
if( !count( $plugins ) )return;
|
219 |
+
|
220 |
+
$active_plugins = get_option( 'active_plugins', [] );
|
221 |
+
|
222 |
+
$inactive = $this->formatPlugins( array_diff_key( $plugins, array_flip( $active_plugins ) ) );
|
223 |
+
$active = $this->formatPlugins( array_diff_key( $plugins, $inactive ) );
|
224 |
+
|
225 |
+
$active_plugins = $this->wordpressPlugins( $active_title, $active );
|
226 |
+
$inactive_plugins = $this->wordpressPlugins( $inactive_title, $inactive );
|
227 |
+
|
228 |
+
return $active_plugins . $inactive_plugins;
|
229 |
+
}
|
230 |
+
|
231 |
+
/**
|
232 |
+
* Get the webhost/webserver info
|
233 |
+
*
|
234 |
+
* @param string $title
|
235 |
+
*
|
236 |
+
* @return null|string
|
237 |
+
*/
|
238 |
+
public function getWebserver( $title = 'Server configuration' )
|
239 |
+
{
|
240 |
+
if( !$this->title( $title ) )return;
|
241 |
+
|
242 |
+
global $wpdb;
|
243 |
+
|
244 |
+
$server_ip = filter_input( INPUT_SERVER, 'SERVER_ADDR' );
|
245 |
+
|
246 |
+
$this->sysinfo[ $title ]['Host Name'] = sprintf( '%s (%s)', $this->webhost(), gethostbyaddr( $server_ip ) );
|
247 |
+
$this->sysinfo[ $title ]['MySQL Version'] = $wpdb->db_version();
|
248 |
+
$this->sysinfo[ $title ]['PHP Version'] = PHP_VERSION;
|
249 |
+
$this->sysinfo[ $title ]['Server Info'] = filter_input( INPUT_SERVER, 'SERVER_SOFTWARE' );
|
250 |
+
$this->sysinfo[ $title ]['Server IP Address'] = $server_ip;
|
251 |
+
|
252 |
+
return $this->implode( $title );
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* Get the PHP config
|
257 |
+
*
|
258 |
+
* @param string $title
|
259 |
+
*
|
260 |
+
* @return null|string
|
261 |
+
*/
|
262 |
+
public function getPHPConfig( $title = 'PHP Configuration' )
|
263 |
+
{
|
264 |
+
if( !$this->title( $title ) )return;
|
265 |
+
|
266 |
+
$this->sysinfo[ $title ]['Display Errors'] = ini_get( 'display_errors' ) ? 'On (' . ini_get( 'display_errors' ) . ')' : 'N/A';
|
267 |
+
$this->sysinfo[ $title ]['Max Execution Time'] = ini_get( 'max_execution_time' );
|
268 |
+
$this->sysinfo[ $title ]['Max Input Nesting Level'] = ini_get( 'max_input_nesting_level' );
|
269 |
+
$this->sysinfo[ $title ]['Max Input Vars'] = ini_get( 'max_input_vars' );
|
270 |
+
$this->sysinfo[ $title ]['Memory Limit'] = ini_get( 'memory_limit' );
|
271 |
+
$this->sysinfo[ $title ]['Post Max Size'] = ini_get( 'post_max_size' );
|
272 |
+
$this->sysinfo[ $title ]['Session Cookie Path'] = esc_html( ini_get( 'session.cookie_path' ) );
|
273 |
+
$this->sysinfo[ $title ]['Session Name'] = esc_html( ini_get( 'session.name' ) );
|
274 |
+
$this->sysinfo[ $title ]['Session Save Path'] = esc_html( ini_get( 'session.save_path' ) );
|
275 |
+
$this->sysinfo[ $title ]['Session Use Cookies'] = ini_get( 'session.use_cookies' ) ? 'On' : 'Off';
|
276 |
+
$this->sysinfo[ $title ]['Session Use Only Cookies'] = ini_get( 'session.use_only_cookies' ) ? 'On' : 'Off';
|
277 |
+
$this->sysinfo[ $title ]['Upload Max Filesize'] = ini_get( 'upload_max_filesize' );
|
278 |
+
|
279 |
+
return $this->implode( $title );
|
280 |
+
}
|
281 |
+
|
282 |
+
/**
|
283 |
+
* Get the status of required PHP extensions
|
284 |
+
*
|
285 |
+
* @param string $title
|
286 |
+
*
|
287 |
+
* @return null|string
|
288 |
+
*/
|
289 |
+
public function getPHPExtensions( $title = 'PHP Extensions' )
|
290 |
+
{
|
291 |
+
if( !$this->title( $title ) )return;
|
292 |
+
|
293 |
+
$this->sysinfo[ $title ]['cURL'] = function_exists( 'curl_init' ) ? 'Supported' : 'Not Supported';
|
294 |
+
$this->sysinfo[ $title ]['fsockopen'] = function_exists( 'fsockopen' ) ? 'Supported' : 'Not Supported';
|
295 |
+
$this->sysinfo[ $title ]['SOAP Client'] = class_exists( 'SoapClient' ) ? 'Installed' : 'Not Installed';
|
296 |
+
$this->sysinfo[ $title ]['Suhosin'] = extension_loaded( 'suhosin' ) ? 'Installed' : 'Not Installed';
|
297 |
+
|
298 |
+
return $this->implode( $title );
|
299 |
+
}
|
300 |
+
|
301 |
+
/**
|
302 |
+
* Formats an array of plugins to [path] => [name version] and sort by value
|
303 |
+
*
|
304 |
+
* @param array $plugins
|
305 |
+
*
|
306 |
+
* @return array
|
307 |
+
*/
|
308 |
+
protected function formatPlugins( array $plugins )
|
309 |
+
{
|
310 |
+
$plugins = array_map( function( $plugin ) {
|
311 |
+
return sprintf( '%s v%s', $plugin['Name'], $plugin['Version'] );
|
312 |
+
}, $plugins );
|
313 |
+
|
314 |
+
natcasesort( $plugins );
|
315 |
+
|
316 |
+
return $plugins;
|
317 |
+
}
|
318 |
+
|
319 |
+
/**
|
320 |
+
* Generates a string from a system info section array
|
321 |
+
*
|
322 |
+
* @param string $section
|
323 |
+
*
|
324 |
+
* @return null|string
|
325 |
+
*/
|
326 |
+
protected function implode( $section )
|
327 |
+
{
|
328 |
+
if( !isset( $this->sysinfo[ $section ] ) )return;
|
329 |
+
|
330 |
+
$strings = [];
|
331 |
+
|
332 |
+
$strings[] = sprintf( '[%s]', strtoupper( $section ) );
|
333 |
+
|
334 |
+
foreach( $this->sysinfo[ $section ] as $key => $value ) {
|
335 |
+
if( is_int( $key ) ) {
|
336 |
+
$strings[] = " - {$value}";
|
337 |
+
continue;
|
338 |
+
}
|
339 |
+
|
340 |
+
$strings[] = sprintf( '%s : %s', $this->pad( $key ), $value );
|
341 |
+
}
|
342 |
+
|
343 |
+
return implode( PHP_EOL, $strings ) . PHP_EOL . PHP_EOL;
|
344 |
+
}
|
345 |
+
|
346 |
+
/**
|
347 |
+
* Pad a string with periods to the required length
|
348 |
+
*
|
349 |
+
* @param string $string
|
350 |
+
* @param int|null $pad
|
351 |
+
* @param string $char
|
352 |
+
*
|
353 |
+
* @return string
|
354 |
+
*/
|
355 |
+
protected function pad( $string, $pad = null, $char = '.' )
|
356 |
+
{
|
357 |
+
$pad = $pad ?: $this->pad;
|
358 |
+
|
359 |
+
if( strlen( $string ) === $pad ) {
|
360 |
+
return $string;
|
361 |
+
}
|
362 |
+
|
363 |
+
return str_pad( "{$string} ", $pad, $char );
|
364 |
+
}
|
365 |
+
|
366 |
+
/**
|
367 |
+
* Get the info title
|
368 |
+
*
|
369 |
+
* @param string $title
|
370 |
+
*
|
371 |
+
* @return bool
|
372 |
+
*/
|
373 |
+
protected function title( $title )
|
374 |
+
{
|
375 |
+
if( !$title ) {
|
376 |
+
return false;
|
377 |
+
}
|
378 |
+
|
379 |
+
$this->sysinfo[ strtoupper( $title ) ] = [];
|
380 |
+
|
381 |
+
return true;
|
382 |
+
}
|
383 |
+
|
384 |
+
/**
|
385 |
+
* Get the webhost name
|
386 |
+
*
|
387 |
+
* @return string
|
388 |
+
*/
|
389 |
+
protected function webhost()
|
390 |
+
{
|
391 |
+
$server_name = filter_input( INPUT_SERVER, 'SERVER_NAME' );
|
392 |
+
|
393 |
+
$uname = php_uname();
|
394 |
+
|
395 |
+
if( defined( 'WPE_APIKEY' ) ) {
|
396 |
+
$host = 'WP Engine';
|
397 |
+
} elseif( defined( 'PAGELYBIN' ) ) {
|
398 |
+
$host = 'Pagely';
|
399 |
+
} elseif( DB_HOST == 'localhost:/tmp/mysql5.sock' ) {
|
400 |
+
$host = 'ICDSoft';
|
401 |
+
} elseif( DB_HOST == 'mysqlv5' ) {
|
402 |
+
$host = 'NetworkSolutions';
|
403 |
+
} elseif( strpos( DB_HOST, 'ipagemysql.com' ) !== false ) {
|
404 |
+
$host = 'iPage';
|
405 |
+
} elseif( strpos( DB_HOST, 'ipowermysql.com' ) !== false ) {
|
406 |
+
$host = 'IPower';
|
407 |
+
} elseif( strpos( DB_HOST, '.gridserver.com' ) !== false ) {
|
408 |
+
$host = 'MediaTemple Grid';
|
409 |
+
} elseif( strpos( DB_HOST, '.pair.com' ) !== false ) {
|
410 |
+
$host = 'pair Networks';
|
411 |
+
} elseif( strpos( DB_HOST, '.stabletransit.com' ) !== false ) {
|
412 |
+
$host = 'Rackspace Cloud';
|
413 |
+
} elseif( strpos( DB_HOST, '.sysfix.eu' ) !== false ) {
|
414 |
+
$host = 'SysFix.eu Power Hosting';
|
415 |
+
} elseif( strpos( $server_name, 'Flywheel' ) !== false ) {
|
416 |
+
$host = 'Flywheel';
|
417 |
+
} elseif( filter_input( INPUT_SERVER, 'DH_USER' ) ) {
|
418 |
+
$host = 'DreamHost';
|
419 |
+
} elseif( strpos( $uname, 'bluehost.com' ) !== false ) {
|
420 |
+
$host = 'Bluehost';
|
421 |
+
} elseif( strpos( $uname, 'secureserver.net') !== false ) {
|
422 |
+
$host = 'GoDaddy';
|
423 |
+
} elseif( strpos( $uname, '.inmotionhosting.com') !== false ) {
|
424 |
+
$host = 'InMotion Hosting';
|
425 |
+
} elseif( strpos( $uname, '.ovh.net') !== false ) {
|
426 |
+
$host = 'OVH';
|
427 |
+
} elseif( strpos( $uname, '.accountservergroup.com ') !== false ) {
|
428 |
+
$host = 'Site5';
|
429 |
+
} elseif( strpos( $uname, '.stratoserver.net ') !== false ) {
|
430 |
+
$host = 'STRATO';
|
431 |
+
} else {
|
432 |
+
$host = sprintf( '%s, %s', DB_HOST, $server_name );
|
433 |
+
}
|
434 |
+
|
435 |
+
return $host;
|
436 |
+
}
|
437 |
+
|
438 |
+
/**
|
439 |
+
* Format an array of passed WordPress plugins
|
440 |
+
*
|
441 |
+
* @param string $title
|
442 |
+
*
|
443 |
+
* @return string|null
|
444 |
+
*/
|
445 |
+
protected function wordpressPlugins( $title, array $plugins )
|
446 |
+
{
|
447 |
+
if( !count( $plugins ) || !$this->title( $title ) )return;
|
448 |
+
|
449 |
+
$pad = max( array_map( 'strlen', $plugins ) );
|
450 |
+
|
451 |
+
foreach( $plugins as $path => $plugin ) {
|
452 |
+
$this->sysinfo[ $title ][] = sprintf( '%s : %s', $this->pad( $plugin, $pad ), $path );
|
453 |
+
}
|
454 |
+
|
455 |
+
return $this->implode( $title );
|
456 |
+
}
|
457 |
+
}
|
plugin/Validator.php
ADDED
@@ -0,0 +1,587 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
*
|
9 |
+
* Much of the code in this class is derived from Illuminate\Validation\Validator (5.3)
|
10 |
+
* Copyright (c) <Taylor Otwell>
|
11 |
+
* -------------------------------------------------------------------------------------------------
|
12 |
+
*/
|
13 |
+
|
14 |
+
namespace GeminiLabs\SiteReviews;
|
15 |
+
|
16 |
+
use BadMethodCallException;
|
17 |
+
use GeminiLabs\SiteReviews\Strings;
|
18 |
+
use InvalidArgumentException;
|
19 |
+
|
20 |
+
class Validator
|
21 |
+
{
|
22 |
+
/**
|
23 |
+
* @var array
|
24 |
+
*/
|
25 |
+
public $errors = [];
|
26 |
+
|
27 |
+
/**
|
28 |
+
* The data under validation.
|
29 |
+
*
|
30 |
+
* @var array
|
31 |
+
*/
|
32 |
+
protected $data = [];
|
33 |
+
|
34 |
+
/**
|
35 |
+
* The failed validation rules.
|
36 |
+
*
|
37 |
+
* @var array
|
38 |
+
*/
|
39 |
+
protected $failedRules = [];
|
40 |
+
|
41 |
+
/**
|
42 |
+
* The rules to be applied to the data.
|
43 |
+
*
|
44 |
+
* @var array
|
45 |
+
*/
|
46 |
+
protected $rules = [];
|
47 |
+
|
48 |
+
/**
|
49 |
+
* The size related validation rules.
|
50 |
+
*
|
51 |
+
* @var array
|
52 |
+
*/
|
53 |
+
protected $sizeRules = [
|
54 |
+
'Between',
|
55 |
+
'Max',
|
56 |
+
'Min',
|
57 |
+
];
|
58 |
+
|
59 |
+
/**
|
60 |
+
* The validation rules that imply the field is required.
|
61 |
+
*
|
62 |
+
* @var array
|
63 |
+
*/
|
64 |
+
protected $implicitRules = [
|
65 |
+
'Required',
|
66 |
+
];
|
67 |
+
|
68 |
+
/**
|
69 |
+
* The numeric related validation rules.
|
70 |
+
*
|
71 |
+
* @var array
|
72 |
+
*/
|
73 |
+
protected $numericRules = [
|
74 |
+
'Numeric',
|
75 |
+
];
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Run the validator's rules against its data.
|
79 |
+
*
|
80 |
+
* @param mixed $data
|
81 |
+
*
|
82 |
+
* @return array
|
83 |
+
*/
|
84 |
+
public function validate( $data, array $rules = [] )
|
85 |
+
{
|
86 |
+
$this->normalizeData( $data );
|
87 |
+
$this->setRules( $rules );
|
88 |
+
|
89 |
+
foreach( $this->rules as $attribute => $rules ) {
|
90 |
+
foreach( $rules as $rule ) {
|
91 |
+
$this->validateAttribute( $attribute, $rule );
|
92 |
+
|
93 |
+
if( $this->shouldStopValidating( $attribute ) )break;
|
94 |
+
}
|
95 |
+
}
|
96 |
+
|
97 |
+
return $this->errors;
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Add an error message to the validator's collection of errors.
|
102 |
+
*
|
103 |
+
* @param string $attribute
|
104 |
+
* @param string $rule
|
105 |
+
*
|
106 |
+
* @return void
|
107 |
+
*/
|
108 |
+
protected function addError( $attribute, $rule, array $parameters )
|
109 |
+
{
|
110 |
+
$message = $this->getMessage( $attribute, $rule, $parameters );
|
111 |
+
|
112 |
+
$this->errors[ $attribute ]['errors'][] = $message;
|
113 |
+
|
114 |
+
if( !isset( $this->errors[ $attribute ]['value'] ) ) {
|
115 |
+
$this->errors[ $attribute ]['value'] = $this->getValue( $attribute );
|
116 |
+
}
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Add a failed rule and error message to the collection.
|
121 |
+
*
|
122 |
+
* @param string $attribute
|
123 |
+
* @param string $rule
|
124 |
+
*
|
125 |
+
* @return void
|
126 |
+
*/
|
127 |
+
protected function addFailure( $attribute, $rule, array $parameters )
|
128 |
+
{
|
129 |
+
$this->addError( $attribute, $rule, $parameters );
|
130 |
+
|
131 |
+
$this->failedRules[ $attribute ][ $rule ] = $parameters;
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* Get the data type of the given attribute.
|
136 |
+
*
|
137 |
+
* @param string $attribute
|
138 |
+
* @return string
|
139 |
+
*/
|
140 |
+
protected function getAttributeType( $attribute )
|
141 |
+
{
|
142 |
+
return $this->hasRule( $attribute, $this->numericRules )
|
143 |
+
? 'numeric'
|
144 |
+
: 'string';
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Get the validation message for an attribute and rule.
|
149 |
+
*
|
150 |
+
* @param string $attribute
|
151 |
+
* @param string $rule
|
152 |
+
*
|
153 |
+
* @return string|null
|
154 |
+
*/
|
155 |
+
protected function getMessage( $attribute, $rule, array $parameters )
|
156 |
+
{
|
157 |
+
if( in_array( $rule, $this->sizeRules ) ) {
|
158 |
+
return $this->getSizeMessage( $attribute, $rule, $parameters );
|
159 |
+
}
|
160 |
+
|
161 |
+
$lowerRule = $this->snakeCase( $rule );
|
162 |
+
|
163 |
+
return $this->translator( $lowerRule, $rule, $attribute, $parameters );
|
164 |
+
}
|
165 |
+
|
166 |
+
/**
|
167 |
+
* Get a rule and its parameters for a given attribute.
|
168 |
+
*
|
169 |
+
* @param string $attribute
|
170 |
+
* @param string|array $rules
|
171 |
+
*
|
172 |
+
* @return array|null
|
173 |
+
*/
|
174 |
+
protected function getRule( $attribute, $rules )
|
175 |
+
{
|
176 |
+
if( !array_key_exists( $attribute, $this->rules ) )return;
|
177 |
+
|
178 |
+
$rules = (array) $rules;
|
179 |
+
|
180 |
+
foreach( $this->rules[ $attribute ] as $rule ) {
|
181 |
+
list( $rule, $parameters ) = $this->parseRule( $rule );
|
182 |
+
|
183 |
+
if( in_array( $rule, $rules ) ) {
|
184 |
+
return [ $rule, $parameters ];
|
185 |
+
}
|
186 |
+
}
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Get the size of an attribute.
|
191 |
+
*
|
192 |
+
* @param string $attribute
|
193 |
+
* @param mixed $value
|
194 |
+
*
|
195 |
+
* @return mixed
|
196 |
+
*/
|
197 |
+
protected function getSize( $attribute, $value )
|
198 |
+
{
|
199 |
+
$hasNumeric = $this->hasRule( $attribute, $this->numericRules );
|
200 |
+
|
201 |
+
if( is_numeric( $value ) && $hasNumeric ) {
|
202 |
+
return $value;
|
203 |
+
}
|
204 |
+
elseif( is_array( $value ) ) {
|
205 |
+
return count( $value );
|
206 |
+
}
|
207 |
+
|
208 |
+
return mb_strlen( $value );
|
209 |
+
}
|
210 |
+
|
211 |
+
/**
|
212 |
+
* Get the proper error message for an attribute and size rule.
|
213 |
+
*
|
214 |
+
* @param string $attribute
|
215 |
+
* @param string $rule
|
216 |
+
*
|
217 |
+
* @return string|null
|
218 |
+
*/
|
219 |
+
protected function getSizeMessage( $attribute, $rule, array $parameters )
|
220 |
+
{
|
221 |
+
$lowerRule = $this->snakeCase( $rule );
|
222 |
+
$type = $this->getAttributeType( $attribute );
|
223 |
+
|
224 |
+
$lowerRule .= ".{$type}";
|
225 |
+
|
226 |
+
return $this->translator( $lowerRule, $rule, $attribute, $parameters );
|
227 |
+
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* Get the value of a given attribute.
|
231 |
+
*
|
232 |
+
* @param string $attribute
|
233 |
+
*
|
234 |
+
* @return mixed
|
235 |
+
*/
|
236 |
+
protected function getValue( $attribute )
|
237 |
+
{
|
238 |
+
if( isset( $this->data[ $attribute ] ) ) {
|
239 |
+
return $this->data[ $attribute ];
|
240 |
+
}
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* Determine if the given attribute has a rule in the given set.
|
245 |
+
*
|
246 |
+
* @param string $attribute
|
247 |
+
* @param string|array $rules
|
248 |
+
*
|
249 |
+
* @return bool
|
250 |
+
*/
|
251 |
+
protected function hasRule( $attribute, $rules )
|
252 |
+
{
|
253 |
+
return !is_null( $this->getRule( $attribute, $rules ) );
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Normalize the provided data to an array.
|
258 |
+
*
|
259 |
+
* @param mixed $data
|
260 |
+
*
|
261 |
+
* @return $this
|
262 |
+
*/
|
263 |
+
protected function normalizeData( $data )
|
264 |
+
{
|
265 |
+
// If an object was provided, get its public properties
|
266 |
+
if( is_object( $data ) ) {
|
267 |
+
$this->data = get_object_vars( $data );
|
268 |
+
}
|
269 |
+
else {
|
270 |
+
$this->data = $data;
|
271 |
+
}
|
272 |
+
|
273 |
+
return $this;
|
274 |
+
}
|
275 |
+
|
276 |
+
/**
|
277 |
+
* Parse a parameter list.
|
278 |
+
*
|
279 |
+
* @param string $rule
|
280 |
+
* @param string $parameter
|
281 |
+
*
|
282 |
+
* @return array
|
283 |
+
*/
|
284 |
+
protected function parseParameters( $rule, $parameter )
|
285 |
+
{
|
286 |
+
if( strtolower( $rule ) == 'regex' ) {
|
287 |
+
return [ $parameter ];
|
288 |
+
}
|
289 |
+
|
290 |
+
return str_getcsv( $parameter );
|
291 |
+
}
|
292 |
+
|
293 |
+
/**
|
294 |
+
* Extract the rule name and parameters from a rule.
|
295 |
+
*
|
296 |
+
* @param string $rule
|
297 |
+
*
|
298 |
+
* @return array
|
299 |
+
*/
|
300 |
+
protected function parseRule( $rule )
|
301 |
+
{
|
302 |
+
$parameters = [];
|
303 |
+
|
304 |
+
// example: {rule}:{parameters}
|
305 |
+
if( strpos( $rule, ':' ) !== false ) {
|
306 |
+
list( $rule, $parameter ) = explode( ':', $rule, 2 );
|
307 |
+
|
308 |
+
// example: {parameter1,parameter2,...}
|
309 |
+
$parameters = $this->parseParameters( $rule, $parameter );
|
310 |
+
}
|
311 |
+
|
312 |
+
$rule = ucwords( str_replace( ['-', '_'], ' ', trim( $rule ) ) );
|
313 |
+
$rule = str_replace( ' ', '', $rule );
|
314 |
+
|
315 |
+
return [ $rule, $parameters ];
|
316 |
+
}
|
317 |
+
|
318 |
+
/**
|
319 |
+
* Replace all placeholders for the between rule.
|
320 |
+
*
|
321 |
+
* @param string $message
|
322 |
+
*
|
323 |
+
* @return string
|
324 |
+
*/
|
325 |
+
protected function replaceBetween( $message, array $parameters )
|
326 |
+
{
|
327 |
+
return str_replace([':min', ':max'], $parameters, $message );
|
328 |
+
}
|
329 |
+
|
330 |
+
/**
|
331 |
+
* Replace all placeholders for the max rule.
|
332 |
+
*
|
333 |
+
* @param string $message
|
334 |
+
*
|
335 |
+
* @return string
|
336 |
+
*/
|
337 |
+
protected function replaceMax( $message, array $parameters )
|
338 |
+
{
|
339 |
+
return str_replace( ':max', $parameters[0], $message );
|
340 |
+
}
|
341 |
+
|
342 |
+
/**
|
343 |
+
* Replace all placeholders for the min rule.
|
344 |
+
*
|
345 |
+
* @param string $message
|
346 |
+
*
|
347 |
+
* @return string
|
348 |
+
*/
|
349 |
+
protected function replaceMin( $message, array $parameters )
|
350 |
+
{
|
351 |
+
return str_replace( ':min', $parameters[0], $message );
|
352 |
+
}
|
353 |
+
|
354 |
+
/**
|
355 |
+
* Require a certain number of parameters to be present.
|
356 |
+
*
|
357 |
+
* @param int $count
|
358 |
+
* @param string $rule
|
359 |
+
*
|
360 |
+
* @return void
|
361 |
+
* @throws InvalidArgumentException
|
362 |
+
*/
|
363 |
+
protected function requireParameterCount( $count, array $parameters, $rule )
|
364 |
+
{
|
365 |
+
if( count( $parameters ) < $count ) {
|
366 |
+
throw new InvalidArgumentException( "Validation rule $rule requires at least $count parameters." );
|
367 |
+
}
|
368 |
+
}
|
369 |
+
|
370 |
+
/**
|
371 |
+
* Set the validation rules.
|
372 |
+
*
|
373 |
+
* @return $this
|
374 |
+
*/
|
375 |
+
protected function setRules( array $rules )
|
376 |
+
{
|
377 |
+
foreach( $rules as $key => $rule ) {
|
378 |
+
$rules[ $key ] = is_string( $rule ) ? explode( '|', $rule ) : $rule;
|
379 |
+
}
|
380 |
+
|
381 |
+
$this->rules = $rules;
|
382 |
+
|
383 |
+
return $this;
|
384 |
+
}
|
385 |
+
|
386 |
+
/**
|
387 |
+
* Check if we should stop further validations on a given attribute.
|
388 |
+
*
|
389 |
+
* @param string $attribute
|
390 |
+
*
|
391 |
+
* @return bool
|
392 |
+
*/
|
393 |
+
protected function shouldStopValidating( $attribute )
|
394 |
+
{
|
395 |
+
return $this->hasRule( $attribute, $this->implicitRules )
|
396 |
+
&& isset( $this->failedRules[ $attribute ] )
|
397 |
+
&& array_intersect( array_keys( $this->failedRules[ $attribute ] ), $this->implicitRules );
|
398 |
+
}
|
399 |
+
|
400 |
+
/**
|
401 |
+
* Convert a string to snake case.
|
402 |
+
*
|
403 |
+
* @param string $string
|
404 |
+
*
|
405 |
+
* @return string
|
406 |
+
*/
|
407 |
+
protected function snakeCase( $string )
|
408 |
+
{
|
409 |
+
if( !ctype_lower( $string ) ) {
|
410 |
+
$string = preg_replace( '/\s+/u', '', $string );
|
411 |
+
$string = preg_replace( '/(.)(?=[A-Z])/u', '$1_', $string );
|
412 |
+
$string = mb_strtolower( $string, 'UTF-8' );
|
413 |
+
}
|
414 |
+
|
415 |
+
return $string;
|
416 |
+
}
|
417 |
+
|
418 |
+
/**
|
419 |
+
* Returns a translated message for the attribute
|
420 |
+
*
|
421 |
+
* @param string $key
|
422 |
+
* @param string $rule
|
423 |
+
* @param string $attribute
|
424 |
+
*
|
425 |
+
* @return string|null
|
426 |
+
*/
|
427 |
+
protected function translator( $key, $rule, $attribute, array $parameters )
|
428 |
+
{
|
429 |
+
$strings = (new Strings)->validation();
|
430 |
+
|
431 |
+
$message = isset( $strings[ $key ] )
|
432 |
+
? $strings[ $key ]
|
433 |
+
: false;
|
434 |
+
|
435 |
+
if( !$message )return;
|
436 |
+
|
437 |
+
$message = str_replace( ':attribute', $attribute, $message );
|
438 |
+
|
439 |
+
if( method_exists( $this, $replacer = "replace{$rule}" ) ) {
|
440 |
+
$message = $this->$replacer( $message, $parameters );
|
441 |
+
}
|
442 |
+
|
443 |
+
return $message;
|
444 |
+
}
|
445 |
+
|
446 |
+
// Rules Validation
|
447 |
+
// ---------------------------------------------------------------------------------------------
|
448 |
+
|
449 |
+
/**
|
450 |
+
* Validate that an attribute was "accepted".
|
451 |
+
*
|
452 |
+
* This validation rule implies the attribute is "required".
|
453 |
+
*
|
454 |
+
* @param string $attribute
|
455 |
+
* @param mixed $value
|
456 |
+
*
|
457 |
+
* @return bool
|
458 |
+
*/
|
459 |
+
protected function validateAccepted( $attribute, $value )
|
460 |
+
{
|
461 |
+
$acceptable = ['yes', 'on', '1', 1, true, 'true'];
|
462 |
+
|
463 |
+
return $this->validateRequired( $attribute, $value ) && in_array( $value, $acceptable, true );
|
464 |
+
}
|
465 |
+
|
466 |
+
/**
|
467 |
+
* Validate a given attribute against a rule.
|
468 |
+
*
|
469 |
+
* @param string $attribute
|
470 |
+
* @param string $rule
|
471 |
+
*
|
472 |
+
* @return void
|
473 |
+
* @throws BadMethodCallException
|
474 |
+
*/
|
475 |
+
protected function validateAttribute( $attribute, $rule )
|
476 |
+
{
|
477 |
+
list( $rule, $parameters ) = $this->parseRule( $rule );
|
478 |
+
|
479 |
+
if( $rule == '' )return;
|
480 |
+
|
481 |
+
$value = $this->getValue( $attribute );
|
482 |
+
|
483 |
+
// is the value filled or is the attribute required?
|
484 |
+
// - removed $validatable assignment
|
485 |
+
$this->validateRequired( $attribute, $value ) || in_array( $rule, $this->implicitRules );
|
486 |
+
|
487 |
+
$method = "validate{$rule}";
|
488 |
+
|
489 |
+
if( !method_exists( $this, $method ) ) {
|
490 |
+
throw new BadMethodCallException( "Method [$method] does not exist." );
|
491 |
+
}
|
492 |
+
|
493 |
+
if( !$this->$method( $attribute, $value, $parameters ) ) {
|
494 |
+
$this->addFailure( $attribute, $rule, $parameters );
|
495 |
+
}
|
496 |
+
}
|
497 |
+
|
498 |
+
/**
|
499 |
+
* Validate the size of an attribute is between a set of values.
|
500 |
+
*
|
501 |
+
* @param string $attribute
|
502 |
+
* @param mixed $value
|
503 |
+
*
|
504 |
+
* @return bool
|
505 |
+
*/
|
506 |
+
protected function validateBetween( $attribute, $value, array $parameters )
|
507 |
+
{
|
508 |
+
$this->requireParameterCount( 2, $parameters, 'between' );
|
509 |
+
|
510 |
+
$size = $this->getSize( $attribute, $value );
|
511 |
+
|
512 |
+
return $size >= $parameters[0] && $size <= $parameters[1];
|
513 |
+
}
|
514 |
+
|
515 |
+
/**
|
516 |
+
* Validate that an attribute is a valid e-mail address.
|
517 |
+
*
|
518 |
+
* @param string $attribute
|
519 |
+
* @param mixed $value
|
520 |
+
*
|
521 |
+
* @return bool
|
522 |
+
*/
|
523 |
+
protected function validateEmail( $attribute, $value )
|
524 |
+
{
|
525 |
+
return filter_var( $value, FILTER_VALIDATE_EMAIL ) !== false;
|
526 |
+
}
|
527 |
+
|
528 |
+
/**
|
529 |
+
* Validate the size of an attribute is less than a maximum value.
|
530 |
+
*
|
531 |
+
* @param string $attribute
|
532 |
+
* @param mixed $value
|
533 |
+
*
|
534 |
+
* @return bool
|
535 |
+
*/
|
536 |
+
protected function validateMax( $attribute, $value, array $parameters )
|
537 |
+
{
|
538 |
+
$this->requireParameterCount( 1, $parameters, 'max' );
|
539 |
+
|
540 |
+
return $this->getSize( $attribute, $value ) <= $parameters[0];
|
541 |
+
}
|
542 |
+
|
543 |
+
/**
|
544 |
+
* Validate the size of an attribute is greater than a minimum value.
|
545 |
+
*
|
546 |
+
* @param string $attribute
|
547 |
+
* @param mixed $value
|
548 |
+
*
|
549 |
+
* @return bool
|
550 |
+
*/
|
551 |
+
protected function validateMin( $attribute, $value, array $parameters )
|
552 |
+
{
|
553 |
+
$this->requireParameterCount( 1, $parameters, 'min' );
|
554 |
+
|
555 |
+
return $this->getSize( $attribute, $value ) >= $parameters[0];
|
556 |
+
}
|
557 |
+
|
558 |
+
/**
|
559 |
+
* Validate that an attribute is numeric.
|
560 |
+
*
|
561 |
+
* @param string $attribute
|
562 |
+
* @param mixed $value
|
563 |
+
*
|
564 |
+
* @return bool
|
565 |
+
*/
|
566 |
+
protected function validateNumeric( $attribute, $value )
|
567 |
+
{
|
568 |
+
return is_numeric( $value );
|
569 |
+
}
|
570 |
+
|
571 |
+
/**
|
572 |
+
* Validate that a required attribute exists.
|
573 |
+
*
|
574 |
+
* @param string $attribute
|
575 |
+
* @param mixed $value
|
576 |
+
*
|
577 |
+
* @return bool
|
578 |
+
*/
|
579 |
+
protected function validateRequired( $attribute, $value )
|
580 |
+
{
|
581 |
+
return is_null( $value )
|
582 |
+
|| ( is_string( $value ) && trim( $value ) === '' )
|
583 |
+
|| ( is_array( $value ) && count( $value ) < 1 )
|
584 |
+
? false
|
585 |
+
: true;
|
586 |
+
}
|
587 |
+
}
|
plugin/Widget.php
ADDED
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Widget Boilerplate
|
5 |
+
*
|
6 |
+
* @package GeminiLabs\SiteReviews
|
7 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
8 |
+
* @license GPLv2 or later
|
9 |
+
* @since 1.0.0
|
10 |
+
* -------------------------------------------------------------------------------------------------
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace GeminiLabs\SiteReviews;
|
14 |
+
|
15 |
+
use WP_Widget;
|
16 |
+
|
17 |
+
abstract class Widget extends WP_Widget
|
18 |
+
{
|
19 |
+
public function __construct( $id_base, $name, $values )
|
20 |
+
{
|
21 |
+
$control_options = $widget_options = [];
|
22 |
+
|
23 |
+
if( isset( $values['class'] ) ) {
|
24 |
+
$widget_options['classname'] = $values['class'];
|
25 |
+
}
|
26 |
+
|
27 |
+
if( isset( $values['description'] ) ) {
|
28 |
+
$widget_options['description'] = $values['description'];
|
29 |
+
}
|
30 |
+
|
31 |
+
if( isset( $values['width'] ) ) {
|
32 |
+
$control_options['width'] = $values['width'];
|
33 |
+
}
|
34 |
+
|
35 |
+
parent::__construct( $id_base, $name, $widget_options, $control_options );
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Display the widget form
|
40 |
+
* Subclasses should over-ride this function to generate the widget form
|
41 |
+
*
|
42 |
+
* @param array $instance
|
43 |
+
*
|
44 |
+
* @return void
|
45 |
+
*/
|
46 |
+
public function form( $instance )
|
47 |
+
{
|
48 |
+
parent::form( $instance );
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Update the widget form
|
53 |
+
*
|
54 |
+
* @param array $new_instance
|
55 |
+
* @param array $old_instance
|
56 |
+
*
|
57 |
+
* @return array
|
58 |
+
*/
|
59 |
+
public function update( $new_instance, $old_instance )
|
60 |
+
{
|
61 |
+
return parent::update( $new_instance, $old_instance );
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Display the widget Html
|
66 |
+
* Subclasses should over-ride this function to generate the widget
|
67 |
+
*
|
68 |
+
* @param array $args
|
69 |
+
* @param array $instance
|
70 |
+
*
|
71 |
+
* @return void
|
72 |
+
*/
|
73 |
+
public function widget( $args, $instance )
|
74 |
+
{
|
75 |
+
parent::widget( $args, $instance );
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Create a widget form field
|
80 |
+
*
|
81 |
+
* @return void
|
82 |
+
*/
|
83 |
+
protected function create_field( array $atts = [] )
|
84 |
+
{
|
85 |
+
// don't prefix the name field
|
86 |
+
$atts['prefix'] = false;
|
87 |
+
$atts['name'] = $this->get_field_name( $atts['name'] );
|
88 |
+
|
89 |
+
if( isset( $atts['depends'] ) ) {
|
90 |
+
$atts['depends'] = $this->get_field_name( $atts['depends'] );
|
91 |
+
}
|
92 |
+
|
93 |
+
if( !isset( $atts['type'] ) || $atts['type'] == 'text' ) {
|
94 |
+
$atts['class'] = isset( $atts['class'] ) ? trim( "{$atts['class']} widefat" ) : 'widefat';
|
95 |
+
}
|
96 |
+
|
97 |
+
glsr_resolve( 'Html' )->renderField( $atts, 'echo' );
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Generate a unique ID string
|
102 |
+
*
|
103 |
+
* @param mixed $from
|
104 |
+
*
|
105 |
+
* @return string
|
106 |
+
*/
|
107 |
+
protected function generate_id( $from = [] )
|
108 |
+
{
|
109 |
+
!empty( $from ) ?: $from = $this->id;
|
110 |
+
|
111 |
+
return substr( md5( serialize( $from ) ), 0, 8 );
|
112 |
+
}
|
113 |
+
}
|
plugin/Widgets/RecentReviews.php
ADDED
@@ -0,0 +1,184 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Recent Reviews widget
|
5 |
+
*
|
6 |
+
* @package GeminiLabs\SiteReviews
|
7 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
8 |
+
* @license GPLv2 or later
|
9 |
+
* @since 1.0.0
|
10 |
+
* -------------------------------------------------------------------------------------------------
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace GeminiLabs\SiteReviews\Widgets;
|
14 |
+
|
15 |
+
use GeminiLabs\SiteReviews\Widget;
|
16 |
+
|
17 |
+
class RecentReviews extends Widget
|
18 |
+
{
|
19 |
+
/**
|
20 |
+
* Display the widget form
|
21 |
+
*
|
22 |
+
* @param array $instance
|
23 |
+
*
|
24 |
+
* @return void
|
25 |
+
*/
|
26 |
+
public function form( $instance )
|
27 |
+
{
|
28 |
+
$defaults = [
|
29 |
+
'class' => '',
|
30 |
+
'display' => 'both',
|
31 |
+
'max_reviews' => 5,
|
32 |
+
'min_rating' => '',
|
33 |
+
'order_by' => '',
|
34 |
+
'show' => ['show_author', 'show_date', 'show_rating'],
|
35 |
+
'title' => '',
|
36 |
+
'type' => '',
|
37 |
+
];
|
38 |
+
|
39 |
+
if( !empty( $instance ) ) {
|
40 |
+
isset( $instance['show'] ) ?: $instance['show'] = [];
|
41 |
+
}
|
42 |
+
|
43 |
+
$args = shortcode_atts( $defaults, $instance );
|
44 |
+
|
45 |
+
$this->create_field([
|
46 |
+
'type' => 'text',
|
47 |
+
'name' => 'title',
|
48 |
+
'label' => __( 'Title', 'site-reviews' ),
|
49 |
+
'value' => $args['title'],
|
50 |
+
]);
|
51 |
+
|
52 |
+
$this->create_field([
|
53 |
+
'type' => 'select',
|
54 |
+
'name' => 'display',
|
55 |
+
'class' => 'widefat',
|
56 |
+
'value' => $args['display'],
|
57 |
+
'options' => [
|
58 |
+
'title' => __( 'Display only title', 'site-reviews' ),
|
59 |
+
'excerpt' => __( 'Display only excerpt', 'site-reviews' ),
|
60 |
+
'both' => __( 'Display title and excerpt', 'site-reviews' ),
|
61 |
+
],
|
62 |
+
]);
|
63 |
+
|
64 |
+
$types = glsr_resolve( 'Database' )->getReviewTypes();
|
65 |
+
|
66 |
+
if( count( $types ) > 1 ) {
|
67 |
+
$this->create_field([
|
68 |
+
'type' => 'select',
|
69 |
+
'name' => 'type',
|
70 |
+
'label' => __( 'Which reviews would you like to display? ', 'site-reviews' ),
|
71 |
+
'value' => $args['type'],
|
72 |
+
'options' => ['' => __( 'All Reviews', 'site-reviews' ) ] + $types,
|
73 |
+
]);
|
74 |
+
}
|
75 |
+
|
76 |
+
$this->create_field([
|
77 |
+
'type' => 'select',
|
78 |
+
'name' => 'min_rating',
|
79 |
+
'label' => __( 'What is the minimum rating to display? ', 'site-reviews' ),
|
80 |
+
'value' => $args['min_rating'],
|
81 |
+
'options' => [
|
82 |
+
'5' => __( '5 stars', 'site-reviews' ),
|
83 |
+
'4' => __( '4 stars', 'site-reviews' ),
|
84 |
+
'3' => __( '3 stars', 'site-reviews' ),
|
85 |
+
'2' => __( '2 stars', 'site-reviews' ),
|
86 |
+
'1' => __( '1 star', 'site-reviews' ),
|
87 |
+
],
|
88 |
+
]);
|
89 |
+
|
90 |
+
$this->create_field([
|
91 |
+
'type' => 'number',
|
92 |
+
'name' => 'max_reviews',
|
93 |
+
'label' => __( 'How many reviews would you like to display? ', 'site-reviews' ),
|
94 |
+
'value' => $args['max_reviews'],
|
95 |
+
'default' => 5,
|
96 |
+
'max' => 100,
|
97 |
+
]);
|
98 |
+
|
99 |
+
$this->create_field([
|
100 |
+
'type' => 'checkbox',
|
101 |
+
'name' => 'show',
|
102 |
+
'value' => $args['show'],
|
103 |
+
'options' => [
|
104 |
+
'show_author' => __( 'Show the name of the reviewer?', 'site-reviews' ),
|
105 |
+
'show_date' => __( 'Show the review date?', 'site-reviews' ),
|
106 |
+
'show_rating' => __( 'Show the review rating?', 'site-reviews' ),
|
107 |
+
],
|
108 |
+
]);
|
109 |
+
|
110 |
+
$this->create_field([
|
111 |
+
'type' => 'text',
|
112 |
+
'name' => 'class',
|
113 |
+
'label' => __( 'Enter any custom CSS classes here', 'site-reviews' ),
|
114 |
+
'value' => $args['class'],
|
115 |
+
]);
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Update the widget form
|
120 |
+
*
|
121 |
+
* @param array $new_instance
|
122 |
+
* @param array $old_instance
|
123 |
+
*
|
124 |
+
* @return array
|
125 |
+
*/
|
126 |
+
public function update( $new_instance, $old_instance )
|
127 |
+
{
|
128 |
+
if( $new_instance['max_reviews'] < 0 ) {
|
129 |
+
$new_instance['max_reviews'] = 0;
|
130 |
+
}
|
131 |
+
|
132 |
+
if( $new_instance['max_reviews'] > 100 ) {
|
133 |
+
$new_instance['max_reviews'] = 100;
|
134 |
+
}
|
135 |
+
|
136 |
+
return parent::update( $new_instance, $old_instance );
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* Display the widget Html
|
141 |
+
*
|
142 |
+
* @param array $args
|
143 |
+
* @param array $instance
|
144 |
+
*
|
145 |
+
* @return void
|
146 |
+
*/
|
147 |
+
public function widget( $args, $instance )
|
148 |
+
{
|
149 |
+
$defaults = [
|
150 |
+
'class' => '',
|
151 |
+
'display' => 'both',
|
152 |
+
'max_reviews' => '10',
|
153 |
+
'min_rating' => '5',
|
154 |
+
'order_by' => 'date',
|
155 |
+
'show' => [],
|
156 |
+
'title' => '',
|
157 |
+
'type' => '',
|
158 |
+
];
|
159 |
+
|
160 |
+
$instance = shortcode_atts( $defaults, $instance );
|
161 |
+
|
162 |
+
foreach( $instance['show'] as $key ) {
|
163 |
+
$instance[ $key ] = true;
|
164 |
+
}
|
165 |
+
|
166 |
+
unset( $instance['show'] );
|
167 |
+
|
168 |
+
$instance['order_by'] ?: $instance['order_by'] = 'date';
|
169 |
+
|
170 |
+
$instance['site_name'] = $instance['type'];
|
171 |
+
|
172 |
+
unset( $instance['type'] );
|
173 |
+
|
174 |
+
$title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
|
175 |
+
|
176 |
+
echo $args['before_widget'];
|
177 |
+
|
178 |
+
echo $title ? $args['before_title'] . $title . $args['after_title'] : '';
|
179 |
+
|
180 |
+
glsr_resolve( 'Html' )->renderPartial( 'reviews', $instance );
|
181 |
+
|
182 |
+
echo $args['after_widget'];
|
183 |
+
}
|
184 |
+
}
|
plugin/Widgets/ReviewsForm.php
ADDED
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Reviews Form widget
|
5 |
+
*
|
6 |
+
* @package GeminiLabs\SiteReviews
|
7 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
8 |
+
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
9 |
+
* @since 1.0.0
|
10 |
+
* -------------------------------------------------------------------------------------------------
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace GeminiLabs\SiteReviews\Widgets;
|
14 |
+
|
15 |
+
use GeminiLabs\SiteReviews\Widget;
|
16 |
+
|
17 |
+
class ReviewsForm extends Widget
|
18 |
+
{
|
19 |
+
/**
|
20 |
+
* Display the widget form
|
21 |
+
*
|
22 |
+
* @param array $instance
|
23 |
+
*
|
24 |
+
* @return void
|
25 |
+
*/
|
26 |
+
public function form( $instance )
|
27 |
+
{
|
28 |
+
$defaults = [
|
29 |
+
'class' => '',
|
30 |
+
'description' => sprintf( __( 'Your email address will not be published. Required fields are marked %s*%s', 'site-reviews' ), '<span>', '</span>' ),
|
31 |
+
'fields' => [],
|
32 |
+
'title' => '',
|
33 |
+
];
|
34 |
+
|
35 |
+
$args = shortcode_atts( $defaults, $instance );
|
36 |
+
|
37 |
+
$this->create_field([
|
38 |
+
'type' => 'text',
|
39 |
+
'name' => 'title',
|
40 |
+
'label' => __( 'Title', 'site-reviews' ),
|
41 |
+
'value' => $args['title'],
|
42 |
+
]);
|
43 |
+
|
44 |
+
$this->create_field([
|
45 |
+
'type' => 'textarea',
|
46 |
+
'name' => 'description',
|
47 |
+
'class' => 'widefat',
|
48 |
+
'label' => __( 'Description', 'site-reviews' ),
|
49 |
+
'value' => $args['description'],
|
50 |
+
]);
|
51 |
+
|
52 |
+
$this->create_field([
|
53 |
+
'type' => 'checkbox',
|
54 |
+
'name' => 'fields',
|
55 |
+
'value' => $args['fields'],
|
56 |
+
'options' => [
|
57 |
+
'title' => __( 'Hide the title field', 'site-reviews' ),
|
58 |
+
'reviewer' => __( 'Hide the reviewer field', 'site-reviews' ),
|
59 |
+
'email' => __( 'Hide the email field', 'site-reviews' ),
|
60 |
+
'terms' => __( 'Hide the terms field', 'site-reviews' ),
|
61 |
+
],
|
62 |
+
]);
|
63 |
+
|
64 |
+
$this->create_field([
|
65 |
+
'type' => 'text',
|
66 |
+
'name' => 'class',
|
67 |
+
'label' => __( 'Enter any custom CSS classes here', 'site-reviews' ),
|
68 |
+
'value' => $args['class'],
|
69 |
+
]);
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Display the widget Html
|
74 |
+
*
|
75 |
+
* @param array $args
|
76 |
+
* @param array $instance
|
77 |
+
*
|
78 |
+
* @return void
|
79 |
+
*/
|
80 |
+
public function widget( $args, $instance )
|
81 |
+
{
|
82 |
+
$defaults = [
|
83 |
+
'class' => '',
|
84 |
+
'description' => '',
|
85 |
+
'fields' => [],
|
86 |
+
'title' => '',
|
87 |
+
];
|
88 |
+
|
89 |
+
// custom widget attributes
|
90 |
+
$instance = shortcode_atts( $defaults, $instance );
|
91 |
+
|
92 |
+
$controller = glsr_resolve( 'Controllers\ReviewController' );
|
93 |
+
$session = glsr_resolve( 'Session' );
|
94 |
+
|
95 |
+
$defaults = [
|
96 |
+
'content' => '',
|
97 |
+
'email' => '',
|
98 |
+
'rating' => '',
|
99 |
+
'reviewer' => '',
|
100 |
+
'terms' => '',
|
101 |
+
'title' => '',
|
102 |
+
];
|
103 |
+
|
104 |
+
$formId = $this->generate_id();
|
105 |
+
|
106 |
+
$errors = $session->get( "{$formId}-errors", [], 'and then remove errors' );
|
107 |
+
$message = $session->get( "{$formId}-message", [], 'and then remove message' );
|
108 |
+
|
109 |
+
$values = !empty( $errors )
|
110 |
+
? $this->session->get( "{$formId}-values", [], 'and then remove values' )
|
111 |
+
: [];
|
112 |
+
|
113 |
+
$title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
|
114 |
+
|
115 |
+
$description = apply_filters( 'widget_description', $instance['description'], $instance, $this->id_base );
|
116 |
+
|
117 |
+
echo $args['before_widget'];
|
118 |
+
|
119 |
+
echo $title ? $args['before_title'] . $title . $args['after_title'] : '';
|
120 |
+
|
121 |
+
$requireUser = glsr_resolve( 'Database' )->getOption( 'general.require.login', false );
|
122 |
+
|
123 |
+
if( $requireUser && !is_user_logged_in() ) {
|
124 |
+
$message = sprintf(
|
125 |
+
__( 'You must be <a href="%s">logged in</a> to submit a review.', 'site-reviews' ),
|
126 |
+
wp_login_url( get_permalink() )
|
127 |
+
);
|
128 |
+
echo wpautop( $message );
|
129 |
+
return;
|
130 |
+
}
|
131 |
+
else {
|
132 |
+
echo $description ? sprintf( '<p class="glsr-form-description">%s</p>', $description ) : '';
|
133 |
+
|
134 |
+
$controller->render( 'submit/index', [
|
135 |
+
'class' => trim( 'glsr-submit-review-form ' . $instance['class'] ),
|
136 |
+
'errors' => $errors,
|
137 |
+
'exclude' => $instance['fields'],
|
138 |
+
'form_id' => $formId,
|
139 |
+
'message' => $message,
|
140 |
+
'values' => shortcode_atts( $defaults, $values ),
|
141 |
+
]);
|
142 |
+
}
|
143 |
+
|
144 |
+
echo $args['after_widget'];
|
145 |
+
}
|
146 |
+
}
|
readme.txt
ADDED
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
=== Site Reviews ===
|
2 |
+
Contributors: geminilabs, pryley
|
3 |
+
Donate link: http://geminilabs.io/donate
|
4 |
+
Tags: best reviews, business ratings, business reviews, curated reviews, moderated reviews, rating widget, rating, ratings shortcode, review widget, reviews login, reviews shortcode, reviews, simple reviews, site reviews, star rating, star review, submit review, testimonial, user rating, user review, user reviews, wp rating, wp review, wp testimonials
|
5 |
+
Requires at least: 4.0.0
|
6 |
+
Tested up to: 4.7
|
7 |
+
Stable tag: 1.2.2
|
8 |
+
License: GPLv2 or later
|
9 |
+
License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
10 |
+
|
11 |
+
Site Reviews is a WordPress plugin which allows you to easily receive and display reviews for your business or website.
|
12 |
+
|
13 |
+
== Description ==
|
14 |
+
|
15 |
+
Site Reviews is a plugin that allows your visitors to submit site reviews with a 1-5 star rating on your website, similar to the way you would on TripAdvisor or Yelp, and display them using a widget or shortcode.
|
16 |
+
|
17 |
+
You can pin your best reviews so that they are always shown first, require approval before new review submissions are published, require visitors to be logged-in in order to write a review, send custom notifications on a new submission, and more. The plugin provides both widgets and shortcodes along with full shortcode documentation.
|
18 |
+
|
19 |
+
Add-ons will be released shortly that allow you to sync your TripAdvisor and Yelp reviews in order to display them locally on your own website.
|
20 |
+
|
21 |
+
The plugin [roadmap](https://github.com/geminilabs/site-reviews/blob/develop/ROADMAP.md) defines the upcoming features which include support for custom review categories, webhook notifications, paginated reviews, allowing visitors to up/down vote submitted reviews, reviewer avatars, percentage/fraction ratings, and more.
|
22 |
+
|
23 |
+
Follow plugin development on github at: https://github.com/geminilabs/site-reviews/
|
24 |
+
|
25 |
+
= Current Features =
|
26 |
+
|
27 |
+
* [new] Webhook notifications for Slack
|
28 |
+
* Actively developed and supported
|
29 |
+
* Clean and easy-to-configure user interface
|
30 |
+
* Configurable Widgets
|
31 |
+
* Easy setup and implementation
|
32 |
+
* Filter reviews by rating
|
33 |
+
* Logging
|
34 |
+
* Minimal widget styling (tested with all official WP themes)
|
35 |
+
* Review Pagination
|
36 |
+
* Shortcodes: Display reviews in your post content and templates
|
37 |
+
* WordPress.org support
|
38 |
+
* WP Filter Hooks
|
39 |
+
|
40 |
+
== Installation ==
|
41 |
+
|
42 |
+
= Minimum plugin requirements =
|
43 |
+
|
44 |
+
* WordPress 4.0.0
|
45 |
+
* PHP 5.4
|
46 |
+
|
47 |
+
= Automatic installation =
|
48 |
+
|
49 |
+
Log in to your WordPress dashboard, navigate to the Plugins menu and click "Add New".
|
50 |
+
|
51 |
+
In the search field type "Site Reviews" and click Search Plugins. Once you have found the plugin you can view details about it such as the point release, rating and description. You can install it by simply clicking "Install Now".
|
52 |
+
|
53 |
+
= Manual installation =
|
54 |
+
|
55 |
+
Download the Site Reviews plugin and uploading it to your server via your favorite FTP application. The WordPress codex contains [instructions on how to do this here](https://codex.wordpress.org/Managing_Plugins#Manual_Plugin_Installation).
|
56 |
+
|
57 |
+
== Frequently Asked Questions ==
|
58 |
+
|
59 |
+
= I don't want form placeholders. How do I remove them? =
|
60 |
+
|
61 |
+
To remove the form placeholders, go to "Settings" -> "Submission Form" and replace all placeholder text with a single space character. Placeholders no more!
|
62 |
+
|
63 |
+
= How do I disable the plugin CSS and/or Javascript? =
|
64 |
+
|
65 |
+
To disable the plugin stylesheet or javascript from loading on your website, copy and paste the following WordPress Filter Hooks into your theme's functions.php file:
|
66 |
+
|
67 |
+
`add_filter( 'site-reviews/assets/css', '__return_false' );`
|
68 |
+
`add_filter( 'site-reviews/assets/js', '__return_false' );`
|
69 |
+
|
70 |
+
= The widgets look funny in my sidebar. What's happening? =
|
71 |
+
|
72 |
+
Some themes may have very small sidebars and/or CSS styles that conflict or alter the styles within Site Reviews. To correct any styling errors you can either disable the plugin's CSS altogether, or override the CSS selectors in use to make the widget or shortcode appear how you'd like. CSS-related issues are not actively supported as there are too many variations between the thousands of WordPress themes available.
|
73 |
+
|
74 |
+
== Screenshots ==
|
75 |
+
|
76 |
+
1. A view of the All Reviews page
|
77 |
+
|
78 |
+
2. A view of the Edit Review page
|
79 |
+
|
80 |
+
3. A view of the Site Reviews > Settings page
|
81 |
+
|
82 |
+
4. A view of the Site Reviews > Get Help > Documentation tab
|
83 |
+
|
84 |
+
5. A view of the Site Reviews > Get Help > System Info tab
|
85 |
+
|
86 |
+
6. A view of the Site Reviews > Add-Ons page
|
87 |
+
|
88 |
+
7. A view of the Recent Site Reviews widget settings
|
89 |
+
|
90 |
+
8. A view of the Submit a Site Review widget settings
|
91 |
+
|
92 |
+
9. How the Recent Site Reviews widget looks like using the Twenty Sixteen WordPress theme
|
93 |
+
|
94 |
+
10. How the Submit a Site Review widget looks like using the Twenty Sixteen WordPress theme
|
95 |
+
|
96 |
+
== Changelog ==
|
97 |
+
|
98 |
+
= 1.2.2 (2017-01-06) =
|
99 |
+
|
100 |
+
* Added hook to change excerpt length
|
101 |
+
* Added hook to disable the excerpt and instead display the full review content
|
102 |
+
|
103 |
+
= 1.2.1 (2016-12-28) =
|
104 |
+
|
105 |
+
* Fix PHP 5.4 compatibility regression
|
106 |
+
|
107 |
+
= 1.2.0 (2016-12-27) =
|
108 |
+
|
109 |
+
* [feature] Send notifications to Slack
|
110 |
+
* Fix notifications to use the email template setting
|
111 |
+
|
112 |
+
= 1.1.1 (2016-12-05) =
|
113 |
+
|
114 |
+
* Remove ".star-rating" class on frontend which conflicts with the woocommerce plugin CSS
|
115 |
+
* Added hooks to modify rendered fields/partials HTML
|
116 |
+
|
117 |
+
= 1.1.0 (2016-11-16) =
|
118 |
+
|
119 |
+
* [feature] Pagination
|
120 |
+
* [breaking] Changed internal widget hook names
|
121 |
+
* [breaking] Changed text-domain to "site-reviews"
|
122 |
+
* Set post_meta defaults when creating a review
|
123 |
+
* [addon support] Display read-only reviews
|
124 |
+
* [addon support] Display widget link options (conditional field)
|
125 |
+
* [addon support] Show all review types by default in widgets and shortcodes
|
126 |
+
|
127 |
+
= 1.0.4 (2016-11-14) =
|
128 |
+
|
129 |
+
* use the logged-in user's display_name by default instead of "Anonymous" when submitting reviews
|
130 |
+
* Fix shortcodes to insert in the_content correctly
|
131 |
+
|
132 |
+
= 1.0.3 (2016-11-09) =
|
133 |
+
|
134 |
+
* Updated plugin description
|
135 |
+
* Fix plain-text emails
|
136 |
+
* Fix inconsistencies with plugin settings form fields
|
137 |
+
* Fix internal add-on integration code
|
138 |
+
|
139 |
+
= 1.0.2 (2016-10-24) =
|
140 |
+
|
141 |
+
* Set widget and settings defaults
|
142 |
+
* Fix PHP error that is thrown when settings have not yet been saved to DB
|
143 |
+
|
144 |
+
= 1.0.0 (2016-10-21) =
|
145 |
+
|
146 |
+
* Initial plugin release
|
site-reviews.php
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* ╔═╗╔═╗╔╦╗╦╔╗╔╦ ╦ ╔═╗╔╗ ╔═╗
|
4 |
+
* ║ ╦║╣ ║║║║║║║║ ║ ╠═╣╠╩╗╚═╗
|
5 |
+
* ╚═╝╚═╝╩ ╩╩╝╚╝╩ ╩═╝╩ ╩╚═╝╚═╝
|
6 |
+
*
|
7 |
+
* Plugin Name: Site Reviews
|
8 |
+
* Plugin URI: https://wordpress.org/plugins/site-reviews
|
9 |
+
* Description: Receive and display site reviews
|
10 |
+
* Version: 1.2.2
|
11 |
+
* Author: Paul Ryley
|
12 |
+
* Author URI: http://geminilabs.io
|
13 |
+
* License: GPL2
|
14 |
+
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
15 |
+
* Text Domain: site-reviews
|
16 |
+
*/
|
17 |
+
|
18 |
+
defined( 'WPINC' ) || die;
|
19 |
+
|
20 |
+
require_once __DIR__ . '/activate.php';
|
21 |
+
require_once __DIR__ . '/autoload.php';
|
22 |
+
|
23 |
+
use GeminiLabs\SiteReviews\App;
|
24 |
+
use GeminiLabs\SiteReviews\Providers\MainProvider;
|
25 |
+
|
26 |
+
$app = App::load();
|
27 |
+
|
28 |
+
$app->register( new MainProvider );
|
29 |
+
|
30 |
+
register_activation_hook( __FILE__, array( $app, 'activate' ) );
|
31 |
+
register_deactivation_hook( __FILE__, array( $app, 'deactivate' ) );
|
32 |
+
|
33 |
+
$app->init();
|
34 |
+
|
35 |
+
// Global helper to return $app
|
36 |
+
function glsr_app() {
|
37 |
+
return App::load();
|
38 |
+
}
|
39 |
+
|
40 |
+
// Global helper to resolve a class instance where $app is not accessible
|
41 |
+
function glsr_resolve( $class ) {
|
42 |
+
return App::load()->make( $class );
|
43 |
+
}
|
44 |
+
|
45 |
+
// Wordpress 4.0-4.2 support
|
46 |
+
if( !function_exists( 'wp_roles' ) ) {
|
47 |
+
function wp_roles() {
|
48 |
+
global $wp_roles;
|
49 |
+
isset( $wp_roles ) ?: $wp_roles = new WP_Roles;
|
50 |
+
return $wp_roles;
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
// Wordpress 4.0-4.2 support
|
55 |
+
if( !function_exists( 'get_avatar_url' ) ) {
|
56 |
+
function get_avatar_url( $id_or_email, $args = null ) {
|
57 |
+
isset( $args['size'] ) ?: $args['size'] = 96;
|
58 |
+
isset( $args['default'] ) ?: $args['default'] = 'mystery';
|
59 |
+
$avatar = get_avatar( $id_or_email, $args['size'], $args['default'] );
|
60 |
+
$dom = new \DOMDocument;
|
61 |
+
$dom->loadHTML( $avatar );
|
62 |
+
return $dom->getElementsByTagName( 'img' )->item(0)->getAttribute( 'src' );
|
63 |
+
}
|
64 |
+
}
|
uninstall.php
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @package GeminiLabs\SiteReviews
|
5 |
+
* @copyright Copyright (c) 2016, Paul Ryley
|
6 |
+
* @license GPLv2 or later
|
7 |
+
* @since 1.0.0
|
8 |
+
* -------------------------------------------------------------------------------------------------
|
9 |
+
*/
|
10 |
+
|
11 |
+
defined( 'WP_UNINSTALL_PLUGIN' ) or die;
|
12 |
+
|
13 |
+
require_once __DIR__ . '/site-reviews.php';
|
14 |
+
|
15 |
+
$options = [
|
16 |
+
'logging',
|
17 |
+
'settings',
|
18 |
+
'version',
|
19 |
+
'version_upgraded_from',
|
20 |
+
];
|
21 |
+
|
22 |
+
foreach( $options as $option ) {
|
23 |
+
delete_option( glsr_app()->prefix . "_{$option}" );
|
24 |
+
}
|
25 |
+
|
26 |
+
glsr_resolve( 'Session' )->deleteSessions();
|
vendor/sinergi/browser-detector/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
The MIT License (MIT)
|
2 |
+
|
3 |
+
Copyright (c) 2013-2017 Chris Schuld <chris@chrisschuld.com>
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6 |
+
this software and associated documentation files (the "Software"), to deal in
|
7 |
+
the Software without restriction, including without limitation the rights to
|
8 |
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9 |
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10 |
+
subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17 |
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18 |
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19 |
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20 |
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
vendor/sinergi/browser-detector/src/Browser.php
ADDED
@@ -0,0 +1,322 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Sinergi\BrowserDetector;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Browser Detection.
|
7 |
+
*/
|
8 |
+
class Browser
|
9 |
+
{
|
10 |
+
const UNKNOWN = 'unknown';
|
11 |
+
const VIVALDI = 'Vivaldi';
|
12 |
+
const OPERA = 'Opera';
|
13 |
+
const OPERA_MINI = 'Opera Mini';
|
14 |
+
const WEBTV = 'WebTV';
|
15 |
+
const IE = 'Internet Explorer';
|
16 |
+
const POCKET_IE = 'Pocket Internet Explorer';
|
17 |
+
const KONQUEROR = 'Konqueror';
|
18 |
+
const ICAB = 'iCab';
|
19 |
+
const OMNIWEB = 'OmniWeb';
|
20 |
+
const FIREBIRD = 'Firebird';
|
21 |
+
const FIREFOX = 'Firefox';
|
22 |
+
const SEAMONKEY = 'SeaMonkey';
|
23 |
+
const ICEWEASEL = 'Iceweasel';
|
24 |
+
const SHIRETOKO = 'Shiretoko';
|
25 |
+
const MOZILLA = 'Mozilla';
|
26 |
+
const AMAYA = 'Amaya';
|
27 |
+
const LYNX = 'Lynx';
|
28 |
+
const WKHTMLTOPDF = 'wkhtmltopdf';
|
29 |
+
const SAFARI = 'Safari';
|
30 |
+
const SAMSUNG_BROWSER = 'SamsungBrowser';
|
31 |
+
const CHROME = 'Chrome';
|
32 |
+
const NAVIGATOR = 'Navigator';
|
33 |
+
const GOOGLEBOT = 'GoogleBot';
|
34 |
+
const SLURP = 'Yahoo! Slurp';
|
35 |
+
const W3CVALIDATOR = 'W3C Validator';
|
36 |
+
const BLACKBERRY = 'BlackBerry';
|
37 |
+
const ICECAT = 'IceCat';
|
38 |
+
const NOKIA_S60 = 'Nokia S60 OSS Browser';
|
39 |
+
const NOKIA = 'Nokia Browser';
|
40 |
+
const MSN = 'MSN Browser';
|
41 |
+
const MSNBOT = 'MSN Bot';
|
42 |
+
const NETSCAPE_NAVIGATOR = 'Netscape Navigator';
|
43 |
+
const GALEON = 'Galeon';
|
44 |
+
const NETPOSITIVE = 'NetPositive';
|
45 |
+
const PHOENIX = 'Phoenix';
|
46 |
+
const GSA = 'Google Search Appliance';
|
47 |
+
const YANDEX = 'Yandex';
|
48 |
+
const EDGE = 'Edge';
|
49 |
+
const DRAGON = 'Dragon';
|
50 |
+
|
51 |
+
const VERSION_UNKNOWN = 'unknown';
|
52 |
+
|
53 |
+
/**
|
54 |
+
* @var UserAgent
|
55 |
+
*/
|
56 |
+
private $userAgent;
|
57 |
+
|
58 |
+
/**
|
59 |
+
* @var string
|
60 |
+
*/
|
61 |
+
private $name;
|
62 |
+
/**
|
63 |
+
* @var string
|
64 |
+
*/
|
65 |
+
private $version;
|
66 |
+
|
67 |
+
/**
|
68 |
+
* @var bool
|
69 |
+
*/
|
70 |
+
private $isRobot = false;
|
71 |
+
|
72 |
+
/**
|
73 |
+
* @var bool
|
74 |
+
*/
|
75 |
+
private $isChromeFrame = false;
|
76 |
+
|
77 |
+
/**
|
78 |
+
* @var bool
|
79 |
+
*/
|
80 |
+
private $isFacebookWebView = false;
|
81 |
+
|
82 |
+
/**
|
83 |
+
* @var bool
|
84 |
+
*/
|
85 |
+
private $isCompatibilityMode = false;
|
86 |
+
|
87 |
+
/**
|
88 |
+
* @param null|string|UserAgent $userAgent
|
89 |
+
*
|
90 |
+
* @throws \Sinergi\BrowserDetector\InvalidArgumentException
|
91 |
+
*/
|
92 |
+
public function __construct($userAgent = null)
|
93 |
+
{
|
94 |
+
if ($userAgent instanceof UserAgent) {
|
95 |
+
$this->setUserAgent($userAgent);
|
96 |
+
} elseif (null === $userAgent || is_string($userAgent)) {
|
97 |
+
$this->setUserAgent(new UserAgent($userAgent));
|
98 |
+
} else {
|
99 |
+
throw new InvalidArgumentException();
|
100 |
+
}
|
101 |
+
}
|
102 |
+
|
103 |
+
/**
|
104 |
+
* Set the name of the OS.
|
105 |
+
*
|
106 |
+
* @param string $name
|
107 |
+
*
|
108 |
+
* @return $this
|
109 |
+
*/
|
110 |
+
public function setName($name)
|
111 |
+
{
|
112 |
+
$this->name = (string)$name;
|
113 |
+
|
114 |
+
return $this;
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Return the name of the Browser.
|
119 |
+
*
|
120 |
+
* @return string
|
121 |
+
*/
|
122 |
+
public function getName()
|
123 |
+
{
|
124 |
+
if (!isset($this->name)) {
|
125 |
+
BrowserDetector::detect($this, $this->getUserAgent());
|
126 |
+
}
|
127 |
+
|
128 |
+
return $this->name;
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Check to see if the specific browser is valid.
|
133 |
+
*
|
134 |
+
* @param string $name
|
135 |
+
*
|
136 |
+
* @return bool
|
137 |
+
*/
|
138 |
+
public function isBrowser($name)
|
139 |
+
{
|
140 |
+
return (0 == strcasecmp($this->getName(), trim($name)));
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* Set the version of the browser.
|
145 |
+
*
|
146 |
+
* @param string $version
|
147 |
+
*
|
148 |
+
* @return $this
|
149 |
+
*/
|
150 |
+
public function setVersion($version)
|
151 |
+
{
|
152 |
+
$this->version = (string)$version;
|
153 |
+
|
154 |
+
return $this;
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* The version of the browser.
|
159 |
+
*
|
160 |
+
* @return string
|
161 |
+
*/
|
162 |
+
public function getVersion()
|
163 |
+
{
|
164 |
+
if (!isset($this->name)) {
|
165 |
+
BrowserDetector::detect($this, $this->getUserAgent());
|
166 |
+
}
|
167 |
+
|
168 |
+
return (string) $this->version;
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
172 |
+
* Set the Browser to be a robot.
|
173 |
+
*
|
174 |
+
* @param bool $isRobot
|
175 |
+
*
|
176 |
+
* @return $this
|
177 |
+
*/
|
178 |
+
public function setIsRobot($isRobot)
|
179 |
+
{
|
180 |
+
$this->isRobot = (bool)$isRobot;
|
181 |
+
|
182 |
+
return $this;
|
183 |
+
}
|
184 |
+
|
185 |
+
/**
|
186 |
+
* Is the browser from a robot (ex Slurp,GoogleBot)?
|
187 |
+
*
|
188 |
+
* @return bool
|
189 |
+
*/
|
190 |
+
public function getIsRobot()
|
191 |
+
{
|
192 |
+
if (!isset($this->name)) {
|
193 |
+
BrowserDetector::detect($this, $this->getUserAgent());
|
194 |
+
}
|
195 |
+
|
196 |
+
return $this->isRobot;
|
197 |
+
}
|
198 |
+
|
199 |
+
/**
|
200 |
+
* @return bool
|
201 |
+
*/
|
202 |
+
public function isRobot()
|
203 |
+
{
|
204 |
+
return $this->getIsRobot();
|
205 |
+
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* @param bool $isChromeFrame
|
209 |
+
*
|
210 |
+
* @return $this
|
211 |
+
*/
|
212 |
+
public function setIsChromeFrame($isChromeFrame)
|
213 |
+
{
|
214 |
+
$this->isChromeFrame = (bool)$isChromeFrame;
|
215 |
+
|
216 |
+
return $this;
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Used to determine if the browser is actually "chromeframe".
|
221 |
+
*
|
222 |
+
* @return bool
|
223 |
+
*/
|
224 |
+
public function getIsChromeFrame()
|
225 |
+
{
|
226 |
+
if (!isset($this->name)) {
|
227 |
+
BrowserDetector::detect($this, $this->getUserAgent());
|
228 |
+
}
|
229 |
+
|
230 |
+
return $this->isChromeFrame;
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* @return bool
|
235 |
+
*/
|
236 |
+
public function isChromeFrame()
|
237 |
+
{
|
238 |
+
return $this->getIsChromeFrame();
|
239 |
+
}
|
240 |
+
|
241 |
+
/**
|
242 |
+
* @param bool $isFacebookWebView
|
243 |
+
*
|
244 |
+
* @return $this
|
245 |
+
*/
|
246 |
+
public function setIsFacebookWebView($isFacebookWebView)
|
247 |
+
{
|
248 |
+
$this->isFacebookWebView = (bool) $isFacebookWebView;
|
249 |
+
|
250 |
+
return $this;
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* Used to determine if the browser is actually "facebook".
|
255 |
+
*
|
256 |
+
* @return bool
|
257 |
+
*/
|
258 |
+
public function getIsFacebookWebView()
|
259 |
+
{
|
260 |
+
if (!isset($this->name)) {
|
261 |
+
BrowserDetector::detect($this, $this->getUserAgent());
|
262 |
+
}
|
263 |
+
|
264 |
+
return $this->isFacebookWebView;
|
265 |
+
}
|
266 |
+
|
267 |
+
/**
|
268 |
+
* @return bool
|
269 |
+
*/
|
270 |
+
public function isFacebookWebView()
|
271 |
+
{
|
272 |
+
return $this->getIsFacebookWebView();
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* @param UserAgent $userAgent
|
277 |
+
*
|
278 |
+
* @return $this
|
279 |
+
*/
|
280 |
+
public function setUserAgent(UserAgent $userAgent)
|
281 |
+
{
|
282 |
+
$this->userAgent = $userAgent;
|
283 |
+
|
284 |
+
return $this;
|
285 |
+
}
|
286 |
+
|
287 |
+
/**
|
288 |
+
* @return UserAgent
|
289 |
+
*/
|
290 |
+
public function getUserAgent()
|
291 |
+
{
|
292 |
+
return $this->userAgent;
|
293 |
+
}
|
294 |
+
|
295 |
+
/**
|
296 |
+
* @param bool
|
297 |
+
*
|
298 |
+
* @return $this
|
299 |
+
*/
|
300 |
+
public function setIsCompatibilityMode($isCompatibilityMode)
|
301 |
+
{
|
302 |
+
$this->isCompatibilityMode = $isCompatibilityMode;
|
303 |
+
|
304 |
+
return $this;
|
305 |
+
}
|
306 |
+
|
307 |
+
/**
|
308 |
+
* @return bool
|
309 |
+
*/
|
310 |
+
public function isCompatibilityMode()
|
311 |
+
{
|
312 |
+
return $this->isCompatibilityMode;
|
313 |
+
}
|
314 |
+
|
315 |
+
/**
|
316 |
+
* Render pages outside of IE's compatibility mode.
|
317 |
+
*/
|
318 |
+
public function endCompatibilityMode()
|
319 |
+
{
|
320 |
+
header('X-UA-Compatible: IE=edge');
|
321 |
+
}
|
322 |
+
}
|
vendor/sinergi/browser-detector/src/BrowserDetector.php
ADDED
@@ -0,0 +1,1006 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Sinergi\BrowserDetector;
|
4 |
+
|
5 |
+
class BrowserDetector implements DetectorInterface
|
6 |
+
{
|
7 |
+
const FUNC_PREFIX = 'checkBrowser';
|
8 |
+
|
9 |
+
protected static $userAgentString;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var Browser
|
13 |
+
*/
|
14 |
+
protected static $browser;
|
15 |
+
|
16 |
+
protected static $browsersList = array(
|
17 |
+
// well-known, well-used
|
18 |
+
// Special Notes:
|
19 |
+
// (1) Opera must be checked before FireFox due to the odd
|
20 |
+
// user agents used in some older versions of Opera
|
21 |
+
// (2) WebTV is strapped onto Internet Explorer so we must
|
22 |
+
// check for WebTV before IE
|
23 |
+
// (3) Because of Internet Explorer 11 using
|
24 |
+
// "Mozilla/5.0 ([...] Trident/7.0; rv:11.0) like Gecko"
|
25 |
+
// as user agent, tests for IE must be run before any
|
26 |
+
// tests checking for "Mozilla"
|
27 |
+
// (4) (deprecated) Galeon is based on Firefox and needs to be
|
28 |
+
// tested before Firefox is tested
|
29 |
+
// (5) OmniWeb is based on Safari so OmniWeb check must occur
|
30 |
+
// before Safari
|
31 |
+
// (6) Netscape 9+ is based on Firefox so Netscape checks
|
32 |
+
// before FireFox are necessary
|
33 |
+
// (7) Microsoft Edge must be checked before Chrome and Safari
|
34 |
+
// (7) Vivaldi must be checked before Chrome
|
35 |
+
'WebTv',
|
36 |
+
'InternetExplorer',
|
37 |
+
'Edge',
|
38 |
+
'Opera',
|
39 |
+
'Vivaldi',
|
40 |
+
'Dragon',
|
41 |
+
'Galeon',
|
42 |
+
'NetscapeNavigator9Plus',
|
43 |
+
'SeaMonkey',
|
44 |
+
'Firefox',
|
45 |
+
'Yandex',
|
46 |
+
'Samsung',
|
47 |
+
'Chrome',
|
48 |
+
'OmniWeb',
|
49 |
+
// common mobile
|
50 |
+
'Android',
|
51 |
+
'BlackBerry',
|
52 |
+
'Nokia',
|
53 |
+
'Gsa',
|
54 |
+
// common bots
|
55 |
+
'Robot',
|
56 |
+
// wkhtmltopdf before Safari
|
57 |
+
'Wkhtmltopdf',
|
58 |
+
// WebKit base check (post mobile and others)
|
59 |
+
'Safari',
|
60 |
+
// everyone else
|
61 |
+
'NetPositive',
|
62 |
+
'Firebird',
|
63 |
+
'Konqueror',
|
64 |
+
'Icab',
|
65 |
+
'Phoenix',
|
66 |
+
'Amaya',
|
67 |
+
'Lynx',
|
68 |
+
'Shiretoko',
|
69 |
+
'IceCat',
|
70 |
+
'Iceweasel',
|
71 |
+
'Mozilla', /* Mozilla is such an open standard that you must check it last */
|
72 |
+
);
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Routine to determine the browser type.
|
76 |
+
*
|
77 |
+
* @param Browser $browser
|
78 |
+
* @param UserAgent $userAgent
|
79 |
+
*
|
80 |
+
* @return bool
|
81 |
+
*/
|
82 |
+
public static function detect(Browser $browser, UserAgent $userAgent = null)
|
83 |
+
{
|
84 |
+
self::$browser = $browser;
|
85 |
+
if (is_null($userAgent)) {
|
86 |
+
$userAgent = self::$browser->getUserAgent();
|
87 |
+
}
|
88 |
+
self::$userAgentString = $userAgent->getUserAgentString();
|
89 |
+
|
90 |
+
self::$browser->setName(Browser::UNKNOWN);
|
91 |
+
self::$browser->setVersion(Browser::VERSION_UNKNOWN);
|
92 |
+
|
93 |
+
self::checkChromeFrame();
|
94 |
+
self::checkFacebookWebView();
|
95 |
+
|
96 |
+
foreach (self::$browsersList as $browserName) {
|
97 |
+
$funcName = self::FUNC_PREFIX . $browserName;
|
98 |
+
|
99 |
+
if (self::$funcName()) {
|
100 |
+
return true;
|
101 |
+
}
|
102 |
+
}
|
103 |
+
|
104 |
+
return false;
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Determine if the user is using Chrome Frame.
|
109 |
+
*
|
110 |
+
* @return bool
|
111 |
+
*/
|
112 |
+
public static function checkChromeFrame()
|
113 |
+
{
|
114 |
+
if (strpos(self::$userAgentString, 'chromeframe') !== false) {
|
115 |
+
self::$browser->setIsChromeFrame(true);
|
116 |
+
|
117 |
+
return true;
|
118 |
+
}
|
119 |
+
|
120 |
+
return false;
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Determine if the user is using Facebook.
|
125 |
+
*
|
126 |
+
* @return bool
|
127 |
+
*/
|
128 |
+
public static function checkFacebookWebView()
|
129 |
+
{
|
130 |
+
if (strpos(self::$userAgentString, 'FBAV') !== false) {
|
131 |
+
self::$browser->setIsFacebookWebView(true);
|
132 |
+
|
133 |
+
return true;
|
134 |
+
}
|
135 |
+
|
136 |
+
return false;
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* Determine if the user is using a BlackBerry.
|
141 |
+
*
|
142 |
+
* @return bool
|
143 |
+
*/
|
144 |
+
public static function checkBrowserBlackBerry()
|
145 |
+
{
|
146 |
+
if (stripos(self::$userAgentString, 'blackberry') !== false) {
|
147 |
+
if (stripos(self::$userAgentString, 'Version/') !== false) {
|
148 |
+
$aresult = explode('Version/', self::$userAgentString);
|
149 |
+
if (isset($aresult[1])) {
|
150 |
+
$aversion = explode(' ', $aresult[1]);
|
151 |
+
self::$browser->setVersion($aversion[0]);
|
152 |
+
}
|
153 |
+
} else {
|
154 |
+
$aresult = explode('/', stristr(self::$userAgentString, 'BlackBerry'));
|
155 |
+
if (isset($aresult[1])) {
|
156 |
+
$aversion = explode(' ', $aresult[1]);
|
157 |
+
self::$browser->setVersion($aversion[0]);
|
158 |
+
}
|
159 |
+
}
|
160 |
+
self::$browser->setName(Browser::BLACKBERRY);
|
161 |
+
|
162 |
+
return true;
|
163 |
+
} elseif (stripos(self::$userAgentString, 'BB10') !== false) {
|
164 |
+
$aresult = explode('Version/10.', self::$userAgentString);
|
165 |
+
if (isset($aresult[1])) {
|
166 |
+
$aversion = explode(' ', $aresult[1]);
|
167 |
+
self::$browser->setVersion('10.' . $aversion[0]);
|
168 |
+
}
|
169 |
+
self::$browser->setName(Browser::BLACKBERRY);
|
170 |
+
return true;
|
171 |
+
}
|
172 |
+
|
173 |
+
return false;
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* Determine if the browser is a robot.
|
178 |
+
*
|
179 |
+
* @return bool
|
180 |
+
*/
|
181 |
+
public static function checkBrowserRobot()
|
182 |
+
{
|
183 |
+
if (stripos(self::$userAgentString, 'bot') !== false ||
|
184 |
+
stripos(self::$userAgentString, 'spider') !== false ||
|
185 |
+
stripos(self::$userAgentString, 'crawler') !== false
|
186 |
+
) {
|
187 |
+
self::$browser->setIsRobot(true);
|
188 |
+
|
189 |
+
return true;
|
190 |
+
}
|
191 |
+
|
192 |
+
return false;
|
193 |
+
}
|
194 |
+
|
195 |
+
/**
|
196 |
+
* Determine if the browser is Internet Explorer.
|
197 |
+
*
|
198 |
+
* @return bool
|
199 |
+
*/
|
200 |
+
public static function checkBrowserInternetExplorer()
|
201 |
+
{
|
202 |
+
// Test for v1 - v1.5 IE
|
203 |
+
if (stripos(self::$userAgentString, 'microsoft internet explorer') !== false) {
|
204 |
+
self::$browser->setName(Browser::IE);
|
205 |
+
self::$browser->setVersion('1.0');
|
206 |
+
$aresult = stristr(self::$userAgentString, '/');
|
207 |
+
if (preg_match('/308|425|426|474|0b1/i', $aresult)) {
|
208 |
+
self::$browser->setVersion('1.5');
|
209 |
+
}
|
210 |
+
|
211 |
+
return true;
|
212 |
+
} // Test for versions > 1.5 and < 11 and some cases of 11
|
213 |
+
else {
|
214 |
+
if (stripos(self::$userAgentString, 'msie') !== false && stripos(self::$userAgentString, 'opera') === false
|
215 |
+
) {
|
216 |
+
// See if the browser is the odd MSN Explorer
|
217 |
+
if (stripos(self::$userAgentString, 'msnb') !== false) {
|
218 |
+
$aresult = explode(' ', stristr(str_replace(';', '; ', self::$userAgentString), 'MSN'));
|
219 |
+
self::$browser->setName(Browser::MSN);
|
220 |
+
if (isset($aresult[1])) {
|
221 |
+
self::$browser->setVersion(str_replace(array('(', ')', ';'), '', $aresult[1]));
|
222 |
+
}
|
223 |
+
|
224 |
+
return true;
|
225 |
+
}
|
226 |
+
$aresult = explode(' ', stristr(str_replace(';', '; ', self::$userAgentString), 'msie'));
|
227 |
+
self::$browser->setName(Browser::IE);
|
228 |
+
if (isset($aresult[1])) {
|
229 |
+
self::$browser->setVersion(str_replace(array('(', ')', ';'), '', $aresult[1]));
|
230 |
+
}
|
231 |
+
// See https://msdn.microsoft.com/en-us/library/ie/hh869301%28v=vs.85%29.aspx
|
232 |
+
// Might be 11, anyway !
|
233 |
+
if (stripos(self::$userAgentString, 'trident') !== false) {
|
234 |
+
preg_match('/rv:(\d+\.\d+)/', self::$userAgentString, $matches);
|
235 |
+
if (isset($matches[1])) {
|
236 |
+
self::$browser->setVersion($matches[1]);
|
237 |
+
}
|
238 |
+
|
239 |
+
// At this poing in the method, we know the MSIE and Trident
|
240 |
+
// strings are present in the $userAgentString. If we're in
|
241 |
+
// compatibility mode, we need to determine the true version.
|
242 |
+
// If the MSIE version is 7.0, we can look at the Trident
|
243 |
+
// version to *approximate* the true IE version. If we don't
|
244 |
+
// find a matching pair, ( e.g. MSIE 7.0 && Trident/7.0 )
|
245 |
+
// we're *not* in compatibility mode and the browser really
|
246 |
+
// is version 7.0.
|
247 |
+
if (stripos(self::$userAgentString, 'MSIE 7.0;')) {
|
248 |
+
if (stripos(self::$userAgentString, 'Trident/7.0;')) {
|
249 |
+
// IE11 in compatibility mode
|
250 |
+
self::$browser->setVersion('11.0');
|
251 |
+
self::$browser->setIsCompatibilityMode(true);
|
252 |
+
} elseif (stripos(self::$userAgentString, 'Trident/6.0;')) {
|
253 |
+
// IE10 in compatibility mode
|
254 |
+
self::$browser->setVersion('10.0');
|
255 |
+
self::$browser->setIsCompatibilityMode(true);
|
256 |
+
} elseif (stripos(self::$userAgentString, 'Trident/5.0;')) {
|
257 |
+
// IE9 in compatibility mode
|
258 |
+
self::$browser->setVersion('9.0');
|
259 |
+
self::$browser->setIsCompatibilityMode(true);
|
260 |
+
} elseif (stripos(self::$userAgentString, 'Trident/4.0;')) {
|
261 |
+
// IE8 in compatibility mode
|
262 |
+
self::$browser->setVersion('8.0');
|
263 |
+
self::$browser->setIsCompatibilityMode(true);
|
264 |
+
}
|
265 |
+
}
|
266 |
+
}
|
267 |
+
|
268 |
+
return true;
|
269 |
+
} // Test for versions >= 11
|
270 |
+
else {
|
271 |
+
if (stripos(self::$userAgentString, 'trident') !== false) {
|
272 |
+
self::$browser->setName(Browser::IE);
|
273 |
+
|
274 |
+
preg_match('/rv:(\d+\.\d+)/', self::$userAgentString, $matches);
|
275 |
+
if (isset($matches[1])) {
|
276 |
+
self::$browser->setVersion($matches[1]);
|
277 |
+
|
278 |
+
return true;
|
279 |
+
} else {
|
280 |
+
return false;
|
281 |
+
}
|
282 |
+
} // Test for Pocket IE
|
283 |
+
else {
|
284 |
+
if (stripos(self::$userAgentString, 'mspie') !== false ||
|
285 |
+
stripos(
|
286 |
+
self::$userAgentString,
|
287 |
+
'pocket'
|
288 |
+
) !== false
|
289 |
+
) {
|
290 |
+
$aresult = explode(' ', stristr(self::$userAgentString, 'mspie'));
|
291 |
+
self::$browser->setName(Browser::POCKET_IE);
|
292 |
+
|
293 |
+
if (stripos(self::$userAgentString, 'mspie') !== false) {
|
294 |
+
if (isset($aresult[1])) {
|
295 |
+
self::$browser->setVersion($aresult[1]);
|
296 |
+
}
|
297 |
+
} else {
|
298 |
+
$aversion = explode('/', self::$userAgentString);
|
299 |
+
if (isset($aversion[1])) {
|
300 |
+
self::$browser->setVersion($aversion[1]);
|
301 |
+
}
|
302 |
+
}
|
303 |
+
|
304 |
+
return true;
|
305 |
+
}
|
306 |
+
}
|
307 |
+
}
|
308 |
+
}
|
309 |
+
|
310 |
+
return false;
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* Determine if the browser is Opera.
|
315 |
+
*
|
316 |
+
* @return bool
|
317 |
+
*/
|
318 |
+
public static function checkBrowserOpera()
|
319 |
+
{
|
320 |
+
if (stripos(self::$userAgentString, 'opera mini') !== false) {
|
321 |
+
$resultant = stristr(self::$userAgentString, 'opera mini');
|
322 |
+
if (preg_match('/\//', $resultant)) {
|
323 |
+
$aresult = explode('/', $resultant);
|
324 |
+
if (isset($aresult[1])) {
|
325 |
+
$aversion = explode(' ', $aresult[1]);
|
326 |
+
self::$browser->setVersion($aversion[0]);
|
327 |
+
}
|
328 |
+
} else {
|
329 |
+
$aversion = explode(' ', stristr($resultant, 'opera mini'));
|
330 |
+
if (isset($aversion[1])) {
|
331 |
+
self::$browser->setVersion($aversion[1]);
|
332 |
+
}
|
333 |
+
}
|
334 |
+
self::$browser->setName(Browser::OPERA_MINI);
|
335 |
+
|
336 |
+
return true;
|
337 |
+
} elseif (stripos(self::$userAgentString, 'OPiOS') !== false) {
|
338 |
+
$aresult = explode('/', stristr(self::$userAgentString, 'OPiOS'));
|
339 |
+
if (isset($aresult[1])) {
|
340 |
+
$aversion = explode(' ', $aresult[1]);
|
341 |
+
self::$browser->setVersion($aversion[0]);
|
342 |
+
}
|
343 |
+
self::$browser->setName(Browser::OPERA_MINI);
|
344 |
+
|
345 |
+
return true;
|
346 |
+
} elseif (stripos(self::$userAgentString, 'opera') !== false) {
|
347 |
+
$resultant = stristr(self::$userAgentString, 'opera');
|
348 |
+
if (preg_match('/Version\/(1[0-2].*)$/', $resultant, $matches)) {
|
349 |
+
if (isset($matches[1])) {
|
350 |
+
self::$browser->setVersion($matches[1]);
|
351 |
+
}
|
352 |
+
} elseif (preg_match('/\//', $resultant)) {
|
353 |
+
$aresult = explode('/', str_replace('(', ' ', $resultant));
|
354 |
+
if (isset($aresult[1])) {
|
355 |
+
$aversion = explode(' ', $aresult[1]);
|
356 |
+
self::$browser->setVersion($aversion[0]);
|
357 |
+
}
|
358 |
+
} else {
|
359 |
+
$aversion = explode(' ', stristr($resultant, 'opera'));
|
360 |
+
self::$browser->setVersion(isset($aversion[1]) ? $aversion[1] : '');
|
361 |
+
}
|
362 |
+
self::$browser->setName(Browser::OPERA);
|
363 |
+
|
364 |
+
return true;
|
365 |
+
} elseif (stripos(self::$userAgentString, ' OPR/') !== false) {
|
366 |
+
self::$browser->setName(Browser::OPERA);
|
367 |
+
if (preg_match('/OPR\/([\d\.]*)/', self::$userAgentString, $matches)) {
|
368 |
+
if (isset($matches[1])) {
|
369 |
+
self::$browser->setVersion($matches[1]);
|
370 |
+
}
|
371 |
+
}
|
372 |
+
|
373 |
+
return true;
|
374 |
+
}
|
375 |
+
|
376 |
+
return false;
|
377 |
+
}
|
378 |
+
|
379 |
+
/**
|
380 |
+
* Determine if the browser is Samsung.
|
381 |
+
*
|
382 |
+
* @return bool
|
383 |
+
*/
|
384 |
+
public static function checkBrowserSamsung()
|
385 |
+
{
|
386 |
+
if (stripos(self::$userAgentString, 'SamsungBrowser') !== false) {
|
387 |
+
$aresult = explode('/', stristr(self::$userAgentString, 'SamsungBrowser'));
|
388 |
+
if (isset($aresult[1])) {
|
389 |
+
$aversion = explode(' ', $aresult[1]);
|
390 |
+
self::$browser->setVersion($aversion[0]);
|
391 |
+
}
|
392 |
+
self::$browser->setName(Browser::SAMSUNG_BROWSER);
|
393 |
+
|
394 |
+
return true;
|
395 |
+
}
|
396 |
+
|
397 |
+
return false;
|
398 |
+
}
|
399 |
+
|
400 |
+
/**
|
401 |
+
* Determine if the browser is Chrome.
|
402 |
+
*
|
403 |
+
* @return bool
|
404 |
+
*/
|
405 |
+
public static function checkBrowserChrome()
|
406 |
+
{
|
407 |
+
if (stripos(self::$userAgentString, 'Chrome') !== false) {
|
408 |
+
$aresult = explode('/', stristr(self::$userAgentString, 'Chrome'));
|
409 |
+
if (isset($aresult[1])) {
|
410 |
+
$aversion = explode(' ', $aresult[1]);
|
411 |
+
self::$browser->setVersion($aversion[0]);
|
412 |
+
}
|
413 |
+
self::$browser->setName(Browser::CHROME);
|
414 |
+
|
415 |
+
return true;
|
416 |
+
} elseif (stripos(self::$userAgentString, 'CriOS') !== false) {
|
417 |
+
$aresult = explode('/', stristr(self::$userAgentString, 'CriOS'));
|
418 |
+
if (isset($aresult[1])) {
|
419 |
+
$aversion = explode(' ', $aresult[1]);
|
420 |
+
self::$browser->setVersion($aversion[0]);
|
421 |
+
}
|
422 |
+
self::$browser->setName(Browser::CHROME);
|
423 |
+
|
424 |
+
return true;
|
425 |
+
}
|
426 |
+
|
427 |
+
return false;
|
428 |
+
}
|
429 |
+
|
430 |
+
/**
|
431 |
+
* Determine if the browser is Vivaldi.
|
432 |
+
*
|
433 |
+
* @return bool
|
434 |
+
*/
|
435 |
+
public static function checkBrowserVivaldi()
|
436 |
+
{
|
437 |
+
if (stripos(self::$userAgentString, 'Vivaldi') !== false) {
|
438 |
+
$aresult = explode('/', stristr(self::$userAgentString, 'Vivaldi'));
|
439 |
+
if (isset($aresult[1])) {
|
440 |
+
$aversion = explode(' ', $aresult[1]);
|
441 |
+
self::$browser->setVersion($aversion[0]);
|
442 |
+
}
|
443 |
+
self::$browser->setName(Browser::VIVALDI);
|
444 |
+
|
445 |
+
return true;
|
446 |
+
}
|
447 |
+
|
448 |
+
return false;
|
449 |
+
}
|
450 |
+
|
451 |
+
/**
|
452 |
+
* Determine if the browser is Microsoft Edge.
|
453 |
+
*
|
454 |
+
* @return bool
|
455 |
+
*/
|
456 |
+
public static function checkBrowserEdge()
|
457 |
+
{
|
458 |
+
if (stripos(self::$userAgentString, 'Edge') !== false) {
|
459 |
+
$version = explode('Edge/', self::$userAgentString);
|
460 |
+
if (isset($version[1])) {
|
461 |
+
self::$browser->setVersion((float)$version[1]);
|
462 |
+
}
|
463 |
+
self::$browser->setName(Browser::EDGE);
|
464 |
+
|
465 |
+
return true;
|
466 |
+
}
|
467 |
+
|
468 |
+
return false;
|
469 |
+
}
|
470 |
+
|
471 |
+
/**
|
472 |
+
* Determine if the browser is Google Search Appliance.
|
473 |
+
*
|
474 |
+
* @return bool
|
475 |
+
*/
|
476 |
+
public static function checkBrowserGsa()
|
477 |
+
{
|
478 |
+
if (stripos(self::$userAgentString, 'GSA') !== false) {
|
479 |
+
$aresult = explode('/', stristr(self::$userAgentString, 'GSA'));
|
480 |
+
if (isset($aresult[1])) {
|
481 |
+
$aversion = explode(' ', $aresult[1]);
|
482 |
+
self::$browser->setVersion($aversion[0]);
|
483 |
+
}
|
484 |
+
self::$browser->setName(Browser::GSA);
|
485 |
+
|
486 |
+
return true;
|
487 |
+
}
|
488 |
+
|
489 |
+
return false;
|
490 |
+
}
|
491 |
+
|
492 |
+
/**
|
493 |
+
* Determine if the browser is WebTv.
|
494 |
+
*
|
495 |
+
* @return bool
|
496 |
+
*/
|
497 |
+
public static function checkBrowserWebTv()
|
498 |
+
{
|
499 |
+
if (stripos(self::$userAgentString, 'webtv') !== false) {
|
500 |
+
$aresult = explode('/', stristr(self::$userAgentString, 'webtv'));
|
501 |
+
if (isset($aresult[1])) {
|
502 |
+
$aversion = explode(' ', $aresult[1]);
|
503 |
+
self::$browser->setVersion($aversion[0]);
|
504 |
+
}
|
505 |
+
self::$browser->setName(Browser::WEBTV);
|
506 |
+
|
507 |
+
return true;
|
508 |
+
}
|
509 |
+
|
510 |
+
return false;
|
511 |
+
}
|
512 |
+
|
513 |
+
/**
|
514 |
+
* Determine if the browser is NetPositive.
|
515 |
+
*
|
516 |
+
* @return bool
|
517 |
+
*/
|
518 |
+
public static function checkBrowserNetPositive()
|
519 |
+
{
|
520 |
+
if (stripos(self::$userAgentString, 'NetPositive') !== false) {
|
521 |
+
$aresult = explode('/', stristr(self::$userAgentString, 'NetPositive'));
|
522 |
+
if (isset($aresult[1])) {
|
523 |
+
$aversion = explode(' ', $aresult[1]);
|
524 |
+
self::$browser->setVersion(str_replace(array('(', ')', ';'), '', $aversion[0]));
|
525 |
+
}
|
526 |
+
self::$browser->setName(Browser::NETPOSITIVE);
|
527 |
+
|
528 |
+
return true;
|
529 |
+
}
|
530 |
+
|
531 |
+
return false;
|
532 |
+
}
|
533 |
+
|
534 |
+
/**
|
535 |
+
* Determine if the browser is Galeon.
|
536 |
+
*
|
537 |
+
* @return bool
|
538 |
+
*/
|
539 |
+
public static function checkBrowserGaleon()
|
540 |
+
{
|
541 |
+
if (stripos(self::$userAgentString, 'galeon') !== false) {
|
542 |
+
$aresult = explode(' ', stristr(self::$userAgentString, 'galeon'));
|
543 |
+
$aversion = explode('/', $aresult[0]);
|
544 |
+
if (isset($aversion[1])) {
|
545 |
+
self::$browser->setVersion($aversion[1]);
|
546 |
+
}
|
547 |
+
self::$browser->setName(Browser::GALEON);
|
548 |
+
|
549 |
+
return true;
|
550 |
+
}
|
551 |
+
|
552 |
+
return false;
|
553 |
+
}
|
554 |
+
|
555 |
+
/**
|
556 |
+
* Determine if the browser is Konqueror.
|
557 |
+
*
|
558 |
+
* @return bool
|
559 |
+
*/
|
560 |
+
public static function checkBrowserKonqueror()
|
561 |
+
{
|
562 |
+
if (stripos(self::$userAgentString, 'Konqueror') !== false) {
|
563 |
+
$aresult = explode(' ', stristr(self::$userAgentString, 'Konqueror'));
|
564 |
+
$aversion = explode('/', $aresult[0]);
|
565 |
+
if (isset($aversion[1])) {
|
566 |
+
self::$browser->setVersion($aversion[1]);
|
567 |
+
}
|
568 |
+
self::$browser->setName(Browser::KONQUEROR);
|
569 |
+
|
570 |
+
return true;
|
571 |
+
}
|
572 |
+
|
573 |
+
return false;
|
574 |
+
}
|
575 |
+
|
576 |
+
/**
|
577 |
+
* Determine if the browser is iCab.
|
578 |
+
*
|
579 |
+
* @return bool
|
580 |
+
*/
|
581 |
+
public static function checkBrowserIcab()
|
582 |
+
{
|
583 |
+
if (stripos(self::$userAgentString, 'icab') !== false) {
|
584 |
+
$aversion = explode(' ', stristr(str_replace('/', ' ', self::$userAgentString), 'icab'));
|
585 |
+
if (isset($aversion[1])) {
|
586 |
+
self::$browser->setVersion($aversion[1]);
|
587 |
+
}
|
588 |
+
self::$browser->setName(Browser::ICAB);
|
589 |
+
|
590 |
+
return true;
|
591 |
+
}
|
592 |
+
|
593 |
+
return false;
|
594 |
+
}
|
595 |
+
|
596 |
+
/**
|
597 |
+
* Determine if the browser is OmniWeb.
|
598 |
+
*
|
599 |
+
* @return bool
|
600 |
+
*/
|
601 |
+
public static function checkBrowserOmniWeb()
|
602 |
+
{
|
603 |
+
if (stripos(self::$userAgentString, 'omniweb') !== false) {
|
604 |
+
$aresult = explode('/', stristr(self::$userAgentString, 'omniweb'));
|
605 |
+
$aversion = explode(' ', isset($aresult[1]) ? $aresult[1] : '');
|
606 |
+
self::$browser->setVersion($aversion[0]);
|
607 |
+
self::$browser->setName(Browser::OMNIWEB);
|
608 |
+
|
609 |
+
return true;
|
610 |
+
}
|
611 |
+
|
612 |
+
return false;
|
613 |
+
}
|
614 |
+
|
615 |
+
/**
|
616 |
+
* Determine if the browser is Phoenix.
|
617 |
+
*
|
618 |
+
* @return bool
|
619 |
+
*/
|
620 |
+
public static function checkBrowserPhoenix()
|
621 |
+
{
|
622 |
+
if (stripos(self::$userAgentString, 'Phoenix') !== false) {
|
623 |
+
$aversion = explode('/', stristr(self::$userAgentString, 'Phoenix'));
|
624 |
+
if (isset($aversion[1])) {
|
625 |
+
self::$browser->setVersion($aversion[1]);
|
626 |
+
}
|
627 |
+
self::$browser->setName(Browser::PHOENIX);
|
628 |
+
|
629 |
+
return true;
|
630 |
+
}
|
631 |
+
|
632 |
+
return false;
|
633 |
+
}
|
634 |
+
|
635 |
+
/**
|
636 |
+
* Determine if the browser is Firebird.
|
637 |
+
*
|
638 |
+
* @return bool
|
639 |
+
*/
|
640 |
+
public static function checkBrowserFirebird()
|
641 |
+
{
|
642 |
+
if (stripos(self::$userAgentString, 'Firebird') !== false) {
|
643 |
+
$aversion = explode('/', stristr(self::$userAgentString, 'Firebird'));
|
644 |
+
if (isset($aversion[1])) {
|
645 |
+
self::$browser->setVersion($aversion[1]);
|
646 |
+
}
|
647 |
+
self::$browser->setName(Browser::FIREBIRD);
|
648 |
+
|
649 |
+
return true;
|
650 |
+
}
|
651 |
+
|
652 |
+
return false;
|
653 |
+
}
|
654 |
+
|
655 |
+
/**
|
656 |
+
* Determine if the browser is Netscape Navigator 9+.
|
657 |
+
*
|
658 |
+
* @return bool
|
659 |
+
*/
|
660 |
+
public static function checkBrowserNetscapeNavigator9Plus()
|
661 |
+
{
|
662 |
+
if (stripos(self::$userAgentString, 'Firefox') !== false &&
|
663 |
+
preg_match('/Navigator\/([^ ]*)/i', self::$userAgentString, $matches)
|
664 |
+
) {
|
665 |
+
if (isset($matches[1])) {
|
666 |
+
self::$browser->setVersion($matches[1]);
|
667 |
+
}
|
668 |
+
self::$browser->setName(Browser::NETSCAPE_NAVIGATOR);
|
669 |
+
|
670 |
+
return true;
|
671 |
+
} elseif (stripos(self::$userAgentString, 'Firefox') === false &&
|
672 |
+
preg_match('/Netscape6?\/([^ ]*)/i', self::$userAgentString, $matches)
|
673 |
+
) {
|
674 |
+
if (isset($matches[1])) {
|
675 |
+
self::$browser->setVersion($matches[1]);
|
676 |
+
}
|
677 |
+
self::$browser->setName(Browser::NETSCAPE_NAVIGATOR);
|
678 |
+
|
679 |
+
return true;
|
680 |
+
}
|
681 |
+
|
682 |
+
return false;
|
683 |
+
}
|
684 |
+
|
685 |
+
/**
|
686 |
+
* Determine if the browser is Shiretoko.
|
687 |
+
*
|
688 |
+
* @return bool
|
689 |
+
*/
|
690 |
+
public static function checkBrowserShiretoko()
|
691 |
+
{
|
692 |
+
if (stripos(self::$userAgentString, 'Mozilla') !== false &&
|
693 |
+
preg_match('/Shiretoko\/([^ ]*)/i', self::$userAgentString, $matches)
|
694 |
+
) {
|
695 |
+
if (isset($matches[1])) {
|
696 |
+
self::$browser->setVersion($matches[1]);
|
697 |
+
}
|
698 |
+
self::$browser->setName(Browser::SHIRETOKO);
|
699 |
+
|
700 |
+
return true;
|
701 |
+
}
|
702 |
+
|
703 |
+
return false;
|
704 |
+
}
|
705 |
+
|
706 |
+
/**
|
707 |
+
* Determine if the browser is Ice Cat.
|
708 |
+
*
|
709 |
+
* @return bool
|
710 |
+
*/
|
711 |
+
public static function checkBrowserIceCat()
|
712 |
+
{
|
713 |
+
if (stripos(self::$userAgentString, 'Mozilla') !== false &&
|
714 |
+
preg_match('/IceCat\/([^ ]*)/i', self::$userAgentString, $matches)
|
715 |
+
) {
|
716 |
+
if (isset($matches[1])) {
|
717 |
+
self::$browser->setVersion($matches[1]);
|
718 |
+
}
|
719 |
+
self::$browser->setName(Browser::ICECAT);
|
720 |
+
|
721 |
+
return true;
|
722 |
+
}
|
723 |
+
|
724 |
+
return false;
|
725 |
+
}
|
726 |
+
|
727 |
+
/**
|
728 |
+
* Determine if the browser is Nokia.
|
729 |
+
*
|
730 |
+
* @return bool
|
731 |
+
*/
|
732 |
+
public static function checkBrowserNokia()
|
733 |
+
{
|
734 |
+
if (preg_match("/Nokia([^\/]+)\/([^ SP]+)/i", self::$userAgentString, $matches)) {
|
735 |
+
self::$browser->setVersion($matches[2]);
|
736 |
+
if (stripos(self::$userAgentString, 'Series60') !== false ||
|
737 |
+
strpos(self::$userAgentString, 'S60') !== false
|
738 |
+
) {
|
739 |
+
self::$browser->setName(Browser::NOKIA_S60);
|
740 |
+
} else {
|
741 |
+
self::$browser->setName(Browser::NOKIA);
|
742 |
+
}
|
743 |
+
|
744 |
+
return true;
|
745 |
+
}
|
746 |
+
|
747 |
+
return false;
|
748 |
+
}
|
749 |
+
|
750 |
+
/**
|
751 |
+
* Determine if the browser is Firefox.
|
752 |
+
*
|
753 |
+
* @return bool
|
754 |
+
*/
|
755 |
+
public static function checkBrowserFirefox()
|
756 |
+
{
|
757 |
+
if (stripos(self::$userAgentString, 'safari') === false) {
|
758 |
+
if (preg_match("/Firefox[\/ \(]([^ ;\)]+)/i", self::$userAgentString, $matches)) {
|
759 |
+
if (isset($matches[1])) {
|
760 |
+
self::$browser->setVersion($matches[1]);
|
761 |
+
}
|
762 |
+
self::$browser->setName(Browser::FIREFOX);
|
763 |
+
|
764 |
+
return true;
|
765 |
+
} elseif (preg_match('/Firefox$/i', self::$userAgentString, $matches)) {
|
766 |
+
self::$browser->setVersion('');
|
767 |
+
self::$browser->setName(Browser::FIREFOX);
|
768 |
+
|
769 |
+
return true;
|
770 |
+
}
|
771 |
+
}
|
772 |
+
|
773 |
+
return false;
|
774 |
+
}
|
775 |
+
|
776 |
+
/**
|
777 |
+
* Determine if the browser is SeaMonkey.
|
778 |
+
*
|
779 |
+
* @return bool
|
780 |
+
*/
|
781 |
+
public static function checkBrowserSeaMonkey()
|
782 |
+
{
|
783 |
+
if (stripos(self::$userAgentString, 'safari') === false) {
|
784 |
+
if (preg_match("/SeaMonkey[\/ \(]([^ ;\)]+)/i", self::$userAgentString, $matches)) {
|
785 |
+
if (isset($matches[1])) {
|
786 |
+
self::$browser->setVersion($matches[1]);
|
787 |
+
}
|
788 |
+
self::$browser->setName(Browser::SEAMONKEY);
|
789 |
+
|
790 |
+
return true;
|
791 |
+
} elseif (preg_match('/SeaMonkey$/i', self::$userAgentString, $matches)) {
|
792 |
+
self::$browser->setVersion('');
|
793 |
+
self::$browser->setName(Browser::SEAMONKEY);
|
794 |
+
|
795 |
+
return true;
|
796 |
+
}
|
797 |
+
}
|
798 |
+
|
799 |
+
return false;
|
800 |
+
}
|
801 |
+
|
802 |
+
/**
|
803 |
+
* Determine if the browser is Iceweasel.
|
804 |
+
*
|
805 |
+
* @return bool
|
806 |
+
*/
|
807 |
+
public static function checkBrowserIceweasel()
|
808 |
+
{
|
809 |
+
if (stripos(self::$userAgentString, 'Iceweasel') !== false) {
|
810 |
+
$aresult = explode('/', stristr(self::$userAgentString, 'Iceweasel'));
|
811 |
+
if (isset($aresult[1])) {
|
812 |
+
$aversion = explode(' ', $aresult[1]);
|
813 |
+
self::$browser->setVersion($aversion[0]);
|
814 |
+
}
|
815 |
+
self::$browser->setName(Browser::ICEWEASEL);
|
816 |
+
|
817 |
+
return true;
|
818 |
+
}
|
819 |
+
|
820 |
+
return false;
|
821 |
+
}
|
822 |
+
|
823 |
+
/**
|
824 |
+
* Determine if the browser is Mozilla.
|
825 |
+
*
|
826 |
+
* @return bool
|
827 |
+
*/
|
828 |
+
public static function checkBrowserMozilla()
|
829 |
+
{
|
830 |
+
if (stripos(self::$userAgentString, 'mozilla') !== false &&
|
831 |
+
preg_match('/rv:[0-9].[0-9][a-b]?/i', self::$userAgentString) &&
|
832 |
+
stripos(self::$userAgentString, 'netscape') === false
|
833 |
+
) {
|
834 |
+
$aversion = explode(' ', stristr(self::$userAgentString, 'rv:'));
|
835 |
+
preg_match('/rv:[0-9].[0-9][a-b]?/i', self::$userAgentString, $aversion);
|
836 |
+
self::$browser->setVersion(str_replace('rv:', '', $aversion[0]));
|
837 |
+
self::$browser->setName(Browser::MOZILLA);
|
838 |
+
|
839 |
+
return true;
|
840 |
+
} elseif (stripos(self::$userAgentString, 'mozilla') !== false &&
|
841 |
+
preg_match('/rv:[0-9]\.[0-9]/i', self::$userAgentString) &&
|
842 |
+
stripos(self::$userAgentString, 'netscape') === false
|
843 |
+
) {
|
844 |
+
$aversion = explode('', stristr(self::$userAgentString, 'rv:'));
|
845 |
+
self::$browser->setVersion(str_replace('rv:', '', $aversion[0]));
|
846 |
+
self::$browser->setName(Browser::MOZILLA);
|
847 |
+
|
848 |
+
return true;
|
849 |
+
} elseif (stripos(self::$userAgentString, 'mozilla') !== false &&
|
850 |
+
preg_match('/mozilla\/([^ ]*)/i', self::$userAgentString, $matches) &&
|
851 |
+
stripos(self::$userAgentString, 'netscape') === false
|
852 |
+
) {
|
853 |
+
if (isset($matches[1])) {
|
854 |
+
self::$browser->setVersion($matches[1]);
|
855 |
+
}
|
856 |
+
self::$browser->setName(Browser::MOZILLA);
|
857 |
+
|
858 |
+
return true;
|
859 |
+
}
|
860 |
+
|
861 |
+
return false;
|
862 |
+
}
|
863 |
+
|
864 |
+
/**
|
865 |
+
* Determine if the browser is Lynx.
|
866 |
+
*
|
867 |
+
* @return bool
|
868 |
+
*/
|
869 |
+
public static function checkBrowserLynx()
|
870 |
+
{
|
871 |
+
if (stripos(self::$userAgentString, 'lynx') !== false) {
|
872 |
+
$aresult = explode('/', stristr(self::$userAgentString, 'Lynx'));
|
873 |
+
$aversion = explode(' ', (isset($aresult[1]) ? $aresult[1] : ''));
|
874 |
+
self::$browser->setVersion($aversion[0]);
|
875 |
+
self::$browser->setName(Browser::LYNX);
|
876 |
+
|
877 |
+
return true;
|
878 |
+
}
|
879 |
+
|
880 |
+
return false;
|
881 |
+
}
|
882 |
+
|
883 |
+
/**
|
884 |
+
* Determine if the browser is Amaya.
|
885 |
+
*
|
886 |
+
* @return bool
|
887 |
+
*/
|
888 |
+
public static function checkBrowserAmaya()
|
889 |
+
{
|
890 |
+
if (stripos(self::$userAgentString, 'amaya') !== false) {
|
891 |
+
$aresult = explode('/', stristr(self::$userAgentString, 'Amaya'));
|
892 |
+
if (isset($aresult[1])) {
|
893 |
+
$aversion = explode(' ', $aresult[1]);
|
894 |
+
self::$browser->setVersion($aversion[0]);
|
895 |
+
}
|
896 |
+
self::$browser->setName(Browser::AMAYA);
|
897 |
+
|
898 |
+
return true;
|
899 |
+
}
|
900 |
+
|
901 |
+
return false;
|
902 |
+
}
|
903 |
+
|
904 |
+
/**
|
905 |
+
* Determine if the browser is Safari.
|
906 |
+
*
|
907 |
+
* @return bool
|
908 |
+
*/
|
909 |
+
public static function checkBrowserWkhtmltopdf()
|
910 |
+
{
|
911 |
+
if (stripos(self::$userAgentString, 'wkhtmltopdf') !== false) {
|
912 |
+
self::$browser->setName(Browser::WKHTMLTOPDF);
|
913 |
+
return true;
|
914 |
+
}
|
915 |
+
|
916 |
+
return false;
|
917 |
+
}
|
918 |
+
/**
|
919 |
+
* Determine if the browser is Safari.
|
920 |
+
*
|
921 |
+
* @return bool
|
922 |
+
*/
|
923 |
+
public static function checkBrowserSafari()
|
924 |
+
{
|
925 |
+
if (stripos(self::$userAgentString, 'Safari') !== false) {
|
926 |
+
$aresult = explode('/', stristr(self::$userAgentString, 'Version'));
|
927 |
+
if (isset($aresult[1])) {
|
928 |
+
$aversion = explode(' ', $aresult[1]);
|
929 |
+
self::$browser->setVersion($aversion[0]);
|
930 |
+
} else {
|
931 |
+
self::$browser->setVersion(Browser::VERSION_UNKNOWN);
|
932 |
+
}
|
933 |
+
self::$browser->setName(Browser::SAFARI);
|
934 |
+
|
935 |
+
return true;
|
936 |
+
}
|
937 |
+
|
938 |
+
return false;
|
939 |
+
}
|
940 |
+
|
941 |
+
/**
|
942 |
+
* Determine if the browser is Yandex.
|
943 |
+
*
|
944 |
+
* @return bool
|
945 |
+
*/
|
946 |
+
public static function checkBrowserYandex()
|
947 |
+
{
|
948 |
+
if (stripos(self::$userAgentString, 'YaBrowser') !== false) {
|
949 |
+
$aresult = explode('/', stristr(self::$userAgentString, 'YaBrowser'));
|
950 |
+
if (isset($aresult[1])) {
|
951 |
+
$aversion = explode(' ', $aresult[1]);
|
952 |
+
self::$browser->setVersion($aversion[0]);
|
953 |
+
}
|
954 |
+
self::$browser->setName(Browser::YANDEX);
|
955 |
+
|
956 |
+
return true;
|
957 |
+
}
|
958 |
+
|
959 |
+
return false;
|
960 |
+
}
|
961 |
+
|
962 |
+
/**
|
963 |
+
* Determine if the browser is Comodo Dragon / Ice Dragon / Chromodo.
|
964 |
+
*
|
965 |
+
* @return bool
|
966 |
+
*/
|
967 |
+
public static function checkBrowserDragon()
|
968 |
+
{
|
969 |
+
if (stripos(self::$userAgentString, 'Dragon') !== false) {
|
970 |
+
$aresult = explode('/', stristr(self::$userAgentString, 'Dragon'));
|
971 |
+
if (isset($aresult[1])) {
|
972 |
+
$aversion = explode(' ', $aresult[1]);
|
973 |
+
self::$browser->setVersion($aversion[0]);
|
974 |
+
}
|
975 |
+
self::$browser->setName(Browser::DRAGON);
|
976 |
+
|
977 |
+
return true;
|
978 |
+
}
|
979 |
+
|
980 |
+
return false;
|
981 |
+
}
|
982 |
+
|
983 |
+
/**
|
984 |
+
* Determine if the browser is Android.
|
985 |
+
*
|
986 |
+
* @return bool
|
987 |
+
*/
|
988 |
+
public static function checkBrowserAndroid()
|
989 |
+
{
|
990 |
+
// Navigator
|
991 |
+
if (stripos(self::$userAgentString, 'Android') !== false) {
|
992 |
+
if (preg_match('/Version\/([\d\.]*)/i', self::$userAgentString, $matches)) {
|
993 |
+
if (isset($matches[1])) {
|
994 |
+
self::$browser->setVersion($matches[1]);
|
995 |
+
}
|
996 |
+
} else {
|
997 |
+
self::$browser->setVersion(Browser::VERSION_UNKNOWN);
|
998 |
+
}
|
999 |
+
self::$browser->setName(Browser::NAVIGATOR);
|
1000 |
+
|
1001 |
+
return true;
|
1002 |
+
}
|
1003 |
+
|
1004 |
+
return false;
|
1005 |
+
}
|
1006 |
+
}
|
vendor/sinergi/browser-detector/src/DetectorInterface.php
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Sinergi\BrowserDetector;
|
4 |
+
|
5 |
+
interface DetectorInterface
|
6 |
+
{
|
7 |
+
}
|
vendor/sinergi/browser-detector/src/InvalidArgumentException.php
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Sinergi\BrowserDetector;
|
4 |
+
|
5 |
+
class InvalidArgumentException extends \InvalidArgumentException
|
6 |
+
{
|
7 |
+
}
|
vendor/sinergi/browser-detector/src/UserAgent.php
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Sinergi\BrowserDetector;
|
4 |
+
|
5 |
+
class UserAgent
|
6 |
+
{
|
7 |
+
/**
|
8 |
+
* @var string
|
9 |
+
*/
|
10 |
+
private $userAgentString;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* @param string $userAgentString
|
14 |
+
*/
|
15 |
+
public function __construct($userAgentString = null)
|
16 |
+
{
|
17 |
+
if (null !== $userAgentString) {
|
18 |
+
$this->setUserAgentString($userAgentString);
|
19 |
+
}
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @param string $userAgentString
|
24 |
+
*
|
25 |
+
* @return $this
|
26 |
+
*/
|
27 |
+
public function setUserAgentString($userAgentString)
|
28 |
+
{
|
29 |
+
$this->userAgentString = (string)$userAgentString;
|
30 |
+
|
31 |
+
return $this;
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* @return string
|
36 |
+
*/
|
37 |
+
public function getUserAgentString()
|
38 |
+
{
|
39 |
+
if (null === $this->userAgentString) {
|
40 |
+
$this->createUserAgentString();
|
41 |
+
}
|
42 |
+
|
43 |
+
return $this->userAgentString;
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* @return string
|
48 |
+
*/
|
49 |
+
public function createUserAgentString()
|
50 |
+
{
|
51 |
+
$userAgentString = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null;
|
52 |
+
$this->setUserAgentString($userAgentString);
|
53 |
+
|
54 |
+
return $userAgentString;
|
55 |
+
}
|
56 |
+
}
|
views/base.php
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die;
|
2 |
+
|
3 |
+
$file = trailingslashit( __DIR__ ) . "{$view}.php";
|
4 |
+
|
5 |
+
$file = apply_filters( 'site-reviews/addon/views/file', $file, $view, $data );
|
6 |
+
|
7 |
+
if( file_exists( $file ) ) {
|
8 |
+
include $file;
|
9 |
+
}
|
10 |
+
else {
|
11 |
+
$log->error( sprintf( 'File not found: %s', $file ) );
|
12 |
+
}
|
views/edit/meta.php
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die; ?>
|
2 |
+
|
3 |
+
<?php
|
4 |
+
$siteName = get_post_meta( $post->ID, 'site_name', true );
|
5 |
+
|
6 |
+
if( $siteName == 'local' ) {
|
7 |
+
$siteName = __( 'Local Review', 'site-reviews' );
|
8 |
+
}
|
9 |
+
|
10 |
+
$reviewUrl = get_post_meta( $post->ID, 'url', true );
|
11 |
+
|
12 |
+
if( $reviewUrl ) {
|
13 |
+
$siteName = sprintf( '<a href="%s" target="_blank">%s</a>', $reviewUrl, ucfirst( $siteName ) );
|
14 |
+
}
|
15 |
+
|
16 |
+
$modified = false;
|
17 |
+
|
18 |
+
if( $post->post_title !== get_post_meta( $post->ID, 'title', true )
|
19 |
+
|| $post->post_content !== get_post_meta( $post->ID, 'content', true )
|
20 |
+
|| $post->post_date !== get_post_meta( $post->ID, 'date', true ) ) {
|
21 |
+
$modified = true;
|
22 |
+
}
|
23 |
+
|
24 |
+
$revertUrl = wp_nonce_url( admin_url( sprintf( 'post.php?post=%s&action=revert', $post->ID ) ), 'revert-review_' . $post->ID );
|
25 |
+
|
26 |
+
$revert = !$modified
|
27 |
+
? __( 'Nothing to Revert', 'site-reviews' )
|
28 |
+
: __( 'Revert Changes', 'site-reviews' );
|
29 |
+
|
30 |
+
$revertButton = !$modified
|
31 |
+
? sprintf( '<button id="revert" class="button button-large" disabled>%s</button>', $revert )
|
32 |
+
: sprintf( '<a href="%s" id="revert" class="button button-large">%s</a>', $revertUrl, $revert );
|
33 |
+
?>
|
34 |
+
|
35 |
+
<table class="glsr-metabox-table">
|
36 |
+
<tbody>
|
37 |
+
<tr>
|
38 |
+
<td><?= __( 'Rating', 'site-reviews' ); ?></th>
|
39 |
+
<td><?php $html->renderPartial( 'rating', ['stars' => get_post_meta( $post->ID, 'rating', true ) ] ); ?></td>
|
40 |
+
</tr>
|
41 |
+
<tr>
|
42 |
+
<td><?= __( 'Type', 'site-reviews' ); ?></th>
|
43 |
+
<td><?= $siteName; ?></td>
|
44 |
+
</tr>
|
45 |
+
<tr>
|
46 |
+
<td><?= __( 'Date', 'site-reviews' ); ?></th>
|
47 |
+
<td><?= get_date_from_gmt( get_post_meta( $post->ID, 'date', true ), 'F j, Y' ); ?></td>
|
48 |
+
</tr>
|
49 |
+
<tr>
|
50 |
+
<td><?= __( 'Reviewer', 'site-reviews' ); ?></th>
|
51 |
+
<td><?= get_post_meta( $post->ID, 'author', true ); ?></td>
|
52 |
+
</tr>
|
53 |
+
<tr>
|
54 |
+
<td><?= __( 'Avatar', 'site-reviews' ); ?></th>
|
55 |
+
<td><img src="<?= get_post_meta( $post->ID, 'avatar', true ); ?>" width="96"></td>
|
56 |
+
</tr>
|
57 |
+
</tbody>
|
58 |
+
</table>
|
59 |
+
|
60 |
+
<div class="revert-action">
|
61 |
+
<span class="spinner"></span>
|
62 |
+
<?= $revertButton; ?>
|
63 |
+
</div>
|
views/edit/notice.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die;
|
2 |
+
|
3 |
+
$notices->show();
|
views/edit/pinned.php
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die; ?>
|
2 |
+
|
3 |
+
<div id="misc-pub-pinned" class="misc-pub-section misc-pub-pinned">
|
4 |
+
|
5 |
+
<?php
|
6 |
+
global $wp_version;
|
7 |
+
|
8 |
+
// WP < 4.4 support
|
9 |
+
if( version_compare( $wp_version, '4.4', '<' ) ) {
|
10 |
+
printf( '<span class="pinned-icon">%s</span>', file_get_contents( glsr_app()->path . 'assets/img/pinned.svg' ) );
|
11 |
+
}
|
12 |
+
|
13 |
+
$pinnedNo = __( 'No', 'site-reviews' );
|
14 |
+
$pinnedYes = __( 'Yes', 'site-reviews' );
|
15 |
+
?>
|
16 |
+
|
17 |
+
<label for="pinned-status"><?= __( 'Pinned', 'site-reviews' ) ?>:</label>
|
18 |
+
<span id="pinned-status-text" class="pinned-status-text"><?= $pinned ? $pinnedYes : $pinnedNo; ?></span>
|
19 |
+
|
20 |
+
<a href="#pinned-status" class="edit-pinned-status hide-if-no-js">
|
21 |
+
<span aria-hidden="true"><?= __( 'Edit', 'site-reviews' ); ?></span>
|
22 |
+
<span class="screen-reader-text"><?= __( 'Edit pinned status', 'site-reviews' ); ?></span>
|
23 |
+
</a>
|
24 |
+
|
25 |
+
<div id="pinned-status-select" class="pinned-status-select hide-if-js">
|
26 |
+
|
27 |
+
<input type="hidden" id="hidden-pinned-status" value="<?= intval( $pinned ); ?>">
|
28 |
+
|
29 |
+
<select name="pinned" id="pinned-status">
|
30 |
+
<option value="1"<?php selected( $pinned, false ); ?>><?= __( 'Pin', 'site-reviews' ); ?></option>
|
31 |
+
<option value="0"<?php selected( $pinned, true ); ?>><?= __( 'Unpin', 'site-reviews' ); ?></option>
|
32 |
+
</select>
|
33 |
+
|
34 |
+
<a href="#pinned-status" class="save-pinned-status hide-if-no-js button" data-no="<?= $pinnedNo; ?>" data-yes="<?= $pinnedYes; ?>"><?= __( 'OK', 'site-reviews' ); ?></a>
|
35 |
+
<a href="#pinned-status" class="cancel-pinned-status hide-if-no-js button-cancel"><?= __( 'Cancel', 'site-reviews' ); ?></a>
|
36 |
+
|
37 |
+
</div>
|
38 |
+
|
39 |
+
</div>
|
views/edit/review.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die; ?>
|
2 |
+
|
3 |
+
<div id="titlediv">
|
4 |
+
<input type="text" id="title" value="<?= $post->post_title ? esc_attr( $post->post_title ) : sprintf( '(%s)', __( 'no title', 'site-reviews' ) ); ?>" readonly>
|
5 |
+
</div>
|
6 |
+
|
7 |
+
<div id="contentdiv">
|
8 |
+
<textarea readonly><?= esc_attr( $post->post_content ); ?></textarea>
|
9 |
+
</div>
|
views/email/body.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die; ?>
|
2 |
+
|
3 |
+
{message}
|
views/email/footer.php
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die; ?>
|
2 |
+
|
3 |
+
</body>
|
4 |
+
</html>
|
views/email/header.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die; ?>
|
2 |
+
|
3 |
+
<!doctype html>
|
4 |
+
<html>
|
5 |
+
<head>
|
6 |
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
7 |
+
<title><?= wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); ?></title>
|
8 |
+
</head>
|
9 |
+
<body>
|
views/email/index.php
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die;
|
2 |
+
|
3 |
+
include trailingslashit(__DIR__) . "header.php";
|
4 |
+
include trailingslashit(__DIR__) . "body.php";
|
5 |
+
include trailingslashit(__DIR__) . "footer.php";
|
views/email/templates/review-notification.php
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die; ?>
|
2 |
+
|
3 |
+
<strong>A new {review_rating}-star review has been submitted:</strong>
|
4 |
+
|
5 |
+
{review_title}
|
6 |
+
|
7 |
+
{review_content}
|
8 |
+
|
9 |
+
{review_author} <{review_email}> - {review_ip}
|
10 |
+
|
11 |
+
{review_link}
|
views/menu/addons/addons.php
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die; ?>
|
2 |
+
|
3 |
+
<p><?= __( 'The following Add-Ons extend the functionality of Site Reviews.', 'site-reviews' ); ?></p>
|
4 |
+
|
5 |
+
<div class="glsr-addons wp-clearfix">
|
6 |
+
|
7 |
+
<?php
|
8 |
+
|
9 |
+
$html->renderPartial( 'addon', [
|
10 |
+
'name' => 'tripadvisor',
|
11 |
+
'title' => 'Coming soon...',
|
12 |
+
'description' => __( 'Sync your Tripadvisor business reviews with an optional minimum rating and display them on your site.', 'site-reviews' ),
|
13 |
+
'link' => '',
|
14 |
+
]);
|
15 |
+
|
16 |
+
$html->renderPartial( 'addon', [
|
17 |
+
'name' => 'yelp',
|
18 |
+
'title' => 'Coming soon...',
|
19 |
+
'description' => __( 'Sync your Yelp business reviews with an optional minimum rating and display them on your site.', 'site-reviews' ),
|
20 |
+
'link' => '',
|
21 |
+
]);
|
22 |
+
|
23 |
+
?>
|
24 |
+
|
25 |
+
</div>
|
views/menu/help/documentation/hooks.php
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die; ?>
|
2 |
+
|
3 |
+
<p>Hooks (also known as Filters & Actions) are used to make changes to the plugin without modifying the core files of the plugin directly. In order to use the following hooks, you must add them to your theme's <code>functions.php</code> file.</p>
|
4 |
+
|
5 |
+
<div class="glsr-card card">
|
6 |
+
<h3>Change the review excerpt length</h3>
|
7 |
+
<pre><code>add_filter( 'site-reviews/reviews/excerpt_length', function() { return 55; });</code></pre>
|
8 |
+
<p>Use this hook if you want to modify the excerpt word length (the default excerpt length is 55 words).</p>
|
9 |
+
</div>
|
10 |
+
|
11 |
+
<div class="glsr-card card">
|
12 |
+
<h3>Disable the plugin CSS</h3>
|
13 |
+
<pre><code>add_filter( 'site-reviews/assets/css', '__return_false' );</code></pre>
|
14 |
+
<p>Use this hook if you want to disable the plugin stylesheet from loading on your website.</p>
|
15 |
+
</div>
|
16 |
+
|
17 |
+
<div class="glsr-card card">
|
18 |
+
<h3>Disable the plugin javascript</h3>
|
19 |
+
<pre><code>add_filter( 'site-reviews/assets/js', '__return_false' );</code></pre>
|
20 |
+
<p>Use this hook if you want to disable the plugin javascript from loading on your website.</p>
|
21 |
+
</div>
|
22 |
+
|
23 |
+
<div class="glsr-card card">
|
24 |
+
<h3>Display the full review instead of an excerpt</h3>
|
25 |
+
<pre><code>add_filter( 'site-reviews/reviews/use_excerpt', '__return_false' );</code></pre>
|
26 |
+
<p>Use this hook if you want to show the full review content instead of just an excerpt.</p>
|
27 |
+
</div>
|
28 |
+
|
views/menu/help/documentation/shortcodes.php
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die; ?>
|
2 |
+
|
3 |
+
<div class="glsr-card card">
|
4 |
+
<h3>[site_reviews_form]</h3>
|
5 |
+
<p>This shortcode displays the review submission form.</p>
|
6 |
+
<code>id="type some random text here"</code>
|
7 |
+
<p>This shortcode should only be used on a page once. However, if for any reason you need to include more than one on a page, add the "id" attribute to each with some random text to make it a unique shortcode form.</p>
|
8 |
+
<code>title="Submit a Review"</code>
|
9 |
+
<p>By default, the shortcode displays no heading. Include the "title" attribute to display a custom shortcode heading.</p>
|
10 |
+
<code>description="Required fields are marked <span>*</span>"</code>
|
11 |
+
<p>By default, the shortcode displays no description. Include the "description" attribute to display a custom shortcode description.</p>
|
12 |
+
<code>hide=title,reviewer,email,terms</code>
|
13 |
+
<p>Add the "hide" attribute to exclude certain fields in the form.</p>
|
14 |
+
<code>class="my-reviews-form"</code>
|
15 |
+
<p>Include the "class" attribute to add custom CSS classes to the shortcode form.</p>
|
16 |
+
</div>
|
17 |
+
|
18 |
+
<div class="glsr-card card">
|
19 |
+
<h3>[site_reviews]</h3>
|
20 |
+
<p>This shortcode displays your most recent submitted reviews.</p>
|
21 |
+
<code>title="Our Reviews"</code>
|
22 |
+
<p>By default, the shortcode displays no heading. Include the "title" attribute to display a custom shortcode heading.</p>
|
23 |
+
<code>display=title,excerpt,author,date,rating,url</code>
|
24 |
+
<p>By default, the shortcode displays all review fields. Include the "display" attribute to display only the specific fields you want.</p>
|
25 |
+
<code>count=10</code>
|
26 |
+
<p>By default, the shortcode displays the latest 10 reviews. Include the "count" attribute to change the number of reviews that are displayed.</p>
|
27 |
+
<code>rating=4</code>
|
28 |
+
<p>By default, the shortcode displays only 5-star reviews. Include the "rating" attribute to set the minimum star-rating of reviews to display.</p>
|
29 |
+
<code>class="my-reviews full-width"</code>
|
30 |
+
<p>Include the "class" attribute to add custom CSS classes to the shortcode.</p>
|
31 |
+
<code>pagination=true</code>
|
32 |
+
<p>Include the "pagination" attribute to display reviews in multiple pages (i.e. Page 1, Page 2, etc.). When using pagination, only one [site_reviews] shortcode can be used on a page at a time.</p>
|
33 |
+
</div>
|
views/menu/help/documentation/support.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die; ?>
|
2 |
+
|
3 |
+
<div class="glsr-card card">
|
4 |
+
<h3>F.A.Q.</h3>
|
5 |
+
<dl>
|
6 |
+
<dt>I don't want form placeholders. How do I remove them?</dt>
|
7 |
+
<dd>To remove the form placeholders, go to <code><a href="<?= admin_url( 'edit.php?post_type=site-review&page=settings&tab=settings§ion=form' ); ?>">Settings -> Submission Form</a></code> and replace all placeholder text with a single space character. Placeholders no more!</dd>
|
8 |
+
|
9 |
+
<dt>How do I disable the plugin CSS and/or Javascript?</dt>
|
10 |
+
<dd>To disable the plugin stylesheet or javascript from loading on your website, copy and paste the provided <code><a href="<?= admin_url( 'edit.php?post_type=site-review&page=help&tab=documentation§ion=hooks' ); ?>">WordPress Filter Hooks</a></code> into your theme's <code>functions.php</code> file.</dd>
|
11 |
+
</dl>
|
12 |
+
</div>
|
13 |
+
|
14 |
+
<div class="glsr-card card">
|
15 |
+
<h3>Basic Troubleshooting</h3>
|
16 |
+
<ol>
|
17 |
+
<li>
|
18 |
+
<p><strong>Make sure you are using the latest version of Site Reviews.</strong></p>
|
19 |
+
<p>Site Reviews is updated frequently with bug patches, security updates, code improvements, and new features. It’s always best to be on the latest version of Site Reviews, and if you are not, chances are that if you are experiencing a real bug it has already been fixed.</p>
|
20 |
+
</li>
|
21 |
+
<li>
|
22 |
+
<p><strong>Switch to the default “Twenty Sixteen” WordPress Theme.</strong></p>
|
23 |
+
<p>If you have jQuery or Javascript errors on your page, try switching to the default “"Twenty Sixteen” WordPress Theme and then see if you are still experiencing problems with the plugin. This is especially true if you have purchased a third party theme or had a theme developed by someone else. Theme authors are notorious for improperly adding their own Javascript and/or jQuery.</p>
|
24 |
+
</li>
|
25 |
+
<li>
|
26 |
+
<p><strong>Deactivate all of your plugins.</strong></p>
|
27 |
+
<p>If switching to the default WordPress theme did not fix anything, the next thing to try is to deactivate all your plugins and then activate them one at a time, starting with Site Reviews, and reloading the page between each activation. Once the Javascript on your page stops working properly, you have probably found the problem plugin. If you think that you’ve found the problem plugin, deactivate it and go through the rest of your plugins anyway. Hopefully you don’t find any more, but it’s better to be safe than sorry.</p>
|
28 |
+
</li>
|
29 |
+
</ol>
|
30 |
+
</div>
|
31 |
+
|
32 |
+
<div class="glsr-card card">
|
33 |
+
<h3>Contacting Support</h3>
|
34 |
+
<p>If none of the above troubleshooting steps help, then congratulations you <em>may</em> just have found a pesky little bug that must be squashed!</p>
|
35 |
+
<p>Send an email to <a href="mailto:site-reviews@geminilabs.io?subject=Support%20request">site-reviews@geminilabs.io</a> and make sure to write a detailed description of the problem you are having and the steps needed to reproduce it.</p>
|
36 |
+
<p><span class="required">Important:</span> Please also make sure to attach the <em>System Info</em> report to your email. You can download it from the <a href="<?= admin_url( 'edit.php?post_type=site-review&page=help&tab=system' ); ?>">System Info</a> tab.</p>
|
37 |
+
</div>
|
views/menu/help/system.php
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die; ?>
|
2 |
+
|
3 |
+
<br>
|
4 |
+
|
5 |
+
<form method="post"><?php
|
6 |
+
wp_nonce_field( 'download-system-info' );
|
7 |
+
printf( '<textarea rows="13" name="%s[system-info]" class="glsr-log" onclick="this.select()" readonly>%s</textarea>', glsr_app()->prefix, $system_info->getAll() );
|
8 |
+
printf( '<input type="hidden" name="%s[action]" value="download-system-info">', glsr_app()->prefix );
|
9 |
+
submit_button( __( 'Download System Info', 'site-reviews' ), 'secondary', '', false ); ?>
|
10 |
+
</form>
|
11 |
+
|
12 |
+
<br>
|
13 |
+
|
14 |
+
<h3><?= __( 'Logging', 'site-reviews' ); ?></h3>
|
15 |
+
<p><?= __( "You can enable logging for debugging purposes. Log files can grow quickly, so don't leave this on.", 'site-reviews' ); ?></p>
|
16 |
+
|
17 |
+
<?php $logging_enabled = $db->getOption( '', '', 'logging' ); ?>
|
18 |
+
|
19 |
+
<?php if( $logging_enabled == 1 ) : ?>
|
20 |
+
|
21 |
+
<textarea rows="13" id="log-file" class="glsr-log" onclick="this.select()" readonly><?= $log; ?></textarea>
|
22 |
+
|
23 |
+
<form method="post" class="float-left"><?php
|
24 |
+
wp_nonce_field( 'clear-log' );
|
25 |
+
printf( '<input type="hidden" name="%s[action]" value="clear-log">', glsr_app()->prefix );
|
26 |
+
submit_button( __( 'Clear Log', 'site-reviews' ), 'secondary', 'clear-log', false ); ?>
|
27 |
+
</form>
|
28 |
+
|
29 |
+
<form method="post"><?php
|
30 |
+
wp_nonce_field( 'download-log' );
|
31 |
+
printf( '<input type="hidden" name="%s[action]" value="download-log">', glsr_app()->prefix );
|
32 |
+
submit_button( __( 'Download Log', 'site-reviews' ), 'secondary', '', false ); ?>
|
33 |
+
</form>
|
34 |
+
|
35 |
+
<?php endif; ?>
|
36 |
+
|
37 |
+
<form method="post" action="<?= admin_url('options.php'); ?>"><?php
|
38 |
+
|
39 |
+
settings_fields( glsr_app()->id . '-logging' );
|
40 |
+
do_settings_sections( glsr_app()->id . '-logging' );
|
41 |
+
|
42 |
+
if( $logging_enabled == 1 ) {
|
43 |
+
printf( '<input type="hidden" name="%s_logging" value="0">', glsr_app()->prefix );
|
44 |
+
submit_button( __( 'Disable Logging', 'site-reviews' ) );
|
45 |
+
}
|
46 |
+
else {
|
47 |
+
printf( '<input type="hidden" name="%s_logging" value="1">', glsr_app()->prefix );
|
48 |
+
submit_button( __( 'Enable Logging', 'site-reviews' ) );
|
49 |
+
}?>
|
50 |
+
</form>
|
views/menu/index.php
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die; ?>
|
2 |
+
|
3 |
+
<div class="wrap">
|
4 |
+
|
5 |
+
<h1 class="page-title"><?= esc_html( get_admin_page_title() ); ?></h1>
|
6 |
+
|
7 |
+
<?php
|
8 |
+
|
9 |
+
printf( '<div id="glsr-notices">%s</div>', $notices->show( false ) );
|
10 |
+
|
11 |
+
$html->renderPartial( 'tabs' , [
|
12 |
+
'page' => $page,
|
13 |
+
'tabs' => $tabs,
|
14 |
+
'tab' => $tabView,
|
15 |
+
]);
|
16 |
+
|
17 |
+
$html->renderPartial( 'subsubsub' , [
|
18 |
+
'page' => $page,
|
19 |
+
'tabs' => $tabs,
|
20 |
+
'tab' => $tabView,
|
21 |
+
'section' => $tabViewSection,
|
22 |
+
]);
|
23 |
+
|
24 |
+
$file = $tabViewSection ? sprintf( '%s/%s', $tabView, $tabViewSection ) : $tabView;
|
25 |
+
$file = trailingslashit( __DIR__ ) . sprintf( '%s/%s.php', $page, $file );
|
26 |
+
|
27 |
+
$file = apply_filters( 'site-reviews/addon/views/file', $file, $view, $data );
|
28 |
+
|
29 |
+
if( file_exists( $file ) ) {
|
30 |
+
include $file;
|
31 |
+
}
|
32 |
+
else {
|
33 |
+
$log->error( sprintf( 'File not found: %s', $file ) );
|
34 |
+
}
|
35 |
+
|
36 |
+
?>
|
37 |
+
|
38 |
+
</div>
|
views/menu/settings/licenses.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die; ?>
|
2 |
+
|
3 |
+
<br>
|
views/menu/settings/settings/form.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die;
|
2 |
+
|
3 |
+
$html->renderForm( "{$tabView}/{$tabViewSection}" );
|
views/menu/settings/settings/general.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die;
|
2 |
+
|
3 |
+
$html->renderForm( "{$tabView}/{$tabViewSection}" );
|
views/menu/settings/welcome.php
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die; ?>
|
2 |
+
|
3 |
+
<div id="welcome-panel" class="welcome-panel">
|
4 |
+
<a class="welcome-panel-close" href="<?= admin_url( 'edit.php?post_type=site-review&page=site-reviews&welcome=0' ); ?>" aria-label="Dismiss the welcome panel">Dismiss</a>
|
5 |
+
<div class="welcome-panel-content">
|
6 |
+
|
7 |
+
<h2>Thank you for installing Reviews!</h2>
|
8 |
+
<p class="about-description">Here is how to get started:</p>
|
9 |
+
|
10 |
+
<div class="welcome-panel-column-container">
|
11 |
+
|
12 |
+
<div class="welcome-panel-column" style="width:68%;">
|
13 |
+
<h3>Get Started</h3>
|
14 |
+
<p></p>
|
15 |
+
</div>
|
16 |
+
|
17 |
+
<div class="welcome-panel-column" style="width:32%;">
|
18 |
+
<h3>More Actions</h3>
|
19 |
+
<ul>
|
20 |
+
<li><div class="welcome-icon welcome-widgets-menus">Manage <a href="<?= admin_url( 'widgets.php' ); ?>">widgets</a></div></li>
|
21 |
+
</ul>
|
22 |
+
</div>
|
23 |
+
|
24 |
+
</div>
|
25 |
+
</div>
|
26 |
+
</div>
|
views/menu/status.php
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die; ?>
|
2 |
+
|
3 |
+
<form method="post" action="">
|
4 |
+
|
5 |
+
<?php
|
6 |
+
echo $html->p( sprintf(
|
7 |
+
__( "All dates and times shown here use the WordPress <a href='%s'>configured timezone</a>.", 'site-reviews' ),
|
8 |
+
admin_url( 'options-general.php' )
|
9 |
+
));
|
10 |
+
?>
|
11 |
+
|
12 |
+
<table class="wp-list-table widefat fixed striped glsr-status">
|
13 |
+
|
14 |
+
<thead>
|
15 |
+
<tr>
|
16 |
+
<th class="site"><?= __( 'Site', 'site-reviews' ); ?></th>
|
17 |
+
<th class="total-fetched column-primary"><?= __( 'Reviews', 'site-reviews' ); ?></th>
|
18 |
+
<th class="last-fetch"><?= __( 'Last fetch', 'site-reviews' ); ?></th>
|
19 |
+
<th class="next-fetch"><?= __( 'Next scheduled fetch', 'site-reviews' ); ?></th>
|
20 |
+
</tr>
|
21 |
+
</thead>
|
22 |
+
|
23 |
+
<tbody>
|
24 |
+
|
25 |
+
<?php foreach( $tabs['settings']['sections'] as $key => $title ) : ?>
|
26 |
+
|
27 |
+
<tr data-site="<?= $key; ?>">
|
28 |
+
<td class="site">
|
29 |
+
<a href="<?= admin_url( 'edit.php?post_type=site-review&page=' . glsr_app()->id . "&tab=settings§ion={$key}" ); ?>"><?= $title; ?></a>
|
30 |
+
</td>
|
31 |
+
<td class="total-fetched column-primary">
|
32 |
+
<a href="<?= admin_url( "edit.php?post_type=site-review&post_status=all&site_name={$key}" ); ?>"><?= $db->countReviews( $key ); ?></a>
|
33 |
+
<button type="button" class="toggle-row"><span class="screen-reader-text">Show more details</span></button>
|
34 |
+
</td>
|
35 |
+
<td class="last-fetch" data-colname="<?= __( 'Last fetch', 'site-reviews' ); ?>">
|
36 |
+
<?= $db->getOption( $key, __( 'No fetch has been completed', 'site-reviews' ), 'last_fetch' ); ?>
|
37 |
+
</td>
|
38 |
+
<td class="next-fetch" data-colname="<?= __( 'Next scheduled fetch', 'site-reviews' ); ?>">
|
39 |
+
<?= $db->getOption( $key, __( 'Nothing currently scheduled', 'site-reviews' ), 'next_fetch' ); ?>
|
40 |
+
</td>
|
41 |
+
</tr>
|
42 |
+
|
43 |
+
<?php endforeach; ?>
|
44 |
+
|
45 |
+
</tbody>
|
46 |
+
|
47 |
+
</table>
|
48 |
+
|
49 |
+
<br>
|
50 |
+
|
51 |
+
<hr>
|
52 |
+
|
53 |
+
<table class="form-table">
|
54 |
+
<tbody>
|
55 |
+
|
56 |
+
<?php
|
57 |
+
|
58 |
+
echo $html->row()->select( 'site', [
|
59 |
+
'label' => __( 'Fetch reviews from', 'site-reviews' ),
|
60 |
+
'options' => $tabs['settings']['sections'],
|
61 |
+
'attributes' => 'data-site',
|
62 |
+
'prefix' => glsr_app()->prefix,
|
63 |
+
]);
|
64 |
+
|
65 |
+
echo $html->row()->progress([
|
66 |
+
'label' => __( 'Fetch status', 'site-reviews' ),
|
67 |
+
'active' => __( 'Fetching reviews...', 'site-reviews' ),
|
68 |
+
'class' => 'green',
|
69 |
+
]);
|
70 |
+
?>
|
71 |
+
|
72 |
+
</tbody>
|
73 |
+
</table>
|
74 |
+
|
75 |
+
<?php wp_nonce_field( 'fetch-reviews' ); ?>
|
76 |
+
|
77 |
+
<?php printf( '<input type="hidden" name="%s[action]" value="fetch-reviews">', glsr_app()->prefix ); ?>
|
78 |
+
|
79 |
+
<?php submit_button( __( 'Fetch Reviews', 'site-reviews' ), 'large primary', 'fetch-reviews' ); ?>
|
80 |
+
|
81 |
+
</form>
|
views/submit/index.php
ADDED
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined( 'WPINC' ) || die; ?>
|
2 |
+
|
3 |
+
<form method="post" action="" name="glsr-<?= $form_id; ?>" class="<?= $class; ?>">
|
4 |
+
<?php
|
5 |
+
|
6 |
+
$html->renderField([
|
7 |
+
'type' => 'select',
|
8 |
+
'name' => 'rating',
|
9 |
+
'class' => 'glsr-star-rating',
|
10 |
+
'errors' => $errors,
|
11 |
+
'label' => $db->getOption( 'form.rating.label' ),
|
12 |
+
'prefix' => false,
|
13 |
+
'render' => !in_array( 'rating', $exclude ),
|
14 |
+
'suffix' => $form_id,
|
15 |
+
'value' => $values['rating'],
|
16 |
+
'options' => [
|
17 |
+
'' => __( 'Select a Rating', 'site-reviews' ),
|
18 |
+
'5' => __( 'Excellent', 'site-reviews' ),
|
19 |
+
'4' => __( 'Very Good', 'site-reviews' ),
|
20 |
+
'3' => __( 'Average', 'site-reviews' ),
|
21 |
+
'2' => __( 'Poor', 'site-reviews' ),
|
22 |
+
'1' => __( 'Terrible', 'site-reviews' ),
|
23 |
+
],
|
24 |
+
]);
|
25 |
+
|
26 |
+
$html->renderField([
|
27 |
+
'type' => 'text',
|
28 |
+
'name' => 'title',
|
29 |
+
'errors' => $errors,
|
30 |
+
'label' => $db->getOption( 'form.title.label' ),
|
31 |
+
'placeholder' => $db->getOption( 'form.title.placeholder' ),
|
32 |
+
'prefix' => false,
|
33 |
+
'render' => !in_array( 'title', $exclude ),
|
34 |
+
'required' => true,
|
35 |
+
'suffix' => $form_id,
|
36 |
+
'value' => $values['title'],
|
37 |
+
]);
|
38 |
+
|
39 |
+
$html->renderField([
|
40 |
+
'type' => 'textarea',
|
41 |
+
'name' => 'content',
|
42 |
+
'errors' => $errors,
|
43 |
+
'label' => $db->getOption( 'form.content.label' ),
|
44 |
+
'placeholder' => $db->getOption( 'form.content.placeholder' ),
|
45 |
+
'prefix' => false,
|
46 |
+
'rows' => 5,
|
47 |
+
'render' => !in_array( 'content', $exclude ),
|
48 |
+
'required' => true,
|
49 |
+
'suffix' => $form_id,
|
50 |
+
'value' => $values['content'],
|
51 |
+
]);
|
52 |
+
|
53 |
+
$html->renderField([
|
54 |
+
'type' => 'text',
|
55 |
+
'name' => 'reviewer',
|
56 |
+
'errors' => $errors,
|
57 |
+
'label' => $db->getOption( 'form.reviewer.label' ),
|
58 |
+
'placeholder' => $db->getOption( 'form.reviewer.placeholder' ),
|
59 |
+
'prefix' => false,
|
60 |
+
'render' => !in_array( 'reviewer', $exclude ),
|
61 |
+
'required' => true,
|
62 |
+
'suffix' => $form_id,
|
63 |
+
'value' => $values['reviewer'],
|
64 |
+
]);
|
65 |
+
|
66 |
+
$html->renderField([
|
67 |
+
'type' => 'email',
|
68 |
+
'name' => 'email',
|
69 |
+
'errors' => $errors,
|
70 |
+
'label' => $db->getOption( 'form.email.label' ),
|
71 |
+
'placeholder' => $db->getOption( 'form.email.placeholder' ),
|
72 |
+
'prefix' => false,
|
73 |
+
'render' => !in_array( 'email', $exclude ),
|
74 |
+
'required' => true,
|
75 |
+
'suffix' => $form_id,
|
76 |
+
'value' => $values['email'],
|
77 |
+
]);
|
78 |
+
|
79 |
+
$html->renderField([
|
80 |
+
'type' => 'checkbox',
|
81 |
+
'name' => 'terms',
|
82 |
+
'errors' => $errors,
|
83 |
+
'options' => $db->getOption( 'form.terms.label' ),
|
84 |
+
'prefix' => false,
|
85 |
+
'render' => !in_array( 'terms', $exclude ),
|
86 |
+
'required' => true,
|
87 |
+
'suffix' => $form_id,
|
88 |
+
'value' => $values['terms'],
|
89 |
+
]);
|
90 |
+
|
91 |
+
$html->renderField([
|
92 |
+
'type' => 'hidden',
|
93 |
+
'name' => 'excluded',
|
94 |
+
'prefix' => false,
|
95 |
+
'value' => esc_attr( json_encode( $exclude ) ),
|
96 |
+
]);
|
97 |
+
|
98 |
+
$html->renderField([
|
99 |
+
'type' => 'hidden',
|
100 |
+
'name' => 'action',
|
101 |
+
'prefix' => false,
|
102 |
+
'value' => 'post-review',
|
103 |
+
]);
|
104 |
+
|
105 |
+
$html->renderField([
|
106 |
+
'type' => 'hidden',
|
107 |
+
'name' => 'form_id',
|
108 |
+
'prefix' => false,
|
109 |
+
'value' => $form_id,
|
110 |
+
]);
|
111 |
+
|
112 |
+
wp_nonce_field( 'post-review' );
|
113 |
+
|
114 |
+
if( $message ) {
|
115 |
+
printf( '<div class="glsr-form-messages%s">%s</div>', ( $errors ? ' gslr-has-errors' : '' ), $message );
|
116 |
+
}
|
117 |
+
|
118 |
+
$html->renderField([
|
119 |
+
'type' => 'submit',
|
120 |
+
'prefix' => false,
|
121 |
+
'value' => __( 'Submit your review', 'site-reviews' ),
|
122 |
+
]);
|
123 |
+
|
124 |
+
?>
|
125 |
+
</form>
|