iThemes Sync - Version 2.0.0

Version Description

  • Initial Release to wordpress.org

=

Download this release

Release Info

Developer ithemes
Plugin Icon 128x128 iThemes Sync
Version 2.0.0
Comparing to
See all releases

Version 2.0.0

Files changed (105) hide show
  1. admin.php +233 -0
  2. api.php +274 -0
  3. api.txt +60 -0
  4. ca/index.php +1 -0
  5. ca/roots.crt +58 -0
  6. class-ithemes-credentials.php +143 -0
  7. class-ithemes-sync-json.php +64 -0
  8. class-ithemes-sync-utf8-encoder.php +343 -0
  9. client-dashboard.php +413 -0
  10. css/admin-notice.css +53 -0
  11. css/client-dashboard.css +147 -0
  12. css/index.php +1 -0
  13. css/settings-page.css +195 -0
  14. css/social-metabox.css +52 -0
  15. duplicator.php +127 -0
  16. functions.php +1011 -0
  17. history.txt +184 -0
  18. images/index.php +1 -0
  19. index.php +1 -0
  20. init.php +54 -0
  21. js/admin-notice.js +25 -0
  22. js/index.php +1 -0
  23. js/settings-page.js +10 -0
  24. js/social-metabox.js +49 -0
  25. lang/index.php +1 -0
  26. lang/ithemes-sync.pot +620 -0
  27. lib/index.php +1 -0
  28. lib/updater/admin.php +215 -0
  29. lib/updater/api.php +301 -0
  30. lib/updater/api.txt +40 -0
  31. lib/updater/ca/index.php +1 -0
  32. lib/updater/ca/roots.crt +58 -0
  33. lib/updater/class-ithemes-credentials.php +143 -0
  34. lib/updater/css/index.php +1 -0
  35. lib/updater/css/settings-page.css +67 -0
  36. lib/updater/functions.php +61 -0
  37. lib/updater/history.txt +38 -0
  38. lib/updater/images/index.php +1 -0
  39. lib/updater/index.php +1 -0
  40. lib/updater/information.php +56 -0
  41. lib/updater/init.php +54 -0
  42. lib/updater/keys.php +165 -0
  43. lib/updater/load.php +54 -0
  44. lib/updater/packages.php +223 -0
  45. lib/updater/server.php +217 -0
  46. lib/updater/settings-page.php +659 -0
  47. lib/updater/settings.php +336 -0
  48. lib/updater/updates.php +166 -0
  49. load-translations.php +21 -0
  50. load.php +188 -0
  51. notice-handler.php +106 -0
  52. notices.php +187 -0
  53. readme.txt +144 -0
  54. request-handler.php +459 -0
  55. server.php +239 -0
  56. settings-page.php +349 -0
  57. settings.php +211 -0
  58. social.php +167 -0
  59. uninstall.php +22 -0
  60. upgrader-skin.php +23 -0
  61. verbs/db-optimization.php +388 -0
  62. verbs/deauthenticate-user.php +36 -0
  63. verbs/do-update.php +326 -0
  64. verbs/get-admin-bar-item-whitelist.php +32 -0
  65. verbs/get-admin-bar-items.php +32 -0
  66. verbs/get-admin-menu.php +22 -0
  67. verbs/get-authentication-token.php +52 -0
  68. verbs/get-comment-details.php +173 -0
  69. verbs/get-dashboard-widgets.php +22 -0
  70. verbs/get-gf-form-entries.php +64 -0
  71. verbs/get-gf-forms.php +25 -0
  72. verbs/get-notices.php +40 -0
  73. verbs/get-options.php +73 -0
  74. verbs/get-php-details.php +30 -0
  75. verbs/get-plugin-details.php +30 -0
  76. verbs/get-post-types.php +29 -0
  77. verbs/get-posts.php +84 -0
  78. verbs/get-role-details.php +40 -0
  79. verbs/get-server-details.php +30 -0
  80. verbs/get-status-elements.php +28 -0
  81. verbs/get-status.php +79 -0
  82. verbs/get-supported-verbs.php +32 -0
  83. verbs/get-sync-settings.php +30 -0
  84. verbs/get-theme-details.php +30 -0
  85. verbs/get-update-details.php +30 -0
  86. verbs/get-updates.php +32 -0
  87. verbs/get-user-details.php +52 -0
  88. verbs/get-wordpress-details.php +30 -0
  89. verbs/get-wordpress-settings.php +248 -0
  90. verbs/get-wordpress-users.php +31 -0
  91. verbs/get-yoast-seo-summary.php +27 -0
  92. verbs/index.php +1 -0
  93. verbs/manage-comments.php +344 -0
  94. verbs/manage-ithemes-licenses.php +111 -0
  95. verbs/manage-options.php +75 -0
  96. verbs/manage-plugins.php +215 -0
  97. verbs/manage-posts.php +136 -0
  98. verbs/manage-reports.php +107 -0
  99. verbs/manage-roles.php +100 -0
  100. verbs/manage-themes.php +189 -0
  101. verbs/manage-users.php +120 -0
  102. verbs/set-admin-bar-item-whitelist.php +40 -0
  103. verbs/update-google-site-verification-token.php +23 -0
  104. verbs/update-show-sync.php +38 -0
  105. verbs/verb.php +30 -0
admin.php ADDED
@@ -0,0 +1,233 @@
1
+ <?php
2
+
3
+ /*
4
+ Set up admin interface.
5
+ Written by Chris Jean for iThemes.com
6
+ Version 1.2.0
7
+
8
+ Version History
9
+ 1.0.0 - 2013-10-02 - Chris Jean
10
+ Initial version
11
+ 1.1.0 - 2013-11-19 - Chris Jean
12
+ Added the ability for the show_sync option to control who sees the Sync interface and plugin.
13
+ 1.2.0 - 2014-02-14 - Chris Jean
14
+ Added support for ?ithemes-sync-force-display=1 in the admin page to force a hidden Sync plugin to display for that specific user.
15
+ */
16
+
17
+
18
+ require_once( $GLOBALS['ithemes_sync_path'] . '/load-translations.php' );
19
+
20
+ class Ithemes_Sync_Admin {
21
+ private $page_name = 'ithemes-sync';
22
+
23
+ private $page_ref;
24
+
25
+
26
+ public function __construct() {
27
+ add_action( 'init', array( $this, 'init' ) );
28
+ add_action( 'wp_ajax_ithemes_sync_hide_notice', array( $this, 'hide_authenticate_notice' ) );
29
+ }
30
+
31
+ public function modify_plugins_page() {
32
+ add_filter( 'all_plugins', array( $this, 'remove_sync_plugin' ) );
33
+ }
34
+
35
+ public function remove_sync_plugin( $plugins ) {
36
+ unset( $plugins[basename( $GLOBALS['ithemes_sync_path'] ) . '/init.php'] );
37
+
38
+ return $plugins;
39
+ }
40
+
41
+ public function init() {
42
+ require_once( $GLOBALS['ithemes_sync_path'] . '/settings.php' );
43
+
44
+
45
+ $show_sync = $GLOBALS['ithemes-sync-settings']->get_option( 'show_sync' );
46
+
47
+ if ( is_array( $show_sync ) ) {
48
+ $show_sync = in_array( get_current_user_id(), $show_sync );
49
+ }
50
+
51
+ if ( ! $show_sync && current_user_can( 'manage_options' ) ) {
52
+ $user_id = get_current_user_id();
53
+
54
+ if ( isset( $_GET['ithemes-sync-force-display'] ) ) {
55
+ if ( ! empty( $_GET['ithemes-sync-force-display'] ) ) {
56
+ $show_sync = true;
57
+ set_site_transient( "ithemes-sync-force-display-$user_id", true, 600 );
58
+
59
+ add_action( 'all_admin_notices', array( $this, 'show_force_display_notice' ), 0 );
60
+ } else {
61
+ delete_site_transient( "ithemes-sync-force-display-$user_id" );
62
+
63
+ add_action( 'all_admin_notices', array( $this, 'show_force_display_disable_notice' ), 0 );
64
+ }
65
+ } else if ( false !== get_site_transient( "ithemes-sync-force-display-$user_id" ) ) {
66
+ $show_sync = true;
67
+
68
+ add_action( 'all_admin_notices', array( $this, 'show_force_display_notice' ), 0 );
69
+ }
70
+ }
71
+
72
+
73
+ if ( $show_sync ) {
74
+ if ( ! is_multisite() || is_super_admin() ) {
75
+ add_action( 'admin_menu', array( $this, 'add_admin_pages' ) );
76
+ }
77
+
78
+ add_action( 'network_admin_menu', array( $this, 'add_network_admin_pages' ) );
79
+
80
+
81
+ if ( current_user_can( 'manage_options' ) ) {
82
+ if ( ! get_site_option( 'ithemes-sync-authenticated' ) && ( empty( $_GET['page'] ) || ( $this->page_name != $_GET['page'] ) ) && ! get_site_option( 'ithemes_sync_hide_authenticate_notice' ) ) {
83
+ require_once( $GLOBALS['ithemes_sync_path'] . '/functions.php' );
84
+
85
+ $path_url = Ithemes_Sync_Functions::get_url( $GLOBALS['ithemes_sync_path'] );
86
+ wp_enqueue_style( 'ithemes-updater-admin-notice-style', "$path_url/css/admin-notice.css" );
87
+ wp_enqueue_script( 'ithemes-updater-admin-notice-script', "$path_url/js/admin-notice.js", array( 'jquery' ) );
88
+
89
+ add_action( 'all_admin_notices', array( $this, 'show_authenticate_notice' ), 0 );
90
+
91
+ delete_site_transient( 'ithemes-sync-activated' );
92
+ }
93
+ else if ( ! empty( $_GET['activate'] ) && get_site_transient( 'ithemes-sync-activated' ) ) {
94
+ require_once( $GLOBALS['ithemes_sync_path'] . '/functions.php' );
95
+
96
+ $path_url = Ithemes_Sync_Functions::get_url( $GLOBALS['ithemes_sync_path'] );
97
+ wp_enqueue_style( 'ithemes-updater-admin-notice-style', "$path_url/css/admin-notice.css" );
98
+ wp_enqueue_script( 'ithemes-updater-admin-notice-script', "$path_url/js/admin-notice.js", array( 'jquery' ) );
99
+
100
+ add_action( 'all_admin_notices', array( $this, 'show_activate_notice' ), 0 );
101
+
102
+ delete_site_transient( 'ithemes-sync-activated' );
103
+ }
104
+ }
105
+ } else {
106
+ add_action( 'load-plugins.php', array( $this, 'modify_plugins_page' ) );
107
+ }
108
+ }
109
+
110
+ public function show_activate_notice() {
111
+ if ( is_multisite() && is_network_admin() )
112
+ $url = network_admin_url( 'settings.php' ) . "?page={$this->page_name}";
113
+ else
114
+ $url = admin_url( 'options-general.php' ) . "?page={$this->page_name}";
115
+
116
+ ?>
117
+ <div class="updated" id="ithemes-sync-notice">
118
+ <?php printf( __( 'iThemes Sync is active. <a class="ithemes-sync-notice-button" href="%s">Manage Sync</a> <a class="ithemes-sync-notice-dismiss" href="#">×</a>', 'it-l10n-ithemes-sync' ), $url ); ?>
119
+ </div>
120
+ <?php
121
+
122
+ }
123
+
124
+ public function show_authenticate_notice() {
125
+ if ( is_multisite() && is_network_admin() )
126
+ $url = network_admin_url( 'settings.php' ) . "?page={$this->page_name}";
127
+ else
128
+ $url = admin_url( 'options-general.php' ) . "?page={$this->page_name}";
129
+
130
+ ?>
131
+ <div class="updated" id="ithemes-sync-notice">
132
+ <?php printf( __( 'iThemes Sync is almost ready. <a class="ithemes-sync-notice-button" href="%s">Set Up Sync</a> <a class="ithemes-sync-notice-hide" href="#">×</a>', 'it-l10n-ithemes-sync' ), $url ); ?>
133
+ </div>
134
+ <?php
135
+
136
+ }
137
+
138
+ public function show_force_display_notice() {
139
+ $user_id = get_current_user_id();
140
+ $time = get_site_option( "_site_transient_timeout_ithemes-sync-force-display-$user_id" );
141
+ $time_diff = human_time_diff( time(), $time );
142
+
143
+ $url = admin_url( 'index.php?ithemes-sync-force-display=0' );
144
+
145
+ ?>
146
+ <div class="updated">
147
+ <p><?php printf( __( 'iThemes Sync will show for your user for the next %1$s. Click <a href="%2$s">here</a> to hide iThemes Sync again.', 'it-l10n-ithemes-sync' ), $time_diff, $url ); ?></p>
148
+ </div>
149
+ <?php
150
+
151
+ }
152
+
153
+ public function show_force_display_disable_notice() {
154
+
155
+ ?>
156
+ <div class="updated">
157
+ <p><?php _e( 'iThemes Sync is now hidden from your user again.', 'it-l10n-ithemes-sync' ); ?></p>
158
+ </div>
159
+ <?php
160
+
161
+ }
162
+
163
+ public function hide_authenticate_notice() {
164
+ update_site_option( 'ithemes_sync_hide_authenticate_notice', true );
165
+ }
166
+
167
+ public function add_admin_pages() {
168
+ $this->page_ref = add_options_page( __( 'iThemes Sync', 'it-l10n-ithemes-sync' ), __( 'iThemes Sync', 'it-l10n-ithemes-sync' ), 'manage_options', $this->page_name, array( $this, 'settings_index' ) );
169
+
170
+ add_action( "load-{$this->page_ref}", array( $this, 'load_settings_page' ) );
171
+ }
172
+
173
+ public function add_network_admin_pages() {
174
+ $this->page_ref = add_submenu_page( 'settings.php', __( 'iThemes Sync', 'it-l10n-ithemes-sync' ), __( 'iThemes Sync', 'it-l10n-ithemes-sync' ), 'manage_options', $this->page_name, array( $this, 'settings_index' ) );
175
+
176
+ add_action( "load-{$this->page_ref}", array( $this, 'load_settings_page' ) );
177
+ }
178
+
179
+ public function load_settings_page() {
180
+ require_once( $GLOBALS['ithemes_sync_path'] . '/settings.php' );
181
+
182
+ require( $GLOBALS['ithemes_sync_path'] . '/settings-page.php' );
183
+ }
184
+
185
+ public function settings_index() {
186
+ do_action( 'ithemes_sync_settings_page_index' );
187
+ }
188
+
189
+ private function set_package_details() {
190
+ if ( false !== $this->package_details )
191
+ return;
192
+
193
+ require_once( $GLOBALS['ithemes_updater_path'] . '/packages.php' );
194
+ $this->package_details = Ithemes_Updater_Packages::get_local_details();
195
+ }
196
+
197
+ private function set_registration_link() {
198
+ if ( false !== $this->registration_link )
199
+ return;
200
+
201
+ $url = admin_url( 'options-general.php' ) . "?page={$this->page_name}";
202
+ $this->registration_link = sprintf( '<a href="%1$s" title="%2$s">%3$s</a>', $url, __( 'Manage iThemes product licenses to receive automatic upgrade support', 'it-l10n-ithemes-sync' ), __( 'License', 'it-l10n-ithemes-sync' ) );
203
+ }
204
+
205
+ public function filter_plugin_action_links( $actions, $plugin_file, $plugin_data, $context ) {
206
+ $this->set_package_details();
207
+ $this->set_registration_link();
208
+
209
+ if ( isset( $this->package_details[$plugin_file] ) )
210
+ $actions[] = $this->registration_link;
211
+
212
+ return $actions;
213
+ }
214
+
215
+ public function filter_theme_action_links( $actions, $theme ) {
216
+ $this->set_package_details();
217
+ $this->set_registration_link();
218
+
219
+ if ( is_object( $theme ) )
220
+ $path = basename( $theme->get_stylesheet_directory() ) . '/style.css';
221
+ else if ( is_array( $theme ) && isset( $theme['Stylesheet Dir'] ) )
222
+ $path = $theme['Stylesheet Dir'] . '/style.css';
223
+ else
224
+ $path = '';
225
+
226
+ if ( isset( $this->package_details[$path] ) )
227
+ $actions[] = $this->registration_link;
228
+
229
+ return $actions;
230
+ }
231
+ }
232
+
233
+ new Ithemes_Sync_Admin();
api.php ADDED
@@ -0,0 +1,274 @@
1
+ <?php
2
+
3
+ /*
4
+ Simple API for managing verbs for Sync.
5
+ Written by Chris Jean for iThemes.com
6
+ Version 1.4.1
7
+
8
+ Version History
9
+ 1.0.0 - 2013-10-01 - Chris Jean
10
+ Initial version
11
+ 1.1.0 - 2013-11-18 - Chris Jean
12
+ Updated default verbs.
13
+ 1.2.0 - 2014-01-20 - Chris Jean
14
+ Added: get-status-elements, manage-plugins, and manage-themes.
15
+ Added: is_default_status_element(), get_default_status_elements().
16
+ 1.2.1 - 2014-02-07 - Chris Jean
17
+ When including a verb's file, errors are no longer hidden. This allows for easier detection of verbs with problems.
18
+ Better handling of invalid verbs.
19
+ 1.3.0 - 2014-03-25 - Chris Jean
20
+ Added: get-notices.
21
+ 1.4.0 - 2014-07-01 - Chris Jean
22
+ Added: get-comment-details, get-options, get-role-details, get-user-details, manage-commments, manage-roles, manage-users.
23
+ register() now returns true when a Verb has been successfully registered.
24
+ 1.4.1 - 2014-11-10 - Chris Jean
25
+ Changed init hook priority to 11 in order to avoid issues with some plugin updates not appearing.
26
+ */
27
+
28
+
29
+ class Ithemes_Sync_API {
30
+ private $verbs = array();
31
+
32
+ private $default_verbs = array(
33
+ 'db-optimization' => 'Ithemes_Sync_Verb_DB_Optimization',
34
+ 'deauthenticate-user' => 'Ithemes_Sync_Verb_Deauthenticate_User',
35
+ 'do-update' => 'Ithemes_Sync_Verb_Do_Update',
36
+ 'get-admin-bar-item-whitelist' => 'Ithemes_Sync_Verb_Get_Admin_Bar_Item_Whitelist',
37
+ 'get-admin-bar-items' => 'Ithemes_Sync_Verb_Get_Admin_Bar_Items',
38
+ 'get-admin-menu' => 'Ithemes_Sync_Verb_Get_Admin_Menu',
39
+ 'get-authentication-token' => 'Ithemes_Sync_Verb_Get_Authentication_Token',
40
+ 'get-comment-details' => 'Ithemes_Sync_Verb_Get_Comment_Details',
41
+ 'get-dashboard-widgets' => 'Ithemes_Sync_Verb_Get_Dashboard_Widgets',
42
+ 'get-options' => 'Ithemes_Sync_Verb_Get_Options',
43
+ 'get-php-details' => 'Ithemes_Sync_Verb_Get_PHP_Details',
44
+ 'get-plugin-details' => 'Ithemes_Sync_Verb_Get_Plugin_Details',
45
+ 'get-posts' => 'Ithemes_Sync_Verb_Get_Posts',
46
+ 'get-post-types' => 'Ithemes_Sync_Verb_Get_Post_Types',
47
+ 'get-notices' => 'Ithemes_Sync_Verb_Get_Notices',
48
+ 'get-role-details' => 'Ithemes_Sync_Verb_Get_Role_Details',
49
+ 'get-server-details' => 'Ithemes_Sync_Verb_Get_Server_Details',
50
+ 'get-status' => 'Ithemes_Sync_Verb_Get_Status',
51
+ 'get-status-elements' => 'Ithemes_Sync_Verb_Get_Status_Elements',
52
+ 'get-supported-verbs' => 'Ithemes_Sync_Verb_Get_Supported_Verbs',
53
+ 'get-sync-settings' => 'Ithemes_Sync_Verb_Get_Sync_Settings',
54
+ 'get-theme-details' => 'Ithemes_Sync_Verb_Get_Theme_Details',
55
+ 'get-update-details' => 'Ithemes_Sync_Verb_Get_Update_Details',
56
+ 'get-updates' => 'Ithemes_Sync_Verb_Get_Updates',
57
+ 'get-user-details' => 'Ithemes_Sync_Verb_Get_User_Details',
58
+ 'get-wordpress-details' => 'Ithemes_Sync_Verb_Get_Wordpress_Details',
59
+ 'get-wordpress-settings' => 'Ithemes_Sync_Verb_Get_Wordpress_Settings',
60
+ 'get-wordpress-users' => 'Ithemes_Sync_Verb_Get_Wordpress_Users',
61
+ 'manage-posts' => 'Ithemes_Sync_Verb_Manage_Posts',
62
+ 'manage-comments' => 'Ithemes_Sync_Verb_Manage_Comments',
63
+ 'manage-ithemes-licenses' => 'Ithemes_Sync_Verb_Manage_Ithemes_Licenses',
64
+ 'manage-options' => 'Ithemes_Sync_Verb_Manage_Options',
65
+ 'manage-plugins' => 'Ithemes_Sync_Verb_Manage_Plugins',
66
+ 'manage-reports' => 'Ithemes_Sync_Verb_Manage_Reports',
67
+ 'manage-roles' => 'Ithemes_Sync_Verb_Manage_Roles',
68
+ 'manage-themes' => 'Ithemes_Sync_Verb_Manage_Themes',
69
+ 'manage-users' => 'Ithemes_Sync_Verb_Manage_Users',
70
+ 'set-admin-bar-item-whitelist' => 'Ithemes_Sync_Verb_Set_Admin_Bar_Item_Whitelist',
71
+ 'update-show-sync' => 'Ithemes_Sync_Verb_Update_Show_Sync',
72
+ 'update-google-site-verification-token' => 'Ithemes_Sync_Verb_Update_Google_Site_Verification_Token',
73
+ );
74
+
75
+ public function __construct() {
76
+ $GLOBALS['ithemes-sync-api'] = $this;
77
+
78
+ require_once( $GLOBALS['ithemes_sync_path'] . '/functions.php' );
79
+ include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
80
+
81
+ // Gravity Forms Verbs
82
+ if ( class_exists( 'GFForms' ) ) {
83
+ $this->default_verbs['get-gf-forms'] = 'Ithemes_Sync_Verb_Get_GF_Forms';
84
+ $this->default_verbs['get-gf-form-entries'] = 'Ithemes_Sync_Verb_Get_GF_Form_Entries';
85
+ }
86
+
87
+ // Yoast SEO Verbs
88
+ if ( is_plugin_active( 'wordpress-seo/wp-seo.php' ) ) {
89
+ $this->default_verbs['get-yoast-seo-summary'] = 'Ithemes_Sync_Verb_Get_Yoast_SEO_Summary';
90
+ }
91
+
92
+ add_action( 'init', array( $this, 'init' ), 11 );
93
+ }
94
+
95
+ public function init() {
96
+ require_once( $GLOBALS['ithemes_sync_path'] . "/verbs/verb.php" );
97
+
98
+ foreach ( $this->default_verbs as $name => $class ) {
99
+ $this->register( $name, $class, $GLOBALS['ithemes_sync_path'] . "/verbs/$name.php" );
100
+ }
101
+
102
+ do_action( 'ithemes_sync_register_verbs', $this );
103
+
104
+ do_action( 'ithemes_sync_verbs_registered' );
105
+ }
106
+
107
+ public function register( $name, $class, $file = '' ) {
108
+ if ( isset( $this->verbs[$name] ) ) {
109
+ do_action( 'ithemes-sync-add-log', 'Tried to add a verb name that already exists.', compact( 'name', 'class', 'file' ) );
110
+ return false;
111
+ }
112
+
113
+ $this->verbs[$name] = compact( 'name', 'class', 'file' );
114
+
115
+ return true;
116
+ }
117
+
118
+ public function get_names() {
119
+ return array_keys( $this->verbs );
120
+ }
121
+
122
+ public function get_description( $name ) {
123
+ $class = $this->get_class( $name );
124
+
125
+ if ( false === $class )
126
+ return '';
127
+
128
+
129
+ $vars = get_class_vars( $class );
130
+
131
+ if ( isset( $vars['description'] ) )
132
+ return $vars['description'];
133
+
134
+ return '';
135
+ }
136
+
137
+ public function get_descriptions() {
138
+ $names = $this->get_names();
139
+ $descriptions = array();
140
+
141
+ foreach ( $names as $name )
142
+ $descriptions[$name] = $this->get_description( $name );
143
+
144
+ return $descriptions;
145
+ }
146
+
147
+ public function get_status_element( $name ) {
148
+ $class = $this->get_class( $name );
149
+
150
+ if ( false === $class ) {
151
+ return false;
152
+ }
153
+
154
+
155
+ $vars = get_class_vars( $class );
156
+
157
+ if ( ! empty( $vars['status_element_name'] ) ) {
158
+ return $vars['status_element_name'];
159
+ }
160
+
161
+ return false;
162
+ }
163
+
164
+ public function get_status_elements() {
165
+ $names = $this->get_names();
166
+ $status_elements = array();
167
+
168
+ foreach ( $names as $name ) {
169
+ $status_element = $this->get_status_element( $name );
170
+
171
+ if ( false !== $status_element ) {
172
+ $status_elements[$status_element] = $name;
173
+ }
174
+ }
175
+
176
+ return $status_elements;
177
+ }
178
+
179
+ public function is_default_status_element( $name ) {
180
+ $class = $this->get_class( $name );
181
+
182
+ if ( false === $class ) {
183
+ return false;
184
+ }
185
+
186
+
187
+ $vars = get_class_vars( $class );
188
+
189
+ if ( ! empty( $vars['show_in_status_by_default'] ) ) {
190
+ return $vars['show_in_status_by_default'];
191
+ }
192
+
193
+ return false;
194
+ }
195
+
196
+ public function get_default_status_elements() {
197
+ $names = $this->get_names();
198
+ $default_status_elements = array();
199
+
200
+ foreach ( $names as $name ) {
201
+ if ( $this->is_default_status_element( $name ) ) {
202
+ $default_status_elements[] = $this->get_status_element( $name );
203
+ }
204
+ }
205
+
206
+ return $default_status_elements;
207
+ }
208
+
209
+ public function run( $name, $arguments = array() ) {
210
+ $object = $this->get_object( $name );
211
+
212
+ if ( false == $object ) {
213
+ return new WP_Error( 'invalid-verb-object', "Unable to find a valid object for the requested verb: $name" );
214
+ }
215
+
216
+ return $object->run( $arguments );
217
+ }
218
+
219
+ private function get_class( $name ) {
220
+ if ( ! isset( $this->verbs[$name] ) ) {
221
+ do_action( 'ithemes-sync-add-log', 'Unable to find requested verb.', array( 'name' => $name ) );
222
+ return false;
223
+ }
224
+
225
+ if ( ! class_exists( $this->verbs[$name]['class'] ) ) {
226
+ if ( empty( $this->verbs[$name]['file'] ) ) {
227
+ do_action( 'ithemes-sync-add-log', 'Unable to find requested verb\'s class.', $this->verbs[$name] );
228
+ return false;
229
+ }
230
+ else if ( ! is_file( $this->verbs[$name]['file'] ) ) {
231
+ do_action( 'ithemes-sync-add-log', 'Unable to find requested verb\'s file.', $this->verbs[$name] );
232
+ return false;
233
+ }
234
+ else {
235
+ include_once( $this->verbs[$name]['file'] );
236
+
237
+ if ( ! class_exists( $this->verbs[$name]['class'] ) ) {
238
+ do_action( 'ithemes-sync-add-log', 'Unable to find requested verb\'s class even after loading its file.', $this->verbs[$name] );
239
+ return false;
240
+ }
241
+
242
+ if ( ! is_subclass_of( $this->verbs[$name]['class'], 'Ithemes_Sync_Verb' ) ) {
243
+ do_action( 'ithemes-sync-add-log', 'Verb added without being a subclass of Ithemes_Sync_Verb', $this->verbs[$name] );
244
+ return false;
245
+ }
246
+ }
247
+ }
248
+
249
+ return $this->verbs[$name]['class'];
250
+ }
251
+
252
+ private function get_object( $name ) {
253
+ if ( ! isset( $this->verbs[$name] ) ) {
254
+ do_action( 'ithemes-sync-add-log', 'Unable to find requested verb.', array( 'name' => $name ) );
255
+ return false;
256
+ }
257
+
258
+ if ( ! isset( $this->verbs[$name]['object'] ) ) {
259
+ $class = $this->get_class( $name );
260
+
261
+ if ( false === $class ) {
262
+ return false;
263
+ }
264
+
265
+ $object = new $class();
266
+
267
+ $this->verbs[$name]['object'] = $object;
268
+ }
269
+
270
+ return $this->verbs[$name]['object'];
271
+ }
272
+ }
273
+
274
+ new Ithemes_Sync_API();
api.txt ADDED
@@ -0,0 +1,60 @@
1
+ #### Notices ####
2
+
3
+ Sync can receive notifications from other projects. These notifications are of two kinds: normal and urgent.
4
+
5
+
6
+ ## Normal Notices ##
7
+
8
+ Normal notices are for providing information that is not urgent but should be made note of when an admin is maintaining the site. They are sent to the server when the server makes status requests or when specifically requesting notices from the site.
9
+
10
+ Example:
11
+
12
+ function backup_plugin_add_notices( $arguments ) {
13
+ $data = array(
14
+ 'last_backup' => 1394859600,
15
+ 'last_backup_diff_from_now' => 988402,
16
+ );
17
+
18
+ // The verbose argument can be checked for in order to provide additional information that may not always be necessary.
19
+ if ( ! empty( $arguments['verbose'] ) ) {
20
+ $data['last_backup_type'] = 'full';
21
+ }
22
+
23
+ ithemes_sync_add_notice( 'backup_plugin', 'no_recent_backups', 'No recent backups', 'It has been more than a week since the last backup.', $data );
24
+
25
+
26
+ // Plugin-specific arguments could be provided to enable additional notices that would be disabled by default.
27
+ if ( ! empty( $arguments['backup_plugin_show_extra_notices'] ) ) {
28
+ ithemes_sync_add_notice( 'backup_plugin', 'superfluous_notice', 'Superfluous notice', 'I ran out of good ideas, so this is merely an example.' );
29
+ // Note how the $data argument is optional in the call above.
30
+ }
31
+ }
32
+ add_action( 'ithemes_sync_add_notices', 'backup_plugin_add_notices' );
33
+
34
+ Details about the ithemes_sync_add_notice function:
35
+ * @param string $source Uniquely identifies the project that is sending the notice.
36
+ * @param string $id Identifies the type of notice. This is to allow the server to differentiate different kinds of notices without having to parse the message.
37
+ * @param string $subject A brief subject description of the notice that is fit for presentation to Sync users.
38
+ * @param string $message A message that is fit for presentation to Sync users.
39
+ * @param array $data Optional. Data that is relevant to the notice. For notices that may be best presented in a graphical manner, this field could be used to send data used to construct the graphic.
40
+ * @return bool True. Currently, it always returns true.
41
+
42
+
43
+ ## Urgent Notices ##
44
+
45
+ Urgent notices are sent immediately to the server so that the administator can be notified directly. These notices are to be used when something important happens on the site that requires immediate attention.
46
+
47
+ Example:
48
+
49
+ function security_plugin_send_urgent_notice() {
50
+ $result = ithemes_sync_send_urgent_notice( 'security_plugin', 'intruder_detected', 'Intruder detected', 'An intruder has been detected on the site.' );
51
+ }
52
+ add_action( 'init', 'security_plugin_send_urgent_notice' );
53
+
54
+ Details about the ithemes_sync_send_urgent_notice function:
55
+ * @param string $source Uniquely identifies the project that is sending the notice.
56
+ * @param string $id Identifies the type of notice. This is to allow the server to differentiate different kinds of notices without having to parse the message.
57
+ * @param string $subject A brief subject description of the notice that is fit for presentation to Sync users.
58
+ * @param string $message A message that is fit for presentation to Sync users.
59
+ * @param array $data Optional. Data that is relevant to the notice. For notices that may be best presented in a graphical manner, this field could be used to send data used to construct the graphic.
60
+ * @return bool|WP_Error True on successful receipt by the server. WP_Error object otherwise. In the event of an error, the notice is queued for sending to the server when it makes a status or notice request.
ca/index.php ADDED
@@ -0,0 +1 @@
1
+ <?php // Silence is golden.
ca/roots.crt ADDED
@@ -0,0 +1,58 @@
1
+ ##
2
+ ## CA Root Certificate for api.ithemes.com and s3.amazonaws.com
3
+ ##
4
+ ## Certificate data from Mozilla as of: Sat Dec 29 20:03:40 2012
5
+ ##
6
+
7
+ # @(#) $RCSfile: certdata.txt,v $ $Revision: 1.87 $ $Date: 2012/12/29 16:32:45 $
8
+
9
+ Go Daddy Class 2 CA
10
+ ===================
11
+ -----BEGIN CERTIFICATE-----
12
+ MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY
13
+ VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp
14
+ ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG
15
+ A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g
16
+ RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD
17
+ ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv
18
+ 2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32
19
+ qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j
20
+ YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY
21
+ vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O
22
+ BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o
23
+ atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu
24
+ MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG
25
+ A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim
26
+ PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt
27
+ I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
28
+ HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI
29
+ Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b
30
+ vZ8=
31
+ -----END CERTIFICATE-----
32
+
33
+ VeriSign Class 3 Public Primary Certification Authority - G5
34
+ ============================================================
35
+ -----BEGIN CERTIFICATE-----
36
+ MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE
37
+ BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO
38
+ ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk
39
+ IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp
40
+ ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB
41
+ yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln
42
+ biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh
43
+ dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt
44
+ YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
45
+ ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz
46
+ j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD
47
+ Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
48
+ Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r
49
+ fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/
50
+ BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv
51
+ Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
52
+ aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG
53
+ SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+
54
+ X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE
55
+ KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC
56
+ Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE
57
+ ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
58
+ -----END CERTIFICATE-----
class-ithemes-credentials.php ADDED
@@ -0,0 +1,143 @@
1
+ <?php
2
+
3
+ //-----------------------------------------------------------------------------
4
+
5
+ class iThemes_Credentials
6
+ {
7
+
8
+ //-----------------------------------------------------------------------------
9
+
10
+ protected $hash = 'sha256';
11
+
12
+ protected $salt_padding = 'wdHVwU&HcYcWnllo%kTUUnxpScy4%ICM29';
13
+
14
+ protected $iteration_count = 131072;
15
+
16
+ protected $key_length = 64;
17
+
18
+ protected $password;
19
+
20
+ //-----------------------------------------------------------------------------
21
+
22
+ public function __construct($username, $password, $options = array())
23
+ {
24
+
25
+ $this->username = $username;
26
+
27
+ $this->password = $password;
28
+
29
+
30
+ if(!empty($options['hash']))
31
+ $this->hash = strtolower(trim($options['hash']));
32
+
33
+ if(!empty($options['iterations']))
34
+ $this->iteration_count = intval($options['iterations']);
35
+
36
+ if(!empty($options['salt']))
37
+ $this->salt_padding = $options['salt'];
38
+
39
+ if(!empty($options['key_length']))
40
+ $this->key_length = intval($options['key_length']);
41
+
42
+ }
43
+
44
+ //-----------------------------------------------------------------------------
45
+
46
+ public static function get_password_hash($username, $password, $options = array())
47
+ {
48
+
49
+ $hasher = new iThemes_Credentials($username, $password, $options);
50
+
51
+ return $hasher->get_pbkdf2();
52
+
53
+ }
54
+
55
+ //-----------------------------------------------------------------------------
56
+
57
+ public function get_salt()
58
+ {
59
+
60
+ return strtolower(trim($this->username)) . $this->salt_padding;
61
+
62
+ }
63
+
64
+ //-----------------------------------------------------------------------------
65
+
66
+ public function get_pbkdf2()
67
+ {
68
+
69
+ return $this->pbkdf2($this->hash,
70
+ $this->password,
71
+ $this->get_salt(),
72
+ $this->iteration_count,
73
+ $this->key_length / 2,
74
+ false);
75
+
76
+ }
77
+
78
+ //-----------------------------------------------------------------------------
79
+
80
+ /*
81
+ * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
82
+ * $algorithm - The hash algorithm to use. Recommended: SHA256
83
+ * $password - The password.
84
+ * $salt - A salt that is unique to the password.
85
+ * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
86
+ * $key_length - The length of the derived key in bytes.
87
+ * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
88
+ * Returns: A $key_length-byte key derived from the password and salt.
89
+ *
90
+ * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
91
+ *
92
+ * This implementation of PBKDF2 was originally created by https://defuse.ca
93
+ * With improvements by http://www.variations-of-shadow.com
94
+ */
95
+ private function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
96
+ {
97
+
98
+ $algorithm = strtolower($algorithm);
99
+
100
+ if(!in_array($algorithm, hash_algos(), true))
101
+ trigger_error('PBKDF2 ERROR: Invalid hash algorithm.', E_USER_ERROR);
102
+
103
+ if($count <= 0 || $key_length <= 0)
104
+ trigger_error('PBKDF2 ERROR: Invalid parameters.', E_USER_ERROR);
105
+
106
+
107
+ $hash_length = strlen(hash($algorithm, '', true));
108
+ $block_count = ceil($key_length / $hash_length);
109
+
110
+ $output = '';
111
+
112
+ for($i = 1; $i <= $block_count; $i++)
113
+ {
114
+
115
+ // $i encoded as 4 bytes, big endian.
116
+ $last = $salt . pack("N", $i);
117
+
118
+ // first iteration
119
+ $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
120
+
121
+ // perform the other $count - 1 iterations
122
+ for ($j = 1; $j < $count; $j++)
123
+ {
124
+ $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
125
+ }
126
+
127
+ $output .= $xorsum;
128
+
129
+ }
130
+
131
+ if($raw_output)
132
+ return substr($output, 0, $key_length);
133
+ else
134
+ return bin2hex(substr($output, 0, $key_length));
135
+
136
+ }
137
+
138
+ //-----------------------------------------------------------------------------
139
+
140
+ }
141
+
142
+ //-----------------------------------------------------------------------------
143
+
class-ithemes-sync-json.php ADDED
@@ -0,0 +1,64 @@
1
+ <?php
2
+
3
+ final class Ithemes_Sync_JSON {
4
+ public static function encode( $data ) {
5
+ $json = json_encode( $data );
6
+
7
+ if ( false !== $json ) {
8
+ return $json;
9
+ }
10
+
11
+ $data = self::get_fixed_data( $data );
12
+ $json = json_encode( $data );
13
+
14
+ if ( false !== $json ) {
15
+ return $json;
16
+ }
17
+
18
+ return json_encode( 'Encoding error. Unable to JSON encode the data due to an unknown error.' );
19
+ }
20
+
21
+ private static function get_fixed_data( $data ) {
22
+ if ( false !== json_encode( $data ) ) {
23
+ return $data;
24
+ }
25
+
26
+ if ( is_string( $data ) ) {
27
+ require_once( $GLOBALS['ithemes_sync_path'] . '/class-ithemes-sync-utf8-encoder.php' );
28
+ $data = Ithemes_Sync_UTF8_Encoder::toUTF8( $data );
29
+
30
+ if ( false === json_encode( $data ) ) {
31
+ return 'INVALID STRING DATA';
32
+ }
33
+
34
+ return $data;
35
+ }
36
+
37
+ if ( is_array( $data ) ) {
38
+ foreach ( $data as $key => $value ) {
39
+ if ( false === json_encode( $key ) ) {
40
+ unset( $data[$key] );
41
+ $key = self::get_fixed_data( $key );
42
+ $data[$key] = $value;
43
+ }
44
+
45
+ if ( false === json_encode( $value ) ) {
46
+ $value = self::get_fixed_data( $value );
47
+ $data[$key] = $value;
48
+ }
49
+ }
50
+
51
+ if ( false === json_encode( $data ) ) {
52
+ return 'INVALID ARRAY DATA';
53
+ }
54
+
55
+ return $data;
56
+ }
57
+
58
+ if ( is_object( $data ) ) {
59
+ return 'INVALID CLASS ' . get_class( $data ) . ' DATA';
60
+ }
61
+
62
+ return 'INVALID DATA (' . gettype( $data ) . ')';
63
+ }
64
+ }
class-ithemes-sync-utf8-encoder.php ADDED
@@ -0,0 +1,343 @@
1
+ <?php
2
+ /*
3
+ Copyright (c) 2008 Sebastián Grignoli
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions
8
+ are met:
9
+ 1. Redistributions of source code must retain the above copyright
10
+ notice, this list of conditions and the following disclaimer.
11
+ 2. Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in the
13
+ documentation and/or other materials provided with the distribution.
14
+ 3. Neither the name of copyright holders nor the names of its
15
+ contributors may be used to endorse or promote products derived
16
+ from this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS
22
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
31
+ /**
32
+ * @author "Sebastián Grignoli" <grignoli@gmail.com>
33
+ * @package Encoding
34
+ * @version 2.0
35
+ * @link https://github.com/neitanod/forceutf8
36
+ * @example https://github.com/neitanod/forceutf8
37
+ * @license Revised BSD
38
+ */
39
+
40
+ // Modified by Chris Jean of iThemes.com to add backcompat support for PHP 5.2.4.
41
+
42
+ class Ithemes_Sync_UTF8_Encoder {
43
+
44
+ protected static $win1252ToUtf8 = array(
45
+ 128 => "\xe2\x82\xac",
46
+
47
+ 130 => "\xe2\x80\x9a",
48
+ 131 => "\xc6\x92",
49
+ 132 => "\xe2\x80\x9e",
50
+ 133 => "\xe2\x80\xa6",
51
+ 134 => "\xe2\x80\xa0",
52
+ 135 => "\xe2\x80\xa1",
53
+ 136 => "\xcb\x86",
54
+ 137 => "\xe2\x80\xb0",
55
+ 138 => "\xc5\xa0",
56
+ 139 => "\xe2\x80\xb9",
57
+ 140 => "\xc5\x92",
58
+
59
+ 142 => "\xc5\xbd",
60
+
61
+
62
+ 145 => "\xe2\x80\x98",
63
+ 146 => "\xe2\x80\x99",
64
+ 147 => "\xe2\x80\x9c",
65
+ 148 => "\xe2\x80\x9d",
66
+ 149 => "\xe2\x80\xa2",
67
+ 150 => "\xe2\x80\x93",
68
+ 151 => "\xe2\x80\x94",
69
+ 152 => "\xcb\x9c",
70
+ 153 => "\xe2\x84\xa2",
71
+ 154 => "\xc5\xa1",
72
+ 155 => "\xe2\x80\xba",
73
+ 156 => "\xc5\x93",
74
+
75
+ 158 => "\xc5\xbe",
76
+ 159 => "\xc5\xb8"
77
+ );
78
+
79
+ protected static $brokenUtf8ToUtf8 = array(
80
+ "\xc2\x80" => "\xe2\x82\xac",
81
+
82
+ "\xc2\x82" => "\xe2\x80\x9a",
83
+ "\xc2\x83" => "\xc6\x92",
84
+ "\xc2\x84" => "\xe2\x80\x9e",
85
+ "\xc2\x85" => "\xe2\x80\xa6",
86
+ "\xc2\x86" => "\xe2\x80\xa0",
87
+ "\xc2\x87" => "\xe2\x80\xa1",
88
+ "\xc2\x88" => "\xcb\x86",
89
+ "\xc2\x89" => "\xe2\x80\xb0",
90
+ "\xc2\x8a" => "\xc5\xa0",
91
+ "\xc2\x8b" => "\xe2\x80\xb9",
92
+ "\xc2\x8c" => "\xc5\x92",
93
+
94
+ "\xc2\x8e" => "\xc5\xbd",
95
+
96
+
97
+ "\xc2\x91" => "\xe2\x80\x98",
98
+ "\xc2\x92" => "\xe2\x80\x99",
99
+ "\xc2\x93" => "\xe2\x80\x9c",
100
+ "\xc2\x94" => "\xe2\x80\x9d",
101
+ "\xc2\x95" => "\xe2\x80\xa2",
102
+ "\xc2\x96" => "\xe2\x80\x93",
103
+ "\xc2\x97" => "\xe2\x80\x94",
104
+ "\xc2\x98" => "\xcb\x9c",
105
+ "\xc2\x99" => "\xe2\x84\xa2",
106
+ "\xc2\x9a" => "\xc5\xa1",
107
+ "\xc2\x9b" => "\xe2\x80\xba",
108
+ "\xc2\x9c" => "\xc5\x93",
109
+
110
+ "\xc2\x9e" => "\xc5\xbe",
111
+ "\xc2\x9f" => "\xc5\xb8"
112
+ );
113
+
114
+ protected static $utf8ToWin1252 = array(
115
+ "\xe2\x82\xac" => "\x80",
116
+
117
+ "\xe2\x80\x9a" => "\x82",
118
+ "\xc6\x92" => "\x83",
119
+ "\xe2\x80\x9e" => "\x84",
120
+ "\xe2\x80\xa6" => "\x85",
121
+ "\xe2\x80\xa0" => "\x86",
122
+ "\xe2\x80\xa1" => "\x87",
123
+ "\xcb\x86" => "\x88",
124
+ "\xe2\x80\xb0" => "\x89",
125
+ "\xc5\xa0" => "\x8a",
126
+ "\xe2\x80\xb9" => "\x8b",
127
+ "\xc5\x92" => "\x8c",
128
+
129
+ "\xc5\xbd" => "\x8e",
130
+
131
+
132
+ "\xe2\x80\x98" => "\x91",
133
+ "\xe2\x80\x99" => "\x92",
134
+ "\xe2\x80\x9c" => "\x93",
135
+ "\xe2\x80\x9d" => "\x94",
136
+ "\xe2\x80\xa2" => "\x95",
137
+ "\xe2\x80\x93" => "\x96",
138
+ "\xe2\x80\x94" => "\x97",
139
+ "\xcb\x9c" => "\x98",
140
+ "\xe2\x84\xa2" => "\x99",
141
+ "\xc5\xa1" => "\x9a",
142
+ "\xe2\x80\xba" => "\x9b",
143
+ "\xc5\x93" => "\x9c",
144
+
145
+ "\xc5\xbe" => "\x9e",
146
+ "\xc5\xb8" => "\x9f"
147
+ );
148
+
149
+ static function toUTF8($text){
150
+ /**
151
+ * Function \ForceUTF8\Encoding::toUTF8
152
+ *
153
+ * This function leaves UTF8 characters alone, while converting almost all non-UTF8 to UTF8.
154
+ *
155
+ * It assumes that the encoding of the original string is either Windows-1252 or ISO 8859-1.
156
+ *
157
+ * It may fail to convert characters to UTF-8 if they fall into one of these scenarios:
158
+ *
159
+ * 1) when any of these characters: ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß
160
+ * are followed by any of these: ("group B")
161
+ * ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶•¸¹º»¼½¾¿
162
+ * For example: %ABREPRESENT%C9%BB. «REPRESENTÉ»
163
+ * The "«" (%AB) character will be converted, but the "É" followed by "»" (%C9%BB)
164
+ * is also a valid unicode character, and will be left unchanged.
165
+ *
166
+ * 2) when any of these: àáâãäåæçèéêëìíîï are followed by TWO chars from group B,
167
+ * 3) when any of these: ðñòó are followed by THREE chars from group B.
168
+ *
169
+ * @name toUTF8
170
+ * @param string $text Any string.
171
+ * @return string The same string, UTF8 encoded
172
+ *
173
+ */
174
+
175
+ if(is_array($text))
176
+ {
177
+ foreach($text as $k => $v)
178
+ {
179
+ $text[$k] = self::toUTF8($v);
180
+ }
181
+ return $text;
182
+ }
183
+
184
+ if(!is_string($text)) {
185
+ return $text;
186
+ }
187
+
188
+ $max = self::strlen($text);
189
+
190
+ $buf = "";
191
+ for($i = 0; $i < $max; $i++){
192
+ $c1 = $text{$i};
193
+ if($c1>="\xc0"){ //Should be converted to UTF8, if it's not UTF8 already
194
+ $c2 = $i+1 >= $max? "\x00" : $text{$i+1};
195
+ $c3 = $i+2 >= $max? "\x00" : $text{$i+2};
196
+ $c4 = $i+3 >= $max? "\x00" : $text{$i+3};
197
+ if($c1 >= "\xc0" & $c1 <= "\xdf"){ //looks like 2 bytes UTF8
198
+ if($c2 >= "\x80" && $c2 <= "\xbf"){ //yeah, almost sure it's UTF8 already
199
+ $buf .= $c1 . $c2;
200
+ $i++;
201
+ } else { //not valid UTF8. Convert it.
202
+ $cc1 = (chr(ord($c1) / 64) | "\xc0");
203
+ $cc2 = ($c1 & "\x3f") | "\x80";
204
+ $buf .= $cc1 . $cc2;
205
+ }
206
+ } elseif($c1 >= "\xe0" & $c1 <= "\xef"){ //looks like 3 bytes UTF8
207
+ if($c2 >= "\x80" && $c2 <= "\xbf" && $c3 >= "\x80" && $c3 <= "\xbf"){ //yeah, almost sure it's UTF8 already
208
+ $buf .= $c1 . $c2 . $c3;
209
+ $i = $i + 2;
210
+ } else { //not valid UTF8. Convert it.
211
+ $cc1 = (chr(ord($c1) / 64) | "\xc0");
212
+ $cc2 = ($c1 & "\x3f") | "\x80";
213
+ $buf .= $cc1 . $cc2;
214
+ }
215
+ } elseif($c1 >= "\xf0" & $c1 <= "\xf7"){ //looks like 4 bytes UTF8
216
+ if($c2 >= "\x80" && $c2 <= "\xbf" && $c3 >= "\x80" && $c3 <= "\xbf" && $c4 >= "\x80" && $c4 <= "\xbf"){ //yeah, almost sure it's UTF8 already
217
+ $buf .= $c1 . $c2 . $c3 . $c4;
218
+ $i = $i + 3;
219
+ } else { //not valid UTF8. Convert it.
220
+ $cc1 = (chr(ord($c1) / 64) | "\xc0");
221
+ $cc2 = ($c1 & "\x3f") | "\x80";
222
+ $buf .= $cc1 . $cc2;
223
+ }
224
+ } else { //doesn't look like UTF8, but should be converted
225
+ $cc1 = (chr(ord($c1) / 64) | "\xc0");
226
+ $cc2 = (($c1 & "\x3f") | "\x80");
227
+ $buf .= $cc1 . $cc2;
228
+ }
229
+ } elseif(($c1 & "\xc0") == "\x80"){ // needs conversion
230
+ if(isset(self::$win1252ToUtf8[ord($c1)])) { //found in Windows-1252 special cases
231
+ $buf .= self::$win1252ToUtf8[ord($c1)];
232
+ } else {
233
+ $cc1 = (chr(ord($c1) / 64) | "\xc0");
234
+ $cc2 = (($c1 & "\x3f") | "\x80");
235
+ $buf .= $cc1 . $cc2;
236
+ }
237
+ } else { // it doesn't need conversion
238
+ $buf .= $c1;
239
+ }
240
+ }
241
+ return $buf;
242
+ }
243
+
244
+ static function toWin1252($text, $option = '') {
245
+ if(is_array($text)) {
246
+ foreach($text as $k => $v) {
247
+ $text[$k] = self::toWin1252($v, $option);
248
+ }
249
+ return $text;
250
+ } elseif(is_string($text)) {
251
+ return static::utf8_decode($text, $option);
252
+ } else {
253
+ return $text;
254
+ }
255
+ }
256
+
257
+ static function toISO8859($text) {
258
+ return self::toWin1252($text);
259
+ }
260
+
261
+ static function toLatin1($text) {
262
+ return self::toWin1252($text);
263
+ }
264
+
265
+ static function fixUTF8($text, $option = ''){
266
+ if(is_array($text)) {
267
+ foreach($text as $k => $v) {
268
+ $text[$k] = self::fixUTF8($v, $option);
269
+ }
270
+ return $text;
271
+ }
272
+
273
+ $last = "";
274
+ while($last <> $text){
275
+ $last = $text;
276
+ $text = self::toUTF8(static::utf8_decode($text, $option));
277
+ }
278
+ $text = self::toUTF8(static::utf8_decode($text, $option));
279
+ return $text;
280
+ }
281
+
282
+ static function UTF8FixWin1252Chars($text){
283
+ // If you received an UTF-8 string that was converted from Windows-1252 as it was ISO8859-1
284
+ // (ignoring Windows-1252 chars from 80 to 9F) use this function to fix it.
285
+ // See: http://en.wikipedia.org/wiki/Windows-1252
286
+
287
+ return str_replace(array_keys(self::$brokenUtf8ToUtf8), array_values(self::$brokenUtf8ToUtf8), $text);
288
+ }
289
+
290
+ static function removeBOM($str=""){
291
+ if(substr($str, 0,3) == pack("CCC",0xef,0xbb,0xbf)) {
292
+ $str=substr($str, 3);
293
+ }
294
+ return $str;
295
+ }
296
+
297
+ protected static function strlen($text){
298
+ return (function_exists('mb_strlen') && ((int) ini_get('mbstring.func_overload')) & 2) ?
299
+ mb_strlen($text,'8bit') : strlen($text);
300
+ }
301
+
302
+ public static function normalizeEncoding($encodingLabel)
303
+ {
304
+ $encoding = strtoupper($encodingLabel);
305
+ $encoding = preg_replace('/[^a-zA-Z0-9\s]/', '', $encoding);
306
+ $equivalences = array(
307
+ 'ISO88591' => 'ISO-8859-1',
308
+ 'ISO8859' => 'ISO-8859-1',
309
+ 'ISO' => 'ISO-8859-1',
310
+ 'LATIN1' => 'ISO-8859-1',
311
+ 'LATIN' => 'ISO-8859-1',
312
+ 'UTF8' => 'UTF-8',
313
+ 'UTF' => 'UTF-8',
314
+ 'WIN1252' => 'ISO-8859-1',
315
+ 'WINDOWS1252' => 'ISO-8859-1'
316
+ );
317
+
318
+ if(empty($equivalences[$encoding])){
319
+ return 'UTF-8';
320
+ }
321
+
322
+ return $equivalences[$encoding];
323
+ }
324
+
325
+ public static function encode($encodingLabel, $text)
326
+ {
327
+ $encodingLabel = self::normalizeEncoding($encodingLabel);
328
+ if($encodingLabel == 'ISO-8859-1') return self::toLatin1($text);
329
+ return self::toUTF8($text);
330
+ }
331
+
332
+ protected static function utf8_decode($text, $option)
333
+ {
334
+ if ($option == '' || !function_exists('iconv')) {
335
+ $o = utf8_decode(
336
+ str_replace(array_keys(self::$utf8ToWin1252), array_values(self::$utf8ToWin1252), self::toUTF8($text))
337
+ );
338
+ } else {
339
+ $o = iconv("UTF-8", "Windows-1252" . ($option == 'TRANSLIT' ? '//TRANSLIT' : ($option == 'IGNORE' ? '//IGNORE' : '')), $text);
340
+ }
341
+ return $o;
342
+ }
343
+ }
client-dashboard.php ADDED
@@ -0,0 +1,413 @@
1
+ <?php
2
+
3
+ class Ithemes_Sync_Client_Dashboard {
4
+
5
+ /**
6
+ * @var array List of item IDs to ignore from our list and never send to Sync
7
+ */
8
+ private $_admin_bar_ignored_items = array(
9
+ 'menu-toggle', //This is for admin responsiveness
10
+ );
11
+
12
+ public function __construct() {
13
+ add_action( 'init', array( $this, 'init' ) );
14
+ }
15
+
16
+ public function init() {
17
+ // If this user is supposed to see the client dashboard
18
+ if ( get_user_meta( get_current_user_id(), 'ithemes-sync-client-dashboard', true ) ) {
19
+ if ( ! get_user_meta( get_current_user_id(), 'ithemes-sync-client-dashboard-no-css', true ) ) {
20
+ // Enqueue our admin scripts and styles
21
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_dashboard_scripts' ) );
22
+ }
23
+
24
+ if ( get_user_meta( get_current_user_id(), 'ithemes-sync-suppress-admin-notices', true ) ) {
25
+ add_action( 'network_admin_notices', array( $this, 'admin_notices_start' ), 1 );
26
+ add_action( 'user_admin_notices', array( $this, 'admin_notices_start' ), 1 );
27
+ add_action( 'admin_notices', array( $this, 'admin_notices_start' ), 1 );
28
+ add_action( 'all_admin_notices', array( $this, 'admin_notices_start' ), 1 );
29
+ add_action( 'network_admin_notices', array( $this, 'admin_notices_end' ), 999 );
30
+ add_action( 'user_admin_notices', array( $this, 'admin_notices_end' ), 999 );
31
+ add_action( 'admin_notices', array( $this, 'admin_notices_end' ), 999 );
32
+ add_action( 'all_admin_notices', array( $this, 'admin_notices_end' ), 999 );
33
+ }
34
+
35
+ // Filter menu items
36
+ add_action( 'admin_menu', array( $this, 'filter_admin_menu' ), 999999 ); //We want to be last!
37
+
38
+ // Filter admin bar items
39
+ add_action( 'wp_before_admin_bar_render', array( $this, 'filter_admin_bar_menu' ), 1002 );
40
+
41
+ // Filter dashboard widgets - screen_layout_columns fires late but before dashboard widgets are run or referenced in screen options
42
+ add_action( 'screen_layout_columns', array( $this, 'filter_dashboard_widgets' ) );
43
+
44
+ // Filter welcome panel
45
+ add_filter( 'show_welcome_panel', array( $this, 'show_welcome_panel' ) );
46
+ }
47
+
48
+ add_action( 'admin_menu', array( $this, 'admin_menu' ), 999999 ); //We want to be last!
49
+ add_action( 'wp_before_admin_bar_render', array( $this, 'admin_bar_menu' ), 999 );
50
+
51
+ add_action( 'switch_theme', array( $this, 'clear_cache' ) );
52
+ add_action( 'activate_plugin', array( $this, 'clear_cache' ) );
53
+ add_action( 'deactivate_plugin', array( $this, 'clear_cache' ) );
54
+ add_action( 'update_option_active_plugins', array( $this, 'clear_cache' ) );
55
+ add_action( 'add_option_active_plugins', array( $this, 'clear_cache' ) );
56
+
57
+ add_action( 'update_site_option_active_sitewide_plugins', array( $this, 'clear_cache_network' ) );
58
+ add_action( 'add_site_option_active_sitewide_plugins', array( $this, 'clear_cache_network' ) );
59
+
60
+ /**
61
+ * Handle Dashboard Widgets
62
+ */
63
+ add_action( 'admin_footer-index.php', array( $this, 'dashboard_admin_footer' ) );
64
+ }
65
+
66
+ public function enqueue_dashboard_scripts() {
67
+ // Enqueue an additional CSS file on the admin dashboard
68
+ wp_enqueue_style( 'ithemes-sync-client-dashboard', plugins_url( 'css/client-dashboard.css', __FILE__ ), array( 'wp-admin' ), '20140801' );
69
+ }
70
+
71
+ /**
72
+ * @param int User Id
73
+ *
74
+ * @return array List of menu_slugs to allow in menu. index is top level item, value is array of submenu items (or true for 'all')
75
+ */
76
+ public function get_allowed_admin_menu_items( $user_id = null ) {
77
+ if ( ! $user_id ) {
78
+ $user_id = get_current_user_id();
79
+ }
80
+
81
+ $menu_whitelist = get_user_meta( $user_id, 'ithemes-sync-admin_menu-whitelist', true );
82
+
83
+ // If the user's admin_menu whitelist doesn't exist, set it.
84
+ if ( ! is_array( $menu_whitelist ) ) {
85
+ $menu_whitelist = array(
86
+ 'index.php' => array(),
87
+ 'edit.php' => true,
88
+ 'edit.php?post_type=page' => true,
89
+ 'edit-comments.php' => array(),
90
+ );
91
+ update_user_meta( $user_id, 'ithemes-sync-admin_menu-whitelist', $menu_whitelist );
92
+ }
93
+
94
+ return apply_filters( 'ithemes-sync-menu-items-whitelist', $menu_whitelist );
95
+ }
96
+
97
+ public function filter_admin_menu() {
98
+ /**
99
+ * @var array An array of menu elements. Each element is an array containing:
100
+ * [0] - Label
101
+ * [1] = capability
102
+ * [2] = menu_slug
103
+ * [3] = page_title
104
+ * [4] = classes
105
+ * [5] = hookname
106
+ * [6] = icon_url
107
+ */
108
+ global $menu;
109
+
110
+ /**
111
+ * @var array An array of arrays of submenu elements, indexed by the menu_slug of the main menu item. Each submenu element is an array containing:
112
+ * [0] - Label
113
+ * [1] = capability
114
+ * [2] = menu_slug
115
+ */
116
+ global $submenu;
117
+
118
+ /**
119
+ * @var array List of menu_slugs to allow in menu. index is top level item, value is array of submenu items (or true for 'all')
120
+ */
121
+ $menu_whitelist = $this->get_allowed_admin_menu_items();
122
+
123
+ foreach ( $menu as $pos => $menu_item ) {
124
+ if ( 'wp-menu-separator' == $menu_item[4] ) {
125
+ continue;
126
+ }
127
+ if ( ! in_array( $menu_item[2], array_keys( $menu_whitelist ) ) ) {
128
+ if ( isset( $submenu[ $menu_item[2] ] ) ) {
129
+ unset( $submenu[ $menu_item[2] ] );
130
+ }
131
+ unset( $menu[ $pos ] );
132
+ } elseif ( true !== $menu_whitelist[ $menu_item[2] ] && isset( $submenu[ $menu_item[2] ] ) ) {
133
+ foreach ( $submenu[ $menu_item[2] ] as $subpos => $submenu_item ) {
134
+ if ( ! in_array( $submenu_item[2], $menu_whitelist[ $menu_item[2] ] ) ) {
135
+ unset( $submenu[ $menu_item[2] ][ $subpos ] );
136
+ }
137
+ }
138
+ }
139
+ }
140
+ }
141
+
142
+ public function admin_menu() {
143
+ $admin_menu = get_option( 'ithemes-sync-admin_menu' );
144
+ if ( false === $admin_menu ) {
145
+ /**
146
+ * @var array An array of menu elements. Each element is an array containing:
147
+ * [0] - Label
148
+ * [1] = capability
149
+ * [2] = menu_slug
150
+ * [3] = page_title
151
+ * [4] = classes
152
+ * [5] = hookname
153
+ * [6] = icon_url
154
+ */
155
+ global $menu;
156
+
157
+ /**
158
+ * @var array An array of arrays of submenu elements, indexed by the menu_slug of the main menu item. Each submenu element is an array containing:
159
+ * [0] - Label
160
+ * [1] = capability
161
+ * [2] = menu_slug
162
+ */
163
+ global $submenu;
164
+
165
+ $admin_menu = array();
166
+ foreach ( $menu as $menu_item ) {
167
+ $admin_menu[$menu_item[2]] = array();
168
+ $admin_menu[$menu_item[2]]['label'] = $menu_item[0];
169
+
170
+ if ( ! empty( $submenu[$menu_item[2]] ) ) {
171
+ $admin_menu[$menu_item[2]]['children'] = array();
172
+ foreach ( $submenu[$menu_item[2]] as $submenu_item ) {
173
+ $admin_menu[$menu_item[2]]['children'][$submenu_item[2]] = array( 'label' => $submenu_item[0] );
174
+ }
175
+ }
176
+ }
177
+
178
+ update_option( 'ithemes-sync-admin_menu', $admin_menu );
179
+ }
180
+ }
181
+
182
+ /**
183
+ * @param int User Id
184
+ *
185
+ * @return array List of dashboard widget ids to allow
186
+ */
187
+ public function get_allowed_admin_bar_items( $user_id = null ) {
188
+ if ( ! $user_id ) {
189
+ $user_id = get_current_user_id();
190
+ }
191
+
192
+ $whitelist = get_user_meta( $user_id, 'ithemes-sync-admin-bar-item-whitelist-' . get_current_blog_id(), true );
193
+
194
+ // If the user's admin_menu whitelist doesn't exist, set it.
195
+ if ( ! is_array( $whitelist ) ) {
196
+ $whitelist = array(
197
+ 'wp-logo',
198
+ 'about',
199
+ 'wp-logo-external',
200
+ 'wporg',
201
+ 'documentation',
202
+ 'support-forums',
203
+ 'feedback',
204
+ 'site-name',
205
+ 'comments',
206
+ 'new-content',
207
+ 'new-post',
208
+ 'new-page',
209
+ 'top-secondary',
210
+ 'my-account',
211
+ 'user-actions',
212
+ 'user-info',
213
+ 'edit-profile',
214
+ 'logout',
215
+ );
216
+ update_user_meta( $user_id, 'ithemes-sync-admin-bar-item-whitelist-' . get_current_blog_id(), $whitelist );
217
+ }
218
+
219
+ $whitelist = array_merge( $whitelist, $this->_admin_bar_ignored_items );
220
+
221
+ return apply_filters( 'ithemes-sync-admin-bar-item-whitelist-' . get_current_blog_id(), $whitelist );
222
+ }
223
+
224
+ public function filter_admin_bar_menu() {
225
+ global $wp_admin_bar;
226
+ $whitelist = $this->get_allowed_admin_bar_items();
227
+
228
+ foreach ( $wp_admin_bar->get_nodes() as $node ) {
229
+ if ( ! in_array( $node->id, $whitelist ) ) {
230
+ $wp_admin_bar->remove_node( $node->id );
231
+ }
232
+ }
233
+ }
234
+
235
+ public function admin_bar_menu() {
236
+ global $wp_admin_bar;
237
+ $meta_key = 'ithemes-sync-admin-bar-items-' . get_current_blog_id();
238
+
239
+ if ( is_array( get_user_meta( get_current_user_id(), $meta_key, true ) ) ) {
240
+ return true;
241
+ }
242
+
243
+ $admin_bar = array();
244
+ $admin_bar_nodes = $wp_admin_bar->get_nodes();
245
+
246
+ $last_count = null;
247
+ $iterations = 0;
248
+ while ( ! empty( $admin_bar_nodes ) && ++$iterations <= 100 ) {
249
+ foreach ( $admin_bar_nodes as $key => $current_node ) {
250
+ if ( in_array( $current_node->id, $this->_admin_bar_ignored_items ) ) {
251
+ // Don't send ignored items
252
+ unset( $admin_bar_nodes[ $key ] );
253
+ } elseif ( false == $current_node->parent ) {
254
+ // Process parents
255
+ $admin_bar[ $current_node->id ] = $this->_create_admin_bar_node( $current_node );
256
+ unset( $admin_bar_nodes[ $key ] );
257
+ } elseif ( $this->_place_child( $admin_bar, $this->_create_admin_bar_node( $current_node ) ) ) {
258
+ // If we placed a child node successfully, remove it
259
+ unset( $admin_bar_nodes[ $key ] );
260
+ }
261
+ }
262
+
263
+ /**
264
+ * If we haven't parsed any elements out since the last time through
265
+ * the loop, break out to avoid an infinite loop. This only happens
266
+ * when there are mal-formed admin bar nodes (such as nodes with a
267
+ * non-existent parent)
268
+ */
269
+ if ( $last_count == count( $admin_bar_nodes ) ) {
270
+ break;
271
+ }
272
+ $last_count = count( $admin_bar_nodes );
273
+ }
274
+
275
+ update_user_meta( get_current_user_id(), $meta_key, $admin_bar );
276
+ }
277
+
278
+ private function _place_child( &$all_nodes, $child_node ) {
279
+ foreach ( $all_nodes as $id => &$node ) {
280
+ if ( $id == $child_node->parent ) {
281
+ $node->children[ $child_node->id ] = $child_node;
282
+ return true;
283
+ } elseif ( ! empty( $node->children ) && $this->_place_child( $node->children, $child_node ) ) {
284
+ return true;
285
+ }
286
+ }
287
+ return false;
288
+ }
289
+
290
+ private function _create_admin_bar_node( $node_info ) {
291
+ $node = new stdClass();
292
+ $node->id = $node_info->id;
293
+ $node->title = $node_info->title;
294
+ $node->parent = $node_info->parent;
295
+ $node->type = $node_info->group? 'group':'item';
296
+ $node->children = array();
297
+ return $node;
298
+ }
299
+
300
+ /**
301
+ * Clear the cache of our admin_menu and dashboard metaboxes by deleting the option
302
+ */
303
+ public function clear_cache() {
304
+ delete_option( 'ithemes-sync-admin_menu' );
305
+ delete_option( 'ithemes-sync-dashboard-metaboxes' );
306
+
307
+ $users = get_users( array( 'blog_id' => get_current_blog_id(), 'fields' => array( 'ID' ) ) );
308
+ $meta_key = 'ithemes-sync-admin-bar-items-' . get_current_blog_id();
309
+ foreach ( $users as $user ) {
310
+ delete_user_meta( $user->ID, $meta_key );
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Clear the cache of our admin_menu and dashboard metaboxes from the whole
316
+ * network (site...ugh) by deleting the option from each site (blog...ugh again)
317
+ */
318
+ public function clear_cache_network() {
319
+ // Get the current site and only clear options from blogs in this site
320
+ $site = get_current_site(); // Site = Network? Ugh.
321
+ if ( $site && isset( $site->id ) ) {
322
+ global $wpdb;
323
+ $query = $wpdb->prepare( "SELECT `blog_id` FROM $wpdb->blogs WHERE `site_id`=%d", absint( $site->id ) );
324
+ $users = get_users( array( 'blog_id' => 0, 'fields' => array( 'ID' ) ) );
325
+ foreach( $wpdb->get_col( $query ) as $blog_id ) {
326
+ delete_blog_option( $blog_id, 'ithemes-sync-admin_menu' );
327
+ delete_blog_option( $blog_id, 'ithemes-sync-dashboard-metaboxes' );
328
+
329
+ $meta_key = 'ithemes-sync-admin-bar-items-' . $blog_id;
330
+ foreach ( $users as $user ) {
331
+ delete_user_meta( $user->ID, $meta_key );
332
+ }
333
+ }
334
+ }
335
+ }
336
+
337
+ public function dashboard_admin_footer() {
338
+ $meta_box_list = get_option( 'ithemes-sync-dashboard-metaboxes' );
339
+ if ( false === $meta_box_list ) {
340
+ global $wp_meta_boxes;
341
+ $screen = get_current_screen();
342
+ $meta_box_list = array();
343
+ foreach ( $wp_meta_boxes[$screen->id] as $box_position ) {
344
+ foreach ( $box_position as $box_set ) {
345
+ foreach ( $box_set as $box ) {
346
+ $meta_box_list[ $box['id'] ] = $box['title'];
347
+ }
348
+ }
349
+ }
350
+ $meta_box_list['show_welcome_panel'] = _x( 'Welcome', 'Welcome panel' );
351
+ update_option( 'ithemes-sync-dashboard-metaboxes', $meta_box_list );
352
+ }
353
+ }
354
+
355
+ /**
356
+ * @param int User Id
357
+ *
358
+ * @return array List of dashboard widget ids to allow
359
+ */
360
+ public function get_allowed_dashboard_widgets( $user_id = null ) {
361
+ if ( ! $user_id ) {
362
+ $user_id = get_current_user_id();
363
+ }
364
+
365
+ $whitelist = get_user_meta( $user_id, 'ithemes-sync-dashboard-widget-whitelist', true );
366
+
367
+ // If the user's admin_menu whitelist doesn't exist, set it.
368
+ if ( ! is_array( $whitelist ) ) {
369
+ $whitelist = array(
370
+ 'dashboard_right_now',
371
+ 'dashboard_quick_press',
372
+ 'show_welcome_panel',
373
+ );
374
+ update_user_meta( $user_id, 'ithemes-sync-dashboard-widget-whitelist', $whitelist );
375
+ }
376
+
377
+ return apply_filters( 'ithemes-sync-dashboard-widget-whitelist', $whitelist );
378
+ }
379
+
380
+ public function filter_dashboard_widgets() {
381
+ $screen = get_current_screen();
382
+ if ( $screen && 'dashboard' == $screen->id ) {
383
+ global $wp_meta_boxes;
384
+ $whitelist = $this->get_allowed_dashboard_widgets();
385
+
386
+ foreach ( $wp_meta_boxes[$screen->id] as $box_position => $box_position_boxes ) {
387
+ foreach ( $box_position_boxes as $priority => $box_set ) {
388
+ foreach ( $box_set as $index => $box ) {
389
+ if ( ! in_array( $box['id'], $whitelist ) ) {
390
+ unset( $wp_meta_boxes[$screen->id][$box_position][$priority][$index] );
391
+ }
392
+ }
393
+ }
394
+ }
395
+ }
396
+ }
397
+
398
+ public function show_welcome_panel( $show ) {
399
+ if ( ! in_array( 'show_welcome_panel', $this->get_allowed_dashboard_widgets() ) ) {
400
+ $show = 0;
401
+ }
402
+ return $show;
403
+ }
404
+
405
+ public function admin_notices_start() {
406
+ ob_start();
407
+ }
408
+
409
+ public function admin_notices_end() {
410
+ ob_end_clean();
411
+ }
412
+ }
413
+ new Ithemes_Sync_Client_Dashboard();
css/admin-notice.css ADDED
@@ -0,0 +1,53 @@
1
+ #ithemes-sync-notice {
2
+ background: #F3FCE6 url('../images/sync-icon.png') .85em center no-repeat;
3
+ border: 1px solid #D8E9C1;
4
+ border-bottom-width: 2px;
5
+ border-radius: 3px;
6
+ color: #44654E;
7
+ font-size: 14px;
8
+ line-height: 1.6;
9
+ margin: 1em 0;
10
+ padding: 1em .75em 1em 5em;
11
+ position: relative;
12
+ }
13
+
14
+ #ithemes-sync-notice a {
15
+ color: #495D68;
16
+ font-weight: bold;
17
+ text-decoration: none;
18
+ }
19
+
20
+ #ithemes-sync-notice .ithemes-sync-notice-button {
21
+ background: #D8E9C1;
22
+ border: 1px solid #B7CC9B;
23
+ border-bottom-width: 2px;
24
+ border-radius: 3px;
25
+ display: inline-block;
26
+ margin: -8px 0;
27
+ padding: 6px 14px;
28
+ -webkit-transition: all .1s linear;
29
+ -moz-transition: all .1s linear;
30
+ transition: all .1s linear;
31
+ }
32
+ #ithemes-sync-notice .ithemes-sync-notice-button:hover {
33
+ background: #F9FFF0;
34
+ }
35
+
36
+ #ithemes-sync-notice .ithemes-sync-notice-dismiss,
37
+ #ithemes-sync-notice .ithemes-sync-notice-hide {
38
+ border: 1px solid transparent;
39
+ border-radius: 3px;
40
+ display: inline-block;
41
+ float: right;
42
+ margin: -8px 0;
43
+ padding: 6px 14px;
44
+ -webkit-transition: all .1s linear;
45
+ -moz-transition: all .1s linear;
46
+ transition: all .1s linear;
47
+ }
48
+ #ithemes-sync-notice .ithemes-sync-notice-dismiss:hover,
49
+ #ithemes-sync-notice .ithemes-sync-notice-hide:hover {
50
+ background: #D8E9C1;
51
+ border: 1px solid #B7CC9B;
52
+ border-bottom-width: 2px;
53
+ }
css/client-dashboard.css ADDED
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Admin bar and left menu adjustments
3
+ */
4
+
5
+ #wpadminbar {
6
+ height: 42px;
7
+ }
8
+ html.wp-toolbar {
9
+ padding-top: 42px;
10
+ }
11
+ #wpadminbar .quicklinks .ab-empty-item,
12
+ #wpadminbar .quicklinks a,
13
+ #wpadminbar .shortlink-input {
14
+ height: 42px;
15
+ }
16
+ #wpadminbar .quicklinks a,
17
+ #wpadminbar #wp-admin-bar-user-info span {
18
+ font-size: 16px;
19
+ font-weight: 300;
20
+ line-height: 40px;
21
+ }
22
+ #wpadminbar #wp-admin-bar-user-info span {
23
+ height: 26px;
24
+ }
25
+ #wpadminbar .ab-icon, #wpadminbar .ab-item:before, #wpadminbar>#wp-toolbar>#wp-admin-bar-root-default .ab-icon {
26
+ font-size: 26px;
27
+ line-height: 1.1em;
28
+ }
29
+ #wpadminbar .quicklinks > ul > li#wp-admin-bar-my-account > a {
30
+ padding: 0 14px;
31
+ }
32
+ #wpadminbar .quicklinks li#wp-admin-bar-my-account.with-avatar>a img {
33
+ width: 26px;
34
+ height: 26px;
35
+ margin-left: 12px;
36
+ }
37
+ #adminmenu,
38
+ #adminmenu .wp-submenu,
39
+ #adminmenuback,
40
+ #adminmenuwrap {
41
+ width: 200px;
42
+ }
43
+ #adminmenu {
44
+ margin: 0;
45
+ }
46
+ #wpcontent, #wpfooter {
47
+ margin-left: 200px;
48
+ }
49
+ #adminmenu .wp-submenu {
50
+ left: 200px;
51
+ }
52
+ #adminmenu .wp-submenu a {
53
+ font-size: 14px;
54
+ font-weight: 300;
55
+ }
56
+ #adminmenu li.wp-menu-separator {
57
+ height: 0;
58
+ margin: 0;
59
+ }
60
+ #adminmenu .wp-not-current-submenu .wp-submenu,
61
+ .folded #adminmenu .wp-has-current-submenu .wp-submenu {
62
+ min-width: 200px;
63
+ }
64
+ #adminmenu .wp-submenu-head,
65
+ #adminmenu a.menu-top {
66
+ font-size: 18px;
67
+ font-weight: 300;
68
+ line-height: 30px;
69
+ min-height: 50px;
70
+ }
71
+ #adminmenu .wp-submenu-head {
72
+ min-height: 34px;
73
+ }
74
+ div.wp-menu-image:before {
75
+ padding: 14px 0;
76
+ }
77
+ #adminmenu li.wp-has-submenu.wp-not-current-submenu:hover:after {
78
+ top: 16px;
79
+ }
80
+
81
+ #adminmenu .awaiting-mod,
82
+ #adminmenu .update-plugins,
83
+ #sidemenu li a span.update-plugins {
84
+ margin: 7px 0 0 14px;
85
+ font-size: 12px;
86
+ font-weight: 300;
87
+ line-height: 19px;
88
+ }
89
+
90
+ /**
91
+ * Screen wpbody-content area
92
+ */
93
+
94
+ .wrap {
95
+ margin-top: 5px;
96
+ }
97
+ #poststuff h3,
98
+ .metabox-holder h3 {
99
+ padding: 14px 18px;
100
+ font-size: 16px;
101
+ }
102
+ .js .meta-box-sortables .postbox .handlediv:before,
103
+ .js .sidebar-name .sidebar-name-arrow:before {
104
+ right: 18px;
105
+ padding: 15px 12px;
106
+ }
107
+ .tablenav {
108
+ height: 48px;
109
+ }
110
+
111
+ /**
112
+ * Buttons and form fields
113
+ */
114
+
115
+ .wp-core-ui .button,
116
+ .wp-core-ui .button-primary,
117
+ .wp-core-ui .button-secondary {
118
+ font-size: 15px;
119
+ height: 40px;
120
+ line-height: 38px;
121
+ padding: 0px 20px 2px;
122
+ }
123
+ .wp-core-ui .button-group.button-large .button, .wp-core-ui .button.button-large {
124
+ height: 40px;
125
+ line-height: 38px;
126
+ padding: 0px 20px 2px;
127
+ }
128
+ .wp-media-buttons span.wp-media-buttons-icon {
129
+ line-height: 1em;
130
+ }
131
+ .wp-admin select {
132
+ line-height: 42px;
133
+ height: 42px;
134
+ }
135
+ .search-box input[name="s"],
136
+ .tablenav .search-plugins input[name="s"],
137
+ .tagsdiv .newtag {
138
+ height: 42px;
139
+ }
140
+ .wp-editor-tabs .wp-switch-editor {
141
+ height: 27px;
142
+ padding: 12px 12px 4px;
143
+ }
144
+ .html-active .wp-editor-tabs .switch-html,
145
+ .tmce-active .wp-editor-tabs .switch-tmce {
146
+ height: 28px;
147
+ }
css/index.php ADDED
@@ -0,0 +1 @@
1
+ <?php // Silence is golden.
css/settings-page.css ADDED
@@ -0,0 +1,195 @@