--- a/depicter/app/config.php
+++ b/depicter/app/config.php
@@ -36,6 +36,7 @@
DepicterDashboardDashboardServiceProvider::class,
DepicterRulesServiceProvider::class,
DepicterFrontServiceProvider::class,
+ DepicterIntegrationServiceProvider::class,
DepicterViewViewServiceProvider::class,
DepicterWordPressRestApiServiceProvider::class,
DepicterWordPressAdminServiceProvider::class,
--- a/depicter/app/requirement.php
+++ b/depicter/app/requirement.php
@@ -28,6 +28,7 @@
add_action(
'admin_notices',
function () use ( $name, $min ) {
+ /* translators: 1: name of plugin, 2: minimum php version required, 3: current php version */
$message = __( '%1$s plugin requires PHP version %2$s but current version is %3$s. Please contact your host provider and ask them to upgrade PHP version.', 'depicter' );
?>
<div class="notice notice-error">
@@ -73,6 +74,7 @@
add_action(
'admin_notices',
function () use ( $name ) {
+ /* translators: 1: depicter plugin name */
$message = __( 'Your website uses OpCache but requires the "opcache.save_comments" option enabled for %1$s plugin to work correctly. Please ask your hosting provider to turn on this setting.', 'depicter' );
?>
<div class="notice notice-error">
--- a/depicter/app/routes/ajax.php
+++ b/depicter/app/routes/ajax.php
@@ -505,12 +505,12 @@
// ========================================================
Depicter::route()->methods(['GET'])
->where('ajax', 'depicter-lead-index', true, true)
- ->middleware('csrf-api:depicter-dashboard')
+ ->middleware('csrf-api:depicter-dashboard|depicter-editor')
->handle('LeadsAjaxController@index');
Depicter::route()->methods(['GET'])
->where('ajax', 'depicter-lead-list', true, true)
- ->middleware('csrf-api:depicter-dashboard')
+ ->middleware('csrf-api:depicter-dashboard|depicter-editor')
->handle('LeadsAjaxController@list');
Depicter::route()->methods(['POST'])
@@ -519,12 +519,12 @@
Depicter::route()->methods(['POST'])
->where('ajax', 'depicter-lead-update', true, true)
- ->middleware('csrf-api:depicter-dashboard')
+ ->middleware('csrf-api:depicter-dashboard|depicter-editor')
->handle('LeadsAjaxController@update');
Depicter::route()->methods(['POST'])
->where('ajax', 'depicter-lead-delete', true, true)
- ->middleware('csrf-api:depicter-dashboard')
+ ->middleware('csrf-api:depicter-dashboard|depicter-editor')
->handle('LeadsAjaxController@delete');
Depicter::route()->methods(['POST'])
@@ -533,7 +533,7 @@
Depicter::route()->methods(['GET'])
->where('ajax', 'depicter-lead-export', true, true)
- ->middleware('csrf-api:depicter-dashboard')
+ ->middleware('csrf-api:depicter-dashboard|depicter-editor')
->handle('LeadsAjaxController@export');
// Depicter Options
@@ -585,3 +585,30 @@
Depicter::route()->methods(['POST'])
->where('ajax', 'depicter-wc-add-to-cart', true, true)
->handle('WooCommerceAjaxController@addToCart');
+
+// Integrations
+// ===========================================================
+Depicter::route()->methods(['GET'])
+ ->where('ajax', 'depicter-integration-mailchimp-api-keys', true, true)
+ ->middleware(['csrf-api:depicter-editor', 'userCan:manage_depicter'])
+ ->handle('MailChimpIntegrationAjaxController@getApiKeys');
+
+Depicter::route()->methods(['POST'])
+ ->where('ajax', 'depicter-integration-mailchimp-api-keys', true, true)
+ ->middleware(['csrf-api:depicter-editor', 'userCan:edit_depicter'])
+ ->handle('MailChimpIntegrationAjaxController@saveApiKey');
+
+Depicter::route()->methods(['POST'])
+ ->where('ajax', 'depicter-integration-mailchimp-delete-api-key', true, true)
+ ->middleware(['csrf-api:depicter-editor', 'userCan:edit_depicter'])
+ ->handle('MailChimpIntegrationAjaxController@deleteApiKey');
+
+Depicter::route()->methods(['GET'])
+ ->where('ajax', 'depicter-integration-mailchimp-audience-list', true, true)
+ ->middleware('csrf-api:depicter-editor')
+ ->handle('MailChimpIntegrationAjaxController@audienceLists');
+
+Depicter::route()->methods(['GET'])
+ ->where('ajax', 'depicter-integration-mailchimp-audience-fields', true, true)
+ ->middleware('csrf-api:depicter-editor')
+ ->handle('MailChimpIntegrationAjaxController@audienceFields');
--- a/depicter/app/src/Application/AppMixin.php
+++ b/depicter/app/src/Application/AppMixin.php
@@ -43,6 +43,9 @@
use WPEmergeAppCoreAppCoreAppCore;
use DepicterServicesGoogleFontsService;
use DepicterRulesConditionConditions;
+use DepicterIntegrationManager as IntegrationManager;
+use DepicterDatabaseRepositoryQueueJobRepository;
+use DepicterServicesQueueService;
/**
* "@mixin" annotation for better IDE support.
@@ -261,4 +264,16 @@
* @return SettingsManagerService
*/
public static function settings(): SettingsManagerService {}
+
+ public static function integration(): IntegrationManager {}
+
+ /**
+ * @return QueueJobRepository
+ */
+ public static function queueJobsRepository(): QueueJobRepository {}
+
+ /**
+ * @return QueueService
+ */
+ public static function queue(): QueueService {}
}
--- a/depicter/app/src/Controllers/Ajax/AIWizardController.php
+++ b/depicter/app/src/Controllers/Ajax/AIWizardController.php
@@ -101,7 +101,7 @@
}
try {
- $response = Depicter::remote()->post( 'v1/ai/text/wizard/complete', [
+ $response = Depicter::remote()->post( 'v2/ai/text/wizard/complete', [
'form_params' => $args
]);
$result = JSON::decode( $response->getBody(), true );
--- a/depicter/app/src/Controllers/Ajax/CuratedAPIAjaxController.php
+++ b/depicter/app/src/Controllers/Ajax/CuratedAPIAjaxController.php
@@ -65,7 +65,6 @@
$perpage = !empty( $request->query('perpage') ) ? Sanitize::int( $request->query('perpage') ) : 20;
$category = !empty( $request->query('category') ) ? Sanitize::textfield( $request->query('category') ) : '';
$search = !empty( $request->query('s') ) ? Sanitize::textfield( $request->query('s') ) : '';
- $version = !empty( $request->query('v') ) ? Sanitize::textfield( $request->query('v') ) : '1';
$from = !empty( $request->query('from') ) ? Sanitize::textfield( $request->query('from') ) : 'website';
$directory= !empty( $request->query('directory') ) ? Sanitize::textfield( $request->query('directory') ) : 2;
@@ -74,7 +73,6 @@
'perpage' => $perpage,
'category' => $category,
's' => $search,
- 'v' => $version,
'from' => $from,
'directory' => $directory
];
@@ -112,7 +110,6 @@
$perpage = !empty( $request->query('perpage') ) ? Sanitize::int( $request->query('perpage') ) : 20;
$group = !empty( $request->query('group') ) ? Sanitize::textfield( $request->query('group') ) : '';
$search = !empty( $request->query('s') ) ? Sanitize::textfield( $request->query('s') ) : '';
- $version = !empty( $request->query('v') ) ? Sanitize::textfield( $request->query('v') ) : '1';
$from = !empty( $request->query('from') ) ? Sanitize::textfield( $request->query('from') ) : 'website';
$directory= !empty( $request->query('directory') ) ? Sanitize::textfield( $request->query('directory') ) : 2;
$flush = !empty( $request->query('flush') );
@@ -122,7 +119,6 @@
'perpage' => $perpage,
'group' => $group,
's' => $search,
- 'v' => $version,
'from' => $from,
'flush' => $flush,
'directory' => $directory
--- a/depicter/app/src/Controllers/Ajax/DashboardAjaxController.php
+++ b/depicter/app/src/Controllers/Ajax/DashboardAjaxController.php
@@ -29,6 +29,7 @@
's' => !empty( $request->query('s' ) ) ? Sanitize::textfield( $request->query('s') ) : '',
'has' => !empty( $request->query('has' ) ) ? Sanitize::textfield( $request->query('has') ) : '',
'types' => !empty( $request->query('types' ) ) ? Sanitize::textfield( $request->query('types') ) : '',
+ 'status' => !empty( $request->query('status' ) ) ? Sanitize::textfield( $request->query('status') ) : '',
];
$args['has'] = $args['has'] ? explode(',', $args['has'] ) : '';
@@ -38,6 +39,8 @@
return Depicter::json([
'hasMore' => isset( $results['numberOfPages'] ) && $results['numberOfPages'] > $results['page'],
+ 'total' => $results['total'],
+ 'published' => Depicter::documentRepository()->getNumberOfPublishedDocuments(),
'hits' => Arr::camelizeKeys( $hits, '_', [], true )
])->withStatus(200);
}
--- a/depicter/app/src/Controllers/Ajax/EditorAjaxController.php
+++ b/depicter/app/src/Controllers/Ajax/EditorAjaxController.php
@@ -99,6 +99,10 @@
throw new Exception(esc_html__('Media files cannot be published due to lack of proper file permissions for uploads directory.', 'depicter'));
}
+ if ( ! Depicter::authorization()->userHasPublishQuota() ) {
+ throw new Exception(esc_html__('You’ve reached your limit. On the free plan, you can publish up to 2 projects. To publish more, you can unpublish an existing project, or upgrade to PRO for unlimited publishing.', 'depicter'));
+ }
+
$editorRawData = $properties['editor'] ?? Depicter::document()->getEditorRawData($id);
// Download media if document published
--- a/depicter/app/src/Controllers/Ajax/ImportAjaxController.php
+++ b/depicter/app/src/Controllers/Ajax/ImportAjaxController.php
@@ -26,7 +26,8 @@
if ( $zipFile->getError() ) {
return Depicter::json([
'errors' => [
- sprintf( __( 'Cannot upload the file, because max permitted file upload size is %s.', 'depicter' ), ini_get('upload_max_filesize') )
+ /* translators: maximum file size allowed to upload */
+ sprintf( __( 'Cannot upload the file, because max permitted file upload size is %s.', 'depicter' ), ini_get('upload_max_filesize') )
]
]);
}
--- a/depicter/app/src/Controllers/Ajax/MailChimpIntegrationAjaxController.php
+++ b/depicter/app/src/Controllers/Ajax/MailChimpIntegrationAjaxController.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace DepicterControllersAjax;
+
+use AvertaWordPressUtilityJSON;
+use Depicter;
+use DepicterUtilitySanitize;
+use PsrHttpMessageResponseInterface;
+use WPEmergeRequestsRequestInterface;
+
+class MailChimpIntegrationAjaxController
+{
+
+ /**
+ * Get API keys
+ *
+ * @param RequestInterface $request
+ * @param $view
+ *
+ * @return ResponseInterface
+ */
+ public function getApiKeys( RequestInterface $request, $view ): ResponseInterface {
+ return Depicter::json(Depicter::options()->get('integration.mailchimp.api_keys', []));
+ }
+
+ /**
+ * Save API keys
+ *
+ * @param RequestInterface $request
+ * @param $view
+ *
+ * @return ResponseInterface
+ */
+ public function saveApiKey( RequestInterface $request, $view): ResponseInterface
+ {
+ $apiKey = Sanitize::textfield( $request->body( 'api_key', '') );
+ $apiKey = trim($apiKey);
+
+ if ( empty( $apiKey ) ) {
+ return Depicter::json([
+ 'errors' => [ __( 'API Key is required.', 'depicter' ) ]
+ ])->withStatus(400);
+ }
+
+ $apiKeys = Depicter::options()->get('integration.mailchimp.api_keys', []);
+ if ( ! in_array( $apiKey, $apiKeys ) ) {
+ $apiKeys[] = $apiKey;
+ }
+
+ // return true only if the api key is changed or new api key provided
+ Depicter::options()->set('integration.mailchimp.api_keys', $apiKeys );
+
+ return Depicter::json([
+ 'success' => true
+ ])->withStatus(200);
+ }
+
+ /**
+ * Delete API key
+ *
+ * @param RequestInterface $request
+ * @param $view
+ *
+ * @return ResponseInterface
+ */
+ public function deleteApiKey( RequestInterface $request, $view): ResponseInterface
+ {
+ $apiKey = Sanitize::textfield( $request->body( 'api_key', '') );
+ $apiKey = trim($apiKey);
+
+ if ( empty( $apiKey ) ) {
+ return Depicter::json([
+ 'errors' => [ __( 'API Key is required.', 'depicter' ) ]
+ ])->withStatus(400);
+ }
+
+ $apiKeys = Depicter::options()->get('integration.mailchimp.api_keys', []);
+ if ( in_array( $apiKey, $apiKeys ) ) {
+ $apiKeys = array_diff($apiKeys, [$apiKey]);
+ Depicter::options()->set('integration.mailchimp.api_keys', array_values($apiKeys) );
+
+ return Depicter::json([
+ 'success' => true
+ ])->withStatus(200);
+ }
+
+ return Depicter::json([
+ 'errors' => [ __( 'API Key not found.', 'depicter' ) ]
+ ])->withStatus(404);
+ }
+
+ /**
+ * Retrieves the list of audiences from provided account
+ *
+ * @param RequestInterface $request
+ * @param $view
+ *
+ * @return ResponseInterface
+ */
+ public function audienceLists( RequestInterface $request, $view ): ResponseInterface
+ {
+ $apiKey = Sanitize::textfield( $request->query( 'api_key', '') );
+ if ( empty( $apiKey ) ) {
+ return Depicter::json([
+ 'errors' => [ __( 'API Key is required.', 'depicter' ) ]
+ ])->withStatus(400);
+ }
+
+ return Depicter::json(
+ Depicter::integration()->mailchimp()->getAudienceList( $apiKey )
+ );
+ }
+
+ /**
+ * Retrieves the list of fields from a provided audience
+ *
+ * @param RequestInterface $request
+ * @param $view
+ *
+ * @return ResponseInterface
+ */
+ public function audienceFields( RequestInterface $request, $view ): ResponseInterface
+ {
+ $apiKey = Sanitize::textfield( $request->query( 'api_key', '') );
+ if ( empty( $apiKey ) ) {
+ return Depicter::json([
+ 'errors' => [ __( 'API Key is required.', 'depicter' ) ]
+ ])->withStatus(400);
+ }
+
+ $audienceID = Sanitize::textfield( $request->query( 'audience_id', '') );
+ if ( empty( $audienceID ) ) {
+ return Depicter::json([
+ 'errors' => [ __( 'Audience ID is required.', 'depicter' ) ]
+ ])->withStatus(400);
+ }
+
+ return Depicter::json(
+ Depicter::integration()->mailchimp()->getListFields( $apiKey, $audienceID )
+ );
+ }
+}
--- a/depicter/app/src/Controllers/Ajax/OptionsAjaxController.php
+++ b/depicter/app/src/Controllers/Ajax/OptionsAjaxController.php
@@ -106,6 +106,7 @@
}
return Depicter::json([
+ /* translators: failed option keys while saving */
'errors' => [ sprintf( __( 'Saving the following options failed: %s. Please try again!', 'depicter' ) , implode( ',', $failedJobs ) ) ]
])->withStatus(400);
}
--- a/depicter/app/src/Dashboard/DashboardPage.php
+++ b/depicter/app/src/Dashboard/DashboardPage.php
@@ -34,13 +34,19 @@
* @return void
*/
public function registerPage() {
+
+ $depicterMenuTitle = __( 'Depicter', 'depicter' );
+ $notifNumber = $this->countOfUnreadNotifications();
+ $depicterMenuTitle = $notifNumber ? $depicterMenuTitle . " <span class='awaiting-mod depicter-notif-number'>" . $notifNumber . "</span>" : $depicterMenuTitle;
+
$this->hook_suffix = add_menu_page(
__('Depicter', 'depicter'),
- __('Depicter', 'depicter'),
+ $depicterMenuTitle,
'access_depicter',
self::PAGE_ID,
[ $this, 'render' ], // called to output the content for this page
- Depicter::core()->assets()->getUrl() . '/resources/images/svg/wp-logo.svg'
+ Depicter::core()->assets()->getUrl() . '/resources/images/svg/wp-logo.svg',
+ 58
);
add_submenu_page(
@@ -53,15 +59,6 @@
add_submenu_page(
self::PAGE_ID,
- __( 'Settings', 'depicter' ),
- __( 'Settings', 'depicter' ),
- 'access_depicter',
- 'depicter-settings',
- [ $this, 'printSettingsPage' ]
- );
-
- add_submenu_page(
- self::PAGE_ID,
__( 'Support', 'depicter' ),
__( 'Support', 'depicter' ),
'access_depicter',
@@ -127,36 +124,19 @@
* @param string $hook_suffix
*/
public function enqueueScripts( $hook_suffix = '' ){
-
- if( $hook_suffix !== $this->hook_suffix ){
-
- if ( !empty( $_GET['page'] ) && $_GET['page'] == 'depicter-settings' ) {
- Depicter::core()->assets()->enqueueScript(
- 'depicter-admin',
- Depicter::core()->assets()->getUrl() . '/resources/scripts/admin/index.js',
- ['jquery'],
- true
- );
-
- wp_localize_script( 'depicter-admin', 'depicterParams', [
- 'ajaxUrl' => admin_url('admin-ajax.php'),
- 'token' => Depicter::csrf()->getToken( DepicterSecurityCSRF::DASHBOARD_ACTION ),
- ]);
- }
-
- return;
- }
-
+
// Enqueue scripts.
- Depicter::core()->assets()->enqueueScript(
+ Depicter::core()->assets()->enqueueScriptModule(
'depicter--dashboard',
- Depicter::core()->assets()->getUrl() . '/resources/scripts/dashboard/depicter-dashboard.js',
- [],
- true
+ Depicter::core()->assets()->getUrl() . '/resources/scripts/dashboard/depicter-dashboard.js'
);
// Enqueue styles.
Depicter::core()->assets()->enqueueStyle(
+ 'depicter-dashboard-styles',
+ Depicter::core()->assets()->getUrl() . '/resources/scripts/dashboard/depicter-dashboard-styles.css'
+ );
+ Depicter::core()->assets()->enqueueStyle(
'depicter-dashboard',
Depicter::core()->assets()->getUrl() . '/resources/styles/dashboard/index.css'
);
@@ -190,7 +170,7 @@
$refreshTokenPayload = Extract::JWTPayload( $refreshToken );
$displayReviewNotice = !empty( $refreshTokenPayload['ict'] ) && ( time() - Data::cast( $refreshTokenPayload['ict'], 'int' ) > 5 * DAY_IN_SECONDS );
- wp_add_inline_script('depicter--dashboard', 'window.depicterEnv = '. JSON::encode(
+ wp_print_inline_script_tag('window.depicterEnv = '. JSON::encode(
[
'wpVersion' => $wp_version,
"scriptsPath" => Depicter::core()->assets()->getUrl(). '/resources/scripts/dashboard/',
@@ -244,12 +224,9 @@
],
'display' => [
'reviewNotice' => $displayReviewNotice
- ],
- 'routes'=>[
- 'settingPage' => Escape::url( add_query_arg( [ 'page' => 'depicter-settings', ], self_admin_url( 'admin.php' ) ) )
]
]
- ), 'before' );
+ ) );
}
@@ -263,9 +240,51 @@
UserAPIService::renewTokens();
}
}
+
+ public function get_notifications() {
+ if ( false === $notifications = Depicter::cache()->get( 'retrievedNotifications' ) ) {
+ try{
+ $response = Depicter::remote()->get( 'v1/core/notifications', [
+ 'query' => [
+ 'page' => 1,
+ 'perPage' => 20
+ ]
+ ]);
+
+ if ($response->getStatusCode() === 200) {
+ $notifications = $response->getBody()->getContents();
+ Depicter::cache()->set( 'retrievedNotifications', $notifications, 12 * HOUR_IN_SECONDS );
+ return JSON::decode( $notifications, true );
+ }
+ } catch ( GuzzleException $e ){
+ }
+
+ return false;
+
+ }
+
+ return JSON::decode( $notifications, true );
+ }
+
+ protected function countOfUnreadNotifications(): ?int
+ {
+
+ if ( false === $notifications = $this->get_notifications() ) {
+ return 0;
+ }
+
+ if ( false === $lastSeenDate = Depicter::options()->get('lastSeenNotificationDate', false) ) {
+ return count( $notifications['hits'] );
+ }
+
+ $unreadNotifications = 0;
+ $lastSeenDate = strtotime( $lastSeenDate );
+ foreach ( $notifications['hits'] as $notification ) {
+ if ( strtotime( $notification['date'] ) > $lastSeenDate ) {
+ $unreadNotifications++;
+ }
+ }
- public function printSettingsPage() {
- Depicter::resolve('depicter.dashboard.settings')->render();
- }
-
+ return $unreadNotifications;
+ }
}
--- a/depicter/app/src/Dashboard/DashboardServiceProvider.php
+++ b/depicter/app/src/Dashboard/DashboardServiceProvider.php
@@ -18,10 +18,6 @@
$container[ 'depicter.dashboard.page' ] = function () {
return new DashboardPage();
};
-
- $container[ 'depicter.dashboard.settings' ] = function () {
- return new DashboardSettings();
- };
}
/**
@@ -29,7 +25,6 @@
*/
public function bootstrap( $container ) {
Depicter::resolve('depicter.dashboard.page')->bootstrap();
- Depicter::resolve('depicter.dashboard.settings');
}
}
--- a/depicter/app/src/Dashboard/DashboardSettings.php
+++ b/depicter/app/src/Dashboard/DashboardSettings.php
@@ -1,158 +0,0 @@
-<?php
-namespace DepicterDashboard;
-
-use DepicterJeffreyvrWPSettingsError;
-use DepicterJeffreyvrWPSettingsFlash;
-use DepicterWordPressSettingsSettings;
-
-// Exit if accessed directly.
-if ( ! defined( 'ABSPATH' ) ) {
- exit;
-}
-
-class DashboardSettings {
-
- const PAGE_ID = 'depicter-settings';
-
- public $settings = null;
-
- public function __construct() {
- if ( ! empty( $_GET['page'] ) && $_GET['page'] == 'depicter-settings') {
- $this->settingsPage();
-
- $this->settings->errors = new Error( $this->settings );
- $this->settings->flash = new Flash( $this->settings );
-
- add_action('admin_head', [$this->settings, 'styling']);
- add_action('admin_init', [$this->settings, 'save']);
- }
- }
-
- /**
- * Settings page markup
- *
- * @return void
- */
- public function settingsPage() {
-
- $settings = new Settings(__('Settings', 'depicter'), 'depicter-settings');
- $settings->set_option_name('depicter_options');
- $settings->set_menu_parent_slug( 'depicter-dashboard' );
-
- $settings->add_tab(__( 'General', 'depicter' ));
- $settings->add_section( __( 'General Settings', 'depicter' ) );
-
- $settings->add_option('nonce',[
- 'action' => 'depicter-settings',
- 'name' => '_depicter_settings_nonce'
- ]);
-
- $settings->add_option('select', [
- 'name' => 'use_google_fonts',
- 'label' => __( 'Google Fonts', 'depicter' ),
- 'options' => [
- 'on' => __( 'Default (Enable)', 'depicter' ),
- 'off' => __( 'Disable', 'depicter' ),
- 'editor_only' => __( 'Load in Editor Only', 'depicter' ),
- 'save_locally' => __( 'Save Locally', 'depicter' )
- ],
- 'description' => __( 'Enable, disable, or save Google Fonts locally on your host.', 'depicter' )
- ]);
-
- $settings->add_option('select', [
- 'name' => 'resource_preloading',
- 'label' => __( 'Resource Preloading', 'depicter' ),
- 'options' => [
- 'on' => __( 'Default (Enable)', 'depicter' ),
- 'off' => __( 'Disable', 'depicter' )
- ],
- 'description' => __( 'Enable or disable preloading of website resources (images and CSS) for faster page load speed.', 'depicter' )
- ]);
-
- $settings->add_option('select', [
- 'name' => 'allow_unfiltered_data_upload',
- 'label' => __( 'Allow SVG & JSON Upload?', 'depicter' ),
- 'options' => [
- 'off' => __( 'Disable', 'depicter' ),
- 'on' => __( 'Enable', 'depicter' )
- ],
- 'description' => __( 'Attention! Allowing uploads of SVG or JSON files is a potential security risk.<br/>Although Depicter sanitizes such files, we recommend that you only enable this feature if you understand the security risks involved.', 'depicter' ),
- ]);
-
- $settings->add_option('button', [
- 'name' => 'regenerate_css_flush_cache',
- 'label' => __( 'Regenerate CSS & Flush Cache', 'depicter' ),
- 'button_text' => __( 'Regenerate CSS & Flush Cache', 'depicter' ),
- 'class' => 'button button-secondary depicter-flush-cache',
- 'icon' => '<span class="dashicons dashicons-update" style="line-height:28px; margin-right:8px; height:28px;"></span>'
- ]);
-
- $settings->add_option('checkbox', [
- 'name' => 'always_load_assets',
- 'label' => __( 'Load assets on all pages?', 'depicter' ),
- 'description' => "<br><br>". __( 'By default, Depicter will load corresponding JavaScript and CSS files on demand. but if you need to load assets on all pages, check this option. <br>(For example, if you plan to load Depicter via Ajax, you need to enable this option)', 'depicter' ),
- ]);
-
- if ( Depicter::auth()->isPaid() ) {
-
- $settings->add_tab(__( 'CAPTCHA', 'depicter' ));
- $settings->add_section( __( 'Google Recaptcha V3', 'depicter' ) );
-
- $settings->add_option('nonce',[
- 'action' => 'depicter-settings',
- 'name' => '_depicter_settings_nonce'
- ]);
-
- $settings->add_option('text', [
- 'name' => 'google_recaptcha_client_key',
- 'label' => __( 'Google Recaptcha (v3) Client key', 'depicter' ),
- 'description' => "",
- ]);
-
- $settings->add_option('password', [
- 'name' => 'google_recaptcha_secret_key',
- 'label' => __( 'Google Recaptcha (v3) Secret key', 'depicter' ),
- 'description' => "",
- ]);
-
- $settings->add_option('number', [
- 'name' => 'google_recaptcha_score_threshold',
- 'label' => __( 'Score Threshold', 'depicter' ),
- 'default' => 0.6,
- 'description' => __( 'reCAPTCHA v3 returns a score (1.0 is very likely a good interaction, 0.0 is very likely a bot). If the score less than or equal to this threshold, the form submission will be blocked and the message below will be displayed.', 'depicter' ),
- ]);
-
- $settings->add_option('textarea', [
- 'name' => 'google_recaptcha_fail_message',
- 'label' => __( 'Fail Message', 'depicter' ),
- 'default' => __('Google reCAPTCHA verification failed, please try again later.', 'depicter' ),
- 'description' => __( 'Displays to users who fail the verification process.', 'depicter'),
- ]);
-
- $settings->add_tab(__( 'Integrations', 'depicter' ));
- $settings->add_section( '' );
-
- $settings->add_option('nonce',[
- 'action' => 'depicter-settings',
- 'name' => '_depicter_settings_nonce'
- ]);
-
- $settings->add_option('password', [
- 'name' => 'google_places_api_key',
- 'label' => __( 'Google Places Api key', 'depicter' ),
- 'description' => sprintf(
- __("To fetch and display reviews of a place on your website (Google Reviews), you need to provide %1$s a valid Google Places API key%2$s.", 'depicter' ),
- '<a href="https://docs.depicter.com/article/290-google-places-api-key" target="_blank">',
- '</a>'
- )
- ]);
- }
-
- $this->settings = $settings;
- }
-
- public function render() {
-
- $this->settings->render();
- }
-}
--- a/depicter/app/src/Database/DatabaseServiceProvider.php
+++ b/depicter/app/src/Database/DatabaseServiceProvider.php
@@ -7,6 +7,7 @@
use DepicterDatabaseRepositoryLeadFieldRepository;
use DepicterDatabaseRepositoryLeadRepository;
use DepicterDatabaseRepositoryMetaRepository;
+use DepicterDatabaseRepositoryQueueJobRepository;
use WPEmergeServiceProvidersServiceProviderInterface;
/**
@@ -41,11 +42,16 @@
return new LeadFieldRepository();
};
+ $container[ 'depicter.database.repository.queue.jobs' ] = function () {
+ return new QueueJobRepository();
+ };
+
$app = $container[ WPEMERGE_APPLICATION_KEY ];
$app->alias( 'documentRepository', 'depicter.database.repository.document' );
$app->alias( 'metaRepository', 'depicter.database.repository.meta' );
$app->alias( 'leadRepository', 'depicter.database.repository.lead' );
$app->alias( 'leadFieldRepository', 'depicter.database.repository.lead.field' );
+ $app->alias( 'queueJobsRepository', 'depicter.database.repository.queue.jobs' );
}
/**
--- a/depicter/app/src/Database/Entity/QueueJob.php
+++ b/depicter/app/src/Database/Entity/QueueJob.php
@@ -0,0 +1,42 @@
+<?php
+namespace DepicterDatabaseEntity;
+
+use AvertaWordPressDatabaseEntityModel;
+
+class QueueJob extends Model
+{
+ protected $idColumn = 'id';
+
+ /**
+ * Resource name.
+ *
+ * @var string
+ */
+ protected $resource = 'depicter_queue_jobs';
+
+ /**
+ * Determines what fields can be saved without be explicitly.
+ *
+ * @var array
+ */
+ protected $builtin = [
+ 'attempts',
+ 'reserved_at',
+ 'available_at',
+ 'created_at',
+ 'status',
+ 'last_error'
+ ];
+
+ protected $guard = [ 'id' ];
+
+ protected $format = [
+ 'created_at' => 'currentDateTime',
+ 'reserved_at' => 'currentDateTime',
+ 'available_at' => 'currentDateTime',
+ ];
+
+ public function currentDateTime() {
+ return gmdate('Y-m-d H:i:s', time());
+ }
+}
--- a/depicter/app/src/Database/Migration.php
+++ b/depicter/app/src/Database/Migration.php
@@ -18,7 +18,7 @@
/**
* Current tables migration version
*/
- const MIGRATION_VERSION = "0.4.6";
+ const MIGRATION_VERSION = "0.4.7";
/**
* Prefix for version option name
@@ -33,7 +33,7 @@
/**
* Table names
*/
- protected $table_names = [ 'documents', 'options', 'meta', 'leads', 'lead_fields' ];
+ protected $table_names = [ 'documents', 'options', 'meta', 'leads', 'lead_fields', 'queue_jobs' ];
/**
@@ -146,6 +146,26 @@
$this->dbDelta( $sql_create_table );
}
+ public function create_table_queue_jobs() {
+
+ $sql_create_table = "CREATE TABLE {$this->queue_jobs} (
+ id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
+ queue VARCHAR(50) NOT NULL DEFAULT 'default',
+ payload LONGTEXT NOT NULL,
+ attempts TINYINT UNSIGNED NOT NULL DEFAULT 0,
+ reserved_at DATETIME DEFAULT NULL,
+ available_at DATETIME NOT NULL,
+ created_at DATETIME NOT NULL,
+ status VARCHAR(20) NOT NULL DEFAULT 'pending', -- pending, processing, failed, completed
+ last_error TEXT DEFAULT NULL,
+ PRIMARY KEY (id),
+ INDEX (status),
+ INDEX (available_at),
+ INDEX (queue)
+ ) {$this->charset_collate()};n";
+
+ $this->dbDelta($sql_create_table);
+ }
}
--- a/depicter/app/src/Database/Repository/DocumentRepository.php
+++ b/depicter/app/src/Database/Repository/DocumentRepository.php
@@ -48,7 +48,7 @@
if ( $id && $document = $this->document()->findById( $id ) ) {
$this->document = $document;
}
-
+
if( isset( $properties['editor'] ) ){
$editor = $properties['editor'];
unset( $properties['editor'] );
@@ -261,6 +261,10 @@
$documents = $documents->where( 'name', 'like', '%' . $args['s'] . '%' );
}
+ if ( !empty( $args['status'] ) ) {
+ $documents = $documents->where( 'status', $args['status'] );
+ }
+
if ( !empty( $args['types'] ) ) {
$types = explode( ',', $args['types'] );
$where = [];
@@ -289,6 +293,8 @@
$documents = $documents->where( $where );
}
+ $total = $documents->count();
+
if ( !empty( $args['page'] ) && !empty( $args['perPage'] ) ) {
$pager = $documents->paginate( $args['perPage'], $args['page'] );
if ( $pager ) {
@@ -330,6 +336,7 @@
return [
'page' => $args['page'],
'perPage' => $args['perPage'],
+ 'total' => $total,
'numberOfPages' => $numberOfPages,
'documents' => $documents
];
@@ -966,4 +973,28 @@
return [];
}
}
+
+ /**
+ * Get number of published documents
+ *
+ * @return int
+ */
+ public function getNumberOfPublishedDocuments(){
+ try{
+ $publishedDocuments = $this->document()->where( 'parent', '0' )->published()->count();
+ $draftDocuments = $this->document()->select('id')->where( 'parent', '0' )->draft()->findAll()->get();
+ if ( $draftDocuments ) {
+ $draftDocuments = $draftDocuments->toArray();
+ foreach ( $draftDocuments as $document ) {
+ if ( $this->isPublishedBefore( $document['id'] ) ) {
+ ++$publishedDocuments;
+ }
+ }
+ }
+
+ return $publishedDocuments;
+ } catch ( Exception $e ){
+ return 0;
+ }
+ }
}
--- a/depicter/app/src/Database/Repository/LeadRepository.php
+++ b/depicter/app/src/Database/Repository/LeadRepository.php
@@ -209,10 +209,12 @@
)->join( "{$this->leadField()->getTable()} AS lf", "{$leadTable}.id", "=", "lf.lead_id" );
if( ! empty( $args['dateStart'] ) ){
+ $args['dateStart'] = $this->normalizeDateTime( $args['dateStart'], 'start' );
$leads->where( "{$leadTable}.created_at", '>=', $args['dateStart'] );
}
if( ! empty( $args['dateEnd'] ) ){
+ $args['dateEnd'] = $this->normalizeDateTime( $args['dateEnd'], 'end' );
$leads->where( "{$leadTable}.created_at", '<=', $args['dateEnd'] );
}
@@ -240,6 +242,21 @@
] : [];
}
+ /**
+ * Normalize date to include time
+ *
+ * @param string $date
+ * @param string $type
+ *
+ * @return string
+ */
+ public function normalizeDateTime( string $date, $type = 'start' ){
+ if (preg_match('/^d{4}-d{2}-d{2}$/', $date)){
+ return $date . ' ' . ($type === 'start' ? '00:00:00' : '23:59:59');
+ }
+ return $date;
+ }
+
/**
* Apply pagination if possible
--- a/depicter/app/src/Database/Repository/QueueJobRepository.php
+++ b/depicter/app/src/Database/Repository/QueueJobRepository.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace DepicterDatabaseRepository;
+
+use DepicterDatabaseEntityQueueJob;
+use DepicterUtilitySanitize;
+
+class QueueJobRepository
+{
+
+ private QueueJob $job;
+
+ public function __construct(){
+ $this->job = QueueJob::new();
+ }
+
+ /**
+ * Access to an instance of Lead entity
+ *
+ * @return QueueJob
+ */
+ public function job(): QueueJob{
+ return QueueJob::new();
+ }
+
+ public function create( $queue, $payload ) {
+ return $this->job()->create([
+ 'queue' => $queue,
+ 'payload' => $payload,
+ 'attempts' => 0,
+ ]);
+ }
+
+ public function update( $id, array $fields = [] ) {
+ if ( empty( $fields ) ) {
+ return false;
+ }
+
+ $job = $this->job()->findById( $id );
+
+ if ( $job && $job->count() ){
+ return $job->first()->update($fields);
+ }
+
+ return false;
+ }
+
+ public function delete( $id ): bool
+ {
+ $succeed = false;
+
+ if( is_array( $id ) ){
+ $ids = $id;
+ } elseif( false !== strpos( $id, ',' ) ){
+ $ids = explode(',', $id );
+ } else {
+ $ids = [$id];
+ }
+
+ foreach( $ids as $id ){
+ $id = Sanitize::int( $id );
+ if( $job = $this->job()->findById( $id ) ){
+ $job->delete();
+ $succeed = true;
+ }
+ }
+ return $succeed;
+ }
+
+}
--- a/depicter/app/src/Document/Models/Common/InnerStyles.php
+++ b/depicter/app/src/Document/Models/Common/InnerStyles.php
@@ -7,4 +7,21 @@
* @var Styles|null
*/
public $items;
+
+ /**
+ * @var array
+ */
+ public $dynamicStyleProperties = [];
+
+ public function __set(string $name, $value) {
+ $this->dynamicStyleProperties[ $name ] = $value;
+ }
+
+ public function __get(string $name) {
+ return $this->dynamicStyleProperties[ $name ] ?? null;
+ }
+
+ public function __isset(string $name) {
+ return isset( $this->dynamicStyleProperties[ $name ] );
+ }
}
--- a/depicter/app/src/Document/Models/Document.php
+++ b/depicter/app/src/Document/Models/Document.php
@@ -230,6 +230,15 @@
}
/**
+ * Check if document has teaser or not
+ *
+ * @return boolean
+ */
+ public function hasTeaser(): bool {
+ return !empty( $this->options->documentTypeOptions->teaser->enabled );
+ }
+
+ /**
* Render markup for possible notices
*
* @return void
@@ -320,6 +329,9 @@
*/
protected function renderSectionsAndElements(){
foreach ( $this->sections as $section ) {
+ if ( $section->getType() == 'teaser' && ! $this->hasTeaser() ) {
+ continue;
+ }
$this->html->nest( $section->render() . "n" );
$this->stylesList = array_merge( $this->stylesList, $section->getCss() );
@@ -391,9 +403,8 @@
// add before init styles separately to style list as well
$this->stylesList[ '.'. $this->getStyleSelector() ]['beforeInitStyle'] = [
- '.'. $this->getStyleSelector() => $this->options->getStyles(),
+ '.'. $this->getStyleSelector() => array_merge_recursive( $this->options->getStyles(), $this->options->getPrimaryContainerStyles() ),
'.'. $this->getStyleSelector( true ) . ':not(.depicter-ready)' => $this->options->getBeforeInitStyles(), // styles to prevent FOUC. It should not have depicter-revert class in selector
- '.'. $this->getStyleSelector() => $this->options->getPrimaryContainerStyles(),
];
return $this->stylesList;
--- a/depicter/app/src/Document/Models/Element.php
+++ b/depicter/app/src/Document/Models/Element.php
@@ -256,9 +256,9 @@
if ( strpos( $this->type, 'dpc' ) !== false ) {
$this->componentType = $this->type;
$this->type = 'component';
- } else if ( strpos( $this->type, 'form:' ) !== false ) {
+ } else if ( strpos( $this->type, 'form:' ) !== false || $this->type === "hiddenInput" ) {
$this->componentType = $this->type;
- $this->type = 'form';
+ $this->type = $this->type !== 'form:submit'? 'form' : 'button';
} else if ( strpos( $this->type, 'survey:' ) !== false ) {
$this->componentType = $this->type;
$this->type = $this->type == 'survey:submit' || $this->type == 'survey:next' || $this->type == 'survey:prev' ? 'button' : 'survey';
@@ -551,19 +551,31 @@
if ( !empty( $innerStyles ) ) {
foreach( $innerStyles as $cssSelector => $styles ){
- if ( empty( $styles ) || ! $styles instanceof CommonStyles ) {
- continue;
- }
+ // Handle "dynamicStyleProperties" specially
+ $styleItems = $cssSelector === 'dynamicStyleProperties' ? (array) $styles : [$cssSelector => $styles];
- $generalCss = $innerStyles->{$cssSelector}->getGeneralCss('normal');
- // Add SVG selector and css
- $svgCss = $this->getInnerSvgCss( $cssSelector );
- $this->selectorCssList[ '.' . $this->getStyleSelector() . ' .' . Helper::camelCaseToHyphenated( $cssSelector ) ] = array_merge_recursive( $generalCss, $svgCss );
-
- $hoverCss = $innerStyles->{$cssSelector}->getGeneralCss('hover');
- if ( !empty( $hoverCss ) ) {
- $this->selectorCssList[ '.' . $this->getStyleSelector() . ' .' . Helper::camelCaseToHyphenated( $cssSelector ) ]['hover'] = Arr::merge( $hoverCss['hover'] , $this->selectorCssList[ '.' . $this->getStyleSelector() . ' .' . Helper::camelCaseToHyphenated( $cssSelector ) ]['hover']);
- }
+ foreach ($styleItems as $selector => $style) {
+ if (empty($style) || ! $style instanceof CommonStyles) {
+ continue;
+ }
+
+ $key = '.' . $this->getStyleSelector() . ' .' . Helper::camelCaseToHyphenated($selector);
+
+ // Normal CSS
+ $generalCss = $style->getGeneralCss('normal');
+ $svgCss = $this->getInnerSvgCss($selector);
+
+ $this->selectorCssList[$key] = array_merge_recursive($generalCss, $svgCss);
+
+ // Hover CSS
+ $hoverCss = $style->getGeneralCss('hover');
+ if (!empty($hoverCss)) {
+ $this->selectorCssList[$key]['hover'] = Arr::merge(
+ $hoverCss['hover'],
+ $this->selectorCssList[$key]['hover']
+ );
+ }
+ }
}
}
@@ -579,7 +591,13 @@
protected function getInnerSvgCss( $cssSelector ): array
{
// Get styles list from styles property
- return ! empty( $this->innerStyles->{$cssSelector} ) ? $this->innerStyles->{$cssSelector}->getSvgCss() : [];
+ if ( ! empty( $this->innerStyles->{$cssSelector} ) ) {
+ return $this->innerStyles->{$cssSelector}->getSvgCss();
+ } else if ( ! empty( $this->innerStyles->dynamicStyleProperties[ $cssSelector ] ) ) {
+ return $this->innerStyles->dynamicStyleProperties[ $cssSelector ]->getSvgCss();
+ }
+
+ return [];
}
/**
--- a/depicter/app/src/Document/Models/Elements/Button.php
+++ b/depicter/app/src/Document/Models/Elements/Button.php
@@ -1,9 +1,7 @@
<?php
namespace DepicterDocumentModelsElements;
-use AvertaCoreUtilityArr;
use DepicterDocumentModels;
-use DepicterDocumentModelsCommonStyles;
use DepicterHtmlHtml;
class Button extends ModelsElement
@@ -13,7 +11,9 @@
$args = $this->getDefaultAttributes();
- if ( $this->componentType && ( $this->componentType == 'survey:submit' || $this->componentType == 'survey:next' || $this->componentType == 'survey:prev' ) ) {
+ if ( $this->componentType &&
+ in_array( $this->componentType, ['form:submit', 'survey:submit', 'survey:next', 'survey:prev'] )
+ ) {
$args['data-type'] = $this->componentType;
}
@@ -28,9 +28,9 @@
if ( ! empty( $this->options->submitContent ) ) {
$args['data-submit-text'] = $this->options->submitContent;
- }
+ }
}
-
+
if ( ! empty( $this->options->iconAlign ) && $this->options->iconAlign == 'right' ) {
$args['class'] .= ' dp-icon-right';
}
@@ -51,7 +51,7 @@
}
$content = ! empty ( $this->options->iconOnly ) && $this->options->iconOnly ? "" : $this->maybeReplaceDataSheetTags( $this->options->content );
-
+
$buttonContent = Html::span( [
'class' => 'dp-inner-content'
], $iconTag . $content);
--- a/depicter/app/src/Document/Models/Elements/Form.php
+++ b/depicter/app/src/Document/Models/Elements/Form.php
@@ -3,9 +3,7 @@
use AvertaCoreUtilityArr;
use DepicterDocumentCSSSelector;
-use DepicterDocumentHelperHelper;
use DepicterDocumentModels;
-use DepicterDocumentModelsCommonStyles;
use DepicterHtmlHtml;
class Form extends ModelsElement
@@ -35,6 +33,10 @@
case 'form:message':
$output = Html::div( $args, $this->getMessageContent() );
break;
+ case "hiddenInput":
+ $args['data-source'] = $this->options->source;
+ $args['data-source-key'] = $this->options->sourceKey;
+ $output = Html::div( $args, $this->getHiddenInputContent() );
default:
break;
}
@@ -102,7 +104,7 @@
}
if ( ! empty( $this->options->label ) && ! empty( $this->options->showLabel ) ) {
$output .= Html::label([
- 'class' => Selector::prefixify( 'field-label' ),
+ 'class' => $this->options->type === "checkbox" ? Selector::prefixify( 'choice-label' ) : Selector::prefixify( 'field-label' ),
'for' => $this->options->attributes->name
], $this->options->label ) . "n";
}
@@ -176,6 +178,10 @@
return $output;
}
+ protected function getHiddenInputContent(): string {
+ return Html::input( 'hidden', $this->options->name, '', [] );
+ }
+
/**
* Get list of selector and CSS for element and belonging child elements
*
--- a/depicter/app/src/Document/Models/Options/Script.php
+++ b/depicter/app/src/Document/Models/Options/Script.php
@@ -187,6 +187,24 @@
$attributes['useWatermark'] = Depicter::auth()->isSubscriptionExpired();
+ if ( $document->hasTeaser() ) {
+ $attributes['teaser'] = [
+ 'enabled' => true,
+ 'placement' => $document->options->documentTypeOptions->teaser->placement,
+ 'behavior' => $document->options->documentTypeOptions->teaser->behavior ?? "always",
+ 'vSpace' => [
+ 'value' => $document->options->documentTypeOptions->teaser->vSpace->value,
+ 'unit' => $document->options->documentTypeOptions->teaser->vSpace->unit
+ ],
+ 'hSpace' => [
+ 'value' => $document->options->documentTypeOptions->teaser->hSpace->value,
+ 'unit' => $document->options->documentTypeOptions->teaser->hSpace->unit
+ ]
+ ];
+ }
+
+ $attributes = apply_filters( 'depicter/player/script/attributes', $attributes );
+
$basePath = Depicter::core()->assets()->getUrl() . '/resources/scripts/player/';
$script = "n(window.depicterSetups = window.depicterSetups || []).push(function(){";
--- a/depicter/app/src/Document/Models/Section.php
+++ b/depicter/app/src/Document/Models/Section.php
@@ -113,6 +113,10 @@
*/
public $visibilitySchedule;
+ /**
+ * @var string
+ */
+ public $type = 'section';
/**
* get section ID
@@ -142,6 +146,15 @@
}
/**
+ * Get section name
+ *
+ * @return string
+ */
+ public function getType() {
+ return $this->type ?? "section";
+ }
+
+ /**
* Gets belonging element ids
*
* @return array
@@ -228,6 +241,7 @@
'id' => $this->getCssID(),
'class' => $this->getClassNames(),
'data-name' => $this->getName(),
+ 'data-type' => $this->getType(),
'data-local-id' => $this->id
];
--- a/depicter/app/src/Editor/EditorAssets.php
+++ b/depicter/app/src/Editor/EditorAssets.php
@@ -27,21 +27,17 @@
['jquery'],
true
);
+ Depicter::core()->assets()->enqueueStyle(
+ 'depicter-editor-style',
+ Depicter::core()->assets()->getUrl() . '/resources/scripts/editor/depicter-editor-styles.css'
+ );
wp_enqueue_style('common');
// Enqueue scripts.
- Depicter::core()->assets()->enqueueScript(
- 'depicter-editor-vendors',
- Depicter::core()->assets()->getUrl() . '/resources/scripts/editor/vendors-main.js',
- [],
- true
- );
- Depicter::core()->assets()->enqueueScript(
+ Depicter::core()->assets()->enqueueScriptModule(
'depicter-editor-js',
- Depicter::core()->assets()->getUrl() . '/resources/scripts/editor/depicter-editor.js',
- ['depicter-editor-vendors'],
- true
+ Depicter::core()->assets()->getUrl() . '/resources/scripts/editor/depicter-editor.js'
);
$currentUser = wp_get_current_user();
@@ -121,7 +117,7 @@
}
// Add Environment variables
- wp_add_inline_script( 'depicter-editor-vendors', 'window.depicterEnv = '. JSON::encode( $envData ), 'before' );
+ wp_print_inline_script_tag( 'window.depicterEnv = '. JSON::encode( $envData ) );
}
--- a/depicter/app/src/Front/Assets.php
+++ b/depicter/app/src/Front/Assets.php
@@ -110,6 +110,11 @@
foreach( $documentIDs as $documentID ){
if( false !== $cssLinkToEnqueue = Depicter::cache('document')->get( $documentID . '_css_files' ) ){
$cssLinksToEnqueue = $cssLinksToEnqueue + $cssLinkToEnqueue;
+ } else {
+ Depicter::document()->cacheCustomStyles( $documentID );
+ if( false !== $cssLinkToEnqueue = Depicter::cache('document')->get( $documentID . '_css_files' ) ){
+ $cssLinksToEnqueue = $cssLinksToEnqueue + $cssLinkToEnqueue;
+ }
}
}
--- a/depicter/app/src/Front/Symbols.php
+++ b/depicter/app/src/Front/Symbols.php
@@ -65,7 +65,8 @@
$clipPathContent = Html::el('svg', [
//'xmlns' => "http://www.w3.org/2000/svg",
'width' => '0',
- 'height' => '0'
+ 'height' => '0',
+ 'class' => 'depicter-svg-clip-paths'
], $clipPathContent . "n" );
}
--- a/depicter/app/src/Integration/MailChimp/MailChimp.php
+++ b/depicter/app/src/Integration/MailChimp/MailChimp.php
@@ -0,0 +1,225 @@
+<?php
+namespace DepicterIntegrationMailChimp;
+
+
+use AvertaCoreUtilityArr;
+use AvertaWordPressUtilityJSON;
+use DepicterGuzzleHttpClient;
+use DepicterGuzzleHttpExceptionGuzzleException;
+
+class MailChimp
+{
+
+ /**
+ * Retrieves the list of audiences from provided account
+ *
+ * @param string $apiKey
+ *
+ * @return array
+ */
+ public function getAudienceList($apiKey) {
+ $region = substr($apiKey, strpos($apiKey, '-') + 1);
+ $url = "https://$region.api.mailchimp.com/3.0/lists";
+
+ try {
+ $body = $this->callApi($apiKey, $url);
+
+ return [
+ 'hits' => $body['lists'] ?? []
+ ];
+ } catch (GuzzleException $e) {
+ return [
+ 'hits' => [],
+ 'errors' => [ $e->getMessage() ]
+ ];
+ }
+ }
+
+ /**
+ * Retrieves the list of fields from provided audience
+ *
+ * @param string $apiKey
+ * @param string|int $listId
+ *
+ * @return array
+ */
+ public function getListFields( $apiKey, $listId ) {
+ $region = substr($apiKey, strpos($apiKey, '-') + 1);
+ $url = "https://$region.api.mailchimp.com/3.0/lists/$listId/merge-fields";
+
+ try{
+ $body = $this->callApi($apiKey, $url);
+
+ $mergeFields = empty( $body['status'] ) ? [
+ [
+ "merge_id" => 0,
+ "tag" => "email_address",
+ "name" => "Email",
+ "type" => "text",
+ "required" => true,
+ "default_value" => "",
+ "public" => true,
+ "display_order" => 0,
+ "options" => [
+ "size" => 25,
+ "choices" => null
+ ]
+ ]
+ ] : [];
+
+ if ( ! empty( $body['merge_fields'] ) ) {
+ $mergeFields = Arr::merge( $mergeFields, $body['merge_fields']);
+ }
+
+ return [
+ 'hits' => $mergeFields
+ ];
+ } catch (GuzzleException $e) {
+ return [
+ 'hits' => [],
+ 'errors' => [ $e->getMessage() ]
+ ];
+ }
+ }
+
+ public function submitToMailchimp( $leadId ): array
+ {
+ try{
+ $lead = Depicter::leadRepository()->get( $leadId );
+ if ( empty( $lead ) ) {
+ return [
+ 'success' => false,
+ 'error' => __( 'Lead does not exist', 'depicter' )
+ ];
+ }
+ } catch ( Exception $e ){
+ return [
+ 'success' => false,
+ 'error' => $e->getMessage()
+ ];
+ }
+
+ $config = $this->getConfig( $lead['source_id'] );
+ if ( empty( $config ) ) {
+ return [
+ 'success' => false,
+ 'error' => __( 'Lead form is not connected to mailchimp', 'depicter' )
+ ];
+ }
+
+ if ( empty( $config['apiKey'] ) ) {
+ return [
+ 'success' => false,
+ 'error' => __( 'Mailchimp API key is required', 'depicter' )
+ ];
+ }
+
+ $mappedEmailField = array_filter( $config['fieldMapping'], function ( $fieldMap ) {
+ return !empty( $fieldMap['to'] ) && $fieldMap['to'] == 'email_address';
+ });
+
+ if ( empty( $mappedEmailField ) ) {
+ return [
+ 'success' => false,
+ 'error' => __( 'Email is required', 'depicter' )
+ ];
+ }
+
+ try{
+ $leadFields = Depicter::leadFieldRepository()->leadField()->where( 'lead_id', $leadId )->findAll()->get();
+ if ( $leadFields ) {
+ $leadFields = $leadFields->toArray();
+ } else {
+ return [
+ 'success' => false,
+ 'error' => __( 'Lead does not have any field', 'depicter' )
+ ];
+ }
+ } catch ( Exception $e ){
+ return [
+ 'success' => false,
+ 'error' => $e->getMessage()
+ ];
+ }
+
+ $region = substr($config['apiKey'], strpos($config['apiKey'], '-') + 1);
+ $url = "https://" . $region . ".api.mailchimp.com/3.0/lists/" . $config['listId'] . "/members/";
+
+
+ $payload = [
+ 'status' => 'subscribed',
+ ];
+ $mergeFields = [];
+ foreach ( $leadFields as $leadField ) {
+ if ( $leadField['name'] == 'email' ) {
+ $payload['email_address'] = $leadField['value'];
+ continue;
+ }
+
+ $mappedField = array_filter( $config['fieldMapping'], function ( $fieldMap ) use ( $leadField ) {
+ return !empty( $fieldMap['from'] ) && $fieldMap['from'] == 'field:' . $leadField['name'];
+ });
+
+ if ( empty( $mappedField ) ) {
+ continue;
+ }
+
+ $mergeFields[ $mappedField[0]['to'] ] = $leadField['value'];
+ }
+ $payload['merge_fields'] = $mergeFields;
+
+ $client = new Client();
+ try {
+ $response = $client->post( $url, [
+ 'auth' => [
+ 'myToken', $config['apiKey'],
+ ],
+ 'json' => $payload
+ ]);
+
+ // todo: we have to check the final response here
+ return [
+ 'success' => true,
+ 'response' => JSON::decode($response->getBody()->getContents(), true)
+ ];
+ } catch ( GuzzleException $e ){
+ return [
+ 'success' => false,
+ 'error' => __( 'Mailchimp API error', 'depicter' )
+ ];
+ }
+ }
+
+ public function getConfig( $source_id ) {
+ $editorData = Depicter::document()->getEditorRawData( $source_id );
+ if ( empty( $editorData ) ) {
+ return [
+ 'success' => false,
+ 'error' => __( 'Empty document data', 'depicter' )
+ ];
+ }
+
+ $editorData = JSON::decode( $editorData, true );
+ if ( empty( $editorData['options']['integrations']['mailchimp']['config'] ) || empty( $editorData['options']['integrations']['mailchimp']['enabled'] ) ) {
+ return [
+ 'success' => false,
+ 'error' => __( 'Document is not connected to mailchimp', 'depicter' )
+ ];
+ }
+
+ return $editorData['options']['integrations']['mailchimp']['config'];
+ }
+
+ protected function callApi($apiKey, $url){
+
+ $client = new Client();
+ $response = $client->get( $url, [
+ 'auth' => ['anystring', $apiKey], // Mailchimp expects username:apikey format
+ 'headers' => [
+ 'Content-Type' => 'application/json'
+ ]
+ ]);
+
+ return JSON::decode($response->getBody()->getContents(), true);
+ }
+}
--- a/depicter/app/src/Integration/Manager.php
+++ b/depicter/app/src/Integration/Manager.php
@@ -0,0 +1,29 @@
+<?php
+namespace DepicterIntegration;
+
+use AvertaWordPressUtilityJSON;
+use DepicterIntegrationMailChimpMailChimp;
+
+class Manager {
+
+ public function init() {
+ add_action( 'depicter/lead/created', [ $this, 'add_queue_job' ], 10, 3 );
+ }
+
+ /**
+ * @return MailChimp
+ */
+ public function mailchimp(): MailChimp {
+ return Depicter::resolve('depicter.integration.mailchimp');
+ }
+
+ public function add_queue_job( $lead_id, $source_id, $content_id ) {
+ if ( false !== $config = $this->mailchimp()->getConfig( $source_id, $content_id ) ) {
+ $payload = JSON::encode([
+ 'lead_id' => $lead_id,
+ ]);
+
+ Depicter::queueJobsRepository()->create( 'mailchimp',$payload );
+ }
+ }
+}
--- a/depicter/app/src/Integration/ServiceProvider.php
+++ b/depicter/app/src/Integration/ServiceProvider.php
@@ -0,0 +1,38 @@
+<?php
+namespace DepicterIntegration;
+
+use DepicterIntegrationMailChimpMailChimp;
+use DepicterIntegrationManager;
+use WPEmergeServiceProvidersServiceProviderInterface;
+
+/**
+ * Load document data manager.
+ */
+class ServiceProvider implements ServiceProviderInterface {
+
+ /**
+ * {@inheritDoc}
+ */
+ public function register( $container ) {
+ $app = $container[ WPEMERGE_APPLICATION_KEY ];
+
+ $container[ 'depicter.integration.manager' ] = function () {
+ return new Manager();
+ };
+
+ $app->alias( 'integration', 'depicter.integration.manager' );
+
+ $container[ 'depicter.integration.mailchimp' ] = function () {
+ return new MailChimp();
+ };
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function bootstrap( $container ) {
+ Depicter::integration()->init();
+ }
+
+}
--- a/depicter/app/src/Middleware/CapabilityMiddleware.php
+++ b/depicter/app/src/Middleware/CapabilityMiddleware.php
@@ -35,7 +35,7 @@
// ignore the request if the current user doesn't have sufficient permissions
if ( ! current_user_can( $capability ) ) {
return $this->responseService->json([
- 'errors' => [ __( "Sorry, insufficient permission!", 'depicter' ) ]
+ 'errors' => [ __( sprintf( "Sorry, insufficient permission to perform this action! '%s' capability is required!", $capability ), 'depicter' ) ]
])->withStatus(403);
}
--- a/depicter/app/src/Modules/Gutenberg/build/index.asset.php
+++ b/depicter/app/src/Modules/Gutenberg/build/index.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element'), 'version' => '7c9b8dc55616a7f1a99f44519a29503c');
No newline at end of file
+<?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-blocks', 'wp-components'), 'version' => '29fbdd9be20a4812ce21');
--- a/depicter/app/src/Modules/Gutenberg/module.php
+++ b/depicter/app/src/Modules/Gutenberg/module.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace DepicterModulesGutenberg;
+
+class Module
+{
+ public function __construct() {
+ $this->initGutenbergBlock();
+ add_action( 'admin_enqueue_scripts', [ $this, 'loadGutenbergAdminWidgetScripts'] );
+ add_action( 'wp_enqueue_scripts', [$this, 'loadGutenbergWidgetScripts']);
+ }
+
+ public function loadGutenbergWidgetScripts() {
+ if ( $this->hasDepicter() ) {
+ wp_enqueue_style(
+ 'depicter-gutenberg',
+ Depicter::core()->assets()->getUrl() . '/app/src/Modules/Gutenberg/build/index.css',
+ [],
+ '1.0.0'
+ );
+ }
+ }
+
+ public function loadGutenbergAdminWidgetScripts() {
+
+ $current_screen = get_current_screen();
+ if ( !$current_screen->is_block_editor() ) {
+ return;
+ }
+
+ $list = [
+ [
+ 'id' => "0",
+ 'name' => __( 'Select Slider', 'depicter' )
+ ]
+ ];
+ $documents = Depicter::documentRepository()->select( ['id', 'name'] )->where('type', 'not in', ['popup', 'banner-bar'])->orderBy('modified_at', 'DESC')->findAll()->get();
+ $list = $documents ? array_merge( $list, $documents->toArray() ) : $list;
+ if ( !empty( $list ) ) {
+ foreach ( $list as $key => $item ) {
+ $list[ $key ]['label'] = $item['name'];
+ unset( $list[ $key ]['name'] );
+
+ $list[ $key ]['value'] = $item['id'];
+ unset( $list[ $key ]['id'] );
+ }
+ }
+
+ // load common assets
+ Depicter::front()->assets()->enqueueStyles();
+ Depicter::front()->assets()->enqueueScripts(['player', 'iframe-resizer']);
+
+ wp_localize_script( 'wp-block-editor', 'depicterSliders',[
+ 'list' => $list,
+ 'ajax_url' => admin_url('admin-ajax.php'),
+ 'editor_url' => Depicter::editor()->getEditUrl('1'),
+ 'token' => Depicter::csrf()->getToken( DepicterSecurityCSRF::EDITOR_ACTION ),
+ 'publish_text' => esc_html__( 'Publish Slider', 'depicter' ),
+ 'edit_text' => esc_html__( 'Edit Slider', 'depicter' )
+ ]);
+
+ }
+
+ public function initGutenbergBlock() {
+ register_block_type( __DIR__ . '/build', [
+ 'render_callback' => [ $this, 'renderGutenbergBlock' ]
+ ] );
+ }
+
+ public function renderGutenbergBlock( $blockAttributes ) {
+
+ if ( !empty( $blockAttributes['id'] ) ) {
+ $id = (int) $blockAttributes['id'];
+ return depicter( $id, ['echo' => false ] );
+ } else {
+ echo esc_html__( 'Slider ID required', 'depicter' );
+ }
+
+ }
+
+ public function hasDepicter($post_id = null) {
+ if (!$post_id) {
+ global $post;
+ $post_id = $post->ID ?? null;
+