rtMedia for WordPress, BuddyPress and bbPress - Version 2.4.3

Version Description

  • Fixed latest activity formatting.
  • Added auto-update for add-ons.
  • Made minor changes for add-on compatibility.
Download this release

Release Info

Developer saurabhshukla
Plugin Icon 128x128 rtMedia for WordPress, BuddyPress and bbPress
Version 2.4.3
Comparing to
See all releases

Code changes from version 2.4.2 to 2.4.3

app/admin/BPMediaAdmin.php CHANGED
@@ -21,7 +21,7 @@ if (!class_exists('BPMediaAdmin')) {
21
  add_action('wp_ajax_bp_media_fetch_feed', array($bp_media_feed, 'fetch_feed'), 1);
22
  $bp_media_support = new BPMediaSupport();
23
  add_action('wp_ajax_bp_media_select_request', array($bp_media_support, 'get_form'), 1);
24
- add_action('wp_ajax_cancel_request', create_function('', 'do_settings_sections("bp-media-support"); die();'), 1);
25
  add_action('wp_ajax_bp_media_submit_request', array($bp_media_support, 'submit_request'), 1);
26
  add_action('wp_ajax_bp_media_fetch_feed', array($bp_media_feed, 'fetch_feed'), 1);
27
  if (is_admin()) {
21
  add_action('wp_ajax_bp_media_fetch_feed', array($bp_media_feed, 'fetch_feed'), 1);
22
  $bp_media_support = new BPMediaSupport();
23
  add_action('wp_ajax_bp_media_select_request', array($bp_media_support, 'get_form'), 1);
24
+ add_action('wp_ajax_bp_media_cancel_request', create_function('', 'do_settings_sections("bp-media-support"); die();'), 1);
25
  add_action('wp_ajax_bp_media_submit_request', array($bp_media_support, 'submit_request'), 1);
26
  add_action('wp_ajax_bp_media_fetch_feed', array($bp_media_feed, 'fetch_feed'), 1);
27
  if (is_admin()) {
app/helper/BPMediaAddon.php CHANGED
@@ -9,70 +9,69 @@
9
  * @author Gagandeep Singh <gagandeep.singh@rtcamp.com>
10
  * @author Joshua Abenazer <joshua.abenazer@rtcamp.com>
11
  */
12
- if ( ! class_exists( 'BPMediaAddon' ) ) {
13
 
14
- class BPMediaAddon {
15
 
16
- public $enquiry_link = 'http://rtcamp.com/contact';
17
 
18
- public function coming_soon_div(){
19
- return
20
- '<div class="coming-soon coming-soon-l"></div>
21
- <a class="coming-soon coming-soon-r" href="'.$this->enquiry_link.'" target="_blank">'
22
- //<a></a>
23
- .'</a>';
24
- }
25
 
26
- public function get_addons() {
27
- $addons = array(
28
- array(
29
- 'title' => __( 'BuddyPress-Media FFMPEG Add-on', BP_MEDIA_TXT_DOMAIN ),
30
- 'img_src' => 'http://cdn.rtcamp.com/files/2012/09/ffmpeg-logo-240x184.png',
31
- 'product_link' => 'http://rtcamp.com/store/buddypress-media-ffmpeg-converter/',
32
- 'desc' => '<p>' . __( 'Add supports for more audio & video formats using open-source media-node.', BP_MEDIA_TXT_DOMAIN ) . '</p>
33
- <p>' . __( 'Media node comes with automated setup script for Ubuntu/Debian.', BP_MEDIA_TXT_DOMAIN ) . '</p>',
34
- 'price' => '$49',
35
- 'demo_link' => 'http://demo.rtcamp.com/bpm-media',
36
- 'buy_now' => 'http://rtcamp.com/store/?add-to-cart=13677'
37
- ),
38
- array(
39
- 'title' => __( 'BuddyPress-Media Kaltura Add-on', BP_MEDIA_TXT_DOMAIN ),
40
- 'img_src' => 'http://cdn.rtcamp.com/files/2012/10/new-buddypress-media-kaltura-logo-240x184.png',
41
- 'product_link' => 'http://rtcamp.com/store/buddypress-media-kaltura/',
42
- 'desc' => '<p>' . __( 'Add support for more video formats using Kaltura video solution.', BP_MEDIA_TXT_DOMAIN ) . '</p>
43
- <p>' . __( 'Works with Kaltura.com, self-hosted Kaltura-CE and Kaltura-on-premise.', BP_MEDIA_TXT_DOMAIN ) . '</p>',
44
- 'price' => '$99',
45
- 'demo_link' => 'http://demo.rtcamp.com/bpm-kaltura/',
46
- 'buy_now' => 'http://rtcamp.com/store/?add-to-cart=15446',
47
- 'coming_soon' => true
48
- )
49
- );
50
- $addons = apply_filters('bp_media_addons',$addons);
51
- foreach ( $addons as $addon ) {
52
- $this->addon( $addon );
53
- }
54
- }
55
 
56
- public function addon( $args ) {
57
- global $bp_media;
58
 
59
- $defaults = array(
60
- 'title' => '',
61
- 'img_src' => '',
62
- 'product_link' => '',
63
- 'desc' => '',
64
- 'price' => '',
65
- 'demo_link' => '',
66
- 'buy_now' => '',
67
- 'coming_soon' => false,
68
- );
69
- $args = wp_parse_args( $args, $defaults );
70
- extract( $args );
71
 
72
- $coming_soon ? ' coming-soon' : '';
73
 
74
- $coming_soon_div = ($coming_soon) ? $this->coming_soon_div() : '';
75
- $addon = '<div class="bp-media-addon">
76
  <a href="' . $product_link . '" title="' . $title . '" target="_blank">
77
  <img width="240" height="184" title="' . $title . '" alt="' . $title . '" src="' . $img_src . '">
78
  </a>
@@ -82,15 +81,15 @@ if ( ! class_exists( 'BPMediaAddon' ) ) {
82
  </div>
83
  <div class="product_footer">
84
  <span class="price alignleft"><span class="amount">' . $price . '</span></span>
85
- <a class="add_to_cart_button alignright product_type_simple" href="' . $buy_now . '" target="_blank">' . __( 'Buy Now', BP_MEDIA_TXT_DOMAIN ) . '</a>
86
- <a class="alignleft product_demo_link" href="' . $demo_link . '" title="' . $title . '" target="_blank">' . __( 'Live Demo', BP_MEDIA_TXT_DOMAIN ) . '</a>
87
  </div>'
88
- . $coming_soon_div .
89
- '</div>';
90
- echo $addon;
91
- }
92
 
93
- }
94
 
95
  }
96
  ?>
9
  * @author Gagandeep Singh <gagandeep.singh@rtcamp.com>
10
  * @author Joshua Abenazer <joshua.abenazer@rtcamp.com>
11
  */
12
+ if (!class_exists('BPMediaAddon')) {
13
 
14
+ class BPMediaAddon {
15
 
16
+ public $enquiry_link = 'http://rtcamp.com/contact';
17
 
18
+ public function coming_soon_div() {
19
+ return
20
+ '<div class="coming-soon coming-soon-l"></div>
21
+ <a class="coming-soon coming-soon-r" href="' . $this->enquiry_link . '" target="_blank">'
22
+ //<a></a>
23
+ . '</a>';
24
+ }
25
 
26
+ public function get_addons() {
27
+ $addons = array(
28
+ array(
29
+ 'title' => __('BuddyPress-Media Kaltura Add-on', BP_MEDIA_TXT_DOMAIN),
30
+ 'img_src' => 'http://cdn.rtcamp.com/files/2012/10/new-buddypress-media-kaltura-logo-240x184.png',
31
+ 'product_link' => 'http://rtcamp.com/store/buddypress-media-kaltura/',
32
+ 'desc' => '<p>' . __('Add support for more video formats using Kaltura video solution.', BP_MEDIA_TXT_DOMAIN) . '</p>
33
+ <p>' . __('Works with Kaltura.com, self-hosted Kaltura-CE and Kaltura-on-premise.', BP_MEDIA_TXT_DOMAIN) . '</p>',
34
+ 'price' => '$99',
35
+ 'demo_link' => 'http://demo.rtcamp.com/bpm-kaltura/',
36
+ 'buy_now' => 'http://rtcamp.com/store/?add-to-cart=15446'
37
+ ),
38
+ array(
39
+ 'title' => __('BuddyPress-Media FFMPEG Add-on', BP_MEDIA_TXT_DOMAIN),
40
+ 'img_src' => 'http://cdn.rtcamp.com/files/2012/09/ffmpeg-logo-240x184.png',
41
+ 'product_link' => 'http://rtcamp.com/store/buddypress-media-ffmpeg-converter/',
42
+ 'desc' => '<p>' . __('Add supports for more audio & video formats using open-source media-node.', BP_MEDIA_TXT_DOMAIN) . '</p>
43
+ <p>' . __('Media node comes with automated setup script for Ubuntu/Debian.', BP_MEDIA_TXT_DOMAIN) . '</p>',
44
+ 'price' => '$49',
45
+ 'demo_link' => 'http://demo.rtcamp.com/bpm-media',
46
+ 'buy_now' => 'http://rtcamp.com/store/?add-to-cart=13677'
47
+ )
48
+ );
49
+ $addons = apply_filters('bp_media_addons', $addons);
50
+ foreach ($addons as $addon) {
51
+ $this->addon($addon);
52
+ }
53
+ }
 
54
 
55
+ public function addon($args) {
56
+ global $bp_media;
57
 
58
+ $defaults = array(
59
+ 'title' => '',
60
+ 'img_src' => '',
61
+ 'product_link' => '',
62
+ 'desc' => '',
63
+ 'price' => '',
64
+ 'demo_link' => '',
65
+ 'buy_now' => '',
66
+ 'coming_soon' => false,
67
+ );
68
+ $args = wp_parse_args($args, $defaults);
69
+ extract($args);
70
 
71
+ $coming_soon ? ' coming-soon' : '';
72
 
73
+ $coming_soon_div = ($coming_soon) ? $this->coming_soon_div() : '';
74
+ $addon = '<div class="bp-media-addon">
75
  <a href="' . $product_link . '" title="' . $title . '" target="_blank">
76
  <img width="240" height="184" title="' . $title . '" alt="' . $title . '" src="' . $img_src . '">
77
  </a>
81
  </div>
82
  <div class="product_footer">
83
  <span class="price alignleft"><span class="amount">' . $price . '</span></span>
84
+ <a class="add_to_cart_button alignright product_type_simple" href="' . $buy_now . '" target="_blank">' . __('Buy Now', BP_MEDIA_TXT_DOMAIN) . '</a>
85
+ <a class="alignleft product_demo_link" href="' . $demo_link . '" title="' . $title . '" target="_blank">' . __('Live Demo', BP_MEDIA_TXT_DOMAIN) . '</a>
86
  </div>'
87
+ . $coming_soon_div .
88
+ '</div>';
89
+ echo $addon;
90
+ }
91
 
92
+ }
93
 
94
  }
95
  ?>
app/helper/BPMediaLog.php CHANGED
@@ -1,19 +1,65 @@
1
  <?php
2
 
3
  /**
4
- * Description of BPMediaLog
5
  *
6
- * @author Joshua Abenazer <joshua.abenazer@rtcamp.com>
7
  */
8
- if (!class_exists('BPMediaLog')) {
9
 
10
- class BPMediaLog {
11
 
12
- public function __construct() {
13
- ;
14
- }
 
 
 
 
 
 
 
 
 
 
 
15
 
16
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  }
19
  ?>
1
  <?php
2
 
3
  /**
4
+ * Logs a given message with an optional context string and timestamp
5
  *
6
+ * @author Saurabh Shukla <saurabh.shukla@rtcamp.com>
7
  */
8
+ if ( ! class_exists( 'BPMediaLog' ) ) {
9
 
10
+ class BPMediaLog {
11
 
12
+ /**
13
+ * Formats and logs the error message
14
+ *
15
+ * @param any $msg The message to log
16
+ * @param string $context The context string, optional
17
+ * @return boolean True if successful
18
+ */
19
+ public function __construct( $msg, $context = '', $log_file = '' ) {
20
+ $log_msg = $this->log_msg( $msg, $context = '' );
21
+ if ($log_file == ''){
22
+ $log_file = BP_MEDIA_PATH.'log/bpmedia.log';
23
+ }
24
+ return $this->log( $log_msg );
25
+ }
26
 
27
+ /**
28
+ * Formats the message
29
+ *
30
+ * @param any $msg The message to format
31
+ * @param string $context The context string, optional
32
+ * @return string The formatted log entry
33
+ */
34
+ function log_msg( $msg, $context = '' ) {
35
+ $logmsg = gmdate( "Y-m-d H:i:s " ) . " | ";
36
+ if ( $context ) {
37
+ $logmsg .= $context . " | ";
38
+ }
39
+ if ( ! is_string( $msg ) ) {
40
+ $msg = var_export( $msg, false );
41
+ }
42
+ $logmsg .= $msg;
43
+ return $logmsg;
44
+ }
45
+
46
+ /**
47
+ * Logs the entry to the log file
48
+ *
49
+ * @param string $logmsg The formatted log entry
50
+ * @param string $file The log file's path
51
+ * @return boolean Success
52
+ */
53
+ public function log( $logmsg, $file ) {
54
+ $fp = fopen( BP_MEDIA_PATH . 'plugin.log', "a+" );
55
+ if ( $fp ) {
56
+ fwrite( $fp, "\n" . $logmsg );
57
+ fclose( $fp );
58
+ }
59
+ return true;
60
+ }
61
+
62
+ }
63
 
64
  }
65
  ?>
app/helper/BPMediaSettings.php CHANGED
@@ -173,6 +173,7 @@ if (!class_exists('BPMediaSettings')) {
173
  'setting' => '',
174
  'option' => '',
175
  'desc' => '',
 
176
  );
177
  $args = wp_parse_args($args, $defaults);
178
  extract($args);
@@ -186,12 +187,12 @@ if (!class_exists('BPMediaSettings')) {
186
  $options = bp_get_option($setting);
187
  } else
188
  $name = $option;
189
-
190
  if ((isset($options[$option]) && empty($options[$option])) || !isset($options[$option])) {
191
  $options[$option] = '';
192
  }
193
  ?>
194
- <label for="<?php echo sanitize_title($option); ?>"><input value="<?php echo $options[$option]; ?>" name="<?php echo $name; ?>" id="<?php echo sanitize_title($option); ?>" type="text" /><?php
195
  if (!empty($desc)) {
196
  echo '<br /><span class="description">' . $desc . '</span>';
197
  }
@@ -237,7 +238,7 @@ if (!class_exists('BPMediaSettings')) {
237
  foreach ($values as $value => $text) {
238
  ?>
239
  <option<?php selected($options[$option], $value); ?> value="<?php echo $value; ?>"><?php echo $text; ?></option><?php }
240
- ?>
241
  </select><?php
242
  }
243
 
173
  'setting' => '',
174
  'option' => '',
175
  'desc' => '',
176
+ 'password' => false,
177
  );
178
  $args = wp_parse_args($args, $defaults);
179
  extract($args);
187
  $options = bp_get_option($setting);
188
  } else
189
  $name = $option;
190
+
191
  if ((isset($options[$option]) && empty($options[$option])) || !isset($options[$option])) {
192
  $options[$option] = '';
193
  }
194
  ?>
195
+ <label for="<?php echo sanitize_title($option); ?>"><input value="<?php echo $options[$option]; ?>" name="<?php echo $name; ?>" id="<?php echo sanitize_title($option); ?>" type="<?php echo $password ? 'password' : 'text'; ?>" /><?php
196
  if (!empty($desc)) {
197
  echo '<br /><span class="description">' . $desc . '</span>';
198
  }
238
  foreach ($values as $value => $text) {
239
  ?>
240
  <option<?php selected($options[$option], $value); ?> value="<?php echo $value; ?>"><?php echo $text; ?></option><?php }
241
+ ?>
242
  </select><?php
243
  }
244
 
app/helper/BPMediaSupport.php CHANGED
@@ -60,7 +60,7 @@ if (!class_exists('BPMediaSupport')) {
60
 
61
  </ul>
62
  </div><!-- .submit-bug-box --><?php if ($form == 'bug_report') { ?>
63
- <h3>Additional Information</h3>
64
  <div id="support-form" class="bp-media-form">
65
  <ul>
66
 
60
 
61
  </ul>
62
  </div><!-- .submit-bug-box --><?php if ($form == 'bug_report') { ?>
63
+ <h3><?php _e('Additional Information', BP_MEDIA_TXT_DOMAIN); ?></h3>
64
  <div id="support-form" class="bp-media-form">
65
  <ul>
66
 
app/helper/rtPluginInfo.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * A container class for holding and transforming various plugin metadata.
4
+ *
5
+ * @author faishal
6
+ */
7
+ class rtPluginInfo {
8
+ //Most fields map directly to the contents of the plugin's info.json file.
9
+ //See the relevant docs for a description of their meaning.
10
+ public $name;
11
+ public $slug;
12
+ public $version;
13
+ public $homepage;
14
+ public $sections;
15
+ public $download_url;
16
+
17
+ public $author;
18
+ public $author_homepage;
19
+
20
+ public $requires;
21
+ public $tested;
22
+ public $upgrade_notice;
23
+
24
+ public $rating;
25
+ public $num_ratings;
26
+ public $downloaded;
27
+ public $last_updated;
28
+
29
+ public $id = 0; //The native WP.org API returns numeric plugin IDs, but they're not used for anything.
30
+
31
+ /**
32
+ * Create a new instance of PluginInfo from JSON-encoded plugin info
33
+ * returned by an external update API.
34
+ *
35
+ * @param string $json Valid JSON string representing plugin info.
36
+ * @param bool $triggerErrors
37
+ * @return PluginInfo|null New instance of PluginInfo, or NULL on error.
38
+ */
39
+ public static function fromJson($json, $triggerErrors = false){
40
+ /** @var StdClass $apiResponse */
41
+ $apiResponse = json_decode($json);
42
+ if ( empty($apiResponse) || !is_object($apiResponse) ){
43
+ if ( $triggerErrors ) {
44
+ trigger_error(
45
+ "Failed to parse plugin metadata. Try validating your .json file with http://jsonlint.com/",
46
+ E_USER_NOTICE
47
+ );
48
+ }
49
+ return null;
50
+ }
51
+
52
+ //Very, very basic validation.
53
+ $valid = isset($apiResponse->name) && !empty($apiResponse->name) && isset($apiResponse->version) && !empty($apiResponse->version);
54
+ if ( !$valid ){
55
+ if ( $triggerErrors ) {
56
+ trigger_error(
57
+ "The plugin metadata file does not contain the required 'name' and/or 'version' keys.",
58
+ E_USER_NOTICE
59
+ );
60
+ }
61
+ return null;
62
+ }
63
+
64
+ $info = new self();
65
+ foreach(get_object_vars($apiResponse) as $key => $value){
66
+ $info->$key = $value;
67
+ }
68
+
69
+ return $info;
70
+ }
71
+
72
+ /**
73
+ * Transform plugin info into the format used by the native WordPress.org API
74
+ *
75
+ * @return object
76
+ */
77
+ public function toWpFormat(){
78
+ $info = new StdClass;
79
+
80
+ //The custom update API is built so that many fields have the same name and format
81
+ //as those returned by the native WordPress.org API. These can be assigned directly.
82
+ $sameFormat = array(
83
+ 'name', 'slug', 'version', 'requires', 'tested', 'rating', 'upgrade_notice',
84
+ 'num_ratings', 'downloaded', 'homepage', 'last_updated',
85
+ );
86
+ foreach($sameFormat as $field){
87
+ if ( isset($this->$field) ) {
88
+ $info->$field = $this->$field;
89
+ } else {
90
+ $info->$field = null;
91
+ }
92
+ }
93
+
94
+ //Other fields need to be renamed and/or transformed.
95
+ $info->download_link = $this->download_url;
96
+
97
+ if ( !empty($this->author_homepage) ){
98
+ $info->author = sprintf('<a href="%s">%s</a>', $this->author_homepage, $this->author);
99
+ } else {
100
+ $info->author = $this->author;
101
+ }
102
+
103
+ if ( is_object($this->sections) ){
104
+ $info->sections = get_object_vars($this->sections);
105
+ } elseif ( is_array($this->sections) ) {
106
+ $info->sections = $this->sections;
107
+ } else {
108
+ $info->sections = array('description' => '');
109
+ }
110
+
111
+ return $info;
112
+ }
113
+ }
114
+
115
+
116
+ ?>
app/helper/rtPluginUpdate.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Description of rtPluginUpdate
4
+ * A simple container class for holding information about an available update.
5
+ * @author faishal
6
+ */
7
+ class rtPluginUpdate {
8
+ public $id = 0;
9
+ public $slug;
10
+ public $version;
11
+ public $homepage;
12
+ public $download_url;
13
+ public $upgrade_notice;
14
+ private static $fields = array('id', 'slug', 'version', 'homepage', 'download_url', 'upgrade_notice');
15
+
16
+ /**
17
+ * Create a new instance of PluginUpdate from its JSON-encoded representation.
18
+ *
19
+ * @param string $json
20
+ * @param bool $triggerErrors
21
+ * @return PluginUpdate|null
22
+ */
23
+ public static function fromJson($json, $triggerErrors = false){
24
+ //Since update-related information is simply a subset of the full plugin info,
25
+ //we can parse the update JSON as if it was a plugin info string, then copy over
26
+ //the parts that we care about.
27
+ $pluginInfo = rtPluginInfo::fromJson($json, $triggerErrors);
28
+ if ( $pluginInfo != null ) {
29
+ return self::fromPluginInfo($pluginInfo);
30
+ } else {
31
+ return null;
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Create a new instance of PluginUpdate based on an instance of PluginInfo.
37
+ * Basically, this just copies a subset of fields from one object to another.
38
+ *
39
+ * @param PluginInfo $info
40
+ * @return PluginUpdate
41
+ */
42
+ public static function fromPluginInfo($info){
43
+ return self::fromObject($info);
44
+ }
45
+
46
+ /**
47
+ * Create a new instance of PluginUpdate by copying the necessary fields from
48
+ * another object.
49
+ *
50
+ * @param StdClass|PluginInfo|PluginUpdate $object The source object.
51
+ * @return PluginUpdate The new copy.
52
+ */
53
+ public static function fromObject($object) {
54
+ $update = new self();
55
+ foreach(self::$fields as $field){
56
+ $update->$field = $object->$field;
57
+ }
58
+ return $update;
59
+ }
60
+
61
+ /**
62
+ * Create an instance of StdClass that can later be converted back to
63
+ * a PluginUpdate. Useful for serialization and caching, as it avoids
64
+ * the "incomplete object" problem if the cached value is loaded before
65
+ * this class.
66
+ *
67
+ * @return StdClass
68
+ */
69
+ public function toStdClass() {
70
+ $object = new StdClass();
71
+ foreach(self::$fields as $field){
72
+ $object->$field = $this->$field;
73
+ }
74
+ return $object;
75
+ }
76
+
77
+
78
+ /**
79
+ * Transform the update into the format used by WordPress native plugin API.
80
+ *
81
+ * @return object
82
+ */
83
+ public function toWpFormat(){
84
+ $update = new StdClass;
85
+
86
+ $update->id = $this->id;
87
+ $update->slug = $this->slug;
88
+ $update->new_version = $this->version;
89
+ $update->url = $this->homepage;
90
+ $update->package = $this->download_url;
91
+ if ( !empty($this->upgrade_notice) ){
92
+ $update->upgrade_notice = $this->upgrade_notice;
93
+ }
94
+
95
+ return $update;
96
+ }
97
+ }
98
+ ?>
app/helper/rtPluginUpdateChecker.php ADDED
@@ -0,0 +1,569 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Description of rtUpdateCheker
5
+ * A custom plugin update checker.
6
+ *
7
+ * @author faishal
8
+ */
9
+ class rtPluginUpdateChecker {
10
+ public $metadataUrl = ''; //The URL of the plugin's metadata file.
11
+ public $pluginFile = ''; //Plugin filename relative to the plugins directory.
12
+ public $slug = ''; //Plugin slug.
13
+ public $checkPeriod = 12; //How often to check for updates (in hours).
14
+ public $optionName = ''; //Where to store the update info.
15
+
16
+ public $debugMode = true; //Set to TRUE to enable error reporting. Errors are raised using trigger_error()
17
+ //and should be logged to the standard PHP error log.
18
+
19
+ private $cronHook = null;
20
+ private $debugBarPlugin = null;
21
+
22
+ /**
23
+ * Class constructor.
24
+ *
25
+ * @param string $metadataUrl The URL of the plugin's metadata file.
26
+ * @param string $pluginFile Fully qualified path to the main plugin file.
27
+ * @param string $slug The plugin's 'slug'. If not specified, the filename part of $pluginFile sans '.php' will be used as the slug.
28
+ * @param integer $checkPeriod How often to check for updates (in hours). Defaults to checking every 12 hours. Set to 0 to disable automatic update checks.
29
+ * @param string $optionName Where to store book-keeping info about update checks. Defaults to 'external_updates-$slug'.
30
+ */
31
+ public function __construct($metadataUrl, $pluginFile, $slug = '', $checkPeriod = 12, $optionName = ''){
32
+ $this->metadataUrl = $metadataUrl;
33
+ $this->pluginFile = plugin_basename($pluginFile);
34
+ $this->checkPeriod = $checkPeriod;
35
+ $this->slug = $slug;
36
+ $this->optionName = $optionName;
37
+ $this->debugMode = defined('WP_DEBUG') && WP_DEBUG;
38
+
39
+ //If no slug is specified, use the name of the main plugin file as the slug.
40
+ //For example, 'my-cool-plugin/cool-plugin.php' becomes 'cool-plugin'.
41
+ if ( empty($this->slug) ){
42
+ $this->slug = basename($this->pluginFile, '.php');
43
+ }
44
+
45
+ if ( empty($this->optionName) ){
46
+ $this->optionName = 'external_updates-' . $this->slug;
47
+ }
48
+
49
+ $this->installHooks();
50
+ }
51
+
52
+ /**
53
+ * Install the hooks required to run periodic update checks and inject update info
54
+ * into WP data structures.
55
+ *
56
+ * @return void
57
+ */
58
+ protected function installHooks(){
59
+ //Override requests for plugin information
60
+ add_filter('plugins_api', array($this, 'injectInfo'), 20, 3);
61
+
62
+ //Insert our update info into the update array maintained by WP
63
+ add_filter('site_transient_update_plugins', array($this,'injectUpdate')); //WP 3.0+
64
+ add_filter('transient_update_plugins', array($this,'injectUpdate')); //WP 2.8+
65
+
66
+ add_filter('plugin_row_meta', array($this, 'addCheckForUpdatesLink'), 10, 4);
67
+ add_action('admin_init', array($this, 'handleManualCheck'));
68
+ add_action('all_admin_notices', array($this, 'displayManualCheckResult'));
69
+
70
+ //Set up the periodic update checks
71
+ $this->cronHook = 'check_plugin_updates-' . $this->slug;
72
+ if ( $this->checkPeriod > 0 ){
73
+
74
+ //Trigger the check via Cron
75
+ add_filter('cron_schedules', array($this, '_addCustomSchedule'));
76
+ if ( !wp_next_scheduled($this->cronHook) && !defined('WP_INSTALLING') ) {
77
+ $scheduleName = 'every' . $this->checkPeriod . 'hours';
78
+ wp_schedule_event(time(), $scheduleName, $this->cronHook);
79
+ }
80
+ add_action($this->cronHook, array($this, 'checkForUpdates'));
81
+
82
+ register_deactivation_hook($this->pluginFile, array($this, '_removeUpdaterCron'));
83
+
84
+ //In case Cron is disabled or unreliable, we also manually trigger
85
+ //the periodic checks while the user is browsing the Dashboard.
86
+ add_action( 'admin_init', array($this, 'maybeCheckForUpdates') );
87
+
88
+ } else {
89
+ //Periodic checks are disabled.
90
+ wp_clear_scheduled_hook($this->cronHook);
91
+ }
92
+
93
+ add_action('plugins_loaded', array($this, 'initDebugBarPanel'));
94
+ }
95
+
96
+ /**
97
+ * Add our custom schedule to the array of Cron schedules used by WP.
98
+ *
99
+ * @param array $schedules
100
+ * @return array
101
+ */
102
+ public function _addCustomSchedule($schedules){
103
+ if ( $this->checkPeriod && ($this->checkPeriod > 0) ){
104
+ $scheduleName = 'every' . $this->checkPeriod . 'hours';
105
+ $schedules[$scheduleName] = array(
106
+ 'interval' => $this->checkPeriod * 3600,
107
+ 'display' => sprintf('Every %d hours', $this->checkPeriod),
108
+ );
109
+ }
110
+ return $schedules;
111
+ }
112
+
113
+ /**
114
+ * Remove the scheduled cron event that the library uses to check for updates.
115
+ *
116
+ * @return void
117
+ */
118
+ public function _removeUpdaterCron(){
119
+ wp_clear_scheduled_hook($this->cronHook);
120
+ }
121
+
122
+ /**
123
+ * Get the name of the update checker's WP-cron hook. Mostly useful for debugging.
124
+ *
125
+ * @return string
126
+ */
127
+ public function getCronHookName() {
128
+ return $this->cronHook;
129
+ }
130
+
131
+ /**
132
+ * Retrieve plugin info from the configured API endpoint.
133
+ *
134
+ * @uses wp_remote_get()
135
+ *
136
+ * @param array $queryArgs Additional query arguments to append to the request. Optional.
137
+ * @return PluginInfo
138
+ */
139
+ public function requestInfo($queryArgs = array()){
140
+ //Query args to append to the URL. Plugins can add their own by using a filter callback (see addQueryArgFilter()).
141
+ $installedVersion = $this->getInstalledVersion();
142
+ $queryArgs['installed_version'] = ($installedVersion !== null) ? $installedVersion : '';
143
+ $queryArgs['admin_email'] = get_option("admin_email");
144
+ $queryArgs['slug'] =$this->slug;
145
+ $queryArgs = apply_filters('puc_request_info_query_args-'.$this->slug, $queryArgs);
146
+ //Various options for the wp_remote_get() call. Plugins can filter these, too.
147
+ $options = array(
148
+ 'timeout' => 10, //seconds
149
+ 'headers' => array(
150
+ 'Accept' => 'application/json'
151
+ ),
152
+ );
153
+ $options = apply_filters('puc_request_info_options-'.$this->slug, $options);
154
+
155
+ //The plugin info should be at 'http://your-api.com/url/here/$slug/info.json'
156
+ $url = $this->metadataUrl;
157
+ if ( !empty($queryArgs) ){
158
+ $url = add_query_arg($queryArgs, $url);
159
+ }
160
+
161
+ $result = wp_remote_get(
162
+ $url,
163
+ $options
164
+ );
165
+ //Try to parse the response
166
+ $pluginInfo = null;
167
+ if ( !is_wp_error($result) && isset($result['response']['code']) && ($result['response']['code'] == 200) && !empty($result['body']) ){
168
+ $pluginInfo = rtPluginInfo::fromJson($result['body'], $this->debugMode);
169
+
170
+ } else if ( $this->debugMode ) {
171
+ $message = sprintf("The URL %s does not point to a valid plugin metadata file. ", $url);
172
+ if ( is_wp_error($result) ) {
173
+ $message .= "WP HTTP error: " . $result->get_error_message();
174
+ } else if ( isset($result['response']['code']) ) {
175
+ $message .= "HTTP response code is " . $result['response']['code'] . " (expected: 200)";
176
+ } else {
177
+ $message .= "wp_remote_get() returned an unexpected result.";
178
+ }
179
+ trigger_error($message, E_USER_WARNING);
180
+ }
181
+
182
+ $pluginInfo = apply_filters('puc_request_info_result-'.$this->slug, $pluginInfo, $result);
183
+ return $pluginInfo;
184
+ }
185
+
186
+ /**
187
+ * Retrieve the latest update (if any) from the configured API endpoint.
188
+ *
189
+ * @uses PluginUpdateChecker::requestInfo()
190
+ *
191
+ * @return PluginUpdate An instance of PluginUpdate, or NULL when no updates are available.
192
+ */
193
+ public function requestUpdate(){
194
+ //For the sake of simplicity, this function just calls requestInfo()
195
+ //and transforms the result accordingly.
196
+ $pluginInfo = $this->requestInfo(array('checking_for_updates' => '1'));
197
+ if ( $pluginInfo == null ){
198
+ return null;
199
+ }
200
+ return rtPluginUpdate::fromPluginInfo($pluginInfo);
201
+ }
202
+
203
+ /**
204
+ * Get the currently installed version of the plugin.
205
+ *
206
+ * @return string Version number.
207
+ */
208
+ public function getInstalledVersion(){
209
+
210
+ if ( !function_exists('get_plugins') ){
211
+ if(is_multisite()){
212
+ require_once( ABSPATH .'/wp-admin/network/includes/plugin.php' );
213
+ }else{require_once( ABSPATH .'/wp-admin/includes/plugin.php' );}
214
+ }
215
+ $allPlugins = get_plugins();
216
+ if ( array_key_exists($this->pluginFile, $allPlugins) && array_key_exists('Version', $allPlugins[$this->pluginFile]) ){
217
+ return $allPlugins[$this->pluginFile]['Version'];
218
+
219
+ } else {
220
+ //This can happen if the filename is wrong or the plugin is installed in mu-plugins.
221
+ if ( $this->debugMode ) {
222
+ trigger_error(
223
+ sprintf(
224
+ "Can't to read the Version header for %s. The filename may be incorrect, or the file is not present in /wp-content/plugins.",
225
+ $this->pluginFile
226
+ ),
227
+ E_USER_WARNING
228
+ );
229
+ }
230
+ return null;
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Check for plugin updates.
236
+ * The results are stored in the DB option specified in $optionName.
237
+ *
238
+ * @return PluginUpdate|null
239
+ */
240
+ public function checkForUpdates(){
241
+ $installedVersion = $this->getInstalledVersion();
242
+ //Fail silently if we can't find the plugin or read its header.
243
+ if ( $installedVersion === null ) {
244
+ if ( $this->debugMode ) {
245
+ trigger_error(
246
+ sprintf('Skipping update check for %s - installed version unknown.', $this->pluginFile),
247
+ E_USER_WARNING
248
+ );
249
+ }
250
+ return null;
251
+ }
252
+
253
+ $state = $this->getUpdateState();
254
+ if ( empty($state) ){
255
+ $state = new StdClass;
256
+ $state->lastCheck = 0;
257
+ $state->checkedVersion = '';
258
+ $state->update = null;
259
+ }
260
+
261
+ $state->lastCheck = time();
262
+ $state->checkedVersion = $installedVersion;
263
+ $this->setUpdateState($state); //Save before checking in case something goes wrong
264
+
265
+ $state->update = $this->requestUpdate();
266
+ $this->setUpdateState($state);
267
+
268
+ return $this->getUpdate();
269
+ }
270
+
271
+ /**
272
+ * Check for updates only if the configured check interval has already elapsed.
273
+ *
274
+ * @return void
275
+ */
276
+ public function maybeCheckForUpdates(){
277
+ if ( empty($this->checkPeriod) ){
278
+ return;
279
+ }
280
+ $state = $this->getUpdateState();
281
+
282
+ $shouldCheck =
283
+ empty($state) ||
284
+ !isset($state->lastCheck) ||
285
+ ( (time() - $state->lastCheck) >= $this->checkPeriod*3600 );
286
+
287
+ if ( $shouldCheck ){
288
+ $this->checkForUpdates();
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Load the update checker state from the DB.
294
+ *
295
+ * @return StdClass|null
296
+ */
297
+ public function getUpdateState() {
298
+ $state = get_site_option($this->optionName, null);
299
+ if ( empty($state) || !is_object($state)) {
300
+ $state = null;
301
+ }
302
+
303
+ if ( !empty($state) && isset($state->update) && is_object($state->update) ){
304
+ $state->update = rtPluginUpdate::fromObject($state->update);
305
+ }
306
+ return $state;
307
+ }
308
+
309
+
310
+ /**
311
+ * Persist the update checker state to the DB.
312
+ *
313
+ * @param StdClass $state
314
+ * @return void
315
+ */
316
+ private function setUpdateState($state) {
317
+ if ( isset($state->update) && is_object($state->update) && method_exists($state->update, 'toStdClass') ) {
318
+ $update = $state->update; /** @var PluginUpdate $update */
319
+ $state->update = $update->toStdClass();
320
+ }
321
+ update_site_option($this->optionName, $state);
322
+ }
323
+
324
+ /**
325
+ * Reset update checker state - i.e. last check time, cached update data and so on.
326
+ *
327
+ * Call this when your plugin is being uninstalled, or if you want to
328
+ * clear the update cache.
329
+ */
330
+ public function resetUpdateState() {
331
+ delete_site_option($this->optionName);
332
+ }
333
+
334
+ /**
335
+ * Intercept plugins_api() calls that request information about our plugin and
336
+ * use the configured API endpoint to satisfy them.
337
+ *
338
+ * @see plugins_api()
339
+ *
340
+ * @param mixed $result
341
+ * @param string $action
342
+ * @param array|object $args
343
+ * @return mixed
344
+ */
345
+ public function injectInfo($result, $action = null, $args = null){
346
+ $relevant = ($action == 'plugin_information') && isset($args->slug) && ($args->slug == $this->slug);
347
+ if ( !$relevant ){
348
+ return $result;
349
+ }
350
+
351
+ $pluginInfo = $this->requestInfo();
352
+ $pluginInfo = apply_filters('puc_pre_inject_info-' . $this->slug, $pluginInfo);
353
+ if ($pluginInfo){
354
+ return $pluginInfo->toWpFormat();
355
+ }
356
+
357
+ return $result;
358
+ }
359
+
360
+ /**
361
+ * Insert the latest update (if any) into the update list maintained by WP.
362
+ *
363
+ * @param StdClass $updates Update list.
364
+ * @return StdClass Modified update list.
365
+ */
366
+ public function injectUpdate($updates){
367
+ //Is there an update to insert?
368
+ $update = $this->getUpdate();
369
+ if ( !empty($update) ) {
370
+ //Let plugins filter the update info before it's passed on to WordPress.
371
+ $update = apply_filters('puc_pre_inject_update-' . $this->slug, $update);
372
+ if ( !is_object($updates) ) {
373
+ $updates = new StdClass();
374
+ $updates->response = array();
375
+ }
376
+ $updates->response[$this->pluginFile] = $update->toWpFormat();
377
+ } else if ( isset($updates, $updates->response) ) {
378
+ unset($updates->response[$this->pluginFile]);
379
+ }
380
+
381
+ return $updates;
382
+ }
383
+
384
+ /**
385
+ * Get the details of the currently available update, if any.
386
+ *
387
+ * If no updates are available, or if the last known update version is below or equal
388
+ * to the currently installed version, this method will return NULL.
389
+ *
390
+ * Uses cached update data. To retrieve update information straight from
391
+ * the metadata URL, call requestUpdate() instead.
392
+ *
393
+ * @return PluginUpdate|null
394
+ */
395
+ public function getUpdate() {
396
+ $state = $this->getUpdateState(); /** @var StdClass $state */
397
+
398
+ //Is there an update available insert?
399
+ if ( !empty($state) && isset($state->update) && !empty($state->update) ){
400
+ $update = $state->update;
401
+ //Check if the update is actually newer than the currently installed version.
402
+ $installedVersion = $this->getInstalledVersion();
403
+ if ( ($installedVersion !== null) && version_compare($update->version, $installedVersion, '>') ){
404
+ return $update;
405
+ }
406
+ }
407
+ return null;
408
+ }
409
+
410
+ /**
411
+ * Add a "Check for updates" link to the plugin row in the "Plugins" page. By default,
412
+ * the new link will appear after the "Visit plugin site" link.
413
+ *
414
+ * You can change the link text by using the "puc_manual_check_link-$slug" filter.
415
+ * Returning an empty string from the filter will disable the link.
416
+ *
417
+ * @param array $pluginMeta Array of meta links.
418
+ * @param string $pluginFile
419
+ * @param array|null $pluginData Currently ignored.
420
+ * @param string|null $status Currently ignored.
421
+ * @return array
422
+ */
423
+ public function addCheckForUpdatesLink($pluginMeta, $pluginFile, $pluginData = null, $status = null) {
424
+ if ( $pluginFile == $this->pluginFile && current_user_can('update_plugins') ) {
425
+ $linkUrl = wp_nonce_url(
426
+ add_query_arg(
427
+ array(
428
+ 'puc_check_for_updates' => 1,
429
+ 'puc_slug' => $this->slug,
430
+ ),
431
+ is_network_admin() ? network_admin_url('plugins.php') : admin_url('plugins.php')
432
+ ),
433
+ 'puc_check_for_updates'
434
+ );
435
+
436
+ $linkText = apply_filters('puc_manual_check_link-' . $this->slug, 'Check for updates');
437
+ if ( !empty($linkText) ) {
438
+ $pluginMeta[] = sprintf('<a href="%s">%s</a>', esc_attr($linkUrl), $linkText);
439
+ }
440
+ }
441
+ return $pluginMeta;
442
+ }
443
+
444
+ /**
445
+ * Check for updates when the user clicks the "Check for updates" link.
446
+ * @see self::addCheckForUpdatesLink()
447
+ *
448
+ * @return void
449
+ */
450
+ public function handleManualCheck() {
451
+ $shouldCheck =
452
+ isset($_GET['puc_check_for_updates'], $_GET['puc_slug'])
453
+ && $_GET['puc_slug'] == $this->slug
454
+ && current_user_can('update_plugins')
455
+ && check_admin_referer('puc_check_for_updates');
456
+
457
+ if ( $shouldCheck ) {
458
+ $update = $this->checkForUpdates();
459
+ $status = ($update === null) ? 'no_update' : 'update_available';
460
+ wp_redirect(add_query_arg(
461
+ array(
462
+ 'puc_update_check_result' => $status,
463
+ 'puc_slug' => $this->slug,
464
+ ),
465
+ is_network_admin() ? network_admin_url('plugins.php') : admin_url('plugins.php')
466
+ ));
467
+ }
468
+ }
469
+
470
+ /**
471
+ * Display the results of a manual update check.
472
+ * @see self::handleManualCheck()
473
+ *
474
+ * You can change the result message by using the "puc_manual_check_message-$slug" filter.
475
+ */
476
+ public function displayManualCheckResult() {
477
+ if ( isset($_GET['puc_update_check_result'], $_GET['puc_slug']) && ($_GET['puc_slug'] == $this->slug) ) {
478
+ $status = strval($_GET['puc_update_check_result']);
479
+ if ( $status == 'no_update' ) {
480
+ $message = 'This plugin is up to date.';
481
+ } else if ( $status == 'update_available' ) {
482
+ $message = 'A new version of this plugin is available.';
483
+ } else {
484
+ $message = sprintf('Unknown update checker status "%s"', htmlentities($status));
485
+ }
486
+ printf(
487
+ '<div class="updated"><p>%s</p></div>',
488
+ apply_filters('puc_manual_check_message-' . $this->slug, $message, $status)
489
+ );
490
+ }
491
+ }
492
+
493
+ /**
494
+ * Register a callback for filtering query arguments.
495
+ *
496
+ * The callback function should take one argument - an associative array of query arguments.
497
+ * It should return a modified array of query arguments.
498
+ *
499
+ * @uses add_filter() This method is a convenience wrapper for add_filter().
500
+ *
501
+ * @param callable $callback
502
+ * @return void
503
+ */
504
+ public function addQueryArgFilter($callback){
505
+ add_filter('puc_request_info_query_args-'.$this->slug, $callback);
506
+ }
507
+
508
+ /**
509
+ * Register a callback for filtering arguments passed to wp_remote_get().
510
+ *
511
+ * The callback function should take one argument - an associative array of arguments -
512
+ * and return a modified array or arguments. See the WP documentation on wp_remote_get()
513
+ * for details on what arguments are available and how they work.
514
+ *
515
+ * @uses add_filter() This method is a convenience wrapper for add_filter().
516
+ *
517
+ * @param callable $callback
518
+ * @return void
519
+ */
520
+ public function addHttpRequestArgFilter($callback){
521
+ add_filter('puc_request_info_options-'.$this->slug, $callback);
522
+ }
523
+
524
+ /**
525
+ * Register a callback for filtering the plugin info retrieved from the external API.
526
+ *
527
+ * The callback function should take two arguments. If the plugin info was retrieved
528
+ * successfully, the first argument passed will be an instance of PluginInfo. Otherwise,
529
+ * it will be NULL. The second argument will be the corresponding return value of
530
+ * wp_remote_get (see WP docs for details).
531
+ *
532
+ * The callback function should return a new or modified instance of PluginInfo or NULL.
533
+ *
534
+ * @uses add_filter() This method is a convenience wrapper for add_filter().
535
+ *
536
+ * @param callable $callback
537
+ * @return void
538
+ */
539
+ public function addResultFilter($callback){
540
+ add_filter('puc_request_info_result-'.$this->slug, $callback, 10, 2);
541
+ }
542
+
543
+ /**
544
+ * Register a callback for one of the update checker filters.
545
+ *
546
+ * Identical to add_filter(), except it automatically adds the "puc_" prefix
547
+ * and the "-$plugin_slug" suffix to the filter name. For example, "request_info_result"
548
+ * becomes "puc_request_info_result-your_plugin_slug".
549
+ *
550
+ * @param string $tag
551
+ * @param callable $callback
552
+ * @param int $priority
553
+ * @param int $acceptedArgs
554
+ */
555
+ public function addFilter($tag, $callback, $priority = 10, $acceptedArgs = 1) {
556
+ add_filter('puc_' . $tag . '-' . $this->slug, $callback, $priority, $acceptedArgs);
557
+ }
558
+
559
+ /**
560
+ * Initialize the update checker Debug Bar plugin/add-on thingy.
561
+ */
562
+ public function initDebugBarPanel() {
563
+ if ( class_exists('Debug_Bar') ) {
564
+ require_once dirname(__FILE__) . '/debug-bar-plugin.php';
565
+ $this->debugBarPlugin = new PucDebugBarPlugin($this);
566
+ }
567
+ }
568
+ }
569
+ ?>
app/main/BuddyPressMedia.php CHANGED
@@ -212,7 +212,7 @@ class BuddyPressMedia {
212
 
213
  function settings_link($links, $file) {
214
  /* create link */
215
- $plugin_name = plugin_basename(__FILE__);
216
  $admin_link = $this->get_admin_url(add_query_arg(array('page' => 'bp-media-settings'), 'admin.php'));
217
  if ($file == $plugin_name) {
218
  array_unshift(
212
 
213
  function settings_link($links, $file) {
214
  /* create link */
215
+ $plugin_name = plugin_basename(BP_MEDIA_PATH.'index.php');
216
  $admin_link = $this->get_admin_url(add_query_arg(array('page' => 'bp-media-settings'), 'admin.php'));
217
  if ($file == $plugin_name) {
218
  array_unshift(
app/main/group/BPMediaGroupAction.php CHANGED
@@ -78,7 +78,7 @@ class BPMediaGroupAction {
78
  'meta_compare' => '='
79
  );
80
  $bp_media_albums_query = new WP_Query($args);
81
-
82
  }
83
  }
84
 
78
  'meta_compare' => '='
79
  );
80
  $bp_media_albums_query = new WP_Query($args);
81
+
82
  }
83
  }
84
 
app/main/includes/BPMediaActions.php CHANGED
@@ -45,7 +45,7 @@ class BPMediaActions {
45
  /** This section can help in the group activity handling */
46
  if (isset($_POST['bp_media_group_id']) && intval($_POST['bp_media_group_id'])) {
47
  remove_action('bp_media_after_add_media', 'BPMediaActions::activity_create_after_add_media', 10, 2);
48
- add_action('bp_media_after_add_media', 'BPMediaGroupAction::groups_activity_create_after_add_media', 10, 2);
49
  add_filter('bp_media_force_hide_activity', 'BPMediaGroupAction::bp_media_groups_force_hide_activity');
50
  }
51
  /* @var $bp_media_entry BPMediaHostWordpress */
@@ -561,9 +561,11 @@ class BPMediaActions {
561
  return false;
562
  }
563
  }
 
 
564
  $args = array(
565
  'action' => apply_filters('bp_media_added_media', sprintf(__('%1$s added a %2$s', BP_MEDIA_TXT_DOMAIN), bp_core_get_userlink($media->get_author()), '<a href="' . $media->get_url() . '">' . $media->get_media_activity_type() . '</a>')),
566
- 'content' => $media->get_media_activity_content(),
567
  'primary_link' => $media->get_url(),
568
  'item_id' => $media->get_id(),
569
  'type' => 'media_upload',
45
  /** This section can help in the group activity handling */
46
  if (isset($_POST['bp_media_group_id']) && intval($_POST['bp_media_group_id'])) {
47
  remove_action('bp_media_after_add_media', 'BPMediaActions::activity_create_after_add_media', 10, 2);
48
+ add_action('bp_media_after_add_media', 'BPMediaGroupAction::bp_media_groups_activity_create_after_add_media', 10, 2);
49
  add_filter('bp_media_force_hide_activity', 'BPMediaGroupAction::bp_media_groups_force_hide_activity');
50
  }
51
  /* @var $bp_media_entry BPMediaHostWordpress */
561
  return false;
562
  }
563
  }
564
+ $activity_content = $media->get_media_activity_content();
565
+ new BPMediaLog($activity_content);
566
  $args = array(
567
  'action' => apply_filters('bp_media_added_media', sprintf(__('%1$s added a %2$s', BP_MEDIA_TXT_DOMAIN), bp_core_get_userlink($media->get_author()), '<a href="' . $media->get_url() . '">' . $media->get_media_activity_type() . '</a>')),
568
+ 'content' => $activity_content,
569
  'primary_link' => $media->get_url(),
570
  'item_id' => $media->get_id(),
571
  'type' => 'media_upload',
app/main/includes/BPMediaFunction.php CHANGED
@@ -4,9 +4,9 @@ class BPMediaFunction {
4
 
5
  function __construct() {
6
  //add_action('bp_init', array($this,'swap_filters'));
7
- add_filter('bp_get_activity_content_body', array($this,'conditional_override_allowed_tags'), 1, 2);
8
- add_action('wp_ajax_my_featured_action', array($this,'implement_featured_ajax'));
9
- add_action('wp_ajax_nopriv_my_featured_action', array($this,'implement_featured_ajax'));
10
  }
11
 
12
  static function record_activity($args = '') {
@@ -43,6 +43,7 @@ class BPMediaFunction {
43
  $activity_allowedtags['audio']['title'] = array();
44
  $activity_allowedtags['script'] = array();
45
  $activity_allowedtags['script']['type'] = array();
 
46
  $activity_allowedtags['div'] = array();
47
  $activity_allowedtags['div']['id'] = array();
48
  $activity_allowedtags['div']['class'] = array();
@@ -70,24 +71,25 @@ class BPMediaFunction {
70
  }
71
  echo '</div>';
72
  }
73
- /*
74
- function remove_kses_filter(){
75
- global $bp_media;
76
- if ($activity != null && in_array($activity->type, $bp_media->activity_types)) {
77
- remove_filter('bp_get_activity_content_body', 'bp_activity_filter_kses', 1);
78
- }
79
- }
80
- *
81
- */
 
 
82
  function conditional_override_allowed_tags($content, $activity = null) {
83
- global $bp_media;
84
 
85
  if ($activity != null && in_array($activity->type, $bp_media->activity_types)) {
86
  add_filter('bp_activity_allowed_tags', 'BPMediaFunction::override_allowed_tags', 1);
87
- }
88
-
89
- return $content;
90
 
 
91
  }
92
 
93
  function swap_filters() {
4
 
5
  function __construct() {
6
  //add_action('bp_init', array($this,'swap_filters'));
7
+ add_filter('bp_get_activity_action', array($this, 'conditional_override_allowed_tags'), 1, 2);
8
+ add_action('wp_ajax_my_featured_action', array($this, 'implement_featured_ajax'));
9
+ add_action('wp_ajax_nopriv_my_featured_action', array($this, 'implement_featured_ajax'));
10
  }
11
 
12
  static function record_activity($args = '') {
43
  $activity_allowedtags['audio']['title'] = array();
44
  $activity_allowedtags['script'] = array();
45
  $activity_allowedtags['script']['type'] = array();
46
+ $activity_allowedtags['script']['src'] = array();
47
  $activity_allowedtags['div'] = array();
48
  $activity_allowedtags['div']['id'] = array();
49
  $activity_allowedtags['div']['class'] = array();
71
  }
72
  echo '</div>';
73
  }
74
+
75
+ /*
76
+ function remove_kses_filter(){
77
+ global $bp_media;
78
+ if ($activity != null && in_array($activity->type, $bp_media->activity_types)) {
79
+ remove_filter('bp_get_activity_content_body', 'bp_activity_filter_kses', 1);
80
+ }
81
+ }
82
+ *
83
+ */
84
+
85
  function conditional_override_allowed_tags($content, $activity = null) {
86
+ global $bp_media;
87
 
88
  if ($activity != null && in_array($activity->type, $bp_media->activity_types)) {
89
  add_filter('bp_activity_allowed_tags', 'BPMediaFunction::override_allowed_tags', 1);
90
+ }
 
 
91
 
92
+ return $content;
93
  }
94
 
95
  function swap_filters() {
app/main/includes/BPMediaHostWordpress.php CHANGED
@@ -210,18 +210,18 @@ class BPMediaHostWordpress {
210
  case 'video' :
211
  if ($this->thumbnail_id) {
212
  $image_array = image_downsize($this->thumbnail_id, 'bp_media_activity_image');
213
- $activity_content.='<video poster="' . $image_array[0] . '" src="' . wp_get_attachment_url($attachment_id) . '" width="320" height="240" type="video/mp4" id="bp_media_video_' . $this->id . '_' . $bp_media_counter . '" controls="controls" preload="none"></video></span><script>bp_media_create_element("bp_media_video_' . $this->id . '_' . $bp_media_counter . '");</script>';
214
  } else {
215
- $activity_content.='<video src="' . wp_get_attachment_url($attachment_id) . '" width="320" height="240" type="video/mp4" id="bp_media_video_' . $this->id . '_' . $bp_media_counter . '" controls="controls" preload="none"></video></span><script>bp_media_create_element("bp_media_video_' . $this->id . '_' . $bp_media_counter . '");</script>';
216
  }
217
  break;
218
  case 'audio' :
219
- $activity_content.='<audio src="' . wp_get_attachment_url($attachment_id) . '" width="320" type="audio/mp3" id="bp_media_audio_' . $this->id . '_' . $bp_media_counter . '" controls="controls" preload="none" ></audio></span><script>bp_media_create_element("bp_media_audio_' . $this->id . '_' . $bp_media_counter . '");</script>';
220
  $type = 'audio';
221
  break;
222
  case 'image' :
223
  $image_array = image_downsize($attachment_id, 'bp_media_activity_image');
224
- $activity_content.='<a href="' . $this->url . '" title="' . __($this->name, BP_MEDIA_TXT_DOMAIN) . '"><img src="' . $image_array[0] . '" id="bp_media_image_' . $this->id . '_' . $bp_media_counter++ . '" alt="' . __($this->name, BP_MEDIA_TXT_DOMAIN) . '" /></a>';
225
  $type = 'image';
226
  break;
227
  default :
@@ -316,7 +316,7 @@ class BPMediaHostWordpress {
316
  ?>
317
  <li>
318
  <a href="<?php echo $this->url ?>" title="<?php _e($this->description, BP_MEDIA_TXT_DOMAIN); ?>">
319
- <img src="<?php echo $thumb_url; ?>" />
320
  </a>
321
  <h3 title="<?php echo $this->name; ?>"><a href="<?php echo $this->url ?>" title="<?php _e($this->description, BP_MEDIA_TXT_DOMAIN); ?>"><?php echo $this->name; ?></a></h3>
322
  </li>
@@ -376,17 +376,18 @@ class BPMediaHostWordpress {
376
  'include' => $activity_id,
377
  'max' => 1
378
  ))) {
379
- while (bp_activities()) { bp_the_activity();
 
380
  do_action('bp_before_activity_entry');
381
  ?>
382
  <div class="activity">
383
  <ul id="activity-stream" class="activity-list item-list">
384
  <li class="activity activity_update" id="activity-<?php echo $activity_id; ?>">
385
  <div class="activity-content">
386
- <?php do_action('bp_activity_entry_content'); ?>
387
- <?php if (is_user_logged_in()) : ?>
388
  <div class="activity-meta no-ajax">
389
- <?php if (bp_activity_can_comment()) : ?>
390
  <a href="<?php bp_get_activity_comment_link(); ?>" class="button acomment-reply bp-primary-action" id="acomment-comment-<?php bp_activity_id(); ?>"><?php printf(__('Comment <span>%s</span>', BP_MEDIA_TXT_DOMAIN), bp_activity_get_comment_count()); ?></a>
391
  <?php endif; ?>
392
  <?php if (bp_activity_can_favorite()) : ?>
@@ -397,15 +398,15 @@ class BPMediaHostWordpress {
397
  <?php endif; ?>
398
  <?php endif; ?>
399
  <?php if (bp_activity_user_can_delete()) bp_activity_delete_link(); ?>
400
- <?php do_action('bp_activity_entry_meta'); ?>
401
  </div>
402
- <?php endif; ?>
403
  </div>
404
- <?php do_action('bp_before_activity_entry_comments'); ?>
405
  <?php if (( is_user_logged_in() && bp_activity_can_comment() ) || bp_activity_get_comment_count()) : ?>
406
  <div class="activity-comments">
407
- <?php bp_activity_comments(); ?>
408
- <?php if (is_user_logged_in()) : ?>
409
  <form action="<?php bp_activity_comment_form_action(); ?>" method="post" id="ac-form-<?php bp_activity_id(); ?>" class="ac-form"<?php bp_activity_comment_form_nojs_display(); ?>>
410
  <div class="ac-reply-avatar"><?php bp_loggedin_user_avatar('width=' . BP_AVATAR_THUMB_WIDTH . '&height=' . BP_AVATAR_THUMB_HEIGHT); ?></div>
411
  <div class="ac-reply-content">
@@ -415,27 +416,27 @@ class BPMediaHostWordpress {
415
  <input type="submit" name="ac_form_submit" value="<?php _e('Post', BP_MEDIA_TXT_DOMAIN); ?>" /> &nbsp; <?php _e('or press esc to cancel.', BP_MEDIA_TXT_DOMAIN); ?>
416
  <input type="hidden" name="comment_form_id" value="<?php bp_activity_id(); ?>" />
417
  </div>
418
- <?php do_action('bp_activity_entry_comments'); ?>
419
- <?php wp_nonce_field('new_activity_comment', '_wpnonce_new_activity_comment'); ?>
420
  </form>
421
- <?php endif; ?>
422
- </div>
423
  <?php endif; ?>
424
- <?php do_action('bp_after_activity_entry_comments'); ?>
 
 
425
  </li>
426
  </ul>
427
  </div>
428
  <?php
429
  }
430
- }
431
- else{
432
  ?>
433
  <div class="activity">
434
  <ul id="activity-stream" class="activity-list item-list">
435
  <li class="activity activity_update" id="activity-<?php echo $activity_id; ?>">
436
  <div class="activity-content">
437
- <?php do_action('bp_activity_entry_content'); ?>
438
- <?php if (is_user_logged_in()) : ?>
439
  <div class="activity-meta no-ajax">
440
  <a href="<?php echo $this->get_delete_url(); ?>" class="button item-button bp-secondary-action delete-activity-single confirm" rel="nofollow"><?php _e("Delete", BP_MEDIA_TXT_DOMAIN); ?></a>
441
  </div>
@@ -444,7 +445,7 @@ class BPMediaHostWordpress {
444
  </li>
445
  </ul>
446
  </div>
447
- <?php
448
  }
449
  }
450
 
210
  case 'video' :
211
  if ($this->thumbnail_id) {
212
  $image_array = image_downsize($this->thumbnail_id, 'bp_media_activity_image');
213
+ $activity_content.=apply_filters('bp_media_single_activity_filter', '<video poster="' . $image_array[0] . '" src="' . wp_get_attachment_url($attachment_id) . '" width="320" height="240" type="video/mp4" id="bp_media_video_' . $this->id . '_' . $bp_media_counter . '" controls="controls" preload="none"></video></span><script>bp_media_create_element("bp_media_video_' . $this->id . '_' . $bp_media_counter . '");</script>', $this, true);
214
  } else {
215
+ $activity_content.=apply_filters('bp_media_single_activity_filter', '<video src="' . wp_get_attachment_url($attachment_id) . '" width="320" height="240" type="video/mp4" id="bp_media_video_' . $this->id . '_' . $bp_media_counter . '" controls="controls" preload="none"></video></span><script>bp_media_create_element("bp_media_video_' . $this->id . '_' . $bp_media_counter . '");</script>', $this, true);
216
  }
217
  break;
218
  case 'audio' :
219
+ $activity_content.=apply_filters('bp_media_single_activity_filter', '<audio src="' . wp_get_attachment_url($attachment_id) . '" width="320" type="audio/mp3" id="bp_media_audio_' . $this->id . '_' . $bp_media_counter . '" controls="controls" preload="none" ></audio></span><script>bp_media_create_element("bp_media_audio_' . $this->id . '_' . $bp_media_counter . '");</script>', $this, true);
220
  $type = 'audio';
221
  break;
222
  case 'image' :
223
  $image_array = image_downsize($attachment_id, 'bp_media_activity_image');
224
+ $activity_content.=apply_filters('bp_media_single_activity_filter', '<a href="' . $this->url . '" title="' . __($this->name, BP_MEDIA_TXT_DOMAIN) . '"><img src="' . $image_array[0] . '" id="bp_media_image_' . $this->id . '_' . $bp_media_counter++ . '" alt="' . __($this->name, BP_MEDIA_TXT_DOMAIN) . '" /></a>', $this, true);
225
  $type = 'image';
226
  break;
227
  default :
316
  ?>
317
  <li>
318
  <a href="<?php echo $this->url ?>" title="<?php _e($this->description, BP_MEDIA_TXT_DOMAIN); ?>">
319
+ <img src="<?php echo apply_filters('bp_media_video_thumb', $thumb_url, $attachment, $this->type); ?>" />
320
  </a>
321
  <h3 title="<?php echo $this->name; ?>"><a href="<?php echo $this->url ?>" title="<?php _e($this->description, BP_MEDIA_TXT_DOMAIN); ?>"><?php echo $this->name; ?></a></h3>
322
  </li>
376
  'include' => $activity_id,
377
  'max' => 1
378
  ))) {
379
+ while (bp_activities()) {
380
+ bp_the_activity();
381
  do_action('bp_before_activity_entry');
382
  ?>
383
  <div class="activity">
384
  <ul id="activity-stream" class="activity-list item-list">
385
  <li class="activity activity_update" id="activity-<?php echo $activity_id; ?>">
386
  <div class="activity-content">
387
+ <?php do_action('bp_activity_entry_content'); ?>
388
+ <?php if (is_user_logged_in()) : ?>
389
  <div class="activity-meta no-ajax">
390
+ <?php if (bp_activity_can_comment()) : ?>
391
  <a href="<?php bp_get_activity_comment_link(); ?>" class="button acomment-reply bp-primary-action" id="acomment-comment-<?php bp_activity_id(); ?>"><?php printf(__('Comment <span>%s</span>', BP_MEDIA_TXT_DOMAIN), bp_activity_get_comment_count()); ?></a>
392
  <?php endif; ?>
393
  <?php if (bp_activity_can_favorite()) : ?>
398
  <?php endif; ?>
399
  <?php endif; ?>
400
  <?php if (bp_activity_user_can_delete()) bp_activity_delete_link(); ?>
401
+ <?php do_action('bp_activity_entry_meta'); ?>
402
  </div>
403
+ <?php endif; ?>
404
  </div>
405
+ <?php do_action('bp_before_activity_entry_comments'); ?>
406
  <?php if (( is_user_logged_in() && bp_activity_can_comment() ) || bp_activity_get_comment_count()) : ?>
407
  <div class="activity-comments">
408
+ <?php bp_activity_comments(); ?>
409
+ <?php if (is_user_logged_in()) : ?>
410
  <form action="<?php bp_activity_comment_form_action(); ?>" method="post" id="ac-form-<?php bp_activity_id(); ?>" class="ac-form"<?php bp_activity_comment_form_nojs_display(); ?>>
411
  <div class="ac-reply-avatar"><?php bp_loggedin_user_avatar('width=' . BP_AVATAR_THUMB_WIDTH . '&height=' . BP_AVATAR_THUMB_HEIGHT); ?></div>
412
  <div class="ac-reply-content">
416
  <input type="submit" name="ac_form_submit" value="<?php _e('Post', BP_MEDIA_TXT_DOMAIN); ?>" /> &nbsp; <?php _e('or press esc to cancel.', BP_MEDIA_TXT_DOMAIN); ?>
417
  <input type="hidden" name="comment_form_id" value="<?php bp_activity_id(); ?>" />
418
  </div>
419
+ <?php do_action('bp_activity_entry_comments'); ?>
420
+ <?php wp_nonce_field('new_activity_comment', '_wpnonce_new_activity_comment'); ?>
421
  </form>
 
 
422
  <?php endif; ?>
423
+ </div>
424
+ <?php endif; ?>
425
+ <?php do_action('bp_after_activity_entry_comments'); ?>
426
  </li>
427
  </ul>
428
  </div>
429
  <?php
430
  }
431
+ }
432
+ else {
433
  ?>
434
  <div class="activity">
435
  <ul id="activity-stream" class="activity-list item-list">
436
  <li class="activity activity_update" id="activity-<?php echo $activity_id; ?>">
437
  <div class="activity-content">
438
+ <?php do_action('bp_activity_entry_content'); ?>
439
+ <?php if (is_user_logged_in()) : ?>
440
  <div class="activity-meta no-ajax">
441
  <a href="<?php echo $this->get_delete_url(); ?>" class="button item-button bp-secondary-action delete-activity-single confirm" rel="nofollow"><?php _e("Delete", BP_MEDIA_TXT_DOMAIN); ?></a>
442
  </div>
445
  </li>
446
  </ul>
447
  </div>
448
+ <?php
449
  }
450
  }
451
 
app/main/profile/BPMediaUploadScreen.php CHANGED
@@ -80,7 +80,7 @@ class BPMediaUploadScreen extends BPMediaScreen {
80
  /** This section can help in the group activity handling */
81
  if ( isset( $_POST[ 'bp_media_group_id' ] ) && intval( $_POST[ 'bp_media_group_id' ] ) ) {
82
  remove_action( 'bp_media_after_add_media', 'BPMediaActions::activity_create_after_add_media', 10, 2 );
83
- add_action( 'bp_media_after_add_media', 'BPMediaGroupAction::groups_activity_create_after_add_media', 10, 2 );
84
  add_filter( 'bp_media_force_hide_activity', 'BPMediaGroupAction::bp_media_groups_force_hide_activity' );
85
  }
86
  /* @var $bp_media_entry BPMediaHostWordpress */
80
  /** This section can help in the group activity handling */
81
  if ( isset( $_POST[ 'bp_media_group_id' ] ) && intval( $_POST[ 'bp_media_group_id' ] ) ) {
82
  remove_action( 'bp_media_after_add_media', 'BPMediaActions::activity_create_after_add_media', 10, 2 );
83
+ add_action( 'bp_media_after_add_media', 'BPMediaGroupAction::bp_media_groups_activity_create_after_add_media', 10, 2 );
84
  add_filter( 'bp_media_force_hide_activity', 'BPMediaGroupAction::bp_media_groups_force_hide_activity' );
85
  }
86
  /* @var $bp_media_entry BPMediaHostWordpress */
index.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: BuddyPress Media
4
  Plugin URI: http://rtcamp.com/buddypress-media/
5
  Description: This plugin adds missing media rich features like photos, videos and audios uploading to BuddyPress which are essential if you are building social network, seriously!
6
- Version: 2.4.2
7
  Author: rtCamp
8
  Text Domain: buddypress-media
9
  Author URI: http://rtcamp.com
@@ -51,6 +51,7 @@ function buddypress_media_autoloader( $class_name ) {
51
  'app/main/group/dummy/' . $class_name . '.php',
52
  'app/main/includes/' . $class_name . '.php',
53
  'app/main/widgets/' . $class_name . '.php',
 
54
  );
55
  foreach ( $rtlibpath as $i => $path ) {
56
  $path = BP_MEDIA_PATH . $path;
3
  Plugin Name: BuddyPress Media
4
  Plugin URI: http://rtcamp.com/buddypress-media/
5
  Description: This plugin adds missing media rich features like photos, videos and audios uploading to BuddyPress which are essential if you are building social network, seriously!
6
+ Version: 2.4.3
7
  Author: rtCamp
8
  Text Domain: buddypress-media
9
  Author URI: http://rtcamp.com
51
  'app/main/group/dummy/' . $class_name . '.php',
52
  'app/main/includes/' . $class_name . '.php',
53
  'app/main/widgets/' . $class_name . '.php',
54
+ 'app/log/' . $class_name . '.php',
55
  );
56
  foreach ( $rtlibpath as $i => $path ) {
57
  $path = BP_MEDIA_PATH . $path;
readme.txt CHANGED
@@ -6,7 +6,7 @@ License: GPLv2 or later
6
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
7
  Requires at least: 3.5
8
  Tested up to: 3.5
9
- Stable tag: 2.4.2
10
 
11
  Adds Photos, Music, Videos & Albums to your BuddyPress. Supports mobile devices (iPhone/iPad, etc) and automatic audio/video conversion.
12
 
@@ -80,6 +80,11 @@ Please visit [BuddyPress Media's Features page](http://rtcamp.com/buddypress-med
80
  == Changelog ==
81
 
82
  Please visit [BuddyPress Media's Roadmap page](http://rtcamp.com/buddypress-media/roadmap/ "Visit BuddyPress Media's Features page") to get some details about future releases.
 
 
 
 
 
83
  = 2.4.2 =
84
  * Fixed bug where settings weren't getting saved on multisites.
85
  * Workaround for bug where the last activity wouldn't show up.
@@ -187,5 +192,5 @@ Please visit [BuddyPress Media's Roadmap page](http://rtcamp.com/buddypress-medi
187
  * HTML5 Video Tag Support (with fallback)
188
 
189
  == Upgrade Notice ==
190
- =2.4.1=
191
- New Widget and Bug Fixes.
6
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
7
  Requires at least: 3.5
8
  Tested up to: 3.5
9
+ Stable tag: 2.4.3
10
 
11
  Adds Photos, Music, Videos & Albums to your BuddyPress. Supports mobile devices (iPhone/iPad, etc) and automatic audio/video conversion.
12
 
80
  == Changelog ==
81
 
82
  Please visit [BuddyPress Media's Roadmap page](http://rtcamp.com/buddypress-media/roadmap/ "Visit BuddyPress Media's Features page") to get some details about future releases.
83
+ = 2.4.3 =
84
+ * Fixed latest activity formatting.
85
+ * Added auto-update for add-ons.
86
+ * Made minor changes for add-on compatibility.
87
+
88
  = 2.4.2 =
89
  * Fixed bug where settings weren't getting saved on multisites.
90
  * Workaround for bug where the last activity wouldn't show up.
192
  * HTML5 Video Tag Support (with fallback)
193
 
194
  == Upgrade Notice ==
195
+ =2.4.3=
196
+ Fixed activity formatting and added support for addon updates.