WPide - Version 1.0.5

Version Description

  • Added the facility to download and run the cutting edge development version of WPide from the Github repository
Download this release

Release Info

Developer WPsites
Plugin Icon 128x128 WPide
Version 1.0.5
Comparing to
See all releases

Code changes from version 1.0.4 to 1.0.5

Files changed (109) hide show
  1. WPide.php +118 -8
  2. ace-0.2.0/ChangeLog.txt +0 -0
  3. ace-0.2.0/LICENSE +0 -0
  4. ace-0.2.0/Readme.md +0 -0
  5. ace-0.2.0/demo/kitchen-sink-uncompressed.js +0 -0
  6. ace-0.2.0/demo/kitchen-sink.js +0 -0
  7. ace-0.2.0/demo/logo.png +0 -0
  8. ace-0.2.0/demo/styles.css +0 -0
  9. ace-0.2.0/editor.html +0 -0
  10. ace-0.2.0/kitchen-sink.html +0 -0
  11. ace-0.2.0/src/ace-uncompressed.js +0 -0
  12. ace-0.2.0/src/ace.js +0 -0
  13. ace-0.2.0/src/cockpit-uncompressed.js +0 -0
  14. ace-0.2.0/src/cockpit.js +0 -0
  15. ace-0.2.0/src/keybinding-emacs.js +0 -0
  16. ace-0.2.0/src/keybinding-vim.js +0 -0
  17. ace-0.2.0/src/mode-c_cpp.js +0 -0
  18. ace-0.2.0/src/mode-clojure.js +0 -0
  19. ace-0.2.0/src/mode-coffee.js +0 -0
  20. ace-0.2.0/src/mode-csharp.js +0 -0
  21. ace-0.2.0/src/mode-css.js +0 -0
  22. ace-0.2.0/src/mode-groovy.js +0 -0
  23. ace-0.2.0/src/mode-html.js +0 -0
  24. ace-0.2.0/src/mode-java.js +0 -0
  25. ace-0.2.0/src/mode-javascript.js +0 -0
  26. ace-0.2.0/src/mode-json.js +0 -0
  27. ace-0.2.0/src/mode-ocaml.js +0 -0
  28. ace-0.2.0/src/mode-perl.js +0 -0
  29. ace-0.2.0/src/mode-php.js +0 -0
  30. ace-0.2.0/src/mode-python.js +0 -0
  31. ace-0.2.0/src/mode-ruby.js +0 -0
  32. ace-0.2.0/src/mode-scad.js +0 -0
  33. ace-0.2.0/src/mode-scala.js +0 -0
  34. ace-0.2.0/src/mode-scss.js +0 -0
  35. ace-0.2.0/src/mode-svg.js +0 -0
  36. ace-0.2.0/src/mode-textile.js +0 -0
  37. ace-0.2.0/src/mode-xml.js +0 -0
  38. ace-0.2.0/src/theme-clouds.js +0 -0
  39. ace-0.2.0/src/theme-clouds_midnight.js +0 -0
  40. ace-0.2.0/src/theme-cobalt.js +0 -0
  41. ace-0.2.0/src/theme-crimson_editor.js +0 -0
  42. ace-0.2.0/src/theme-dawn.js +0 -0
  43. ace-0.2.0/src/theme-eclipse.js +0 -0
  44. ace-0.2.0/src/theme-idle_fingers.js +0 -0
  45. ace-0.2.0/src/theme-kr_theme.js +0 -0
  46. ace-0.2.0/src/theme-merbivore.js +0 -0
  47. ace-0.2.0/src/theme-merbivore_soft.js +0 -0
  48. ace-0.2.0/src/theme-mono_industrial.js +0 -0
  49. ace-0.2.0/src/theme-monokai.js +0 -0
  50. ace-0.2.0/src/theme-pastel_on_dark.js +0 -0
  51. ace-0.2.0/src/theme-solarized_dark.js +0 -0
  52. ace-0.2.0/src/theme-solarized_light.js +0 -0
  53. ace-0.2.0/src/theme-textmate.js +0 -0
  54. ace-0.2.0/src/theme-twilight.js +0 -0
  55. ace-0.2.0/src/theme-vibrant_ink.js +0 -0
  56. ace-0.2.0/src/worker-coffee.js +0 -0
  57. ace-0.2.0/src/worker-css.js +0 -0
  58. ace-0.2.0/src/worker-javascript.js +0 -0
  59. ace-0.2.0/textarea/ChangeLog.txt +0 -0
  60. ace-0.2.0/textarea/LICENSE +0 -0
  61. ace-0.2.0/textarea/Readme.md +0 -0
  62. ace-0.2.0/textarea/editor.html +0 -0
  63. ace-0.2.0/textarea/src/ace-uncompressed.js +0 -0
  64. ace-0.2.0/textarea/src/ace.js +0 -0
  65. ace-0.2.0/textarea/src/mode-c_cpp.js +0 -0
  66. ace-0.2.0/textarea/src/mode-clojure.js +0 -0
  67. ace-0.2.0/textarea/src/mode-coffee.js +0 -0
  68. ace-0.2.0/textarea/src/mode-csharp.js +0 -0
  69. ace-0.2.0/textarea/src/mode-css.js +0 -0
  70. ace-0.2.0/textarea/src/mode-groovy.js +0 -0
  71. ace-0.2.0/textarea/src/mode-html.js +0 -0
  72. ace-0.2.0/textarea/src/mode-java.js +0 -0
  73. ace-0.2.0/textarea/src/mode-javascript.js +0 -0
  74. ace-0.2.0/textarea/src/mode-json.js +0 -0
  75. ace-0.2.0/textarea/src/mode-ocaml.js +0 -0
  76. ace-0.2.0/textarea/src/mode-perl.js +0 -0
  77. ace-0.2.0/textarea/src/mode-php.js +0 -0
  78. ace-0.2.0/textarea/src/mode-python.js +0 -0
  79. ace-0.2.0/textarea/src/mode-ruby.js +0 -0
  80. ace-0.2.0/textarea/src/mode-scad.js +0 -0
  81. ace-0.2.0/textarea/src/mode-scala.js +0 -0
  82. ace-0.2.0/textarea/src/mode-scss.js +0 -0
  83. ace-0.2.0/textarea/src/mode-svg.js +0 -0
  84. ace-0.2.0/textarea/src/mode-textile.js +0 -0
  85. ace-0.2.0/textarea/src/mode-xml.js +0 -0
  86. ace-0.2.0/textarea/src/theme-clouds.js +0 -0
  87. ace-0.2.0/textarea/src/theme-clouds_midnight.js +0 -0
  88. ace-0.2.0/textarea/src/theme-cobalt.js +0 -0
  89. ace-0.2.0/textarea/src/theme-crimson_editor.js +0 -0
  90. ace-0.2.0/textarea/src/theme-dawn.js +0 -0
  91. ace-0.2.0/textarea/src/theme-eclipse.js +0 -0
  92. ace-0.2.0/textarea/src/theme-idle_fingers.js +0 -0
  93. ace-0.2.0/textarea/src/theme-kr_theme.js +0 -0
  94. ace-0.2.0/textarea/src/theme-merbivore.js +0 -0
  95. ace-0.2.0/textarea/src/theme-merbivore_soft.js +0 -0
  96. ace-0.2.0/textarea/src/theme-mono_industrial.js +0 -0
  97. ace-0.2.0/textarea/src/theme-monokai.js +0 -0
  98. ace-0.2.0/textarea/src/theme-pastel_on_dark.js +0 -0
  99. ace-0.2.0/textarea/src/theme-solarized_dark.js +0 -0
  100. ace-0.2.0/textarea/src/theme-solarized_light.js +0 -0
  101. ace-0.2.0/textarea/src/theme-textmate.js +0 -0
  102. ace-0.2.0/textarea/src/theme-twilight.js +0 -0
  103. ace-0.2.0/textarea/src/theme-vibrant_ink.js +0 -0
  104. ace-0.2.0/textarea/style.css +0 -0
  105. readme.txt +92 -76
  106. screenshot-1.jpg +0 -0
  107. tgm-plugin-activation/class-tgm-plugin-activation.php +2087 -0
  108. tgm-plugin-activation/example.php +108 -0
  109. tgm-plugin-activation/plugins/tgm-example-plugin.zip +0 -0
WPide.php CHANGED
@@ -4,7 +4,7 @@
4
  Plugin Name: WPide
5
  Plugin URI: https://github.com/WPsites/WPide
6
  Description: Replace the default WordPress code editor for plugins and themes. Adding syntax highlighting, autocomplete of WordPress functions + PHP, line numbers, auto backup of files before editing.
7
- Version: 1.0.4
8
  Author: Simon Dunton
9
  Author URI: http://www.wpsites.co.uk
10
 
@@ -25,10 +25,10 @@ class WPide
25
 
26
  //setup ajax function to save a backup
27
  add_action('wp_ajax_ace_backup_call', 'WPide::ace_backup_call');
 
28
 
29
  }
30
-
31
-
32
  public static function add_admin_head()
33
  {
34
 
@@ -131,12 +131,122 @@ class WPide
131
 
132
  }
133
 
134
- //only include this plugin if on theme editor, plugin editor or an ajax call
135
- if ( $_SERVER['PHP_SELF'] === '/wp-admin/plugin-editor.php' ||
136
- $_SERVER['PHP_SELF'] === '/wp-admin/theme-editor.php' ||
 
 
 
 
137
  $_SERVER['PHP_SELF'] === '/wp-admin/admin-ajax.php' ){
138
 
139
- add_action("init", create_function('', 'new WPide();'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
  }
142
- ?>
4
  Plugin Name: WPide
5
  Plugin URI: https://github.com/WPsites/WPide
6
  Description: Replace the default WordPress code editor for plugins and themes. Adding syntax highlighting, autocomplete of WordPress functions + PHP, line numbers, auto backup of files before editing.
7
+ Version: 1.0.5
8
  Author: Simon Dunton
9
  Author URI: http://www.wpsites.co.uk
10
 
25
 
26
  //setup ajax function to save a backup
27
  add_action('wp_ajax_ace_backup_call', 'WPide::ace_backup_call');
28
+
29
 
30
  }
31
+
 
32
  public static function add_admin_head()
33
  {
34
 
131
 
132
  }
133
 
134
+ //only include this plugin if on theme or plugin editors (Or Multisite network equivalents) or an ajax call
135
+ $is_ms = '';
136
+ if ( is_multisite() )
137
+ $is_ms = 'network/';
138
+
139
+ if ( $_SERVER['PHP_SELF'] === '/wp-admin/' . $is_ms . 'plugin-editor.php' ||
140
+ $_SERVER['PHP_SELF'] === '/wp-admin/' . $is_ms . 'theme-editor.php' ||
141
  $_SERVER['PHP_SELF'] === '/wp-admin/admin-ajax.php' ){
142
 
143
+ add_action( 'init', create_function( '', 'new WPide();' ) );
144
+ }
145
+
146
+
147
+ add_filter("plugin_row_meta", 'wpide_dev_links', 10, 2);
148
+
149
+ function wpide_dev_links($links, $file) {
150
+ static $this_plugin;
151
+
152
+ if (!$this_plugin) {
153
+ $this_plugin = plugin_basename(__FILE__);
154
+ }
155
+
156
+ // check to make sure we are on the correct plugin
157
+ if ($file === $this_plugin) {
158
+ // the anchor tag and href to the URL we want. For a "Settings" link, this needs to be the url of your settings page
159
+ $settings_link = '<a href="' . get_bloginfo('wpurl') . '/wp-admin/plugins.php?page=install-required-plugins" style="font-weight:bold;">Download and install V2 Development version</a>';
160
+ // add the link to the list
161
+ array_push($links, $settings_link);
162
+ }
163
+ return $links;
164
+ }
165
+
166
+
167
+
168
+
169
+
170
+
171
+ /**
172
+ * Include the TGM_Plugin_Activation class.
173
+ */
174
+ require_once dirname( __FILE__ ) . '/tgm-plugin-activation/class-tgm-plugin-activation.php';
175
+
176
+ add_action( 'tgmpa_register', 'wpide_run_dev' );
177
+ /**
178
+ * Register the required plugins for this theme.
179
+ *
180
+ * In this example, we register two plugins - one included with the TGMPA library
181
+ * and one from the .org repo.
182
+ *
183
+ * The variable passed to tgmpa_register_plugins() should be an array of plugin
184
+ * arrays.
185
+ *
186
+ * This function is hooked into tgmpa_init, which is fired within the
187
+ * TGM_Plugin_Activation class constructor.
188
+ */
189
+ function wpide_run_dev() {
190
+
191
+ /**
192
+ * Array of plugin arrays. Required keys are name and slug.
193
+ * If the source is NOT from the .org repo, then source is also required.
194
+ */
195
+ $plugins = array(
196
+
197
+ // This is an example of how to include a plugin from the WordPress Plugin Repository
198
+ array(
199
+ 'name' => 'WPide V2 Dev',
200
+ 'slug' => 'WPideV2',
201
+ 'version' => '2.0',
202
+ 'required' => false,
203
+ 'external_url' => 'https://github.com/WPsites/WPide/tree/v2dev',
204
+ 'source' => 'https://github.com/WPsites/WPide/zipball/v2dev',
205
+ ),
206
+
207
+ );
208
+
209
+ // Change this to your theme text domain, used for internationalising strings
210
+ $theme_text_domain = 'tgmpa';
211
+
212
+ /**
213
+ * Array of configuration settings. Amend each line as needed.
214
+ * If you want the default strings to be available under your own theme domain,
215
+ * leave the strings uncommented.
216
+ * Some of the strings are added into a sprintf, so see the comments at the
217
+ * end of each line for what each argument will be.
218
+ */
219
+ $config = array(
220
+ 'domain' => $theme_text_domain, // Text domain - likely want to be the same as your theme.
221
+ 'default_path' => '', // Default absolute path to pre-packaged plugins
222
+ 'parent_menu_slug' => 'plugins.php', // Default parent menu slug
223
+ 'parent_url_slug' => 'plugins.php', // Default parent URL slug
224
+ 'menu' => 'install-required-plugins', // Menu slug
225
+ 'has_notices' => true, // Show admin notices or not
226
+ 'is_automatic' => true, // Automatically activate plugins after installation or not
227
+ 'message' => '', // Message to output right before the plugins table
228
+ 'strings' => array(
229
+ 'page_title' => __( 'Install Suggested Plugins', $theme_text_domain ),
230
+ 'menu_title' => __( 'Install Plugins', $theme_text_domain ),
231
+ 'installing' => __( 'Installing Plugin: %s', $theme_text_domain ), // %1$s = plugin name
232
+ 'oops' => __( 'Something went wrong with the plugin API.', $theme_text_domain ),
233
+ 'notice_can_install_required' => _n_noop( 'This theme requires the following plugin: %1$s.', 'This theme requires the following plugins: %1$s.' ), // %1$s = plugin name(s)
234
+ 'notice_can_install_recommended' => _n_noop( 'The V2 Development branch of WPide is available for testing - With new features such as multi tab editing.<br />Word of warning: this is the cutting edge development version which could contain bugs so use at your own risk! ', 'WPide recommends the following plugins: %1$s.' ), // %1$s = plugin name(s)
235
+ 'notice_cannot_install' => _n_noop( 'Sorry, but you do not have the correct permissions to install the %s plugin. Contact the administrator of this site for help on getting the plugin installed.', 'Sorry, but you do not have the correct permissions to install the %s plugins. Contact the administrator of this site for help on getting the plugins installed.' ), // %1$s = plugin name(s)
236
+ 'notice_can_activate_required' => _n_noop( 'The following required plugin is currently inactive: %1$s.', 'The following required plugins are currently inactive: %1$s.' ), // %1$s = plugin name(s)
237
+ 'notice_can_activate_recommended' => _n_noop( 'The following recommended plugin is currently inactive: %1$s.', 'The following recommended plugins are currently inactive: %1$s.' ), // %1$s = plugin name(s)
238
+ 'notice_cannot_activate' => _n_noop( 'Sorry, but you do not have the correct permissions to activate the %s plugin. Contact the administrator of this site for help on getting the plugin activated.', 'Sorry, but you do not have the correct permissions to activate the %s plugins. Contact the administrator of this site for help on getting the plugins activated.' ), // %1$s = plugin name(s)
239
+ 'notice_ask_to_update' => _n_noop( 'The following plugin needs to be updated to its latest version to ensure maximum compatibility with this theme: %1$s.', 'The following plugins need to be updated to their latest version to ensure maximum compatibility with this theme: %1$s.' ), // %1$s = plugin name(s)
240
+ 'notice_cannot_update' => _n_noop( 'Sorry, but you do not have the correct permissions to update the %s plugin. Contact the administrator of this site for help on getting the plugin updated.', 'Sorry, but you do not have the correct permissions to update the %s plugins. Contact the administrator of this site for help on getting the plugins updated.' ), // %1$s = plugin name(s)
241
+ 'install_link' => _n_noop( 'Begin installing the development version of WPide', 'Begin installing plugins' ),
242
+ 'activate_link' => _n_noop( 'Activate WPide V2 Dev', 'Activate installed plugins' ),
243
+ 'return' => __( 'Return to Suggested Plugins Installer', $theme_text_domain ),
244
+ 'plugin_activated' => __( 'Plugin activated successfully.', $theme_text_domain ),
245
+ 'complete' => __( 'All plugins installed and activated successfully. %s', $theme_text_domain ) // %1$s = dashboard link
246
+ )
247
+ );
248
+
249
+ tgmpa( $plugins, $config );
250
 
251
  }
252
+ ?>
ace-0.2.0/ChangeLog.txt CHANGED
File without changes
ace-0.2.0/LICENSE CHANGED
File without changes
ace-0.2.0/Readme.md CHANGED
File without changes
ace-0.2.0/demo/kitchen-sink-uncompressed.js CHANGED
File without changes
ace-0.2.0/demo/kitchen-sink.js CHANGED
File without changes
ace-0.2.0/demo/logo.png CHANGED
File without changes
ace-0.2.0/demo/styles.css CHANGED
File without changes
ace-0.2.0/editor.html CHANGED
File without changes
ace-0.2.0/kitchen-sink.html CHANGED
File without changes
ace-0.2.0/src/ace-uncompressed.js CHANGED
File without changes
ace-0.2.0/src/ace.js CHANGED
File without changes
ace-0.2.0/src/cockpit-uncompressed.js CHANGED
File without changes
ace-0.2.0/src/cockpit.js CHANGED
File without changes
ace-0.2.0/src/keybinding-emacs.js CHANGED
File without changes
ace-0.2.0/src/keybinding-vim.js CHANGED
File without changes
ace-0.2.0/src/mode-c_cpp.js CHANGED
File without changes
ace-0.2.0/src/mode-clojure.js CHANGED
File without changes
ace-0.2.0/src/mode-coffee.js CHANGED
File without changes
ace-0.2.0/src/mode-csharp.js CHANGED
File without changes
ace-0.2.0/src/mode-css.js CHANGED
File without changes
ace-0.2.0/src/mode-groovy.js CHANGED
File without changes
ace-0.2.0/src/mode-html.js CHANGED
File without changes
ace-0.2.0/src/mode-java.js CHANGED
File without changes
ace-0.2.0/src/mode-javascript.js CHANGED
File without changes
ace-0.2.0/src/mode-json.js CHANGED
File without changes
ace-0.2.0/src/mode-ocaml.js CHANGED
File without changes
ace-0.2.0/src/mode-perl.js CHANGED
File without changes
ace-0.2.0/src/mode-php.js CHANGED
File without changes
ace-0.2.0/src/mode-python.js CHANGED
File without changes
ace-0.2.0/src/mode-ruby.js CHANGED
File without changes
ace-0.2.0/src/mode-scad.js CHANGED
File without changes
ace-0.2.0/src/mode-scala.js CHANGED
File without changes
ace-0.2.0/src/mode-scss.js CHANGED
File without changes
ace-0.2.0/src/mode-svg.js CHANGED
File without changes
ace-0.2.0/src/mode-textile.js CHANGED
File without changes
ace-0.2.0/src/mode-xml.js CHANGED
File without changes
ace-0.2.0/src/theme-clouds.js CHANGED
File without changes
ace-0.2.0/src/theme-clouds_midnight.js CHANGED
File without changes
ace-0.2.0/src/theme-cobalt.js CHANGED
File without changes
ace-0.2.0/src/theme-crimson_editor.js CHANGED
File without changes
ace-0.2.0/src/theme-dawn.js CHANGED
File without changes
ace-0.2.0/src/theme-eclipse.js CHANGED
File without changes
ace-0.2.0/src/theme-idle_fingers.js CHANGED
File without changes
ace-0.2.0/src/theme-kr_theme.js CHANGED
File without changes
ace-0.2.0/src/theme-merbivore.js CHANGED
File without changes
ace-0.2.0/src/theme-merbivore_soft.js CHANGED
File without changes
ace-0.2.0/src/theme-mono_industrial.js CHANGED
File without changes
ace-0.2.0/src/theme-monokai.js CHANGED
File without changes
ace-0.2.0/src/theme-pastel_on_dark.js CHANGED
File without changes
ace-0.2.0/src/theme-solarized_dark.js CHANGED
File without changes
ace-0.2.0/src/theme-solarized_light.js CHANGED
File without changes
ace-0.2.0/src/theme-textmate.js CHANGED
File without changes
ace-0.2.0/src/theme-twilight.js CHANGED
File without changes
ace-0.2.0/src/theme-vibrant_ink.js CHANGED
File without changes
ace-0.2.0/src/worker-coffee.js CHANGED
File without changes
ace-0.2.0/src/worker-css.js CHANGED
File without changes
ace-0.2.0/src/worker-javascript.js CHANGED
File without changes
ace-0.2.0/textarea/ChangeLog.txt CHANGED
File without changes
ace-0.2.0/textarea/LICENSE CHANGED
File without changes
ace-0.2.0/textarea/Readme.md CHANGED
File without changes
ace-0.2.0/textarea/editor.html CHANGED
File without changes
ace-0.2.0/textarea/src/ace-uncompressed.js CHANGED
File without changes
ace-0.2.0/textarea/src/ace.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-c_cpp.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-clojure.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-coffee.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-csharp.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-css.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-groovy.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-html.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-java.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-javascript.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-json.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-ocaml.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-perl.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-php.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-python.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-ruby.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-scad.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-scala.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-scss.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-svg.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-textile.js CHANGED
File without changes
ace-0.2.0/textarea/src/mode-xml.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-clouds.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-clouds_midnight.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-cobalt.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-crimson_editor.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-dawn.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-eclipse.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-idle_fingers.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-kr_theme.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-merbivore.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-merbivore_soft.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-mono_industrial.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-monokai.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-pastel_on_dark.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-solarized_dark.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-solarized_light.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-textmate.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-twilight.js CHANGED
File without changes
ace-0.2.0/textarea/src/theme-vibrant_ink.js CHANGED
File without changes
ace-0.2.0/textarea/style.css CHANGED
File without changes
readme.txt CHANGED
@@ -1,76 +1,92 @@
1
- === WPide ===
2
- Contributors: WPsites, Thomas Wieczorek
3
- Tags: code, theme editor, plugin editor, code editor
4
- Requires at least: 3.0
5
- Tested up to: 3.3.1
6
- Stable tag: 1.0.4
7
-
8
- Replace the default WordPress code editor for plugins and themes. Adding syntax highlighting, auto complete of WordPress functions + PHP, line numbers, auto backup of files before editing.
9
-
10
- == Description ==
11
-
12
- Out of frustration of unsatisfactory experiences with desktop based IDE's and code editors we decided to make use of the Ajax.org Cloud9 Editor (http://ace.ajax.org/) and embed it in place of the default WordPress file editor.
13
-
14
- = This plugin does not currently work on Internet Explorer! =
15
-
16
- = Current Features: =
17
-
18
- * Syntax highlighting
19
- * Line numbers
20
- * Auto complete of WordPress functions and PHP
21
- * Automatic backup of every file you edit. (one daily backup and one hourly backup of each file stored in plugins/WPide/backups)
22
- * Highlight matching parentheses
23
- * Auto indentation
24
-
25
- = Planned Features: =
26
-
27
- * Tabbed document interface for editing multiple files
28
- * Ajax Save
29
- * Create and edit directories and files
30
- * ctrl + s saving
31
- * Improve the code auto complete so that it shows command arguments rather than just commands, possibly with links through to the WordPress codex for further info
32
- * Create an admin panel to choose between syntax highlighting themes and turn on/off other Ajax.org Cloud9 functionality
33
- * Better automated file backup process
34
- * Integration with git for version control
35
-
36
-
37
- As with most plugins this one is open source. For issue tracking, further information and anyone wishing to get involved and help contribute to this project can do so over on github https://github.com/WPsites/WPide
38
-
39
- == Contributors ==
40
-
41
- Simon Dunton - http://www.wpsites.co.uk
42
- Thomas Wieczorek - http://www.wieczo.net
43
-
44
-
45
- == Installation ==
46
-
47
- 1. Upload the WPide folder to the `/wp-content/plugins/` directory
48
- 1. Activate the plugin through the 'Plugins' menu in WordPress
49
-
50
- == Frequently Asked Questions ==
51
-
52
- = Does this plugin work on Internet Explorer =
53
-
54
- No support for Internet Explorer right now
55
-
56
- == Screenshots ==
57
-
58
- 1. Editor view, showing line numbers and syntax highlighting.
59
-
60
- == Changelog ==
61
-
62
- = 1.0.4 =
63
- * Implemented JavaScript and CSS mode for better syntax highlighing and checking (Thanks to Thomas Wieczorek)
64
- * Organise and format source code
65
-
66
- = 1.0.2 =
67
- * Tidy and comment code
68
- * Added message when backup file is generated
69
- * Adjust code complete dropdown position
70
- * Improved editor responsiveness when using delete or enter keys
71
-
72
- = 1.0.1 =
73
- * Fixed "Folder name case" issue.
74
-
75
- = 1.0 =
76
- * Initial release.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === WPide ===
2
+ Contributors: WPsites, Thomas Wieczorek
3
+ Tags: code, theme editor, plugin editor, code editor
4
+ Requires at least: 3.0
5
+ Tested up to: 3.3.1
6
+ Stable tag: 1.0.5
7
+
8
+ Replace the default WordPress code editor for plugins and themes. Adding syntax highlighting, auto complete of WordPress functions + PHP, line numbers, auto backup of files before editing.
9
+
10
+ == Description ==
11
+
12
+ Out of frustration of unsatisfactory experiences with desktop based IDE's and code editors we decided to make use of the Ajax.org Cloud9 Editor (http://ace.ajax.org/) and embed it in place of the default WordPress file editor.
13
+
14
+ The V2 Development version of this plugin can now be downloaded/installed automatically from the Github repository. You will be given this option when this plugin is first activated. The development version may contain bugs so use with caution.
15
+
16
+ = This plugin does not currently work on Internet Explorer! =
17
+
18
+ = Current Features: =
19
+
20
+ * Syntax highlighting
21
+ * Line numbers
22
+ * Auto complete of WordPress functions and PHP
23
+ * Automatic backup of every file you edit. (one daily backup and one hourly backup of each file stored in plugins/WPide/backups)
24
+ * Highlight matching parentheses
25
+ * Auto indentation
26
+
27
+ = Planned Features: =
28
+
29
+ * Tabbed document interface for editing multiple files
30
+ * Ajax Save
31
+ * Create and edit directories and files
32
+ * ctrl + s saving
33
+ * Improve the code auto complete so that it shows command arguments rather than just commands, possibly with links through to the WordPress codex for further info
34
+ * Create an admin panel to choose between syntax highlighting themes and turn on/off other Ajax.org Cloud9 functionality
35
+ * Better automated file backup process
36
+ * Integration with git for version control
37
+
38
+
39
+ As with most plugins this one is open source. For issue tracking, further information and anyone wishing to get involved and help contribute to this project can do so over on github https://github.com/WPsites/WPide
40
+
41
+ == Contributors ==
42
+
43
+ Simon Dunton - http://www.wpsites.co.uk
44
+ Thomas Wieczorek - http://www.wieczo.net
45
+
46
+
47
+ == Installation ==
48
+
49
+ 1. Upload the WPide folder to the `/wp-content/plugins/` directory
50
+ 1. Activate the plugin through the 'Plugins' menu in WordPress
51
+
52
+ == Frequently Asked Questions ==
53
+
54
+ = Does this plugin work on Internet Explorer =
55
+
56
+ No support for Internet Explorer right now
57
+
58
+ == Screenshots ==
59
+
60
+ 1. Editor view, showing line numbers and syntax highlighting.
61
+
62
+ == Changelog ==
63
+
64
+ = 1.0.5 =
65
+ * Added the facility to download and run the cutting edge development version of WPide from the Github repository
66
+
67
+ = 1.0.4 =
68
+ * Implemented JavaScript and CSS mode for better syntax highlighing and checking (Thanks to Thomas Wieczorek)
69
+ * Organise and format source code
70
+
71
+ = 1.0.2 =
72
+ * Tidy and comment code
73
+ * Added message when backup file is generated
74
+ * Adjust code complete dropdown position
75
+ * Improved editor responsiveness when using delete or enter keys
76
+
77
+ = 1.0.1 =
78
+ * Fixed "Folder name case" issue.
79
+
80
+ = 1.0 =
81
+ * Initial release.
82
+
83
+ == DEV NOTES ==
84
+
85
+ Maybe some interesting things here we could impliment to help with following the WordPress standard and more advanced code styntax checking
86
+ http://magp.ie/2011/01/10/tidy-and-format-your-php-and-meet-wordpress-standards-on-coda-and-textwrangler/
87
+
88
+ Checkout the following WordPress plugin "WP Live CSS Editor" to work out how to do LIVE css editing. Combining a LESS compiler with live CSS editing/compile would be a dream.
89
+
90
+ To give finer control over what files are editable in the plugin/theme editor and to remove the reliance of the built in plugin/theme editor functionality move the plugin to a dedicated admin page with it's own navigation menu and it's own file manager. Use http://abeautifulsite.net/blog/2008/03/jquery-file-tree/ as the file manager component.
91
+
92
+
screenshot-1.jpg CHANGED
File without changes
tgm-plugin-activation/class-tgm-plugin-activation.php ADDED
@@ -0,0 +1,2087 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin installation and activation for WordPress themes.
4
+ *
5
+ * @package TGM-Plugin-Activation
6
+ * @version 2.3.3
7
+ * @author Thomas Griffin <thomas@thomasgriffinmedia.com>
8
+ * @author Gary Jones <gamajo@gamajo.com>
9
+ * @copyright Copyright (c) 2012, Thomas Griffin
10
+ * @license http://opensource.org/licenses/gpl-2.0.php GPL v2 or later
11
+ * @link https://github.com/thomasgriffin/TGM-Plugin-Activation
12
+ */
13
+
14
+ /*
15
+ Copyright 2012 Thomas Griffin (email : thomas@thomasgriffinmedia.com)
16
+
17
+ This program is free software; you can redistribute it and/or modify
18
+ it under the terms of the GNU General Public License, version 3, as
19
+ published by the Free Software Foundation.
20
+
21
+ This program is distributed in the hope that it will be useful,
22
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
23
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
+ GNU General Public License for more details.
25
+
26
+ You should have received a copy of the GNU General Public License
27
+ along with this program; if not, write to the Free Software
28
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29
+ */
30
+
31
+ if ( ! class_exists( 'TGM_Plugin_Activation' ) ) {
32
+ /**
33
+ * Automatic plugin installation and activation library.
34
+ *
35
+ * Creates a way to automatically install and activate plugins from within themes.
36
+ * The plugins can be either pre-packaged, downloaded from the WordPress
37
+ * Plugin Repository or downloaded from a private repository.
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @package TGM-Plugin-Activation
42
+ * @author Thomas Griffin <thomas@thomasgriffinmedia.com>
43
+ * @author Gary Jones <gamajo@gamajo.com>
44
+ */
45
+ class TGM_Plugin_Activation {
46
+
47
+ /**
48
+ * Holds a copy of itself, so it can be referenced by the class name.
49
+ *
50
+ * @since 1.0.0
51
+ *
52
+ * @var TGM_Plugin_Activation
53
+ */
54
+ static $instance;
55
+
56
+ /**
57
+ * Holds arrays of plugin details.
58
+ *
59
+ * @since 1.0.0
60
+ *
61
+ * @var array
62
+ */
63
+ public $plugins = array();
64
+
65
+ /**
66
+ * Parent menu slug for plugins page.
67
+ *
68
+ * @since 2.2.0
69
+ *
70
+ * @var string Parent menu slug. Defaults to 'themes.php'.
71
+ */
72
+ public $parent_menu_slug = 'themes.php';
73
+
74
+ /**
75
+ * Parent URL slug for URL references.
76
+ *
77
+ * This is useful if you want to place the custom plugins page as a
78
+ * submenu item under a custom parent menu.
79
+ *
80
+ * @since 2.2.0
81
+ *
82
+ * @var string Parent URL slug. Defaults to 'themes.php'.
83
+ */
84
+ public $parent_url_slug = 'themes.php';
85
+
86
+ /**
87
+ * Name of the querystring argument for the admin page.
88
+ *
89
+ * @since 1.0.0
90
+ *
91
+ * @var string
92
+ */
93
+ public $menu = 'install-required-plugins';
94
+
95
+ /**
96
+ * Text domain for localization support.
97
+ *
98
+ * @since 1.1.0
99
+ *
100
+ * @var string
101
+ */
102
+ public $domain = 'tgmpa';
103
+
104
+ /**
105
+ * Default absolute path to folder containing pre-packaged plugin zip files.
106
+ *
107
+ * @since 2.0.0
108
+ *
109
+ * @var string Absolute path prefix to packaged zip file location. Default is empty string.
110
+ */
111
+ public $default_path = '';
112
+
113
+ /**
114
+ * Flag to show admin notices or not.
115
+ *
116
+ * @since 2.1.0
117
+ *
118
+ * @var boolean
119
+ */
120
+ public $has_notices = true;
121
+
122
+ /**
123
+ * Flag to set automatic activation of plugins. Off by default.
124
+ *
125
+ * @since 2.2.0
126
+ *
127
+ * @var boolean
128
+ */
129
+ public $is_automatic = false;
130
+
131
+ /**
132
+ * Optional message to display before the plugins table.
133
+ *
134
+ * @since 2.2.0
135
+ *
136
+ * @var string Message filtered by wp_kses_post(). Default is empty string.
137
+ */
138
+ public $message = '';
139
+
140
+ /**
141
+ * Holds configurable array of strings.
142
+ *
143
+ * Default values are added in the constructor.
144
+ *
145
+ * @since 2.0.0
146
+ *
147
+ * @var array
148
+ */
149
+ public $strings = array();
150
+
151
+ /**
152
+ * Adds a reference of this object to $instance, populates default strings,
153
+ * does the tgmpa_init action hook, and hooks in the interactions to init.
154
+ *
155
+ * @since 1.0.0
156
+ *
157
+ * @see TGM_Plugin_Activation::init()
158
+ */
159
+ public function __construct() {
160
+
161
+ self::$instance =& $this;
162
+
163
+ $this->strings = array(
164
+ 'page_title' => __( 'Install Required Plugins', $this->domain ),
165
+ 'menu_title' => __( 'Install Plugins', $this->domain ),
166
+ 'installing' => __( 'Installing Plugin: %s', $this->domain ),
167
+ 'oops' => __( 'Something went wrong.', $this->domain ),
168
+ 'notice_can_install_required' => _n_noop( 'This theme requires the following plugin: %1$s.', 'This theme requires the following plugins: %1$s.' ),
169
+ 'notice_can_install_recommended' => _n_noop( 'This theme recommends the following plugin: %1$s.', 'This theme recommends the following plugins: %1$s.' ),
170
+ 'notice_cannot_install' => _n_noop( 'Sorry, but you do not have the correct permissions to install the %s plugin. Contact the administrator of this site for help on getting the plugin installed.', 'Sorry, but you do not have the correct permissions to install the %s plugins. Contact the administrator of this site for help on getting the plugins installed.' ),
171
+ 'notice_can_activate_required' => _n_noop( 'The following required plugin is currently inactive: %1$s.', 'The following required plugins are currently inactive: %1$s.' ),
172
+ 'notice_can_activate_recommended' => _n_noop( 'The following recommended plugin is currently inactive: %1$s.', 'The following recommended plugins are currently inactive: %1$s.' ),
173
+ 'notice_cannot_activate' => _n_noop( 'Sorry, but you do not have the correct permissions to activate the %s plugin. Contact the administrator of this site for help on getting the plugin activated.', 'Sorry, but you do not have the correct permissions to activate the %s plugins. Contact the administrator of this site for help on getting the plugins activated.' ),
174
+ 'notice_ask_to_update' => _n_noop( 'The following plugin needs to be updated to its latest version to ensure maximum compatibility with this theme: %1$s.', 'The following plugins need to be updated to their latest version to ensure maximum compatibility with this theme: %1$s.' ),
175
+ 'notice_cannot_update' => _n_noop( 'Sorry, but you do not have the correct permissions to update the %s plugin. Contact the administrator of this site for help on getting the plugin updated.', 'Sorry, but you do not have the correct permissions to update the %s plugins. Contact the administrator of this site for help on getting the plugins updated.' ),
176
+ 'install_link' => _n_noop( 'Begin installing plugin', 'Begin installing plugins' ),
177
+ 'activate_link' => _n_noop( 'Activate installed plugin', 'Activate installed plugins' ),
178
+ 'return' => __( 'Return to Required Plugins Installer', $this->domain ),
179
+ 'plugin_activated' => __( 'Plugin activated successfully.', $this->domain ),
180
+ 'complete' => __( 'All plugins installed and activated successfully. %1$s', $this->domain ),
181
+ );
182
+
183
+ /** Annouce that the class is ready, and pass the object (for advanced use) */
184
+ do_action_ref_array( 'tgmpa_init', array( &$this ) );
185
+
186
+ /** When the rest of WP has loaded, kick-start the rest of the class */
187
+ add_action( 'init', array( &$this, 'init' ) );
188
+
189
+ }
190
+
191
+ /**
192
+ * Initialise the interactions between this class and WordPress.
193
+ *
194
+ * Hooks in three new methods for the class: admin_menu, notices and styles.
195
+ *
196
+ * @since 2.0.0
197
+ *
198
+ * @see TGM_Plugin_Activation::admin_menu()
199
+ * @see TGM_Plugin_Activation::notices()
200
+ * @see TGM_Plugin_Activation::styles()
201
+ */
202
+ public function init() {
203
+
204
+ do_action( 'tgmpa_register' );
205
+ /** After this point, the plugins should be registered and the configuration set */
206
+
207
+ /** Proceed only if we have plugins to handle */
208
+ if ( $this->plugins ) {
209
+ $sorted = array(); // Prepare variable for sorting
210
+
211
+ foreach ( $this->plugins as $plugin )
212
+ $sorted[] = $plugin['name'];
213
+
214
+ array_multisort( $sorted, SORT_ASC, $this->plugins ); // Sort plugins alphabetically by name
215
+
216
+ add_action( 'admin_menu', array( &$this, 'admin_menu' ) );
217
+ add_action( 'admin_head', array( &$this, 'dismiss' ) );
218
+ add_filter( 'install_plugin_complete_actions', array( &$this, 'actions' ) );
219
+
220
+ /** Load admin bar in the header to remove flash when installing plugins */
221
+ if ( $this->is_tgmpa_page() ) {
222
+ remove_action( 'wp_footer', 'wp_admin_bar_render', 1000 );
223
+ remove_action( 'admin_footer', 'wp_admin_bar_render', 1000 );
224
+ add_action( 'wp_head', 'wp_admin_bar_render', 1000 );
225
+ add_action( 'admin_head', 'wp_admin_bar_render', 1000 );
226
+ }
227
+
228
+ if ( $this->has_notices ) {
229
+ add_action( 'admin_notices', array( &$this, 'notices' ) );
230
+ add_action( 'admin_init', array( &$this, 'admin_init' ), 1 );
231
+ add_action( 'admin_enqueue_scripts', array( &$this, 'thickbox' ) );
232
+ add_action( 'switch_theme', array( &$this, 'update_dismiss' ) );
233
+ }
234
+
235
+ /** Setup the force activation hook */
236
+ foreach ( $this->plugins as $plugin ) {
237
+ if ( isset( $plugin['force_activation'] ) && true === $plugin['force_activation'] ) {
238
+ add_action( 'admin_init', array( &$this, 'force_activation' ) );
239
+ break;
240
+ }
241
+ }
242
+
243
+ /** Setup the force deactivation hook */
244
+ foreach ( $this->plugins as $plugin ) {
245
+ if ( isset( $plugin['force_deactivation'] ) && true === $plugin['force_deactivation'] ) {
246
+ add_action( 'switch_theme', array( &$this, 'force_deactivation' ) );
247
+ break;
248
+ }
249
+ }
250
+ }
251
+
252
+ }
253
+
254
+ /**
255
+ * Handles calls to show plugin information via links in the notices.
256
+ *
257
+ * We get the links in the admin notices to point to the TGMPA page, rather
258
+ * than the typical plugin-install.php file, so we can prepare everything
259
+ * beforehand.
260
+ *
261
+ * WP doesn't make it easy to show the plugin information in the thickbox -
262
+ * here we have to require a file that includes a function that does the
263
+ * main work of displaying it, enqueue some styles, set up some globals and
264
+ * finally call that function before exiting.
265
+ *
266
+ * Down right easy once you know how...
267
+ *
268
+ * @since 2.1.0
269
+ *
270
+ * @global string $tab Used as iframe div class names, helps with styling
271
+ * @global string $body_id Used as the iframe body ID, helps with styling
272
+ * @return null Returns early if not the TGMPA page.
273
+ */
274
+ public function admin_init() {
275
+
276
+ if ( ! $this->is_tgmpa_page() )
277
+ return;
278
+
279
+ if ( isset( $_REQUEST['tab'] ) && 'plugin-information' == $_REQUEST['tab'] ) {
280
+ require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; // Need for install_plugin_information()
281
+
282
+ wp_enqueue_style( 'plugin-install' );
283
+
284
+ global $tab, $body_id;
285
+ $body_id = $tab = 'plugin-information';
286
+
287
+ install_plugin_information();
288
+
289
+ exit;
290
+ }
291
+
292
+ }
293
+
294
+ /**
295
+ * Enqueues thickbox scripts/styles for plugin info.
296
+ *
297
+ * Thickbox is not automatically included on all admin pages, so we must
298
+ * manually enqueue it for those pages.
299
+ *
300
+ * Thickbox is only loaded if the user has not dismissed the admin
301
+ * notice or if there are any plugins left to install and activate.
302
+ *
303
+ * @since 2.1.0
304
+ */
305
+ public function thickbox() {
306
+
307
+ if ( ! get_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice', true ) )
308
+ add_thickbox();
309
+
310
+ }
311
+
312
+ /**
313
+ * Adds submenu page under 'Appearance' tab.
314
+ *
315
+ * This method adds the submenu page letting users know that a required
316
+ * plugin needs to be installed.
317
+ *
318
+ * This page disappears once the plugin has been installed and activated.
319
+ *
320
+ * @since 1.0.0
321
+ *
322
+ * @see TGM_Plugin_Activation::init()
323
+ * @see TGM_Plugin_Activation::install_plugins_page()
324
+ */
325
+ public function admin_menu() {
326
+
327
+ // Make sure privileges are correct to see the page
328
+ if ( ! current_user_can( 'install_plugins' ) )
329
+ return;
330
+
331
+ $this->populate_file_path();
332
+
333
+ foreach ( $this->plugins as $plugin ) {
334
+ if ( ! is_plugin_active( $plugin['file_path'] ) ) {
335
+ add_submenu_page(
336
+ $this->parent_menu_slug, // Parent menu slug
337
+ $this->strings['page_title'], // Page title
338
+ $this->strings['menu_title'], // Menu title
339
+ 'edit_theme_options', // Capability
340
+ $this->menu, // Menu slug
341
+ array( &$this, 'install_plugins_page' ) // Callback
342
+ );
343
+ break;
344
+ }
345
+ }
346
+
347
+ }
348
+
349
+ /**
350
+ * Echoes plugin installation form.
351
+ *
352
+ * This method is the callback for the admin_menu method function.
353
+ * This displays the admin page and form area where the user can select to install and activate the plugin.
354
+ *
355
+ * @since 1.0.0
356
+ *
357
+ * @return null Aborts early if we're processing a plugin installation action
358
+ */
359
+ public function install_plugins_page() {
360
+
361
+ /** Store new instance of plugin table in object */
362
+ $plugin_table = new TGMPA_List_Table;
363
+
364
+ /** Return early if processing a plugin installation action */
365
+ if ( isset( $_POST[sanitize_key( 'action' )] ) && 'tgmpa-bulk-install' == $_POST[sanitize_key( 'action' )] && $plugin_table->process_bulk_actions() || $this->do_plugin_install() )
366
+ return;
367
+
368
+ ?>
369
+ <div class="tgmpa wrap">
370
+
371
+ <?php screen_icon( apply_filters( 'tgmpa_default_screen_icon', 'themes' ) ); ?>
372
+ <h2><?php echo esc_html( get_admin_page_title() ); ?></h2>
373
+ <?php $plugin_table->prepare_items(); ?>
374
+
375
+ <?php if ( isset( $this->message ) ) _e( wp_kses_post( $this->message ), $this->domain ); ?>
376
+
377
+ <form id="tgmpa-plugins" action="" method="post">
378
+ <input type="hidden" name="tgmpa-page" value="<?php echo $this->menu; ?>" />
379
+ <?php $plugin_table->display(); ?>
380
+ </form>
381
+
382
+ </div>
383
+ <?php
384
+
385
+ }
386
+
387
+ /**
388
+ * Installs a plugin or activates a plugin depending on the hover
389
+ * link clicked by the user.
390
+ *
391
+ * Checks the $_GET variable to see which actions have been
392
+ * passed and responds with the appropriate method.
393
+ *
394
+ * Uses WP_Filesystem to process and handle the plugin installation
395
+ * method.
396
+ *
397
+ * @since 1.0.0
398
+ *
399
+ * @uses WP_Filesystem
400
+ * @uses WP_Error
401
+ * @uses WP_Upgrader
402
+ * @uses Plugin_Upgrader
403
+ * @uses Plugin_Installer_Skin
404
+ *
405
+ * @return boolean True on success, false on failure
406
+ */
407
+ protected function do_plugin_install() {
408
+
409
+ /** All plugin information will be stored in an array for processing */
410
+ $plugin = array();
411
+
412
+ /** Checks for actions from hover links to process the installation */
413
+ if ( isset( $_GET[sanitize_key( 'plugin' )] ) && ( isset( $_GET[sanitize_key( 'tgmpa-install' )] ) && 'install-plugin' == $_GET[sanitize_key( 'tgmpa-install' )] ) ) {
414
+ check_admin_referer( 'tgmpa-install' );
415
+
416
+ $plugin['name'] = $_GET[sanitize_key( 'plugin_name' )]; // Plugin name
417
+ $plugin['slug'] = $_GET[sanitize_key( 'plugin' )]; // Plugin slug
418
+ $plugin['source'] = $_GET[sanitize_key( 'plugin_source' )]; // Plugin source
419
+
420
+ /** Pass all necessary information via URL if WP_Filesystem is needed */
421
+ $url = wp_nonce_url(
422
+ add_query_arg(
423
+ array(
424
+ 'page' => $this->menu,
425
+ 'plugin' => $plugin['slug'],
426
+ 'plugin_name' => $plugin['name'],
427
+ 'plugin_source' => $plugin['source'],
428
+ 'tgmpa-install' => 'install-plugin',
429
+ ),
430
+ admin_url( $this->parent_url_slug )
431
+ ),
432
+ 'tgmpa-install'
433
+ );
434
+ $method = ''; // Leave blank so WP_Filesystem can populate it as necessary
435
+ $fields = array( sanitize_key( 'tgmpa-install' ) ); // Extra fields to pass to WP_Filesystem
436
+
437
+ if ( false === ( $creds = request_filesystem_credentials( $url, $method, false, false, $fields ) ) )
438
+ return true;
439
+
440
+ if ( ! WP_Filesystem( $creds ) ) {
441
+ request_filesystem_credentials( $url, $method, true, false, $fields ); // Setup WP_Filesystem
442
+ return true;
443
+ }
444
+
445
+ require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; // Need for plugins_api
446
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; // Need for upgrade classes
447
+
448
+ $api = plugins_api( 'plugin_information', array( 'slug' => $plugin['slug'], 'fields' => array( 'sections' => false ) ) );
449
+
450
+ if ( is_wp_error( $api ) )
451
+ wp_die( $this->strings['oops'] . var_dump( $api ) );
452
+
453
+ /** Set plugin source to WordPress API link if available */
454
+ if ( isset( $plugin['source'] ) && 'repo' == $plugin['source'] && isset( $api->download_link ) )
455
+ $plugin['source'] = $api->download_link;
456
+
457
+ /** Set type, based on whether the source starts with http:// or https:// */
458
+ $type = preg_match( '|^http(s)?://|', $plugin['source'] ) ? 'web' : 'upload';
459
+
460
+ /** Prep variables for Plugin_Installer_Skin class */
461
+ $title = sprintf( $this->strings['installing'], $plugin['name'] );
462
+ $url = add_query_arg( array( 'action' => 'install-plugin', 'plugin' => $plugin['slug'] ), 'update.php' );
463
+ if ( isset( $_GET['from'] ) )
464
+ $url .= add_query_arg( 'from', urlencode( stripslashes( $_GET['from'] ) ), $url );
465
+
466
+ $nonce = 'install-plugin_' . $plugin['slug'];
467
+
468
+ /** Prefix a default path to pre-packaged plugins */
469
+ $source = ( 'upload' == $type ) ? $this->default_path . $plugin['source'] : $plugin['source'];
470
+
471
+ /** Create a new instance of Plugin_Upgrader */
472
+ $upgrader = new Plugin_Upgrader( $skin = new Plugin_Installer_Skin( compact( 'type', 'title', 'url', 'nonce', 'plugin', 'api' ) ) );
473
+
474
+ /** Perform the action and install the plugin from the $source urldecode() */
475
+ $upgrader->install( $source );
476
+
477
+ /** Flush plugins cache so we can make sure that the installed plugins list is always up to date */
478
+ wp_cache_flush();
479
+
480
+ /** Only activate plugins if the config option is set to true */
481
+ if ( $this->is_automatic ) {
482
+ $plugin_activate = $upgrader->plugin_info(); // Grab the plugin info from the Plugin_Upgrader method
483
+ $activate = activate_plugin( $plugin_activate ); // Activate the plugin
484
+ $this->populate_file_path(); // Re-populate the file path now that the plugin has been installed and activated
485
+
486
+ if ( is_wp_error( $activate ) ) {
487
+ echo '<div id="message" class="error"><p>' . $activate->get_error_message() . '</p></div>';
488
+ echo '<p><a href="' . add_query_arg( 'page', $this->menu, admin_url( $this->parent_url_slug ) ) . '" title="' . esc_attr( $this->strings['return'] ) . '" target="_parent">' . __( 'Return to Required Plugins Installer', $this->domain ) . '</a></p>';
489
+ return true; // End it here if there is an error with automatic activation
490
+ }
491
+ else {
492
+ echo '<p>' . $this->strings['plugin_activated'] . '</p>';
493
+ }
494
+ }
495
+
496
+ /** Display message based on if all plugins are now active or not */
497
+ $complete = array();
498
+ foreach ( $this->plugins as $plugin ) {
499
+ if ( ! is_plugin_active( $plugin['file_path'] ) ) {
500
+ echo '<p><a href="' . add_query_arg( 'page', $this->menu, admin_url( $this->parent_url_slug ) ) . '" title="' . esc_attr( $this->strings['return'] ) . '" target="_parent">' . __( $this->strings['return'], $this->domain ) . '</a></p>';
501
+ $complete[] = $plugin;
502
+ break;
503
+ }
504
+ /** Nothing to store */
505
+ else {
506
+ $complete[] = '';
507
+ }
508
+ }
509
+
510
+ /** Filter out any empty entries */
511
+ $complete = array_filter( $complete );
512
+
513
+ /** All plugins are active, so we display the complete string and hide the plugin menu */
514
+ if ( empty( $complete ) ) {
515
+ echo '<p>' . sprintf( $this->strings['complete'], '<a href="' . admin_url() . '" title="' . __( 'Return to the Dashboard', $this->domain ) . '">' . __( 'Return to the Dashboard', $this->domain ) . '</a>' ) . '</p>';
516
+ echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
517
+ }
518
+
519
+ return true;
520
+ }
521
+ /** Checks for actions from hover links to process the activation */
522
+ elseif ( isset( $_GET[sanitize_key( 'plugin' )] ) && ( isset( $_GET[sanitize_key( 'tgmpa-activate' )] ) && 'activate-plugin' == $_GET[sanitize_key( 'tgmpa-activate' )] ) ) {
523
+ check_admin_referer( 'tgmpa-activate', 'tgmpa-activate-nonce' );
524
+
525
+ /** Populate $plugin array with necessary information */
526
+ $plugin['name'] = $_GET[sanitize_key( 'plugin_name' )];
527
+ $plugin['slug'] = $_GET[sanitize_key( 'plugin' )];
528
+ $plugin['source'] = $_GET[sanitize_key( 'plugin_source' )];
529
+
530
+ $plugin_data = get_plugins( '/' . $plugin['slug'] ); // Retrieve all plugins
531
+ $plugin_file = array_keys( $plugin_data ); // Retrieve all plugin files from installed plugins
532
+ $plugin_to_activate = $plugin['slug'] . '/' . $plugin_file[0]; // Match plugin slug with appropriate plugin file
533
+ $activate = activate_plugin( $plugin_to_activate ); // Activate the plugin
534
+
535
+ if ( is_wp_error( $activate ) ) {
536
+ echo '<div id="message" class="error"><p>' . $activate->get_error_message() . '</p></div>';
537
+ echo '<p><a href="' . add_query_arg( 'page', $this->menu, admin_url( $this->parent_url_slug ) ) . '" title="' . esc_attr( $this->strings['return'] ) . '" target="_parent">' . __( $this->strings['return'], $this->domain ) . '</a></p>';
538
+ return true; // End it here if there is an error with activation
539
+ }
540
+ else {
541
+ /** Make sure message doesn't display again if bulk activation is performed immediately after a single activation */
542
+ if ( ! isset( $_POST[sanitize_key( 'action' )] ) ) {
543
+ $msg = sprintf( __( 'The following plugin was activated successfully: %s.', $this->domain ), '<strong>' . $plugin['name'] . '</strong>' );
544
+ echo '<div id="message" class="updated"><p>' . $msg . '</p></div>';
545
+ }
546
+ }
547
+ }
548
+
549
+ return false;
550
+
551
+ }
552
+
553
+ /**
554
+ * Echoes required plugin notice.
555
+ *
556
+ * Outputs a message telling users that a specific plugin is required for
557
+ * their theme. If appropriate, it includes a link to the form page where
558
+ * users can install and activate the plugin.
559
+ *
560
+ * @since 1.0.0
561
+ *
562
+ * @global object $current_screen
563
+ * @return null Returns early if we're on the Install page
564
+ */
565
+ public function notices() {
566
+
567
+ global $current_screen;
568
+
569
+ /** Remove nag on the install page */
570
+ if ( $this->is_tgmpa_page() )
571
+ return;
572
+
573
+ $installed_plugins = get_plugins(); // Retrieve a list of all the plugins
574
+ $this->populate_file_path();
575
+
576
+ $message = array(); // Store the messages in an array to be outputted after plugins have looped through
577
+ $install_link = false; // Set to false, change to true in loop if conditions exist, used for action link 'install'
578
+ $install_link_count = 0; // Used to determine plurality of install action link text
579
+ $activate_link = false; // Set to false, change to true in loop if conditions exist, used for action link 'activate'
580
+ $activate_link_count = 0; // Used to determine plurality of activate action link text
581
+
582
+ foreach ( $this->plugins as $plugin ) {
583
+ /** If the plugin is installed and active, check for minimum version argument before moving forward */
584
+ if ( is_plugin_active( $plugin['file_path'] ) ) {
585
+ /** A minimum version has been specified */
586
+ if ( isset( $plugin['version'] ) ) {
587
+ if ( isset( $installed_plugins[$plugin['file_path']]['Version'] ) ) {
588
+ /** If the current version is less than the minimum required version, we display a message */
589
+ if ( version_compare( $installed_plugins[$plugin['file_path']]['Version'], $plugin['version'], '<' ) ) {
590
+ if ( current_user_can( 'install_plugins' ) )
591
+ $message['notice_ask_to_update'][] = $plugin['name'];
592
+ else
593
+ $message['notice_cannot_update'][] = $plugin['name'];
594
+ }
595
+ }
596
+ /** Can't find the plugin, so iterate to the next condition */
597
+ else {
598
+ continue;
599
+ }
600
+ }
601
+ /** No minimum version specified, so iterate over the plugin */
602
+ else {
603
+ continue;
604
+ }
605
+ }
606
+
607
+ /** Not installed */
608
+ if ( ! isset( $installed_plugins[$plugin['file_path']] ) ) {
609
+ $install_link = true; // We need to display the 'install' action link
610
+ $install_link_count++; // Increment the install link count
611
+ if ( current_user_can( 'install_plugins' ) ) {
612
+ if ( $plugin['required'] )
613
+ $message['notice_can_install_required'][] = $plugin['name'];
614
+ /** This plugin is only recommended */
615
+ else
616
+ $message['notice_can_install_recommended'][] = $plugin['name'];
617
+ }
618
+ /** Need higher privileges to install the plugin */
619
+ else {
620
+ $message['notice_cannot_install'][] = $plugin['name'];
621
+ }
622
+ }
623
+ /** Installed but not active */
624
+ elseif ( is_plugin_inactive( $plugin['file_path'] ) ) {
625
+ $activate_link = true; // We need to display the 'activate' action link
626
+ $activate_link_count++; // Increment the activate link count
627
+ if ( current_user_can( 'activate_plugins' ) ) {
628
+ if ( $plugin['required'] )
629
+ $message['notice_can_activate_required'][] = $plugin['name'];
630
+ /** This plugin is only recommended */
631
+ else {
632
+ $message['notice_can_activate_recommended'][] = $plugin['name'];
633
+ }
634
+ }
635
+ /** Need higher privileges to activate the plugin */
636
+ else {
637
+ $message['notice_cannot_activate'][] = $plugin['name'];
638
+ }
639
+ }
640
+ }
641
+
642
+ /** Only process the nag messages if the user has not dismissed them already */
643
+ if ( ! get_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice', true ) ) {
644
+ /** If we have notices to display, we move forward */
645
+ if ( ! empty( $message ) ) {
646
+ krsort( $message ); // Sort messages
647
+ $rendered = ''; // Display all nag messages as strings
648
+
649
+ /** Grab all plugin names */
650
+ foreach ( $message as $type => $plugin_groups ) {
651
+ $linked_plugin_groups = array();
652
+
653
+ /** Count number of plugins in each message group to calculate singular/plural message */
654
+ $count = count( $plugin_groups );
655
+
656
+ /** Loop through the plugin names to make the ones pulled from the .org repo linked */
657
+ foreach ( $plugin_groups as $plugin_group_single_name ) {
658
+ $external_url = $this->_get_plugin_data_from_name( $plugin_group_single_name, 'external_url' );
659
+ $source = $this->_get_plugin_data_from_name( $plugin_group_single_name, 'source' );
660
+
661
+ if ( $external_url && preg_match( '|^http(s)?://|', $external_url ) ) {
662
+ $linked_plugin_groups[] = '<a href="' . esc_url( $external_url ) . '" title="' . $plugin_group_single_name . '" target="_blank">' . $plugin_group_single_name . '</a>';
663
+ }
664
+ elseif ( ! $source || preg_match( '|^http://wordpress.org/extend/plugins/|', $source ) ) {
665
+ $url = add_query_arg(
666
+ array(
667
+ 'tab' => 'plugin-information',
668
+ 'plugin' => $this->_get_plugin_data_from_name( $plugin_group_single_name ),
669
+ 'TB_iframe' => 'true',
670
+ 'width' => '640',
671
+ 'height' => '500',
672
+ ),
673
+ admin_url( 'plugin-install.php' )
674
+ );
675
+
676
+ $linked_plugin_groups[] = '<a href="' . esc_url( $url ) . '" class="thickbox" title="' . $plugin_group_single_name . '">' . $plugin_group_single_name . '</a>';
677
+ }
678
+ else {
679
+ $linked_plugin_groups[] = $plugin_group_single_name; // No hyperlink
680
+ }
681
+
682
+ if ( isset( $linked_plugin_groups ) && (array) $linked_plugin_groups )
683
+ $plugin_groups = $linked_plugin_groups;
684
+ }
685
+
686
+ $last_plugin = array_pop( $plugin_groups ); // Pop off last name to prep for readability
687
+ $imploded = empty( $plugin_groups ) ? '<em>' . $last_plugin . '</em>' : '<em>' . ( implode( ', ', $plugin_groups ) . '</em> and <em>' . $last_plugin . '</em>' );
688
+
689
+ $rendered .= '<p>' . sprintf( translate_nooped_plural( $this->strings[$type], $count, $this->domain ), $imploded, $count ) . '</p>'; // All messages now stored
690
+ }
691
+
692
+ /** Setup variables to determine if action links are needed */
693
+ $show_install_link = $install_link ? '<a href="' . add_query_arg( 'page', $this->menu, admin_url( $this->parent_url_slug ) ) . '">' . translate_nooped_plural( $this->strings['install_link'], $install_link_count, $this->domain ) . '</a>' : '';
694
+ $show_activate_link = $activate_link ? '<a href="' . admin_url( 'plugins.php' ) . '">' . translate_nooped_plural( $this->strings['activate_link'], $activate_link_count, $this->domain ) . '</a>' : '';
695
+
696
+ /** Define all of the action links */
697
+ $action_links = apply_filters(
698
+ 'tgmpa_notice_action_links',
699
+ array(
700
+ 'install' => ( current_user_can( 'install_plugins' ) ) ? $show_install_link : '',
701
+ 'activate' => ( current_user_can( 'activate_plugins' ) ) ? $show_activate_link : '',
702
+ 'dismiss' => '<a class="dismiss-notice" href="' . add_query_arg( 'tgmpa-dismiss', 'dismiss_admin_notices' ) . '" target="_parent">' . __( 'Dismiss this notice', $this->domain ) . '</a>',
703
+ )
704
+ );
705
+
706
+ $action_links = array_filter( $action_links ); // Remove any empty array items
707
+ if ( $action_links )
708
+ $rendered .= '<p>' . implode( ' | ', $action_links ) . '</p>';
709
+
710
+ /** Register the nag messages and prepare them to be processed */
711
+ add_settings_error( 'tgmpa', 'tgmpa', $rendered, 'updated' );
712
+ }
713
+ }
714
+
715
+ /** Admin options pages already output settings_errors, so this is to avoid duplication */
716
+ if ( 'options-general' !== $current_screen->parent_base )
717
+ settings_errors( 'tgmpa' );
718
+
719
+ }
720
+
721
+ /**
722
+ * Add dismissable admin notices.
723
+ *
724
+ * Appends a link to the admin nag messages. If clicked, the admin notice disappears and no longer is visible to users.
725
+ *
726
+ * @since 2.1.0
727
+ */
728
+ public function dismiss() {
729
+
730
+ if ( isset( $_GET[sanitize_key( 'tgmpa-dismiss' )] ) )
731
+ update_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice', 1 );
732
+
733
+ }
734
+
735
+ /**
736
+ * Add individual plugin to our collection of plugins.
737
+ *
738
+ * If the required keys are not set, the plugin is not added.
739
+ *
740
+ * @since 2.0.0
741
+ *
742
+ * @param array $plugin Array of plugin arguments.
743
+ */
744
+ public function register( $plugin ) {
745
+
746
+ if ( ! isset( $plugin['slug'] ) || ! isset( $plugin['name'] ) )
747
+ return;
748
+
749
+ $this->plugins[] = $plugin;
750
+
751
+ }
752
+
753
+ /**
754
+ * Amend default configuration settings.
755
+ *
756
+ * @since 2.0.0
757
+ *
758
+ * @param array $config
759
+ */
760
+ public function config( $config ) {
761
+
762
+ $keys = array( 'default_path', 'parent_menu_slug', 'parent_url_slug', 'domain', 'has_notices', 'menu', 'is_automatic', 'message', 'strings' );
763
+
764
+ foreach ( $keys as $key ) {
765
+ if ( isset( $config[$key] ) ) {
766
+ if ( is_array( $config[$key] ) ) {
767
+ foreach ( $config[$key] as $subkey => $value )
768
+ $this->{$key}[$subkey] = $value;
769
+ } else {
770
+ $this->$key = $config[$key];
771
+ }
772
+ }
773
+ }
774
+
775
+ }
776
+
777
+ /**
778
+ * Amend action link after plugin installation.
779
+ *
780
+ * @since 2.0.0
781
+ *
782
+ * @param array $install_actions Existing array of actions
783
+ * @return array Amended array of actions
784
+ */
785
+ public function actions( $install_actions ) {
786
+
787
+ /** Remove action links on the TGMPA install page */
788
+ if ( $this->is_tgmpa_page() )
789
+ return false;
790
+
791
+ return $install_actions;
792
+
793
+ }
794
+
795
+ /**
796
+ * Set file_path key for each installed plugin.
797
+ *
798
+ * @since 2.1.0
799
+ */
800
+ public function populate_file_path() {
801
+
802
+ /** Add file_path key for all plugins */
803
+ foreach ( $this->plugins as $plugin => $values )
804
+ $this->plugins[$plugin]['file_path'] = $this->_get_plugin_basename_from_slug( $values['slug'] );
805
+
806
+ }
807
+
808
+ /**
809
+ * Helper function to extract the file path of the plugin file from the
810
+ * plugin slug, if the plugin is installed.
811
+ *
812
+ * @since 2.0.0
813
+ *
814
+ * @param string $slug Plugin slug (typically folder name) as provided by the developer
815
+ * @return string Either file path for plugin if installed, or just the plugin slug
816
+ */
817
+ protected function _get_plugin_basename_from_slug( $slug ) {
818
+
819
+ $keys = array_keys( get_plugins() );
820
+
821
+ foreach ( $keys as $key ) {
822
+ if ( preg_match( '|^' . $slug .'|', $key ) )
823
+ return $key;
824
+ }
825
+
826
+ return $slug;
827
+
828
+ }
829
+
830
+ /**
831
+ * Retrieve plugin data, given the plugin name.
832
+ *
833
+ * Loops through the registered plugins looking for $name. If it finds it,
834
+ * it returns the $data from that plugin. Otherwise, returns false.
835
+ *
836
+ * @since 2.1.0
837
+ *
838
+ * @param string $name Name of the plugin, as it was registered
839
+ * @param string $data Optional. Array key of plugin data to return. Default is slug
840
+ * @return string|boolean Plugin slug if found, false otherwise.
841
+ */
842
+ protected function _get_plugin_data_from_name( $name, $data = 'slug' ) {
843
+
844
+ foreach ( $this->plugins as $plugin => $values ) {
845
+ if ( $name == $values['name'] && isset( $values[$data] ) )
846
+ return $values[$data];
847
+ }
848
+
849
+ return false;
850
+
851
+ }
852
+
853
+ /**
854
+ * Determine if we're on the TGMPA Install page.
855
+ *
856
+ * We use $current_screen when it is available, and a slightly less ideal
857
+ * conditional when it isn't (like when displaying the plugin information
858
+ * thickbox).
859
+ *
860
+ * @since 2.1.0
861
+ *
862
+ * @global object $current_screen
863
+ * @return boolean True when on the TGMPA page, false otherwise.
864
+ */
865
+ protected function is_tgmpa_page() {
866
+
867
+ global $current_screen;
868
+
869
+ if ( ! is_null( $current_screen ) && $this->parent_menu_slug == $current_screen->parent_file && isset( $_GET['page'] ) && $this->menu === $_GET['page'] )
870
+ return true;
871
+
872
+ if ( isset( $_GET['page'] ) && $this->menu === $_GET['page'] )
873
+ return true;
874
+
875
+ return false;
876
+
877
+ }
878
+
879
+ /**
880
+ * Delete dismissable nag option when theme is switched.
881
+ *
882
+ * This ensures that the user is again reminded via nag of required
883
+ * and/or recommended plugins if they re-activate the theme.
884
+ *
885
+ * @since 2.1.1
886
+ */
887
+ public function update_dismiss() {
888
+
889
+ delete_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice' );
890
+
891
+ }
892
+
893
+ /**
894
+ * Forces plugin activation if the parameter 'force_activation' is
895
+ * set to true.
896
+ *
897
+ * This allows theme authors to specify certain plugins that must be
898
+ * active at all times while using the current theme.
899
+ *
900
+ * Please take special care when using this parameter as it has the
901
+ * potential to be harmful if not used correctly. Setting this parameter
902
+ * to true will not allow the specified plugin to be deactivated unless
903
+ * the user switches themes.
904
+ *
905
+ * @since 2.2.0
906
+ */
907
+ public function force_activation() {
908
+
909
+ /** Set file_path parameter for any installed plugins */
910
+ $this->populate_file_path();
911
+
912
+ $installed_plugins = get_plugins();
913
+
914
+ foreach ( $this->plugins as $plugin ) {
915
+ /** Oops, plugin isn't there so iterate to next condition */
916
+ if ( isset( $plugin['force_activation'] ) && $plugin['force_activation'] && ! isset( $installed_plugins[$plugin['file_path']] ) )
917
+ continue;
918
+ /** There we go, activate the plugin */
919
+ elseif ( isset( $plugin['force_activation'] ) && $plugin['force_activation'] && is_plugin_inactive( $plugin['file_path'] ) )
920
+ activate_plugin( $plugin['file_path'] );
921
+ }
922
+
923
+ }
924
+
925
+ /**
926
+ * Forces plugin deactivation if the parameter 'force_deactivation'
927
+ * is set to true.
928
+ *
929
+ * This allows theme authors to specify certain plugins that must be
930
+ * deactived upon switching from the current theme to another.
931
+ *
932
+ * Please take special care when using this parameter as it has the
933
+ * potential to be harmful if not used correctly.
934
+ *
935
+ * @since 2.2.0
936
+ */
937
+ public function force_deactivation() {
938
+
939
+ /** Set file_path parameter for any installed plugins */
940
+ $this->populate_file_path();
941
+
942
+ foreach ( $this->plugins as $plugin ) {
943
+ /** Only proceed forward if the paramter is set to true and plugin is active */
944
+ if ( isset( $plugin['force_deactivation'] ) && $plugin['force_deactivation'] && is_plugin_active( $plugin['file_path'] ) )
945
+ deactivate_plugins( $plugin['file_path'] );
946
+ }
947
+
948
+ }
949
+
950
+ }
951
+ }
952
+
953
+ /** Create a new instance of the class */
954
+ new TGM_Plugin_Activation;
955
+
956
+ if ( ! function_exists( 'tgmpa' ) ) {
957
+ /**
958
+ * Helper function to register a collection of required plugins.
959
+ *
960
+ * @since 2.0.0
961
+ * @api
962
+ *
963
+ * @param array $plugins An array of plugin arrays
964
+ * @param array $config Optional. An array of configuration values
965
+ */
966
+ function tgmpa( $plugins, $config = array() ) {
967
+
968
+ foreach ( $plugins as $plugin )
969
+ TGM_Plugin_Activation::$instance->register( $plugin );
970
+
971
+ if ( $config )
972
+ TGM_Plugin_Activation::$instance->config( $config );
973
+
974
+ }
975
+ }
976
+
977
+ /**
978
+ * WP_List_Table isn't always available. If it isn't available,
979
+ * we load it here.
980
+ *
981
+ * @since 2.2.0
982
+ */
983
+ if ( ! class_exists( 'WP_List_Table' ) )
984
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
985
+
986
+ if ( ! class_exists( 'TGMPA_List_Table' ) ) {
987
+ /**
988
+ * List table class for handling plugins.
989
+ *
990
+ * Extends the WP_List_Table class to provide a future-compatible
991
+ * way of listing out all required/recommended plugins.
992
+ *
993
+ * Gives users an interface similar to the Plugin Administration
994
+ * area with similar (albeit stripped down) capabilities.
995
+ *
996
+ * This class also allows for the bulk install of plugins.
997
+ *
998
+ * @since 2.2.0
999
+ *
1000
+ * @package TGM-Plugin-Activation
1001
+ * @author Thomas Griffin <thomas@thomasgriffinmedia.com>
1002
+ * @author Gary Jones <gamajo@gamajo.com>
1003
+ */
1004
+ class TGMPA_List_Table extends WP_List_Table {
1005
+
1006
+ /**
1007
+ * References parent constructor and sets defaults for class.
1008
+ *
1009
+ * The constructor also grabs a copy of $instance from the TGMPA class
1010
+ * and stores it in the global object TGM_Plugin_Activation::$instance.
1011
+ *
1012
+ * @since 2.2.0
1013
+ *
1014
+ * @global unknown $status
1015
+ * @global string $page
1016
+ */
1017
+ public function __construct() {
1018
+
1019
+ global $status, $page;
1020
+
1021
+ parent::__construct(
1022
+ array(
1023
+ 'singular' => 'plugin',
1024
+ 'plural' => 'plugins',
1025
+ 'ajax' => false,
1026
+ )
1027
+ );
1028
+
1029
+ }
1030
+
1031
+ /**
1032
+ * Gathers and renames all of our plugin information to be used by
1033
+ * WP_List_Table to create our table.
1034
+ *
1035
+ * @since 2.2.0
1036
+ *
1037
+ * @return array $table_data Information for use in table
1038
+ */
1039
+ protected function _gather_plugin_data() {
1040
+
1041
+ /** Load thickbox for plugin links */
1042
+ TGM_Plugin_Activation::$instance->admin_init();
1043
+ TGM_Plugin_Activation::$instance->thickbox();
1044
+
1045
+ /** Prep variables for use and grab list of all installed plugins */
1046
+ $table_data = array();
1047
+ $i = 0;
1048
+ $installed_plugins = get_plugins();
1049
+
1050
+ foreach ( TGM_Plugin_Activation::$instance->plugins as $plugin ) {
1051
+ if ( is_plugin_active( $plugin['file_path'] ) )
1052
+ continue; // No need to display plugins if they are installed and activated
1053
+
1054
+ $table_data[$i]['sanitized_plugin'] = $plugin['name'];
1055
+ $table_data[$i]['slug'] = $this->_get_plugin_data_from_name( $plugin['name'] );
1056
+
1057
+ $external_url = $this->_get_plugin_data_from_name( $plugin['name'], 'external_url' );
1058
+ $source = $this->_get_plugin_data_from_name( $plugin['name'], 'source' );
1059
+
1060
+ if ( $external_url && preg_match( '|^http(s)?://|', $external_url ) ) {
1061
+ $table_data[$i]['plugin'] = '<strong><a href="' . esc_url( $external_url ) . '" title="' . $plugin['name'] . '" target="_blank">' . $plugin['name'] . '</a></strong>';
1062
+ }
1063
+ elseif ( ! $source || preg_match( '|^http://wordpress.org/extend/plugins/|', $source ) ) {
1064
+ $url = add_query_arg(
1065
+ array(
1066
+ 'tab' => 'plugin-information',
1067
+ 'plugin' => $this->_get_plugin_data_from_name( $plugin['name'] ),
1068
+ 'TB_iframe' => 'true',
1069
+ 'width' => '640',
1070
+ 'height' => '500',
1071
+ ),
1072
+ admin_url( 'plugin-install.php' )
1073
+ );
1074
+
1075
+ $table_data[$i]['plugin'] = '<strong><a href="' . esc_url( $url ) . '" class="thickbox" title="' . $plugin['name'] . '">' . $plugin['name'] . '</a></strong>';
1076
+ }
1077
+ else {
1078
+ $table_data[$i]['plugin'] = '<strong>' . $plugin['name'] . '</strong>'; // No hyperlink
1079
+ }
1080
+
1081
+ if ( isset( $table_data[$i]['plugin'] ) && (array) $table_data[$i]['plugin'] )
1082
+ $plugin['name'] = $table_data[$i]['plugin'];
1083
+
1084
+ if ( isset( $plugin['external_url'] ) ) {
1085
+ /** The plugin is linked to an external source */
1086
+ $table_data[$i]['source'] = __( 'External Link', TGM_Plugin_Activation::$instance->domain );
1087
+ }
1088
+ elseif ( isset( $plugin['source'] ) ) {
1089
+ /** The plugin must be from a private repository */
1090
+ if ( preg_match( '|^http(s)?://|', $plugin['source'] ) )
1091
+ $table_data[$i]['source'] = __( 'Private Repository', TGM_Plugin_Activation::$instance->domain );
1092
+ /** The plugin is pre-packaged with the theme */
1093
+ else
1094
+ $table_data[$i]['source'] = __( 'Pre-Packaged', TGM_Plugin_Activation::$instance->domain );
1095
+ }
1096
+ /** The plugin is from the WordPress repository */
1097
+ else {
1098
+ $table_data[$i]['source'] = __( 'WordPress Repository', TGM_Plugin_Activation::$instance->domain );
1099
+ }
1100
+
1101
+ $table_data[$i]['type'] = $plugin['required'] ? __( 'Required', TGM_Plugin_Activation::$instance->domain ) : __( 'Recommended', TGM_Plugin_Activation::$instance->domain );
1102
+
1103
+ if ( ! isset( $installed_plugins[$plugin['file_path']] ) )
1104
+ $table_data[$i]['status'] = sprintf( '%1$s', __( 'Not Installed', TGM_Plugin_Activation::$instance->domain ) );
1105
+ elseif ( is_plugin_inactive( $plugin['file_path'] ) )
1106
+ $table_data[$i]['status'] = sprintf( '%1$s', __( 'Installed But Not Activated', TGM_Plugin_Activation::$instance->domain ) );
1107
+
1108
+ $table_data[$i]['file_path'] = $plugin['file_path'];
1109
+ $table_data[$i]['url'] = isset( $plugin['source'] ) ? $plugin['source'] : 'repo';
1110
+
1111
+ $i++;
1112
+ }
1113
+
1114
+ /** Sort plugins by Required/Recommended type and by alphabetical listing within each type */
1115
+ $resort = array();
1116
+ $req = array();
1117
+ $rec = array();
1118
+
1119
+ /** Grab all the plugin types */
1120
+ foreach ( $table_data as $plugin )
1121
+ $resort[] = $plugin['type'];
1122
+
1123
+ /** Sort each plugin by type */
1124
+ foreach ( $resort as $type )
1125
+ if ( 'Required' == $type )
1126
+ $req[] = $type;
1127
+ else
1128
+ $rec[] = $type;
1129
+
1130
+ /** Sort alphabetically each plugin type array, merge them and then sort in reverse (lists Required plugins first) */
1131
+ sort( $req );
1132
+ sort( $rec );
1133
+ array_merge( $resort, $req, $rec );
1134
+ array_multisort( $resort, SORT_DESC, $table_data );
1135
+
1136
+ return $table_data;
1137
+
1138
+ }
1139
+
1140
+ /**
1141
+ * Retrieve plugin data, given the plugin name. Taken from the
1142
+ * TGM_Plugin_Activation class.
1143
+ *
1144
+ * Loops through the registered plugins looking for $name. If it finds it,
1145
+ * it returns the $data from that plugin. Otherwise, returns false.
1146
+ *
1147
+ * @since 2.2.0
1148
+ *
1149
+ * @param string $name Name of the plugin, as it was registered
1150
+ * @param string $data Optional. Array key of plugin data to return. Default is slug
1151
+ * @return string|boolean Plugin slug if found, false otherwise
1152
+ */
1153
+ protected function _get_plugin_data_from_name( $name, $data = 'slug' ) {
1154
+
1155
+ foreach ( TGM_Plugin_Activation::$instance->plugins as $plugin => $values ) {
1156
+ if ( $name == $values['name'] && isset( $values[$data] ) )
1157
+ return $values[$data];
1158
+ }
1159
+
1160
+ return false;
1161
+
1162
+ }
1163
+
1164
+ /**
1165
+ * Create default columns to display important plugin information
1166
+ * like type, action and status.
1167
+ *
1168
+ * @since 2.2.0
1169
+ *
1170
+ * @param array $item
1171
+ * @param string $column_name
1172
+ */
1173
+ public function column_default( $item, $column_name ) {
1174
+
1175
+ switch ( $column_name ) {
1176
+ case 'source':
1177
+ case 'type':
1178
+ case 'status':
1179
+ return $item[$column_name];
1180
+ }
1181
+
1182
+ }
1183
+
1184
+ /**
1185
+ * Create default title column along with action links of 'Install'
1186
+ * and 'Activate'.
1187
+ *
1188
+ * @since 2.2.0
1189
+ *
1190
+ * @param array $item
1191
+ * @return string The action hover links
1192
+ */
1193
+ public function column_plugin( $item ) {
1194
+
1195
+ $installed_plugins = get_plugins();
1196
+
1197
+ /** No need to display any hover links */
1198
+ if ( is_plugin_active( $item['file_path'] ) )
1199
+ $actions = array();
1200
+
1201
+ /** We need to display the 'Install' hover link */
1202
+ if ( ! isset( $installed_plugins[$item['file_path']] ) ) {
1203
+ $actions = array(
1204
+ 'install' => sprintf(
1205
+ '<a href="%1$s" title="Install %2$s">Install</a>',
1206
+ wp_nonce_url(
1207
+ add_query_arg(
1208
+ array(
1209
+ 'page' => TGM_Plugin_Activation::$instance->menu,
1210
+ 'plugin' => $item['slug'],
1211
+ 'plugin_name' => $item['sanitized_plugin'],
1212
+ 'plugin_source' => $item['url'],
1213
+ 'tgmpa-install' => 'install-plugin',
1214
+ ),
1215
+ admin_url( TGM_Plugin_Activation::$instance->parent_url_slug )
1216
+ ),
1217
+ 'tgmpa-install'
1218
+ ),
1219
+ $item['sanitized_plugin']
1220
+ ),
1221
+ );
1222
+ }
1223
+ /** We need to display the 'Activate' hover link */
1224
+ elseif ( is_plugin_inactive( $item['file_path'] ) ) {
1225
+ $actions = array(
1226
+ 'activate' => sprintf(
1227
+ '<a href="%1$s" title="Activate %2$s">Activate</a>',
1228
+ add_query_arg(
1229
+ array(
1230
+ 'page' => TGM_Plugin_Activation::$instance->menu,
1231
+ 'plugin' => $item['slug'],
1232
+ 'plugin_name' => $item['sanitized_plugin'],
1233
+ 'plugin_source' => $item['url'],
1234
+ 'tgmpa-activate' => 'activate-plugin',
1235
+ 'tgmpa-activate-nonce' => wp_create_nonce( 'tgmpa-activate' ),
1236
+ ),
1237
+ admin_url( TGM_Plugin_Activation::$instance->parent_url_slug )
1238
+ ),
1239
+ $item['sanitized_plugin']
1240
+ ),
1241
+ );
1242
+ }
1243
+
1244
+ return sprintf( '%1$s %2$s', $item['plugin'], $this->row_actions( $actions ) );
1245
+
1246
+ }
1247
+
1248
+ /**
1249
+ * Required for bulk installing.
1250
+ *
1251
+ * Adds a checkbox for each plugin.
1252
+ *
1253
+ * @since 2.2.0
1254
+ *
1255
+ * @param array $item
1256
+ * @return string The input checkbox with all necessary info
1257
+ */
1258
+ public function column_cb( $item ) {
1259
+
1260
+ $value = $item['file_path'] . ',' . $item['url'] . ',' . $item['sanitized_plugin'];
1261
+ return sprintf( '<input type="checkbox" name="%1$s[]" value="%2$s" id="%3$s" />', $this->_args['singular'], $value, $item['sanitized_plugin'] );
1262
+
1263
+ }
1264
+
1265
+ /**
1266
+ * Sets default message within the plugins table if no plugins
1267
+ * are left for interaction.
1268
+ *
1269
+ * Hides the menu item to prevent the user from clicking and
1270
+ * getting a permissions error.
1271
+ *
1272
+ * @since 2.2.0
1273
+ */
1274
+ public function no_items() {
1275
+
1276
+ printf( __( 'No plugins to install or activate. <a href="%1$s" title="Return to the Dashboard">Return to the Dashboard</a>', TGM_Plugin_Activation::$instance->domain ), admin_url() );
1277
+ echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
1278
+
1279
+ }
1280
+
1281
+ /**
1282
+ * Output all the column information within the table.
1283
+ *
1284
+ * @since 2.2.0
1285
+ *
1286
+ * @return array $columns The column names
1287
+ */
1288
+ public function get_columns() {
1289
+
1290
+ $columns = array(
1291
+ 'cb' => '<input type="checkbox" />',
1292
+ 'plugin' => __( 'Plugin', TGM_Plugin_Activation::$instance->domain ),
1293
+ 'source' => __( 'Source', TGM_Plugin_Activation::$instance->domain ),
1294
+ 'type' => __( 'Type', TGM_Plugin_Activation::$instance->domain ),
1295
+ 'status' => __( 'Status', TGM_Plugin_Activation::$instance->domain )
1296
+ );
1297
+
1298
+ return $columns;
1299
+
1300
+ }
1301
+
1302
+ /**
1303
+ * Defines all types of bulk actions for handling
1304
+ * registered plugins.
1305
+ *
1306
+ * @since 2.2.0
1307
+ *
1308
+ * @return array $actions The bulk actions for the plugin install table
1309
+ */
1310
+ public function get_bulk_actions() {
1311
+
1312
+ $actions = array(
1313
+ 'tgmpa-bulk-install' => __( 'Install', TGM_Plugin_Activation::$instance->domain ),
1314
+ 'tgmpa-bulk-activate' => __( 'Activate', TGM_Plugin_Activation::$instance->domain ),
1315
+ );
1316
+
1317
+ return $actions;
1318
+
1319
+ }
1320
+
1321
+ /**
1322
+ * Processes bulk installation and activation actions.
1323
+ *
1324
+ * The bulk installation process looks either for the $_POST
1325
+ * information or for the plugin info within the $_GET variable if
1326
+ * a user has to use WP_Filesystem to enter their credentials.
1327
+ *
1328
+ * @since 2.2.0
1329
+ */
1330
+ public function process_bulk_actions() {
1331
+
1332
+ /** Bulk installation process */
1333
+ if ( 'tgmpa-bulk-install' === $this->current_action() ) {
1334
+ check_admin_referer( 'bulk-' . $this->_args['plural'] );
1335
+
1336
+ /** Prep variables to be populated */
1337
+ $plugins_to_install = array();
1338
+ $plugin_installs = array();
1339
+ $plugin_path = array();
1340
+ $plugin_name = array();
1341
+
1342
+ /** Look first to see if information has been passed via WP_Filesystem */
1343
+ if ( isset( $_GET[sanitize_key( 'plugins' )] ) )
1344
+ $plugins = explode( ',', stripslashes( $_GET[sanitize_key( 'plugins' )] ) );
1345
+ /** Looks like the user can use the direct method, take from $_POST */
1346
+ elseif ( isset( $_POST[sanitize_key( 'plugin' )] ) )
1347
+ $plugins = (array) $_POST[sanitize_key( 'plugin' )];
1348
+ /** Nothing has been submitted */
1349
+ else
1350
+ $plugins = array();
1351
+
1352
+ $a = 0; // Incremental variable
1353
+
1354
+ /** Grab information from $_POST if available */
1355
+ if ( isset( $_POST[sanitize_key( 'plugin' )] ) ) {
1356
+ foreach ( $plugins as $plugin_data )
1357
+ $plugins_to_install[] = explode( ',', $plugin_data );
1358
+
1359
+ foreach ( $plugins_to_install as $plugin_data ) {
1360
+ $plugin_installs[] = $plugin_data[0];
1361
+ $plugin_path[] = $plugin_data[1];
1362
+ $plugin_name[] = $plugin_data[2];
1363
+ }
1364
+ }
1365
+ /** Information has been passed via $_GET */
1366
+ else {
1367
+ foreach ( $plugins as $key => $value ) {
1368
+ /** Grab plugin slug for each plugin */
1369
+ if ( 0 == $key % 3 || 0 == $key ) {
1370
+ $plugins_to_install[] = $value;
1371
+ $plugin_installs[] = $value;
1372
+ }
1373
+ $a++;
1374
+ }
1375
+ }
1376
+
1377
+ /** Look first to see if information has been passed via WP_Filesystem */
1378
+ if ( isset( $_GET[sanitize_key( 'plugin_paths' )] ) )
1379
+ $plugin_paths = explode( ',', stripslashes( $_GET[sanitize_key( 'plugin_paths' )] ) );
1380
+ /** Looks like the user doesn't need to enter his FTP creds */
1381
+ elseif ( isset( $_POST[sanitize_key( 'plugin' )] ) )
1382
+ $plugin_paths = (array) $plugin_path;
1383
+ /** Nothing has been submitted */
1384
+ else
1385
+ $plugin_paths = array();
1386
+
1387
+ /** Look first to see if information has been passed via WP_Filesystem */
1388
+ if ( isset( $_GET[sanitize_key( 'plugin_names' )] ) )
1389
+ $plugin_names = explode( ',', stripslashes( $_GET[sanitize_key( 'plugin_names' )] ) );
1390
+ /** Looks like the user doesn't need to enter his FTP creds */
1391
+ elseif ( isset( $_POST[sanitize_key( 'plugin' )] ) )
1392
+ $plugin_names = (array) $plugin_name;
1393
+ /** Nothing has been submitted */
1394
+ else
1395
+ $plugin_names = array();
1396
+
1397
+ $b = 0; // Incremental variable
1398
+
1399
+ /** Loop through plugin slugs and remove already installed plugins from the list */
1400
+ foreach ( $plugin_installs as $key => $plugin ) {
1401
+ if ( preg_match( '|.php$|', $plugin ) ) {
1402
+ unset( $plugin_installs[$key] );
1403
+
1404
+ /** If the plugin path isn't in the $_GET variable, we can unset the corresponding path */
1405
+ if ( ! isset( $_GET[sanitize_key( 'plugin_paths' )] ) )
1406
+ unset( $plugin_paths[$b] );
1407
+
1408
+ /** If the plugin name isn't in the $_GET variable, we can unset the corresponding name */
1409
+ if ( ! isset( $_GET[sanitize_key( 'plugin_names' )] ) )
1410
+ unset( $plugin_names[$b] );
1411
+ }
1412
+ $b++;
1413
+ }
1414
+
1415
+ /** No need to proceed further if we have no plugins to install */
1416
+ if ( empty( $plugin_installs ) )
1417
+ return false;
1418
+
1419
+ /** Reset array indexes in case we removed already installed plugins */
1420
+ $plugin_installs = array_values( $plugin_installs );
1421
+ $plugin_paths = array_values( $plugin_paths );
1422
+ $plugin_names = array_values( $plugin_names );
1423
+
1424
+ /** If we grabbed our plugin info from $_GET, we need to decode it for use */
1425
+ $plugin_installs = array_map( 'urldecode', $plugin_installs );
1426
+ $plugin_paths = array_map( 'urldecode', $plugin_paths );
1427
+ $plugin_names = array_map( 'urldecode', $plugin_names );
1428
+
1429
+ /** Pass all necessary information via URL if WP_Filesystem is needed */
1430
+ $url = wp_nonce_url(
1431
+ add_query_arg(
1432
+ array(
1433
+ 'page' => TGM_Plugin_Activation::$instance->menu,
1434
+ 'tgmpa-action' => 'install-selected',
1435
+ 'plugins' => urlencode( implode( ',', $plugins ) ),
1436
+ 'plugin_paths' => urlencode( implode( ',', $plugin_paths ) ),
1437
+ 'plugin_names' => urlencode( implode( ',', $plugin_names ) ),
1438
+ ),
1439
+ admin_url( TGM_Plugin_Activation::$instance->parent_url_slug )
1440
+ ),
1441
+ 'bulk-plugins'
1442
+ );
1443
+ $method = ''; // Leave blank so WP_Filesystem can populate it as necessary
1444
+ $fields = array( sanitize_key( 'action' ), sanitize_key( '_wp_http_referer' ), sanitize_key( '_wpnonce' ) ); // Extra fields to pass to WP_Filesystem
1445
+
1446
+ if ( false === ( $creds = request_filesystem_credentials( $url, $method, false, false, $fields ) ) )
1447
+ return true;
1448
+
1449
+ if ( ! WP_Filesystem( $creds ) ) {
1450
+ request_filesystem_credentials( $url, $method, true, false, $fields ); // Setup WP_Filesystem
1451
+ return true;
1452
+ }
1453
+
1454
+ require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; // Need for plugins_api
1455
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; // Need for upgrade classes
1456
+
1457
+ /** Store all information in arrays since we are processing a bulk installation */
1458
+ $api = array();
1459
+ $sources = array();
1460
+ $install_path = array();
1461
+
1462
+ $c = 0; // Incremental variable
1463
+
1464
+ /** Loop through each plugin to install and try to grab information from WordPress API, if not create 'tgmpa-empty' scalar */
1465
+ foreach ( $plugin_installs as $plugin ) {
1466
+ $api[$c] = plugins_api( 'plugin_information', array( 'slug' => $plugin, 'fields' => array( 'sections' => false ) ) ) ? plugins_api( 'plugin_information', array( 'slug' => $plugin, 'fields' => array( 'sections' => false ) ) ) : (object) $api[$c] = 'tgmpa-empty';
1467
+ $c++;
1468
+ }
1469
+
1470
+ if ( is_wp_error( $api ) )
1471
+ wp_die( TGM_Plugin_Activation::$instance->strings['oops'] . var_dump( $api ) );
1472
+
1473
+ $d = 0; // Incremental variable
1474
+
1475
+ /** Capture download links from $api or set install link to pre-packaged/private repo */
1476
+ foreach ( $api as $object ) {
1477
+ $sources[$d] = isset( $object->download_link ) ? $object->download_link : $plugin_paths[$d];
1478
+ $d++;
1479
+ }
1480
+
1481
+ /** Finally, all the data is prepared to be sent to the installer */
1482
+ $url = add_query_arg( array( 'page' => TGM_Plugin_Activation::$instance->menu ), admin_url( TGM_Plugin_Activation::$instance->parent_url_slug ) );
1483
+ $nonce = 'bulk-plugins';
1484
+ $names = $plugin_names;
1485
+
1486
+ /** Create a new instance of TGM_Bulk_Installer */
1487
+ $installer = new TGM_Bulk_Installer( $skin = new TGM_Bulk_Installer_Skin( compact( 'url', 'nonce', 'names' ) ) );
1488
+
1489
+ /** Wrap the install process with the appropriate HTML */
1490
+ echo '<div class="tgmpa wrap">';
1491
+ screen_icon( apply_filters( 'tgmpa_default_screen_icon', 'themes' ) );
1492
+ echo '<h2>' . esc_html( get_admin_page_title() ) . '</h2>';
1493
+ /** Process the bulk installation submissions */
1494
+ $installer->bulk_install( $sources );
1495
+ echo '</div>';
1496
+
1497
+ return true;
1498
+ }
1499
+
1500
+ /** Bulk activation process */
1501
+ if ( 'tgmpa-bulk-activate' === $this->current_action() ) {
1502
+ check_admin_referer( 'bulk-' . $this->_args['plural'] );
1503
+
1504
+ /** Grab plugin data from $_POST */
1505
+ $plugins = isset( $_POST[sanitize_key( 'plugin' )] ) ? (array) $_POST[sanitize_key( 'plugin' )] : array();
1506
+ $plugins_to_activate = array();
1507
+
1508
+ /** Split plugin value into array with plugin file path, plugin source and plugin name */
1509
+ foreach ( $plugins as $i => $plugin )
1510
+ $plugins_to_activate[] = explode( ',', $plugin );
1511
+
1512
+ foreach ( $plugins_to_activate as $i => $array ) {
1513
+ if ( ! preg_match( '|.php$|', $array[0] ) ) // Plugins that haven't been installed yet won't have the correct file path
1514
+ unset( $plugins_to_activate[$i] );
1515
+ }
1516
+
1517
+ /** Return early if there are no plugins to activate */
1518
+ if ( empty( $plugins_to_activate ) )
1519
+ return;
1520
+
1521
+ $plugins = array();
1522
+ $plugin_names = array();
1523
+
1524
+ foreach ( $plugins_to_activate as $plugin_string ) {
1525
+ $plugins[] = $plugin_string[0];
1526
+ $plugin_names[] = $plugin_string[2];
1527
+ }
1528
+
1529
+ $count = count( $plugin_names ); // Count so we can use _n function
1530
+ $last_plugin = array_pop( $plugin_names ); // Pop off last name to prep for readability
1531
+ $imploded = empty( $plugin_names ) ? '<strong>' . $last_plugin . '</strong>' : '<strong>' . ( implode( ', ', $plugin_names ) . '</strong> and <strong>' . $last_plugin . '</strong>.' );
1532
+
1533
+ /** Now we are good to go - let's start activating plugins */
1534
+ $activate = activate_plugins( $plugins );
1535
+
1536
+ if ( is_wp_error( $activate ) )
1537
+ echo '<div id="message" class="error"><p>' . $activate->get_error_message() . '</p></div>';
1538
+ else
1539
+ printf( '<div id="message" class="updated"><p>%1$s %2$s</p></div>', _n( 'The following plugin was activated successfully:', 'The following plugins were activated successfully:', $count, TGM_Plugin_Activation::$instance->domain ), $imploded );
1540
+
1541
+ /** Update recently activated plugins option */
1542
+ $recent = (array) get_option( 'recently_activated' );
1543
+
1544
+ foreach ( $plugins as $plugin => $time )
1545
+ if ( isset( $recent[$plugin] ) )
1546
+ unset( $recent[$plugin] );
1547
+
1548
+ update_option( 'recently_activated', $recent );
1549
+
1550
+ unset( $_POST ); // Reset the $_POST variable in case user wants to perform one action after another
1551
+ }
1552
+ }
1553
+
1554
+ /**
1555
+ * Prepares all of our information to be outputted into a usable table.
1556
+ *
1557
+ * @since 2.2.0
1558
+ */
1559
+ public function prepare_items() {
1560
+
1561
+ $per_page = 100; // Set it high so we shouldn't have to worry about pagination
1562
+ $columns = $this->get_columns(); // Get all necessary column information
1563
+ $hidden = array(); // No columns to hide, but we must set as an array
1564
+ $sortable = array(); // No reason to make sortable columns
1565
+ $this->_column_headers = array( $columns, $hidden, $sortable ); // Get all necessary column headers
1566
+
1567
+ /** Process our bulk actions here */
1568
+ $this->process_bulk_actions();
1569
+
1570
+ /** Store all of our plugin data into $items array so WP_List_Table can use it */
1571
+ $this->items = $this->_gather_plugin_data();
1572
+
1573
+ }
1574
+
1575
+ }
1576
+ }
1577
+
1578
+ /**
1579
+ * The WP_Upgrader file isn't always available. If it isn't available,
1580
+ * we load it here.
1581
+ *
1582
+ * We check to make sure no action or activation keys are set so that WordPress
1583
+ * doesn't try to re-include the class when processing upgrades or installs outside
1584
+ * of the class.
1585
+ *
1586
+ * @since 2.2.0
1587
+ */
1588
+ if ( ! class_exists( 'WP_Upgrader' ) && ( isset( $_GET[sanitize_key( 'page' )] ) && TGM_Plugin_Activation::$instance->menu = $_GET[sanitize_key( 'page' )] ) ) {
1589
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
1590
+
1591
+ if ( ! class_exists( 'TGM_Bulk_Installer' ) ) {
1592
+ /**
1593
+ * Installer class to handle bulk plugin installations.
1594
+ *
1595
+ * Extends WP_Upgrader and customizes to suit the installation of multiple
1596
+ * plugins.
1597
+ *
1598
+ * @since 2.2.0
1599
+ *
1600
+ * @package TGM-Plugin-Activation
1601
+ * @author Thomas Griffin <thomas@thomasgriffinmedia.com>
1602
+ * @author Gary Jones <gamajo@gamajo.com>
1603
+ */
1604
+ class TGM_Bulk_Installer extends WP_Upgrader {
1605
+
1606
+ /**
1607
+ * Holds result of bulk plugin installation.
1608
+ *
1609
+ * @since 2.2.0
1610
+ *
1611
+ * @var string
1612
+ */
1613
+ public $result;
1614
+
1615
+ /**
1616
+ * Flag to check if bulk installation is occurring or not.
1617
+ *
1618
+ * @since 2.2.0
1619
+ *
1620
+ * @var boolean
1621
+ */
1622
+ public $bulk = false;
1623
+
1624
+ /**
1625
+ * Processes the bulk installation of plugins.
1626
+ *
1627
+ * @since 2.2.0
1628
+ *
1629
+ * @param array $packages The plugin sources needed for installation
1630
+ * @return string|boolean Install confirmation messages on success, false on failure
1631
+ */
1632
+ public function bulk_install( $packages ) {
1633
+
1634
+ /** Pass installer skin object and set bulk property to true */
1635
+ $this->init();
1636
+ $this->bulk = true;
1637
+
1638
+ /** Set install strings and automatic activation strings (if config option is set to true) */
1639
+ $this->install_strings();
1640
+ if ( TGM_Plugin_Activation::$instance->is_automatic )
1641
+ $this->activate_strings();
1642
+
1643
+ /** Run the header string to notify user that the process has begun */
1644
+ $this->skin->header();
1645
+
1646
+ /** Connect to the Filesystem */
1647
+ $res = $this->fs_connect( array( WP_CONTENT_DIR, WP_PLUGIN_DIR ) );
1648
+ if ( ! $res ) {
1649
+ $this->skin->footer();
1650
+ return false;
1651
+ }
1652
+
1653
+ /** Set the bulk header and prepare results array */
1654
+ $this->skin->bulk_header();
1655
+ $results = array();
1656
+
1657
+ /** Get the total number of packages being processed and iterate as each package is successfully installed */
1658
+ $this->update_count = count( $packages );
1659
+ $this->update_current = 0;
1660
+
1661
+ /** Loop through each plugin and process the installation */
1662
+ foreach ( $packages as $plugin ) {
1663
+ $this->update_current++; // Increment counter
1664
+
1665
+ /** Do the plugin install */
1666
+ $result = $this->run(
1667
+ array(
1668
+ 'package' => $plugin, // The plugin source
1669
+ 'destination' => WP_PLUGIN_DIR, // The destination dir
1670
+ 'clear_destination' => false, // Do we want to clear the destination or not?
1671
+ 'clear_working' => true, // Remove original install file
1672
+ 'is_multi' => true, // Are we processing multiple installs?
1673
+ 'hook_extra' => array( 'plugin' => $plugin, ), // Pass plugin source as extra data
1674
+ )
1675
+ );
1676
+
1677
+ /** Store installation results in result property */
1678
+ $results[$plugin] = $this->result;
1679
+
1680
+ /** Prevent credentials auth screen from displaying multiple times */
1681
+ if ( false === $result )
1682
+ break;
1683
+ }
1684
+
1685
+ /** Pass footer skin strings */
1686
+ $this->skin->bulk_footer();
1687
+ $this->skin->footer();
1688
+
1689
+ /** Return our results */
1690
+ return $results;
1691
+
1692
+ }
1693
+
1694
+ /**
1695
+ * Performs the actual installation of each plugin.
1696
+ *
1697
+ * This method also activates the plugin in the automatic flag has been
1698
+ * set to true for the TGMPA class.
1699
+ *
1700
+ * @since 2.2.0
1701
+ *
1702
+ * @param array $options The installation cofig options
1703
+ * @return null/array Return early if error, array of installation data on success
1704
+ */
1705
+ public function run( $options ) {
1706
+
1707
+ /** Default config options */
1708
+ $defaults = array(
1709
+ 'package' => '',
1710
+ 'destination' => '',
1711
+ 'clear_destination' => false,
1712
+ 'clear_working' => true,
1713
+ 'is_multi' => false,
1714
+ 'hook_extra' => array(),
1715
+ );
1716
+
1717
+ /** Parse default options with config options from $this->bulk_upgrade and extract them */
1718
+ $options = wp_parse_args( $options, $defaults );
1719
+ extract( $options );
1720
+
1721
+ /** Connect to the Filesystem */
1722
+ $res = $this->fs_connect( array( WP_CONTENT_DIR, $destination ) );
1723
+ if ( ! $res )
1724
+ return false;
1725
+
1726
+ /** Return early if there is an error connecting to the Filesystem */
1727
+ if ( is_wp_error( $res ) ) {
1728
+ $this->skin->error( $res );
1729
+ return $res;
1730
+ }
1731
+
1732
+ /** Call $this->header separately if running multiple times */
1733
+ if ( ! $is_multi )
1734
+ $this->skin->header();
1735
+
1736
+ /** Set strings before the package is installed */
1737
+ $this->skin->before();
1738
+
1739
+ /** Download the package (this just returns the filename of the file if the package is a local file) */
1740
+ $download = $this->download_package( $package );
1741
+ if ( is_wp_error( $download ) ) {
1742
+ $this->skin->error( $download );
1743
+ $this->skin->after();
1744
+ return $download;
1745
+ }
1746
+
1747
+ /** Don't accidentally delete a local file */
1748
+ $delete_package = ( $download != $package );
1749
+
1750
+ /** Unzip file into a temporary working directory */
1751
+ $working_dir = $this->unpack_package( $download, $delete_package );
1752
+ if ( is_wp_error( $working_dir ) ) {
1753
+ $this->skin->error( $working_dir );
1754
+ $this->skin->after();
1755
+ return $working_dir;
1756
+ }
1757
+
1758
+ /** Install the package into the working directory with all passed config options */
1759
+ $result = $this->install_package(
1760
+ array(
1761
+ 'source' => $working_dir,
1762
+ 'destination' => $destination,
1763
+ 'clear_destination' => $clear_destination,
1764
+ 'clear_working' => $clear_working,
1765
+ 'hook_extra' => $hook_extra,
1766
+ )
1767
+ );
1768
+
1769
+ /** Pass the result of the installation */
1770
+ $this->skin->set_result( $result );
1771
+
1772
+ /** Set correct strings based on results */
1773
+ if ( is_wp_error( $result ) ) {
1774
+ $this->skin->error( $result );
1775
+ $this->skin->feedback( 'process_failed' );
1776
+ }
1777
+ /** The plugin install is successful */
1778
+ else {
1779
+ $this->skin->feedback( 'process_success' );
1780
+ }
1781
+
1782
+ /** Only process the activation of installed plugins if the automatic flag is set to true */
1783
+ if ( TGM_Plugin_Activation::$instance->is_automatic ) {
1784
+ /** Flush plugins cache so we can make sure that the installed plugins list is always up to date */
1785
+ wp_cache_flush();
1786
+
1787
+ /** Get the installed plugin file and activate it */
1788
+ $plugin_info = $this->plugin_info( $package );
1789
+ $activate = activate_plugin( $plugin_info );
1790
+
1791
+ /** Re-populate the file path now that the plugin has been installed and activated */
1792
+ TGM_Plugin_Activation::$instance->populate_file_path();
1793
+
1794
+ /** Set correct strings based on results */
1795
+ if ( is_wp_error( $activate ) ) {
1796
+ $this->skin->error( $activate );
1797
+ $this->skin->feedback( 'activation_failed' );
1798
+ }
1799
+ /** The plugin activation is successful */
1800
+ else {
1801
+ $this->skin->feedback( 'activation_success' );
1802
+ }
1803
+ }
1804
+
1805
+ /** Flush plugins cache so we can make sure that the installed plugins list is always up to date */
1806
+ wp_cache_flush();
1807
+
1808
+ /** Set install footer strings */
1809
+ $this->skin->after();
1810
+ if ( ! $is_multi )
1811
+ $this->skin->footer();
1812
+
1813
+ return $result;
1814
+
1815
+ }
1816
+
1817
+ /**
1818
+ * Sets the correct install strings for the installer skin to use.
1819
+ *
1820
+ * @since 2.2.0
1821
+ */
1822
+ public function install_strings() {
1823
+
1824
+ $this->strings['no_package'] = __( 'Install package not available.', TGM_Plugin_Activation::$instance->domain );
1825
+ $this->strings['downloading_package'] = __( 'Downloading install package from <span class="code">%s</span>&#8230;', TGM_Plugin_Activation::$instance->domain );
1826
+ $this->strings['unpack_package'] = __( 'Unpacking the package&#8230;', TGM_Plugin_Activation::$instance->domain );
1827
+ $this->strings['installing_package'] = __( 'Installing the plugin&#8230;', TGM_Plugin_Activation::$instance->domain );
1828
+ $this->strings['process_failed'] = __( 'Plugin install failed.', TGM_Plugin_Activation::$instance->domain );
1829
+ $this->strings['process_success'] = __( 'Plugin installed successfully.', TGM_Plugin_Activation::$instance->domain );
1830
+
1831
+ }
1832
+
1833
+ /**
1834
+ * Sets the correct activation strings for the installer skin to use.
1835
+ *
1836
+ * @since 2.2.0
1837
+ */
1838
+ public function activate_strings() {
1839
+
1840
+ $this->strings['activation_failed'] = __( 'Plugin activation failed.', TGM_Plugin_Activation::$instance->domain );
1841
+ $this->strings['activation_success'] = __( 'Plugin activated successfully.', TGM_Plugin_Activation::$instance->domain );
1842
+
1843
+ }
1844
+
1845
+ /**
1846
+ * Grabs the plugin file from an installed plugin.
1847
+ *
1848
+ * @since 2.2.0
1849
+ *
1850
+ * @return string|boolean Return plugin file on success, false on failure
1851
+ */
1852
+ public function plugin_info() {
1853
+
1854
+ /** Return false if installation result isn't an array or the destination name isn't set */
1855
+ if ( ! is_array( $this->result ) )
1856
+ return false;
1857
+ if ( empty( $this->result['destination_name'] ) )
1858
+ return false;
1859
+
1860
+ /** Get the installed plugin file or return false if it isn't set */
1861
+ $plugin = get_plugins( '/' . $this->result['destination_name'] );
1862
+ if ( empty( $plugin ) )
1863
+ return false;
1864
+
1865
+ /** Assume the requested plugin is the first in the list */
1866
+ $pluginfiles = array_keys( $plugin );
1867
+
1868
+ return $this->result['destination_name'] . '/' . $pluginfiles[0];
1869
+
1870
+ }
1871
+
1872
+ }
1873
+ }
1874
+
1875
+ if ( ! class_exists( 'TGM_Bulk_Installer_Skin' ) ) {
1876
+ /**
1877
+ * Installer skin to set strings for the bulk plugin installations..
1878
+ *
1879
+ * Extends Bulk_Upgrader_Skin and customizes to suit the installation of multiple
1880
+ * plugins.
1881
+ *
1882
+ * @since 2.2.0
1883
+ *
1884
+ * @package TGM-Plugin-Activation
1885
+ * @author Thomas Griffin <thomas@thomasgriffinmedia.com>
1886
+ * @author Gary Jones <gamajo@gamajo.com>
1887
+ */
1888
+ class TGM_Bulk_Installer_Skin extends Bulk_Upgrader_Skin {
1889
+
1890
+ /**
1891
+ * Holds plugin info for each individual plugin installation.
1892
+ *
1893
+ * @since 2.2.0
1894
+ *
1895
+ * @var array
1896
+ */
1897
+ public $plugin_info = array();
1898
+
1899
+ /**
1900
+ * Holds names of plugins that are undergoing bulk installations.
1901
+ *
1902
+ * @since 2.2.0
1903
+ *
1904
+ * @var array
1905
+ */
1906
+ public $plugin_names = array();
1907
+
1908
+ /**
1909
+ * Integer to use for iteration through each plugin installation.
1910
+ *
1911
+ * @since 2.2.0
1912
+ *
1913
+ * @var integer
1914
+ */
1915
+ public $i = 0;
1916
+
1917
+ /**
1918
+ * Constructor. Parses default args with new ones and extracts them for use.
1919
+ *
1920
+ * @since 2.2.0
1921
+ *
1922
+ * @param array $args Arguments to pass for use within the class
1923
+ */
1924
+ public function __construct( $args = array() ) {
1925
+
1926
+ /** Parse default and new args */
1927
+ $defaults = array( 'url' => '', 'nonce' => '', 'names' => array() );
1928
+ $args = wp_parse_args( $args, $defaults );
1929
+
1930
+ /** Set plugin names to $this->plugin_names property */
1931
+ $this->plugin_names = $args['names'];
1932
+
1933
+ /** Extract the new args */
1934
+ parent::__construct( $args );
1935
+
1936
+ }
1937
+
1938
+ /**
1939
+ * Sets install skin strings for each individual plugin.
1940
+ *
1941
+ * Checks to see if the automatic activation flag is set and uses the
1942
+ * the proper strings accordingly.
1943
+ *
1944
+ * @since 2.2.0
1945
+ */
1946
+ public function add_strings() {
1947
+
1948
+ /** Automatic activation strings */
1949
+ if ( TGM_Plugin_Activation::$instance->is_automatic ) {
1950
+ $this->upgrader->strings['skin_upgrade_start'] = __( 'The installation and activation process is starting. This process may take a while on some hosts, so please be patient.', TGM_Plugin_Activation::$instance->domain );
1951
+ $this->upgrader->strings['skin_update_successful'] = __( '%1$s installed and activated successfully.', TGM_Plugin_Activation::$instance->domain ) . ' <a onclick="%2$s" href="#" class="hide-if-no-js"><span>' . __( 'Show Details', TGM_Plugin_Activation::$instance->domain ) . '</span><span class="hidden">' . __( 'Hide Details', TGM_Plugin_Activation::$instance->domain ) . '</span>.</a>';
1952
+ $this->upgrader->strings['skin_upgrade_end'] = __( 'All installations and activations have been completed.', TGM_Plugin_Activation::$instance->domain );
1953
+ $this->upgrader->strings['skin_before_update_header'] = __( 'Installing and Activating Plugin %1$s (%2$d/%3$d)', TGM_Plugin_Activation::$instance->domain );
1954
+ }
1955
+ /** Default installation strings */
1956
+ else {
1957
+ $this->upgrader->strings['skin_upgrade_start'] = __( 'The installation process is starting. This process may take a while on some hosts, so please be patient.', TGM_Plugin_Activation::$instance->domain );
1958
+ $this->upgrader->strings['skin_update_failed_error'] = __( 'An error occurred while installing %1$s: <strong>%2$s</strong>.', TGM_Plugin_Activation::$instance->domain );
1959
+ $this->upgrader->strings['skin_update_failed'] = __( 'The installation of %1$s failed.', TGM_Plugin_Activation::$instance->domain );
1960
+ $this->upgrader->strings['skin_update_successful'] = __( '%1$s installed successfully.', TGM_Plugin_Activation::$instance->domain ) . ' <a onclick="%2$s" href="#" class="hide-if-no-js"><span>' . __( 'Show Details', TGM_Plugin_Activation::$instance->domain ) . '</span><span class="hidden">' . __( 'Hide Details', TGM_Plugin_Activation::$instance->domain ) . '</span>.</a>';
1961
+ $this->upgrader->strings['skin_upgrade_end'] = __( 'All installations have been completed.', TGM_Plugin_Activation::$instance->domain );
1962
+ $this->upgrader->strings['skin_before_update_header'] = __( 'Installing Plugin %1$s (%2$d/%3$d)', TGM_Plugin_Activation::$instance->domain );
1963
+ }
1964
+
1965
+ }
1966
+
1967
+ /**
1968
+ * Outputs the header strings and necessary JS before each plugin installation.
1969
+ *
1970
+ * @since 2.2.0
1971
+ */
1972
+ public function before() {
1973
+
1974
+ /** We are currently in the plugin installation loop, so set to true */
1975
+ $this->in_loop = true;
1976
+
1977
+ printf( '<h4>' . $this->upgrader->strings['skin_before_update_header'] . ' <img alt="" src="' . admin_url( 'images/wpspin_light.gif' ) . '" class="hidden waiting-' . $this->upgrader->update_current . '" style="vertical-align:middle;" /></h4>', $this->plugin_names[$this->i], $this->upgrader->update_current, $this->upgrader->update_count );
1978
+ echo '<script type="text/javascript">jQuery(\'.waiting-' . esc_js( $this->upgrader->update_current ) . '\').show();</script>';
1979
+ echo '<div class="update-messages hide-if-js" id="progress-' . esc_attr( $this->upgrader->update_current ) . '"><p>';
1980
+
1981
+ /** Flush header output buffer */
1982
+ $this->before_flush_output();
1983
+
1984
+ }
1985
+
1986
+ /**
1987
+ * Outputs the footer strings and necessary JS after each plugin installation.
1988
+ *
1989
+ * Checks for any errors and outputs them if they exist, else output
1990
+ * success strings.
1991
+ *
1992
+ * @since 2.2.0
1993
+ */
1994
+ public function after() {
1995
+
1996
+ /** Close install strings */
1997
+ echo '</p></div>';
1998
+
1999
+ /** Output error strings if an error has occurred */
2000
+ if ( $this->error || ! $this->result ) {
2001
+ if ( $this->error )
2002
+ echo '<div class="error"><p>' . sprintf( $this->upgrader->strings['skin_update_failed_error'], $this->plugin_names[$this->i], $this->error ) . '</p></div>';
2003
+ else
2004
+ echo '<div class="error"><p>' . sprintf( $this->upgrader->strings['skin_update_failed'], $this->plugin_names[$this->i] ) . '</p></div>';
2005
+
2006
+ echo '<script type="text/javascript">jQuery(\'#progress-' . esc_js( $this->upgrader->update_current ) . '\').show();</script>';
2007
+ }
2008
+
2009
+ /** If the result is set and there are no errors, success! */
2010
+ if ( ! empty( $this->result ) && ! is_wp_error( $this->result ) ) {
2011
+ echo '<div class="updated"><p>' . sprintf( $this->upgrader->strings['skin_update_successful'], $this->plugin_names[$this->i], 'jQuery(\'#progress-' . esc_js( $this->upgrader->update_current ) . '\').toggle();jQuery(\'span\', this).toggle(); return false;' ) . '</p></div>';
2012
+ echo '<script type="text/javascript">jQuery(\'.waiting-' . esc_js( $this->upgrader->update_current ) . '\').hide();</script>';
2013
+ }
2014
+
2015
+ /** Set in_loop and error to false and flush footer output buffer */
2016
+ $this->reset();
2017
+ $this->after_flush_output();
2018
+
2019
+ }
2020
+
2021
+ /**
2022
+ * Outputs links after bulk plugin installation is complete.
2023
+ *
2024
+ * @since 2.2.0
2025
+ */
2026
+ public function bulk_footer() {
2027
+
2028
+ /** Serve up the string to say installations (and possibly activations) are complete */
2029
+ parent::bulk_footer();
2030
+
2031
+ /** Flush plugins cache so we can make sure that the installed plugins list is always up to date */
2032
+ wp_cache_flush();
2033
+
2034
+ /** Display message based on if all plugins are now active or not */
2035
+ $complete = array();
2036
+ foreach ( TGM_Plugin_Activation::$instance->plugins as $plugin ) {
2037
+ if ( ! is_plugin_active( $plugin['file_path'] ) ) {
2038
+ echo '<p><a href="' . add_query_arg( 'page', TGM_Plugin_Activation::$instance->menu, admin_url( TGM_Plugin_Activation::$instance->parent_url_slug ) ) . '" title="' . esc_attr( TGM_Plugin_Activation::$instance->strings['return'] ) . '" target="_parent">' . __( TGM_Plugin_Activation::$instance->strings['return'], TGM_Plugin_Activation::$instance->domain ) . '</a></p>';
2039
+ $complete[] = $plugin;
2040
+ break;
2041
+ }
2042
+ /** Nothing to store */
2043
+ else {
2044
+ $complete[] = '';
2045
+ }
2046
+ }
2047
+
2048
+ /** Filter out any empty entries */
2049
+ $complete = array_filter( $complete );
2050
+
2051
+ /** All plugins are active, so we display the complete string and hide the menu to protect users */
2052
+ if ( empty( $complete ) ) {
2053
+ echo '<p>' . sprintf( TGM_Plugin_Activation::$instance->strings['complete'], '<a href="' . admin_url() . '" title="' . __( 'Return to the Dashboard', TGM_Plugin_Activation::$instance->domain ) . '">' . __( 'Return to the Dashboard', TGM_Plugin_Activation::$instance->domain ) . '</a>' ) . '</p>';
2054
+ echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
2055
+ }
2056
+
2057
+ }
2058
+
2059
+ /**
2060
+ * Flush header output buffer.
2061
+ *
2062
+ * @since 2.2.0
2063
+ */
2064
+ public function before_flush_output() {
2065
+
2066
+ wp_ob_end_flush_all();
2067
+ flush();
2068
+
2069
+ }
2070
+
2071
+ /**
2072
+ * Flush footer output buffer and iterate $this->i to make sure the
2073
+ * installation strings reference the correct plugin.
2074
+ *
2075
+ * @since 2.2.0
2076
+ */
2077
+ public function after_flush_output() {
2078
+
2079
+ wp_ob_end_flush_all();
2080
+ flush();
2081
+ $this->i++;
2082
+
2083
+ }
2084
+
2085
+ }
2086
+ }
2087
+ }
tgm-plugin-activation/example.php ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file represents an example of the code that themes would use to register
4
+ * the required plugins.
5
+ *
6
+ * It is expected that theme authors would copy and paste this code into their
7
+ * functions.php file, and amend to suit.
8
+ *
9
+ * @package TGM-Plugin-Activation
10
+ * @subpackage Example
11
+ * @version 2.3.3
12
+ * @author Thomas Griffin <thomas@thomasgriffinmedia.com>
13
+ * @author Gary Jones <gamajo@gamajo.com>
14
+ * @copyright Copyright (c) 2012, Thomas Griffin
15
+ * @license http://opensource.org/licenses/gpl-2.0.php GPL v2 or later
16
+ * @link https://github.com/thomasgriffin/TGM-Plugin-Activation
17
+ */
18
+
19
+ /**
20
+ * Include the TGM_Plugin_Activation class.
21
+ */
22
+ require_once dirname( __FILE__ ) . '/class-tgm-plugin-activation.php';
23
+
24
+ add_action( 'tgmpa_register', 'my_theme_register_required_plugins' );
25
+ /**
26
+ * Register the required plugins for this theme.
27
+ *
28
+ * In this example, we register two plugins - one included with the TGMPA library
29
+ * and one from the .org repo.
30
+ *
31
+ * The variable passed to tgmpa_register_plugins() should be an array of plugin
32
+ * arrays.
33
+ *
34
+ * This function is hooked into tgmpa_init, which is fired within the
35
+ * TGM_Plugin_Activation class constructor.
36
+ */
37
+ function my_theme_register_required_plugins() {
38
+
39
+ /**
40
+ * Array of plugin arrays. Required keys are name and slug.
41
+ * If the source is NOT from the .org repo, then source is also required.
42
+ */
43
+ $plugins = array(
44
+
45
+ // This is an example of how to include a plugin pre-packaged with a theme
46
+ array(
47
+ 'name' => 'TGM Example Plugin', // The plugin name
48
+ 'slug' => 'tgm-example-plugin', // The plugin slug (typically the folder name)
49
+ 'source' => get_stylesheet_directory() . '/lib/plugins/tgm-example-plugin.zip', // The plugin source
50
+ 'required' => true, // If false, the plugin is only 'recommended' instead of required
51
+ 'version' => '', // E.g. 1.0.0. If set, the active plugin must be this version or higher, otherwise a notice is presented
52
+ 'force_activation' => false, // If true, plugin is activated upon theme activation and cannot be deactivated until theme switch
53
+ 'force_deactivation' => false, // If true, plugin is deactivated upon theme switch, useful for theme-specific plugins
54
+ 'external_url' => '', // If set, overrides default API URL and points to an external URL
55
+ ),
56
+
57
+ // This is an example of how to include a plugin from the WordPress Plugin Repository
58
+ array(
59
+ 'name' => 'BuddyPress',
60
+ 'slug' => 'buddypress',
61
+ 'required' => false,
62
+ ),
63
+
64
+ );
65
+
66
+ // Change this to your theme text domain, used for internationalising strings
67
+ $theme_text_domain = 'tgmpa';
68
+
69
+ /**
70
+ * Array of configuration settings. Amend each line as needed.
71
+ * If you want the default strings to be available under your own theme domain,
72
+ * leave the strings uncommented.
73
+ * Some of the strings are added into a sprintf, so see the comments at the
74
+ * end of each line for what each argument will be.
75
+ */
76
+ $config = array(
77
+ 'domain' => $theme_text_domain, // Text domain - likely want to be the same as your theme.
78
+ 'default_path' => '', // Default absolute path to pre-packaged plugins
79
+ 'parent_menu_slug' => 'themes.php', // Default parent menu slug
80
+ 'parent_url_slug' => 'themes.php', // Default parent URL slug
81
+ 'menu' => 'install-required-plugins', // Menu slug
82
+ 'has_notices' => true, // Show admin notices or not
83
+ 'is_automatic' => false, // Automatically activate plugins after installation or not
84
+ 'message' => '', // Message to output right before the plugins table
85
+ 'strings' => array(
86
+ 'page_title' => __( 'Install Required Plugins', $theme_text_domain ),
87
+ 'menu_title' => __( 'Install Plugins', $theme_text_domain ),
88
+ 'installing' => __( 'Installing Plugin: %s', $theme_text_domain ), // %1$s = plugin name
89
+ 'oops' => __( 'Something went wrong with the plugin API.', $theme_text_domain ),
90
+ 'notice_can_install_required' => _n_noop( 'This theme requires the following plugin: %1$s.', 'This theme requires the following plugins: %1$s.' ), // %1$s = plugin name(s)
91
+ 'notice_can_install_recommended' => _n_noop( 'This theme recommends the following plugin: %1$s.', 'This theme recommends the following plugins: %1$s.' ), // %1$s = plugin name(s)
92
+ 'notice_cannot_install' => _n_noop( 'Sorry, but you do not have the correct permissions to install the %s plugin. Contact the administrator of this site for help on getting the plugin installed.', 'Sorry, but you do not have the correct permissions to install the %s plugins. Contact the administrator of this site for help on getting the plugins installed.' ), // %1$s = plugin name(s)
93
+ 'notice_can_activate_required' => _n_noop( 'The following required plugin is currently inactive: %1$s.', 'The following required plugins are currently inactive: %1$s.' ), // %1$s = plugin name(s)
94
+ 'notice_can_activate_recommended' => _n_noop( 'The following recommended plugin is currently inactive: %1$s.', 'The following recommended plugins are currently inactive: %1$s.' ), // %1$s = plugin name(s)
95
+ 'notice_cannot_activate' => _n_noop( 'Sorry, but you do not have the correct permissions to activate the %s plugin. Contact the administrator of this site for help on getting the plugin activated.', 'Sorry, but you do not have the correct permissions to activate the %s plugins. Contact the administrator of this site for help on getting the plugins activated.' ), // %1$s = plugin name(s)
96
+ 'notice_ask_to_update' => _n_noop( 'The following plugin needs to be updated to its latest version to ensure maximum compatibility with this theme: %1$s.', 'The following plugins need to be updated to their latest version to ensure maximum compatibility with this theme: %1$s.' ), // %1$s = plugin name(s)
97
+ 'notice_cannot_update' => _n_noop( 'Sorry, but you do not have the correct permissions to update the %s plugin. Contact the administrator of this site for help on getting the plugin updated.', 'Sorry, but you do not have the correct permissions to update the %s plugins. Contact the administrator of this site for help on getting the plugins updated.' ), // %1$s = plugin name(s)
98
+ 'install_link' => _n_noop( 'Begin installing plugin', 'Begin installing plugins' ),
99
+ 'activate_link' => _n_noop( 'Activate installed plugin', 'Activate installed plugins' ),
100
+ 'return' => __( 'Return to Required Plugins Installer', $theme_text_domain ),
101
+ 'plugin_activated' => __( 'Plugin activated successfully.', $theme_text_domain ),
102
+ 'complete' => __( 'All plugins installed and activated successfully. %s', $theme_text_domain ) // %1$s = dashboard link
103
+ )
104
+ );
105
+
106
+ tgmpa( $plugins, $config );
107
+
108
+ }
tgm-plugin-activation/plugins/tgm-example-plugin.zip ADDED
Binary file