class-vc-settings.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005
  1. <?php
  2. if ( ! defined( 'ABSPATH' ) ) {
  3. die( '-1' );
  4. }
  5. /**
  6. * Settings page for VC. list of tabs for function composer
  7. *
  8. * Settings page for VC creates menu item in admin menu as subpage of Settings section.
  9. * Settings are build with WP settings API and organized as tabs.
  10. *
  11. * List of tabs
  12. * 1. General Settings - set access rules and allowed content types for editors.
  13. * 2. Design Options - custom color and spacing editor for VC shortcodes elements.
  14. * 3. Custom CSS - add custom css to your WP pages.
  15. * 4. Product License - license key activation for automatic VC updates.
  16. * 5. My Shortcodes - automated mapping tool for shortcodes.
  17. *
  18. * @link http://codex.wordpress.org/Settings_API WordPress settings API
  19. * @since 3.4
  20. */
  21. class Vc_Settings {
  22. public $tabs;
  23. public $deactivate;
  24. public $locale;
  25. /**
  26. * @var string
  27. */
  28. protected $option_group = 'wpb_js_composer_settings';
  29. /**
  30. * @var string
  31. */
  32. protected $page = 'vc_settings';
  33. /**
  34. * @var string
  35. */
  36. protected static $field_prefix = 'wpb_js_';
  37. /**
  38. * @var string
  39. */
  40. protected static $notification_name = 'wpb_js_notify_user_about_element_class_names';
  41. /**
  42. * @var
  43. */
  44. protected static $color_settings;
  45. /**
  46. * @var
  47. */
  48. protected static $defaults;
  49. /**
  50. * @var
  51. */
  52. protected $composer;
  53. /**
  54. * @var array
  55. */
  56. protected $google_fonts_subsets_default = array( 'latin' );
  57. /**
  58. * @var array
  59. */
  60. protected $google_fonts_subsets = array(
  61. 'latin',
  62. 'vietnamese',
  63. 'cyrillic',
  64. 'latin-ext',
  65. 'greek',
  66. 'cyrillic-ext',
  67. 'greek-ext',
  68. );
  69. /**
  70. * @var array
  71. */
  72. public $google_fonts_subsets_excluded = array();
  73. /**
  74. * @param string $field_prefix
  75. */
  76. public static function setFieldPrefix( $field_prefix ) {
  77. self::$field_prefix = $field_prefix;
  78. }
  79. /**
  80. * @return string
  81. */
  82. public function page() {
  83. return $this->page;
  84. }
  85. /**
  86. * @return bool
  87. */
  88. public function isEditorEnabled() {
  89. global $current_user;
  90. wp_get_current_user();
  91. /** @var $settings - get use group access rules */
  92. $settings = $this->get( 'groups_access_rules' );
  93. $show = true;
  94. foreach ( $current_user->roles as $role ) {
  95. if ( isset( $settings[ $role ]['show'] ) && 'no' === $settings[ $role ]['show'] ) {
  96. $show = false;
  97. break;
  98. }
  99. }
  100. return $show;
  101. }
  102. /**
  103. *
  104. */
  105. public function setTabs() {
  106. $this->tabs = array();
  107. if ( $this->showConfigurationTabs() ) {
  108. $this->tabs['vc-general'] = esc_html__( 'General Settings', 'js_composer' );
  109. if ( ! vc_is_as_theme() || apply_filters( 'vc_settings_page_show_design_tabs', false ) ) {
  110. $this->tabs['vc-color'] = esc_html__( 'Design Options', 'js_composer' );
  111. $this->tabs['vc-custom_css'] = esc_html__( 'Custom CSS', 'js_composer' );
  112. }
  113. }
  114. if ( ! vc_is_network_plugin() || ( vc_is_network_plugin() && is_network_admin() ) ) {
  115. if ( ! vc_is_updater_disabled() ) {
  116. $this->tabs['vc-updater'] = esc_html__( 'Product License', 'js_composer' );
  117. }
  118. }
  119. // TODO: may allow to disable automapper
  120. if ( ! is_network_admin() && ! vc_automapper_is_disabled() ) {
  121. $this->tabs['vc-automapper'] = vc_automapper()->title();
  122. }
  123. }
  124. /**
  125. * @return mixed|void
  126. */
  127. public function getTabs() {
  128. if ( ! isset( $this->tabs ) ) {
  129. $this->setTabs();
  130. }
  131. return apply_filters( 'vc_settings_tabs', $this->tabs );
  132. }
  133. /**
  134. * @return bool
  135. */
  136. public function showConfigurationTabs() {
  137. return ! vc_is_network_plugin() || ! is_network_admin();
  138. }
  139. /**
  140. * Render
  141. *
  142. * @param $tab
  143. * @throws \Exception
  144. */
  145. public function renderTab( $tab ) {
  146. require_once vc_path_dir( 'CORE_DIR', 'class-vc-page.php' );
  147. wp_enqueue_style( 'wp-color-picker' );
  148. wp_enqueue_script( 'wp-color-picker' );
  149. if ( ( ( '1' === vc_get_param( 'build_css' ) || 'true' === vc_get_param( 'build_css' ) ) ) || ( ( '1' === vc_get_param( 'settings-updated' ) || 'true' === vc_get_param( 'settings-updated' ) ) ) ) {
  150. $this->buildCustomCss(); // TODO: remove this - no needs to re-save always
  151. }
  152. $tabs = $this->getTabs();
  153. foreach ( $tabs as $key => $value ) {
  154. if ( ! vc_user_access()->part( 'settings' )->can( $key . '-tab' )->get() ) {
  155. unset( $tabs[ $key ] );
  156. }
  157. }
  158. do_action( 'vc-settings-render-tab-' . $tab );
  159. $page = new Vc_Page();
  160. $page->setSlug( $tab )->setTitle( isset( $tabs[ $tab ] ) ? $tabs[ $tab ] : '' )->setTemplatePath( apply_filters( 'vc_settings-render-tab-' . $tab, 'pages/vc-settings/tab.php' ) );
  161. vc_include_template( 'pages/vc-settings/index.php', array(
  162. 'pages' => $tabs,
  163. 'active_page' => $page,
  164. 'vc_settings' => $this,
  165. ) );
  166. }
  167. /**
  168. * Init settings page && menu item
  169. * vc_filter: vc_settings_tabs - hook to override settings tabs
  170. */
  171. public function initAdmin() {
  172. $this->setTabs();
  173. self::$color_settings = array(
  174. array( 'vc_color' => array( 'title' => esc_html__( 'Main accent color', 'js_composer' ) ) ),
  175. array( 'vc_color_hover' => array( 'title' => esc_html__( 'Hover color', 'js_composer' ) ) ),
  176. array( 'vc_color_call_to_action_bg' => array( 'title' => esc_html__( 'Call to action background color', 'js_composer' ) ) ),
  177. array( 'vc_color_google_maps_bg' => array( 'title' => esc_html__( 'Google maps background color', 'js_composer' ) ) ),
  178. array( 'vc_color_post_slider_caption_bg' => array( 'title' => esc_html__( 'Post slider caption background color', 'js_composer' ) ) ),
  179. array( 'vc_color_progress_bar_bg' => array( 'title' => esc_html__( 'Progress bar background color', 'js_composer' ) ) ),
  180. array( 'vc_color_separator_border' => array( 'title' => esc_html__( 'Separator border color', 'js_composer' ) ) ),
  181. array( 'vc_color_tab_bg' => array( 'title' => esc_html__( 'Tabs navigation background color', 'js_composer' ) ) ),
  182. array( 'vc_color_tab_bg_active' => array( 'title' => esc_html__( 'Active tab background color', 'js_composer' ) ) ),
  183. );
  184. self::$defaults = array(
  185. 'vc_color' => '#f7f7f7',
  186. 'vc_color_hover' => '#F0F0F0',
  187. 'margin' => '35px',
  188. 'gutter' => '15',
  189. 'responsive_max' => '768',
  190. 'compiled_js_composer_less' => '',
  191. );
  192. if ( 'restore_color' === vc_post_param( 'vc_action' ) && vc_user_access()->check( 'wp_verify_nonce', vc_post_param( '_wpnonce' ), vc_settings()->getOptionGroup() . '_color' . '-options' )
  193. ->validateDie()->wpAny( 'manage_options' )->validateDie()->part( 'settings' )->can( 'vc-color-tab' )->validateDie()->get() ) {
  194. $this->restoreColor();
  195. }
  196. /**
  197. * @since 4.5 used to call update file once option is changed
  198. */
  199. add_action( 'update_option_wpb_js_compiled_js_composer_less', array(
  200. $this,
  201. 'buildCustomColorCss',
  202. ) );
  203. /**
  204. * @since 4.5 used to call update file once option is changed
  205. */
  206. add_action( 'update_option_wpb_js_custom_css', array(
  207. $this,
  208. 'buildCustomCss',
  209. ) );
  210. /**
  211. * @since 4.5 used to call update file once option is changed
  212. */
  213. add_action( 'add_option_wpb_js_compiled_js_composer_less', array(
  214. $this,
  215. 'buildCustomColorCss',
  216. ) );
  217. /**
  218. * @since 4.5 used to call update file once option is changed
  219. */
  220. add_action( 'add_option_wpb_js_custom_css', array(
  221. $this,
  222. 'buildCustomCss',
  223. ) );
  224. /**
  225. * Tab: General Settings
  226. */
  227. $tab = 'general';
  228. $this->addSection( $tab );
  229. $this->addField( $tab, esc_html__( 'Disable responsive content elements', 'js_composer' ), 'not_responsive_css', array(
  230. $this,
  231. 'sanitize_not_responsive_css_callback',
  232. ), array(
  233. $this,
  234. 'not_responsive_css_field_callback',
  235. ) );
  236. $this->addField( $tab, esc_html__( 'Google fonts subsets', 'js_composer' ), 'google_fonts_subsets', array(
  237. $this,
  238. 'sanitize_google_fonts_subsets_callback',
  239. ), array(
  240. $this,
  241. 'google_fonts_subsets_callback',
  242. ) );
  243. /**
  244. * Tab: Design Options
  245. */
  246. $tab = 'color';
  247. $this->addSection( $tab );
  248. // Use custom checkbox
  249. $this->addField( $tab, esc_html__( 'Use custom design options', 'js_composer' ), 'use_custom', array(
  250. $this,
  251. 'sanitize_use_custom_callback',
  252. ), array(
  253. $this,
  254. 'use_custom_callback',
  255. ) );
  256. foreach ( self::$color_settings as $color_set ) {
  257. foreach ( $color_set as $key => $data ) {
  258. $this->addField( $tab, $data['title'], $key, array(
  259. $this,
  260. 'sanitize_color_callback',
  261. ), array(
  262. $this,
  263. 'color_callback',
  264. ), array(
  265. 'id' => $key,
  266. ) );
  267. }
  268. }
  269. // Margin
  270. $this->addField( $tab, esc_html__( 'Elements bottom margin', 'js_composer' ), 'margin', array(
  271. $this,
  272. 'sanitize_margin_callback',
  273. ), array(
  274. $this,
  275. 'margin_callback',
  276. ) );
  277. // Gutter
  278. $this->addField( $tab, esc_html__( 'Grid gutter width', 'js_composer' ), 'gutter', array(
  279. $this,
  280. 'sanitize_gutter_callback',
  281. ), array(
  282. $this,
  283. 'gutter_callback',
  284. ) );
  285. // Responsive max width
  286. $this->addField( $tab, esc_html__( 'Mobile screen width', 'js_composer' ), 'responsive_max', array(
  287. $this,
  288. 'sanitize_responsive_max_callback',
  289. ), array(
  290. $this,
  291. 'responsive_max_callback',
  292. ) );
  293. $this->addField( $tab, false, 'compiled_js_composer_less', array(
  294. $this,
  295. 'sanitize_compiled_js_composer_less_callback',
  296. ), array(
  297. $this,
  298. 'compiled_js_composer_less_callback',
  299. ) );
  300. /**
  301. * Tab: Custom CSS
  302. */
  303. $tab = 'custom_css';
  304. $this->addSection( $tab );
  305. $this->addField( $tab, esc_html__( 'Paste your CSS code', 'js_composer' ), 'custom_css', array(
  306. $this,
  307. 'sanitize_custom_css_callback',
  308. ), array(
  309. $this,
  310. 'custom_css_field_callback',
  311. ) );
  312. /**
  313. * Custom Tabs
  314. */
  315. foreach ( $this->getTabs() as $tab => $title ) {
  316. do_action( 'vc_settings_tab-' . preg_replace( '/^vc\-/', '', $tab ), $this );
  317. }
  318. /**
  319. * Tab: Updater
  320. */
  321. $tab = 'updater';
  322. $this->addSection( $tab );
  323. }
  324. /**
  325. * Creates new section.
  326. *
  327. * @param $tab - tab key name as tab section
  328. * @param $title - Human title
  329. * @param $callback - function to build section header.
  330. */
  331. public function addSection( $tab, $title = null, $callback = null ) {
  332. add_settings_section( $this->option_group . '_' . $tab, $title, ( null !== $callback ? $callback : array(
  333. $this,
  334. 'setting_section_callback_function',
  335. ) ), $this->page . '_' . $tab );
  336. }
  337. /**
  338. * Create field in section.
  339. *
  340. * @param $tab
  341. * @param $title
  342. * @param $field_name
  343. * @param $sanitize_callback
  344. * @param $field_callback
  345. * @param array $args
  346. *
  347. * @return $this
  348. */
  349. public function addField( $tab, $title, $field_name, $sanitize_callback, $field_callback, $args = array() ) {
  350. register_setting( $this->option_group . '_' . $tab, self::$field_prefix . $field_name, $sanitize_callback );
  351. add_settings_field( self::$field_prefix . $field_name, $title, $field_callback, $this->page . '_' . $tab, $this->option_group . '_' . $tab, $args );
  352. return $this; // chaining
  353. }
  354. /**
  355. *
  356. */
  357. public function restoreColor() {
  358. foreach ( self::$color_settings as $color_sett ) {
  359. foreach ( $color_sett as $key => $value ) {
  360. delete_option( self::$field_prefix . $key );
  361. }
  362. }
  363. delete_option( self::$field_prefix . 'margin' );
  364. delete_option( self::$field_prefix . 'gutter' );
  365. delete_option( self::$field_prefix . 'responsive_max' );
  366. delete_option( self::$field_prefix . 'use_custom' );
  367. delete_option( self::$field_prefix . 'compiled_js_composer_less' );
  368. delete_option( self::$field_prefix . 'less_version' );
  369. }
  370. /**
  371. * @param $option_name
  372. *
  373. * @param bool $defaultValue
  374. *
  375. * @return mixed
  376. */
  377. public static function get( $option_name, $defaultValue = false ) {
  378. return get_option( self::$field_prefix . $option_name, $defaultValue );
  379. }
  380. /**
  381. * @param $option_name
  382. * @param $value
  383. *
  384. * @return bool
  385. */
  386. public static function set( $option_name, $value ) {
  387. return update_option( self::$field_prefix . $option_name, $value );
  388. }
  389. /**
  390. * Set up the enqueue for the CSS & JavaScript files.
  391. *
  392. */
  393. public function adminLoad() {
  394. wp_register_script( 'wpb_js_composer_settings', vc_asset_url( 'js/dist/settings.min.js' ), array(), WPB_VC_VERSION, true );
  395. wp_enqueue_style( 'js_composer_settings', vc_asset_url( 'css/js_composer_settings.min.css' ), false, WPB_VC_VERSION );
  396. wp_enqueue_script( 'backbone' );
  397. wp_enqueue_script( 'shortcode' );
  398. wp_enqueue_script( 'underscore' );
  399. wp_enqueue_script( 'jquery-ui-accordion' );
  400. wp_enqueue_script( 'jquery-ui-sortable' );
  401. wp_enqueue_script( 'wpb_js_composer_settings' );
  402. $this->locale = array(
  403. 'are_you_sure_reset_css_classes' => esc_html__( 'Are you sure you want to reset to defaults?', 'js_composer' ),
  404. 'are_you_sure_reset_color' => esc_html__( 'Are you sure you want to reset to defaults?', 'js_composer' ),
  405. 'saving' => esc_html__( 'Saving...', 'js_composer' ),
  406. 'save' => esc_html__( 'Save Changes', 'js_composer' ),
  407. 'saved' => esc_html__( 'Design Options successfully saved.', 'js_composer' ),
  408. 'save_error' => esc_html__( 'Design Options could not be saved', 'js_composer' ),
  409. 'form_save_error' => esc_html__( 'Problem with AJAX request execution, check internet connection and try again.', 'js_composer' ),
  410. 'are_you_sure_delete' => esc_html__( 'Are you sure you want to delete this shortcode?', 'js_composer' ),
  411. 'are_you_sure_delete_param' => esc_html__( "Are you sure you want to delete the shortcode's param?", 'js_composer' ),
  412. 'my_shortcodes_category' => esc_html__( 'My shortcodes', 'js_composer' ),
  413. 'error_shortcode_name_is_required' => esc_html__( 'Shortcode name is required.', 'js_composer' ),
  414. 'error_enter_valid_shortcode_tag' => esc_html__( 'Please enter valid shortcode tag.', 'js_composer' ),
  415. 'error_enter_required_fields' => esc_html__( 'Please enter all required fields for params.', 'js_composer' ),
  416. 'new_shortcode_mapped' => esc_html__( 'New shortcode mapped from string!', 'js_composer' ),
  417. 'shortcode_updated' => esc_html__( 'Shortcode updated!', 'js_composer' ),
  418. 'error_content_param_not_manually' => esc_html__( 'Content param can not be added manually, please use checkbox.', 'js_composer' ),
  419. 'error_param_already_exists' => esc_html__( 'Param %s already exists. Param names must be unique.', 'js_composer' ),
  420. 'error_wrong_param_name' => esc_html__( 'Please use only letters, numbers and underscore for param name', 'js_composer' ),
  421. 'error_enter_valid_shortcode' => esc_html__( 'Please enter valid shortcode to parse!', 'js_composer' ),
  422. );
  423. wp_localize_script( 'wpb_js_composer_settings', 'vcData', apply_filters( 'vc_global_js_data', array(
  424. 'version' => WPB_VC_VERSION,
  425. 'debug' => false,
  426. ) ) );
  427. wp_localize_script( 'wpb_js_composer_settings', 'i18nLocaleSettings', $this->locale );
  428. }
  429. /**
  430. *
  431. */
  432. public function custom_css_field_callback() {
  433. $value = get_option( self::$field_prefix . 'custom_css' );
  434. if ( empty( $value ) ) {
  435. $value = '';
  436. }
  437. echo '<textarea name="' . esc_attr( self::$field_prefix ) . 'custom_css' . '" class="wpb_csseditor custom_css" style="display:none">' . esc_textarea( $value ) . '</textarea>';
  438. echo '<pre id="wpb_csseditor" class="wpb_content_element custom_css" >' . esc_textarea( $value ) . '</pre>';
  439. echo '<p class="description indicator-hint">' . esc_html__( 'Add custom CSS code to the plugin without modifying files.', 'js_composer' ) . '</p>';
  440. }
  441. /**
  442. * Not responsive checkbox callback function
  443. */
  444. public function not_responsive_css_field_callback() {
  445. $checked = get_option( self::$field_prefix . 'not_responsive_css' );
  446. if ( empty( $checked ) ) {
  447. $checked = false;
  448. }
  449. ?>
  450. <label>
  451. <input type="checkbox"<?php echo $checked ? ' checked' : ''; ?> value="1"
  452. id="wpb_js_not_responsive_css" name="<?php echo esc_attr( self::$field_prefix . 'not_responsive_css' ); ?>">
  453. <?php esc_html_e( 'Disable', 'js_composer' ); ?>
  454. </label><br/>
  455. <p
  456. class="description indicator-hint"><?php esc_html_e( 'Disable content elements from "stacking" one on top other on small media screens (Example: mobile devices).', 'js_composer' ); ?></p>
  457. <?php
  458. }
  459. /**
  460. * Google fonts subsets callback
  461. */
  462. public function google_fonts_subsets_callback() {
  463. $pt_array = get_option( self::$field_prefix . 'google_fonts_subsets' );
  464. $pt_array = $pt_array ? $pt_array : $this->googleFontsSubsets();
  465. foreach ( $this->getGoogleFontsSubsets() as $pt ) {
  466. if ( ! in_array( $pt, $this->getGoogleFontsSubsetsExcluded(), true ) ) {
  467. $checked = ( in_array( $pt, $pt_array, true ) ) ? ' checked' : '';
  468. ?>
  469. <label>
  470. <input type="checkbox"<?php echo esc_attr( $checked ); ?> value="<?php echo esc_attr( $pt ); ?>"
  471. id="wpb_js_gf_subsets_<?php echo esc_attr( $pt ); ?>"
  472. name="<?php echo esc_attr( self::$field_prefix . 'google_fonts_subsets' ); ?>[]">
  473. <?php echo esc_html( $pt ); ?>
  474. </label><br>
  475. <?php
  476. }
  477. }
  478. ?>
  479. <p class="description indicator-hint"><?php esc_html_e( 'Select subsets for Google Fonts available to content elements.', 'js_composer' ); ?></p>
  480. <?php
  481. }
  482. /**
  483. * Get subsets for google fonts.
  484. *
  485. * @return array
  486. * @since 4.3
  487. * @access public
  488. */
  489. public function googleFontsSubsets() {
  490. if ( ! isset( $this->google_fonts_subsets_settings ) ) {
  491. $pt_array = vc_settings()->get( 'google_fonts_subsets' );
  492. $this->google_fonts_subsets_settings = $pt_array ? $pt_array : $this->googleFontsSubsetsDefault();
  493. }
  494. return $this->google_fonts_subsets_settings;
  495. }
  496. /**
  497. * @return array
  498. */
  499. public function googleFontsSubsetsDefault() {
  500. return $this->google_fonts_subsets_default;
  501. }
  502. /**
  503. * @return array
  504. */
  505. public function getGoogleFontsSubsets() {
  506. return $this->google_fonts_subsets;
  507. }
  508. /**
  509. * @param $subsets
  510. *
  511. * @return bool
  512. */
  513. public function setGoogleFontsSubsets( $subsets ) {
  514. if ( is_array( $subsets ) ) {
  515. $this->google_fonts_subsets = $subsets;
  516. return true;
  517. }
  518. return false;
  519. }
  520. /**
  521. * @return array
  522. */
  523. public function getGoogleFontsSubsetsExcluded() {
  524. return $this->google_fonts_subsets_excluded;
  525. }
  526. /**
  527. * @param $excluded
  528. *
  529. * @return bool
  530. */
  531. public function setGoogleFontsSubsetsExcluded( $excluded ) {
  532. if ( is_array( $excluded ) ) {
  533. $this->google_fonts_subsets_excluded = $excluded;
  534. return true;
  535. }
  536. return false;
  537. }
  538. /**
  539. * Not responsive checkbox callback function
  540. *
  541. */
  542. public function use_custom_callback() {
  543. $field = 'use_custom';
  544. $checked = get_option( self::$field_prefix . $field );
  545. $checked = $checked ? $checked : false;
  546. ?>
  547. <label>
  548. <input type="checkbox"<?php echo( $checked ? ' checked' : '' ); ?> value="1"
  549. id="wpb_js_<?php echo esc_attr( $field ); ?>" name="<?php echo esc_attr( self::$field_prefix . $field ); ?>">
  550. <?php esc_html_e( 'Enable', 'js_composer' ); ?>
  551. </label><br/>
  552. <p class="description indicator-hint"><?php esc_html_e( 'Enable the use of custom design options (Note: when checked - custom css file will be used).', 'js_composer' ); ?></p>
  553. <?php
  554. }
  555. /**
  556. * @param $args
  557. */
  558. public function color_callback( $args ) {
  559. $field = $args['id'];
  560. $value = get_option( self::$field_prefix . $field );
  561. $value = $value ? $value : $this->getDefault( $field );
  562. echo '<input type="text" name="' . esc_attr( self::$field_prefix . $field ) . '" value="' . esc_attr( $value ) . '" class="color-control css-control">';
  563. }
  564. /**
  565. *
  566. */
  567. public function margin_callback() {
  568. $field = 'margin';
  569. $value = get_option( self::$field_prefix . $field );
  570. $value = $value ? $value : $this->getDefault( $field );
  571. echo '<input type="text" name="' . esc_attr( self::$field_prefix . $field ) . '" value="' . esc_attr( $value ) . '" class="css-control">';
  572. echo '<p class="description indicator-hint css-control">' . esc_html__( 'Change default vertical spacing between content elements (Example: 20px).', 'js_composer' ) . '</p>';
  573. }
  574. /**
  575. *
  576. */
  577. public function gutter_callback() {
  578. $field = 'gutter';
  579. $value = get_option( self::$field_prefix . $field );
  580. $value = $value ? $value : $this->getDefault( $field );
  581. echo '<input type="text" name="' . esc_attr( self::$field_prefix . $field ) . '" value="' . esc_attr( $value ) . '" class="css-control"> px';
  582. echo '<p class="description indicator-hint css-control">' . esc_html__( 'Change default horizontal spacing between columns, enter new value in pixels.', 'js_composer' ) . '</p>';
  583. }
  584. /**
  585. *
  586. */
  587. public function responsive_max_callback() {
  588. $field = 'responsive_max';
  589. $value = get_option( self::$field_prefix . $field );
  590. $value = $value ? $value : $this->getDefault( $field );
  591. echo '<input type="text" name="' . esc_attr( self::$field_prefix . $field ) . '" value="' . esc_attr( $value ) . '" class="css-control"> px';
  592. echo '<p class="description indicator-hint css-control">' . esc_html__( 'By default content elements "stack" one on top other when screen size is smaller than 768px. Change the value to change "stacking" size.', 'js_composer' ) . '</p>';
  593. }
  594. /**
  595. *
  596. */
  597. public function compiled_js_composer_less_callback() {
  598. $field = 'compiled_js_composer_less';
  599. echo '<input type="hidden" name="' . esc_attr( self::$field_prefix . $field ) . '" value="">'; // VALUE must be empty
  600. }
  601. /**
  602. * @param $key
  603. *
  604. * @return string
  605. */
  606. public function getDefault( $key ) {
  607. return ! empty( self::$defaults[ $key ] ) ? self::$defaults[ $key ] : '';
  608. }
  609. /**
  610. * Callback function for settings section
  611. *
  612. * @param $tab
  613. */
  614. public function setting_section_callback_function( $tab ) {
  615. if ( 'wpb_js_composer_settings_color' === $tab['id'] ) {
  616. echo '<div class="tab_intro">
  617. <p>' . esc_html__( 'Here you can tweak default WPBakery Page Builder content elements visual appearance. By default WPBakery Page Builder is using neutral light-grey theme. Changing "Main accent color" will affect all content elements if no specific "content block" related color is set.', 'js_composer' ) . '
  618. </p>
  619. </div>';
  620. }
  621. }
  622. /**
  623. * @param $rules
  624. *
  625. * @return mixed
  626. */
  627. public function sanitize_not_responsive_css_callback( $rules ) {
  628. return (bool) $rules;
  629. }
  630. /**
  631. * @param $subsets
  632. *
  633. * @return array
  634. */
  635. public function sanitize_google_fonts_subsets_callback( $subsets ) {
  636. $pt_array = array();
  637. if ( isset( $subsets ) && is_array( $subsets ) ) {
  638. foreach ( $subsets as $pt ) {
  639. if ( ! in_array( $pt, $this->getGoogleFontsSubsetsExcluded(), true ) && in_array( $pt, $this->getGoogleFontsSubsets(), true ) ) {
  640. $pt_array[] = $pt;
  641. }
  642. }
  643. }
  644. return $pt_array;
  645. }
  646. /**
  647. * @param $rules
  648. *
  649. * @return mixed
  650. */
  651. public function sanitize_use_custom_callback( $rules ) {
  652. return (bool) $rules;
  653. }
  654. /**
  655. * @param $css
  656. *
  657. * @return mixed
  658. */
  659. public function sanitize_custom_css_callback( $css ) {
  660. return wp_strip_all_tags( $css );
  661. }
  662. /**
  663. * @param $css
  664. *
  665. * @return mixed
  666. */
  667. public function sanitize_compiled_js_composer_less_callback( $css ) {
  668. return $css;
  669. }
  670. /**
  671. * @param $color
  672. *
  673. * @return mixed
  674. */
  675. public function sanitize_color_callback( $color ) {
  676. return $color;
  677. }
  678. /**
  679. * @param $margin
  680. *
  681. * @return mixed
  682. */
  683. public function sanitize_margin_callback( $margin ) {
  684. $margin = preg_replace( '/\s/', '', $margin );
  685. if ( ! preg_match( '/^\d+(px|%|em|pt){0,1}$/', $margin ) ) {
  686. add_settings_error( self::$field_prefix . 'margin', 1, esc_html__( 'Invalid Margin value.', 'js_composer' ), 'error' );
  687. }
  688. return $margin;
  689. }
  690. /**
  691. * @param $gutter
  692. *
  693. * @return mixed
  694. */
  695. public function sanitize_gutter_callback( $gutter ) {
  696. $gutter = preg_replace( '/[^\d]/', '', $gutter );
  697. if ( ! $this->_isGutterValid( $gutter ) ) {
  698. add_settings_error( self::$field_prefix . 'gutter', 1, esc_html__( 'Invalid Gutter value.', 'js_composer' ), 'error' );
  699. }
  700. return $gutter;
  701. }
  702. /**
  703. * @param $responsive_max
  704. *
  705. * @return mixed
  706. */
  707. public function sanitize_responsive_max_callback( $responsive_max ) {
  708. if ( ! $this->_isNumberValid( $responsive_max ) ) {
  709. add_settings_error( self::$field_prefix . 'responsive_max', 1, esc_html__( 'Invalid "Responsive max" value.', 'js_composer' ), 'error' );
  710. }
  711. return $responsive_max;
  712. }
  713. /**
  714. * @param $number
  715. *
  716. * @return int
  717. */
  718. public static function _isNumberValid( $number ) {
  719. return preg_match( '/^[\d]+(\.\d+){0,1}$/', $number );
  720. }
  721. /**
  722. * @param $gutter
  723. *
  724. * @return int
  725. */
  726. public static function _isGutterValid( $gutter ) {
  727. return self::_isNumberValid( $gutter );
  728. }
  729. /**
  730. * @return mixed|void
  731. */
  732. public function useCustomCss() {
  733. $use_custom = get_option( self::$field_prefix . 'use_custom', false );
  734. return $use_custom;
  735. }
  736. /**
  737. * @return mixed|void
  738. */
  739. public function getCustomCssVersion() {
  740. $less_version = get_option( self::$field_prefix . 'less_version', false );
  741. return $less_version;
  742. }
  743. /**
  744. *
  745. */
  746. public function rebuild() {
  747. /** WordPress Template Administration API */
  748. require_once ABSPATH . 'wp-admin/includes/template.php';
  749. /** WordPress Administration File API */
  750. require_once ABSPATH . 'wp-admin/includes/file.php';
  751. delete_option( self::$field_prefix . 'compiled_js_composer_less' );
  752. $this->initAdmin();
  753. $this->buildCustomCss(); // TODO: remove this - no needs to re-save always
  754. }
  755. /**
  756. *
  757. */
  758. public static function buildCustomColorCss() {
  759. /**
  760. * Filesystem API init.
  761. * */
  762. $url = wp_nonce_url( 'admin.php?page=vc-color&build_css=1', 'wpb_js_settings_save_action' );
  763. self::getFileSystem( $url );
  764. /** @var \WP_Filesystem_Direct $wp_filesystem */ global $wp_filesystem;
  765. /**
  766. *
  767. * Building css file.
  768. *
  769. */
  770. $js_composer_upload_dir = self::checkCreateUploadDir( $wp_filesystem, 'use_custom', 'js_composer_front_custom.css' );
  771. if ( ! $js_composer_upload_dir ) {
  772. return;
  773. }
  774. $filename = $js_composer_upload_dir . '/js_composer_front_custom.css';
  775. $use_custom = get_option( self::$field_prefix . 'use_custom' );
  776. if ( ! $use_custom ) {
  777. $wp_filesystem->put_contents( $filename, '', FS_CHMOD_FILE );
  778. return;
  779. }
  780. $css_string = get_option( self::$field_prefix . 'compiled_js_composer_less' );
  781. if ( strlen( trim( $css_string ) ) > 0 ) {
  782. update_option( self::$field_prefix . 'less_version', WPB_VC_VERSION );
  783. delete_option( self::$field_prefix . 'compiled_js_composer_less' );
  784. $css_string = wp_strip_all_tags( $css_string );
  785. // HERE goes the magic
  786. if ( ! $wp_filesystem->put_contents( $filename, $css_string, FS_CHMOD_FILE ) ) {
  787. if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
  788. add_settings_error( self::$field_prefix . 'main_color', $wp_filesystem->errors->get_error_code(), esc_html__( 'Something went wrong: js_composer_front_custom.css could not be created.', 'js_composer' ) . ' ' . $wp_filesystem->errors->get_error_message(), 'error' );
  789. } elseif ( ! $wp_filesystem->connect() ) {
  790. add_settings_error( self::$field_prefix . 'main_color', $wp_filesystem->errors->get_error_code(), esc_html__( 'js_composer_front_custom.css could not be created. Connection error.', 'js_composer' ), 'error' );
  791. } elseif ( ! $wp_filesystem->is_writable( $filename ) ) {
  792. add_settings_error( self::$field_prefix . 'main_color', $wp_filesystem->errors->get_error_code(), sprintf( esc_html__( 'js_composer_front_custom.css could not be created. Cannot write custom css to "%s".', 'js_composer' ), $filename ), 'error' );
  793. } else {
  794. add_settings_error( self::$field_prefix . 'main_color', $wp_filesystem->errors->get_error_code(), esc_html__( 'js_composer_front_custom.css could not be created. Problem with access.', 'js_composer' ), 'error' );
  795. }
  796. delete_option( self::$field_prefix . 'use_custom' );
  797. delete_option( self::$field_prefix . 'less_version' );
  798. }
  799. }
  800. }
  801. /**
  802. * Builds custom css file using css options from vc settings.
  803. *
  804. * @return bool
  805. */
  806. public static function buildCustomCss() {
  807. /**
  808. * Filesystem API init.
  809. * */
  810. $url = wp_nonce_url( 'admin.php?page=vc-color&build_css=1', 'wpb_js_settings_save_action' );
  811. self::getFileSystem( $url );
  812. /** @var \WP_Filesystem_Direct $wp_filesystem */ global $wp_filesystem;
  813. /**
  814. * Building css file.
  815. */
  816. $js_composer_upload_dir = self::checkCreateUploadDir( $wp_filesystem, 'custom_css', 'custom.css' );
  817. if ( ! $js_composer_upload_dir ) {
  818. return true;
  819. }
  820. $filename = $js_composer_upload_dir . '/custom.css';
  821. $css_string = '';
  822. $custom_css_string = get_option( self::$field_prefix . 'custom_css' );
  823. if ( ! empty( $custom_css_string ) ) {
  824. $assets_url = vc_asset_url( '' );
  825. $css_string .= preg_replace( '/(url\(\.\.\/(?!\.))/', 'url(' . $assets_url, $custom_css_string );
  826. $css_string = wp_strip_all_tags( $css_string );
  827. }
  828. if ( ! $wp_filesystem->put_contents( $filename, $css_string, FS_CHMOD_FILE ) ) {
  829. if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
  830. add_settings_error( self::$field_prefix . 'custom_css', $wp_filesystem->errors->get_error_code(), esc_html__( 'Something went wrong: custom.css could not be created.', 'js_composer' ) . $wp_filesystem->errors->get_error_message(), 'error' );
  831. } elseif ( ! $wp_filesystem->connect() ) {
  832. add_settings_error( self::$field_prefix . 'custom_css', $wp_filesystem->errors->get_error_code(), esc_html__( 'custom.css could not be created. Connection error.', 'js_composer' ), 'error' );
  833. } elseif ( ! $wp_filesystem->is_writable( $filename ) ) {
  834. add_settings_error( self::$field_prefix . 'custom_css', $wp_filesystem->errors->get_error_code(), sprintf( esc_html__( 'custom.css could not be created. Cannot write custom css to %s.', 'js_composer' ), $filename ), 'error' );
  835. } else {
  836. add_settings_error( self::$field_prefix . 'custom_css', $wp_filesystem->errors->get_error_code(), esc_html__( 'custom.css could not be created. Problem with access.', 'js_composer' ), 'error' );
  837. }
  838. return false;
  839. }
  840. return true;
  841. }
  842. /**
  843. * @param \WP_Filesystem_Direct $wp_filesystem
  844. * @param $option
  845. * @param $filename
  846. *
  847. * @return bool|string
  848. */
  849. public static function checkCreateUploadDir( $wp_filesystem, $option, $filename ) {
  850. $js_composer_upload_dir = self::uploadDir();
  851. if ( ! $wp_filesystem->is_dir( $js_composer_upload_dir ) ) {
  852. if ( ! $wp_filesystem->mkdir( $js_composer_upload_dir, 0777 ) ) {
  853. add_settings_error( self::$field_prefix . $option, $wp_filesystem->errors->get_error_code(), sprintf( esc_html__( '%s could not be created. Not available to create js_composer directory in uploads directory (%s).', 'js_composer' ), $filename, $js_composer_upload_dir ), 'error' );
  854. return false;
  855. }
  856. }
  857. return $js_composer_upload_dir;
  858. }
  859. /**
  860. * @return string
  861. */
  862. public static function uploadDir() {
  863. $upload_dir = wp_upload_dir();
  864. /** @var \WP_Filesystem_Direct $wp_filesystem */ global $wp_filesystem;
  865. return $wp_filesystem->find_folder( $upload_dir['basedir'] ) . vc_upload_dir();
  866. }
  867. /**
  868. * @return string
  869. */
  870. public static function uploadURL() {
  871. $upload_dir = wp_upload_dir();
  872. return $upload_dir['baseurl'] . vc_upload_dir();
  873. }
  874. /**
  875. * @return string
  876. */
  877. public static function getFieldPrefix() {
  878. return self::$field_prefix;
  879. }
  880. /**
  881. * @param string $url
  882. * @return \WP_Filesystem_Direct|bool
  883. */
  884. protected static function getFileSystem( $url = '' ) {
  885. /** @var \WP_Filesystem_Direct $wp_filesystem */ global $wp_filesystem;
  886. $status = true;
  887. if ( ! $wp_filesystem || ! is_object( $wp_filesystem ) ) {
  888. require_once ABSPATH . '/wp-admin/includes/file.php';
  889. $status = WP_Filesystem( false, false, true );
  890. }
  891. return $status ? $wp_filesystem : false;
  892. }
  893. /**
  894. * @return string
  895. */
  896. public function getOptionGroup() {
  897. return $this->option_group;
  898. }
  899. }