Stream - Version 3.2.3

Version Description

  • April 23, 2018 =

  • New: Use minimized assets (#973)

  • New: Alert type Slack alerts! (#970 #962)

  • Fix: PHP 7.1 compatibility fix (#974)

  • Fix: Make reset nonce unique (#972)

  • Fix: Stripped settings and alerts inputs (#968)

  • Fix: Update Datetime extension (#966)

  • Fix: WP CLI Namespace collision (#944)

  • Tweak: Coding standards updates (#975)

  • Tweak: Show real client IP (if available) when in reverse-proxy mode (#969 #963)

  • Tweak: Performance improvement when listing roles (#964)

Props @DavidCramer, @lukecarbis, @frozzare, @fjarrett, @shadyvb, @valendesigns, @robbiet480, @cfoellmann

Download this release

Release Info

Developer Desertsnowman
Plugin Icon 128x128 Stream
Version 3.2.3
Comparing to
See all releases

Code changes from version 3.2.2 to 3.2.3

Files changed (197) hide show
  1. alerts/class-alert-trigger-action.php +24 -7
  2. alerts/class-alert-trigger-author.php +31 -11
  3. alerts/class-alert-trigger-context.php +63 -26
  4. alerts/class-alert-type-die.php +1 -0
  5. alerts/class-alert-type-email.php +56 -32
  6. alerts/class-alert-type-highlight.php +62 -26
  7. alerts/class-alert-type-ifttt.php +49 -29
  8. alerts/class-alert-type-menu-alert.php +46 -32
  9. alerts/class-alert-type-none.php +1 -1
  10. alerts/class-alert-type-slack.php +303 -0
  11. alerts/js/alert-type-highlight.js +55 -43
  12. classes/class-admin.php +142 -63
  13. classes/class-alert-type.php +4 -4
  14. classes/class-alert.php +22 -20
  15. classes/class-alerts-list.php +44 -22
  16. classes/class-alerts.php +129 -87
  17. classes/class-author.php +13 -13
  18. classes/class-cli.php +27 -14
  19. classes/class-connector.php +1 -1
  20. classes/class-connectors.php +5 -0
  21. classes/class-date-interval.php +18 -12
  22. classes/class-db-driver-wpdb.php +0 -0
  23. classes/class-db-driver.php +0 -0
  24. classes/class-db.php +7 -5
  25. classes/class-export.php +12 -17
  26. classes/class-exporter.php +1 -1
  27. classes/class-form-generator.php +24 -20
  28. classes/class-install.php +45 -15
  29. classes/class-list-table.php +124 -77
  30. classes/class-live-update.php +7 -2
  31. classes/class-log.php +78 -75
  32. classes/class-network.php +34 -11
  33. classes/class-plugin.php +10 -10
  34. classes/class-preview-list-table.php +13 -5
  35. classes/class-query.php +1 -1
  36. classes/class-record.php +11 -11
  37. classes/class-settings.php +209 -130
  38. classes/class-uninstall.php +2 -2
  39. connectors/class-connector-acf.php +15 -7
  40. connectors/class-connector-bbpress.php +8 -5
  41. connectors/class-connector-blogs.php +5 -0
  42. connectors/class-connector-buddypress.php +91 -39
  43. connectors/class-connector-comments.php +34 -14
  44. connectors/class-connector-edd.php +60 -12
  45. connectors/class-connector-editor.php +19 -21
  46. connectors/class-connector-gravityforms.php +28 -10
  47. connectors/class-connector-installer.php +22 -12
  48. connectors/class-connector-jetpack.php +40 -24
  49. connectors/class-connector-media.php +15 -6
  50. connectors/class-connector-menus.php +10 -1
  51. connectors/class-connector-posts.php +21 -5
  52. connectors/class-connector-settings.php +22 -13
  53. connectors/class-connector-taxonomies.php +9 -10
  54. connectors/class-connector-user-switching.php +8 -5
  55. connectors/class-connector-users.php +22 -6
  56. connectors/class-connector-widgets.php +50 -15
  57. connectors/class-connector-woocommerce.php +45 -21
  58. connectors/class-connector-wordpress-seo.php +83 -73
  59. includes/db-updates.php +14 -2
  60. includes/feeds/atom.php +20 -20
  61. includes/feeds/rss-2.0.php +18 -18
  62. includes/functions.php +15 -1
  63. includes/lib/Carbon.php +3 -2
  64. readme.txt +20 -4
  65. stream.php +1 -1
  66. ui/css/admin.css +10 -10
  67. ui/css/admin.min.css +1 -0
  68. ui/css/alerts-list.css +3 -3
  69. ui/css/alerts-list.min.css +1 -0
  70. ui/css/datepicker.css +6 -6
  71. ui/css/datepicker.min.css +1 -0
  72. ui/js/admin.js +508 -446
  73. ui/js/admin.min.js +1 -0
  74. ui/js/alerts-list.js +11 -7
  75. ui/js/alerts-list.min.js +1 -0
  76. ui/js/alerts.js +273 -235
  77. ui/js/alerts.min.js +1 -0
  78. ui/js/exclude.js +417 -353
  79. ui/js/exclude.min.js +1 -0
  80. ui/js/global.js +33 -25
  81. ui/js/global.min.js +1 -0
  82. ui/js/live-updates.js +119 -106
  83. ui/js/live-updates.min.js +1 -0
  84. ui/js/settings.js +67 -60
  85. ui/js/settings.min.js +1 -0
  86. ui/js/wpseo-admin.js +30 -24
  87. ui/js/wpseo-admin.min.js +1 -0
  88. ui/lib/select2/css/select2.css +0 -0
  89. ui/lib/select2/css/select2.min.css +0 -0
  90. ui/lib/select2/js/i18n/ar.js +0 -0
  91. ui/lib/select2/js/i18n/az.js +0 -0
  92. ui/lib/select2/js/i18n/bg.js +0 -0
  93. ui/lib/select2/js/i18n/ca.js +0 -0
  94. ui/lib/select2/js/i18n/cs.js +0 -0
  95. ui/lib/select2/js/i18n/da.js +0 -0
  96. ui/lib/select2/js/i18n/de.js +0 -0
  97. ui/lib/select2/js/i18n/el.js +0 -0
  98. ui/lib/select2/js/i18n/en.js +0 -0
  99. ui/lib/select2/js/i18n/es.js +0 -0
  100. ui/lib/select2/js/i18n/et.js +0 -0
  101. ui/lib/select2/js/i18n/eu.js +0 -0
  102. ui/lib/select2/js/i18n/fa.js +0 -0
  103. ui/lib/select2/js/i18n/fi.js +0 -0
  104. ui/lib/select2/js/i18n/fr.js +0 -0
  105. ui/lib/select2/js/i18n/gl.js +0 -0
  106. ui/lib/select2/js/i18n/he.js +0 -0
  107. ui/lib/select2/js/i18n/hi.js +0 -0
  108. ui/lib/select2/js/i18n/hr.js +0 -0
  109. ui/lib/select2/js/i18n/hu.js +0 -0
  110. ui/lib/select2/js/i18n/id.js +0 -0
  111. ui/lib/select2/js/i18n/is.js +0 -0
  112. ui/lib/select2/js/i18n/it.js +0 -0
  113. ui/lib/select2/js/i18n/ja.js +0 -0
  114. ui/lib/select2/js/i18n/km.js +0 -0
  115. ui/lib/select2/js/i18n/ko.js +0 -0
  116. ui/lib/select2/js/i18n/lt.js +0 -0
  117. ui/lib/select2/js/i18n/lv.js +0 -0
  118. ui/lib/select2/js/i18n/mk.js +0 -0
  119. ui/lib/select2/js/i18n/ms.js +0 -0
  120. ui/lib/select2/js/i18n/nb.js +0 -0
  121. ui/lib/select2/js/i18n/nl.js +0 -0
  122. ui/lib/select2/js/i18n/pl.js +0 -0
  123. ui/lib/select2/js/i18n/pt-BR.js +0 -0
  124. ui/lib/select2/js/i18n/pt.js +0 -0
  125. ui/lib/select2/js/i18n/ro.js +0 -0
  126. ui/lib/select2/js/i18n/ru.js +0 -0
  127. ui/lib/select2/js/i18n/sk.js +0 -0
  128. ui/lib/select2/js/i18n/sr-Cyrl.js +0 -0
  129. ui/lib/select2/js/i18n/sr.js +0 -0
  130. ui/lib/select2/js/i18n/sv.js +0 -0
  131. ui/lib/select2/js/i18n/th.js +0 -0
  132. ui/lib/select2/js/i18n/tr.js +0 -0
  133. ui/lib/select2/js/i18n/uk.js +0 -0
  134. ui/lib/select2/js/i18n/vi.js +0 -0
  135. ui/lib/select2/js/i18n/zh-CN.js +0 -0
  136. ui/lib/select2/js/i18n/zh-TW.js +0 -0
  137. ui/lib/select2/js/select2.full.js +0 -0
  138. ui/lib/select2/js/select2.full.min.js +0 -0
  139. ui/lib/select2/js/select2.js +0 -0
  140. ui/lib/select2/js/select2.min.js +0 -0
  141. ui/lib/timeago/jquery.timeago.js +0 -0
  142. ui/lib/timeago/locales/README.md +0 -0
  143. ui/lib/timeago/locales/jquery.timeago.ar.js +0 -0
  144. ui/lib/timeago/locales/jquery.timeago.bg.js +0 -0
  145. ui/lib/timeago/locales/jquery.timeago.bs.js +0 -0
  146. ui/lib/timeago/locales/jquery.timeago.ca.js +0 -0
  147. ui/lib/timeago/locales/jquery.timeago.cs.js +0 -0
  148. ui/lib/timeago/locales/jquery.timeago.cy.js +0 -0
  149. ui/lib/timeago/locales/jquery.timeago.da.js +0 -0
  150. ui/lib/timeago/locales/jquery.timeago.de.js +0 -0
  151. ui/lib/timeago/locales/jquery.timeago.el.js +0 -0
  152. ui/lib/timeago/locales/jquery.timeago.en-short.js +0 -0
  153. ui/lib/timeago/locales/jquery.timeago.en.js +0 -0
  154. ui/lib/timeago/locales/jquery.timeago.es-short.js +0 -0
  155. ui/lib/timeago/locales/jquery.timeago.es.js +0 -0
  156. ui/lib/timeago/locales/jquery.timeago.et.js +0 -0
  157. ui/lib/timeago/locales/jquery.timeago.fa.js +0 -0
  158. ui/lib/timeago/locales/jquery.timeago.fi.js +0 -0
  159. ui/lib/timeago/locales/jquery.timeago.fr-short.js +0 -0
  160. ui/lib/timeago/locales/jquery.timeago.fr.js +0 -0
  161. ui/lib/timeago/locales/jquery.timeago.gl.js +0 -0
  162. ui/lib/timeago/locales/jquery.timeago.he.js +0 -0
  163. ui/lib/timeago/locales/jquery.timeago.hr.js +0 -0
  164. ui/lib/timeago/locales/jquery.timeago.hu.js +0 -0
  165. ui/lib/timeago/locales/jquery.timeago.hy.js +0 -0
  166. ui/lib/timeago/locales/jquery.timeago.id.js +0 -0
  167. ui/lib/timeago/locales/jquery.timeago.is.js +0 -0
  168. ui/lib/timeago/locales/jquery.timeago.it.js +0 -0
  169. ui/lib/timeago/locales/jquery.timeago.ja.js +0 -0
  170. ui/lib/timeago/locales/jquery.timeago.jv.js +0 -0
  171. ui/lib/timeago/locales/jquery.timeago.ko.js +0 -0
  172. ui/lib/timeago/locales/jquery.timeago.lt.js +0 -0
  173. ui/lib/timeago/locales/jquery.timeago.mk.js +0 -0
  174. ui/lib/timeago/locales/jquery.timeago.nl.js +0 -0
  175. ui/lib/timeago/locales/jquery.timeago.no.js +0 -0
  176. ui/lib/timeago/locales/jquery.timeago.pl.js +0 -0
  177. ui/lib/timeago/locales/jquery.timeago.pt-br-short.js +0 -0
  178. ui/lib/timeago/locales/jquery.timeago.pt-br.js +0 -0
  179. ui/lib/timeago/locales/jquery.timeago.pt.js +0 -0
  180. ui/lib/timeago/locales/jquery.timeago.ro.js +0 -0
  181. ui/lib/timeago/locales/jquery.timeago.rs.js +0 -0
  182. ui/lib/timeago/locales/jquery.timeago.ru.js +0 -0
  183. ui/lib/timeago/locales/jquery.timeago.sk.js +0 -0
  184. ui/lib/timeago/locales/jquery.timeago.sl.js +0 -0
  185. ui/lib/timeago/locales/jquery.timeago.sv.js +0 -0
  186. ui/lib/timeago/locales/jquery.timeago.th.js +0 -0
  187. ui/lib/timeago/locales/jquery.timeago.tr.js +0 -0
  188. ui/lib/timeago/locales/jquery.timeago.uk.js +0 -0
  189. ui/lib/timeago/locales/jquery.timeago.uz.js +0 -0
  190. ui/lib/timeago/locales/jquery.timeago.vi.js +0 -0
  191. ui/lib/timeago/locales/jquery.timeago.zh-CN.js +0 -0
  192. ui/lib/timeago/locales/jquery.timeago.zh-TW.js +0 -0
  193. ui/stream-icons/font/stream.eot +0 -0
  194. ui/stream-icons/font/stream.svg +0 -0
  195. ui/stream-icons/font/stream.ttf +0 -0
  196. ui/stream-icons/font/stream.woff +0 -0
  197. ui/stream-icons/style.css +0 -0
alerts/class-alert-trigger-action.php CHANGED
@@ -39,16 +39,19 @@ class Alert_Trigger_Action extends Alert_Trigger {
39
  * Checks if a record matches the criteria from the trigger.
40
  *
41
  * @see Alert_Trigger::check_record().
 
42
  * @param bool $success Status of previous checks.
43
  * @param int $record_id Record ID.
44
  * @param array $recordarr Record data.
45
  * @param Alert $alert The Alert being worked on.
 
46
  * @return bool False on failure, otherwise should return original value of $success.
47
  */
48
  public function check_record( $success, $record_id, $recordarr, $alert ) {
49
  if ( ! empty( $alert->alert_meta['trigger_action'] ) && $recordarr['action'] !== $alert->alert_meta['trigger_action'] ) {
50
  return false;
51
  }
 
52
  return $success;
53
  }
54
 
@@ -56,8 +59,10 @@ class Alert_Trigger_Action extends Alert_Trigger {
56
  * Adds fields to the trigger form.
57
  *
58
  * @see Alert_Trigger::add_fields().
 
59
  * @param Form_Generator $form The Form Object to add to.
60
  * @param Alert $alert The Alert being worked on.
 
61
  * @return void
62
  */
63
  public function add_fields( $form, $alert = array() ) {
@@ -67,11 +72,11 @@ class Alert_Trigger_Action extends Alert_Trigger {
67
  }
68
 
69
  $args = array(
70
- 'name' => esc_attr( $this->field_key ),
71
- 'value' => esc_attr( $value ),
72
- 'options' => $this->get_values(),
73
- 'classes' => 'wp_stream_ajax_forward',
74
- 'data' => array(
75
  'placeholder' => __( 'Any Action', 'stream' ),
76
  ),
77
  );
@@ -82,7 +87,9 @@ class Alert_Trigger_Action extends Alert_Trigger {
82
  * Validate and save Alert object
83
  *
84
  * @see Alert_Trigger::save_fields().
 
85
  * @param Alert $alert The Alert being worked on.
 
86
  * @return void
87
  */
88
  public function save_fields( $alert ) {
@@ -98,17 +105,22 @@ class Alert_Trigger_Action extends Alert_Trigger {
98
  * Generate array of possible action values
99
  *
100
  * @param bool $flat If the array should be multidimensional.
 
101
  * @return array
102
  */
103
  public function get_values( $flat = false ) {
104
  $action_values = array();
105
  foreach ( $this->get_terms_labels( 'action' ) as $action_id => $action_data ) {
106
  if ( ! $flat ) {
107
- $action_values[] = array( 'id' => $action_id, 'text' => $action_data );
 
 
 
108
  } else {
109
  $action_values[ $action_id ] = $action_data;
110
  }
111
  }
 
112
  return $action_values;
113
  }
114
 
@@ -116,7 +128,9 @@ class Alert_Trigger_Action extends Alert_Trigger {
116
  * Function will return all terms labels of given column
117
  *
118
  * @todo refactor Settings::get_terms_labels into general utility
 
119
  * @param string $column string Name of the column.
 
120
  * @return array
121
  */
122
  public function get_terms_labels( $column ) {
@@ -141,6 +155,7 @@ class Alert_Trigger_Action extends Alert_Trigger {
141
 
142
  ksort( $return_labels );
143
  }
 
144
  return $return_labels;
145
  }
146
 
@@ -148,11 +163,13 @@ class Alert_Trigger_Action extends Alert_Trigger {
148
  * Returns the trigger's value for the given alert.
149
  *
150
  * @see Alert_Trigger::get_display_value().
 
151
  * @param string $context The location this data will be displayed in.
152
  * @param Alert $alert Alert being processed.
 
153
  * @return string
154
  */
155
- function get_display_value( $context = 'normal', $alert ) {
156
  $action = ( ! empty( $alert->alert_meta['trigger_action'] ) ) ? $alert->alert_meta['trigger_action'] : null;
157
 
158
  if ( 'post_title' === $context ) {
39
  * Checks if a record matches the criteria from the trigger.
40
  *
41
  * @see Alert_Trigger::check_record().
42
+ *
43
  * @param bool $success Status of previous checks.
44
  * @param int $record_id Record ID.
45
  * @param array $recordarr Record data.
46
  * @param Alert $alert The Alert being worked on.
47
+ *
48
  * @return bool False on failure, otherwise should return original value of $success.
49
  */
50
  public function check_record( $success, $record_id, $recordarr, $alert ) {
51
  if ( ! empty( $alert->alert_meta['trigger_action'] ) && $recordarr['action'] !== $alert->alert_meta['trigger_action'] ) {
52
  return false;
53
  }
54
+
55
  return $success;
56
  }
57
 
59
  * Adds fields to the trigger form.
60
  *
61
  * @see Alert_Trigger::add_fields().
62
+ *
63
  * @param Form_Generator $form The Form Object to add to.
64
  * @param Alert $alert The Alert being worked on.
65
+ *
66
  * @return void
67
  */
68
  public function add_fields( $form, $alert = array() ) {
72
  }
73
 
74
  $args = array(
75
+ 'name' => esc_attr( $this->field_key ),
76
+ 'value' => esc_attr( $value ),
77
+ 'options' => $this->get_values(),
78
+ 'classes' => 'wp_stream_ajax_forward',
79
+ 'data' => array(
80
  'placeholder' => __( 'Any Action', 'stream' ),
81
  ),
82
  );
87
  * Validate and save Alert object
88
  *
89
  * @see Alert_Trigger::save_fields().
90
+ *
91
  * @param Alert $alert The Alert being worked on.
92
+ *
93
  * @return void
94
  */
95
  public function save_fields( $alert ) {
105
  * Generate array of possible action values
106
  *
107
  * @param bool $flat If the array should be multidimensional.
108
+ *
109
  * @return array
110
  */
111
  public function get_values( $flat = false ) {
112
  $action_values = array();
113
  foreach ( $this->get_terms_labels( 'action' ) as $action_id => $action_data ) {
114
  if ( ! $flat ) {
115
+ $action_values[] = array(
116
+ 'id' => $action_id,
117
+ 'text' => $action_data,
118
+ );
119
  } else {
120
  $action_values[ $action_id ] = $action_data;
121
  }
122
  }
123
+
124
  return $action_values;
125
  }
126
 
128
  * Function will return all terms labels of given column
129
  *
130
  * @todo refactor Settings::get_terms_labels into general utility
131
+ *
132
  * @param string $column string Name of the column.
133
+ *
134
  * @return array
135
  */
136
  public function get_terms_labels( $column ) {
155
 
156
  ksort( $return_labels );
157
  }
158
+
159
  return $return_labels;
160
  }
161
 
163
  * Returns the trigger's value for the given alert.
164
  *
165
  * @see Alert_Trigger::get_display_value().
166
+ *
167
  * @param string $context The location this data will be displayed in.
168
  * @param Alert $alert Alert being processed.
169
+ *
170
  * @return string
171
  */
172
+ public function get_display_value( $context = 'normal', $alert ) {
173
  $action = ( ! empty( $alert->alert_meta['trigger_action'] ) ) ? $alert->alert_meta['trigger_action'] : null;
174
 
175
  if ( 'post_title' === $context ) {
alerts/class-alert-trigger-author.php CHANGED
@@ -4,6 +4,7 @@
4
  *
5
  * @package WP_Stream
6
  */
 
7
  namespace WP_Stream;
8
 
9
  /**
@@ -31,16 +32,19 @@ class Alert_Trigger_Author extends Alert_Trigger {
31
  * Checks if a record matches the criteria from the trigger.
32
  *
33
  * @see Alert_Trigger::check_record().
 
34
  * @param bool $success Status of previous checks.
35
  * @param int $record_id Record ID.
36
  * @param array $recordarr Record data.
37
  * @param Alert $alert The Alert being worked on.
 
38
  * @return bool False on failure, otherwise should return original value of $success.
39
  */
40
  public function check_record( $success, $record_id, $recordarr, $alert ) {
41
  if ( ! empty( $alert->alert_meta['trigger_author'] ) && intval( $alert->alert_meta['trigger_author'] ) !== intval( $recordarr['user_id'] ) ) {
42
  return false;
43
  }
 
44
  return $success;
45
  }
46
 
@@ -48,8 +52,10 @@ class Alert_Trigger_Author extends Alert_Trigger {
48
  * Adds fields to the trigger form.
49
  *
50
  * @see Alert_Trigger::add_fields().
 
51
  * @param Form_Generator $form The Form Object to add to.
52
  * @param Alert $alert The Alert being worked on.
 
53
  * @return void
54
  */
55
  public function add_fields( $form, $alert = array() ) {
@@ -59,11 +65,11 @@ class Alert_Trigger_Author extends Alert_Trigger {
59
  }
60
 
61
  $args = array(
62
- 'name' => esc_attr( $this->field_key ),
63
- 'value' => esc_attr( $value ),
64
- 'options' => $this->get_values(),
65
- 'classes' => 'wp_stream_ajax_forward',
66
- 'data' => array(
67
  'placeholder' => __( 'Any Author', 'stream' ),
68
  ),
69
  );
@@ -86,24 +92,32 @@ class Alert_Trigger_Author extends Alert_Trigger {
86
  }
87
 
88
  $users = array_map(
89
- function( $user_id ) {
90
  return new Author( $user_id );
91
  },
92
- get_users( array( 'fields' => 'ID' ) )
 
 
 
 
93
  );
94
 
95
  if ( is_multisite() && is_super_admin() ) {
96
  $super_admins = array_map(
97
- function( $login ) {
98
  $user = get_user_by( 'login', $login );
 
99
  return new Author( $user->ID );
100
  },
101
  get_super_admins()
102
  );
103
- $users = array_unique( array_merge( $users, $super_admins ) );
104
  }
105
 
106
- $users[] = new Author( 0, array( 'is_wp_cli' => true ) );
 
 
 
107
 
108
  foreach ( $users as $user ) {
109
  $all_records[] = array(
@@ -112,6 +126,7 @@ class Alert_Trigger_Author extends Alert_Trigger {
112
  'text' => $user->get_display_name(),
113
  );
114
  }
 
115
  return $all_records;
116
  }
117
 
@@ -119,7 +134,9 @@ class Alert_Trigger_Author extends Alert_Trigger {
119
  * Validate and save Alert object
120
  *
121
  * @see Alert_Trigger::save_fields().
 
122
  * @param Alert $alert The Alert being worked on.
 
123
  * @return void
124
  */
125
  public function save_fields( $alert ) {
@@ -135,11 +152,13 @@ class Alert_Trigger_Author extends Alert_Trigger {
135
  * Returns the trigger's value for the given alert.
136
  *
137
  * @see Alert_Trigger::get_display_value().
 
138
  * @param string $context The location this data will be displayed in.
139
  * @param Alert $alert Alert being processed.
 
140
  * @return string
141
  */
142
- function get_display_value( $context = 'normal', $alert ) {
143
  $author = ( ! empty( $alert->alert_meta['trigger_author'] ) ) ? $alert->alert_meta['trigger_author'] : null;
144
  if ( empty( $author ) ) {
145
  $author = __( 'Any User', 'stream' );
@@ -151,6 +170,7 @@ class Alert_Trigger_Author extends Alert_Trigger {
151
  $author = __( 'Unknown User', 'stream' );
152
  }
153
  }
 
154
  return ucfirst( $author );
155
  }
156
  }
4
  *
5
  * @package WP_Stream
6
  */
7
+
8
  namespace WP_Stream;
9
 
10
  /**
32
  * Checks if a record matches the criteria from the trigger.
33
  *
34
  * @see Alert_Trigger::check_record().
35
+ *
36
  * @param bool $success Status of previous checks.
37
  * @param int $record_id Record ID.
38
  * @param array $recordarr Record data.
39
  * @param Alert $alert The Alert being worked on.
40
+ *
41
  * @return bool False on failure, otherwise should return original value of $success.
42
  */
43
  public function check_record( $success, $record_id, $recordarr, $alert ) {
44
  if ( ! empty( $alert->alert_meta['trigger_author'] ) && intval( $alert->alert_meta['trigger_author'] ) !== intval( $recordarr['user_id'] ) ) {
45
  return false;
46
  }
47
+
48
  return $success;
49
  }
50
 
52
  * Adds fields to the trigger form.
53
  *
54
  * @see Alert_Trigger::add_fields().
55
+ *
56
  * @param Form_Generator $form The Form Object to add to.
57
  * @param Alert $alert The Alert being worked on.
58
+ *
59
  * @return void
60
  */
61
  public function add_fields( $form, $alert = array() ) {
65
  }
66
 
67
  $args = array(
68
+ 'name' => esc_attr( $this->field_key ),
69
+ 'value' => esc_attr( $value ),
70
+ 'options' => $this->get_values(),
71
+ 'classes' => 'wp_stream_ajax_forward',
72
+ 'data' => array(
73
  'placeholder' => __( 'Any Author', 'stream' ),
74
  ),
75
  );
92
  }
93
 
94
  $users = array_map(
95
+ function ( $user_id ) {
96
  return new Author( $user_id );
97
  },
98
+ get_users(
99
+ array(
100
+ 'fields' => 'ID',
101
+ )
102
+ )
103
  );
104
 
105
  if ( is_multisite() && is_super_admin() ) {
106
  $super_admins = array_map(
107
+ function ( $login ) {
108
  $user = get_user_by( 'login', $login );
109
+
110
  return new Author( $user->ID );
111
  },
112
  get_super_admins()
113
  );
114
+ $users = array_unique( array_merge( $users, $super_admins ) );
115
  }
116
 
117
+ $user_meta = array(
118
+ 'is_wp_cli' => true,
119
+ );
120
+ $users[] = new Author( 0, $user_meta );
121
 
122
  foreach ( $users as $user ) {
123
  $all_records[] = array(
126
  'text' => $user->get_display_name(),
127
  );
128
  }
129
+
130
  return $all_records;
131
  }
132
 
134
  * Validate and save Alert object
135
  *
136
  * @see Alert_Trigger::save_fields().
137
+ *
138
  * @param Alert $alert The Alert being worked on.
139
+ *
140
  * @return void
141
  */
142
  public function save_fields( $alert ) {
152
  * Returns the trigger's value for the given alert.
153
  *
154
  * @see Alert_Trigger::get_display_value().
155
+ *
156
  * @param string $context The location this data will be displayed in.
157
  * @param Alert $alert Alert being processed.
158
+ *
159
  * @return string
160
  */
161
+ public function get_display_value( $context = 'normal', $alert ) {
162
  $author = ( ! empty( $alert->alert_meta['trigger_author'] ) ) ? $alert->alert_meta['trigger_author'] : null;
163
  if ( empty( $author ) ) {
164
  $author = __( 'Any User', 'stream' );
170
  $author = __( 'Unknown User', 'stream' );
171
  }
172
  }
173
+
174
  return ucfirst( $author );
175
  }
176
  }
alerts/class-alert-trigger-context.php CHANGED
@@ -4,6 +4,7 @@
4
  *
5
  * @package WP_Stream
6
  */
 
7
  namespace WP_Stream;
8
 
9
  /**
@@ -31,10 +32,12 @@ class Alert_Trigger_Context extends Alert_Trigger {
31
  * Checks if a record matches the criteria from the trigger.
32
  *
33
  * @see Alert_Trigger::check_record().
 
34
  * @param bool $success Status of previous checks.
35
  * @param int $record_id Record ID.
36
  * @param array $recordarr Record data.
37
  * @param Alert $alert The Alert being worked on.
 
38
  * @return bool False on failure, otherwise should return original value of $success.
39
  */
40
  public function check_record( $success, $record_id, $recordarr, $alert ) {
@@ -44,6 +47,7 @@ class Alert_Trigger_Context extends Alert_Trigger {
44
  if ( ! empty( $alert->alert_meta['trigger_context'] ) && $recordarr['context'] !== $alert->alert_meta['trigger_context'] ) {
45
  return false;
46
  }
 
47
  return $success;
48
  }
49
 
@@ -51,8 +55,10 @@ class Alert_Trigger_Context extends Alert_Trigger {
51
  * Adds fields to the trigger form.
52
  *
53
  * @see Alert_Trigger::add_fields().
 
54
  * @param Form_Generator $form The Form Object to add to.
55
  * @param Alert $alert The Alert being worked on.
 
56
  * @return void
57
  */
58
  public function add_fields( $form, $alert = array() ) {
@@ -69,38 +75,46 @@ class Alert_Trigger_Context extends Alert_Trigger {
69
  // Context dropdown menu.
70
  $context_values = array();
71
 
72
- $form->add_field( 'select2', array(
73
- 'name' => 'wp_stream_trigger_connector_or_context',
74
- 'options' => $this->get_values(),
75
- 'classes' => 'wp_stream_ajax_forward connector_or_context',
76
- 'data' => array(
77
- 'placeholder' => __( 'Any Context', 'stream' ),
78
- ),
79
- ) );
80
-
81
- $form->add_field( 'hidden', array(
82
- 'name' => 'wp_stream_trigger_connector',
83
- 'value' => $connector,
84
- 'classes' => 'connector wp_stream_ajax_forward',
85
- ) );
86
-
87
- $form->add_field( 'hidden', array(
88
- 'name' => 'wp_stream_trigger_context',
89
- 'value' => $context,
90
- 'classes' => 'context wp_stream_ajax_forward',
91
- ) );
 
 
 
 
 
 
92
  }
93
 
94
  /**
95
  * Validate and save Alert object
96
  *
97
  * @see Alert_Trigger::save_fields().
 
98
  * @param Alert $alert The Alert being worked on.
 
99
  * @return void
100
  */
101
  public function save_fields( $alert ) {
102
  $alert->alert_meta['trigger_connector'] = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_connector' );
103
- $alert->alert_meta['trigger_context'] = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_context' );
104
  }
105
 
106
  /**
@@ -109,22 +123,39 @@ class Alert_Trigger_Context extends Alert_Trigger {
109
  * @return array
110
  */
111
  public function get_values() {
 
 
112
  foreach ( $this->get_terms_labels( 'context' ) as $context_id => $context_data ) {
113
  if ( is_array( $context_data ) ) {
114
  $child_values = array();
115
  if ( isset( $context_data['children'] ) ) {
116
  $child_values = array();
117
  foreach ( $context_data['children'] as $child_id => $child_value ) {
118
- $child_values[] = array( 'value' => $context_id . '-' . $child_id, 'id' => $context_id . '-' . $child_id, 'text' => $child_value, 'parent' => $context_id );
 
 
 
 
 
119
  }
120
  }
121
  if ( isset( $context_data['label'] ) ) {
122
- $context_values[] = array( 'value' => $context_id, 'id' => $context_id, 'text' => $context_data['label'], 'children' => $child_values );
 
 
 
 
 
123
  }
124
  } else {
125
- $context_values[] = array( 'value' => $context_id, 'id' => $context_id, 'text' => $context_data );
 
 
 
 
126
  }
127
  }
 
128
  return $context_values;
129
  }
130
 
@@ -132,7 +163,9 @@ class Alert_Trigger_Context extends Alert_Trigger {
132
  * Function will return all terms labels of given column
133
  *
134
  * @todo refactor Settings::get_terms_labels into general utility
 
135
  * @param string $column string Name of the column.
 
136
  * @return array
137
  */
138
  public function get_terms_labels( $column ) {
@@ -157,6 +190,7 @@ class Alert_Trigger_Context extends Alert_Trigger {
157
 
158
  ksort( $return_labels );
159
  }
 
160
  return $return_labels;
161
  }
162
 
@@ -164,12 +198,14 @@ class Alert_Trigger_Context extends Alert_Trigger {
164
  * Returns the trigger's value for the given alert.
165
  *
166
  * @see Alert_Trigger::get_display_value().
 
167
  * @param string $context The location this data will be displayed in.
168
  * @param Alert $alert Alert being processed.
 
169
  * @return string
170
  */
171
- function get_display_value( $context = 'normal', $alert ) {
172
- $context = ( ! empty( $alert->alert_meta['trigger_context'] ) ) ? $alert->alert_meta['trigger_context'] : null;
173
  $connector = ( ! empty( $alert->alert_meta['trigger_connector'] ) ) ? $alert->alert_meta['trigger_connector'] : null;
174
  if ( empty( $context ) && empty( $connector ) ) {
175
  $context = __( 'Any Context', 'stream' );
@@ -183,6 +219,7 @@ class Alert_Trigger_Context extends Alert_Trigger {
183
  }
184
  }
185
  }
 
186
  return ucfirst( $context );
187
  }
188
  }
4
  *
5
  * @package WP_Stream
6
  */
7
+
8
  namespace WP_Stream;
9
 
10
  /**
32
  * Checks if a record matches the criteria from the trigger.
33
  *
34
  * @see Alert_Trigger::check_record().
35
+ *
36
  * @param bool $success Status of previous checks.
37
  * @param int $record_id Record ID.
38
  * @param array $recordarr Record data.
39
  * @param Alert $alert The Alert being worked on.
40
+ *
41
  * @return bool False on failure, otherwise should return original value of $success.
42
  */
43
  public function check_record( $success, $record_id, $recordarr, $alert ) {
47
  if ( ! empty( $alert->alert_meta['trigger_context'] ) && $recordarr['context'] !== $alert->alert_meta['trigger_context'] ) {
48
  return false;
49
  }
50
+
51
  return $success;
52
  }
53
 
55
  * Adds fields to the trigger form.
56
  *
57
  * @see Alert_Trigger::add_fields().
58
+ *
59
  * @param Form_Generator $form The Form Object to add to.
60
  * @param Alert $alert The Alert being worked on.
61
+ *
62
  * @return void
63
  */
64
  public function add_fields( $form, $alert = array() ) {
75
  // Context dropdown menu.
76
  $context_values = array();
77
 
78
+ $form->add_field(
79
+ 'select2', array(
80
+ 'name' => 'wp_stream_trigger_connector_or_context',
81
+ 'options' => $this->get_values(),
82
+ 'classes' => 'wp_stream_ajax_forward connector_or_context',
83
+ 'data' => array(
84
+ 'placeholder' => __( 'Any Context', 'stream' ),
85
+ ),
86
+ )
87
+ );
88
+
89
+ $form->add_field(
90
+ 'hidden', array(
91
+ 'name' => 'wp_stream_trigger_connector',
92
+ 'value' => $connector,
93
+ 'classes' => 'connector wp_stream_ajax_forward',
94
+ )
95
+ );
96
+
97
+ $form->add_field(
98
+ 'hidden', array(
99
+ 'name' => 'wp_stream_trigger_context',
100
+ 'value' => $context,
101
+ 'classes' => 'context wp_stream_ajax_forward',
102
+ )
103
+ );
104
  }
105
 
106
  /**
107
  * Validate and save Alert object
108
  *
109
  * @see Alert_Trigger::save_fields().
110
+ *
111
  * @param Alert $alert The Alert being worked on.
112
+ *
113
  * @return void
114
  */
115
  public function save_fields( $alert ) {
116
  $alert->alert_meta['trigger_connector'] = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_connector' );
117
+ $alert->alert_meta['trigger_context'] = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_context' );
118
  }
119
 
120
  /**
123
  * @return array
124
  */
125
  public function get_values() {
126
+ $context_values = array();
127
+
128
  foreach ( $this->get_terms_labels( 'context' ) as $context_id => $context_data ) {
129
  if ( is_array( $context_data ) ) {
130
  $child_values = array();
131
  if ( isset( $context_data['children'] ) ) {
132
  $child_values = array();
133
  foreach ( $context_data['children'] as $child_id => $child_value ) {
134
+ $child_values[] = array(
135
+ 'value' => $context_id . '-' . $child_id,
136
+ 'id' => $context_id . '-' . $child_id,
137
+ 'text' => $child_value,
138
+ 'parent' => $context_id,
139
+ );
140
  }
141
  }
142
  if ( isset( $context_data['label'] ) ) {
143
+ $context_values[] = array(
144
+ 'value' => $context_id,
145
+ 'id' => $context_id,
146
+ 'text' => $context_data['label'],
147
+ 'children' => $child_values,
148
+ );
149
  }
150
  } else {
151
+ $context_values[] = array(
152
+ 'value' => $context_id,
153
+ 'id' => $context_id,
154
+ 'text' => $context_data,
155
+ );
156
  }
157
  }
158
+
159
  return $context_values;
160
  }
161
 
163
  * Function will return all terms labels of given column
164
  *
165
  * @todo refactor Settings::get_terms_labels into general utility
166
+ *
167
  * @param string $column string Name of the column.
168
+ *
169
  * @return array
170
  */
171
  public function get_terms_labels( $column ) {
190
 
191
  ksort( $return_labels );
192
  }
193
+
194
  return $return_labels;
195
  }
196
 
198
  * Returns the trigger's value for the given alert.
199
  *
200
  * @see Alert_Trigger::get_display_value().
201
+ *
202
  * @param string $context The location this data will be displayed in.
203
  * @param Alert $alert Alert being processed.
204
+ *
205
  * @return string
206
  */
207
+ public function get_display_value( $context = 'normal', $alert ) {
208
+ $context = ( ! empty( $alert->alert_meta['trigger_context'] ) ) ? $alert->alert_meta['trigger_context'] : null;
209
  $connector = ( ! empty( $alert->alert_meta['trigger_connector'] ) ) ? $alert->alert_meta['trigger_connector'] : null;
210
  if ( empty( $context ) && empty( $connector ) ) {
211
  $context = __( 'Any Context', 'stream' );
219
  }
220
  }
221
  }
222
+
223
  return ucfirst( $context );
224
  }
225
  }
alerts/class-alert-type-die.php CHANGED
@@ -13,6 +13,7 @@ namespace WP_Stream;
13
  * @package WP_Stream
14
  */
15
  class Alert_Type_Die extends Alert_Type {
 
16
  /**
17
  * Alert type name
18
  *
13
  * @package WP_Stream
14
  */
15
  class Alert_Type_Die extends Alert_Type {
16
+
17
  /**
18
  * Alert type name
19
  *
alerts/class-alert-type-email.php CHANGED
@@ -42,7 +42,12 @@ class Alert_Type_Email extends Alert_Type {
42
  if ( ! is_admin() ) {
43
  return;
44
  }
45
- add_filter( 'wp_stream_alerts_save_meta', array( $this, 'add_alert_meta' ), 10, 2 );
 
 
 
 
 
46
  }
47
 
48
  /**
@@ -54,37 +59,46 @@ class Alert_Type_Email extends Alert_Type {
54
  * @return void
55
  */
56
  public function alert( $record_id, $recordarr, $alert ) {
57
- $options = wp_parse_args( $alert->alert_meta, array(
58
- 'email_recipient' => '',
59
- 'email_subject' => '',
60
- 'trigger_action' => '',
61
- 'trigger_connector' => '',
62
- 'trigger_context' => '',
63
- ) );
 
 
64
 
65
  if ( empty( $options['email_recipient'] ) && empty( $options['email_subject'] ) ) {
66
  return;
67
  }
68
 
69
- $message = sprintf( __( 'A Stream Alert was triggered on %s.', 'stream' ), get_bloginfo( 'name' ) ) . "\n\n";
 
70
 
71
  $user_id = $recordarr['user_id'];
72
- $user = get_user_by( 'id', $user_id );
 
 
73
  $message .= sprintf( __( "User:\t%s", 'stream' ), $user->user_login ) . "\n";
74
 
75
  if ( ! empty( $alert->alert_meta['trigger_context'] ) ) {
76
- $context = $this->plugin->alerts->alert_triggers['context']->get_display_value( 'list_table', $alert );
77
- $message .= sprintf( __( "Context:\t%s", 'stream' ), $context ) . "\n";
 
 
78
  }
79
  if ( ! empty( $alert->alert_meta['trigger_action'] ) ) {
80
- $action = $this->plugin->alerts->alert_triggers['action']->get_display_value( 'list_table', $alert );
81
- $message .= sprintf( __( "Action:\t%s", 'stream' ), $action ) . "\n";
 
 
82
  }
83
 
84
  $post = null;
85
  if ( isset( $recordarr['object_id'] ) ) {
86
  $post_id = $recordarr['object_id'];
87
- $post = get_post( $post_id );
88
  }
89
  if ( is_object( $post ) && ! empty( $post ) ) {
90
  $post_type = get_post_type_object( $post->post_type );
@@ -92,13 +106,15 @@ class Alert_Type_Email extends Alert_Type {
92
  $message .= $post_type->labels->singular_name . ":\t" . $post->post_title . "\n\n";
93
 
94
  $edit_post_link = get_edit_post_link( $post->ID, 'raw' );
 
 
95
  $message .= sprintf( __( 'Edit %s', 'stream' ), $post_type->labels->singular_name ) . "\n<$edit_post_link>\n";
96
  }
97
 
98
  $message .= "\n";
99
 
100
  $edit_alert_link = admin_url( 'edit.php?post_type=wp_stream_alerts#post-' . $alert->ID );
101
- $message .= __( 'Edit Alert', 'stream' ) . "\n<$edit_alert_link>";
102
 
103
  wp_mail( $options['email_recipient'], $options['email_subject'], $message );
104
  }
@@ -114,29 +130,35 @@ class Alert_Type_Email extends Alert_Type {
114
  if ( is_object( $alert ) ) {
115
  $alert_meta = $alert->alert_meta;
116
  }
117
- $options = wp_parse_args( $alert_meta, array(
118
- 'email_recipient' => '',
119
- 'email_subject' => '',
120
- ) );
121
-
122
- $form = new Form_Generator;
 
 
123
  echo '<span class="wp_stream_alert_type_description">' . esc_html__( 'Send a notification email to the recipient.', 'stream' ) . '</span>';
124
  echo '<label for="wp_stream_email_recipient"><span class="title">' . esc_html__( 'Recipient', 'stream' ) . '</span>';
125
  echo '<span class="input-text-wrap">';
126
- echo $form->render_field( 'text', array( // Xss ok.
127
- 'name' => 'wp_stream_email_recipient',
128
- 'title' => esc_attr( __( 'Email Recipient', 'stream' ) ),
129
- 'value' => $options['email_recipient'],
130
- ) );
 
 
131
  echo '</span></label>';
132
 
133
  echo '<label for="wp_stream_email_subject"><span class="title">' . esc_html__( 'Subject', 'stream' ) . '</span>';
134
  echo '<span class="input-text-wrap">';
135
- echo $form->render_field( 'text', array( // Xss ok.
136
- 'name' => 'wp_stream_email_subject',
137
- 'title' => esc_attr( __( 'Email Subject', 'stream' ) ),
138
- 'value' => $options['email_subject'],
139
- ) );
 
 
140
  echo '</span></label>';
141
  }
142
 
@@ -157,6 +179,7 @@ class Alert_Type_Email extends Alert_Type {
157
  $alert->alert_meta['email_subject'] = sanitize_text_field( wp_unslash( $_POST['wp_stream_email_subject'] ) );
158
  }
159
  }
 
160
  /**
161
  * Add alert meta if this is a highlight alert
162
  *
@@ -176,6 +199,7 @@ class Alert_Type_Email extends Alert_Type {
176
  $alert_meta['email_subject'] = $email_subject;
177
  }
178
  }
 
179
  return $alert_meta;
180
  }
181
  }
42
  if ( ! is_admin() ) {
43
  return;
44
  }
45
+ add_filter(
46
+ 'wp_stream_alerts_save_meta', array(
47
+ $this,
48
+ 'add_alert_meta',
49
+ ), 10, 2
50
+ );
51
  }
52
 
53
  /**
59
  * @return void
60
  */
61
  public function alert( $record_id, $recordarr, $alert ) {
62
+ $options = wp_parse_args(
63
+ $alert->alert_meta, array(
64
+ 'email_recipient' => '',
65
+ 'email_subject' => '',
66
+ 'trigger_action' => '',
67
+ 'trigger_connector' => '',
68
+ 'trigger_context' => '',
69
+ )
70
+ );
71
 
72
  if ( empty( $options['email_recipient'] ) && empty( $options['email_subject'] ) ) {
73
  return;
74
  }
75
 
76
+ // translators: Placeholder refers to the title of a site (e.g. "FooBar Website").
77
+ $message = sprintf( __( 'A Stream Alert was triggered on %s.', 'stream' ), get_bloginfo( 'name' ) ) . "\n\n";
78
 
79
  $user_id = $recordarr['user_id'];
80
+ $user = get_user_by( 'id', $user_id );
81
+
82
+ // translators: Placeholder refers to a username (e.g. "administrator").
83
  $message .= sprintf( __( "User:\t%s", 'stream' ), $user->user_login ) . "\n";
84
 
85
  if ( ! empty( $alert->alert_meta['trigger_context'] ) ) {
86
+ $context = $this->plugin->alerts->alert_triggers['context']->get_display_value( 'list_table', $alert );
87
+
88
+ // translators: Placeholder refers to the context of the record (e.g. "Plugins").
89
+ $message .= sprintf( __( "Context:\t%s", 'stream' ), $context ) . "\n";
90
  }
91
  if ( ! empty( $alert->alert_meta['trigger_action'] ) ) {
92
+ $action = $this->plugin->alerts->alert_triggers['action']->get_display_value( 'list_table', $alert );
93
+
94
+ // translators: Placeholder refers to the action of the record (e.g. "Installed").
95
+ $message .= sprintf( __( "Action:\t%s", 'stream' ), $action ) . "\n";
96
  }
97
 
98
  $post = null;
99
  if ( isset( $recordarr['object_id'] ) ) {
100
  $post_id = $recordarr['object_id'];
101
+ $post = get_post( $post_id );
102
  }
103
  if ( is_object( $post ) && ! empty( $post ) ) {
104
  $post_type = get_post_type_object( $post->post_type );
106
  $message .= $post_type->labels->singular_name . ":\t" . $post->post_title . "\n\n";
107
 
108
  $edit_post_link = get_edit_post_link( $post->ID, 'raw' );
109
+
110
+ // translators: Placeholder refers to the post type singular name (e.g. "Post").
111
  $message .= sprintf( __( 'Edit %s', 'stream' ), $post_type->labels->singular_name ) . "\n<$edit_post_link>\n";
112
  }
113
 
114
  $message .= "\n";
115
 
116
  $edit_alert_link = admin_url( 'edit.php?post_type=wp_stream_alerts#post-' . $alert->ID );
117
+ $message .= __( 'Edit Alert', 'stream' ) . "\n<$edit_alert_link>";
118
 
119
  wp_mail( $options['email_recipient'], $options['email_subject'], $message );
120
  }
130
  if ( is_object( $alert ) ) {
131
  $alert_meta = $alert->alert_meta;
132
  }
133
+ $options = wp_parse_args(
134
+ $alert_meta, array(
135
+ 'email_recipient' => '',
136
+ 'email_subject' => '',
137
+ )
138
+ );
139
+
140
+ $form = new Form_Generator();
141
  echo '<span class="wp_stream_alert_type_description">' . esc_html__( 'Send a notification email to the recipient.', 'stream' ) . '</span>';
142
  echo '<label for="wp_stream_email_recipient"><span class="title">' . esc_html__( 'Recipient', 'stream' ) . '</span>';
143
  echo '<span class="input-text-wrap">';
144
+ echo $form->render_field(
145
+ 'text', array(
146
+ 'name' => 'wp_stream_email_recipient',
147
+ 'title' => esc_attr( __( 'Email Recipient', 'stream' ) ),
148
+ 'value' => $options['email_recipient'],
149
+ )
150
+ ); // Xss ok.
151
  echo '</span></label>';
152
 
153
  echo '<label for="wp_stream_email_subject"><span class="title">' . esc_html__( 'Subject', 'stream' ) . '</span>';
154
  echo '<span class="input-text-wrap">';
155
+ echo $form->render_field(
156
+ 'text', array(
157
+ 'name' => 'wp_stream_email_subject',
158
+ 'title' => esc_attr( __( 'Email Subject', 'stream' ) ),
159
+ 'value' => $options['email_subject'],
160
+ )
161
+ ); // Xss ok.
162
  echo '</span></label>';
163
  }
164
 
179
  $alert->alert_meta['email_subject'] = sanitize_text_field( wp_unslash( $_POST['wp_stream_email_subject'] ) );
180
  }
181
  }
182
+
183
  /**
184
  * Add alert meta if this is a highlight alert
185
  *
199
  $alert_meta['email_subject'] = $email_subject;
200
  }
201
  }
202
+
203
  return $alert_meta;
204
  }
205
  }
alerts/class-alert-type-highlight.php CHANGED
@@ -13,6 +13,7 @@ namespace WP_Stream;
13
  * @package WP_Stream
14
  */
15
  class Alert_Type_Highlight extends Alert_Type {
 
16
  /**
17
  * Main JS file script handle.
18
  */
@@ -68,17 +69,42 @@ class Alert_Type_Highlight extends Alert_Type {
68
  if ( ! is_admin() ) {
69
  return;
70
  }
71
- add_filter( 'wp_stream_record_classes', array( $this, 'post_class' ), 10, 2 );
72
- add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
73
- add_action( 'wp_ajax_' . self::REMOVE_ACTION, array( $this, 'ajax_remove_highlight' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
  if ( ! empty( $this->plugin->connectors->connectors ) && is_array( $this->plugin->connectors->connectors ) ) {
76
- foreach ( $this->plugin->connectors->connectors as $connector ) {
77
- add_filter( 'wp_stream_action_links_' . $connector->name, array( $this, 'action_link_remove_highlight' ), 10, 2 );
 
 
 
 
 
78
  }
79
  }
80
 
81
- add_filter( 'wp_stream_alerts_save_meta', array( $this, 'add_alert_meta' ), 10, 2 );
 
 
 
 
 
82
  }
83
 
84
  /**
@@ -93,10 +119,13 @@ class Alert_Type_Highlight extends Alert_Type {
93
  * @return void
94
  */
95
  public function alert( $record_id, $recordarr, $alert ) {
96
- $recordarr['ID'] = $record_id;
97
  $this->single_alert_id = $alert->ID;
98
  if ( ! empty( $alert->alert_meta['color'] ) ) {
99
- Alert::update_record_triggered_alerts( (object) $recordarr, $this->slug, array( 'highlight_color' => $alert->alert_meta['color'] ) );
 
 
 
100
  }
101
  }
102
 
@@ -111,22 +140,27 @@ class Alert_Type_Highlight extends Alert_Type {
111
  if ( is_object( $alert ) ) {
112
  $alert_meta = $alert->alert_meta;
113
  }
114
- $options = wp_parse_args( $alert_meta, array(
115
- 'color' => 'yellow',
116
- ) );
 
 
117
 
118
- $form = new Form_Generator;
119
  echo '<span class="wp_stream_alert_type_description">' . esc_html__( 'Highlight this alert on the Stream records page.', 'stream' ) . '</span>';
120
  echo '<label for="wp_stream_highlight_color"><span class="title">' . esc_html__( 'Color', 'stream' ) . '</span>';
121
  echo '<span class="input-text-wrap">';
122
- echo $form->render_field( 'select', array( // Xss ok.
123
- 'name' => 'wp_stream_highlight_color',
124
- 'title' => esc_attr( __( 'Highlight Color', 'stream' ) ),
125
- 'options' => $this->get_highlight_options(),
126
- 'value' => $options['color'],
127
- ) );
 
 
128
  echo '</span></label>';
129
  }
 
130
  /**
131
  * Lists available color options for alerts.
132
  *
@@ -154,7 +188,7 @@ class Alert_Type_Highlight extends Alert_Type {
154
  $alert->alert_meta['color'] = 'yellow';
155
  }
156
  $input_color = sanitize_text_field( wp_unslash( $_POST['wp_stream_highlight_color'] ) );
157
- if ( ! array_key_exists( $input_color , $this->get_highlight_options() ) ) {
158
  $alert->alert_meta['color'] = 'yellow';
159
  } else {
160
  $alert->alert_meta['color'] = $input_color;
@@ -199,7 +233,7 @@ class Alert_Type_Highlight extends Alert_Type {
199
  * @return mixed
200
  */
201
  public function action_link_remove_highlight( $actions, $record ) {
202
- $record = new Record( $record );
203
  $alerts_triggered = $record->get_meta( Alerts::ALERTS_TRIGGERED_META_KEY, true );
204
  if ( ! empty( $alerts_triggered[ $this->slug ] ) ) {
205
  $actions[ __( 'Remove Highlight', 'stream' ) ] = '#';
@@ -242,9 +276,9 @@ class Alert_Type_Highlight extends Alert_Type {
242
  if ( ! is_numeric( $record_id ) ) {
243
  wp_send_json_error( $failure_message );
244
  }
245
- $record_obj = new \stdClass();
246
- $record_obj->ID = $record_id;
247
- $record = new Record( $record_obj );
248
  $alerts_triggered = $record->get_meta( Alerts::ALERTS_TRIGGERED_META_KEY, true );
249
  if ( isset( $alerts_triggered[ $this->slug ] ) ) {
250
  unset( $alerts_triggered[ $this->slug ] );
@@ -260,12 +294,13 @@ class Alert_Type_Highlight extends Alert_Type {
260
  */
261
  public function enqueue_scripts( $page ) {
262
  if ( 'toplevel_page_wp_stream' === $page ) {
263
- wp_register_script( self::SCRIPT_HANDLE, $this->plugin->locations['url'] . 'alerts/js/alert-type-highlight.js', array( 'jquery' ) );
 
264
 
265
  $exports = array(
266
- 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
267
  'removeAction' => self::REMOVE_ACTION,
268
- 'security' => wp_create_nonce( self::REMOVE_ACTION_NONCE ),
269
  );
270
 
271
  wp_scripts()->add_data(
@@ -296,6 +331,7 @@ class Alert_Type_Highlight extends Alert_Type {
296
  $alert_meta['color'] = $color;
297
  }
298
  }
 
299
  return $alert_meta;
300
  }
301
  }
13
  * @package WP_Stream
14
  */
15
  class Alert_Type_Highlight extends Alert_Type {
16
+
17
  /**
18
  * Main JS file script handle.
19
  */
69
  if ( ! is_admin() ) {
70
  return;
71
  }
72
+ add_filter(
73
+ 'wp_stream_record_classes', array(
74
+ $this,
75
+ 'post_class',
76
+ ), 10, 2
77
+ );
78
+ add_action(
79
+ 'admin_enqueue_scripts', array(
80
+ $this,
81
+ 'enqueue_scripts',
82
+ )
83
+ );
84
+ add_action(
85
+ 'wp_ajax_' . self::REMOVE_ACTION, array(
86
+ $this,
87
+ 'ajax_remove_highlight',
88
+ )
89
+ );
90
 
91
  if ( ! empty( $this->plugin->connectors->connectors ) && is_array( $this->plugin->connectors->connectors ) ) {
92
+ foreach ( $this->plugin->connectors->connectors as $connector ) {
93
+ add_filter(
94
+ 'wp_stream_action_links_' . $connector->name, array(
95
+ $this,
96
+ 'action_link_remove_highlight',
97
+ ), 10, 2
98
+ );
99
  }
100
  }
101
 
102
+ add_filter(
103
+ 'wp_stream_alerts_save_meta', array(
104
+ $this,
105
+ 'add_alert_meta',
106
+ ), 10, 2
107
+ );
108
  }
109
 
110
  /**
119
  * @return void
120
  */
121
  public function alert( $record_id, $recordarr, $alert ) {
122
+ $recordarr['ID'] = $record_id;
123
  $this->single_alert_id = $alert->ID;
124
  if ( ! empty( $alert->alert_meta['color'] ) ) {
125
+ $alert_meta = array(
126
+ 'highlight_color' => $alert->alert_meta['color'],
127
+ );
128
+ Alert::update_record_triggered_alerts( (object) $recordarr, $this->slug, $alert_meta );
129
  }
130
  }
131
 
140
  if ( is_object( $alert ) ) {
141
  $alert_meta = $alert->alert_meta;
142
  }
143
+ $options = wp_parse_args(
144
+ $alert_meta, array(
145
+ 'color' => 'yellow',
146
+ )
147
+ );
148
 
149
+ $form = new Form_Generator();
150
  echo '<span class="wp_stream_alert_type_description">' . esc_html__( 'Highlight this alert on the Stream records page.', 'stream' ) . '</span>';
151
  echo '<label for="wp_stream_highlight_color"><span class="title">' . esc_html__( 'Color', 'stream' ) . '</span>';
152
  echo '<span class="input-text-wrap">';
153
+ echo $form->render_field(
154
+ 'select', array(
155
+ 'name' => 'wp_stream_highlight_color',
156
+ 'title' => esc_attr( __( 'Highlight Color', 'stream' ) ),
157
+ 'options' => $this->get_highlight_options(),
158
+ 'value' => $options['color'],
159
+ )
160
+ ); // Xss ok.
161
  echo '</span></label>';
162
  }
163
+
164
  /**
165
  * Lists available color options for alerts.
166
  *
188
  $alert->alert_meta['color'] = 'yellow';
189
  }
190
  $input_color = sanitize_text_field( wp_unslash( $_POST['wp_stream_highlight_color'] ) );
191
+ if ( ! array_key_exists( $input_color, $this->get_highlight_options() ) ) {
192
  $alert->alert_meta['color'] = 'yellow';
193
  } else {
194
  $alert->alert_meta['color'] = $input_color;
233
  * @return mixed
234
  */
235
  public function action_link_remove_highlight( $actions, $record ) {
236
+ $record = new Record( $record );
237
  $alerts_triggered = $record->get_meta( Alerts::ALERTS_TRIGGERED_META_KEY, true );
238
  if ( ! empty( $alerts_triggered[ $this->slug ] ) ) {
239
  $actions[ __( 'Remove Highlight', 'stream' ) ] = '#';
276
  if ( ! is_numeric( $record_id ) ) {
277
  wp_send_json_error( $failure_message );
278
  }
279
+ $record_obj = new \stdClass();
280
+ $record_obj->ID = $record_id;
281
+ $record = new Record( $record_obj );
282
  $alerts_triggered = $record->get_meta( Alerts::ALERTS_TRIGGERED_META_KEY, true );
283
  if ( isset( $alerts_triggered[ $this->slug ] ) ) {
284
  unset( $alerts_triggered[ $this->slug ] );
294
  */
295
  public function enqueue_scripts( $page ) {
296
  if ( 'toplevel_page_wp_stream' === $page ) {
297
+ $min = wp_stream_min_suffix();
298
+ wp_register_script( self::SCRIPT_HANDLE, $this->plugin->locations['url'] . 'alerts/js/alert-type-highlight.' . $min . 'js', array( 'jquery' ) );
299
 
300
  $exports = array(
301
+ 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
302
  'removeAction' => self::REMOVE_ACTION,
303
+ 'security' => wp_create_nonce( self::REMOVE_ACTION_NONCE ),
304
  );
305
 
306
  wp_scripts()->add_data(
331
  $alert_meta['color'] = $color;
332
  }
333
  }
334
+
335
  return $alert_meta;
336
  }
337
  }
alerts/class-alert-type-ifttt.php CHANGED
@@ -49,6 +49,7 @@ namespace WP_Stream;
49
  * @package WP_Stream
50
  */
51
  class Alert_Type_IFTTT extends Alert_Type {
 
52
  /**
53
  * Alert type name
54
  *
@@ -67,7 +68,6 @@ class Alert_Type_IFTTT extends Alert_Type {
67
  * Class Constructor
68
  *
69
  * @param Plugin $plugin Plugin object.
70
- * @return void
71
  */
72
  public function __construct( $plugin ) {
73
  parent::__construct( $plugin );
@@ -75,8 +75,14 @@ class Alert_Type_IFTTT extends Alert_Type {
75
  if ( ! is_admin() ) {
76
  return;
77
  }
78
- add_filter( 'wp_stream_alerts_save_meta', array( $this, 'add_alert_meta' ), 10, 2 );
 
 
 
 
 
79
  }
 
80
  /**
81
  * Record that the Alert was triggered by a Record.
82
  *
@@ -104,12 +110,14 @@ class Alert_Type_IFTTT extends Alert_Type {
104
  if ( is_object( $alert ) ) {
105
  $alert_meta = $alert->alert_meta;
106
  }
107
- $options = wp_parse_args( $alert_meta, array(
108
- 'maker_key' => '',
109
- 'event_name' => '',
110
- ) );
 
 
111
 
112
- $form = new Form_Generator;
113
 
114
  echo '<span class="wp_stream_alert_type_description">';
115
  echo esc_html__( 'Trigger an IFTTT Maker recipe.', 'stream' );
@@ -117,11 +125,13 @@ class Alert_Type_IFTTT extends Alert_Type {
117
  echo '</span>';
118
  echo '<label for="wp_stream_ifttt_maker_key"><span class="title">' . esc_html__( 'Maker Key', 'stream' ) . '</span>';
119
  echo '<span class="input-text-wrap">';
120
- echo $form->render_field( 'text', array( // Xss ok.
121
- 'name' => 'wp_stream_ifttt_maker_key',
122
- 'title' => esc_attr( __( 'Maker Key', 'stream' ) ),
123
- 'value' => $options['maker_key'],
124
- ) );
 
 
125
  echo '</span>';
126
  printf(
127
  '<span class="input-text-wrap"><a href="%1$s" target="_blank">%2$s %3$s</a></span>',
@@ -133,11 +143,13 @@ class Alert_Type_IFTTT extends Alert_Type {
133
 
134
  echo '<label for="wp_stream_ifttt_event_name"><span class="title">' . esc_html__( 'Event Name', 'stream' ) . '</span>';
135
  echo '<span class="input-text-wrap">';
136
- echo $form->render_field( 'text', array( // Xss ok.
137
- 'name' => 'wp_stream_ifttt_event_name',
138
- 'title' => esc_attr( __( 'Event Name', 'stream' ) ),
139
- 'value' => $options['event_name'],
140
- ) );
 
 
141
  echo '</span>';
142
  printf(
143
  '<span class="input-text-wrap"><a href="%1$s" target="_blank">%2$s %3$s</a></span>',
@@ -178,7 +190,7 @@ class Alert_Type_IFTTT extends Alert_Type {
178
  * array keys of data. (also documented below)
179
  *
180
  * @param object $alert The Alert object.
181
- * @param array $recordarr Array of Record data.
182
  *
183
  * @return bool
184
  */
@@ -187,14 +199,18 @@ class Alert_Type_IFTTT extends Alert_Type {
187
  return false;
188
  }
189
 
190
- $record_data = wp_parse_args( $recordarr, array(
191
- 'summary' => sprintf( __( 'The event %s was triggered' ), $alert->alert_meta['event_name'] ),
192
- 'user_id' => get_current_user_id(),
193
- 'created' => current_time( 'Y-m-d H:i:s' ), // Blog's local time.
194
- ) );
 
 
 
 
195
 
196
  $user_id = $recordarr['user_id'];
197
- $user = get_user_by( 'id', $user_id );
198
 
199
  /**
200
  * Filter User data field
@@ -219,7 +235,7 @@ class Alert_Type_IFTTT extends Alert_Type {
219
  * @return string
220
  */
221
  $date_format = apply_filters( 'wp_stream_alert_ifttt_date_format', 'Y-m-d H:i:s', $alert, $recordarr );
222
- $date = date( $date_format, strtotime( $created ) );
223
 
224
  $url = 'https://maker.ifttt.com/trigger/' . $alert->alert_meta['event_name'] . '/with/key/' . $alert->alert_meta['maker_key'];
225
 
@@ -242,13 +258,13 @@ class Alert_Type_IFTTT extends Alert_Type {
242
  'headers' => array(
243
  'Content-Type' => 'application/json',
244
  ),
245
- 'body' => wp_json_encode(
246
  array(
247
  /**
248
  * Filter the first IFTTT alert value
249
  *
250
  * @param string $summary The Record's summary.
251
- * @param object $alert The Alert.
252
  * @param array $recordarr Array of Record data.
253
  * @return mixed
254
  */
@@ -258,7 +274,7 @@ class Alert_Type_IFTTT extends Alert_Type {
258
  * Filter the second IFTTT alert value
259
  *
260
  * @param string $user_value The user meta value requested above.
261
- * @param int $user_id The user ID who fired the Alert.
262
  * @param object $alert The Alert.
263
  * @param array $recordarr Array of Record data.
264
  * @return mixed
@@ -269,7 +285,7 @@ class Alert_Type_IFTTT extends Alert_Type {
269
  * Filter the third IFTTT alert value
270
  *
271
  * @param string $date The Record's date.
272
- * @param object $alert The Alert.
273
  * @param array $recordarr Array of Record data.
274
  * @return mixed
275
  */
@@ -277,12 +293,15 @@ class Alert_Type_IFTTT extends Alert_Type {
277
  )
278
  ),
279
  );
 
280
  $response = wp_remote_post( $url, $args );
281
  if ( ! is_array( $response ) ) {
282
  return false;
283
  }
 
284
  return true;
285
  }
 
286
  /**
287
  * Add alert meta if this is a highlight alert
288
  *
@@ -302,6 +321,7 @@ class Alert_Type_IFTTT extends Alert_Type {
302
  $alert_meta['event_name'] = $event_name;
303
  }
304
  }
 
305
  return $alert_meta;
306
  }
307
  }
49
  * @package WP_Stream
50
  */
51
  class Alert_Type_IFTTT extends Alert_Type {
52
+
53
  /**
54
  * Alert type name
55
  *
68
  * Class Constructor
69
  *
70
  * @param Plugin $plugin Plugin object.
 
71
  */
72
  public function __construct( $plugin ) {
73
  parent::__construct( $plugin );
75
  if ( ! is_admin() ) {
76
  return;
77
  }
78
+ add_filter(
79
+ 'wp_stream_alerts_save_meta', array(
80
+ $this,
81
+ 'add_alert_meta',
82
+ ), 10, 2
83
+ );
84
  }
85
+
86
  /**
87
  * Record that the Alert was triggered by a Record.
88
  *
110
  if ( is_object( $alert ) ) {
111
  $alert_meta = $alert->alert_meta;
112
  }
113
+ $options = wp_parse_args(
114
+ $alert_meta, array(
115
+ 'maker_key' => '',
116
+ 'event_name' => '',
117
+ )
118
+ );
119
 
120
+ $form = new Form_Generator();
121
 
122
  echo '<span class="wp_stream_alert_type_description">';
123
  echo esc_html__( 'Trigger an IFTTT Maker recipe.', 'stream' );
125
  echo '</span>';
126
  echo '<label for="wp_stream_ifttt_maker_key"><span class="title">' . esc_html__( 'Maker Key', 'stream' ) . '</span>';
127
  echo '<span class="input-text-wrap">';
128
+ echo $form->render_field(
129
+ 'text', array(
130
+ 'name' => 'wp_stream_ifttt_maker_key',
131
+ 'title' => esc_attr( __( 'Maker Key', 'stream' ) ),
132
+ 'value' => $options['maker_key'],
133
+ )
134
+ ); // Xss ok.
135
  echo '</span>';
136
  printf(
137
  '<span class="input-text-wrap"><a href="%1$s" target="_blank">%2$s %3$s</a></span>',
143
 
144
  echo '<label for="wp_stream_ifttt_event_name"><span class="title">' . esc_html__( 'Event Name', 'stream' ) . '</span>';
145
  echo '<span class="input-text-wrap">';
146
+ echo $form->render_field(
147
+ 'text', array(
148
+ 'name' => 'wp_stream_ifttt_event_name',
149
+ 'title' => esc_attr( __( 'Event Name', 'stream' ) ),
150
+ 'value' => $options['event_name'],
151
+ )
152
+ ); // Xss ok.
153
  echo '</span>';
154
  printf(
155
  '<span class="input-text-wrap"><a href="%1$s" target="_blank">%2$s %3$s</a></span>',
190
  * array keys of data. (also documented below)
191
  *
192
  * @param object $alert The Alert object.
193
+ * @param array $recordarr Array of Record data.
194
  *
195
  * @return bool
196
  */
199
  return false;
200
  }
201
 
202
+ $record_data = wp_parse_args(
203
+ $recordarr, array(
204
+ // translators: Placeholder refers to the Event Name of the Alert (e.g. "Update a post")
205
+ 'summary' => sprintf( __( 'The event %s was triggered' ), $alert->alert_meta['event_name'] ),
206
+ 'user_id' => get_current_user_id(),
207
+ 'created' => current_time( 'Y-m-d H:i:s' ),
208
+ // Blog's local time.
209
+ )
210
+ );
211
 
212
  $user_id = $recordarr['user_id'];
213
+ $user = get_user_by( 'id', $user_id );
214
 
215
  /**
216
  * Filter User data field
235
  * @return string
236
  */
237
  $date_format = apply_filters( 'wp_stream_alert_ifttt_date_format', 'Y-m-d H:i:s', $alert, $recordarr );
238
+ $date = date( $date_format, strtotime( $created ) );
239
 
240
  $url = 'https://maker.ifttt.com/trigger/' . $alert->alert_meta['event_name'] . '/with/key/' . $alert->alert_meta['maker_key'];
241
 
258
  'headers' => array(
259
  'Content-Type' => 'application/json',
260
  ),
261
+ 'body' => wp_json_encode(
262
  array(
263
  /**
264
  * Filter the first IFTTT alert value
265
  *
266
  * @param string $summary The Record's summary.
267
+ * @param object $alert The Alert.
268
  * @param array $recordarr Array of Record data.
269
  * @return mixed
270
  */
274
  * Filter the second IFTTT alert value
275
  *
276
  * @param string $user_value The user meta value requested above.
277
+ * @param int $user_id The user ID who fired the Alert.
278
  * @param object $alert The Alert.
279
  * @param array $recordarr Array of Record data.
280
  * @return mixed
285
  * Filter the third IFTTT alert value
286
  *
287
  * @param string $date The Record's date.
288
+ * @param object $alert The Alert.
289
  * @param array $recordarr Array of Record data.
290
  * @return mixed
291
  */
293
  )
294
  ),
295
  );
296
+
297
  $response = wp_remote_post( $url, $args );
298
  if ( ! is_array( $response ) ) {
299
  return false;
300
  }
301
+
302
  return true;
303
  }
304
+
305
  /**
306
  * Add alert meta if this is a highlight alert
307
  *
321
  $alert_meta['event_name'] = $event_name;
322
  }
323
  }
324
+
325
  return $alert_meta;
326
  }
327
  }
alerts/class-alert-type-menu-alert.php CHANGED
@@ -13,6 +13,7 @@ namespace WP_Stream;
13
  * @package WP_Stream
14
  */
15
  class Alert_Type_Menu_Alert extends Alert_Type {
 
16
  /**
17
  * Alert type name
18
  *
@@ -48,7 +49,6 @@ class Alert_Type_Menu_Alert extends Alert_Type {
48
  */
49
  public function alert( $record_id, $recordarr, $options ) {
50
  $this->add_message( $recordarr['summary'] );
51
- return;
52
  }
53
 
54
  /**
@@ -62,19 +62,23 @@ class Alert_Type_Menu_Alert extends Alert_Type {
62
  if ( is_object( $alert ) ) {
63
  $alert_meta = $alert->alert_meta;
64
  }
65
- $options = wp_parse_args( $alert_meta, array(
66
- 'clear_immediate' => false,
67
- ) );
68
-
69
- $form = new Form_Generator;
70
- $form->add_field( 'checkbox', array(
71
- 'name' => 'wp_stream_menu_alert_clear_immediate',
72
- 'text' => esc_attr( __( 'Clear alerts after seen.', 'stream' ) ),
73
- 'value' => $options['clear_immediate'],
74
- 'title' => __( 'Menu Bar', 'stream' ),
75
- ) );
76
-
77
- echo $form->render_all(); // Xss ok.
 
 
 
 
78
  }
79
 
80
  /**
@@ -101,25 +105,34 @@ class Alert_Type_Menu_Alert extends Alert_Type {
101
  return false;
102
  }
103
 
104
- $wp_admin_bar->add_node( array(
105
- 'id' => 'wp_stream_alert_notify',
106
- 'parent' => false,
107
- 'title' => __( 'New Stream Alert', 'stream' ),
108
- 'href' => '#',
109
- 'meta' => array( 'class' => 'opposite' ),
110
- ) );
 
 
 
 
111
 
112
  foreach ( $messages as $key => $message ) {
113
- $wp_admin_bar->add_node( array(
114
- 'id' => 'wp_stream_alert_notify_' . $key,
115
- 'parent' => 'wp_stream_alert_notify',
116
- 'title' => esc_html( $message ),
117
- 'href' => '#',
118
- 'meta' => array( 'class' => 'opposite' ),
119
- ) );
 
 
 
 
120
  }
121
 
122
  $this->clear_messages();
 
123
  return true;
124
  }
125
 
@@ -131,8 +144,9 @@ class Alert_Type_Menu_Alert extends Alert_Type {
131
  * @return array List of alert messages
132
  */
133
  public function get_messages() {
134
- $current_user = wp_get_current_user();
135
- $messages = get_user_meta( $current_user->ID, $this->get_key(), false );
 
136
  return $messages;
137
  }
138
 
@@ -145,7 +159,7 @@ class Alert_Type_Menu_Alert extends Alert_Type {
145
  * @return void
146
  */
147
  public function add_message( $message ) {
148
- $current_user = wp_get_current_user();
149
  add_user_meta( $current_user->ID, $this->get_key(), $message, false );
150
  }
151
 
@@ -158,7 +172,7 @@ class Alert_Type_Menu_Alert extends Alert_Type {
158
  * @return void
159
  */
160
  public function clear_messages( $global = false ) {
161
- $current_user = wp_get_current_user();
162
  delete_user_meta( $current_user->ID, $this->get_key(), $global );
163
  }
164
 
13
  * @package WP_Stream
14
  */
15
  class Alert_Type_Menu_Alert extends Alert_Type {
16
+
17
  /**
18
  * Alert type name
19
  *
49
  */
50
  public function alert( $record_id, $recordarr, $options ) {
51
  $this->add_message( $recordarr['summary'] );
 
52
  }
53
 
54
  /**
62
  if ( is_object( $alert ) ) {
63
  $alert_meta = $alert->alert_meta;
64
  }
65
+ $options = wp_parse_args(
66
+ $alert_meta, array(
67
+ 'clear_immediate' => false,
68
+ )
69
+ );
70
+
71
+ $form = new Form_Generator();
72
+ $form->add_field(
73
+ 'checkbox', array(
74
+ 'name' => 'wp_stream_menu_alert_clear_immediate',
75
+ 'text' => esc_attr( __( 'Clear alerts after seen.', 'stream' ) ),
76
+ 'value' => $options['clear_immediate'],
77
+ 'title' => __( 'Menu Bar', 'stream' ),
78
+ )
79
+ );
80
+
81
+ echo $form->render_fields(); // Xss ok.
82
  }
83
 
84
  /**
105
  return false;
106
  }
107
 
108
+ $wp_admin_bar->add_node(
109
+ array(
110
+ 'id' => 'wp_stream_alert_notify',
111
+ 'parent' => false,
112
+ 'title' => __( 'New Stream Alert', 'stream' ),
113
+ 'href' => '#',
114
+ 'meta' => array(
115
+ 'class' => 'opposite',
116
+ ),
117
+ )
118
+ );
119
 
120
  foreach ( $messages as $key => $message ) {
121
+ $wp_admin_bar->add_node(
122
+ array(
123
+ 'id' => 'wp_stream_alert_notify_' . $key,
124
+ 'parent' => 'wp_stream_alert_notify',
125
+ 'title' => esc_html( $message ),
126
+ 'href' => '#',
127
+ 'meta' => array(
128
+ 'class' => 'opposite',
129
+ ),
130
+ )
131
+ );
132
  }
133
 
134
  $this->clear_messages();
135
+
136
  return true;
137
  }
138
 
144
  * @return array List of alert messages
145
  */
146
  public function get_messages() {
147
+ $current_user = wp_get_current_user();
148
+ $messages = get_user_meta( $current_user->ID, $this->get_key(), false );
149
+
150
  return $messages;
151
  }
152
 
159
  * @return void
160
  */
161
  public function add_message( $message ) {
162
+ $current_user = wp_get_current_user();
163
  add_user_meta( $current_user->ID, $this->get_key(), $message, false );
164
  }
165
 
172
  * @return void
173
  */
174
  public function clear_messages( $global = false ) {
175
+ $current_user = wp_get_current_user();
176
  delete_user_meta( $current_user->ID, $this->get_key(), $global );
177
  }
178
 
alerts/class-alert-type-none.php CHANGED
@@ -36,6 +36,6 @@ class Alert_Type_None extends Alert_Type {
36
  * @return void
37
  */
38
  public function alert( $record_id, $recordarr, $options ) {
39
- return;
40
  }
41
  }
36
  * @return void
37
  */
38
  public function alert( $record_id, $recordarr, $options ) {
39
+ // Do nothing.
40
  }
41
  }
alerts/class-alert-type-slack.php ADDED
@@ -0,0 +1,303 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Slack Alerts.
4
+ *
5
+ * @package WP_Stream
6
+ */
7
+
8
+ namespace WP_Stream;
9
+
10
+ /**
11
+ * Class Alert_Type_Slack
12
+ *
13
+ * @package WP_Stream
14
+ */
15
+ class Alert_Type_Slack extends Alert_Type {
16
+
17
+ /**
18
+ * Alert type name
19
+ *
20
+ * @var string
21
+ */
22
+ public $name = 'Slack';
23
+
24
+ /**
25
+ * Alert type slug
26
+ *
27
+ * @var string
28
+ */
29
+ public $slug = 'slack';
30
+
31
+ /**
32
+ * Class Constructor
33
+ *
34
+ * @param Plugin $plugin Plugin object.
35
+ */
36
+ public function __construct( $plugin ) {
37
+ parent::__construct( $plugin );
38
+ $this->plugin = $plugin;
39
+ if ( ! is_admin() ) {
40
+ return;
41
+ }
42
+ add_filter(
43
+ 'wp_stream_alerts_save_meta', array(
44
+ $this,
45
+ 'add_alert_meta',
46
+ ), 10, 2
47
+ );
48
+ }
49
+
50
+ /**
51
+ * Sends an message to the Slack channel.
52
+ *
53
+ * @param int $record_id Record that triggered notification.
54
+ * @param array $recordarr Record details.
55
+ * @param Alert $alert Alert options.
56
+ * @return void
57
+ */
58
+ public function alert( $record_id, $recordarr, $alert ) {
59
+ $options = wp_parse_args(
60
+ $alert->alert_meta, array(
61
+ 'webhook' => '',
62
+ 'channel' => '',
63
+ 'username' => '',
64
+ 'icon' => '',
65
+ 'trigger_action' => '',
66
+ 'trigger_connector' => '',
67
+ 'trigger_context' => '',
68
+ )
69
+ );
70
+ if ( empty( $options['webhook'] ) ) {
71
+ return;
72
+ }
73
+ $user_id = (int) $recordarr['user_id'];
74
+ $user = get_userdata( $user_id );
75
+ $logo = wp_get_attachment_image_src( get_theme_mod( 'custom_logo' ), 'full' );
76
+ $context = $recordarr['context'];
77
+ $action = $recordarr['action'];
78
+
79
+ if ( ! empty( $alert->alert_meta['trigger_context'] ) ) {
80
+ $context = $this->plugin->alerts->alert_triggers['context']->get_display_value( 'list_table', $alert );
81
+ }
82
+ if ( ! empty( $alert->alert_meta['trigger_action'] ) ) {
83
+ $action = $this->plugin->alerts->alert_triggers['action']->get_display_value( 'list_table', $alert );
84
+ }
85
+
86
+ $fields = array(
87
+ array(
88
+ 'title' => 'IP Address',
89
+ 'value' => $recordarr['ip'],
90
+ 'short' => true,
91
+ ),
92
+ array(
93
+ 'title' => 'Connector',
94
+ 'value' => $recordarr['connector'],
95
+ 'short' => true,
96
+ ),
97
+ array(
98
+ 'title' => 'Context',
99
+ 'value' => $context,
100
+ 'short' => true,
101
+ ),
102
+ array(
103
+ 'title' => 'Action',
104
+ 'value' => $action,
105
+ 'short' => true,
106
+ ),
107
+ );
108
+
109
+ $post = null;
110
+ if ( isset( $recordarr['object_id'] ) ) {
111
+ $post_id = $recordarr['object_id'];
112
+ $post = get_post( $post_id );
113
+ }
114
+ if ( is_object( $post ) && ! empty( $post ) ) {
115
+ $post_type = get_post_type_object( $post->post_type );
116
+ $edit_post_link = get_edit_post_link( $post->ID, 'raw' );
117
+ array_push(
118
+ $fields, array(
119
+ 'title' => 'Edit ' . $post_type->labels->singular_name,
120
+ 'value' => "<$edit_post_link>",
121
+ 'short' => false,
122
+ )
123
+ );
124
+ }
125
+
126
+ $edit_alert_link = admin_url( 'edit.php?post_type=wp_stream_alerts#post-' . $alert->ID );
127
+ array_push(
128
+ $fields, array(
129
+ 'title' => 'Edit Alert',
130
+ 'value' => "<$edit_alert_link>",
131
+ 'short' => false,
132
+ )
133
+ );
134
+
135
+ $attachment = array(
136
+ 'author_icon' => get_avatar_url( $user_id, 16 ),
137
+ 'author_link' => admin_url( "admin.php?page=wp_stream&user_id=$user_id" ),
138
+ 'author_name' => trim( "$user->first_name $user->last_name" ),
139
+ 'fallback' => html_entity_decode( $recordarr['summary'] ),
140
+ 'fields' => $fields,
141
+ 'footer' => get_bloginfo( 'name' ),
142
+ 'footer_icon' => get_site_icon_url( 16, $logo[0], $recordarr['blog_id'] ),
143
+ 'title' => html_entity_decode( $recordarr['summary'] ),
144
+ 'ts' => strtotime( $recordarr['created'] ),
145
+ );
146
+ if ( array_key_exists( 'object_id', $recordarr ) ) {
147
+ $object_id = (int) $recordarr['object_id'];
148
+ $context = $recordarr['context'];
149
+ $attachment['title_link'] = admin_url( "admin.php?page=wp_stream&object_id=$object_id&context=$context" );
150
+ }
151
+ $data = array(
152
+ 'attachments' => array( $attachment ),
153
+ );
154
+ if ( ! empty( $options['channel'] ) ) {
155
+ $data['channel'] = $options['channel'];
156
+ }
157
+ if ( ! empty( $options['username'] ) ) {
158
+ $data['username'] = $options['username'];
159
+ }
160
+ if ( ! empty( $options['icon'] ) ) {
161
+ if ( substr( $options['icon'], 0, 1 ) === ':' ) {
162
+ $data['icon_emoji'] = $options['icon'];
163
+ } elseif ( substr( $options['icon'], 0, 4 ) === 'http' ) {
164
+ $data['icon_url'] = $options['icon'];
165
+ }
166
+ }
167
+ wp_remote_post(
168
+ $options['webhook'], array(
169
+ 'body' => wp_json_encode( $data ),
170
+ 'headers' => array( 'Content-Type' => 'application/json' ),
171
+ )
172
+ );
173
+ }
174
+
175
+ /**
176
+ * Displays a settings form for the alert type
177
+ *
178
+ * @param Alert $alert Alert object for the currently displayed alert.
179
+ * @return void
180
+ */
181
+ public function display_fields( $alert ) {
182
+ $alert_meta = array();
183
+ if ( is_object( $alert ) ) {
184
+ $alert_meta = $alert->alert_meta;
185
+ }
186
+ $options = wp_parse_args(
187
+ $alert_meta, array(
188
+ 'webhook' => '',
189
+ 'channel' => '',
190
+ 'username' => '',
191
+ 'icon' => '',
192
+ )
193
+ );
194
+ $form = new Form_Generator();
195
+ echo '<span class="wp_stream_alert_type_description">' . esc_html__( 'Send a rich message notification to Slack.', 'stream' ) . '</span>';
196
+ echo '<label for="wp_stream_slack_webhook"><span class="title">' . esc_html__( 'Webhook URL', 'stream' ) . '</span>';
197
+ echo '<span class="input-text-wrap">';
198
+ echo $form->render_field(
199
+ 'text', array(
200
+ 'name' => 'wp_stream_slack_webhook',
201
+ 'title' => esc_attr( __( 'Webhook URL', 'stream' ) ),
202
+ 'value' => $options['webhook'],
203
+ )
204
+ ); // Xss ok.
205
+ echo '</span>';
206
+ echo '<span class="input-text-wrap">' . esc_html__( 'The webhook URL', 'stream' ) . '</span>';
207
+ echo '</label>';
208
+ echo '<label for="wp_stream_slack_channel"><span class="title">' . esc_html__( 'Channel', 'stream' ) . '</span>';
209
+ echo '<span class="input-text-wrap">';
210
+ echo $form->render_field(
211
+ 'text', array(
212
+ 'name' => 'wp_stream_slack_channel',
213
+ 'title' => esc_attr( __( 'Channel', 'stream' ) ),
214
+ 'value' => $options['channel'],
215
+ )
216
+ ); // Xss ok.
217
+ echo '</span>';
218
+ echo '<span class="input-text-wrap">' . esc_html__( 'The channel to send to (optional)', 'stream' ) . '</span>';
219
+ echo '</label>';
220
+ echo '<label for="wp_stream_slack_username"><span class="title">' . esc_html__( 'Username', 'stream' ) . '</span>';
221
+ echo '<span class="input-text-wrap">';
222
+ echo $form->render_field(
223
+ 'text', array(
224
+ 'name' => 'wp_stream_slack_username',
225
+ 'title' => esc_attr( __( 'Username', 'stream' ) ),
226
+ 'value' => $options['username'],
227
+ )
228
+ ); // Xss ok.
229
+ echo '</span>';
230
+ echo '<span class="input-text-wrap">' . esc_html__( 'The username to send as (optional)', 'stream' ) . '</span>';
231
+ echo '</label>';
232
+ echo '<label for="wp_stream_slack_icon"><span class="title">' . esc_html__( 'Icon', 'stream' ) . '</span>';
233
+ echo '<span class="input-text-wrap">';
234
+ echo $form->render_field(
235
+ 'text', array(
236
+ 'name' => 'wp_stream_slack_icon',
237
+ 'title' => esc_attr( __( 'Icon', 'stream' ) ),
238
+ 'value' => $options['icon'],
239
+ )
240
+ ); // Xss ok.
241
+ echo '</span>';
242
+ echo '<span class="input-text-wrap">' . esc_html__( 'The URL or emoji (with colons!) to use as the icon (optional)', 'stream' ) . '</span>';
243
+ echo '</label>';
244
+ }
245
+
246
+ /**
247
+ * Validates and saves form settings for later use.
248
+ *
249
+ * @param Alert $alert Alert object for the currently displayed alert.
250
+ * @return void
251
+ */
252
+ public function save_fields( $alert ) {
253
+ check_admin_referer( 'save_alert', 'wp_stream_alerts_nonce' );
254
+
255
+ $webhook = wp_stream_filter_input( 'INPUT_POST', 'wp_stream_slack_webhook', 'FILTER_VALIDATE_URL' );
256
+ if ( ! empty( $webhook ) ) {
257
+ $alert->alert_meta['webhook'] = $webhook;
258
+ }
259
+ $channel = wp_stream_filter_input( INPUT_POST, 'wp_stream_slack_channel' );
260
+ if ( ! empty( $channel ) ) {
261
+ $alert->alert_meta['channel'] = $channel;
262
+ }
263
+ $username = wp_stream_filter_input( INPUT_POST, 'wp_stream_slack_username' );
264
+ if ( ! empty( $username ) ) {
265
+ $alert->alert_meta['username'] = $username;
266
+ }
267
+ $icon = wp_stream_filter_input( INPUT_POST, 'wp_stream_slack_icon' );
268
+ if ( ! empty( $icon ) ) {
269
+ $alert->alert_meta['icon'] = $icon;
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Add alert meta if this is a Slack alert
275
+ *
276
+ * @param array $alert_meta The metadata to be inserted for this alert.
277
+ * @param string $alert_type The type of alert being added or updated.
278
+ *
279
+ * @return mixed
280
+ */
281
+ public function add_alert_meta( $alert_meta, $alert_type ) {
282
+ if ( $this->slug === $alert_type ) {
283
+ $webhook = wp_stream_filter_input( INPUT_POST, 'wp_stream_slack_webhook' );
284
+ if ( ! empty( $webhook ) ) {
285
+ $alert_meta['webhook'] = $webhook;
286
+ }
287
+ $channel = wp_stream_filter_input( INPUT_POST, 'wp_stream_slack_channel' );
288
+ if ( ! empty( $channel ) ) {
289
+ $alert_meta['channel'] = $channel;
290
+ }
291
+ $username = wp_stream_filter_input( INPUT_POST, 'wp_stream_slack_username' );
292
+ if ( ! empty( $username ) ) {
293
+ $alert_meta['username'] = $username;
294
+ }
295
+ $icon = wp_stream_filter_input( INPUT_POST, 'wp_stream_slack_icon' );
296
+ if ( ! empty( $icon ) ) {
297
+ $alert_meta['icon'] = $icon;
298
+ }
299
+ }
300
+
301
+ return $alert_meta;
302
+ }
303
+ }
alerts/js/alert-type-highlight.js CHANGED
@@ -17,59 +17,71 @@ var streamAlertTypeHighlight = ( function( $ ) {
17
  * @returns void.
18
  */
19
  self.init = function() {
20
- $( document ).ready( function() {
 
21
 
22
- /**
23
  * Remove highlights on Record list screen.
24
  *
25
  * @returns void.
26
  */
27
- $( '.alert-highlight .action-link[href="#"]' ).each( function() {
28
- var actionLink = $( this );
 
29
 
30
- /**
31
- * Ajax call to remove the highlight.
32
- *
33
- * @returns void.
34
- */
35
- actionLink.click( function( e ) {
36
- var recordId, data;
37
- e.preventDefault();
38
- recordId = actionLink.parents( '.alert-highlight' ).attr( 'class' ).match( /record\-id\-[\w-]*\b/ );
39
- recordId = recordId[0].replace( 'record-id-', '' );
 
40
 
41
- data = {
42
- action: self.removeAction,
43
- security: self.security,
44
- recordId: recordId
45
- };
46
 
47
- $.post( self.ajaxUrl, data, function( response ) {
48
- if ( true === response.success ) {
49
- ajaxDone();
50
- }
51
- });
 
 
52
 
53
- /**
54
- * Fires when Ajax complete.
55
- */
56
- function ajaxDone() {
57
- var row = actionLink.parents( '.alert-highlight' ),
58
- odd = $( '.striped > tbody > :nth-child( odd )' );
59
- if ( row.is( odd ) ) {
60
- row.animate( { backgroundColor: '#f9f9f9' }, 300, function() {
61
- row.removeClass( 'alert-highlight' );
62
- });
63
- } else {
64
- row.animate( { backgroundColor: '' }, 300, function() {
65
- row.removeClass( 'alert-highlight' );
66
- });
 
 
 
 
 
 
 
 
 
67
  }
68
- actionLink.remove();
69
- }
70
- });
71
- });
72
- }); // End document.ready().
73
  };
74
 
75
  return self;
17
  * @returns void.
18
  */
19
  self.init = function() {
20
+ $( document ).ready(
21
+ function() {
22
 
23
+ /**
24
  * Remove highlights on Record list screen.
25
  *
26
  * @returns void.
27
  */
28
+ $( '.alert-highlight .action-link[href="#"]' ).each(
29
+ function() {
30
+ var actionLink = $( this );
31
 
32
+ /**
33
+ * Ajax call to remove the highlight.
34
+ *
35
+ * @returns void.
36
+ */
37
+ actionLink.click(
38
+ function( e ) {
39
+ var recordId, data;
40
+ e.preventDefault();
41
+ recordId = actionLink.parents( '.alert-highlight' ).attr( 'class' ).match( /record\-id\-[\w-]*\b/ );
42
+ recordId = recordId[0].replace( 'record-id-', '' );
43
 
44
+ data = {
45
+ action: self.removeAction,
46
+ security: self.security,
47
+ recordId: recordId
48
+ };
49
 
50
+ $.post(
51
+ self.ajaxUrl, data, function( response ) {
52
+ if ( true === response.success ) {
53
+ ajaxDone();
54
+ }
55
+ }
56
+ );
57
 
58
+ /**
59
+ * Fires when Ajax complete.
60
+ */
61
+ function ajaxDone() {
62
+ var row = actionLink.parents( '.alert-highlight' ),
63
+ odd = $( '.striped > tbody > :nth-child( odd )' );
64
+ if ( row.is( odd ) ) {
65
+ row.animate(
66
+ { backgroundColor: '#f9f9f9' }, 300, function() {
67
+ row.removeClass( 'alert-highlight' );
68
+ }
69
+ );
70
+ } else {
71
+ row.animate(
72
+ { backgroundColor: '' }, 300, function() {
73
+ row.removeClass( 'alert-highlight' );
74
+ }
75
+ );
76
+ }
77
+ actionLink.remove();
78
+ }
79
+ }
80
+ );
81
  }
82
+ );
83
+ }
84
+ ); // End document.ready().
 
 
85
  };
86
 
87
  return self;
classes/class-admin.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WP_Stream;
3
 
4
  use DateTime;
@@ -8,6 +9,7 @@ use \WP_CLI;
8
  use \WP_Roles;
9
 
10
  class Admin {
 
11
  /**
12
  * Hold Plugin class
13
  *
@@ -125,7 +127,7 @@ class Admin {
125
 
126
  // Ensure function used in various methods is pre-loaded.
127
  if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
128
- require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
129
  }
130
 
131
  // User and role caps.
@@ -152,24 +154,49 @@ class Admin {
152
  add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
153
 
154
  // Plugin action links.
155
- add_filter( 'plugin_action_links', array( $this, 'plugin_action_links' ), 10, 2 );
 
 
 
 
 
156
 
157
  // Load admin scripts and styles.
158
- add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
 
 
 
 
 
159
  add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
160
 
161
  // Reset Streams database.
162
- add_action( 'wp_ajax_wp_stream_reset', array( $this, 'wp_ajax_reset' ) );
 
 
 
 
 
163
 
164
  // Uninstall Streams and Deactivate plugin.
165
  $uninstall = $this->plugin->db->driver->purge_storage( $this->plugin );
166
 
167
  // Auto purge setup.
168
  add_action( 'wp_loaded', array( $this, 'purge_schedule_setup' ) );
169
- add_action( 'wp_stream_auto_purge', array( $this, 'purge_scheduled_action' ) );
 
 
 
 
 
170
 
171
  // Ajax users list.
172
- add_action( 'wp_ajax_wp_stream_filters', array( $this, 'ajax_filters' ) );
 
 
 
 
 
173
  }
174
 
175
  /**
@@ -331,7 +358,12 @@ class Admin {
331
  do_action( 'wp_stream_admin_menu_screens' );
332
 
333
  // Register the list table early, so it associates the column headers with 'Screen settings'.
334
- add_action( 'load-' . $this->screen_id['main'], array( $this, 'register_list_table' ) );
 
 
 
 
 
335
  }
336
  }
337
 
@@ -358,7 +390,8 @@ class Admin {
358
  wp_register_script( 'wp-stream-timeago-locale', $this->plugin->locations['url'] . sprintf( $file_tmpl, 'en' ), array( 'wp-stream-timeago' ), '1' );
359
  }
360
 
361
- wp_enqueue_style( 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/css/admin.css', array(), $this->plugin->get_version() );
 
362
 
363
  $script_screens = array( 'plugins.php' );
364
 
@@ -369,9 +402,24 @@ class Admin {
369
  wp_enqueue_script( 'wp-stream-timeago' );
370
  wp_enqueue_script( 'wp-stream-timeago-locale' );
371
 
372
- wp_enqueue_script( 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/js/admin.js', array( 'jquery', 'wp-stream-select2' ), $this->plugin->get_version() );
373
- wp_enqueue_script( 'wp-stream-admin-exclude', $this->plugin->locations['url'] . 'ui/js/exclude.js', array( 'jquery', 'wp-stream-select2' ), $this->plugin->get_version() );
374
- wp_enqueue_script( 'wp-stream-live-updates', $this->plugin->locations['url'] . 'ui/js/live-updates.js', array( 'jquery', 'heartbeat' ), $this->plugin->get_version() );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
375
 
376
  wp_localize_script(
377
  'wp-stream-admin',
@@ -392,10 +440,14 @@ class Admin {
392
  'wp_stream_live_updates',
393
  array(
394
  'current_screen' => $hook,
395
- 'current_page' => isset( $_GET['paged'] ) ? esc_js( $_GET['paged'] ) : '1', // input var okay
396
- 'current_order' => isset( $_GET['order'] ) ? esc_js( $_GET['order'] ) : 'desc', // input var okay
397
- 'current_query' => wp_stream_json_encode( $_GET ), // input var okay
398
- 'current_query_count' => count( $_GET ), // input var okay
 
 
 
 
399
  )
400
  );
401
  }
@@ -414,13 +466,14 @@ class Admin {
414
  */
415
  $bulk_actions_threshold = apply_filters( 'wp_stream_bulk_actions_threshold', 100 );
416
 
417
- wp_enqueue_script( 'wp-stream-global', $this->plugin->locations['url'] . 'ui/js/global.js', array( 'jquery' ), $this->plugin->get_version() );
418
  wp_localize_script(
419
  'wp-stream-global',
420
  'wp_stream_global',
421
  array(
422
- 'bulk_actions' => array(
423
- 'i18n' => array(
 
424
  'confirm_action' => sprintf( esc_html__( 'Are you sure you want to perform bulk actions on over %s items? This process could take a while to complete.', 'stream' ), number_format( absint( $bulk_actions_threshold ) ) ),
425
  ),
426
  'threshold' => absint( $bulk_actions_threshold ),
@@ -463,8 +516,8 @@ class Admin {
463
  if ( $this->is_stream_screen() ) {
464
  $stream_classes[] = $this->admin_body_class;
465
 
466
- if ( isset( $_GET['page'] ) ) {
467
- $stream_classes[] = sanitize_key( $_GET['page'] ); // input var okay
468
  }
469
  }
470
 
@@ -487,14 +540,15 @@ class Admin {
487
  * @action admin_enqueue_scripts
488
  */
489
  public function admin_menu_css() {
490
- wp_register_style( 'wp-stream-datepicker', $this->plugin->locations['url'] . 'ui/css/datepicker.css', array(), $this->plugin->get_version() );
 
491
  wp_register_style( 'wp-stream-icons', $this->plugin->locations['url'] . 'ui/stream-icons/style.css', array(), $this->plugin->get_version() );
492
 
493
  // Make sure we're working off a clean version
494
  if ( ! file_exists( ABSPATH . WPINC . '/version.php' ) ) {
495
  return;
496
  }
497
- include( ABSPATH . WPINC . '/version.php' );
498
 
499
  if ( ! isset( $wp_version ) ) {
500
  return;
@@ -555,8 +609,13 @@ class Admin {
555
  \wp_add_inline_style( 'wp-admin', $css );
556
  }
557
 
 
 
 
 
 
558
  public function wp_ajax_reset() {
559
- check_ajax_referer( 'stream_nonce', 'wp_stream_nonce' );
560
 
561
  if ( ! current_user_can( $this->settings_cap ) ) {
562
  wp_die(
@@ -631,8 +690,9 @@ class Admin {
631
  return;
632
  }
633
 
634
- $days = $options['general_records_ttl'];
635
- $date = new DateTime( 'now', $timezone = new DateTimeZone( 'UTC' ) );
 
636
 
637
  $date->sub( DateInterval::createFromDateString( "$days days" ) );
638
 
@@ -653,7 +713,7 @@ class Admin {
653
  }
654
 
655
  /**
656
- * @param array $links
657
  * @param string $file
658
  *
659
  * @filter plugin_action_links
@@ -671,9 +731,17 @@ class Admin {
671
  }
672
 
673
  if ( is_network_admin() ) {
674
- $admin_page_url = add_query_arg( array( 'page' => $this->network->network_settings_page_slug ), network_admin_url( $this->admin_parent_page ) );
 
 
 
 
675
  } else {
676
- $admin_page_url = add_query_arg( array( 'page' => $this->settings_page_slug ), admin_url( $this->admin_parent_page ) );
 
 
 
 
677
  }
678
 
679
  $links[] = sprintf( '<a href="%s">%s</a>', esc_url( $admin_page_url ), esc_html__( 'Settings', 'default' ) );
@@ -701,7 +769,7 @@ class Admin {
701
  <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
702
  <?php $this->list_table->display(); ?>
703
  </div>
704
- <?php
705
  }
706
 
707
  /**
@@ -715,8 +783,8 @@ class Admin {
715
 
716
  $sections = $this->plugin->settings->get_fields();
717
  $active_tab = wp_stream_filter_input( INPUT_GET, 'tab' );
718
-
719
- wp_enqueue_script( 'wp-stream-settings', $this->plugin->locations['url'] . 'ui/js/settings.js', array( 'jquery' ), $this->plugin->get_version(), true );
720
  ?>
721
  <div class="wrap">
722
  <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
@@ -729,11 +797,11 @@ class Admin {
729
 
730
  <?php if ( count( $sections ) > 1 ) : ?>
731
  <h2 class="nav-tab-wrapper">
732
- <?php $i = 0 ?>
733
  <?php foreach ( $sections as $section => $data ) : ?>
734
- <?php $i ++ ?>
735
  <?php $is_active = ( ( 1 === $i && ! $active_tab ) || $active_tab === $section ); ?>
736
- <a href="<?php echo esc_url( add_query_arg( 'tab', $section ) ); ?>" class="nav-tab<?php if ( $is_active ) { echo esc_attr( ' nav-tab-active' ); } ?>">
737
  <?php echo esc_html( $data['title'] ); ?>
738
  </a>
739
  <?php endforeach; ?>
@@ -743,32 +811,36 @@ class Admin {
743
  <div class="nav-tab-content" id="tab-content-settings">
744
  <form method="post" action="<?php echo esc_attr( $form_action ); ?>" enctype="multipart/form-data">
745
  <div class="settings-sections">
746
- <?php
747
- $i = 0;
748
- foreach ( $sections as $section => $data ) {
749
- $i++;
750
 
751
- $is_active = ( ( 1 === $i && ! $active_tab ) || $active_tab === $section );
752
 
753
- if ( $is_active ) {
754
- settings_fields( $option_key );
755
- do_settings_sections( $option_key );
756
- }
757
- }
758
- ?>
759
  </div>
760
  <?php submit_button(); ?>
761
  </form>
762
  </div>
763
  </div>
764
- <?php
765
  }
766
 
767
  /**
768
  * Instantiate the list table
769
  */
770
  public function register_list_table() {
771
- $this->list_table = new List_Table( $this->plugin, array( 'screen' => $this->screen_id['main'] ) );
 
 
 
 
772
  }
773
 
774
  /**
@@ -868,7 +940,11 @@ class Admin {
868
  switch ( wp_stream_filter_input( INPUT_GET, 'filter' ) ) {
869
  case 'user_id':
870
  $users = array_merge(
871
- array( 0 => (object) array( 'display_name' => 'WP-CLI' ) ),
 
 
 
 
872
  get_users()
873
  );
874
 
@@ -877,7 +953,7 @@ class Admin {
877
  // `search` arg for get_users() is not enough
878
  $users = array_filter(
879
  $users,
880
- function( $user ) use ( $search ) {
881
  return false !== mb_strpos( mb_strtolower( $user->display_name ), mb_strtolower( $search ) );
882
  }
883
  );
@@ -907,11 +983,11 @@ class Admin {
907
  $author = new Author( $args->ID );
908
 
909
  $authors_records[ $user_id ] = array(
910
- 'text' => $author->get_display_name(),
911
- 'id' => $author->id,
912
- 'label' => $author->get_display_name(),
913
- 'icon' => $author->get_avatar_src( 32 ),
914
- 'title' => '',
915
  );
916
  }
917
 
@@ -921,49 +997,52 @@ class Admin {
921
  /**
922
  * Get user meta in a way that is also safe for VIP
923
  *
924
- * @param int $user_id
925
  * @param string $meta_key
926
- * @param bool $single (optional)
927
  *
928
  * @return mixed
929
  */
930
- function get_user_meta( $user_id, $meta_key, $single = true ) {
931
  if ( wp_stream_is_vip() && function_exists( 'get_user_attribute' ) ) {
932
  return get_user_attribute( $user_id, $meta_key );
933
  }
 
934
  return get_user_meta( $user_id, $meta_key, $single );
935
  }
936
 
937
  /**
938
  * Update user meta in a way that is also safe for VIP
939
  *
940
- * @param int $user_id
941
  * @param string $meta_key
942
  * @param mixed $meta_value
943
- * @param mixed $prev_value (optional)
944
  *
945
  * @return int|bool
946
  */
947
- function update_user_meta( $user_id, $meta_key, $meta_value, $prev_value = '' ) {
948
  if ( wp_stream_is_vip() && function_exists( 'update_user_attribute' ) ) {
949
  return update_user_attribute( $user_id, $meta_key, $meta_value );
950
  }
 
951
  return update_user_meta( $user_id, $meta_key, $meta_value, $prev_value );
952
  }
953
 
954
  /**
955
  * Delete user meta in a way that is also safe for VIP
956
  *
957
- * @param int $user_id
958
  * @param string $meta_key
959
- * @param mixed $meta_value (optional)
960
  *
961
  * @return bool
962
  */
963
- function delete_user_meta( $user_id, $meta_key, $meta_value = '' ) {
964
  if ( wp_stream_is_vip() && function_exists( 'delete_user_attribute' ) ) {
965
  return delete_user_attribute( $user_id, $meta_key, $meta_value );
966
  }
 
967
  return delete_user_meta( $user_id, $meta_key, $meta_value );
968
  }
969
  }
1
  <?php
2
+
3
  namespace WP_Stream;
4
 
5
  use DateTime;
9
  use \WP_Roles;
10
 
11
  class Admin {
12
+
13
  /**
14
  * Hold Plugin class
15
  *
127
 
128
  // Ensure function used in various methods is pre-loaded.
129
  if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
130
+ require_once ABSPATH . '/wp-admin/includes/plugin.php';
131
  }
132
 
133
  // User and role caps.
154
  add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
155
 
156
  // Plugin action links.
157
+ add_filter(
158
+ 'plugin_action_links', array(
159
+ $this,
160
+ 'plugin_action_links',
161
+ ), 10, 2
162
+ );
163
 
164
  // Load admin scripts and styles.
165
+ add_action(
166
+ 'admin_enqueue_scripts', array(
167
+ $this,
168
+ 'admin_enqueue_scripts',
169
+ )
170
+ );
171
  add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
172
 
173
  // Reset Streams database.
174
+ add_action(
175
+ 'wp_ajax_wp_stream_reset', array(
176
+ $this,
177
+ 'wp_ajax_reset',
178
+ )
179
+ );
180
 
181
  // Uninstall Streams and Deactivate plugin.
182
  $uninstall = $this->plugin->db->driver->purge_storage( $this->plugin );
183
 
184
  // Auto purge setup.
185
  add_action( 'wp_loaded', array( $this, 'purge_schedule_setup' ) );
186
+ add_action(
187
+ 'wp_stream_auto_purge', array(
188
+ $this,
189
+ 'purge_scheduled_action',
190
+ )
191
+ );
192
 
193
  // Ajax users list.
194
+ add_action(
195
+ 'wp_ajax_wp_stream_filters', array(
196
+ $this,
197
+ 'ajax_filters',
198
+ )
199
+ );
200
  }
201
 
202
  /**
358
  do_action( 'wp_stream_admin_menu_screens' );
359
 
360
  // Register the list table early, so it associates the column headers with 'Screen settings'.
361
+ add_action(
362
+ 'load-' . $this->screen_id['main'], array(
363
+ $this,
364
+ 'register_list_table',
365
+ )
366
+ );
367
  }
368
  }
369
 
390
  wp_register_script( 'wp-stream-timeago-locale', $this->plugin->locations['url'] . sprintf( $file_tmpl, 'en' ), array( 'wp-stream-timeago' ), '1' );
391
  }
392
 
393
+ $min = wp_stream_min_suffix();
394
+ wp_enqueue_style( 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/css/admin.' . $min . 'css', array(), $this->plugin->get_version() );
395
 
396
  $script_screens = array( 'plugins.php' );
397
 
402
  wp_enqueue_script( 'wp-stream-timeago' );
403
  wp_enqueue_script( 'wp-stream-timeago-locale' );
404
 
405
+ wp_enqueue_script(
406
+ 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/js/admin.' . $min . 'js', array(
407
+ 'jquery',
408
+ 'wp-stream-select2',
409
+ ), $this->plugin->get_version()
410
+ );
411
+ wp_enqueue_script(
412
+ 'wp-stream-admin-exclude', $this->plugin->locations['url'] . 'ui/js/exclude.' . $min . 'js', array(
413
+ 'jquery',
414
+ 'wp-stream-select2',
415
+ ), $this->plugin->get_version()
416
+ );
417
+ wp_enqueue_script(
418
+ 'wp-stream-live-updates', $this->plugin->locations['url'] . 'ui/js/live-updates.' . $min . 'js', array(
419
+ 'jquery',
420
+ 'heartbeat',
421
+ ), $this->plugin->get_version()
422
+ );
423
 
424
  wp_localize_script(
425
  'wp-stream-admin',
440
  'wp_stream_live_updates',
441
  array(
442
  'current_screen' => $hook,
443
+ 'current_page' => isset( $_GET['paged'] ) ? esc_js( $_GET['paged'] ) : '1', // WPCS: CSRF ok.
444
+ // input var okay, CSRF okay
445
+ 'current_order' => isset( $_GET['order'] ) ? esc_js( $_GET['order'] ) : 'desc', // WPCS: CSRF ok.
446
+ // input var okay, CSRF okay
447
+ 'current_query' => wp_stream_json_encode( $_GET ), // WPCS: CSRF ok.
448
+ // input var okay, CSRF okay
449
+ 'current_query_count' => count( $_GET ), // WPCS: CSRF ok.
450
+ // input var okay, CSRF okay
451
  )
452
  );
453
  }
466
  */
467
  $bulk_actions_threshold = apply_filters( 'wp_stream_bulk_actions_threshold', 100 );
468
 
469
+ wp_enqueue_script( 'wp-stream-global', $this->plugin->locations['url'] . 'ui/js/global.' . $min . 'js', array( 'jquery' ), $this->plugin->get_version() );
470
  wp_localize_script(
471
  'wp-stream-global',
472
  'wp_stream_global',
473
  array(
474
+ 'bulk_actions' => array(
475
+ 'i18n' => array(
476
+ // translators: Placeholder refers to a number of items (e.g. "1,742")
477
  'confirm_action' => sprintf( esc_html__( 'Are you sure you want to perform bulk actions on over %s items? This process could take a while to complete.', 'stream' ), number_format( absint( $bulk_actions_threshold ) ) ),
478
  ),
479
  'threshold' => absint( $bulk_actions_threshold ),
516
  if ( $this->is_stream_screen() ) {
517
  $stream_classes[] = $this->admin_body_class;
518
 
519
+ if ( isset( $_GET['page'] ) ) { // CSRF okay
520
+ $stream_classes[] = sanitize_key( $_GET['page'] ); // input var okay, CSRF okay
521
  }
522
  }
523
 
540
  * @action admin_enqueue_scripts
541
  */
542
  public function admin_menu_css() {
543
+ $min = wp_stream_min_suffix();
544
+ wp_register_style( 'wp-stream-datepicker', $this->plugin->locations['url'] . 'ui/css/datepicker.' . $min . 'css', array(), $this->plugin->get_version() );
545
  wp_register_style( 'wp-stream-icons', $this->plugin->locations['url'] . 'ui/stream-icons/style.css', array(), $this->plugin->get_version() );
546
 
547
  // Make sure we're working off a clean version
548
  if ( ! file_exists( ABSPATH . WPINC . '/version.php' ) ) {
549
  return;
550
  }
551
+ include ABSPATH . WPINC . '/version.php';
552
 
553
  if ( ! isset( $wp_version ) ) {
554
  return;
609
  \wp_add_inline_style( 'wp-admin', $css );
610
  }
611
 
612
+ /**
613
+ * Handle the reset AJAX request to reset logs.
614
+ *
615
+ * @return bool
616
+ */
617
  public function wp_ajax_reset() {
618
+ check_ajax_referer( 'stream_nonce_reset', 'wp_stream_nonce_reset' );
619
 
620
  if ( ! current_user_can( $this->settings_cap ) ) {
621
  wp_die(
690
  return;
691
  }
692
 
693
+ $days = $options['general_records_ttl'];
694
+ $timezone = new DateTimeZone( 'UTC' );
695
+ $date = new DateTime( 'now', $timezone );
696
 
697
  $date->sub( DateInterval::createFromDateString( "$days days" ) );
698
 
713
  }
714
 
715
  /**
716
+ * @param array $links
717
  * @param string $file
718
  *
719
  * @filter plugin_action_links
731
  }
732
 
733
  if ( is_network_admin() ) {
734
+ $admin_page_url = add_query_arg(
735
+ array(
736
+ 'page' => $this->network->network_settings_page_slug,
737
+ ), network_admin_url( $this->admin_parent_page )
738
+ );
739
  } else {
740
+ $admin_page_url = add_query_arg(
741
+ array(
742
+ 'page' => $this->settings_page_slug,
743
+ ), admin_url( $this->admin_parent_page )
744
+ );
745
  }
746
 
747
  $links[] = sprintf( '<a href="%s">%s</a>', esc_url( $admin_page_url ), esc_html__( 'Settings', 'default' ) );
769
  <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
770
  <?php $this->list_table->display(); ?>
771
  </div>
772
+ <?php
773
  }
774
 
775
  /**
783
 
784
  $sections = $this->plugin->settings->get_fields();
785
  $active_tab = wp_stream_filter_input( INPUT_GET, 'tab' );
786
+ $min = wp_stream_min_suffix();
787
+ wp_enqueue_script( 'wp-stream-settings', $this->plugin->locations['url'] . 'ui/js/settings.' . $min . 'js', array( 'jquery' ), $this->plugin->get_version(), true );
788
  ?>
789
  <div class="wrap">
790
  <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
797
 
798
  <?php if ( count( $sections ) > 1 ) : ?>
799
  <h2 class="nav-tab-wrapper">
800
+ <?php $i = 0; ?>
801
  <?php foreach ( $sections as $section => $data ) : ?>
802
+ <?php $i++; ?>
803
  <?php $is_active = ( ( 1 === $i && ! $active_tab ) || $active_tab === $section ); ?>
804
+ <a href="<?php echo esc_url( add_query_arg( 'tab', $section ) ); ?>" class="nav-tab <?php echo $is_active ? esc_attr( ' nav-tab-active' ) : ''; ?>">
805
  <?php echo esc_html( $data['title'] ); ?>
806
  </a>
807
  <?php endforeach; ?>
811
  <div class="nav-tab-content" id="tab-content-settings">
812
  <form method="post" action="<?php echo esc_attr( $form_action ); ?>" enctype="multipart/form-data">
813
  <div class="settings-sections">
814
+ <?php
815
+ $i = 0;
816
+ foreach ( $sections as $section => $data ) {
817
+ $i++;
818
 
819
+ $is_active = ( ( 1 === $i && ! $active_tab ) || $active_tab === $section );
820
 
821
+ if ( $is_active ) {
822
+ settings_fields( $option_key );
823
+ do_settings_sections( $option_key );
824
+ }
825
+ }
826
+ ?>
827
  </div>
828
  <?php submit_button(); ?>
829
  </form>
830
  </div>
831
  </div>
832
+ <?php
833
  }
834
 
835
  /**
836
  * Instantiate the list table
837
  */
838
  public function register_list_table() {
839
+ $this->list_table = new List_Table(
840
+ $this->plugin, array(
841
+ 'screen' => $this->screen_id['main'],
842
+ )
843
+ );
844
  }
845
 
846
  /**
940
  switch ( wp_stream_filter_input( INPUT_GET, 'filter' ) ) {
941
  case 'user_id':
942
  $users = array_merge(
943
+ array(
944
+ 0 => (object) array(
945
+ 'display_name' => 'WP-CLI',
946
+ ),
947
+ ),
948
  get_users()
949
  );
950
 
953
  // `search` arg for get_users() is not enough
954
  $users = array_filter(
955
  $users,
956
+ function ( $user ) use ( $search ) {
957
  return false !== mb_strpos( mb_strtolower( $user->display_name ), mb_strtolower( $search ) );
958
  }
959
  );
983
  $author = new Author( $args->ID );
984
 
985
  $authors_records[ $user_id ] = array(
986
+ 'text' => $author->get_display_name(),
987
+ 'id' => $author->id,
988
+ 'label' => $author->get_display_name(),
989
+ 'icon' => $author->get_avatar_src( 32 ),
990
+ 'title' => '',
991
  );
992
  }
993
 
997
  /**
998
  * Get user meta in a way that is also safe for VIP
999
  *
1000
+ * @param int $user_id
1001
  * @param string $meta_key
1002
+ * @param bool $single (optional)
1003
  *
1004
  * @return mixed
1005
  */
1006
+ public function get_user_meta( $user_id, $meta_key, $single = true ) {
1007
  if ( wp_stream_is_vip() && function_exists( 'get_user_attribute' ) ) {
1008
  return get_user_attribute( $user_id, $meta_key );
1009
  }
1010
+
1011
  return get_user_meta( $user_id, $meta_key, $single );
1012
  }
1013
 
1014
  /**
1015
  * Update user meta in a way that is also safe for VIP
1016
  *
1017
+ * @param int $user_id
1018
  * @param string $meta_key
1019
  * @param mixed $meta_value
1020
+ * @param mixed $prev_value (optional)
1021
  *
1022
  * @return int|bool
1023
  */
1024
+ public function update_user_meta( $user_id, $meta_key, $meta_value, $prev_value = '' ) {
1025
  if ( wp_stream_is_vip() && function_exists( 'update_user_attribute' ) ) {
1026
  return update_user_attribute( $user_id, $meta_key, $meta_value );
1027
  }
1028
+
1029
  return update_user_meta( $user_id, $meta_key, $meta_value, $prev_value );
1030
  }
1031
 
1032
  /**
1033
  * Delete user meta in a way that is also safe for VIP
1034
  *
1035
+ * @param int $user_id
1036
  * @param string $meta_key
1037
+ * @param mixed $meta_value (optional)
1038
  *
1039
  * @return bool
1040
  */
1041
+ public function delete_user_meta( $user_id, $meta_key, $meta_value = '' ) {
1042
  if ( wp_stream_is_vip() && function_exists( 'delete_user_attribute' ) ) {
1043
  return delete_user_attribute( $user_id, $meta_key, $meta_value );
1044
  }
1045
+
1046
  return delete_user_meta( $user_id, $meta_key, $meta_value );
1047
  }
1048
  }
classes/class-alert-type.php CHANGED
@@ -34,14 +34,13 @@ abstract class Alert_Type {
34
  * Class constructor.
35
  *
36
  * @param Plugin $plugin Plugin object.
37
- * @return void
38
  */
39
  public function __construct( $plugin ) {
40
  $this->plugin = $plugin;
41
  }
42
 
43
  /**
44
- * Alert recipients about the new record
45
  *
46
  * @param int $record_id Record ID.
47
  * @param array $recordarr Record details.
@@ -55,7 +54,7 @@ abstract class Alert_Type {
55
  * @param Alert $alert Alert currently being worked on.
56
  */
57
  public function display_fields( $alert ) {
58
- return;
59
  }
60
 
61
  /**
@@ -64,7 +63,7 @@ abstract class Alert_Type {
64
  * @param Alert $alert Alert currently being worked on.
65
  */
66
  public function save_fields( $alert ) {
67
- return;
68
  }
69
 
70
  /**
@@ -73,6 +72,7 @@ abstract class Alert_Type {
73
  * @return bool
74
  */
75
  public function is_dependency_satisfied() {
 
76
  return true;
77
  }
78
  }
34
  * Class constructor.
35
  *
36
  * @param Plugin $plugin Plugin object.
 
37
  */
38
  public function __construct( $plugin ) {
39
  $this->plugin = $plugin;
40
  }
41
 
42
  /**
43
+ * Alert recipients about the new record
44
  *
45
  * @param int $record_id Record ID.
46
  * @param array $recordarr Record details.
54
  * @param Alert $alert Alert currently being worked on.
55
  */
56
  public function display_fields( $alert ) {
57
+ // Implementation optional, but recommended
58
  }
59
 
60
  /**
63
  * @param Alert $alert Alert currently being worked on.
64
  */
65
  public function save_fields( $alert ) {
66
+ // Implementation optional, but recommended
67
  }
68
 
69
  /**
72
  * @return bool
73
  */
74
  public function is_dependency_satisfied() {
75
+ // Implementation optional, but recommended
76
  return true;
77
  }
78
  }
classes/class-alert.php CHANGED
@@ -71,12 +71,12 @@ class Alert {
71
  * @return void
72
  */
73
  public function __construct( $item, $plugin ) {
74
- $this->plugin = $plugin;
75
 
76
- $this->ID = isset( $item->ID ) ? $item->ID : null;
77
- $this->status = isset( $item->status ) ? $item->status : 'wp_stream_disabled';
78
- $this->date = isset( $item->date ) ? $item->date : null;
79
- $this->author = isset( $item->author ) ? $item->author : null;
80
 
81
  $this->alert_type = isset( $item->alert_type ) ? $item->alert_type : null;
82
  $this->alert_meta = isset( $item->alert_meta ) ? $item->alert_meta : array();
@@ -109,8 +109,8 @@ class Alert {
109
  }
110
 
111
  $meta = array(
112
- 'alert_type' => $this->alert_type,
113
- 'alert_meta' => $this->alert_meta,
114
  );
115
 
116
  foreach ( $meta as $key => $value ) {
@@ -129,11 +129,11 @@ class Alert {
129
  public function process_settings_form( $data ) {
130
 
131
  $args = array(
132
- 'post_date' => $this->date,
133
- 'post_status' => $this->status,
134
- 'post_title' => $this->get_title(),
135
- 'post_author' => $this->author,
136
- 'post_type' => Alerts::POST_TYPE,
137
  );
138
 
139
  foreach ( $args as $key => $value ) {
@@ -141,8 +141,8 @@ class Alert {
141
  }
142
 
143
  $meta_input = array(
144
- 'alert_type' => $this->alert_type,
145
- 'alert_meta' => $this->alert_meta,
146
  );
147
 
148
  foreach ( $meta_input as $key => $value ) {
@@ -156,7 +156,7 @@ class Alert {
156
  * Query record meta
157
  *
158
  * @param string $meta_key Meta key to retrieve (optional). Otherwise will
159
- * grab all meta data for the ID.
160
  * @param bool $single Whether to only retrieve the first value (optional).
161
  *
162
  * @return mixed Single value if $single is true, array if false.
@@ -183,7 +183,7 @@ class Alert {
183
  * @todo enhance human readibility
184
  * @return string The title of the alert
185
  */
186
- function get_title() {
187
 
188
  $alert_type = $this->get_alert_type_obj()->name;
189
 
@@ -193,7 +193,7 @@ class Alert {
193
  }
194
  $title = '';
195
  foreach ( $this->plugin->alerts->alert_triggers as $trigger_type => $trigger_obj ) {
196
- $value = $trigger_obj->get_display_value( 'list_table', $this );
197
  $title .= $value . ' > ';
198
  }
199
  $title = rtrim( $title, ' > ' );
@@ -267,11 +267,13 @@ class Alert {
267
  if ( empty( $record->ID ) ) {
268
  return false;
269
  }
270
- $record = new Record( $record );
271
  $alerts_triggered = $record->get_meta( Alerts::ALERTS_TRIGGERED_META_KEY, true );
272
 
273
  if ( empty( $alerts_triggered ) || ! is_array( $alerts_triggered ) ) {
274
- $alerts_triggered = array( $alert_slug => $alert_meta );
 
 
275
  } elseif ( ! array_key_exists( $alert_slug, $alerts_triggered ) || ! is_array( $alerts_triggered[ $alert_slug ] ) ) {
276
  $alerts_triggered[ $alert_slug ] = $alert_meta;
277
  }
@@ -304,7 +306,7 @@ class Alert {
304
  if ( ! is_object( $record ) || ! is_string( $alert_slug ) || ! is_string( $setting ) ) {
305
  return false;
306
  }
307
- $record = new Record( $record );
308
  $alerts_triggered = $record->get_meta( Alerts::ALERTS_TRIGGERED_META_KEY, true );
309
 
310
  // Ensure we have a meta array and that this record has triggered a highlight alert.
71
  * @return void
72
  */
73
  public function __construct( $item, $plugin ) {
74
+ $this->plugin = $plugin;
75
 
76
+ $this->ID = isset( $item->ID ) ? $item->ID : null;
77
+ $this->status = isset( $item->status ) ? $item->status : 'wp_stream_disabled';
78
+ $this->date = isset( $item->date ) ? $item->date : null;
79
+ $this->author = isset( $item->author ) ? $item->author : null;
80
 
81
  $this->alert_type = isset( $item->alert_type ) ? $item->alert_type : null;
82
  $this->alert_meta = isset( $item->alert_meta ) ? $item->alert_meta : array();
109
  }
110
 
111
  $meta = array(
112
+ 'alert_type' => $this->alert_type,
113
+ 'alert_meta' => $this->alert_meta,
114
  );
115
 
116
  foreach ( $meta as $key => $value ) {
129
  public function process_settings_form( $data ) {
130
 
131
  $args = array(
132
+ 'post_date' => $this->date,
133
+ 'post_status' => $this->status,
134
+ 'post_title' => $this->get_title(),
135
+ 'post_author' => $this->author,
136
+ 'post_type' => Alerts::POST_TYPE,
137
  );
138
 
139
  foreach ( $args as $key => $value ) {
141
  }
142
 
143
  $meta_input = array(
144
+ 'alert_type' => $this->alert_type,
145
+ 'alert_meta' => $this->alert_meta,
146
  );
147
 
148
  foreach ( $meta_input as $key => $value ) {
156
  * Query record meta
157
  *
158
  * @param string $meta_key Meta key to retrieve (optional). Otherwise will
159
+ * grab all meta data for the ID.
160
  * @param bool $single Whether to only retrieve the first value (optional).
161
  *
162
  * @return mixed Single value if $single is true, array if false.
183
  * @todo enhance human readibility
184
  * @return string The title of the alert
185
  */
186
+ public function get_title() {
187
 
188
  $alert_type = $this->get_alert_type_obj()->name;
189
 
193
  }
194
  $title = '';
195
  foreach ( $this->plugin->alerts->alert_triggers as $trigger_type => $trigger_obj ) {
196
+ $value = $trigger_obj->get_display_value( 'list_table', $this );
197
  $title .= $value . ' > ';
198
  }
199
  $title = rtrim( $title, ' > ' );
267
  if ( empty( $record->ID ) ) {
268
  return false;
269
  }
270
+ $record = new Record( $record );
271
  $alerts_triggered = $record->get_meta( Alerts::ALERTS_TRIGGERED_META_KEY, true );
272
 
273
  if ( empty( $alerts_triggered ) || ! is_array( $alerts_triggered ) ) {
274
+ $alerts_triggered = array(
275
+ $alert_slug => $alert_meta,
276
+ );
277
  } elseif ( ! array_key_exists( $alert_slug, $alerts_triggered ) || ! is_array( $alerts_triggered[ $alert_slug ] ) ) {
278
  $alerts_triggered[ $alert_slug ] = $alert_meta;
279
  }
306
  if ( ! is_object( $record ) || ! is_string( $alert_slug ) || ! is_string( $setting ) ) {
307
  return false;
308
  }
309
+ $record = new Record( $record );
310
  $alerts_triggered = $record->get_meta( Alerts::ALERTS_TRIGGERED_META_KEY, true );
311
 
312
  // Ensure we have a meta array and that this record has triggered a highlight alert.
classes/class-alerts-list.php CHANGED
@@ -55,7 +55,7 @@ class Alerts_List {
55
  * @param array $query_vars Arguments for query to populate table.
56
  * @return array
57
  */
58
- function parse_request( $query_vars ) {
59
  $screen = get_current_screen();
60
  if ( 'edit-wp_stream_alerts' === $screen->id && Alerts::POST_TYPE === $query_vars['post_type'] && empty( $query_vars['post_status'] ) ) {
61
  $query_vars['post_status'] = array( 'wp_stream_enabled', 'wp_stream_disabled' );
@@ -71,7 +71,7 @@ class Alerts_List {
71
  * @param array $views View links HTML.
72
  * @return array
73
  */
74
- function manage_views( $views ) {
75
 
76
  if ( array_key_exists( 'trash', $views ) ) {
77
  $trash = $views['trash'];
@@ -90,7 +90,7 @@ class Alerts_List {
90
  * @param array $columns Column id -> title array.
91
  * @return array
92
  */
93
- function manage_columns( $columns ) {
94
  $columns = array(
95
  'cb' => $columns['cb'],
96
  'alert_trigger' => __( 'Trigger', 'stream' ),
@@ -109,7 +109,7 @@ class Alerts_List {
109
  * @param int $post_id The post being processed.
110
  * @return mixed
111
  */
112
- function column_data( $column_name, $post_id ) {
113
 
114
  $alert = $this->plugin->alerts->get_alert( $post_id );
115
  if ( ! $alert ) {
@@ -117,10 +117,10 @@ class Alerts_List {
117
  }
118
 
119
  switch ( $column_name ) {
120
- case 'alert_trigger' :
121
  $values = array();
122
  foreach ( $this->plugin->alerts->alert_triggers as $trigger_type => $trigger_obj ) {
123
- $value = $trigger_obj->get_display_value( 'list_table', $alert );
124
  $values[] = '<span class="alert_trigger_value alert_trigger_' . esc_attr( $trigger_type ) . '">' . esc_html( $value ) . '</span>';
125
  }
126
  ?>
@@ -152,7 +152,7 @@ class Alerts_List {
152
  <?php
153
  echo wp_kses_post( $this->custom_column_actions( $post_id ) );
154
  break;
155
- case 'alert_type' :
156
  $alert_type = $alert->alert_type;
157
  if ( ! empty( $this->plugin->alerts->alert_types[ $alert_type ]->name ) ) {
158
  $alert_name = $this->plugin->alerts->alert_types[ $alert_type ]->name;
@@ -188,8 +188,28 @@ class Alerts_List {
188
  <input type="hidden" name="wp_stream_ifttt_maker_key" value="<?php echo esc_attr( $alert->alert_meta['maker_key'] ); ?>" />
189
  <?php
190
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  break;
192
- case 'alert_status' :
193
  $post_status_object = get_post_status_object( get_post_status( $post_id ) );
194
  if ( ! empty( $post_status_object ) ) {
195
  echo esc_html( $post_status_object->label );
@@ -222,7 +242,7 @@ class Alerts_List {
222
  * @param array $actions List of inline edit actions available.
223
  * @return array
224
  */
225
- function suppress_quick_edit( $actions ) {
226
  if ( Alerts::POST_TYPE !== get_post_type() ) {
227
  return $actions;
228
  }
@@ -295,11 +315,12 @@ class Alerts_List {
295
  ?>
296
  <legend class="inline-edit-legend"><?php esc_html_e( 'Edit', 'stream' ); ?></legend>
297
  <?php
298
- foreach ( $box_type as $type ) : // @todo remove inline styles. ?>
 
299
  <fieldset class="inline-edit-col inline-edit-<?php echo esc_attr( Alerts::POST_TYPE ); ?>">
300
  <?php
301
  $function_name = 'display_' . $type . '_box';
302
- $the_post = get_post();
303
  call_user_func( array( $this->plugin->alerts, $function_name ), $the_post );
304
  ?>
305
  </fieldset>
@@ -318,9 +339,10 @@ class Alerts_List {
318
  if ( 'edit-wp_stream_alerts' !== $screen->id ) {
319
  return;
320
  }
321
- wp_register_script( 'wp-stream-alerts-list-js', $this->plugin->locations['url'] . 'ui/js/alerts-list.js', array( 'wp-stream-alerts', 'jquery' ) );
 
322
  wp_enqueue_script( 'wp-stream-alerts-list-js' );
323
- wp_register_style( 'wp-stream-alerts-list-css', $this->plugin->locations['url'] . 'ui/css/alerts-list.css' );
324
  wp_enqueue_style( 'wp-stream-alerts-list-css' );
325
  wp_enqueue_style( 'wp-stream-select2' );
326
  }
@@ -333,12 +355,12 @@ class Alerts_List {
333
  *
334
  * @return array
335
  */
336
- function save_alert_inline_edit( $data, $postarr ) {
337
  if ( did_action( 'customize_preview_init' ) || empty( $postarr['ID'] ) ) {
338
  return $data;
339
  }
340
 
341
- $post_id = $postarr['ID'];
342
  $post_type = wp_stream_filter_input( INPUT_POST, 'post_type' );
343
  if ( Alerts::POST_TYPE !== $post_type ) {
344
  return $data;
@@ -352,15 +374,15 @@ class Alerts_List {
352
  return $data;
353
  }
354
 
355
- $trigger_author = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_author' );
356
- $trigger_connector_and_context = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_connector_or_context' );
357
  $trigger_connector_and_context_split = explode( '-', $trigger_connector_and_context );
358
- $trigger_connector = $trigger_connector_and_context_split[0];
359
- $trigger_context = $trigger_connector_and_context_split[1];
360
 
361
- $trigger_action = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_action' );
362
- $alert_type = wp_stream_filter_input( INPUT_POST, 'wp_stream_alert_type' );
363
- $alert_status = wp_stream_filter_input( INPUT_POST, 'wp_stream_alert_status' );
364
  $data['post_status'] = $alert_status;
365
 
366
  update_post_meta( $post_id, 'alert_type', $alert_type );
55
  * @param array $query_vars Arguments for query to populate table.
56
  * @return array
57
  */
58
+ public function parse_request( $query_vars ) {
59
  $screen = get_current_screen();
60
  if ( 'edit-wp_stream_alerts' === $screen->id && Alerts::POST_TYPE === $query_vars['post_type'] && empty( $query_vars['post_status'] ) ) {
61
  $query_vars['post_status'] = array( 'wp_stream_enabled', 'wp_stream_disabled' );
71
  * @param array $views View links HTML.
72
  * @return array
73
  */
74
+ public function manage_views( $views ) {
75
 
76
  if ( array_key_exists( 'trash', $views ) ) {
77
  $trash = $views['trash'];
90
  * @param array $columns Column id -> title array.
91
  * @return array
92
  */
93
+ public function manage_columns( $columns ) {
94
  $columns = array(
95
  'cb' => $columns['cb'],
96
  'alert_trigger' => __( 'Trigger', 'stream' ),
109
  * @param int $post_id The post being processed.
110
  * @return mixed
111
  */
112
+ public function column_data( $column_name, $post_id ) {
113
 
114
  $alert = $this->plugin->alerts->get_alert( $post_id );
115
  if ( ! $alert ) {
117
  }
118
 
119
  switch ( $column_name ) {
120
+ case 'alert_trigger':
121
  $values = array();
122
  foreach ( $this->plugin->alerts->alert_triggers as $trigger_type => $trigger_obj ) {
123
+ $value = $trigger_obj->get_display_value( 'list_table', $alert );
124
  $values[] = '<span class="alert_trigger_value alert_trigger_' . esc_attr( $trigger_type ) . '">' . esc_html( $value ) . '</span>';
125
  }
126
  ?>
152
  <?php
153
  echo wp_kses_post( $this->custom_column_actions( $post_id ) );
154
  break;
155
+ case 'alert_type':
156
  $alert_type = $alert->alert_type;
157
  if ( ! empty( $this->plugin->alerts->alert_types[ $alert_type ]->name ) ) {
158
  $alert_name = $this->plugin->alerts->alert_types[ $alert_type ]->name;
188
  <input type="hidden" name="wp_stream_ifttt_maker_key" value="<?php echo esc_attr( $alert->alert_meta['maker_key'] ); ?>" />
189
  <?php
190
  }
191
+ if ( ! empty( $alert->alert_meta['slack_webhook'] ) ) {
192
+ ?>
193
+ <input type="hidden" name="wp_stream_slack_webhook" value="<?php echo esc_attr( $alert->alert_meta['slack_webhook'] ); ?>" />
194
+ <?php
195
+ }
196
+ if ( ! empty( $alert->alert_meta['slack_channel'] ) ) {
197
+ ?>
198
+ <input type="hidden" name="wp_stream_slack_channel" value="<?php echo esc_attr( $alert->alert_meta['slack_channel'] ); ?>" />
199
+ <?php
200
+ }
201
+ if ( ! empty( $alert->alert_meta['slack_username'] ) ) {
202
+ ?>
203
+ <input type="hidden" name="wp_stream_slack_username" value="<?php echo esc_attr( $alert->alert_meta['slack_username'] ); ?>" />
204
+ <?php
205
+ }
206
+ if ( ! empty( $alert->alert_meta['slack_icon'] ) ) {
207
+ ?>
208
+ <input type="hidden" name="wp_stream_slack_icon" value="<?php echo esc_attr( $alert->alert_meta['slack_icon'] ); ?>" />
209
+ <?php
210
+ }
211
  break;
212
+ case 'alert_status':
213
  $post_status_object = get_post_status_object( get_post_status( $post_id ) );
214
  if ( ! empty( $post_status_object ) ) {
215
  echo esc_html( $post_status_object->label );
242
  * @param array $actions List of inline edit actions available.
243
  * @return array
244
  */
245
+ public function suppress_quick_edit( $actions ) {
246
  if ( Alerts::POST_TYPE !== get_post_type() ) {
247
  return $actions;
248
  }
315
  ?>
316
  <legend class="inline-edit-legend"><?php esc_html_e( 'Edit', 'stream' ); ?></legend>
317
  <?php
318
+ foreach ( $box_type as $type ) : // @todo remove inline styles.
319
+ ?>
320
  <fieldset class="inline-edit-col inline-edit-<?php echo esc_attr( Alerts::POST_TYPE ); ?>">
321
  <?php
322
  $function_name = 'display_' . $type . '_box';
323
+ $the_post = get_post();
324
  call_user_func( array( $this->plugin->alerts, $function_name ), $the_post );
325
  ?>
326
  </fieldset>
339
  if ( 'edit-wp_stream_alerts' !== $screen->id ) {
340
  return;
341
  }
342
+ $min = wp_stream_min_suffix();
343
+ wp_register_script( 'wp-stream-alerts-list-js', $this->plugin->locations['url'] . 'ui/js/alerts-list.' . $min . 'js', array( 'wp-stream-alerts', 'jquery' ) );
344
  wp_enqueue_script( 'wp-stream-alerts-list-js' );
345
+ wp_register_style( 'wp-stream-alerts-list-css', $this->plugin->locations['url'] . 'ui/css/alerts-list.' . $min . 'css' );
346
  wp_enqueue_style( 'wp-stream-alerts-list-css' );
347
  wp_enqueue_style( 'wp-stream-select2' );
348
  }
355
  *
356
  * @return array
357
  */
358
+ public function save_alert_inline_edit( $data, $postarr ) {
359
  if ( did_action( 'customize_preview_init' ) || empty( $postarr['ID'] ) ) {
360
  return $data;
361
  }
362
 
363
+ $post_id = $postarr['ID'];
364
  $post_type = wp_stream_filter_input( INPUT_POST, 'post_type' );
365
  if ( Alerts::POST_TYPE !== $post_type ) {
366
  return $data;
374
  return $data;
375
  }
376
 
377
+ $trigger_author = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_author' );
378
+ $trigger_connector_and_context = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_connector_or_context' );
379
  $trigger_connector_and_context_split = explode( '-', $trigger_connector_and_context );
380
+ $trigger_connector = $trigger_connector_and_context_split[0];
381
+ $trigger_context = $trigger_connector_and_context_split[1];
382
 
383
+ $trigger_action = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_action' );
384
+ $alert_type = wp_stream_filter_input( INPUT_POST, 'wp_stream_alert_type' );
385
+ $alert_status = wp_stream_filter_input( INPUT_POST, 'wp_stream_alert_status' );
386
  $data['post_status'] = $alert_status;
387
 
388
  update_post_meta( $post_id, 'alert_type', $alert_type );
classes/class-alerts.php CHANGED
@@ -13,6 +13,7 @@ namespace WP_Stream;
13
  * @package WP_Stream
14
  */
15
  class Alerts {
 
16
  /**
17
  * Alerts post type slug
18
  */
@@ -66,24 +67,44 @@ class Alerts {
66
  add_action( 'wp_stream_admin_menu', array( $this, 'register_menu' ) );
67
 
68
  // Add scripts to post screens.
69
- add_action( 'admin_enqueue_scripts', array( $this, 'register_scripts' ) );
 
 
 
70
 
71
- add_action( 'network_admin_menu', array( $this, 'change_menu_link_url' ), 99 );
 
 
 
72
 
73
- add_filter( 'wp_stream_record_inserted', array( $this, 'check_records' ), 10, 2 );
 
 
 
74
 
75
- add_action( 'wp_ajax_load_alerts_settings', array( $this, 'load_alerts_settings' ) );
 
 
 
76
  add_action( 'wp_ajax_get_actions', array( $this, 'get_actions' ) );
77
- add_action( 'wp_ajax_save_new_alert', array( $this, 'save_new_alert' ) );
78
- add_action( 'wp_ajax_get_new_alert_triggers_notifications', array(
79
  $this,
80
- 'get_new_alert_triggers_notifications',
81
  ) );
 
 
 
 
 
 
82
 
83
  $this->load_alert_types();
84
  $this->load_alert_triggers();
85
 
86
- add_filter( 'wp_stream_action_links_posts', array( $this, 'change_alert_action_links' ), 11, 2 );
 
 
 
87
 
88
  }
89
 
@@ -92,12 +113,13 @@ class Alerts {
92
  *
93
  * @return void
94
  */
95
- function load_alert_types() {
96
  $alert_types = array(
97
  'none',
98
  'highlight',
99
  'email',
100
  'ifttt',
 
101
  );
102
 
103
  $classes = array();
@@ -128,12 +150,6 @@ class Alerts {
128
  foreach ( $this->alert_types as $key => $alert_type ) {
129
  if ( ! $this->is_valid_alert_type( $alert_type ) ) {
130
  unset( $this->alert_types[ $key ] );
131
- trigger_error(
132
- sprintf(
133
- esc_html__( 'Registered alert_type %s does not extend WP_Stream\Alert_Type.', 'stream' ),
134
- esc_html( get_class( $alert_type ) )
135
- )
136
- );
137
  }
138
  }
139
  }
@@ -143,7 +159,7 @@ class Alerts {
143
  *
144
  * @return void
145
  */
146
- function load_alert_triggers() {
147
  $alert_triggers = array(
148
  'author',
149
  'context',
@@ -178,12 +194,6 @@ class Alerts {
178
  foreach ( $this->alert_triggers as $key => $alert_trigger ) {
179
  if ( ! $this->is_valid_alert_trigger( $alert_trigger ) ) {
180
  unset( $this->alert_triggers[ $key ] );
181
- trigger_error(
182
- sprintf(
183
- esc_html__( 'Registered alert_trigger %s does not extend WP_Stream\Alert_Trigger.', 'stream' ),
184
- esc_html( get_class( $alert_trigger ) )
185
- )
186
- );
187
  }
188
  }
189
  }
@@ -236,7 +246,7 @@ class Alerts {
236
  *
237
  * @return array
238
  */
239
- function check_records( $record_id, $recordarr ) {
240
  $args = array(
241
  'post_type' => self::POST_TYPE,
242
  'post_status' => 'wp_stream_enabled',
@@ -263,15 +273,19 @@ class Alerts {
263
  *
264
  * @return void
265
  */
266
- function register_scripts() {
267
  $screen = get_current_screen();
268
  if ( 'edit-wp_stream_alerts' === $screen->id ) {
269
- wp_register_script( 'wp-stream-alerts', $this->plugin->locations['url'] . 'ui/js/alerts.js', array(
 
 
270
  'wp-stream-select2',
271
  'jquery',
272
  'inline-edit-post',
273
  ) );
274
- wp_localize_script( 'wp-stream-alerts', 'streamAlerts',
 
 
275
  array(
276
  'any' => __( 'Any', 'stream' ),
277
  'anyContext' => __( 'Any Context', 'stream' ),
@@ -335,6 +349,7 @@ class Alerts {
335
  'public' => false,
336
  'show_in_admin_all_list' => true,
337
  'show_in_admin_status_list' => true,
 
338
  'label_count' => _n_noop( 'Enabled <span class="count">(%s)</span>', 'Enabled <span class="count">(%s)</span>', 'stream' ),
339
  );
340
 
@@ -346,6 +361,7 @@ class Alerts {
346
  'internal' => false,
347
  'show_in_admin_all_list' => true,
348
  'show_in_admin_status_list' => true,
 
349
  'label_count' => _n_noop( 'Disabled <span class="count">(%s)</span>', 'Disabled <span class="count">(%s)</span>', 'stream' ),
350
  );
351
 
@@ -362,21 +378,22 @@ class Alerts {
362
  public function get_alert( $post_id = '' ) {
363
  if ( ! $post_id ) {
364
  $obj = new Alert( null, $this->plugin );
 
365
  return $obj;
366
  }
367
 
368
  $post = get_post( $post_id );
369
 
370
- $alert_type = get_post_meta( $post_id, 'alert_type', true );
371
- $alert_meta = get_post_meta( $post_id, 'alert_meta', true );
372
 
373
  $obj = (object) array(
374
- 'ID' => $post->ID,
375
- 'status' => $post->post_status,
376
- 'date' => $post->post_date,
377
- 'author' => $post->post_author,
378
- 'alert_type' => $alert_type,
379
- 'alert_meta' => $alert_meta,
380
  );
381
 
382
  return new Alert( $obj, $this->plugin );
@@ -390,7 +407,7 @@ class Alerts {
390
  *
391
  * @return void
392
  */
393
- function register_menu() {
394
  add_submenu_page(
395
  $this->plugin->admin->records_page_slug,
396
  __( 'Alerts', 'stream' ),
@@ -413,7 +430,7 @@ class Alerts {
413
  * @action network_admin_menu
414
  * @return bool
415
  */
416
- function change_menu_link_url() {
417
  global $submenu;
418
 
419
  $parent = 'wp_stream';
@@ -441,10 +458,10 @@ class Alerts {
441
  $new_url = get_admin_url( $site_id, $page );
442
 
443
  foreach ( $submenu[ $parent ] as $key => $value ) {
444
-
445
  // Set correct URL for the menu item.
446
  if ( $page === $value[2] ) {
447
- $submenu[ $parent ][ $key ][2] = $new_url;
 
448
  break;
449
  }
450
  }
@@ -459,22 +476,24 @@ class Alerts {
459
  *
460
  * @return void
461
  */
462
- function display_notification_box( $post = array() ) {
463
  $alert_type = 'none';
464
  if ( is_object( $post ) ) {
465
  $alert = $this->get_alert( $post->ID );
466
  $alert_type = $alert->alert_type;
467
  }
468
- $form = new Form_Generator;
469
-
470
- $field_html = $form->render_field( 'select', array(
471
- 'id' => 'wp_stream_alert_type',
472
- 'name' => 'wp_stream_alert_type',
473
- 'value' => $alert_type,
474
- 'options' => $this->get_notification_values(),
475
- 'placeholder' => __( 'No Alert', 'stream' ),
476
- 'title' => 'Alert Type:',
477
- ) );
 
 
478
 
479
  echo '<label>' . esc_html__( 'Alert me by', 'stream' ) . '</label>';
480
  echo $field_html; // Xss ok.
@@ -496,15 +515,17 @@ class Alerts {
496
  *
497
  * @return void
498
  */
499
- function load_alerts_settings() {
500
- $alert = array();
501
  $post_id = wp_stream_filter_input( INPUT_POST, 'post_id' );
502
  if ( ! empty( $post_id ) ) {
503
  $alert = $this->get_alert( $post_id );
504
  if ( ! $alert ) {
505
- wp_send_json_error( array(
506
- 'message' => 'Could not find alert.',
507
- ) );
 
 
508
  }
509
  }
510
 
@@ -513,9 +534,11 @@ class Alerts {
513
  $alert_type = 'none';
514
  }
515
  if ( ! array_key_exists( $alert_type, $this->alert_types ) ) {
516
- wp_send_json_error( array(
517
- 'message' => 'Could not find alert type.',
518
- ) );
 
 
519
  }
520
 
521
  ob_start();
@@ -523,7 +546,9 @@ class Alerts {
523
  $output = ob_get_contents();
524
  ob_end_clean();
525
 
526
- $data = array( 'html' => $output );
 
 
527
  wp_send_json_success( $data );
528
  }
529
 
@@ -534,13 +559,13 @@ class Alerts {
534
  *
535
  * @return void
536
  */
537
- function display_triggers_box( $post = array() ) {
538
  if ( is_object( $post ) ) {
539
  $alert = $this->get_alert( $post->ID );
540
  } else {
541
  $alert = array();
542
  }
543
- $form = new Form_Generator;
544
  do_action( 'wp_stream_alert_trigger_form_display', $form, $alert );
545
  // @TODO use human readable text.
546
  echo '<label>' . esc_html__( 'Alert me when', 'stream' ) . '</label>';
@@ -555,7 +580,7 @@ class Alerts {
555
  *
556
  * @return void
557
  */
558
- function display_submit_box( $post ) {
559
  if ( empty( $post ) ) {
560
  return;
561
  }
@@ -572,9 +597,9 @@ class Alerts {
572
  <label for="wp_stream_alert_status"><?php esc_html_e( 'Status', 'stream' ); ?></label>
573
  <select name='wp_stream_alert_status' id='wp_stream_alert_status'>
574
  <option<?php selected( $post_status, 'wp_stream_enabled' ); ?>
575
- value='wp_stream_enabled'><?php esc_html_e( 'Enabled', 'stream' ); ?></option>
576
  <option<?php selected( $post_status, 'wp_stream_disabled' ); ?>
577
- value='wp_stream_disabled'><?php esc_html_e( 'Disabled', 'stream' ); ?></option>
578
  </select>
579
  </div>
580
  </div>
@@ -591,9 +616,12 @@ class Alerts {
591
  $delete_text = __( 'Move to Trash', 'stream' );
592
  }
593
  ?>
594
- <a class="submitdelete deletion"
595
- href="<?php echo get_delete_post_link( $post->ID ); ?>"><?php esc_html( $delete_text ); ?></a><?php
596
- } ?>
 
 
 
597
  </div>
598
  <div id="publishing-action">
599
  <span class="spinner"></span>
@@ -610,7 +638,7 @@ class Alerts {
610
  *
611
  * @return void
612
  */
613
- function display_status_box() {
614
  ?>
615
  <div id="minor-publishing">
616
  <div id="misc-publishing-actions">
@@ -636,7 +664,7 @@ class Alerts {
636
  *
637
  * @return array
638
  */
639
- function get_notification_values() {
640
  $result = array();
641
  $names = wp_list_pluck( $this->alert_types, 'name', 'slug' );
642
  foreach ( $names as $slug => $name ) {
@@ -649,7 +677,7 @@ class Alerts {
649
  /**
650
  * Update actions dropdown options based on the connector selected.
651
  */
652
- function get_actions() {
653
  $connector_name = wp_stream_filter_input( INPUT_POST, 'connector' );
654
  $stream_connectors = wp_stream_get_instance()->connectors;
655
  if ( ! empty( $connector_name ) ) {
@@ -669,7 +697,7 @@ class Alerts {
669
  /**
670
  * Save a new alert
671
  */
672
- function save_new_alert() {
673
  check_ajax_referer( 'save_alert', 'wp_stream_alerts_nonce' );
674
  $trigger_author = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_author' );
675
  $trigger_connector_and_context = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_context' );
@@ -695,9 +723,9 @@ class Alerts {
695
  $alert_status = wp_stream_filter_input( INPUT_POST, 'wp_stream_alert_status' );
696
 
697
  // Insert the post into the database
698
- $item = (object) array(
699
- 'alert_type' => $alert_type,
700
- 'alert_meta' => array(
701
  'trigger_author' => $trigger_author,
702
  'trigger_connector' => $trigger_connector,
703
  'trigger_action' => $trigger_action,
@@ -705,13 +733,15 @@ class Alerts {
705
  ),
706
  'alert_status' => $alert_status,
707
  );
708
- $alert = new Alert( $item, $this->plugin );
709
- $title = $alert->get_title();
710
- $post_id = wp_insert_post( array(
711
- 'post_status' => $alert_status,
712
- 'post_type' => 'wp_stream_alerts',
713
- 'post_title' => $title,
714
- ) );
 
 
715
  if ( empty( $post_id ) ) {
716
  wp_send_json_error();
717
  }
@@ -725,13 +755,17 @@ class Alerts {
725
  );
726
  $alert_meta = apply_filters( 'wp_stream_alerts_save_meta', $alert_meta, $alert_type );
727
  add_post_meta( $post_id, 'alert_meta', $alert_meta );
728
- wp_send_json_success( array( 'success' => true ) );
 
 
 
 
729
  }
730
 
731
  /**
732
  *
733
  */
734
- function get_new_alert_triggers_notifications() {
735
  ob_start();
736
  ?>
737
  <fieldset class="inline-edit-col inline-edit-wp_stream_alerts inline-edit-add-new-triggers">
@@ -746,30 +780,38 @@ class Alerts {
746
  </fieldset>
747
  <?php
748
  $html = ob_get_clean();
749
- wp_send_json_success( array( 'success' => true, 'html' => $html ) );
 
 
 
 
 
750
  }
 
751
  /**
752
  * Add action links to Stream drop row in admin list screen
753
  *
754
  * @filter wp_stream_action_links_{connector}
755
  *
756
- * @param array $links Previous links registered
757
  * @param Record $record Stream record
758
  *
759
  * @return array Action links
760
  */
761
- function change_alert_action_links( $links, $record ) {
762
  $post = get_post( $record->object_id );
763
 
764
  if ( $post && self::POST_TYPE === $post->post_type && $post->post_status === $record->get_meta( 'new_status', true ) ) {
765
  if ( 'trash' !== $post->post_status ) {
766
- $connector_posts = new \WP_Stream\Connector_Posts;
767
- $post_type_name = $connector_posts->get_post_type_name( get_post_type( $post->ID ) );
768
 
 
769
  $links[ sprintf( esc_html_x( 'Edit %s', 'Post type singular name', 'stream' ), $post_type_name ) ] = admin_url( 'edit.php?post_type=wp_stream_alerts#post-' . $post->ID );
770
  unset( $links[ esc_html__( 'View', 'stream' ) ] );
771
  }
772
  }
 
773
  return $links;
774
  }
775
  }
13
  * @package WP_Stream
14
  */
15
  class Alerts {
16
+
17
  /**
18
  * Alerts post type slug
19
  */
67
  add_action( 'wp_stream_admin_menu', array( $this, 'register_menu' ) );
68
 
69
  // Add scripts to post screens.
70
+ add_action( 'admin_enqueue_scripts', array(
71
+ $this,
72
+ 'register_scripts',
73
+ ) );
74
 
75
+ add_action( 'network_admin_menu', array(
76
+ $this,
77
+ 'change_menu_link_url',
78
+ ), 99 );
79
 
80
+ add_filter( 'wp_stream_record_inserted', array(
81
+ $this,
82
+ 'check_records',
83
+ ), 10, 2 );
84
 
85
+ add_action( 'wp_ajax_load_alerts_settings', array(
86
+ $this,
87
+ 'load_alerts_settings',
88
+ ) );
89
  add_action( 'wp_ajax_get_actions', array( $this, 'get_actions' ) );
90
+ add_action( 'wp_ajax_save_new_alert', array(
 
91
  $this,
92
+ 'save_new_alert',
93
  ) );
94
+ add_action(
95
+ 'wp_ajax_get_new_alert_triggers_notifications', array(
96
+ $this,
97
+ 'get_new_alert_triggers_notifications',
98
+ )
99
+ );
100
 
101
  $this->load_alert_types();
102
  $this->load_alert_triggers();
103
 
104
+ add_filter( 'wp_stream_action_links_posts', array(
105
+ $this,
106
+ 'change_alert_action_links',
107
+ ), 11, 2 );
108
 
109
  }
110
 
113
  *
114
  * @return void
115
  */
116
+ public function load_alert_types() {
117
  $alert_types = array(
118
  'none',
119
  'highlight',
120
  'email',
121
  'ifttt',
122
+ 'slack',
123
  );
124
 
125
  $classes = array();
150
  foreach ( $this->alert_types as $key => $alert_type ) {
151
  if ( ! $this->is_valid_alert_type( $alert_type ) ) {
152
  unset( $this->alert_types[ $key ] );
 
 
 
 
 
 
153
  }
154
  }
155
  }
159
  *
160
  * @return void
161
  */
162
+ public function load_alert_triggers() {
163
  $alert_triggers = array(
164
  'author',
165
  'context',
194
  foreach ( $this->alert_triggers as $key => $alert_trigger ) {
195
  if ( ! $this->is_valid_alert_trigger( $alert_trigger ) ) {
196
  unset( $this->alert_triggers[ $key ] );
 
 
 
 
 
 
197
  }
198
  }
199
  }
246
  *
247
  * @return array
248
  */
249
+ public function check_records( $record_id, $recordarr ) {
250
  $args = array(
251
  'post_type' => self::POST_TYPE,
252
  'post_status' => 'wp_stream_enabled',
273
  *
274
  * @return void
275
  */
276
+ public function register_scripts() {
277
  $screen = get_current_screen();
278
  if ( 'edit-wp_stream_alerts' === $screen->id ) {
279
+
280
+ $min = wp_stream_min_suffix();
281
+ wp_register_script( 'wp-stream-alerts', $this->plugin->locations['url'] . 'ui/js/alerts.' . $min . 'js', array(
282
  'wp-stream-select2',
283
  'jquery',
284
  'inline-edit-post',
285
  ) );
286
+
287
+ wp_localize_script(
288
+ 'wp-stream-alerts', 'streamAlerts',
289
  array(
290
  'any' => __( 'Any', 'stream' ),
291
  'anyContext' => __( 'Any Context', 'stream' ),
349
  'public' => false,
350
  'show_in_admin_all_list' => true,
351
  'show_in_admin_status_list' => true,
352
+ // translators: Placeholder refers to a number of items (e.g. "42")
353
  'label_count' => _n_noop( 'Enabled <span class="count">(%s)</span>', 'Enabled <span class="count">(%s)</span>', 'stream' ),
354
  );
355
 
361
  'internal' => false,
362
  'show_in_admin_all_list' => true,
363
  'show_in_admin_status_list' => true,
364
+ // translators: Placeholder refers to a number of items (e.g. "42")
365
  'label_count' => _n_noop( 'Disabled <span class="count">(%s)</span>', 'Disabled <span class="count">(%s)</span>', 'stream' ),
366
  );
367
 
378
  public function get_alert( $post_id = '' ) {
379
  if ( ! $post_id ) {
380
  $obj = new Alert( null, $this->plugin );
381
+
382
  return $obj;
383
  }
384
 
385
  $post = get_post( $post_id );
386
 
387
+ $alert_type = get_post_meta( $post_id, 'alert_type', true );
388
+ $alert_meta = get_post_meta( $post_id, 'alert_meta', true );
389
 
390
  $obj = (object) array(
391
+ 'ID' => $post->ID,
392
+ 'status' => $post->post_status,
393
+ 'date' => $post->post_date,
394
+ 'author' => $post->post_author,
395
+ 'alert_type' => $alert_type,
396
+ 'alert_meta' => $alert_meta,
397
  );
398
 
399
  return new Alert( $obj, $this->plugin );
407
  *
408
  * @return void
409
  */
410
+ public function register_menu() {
411
  add_submenu_page(
412
  $this->plugin->admin->records_page_slug,
413
  __( 'Alerts', 'stream' ),
430
  * @action network_admin_menu
431
  * @return bool
432
  */
433
+ public function change_menu_link_url() {
434
  global $submenu;
435
 
436
  $parent = 'wp_stream';
458
  $new_url = get_admin_url( $site_id, $page );
459
 
460
  foreach ( $submenu[ $parent ] as $key => $value ) {
 
461
  // Set correct URL for the menu item.
462
  if ( $page === $value[2] ) {
463
+ // This hack is not kosher, see the docblock for an explanation.
464
+ $submenu[ $parent ][ $key ][2] = $new_url; // override okay
465
  break;
466
  }
467
  }
476
  *
477
  * @return void
478
  */
479
+ public function display_notification_box( $post = array() ) {
480
  $alert_type = 'none';
481
  if ( is_object( $post ) ) {
482
  $alert = $this->get_alert( $post->ID );
483
  $alert_type = $alert->alert_type;
484
  }
485
+ $form = new Form_Generator();
486
+
487
+ $field_html = $form->render_field(
488
+ 'select', array(
489
+ 'id' => 'wp_stream_alert_type',
490
+ 'name' => 'wp_stream_alert_type',
491
+ 'value' => $alert_type,
492
+ 'options' => $this->get_notification_values(),
493
+ 'placeholder' => __( 'No Alert', 'stream' ),
494
+ 'title' => 'Alert Type:',
495
+ )
496
+ );
497
 
498
  echo '<label>' . esc_html__( 'Alert me by', 'stream' ) . '</label>';
499
  echo $field_html; // Xss ok.
515
  *
516
  * @return void
517
  */
518
+ public function load_alerts_settings() {
519
+ $alert = array();
520
  $post_id = wp_stream_filter_input( INPUT_POST, 'post_id' );
521
  if ( ! empty( $post_id ) ) {
522
  $alert = $this->get_alert( $post_id );
523
  if ( ! $alert ) {
524
+ wp_send_json_error(
525
+ array(
526
+ 'message' => 'Could not find alert.',
527
+ )
528
+ );
529
  }
530
  }
531
 
534
  $alert_type = 'none';
535
  }
536
  if ( ! array_key_exists( $alert_type, $this->alert_types ) ) {
537
+ wp_send_json_error(
538
+ array(
539
+ 'message' => 'Could not find alert type.',
540
+ )
541
+ );
542
  }
543
 
544
  ob_start();
546
  $output = ob_get_contents();
547
  ob_end_clean();
548
 
549
+ $data = array(
550
+ 'html' => $output,
551
+ );
552
  wp_send_json_success( $data );
553
  }
554
 
559
  *
560
  * @return void
561
  */
562
+ public function display_triggers_box( $post = array() ) {
563
  if ( is_object( $post ) ) {
564
  $alert = $this->get_alert( $post->ID );
565
  } else {
566
  $alert = array();
567
  }
568
+ $form = new Form_Generator();
569
  do_action( 'wp_stream_alert_trigger_form_display', $form, $alert );
570
  // @TODO use human readable text.
571
  echo '<label>' . esc_html__( 'Alert me when', 'stream' ) . '</label>';
580
  *
581
  * @return void
582
  */
583
+ public function display_submit_box( $post ) {
584
  if ( empty( $post ) ) {
585
  return;
586
  }
597
  <label for="wp_stream_alert_status"><?php esc_html_e( 'Status', 'stream' ); ?></label>
598
  <select name='wp_stream_alert_status' id='wp_stream_alert_status'>
599
  <option<?php selected( $post_status, 'wp_stream_enabled' ); ?>
600
+ value='wp_stream_enabled'><?php esc_html_e( 'Enabled', 'stream' ); ?></option>
601
  <option<?php selected( $post_status, 'wp_stream_disabled' ); ?>
602
+ value='wp_stream_disabled'><?php esc_html_e( 'Disabled', 'stream' ); ?></option>
603
  </select>
604
  </div>
605
  </div>
616
  $delete_text = __( 'Move to Trash', 'stream' );
617
  }
618
  ?>
619
+ <a class="submitdelete deletion" href="<?php echo get_delete_post_link( $post->ID ); ?>">
620
+ <?php esc_html( $delete_text ); ?>
621
+ </a>
622
+ <?php
623
+ }
624
+ ?>
625
  </div>
626
  <div id="publishing-action">
627
  <span class="spinner"></span>
638
  *
639
  * @return void
640
  */
641
+ public function display_status_box() {
642
  ?>
643
  <div id="minor-publishing">
644
  <div id="misc-publishing-actions">
664
  *
665
  * @return array
666
  */
667
+ public function get_notification_values() {
668
  $result = array();
669
  $names = wp_list_pluck( $this->alert_types, 'name', 'slug' );
670
  foreach ( $names as $slug => $name ) {
677
  /**
678
  * Update actions dropdown options based on the connector selected.
679
  */
680
+ public function get_actions() {
681
  $connector_name = wp_stream_filter_input( INPUT_POST, 'connector' );
682
  $stream_connectors = wp_stream_get_instance()->connectors;
683
  if ( ! empty( $connector_name ) ) {
697
  /**
698
  * Save a new alert
699
  */
700
+ public function save_new_alert() {
701
  check_ajax_referer( 'save_alert', 'wp_stream_alerts_nonce' );
702
  $trigger_author = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_author' );
703
  $trigger_connector_and_context = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_context' );
723
  $alert_status = wp_stream_filter_input( INPUT_POST, 'wp_stream_alert_status' );
724
 
725
  // Insert the post into the database
726
+ $item = (object) array(
727
+ 'alert_type' => $alert_type,
728
+ 'alert_meta' => array(
729
  'trigger_author' => $trigger_author,
730
  'trigger_connector' => $trigger_connector,
731
  'trigger_action' => $trigger_action,
733
  ),
734
  'alert_status' => $alert_status,
735
  );
736
+ $alert = new Alert( $item, $this->plugin );
737
+ $title = $alert->get_title();
738
+ $post_id = wp_insert_post(
739
+ array(
740
+ 'post_status' => $alert_status,
741
+ 'post_type' => 'wp_stream_alerts',
742
+ 'post_title' => $title,
743
+ )
744
+ );
745
  if ( empty( $post_id ) ) {
746
  wp_send_json_error();
747
  }
755
  );
756
  $alert_meta = apply_filters( 'wp_stream_alerts_save_meta', $alert_meta, $alert_type );
757
  add_post_meta( $post_id, 'alert_meta', $alert_meta );
758
+ wp_send_json_success(
759
+ array(
760
+ 'success' => true,
761
+ )
762
+ );
763
  }
764
 
765
  /**
766
  *
767
  */
768
+ public function get_new_alert_triggers_notifications() {
769
  ob_start();
770
  ?>
771
  <fieldset class="inline-edit-col inline-edit-wp_stream_alerts inline-edit-add-new-triggers">
780
  </fieldset>
781
  <?php
782
  $html = ob_get_clean();
783
+ wp_send_json_success(
784
+ array(
785
+ 'success' => true,
786
+ 'html' => $html,
787
+ )
788
+ );
789
  }
790
+
791
  /**
792
  * Add action links to Stream drop row in admin list screen
793
  *
794
  * @filter wp_stream_action_links_{connector}
795
  *
796
+ * @param array $links Previous links registered
797
  * @param Record $record Stream record
798
  *
799
  * @return array Action links
800
  */
801
+ public function change_alert_action_links( $links, $record ) {
802
  $post = get_post( $record->object_id );
803
 
804
  if ( $post && self::POST_TYPE === $post->post_type && $post->post_status === $record->get_meta( 'new_status', true ) ) {
805
  if ( 'trash' !== $post->post_status ) {
806
+ $connector_posts = new \WP_Stream\Connector_Posts();
807
+ $post_type_name = $connector_posts->get_post_type_name( get_post_type( $post->ID ) );
808
 
809
+ // translators: Placeholder refers to the post type singular name (e.g. "Post")
810
  $links[ sprintf( esc_html_x( 'Edit %s', 'Post type singular name', 'stream' ), $post_type_name ) ] = admin_url( 'edit.php?post_type=wp_stream_alerts#post-' . $post->ID );
811
  unset( $links[ esc_html__( 'View', 'stream' ) ] );
812
  }
813
  }
814
+
815
  return $links;
816
  }
817
  }
classes/class-author.php CHANGED
@@ -23,7 +23,7 @@ class Author {
23
  * @param int $user_id The user ID.
24
  * @param array $user_meta The user meta array.
25
  */
26
- function __construct( $user_id, $user_meta = array() ) {
27
  $this->id = absint( $user_id );
28
  $this->meta = $user_meta;
29
 
@@ -41,7 +41,7 @@ class Author {
41
  *
42
  * @return string
43
  */
44
- function __get( $name ) {
45
  if ( 'display_name' === $name ) {
46
  return $this->get_display_name();
47
  } elseif ( 'avatar_img' === $name ) {
@@ -62,7 +62,7 @@ class Author {
62
  /**
63
  * @return string
64
  */
65
- function __toString() {
66
  return $this->get_display_name();
67
  }
68
 
@@ -71,7 +71,7 @@ class Author {
71
  *
72
  * @return string
73
  */
74
- function get_display_name() {
75
  if ( 0 === $this->id ) {
76
  if ( isset( $this->meta['system_user_name'] ) ) {
77
  return esc_html( $this->meta['system_user_name'] );
@@ -101,7 +101,7 @@ class Author {
101
  *
102
  * @return string
103
  */
104
- function get_agent() {
105
  $agent = '';
106
 
107
  if ( ! empty( $this->meta['agent'] ) ) {
@@ -122,7 +122,7 @@ class Author {
122
  *
123
  * @return string|bool An img HTML element, or false if avatars are disabled
124
  */
125
- function get_avatar_img( $size = 80 ) {
126
  if ( ! get_option( 'show_avatars' ) ) {
127
  return false;
128
  }
@@ -150,7 +150,7 @@ class Author {
150
  *
151
  * @return string|bool Gravatar image URL, or false on failure
152
  */
153
- function get_avatar_src( $size = 80 ) {
154
  $img = $this->get_avatar_img( $size );
155
 
156
  if ( ! $img ) {
@@ -177,7 +177,7 @@ class Author {
177
  *
178
  * @return string
179
  */
180
- function get_role() {
181
  global $wp_roles;
182
 
183
  $user_role = '';
@@ -200,7 +200,7 @@ class Author {
200
  *
201
  * @return bool
202
  */
203
- function is_deleted() {
204
  return ( 0 !== $this->id && 0 === $this->user->ID );
205
  }
206
 
@@ -209,7 +209,7 @@ class Author {
209
  *
210
  * @return bool
211
  */
212
- function is_wp_cli() {
213
  return ( 'wp_cli' === $this->get_agent() );
214
  }
215
 
@@ -222,7 +222,7 @@ class Author {
222
  *
223
  * @return bool
224
  */
225
- function is_doing_wp_cron() {
226
  return (
227
  wp_stream_is_cron_enabled()
228
  &&
@@ -237,7 +237,7 @@ class Author {
237
  *
238
  * @return string
239
  */
240
- function get_current_agent() {
241
  $agent = '';
242
 
243
  if ( defined( '\WP_CLI' ) && \WP_CLI ) {
@@ -263,7 +263,7 @@ class Author {
263
  *
264
  * @return string
265
  */
266
- function get_agent_label( $agent ) {
267
  if ( 'wp_cli' === $agent ) {
268
  $label = esc_html__( 'via WP-CLI', 'stream' );
269
  } elseif ( 'wp_cron' === $agent ) {
23
  * @param int $user_id The user ID.
24
  * @param array $user_meta The user meta array.
25
  */
26
+ public function __construct( $user_id, $user_meta = array() ) {
27
  $this->id = absint( $user_id );
28
  $this->meta = $user_meta;
29
 
41
  *
42
  * @return string
43
  */
44
+ public function __get( $name ) {
45
  if ( 'display_name' === $name ) {
46
  return $this->get_display_name();
47
  } elseif ( 'avatar_img' === $name ) {
62
  /**
63
  * @return string
64
  */
65
+ public function __toString() {
66
  return $this->get_display_name();
67
  }
68
 
71
  *
72
  * @return string
73
  */
74
+ public function get_display_name() {
75
  if ( 0 === $this->id ) {
76
  if ( isset( $this->meta['system_user_name'] ) ) {
77
  return esc_html( $this->meta['system_user_name'] );
101
  *
102
  * @return string
103
  */
104
+ public function get_agent() {
105
  $agent = '';
106
 
107
  if ( ! empty( $this->meta['agent'] ) ) {
122
  *
123
  * @return string|bool An img HTML element, or false if avatars are disabled
124
  */
125
+ public function get_avatar_img( $size = 80 ) {
126
  if ( ! get_option( 'show_avatars' ) ) {
127
  return false;
128
  }
150
  *
151
  * @return string|bool Gravatar image URL, or false on failure
152
  */
153
+ public function get_avatar_src( $size = 80 ) {
154
  $img = $this->get_avatar_img( $size );
155
 
156
  if ( ! $img ) {
177
  *
178
  * @return string
179
  */
180
+ public function get_role() {
181
  global $wp_roles;
182
 
183
  $user_role = '';
200
  *
201
  * @return bool
202
  */
203
+ public function is_deleted() {
204
  return ( 0 !== $this->id && 0 === $this->user->ID );
205
  }
206
 
209
  *
210
  * @return bool
211
  */
212
+ public function is_wp_cli() {
213
  return ( 'wp_cli' === $this->get_agent() );
214
  }
215
 
222
  *
223
  * @return bool
224
  */
225
+ public function is_doing_wp_cron() {
226
  return (
227
  wp_stream_is_cron_enabled()
228
  &&
237
  *
238
  * @return string
239
  */
240
+ public function get_current_agent() {
241
  $agent = '';
242
 
243
  if ( defined( '\WP_CLI' ) && \WP_CLI ) {
263
  *
264
  * @return string
265
  */
266
+ public function get_agent_label( $agent ) {
267
  if ( 'wp_cli' === $agent ) {
268
  $label = esc_html__( 'via WP-CLI', 'stream' );
269
  } elseif ( 'wp_cron' === $agent ) {
classes/class-cli.php CHANGED
@@ -4,9 +4,11 @@
4
  *
5
  * @see https://github.com/wp-cli/wp-cli
6
  */
 
7
  namespace WP_Stream;
8
 
9
  class CLI extends \WP_CLI_Command {
 
10
  /**
11
  * Query a set of Stream records.
12
  *
@@ -96,7 +98,13 @@ class CLI extends \WP_CLI_Command {
96
  $this->connection();
97
 
98
  if ( empty( $assoc_args['fields'] ) ) {
99
- $fields = array( 'created', 'ip', 'user_id', 'user_role', 'summary' );
 
 
 
 
 
 
100
  } else {
101
  $fields = explode( ',', $assoc_args['fields'] );
102
  }
@@ -135,23 +143,23 @@ class CLI extends \WP_CLI_Command {
135
 
136
  if ( isset( $assoc_args['format'] ) && 'table' !== $assoc_args['format'] ) {
137
  if ( 'count' === $assoc_args['format'] ) {
138
- WP_CLI::line( count( $records ) );
139
  }
140
 
141
  if ( 'json' === $assoc_args['format'] ) {
142
- WP_CLI::line( wp_stream_json_encode( $formatted_records ) );
143
  }
144
 
145
  if ( 'json_pretty' === $assoc_args['format'] ) {
146
  if ( version_compare( PHP_VERSION, '5.4', '<' ) ) {
147
- WP_CLI::line( wp_stream_json_encode( $formatted_records ) ); // xss ok
148
  } else {
149
- WP_CLI::line( wp_stream_json_encode( $formatted_records, JSON_PRETTY_PRINT ) ); // xss ok
150
  }
151
  }
152
 
153
  if ( 'csv' === $assoc_args['format'] ) {
154
- WP_CLI::line( $this->csv_format( $formatted_records ) );
155
  }
156
 
157
  return;
@@ -168,8 +176,8 @@ class CLI extends \WP_CLI_Command {
168
  /**
169
  * Convert any field to a flat array.
170
  *
171
- * @param string $name The output array element name
172
- * @param mixed $object Any value to be converted to an array
173
  *
174
  * @return array The flat array
175
  */
@@ -192,18 +200,18 @@ class CLI extends \WP_CLI_Command {
192
  /**
193
  * Convert an array of flat records to CSV
194
  *
195
- * @param array $array The input array of records
196
  *
197
  * @return string The CSV output
198
  */
199
  private function csv_format( $array ) {
200
- $output = fopen( 'php://output', 'w' );
201
 
202
  foreach ( $array as $line ) {
203
- fputcsv( $output, $line );
204
  }
205
 
206
- fclose( $output );
207
  }
208
 
209
  /**
@@ -212,10 +220,15 @@ class CLI extends \WP_CLI_Command {
212
  * @return void
213
  */
214
  private function connection() {
215
- $query = wp_stream_get_instance()->db->query( array( 'records_per_page' => 1, 'fields' => 'created' ) );
 
 
 
 
 
216
 
217
  if ( ! $query ) {
218
- WP_CLI::error( esc_html__( 'SITE IS DISCONNECTED', 'stream' ) );
219
  }
220
  }
221
  }
4
  *
5
  * @see https://github.com/wp-cli/wp-cli
6
  */
7
+
8
  namespace WP_Stream;
9
 
10
  class CLI extends \WP_CLI_Command {
11
+
12
  /**
13
  * Query a set of Stream records.
14
  *
98
  $this->connection();
99
 
100
  if ( empty( $assoc_args['fields'] ) ) {
101
+ $fields = array(
102
+ 'created',
103
+ 'ip',
104
+ 'user_id',
105
+ 'user_role',
106
+ 'summary',
107
+ );
108
  } else {
109
  $fields = explode( ',', $assoc_args['fields'] );
110
  }
143
 
144
  if ( isset( $assoc_args['format'] ) && 'table' !== $assoc_args['format'] ) {
145
  if ( 'count' === $assoc_args['format'] ) {
146
+ \WP_CLI::line( count( $records ) );
147
  }
148
 
149
  if ( 'json' === $assoc_args['format'] ) {
150
+ \WP_CLI::line( wp_stream_json_encode( $formatted_records ) );
151
  }
152
 
153
  if ( 'json_pretty' === $assoc_args['format'] ) {
154
  if ( version_compare( PHP_VERSION, '5.4', '<' ) ) {
155
+ \WP_CLI::line( wp_stream_json_encode( $formatted_records ) ); // xss ok
156
  } else {
157
+ \WP_CLI::line( wp_stream_json_encode( $formatted_records, JSON_PRETTY_PRINT ) ); // xss ok
158
  }
159
  }
160
 
161
  if ( 'csv' === $assoc_args['format'] ) {
162
+ \WP_CLI::line( $this->csv_format( $formatted_records ) );
163
  }
164
 
165
  return;
176
  /**
177
  * Convert any field to a flat array.
178
  *
179
+ * @param string $name The output array element name
180
+ * @param mixed $object Any value to be converted to an array
181
  *
182
  * @return array The flat array
183
  */
200
  /**
201
  * Convert an array of flat records to CSV
202
  *
203
+ * @param array $array The input array of records
204
  *
205
  * @return string The CSV output
206
  */
207
  private function csv_format( $array ) {
208
+ $output = fopen( 'php://output', 'w' ); // @codingStandardsIgnoreLine Clever output for WP CLI using php://output
209
 
210
  foreach ( $array as $line ) {
211
+ fputcsv( $output, $line ); // @codingStandardsIgnoreLine
212
  }
213
 
214
+ fclose( $output ); // @codingStandardsIgnoreLine
215
  }
216
 
217
  /**
220
  * @return void
221
  */
222
  private function connection() {
223
+ $query = wp_stream_get_instance()->db->query(
224
+ array(
225
+ 'records_per_page' => 1,
226
+ 'fields' => 'created',
227
+ )
228
+ );
229
 
230
  if ( ! $query ) {
231
+ \WP_CLI::error( esc_html__( 'SITE IS DISCONNECTED', 'stream' ) );
232
  }
233
  }
234
  }
classes/class-connector.php CHANGED
@@ -229,7 +229,7 @@ abstract class Connector {
229
  $inner[ $parent . '::' . $child ] = $change;
230
  }
231
  $result[ $key ] = 0; // Changed parent which has a changed children
232
- $result = array_merge( $result, $inner );
233
  }
234
  }
235
  }
229
  $inner[ $parent . '::' . $child ] = $change;
230
  }
231
  $result[ $key ] = 0; // Changed parent which has a changed children
232
+ $result = array_merge( $result, $inner );
233
  }
234
  }
235
  }
classes/class-connectors.php CHANGED
@@ -133,24 +133,29 @@ class Connectors {
133
 
134
  foreach ( $this->connectors as $connector ) {
135
  if ( ! method_exists( $connector, 'get_label' ) ) {
 
136
  $this->plugin->admin->notice( sprintf( __( '%s class wasn\'t loaded because it doesn\'t implement the get_label method.', 'stream' ), $connector->name, 'Connector' ), true );
137
  continue;
138
  }
139
  if ( ! method_exists( $connector, 'register' ) ) {
 
140
  $this->plugin->admin->notice( sprintf( __( '%s class wasn\'t loaded because it doesn\'t implement the register method.', 'stream' ), $connector->name, 'Connector' ), true );
141
  continue;
142
  }
143
  if ( ! method_exists( $connector, 'get_context_labels' ) ) {
 
144
  $this->plugin->admin->notice( sprintf( __( '%s class wasn\'t loaded because it doesn\'t implement the get_context_labels method.', 'stream' ), $connector->name, 'Connector' ), true );
145
  continue;
146
  }
147
  if ( ! method_exists( $connector, 'get_action_labels' ) ) {
 
148
  $this->plugin->admin->notice( sprintf( __( '%s class wasn\'t loaded because it doesn\'t implement the get_action_labels method.', 'stream' ), $connector->name, 'Connector' ), true );
149
  continue;
150
  }
151
 
152
  // Check if the connectors extends the Connector class, if not skip it.
153
  if ( ! is_subclass_of( $connector, '\WP_Stream\Connector' ) ) {
 
154
  $this->plugin->admin->notice( sprintf( __( '%1$s class wasn\'t loaded because it doesn\'t extends the %2$s class.', 'stream' ), $connector->name, 'Connector' ), true );
155
  continue;
156
  }
133
 
134
  foreach ( $this->connectors as $connector ) {
135
  if ( ! method_exists( $connector, 'get_label' ) ) {
136
+ // translators: Placeholder refers to a Connector class name, intended to provide help to developers (e.g. "Connector_BuddyPress")
137
  $this->plugin->admin->notice( sprintf( __( '%s class wasn\'t loaded because it doesn\'t implement the get_label method.', 'stream' ), $connector->name, 'Connector' ), true );
138
  continue;
139
  }
140
  if ( ! method_exists( $connector, 'register' ) ) {
141
+ // translators: Placeholder refers to a Connector class name, intended to provide help to developers (e.g. "Connector_BuddyPress")
142
  $this->plugin->admin->notice( sprintf( __( '%s class wasn\'t loaded because it doesn\'t implement the register method.', 'stream' ), $connector->name, 'Connector' ), true );
143
  continue;
144
  }
145
  if ( ! method_exists( $connector, 'get_context_labels' ) ) {
146
+ // translators: Placeholder refers to a Connector class name, intended to provide help to developers (e.g. "Connector_BuddyPress")
147
  $this->plugin->admin->notice( sprintf( __( '%s class wasn\'t loaded because it doesn\'t implement the get_context_labels method.', 'stream' ), $connector->name, 'Connector' ), true );
148
  continue;
149
  }
150
  if ( ! method_exists( $connector, 'get_action_labels' ) ) {
151
+ // translators: Placeholder refers to a Connector class name, intended to provide help to developers (e.g. "Connector_BuddyPress")
152
  $this->plugin->admin->notice( sprintf( __( '%s class wasn\'t loaded because it doesn\'t implement the get_action_labels method.', 'stream' ), $connector->name, 'Connector' ), true );
153
  continue;
154
  }
155
 
156
  // Check if the connectors extends the Connector class, if not skip it.
157
  if ( ! is_subclass_of( $connector, '\WP_Stream\Connector' ) ) {
158
+ // translators: Placeholder refers to a Connector class name, intended to provide help to developers (e.g. "Connector_BuddyPress")
159
  $this->plugin->admin->notice( sprintf( __( '%1$s class wasn\'t loaded because it doesn\'t extends the %2$s class.', 'stream' ), $connector->name, 'Connector' ), true );
160
  continue;
161
  }
classes/class-date-interval.php CHANGED
@@ -25,7 +25,7 @@ class Date_Interval {
25
  }
26
 
27
  /**
28
- * @return mixed|void
29
  */
30
  public function get_predefined_intervals() {
31
  $timezone = get_option( 'timezone_string' );
@@ -44,62 +44,68 @@ class Date_Interval {
44
  return apply_filters(
45
  'wp_stream_predefined_date_intervals',
46
  array(
47
- 'today' => array(
48
  'label' => esc_html__( 'Today', 'stream' ),
49
  'start' => Carbon::today( $timezone )->startOfDay(),
50
  'end' => Carbon::today( $timezone )->endOfDay(),
51
  ),
52
- 'yesterday' => array(
53
  'label' => esc_html__( 'Yesterday', 'stream' ),
54
  'start' => Carbon::today( $timezone )->startOfDay()->subDay(),
55
  'end' => Carbon::today( $timezone )->startOfDay()->subSecond(),
56
  ),
57
- 'last-7-days' => array(
 
58
  'label' => sprintf( esc_html__( 'Last %d Days', 'stream' ), 7 ),
59
  'start' => Carbon::today( $timezone )->subDays( 7 ),
60
  'end' => Carbon::today( $timezone ),
61
  ),
62
- 'last-14-days' => array(
 
63
  'label' => sprintf( esc_html__( 'Last %d Days', 'stream' ), 14 ),
64
  'start' => Carbon::today( $timezone )->subDays( 14 ),
65
  'end' => Carbon::today( $timezone ),
66
  ),
67
- 'last-30-days' => array(
 
68
  'label' => sprintf( esc_html__( 'Last %d Days', 'stream' ), 30 ),
69
  'start' => Carbon::today( $timezone )->subDays( 30 ),
70
  'end' => Carbon::today( $timezone ),
71
  ),
72
- 'this-month' => array(
73
  'label' => esc_html__( 'This Month', 'stream' ),
74
  'start' => Carbon::today( $timezone )->startOfMonth(),
75
  'end' => Carbon::today( $timezone )->endOfMonth(),
76
  ),
77
- 'last-month' => array(
78
  'label' => esc_html__( 'Last Month', 'stream' ),
79
  'start' => Carbon::today( $timezone )->startOfMonth()->subMonth(),
80
  'end' => Carbon::today( $timezone )->startOfMonth()->subSecond(),
81
  ),
82
- 'last-3-months' => array(
 
83
  'label' => sprintf( esc_html__( 'Last %d Months', 'stream' ), 3 ),
84
  'start' => Carbon::today( $timezone )->subMonths( 3 ),
85
  'end' => Carbon::today( $timezone ),
86
  ),
87
- 'last-6-months' => array(
 
88
  'label' => sprintf( esc_html__( 'Last %d Months', 'stream' ), 6 ),
89
  'start' => Carbon::today( $timezone )->subMonths( 6 ),
90
  'end' => Carbon::today( $timezone ),
91
  ),
92
  'last-12-months' => array(
 
93
  'label' => sprintf( esc_html__( 'Last %d Months', 'stream' ), 12 ),
94
  'start' => Carbon::today( $timezone )->subMonths( 12 ),
95
  'end' => Carbon::today( $timezone ),
96
  ),
97
- 'this-year' => array(
98
  'label' => esc_html__( 'This Year', 'stream' ),
99
  'start' => Carbon::today( $timezone )->startOfYear(),
100
  'end' => Carbon::today( $timezone )->endOfYear(),
101
  ),
102
- 'last-year' => array(
103
  'label' => esc_html__( 'Last Year', 'stream' ),
104
  'start' => Carbon::today( $timezone )->startOfYear()->subYear(),
105
  'end' => Carbon::today( $timezone )->startOfYear()->subSecond(),
25
  }
26
 
27
  /**
28
+ * @return mixed
29
  */
30
  public function get_predefined_intervals() {
31
  $timezone = get_option( 'timezone_string' );
44
  return apply_filters(
45
  'wp_stream_predefined_date_intervals',
46
  array(
47
+ 'today' => array(
48
  'label' => esc_html__( 'Today', 'stream' ),
49
  'start' => Carbon::today( $timezone )->startOfDay(),
50
  'end' => Carbon::today( $timezone )->endOfDay(),
51
  ),
52
+ 'yesterday' => array(
53
  'label' => esc_html__( 'Yesterday', 'stream' ),
54
  'start' => Carbon::today( $timezone )->startOfDay()->subDay(),
55
  'end' => Carbon::today( $timezone )->startOfDay()->subSecond(),
56
  ),
57
+ 'last-7-days' => array(
58
+ // translators: Placeholder refers to a number of days (e.g. "7")
59
  'label' => sprintf( esc_html__( 'Last %d Days', 'stream' ), 7 ),
60
  'start' => Carbon::today( $timezone )->subDays( 7 ),
61
  'end' => Carbon::today( $timezone ),
62
  ),
63
+ 'last-14-days' => array(
64
+ // translators: Placeholder refers to a number of days (e.g. "7")
65
  'label' => sprintf( esc_html__( 'Last %d Days', 'stream' ), 14 ),
66
  'start' => Carbon::today( $timezone )->subDays( 14 ),
67
  'end' => Carbon::today( $timezone ),
68
  ),
69
+ 'last-30-days' => array(
70
+ // translators: Placeholder refers to a number of days (e.g. "7")
71
  'label' => sprintf( esc_html__( 'Last %d Days', 'stream' ), 30 ),
72
  'start' => Carbon::today( $timezone )->subDays( 30 ),
73
  'end' => Carbon::today( $timezone ),
74
  ),
75
+ 'this-month' => array(
76
  'label' => esc_html__( 'This Month', 'stream' ),
77
  'start' => Carbon::today( $timezone )->startOfMonth(),
78
  'end' => Carbon::today( $timezone )->endOfMonth(),
79
  ),
80
+ 'last-month' => array(
81
  'label' => esc_html__( 'Last Month', 'stream' ),
82
  'start' => Carbon::today( $timezone )->startOfMonth()->subMonth(),
83
  'end' => Carbon::today( $timezone )->startOfMonth()->subSecond(),
84
  ),
85
+ 'last-3-months' => array(
86
+ // translators: Placeholder refers to a number of months (e.g. "3")
87
  'label' => sprintf( esc_html__( 'Last %d Months', 'stream' ), 3 ),
88
  'start' => Carbon::today( $timezone )->subMonths( 3 ),
89
  'end' => Carbon::today( $timezone ),
90
  ),
91
+ 'last-6-months' => array(
92
+ // translators: Placeholder refers to a number of months (e.g. "3")
93
  'label' => sprintf( esc_html__( 'Last %d Months', 'stream' ), 6 ),
94
  'start' => Carbon::today( $timezone )->subMonths( 6 ),
95
  'end' => Carbon::today( $timezone ),
96
  ),
97
  'last-12-months' => array(
98
+ // translators: Placeholder refers to a number of months (e.g. "3")
99
  'label' => sprintf( esc_html__( 'Last %d Months', 'stream' ), 12 ),
100
  'start' => Carbon::today( $timezone )->subMonths( 12 ),
101
  'end' => Carbon::today( $timezone ),
102
  ),
103
+ 'this-year' => array(
104
  'label' => esc_html__( 'This Year', 'stream' ),
105
  'start' => Carbon::today( $timezone )->startOfYear(),
106
  'end' => Carbon::today( $timezone )->endOfYear(),
107
  ),
108
+ 'last-year' => array(
109
  'label' => esc_html__( 'Last Year', 'stream' ),
110
  'start' => Carbon::today( $timezone )->startOfYear()->subYear(),
111
  'end' => Carbon::today( $timezone )->startOfYear()->subSecond(),
classes/class-db-driver-wpdb.php CHANGED
File without changes
classes/class-db-driver.php CHANGED
File without changes
classes/class-db.php CHANGED
@@ -46,11 +46,13 @@ class DB {
46
  */
47
  $record = apply_filters( 'wp_stream_record_array', $record );
48
 
49
- array_walk( $record, function( &$value, &$key ) {
50
- if ( ! is_array( $value ) ) {
51
- $value = strip_tags( $value );
 
 
52
  }
53
- });
54
 
55
  if ( empty( $record ) ) {
56
  return false;
@@ -197,7 +199,7 @@ class DB {
197
  */
198
  $args = apply_filters( 'wp_stream_query_args', $args );
199
 
200
- $result = (array) $this->driver->get_records( $args );
201
  $this->found_records_count = isset( $result['count'] ) ? $result['count'] : 0;
202
 
203
  return empty( $result['items'] ) ? array() : $result['items'];
46
  */
47
  $record = apply_filters( 'wp_stream_record_array', $record );
48
 
49
+ array_walk(
50
+ $record, function( &$value, &$key ) {
51
+ if ( ! is_array( $value ) ) {
52
+ $value = strip_tags( $value );
53
+ }
54
  }
55
+ );
56
 
57
  if ( empty( $record ) ) {
58
  return false;
199
  */
200
  $args = apply_filters( 'wp_stream_query_args', $args );
201
 
202
+ $result = (array) $this->driver->get_records( $args );
203
  $this->found_records_count = isset( $result['count'] ) ? $result['count'] : 0;
204
 
205
  return empty( $result['items'] ) ? array() : $result['items'];
classes/class-export.php CHANGED
@@ -60,15 +60,14 @@ class Export {
60
 
61
  $records = $list_table->get_records();
62
  $columns = $list_table->get_columns();
63
- $output = array();
64
  foreach ( $records as $item ) {
65
  $output[] = $this->build_record( $item, $columns );
66
  }
67
 
68
  $exporters = $this->get_exporters();
69
- $exporter = $exporters[ $output_type ];
70
  $exporter->output_file( $output, $columns );
71
- return;
72
  }
73
 
74
  /**
@@ -76,9 +75,10 @@ class Export {
76
  *
77
  * @return array
78
  */
79
- function actions_menu_export_items( $action_menu_items ) {
80
  foreach ( $this->get_exporters() as $exporter ) {
81
  $action = 'export-' . $exporter->slug;
 
82
  $action_menu_items[ $action ] = sprintf( __( 'Export as %s', 'stream' ), $exporter->name );
83
  }
84
 
@@ -92,23 +92,23 @@ class Export {
92
  * @param array $columns Columns being extracted.
93
  * @return array Numerically-indexed array with extracted data.
94
  */
95
- function build_record( $item, $columns ) {
96
  $record = new Record( $item );
97
 
98
  $row_out = array();
99
  foreach ( array_keys( $columns ) as $column_name ) {
100
  switch ( $column_name ) {
101
- case 'date' :
102
- $created = date( 'Y-m-d H:i:s', strtotime( $record->created ) );
103
  $row_out[ $column_name ] = get_date_from_gmt( $created, 'Y/m/d h:i:s A' );
104
  break;
105
 
106
- case 'summary' :
107
  $row_out[ $column_name ] = $record->summary;
108
  break;
109
 
110
- case 'user_id' :
111
- $user = new Author( (int) $record->user_id, (array) $record->user_meta );
112
  $row_out[ $column_name ] = $user->get_display_name();
113
  break;
114
 
@@ -128,7 +128,7 @@ class Export {
128
  $row_out[ $column_name ] = $record->blog_id;
129
  break;
130
 
131
- case 'ip' :
132
  $row_out[ $column_name ] = $record->{$column_name};
133
  break;
134
  }
@@ -141,6 +141,7 @@ class Export {
141
  * Increase pagination limit for CSV Output
142
  *
143
  * @param int $records_per_page Old limit for records_per_page.
 
144
  */
145
  public function disable_paginate( $records_per_page ) {
146
  return 10000;
@@ -206,12 +207,6 @@ class Export {
206
  foreach ( $this->exporters as $key => $exporter ) {
207
  if ( ! $this->is_valid_exporter( $exporter ) ) {
208
  unset( $this->exporters[ $key ] );
209
- trigger_error(
210
- sprintf(
211
- esc_html__( 'Registered exporter %s does not extend WP_Stream\Exporter.', 'stream' ),
212
- esc_html( get_class( $exporter ) )
213
- )
214
- );
215
  }
216
  }
217
  }
60
 
61
  $records = $list_table->get_records();
62
  $columns = $list_table->get_columns();
63
+ $output = array();
64
  foreach ( $records as $item ) {
65
  $output[] = $this->build_record( $item, $columns );
66
  }
67
 
68
  $exporters = $this->get_exporters();
69
+ $exporter = $exporters[ $output_type ];
70
  $exporter->output_file( $output, $columns );
 
71
  }
72
 
73
  /**
75
  *
76
  * @return array
77
  */
78
+ public function actions_menu_export_items( $action_menu_items ) {
79
  foreach ( $this->get_exporters() as $exporter ) {
80
  $action = 'export-' . $exporter->slug;
81
+ // translators: Placeholder refers to an export format (e.g. "CSV")
82
  $action_menu_items[ $action ] = sprintf( __( 'Export as %s', 'stream' ), $exporter->name );
83
  }
84
 
92
  * @param array $columns Columns being extracted.
93
  * @return array Numerically-indexed array with extracted data.
94
  */
95
+ public function build_record( $item, $columns ) {
96
  $record = new Record( $item );
97
 
98
  $row_out = array();
99
  foreach ( array_keys( $columns ) as $column_name ) {
100
  switch ( $column_name ) {
101
+ case 'date':
102
+ $created = date( 'Y-m-d H:i:s', strtotime( $record->created ) );
103
  $row_out[ $column_name ] = get_date_from_gmt( $created, 'Y/m/d h:i:s A' );
104
  break;
105
 
106
+ case 'summary':
107
  $row_out[ $column_name ] = $record->summary;
108
  break;
109
 
110
+ case 'user_id':
111
+ $user = new Author( (int) $record->user_id, (array) $record->user_meta );
112
  $row_out[ $column_name ] = $user->get_display_name();
113
  break;
114
 
128
  $row_out[ $column_name ] = $record->blog_id;
129
  break;
130
 
131
+ case 'ip':
132
  $row_out[ $column_name ] = $record->{$column_name};
133
  break;
134
  }
141
  * Increase pagination limit for CSV Output
142
  *
143
  * @param int $records_per_page Old limit for records_per_page.
144
+ * @return int
145
  */
146
  public function disable_paginate( $records_per_page ) {
147
  return 10000;
207
  foreach ( $this->exporters as $key => $exporter ) {
208
  if ( ! $this->is_valid_exporter( $exporter ) ) {
209
  unset( $this->exporters[ $key ] );
 
 
 
 
 
 
210
  }
211
  }
212
  }
classes/class-exporter.php CHANGED
@@ -23,7 +23,7 @@ abstract class Exporter {
23
  * @param array $columns Column names included in data set.
24
  * @return void
25
  */
26
- public abstract function output_file( $data, $columns );
27
 
28
  /**
29
  * Allow connectors to determine if their dependencies is satisfied or not
23
  * @param array $columns Column names included in data set.
24
  * @return void
25
  */
26
+ abstract public function output_file( $data, $columns );
27
 
28
  /**
29
  * Allow connectors to determine if their dependencies is satisfied or not
classes/class-form-generator.php CHANGED
@@ -64,15 +64,17 @@ class Form_Generator {
64
  * @return string
65
  */
66
  public function render_field( $field_type, $args ) {
67
- $args = wp_parse_args( $args, array(
68
- 'name' => '',
69
- 'value' => '',
70
- 'options' => array(),
71
- 'description' => '',
72
- 'classes' => '',
73
- 'data' => array(),
74
- 'multiple' => false,
75
- ) );
 
 
76
 
77
  $output = '';
78
  switch ( $field_type ) {
@@ -95,7 +97,7 @@ class Form_Generator {
95
  case 'select':
96
  $current_value = $args['value'];
97
 
98
- $output = sprintf(
99
  '<select name="%1$s" class="%2$s" id="%1$s">',
100
  esc_attr( $args['name'] ),
101
  esc_attr( $args['classes'] )
@@ -115,7 +117,7 @@ class Form_Generator {
115
  $values = array();
116
 
117
  $multiple = ( $args['multiple'] ) ? 'multiple ' : '';
118
- $output = sprintf(
119
  '<select name="%1$s" id="%1$s" class="select2-select %2$s" %3$s%4$s>',
120
  esc_attr( $args['name'] ),
121
  esc_attr( $args['classes'] ),
@@ -128,11 +130,13 @@ class Form_Generator {
128
  }
129
 
130
  foreach ( $args['options'] as $parent ) {
131
- $parent = wp_parse_args( $parent, array(
132
- 'value' => '',
133
- 'text' => '',
134
- 'children' => array(),
135
- ) );
 
 
136
  if ( empty( $parent['value'] ) ) {
137
  continue;
138
  }
@@ -141,7 +145,7 @@ class Form_Generator {
141
  } else {
142
  $selected = selected( $args['value'], $parent['value'], false );
143
  }
144
- $output .= sprintf(
145
  '<option class="parent" value="%1$s" %3$s>%2$s</option>',
146
  $parent['value'],
147
  $parent['text'],
@@ -150,7 +154,7 @@ class Form_Generator {
150
  $values[] = $parent['value'];
151
  if ( ! empty( $parent['children'] ) ) {
152
  foreach ( $parent['children'] as $child ) {
153
- $output .= sprintf(
154
  '<option class="child" value="%1$s" %3$s>%2$s</option>',
155
  $child['value'],
156
  $child['text'],
@@ -188,7 +192,7 @@ class Form_Generator {
188
  break;
189
  }
190
 
191
- $output .= ! empty( $args['description'] ) ? wp_kses_post( sprintf( '<p class="description">%s</p>', $args['description'] ) ) : null;
192
 
193
  return $output;
194
  }
@@ -202,7 +206,7 @@ class Form_Generator {
202
  public function prepare_data_attributes_string( $data ) {
203
  $output = '';
204
  foreach ( $data as $key => $value ) {
205
- $key = 'data-' . esc_attr( $key );
206
  $output .= $key . '="' . esc_attr( $value ) . '" ';
207
  }
208
  return $output;
64
  * @return string
65
  */
66
  public function render_field( $field_type, $args ) {
67
+ $args = wp_parse_args(
68
+ $args, array(
69
+ 'name' => '',
70
+ 'value' => '',
71
+ 'options' => array(),
72
+ 'description' => '',
73
+ 'classes' => '',
74
+ 'data' => array(),
75
+ 'multiple' => false,
76
+ )
77
+ );
78
 
79
  $output = '';
80
  switch ( $field_type ) {
97
  case 'select':
98
  $current_value = $args['value'];
99
 
100
+ $output = sprintf(
101
  '<select name="%1$s" class="%2$s" id="%1$s">',
102
  esc_attr( $args['name'] ),
103
  esc_attr( $args['classes'] )
117
  $values = array();
118
 
119
  $multiple = ( $args['multiple'] ) ? 'multiple ' : '';
120
+ $output = sprintf(
121
  '<select name="%1$s" id="%1$s" class="select2-select %2$s" %3$s%4$s>',
122
  esc_attr( $args['name'] ),
123
  esc_attr( $args['classes'] ),
130
  }
131
 
132
  foreach ( $args['options'] as $parent ) {
133
+ $parent = wp_parse_args(
134
+ $parent, array(
135
+ 'value' => '',
136
+ 'text' => '',
137
+ 'children' => array(),
138
+ )
139
+ );
140
  if ( empty( $parent['value'] ) ) {
141
  continue;
142
  }
145
  } else {
146
  $selected = selected( $args['value'], $parent['value'], false );
147
  }
148
+ $output .= sprintf(
149
  '<option class="parent" value="%1$s" %3$s>%2$s</option>',
150
  $parent['value'],
151
  $parent['text'],
154
  $values[] = $parent['value'];
155
  if ( ! empty( $parent['children'] ) ) {
156
  foreach ( $parent['children'] as $child ) {
157
+ $output .= sprintf(
158
  '<option class="child" value="%1$s" %3$s>%2$s</option>',
159
  $child['value'],
160
  $child['text'],
192
  break;
193
  }
194
 
195
+ $output .= ! empty( $args['description'] ) ? sprintf( '<p class="description">%s</p>', $args['description'] ) : null;
196
 
197
  return $output;
198
  }
206
  public function prepare_data_attributes_string( $data ) {
207
  $output = '';
208
  foreach ( $data as $key => $value ) {
209
+ $key = 'data-' . esc_attr( $key );
210
  $output .= $key . '="' . esc_attr( $value ) . '" ';
211
  }
212
  return $output;
classes/class-install.php CHANGED
@@ -82,7 +82,6 @@ class Install {
82
 
83
  if ( empty( $this->db_version ) ) {
84
  $this->install( $this->plugin->get_version() );
85
-
86
  return;
87
  }
88
 
@@ -90,15 +89,26 @@ class Install {
90
  return;
91
  }
92
 
93
- $update = isset( $_REQUEST['wp_stream_update'] ) ? $_REQUEST['wp_stream_update'] : null;
 
 
 
94
 
95
  if ( ! $update ) {
96
  $this->update_required = true;
97
- $this->success_db = $this->update( $this->db_version, $this->plugin->get_version(), array( 'type' => 'auto' ) );
 
 
 
 
98
  }
99
 
100
  if ( 'update_and_continue' === $update ) {
101
- $this->success_db = $this->update( $this->db_version, $this->plugin->get_version(), array( 'type' => 'user' ) );
 
 
 
 
102
  }
103
 
104
  $versions = $this->db_update_versions();
@@ -129,7 +139,7 @@ class Install {
129
  }
130
 
131
  if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
132
- require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
133
  }
134
 
135
  /**
@@ -168,9 +178,19 @@ class Install {
168
  }
169
 
170
  if ( is_plugin_active_for_network( $this->plugin->locations['plugin'] ) && current_user_can( 'manage_network_plugins' ) ) {
171
- $uninstall_message = sprintf( __( 'Please <a href="%s">uninstall</a> the Stream plugin and activate it again.', 'stream' ), network_admin_url( 'plugins.php#stream' ) );
 
 
 
 
 
172
  } elseif ( current_user_can( 'activate_plugins' ) ) {
173
- $uninstall_message = sprintf( __( 'Please <a href="%s">uninstall</a> the Stream plugin and activate it again.', 'stream' ), admin_url( 'plugins.php#stream' ) );
 
 
 
 
 
174
  }
175
 
176
  if ( ! empty( $database_message ) ) {
@@ -220,8 +240,6 @@ class Install {
220
  } else {
221
  update_option( $this->option_key . '_registered_connectors', $current_versions );
222
  }
223
-
224
- return;
225
  }
226
 
227
  /**
@@ -265,7 +283,10 @@ class Install {
265
  return;
266
  }
267
 
268
- $update = isset( $_REQUEST['wp_stream_update'] ) ? $_REQUEST['wp_stream_update'] : null;
 
 
 
269
 
270
  if ( ! $update ) {
271
  $this->prompt_update();
@@ -315,7 +336,16 @@ class Install {
315
  <div class="updated">
316
  <form method="post" action="<?php echo esc_url( remove_query_arg( 'wp_stream_update' ) ); ?>" style="display:inline;">
317
  <p><strong><?php esc_html_e( 'Update Complete', 'stream' ); ?></strong></p>
318
- <p><?php printf( esc_html__( 'Your Stream database has been successfully updated from %1$s to %2$s!', 'stream' ), esc_html( $this->db_version ), esc_html( $this->plugin->get_version() ) ); ?></p>
 
 
 
 
 
 
 
 
 
319
  <?php submit_button( esc_html__( 'Continue', 'stream' ), 'secondary', false ); ?>
320
  </form>
321
  </div>
@@ -334,9 +364,9 @@ class Install {
334
  */
335
  public function db_update_versions() {
336
  $db_update_versions = array(
337
- '3.0.0' /* @version 3.0.0 Drop the stream_context table, changes to stream table */,
338
- '3.0.2' /* @version 3.0.2 Fix uppercase values in stream table, connector column */,
339
- '3.0.8' /* @version 3.0.8 Increase size of user role IDs, user_roll column */,
340
  );
341
 
342
  /**
@@ -360,7 +390,7 @@ class Install {
360
  */
361
  public function update( $db_version, $current_version, $update_args ) {
362
  $versions = $this->db_update_versions();
363
- include_once( $this->plugin->locations['inc_dir'] . 'db-updates.php' );
364
 
365
  foreach ( $versions as $version ) {
366
  if ( ! isset( $update_args['type'] ) ) {
82
 
83
  if ( empty( $this->db_version ) ) {
84
  $this->install( $this->plugin->get_version() );
 
85
  return;
86
  }
87
 
89
  return;
90
  }
91
 
92
+ $update = null;
93
+ if ( isset( $_REQUEST['wp_stream_update'] ) && wp_verify_nonce( 'wp_stream_update_db' ) ) {
94
+ $update = esc_attr( $_REQUEST['wp_stream_update'] );
95
+ }
96
 
97
  if ( ! $update ) {
98
  $this->update_required = true;
99
+ $this->success_db = $this->update(
100
+ $this->db_version, $this->plugin->get_version(), array(
101
+ 'type' => 'auto',
102
+ )
103
+ );
104
  }
105
 
106
  if ( 'update_and_continue' === $update ) {
107
+ $this->success_db = $this->update(
108
+ $this->db_version, $this->plugin->get_version(), array(
109
+ 'type' => 'user',
110
+ )
111
+ );
112
  }
113
 
114
  $versions = $this->db_update_versions();
139
  }
140
 
141
  if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
142
+ require_once ABSPATH . '/wp-admin/includes/plugin.php';
143
  }
144
 
145
  /**
178
  }
179
 
180
  if ( is_plugin_active_for_network( $this->plugin->locations['plugin'] ) && current_user_can( 'manage_network_plugins' ) ) {
181
+ $uninstall_message = sprintf(
182
+ // translators: Placeholders refer to HTML Link tags (e.g. "<a href="https://foo.com/wp-admin/">")
183
+ __( 'Please %1$suninstall%2$s the Stream plugin and activate it again.', 'stream' ),
184
+ '<a href="' . network_admin_url( 'plugins.php#stream' ) . '">',
185
+ '</a>'
186
+ );
187
  } elseif ( current_user_can( 'activate_plugins' ) ) {
188
+ $uninstall_message = sprintf(
189
+ // translators: Placeholders refer to HTML Link tags (e.g. "<a href="https://foo.com/wp-admin/">")
190
+ __( 'Please %1$suninstall%2$s the Stream plugin and activate it again.', 'stream' ),
191
+ '<a href="' . admin_url( 'plugins.php#stream' ) . '">',
192
+ '</a>'
193
+ );
194
  }
195
 
196
  if ( ! empty( $database_message ) ) {
240
  } else {
241
  update_option( $this->option_key . '_registered_connectors', $current_versions );
242
  }
 
 
243
  }
244
 
245
  /**
283
  return;
284
  }
285
 
286
+ $update = null;
287
+ if ( isset( $_REQUEST['wp_stream_update'] ) && wp_verify_nonce( 'wp_stream_update_db' ) ) {
288
+ $update = esc_attr( $_REQUEST['wp_stream_update'] );
289
+ }
290
 
291
  if ( ! $update ) {
292
  $this->prompt_update();
336
  <div class="updated">
337
  <form method="post" action="<?php echo esc_url( remove_query_arg( 'wp_stream_update' ) ); ?>" style="display:inline;">
338
  <p><strong><?php esc_html_e( 'Update Complete', 'stream' ); ?></strong></p>
339
+ <p>
340
+ <?php
341
+ printf(
342
+ // translators: Placeholders refer to version numbers (e.g. "4.2")
343
+ esc_html__( 'Your Stream database has been successfully updated from %1$s to %2$s!', 'stream' ),
344
+ esc_html( $this->db_version ),
345
+ esc_html( $this->plugin->get_version() )
346
+ );
347
+ ?>
348
+ </p>
349
  <?php submit_button( esc_html__( 'Continue', 'stream' ), 'secondary', false ); ?>
350
  </form>
351
  </div>
364
  */
365
  public function db_update_versions() {
366
  $db_update_versions = array(
367
+ '3.0.0', /* @version 3.0.0 Drop the stream_context table, changes to stream table */
368
+ '3.0.2', /* @version 3.0.2 Fix uppercase values in stream table, connector column */
369
+ '3.0.8', /* @version 3.0.8 Increase size of user role IDs, user_roll column */
370
  );
371
 
372
  /**
390
  */
391
  public function update( $db_version, $current_version, $update_args ) {
392
  $versions = $this->db_update_versions();
393
+ include_once $this->plugin->locations['inc_dir'] . 'db-updates.php';
394
 
395
  foreach ( $versions as $version ) {
396
  if ( ! isset( $update_args['type'] ) ) {
classes/class-list-table.php CHANGED
@@ -1,7 +1,9 @@
1
  <?php
 
2
  namespace WP_Stream;
3
 
4
  class List_Table extends \WP_List_Table {
 
5
  /**
6
  * Hold Plugin class
7
  *
@@ -15,7 +17,7 @@ class List_Table extends \WP_List_Table {
15
  * @param Plugin $plugin The main Plugin class.
16
  * @param array $args
17
  */
18
- function __construct( $plugin, $args = array() ) {
19
  $this->plugin = $plugin;
20
 
21
  $screen_id = isset( $args['screen'] ) ? $args['screen'] : null;
@@ -47,19 +49,25 @@ class List_Table extends \WP_List_Table {
47
  // Check for default hidden columns
48
  $this->get_hidden_columns();
49
 
50
- add_filter( 'screen_settings', array( $this, 'screen_controls' ), 10, 2 );
51
- add_filter( 'set-screen-option', array( $this, 'set_screen_option' ), 10, 3 );
 
 
 
 
 
 
52
 
53
  set_screen_options();
54
  }
55
 
56
- function extra_tablenav( $which ) {
57
  if ( 'top' === $which ) {
58
  echo $this->filters_form(); // xss ok
59
  }
60
  }
61
 
62
- function no_items() {
63
  ?>
64
  <div class="stream-list-table-no-items">
65
  <p><?php esc_html_e( 'Sorry, no activity records were found.', 'stream' ); ?></p>
@@ -67,7 +75,7 @@ class List_Table extends \WP_List_Table {
67
  <?php
68
  }
69
 
70
- function get_columns() {
71
  /**
72
  * Allows devs to add new columns to table
73
  *
@@ -86,14 +94,15 @@ class List_Table extends \WP_List_Table {
86
  );
87
  }
88
 
89
- function get_sortable_columns() {
90
  return array(
91
  'date' => array( 'date', false ),
92
  );
93
  }
94
 
95
- function get_hidden_columns() {
96
- if ( ! $user = wp_get_current_user() ) {
 
97
  return array();
98
  }
99
 
@@ -109,13 +118,18 @@ class List_Table extends \WP_List_Table {
109
  return $hidden;
110
  }
111
 
112
- function prepare_items() {
113
  $columns = $this->get_columns();
114
  $sortable = $this->get_sortable_columns();
115
  $hidden = $this->get_hidden_columns();
116
  $primary = $columns['summary'];
117
 
118
- $this->_column_headers = array( $columns, $hidden, $sortable, $primary );
 
 
 
 
 
119
 
120
  $this->items = $this->get_records();
121
 
@@ -129,15 +143,17 @@ class List_Table extends \WP_List_Table {
129
  );
130
  }
131
 
132
- function get_records() {
133
  $args = array();
134
 
135
  // Parse sorting params
136
- if ( $order = wp_stream_filter_input( INPUT_GET, 'order' ) ) {
 
137
  $args['order'] = $order;
138
  }
139
 
140
- if ( $orderby = wp_stream_filter_input( INPUT_GET, 'orderby' ) ) {
 
141
  $args['orderby'] = $orderby;
142
  }
143
 
@@ -207,6 +223,7 @@ class List_Table extends \WP_List_Table {
207
  $args['records_per_page'] = apply_filters( 'stream_records_per_page', $args['records_per_page'] );
208
 
209
  $items = $this->plugin->db->get_records( $args );
 
210
  return $items;
211
  }
212
 
@@ -219,26 +236,27 @@ class List_Table extends \WP_List_Table {
219
  return $this->plugin->db->get_found_records_count();
220
  }
221
 
222
- function column_default( $item, $column_name ) {
223
- $out = '';
224
  $record = new Record( $item );
225
 
226
  switch ( $column_name ) {
227
- case 'date' :
228
  $created = date( 'Y-m-d H:i:s', strtotime( $record->created ) );
229
  $date_string = sprintf(
230
  '<time datetime="%s" class="relative-time record-created">%s</time>',
231
  wp_stream_get_iso_8601_extended_date( strtotime( $record->created ) ),
232
  get_date_from_gmt( $created, 'Y/m/d' )
233
  );
234
- $out = $this->column_link( $date_string, 'date', get_date_from_gmt( $created, 'Y/m/d' ) );
235
- $out .= '<br />';
236
- $out .= get_date_from_gmt( $created, 'h:i:s A' );
237
  break;
238
 
239
- case 'summary' :
240
- $out = $record->summary;
241
- $object_title = $record->get_object_title();
 
242
  $view_all_text = $object_title ? sprintf( esc_html__( 'View all activity for "%s"', 'stream' ), esc_attr( $object_title ) ) : esc_html__( 'View all activity for this object', 'stream' );
243
 
244
  if ( $record->object_id ) {
@@ -255,7 +273,7 @@ class List_Table extends \WP_List_Table {
255
  $out .= $this->get_action_links( $record );
256
  break;
257
 
258
- case 'user_id' :
259
  $user = new Author( (int) $record->user_id, (array) $record->user_meta );
260
 
261
  $filtered_records_url = add_query_arg(
@@ -301,18 +319,19 @@ class List_Table extends \WP_List_Table {
301
  $out = $this->column_link( $blog->blogname, 'blog_id', $blog->blog_id );
302
  break;
303
 
304
- case 'ip' :
305
  $out = $this->column_link( $record->{$column_name}, 'ip', $record->{$column_name} );
306
  break;
307
 
308
- default :
309
  /**
310
  * Registers new Columns to be inserted into the table. The cell contents of this column is set
311
  * below with 'wp_stream_insert_column_default_'
312
  *
313
  * @return array
314
  */
315
- $inserted_columns = apply_filters( 'wp_stream_register_column_defaults', $new_columns = array() );
 
316
 
317
  if ( ! empty( $inserted_columns ) && is_array( $inserted_columns ) ) {
318
  foreach ( $inserted_columns as $column_title ) {
@@ -328,7 +347,7 @@ class List_Table extends \WP_List_Table {
328
  /**
329
  * Allows for the addition of content under a specified column.
330
  *
331
- * @param object $record Contents of the row
332
  *
333
  * @return string
334
  */
@@ -342,8 +361,11 @@ class List_Table extends \WP_List_Table {
342
  }
343
  }
344
 
345
- $allowed_tags = wp_kses_allowed_html( 'post' );
346
- $allowed_tags['time'] = array( 'datetime' => true, 'class' => true );
 
 
 
347
  $allowed_tags['img']['srcset'] = true;
348
 
349
  echo wp_kses( $out, $allowed_tags );
@@ -402,7 +424,7 @@ class List_Table extends \WP_List_Table {
402
  return $out;
403
  }
404
 
405
- function column_link( $display, $key, $value = null, $title = null ) {
406
  $url = add_query_arg(
407
  array(
408
  'page' => $this->plugin->admin->records_page_slug,
@@ -410,7 +432,9 @@ class List_Table extends \WP_List_Table {
410
  self_admin_url( $this->plugin->admin->admin_parent_page )
411
  );
412
 
413
- $args = ! is_array( $key ) ? array( $key => $value ) : $key;
 
 
414
 
415
  foreach ( $args as $k => $v ) {
416
  $url = add_query_arg( $k, $v, $url );
@@ -425,11 +449,11 @@ class List_Table extends \WP_List_Table {
425
  }
426
 
427
  public function get_term_title( $term, $type ) {
428
- if ( ! isset( $this->plugin->connectors->term_labels[ "stream_$type" ][ $term ] ) ) {
429
  return $term;
430
  }
431
 
432
- return $this->plugin->connectors->term_labels[ "stream_$type" ][ $term ];
433
  }
434
 
435
  /**
@@ -443,7 +467,7 @@ class List_Table extends \WP_List_Table {
443
  *
444
  * @return array Options to be displayed in search filters
445
  */
446
- function assemble_records( $column ) {
447
  // @todo eliminate special condition for authors, especially using a WP_User object as the value; should use string or stringifiable object
448
  if ( 'user_id' === $column ) {
449
  $all_records = array();
@@ -456,31 +480,43 @@ class List_Table extends \WP_List_Table {
456
  $selected_user = wp_stream_filter_input( INPUT_GET, 'user_id' );
457
  if ( $selected_user ) {
458
  $user = new Author( $selected_user );
459
- return array( $selected_user => $user->get_display_name() );
 
 
 
460
  } else {
461
  return array();
462
  }
463
  }
464
 
465
  $users = array_map(
466
- function( $user_id ) {
467
  return new Author( $user_id );
468
  },
469
- get_users( array( 'fields' => 'ID' ) )
 
 
 
 
470
  );
471
 
472
  if ( is_multisite() && is_super_admin() ) {
473
  $super_admins = array_map(
474
- function( $login ) {
475
  $user = get_user_by( 'login', $login );
 
476
  return new Author( $user->ID );
477
  },
478
  get_super_admins()
479
  );
480
- $users = array_unique( array_merge( $users, $super_admins ) );
481
  }
482
 
483
- $users[] = new Author( 0, array( 'is_wp_cli' => true ) );
 
 
 
 
484
 
485
  foreach ( $users as $user ) {
486
  $all_records[ $user->id ] = $user->get_display_name();
@@ -496,9 +532,15 @@ class List_Table extends \WP_List_Table {
496
 
497
  foreach ( $all_records as $record => $label ) {
498
  if ( array_key_exists( $record, $existing_records ) ) {
499
- $active_records[ $record ] = array( 'label' => $label, 'disabled' => '' );
 
 
 
500
  } else {
501
- $disabled_records[ $record ] = array( 'label' => $label, 'disabled' => 'disabled="disabled"' );
 
 
 
502
  }
503
  }
504
 
@@ -507,7 +549,7 @@ class List_Table extends \WP_List_Table {
507
  unset( $disabled_records[0] );
508
  }
509
 
510
- $sort = function( $a, $b ) use ( $column ) {
511
  $label_a = (string) $a['label'];
512
  $label_b = (string) $b['label'];
513
 
@@ -515,7 +557,7 @@ class List_Table extends \WP_List_Table {
515
  return 0;
516
  }
517
 
518
- return ( strtolower( $label_a ) < strtolower( $label_b ) ) ? -1 : 1;
519
  };
520
 
521
  uasort( $active_records, $sort );
@@ -567,7 +609,7 @@ class List_Table extends \WP_List_Table {
567
  return apply_filters( 'wp_stream_list_table_filters', $filters );
568
  }
569
 
570
- function filters_form() {
571
  $filters = $this->get_filters();
572
 
573
  $filters_string = sprintf( '<input type="hidden" name="page" value="%s" />', 'wp_stream' );
@@ -575,11 +617,13 @@ class List_Table extends \WP_List_Table {
575
 
576
  foreach ( $filters as $name => $data ) {
577
 
578
- $data = wp_parse_args( $data, array(
579
- 'title' => '',
580
- 'items' => array(),
581
- 'ajax' => false,
582
- ) );
 
 
583
 
584
  if ( 'date' === $name ) {
585
  $filters_string .= $this->filter_date( $data['items'] );
@@ -661,7 +705,7 @@ class List_Table extends \WP_List_Table {
661
  return sprintf( '<div class="alignleft actions">%s</div>', $filters_string ); // xss ok
662
  }
663
 
664
- function filter_select( $name, $title, $items, $ajax = false ) {
665
  if ( $ajax ) {
666
  $out = sprintf(
667
  '<input type="hidden" name="%s" class="chosen-select" value="%s" data-placeholder="%s" />',
@@ -685,11 +729,11 @@ class List_Table extends \WP_List_Table {
685
  'class' => isset( $item['children'] ) ? 'level-1' : null,
686
  'label' => isset( $item['label'] ) ? $item['label'] : null,
687
  );
688
- $options[] = $this->filter_option( $option_args );
689
 
690
  if ( isset( $item['children'] ) ) {
691
  foreach ( $item['children'] as $child_value => $child_item ) {
692
- $option_args = array(
693
  'value' => $child_value,
694
  'selected' => selected( $child_value, $selected, false ),
695
  'disabled' => isset( $child_item['disabled'] ) ? $child_item['disabled'] : null,
@@ -699,13 +743,14 @@ class List_Table extends \WP_List_Table {
699
  'class' => 'level-2',
700
  'label' => isset( $child_item['label'] ) ? '- ' . $child_item['label'] : null,
701
  );
702
- $options[] = $this->filter_option( $option_args );
703
  }
704
  }
705
  }
706
  $out = sprintf(
707
  '<select name="%s" class="chosen-select" data-placeholder="%s">%s</select>',
708
  esc_attr( $name ),
 
709
  sprintf( esc_attr__( 'Show all %s', 'stream' ), $title ),
710
  implode( '', $options )
711
  );
@@ -714,7 +759,7 @@ class List_Table extends \WP_List_Table {
714
  return $out;
715
  }
716
 
717
- function filter_option( $args ) {
718
  $defaults = array(
719
  'value' => null,
720
  'selected' => null,
@@ -740,11 +785,10 @@ class List_Table extends \WP_List_Table {
740
  );
741
  }
742
 
743
- function filter_search() {
744
  $search = null;
745
- if ( isset( $_GET['search'] ) ) {
746
- // @TODO: Make this pass phpcs
747
- $search = esc_attr( wp_unslash( $_GET['search'] ) ); // input var okay
748
  }
749
  $out = sprintf(
750
  '<p class="search-box">
@@ -759,7 +803,7 @@ class List_Table extends \WP_List_Table {
759
  return $out;
760
  }
761
 
762
- function filter_date( $items ) {
763
  wp_enqueue_style( 'jquery-ui' );
764
  wp_enqueue_style( 'wp-stream-datepicker' );
765
  wp_enqueue_script( 'jquery-ui-datepicker' );
@@ -794,13 +838,13 @@ class List_Table extends \WP_List_Table {
794
  <div class="date-inputs">
795
  <div class="box">
796
  <i class="date-remove dashicons"></i>
797
- <input type="text" name="date_from" class="date-picker field-from" placeholder="<?php esc_attr_e( 'Start Date', 'stream' ); ?>" value="<?php echo esc_attr( $date_from ); ?>" />
798
  </div>
799
  <span class="connector dashicons"></span>
800
 
801
  <div class="box">
802
  <i class="date-remove dashicons"></i>
803
- <input type="text" name="date_to" class="date-picker field-to" placeholder="<?php esc_attr_e( 'End Date', 'stream' ); ?>" value="<?php echo esc_attr( $date_to ); ?>" />
804
  </div>
805
  </div>
806
 
@@ -815,7 +859,7 @@ class List_Table extends \WP_List_Table {
815
  *
816
  * @return string
817
  */
818
- function record_actions_form() {
819
  /**
820
  * Filter the records screen actions dropdown menu
821
  *
@@ -855,7 +899,7 @@ class List_Table extends \WP_List_Table {
855
  return ob_get_clean();
856
  }
857
 
858
- function display() {
859
  $url = self_admin_url( $this->plugin->admin->admin_parent_page );
860
 
861
  echo '<form method="get" action="' . esc_url( $url ) . '" id="record-filter-form">';
@@ -868,7 +912,7 @@ class List_Table extends \WP_List_Table {
868
  echo '</form>';
869
  }
870
 
871
- function single_row( $item ) {
872
  $classes = apply_filters( 'wp_stream_record_classes', array(), $item );
873
  $class_string = '';
874
  if ( ! empty( $classes ) ) {
@@ -880,15 +924,16 @@ class List_Table extends \WP_List_Table {
880
  echo '</tr>';
881
  }
882
 
883
- function display_tablenav( $which ) {
884
- if ( 'top' === $which ) : ?>
 
885
  <div class="tablenav <?php echo esc_attr( $which ); ?>">
886
  <?php
887
  $this->pagination( $which );
888
  $this->extra_tablenav( $which );
889
  ?>
890
 
891
- <br class="clear" />
892
  </div>
893
  <?php else : ?>
894
  <div class="tablenav <?php echo esc_attr( $which ); ?>">
@@ -901,13 +946,13 @@ class List_Table extends \WP_List_Table {
901
  $this->extra_tablenav( $which );
902
  ?>
903
 
904
- <br class="clear" />
905
  </div>
906
- <?php
907
  endif;
908
  }
909
 
910
- function set_screen_option( $dummy, $option, $value ) {
911
  if ( 'edit_stream_per_page' === $option ) {
912
  return $value;
913
  } else {
@@ -915,7 +960,7 @@ class List_Table extends \WP_List_Table {
915
  }
916
  }
917
 
918
- function set_live_update_option( $dummy, $option, $value ) {
919
  unset( $value );
920
 
921
  // @codingStandardsIgnoreStart
@@ -928,6 +973,7 @@ class List_Table extends \WP_List_Table {
928
 
929
  return $value;
930
  }
 
931
  // @codingStandardsIgnoreEnd
932
 
933
  return $dummy;
@@ -955,15 +1001,16 @@ class List_Table extends \WP_List_Table {
955
  <h5><?php esc_html_e( 'Live updates', 'stream' ); ?></h5>
956
 
957
  <div>
958
- <input type="hidden" name="stream_live_update_nonce" id="stream_live_update_nonce" value="<?php echo esc_attr( $nonce ); ?>" />
959
  </div>
960
  <div>
961
- <input type="hidden" name="enable_live_update_user" id="enable_live_update_user" value="<?php echo absint( $user_id ); ?>" />
962
  </div>
963
  <div class="metabox-prefs stream-live-update-checkbox">
964
  <label for="enable_live_update">
965
  <input type="checkbox" value="on" name="enable_live_update" id="enable_live_update" data-heartbeat="<?php echo esc_attr( $heartbeat ); ?>" <?php checked( $option, 'on' ); ?> />
966
- <?php esc_html_e( 'Enabled', 'stream' ); ?><span class="spinner"></span>
 
967
  </label>
968
  </div>
969
  </fieldset>
@@ -978,7 +1025,7 @@ class List_Table extends \WP_List_Table {
978
  *
979
  * @return string setting name for that column
980
  */
981
- function get_column_excluded_setting_key( $column ) {
982
  switch ( $column ) {
983
  case 'connector':
984
  $output = 'connectors';
1
  <?php
2
+
3
  namespace WP_Stream;
4
 
5
  class List_Table extends \WP_List_Table {
6
+
7
  /**
8
  * Hold Plugin class
9
  *
17
  * @param Plugin $plugin The main Plugin class.
18
  * @param array $args
19
  */
20
+ public function __construct( $plugin, $args = array() ) {
21
  $this->plugin = $plugin;
22
 
23
  $screen_id = isset( $args['screen'] ) ? $args['screen'] : null;
49
  // Check for default hidden columns
50
  $this->get_hidden_columns();
51
 
52
+ add_filter( 'screen_settings', array(
53
+ $this,
54
+ 'screen_controls',
55
+ ), 10, 2 );
56
+ add_filter( 'set-screen-option', array(
57
+ $this,
58
+ 'set_screen_option',
59
+ ), 10, 3 );
60
 
61
  set_screen_options();
62
  }
63
 
64
+ public function extra_tablenav( $which ) {
65
  if ( 'top' === $which ) {
66
  echo $this->filters_form(); // xss ok
67
  }
68
  }
69
 
70
+ public function no_items() {
71
  ?>
72
  <div class="stream-list-table-no-items">
73
  <p><?php esc_html_e( 'Sorry, no activity records were found.', 'stream' ); ?></p>
75
  <?php
76
  }
77
 
78
+ public function get_columns() {
79
  /**
80
  * Allows devs to add new columns to table
81
  *
94
  );
95
  }
96
 
97
+ public function get_sortable_columns() {
98
  return array(
99
  'date' => array( 'date', false ),
100
  );
101
  }
102
 
103
+ public function get_hidden_columns() {
104
+ $user = wp_get_current_user();
105
+ if ( ! $user ) {
106
  return array();
107
  }
108
 
118
  return $hidden;
119
  }
120
 
121
+ public function prepare_items() {
122
  $columns = $this->get_columns();
123
  $sortable = $this->get_sortable_columns();
124
  $hidden = $this->get_hidden_columns();
125
  $primary = $columns['summary'];
126
 
127
+ $this->_column_headers = array(
128
+ $columns,
129
+ $hidden,
130
+ $sortable,
131
+ $primary,
132
+ );
133
 
134
  $this->items = $this->get_records();
135
 
143
  );
144
  }
145
 
146
+ public function get_records() {
147
  $args = array();
148
 
149
  // Parse sorting params
150
+ $order = wp_stream_filter_input( INPUT_GET, 'order' );
151
+ if ( $order ) {
152
  $args['order'] = $order;
153
  }
154
 
155
+ $orderby = wp_stream_filter_input( INPUT_GET, 'orderby' );
156
+ if ( $orderby ) {
157
  $args['orderby'] = $orderby;
158
  }
159
 
223
  $args['records_per_page'] = apply_filters( 'stream_records_per_page', $args['records_per_page'] );
224
 
225
  $items = $this->plugin->db->get_records( $args );
226
+
227
  return $items;
228
  }
229
 
236
  return $this->plugin->db->get_found_records_count();
237
  }
238
 
239
+ public function column_default( $item, $column_name ) {
240
+ $out = '';
241
  $record = new Record( $item );
242
 
243
  switch ( $column_name ) {
244
+ case 'date':
245
  $created = date( 'Y-m-d H:i:s', strtotime( $record->created ) );
246
  $date_string = sprintf(
247
  '<time datetime="%s" class="relative-time record-created">%s</time>',
248
  wp_stream_get_iso_8601_extended_date( strtotime( $record->created ) ),
249
  get_date_from_gmt( $created, 'Y/m/d' )
250
  );
251
+ $out = $this->column_link( $date_string, 'date', get_date_from_gmt( $created, 'Y/m/d' ) );
252
+ $out .= '<br />';
253
+ $out .= get_date_from_gmt( $created, 'h:i:s A' );
254
  break;
255
 
256
+ case 'summary':
257
+ $out = $record->summary;
258
+ $object_title = $record->get_object_title();
259
+ // translators: Placeholder refers to the title of any object, like a Post (e.g. "Hello World")
260
  $view_all_text = $object_title ? sprintf( esc_html__( 'View all activity for "%s"', 'stream' ), esc_attr( $object_title ) ) : esc_html__( 'View all activity for this object', 'stream' );
261
 
262
  if ( $record->object_id ) {
273
  $out .= $this->get_action_links( $record );
274
  break;
275
 
276
+ case 'user_id':
277
  $user = new Author( (int) $record->user_id, (array) $record->user_meta );
278
 
279
  $filtered_records_url = add_query_arg(
319
  $out = $this->column_link( $blog->blogname, 'blog_id', $blog->blog_id );
320
  break;
321
 
322
+ case 'ip':
323
  $out = $this->column_link( $record->{$column_name}, 'ip', $record->{$column_name} );
324
  break;
325
 
326
+ default:
327
  /**
328
  * Registers new Columns to be inserted into the table. The cell contents of this column is set
329
  * below with 'wp_stream_insert_column_default_'
330
  *
331
  * @return array
332
  */
333
+ $new_columns = array();
334
+ $inserted_columns = apply_filters( 'wp_stream_register_column_defaults', $new_columns );
335
 
336
  if ( ! empty( $inserted_columns ) && is_array( $inserted_columns ) ) {
337
  foreach ( $inserted_columns as $column_title ) {
347
  /**
348
  * Allows for the addition of content under a specified column.
349
  *
350
+ * @param object $record Contents of the row
351
  *
352
  * @return string
353
  */
361
  }
362
  }
363
 
364
+ $allowed_tags = wp_kses_allowed_html( 'post' );
365
+ $allowed_tags['time'] = array(
366
+ 'datetime' => true,
367
+ 'class' => true,
368
+ );
369
  $allowed_tags['img']['srcset'] = true;
370
 
371
  echo wp_kses( $out, $allowed_tags );
424
  return $out;
425
  }
426
 
427
+ public function column_link( $display, $key, $value = null, $title = null ) {
428
  $url = add_query_arg(
429
  array(
430
  'page' => $this->plugin->admin->records_page_slug,
432
  self_admin_url( $this->plugin->admin->admin_parent_page )
433
  );
434
 
435
+ $args = ! is_array( $key ) ? array(
436
+ $key => $value,
437
+ ) : $key;
438
 
439
  foreach ( $args as $k => $v ) {
440
  $url = add_query_arg( $k, $v, $url );
449
  }
450
 
451
  public function get_term_title( $term, $type ) {
452
+ if ( ! isset( $this->plugin->connectors->term_labels[ 'stream_' . $type ][ $term ] ) ) {
453
  return $term;
454
  }
455
 
456
+ return $this->plugin->connectors->term_labels[ 'stream_' . $type ][ $term ];
457
  }
458
 
459
  /**
467
  *
468
  * @return array Options to be displayed in search filters
469
  */
470
+ public function assemble_records( $column ) {
471
  // @todo eliminate special condition for authors, especially using a WP_User object as the value; should use string or stringifiable object
472
  if ( 'user_id' === $column ) {
473
  $all_records = array();
480
  $selected_user = wp_stream_filter_input( INPUT_GET, 'user_id' );
481
  if ( $selected_user ) {
482
  $user = new Author( $selected_user );
483
+
484
+ return array(
485
+ $selected_user => $user->get_display_name(),
486
+ );
487
  } else {
488
  return array();
489
  }
490
  }
491
 
492
  $users = array_map(
493
+ function ( $user_id ) {
494
  return new Author( $user_id );
495
  },
496
+ get_users(
497
+ array(
498
+ 'fields' => 'ID',
499
+ )
500
+ )
501
  );
502
 
503
  if ( is_multisite() && is_super_admin() ) {
504
  $super_admins = array_map(
505
+ function ( $login ) {
506
  $user = get_user_by( 'login', $login );
507
+
508
  return new Author( $user->ID );
509
  },
510
  get_super_admins()
511
  );
512
+ $users = array_unique( array_merge( $users, $super_admins ) );
513
  }
514
 
515
+ $users[] = new Author(
516
+ 0, array(
517
+ 'is_wp_cli' => true,
518
+ )
519
+ );
520
 
521
  foreach ( $users as $user ) {
522
  $all_records[ $user->id ] = $user->get_display_name();
532
 
533
  foreach ( $all_records as $record => $label ) {
534
  if ( array_key_exists( $record, $existing_records ) ) {
535
+ $active_records[ $record ] = array(
536
+ 'label' => $label,
537
+ 'disabled' => '',
538
+ );
539
  } else {
540
+ $disabled_records[ $record ] = array(
541
+ 'label' => $label,
542
+ 'disabled' => 'disabled="disabled"',
543
+ );
544
  }
545
  }
546
 
549
  unset( $disabled_records[0] );
550
  }
551
 
552
+ $sort = function ( $a, $b ) use ( $column ) {
553
  $label_a = (string) $a['label'];
554
  $label_b = (string) $b['label'];
555
 
557
  return 0;
558
  }
559
 
560
+ return ( strtolower( $label_a ) < strtolower( $label_b ) ) ? - 1 : 1;
561
  };
562
 
563
  uasort( $active_records, $sort );
609
  return apply_filters( 'wp_stream_list_table_filters', $filters );
610
  }
611
 
612
+ public function filters_form() {
613
  $filters = $this->get_filters();
614
 
615
  $filters_string = sprintf( '<input type="hidden" name="page" value="%s" />', 'wp_stream' );
617
 
618
  foreach ( $filters as $name => $data ) {
619
 
620
+ $data = wp_parse_args(
621
+ $data, array(
622
+ 'title' => '',
623
+ 'items' => array(),
624
+ 'ajax' => false,
625
+ )
626
+ );
627
 
628
  if ( 'date' === $name ) {
629
  $filters_string .= $this->filter_date( $data['items'] );
705
  return sprintf( '<div class="alignleft actions">%s</div>', $filters_string ); // xss ok
706
  }
707
 
708
+ public function filter_select( $name, $title, $items, $ajax = false ) {
709
  if ( $ajax ) {
710
  $out = sprintf(
711
  '<input type="hidden" name="%s" class="chosen-select" value="%s" data-placeholder="%s" />',
729
  'class' => isset( $item['children'] ) ? 'level-1' : null,
730
  'label' => isset( $item['label'] ) ? $item['label'] : null,
731
  );
732
+ $options[] = $this->filter_option( $option_args );
733
 
734
  if ( isset( $item['children'] ) ) {
735
  foreach ( $item['children'] as $child_value => $child_item ) {
736
+ $option_args = array(
737
  'value' => $child_value,
738
  'selected' => selected( $child_value, $selected, false ),
739
  'disabled' => isset( $child_item['disabled'] ) ? $child_item['disabled'] : null,
743
  'class' => 'level-2',
744
  'label' => isset( $child_item['label'] ) ? '- ' . $child_item['label'] : null,
745
  );
746
+ $options[] = $this->filter_option( $option_args );
747
  }
748
  }
749
  }
750
  $out = sprintf(
751
  '<select name="%s" class="chosen-select" data-placeholder="%s">%s</select>',
752
  esc_attr( $name ),
753
+ // translators: Placeholder refers to the title of the dropdown menu (e.g. "users")
754
  sprintf( esc_attr__( 'Show all %s', 'stream' ), $title ),
755
  implode( '', $options )
756
  );
759
  return $out;
760
  }
761
 
762
+ public function filter_option( $args ) {
763
  $defaults = array(
764
  'value' => null,
765
  'selected' => null,
785
  );
786
  }
787
 
788
+ public function filter_search() {
789
  $search = null;
790
+ if ( isset( $_GET['search'] ) ) { // CSRF okay
791
+ $search = esc_attr( wp_unslash( $_GET['search'] ) ); // input var okay, CSRF okay
 
792
  }
793
  $out = sprintf(
794
  '<p class="search-box">
803
  return $out;
804
  }
805
 
806
+ public function filter_date( $items ) {
807
  wp_enqueue_style( 'jquery-ui' );
808
  wp_enqueue_style( 'wp-stream-datepicker' );
809
  wp_enqueue_script( 'jquery-ui-datepicker' );
838
  <div class="date-inputs">
839
  <div class="box">
840
  <i class="date-remove dashicons"></i>
841
+ <input type="text" name="date_from" class="date-picker field-from" placeholder="<?php esc_attr_e( 'Start Date', 'stream' ); ?>" value="<?php echo esc_attr( $date_from ); ?>"/>
842
  </div>
843
  <span class="connector dashicons"></span>
844
 
845
  <div class="box">
846
  <i class="date-remove dashicons"></i>
847
+ <input type="text" name="date_to" class="date-picker field-to" placeholder="<?php esc_attr_e( 'End Date', 'stream' ); ?>" value="<?php echo esc_attr( $date_to ); ?>"/>
848
  </div>
849
  </div>
850
 
859
  *
860
  * @return string
861
  */
862
+ public function record_actions_form() {
863
  /**
864
  * Filter the records screen actions dropdown menu
865
  *
899
  return ob_get_clean();
900
  }
901
 
902
+ public function display() {
903
  $url = self_admin_url( $this->plugin->admin->admin_parent_page );
904
 
905
  echo '<form method="get" action="' . esc_url( $url ) . '" id="record-filter-form">';
912
  echo '</form>';
913
  }
914
 
915
+ public function single_row( $item ) {
916
  $classes = apply_filters( 'wp_stream_record_classes', array(), $item );
917
  $class_string = '';
918
  if ( ! empty( $classes ) ) {
924
  echo '</tr>';
925
  }
926
 
927
+ public function display_tablenav( $which ) {
928
+ if ( 'top' === $which ) :
929
+ ?>
930
  <div class="tablenav <?php echo esc_attr( $which ); ?>">
931
  <?php
932
  $this->pagination( $which );
933
  $this->extra_tablenav( $which );
934
  ?>
935
 
936
+ <br class="clear"/>
937
  </div>
938
  <?php else : ?>
939
  <div class="tablenav <?php echo esc_attr( $which ); ?>">
946
  $this->extra_tablenav( $which );
947
  ?>
948
 
949
+ <br class="clear"/>
950
  </div>
951
+ <?php
952
  endif;
953
  }
954
 
955
+ public function set_screen_option( $dummy, $option, $value ) {
956
  if ( 'edit_stream_per_page' === $option ) {
957
  return $value;
958
  } else {
960
  }
961
  }
962
 
963
+ public function set_live_update_option( $dummy, $option, $value ) {
964
  unset( $value );
965
 
966
  // @codingStandardsIgnoreStart
973
 
974
  return $value;
975
  }
976
+
977
  // @codingStandardsIgnoreEnd
978
 
979
  return $dummy;
1001
  <h5><?php esc_html_e( 'Live updates', 'stream' ); ?></h5>
1002
 
1003
  <div>
1004
+ <input type="hidden" name="stream_live_update_nonce" id="stream_live_update_nonce" value="<?php echo esc_attr( $nonce ); ?>"/>
1005
  </div>
1006
  <div>
1007
+ <input type="hidden" name="enable_live_update_user" id="enable_live_update_user" value="<?php echo absint( $user_id ); ?>"/>
1008
  </div>
1009
  <div class="metabox-prefs stream-live-update-checkbox">
1010
  <label for="enable_live_update">
1011
  <input type="checkbox" value="on" name="enable_live_update" id="enable_live_update" data-heartbeat="<?php echo esc_attr( $heartbeat ); ?>" <?php checked( $option, 'on' ); ?> />
1012
+ <?php esc_html_e( 'Enabled', 'stream' ); ?>
1013
+ <span class="spinner"></span>
1014
  </label>
1015
  </div>
1016
  </fieldset>
1025
  *
1026
  * @return string setting name for that column
1027
  */
1028
+ public function get_column_excluded_setting_key( $column ) {
1029
  switch ( $column ) {
1030
  case 'connector':
1031
  $output = 'connectors';
classes/class-live-update.php CHANGED
@@ -176,14 +176,19 @@ class Live_Update {
176
  $enable_stream_update = ( 'off' !== $this->plugin->admin->get_user_meta( get_current_user_id(), $this->user_meta_key ) );
177
 
178
  // Register list table
179
- $this->list_table = new List_Table( $this->plugin, array( 'screen' => 'toplevel_page_' . $this->plugin->admin->records_page_slug ) );
 
 
 
 
180
  $this->list_table->prepare_items();
181
 
182
  $total_items = isset( $this->list_table->_pagination_args['total_items'] ) ? $this->list_table->_pagination_args['total_items'] : null;
183
  $total_pages = isset( $this->list_table->_pagination_args['total_pages'] ) ? $this->list_table->_pagination_args['total_pages'] : null;
184
 
185
  if ( isset( $data['wp-stream-heartbeat'] ) && isset( $total_items ) ) {
186
- $response['total_items'] = $total_items;
 
187
  $response['total_items_i18n'] = sprintf( _n( '%d item', '%d items', $total_items ), number_format_i18n( $total_items ) );
188
  }
189
 
176
  $enable_stream_update = ( 'off' !== $this->plugin->admin->get_user_meta( get_current_user_id(), $this->user_meta_key ) );
177
 
178
  // Register list table
179
+ $this->list_table = new List_Table(
180
+ $this->plugin, array(
181
+ 'screen' => 'toplevel_page_' . $this->plugin->admin->records_page_slug,
182
+ )
183
+ );
184
  $this->list_table->prepare_items();
185
 
186
  $total_items = isset( $this->list_table->_pagination_args['total_items'] ) ? $this->list_table->_pagination_args['total_items'] : null;
187
  $total_pages = isset( $this->list_table->_pagination_args['total_pages'] ) ? $this->list_table->_pagination_args['total_pages'] : null;
188
 
189
  if ( isset( $data['wp-stream-heartbeat'] ) && isset( $total_items ) ) {
190
+ $response['total_items'] = $total_items;
191
+ // translators: Placeholder refers to a number of items (e.g. "42")
192
  $response['total_items_i18n'] = sprintf( _n( '%d item', '%d items', $total_items ), number_format_i18n( $total_items ) );
193
  }
194
 
classes/class-log.php CHANGED
@@ -1,13 +1,24 @@
1
  <?php
 
2
  namespace WP_Stream;
3
 
4
  class Log {
 
5
  /**
6
  * Hold Plugin class
 
7
  * @var Plugin
8
  */
9
  public $plugin;
10
 
 
 
 
 
 
 
 
 
11
  /**
12
  * Previous Stream record ID, used for chaining same-session records
13
  *
@@ -23,22 +34,28 @@ class Log {
23
  public function __construct( $plugin ) {
24
  $this->plugin = $plugin;
25
 
26
- // Ensure function used in various methods is pre-loaded
 
 
 
 
 
 
27
  if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
28
- require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
29
  }
30
  }
31
 
32
  /**
33
  * Log handler
34
  *
35
- * @param Connector $connector Connector responsible for logging the event
36
- * @param string $message sprintf-ready error message string
37
- * @param array $args sprintf (and extra) arguments to use
38
- * @param int $object_id Target object id
39
- * @param string $context Context of the event
40
- * @param string $action Action of the event
41
- * @param int $user_id User responsible for the event
42
  *
43
  * @return mixed True if updated, otherwise false|WP_Error
44
  */
@@ -57,7 +74,7 @@ class Log {
57
  $author = new Author( $user_id );
58
  $agent = $author->get_current_agent();
59
 
60
- // WP Cron tracking requires opt-in and WP Cron to be enabled
61
  if ( ! $wp_cron_tracking && 'wp_cron' === $agent ) {
62
  return false;
63
  }
@@ -84,18 +101,18 @@ class Log {
84
  $user_meta['system_user_name'] = (string) $user_info['name'];
85
  }
86
 
87
- // Prevent any meta with null values from being logged
88
  $stream_meta = array_filter(
89
  $args,
90
- function( $var ) {
91
  return ! is_null( $var );
92
  }
93
  );
94
 
95
- // Add user meta to Stream meta
96
  $stream_meta['user_meta'] = $user_meta;
97
 
98
- // Get the current time in milliseconds
99
  $iso_8601_extended_date = wp_stream_get_iso_8601_extended_date();
100
 
101
  if ( ! empty( $user->roles ) ) {
@@ -108,18 +125,18 @@ class Log {
108
  }
109
 
110
  $recordarr = array(
111
- 'object_id' => (int) $object_id,
112
- 'site_id' => (int) is_multisite() ? get_current_site()->id : 1,
113
- 'blog_id' => (int) apply_filters( 'wp_stream_blog_id_logged', get_current_blog_id() ),
114
- 'user_id' => (int) $user_id,
115
- 'user_role' => (string) $role,
116
- 'created' => (string) $iso_8601_extended_date,
117
- 'summary' => (string) vsprintf( $message, $args ),
118
- 'connector' => (string) $connector,
119
- 'context' => (string) $context,
120
- 'action' => (string) $action,
121
- 'ip' => (string) wp_stream_filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP ),
122
- 'meta' => (array) $stream_meta,
123
  );
124
 
125
  if ( 0 === $recordarr['object_id'] ) {
@@ -128,19 +145,20 @@ class Log {
128
 
129
  $result = $this->plugin->db->insert( $recordarr );
130
 
131
- $this->debug_backtrace( $recordarr );
 
132
 
133
  return $result;
134
  }
135
 
136
  /**
137
- * This function is use to check whether or not a record should be excluded from the log
138
  *
139
- * @param string $connector Name of the connector being logged
140
- * @param string $context Name of the context being logged
141
- * @param string $action Name of the action being logged
142
- * @param \WP_User $user The user being logged
143
- * @param string $ip IP address being logged
144
  *
145
  * @return bool
146
  */
@@ -150,14 +168,14 @@ class Log {
150
  }
151
 
152
  if ( is_null( $ip ) ) {
153
- $ip = wp_stream_filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP );
154
  } else {
155
  $ip = wp_stream_filter_var( $ip, FILTER_VALIDATE_IP );
156
  }
157
 
158
  if ( ! empty( $user->roles ) ) {
159
  $roles = array_values( $user->roles );
160
- $role = $roles[0];
161
  } else {
162
  $role = '';
163
  }
@@ -173,7 +191,7 @@ class Log {
173
  $exclude_settings = isset( $this->plugin->settings->options['exclude_rules'] ) ? $this->plugin->settings->options['exclude_rules'] : array();
174
 
175
  if ( is_multisite() && is_plugin_active_for_network( $this->plugin->locations['plugin'] ) && ! is_network_admin() ) {
176
- $multisite_options = (array) get_site_option( 'wp_stream_network', array() );
177
  $multisite_exclude_settings = isset( $multisite_options['exclude_rules'] ) ? $multisite_options['exclude_rules'] : array();
178
 
179
  if ( ! empty( $multisite_exclude_settings ) ) {
@@ -192,7 +210,7 @@ class Log {
192
 
193
  if ( isset( $exclude_settings['exclude_row'] ) && ! empty( $exclude_settings['exclude_row'] ) ) {
194
  foreach ( $exclude_settings['exclude_row'] as $key => $value ) {
195
- // Prepare values
196
  $author_or_role = isset( $exclude_settings['author_or_role'][ $key ] ) ? $exclude_settings['author_or_role'][ $key ] : '';
197
  $connector = isset( $exclude_settings['connector'][ $key ] ) ? $exclude_settings['connector'][ $key ] : '';
198
  $context = isset( $exclude_settings['context'][ $key ] ) ? $exclude_settings['context'][ $key ] : '';
@@ -233,13 +251,14 @@ class Log {
233
  }
234
  }
235
  }
 
236
  /**
237
- * Filters whether or not a record should be excluded from the log
238
  *
239
  * If true, the record is not logged.
240
  *
241
- * @param array $exclude_record Whether the record should excluded
242
- * @param array $recordarr The record to log
243
  *
244
  * @return bool
245
  */
@@ -247,68 +266,52 @@ class Log {
247
  }
248
 
249
  /**
250
- * Send a full backtrace of calls to the PHP error log for debugging
251
  *
252
- * @param array $recordarr
253
  *
254
- * @return void
255
  */
256
  public function debug_backtrace( $recordarr ) {
257
- /**
258
- * Enable debug backtrace on records.
259
- *
260
- * This filter is for developer use only. When enabled, Stream will send
261
- * a full debug backtrace of PHP calls for each record. Optionally, you may
262
- * use the available $recordarr parameter to specify what types of records to
263
- * create backtrace logs for.
264
- *
265
- * @param array $recordarr
266
- *
267
- * @return bool Set to FALSE by default (backtrace disabled)
268
- */
269
- $enabled = apply_filters( 'wp_stream_debug_backtrace', false, $recordarr );
270
-
271
- if ( ! $enabled ) {
272
- return;
273
- }
274
-
275
  if ( version_compare( PHP_VERSION, '5.3.6', '<' ) ) {
276
- error_log( 'WP Stream debug backtrace requires at least PHP 5.3.6' );
277
- return;
278
  }
279
 
280
- // Record details
281
  $summary = isset( $recordarr['summary'] ) ? $recordarr['summary'] : null;
282
  $author = isset( $recordarr['author'] ) ? $recordarr['author'] : null;
283
  $connector = isset( $recordarr['connector'] ) ? $recordarr['connector'] : null;
284
  $context = isset( $recordarr['context'] ) ? $recordarr['context'] : null;
285
  $action = isset( $recordarr['action'] ) ? $recordarr['action'] : null;
286
 
287
- // Stream meta
288
  $stream_meta = isset( $recordarr['meta'] ) ? $recordarr['meta'] : null;
289
 
290
  unset( $stream_meta['user_meta'] );
291
 
292
  if ( $stream_meta ) {
293
- array_walk( $stream_meta, function( &$value, $key ) {
294
- $value = sprintf( '%s: %s', $key, ( '' === $value ) ? 'null' : $value );
295
- });
296
-
 
297
  $stream_meta = implode( ', ', $stream_meta );
298
  }
299
 
300
- // User meta
301
  $user_meta = isset( $recordarr['meta']['user_meta'] ) ? $recordarr['meta']['user_meta'] : null;
302
 
303
  if ( $user_meta ) {
304
- array_walk( $user_meta, function( &$value, $key ) {
305
- $value = sprintf( '%s: %s', $key, ( '' === $value ) ? 'null' : $value );
306
- });
 
 
307
 
308
  $user_meta = implode( ', ', $user_meta );
309
  }
310
 
311
- // Debug backtrace
312
  ob_start();
313
 
314
  // @codingStandardsIgnoreStart
@@ -330,6 +333,6 @@ class Log {
330
  implode( "\n", $backtrace )
331
  );
332
 
333
- error_log( $output );
334
  }
335
  }
1
  <?php
2
+
3
  namespace WP_Stream;
4
 
5
  class Log {
6
+
7
  /**
8
  * Hold Plugin class
9
+ *
10
  * @var Plugin
11
  */
12
  public $plugin;
13
 
14
+ /**
15
+ * Hold Current visitors IP Address.
16
+ *
17
+ * @var string
18
+ */
19
+ private $ip_address;
20
+
21
+
22
  /**
23
  * Previous Stream record ID, used for chaining same-session records
24
  *
34
  public function __construct( $plugin ) {
35
  $this->plugin = $plugin;
36
 
37
+ // Support proxy mode by checking the `X-Forwarded-For` header first.
38
+ $ip_address = wp_stream_filter_input( INPUT_SERVER, 'HTTP_X_FORWARDED_FOR', FILTER_VALIDATE_IP );
39
+ $ip_address = $ip_address ? $ip_address : wp_stream_filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP );
40
+
41
+ $this->ip_address = $ip_address;
42
+
43
+ // Ensure function used in various methods is pre-loaded.
44
  if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
45
+ require_once ABSPATH . '/wp-admin/includes/plugin.php';
46
  }
47
  }
48
 
49
  /**
50
  * Log handler
51
  *
52
+ * @param Connector $connector Connector responsible for logging the event.
53
+ * @param string $message sprintf-ready error message string.
54
+ * @param array $args sprintf (and extra) arguments to use.
55
+ * @param int $object_id Target object id.
56
+ * @param string $context Context of the event.
57
+ * @param string $action Action of the event.
58
+ * @param int $user_id User responsible for the event.
59
  *
60
  * @return mixed True if updated, otherwise false|WP_Error
61
  */
74
  $author = new Author( $user_id );
75
  $agent = $author->get_current_agent();
76
 
77
+ // WP Cron tracking requires opt-in and WP Cron to be enabled.
78
  if ( ! $wp_cron_tracking && 'wp_cron' === $agent ) {
79
  return false;
80
  }
101
  $user_meta['system_user_name'] = (string) $user_info['name'];
102
  }
103
 
104
+ // Prevent any meta with null values from being logged.
105
  $stream_meta = array_filter(
106
  $args,
107
+ function ( $var ) {
108
  return ! is_null( $var );
109
  }
110
  );
111
 
112
+ // Add user meta to Stream meta.
113
  $stream_meta['user_meta'] = $user_meta;
114
 
115
+ // Get the current time in milliseconds.
116
  $iso_8601_extended_date = wp_stream_get_iso_8601_extended_date();
117
 
118
  if ( ! empty( $user->roles ) ) {
125
  }
126
 
127
  $recordarr = array(
128
+ 'object_id' => (int) $object_id,
129
+ 'site_id' => (int) is_multisite() ? get_current_site()->id : 1,
130
+ 'blog_id' => (int) apply_filters( 'wp_stream_blog_id_logged', get_current_blog_id() ),
131
+ 'user_id' => (int) $user_id,
132
+ 'user_role' => (string) $role,
133
+ 'created' => (string) $iso_8601_extended_date,
134
+ 'summary' => (string) vsprintf( $message, $args ),
135
+ 'connector' => (string) $connector,
136
+ 'context' => (string) $context,
137
+ 'action' => (string) $action,
138
+ 'ip' => (string) $this->ip_address,
139
+ 'meta' => (array) $stream_meta,
140
  );
141
 
142
  if ( 0 === $recordarr['object_id'] ) {
145
 
146
  $result = $this->plugin->db->insert( $recordarr );
147
 
148
+ // This is helpful in development environments:
149
+ // error_log( $this->debug_backtrace( $recordarr ) );
150
 
151
  return $result;
152
  }
153
 
154
  /**
155
+ * This function is use to check whether or not a record should be excluded from the log.
156
  *
157
+ * @param string $connector Name of the connector being logged.
158
+ * @param string $context Name of the context being logged.
159
+ * @param string $action Name of the action being logged.
160
+ * @param \WP_User $user The user being logged.
161
+ * @param string $ip IP address being logged.
162
  *
163
  * @return bool
164
  */
168
  }
169
 
170
  if ( is_null( $ip ) ) {
171
+ $ip = $this->ip_address;
172
  } else {
173
  $ip = wp_stream_filter_var( $ip, FILTER_VALIDATE_IP );
174
  }
175
 
176
  if ( ! empty( $user->roles ) ) {
177
  $roles = array_values( $user->roles );
178
+ $role = $roles[0];
179
  } else {
180
  $role = '';
181
  }
191
  $exclude_settings = isset( $this->plugin->settings->options['exclude_rules'] ) ? $this->plugin->settings->options['exclude_rules'] : array();
192
 
193
  if ( is_multisite() && is_plugin_active_for_network( $this->plugin->locations['plugin'] ) && ! is_network_admin() ) {
194
+ $multisite_options = (array) get_site_option( 'wp_stream_network', array() );
195
  $multisite_exclude_settings = isset( $multisite_options['exclude_rules'] ) ? $multisite_options['exclude_rules'] : array();
196
 
197
  if ( ! empty( $multisite_exclude_settings ) ) {
210
 
211
  if ( isset( $exclude_settings['exclude_row'] ) && ! empty( $exclude_settings['exclude_row'] ) ) {
212
  foreach ( $exclude_settings['exclude_row'] as $key => $value ) {
213
+ // Prepare values.
214
  $author_or_role = isset( $exclude_settings['author_or_role'][ $key ] ) ? $exclude_settings['author_or_role'][ $key ] : '';
215
  $connector = isset( $exclude_settings['connector'][ $key ] ) ? $exclude_settings['connector'][ $key ] : '';
216
  $context = isset( $exclude_settings['context'][ $key ] ) ? $exclude_settings['context'][ $key ] : '';
251
  }
252
  }
253
  }
254
+
255
  /**
256
+ * Filters whether or not a record should be excluded from the log.
257
  *
258
  * If true, the record is not logged.
259
  *
260
+ * @param array $exclude_record Whether the record should excluded.
261
+ * @param array $recordarr The record to log.
262
  *
263
  * @return bool
264
  */
266
  }
267
 
268
  /**
269
+ * Helper function to send a full backtrace of calls to the PHP error log for debugging
270
  *
271
+ * @param array $recordarr Record argument array.
272
  *
273
+ * @return string
274
  */
275
  public function debug_backtrace( $recordarr ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  if ( version_compare( PHP_VERSION, '5.3.6', '<' ) ) {
277
+ return __( 'Debug backtrace requires at least PHP 5.3.6', 'wp_stream' );
 
278
  }
279
 
280
+ // Record details.
281
  $summary = isset( $recordarr['summary'] ) ? $recordarr['summary'] : null;
282
  $author = isset( $recordarr['author'] ) ? $recordarr['author'] : null;
283
  $connector = isset( $recordarr['connector'] ) ? $recordarr['connector'] : null;
284
  $context = isset( $recordarr['context'] ) ? $recordarr['context'] : null;
285
  $action = isset( $recordarr['action'] ) ? $recordarr['action'] : null;
286
 
287
+ // Stream meta.
288
  $stream_meta = isset( $recordarr['meta'] ) ? $recordarr['meta'] : null;
289
 
290
  unset( $stream_meta['user_meta'] );
291
 
292
  if ( $stream_meta ) {
293
+ array_walk(
294
+ $stream_meta, function ( &$value, $key ) {
295
+ $value = sprintf( '%s: %s', $key, ( '' === $value ) ? 'null' : $value );
296
+ }
297
+ );
298
  $stream_meta = implode( ', ', $stream_meta );
299
  }
300
 
301
+ // User meta.
302
  $user_meta = isset( $recordarr['meta']['user_meta'] ) ? $recordarr['meta']['user_meta'] : null;
303
 
304
  if ( $user_meta ) {
305
+ array_walk(
306
+ $user_meta, function ( &$value, $key ) {
307
+ $value = sprintf( '%s: %s', $key, ( '' === $value ) ? 'null' : $value );
308
+ }
309
+ );
310
 
311
  $user_meta = implode( ', ', $user_meta );
312
  }
313
 
314
+ // Debug backtrace.
315
  ob_start();
316
 
317
  // @codingStandardsIgnoreStart
333
  implode( "\n", $backtrace )
334
  );
335
 
336
+ return $output;
337
  }
338
  }
classes/class-network.php CHANGED
@@ -12,7 +12,7 @@ class Network {
12
 
13
  public $default_settings_page_slug = 'wp_stream_default_settings';
14
 
15
- function __construct( $plugin ) {
16
  $this->plugin = $plugin;
17
 
18
  // Always add default site_id/blog_id params when multisite
@@ -78,7 +78,7 @@ class Network {
78
  * @return object
79
  */
80
  public function get_network_blog() {
81
- $blog = new \stdClass;
82
  $blog->blog_id = 0;
83
  $blog->blogname = esc_html__( 'Network Admin', 'stream' );
84
 
@@ -92,12 +92,32 @@ class Network {
92
  */
93
  public function is_network_activated() {
94
  if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
95
- require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
 
 
 
 
96
  }
97
 
98
  return is_plugin_active_for_network( $this->plugin->locations['plugin'] );
99
  }
100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  /**
102
  * Adds Stream to the admin bar under the "My Sites > Network Admin" menu
103
  * if Stream has been network-activated.
@@ -178,7 +198,11 @@ class Network {
178
  public function settings_form_action( $action ) {
179
  if ( is_network_admin() ) {
180
  $current_page = wp_stream_filter_input( INPUT_GET, 'page' );
181
- $action = add_query_arg( array( 'action' => $current_page ), 'edit.php' );
 
 
 
 
182
  }
183
 
184
  return $action;
@@ -199,10 +223,10 @@ class Network {
199
  $current_page = wp_stream_filter_input( INPUT_GET, 'page' );
200
 
201
  switch ( $current_page ) {
202
- case $this->network_settings_page_slug :
203
  $description = __( 'These settings apply to all sites on the network.', 'stream' );
204
  break;
205
- case $this->default_settings_page_slug :
206
  $description = __( 'These default settings will apply to new sites created on the network. These settings do not alter existing sites.', 'stream' );
207
  break;
208
  }
@@ -225,7 +249,7 @@ class Network {
225
  $stream_hidden_options = apply_filters(
226
  'wp_stream_hidden_option_fields',
227
  array(
228
- 'general' => array(
229
  'records_ttl',
230
  ),
231
  'advanced' => array(
@@ -327,13 +351,11 @@ class Network {
327
  $this->default_settings_page_slug,
328
  );
329
 
330
- if ( ! isset( $_GET['action'] ) || ! in_array( $_GET['action'], $allowed_referers, true ) ) {
331
  return;
332
  }
333
 
334
- // @codingStandardsIgnoreStart
335
- $options = isset( $_POST['option_page'] ) ? explode( ',', stripslashes( $_POST['option_page'] ) ) : null;
336
- // @codingStandardsIgnoreEnd
337
 
338
  if ( $options ) {
339
 
@@ -487,6 +509,7 @@ class Network {
487
  */
488
  public function network_admin_page_title( $page_title ) {
489
  if ( is_network_admin() ) {
 
490
  $site_count = sprintf( _n( '%d site', '%d sites', get_blog_count(), 'stream' ), number_format( get_blog_count() ) );
491
  $page_title = sprintf( '%s (%s)', $page_title, $site_count );
492
  }
12
 
13
  public $default_settings_page_slug = 'wp_stream_default_settings';
14
 
15
+ public function __construct( $plugin ) {
16
  $this->plugin = $plugin;
17
 
18
  // Always add default site_id/blog_id params when multisite
78
  * @return object
79
  */
80
  public function get_network_blog() {
81
+ $blog = new \stdClass();
82
  $blog->blog_id = 0;
83
  $blog->blogname = esc_html__( 'Network Admin', 'stream' );
84
 
92
  */
93
  public function is_network_activated() {
94
  if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
95
+ require_once ABSPATH . '/wp-admin/includes/plugin.php';
96
+ }
97
+
98
+ if ( $this->is_mustuse() ) {
99
+ return true;
100
  }
101
 
102
  return is_plugin_active_for_network( $this->plugin->locations['plugin'] );
103
  }
104
 
105
+ /**
106
+ * Returns true if Stream is a must-use plugin, otherwise false
107
+ *
108
+ * @return bool
109
+ */
110
+ public function is_mustuse() {
111
+
112
+ $stream_php = trailingslashit( WPMU_PLUGIN_DIR ) . $this->plugin->locations['plugin'];
113
+
114
+ if ( file_exists( $stream_php ) && class_exists( 'WP_Stream\Plugin' ) ) {
115
+ return true;
116
+ }
117
+
118
+ return false;
119
+ }
120
+
121
  /**
122
  * Adds Stream to the admin bar under the "My Sites > Network Admin" menu
123
  * if Stream has been network-activated.
198
  public function settings_form_action( $action ) {
199
  if ( is_network_admin() ) {
200
  $current_page = wp_stream_filter_input( INPUT_GET, 'page' );
201
+ $action = add_query_arg(
202
+ array(
203
+ 'action' => $current_page,
204
+ ), 'edit.php'
205
+ );
206
  }
207
 
208
  return $action;
223
  $current_page = wp_stream_filter_input( INPUT_GET, 'page' );
224
 
225
  switch ( $current_page ) {
226
+ case $this->network_settings_page_slug:
227
  $description = __( 'These settings apply to all sites on the network.', 'stream' );
228
  break;
229
+ case $this->default_settings_page_slug:
230
  $description = __( 'These default settings will apply to new sites created on the network. These settings do not alter existing sites.', 'stream' );
231
  break;
232
  }
249
  $stream_hidden_options = apply_filters(
250
  'wp_stream_hidden_option_fields',
251
  array(
252
+ 'general' => array(
253
  'records_ttl',
254
  ),
255
  'advanced' => array(
351
  $this->default_settings_page_slug,
352
  );
353
 
354
+ if ( ! isset( $_GET['action'] ) || ! in_array( $_GET['action'], $allowed_referers, true ) ) { // CSRF okay
355
  return;
356
  }
357
 
358
+ $options = isset( $_POST['option_page'] ) ? explode( ',', stripslashes( $_POST['option_page'] ) ) : null; // CSRF okay
 
 
359
 
360
  if ( $options ) {
361
 
509
  */
510
  public function network_admin_page_title( $page_title ) {
511
  if ( is_network_admin() ) {
512
+ // translators: Placeholder refers to a number of sites on the network (e.g. "42")
513
  $site_count = sprintf( _n( '%d site', '%d sites', get_blog_count(), 'stream' ), number_format( get_blog_count() ) );
514
  $page_title = sprintf( '%s (%s)', $page_title, $site_count );
515
  }
classes/class-plugin.php CHANGED
@@ -7,7 +7,7 @@ class Plugin {
7
  *
8
  * @const string
9
  */
10
- const VERSION = '3.2.2';
11
 
12
  /**
13
  * WP-CLI command
@@ -139,7 +139,7 @@ class Plugin {
139
  *
140
  * @param string $class
141
  */
142
- function autoload( $class ) {
143
  if ( ! preg_match( '/^(?P<namespace>.+)\\\\(?P<autoload>[^\\\\]+)$/', $class, $matches ) ) {
144
  return;
145
  }
@@ -178,10 +178,10 @@ class Plugin {
178
  * @action init
179
  */
180
  public function init() {
181
- $this->settings = new Settings( $this );
182
- $this->connectors = new Connectors( $this );
183
- $this->alerts = new Alerts( $this );
184
- $this->alerts_list = new Alerts_List( $this );
185
 
186
  }
187
 
@@ -218,10 +218,10 @@ class Plugin {
218
  * @return array
219
  */
220
  private function locate_plugin() {
221
- $dir_url = trailingslashit( plugins_url( '', dirname( __FILE__ ) ) );
222
- $dir_path = plugin_dir_path( dirname( __FILE__ ) );
223
- $dir_basename = basename( $dir_path );
224
- $plugin_basename = trailingslashit( $dir_basename ) . $dir_basename . '.php';
225
 
226
  return compact( 'dir_url', 'dir_path', 'dir_basename', 'plugin_basename' );
227
  }
7
  *
8
  * @const string
9
  */
10
+ const VERSION = '3.2.3';
11
 
12
  /**
13
  * WP-CLI command
139
  *
140
  * @param string $class
141
  */
142
+ public function autoload( $class ) {
143
  if ( ! preg_match( '/^(?P<namespace>.+)\\\\(?P<autoload>[^\\\\]+)$/', $class, $matches ) ) {
144
  return;
145
  }
178
  * @action init
179
  */
180
  public function init() {
181
+ $this->settings = new Settings( $this );
182
+ $this->connectors = new Connectors( $this );
183
+ $this->alerts = new Alerts( $this );
184
+ $this->alerts_list = new Alerts_List( $this );
185
 
186
  }
187
 
218
  * @return array
219
  */
220
  private function locate_plugin() {
221
+ $dir_url = trailingslashit( plugins_url( '', dirname( __FILE__ ) ) );
222
+ $dir_path = plugin_dir_path( dirname( __FILE__ ) );
223
+ $dir_basename = basename( $dir_path );
224
+ $plugin_basename = trailingslashit( $dir_basename ) . $dir_basename . '.php';
225
 
226
  return compact( 'dir_url', 'dir_path', 'dir_basename', 'plugin_basename' );
227
  }
classes/class-preview-list-table.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WP_Stream;
3
 
4
  class Preview_List_Table extends List_Table {
@@ -7,6 +8,7 @@ class Preview_List_Table extends List_Table {
7
  * Class constructor.
8
  *
9
  * @param Plugin $plugin Plugin object.
 
10
  * @return void
11
  */
12
  public function __construct( $plugin ) {
@@ -18,15 +20,21 @@ class Preview_List_Table extends List_Table {
18
  * Sets up the records for display.
19
  *
20
  * @param array $items List of items for display.
 
21
  * @return void
22
  */
23
- function set_records( $items ) {
24
- $columns = $this->get_columns();
25
  $sortable = $this->get_sortable_columns();
26
  $hidden = $this->get_hidden_columns();
27
- $primary = $columns['summary'];
28
 
29
- $this->_column_headers = array( $columns, $hidden, $sortable, $primary );
 
 
 
 
 
30
 
31
  $this->items = $items;
32
  }
@@ -47,7 +55,7 @@ class Preview_List_Table extends List_Table {
47
  </thead>
48
 
49
  <tbody id="the-list">
50
- <?php $this->display_rows_or_placeholder(); ?>
51
  </tbody>
52
 
53
  <tfoot>
1
  <?php
2
+
3
  namespace WP_Stream;
4
 
5
  class Preview_List_Table extends List_Table {
8
  * Class constructor.
9
  *
10
  * @param Plugin $plugin Plugin object.
11
+ *
12
  * @return void
13
  */
14
  public function __construct( $plugin ) {
20
  * Sets up the records for display.
21
  *
22
  * @param array $items List of items for display.
23
+ *
24
  * @return void
25
  */
26
+ public function set_records( $items ) {
27
+ $columns = $this->get_columns();
28
  $sortable = $this->get_sortable_columns();
29
  $hidden = $this->get_hidden_columns();
30
+ $primary = $columns['summary'];
31
 
32
+ $this->_column_headers = array(
33
+ $columns,
34
+ $hidden,
35
+ $sortable,
36
+ $primary,
37
+ );
38
 
39
  $this->items = $items;
40
  }
55
  </thead>
56
 
57
  <tbody id="the-list">
58
+ <?php $this->display_rows_or_placeholder(); ?>
59
  </tbody>
60
 
61
  <tfoot>
classes/class-query.php CHANGED
@@ -46,7 +46,7 @@ class Query {
46
  }
47
 
48
  if ( ! empty( $args['search'] ) ) {
49
- $field = ! empty( $args['search_field'] ) ? $args['search_field'] : 'summary';
50
 
51
  // Sanitize field
52
  $allowed_fields = array( 'ID', 'site_id', 'blog_id', 'object_id', 'user_id', 'user_role', 'created', 'summary', 'connector', 'context', 'action', 'ip' );
46
  }
47
 
48
  if ( ! empty( $args['search'] ) ) {
49
+ $field = ! empty( $args['search_field'] ) ? $args['search_field'] : 'summary';
50
 
51
  // Sanitize field
52
  $allowed_fields = array( 'ID', 'site_id', 'blog_id', 'object_id', 'user_id', 'user_role', 'created', 'summary', 'connector', 'context', 'action', 'ip' );
classes/class-record.php CHANGED
@@ -18,20 +18,20 @@ class Record {
18
  public $meta;
19
 
20
  public function __construct( $item ) {
21
- $this->ID = isset( $item->ID ) ? $item->ID : null;
22
- $this->created = isset( $item->created ) ? $item->created : null;
23
- $this->site_id = isset( $item->site_id ) ? $item->site_id : null;
24
- $this->blog_id = isset( $item->blog_id ) ? $item->blog_id : null;
25
  $this->object_id = isset( $item->object_id ) ? $item->object_id : null;
26
- $this->user_id = isset( $item->user_id ) ? $item->user_id : null;
27
  $this->user_role = isset( $item->user_role ) ? $item->user_role : null;
28
  $this->user_meta = isset( $item->meta['user_meta'] ) ? $item->meta['user_meta'] : null;
29
- $this->summary = isset( $item->summary ) ? $item->summary : null;
30
  $this->connector = isset( $item->connector ) ? $item->connector : null;
31
- $this->context = isset( $item->context ) ? $item->context : null;
32
- $this->action = isset( $item->action ) ? $item->action : null;
33
- $this->ip = isset( $item->ip ) ? $item->ip : null;
34
- $this->meta = isset( $item->meta ) ? $item->meta : null;
35
 
36
  if ( isset( $this->meta['user_meta'] ) ) {
37
  unset( $this->meta['user_meta'] );
@@ -89,7 +89,7 @@ class Record {
89
  * @param object Record object
90
  * @return mixed The title of the object as a string, otherwise false
91
  */
92
- function get_object_title() {
93
  if ( ! isset( $this->object_id ) || empty( $this->object_id ) ) {
94
  return false;
95
  }
18
  public $meta;
19
 
20
  public function __construct( $item ) {
21
+ $this->ID = isset( $item->ID ) ? $item->ID : null;
22
+ $this->created = isset( $item->created ) ? $item->created : null;
23
+ $this->site_id = isset( $item->site_id ) ? $item->site_id : null;
24
+ $this->blog_id = isset( $item->blog_id ) ? $item->blog_id : null;
25
  $this->object_id = isset( $item->object_id ) ? $item->object_id : null;
26
+ $this->user_id = isset( $item->user_id ) ? $item->user_id : null;
27
  $this->user_role = isset( $item->user_role ) ? $item->user_role : null;
28
  $this->user_meta = isset( $item->meta['user_meta'] ) ? $item->meta['user_meta'] : null;
29
+ $this->summary = isset( $item->summary ) ? $item->summary : null;
30
  $this->connector = isset( $item->connector ) ? $item->connector : null;
31
+ $this->context = isset( $item->context ) ? $item->context : null;
32
+ $this->action = isset( $item->action ) ? $item->action : null;
33
+ $this->ip = isset( $item->ip ) ? $item->ip : null;
34
+ $this->meta = isset( $item->meta ) ? $item->meta : null;
35
 
36
  if ( isset( $this->meta['user_meta'] ) ) {
37
  unset( $this->meta['user_meta'] );
89
  * @param object Record object
90
  * @return mixed The title of the object as a string, otherwise false
91
  */
92
+ public function get_object_title() {
93
  if ( ! isset( $this->object_id ) || empty( $this->object_id ) ) {
94
  return false;
95
  }
classes/class-settings.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WP_Stream;
3
 
4
  use \WP_Roles;
@@ -6,6 +7,7 @@ use \WP_User;
6
  use \WP_User_Query;
7
 
8
  class Settings {
 
9
  /**
10
  * Hold Plugin class
11
  * @var Plugin
@@ -55,10 +57,20 @@ class Settings {
55
  add_action( 'admin_init', array( $this, 'register_settings' ) );
56
 
57
  // Remove records when records TTL is shortened
58
- add_action( 'update_option_' . $this->option_key, array( $this, 'updated_option_ttl_remove_records' ), 10, 2 );
 
 
 
 
 
59
 
60
  // Apply label translations for settings
61
- add_filter( 'wp_stream_serialized_labels', array( $this, 'get_settings_translations' ) );
 
 
 
 
 
62
 
63
  // Ajax callback function to search users
64
  add_action( 'wp_ajax_stream_get_users', array( $this, 'get_users' ) );
@@ -95,23 +107,33 @@ class Settings {
95
  'find' => $search,
96
  );
97
 
98
- add_filter( 'user_search_columns', array( $this, 'add_display_name_search_columns' ), 10, 3 );
 
 
 
 
 
99
 
100
  $users = new WP_User_Query(
101
  array(
102
- 'search' => "*{$request->find}*",
103
  'search_columns' => array(
104
  'user_login',
105
  'user_nicename',
106
  'user_email',
107
  'user_url',
108
  ),
109
- 'orderby' => 'display_name',
110
- 'number' => $this->plugin->admin->preload_users_max,
111
  )
112
  );
113
 
114
- remove_filter( 'user_search_columns', array( $this, 'add_display_name_search_columns' ), 10 );
 
 
 
 
 
115
 
116
  if ( 0 === $users->get_total() ) {
117
  wp_send_json_error( $response );
@@ -121,15 +143,15 @@ class Settings {
121
  if ( is_multisite() && is_super_admin() ) {
122
  $super_admins = get_super_admins();
123
  foreach ( $super_admins as $admin ) {
124
- $user = get_user_by( 'login', $admin );
125
  $users_array[] = $user;
126
  }
127
  }
128
 
129
- $response->status = true;
130
- $response->message = '';
131
- $response->roles = $this->get_roles();
132
- $response->users = array();
133
  $users_added_to_response = array();
134
 
135
  foreach ( $users_array as $key => $user ) {
@@ -149,6 +171,7 @@ class Settings {
149
 
150
  $args['tooltip'] = esc_attr(
151
  sprintf(
 
152
  __( 'ID: %1$d\nUser: %2$s\nEmail: %3$s\nRole: %4$s', 'stream' ),
153
  $author->id,
154
  $author->user_login,
@@ -164,13 +187,13 @@ class Settings {
164
 
165
  usort(
166
  $response->users,
167
- function( $a, $b ) {
168
  return strcmp( $a['text'], $b['text'] );
169
  }
170
  );
171
 
172
  if ( empty( $search ) || preg_match( '/wp|cli|system|unknown/i', $search ) ) {
173
- $author = new Author( 0 );
174
  $response->users[] = array(
175
  'id' => '0',
176
  'text' => $author->get_display_name(),
@@ -183,8 +206,8 @@ class Settings {
183
  }
184
 
185
  /**
186
- * Ajax callback function to search IP addresses, used on exclude setting page
187
- */
188
  public function get_ips() {
189
  if ( ! defined( 'DOING_AJAX' ) || ! current_user_can( $this->plugin->admin->settings_cap ) ) {
190
  return;
@@ -196,9 +219,12 @@ class Settings {
196
  $find = wp_stream_filter_input( INPUT_POST, 'find' );
197
 
198
  if ( isset( $find['term'] ) && '' !== $find['term'] ) {
199
- $ips = array_filter( $ips, function ( $ip ) use ( $find ) {
200
- return 0 === strpos( $ip, $find['term'] );
201
- } );
 
 
 
202
  }
203
 
204
  if ( $ips ) {
@@ -211,9 +237,9 @@ class Settings {
211
  /**
212
  * Filter the columns to search in a WP_User_Query search.
213
  *
214
- * @param array $search_columns Array of column names to be searched.
215
- * @param string $search Text being searched.
216
- * @param \WP_User_Query $query current WP_User_Query instance.
217
  *
218
  * @return array
219
  */
@@ -254,16 +280,16 @@ class Settings {
254
  */
255
  public function get_fields() {
256
  $fields = array(
257
- 'general' => array(
258
  'title' => esc_html__( 'General', 'stream' ),
259
  'fields' => array(
260
  array(
261
- 'name' => 'role_access',
262
- 'title' => esc_html__( 'Role Access', 'stream' ),
263
- 'type' => 'multi_checkbox',
264
- 'desc' => esc_html__( 'Users from the selected roles above will have permission to view Stream Records. However, only site Administrators can access Stream Settings.', 'stream' ),
265
- 'choices' => $this->get_roles(),
266
- 'default' => array( 'administrator' ),
267
  ),
268
  array(
269
  'name' => 'records_ttl',
@@ -287,16 +313,16 @@ class Settings {
287
  ),
288
  ),
289
  ),
290
- 'exclude' => array(
291
  'title' => esc_html__( 'Exclude', 'stream' ),
292
  'fields' => array(
293
  array(
294
- 'name' => 'rules',
295
- 'title' => esc_html__( 'Exclude Rules', 'stream' ),
296
- 'type' => 'rule_list',
297
- 'desc' => esc_html__( 'Create rules to exclude certain kinds of activity from being recorded by Stream.', 'stream' ),
298
- 'default' => array(),
299
- 'nonce' => 'stream_get_ips',
300
  ),
301
  ),
302
  ),
@@ -312,20 +338,20 @@ class Settings {
312
  'default' => 0,
313
  ),
314
  array(
315
- 'name' => 'delete_all_records',
316
- 'title' => esc_html__( 'Reset Stream Database', 'stream' ),
317
- 'type' => 'link',
318
- 'href' => add_query_arg(
319
  array(
320
- 'action' => 'wp_stream_reset',
321
- 'wp_stream_nonce' => wp_create_nonce( 'stream_nonce' ),
322
  ),
323
  admin_url( 'admin-ajax.php' )
324
  ),
325
- 'class' => 'warning',
326
- 'desc' => esc_html__( 'Warning: This will delete all activity records from the database.', 'stream' ),
327
- 'default' => 0,
328
- 'sticky' => 'bottom',
329
  ),
330
  ),
331
  ),
@@ -438,7 +464,12 @@ class Settings {
438
  public function register_settings() {
439
  $sections = $this->get_fields();
440
 
441
- register_setting( $this->option_key, $this->option_key, array( $this, 'sanitize_settings' ) );
 
 
 
 
 
442
 
443
  foreach ( $sections as $section_name => $section ) {
444
  add_settings_section(
@@ -456,12 +487,16 @@ class Settings {
456
  add_settings_field(
457
  $field['name'],
458
  $field['title'],
459
- ( isset( $field['callback'] ) ? $field['callback'] : array( $this, 'output_field' ) ),
 
 
 
460
  $this->option_key,
461
  $section_name,
462
  $field + array(
463
  'section' => $section_name,
464
- 'label_for' => sprintf( '%s_%s_%s', $this->option_key, $section_name, $field['name'] ), // xss ok
 
465
  )
466
  );
467
  }
@@ -492,7 +527,7 @@ class Settings {
492
  continue;
493
  }
494
 
495
- // Sanitize depending on the type of field
496
  switch ( $type ) {
497
  case 'number':
498
  $output[ $name ] = is_numeric( $input[ $name ] ) ? intval( trim( $input[ $name ] ) ) : '';
@@ -504,10 +539,12 @@ class Settings {
504
  if ( is_array( $input[ $name ] ) ) {
505
  $output[ $name ] = $input[ $name ];
506
 
507
- // Support all values in multidimentional arrays too
508
- array_walk_recursive( $output[ $name ], function( &$v, $k ) {
509
- $v = trim( $v );
510
- } );
 
 
511
  } else {
512
  $output[ $name ] = trim( $input[ $name ] );
513
  }
@@ -564,9 +601,7 @@ class Settings {
564
  return '';
565
  }
566
 
567
- if ( 'multi_checkbox' === $type
568
- && ( empty( $field['choices'] ) || ! is_array( $field['choices'] ) )
569
- ) {
570
  return '';
571
  }
572
 
@@ -627,15 +662,15 @@ class Settings {
627
  esc_attr( $section ),
628
  esc_attr( $name )
629
  );
630
- // Fallback if nothing is selected
631
- $output .= sprintf(
632
  '<input type="hidden" name="%1$s[%2$s_%3$s][]" value="__placeholder__" />',
633
  esc_attr( $option_key ),
634
  esc_attr( $section ),
635
  esc_attr( $name )
636
  );
637
  $current_value = (array) $current_value;
638
- $choices = $field['choices'];
639
  if ( is_callable( $choices ) ) {
640
  $choices = call_user_func( $choices );
641
  }
@@ -702,7 +737,7 @@ class Settings {
702
  esc_attr( $title )
703
  );
704
  break;
705
- case 'select2' :
706
  if ( ! isset( $current_value ) ) {
707
  $current_value = '';
708
  }
@@ -721,14 +756,24 @@ class Settings {
721
  if ( isset( $value['children'] ) ) {
722
  $child_values = array();
723
  foreach ( $value['children'] as $child_key => $child_value ) {
724
- $child_values[] = array( 'id' => $child_key, 'text' => $child_value );
 
 
 
725
  }
726
  }
727
  if ( isset( $value['label'] ) ) {
728
- $data_values[] = array( 'id' => $key, 'text' => $value['label'], 'children' => $child_values );
 
 
 
 
729
  }
730
  } else {
731
- $data_values[] = array( 'id' => $key, 'text' => $value );
 
 
 
732
  }
733
  }
734
  $class .= ' with-source';
@@ -742,6 +787,7 @@ class Settings {
742
  esc_attr( wp_stream_json_encode( $data_values ) ),
743
  esc_attr( $current_value ),
744
  esc_attr( $class ),
 
745
  sprintf( esc_html__( 'Any %s', 'stream' ), $title )
746
  );
747
 
@@ -754,12 +800,13 @@ class Settings {
754
  );
755
 
756
  break;
757
- case 'rule_list' :
758
- $form = new Form_Generator;
 
759
  $output = '<p class="description">' . esc_html( $description ) . '</p>';
760
 
761
- $actions_top = sprintf( '<input type="button" class="button" id="%1$s_new_rule" value="&#43; %2$s" />', esc_attr( $section . '_' . $name ), esc_html__( 'Add New Rule', 'stream' ) );
762
- $actions_bottom = sprintf( '<input type="button" class="button" id="%1$s_remove_rules" value="%2$s" />', esc_attr( $section . '_' . $name ), esc_html__( 'Delete Selected Rules', 'stream' ) );
763
 
764
  $output .= sprintf( '<div class="tablenav top">%1$s</div>', $actions_top );
765
  $output .= '<table class="wp-list-table widefat fixed stream-exclude-list">';
@@ -785,11 +832,11 @@ class Settings {
785
 
786
  $exclude_rows = array();
787
 
788
- // Prepend an empty row
789
  $current_value['exclude_row'] = array( 'helper' => '' ) + ( isset( $current_value['exclude_row'] ) ? $current_value['exclude_row'] : array() );
790
 
791
  foreach ( $current_value['exclude_row'] as $key => $value ) {
792
- // Prepare values
793
  $author_or_role = isset( $current_value['author_or_role'][ $key ] ) ? $current_value['author_or_role'][ $key ] : '';
794
  $connector = isset( $current_value['connector'][ $key ] ) ? $current_value['connector'][ $key ] : '';
795
  $context = isset( $current_value['context'][ $key ] ) ? $current_value['context'][ $key ] : '';
@@ -801,11 +848,14 @@ class Settings {
801
  $author_or_role_selected = array();
802
 
803
  foreach ( $this->get_roles() as $role_id => $role ) {
804
- $args = array( 'value' => $role_id, 'text' => $role );
805
- $users = count_users();
 
 
806
  $count = isset( $users['avail_roles'][ $role_id ] ) ? $users['avail_roles'][ $role_id ] : 0;
807
 
808
  if ( ! empty( $count ) ) {
 
809
  $args['user_count'] = sprintf( _n( '%d user', '%d users', absint( $count ), 'stream' ), absint( $count ) );
810
  }
811
 
@@ -820,21 +870,26 @@ class Settings {
820
  if ( empty( $author_or_role_selected ) && is_numeric( $author_or_role ) ) {
821
  $user = new WP_User( $author_or_role );
822
  $display_name = ( 0 === $user->ID ) ? esc_html__( 'N/A', 'stream' ) : $user->display_name;
823
- $author_or_role_selected = array( 'value' => $user->ID, 'text' => $display_name );
 
 
 
824
  $author_or_role_values[] = $author_or_role_selected;
825
  }
826
 
827
- $author_or_role_input = $form->render_field( 'select2', array(
828
- 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'author_or_role' ) ),
829
- 'options' => $author_or_role_values,
830
- 'classes' => 'author_or_role',
831
- 'data' => array(
832
- 'placeholder' => esc_html__( 'Any Author or Role', 'stream' ),
833
- 'nonce' => esc_attr( wp_create_nonce( 'stream_get_users' ) ),
834
- 'selected-id' => isset( $author_or_role_selected['value'] ) ? esc_attr( $author_or_role_selected['value'] ) : '',
835
- 'selected-text' => isset( $author_or_role_selected['text'] ) ? esc_attr( $author_or_role_selected['text'] ) : '',
836
- ),
837
- ) );
 
 
838
 
839
  // Context dropdown menu
840
  $context_values = array();
@@ -845,67 +900,91 @@ class Settings {
845
  if ( isset( $context_data['children'] ) ) {
846
  $child_values = array();
847
  foreach ( $context_data['children'] as $child_id => $child_value ) {
848
- $child_values[] = array( 'value' => $context_id . '-' . $child_id, 'text' => $child_value, 'parent' => $context_id );
 
 
 
 
849
  }
850
  }
851
  if ( isset( $context_data['label'] ) ) {
852
- $context_values[] = array( 'value' => $context_id, 'text' => $context_data['label'], 'children' => $child_values );
 
 
 
 
853
  }
854
  } else {
855
- $context_values[] = array( 'value' => $context_id, 'text' => $context_data );
 
 
 
856
  }
857
  }
858
 
859
- $connector_or_context_input = $form->render_field( 'select2', array(
860
- 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'connector_or_context' ) ),
861
- 'options' => $context_values,
862
- 'classes' => 'connector_or_context',
863
- 'data' => array(
864
- 'group' => 'connector',
865
- 'placeholder' => __( 'Any Context', 'stream' ),
866
- ),
867
- ) );
 
 
868
 
869
- $connector_input = $form->render_field( 'hidden', array(
870
- 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'connector' ) ),
871
- 'value' => $connector,
872
- 'classes' => 'connector',
873
- ) );
 
 
874
 
875
- $context_input = $form->render_field( 'hidden', array(
876
- 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'context' ) ),
877
- 'value' => $context,
878
- 'classes' => 'context',
879
- ) );
 
 
880
 
881
  // Action dropdown menu
882
  $action_values = array();
883
 
884
  foreach ( $this->get_terms_labels( 'action' ) as $action_id => $action_data ) {
885
- $action_values[] = array( 'value' => $action_id, 'text' => $action_data );
 
 
 
886
  }
887
 
888
- $action_input = $form->render_field( 'select2', array(
889
- 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'action' ) ),
890
- 'value' => $action,
891
- 'options' => $action_values,
892
- 'classes' => 'action',
893
- 'data' => array(
894
- 'placeholder' => __( 'Any Action', 'stream' ),
895
- ),
896
- ) );
 
 
897
 
898
  // IP Address input
899
- $ip_address_input = $form->render_field( 'select2', array(
900
- 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'ip_address' ) ),
901
- 'value' => $ip_address,
902
- 'classes' => 'ip_address',
903
- 'data' => array(
904
- 'placeholder' => esc_attr__( 'Any IP Address', 'stream' ),
905
- 'nonce' => esc_attr( wp_create_nonce( 'stream_get_ips' ) ),
906
- ),
907
- 'multiple' => true,
908
- ) );
 
 
909
 
910
  // Hidden helper input
911
  $helper_input = sprintf(
@@ -925,8 +1004,8 @@ class Settings {
925
  <td>%10$s</td>
926
  <th scope="row" class="actions-column">%11$s</th>
927
  </tr>',
928
- ( 0 !== $key % 2 ) ? 'alternate' : '',
929
- ( 'helper' === $key ) ? 'hidden helper' : '',
930
  '<input class="cb-select" type="checkbox" />',
931
  $helper_input,
932
  $author_or_role_input,
@@ -1036,8 +1115,8 @@ class Settings {
1036
  * @param array $new_value
1037
  */
1038
  public function updated_option_ttl_remove_records( $old_value, $new_value ) {
1039
- $ttl_before = isset( $old_value['general_records_ttl'] ) ? (int) $old_value['general_records_ttl'] : -1;
1040
- $ttl_after = isset( $new_value['general_records_ttl'] ) ? (int) $new_value['general_records_ttl'] : -1;
1041
 
1042
  if ( $ttl_after < $ttl_before ) {
1043
  /**
1
  <?php
2
+
3
  namespace WP_Stream;
4
 
5
  use \WP_Roles;
7
  use \WP_User_Query;
8
 
9
  class Settings {
10
+
11
  /**
12
  * Hold Plugin class
13
  * @var Plugin
57
  add_action( 'admin_init', array( $this, 'register_settings' ) );
58
 
59
  // Remove records when records TTL is shortened
60
+ add_action(
61
+ 'update_option_' . $this->option_key, array(
62
+ $this,
63
+ 'updated_option_ttl_remove_records',
64
+ ), 10, 2
65
+ );
66
 
67
  // Apply label translations for settings
68
+ add_filter(
69
+ 'wp_stream_serialized_labels', array(
70
+ $this,
71
+ 'get_settings_translations',
72
+ )
73
+ );
74
 
75
  // Ajax callback function to search users
76
  add_action( 'wp_ajax_stream_get_users', array( $this, 'get_users' ) );
107
  'find' => $search,
108
  );
109
 
110
+ add_filter(
111
+ 'user_search_columns', array(
112
+ $this,
113
+ 'add_display_name_search_columns',
114
+ ), 10, 3
115
+ );
116
 
117
  $users = new WP_User_Query(
118
  array(
119
+ 'search' => "*{$request->find}*",
120
  'search_columns' => array(
121
  'user_login',
122
  'user_nicename',
123
  'user_email',
124
  'user_url',
125
  ),
126
+ 'orderby' => 'display_name',
127
+ 'number' => $this->plugin->admin->preload_users_max,
128
  )
129
  );
130
 
131
+ remove_filter(
132
+ 'user_search_columns', array(
133
+ $this,
134
+ 'add_display_name_search_columns',
135
+ ), 10
136
+ );
137
 
138
  if ( 0 === $users->get_total() ) {
139
  wp_send_json_error( $response );
143
  if ( is_multisite() && is_super_admin() ) {
144
  $super_admins = get_super_admins();
145
  foreach ( $super_admins as $admin ) {
146
+ $user = get_user_by( 'login', $admin );
147
  $users_array[] = $user;
148
  }
149
  }
150
 
151
+ $response->status = true;
152
+ $response->message = '';
153
+ $response->roles = $this->get_roles();
154
+ $response->users = array();
155
  $users_added_to_response = array();
156
 
157
  foreach ( $users_array as $key => $user ) {
171
 
172
  $args['tooltip'] = esc_attr(
173
  sprintf(
174
+ // translators: Placeholders refers to a user ID, a username, an email address, and a user role (e.g. "42", "administrator", "foo@bar.com", "subscriber").
175
  __( 'ID: %1$d\nUser: %2$s\nEmail: %3$s\nRole: %4$s', 'stream' ),
176
  $author->id,
177
  $author->user_login,
187
 
188
  usort(
189
  $response->users,
190
+ function ( $a, $b ) {
191
  return strcmp( $a['text'], $b['text'] );
192
  }
193
  );
194
 
195
  if ( empty( $search ) || preg_match( '/wp|cli|system|unknown/i', $search ) ) {
196
+ $author = new Author( 0 );
197
  $response->users[] = array(
198
  'id' => '0',
199
  'text' => $author->get_display_name(),
206
  }
207
 
208
  /**
209
+ * Ajax callback function to search IP addresses, used on exclude setting page
210
+ */
211
  public function get_ips() {
212
  if ( ! defined( 'DOING_AJAX' ) || ! current_user_can( $this->plugin->admin->settings_cap ) ) {
213
  return;
219
  $find = wp_stream_filter_input( INPUT_POST, 'find' );
220
 
221
  if ( isset( $find['term'] ) && '' !== $find['term'] ) {
222
+ $ips = array_filter(
223
+ $ips,
224
+ function ( $ip ) use ( $find ) {
225
+ return 0 === strpos( $ip, $find['term'] );
226
+ }
227
+ );
228
  }
229
 
230
  if ( $ips ) {
237
  /**
238
  * Filter the columns to search in a WP_User_Query search.
239
  *
240
+ * @param array $search_columns Array of column names to be searched.
241
+ * @param string $search Text being searched.
242
+ * @param \WP_User_Query $query current WP_User_Query instance.
243
  *
244
  * @return array
245
  */
280
  */
281
  public function get_fields() {
282
  $fields = array(
283
+ 'general' => array(
284
  'title' => esc_html__( 'General', 'stream' ),
285
  'fields' => array(
286
  array(
287
+ 'name' => 'role_access',
288
+ 'title' => esc_html__( 'Role Access', 'stream' ),
289
+ 'type' => 'multi_checkbox',
290
+ 'desc' => esc_html__( 'Users from the selected roles above will have permission to view Stream Records. However, only site Administrators can access Stream Settings.', 'stream' ),
291
+ 'choices' => $this->get_roles(),
292
+ 'default' => array( 'administrator' ),
293
  ),
294
  array(
295
  'name' => 'records_ttl',
313
  ),
314
  ),
315
  ),
316
+ 'exclude' => array(
317
  'title' => esc_html__( 'Exclude', 'stream' ),
318
  'fields' => array(
319
  array(
320
+ 'name' => 'rules',
321
+ 'title' => esc_html__( 'Exclude Rules', 'stream' ),
322
+ 'type' => 'rule_list',
323
+ 'desc' => esc_html__( 'Create rules to exclude certain kinds of activity from being recorded by Stream.', 'stream' ),
324
+ 'default' => array(),
325
+ 'nonce' => 'stream_get_ips',
326
  ),
327
  ),
328
  ),
338
  'default' => 0,
339
  ),
340
  array(
341
+ 'name' => 'delete_all_records',
342
+ 'title' => esc_html__( 'Reset Stream Database', 'stream' ),
343
+ 'type' => 'link',
344
+ 'href' => add_query_arg(
345
  array(
346
+ 'action' => 'wp_stream_reset',
347
+ 'wp_stream_nonce_reset' => wp_create_nonce( 'stream_nonce_reset' ),
348
  ),
349
  admin_url( 'admin-ajax.php' )
350
  ),
351
+ 'class' => 'warning',
352
+ 'desc' => esc_html__( 'Warning: This will delete all activity records from the database.', 'stream' ),
353
+ 'default' => 0,
354
+ 'sticky' => 'bottom',
355
  ),
356
  ),
357
  ),
464
  public function register_settings() {
465
  $sections = $this->get_fields();
466
 
467
+ register_setting(
468
+ $this->option_key, $this->option_key, array(
469
+ $this,
470
+ 'sanitize_settings',
471
+ )
472
+ );
473
 
474
  foreach ( $sections as $section_name => $section ) {
475
  add_settings_section(
487
  add_settings_field(
488
  $field['name'],
489
  $field['title'],
490
+ ( isset( $field['callback'] ) ? $field['callback'] : array(
491
+ $this,
492
+ 'output_field',
493
+ ) ),
494
  $this->option_key,
495
  $section_name,
496
  $field + array(
497
  'section' => $section_name,
498
+ 'label_for' => sprintf( '%s_%s_%s', $this->option_key, $section_name, $field['name'] ),
499
+ // xss ok
500
  )
501
  );
502
  }
527
  continue;
528
  }
529
 
530
+ // Sanitize depending on the type of field.
531
  switch ( $type ) {
532
  case 'number':
533
  $output[ $name ] = is_numeric( $input[ $name ] ) ? intval( trim( $input[ $name ] ) ) : '';
539
  if ( is_array( $input[ $name ] ) ) {
540
  $output[ $name ] = $input[ $name ];
541
 
542
+ // Support all values in multidimentional arrays too.
543
+ array_walk_recursive(
544
+ $output[ $name ], function ( &$v, $k ) {
545
+ $v = trim( $v );
546
+ }
547
+ );
548
  } else {
549
  $output[ $name ] = trim( $input[ $name ] );
550
  }
601
  return '';
602
  }
603
 
604
+ if ( 'multi_checkbox' === $type && ( empty( $field['choices'] ) || ! is_array( $field['choices'] ) ) ) {
 
 
605
  return '';
606
  }
607
 
662
  esc_attr( $section ),
663
  esc_attr( $name )
664
  );
665
+ // Fallback if nothing is selected.
666
+ $output .= sprintf(
667
  '<input type="hidden" name="%1$s[%2$s_%3$s][]" value="__placeholder__" />',
668
  esc_attr( $option_key ),
669
  esc_attr( $section ),
670
  esc_attr( $name )
671
  );
672
  $current_value = (array) $current_value;
673
+ $choices = $field['choices'];
674
  if ( is_callable( $choices ) ) {
675
  $choices = call_user_func( $choices );
676
  }
737
  esc_attr( $title )
738
  );
739
  break;
740
+ case 'select2':
741
  if ( ! isset( $current_value ) ) {
742
  $current_value = '';
743
  }
756
  if ( isset( $value['children'] ) ) {
757
  $child_values = array();
758
  foreach ( $value['children'] as $child_key => $child_value ) {
759
+ $child_values[] = array(
760
+ 'id' => $child_key,
761
+ 'text' => $child_value,
762
+ );
763
  }
764
  }
765
  if ( isset( $value['label'] ) ) {
766
+ $data_values[] = array(
767
+ 'id' => $key,
768
+ 'text' => $value['label'],
769
+ 'children' => $child_values,
770
+ );
771
  }
772
  } else {
773
+ $data_values[] = array(
774
+ 'id' => $key,
775
+ 'text' => $value,
776
+ );
777
  }
778
  }
779
  $class .= ' with-source';
787
  esc_attr( wp_stream_json_encode( $data_values ) ),
788
  esc_attr( $current_value ),
789
  esc_attr( $class ),
790
+ // translators: Placeholder refers to the title of the dropdown menu (e.g. "users")
791
  sprintf( esc_html__( 'Any %s', 'stream' ), $title )
792
  );
793
 
800
  );
801
 
802
  break;
803
+ case 'rule_list':
804
+ $users = count_users();
805
+ $form = new Form_Generator();
806
  $output = '<p class="description">' . esc_html( $description ) . '</p>';
807
 
808
+ $actions_top = sprintf( '<input type="button" class="button" id="%1$s_new_rule" value="&#43; %2$s" />', esc_attr( $section . '_' . $name ), esc_html__( 'Add New Rule', 'stream' ) );
809
+ $actions_bottom = sprintf( '<input type="button" class="button" id="%1$s_remove_rules" value="%2$s" />', esc_attr( $section . '_' . $name ), esc_html__( 'Delete Selected Rules', 'stream' ) );
810
 
811
  $output .= sprintf( '<div class="tablenav top">%1$s</div>', $actions_top );
812
  $output .= '<table class="wp-list-table widefat fixed stream-exclude-list">';
832
 
833
  $exclude_rows = array();
834
 
835
+ // Prepend an empty row.
836
  $current_value['exclude_row'] = array( 'helper' => '' ) + ( isset( $current_value['exclude_row'] ) ? $current_value['exclude_row'] : array() );
837
 
838
  foreach ( $current_value['exclude_row'] as $key => $value ) {
839
+ // Prepare values.
840
  $author_or_role = isset( $current_value['author_or_role'][ $key ] ) ? $current_value['author_or_role'][ $key ] : '';
841
  $connector = isset( $current_value['connector'][ $key ] ) ? $current_value['connector'][ $key ] : '';
842
  $context = isset( $current_value['context'][ $key ] ) ? $current_value['context'][ $key ] : '';
848
  $author_or_role_selected = array();
849
 
850
  foreach ( $this->get_roles() as $role_id => $role ) {
851
+ $args = array(
852
+ 'value' => $role_id,
853
+ 'text' => $role,
854
+ );
855
  $count = isset( $users['avail_roles'][ $role_id ] ) ? $users['avail_roles'][ $role_id ] : 0;
856
 
857
  if ( ! empty( $count ) ) {
858
+ // translators: Placeholder refers to a number of users (e.g. "42")
859
  $args['user_count'] = sprintf( _n( '%d user', '%d users', absint( $count ), 'stream' ), absint( $count ) );
860
  }
861
 
870
  if ( empty( $author_or_role_selected ) && is_numeric( $author_or_role ) ) {
871
  $user = new WP_User( $author_or_role );
872
  $display_name = ( 0 === $user->ID ) ? esc_html__( 'N/A', 'stream' ) : $user->display_name;
873
+ $author_or_role_selected = array(
874
+ 'value' => $user->ID,
875
+ 'text' => $display_name,
876
+ );
877
  $author_or_role_values[] = $author_or_role_selected;
878
  }
879
 
880
+ $author_or_role_input = $form->render_field(
881
+ 'select2', array(
882
+ 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]', $option_key, $section, $name, 'author_or_role' ) ),
883
+ 'options' => $author_or_role_values,
884
+ 'classes' => 'author_or_role',
885
+ 'data' => array(
886
+ 'placeholder' => esc_html__( 'Any Author or Role', 'stream' ),
887
+ 'nonce' => esc_attr( wp_create_nonce( 'stream_get_users' ) ),
888
+ 'selected-id' => isset( $author_or_role_selected['value'] ) ? esc_attr( $author_or_role_selected['value'] ) : '',
889
+ 'selected-text' => isset( $author_or_role_selected['text'] ) ? esc_attr( $author_or_role_selected['text'] ) : '',
890
+ ),
891
+ )
892
+ );
893
 
894
  // Context dropdown menu
895
  $context_values = array();
900
  if ( isset( $context_data['children'] ) ) {
901
  $child_values = array();
902
  foreach ( $context_data['children'] as $child_id => $child_value ) {
903
+ $child_values[] = array(
904
+ 'value' => $context_id . '-' . $child_id,
905
+ 'text' => $child_value,
906
+ 'parent' => $context_id,
907
+ );
908
  }
909
  }
910
  if ( isset( $context_data['label'] ) ) {
911
+ $context_values[] = array(
912
+ 'value' => $context_id,
913
+ 'text' => $context_data['label'],
914
+ 'children' => $child_values,
915
+ );
916
  }
917
  } else {
918
+ $context_values[] = array(
919
+ 'value' => $context_id,
920
+ 'text' => $context_data,
921
+ );
922
  }
923
  }
924
 
925
+ $connector_or_context_input = $form->render_field(
926
+ 'select2', array(
927
+ 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]', $option_key, $section, $name, 'connector_or_context' ) ),
928
+ 'options' => $context_values,
929
+ 'classes' => 'connector_or_context',
930
+ 'data' => array(
931
+ 'group' => 'connector',
932
+ 'placeholder' => __( 'Any Context', 'stream' ),
933
+ ),
934
+ )
935
+ );
936
 
937
+ $connector_input = $form->render_field(
938
+ 'hidden', array(
939
+ 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]', $option_key, $section, $name, 'connector' ) ),
940
+ 'value' => $connector,
941
+ 'classes' => 'connector',
942
+ )
943
+ );
944
 
945
+ $context_input = $form->render_field(
946
+ 'hidden', array(
947
+ 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]', $option_key, $section, $name, 'context' ) ),
948
+ 'value' => $context,
949
+ 'classes' => 'context',
950
+ )
951
+ );
952
 
953
  // Action dropdown menu
954
  $action_values = array();
955
 
956
  foreach ( $this->get_terms_labels( 'action' ) as $action_id => $action_data ) {
957
+ $action_values[] = array(
958
+ 'value' => $action_id,
959
+ 'text' => $action_data,
960
+ );
961
  }
962
 
963
+ $action_input = $form->render_field(
964
+ 'select2', array(
965
+ 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]', $option_key, $section, $name, 'action' ) ),
966
+ 'value' => $action,
967
+ 'options' => $action_values,
968
+ 'classes' => 'action',
969
+ 'data' => array(
970
+ 'placeholder' => __( 'Any Action', 'stream' ),
971
+ ),
972
+ )
973
+ );
974
 
975
  // IP Address input
976
+ $ip_address_input = $form->render_field(
977
+ 'select2', array(
978
+ 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]', $option_key, $section, $name, 'ip_address' ) ),
979
+ 'value' => $ip_address,
980
+ 'classes' => 'ip_address',
981
+ 'data' => array(
982
+ 'placeholder' => esc_attr__( 'Any IP Address', 'stream' ),
983
+ 'nonce' => esc_attr( wp_create_nonce( 'stream_get_ips' ) ),
984
+ ),
985
+ 'multiple' => true,
986
+ )
987
+ );
988
 
989
  // Hidden helper input
990
  $helper_input = sprintf(
1004
  <td>%10$s</td>
1005
  <th scope="row" class="actions-column">%11$s</th>
1006
  </tr>',
1007
+ ( 0 !== (int) $key % 2 ) ? 'alternate' : '',
1008
+ ( 'helper' === (string) $key ) ? 'hidden helper' : '',
1009
  '<input class="cb-select" type="checkbox" />',
1010
  $helper_input,
1011
  $author_or_role_input,
1115
  * @param array $new_value
1116
  */
1117
  public function updated_option_ttl_remove_records( $old_value, $new_value ) {
1118
+ $ttl_before = isset( $old_value['general_records_ttl'] ) ? (int) $old_value['general_records_ttl'] : - 1;
1119
+ $ttl_after = isset( $new_value['general_records_ttl'] ) ? (int) $new_value['general_records_ttl'] : - 1;
1120
 
1121
  if ( $ttl_after < $ttl_before ) {
1122
  /**
classes/class-uninstall.php CHANGED
@@ -22,7 +22,7 @@ class Uninstall {
22
  */
23
  public $user_meta;
24
 
25
- function __construct( $plugin ) {
26
  $this->plugin = $plugin;
27
 
28
  $this->user_meta = array(
@@ -197,7 +197,7 @@ class Uninstall {
197
  // Specific user meta
198
  foreach ( $this->user_meta as $meta_key ) {
199
  $wpdb->query(
200
- $wpdb->prepare( "DELETE FROM {$wpdb->usermeta} WHERE meta_key = '{$wpdb->prefix}%s';", $meta_key )
201
  );
202
  }
203
  }
22
  */
23
  public $user_meta;
24
 
25
+ public function __construct( $plugin ) {
26
  $this->plugin = $plugin;
27
 
28
  $this->user_meta = array(
197
  // Specific user meta
198
  foreach ( $this->user_meta as $meta_key ) {
199
  $wpdb->query(
200
+ $wpdb->prepare( "DELETE FROM {$wpdb->usermeta} WHERE meta_key = {$wpdb->prefix}%s;", $meta_key )
201
  );
202
  }
203
  }
connectors/class-connector-acf.php CHANGED
@@ -58,7 +58,7 @@ class Connector_ACF extends Connector {
58
  if ( class_exists( 'acf' ) ) { //TODO: Should this be function_exists?
59
  $acf = \acf();
60
  if ( version_compare( $acf->settings['version'], self::PLUGIN_MIN_VERSION, '>=' ) ) {
61
- return true;
62
  }
63
  }
64
 
@@ -133,7 +133,7 @@ class Connector_ACF extends Connector {
133
  */
134
  public function action_links( $links, $record ) {
135
  $posts_connector = new Connector_Posts();
136
- $links = $posts_connector->action_links( $links, $record );
137
 
138
  return $links;
139
  }
@@ -209,8 +209,9 @@ class Connector_ACF extends Connector {
209
  * @param mixed|null $meta_value
210
  */
211
  public function check_meta( $type, $action, $meta_id, $object_id, $meta_key, $meta_value = null ) {
212
- if ( 'post' !== $type || ! ( $post = get_post( $object_id ) ) || 'acf' !== $post->post_type ) {
213
- $this->check_meta_values( $type, $action, $meta_id, $object_id, $meta_key, $meta_value = null );
 
214
  return;
215
  }
216
 
@@ -223,6 +224,7 @@ class Connector_ACF extends Connector {
223
  }
224
 
225
  $this->log(
 
226
  esc_html_x( '"%1$s" field in "%2$s" %3$s', 'acf', 'stream' ),
227
  array(
228
  'label' => $meta_value['label'],
@@ -253,6 +255,7 @@ class Connector_ACF extends Connector {
253
  );
254
 
255
  $this->log(
 
256
  esc_html_x( 'Position of "%1$s" updated to "%2$s"', 'acf', 'stream' ),
257
  array(
258
  'title' => $post->post_title,
@@ -275,6 +278,7 @@ class Connector_ACF extends Connector {
275
  );
276
 
277
  $this->log(
 
278
  esc_html_x( 'Style of "%1$s" updated to "%2$s"', 'acf', 'stream' ),
279
  array(
280
  'title' => $post->post_title,
@@ -317,6 +321,7 @@ class Connector_ACF extends Connector {
317
  }
318
 
319
  $this->log(
 
320
  esc_html_x( '"%1$s" set to display on "%2$s"', 'acf', 'stream' ),
321
  array(
322
  'title' => $post->post_title,
@@ -369,7 +374,7 @@ class Connector_ACF extends Connector {
369
  $object_key = $taxonomy . '_' . $term_id;
370
  } elseif ( 'option' === $type ) {
371
  $object_key = 'options';
372
- $key = preg_replace( '/^options_/', '', $key );
373
  }
374
 
375
  if ( isset( $this->cached_field_values_updates[ $object_key ][ $key ] ) ) {
@@ -389,7 +394,7 @@ class Connector_ACF extends Connector {
389
  $tax_obj = get_taxonomy( $taxonomy );
390
  $type_name = strtolower( get_taxonomy_labels( $tax_obj )->singular_name );
391
  } elseif ( 'option' === $type ) {
392
- $title = 'settings page';
393
  $type_name = 'option';
394
  } else {
395
  return false;
@@ -398,6 +403,7 @@ class Connector_ACF extends Connector {
398
  $cache = $this->cached_field_values_updates[ $object_key ][ $key ];
399
 
400
  $this->log(
 
401
  esc_html_x( '"%1$s" of "%2$s" %3$s updated', 'acf', 'stream' ),
402
  array(
403
  'field_label' => $cache['field']['label'],
@@ -436,6 +442,7 @@ class Connector_ACF extends Connector {
436
  $deleted = array_diff( $old, $new );
437
 
438
  $this->log(
 
439
  esc_html_x( 'Updated rules of "%1$s" (%2$d added, %3$d deleted)', 'acf', 'stream' ),
440
  array(
441
  'title' => $post->post_title,
@@ -503,6 +510,7 @@ class Connector_ACF extends Connector {
503
  // menu_order, aka Order No.
504
  if ( $data['menu_order'] !== $post->menu_order ) {
505
  $this->log(
 
506
  esc_html_x( '"%1$s" reordered from %2$d to %3$d', 'acf', 'stream' ),
507
  array(
508
  'title' => $post->post_title,
@@ -554,6 +562,6 @@ class Connector_ACF extends Connector {
554
  * @return string
555
  */
556
  private function get_saved_option_type( $key ) {
557
- return substr( $key,0, 8 ) === 'options_' ? 'option' : 'taxonomy';
558
  }
559
  }
58
  if ( class_exists( 'acf' ) ) { //TODO: Should this be function_exists?
59
  $acf = \acf();
60
  if ( version_compare( $acf->settings['version'], self::PLUGIN_MIN_VERSION, '>=' ) ) {
61
+ return true;
62
  }
63
  }
64
 
133
  */
134
  public function action_links( $links, $record ) {
135
  $posts_connector = new Connector_Posts();
136
+ $links = $posts_connector->action_links( $links, $record );
137
 
138
  return $links;
139
  }
209
  * @param mixed|null $meta_value
210
  */
211
  public function check_meta( $type, $action, $meta_id, $object_id, $meta_key, $meta_value = null ) {
212
+ $post = get_post( $object_id );
213
+ if ( 'post' !== $type || ! $post || 'acf' !== $post->post_type ) {
214
+ $this->check_meta_values( $type, $action, $meta_id, $object_id, $meta_key, $meta_value );
215
  return;
216
  }
217
 
224
  }
225
 
226
  $this->log(
227
+ // translators: Placeholders refer to a field label, a form title, and an action (e.g. "Message", "Contact", "Created")
228
  esc_html_x( '"%1$s" field in "%2$s" %3$s', 'acf', 'stream' ),
229
  array(
230
  'label' => $meta_value['label'],
255
  );
256
 
257
  $this->log(
258
+ // translators: Placeholders refer to a form title, and a position (e.g. "Contact", "Side")
259
  esc_html_x( 'Position of "%1$s" updated to "%2$s"', 'acf', 'stream' ),
260
  array(
261
  'title' => $post->post_title,
278
  );
279
 
280
  $this->log(
281
+ // translators: Placeholders refer to a form title, and a layout (e.g. "Contact", "Seamless")
282
  esc_html_x( 'Style of "%1$s" updated to "%2$s"', 'acf', 'stream' ),
283
  array(
284
  'title' => $post->post_title,
321
  }
322
 
323
  $this->log(
324
+ // translators: Placeholders refer to a form title, and a display option (e.g. "Contact", "All screens")
325
  esc_html_x( '"%1$s" set to display on "%2$s"', 'acf', 'stream' ),
326
  array(
327
  'title' => $post->post_title,
374
  $object_key = $taxonomy . '_' . $term_id;
375
  } elseif ( 'option' === $type ) {
376
  $object_key = 'options';
377
+ $key = preg_replace( '/^options_/', '', $key );
378
  }
379
 
380
  if ( isset( $this->cached_field_values_updates[ $object_key ][ $key ] ) ) {
394
  $tax_obj = get_taxonomy( $taxonomy );
395
  $type_name = strtolower( get_taxonomy_labels( $tax_obj )->singular_name );
396
  } elseif ( 'option' === $type ) {
397
+ $title = 'settings page';
398
  $type_name = 'option';
399
  } else {
400
  return false;
403
  $cache = $this->cached_field_values_updates[ $object_key ][ $key ];
404
 
405
  $this->log(
406
+ // translators: Placeholders refer to a field label, an object title, and an object type (e.g. "Message", "Hello World", "post")
407
  esc_html_x( '"%1$s" of "%2$s" %3$s updated', 'acf', 'stream' ),
408
  array(
409
  'field_label' => $cache['field']['label'],
442
  $deleted = array_diff( $old, $new );
443
 
444
  $this->log(
445
+ // translators: Placeholders refer to a form title, the number of rules added, and the number of rules deleted (e.g. "Contact", "42", "7")
446
  esc_html_x( 'Updated rules of "%1$s" (%2$d added, %3$d deleted)', 'acf', 'stream' ),
447
  array(
448
  'title' => $post->post_title,
510
  // menu_order, aka Order No.
511
  if ( $data['menu_order'] !== $post->menu_order ) {
512
  $this->log(
513
+ // translators: Placeholders refer to a form title, a numeric position, and another numeric position (e.g. "Contact", "42", "7")
514
  esc_html_x( '"%1$s" reordered from %2$d to %3$d', 'acf', 'stream' ),
515
  array(
516
  'title' => $post->post_title,
562
  * @return string
563
  */
564
  private function get_saved_option_type( $key ) {
565
+ return substr( $key, 0, 8 ) === 'options_' ? 'option' : 'taxonomy';
566
  }
567
  }
connectors/class-connector-bbpress.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  namespace WP_Stream;
3
 
4
- class Connector_bbPress extends Connector {
5
  /**
6
  * Connector slug
7
  *
@@ -127,7 +127,7 @@ class Connector_bbPress extends Connector {
127
  */
128
  public function action_links( $links, $record ) {
129
  if ( 'settings' === $record->context ) {
130
- $option = $record->get_meta( 'option', true );
131
  $links[ esc_html__( 'Edit', 'stream' ) ] = esc_url(
132
  add_query_arg(
133
  array(
@@ -184,10 +184,12 @@ class Connector_bbPress extends Connector {
184
  } elseif ( 'posts' === $data['connector'] && in_array( $data['context'], array( 'forum', 'topic', 'reply' ), true ) ) {
185
  if ( 'reply' === $data['context'] ) {
186
  if ( 'updated' === $data['action'] ) {
187
- $data['message'] = esc_html__( 'Replied on "%1$s"', 'stream' );
 
188
  $data['args']['post_title'] = get_post( wp_get_post_parent_id( $data['object_id'] ) )->post_title;
189
  }
190
  $data['args']['post_title'] = sprintf(
 
191
  __( 'Reply to: %s', 'stream' ),
192
  get_post( wp_get_post_parent_id( $data['object_id'] ) )->post_title
193
  );
@@ -230,11 +232,12 @@ class Connector_bbPress extends Connector {
230
  $topic = get_post( $message['topic_id'] );
231
 
232
  $this->log(
 
233
  _x( '%1$s "%2$s" topic', '1: Action, 2: Topic title', 'stream' ),
234
  array(
235
  'action_title' => $actions[ $action ],
236
- 'topic_title' => $topic->post_title,
237
- 'action' => $action,
238
  ),
239
  $topic->ID,
240
  'topic',
1
  <?php
2
  namespace WP_Stream;
3
 
4
+ class Connector_BbPress extends Connector {
5
  /**
6
  * Connector slug
7
  *
127
  */
128
  public function action_links( $links, $record ) {
129
  if ( 'settings' === $record->context ) {
130
+ $option = $record->get_meta( 'option', true );
131
  $links[ esc_html__( 'Edit', 'stream' ) ] = esc_url(
132
  add_query_arg(
133
  array(
184
  } elseif ( 'posts' === $data['connector'] && in_array( $data['context'], array( 'forum', 'topic', 'reply' ), true ) ) {
185
  if ( 'reply' === $data['context'] ) {
186
  if ( 'updated' === $data['action'] ) {
187
+ // translators: Placeholder refers to a post title (e.g. "Hello World")
188
+ $data['message'] = esc_html__( 'Replied on "%1$s"', 'stream' );
189
  $data['args']['post_title'] = get_post( wp_get_post_parent_id( $data['object_id'] ) )->post_title;
190
  }
191
  $data['args']['post_title'] = sprintf(
192
+ // translators: Placeholder refers to a post title (e.g. "Hello World")
193
  __( 'Reply to: %s', 'stream' ),
194
  get_post( wp_get_post_parent_id( $data['object_id'] ) )->post_title
195
  );
232
  $topic = get_post( $message['topic_id'] );
233
 
234
  $this->log(
235
+ // translators: Placeholders refer to an action, and a topic title (e.g. "Created", "Read this first")
236
  _x( '%1$s "%2$s" topic', '1: Action, 2: Topic title', 'stream' ),
237
  array(
238
  'action_title' => $actions[ $action ],
239
+ 'topic_title' => $topic->post_title,
240
+ 'action' => $action,
241
  ),
242
  $topic->ID,
243
  'topic',
connectors/class-connector-blogs.php CHANGED
@@ -128,6 +128,7 @@ class Connector_Blogs extends Connector {
128
  $blog = get_blog_details( $blog_id );
129
 
130
  $this->log(
 
131
  _x(
132
  '"%1$s" site was created',
133
  '1. Site name',
@@ -154,6 +155,7 @@ class Connector_Blogs extends Connector {
154
  $blog = get_blog_details( $blog_id );
155
 
156
  $this->log(
 
157
  _x(
158
  '"%1$s" site was registered',
159
  '1. Site name',
@@ -187,6 +189,7 @@ class Connector_Blogs extends Connector {
187
  }
188
 
189
  $this->log(
 
190
  _x(
191
  '%1$s was added to the "%2$s" site with %3$s capabilities',
192
  '1. User\'s name, 2. Site name, 3. Role',
@@ -220,6 +223,7 @@ class Connector_Blogs extends Connector {
220
  }
221
 
222
  $this->log(
 
223
  _x(
224
  '%1$s was removed from the "%2$s" site',
225
  '1. User\'s name, 2. Site name',
@@ -354,6 +358,7 @@ class Connector_Blogs extends Connector {
354
  $blog = get_blog_details( $blog_id );
355
 
356
  $this->log(
 
357
  _x(
358
  '"%1$s" site was %2$s',
359
  '1. Site name, 2. Status',
128
  $blog = get_blog_details( $blog_id );
129
 
130
  $this->log(
131
+ // translators: Placeholder refers to site name (e.g. "FooBar Blog")
132
  _x(
133
  '"%1$s" site was created',
134
  '1. Site name',
155
  $blog = get_blog_details( $blog_id );
156
 
157
  $this->log(
158
+ // translators: Placeholder refers to site name (e.g. "FooBar Blog")
159
  _x(
160
  '"%1$s" site was registered',
161
  '1. Site name',
189
  }
190
 
191
  $this->log(
192
+ // translators: Placeholders refer to a user's display name, a site name, and a user role (e.g. "Jane Doe", "FooBar Blog", "subscriber")
193
  _x(
194
  '%1$s was added to the "%2$s" site with %3$s capabilities',
195
  '1. User\'s name, 2. Site name, 3. Role',
223
  }
224
 
225
  $this->log(
226
+ // translators: Placeholders refer to a user's display name, and a site name (e.g. "Jane Doe", "FooBar Blog")
227
  _x(
228
  '%1$s was removed from the "%2$s" site',
229
  '1. User\'s name, 2. Site name',
358
  $blog = get_blog_details( $blog_id );
359
 
360
  $this->log(
361
+ // translators: Placeholders refer to a site name, and a blog status (e.g. "FooBar Blog", "archived")
362
  _x(
363
  '"%1$s" site was %2$s',
364
  '1. Site name, 2. Status',
connectors/class-connector-buddypress.php CHANGED
@@ -1,7 +1,9 @@
1
  <?php
 
2
  namespace WP_Stream;
3
 
4
  class Connector_BuddyPress extends Connector {
 
5
  /**
6
  * Connector slug
7
  *
@@ -151,7 +153,7 @@ class Connector_BuddyPress extends Connector {
151
  *
152
  * @filter wp_stream_action_links_{connector}
153
  *
154
- * @param array $links Previous links registered
155
  * @param object $record Stream record
156
  *
157
  * @return array Action links
@@ -191,7 +193,11 @@ class Connector_BuddyPress extends Connector {
191
  );
192
  } elseif ( in_array( $record->context, array( 'groups' ), true ) ) {
193
  $group_id = $record->get_meta( 'id', true );
194
- $group = \groups_get_group( array( 'group_id' => $group_id ) );
 
 
 
 
195
 
196
  if ( $group ) {
197
  // Build actions URLs
@@ -200,13 +206,18 @@ class Connector_BuddyPress extends Connector {
200
  $edit_url = $base_url . '&amp;action=edit';
201
  $visit_url = \bp_get_group_permalink( $group );
202
 
203
- $links[ esc_html__( 'Edit group', 'stream' ) ] = $edit_url;
204
- $links[ esc_html__( 'View group', 'stream' ) ] = $visit_url;
205
  $links[ esc_html__( 'Delete group', 'stream' ) ] = $delete_url;
206
  }
207
  } elseif ( in_array( $record->context, array( 'activity' ), true ) ) {
208
  $activity_id = $record->get_meta( 'id', true );
209
- $activities = \bp_activity_get( array( 'in' => $activity_id, 'spam' => 'all' ) );
 
 
 
 
 
210
  if ( ! empty( $activities['activities'] ) ) {
211
  $activity = reset( $activities['activities'] );
212
 
@@ -230,18 +241,18 @@ class Connector_BuddyPress extends Connector {
230
  $group_id = $record->get_meta( 'group_id', true );
231
 
232
  if ( empty( $field_id ) ) { // is a group action
233
- $links[ esc_html__( 'Edit', 'stream' ) ] = add_query_arg(
234
  array(
235
- 'page' => 'bp-profile-setup',
236
- 'mode' => 'edit_group',
237
  'group_id' => $group_id,
238
  ),
239
  admin_url( 'users.php' )
240
  );
241
  $links[ esc_html__( 'Delete', 'stream' ) ] = add_query_arg(
242
  array(
243
- 'page' => 'bp-profile-setup',
244
- 'mode' => 'delete_group',
245
  'group_id' => $group_id,
246
  ),
247
  admin_url( 'users.php' )
@@ -251,10 +262,10 @@ class Connector_BuddyPress extends Connector {
251
  if ( empty( $field->type ) ) {
252
  return $links;
253
  }
254
- $links[ esc_html__( 'Edit', 'stream' ) ] = add_query_arg(
255
  array(
256
- 'page' => 'bp-profile-setup',
257
- 'mode' => 'edit_field',
258
  'group_id' => $group_id,
259
  'field_id' => $field_id,
260
  ),
@@ -262,8 +273,8 @@ class Connector_BuddyPress extends Connector {
262
  );
263
  $links[ esc_html__( 'Delete', 'stream' ) ] = add_query_arg(
264
  array(
265
- 'page' => 'bp-profile-setup',
266
- 'mode' => 'delete_field',
267
  'field_id' => $field_id,
268
  ),
269
  admin_url( 'users.php' )
@@ -356,7 +367,10 @@ class Connector_BuddyPress extends Connector {
356
  $replacement = str_replace( '-', '_', $option );
357
 
358
  if ( method_exists( $this, 'check_' . $replacement ) ) {
359
- call_user_func( array( $this, 'check_' . $replacement ), $old_value, $new_value );
 
 
 
360
  } else {
361
  $data = $this->options[ $option ];
362
  $option_title = $data['label'];
@@ -364,6 +378,7 @@ class Connector_BuddyPress extends Connector {
364
  $page = isset( $data['page'] ) ? $data['page'] : null;
365
 
366
  $this->log(
 
367
  __( '"%s" setting updated', 'stream' ),
368
  compact( 'option_title', 'option', 'old_value', 'new_value', 'page' ),
369
  null,
@@ -398,6 +413,7 @@ class Connector_BuddyPress extends Connector {
398
 
399
  $this->log(
400
  sprintf(
 
401
  __( '"%1$s" component %2$s', 'stream' ),
402
  $components[ $option ]['title'],
403
  $actions[ $option_value ]
@@ -443,6 +459,7 @@ class Connector_BuddyPress extends Connector {
443
 
444
  $this->log(
445
  sprintf(
 
446
  __( '"%1$s" page set to "%2$s"', 'stream' ),
447
  $pages[ $option ],
448
  $page
@@ -464,6 +481,7 @@ class Connector_BuddyPress extends Connector {
464
  public function callback_bp_before_activity_delete( $args ) {
465
  if ( empty( $args['id'] ) ) { // Bail if we're deleting in bulk
466
  $this->_delete_activity_args = $args;
 
467
  return;
468
  }
469
 
@@ -477,14 +495,15 @@ class Connector_BuddyPress extends Connector {
477
  $activity = $this->_deleted_activity;
478
  $this->log(
479
  sprintf(
 
480
  __( '"%s" activity deleted', 'stream' ),
481
  strip_tags( $activity->action )
482
  ),
483
  array(
484
- 'id' => $activity->id,
485
  'item_id' => $activity->item_id,
486
- 'type' => $activity->type,
487
- 'author' => $activity->user_id,
488
  ),
489
  $activity->id,
490
  $activity->component,
@@ -495,10 +514,12 @@ class Connector_BuddyPress extends Connector {
495
  // activities, so we probably don't need to track those
496
  if ( $this->ignore_activity_bulk_deletion ) {
497
  $this->ignore_activity_bulk_deletion = false;
 
498
  return;
499
  }
500
  $this->log(
501
  sprintf(
 
502
  __( '"%s" activities were deleted', 'stream' ),
503
  count( $activities_ids )
504
  ),
@@ -519,14 +540,15 @@ class Connector_BuddyPress extends Connector {
519
 
520
  $this->log(
521
  sprintf(
 
522
  __( 'Marked activity "%s" as spam', 'stream' ),
523
  strip_tags( $activity->action )
524
  ),
525
  array(
526
- 'id' => $activity->id,
527
  'item_id' => $activity->item_id,
528
- 'type' => $activity->type,
529
- 'author' => $activity->user_id,
530
  ),
531
  $activity->id,
532
  $activity->component,
@@ -539,14 +561,15 @@ class Connector_BuddyPress extends Connector {
539
 
540
  $this->log(
541
  sprintf(
 
542
  __( 'Unmarked activity "%s" as spam', 'stream' ),
543
  strip_tags( $activity->action )
544
  ),
545
  array(
546
- 'id' => $activity->id,
547
  'item_id' => $activity->item_id,
548
- 'type' => $activity->type,
549
- 'author' => $activity->user_id,
550
  ),
551
  $activity->id,
552
  $activity->component,
@@ -559,14 +582,15 @@ class Connector_BuddyPress extends Connector {
559
 
560
  $this->log(
561
  sprintf(
 
562
  __( '"%s" activity updated', 'stream' ),
563
  strip_tags( $activity->action )
564
  ),
565
  array(
566
- 'id' => $activity->id,
567
  'item_id' => $activity->item_id,
568
- 'type' => $activity->type,
569
- 'author' => $activity->user_id,
570
  ),
571
  $activity->id,
572
  'activity',
@@ -576,7 +600,11 @@ class Connector_BuddyPress extends Connector {
576
 
577
  public function group_action( $group, $action, $meta = array(), $message = null ) {
578
  if ( is_numeric( $group ) ) {
579
- $group = \groups_get_group( array( 'group_id' => $group ) );
 
 
 
 
580
  }
581
 
582
  $replacements = array(
@@ -585,23 +613,31 @@ class Connector_BuddyPress extends Connector {
585
 
586
  if ( ! $message ) {
587
  if ( 'created' === $action ) {
 
588
  $message = esc_html__( '"%s" group created', 'stream' );
589
  } elseif ( 'updated' === $action ) {
 
590
  $message = esc_html__( '"%s" group updated', 'stream' );
591
  } elseif ( 'deleted' === $action ) {
 
592
  $message = esc_html__( '"%s" group deleted', 'stream' );
593
  } elseif ( 'joined' === $action ) {
 
594
  $message = esc_html__( 'Joined group "%s"', 'stream' );
595
  } elseif ( 'left' === $action ) {
 
596
  $message = esc_html__( 'Left group "%s"', 'stream' );
597
  } elseif ( 'banned' === $action ) {
598
- $message = esc_html__( 'Banned "%2$s" from "%1$s"', 'stream' );
 
599
  $replacements[] = get_user_by( 'id', $meta['user_id'] )->display_name;
600
  } elseif ( 'unbanned' === $action ) {
601
- $message = esc_html__( 'Unbanned "%2$s" from "%1$s"', 'stream' );
 
602
  $replacements[] = get_user_by( 'id', $meta['user_id'] )->display_name;
603
  } elseif ( 'removed' === $action ) {
604
- $message = esc_html__( 'Removed "%2$s" from "%1$s"', 'stream' );
 
605
  $replacements[] = get_user_by( 'id', $meta['user_id'] )->display_name;
606
  } else {
607
  return;
@@ -615,7 +651,7 @@ class Connector_BuddyPress extends Connector {
615
  ),
616
  array_merge(
617
  array(
618
- 'id' => $group->id,
619
  'name' => $group->name,
620
  'slug' => $group->slug,
621
  ),
@@ -666,13 +702,18 @@ class Connector_BuddyPress extends Connector {
666
  }
667
 
668
  public function callback_groups_promote_member( $group_id, $user_id, $status ) {
669
- $group = \groups_get_group( array( 'group_id' => $group_id ) );
670
- $user = new \WP_User( $user_id );
671
- $roles = array(
 
 
 
 
672
  'admin' => esc_html_x( 'Administrator', 'buddypress', 'stream' ),
673
  'mod' => esc_html_x( 'Moderator', 'buddypress', 'stream' ),
674
  );
675
  $message = sprintf(
 
676
  __( 'Promoted "%1$s" to "%2$s" in "%3$s"', 'stream' ),
677
  $user->display_name,
678
  $roles[ $status ],
@@ -682,9 +723,14 @@ class Connector_BuddyPress extends Connector {
682
  }
683
 
684
  public function callback_groups_demote_member( $group_id, $user_id ) {
685
- $group = \groups_get_group( array( 'group_id' => $group_id ) );
686
- $user = new \WP_User( $user_id );
 
 
 
 
687
  $message = sprintf(
 
688
  __( 'Demoted "%1$s" to "%2$s" in "%3$s"', 'stream' ),
689
  $user->display_name,
690
  _x( 'Member', 'buddypress', 'stream' ),
@@ -712,10 +758,13 @@ class Connector_BuddyPress extends Connector {
712
 
713
  if ( ! $message ) {
714
  if ( 'created' === $action ) {
 
715
  $message = esc_html__( 'Created profile field "%s"', 'stream' );
716
  } elseif ( 'updated' === $action ) {
 
717
  $message = esc_html__( 'Updated profile field "%s"', 'stream' );
718
  } elseif ( 'deleted' === $action ) {
 
719
  $message = esc_html__( 'Deleted profile field "%s"', 'stream' );
720
  } else {
721
  return;
@@ -757,10 +806,13 @@ class Connector_BuddyPress extends Connector {
757
 
758
  if ( ! $message ) {
759
  if ( 'created' === $action ) {
 
760
  $message = esc_html__( 'Created profile field group "%s"', 'stream' );
761
  } elseif ( 'updated' === $action ) {
 
762
  $message = esc_html__( 'Updated profile field group "%s"', 'stream' );
763
  } elseif ( 'deleted' === $action ) {
 
764
  $message = esc_html__( 'Deleted profile field group "%s"', 'stream' );
765
  } else {
766
  return;
@@ -806,7 +858,7 @@ class Connector_BuddyPress extends Connector {
806
  // Only components that need directories should be listed here
807
  if ( isset( $bp->{$component_id} ) && ! empty( $bp->{$component_id}->has_directory ) ) {
808
  // component->name was introduced in BP 1.5, so we must provide a fallback
809
- $directory_pages[ $component_id ] = ! empty( $bp->{ $component_id }->name ) ? $bp->{ $component_id }->name : ucwords( $component_id );
810
  }
811
  }
812
  }
1
  <?php
2
+
3
  namespace WP_Stream;
4
 
5
  class Connector_BuddyPress extends Connector {
6
+
7
  /**
8
  * Connector slug
9
  *
153
  *
154
  * @filter wp_stream_action_links_{connector}
155
  *
156
+ * @param array $links Previous links registered
157
  * @param object $record Stream record
158
  *
159
  * @return array Action links
193
  );
194
  } elseif ( in_array( $record->context, array( 'groups' ), true ) ) {
195
  $group_id = $record->get_meta( 'id', true );
196
+ $group = \groups_get_group(
197
+ array(
198
+ 'group_id' => $group_id,
199
+ )
200
+ );
201
 
202
  if ( $group ) {
203
  // Build actions URLs
206
  $edit_url = $base_url . '&amp;action=edit';
207
  $visit_url = \bp_get_group_permalink( $group );
208
 
209
+ $links[ esc_html__( 'Edit group', 'stream' ) ] = $edit_url;
210
+ $links[ esc_html__( 'View group', 'stream' ) ] = $visit_url;
211
  $links[ esc_html__( 'Delete group', 'stream' ) ] = $delete_url;
212
  }
213
  } elseif ( in_array( $record->context, array( 'activity' ), true ) ) {
214
  $activity_id = $record->get_meta( 'id', true );
215
+ $activities = \bp_activity_get(
216
+ array(
217
+ 'in' => $activity_id,
218
+ 'spam' => 'all',
219
+ )
220
+ );
221
  if ( ! empty( $activities['activities'] ) ) {
222
  $activity = reset( $activities['activities'] );
223
 
241
  $group_id = $record->get_meta( 'group_id', true );
242
 
243
  if ( empty( $field_id ) ) { // is a group action
244
+ $links[ esc_html__( 'Edit', 'stream' ) ] = add_query_arg(
245
  array(
246
+ 'page' => 'bp-profile-setup',
247
+ 'mode' => 'edit_group',
248
  'group_id' => $group_id,
249
  ),
250
  admin_url( 'users.php' )
251
  );
252
  $links[ esc_html__( 'Delete', 'stream' ) ] = add_query_arg(
253
  array(
254
+ 'page' => 'bp-profile-setup',
255
+ 'mode' => 'delete_group',
256
  'group_id' => $group_id,
257
  ),
258
  admin_url( 'users.php' )
262
  if ( empty( $field->type ) ) {
263
  return $links;
264
  }
265
+ $links[ esc_html__( 'Edit', 'stream' ) ] = add_query_arg(
266
  array(
267
+ 'page' => 'bp-profile-setup',
268
+ 'mode' => 'edit_field',
269
  'group_id' => $group_id,
270
  'field_id' => $field_id,
271
  ),
273
  );
274
  $links[ esc_html__( 'Delete', 'stream' ) ] = add_query_arg(
275
  array(
276
+ 'page' => 'bp-profile-setup',
277
+ 'mode' => 'delete_field',
278
  'field_id' => $field_id,
279
  ),
280
  admin_url( 'users.php' )
367
  $replacement = str_replace( '-', '_', $option );
368
 
369
  if ( method_exists( $this, 'check_' . $replacement ) ) {
370
+ call_user_func( array(
371
+ $this,
372
+ 'check_' . $replacement,
373
+ ), $old_value, $new_value );
374
  } else {
375
  $data = $this->options[ $option ];
376
  $option_title = $data['label'];
378
  $page = isset( $data['page'] ) ? $data['page'] : null;
379
 
380
  $this->log(
381
+ // translators: Placeholder refers to setting name (e.g. "Group Creation")
382
  __( '"%s" setting updated', 'stream' ),
383
  compact( 'option_title', 'option', 'old_value', 'new_value', 'page' ),
384
  null,
413
 
414
  $this->log(
415
  sprintf(
416
+ // translators: Placeholder refers to component title (e.g. "Members")
417
  __( '"%1$s" component %2$s', 'stream' ),
418
  $components[ $option ]['title'],
419
  $actions[ $option_value ]
459
 
460
  $this->log(
461
  sprintf(
462
+ // translators: Placeholders refer to a directory page, and a page title (e.g. "Register", "Registration" )
463
  __( '"%1$s" page set to "%2$s"', 'stream' ),
464
  $pages[ $option ],
465
  $page
481
  public function callback_bp_before_activity_delete( $args ) {
482
  if ( empty( $args['id'] ) ) { // Bail if we're deleting in bulk
483
  $this->_delete_activity_args = $args;
484
+
485
  return;
486
  }
487
 
495
  $activity = $this->_deleted_activity;
496
  $this->log(
497
  sprintf(
498
+ // translators: Placeholder refers to an activity title (e.g. "Update")
499
  __( '"%s" activity deleted', 'stream' ),
500
  strip_tags( $activity->action )
501
  ),
502
  array(
503
+ 'id' => $activity->id,
504
  'item_id' => $activity->item_id,
505
+ 'type' => $activity->type,
506
+ 'author' => $activity->user_id,
507
  ),
508
  $activity->id,
509
  $activity->component,
514
  // activities, so we probably don't need to track those
515
  if ( $this->ignore_activity_bulk_deletion ) {
516
  $this->ignore_activity_bulk_deletion = false;
517
+
518
  return;
519
  }
520
  $this->log(
521
  sprintf(
522
+ // translators: Placeholder refers to an activity title (e.g. "Update")
523
  __( '"%s" activities were deleted', 'stream' ),
524
  count( $activities_ids )
525
  ),
540
 
541
  $this->log(
542
  sprintf(
543
+ // translators: Placeholder refers to an activity title (e.g. "Update")
544
  __( 'Marked activity "%s" as spam', 'stream' ),
545
  strip_tags( $activity->action )
546
  ),
547
  array(
548
+ 'id' => $activity->id,
549
  'item_id' => $activity->item_id,
550
+ 'type' => $activity->type,
551
+ 'author' => $activity->user_id,
552
  ),
553
  $activity->id,
554
  $activity->component,
561
 
562
  $this->log(
563
  sprintf(
564
+ // translators: Placeholder refers to an activity title (e.g. "Update")
565
  __( 'Unmarked activity "%s" as spam', 'stream' ),
566
  strip_tags( $activity->action )
567
  ),
568
  array(
569
+ 'id' => $activity->id,
570
  'item_id' => $activity->item_id,
571
+ 'type' => $activity->type,
572
+ 'author' => $activity->user_id,
573
  ),
574
  $activity->id,
575
  $activity->component,
582
 
583
  $this->log(
584
  sprintf(
585
+ // translators: Placeholder refers to an activity title (e.g. "Update")
586
  __( '"%s" activity updated', 'stream' ),
587
  strip_tags( $activity->action )
588
  ),
589
  array(
590
+ 'id' => $activity->id,
591
  'item_id' => $activity->item_id,
592
+ 'type' => $activity->type,
593
+ 'author' => $activity->user_id,
594
  ),
595
  $activity->id,
596
  'activity',
600
 
601
  public function group_action( $group, $action, $meta = array(), $message = null ) {
602
  if ( is_numeric( $group ) ) {
603
+ $group = \groups_get_group(
604
+ array(
605
+ 'group_id' => $group,
606
+ )
607
+ );
608
  }
609
 
610
  $replacements = array(
613
 
614
  if ( ! $message ) {
615
  if ( 'created' === $action ) {
616
+ // translators: Placeholder refers to a group name (e.g. "Favourites")
617
  $message = esc_html__( '"%s" group created', 'stream' );
618
  } elseif ( 'updated' === $action ) {
619
+ // translators: Placeholder refers to a group name (e.g. "Favourites")
620
  $message = esc_html__( '"%s" group updated', 'stream' );
621
  } elseif ( 'deleted' === $action ) {
622
+ // translators: Placeholder refers to a group name (e.g. "Favourites")
623
  $message = esc_html__( '"%s" group deleted', 'stream' );
624
  } elseif ( 'joined' === $action ) {
625
+ // translators: Placeholder refers to a group name (e.g. "Favourites")
626
  $message = esc_html__( 'Joined group "%s"', 'stream' );
627
  } elseif ( 'left' === $action ) {
628
+ // translators: Placeholder refers to a group name (e.g. "Favourites")
629
  $message = esc_html__( 'Left group "%s"', 'stream' );
630
  } elseif ( 'banned' === $action ) {
631
+ // translators: Placeholders refer to a user display name, and a group name (e.g. "Jane Doe", "Favourites")
632
+ $message = esc_html__( 'Banned "%2$s" from "%1$s"', 'stream' );
633
  $replacements[] = get_user_by( 'id', $meta['user_id'] )->display_name;
634
  } elseif ( 'unbanned' === $action ) {
635
+ // translators: Placeholders refer to a user display name, and a group name (e.g. "Jane Doe", "Favourites")
636
+ $message = esc_html__( 'Unbanned "%2$s" from "%1$s"', 'stream' );
637
  $replacements[] = get_user_by( 'id', $meta['user_id'] )->display_name;
638
  } elseif ( 'removed' === $action ) {
639
+ // translators: Placeholders refer to a user display name, and a group name (e.g. "Jane Doe", "Favourites")
640
+ $message = esc_html__( 'Removed "%2$s" from "%1$s"', 'stream' );
641
  $replacements[] = get_user_by( 'id', $meta['user_id'] )->display_name;
642
  } else {
643
  return;
651
  ),
652
  array_merge(
653
  array(
654
+ 'id' => $group->id,
655
  'name' => $group->name,
656
  'slug' => $group->slug,
657
  ),
702
  }
703
 
704
  public function callback_groups_promote_member( $group_id, $user_id, $status ) {
705
+ $group = \groups_get_group(
706
+ array(
707
+ 'group_id' => $group_id,
708
+ )
709
+ );
710
+ $user = new \WP_User( $user_id );
711
+ $roles = array(
712
  'admin' => esc_html_x( 'Administrator', 'buddypress', 'stream' ),
713
  'mod' => esc_html_x( 'Moderator', 'buddypress', 'stream' ),
714
  );
715
  $message = sprintf(
716
+ // translators: Placeholders refer to a user's display name, a user role, and a group name (e.g. "Jane Doe", "subscriber", "Favourites")
717
  __( 'Promoted "%1$s" to "%2$s" in "%3$s"', 'stream' ),
718
  $user->display_name,
719
  $roles[ $status ],
723
  }
724
 
725
  public function callback_groups_demote_member( $group_id, $user_id ) {
726
+ $group = \groups_get_group(
727
+ array(
728
+ 'group_id' => $group_id,
729
+ )
730
+ );
731
+ $user = new \WP_User( $user_id );
732
  $message = sprintf(
733
+ // translators: Placeholders refer to a user's display name, a user role, and a group name (e.g. "Jane Doe", "Member", "Favourites")
734
  __( 'Demoted "%1$s" to "%2$s" in "%3$s"', 'stream' ),
735
  $user->display_name,
736
  _x( 'Member', 'buddypress', 'stream' ),
758
 
759
  if ( ! $message ) {
760
  if ( 'created' === $action ) {
761
+ // translators: Placeholder refers to a user profile field (e.g. "Job Title")
762
  $message = esc_html__( 'Created profile field "%s"', 'stream' );
763
  } elseif ( 'updated' === $action ) {
764
+ // translators: Placeholder refers to a user profile field (e.g. "Job Title")
765
  $message = esc_html__( 'Updated profile field "%s"', 'stream' );
766
  } elseif ( 'deleted' === $action ) {
767
+ // translators: Placeholder refers to a user profile field (e.g. "Job Title")
768
  $message = esc_html__( 'Deleted profile field "%s"', 'stream' );
769
  } else {
770
  return;
806
 
807
  if ( ! $message ) {
808
  if ( 'created' === $action ) {
809
+ // translators: Placeholder refers to a user profile field group (e.g. "Appearance")
810
  $message = esc_html__( 'Created profile field group "%s"', 'stream' );
811
  } elseif ( 'updated' === $action ) {
812
+ // translators: Placeholder refers to a user profile field group (e.g. "Appearance")
813
  $message = esc_html__( 'Updated profile field group "%s"', 'stream' );
814
  } elseif ( 'deleted' === $action ) {
815
+ // translators: Placeholder refers to a user profile field group (e.g. "Appearance")
816
  $message = esc_html__( 'Deleted profile field group "%s"', 'stream' );
817
  } else {
818
  return;
858
  // Only components that need directories should be listed here
859
  if ( isset( $bp->{$component_id} ) && ! empty( $bp->{$component_id}->has_directory ) ) {
860
  // component->name was introduced in BP 1.5, so we must provide a fallback
861
+ $directory_pages[ $component_id ] = ! empty( $bp->{$component_id}->name ) ? $bp->{$component_id}->name : ucwords( $component_id );
862
  }
863
  }
864
  }
connectors/class-connector-comments.php CHANGED
@@ -127,7 +127,8 @@ class Connector_Comments extends Connector {
127
  */
128
  public function action_links( $links, $record ) {
129
  if ( $record->object_id ) {
130
- if ( $comment = get_comment( $record->object_id ) ) {
 
131
  $approve_nonce = wp_create_nonce( "approve-comment_$comment->comment_ID" );
132
 
133
  $links[ esc_html__( 'Edit', 'stream' ) ] = admin_url( "comment.php?action=editcomment&c=$comment->comment_ID" );
@@ -226,6 +227,7 @@ class Connector_Comments extends Connector {
226
  }
227
 
228
  $this->log(
 
229
  __( 'Comment flooding by %s detected and prevented', 'stream' ),
230
  compact( 'user_name', 'user_id', 'time_lastcomment', 'time_newcomment' ),
231
  null,
@@ -251,7 +253,8 @@ class Connector_Comments extends Connector {
251
  $user_name = $this->get_comment_author( $comment, 'name' );
252
  $post_id = $comment->comment_post_ID;
253
  $post_type = get_post_type( $post_id );
254
- $post_title = ( $post = get_post( $post_id ) ) ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
 
255
  $comment_status = ( 1 === $comment->comment_approved ) ? esc_html__( 'approved automatically', 'stream' ) : esc_html__( 'pending approval', 'stream' );
256
  $is_spam = false;
257
 
@@ -267,19 +270,19 @@ class Connector_Comments extends Connector {
267
  }
268
  }
269
 
270
- $comment_type = mb_strtolower( $this->get_comment_type_label( $comment_id ) );
271
 
272
  if ( $comment->comment_parent ) {
273
- $parent_user_id = get_comment_author( $comment->comment_parent, 'id' );
274
- $parent_user_name = get_comment_author( $comment->comment_parent, 'name' );
275
 
276
  $this->log(
 
277
  _x(
278
  'Reply to %1$s\'s %5$s by %2$s on %3$s %4$s',
279
  "1: Parent comment's author, 2: Comment author, 3: Post title, 4: Comment status, 5: Comment type",
280
  'stream'
281
  ),
282
- compact( 'parent_user_name', 'user_name', 'post_title', 'comment_status', 'comment_type', 'post_id', 'parent_user_id' ),
283
  $comment_id,
284
  $post_type,
285
  'replied',
@@ -287,6 +290,7 @@ class Connector_Comments extends Connector {
287
  );
288
  } else {
289
  $this->log(
 
290
  _x(
291
  'New %4$s by %1$s on %2$s %3$s',
292
  '1: Comment author, 2: Post title 3: Comment status, 4: Comment type',
@@ -319,10 +323,12 @@ class Connector_Comments extends Connector {
319
  $user_name = $this->get_comment_author( $comment, 'name' );
320
  $post_id = $comment->comment_post_ID;
321
  $post_type = get_post_type( $post_id );
322
- $post_title = ( $post = get_post( $post_id ) ) ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
 
323
  $comment_type = mb_strtolower( $this->get_comment_type_label( $comment_id ) );
324
 
325
  $this->log(
 
326
  _x(
327
  '%1$s\'s %3$s on %2$s edited',
328
  '1: Comment author, 2: Post title, 3: Comment type',
@@ -383,7 +389,8 @@ class Connector_Comments extends Connector {
383
  $user_name = $this->get_comment_author( $comment, 'name' );
384
  $post_id = absint( $comment->comment_post_ID );
385
  $post_type = get_post_type( $post_id );
386
- $post_title = ( $post = get_post( $post_id ) ) ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
 
387
  $comment_type = mb_strtolower( $this->get_comment_type_label( $comment_id ) );
388
 
389
  if ( $this->delete_post === $post_id ) {
@@ -391,6 +398,7 @@ class Connector_Comments extends Connector {
391
  }
392
 
393
  $this->log(
 
394
  _x(
395
  '%1$s\'s %3$s on %2$s deleted permanently',
396
  '1: Comment author, 2: Post title, 3: Comment type',
@@ -421,10 +429,12 @@ class Connector_Comments extends Connector {
421
  $user_name = $this->get_comment_author( $comment, 'name' );
422
  $post_id = $comment->comment_post_ID;
423
  $post_type = get_post_type( $post_id );
424
- $post_title = ( $post = get_post( $post_id ) ) ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
 
425
  $comment_type = mb_strtolower( $this->get_comment_type_label( $comment_id ) );
426
 
427
  $this->log(
 
428
  _x(
429
  '%1$s\'s %3$s on %2$s trashed',
430
  '1: Comment author, 2: Post title, 3: Comment type',
@@ -455,10 +465,12 @@ class Connector_Comments extends Connector {
455
  $user_name = $this->get_comment_author( $comment, 'name' );
456
  $post_id = $comment->comment_post_ID;
457
  $post_type = get_post_type( $post_id );
458
- $post_title = ( $post = get_post( $post_id ) ) ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
 
459
  $comment_type = mb_strtolower( $this->get_comment_type_label( $comment_id ) );
460
 
461
  $this->log(
 
462
  _x(
463
  '%1$s\'s %3$s on %2$s restored',
464
  '1: Comment author, 2: Post title, 3: Comment type',
@@ -489,10 +501,12 @@ class Connector_Comments extends Connector {
489
  $user_name = $this->get_comment_author( $comment, 'name' );
490
  $post_id = $comment->comment_post_ID;
491
  $post_type = get_post_type( $post_id );
492
- $post_title = ( $post = get_post( $post_id ) ) ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
 
493
  $comment_type = mb_strtolower( $this->get_comment_type_label( $comment_id ) );
494
 
495
  $this->log(
 
496
  _x(
497
  '%1$s\'s %3$s on %2$s marked as spam',
498
  '1: Comment author, 2: Post title, 3: Comment type',
@@ -523,10 +537,12 @@ class Connector_Comments extends Connector {
523
  $user_name = $this->get_comment_author( $comment, 'name' );
524
  $post_id = $comment->comment_post_ID;
525
  $post_type = get_post_type( $post_id );
526
- $post_title = ( $post = get_post( $post_id ) ) ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
 
527
  $comment_type = mb_strtolower( $this->get_comment_type_label( $comment_id ) );
528
 
529
  $this->log(
 
530
  _x(
531
  '%1$s\'s %3$s on %2$s unmarked as spam',
532
  '1: Comment author, 2: Post title, 3: Comment type',
@@ -561,10 +577,12 @@ class Connector_Comments extends Connector {
561
  $user_name = $this->get_comment_author( $comment, 'name' );
562
  $post_id = $comment->comment_post_ID;
563
  $post_type = get_post_type( $post_id );
564
- $post_title = ( $post = get_post( $post_id ) ) ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
 
565
  $comment_type = get_comment_type( $comment->comment_ID );
566
 
567
  $this->log(
 
568
  _x(
569
  '%1$s\'s %3$s %2$s',
570
  'Comment status transition. 1: Comment author, 2: Post title, 3: Comment type',
@@ -599,10 +617,12 @@ class Connector_Comments extends Connector {
599
  $user_name = $this->get_comment_author( $comment, 'name' );
600
  $post_id = $comment->comment_post_ID;
601
  $post_type = get_post_type( $post_id );
602
- $post_title = ( $post = get_post( $post_id ) ) ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
 
603
  $comment_type = mb_strtolower( $this->get_comment_type_label( $comment_id ) );
604
 
605
  $this->log(
 
606
  _x(
607
  'Duplicate %3$s by %1$s prevented on %2$s',
608
  '1: Comment author, 2: Post title, 3: Comment type',
127
  */
128
  public function action_links( $links, $record ) {
129
  if ( $record->object_id ) {
130
+ $comment = get_comment( $record->object_id );
131
+ if ( $comment ) {
132
  $approve_nonce = wp_create_nonce( "approve-comment_$comment->comment_ID" );
133
 
134
  $links[ esc_html__( 'Edit', 'stream' ) ] = admin_url( "comment.php?action=editcomment&c=$comment->comment_ID" );
227
  }
228
 
229
  $this->log(
230
+ // translators: Placeholder refers to a username (e.g. "administrator")
231
  __( 'Comment flooding by %s detected and prevented', 'stream' ),
232
  compact( 'user_name', 'user_id', 'time_lastcomment', 'time_newcomment' ),
233
  null,
253
  $user_name = $this->get_comment_author( $comment, 'name' );
254
  $post_id = $comment->comment_post_ID;
255
  $post_type = get_post_type( $post_id );
256
+ $post = get_post( $post_id );
257
+ $post_title = $post ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
258
  $comment_status = ( 1 === $comment->comment_approved ) ? esc_html__( 'approved automatically', 'stream' ) : esc_html__( 'pending approval', 'stream' );
259
  $is_spam = false;
260
 
270
  }
271
  }
272
 
273
+ $comment_type = mb_strtolower( $this->get_comment_type_label( $comment_id ) );
274
 
275
  if ( $comment->comment_parent ) {
276
+ $parent_user_name = get_comment_author( $comment->comment_parent );
 
277
 
278
  $this->log(
279
+ // translators: Placeholders refer to a parent comment's author, a comment author, a post title, a comment status, and a comment type
280
  _x(
281
  'Reply to %1$s\'s %5$s by %2$s on %3$s %4$s',
282
  "1: Parent comment's author, 2: Comment author, 3: Post title, 4: Comment status, 5: Comment type",
283
  'stream'
284
  ),
285
+ compact( 'parent_user_name', 'user_name', 'post_title', 'comment_status', 'comment_type', 'post_id' ),
286
  $comment_id,
287
  $post_type,
288
  'replied',
290
  );
291
  } else {
292
  $this->log(
293
+ // translators: Placeholders refer to a comment author, a post title, a comment status, and a comment type
294
  _x(
295
  'New %4$s by %1$s on %2$s %3$s',
296
  '1: Comment author, 2: Post title 3: Comment status, 4: Comment type',
323
  $user_name = $this->get_comment_author( $comment, 'name' );
324
  $post_id = $comment->comment_post_ID;
325
  $post_type = get_post_type( $post_id );
326
+ $post = get_post( $post_id );
327
+ $post_title = $post ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
328
  $comment_type = mb_strtolower( $this->get_comment_type_label( $comment_id ) );
329
 
330
  $this->log(
331
+ // translators: Placeholders refer to a comment author, a post title, and a comment type
332
  _x(
333
  '%1$s\'s %3$s on %2$s edited',
334
  '1: Comment author, 2: Post title, 3: Comment type',
389
  $user_name = $this->get_comment_author( $comment, 'name' );
390
  $post_id = absint( $comment->comment_post_ID );
391
  $post_type = get_post_type( $post_id );
392
+ $post = get_post( $post_id );
393
+ $post_title = $post ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
394
  $comment_type = mb_strtolower( $this->get_comment_type_label( $comment_id ) );
395
 
396
  if ( $this->delete_post === $post_id ) {
398
  }
399
 
400
  $this->log(
401
+ // translators: Placeholders refer to a comment author, a post title, and a comment type
402
  _x(
403
  '%1$s\'s %3$s on %2$s deleted permanently',
404
  '1: Comment author, 2: Post title, 3: Comment type',
429
  $user_name = $this->get_comment_author( $comment, 'name' );
430
  $post_id = $comment->comment_post_ID;
431
  $post_type = get_post_type( $post_id );
432
+ $post = get_post( $post_id );
433
+ $post_title = $post ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
434
  $comment_type = mb_strtolower( $this->get_comment_type_label( $comment_id ) );
435
 
436
  $this->log(
437
+ // translators: Placeholders refer to a comment author, a post title, and a comment type
438
  _x(
439
  '%1$s\'s %3$s on %2$s trashed',
440
  '1: Comment author, 2: Post title, 3: Comment type',
465
  $user_name = $this->get_comment_author( $comment, 'name' );
466
  $post_id = $comment->comment_post_ID;
467
  $post_type = get_post_type( $post_id );
468
+ $post = get_post( $post_id );
469
+ $post_title = $post ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
470
  $comment_type = mb_strtolower( $this->get_comment_type_label( $comment_id ) );
471
 
472
  $this->log(
473
+ // translators: Placeholders refer to a comment author, a post title, and a comment type
474
  _x(
475
  '%1$s\'s %3$s on %2$s restored',
476
  '1: Comment author, 2: Post title, 3: Comment type',
501
  $user_name = $this->get_comment_author( $comment, 'name' );
502
  $post_id = $comment->comment_post_ID;
503
  $post_type = get_post_type( $post_id );
504
+ $post = get_post( $post_id );
505
+ $post_title = $post ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
506
  $comment_type = mb_strtolower( $this->get_comment_type_label( $comment_id ) );
507
 
508
  $this->log(
509
+ // translators: Placeholders refer to a comment author, a post title, and a comment type
510
  _x(
511
  '%1$s\'s %3$s on %2$s marked as spam',
512
  '1: Comment author, 2: Post title, 3: Comment type',
537
  $user_name = $this->get_comment_author( $comment, 'name' );
538
  $post_id = $comment->comment_post_ID;
539
  $post_type = get_post_type( $post_id );
540
+ $post = get_post( $post_id );
541
+ $post_title = $post ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
542
  $comment_type = mb_strtolower( $this->get_comment_type_label( $comment_id ) );
543
 
544
  $this->log(
545
+ // translators: Placeholders refer to a comment author, a post title, and a comment type
546
  _x(
547
  '%1$s\'s %3$s on %2$s unmarked as spam',
548
  '1: Comment author, 2: Post title, 3: Comment type',
577
  $user_name = $this->get_comment_author( $comment, 'name' );
578
  $post_id = $comment->comment_post_ID;
579
  $post_type = get_post_type( $post_id );
580
+ $post = get_post( $post_id );
581
+ $post_title = $post ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
582
  $comment_type = get_comment_type( $comment->comment_ID );
583
 
584
  $this->log(
585
+ // translators: Placeholders refer to a comment author, a post title, and a comment type
586
  _x(
587
  '%1$s\'s %3$s %2$s',
588
  'Comment status transition. 1: Comment author, 2: Post title, 3: Comment type',
617
  $user_name = $this->get_comment_author( $comment, 'name' );
618
  $post_id = $comment->comment_post_ID;
619
  $post_type = get_post_type( $post_id );
620
+ $post = get_post( $post_id );
621
+ $post_title = $post ? "\"$post->post_title\"" : esc_html__( 'a post', 'stream' );
622
  $comment_type = mb_strtolower( $this->get_comment_type_label( $comment_id ) );
623
 
624
  $this->log(
625
+ // translators: Placeholders refer to a comment author, a post title, and a comment type
626
  _x(
627
  'Duplicate %3$s by %1$s prevented on %2$s',
628
  '1: Comment author, 2: Post title, 3: Comment type',
connectors/class-connector-edd.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WP_Stream;
3
 
4
  class Connector_EDD extends Connector {
@@ -141,19 +142,20 @@ class Connector_EDD extends Connector {
141
  *
142
  * @filter wp_stream_action_links_{connector}
143
  *
144
- * @param array $links Previous links registered
145
- * @param object $record Stream record
146
  *
147
  * @return array Action links
148
  */
149
  public function action_links( $links, $record ) {
150
  if ( in_array( $record->context, array( 'downloads' ), true ) ) {
151
  $posts_connector = new Connector_Posts();
152
- $links = $posts_connector->action_links( $links, $record );
153
  } elseif ( in_array( $record->context, array( 'discounts' ), true ) ) {
154
  $post_type_label = get_post_type_labels( get_post_type_object( 'edd_discount' ) )->singular_name;
155
  $base = admin_url( 'edit.php?post_type=download&page=edd-discounts' );
156
 
 
157
  $links[ sprintf( esc_html__( 'Edit %s', 'stream' ), $post_type_label ) ] = add_query_arg(
158
  array(
159
  'edd-action' => 'edit_discount',
@@ -163,6 +165,7 @@ class Connector_EDD extends Connector {
163
  );
164
 
165
  if ( 'active' === get_post( $record->object_id )->post_status ) {
 
166
  $links[ sprintf( esc_html__( 'Deactivate %s', 'stream' ), $post_type_label ) ] = add_query_arg(
167
  array(
168
  'edd-action' => 'deactivate_discount',
@@ -171,6 +174,7 @@ class Connector_EDD extends Connector {
171
  $base
172
  );
173
  } else {
 
174
  $links[ sprintf( esc_html__( 'Activate %s', 'stream' ), $post_type_label ) ] = add_query_arg(
175
  array(
176
  'edd-action' => 'activate_discount',
@@ -179,18 +183,44 @@ class Connector_EDD extends Connector {
179
  $base
180
  );
181
  }
182
- } elseif ( in_array( $record->context, array( 'download_category', 'download_tag' ), true ) ) {
 
 
 
183
  $tax_label = get_taxonomy_labels( get_taxonomy( $record->context ) )->singular_name;
 
184
  $links[ sprintf( esc_html__( 'Edit %s', 'stream' ), $tax_label ) ] = get_edit_term_link( $record->object_id, $record->get_meta( 'taxonomy', true ) );
185
  } elseif ( 'api_keys' === $record->context ) {
186
  $user = new \WP_User( $record->object_id );
187
 
188
  if ( apply_filters( 'edd_api_log_requests', true ) ) {
189
- $links[ esc_html__( 'View API Log', 'stream' ) ] = add_query_arg( array( 'view' => 'api_requests', 'post_type' => 'download', 'page' => 'edd-reports', 'tab' => 'logs', 's' => $user->user_email ), 'edit.php' );
 
 
 
 
 
 
 
 
190
  }
191
 
192
- $links[ esc_html__( 'Revoke', 'stream' ) ] = add_query_arg( array( 'post_type' => 'download', 'user_id' => $record->object_id, 'edd_action' => 'process_api_key', 'edd_api_process' => 'revoke' ), 'edit.php' );
193
- $links[ esc_html__( 'Reissue', 'stream' ) ] = add_query_arg( array( 'post_type' => 'download', 'user_id' => $record->object_id, 'edd_action' => 'process_api_key', 'edd_api_process' => 'regenerate' ), 'edit.php' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  }
195
 
196
  return $links;
@@ -238,13 +268,17 @@ class Connector_EDD extends Connector {
238
  $replacement = str_replace( '-', '_', $option );
239
 
240
  if ( method_exists( $this, 'check_' . $replacement ) ) {
241
- call_user_func( array( $this, 'check_' . $replacement ), $old_value, $new_value );
 
 
 
242
  } else {
243
  $data = $this->options[ $option ];
244
  $option_title = $data['label'];
245
  $context = isset( $data['context'] ) ? $data['context'] : 'settings';
246
 
247
  $this->log(
 
248
  __( '"%s" setting updated', 'stream' ),
249
  compact( 'option_title', 'option', 'old_value', 'new_value' ),
250
  null,
@@ -275,7 +309,7 @@ class Connector_EDD extends Connector {
275
  $field = array(
276
  'name' => esc_html_x( 'Banned emails', 'edd', 'stream' ),
277
  );
278
- $tab = 'general';
279
  } else {
280
  foreach ( $settings as $tab => $fields ) {
281
  if ( isset( $fields[ $option ] ) ) {
@@ -290,6 +324,7 @@ class Connector_EDD extends Connector {
290
  }
291
 
292
  $this->log(
 
293
  __( '"%s" setting updated', 'stream' ),
294
  array(
295
  'option_title' => $field['name'],
@@ -319,7 +354,7 @@ class Connector_EDD extends Connector {
319
 
320
  if ( 'posts' === $data['connector'] && 'download' === $data['context'] ) {
321
  // Download posts operations
322
- $data['context'] = 'downloads';
323
  $data['connector'] = $this->name;
324
  } elseif ( 'posts' === $data['connector'] && 'edd_discount' === $data['context'] ) {
325
  // Discount posts operations
@@ -328,10 +363,11 @@ class Connector_EDD extends Connector {
328
  }
329
 
330
  if ( 'deleted' === $data['action'] ) {
 
331
  $data['message'] = esc_html__( '"%1s" discount deleted', 'stream' );
332
  }
333
 
334
- $data['context'] = 'discounts';
335
  $data['connector'] = $this->name;
336
  } elseif ( 'posts' === $data['connector'] && 'edd_payment' === $data['context'] ) {
337
  // Payment posts operations
@@ -360,6 +396,7 @@ class Connector_EDD extends Connector {
360
 
361
  $this->log(
362
  sprintf(
 
363
  __( '"%1$s" discount %2$s', 'stream' ),
364
  get_post( $code_id )->post_title,
365
  'active' === $new_status ? esc_html__( 'activated', 'stream' ) : esc_html__( 'deactivated', 'stream' )
@@ -377,20 +414,26 @@ class Connector_EDD extends Connector {
377
  private function callback_edd_generate_pdf() {
378
  $this->report_generated( 'pdf' );
379
  }
 
380
  public function callback_edd_earnings_export() {
381
  $this->report_generated( 'earnings' );
382
  }
 
383
  public function callback_edd_payment_export() {
384
  $this->report_generated( 'payments' );
385
  }
 
386
  public function callback_edd_email_export() {
387
  $this->report_generated( 'emails' );
388
  }
 
389
  public function callback_edd_downloads_history_export() {
390
  $this->report_generated( 'download-history' );
391
  }
392
 
393
  private function report_generated( $type ) {
 
 
394
  if ( 'pdf' === $type ) {
395
  $label = esc_html__( 'Sales and Earnings', 'stream' );
396
  } elseif ( 'earnings' ) {
@@ -405,6 +448,7 @@ class Connector_EDD extends Connector {
405
 
406
  $this->log(
407
  sprintf(
 
408
  __( 'Generated %s report', 'stream' ),
409
  $label
410
  ),
@@ -461,7 +505,10 @@ class Connector_EDD extends Connector {
461
  return false;
462
  }
463
 
464
- return call_user_func( array( $this, 'meta_' . $key ), $object_id, $value, $is_add );
 
 
 
465
  }
466
 
467
  private function meta_edd_user_public_key( $user_id, $value, $is_add = false ) {
@@ -478,6 +525,7 @@ class Connector_EDD extends Connector {
478
 
479
  $this->log(
480
  sprintf(
 
481
  __( 'User API Key %s', 'stream' ),
482
  $action_title
483
  ),
1
  <?php
2
+
3
  namespace WP_Stream;
4
 
5
  class Connector_EDD extends Connector {
142
  *
143
  * @filter wp_stream_action_links_{connector}
144
  *
145
+ * @param array $links Previous links registered
146
+ * @param object $record Stream record
147
  *
148
  * @return array Action links
149
  */
150
  public function action_links( $links, $record ) {
151
  if ( in_array( $record->context, array( 'downloads' ), true ) ) {
152
  $posts_connector = new Connector_Posts();
153
+ $links = $posts_connector->action_links( $links, $record );
154
  } elseif ( in_array( $record->context, array( 'discounts' ), true ) ) {
155
  $post_type_label = get_post_type_labels( get_post_type_object( 'edd_discount' ) )->singular_name;
156
  $base = admin_url( 'edit.php?post_type=download&page=edd-discounts' );
157
 
158
+ // translators: Placeholder refers to a post type (e.g. "Post")
159
  $links[ sprintf( esc_html__( 'Edit %s', 'stream' ), $post_type_label ) ] = add_query_arg(
160
  array(
161
  'edd-action' => 'edit_discount',
165
  );
166
 
167
  if ( 'active' === get_post( $record->object_id )->post_status ) {
168
+ // translators: Placeholder refers to a post type (e.g. "Post")
169
  $links[ sprintf( esc_html__( 'Deactivate %s', 'stream' ), $post_type_label ) ] = add_query_arg(
170
  array(
171
  'edd-action' => 'deactivate_discount',
174
  $base
175
  );
176
  } else {
177
+ // translators: Placeholder refers to a post type (e.g. "Post")
178
  $links[ sprintf( esc_html__( 'Activate %s', 'stream' ), $post_type_label ) ] = add_query_arg(
179
  array(
180
  'edd-action' => 'activate_discount',
183
  $base
184
  );
185
  }
186
+ } elseif ( in_array( $record->context, array(
187
+ 'download_category',
188
+ 'download_tag',
189
+ ), true ) ) {
190
  $tax_label = get_taxonomy_labels( get_taxonomy( $record->context ) )->singular_name;
191
+ // translators: Placeholder refers to a taxonomy (e.g. "Category")
192
  $links[ sprintf( esc_html__( 'Edit %s', 'stream' ), $tax_label ) ] = get_edit_term_link( $record->object_id, $record->get_meta( 'taxonomy', true ) );
193
  } elseif ( 'api_keys' === $record->context ) {
194
  $user = new \WP_User( $record->object_id );
195
 
196
  if ( apply_filters( 'edd_api_log_requests', true ) ) {
197
+ $links[ esc_html__( 'View API Log', 'stream' ) ] = add_query_arg(
198
+ array(
199
+ 'view' => 'api_requests',
200
+ 'post_type' => 'download',
201
+ 'page' => 'edd-reports',
202
+ 'tab' => 'logs',
203
+ 's' => $user->user_email,
204
+ ), 'edit.php'
205
+ );
206
  }
207
 
208
+ $links[ esc_html__( 'Revoke', 'stream' ) ] = add_query_arg(
209
+ array(
210
+ 'post_type' => 'download',
211
+ 'user_id' => $record->object_id,
212
+ 'edd_action' => 'process_api_key',
213
+ 'edd_api_process' => 'revoke',
214
+ ), 'edit.php'
215
+ );
216
+ $links[ esc_html__( 'Reissue', 'stream' ) ] = add_query_arg(
217
+ array(
218
+ 'post_type' => 'download',
219
+ 'user_id' => $record->object_id,
220
+ 'edd_action' => 'process_api_key',
221
+ 'edd_api_process' => 'regenerate',
222
+ ), 'edit.php'
223
+ );
224
  }
225
 
226
  return $links;
268
  $replacement = str_replace( '-', '_', $option );
269
 
270
  if ( method_exists( $this, 'check_' . $replacement ) ) {
271
+ call_user_func( array(
272
+ $this,
273
+ 'check_' . $replacement,
274
+ ), $old_value, $new_value );
275
  } else {
276
  $data = $this->options[ $option ];
277
  $option_title = $data['label'];
278
  $context = isset( $data['context'] ) ? $data['context'] : 'settings';
279
 
280
  $this->log(
281
+ // translators: Placeholder refers to a setting title (e.g. "Language")
282
  __( '"%s" setting updated', 'stream' ),
283
  compact( 'option_title', 'option', 'old_value', 'new_value' ),
284
  null,
309
  $field = array(
310
  'name' => esc_html_x( 'Banned emails', 'edd', 'stream' ),
311
  );
312
+ $tab = 'general';
313
  } else {
314
  foreach ( $settings as $tab => $fields ) {
315
  if ( isset( $fields[ $option ] ) ) {
324
  }
325
 
326
  $this->log(
327
+ // translators: Placeholder refers to a setting title (e.g. "Language")
328
  __( '"%s" setting updated', 'stream' ),
329
  array(
330
  'option_title' => $field['name'],
354
 
355
  if ( 'posts' === $data['connector'] && 'download' === $data['context'] ) {
356
  // Download posts operations
357
+ $data['context'] = 'downloads';
358
  $data['connector'] = $this->name;
359
  } elseif ( 'posts' === $data['connector'] && 'edd_discount' === $data['context'] ) {
360
  // Discount posts operations
363
  }
364
 
365
  if ( 'deleted' === $data['action'] ) {
366
+ // translators: Placeholder refers to a discount title (e.g. "Mother's Day")
367
  $data['message'] = esc_html__( '"%1s" discount deleted', 'stream' );
368
  }
369
 
370
+ $data['context'] = 'discounts';
371
  $data['connector'] = $this->name;
372
  } elseif ( 'posts' === $data['connector'] && 'edd_payment' === $data['context'] ) {
373
  // Payment posts operations
396
 
397
  $this->log(
398
  sprintf(
399
+ // translators: Placeholders refer to a discount title, and a status (e.g. "Mother's Day", "activated")
400
  __( '"%1$s" discount %2$s', 'stream' ),
401
  get_post( $code_id )->post_title,
402
  'active' === $new_status ? esc_html__( 'activated', 'stream' ) : esc_html__( 'deactivated', 'stream' )
414
  private function callback_edd_generate_pdf() {
415
  $this->report_generated( 'pdf' );
416
  }
417
+
418
  public function callback_edd_earnings_export() {
419
  $this->report_generated( 'earnings' );
420
  }
421
+
422
  public function callback_edd_payment_export() {
423
  $this->report_generated( 'payments' );
424
  }
425
+
426
  public function callback_edd_email_export() {
427
  $this->report_generated( 'emails' );
428
  }
429
+
430
  public function callback_edd_downloads_history_export() {
431
  $this->report_generated( 'download-history' );
432
  }
433
 
434
  private function report_generated( $type ) {
435
+ $label = '';
436
+
437
  if ( 'pdf' === $type ) {
438
  $label = esc_html__( 'Sales and Earnings', 'stream' );
439
  } elseif ( 'earnings' ) {
448
 
449
  $this->log(
450
  sprintf(
451
+ // translators: Placeholder refers to a report title (e.g. "Sales and Earnings")
452
  __( 'Generated %s report', 'stream' ),
453
  $label
454
  ),
505
  return false;
506
  }
507
 
508
+ return call_user_func( array(
509
+ $this,
510
+ 'meta_' . $key,
511
+ ), $object_id, $value, $is_add );
512
  }
513
 
514
  private function meta_edd_user_public_key( $user_id, $value, $is_add = false ) {
525
 
526
  $this->log(
527
  sprintf(
528
+ // translators: Placeholder refers to a status (e.g. "revoked")
529
  __( 'User API Key %s', 'stream' ),
530
  $action_title
531
  ),
connectors/class-connector-editor.php CHANGED
@@ -115,6 +115,7 @@ class Connector_Editor extends Connector {
115
  * @return string Translated string
116
  */
117
  public function get_message() {
 
118
  return _x(
119
  '"%1$s" in "%2$s" updated',
120
  '1: File name, 2: Theme/plugin name',
@@ -146,15 +147,15 @@ class Connector_Editor extends Connector {
146
  if ( $theme_exists ) {
147
  $links[ esc_html__( 'Edit File', 'stream' ) ] = add_query_arg(
148
  array(
149
- 'theme' => urlencode( $theme_slug ),
150
- 'file' => urlencode( $file_name ),
151
  ),
152
  self_admin_url( 'theme-editor.php' )
153
  );
154
 
155
  $links[ esc_html__( 'Theme Details', 'stream' ) ] = add_query_arg(
156
  array(
157
- 'theme' => urlencode( $theme_slug ),
158
  ),
159
  self_admin_url( 'themes.php' )
160
  );
@@ -163,8 +164,8 @@ class Connector_Editor extends Connector {
163
  if ( $plugin_exists ) {
164
  $links[ esc_html__( 'Edit File', 'stream' ) ] = add_query_arg(
165
  array(
166
- 'plugin' => urlencode( $plugin_slug ),
167
- 'file' => urlencode( str_ireplace( trailingslashit( WP_PLUGIN_DIR ), '', $file_path ) ),
168
  ),
169
  self_admin_url( 'plugin-editor.php' )
170
  );
@@ -194,12 +195,14 @@ class Connector_Editor extends Connector {
194
  return;
195
  }
196
 
197
- if ( $slug = wp_stream_filter_input( INPUT_POST, 'theme' ) ) {
198
- $this->edited_file = $this->get_theme_data( $slug );
 
199
  }
200
 
201
- if ( $slug = wp_stream_filter_input( INPUT_POST, 'plugin' ) ) {
202
- $this->edited_file = $this->get_plugin_data( $slug );
 
203
  }
204
  }
205
 
@@ -231,15 +234,13 @@ class Connector_Editor extends Connector {
231
  $file_path = sprintf( '%s/%s', $theme->get_stylesheet_directory(), $file_name );
232
  }
233
 
234
- //TODO: phpcs fix
235
- $file_contents_before = file_get_contents( $file_path );
236
-
237
- $name = $theme->get( 'Name' );
238
 
239
  $output = compact(
240
  'file_name',
241
  'file_path',
242
- 'file_contents_before',
243
  'slug',
244
  'name'
245
  );
@@ -259,11 +260,8 @@ class Connector_Editor extends Connector {
259
  $slug = current( explode( '/', $slug ) );
260
  $file_name = wp_stream_filter_input( INPUT_POST, 'file' );
261
  $file_path = WP_PLUGIN_DIR . '/' . $file_name;
262
-
263
- //TODO: phpcs fix
264
- $file_contents_before = file_get_contents( $file_path );
265
-
266
- $plugins = get_plugins();
267
 
268
  foreach ( $plugins as $key => $plugin_data ) {
269
  if ( 0 === strpos( $key, $slug ) ) {
@@ -279,7 +277,7 @@ class Connector_Editor extends Connector {
279
  $output = compact(
280
  'file_name',
281
  'file_path',
282
- 'file_contents_before',
283
  'slug',
284
  'name'
285
  );
@@ -293,7 +291,7 @@ class Connector_Editor extends Connector {
293
  public function log_changes( $location ) {
294
  if ( ! empty( $this->edited_file ) ) {
295
  // TODO: phpcs fix
296
- if ( file_get_contents( $this->edited_file['file_path'] ) !== $this->edited_file['file_contents_before'] ) {
297
  $context = $this->get_context( $location );
298
 
299
  switch ( $context ) {
115
  * @return string Translated string
116
  */
117
  public function get_message() {
118
+ // translators: Placeholders refer to a file name, and a theme / plugin name (e.g. "index.php", "Stream")
119
  return _x(
120
  '"%1$s" in "%2$s" updated',
121
  '1: File name, 2: Theme/plugin name',
147
  if ( $theme_exists ) {
148
  $links[ esc_html__( 'Edit File', 'stream' ) ] = add_query_arg(
149
  array(
150
+ 'theme' => rawurlencode( $theme_slug ),
151
+ 'file' => rawurlencode( $file_name ),
152
  ),
153
  self_admin_url( 'theme-editor.php' )
154
  );
155
 
156
  $links[ esc_html__( 'Theme Details', 'stream' ) ] = add_query_arg(
157
  array(
158
+ 'theme' => rawurlencode( $theme_slug ),
159
  ),
160
  self_admin_url( 'themes.php' )
161
  );
164
  if ( $plugin_exists ) {
165
  $links[ esc_html__( 'Edit File', 'stream' ) ] = add_query_arg(
166
  array(
167
+ 'plugin' => rawurlencode( $plugin_slug ),
168
+ 'file' => rawurlencode( str_ireplace( trailingslashit( WP_PLUGIN_DIR ), '', $file_path ) ),
169
  ),
170
  self_admin_url( 'plugin-editor.php' )
171
  );
195
  return;
196
  }
197
 
198
+ $theme_slug = wp_stream_filter_input( INPUT_POST, 'theme' );
199
+ if ( $theme_slug ) {
200
+ $this->edited_file = $this->get_theme_data( $theme_slug );
201
  }
202
 
203
+ $plugin_slug = wp_stream_filter_input( INPUT_POST, 'plugin' );
204
+ if ( $plugin_slug ) {
205
+ $this->edited_file = $this->get_plugin_data( $plugin_slug );
206
  }
207
  }
208
 
234
  $file_path = sprintf( '%s/%s', $theme->get_stylesheet_directory(), $file_name );
235
  }
236
 
237
+ $file_md5 = md5_file( $file_path );
238
+ $name = $theme->get( 'Name' );
 
 
239
 
240
  $output = compact(
241
  'file_name',
242
  'file_path',
243
+ 'file_md5',
244
  'slug',
245
  'name'
246
  );
260
  $slug = current( explode( '/', $slug ) );
261
  $file_name = wp_stream_filter_input( INPUT_POST, 'file' );
262
  $file_path = WP_PLUGIN_DIR . '/' . $file_name;
263
+ $file_md5 = md5_file( $file_path );
264
+ $plugins = get_plugins();
 
 
 
265
 
266
  foreach ( $plugins as $key => $plugin_data ) {
267
  if ( 0 === strpos( $key, $slug ) ) {
277
  $output = compact(
278
  'file_name',
279
  'file_path',
280
+ 'file_md5',
281
  'slug',
282
  'name'
283
  );
291
  public function log_changes( $location ) {
292
  if ( ! empty( $this->edited_file ) ) {
293
  // TODO: phpcs fix
294
+ if ( md5_file( $this->edited_file['file_path'] ) !== $this->edited_file['file_md5'] ) {
295
  $context = $this->get_context( $location );
296
 
297
  switch ( $context ) {
connectors/class-connector-gravityforms.php CHANGED
@@ -140,7 +140,7 @@ class Connector_GravityForms extends Connector {
140
  $links[ esc_html__( 'Edit', 'stream' ) ] = add_query_arg(
141
  array(
142
  'page' => 'gf_edit_forms',
143
- 'id' => $record->object_id,
144
  ),
145
  admin_url( 'admin.php' )
146
  );
@@ -180,16 +180,16 @@ class Connector_GravityForms extends Connector {
180
  parent::register();
181
 
182
  $this->options = array(
183
- 'rg_gforms_disable_css' => array(
184
  'label' => esc_html_x( 'Output CSS', 'gravityforms', 'stream' ),
185
  ),
186
- 'rg_gforms_enable_html5' => array(
187
  'label' => esc_html_x( 'Output HTML5', 'gravityforms', 'stream' ),
188
  ),
189
- 'gform_enable_noconflict' => array(
190
  'label' => esc_html_x( 'No-Conflict Mode', 'gravityforms', 'stream' ),
191
  ),
192
- 'rg_gforms_currency' => array(
193
  'label' => esc_html_x( 'Currency', 'gravityforms', 'stream' ),
194
  ),
195
  'rg_gforms_captcha_public_key' => array(
@@ -215,6 +215,7 @@ class Connector_GravityForms extends Connector {
215
 
216
  $this->log(
217
  sprintf(
 
218
  __( '"%1$s" form %2$s', 'stream' ),
219
  $title,
220
  $is_new ? esc_html__( 'created', 'stream' ) : esc_html__( 'updated', 'stream' )
@@ -245,6 +246,7 @@ class Connector_GravityForms extends Connector {
245
 
246
  $this->log(
247
  sprintf(
 
248
  __( '"%1$s" confirmation %2$s for "%3$s"', 'stream' ),
249
  $confirmation['name'],
250
  $is_new ? esc_html__( 'created', 'stream' ) : esc_html__( 'updated', 'stream' ),
@@ -277,6 +279,7 @@ class Connector_GravityForms extends Connector {
277
 
278
  $this->log(
279
  sprintf(
 
280
  __( '"%1$s" notification %2$s for "%3$s"', 'stream' ),
281
  $notification['name'],
282
  $is_new ? esc_html__( 'created', 'stream' ) : esc_html__( 'updated', 'stream' ),
@@ -304,6 +307,7 @@ class Connector_GravityForms extends Connector {
304
  public function callback_gform_pre_notification_deleted( $notification, $form ) {
305
  $this->log(
306
  sprintf(
 
307
  __( '"%1$s" notification deleted from "%2$s"', 'stream' ),
308
  $notification['name'],
309
  $form['title']
@@ -328,6 +332,7 @@ class Connector_GravityForms extends Connector {
328
  public function callback_gform_pre_confirmation_deleted( $confirmation, $form ) {
329
  $this->log(
330
  sprintf(
 
331
  __( '"%1$s" confirmation deleted from "%2$s"', 'stream' ),
332
  $confirmation['name'],
333
  $form['title']
@@ -353,6 +358,7 @@ class Connector_GravityForms extends Connector {
353
  public function callback_gform_confirmation_status( $confirmation, $form, $is_active ) {
354
  $this->log(
355
  sprintf(
 
356
  __( '"%1$s" confirmation %2$s from "%3$s"', 'stream' ),
357
  $confirmation['name'],
358
  $is_active ? esc_html__( 'activated', 'stream' ) : esc_html__( 'deactivated', 'stream' ),
@@ -380,6 +386,7 @@ class Connector_GravityForms extends Connector {
380
  public function callback_gform_notification_status( $notification, $form, $is_active ) {
381
  $this->log(
382
  sprintf(
 
383
  __( '"%1$s" notification %2$s from "%3$s"', 'stream' ),
384
  $notification['name'],
385
  $is_active ? esc_html__( 'activated', 'stream' ) : esc_html__( 'deactivated', 'stream' ),
@@ -433,6 +440,7 @@ class Connector_GravityForms extends Connector {
433
  $context = isset( $data['context'] ) ? $data['context'] : 'settings';
434
 
435
  $this->log(
 
436
  __( '"%s" setting updated', 'stream' ),
437
  compact( 'option_title', 'option', 'old_value', 'new_value' ),
438
  null,
@@ -448,6 +456,7 @@ class Connector_GravityForms extends Connector {
448
 
449
  $this->log(
450
  sprintf(
 
451
  __( 'Gravity Forms license key %s', 'stream' ),
452
  $is_update ? esc_html__( 'updated', 'stream' ) : esc_html__( 'deleted', 'stream' )
453
  ),
@@ -461,12 +470,13 @@ class Connector_GravityForms extends Connector {
461
  public function callback_gform_post_export_entries( $form, $start_date, $end_date, $fields ) {
462
  unset( $fields );
463
  $this->log(
 
464
  __( '"%s" form entries exported', 'stream' ),
465
  array(
466
  'form_title' => $form['title'],
467
  'form_id' => $form['id'],
468
  'start_date' => empty( $start_date ) ? null : $start_date,
469
- 'end_date' => empty( $end_date ) ? null : $end_date,
470
  ),
471
  $form['id'],
472
  'export',
@@ -476,15 +486,14 @@ class Connector_GravityForms extends Connector {
476
 
477
  public function callback_gform_forms_post_import( $forms ) {
478
  $forms_total = count( $forms );
479
- $forms_label = ( 1 === $forms_total ) ? 'form' : 'forms';
480
  $forms_ids = wp_list_pluck( $forms, 'id' );
481
  $forms_titles = wp_list_pluck( $forms, 'title' );
482
 
483
  $this->log(
484
- __( '%1$d %2$s imported', 'stream' ),
 
485
  array(
486
  'count' => $forms_total,
487
- 'label' => $forms_label,
488
  'ids' => $forms_ids,
489
  'titles' => $forms_titles,
490
  ),
@@ -498,6 +507,7 @@ class Connector_GravityForms extends Connector {
498
  $form = $this->get_form( $form_id );
499
 
500
  $this->log(
 
501
  __( '"%s" form exported', 'stream' ),
502
  array(
503
  'form_title' => $form['title'],
@@ -516,7 +526,8 @@ class Connector_GravityForms extends Connector {
516
  $titles = wp_list_pluck( $forms, 'title' );
517
 
518
  $this->log(
519
- __( 'Export process started for %d forms', 'stream' ),
 
520
  array(
521
  'count' => count( $forms ),
522
  'ids' => $ids,
@@ -535,6 +546,7 @@ class Connector_GravityForms extends Connector {
535
  $form = $this->get_form( $lead['form_id'] );
536
 
537
  $this->log(
 
538
  __( 'Lead #%1$d from "%2$s" deleted', 'stream' ),
539
  array(
540
  'lead_id' => $lead_id,
@@ -557,6 +569,7 @@ class Connector_GravityForms extends Connector {
557
  $form = $this->get_form( $lead['form_id'] );
558
 
559
  $this->log(
 
560
  __( 'Note #%1$d added to lead #%2$d on "%3$s" form', 'stream' ),
561
  array(
562
  'note_id' => $note_id,
@@ -575,6 +588,7 @@ class Connector_GravityForms extends Connector {
575
  $form = $this->get_form( $lead['form_id'] );
576
 
577
  $this->log(
 
578
  __( 'Note #%1$d deleted from lead #%2$d on "%3$s" form', 'stream' ),
579
  array(
580
  'note_id' => $note_id,
@@ -609,6 +623,7 @@ class Connector_GravityForms extends Connector {
609
 
610
  $this->log(
611
  sprintf(
 
612
  __( 'Lead #%1$d %2$s on "%3$s" form', 'stream' ),
613
  $lead_id,
614
  $actions[ $status ],
@@ -641,6 +656,7 @@ class Connector_GravityForms extends Connector {
641
 
642
  $this->log(
643
  sprintf(
 
644
  __( 'Entry #%1$d marked as %2$s on form #%3$d ("%4$s")', 'stream' ),
645
  $lead_id,
646
  $status,
@@ -674,6 +690,7 @@ class Connector_GravityForms extends Connector {
674
 
675
  $this->log(
676
  sprintf(
 
677
  __( 'Entry #%1$d %2$s on form #%3$d ("%4$s")', 'stream' ),
678
  $lead_id,
679
  $status,
@@ -788,6 +805,7 @@ class Connector_GravityForms extends Connector {
788
 
789
  $this->log(
790
  sprintf(
 
791
  __( 'Form #%1$d ("%2$s") %3$s', 'stream' ),
792
  $form_id,
793
  $form['title'],
140
  $links[ esc_html__( 'Edit', 'stream' ) ] = add_query_arg(
141
  array(
142
  'page' => 'gf_edit_forms',
143
+ 'id' => $record->object_id,
144
  ),
145
  admin_url( 'admin.php' )
146
  );
180
  parent::register();
181
 
182
  $this->options = array(
183
+ 'rg_gforms_disable_css' => array(
184
  'label' => esc_html_x( 'Output CSS', 'gravityforms', 'stream' ),
185
  ),
186
+ 'rg_gforms_enable_html5' => array(
187
  'label' => esc_html_x( 'Output HTML5', 'gravityforms', 'stream' ),
188
  ),
189
+ 'gform_enable_noconflict' => array(
190
  'label' => esc_html_x( 'No-Conflict Mode', 'gravityforms', 'stream' ),
191
  ),
192
+ 'rg_gforms_currency' => array(
193
  'label' => esc_html_x( 'Currency', 'gravityforms', 'stream' ),
194
  ),
195
  'rg_gforms_captcha_public_key' => array(
215
 
216
  $this->log(
217
  sprintf(
218
+ // translators: Placeholders refer to a form title, and a status (e.g. "Contact Form", "created")
219
  __( '"%1$s" form %2$s', 'stream' ),
220
  $title,
221
  $is_new ? esc_html__( 'created', 'stream' ) : esc_html__( 'updated', 'stream' )
246
 
247
  $this->log(
248
  sprintf(
249
+ // translators: Placeholders refer to a confirmation name, a status, and a form title (e.g. "Email", "created", "Contact Form")
250
  __( '"%1$s" confirmation %2$s for "%3$s"', 'stream' ),
251
  $confirmation['name'],
252
  $is_new ? esc_html__( 'created', 'stream' ) : esc_html__( 'updated', 'stream' ),
279
 
280
  $this->log(
281
  sprintf(
282
+ // translators: Placeholders refer to a notification name, a status, and a form title (e.g. "Email", "created", "Contact Form")
283
  __( '"%1$s" notification %2$s for "%3$s"', 'stream' ),
284
  $notification['name'],
285
  $is_new ? esc_html__( 'created', 'stream' ) : esc_html__( 'updated', 'stream' ),
307
  public function callback_gform_pre_notification_deleted( $notification, $form ) {
308
  $this->log(
309
  sprintf(
310
+ // translators: Placeholders refer to a notification name, and a form title (e.g. "Email", "Contact Form")
311
  __( '"%1$s" notification deleted from "%2$s"', 'stream' ),
312
  $notification['name'],
313
  $form['title']
332
  public function callback_gform_pre_confirmation_deleted( $confirmation, $form ) {
333
  $this->log(
334
  sprintf(
335
+ // translators: Placeholders refer to a confirmation name, and a form title (e.g. "Email", "Contact Form")
336
  __( '"%1$s" confirmation deleted from "%2$s"', 'stream' ),
337
  $confirmation['name'],
338
  $form['title']
358
  public function callback_gform_confirmation_status( $confirmation, $form, $is_active ) {
359
  $this->log(
360
  sprintf(
361
+ // translators: Placeholders refer to a confirmation name, a status, and a form title (e.g. "Email", "activated", "Contact Form")
362
  __( '"%1$s" confirmation %2$s from "%3$s"', 'stream' ),
363
  $confirmation['name'],
364
  $is_active ? esc_html__( 'activated', 'stream' ) : esc_html__( 'deactivated', 'stream' ),
386
  public function callback_gform_notification_status( $notification, $form, $is_active ) {
387
  $this->log(
388
  sprintf(
389
+ // translators: Placeholders refer to a notification name, a status, and a form title (e.g. "Email", "activated", "Contact Form")
390
  __( '"%1$s" notification %2$s from "%3$s"', 'stream' ),
391
  $notification['name'],
392
  $is_active ? esc_html__( 'activated', 'stream' ) : esc_html__( 'deactivated', 'stream' ),
440
  $context = isset( $data['context'] ) ? $data['context'] : 'settings';
441
 
442
  $this->log(
443
+ // translators: Placeholder refers to a setting title (e.g. "Language")
444
  __( '"%s" setting updated', 'stream' ),
445
  compact( 'option_title', 'option', 'old_value', 'new_value' ),
446
  null,
456
 
457
  $this->log(
458
  sprintf(
459
+ // translators: Placeholder refers to a status (e.g. "updated")
460
  __( 'Gravity Forms license key %s', 'stream' ),
461
  $is_update ? esc_html__( 'updated', 'stream' ) : esc_html__( 'deleted', 'stream' )
462
  ),
470
  public function callback_gform_post_export_entries( $form, $start_date, $end_date, $fields ) {
471
  unset( $fields );
472
  $this->log(
473
+ // translators: Placeholder refers to a form title (e.g. "Contact Form")
474
  __( '"%s" form entries exported', 'stream' ),
475
  array(
476
  'form_title' => $form['title'],
477
  'form_id' => $form['id'],
478
  'start_date' => empty( $start_date ) ? null : $start_date,
479
+ 'end_date' => empty( $end_date ) ? null : $end_date,
480
  ),
481
  $form['id'],
482
  'export',
486
 
487
  public function callback_gform_forms_post_import( $forms ) {
488
  $forms_total = count( $forms );
 
489
  $forms_ids = wp_list_pluck( $forms, 'id' );
490
  $forms_titles = wp_list_pluck( $forms, 'title' );
491
 
492
  $this->log(
493
+ // translators: Placeholder refers to a number of forms (e.g. "42")
494
+ _n( '%d form imported', '%d forms imported', $forms_total, 'stream' ),
495
  array(
496
  'count' => $forms_total,
 
497
  'ids' => $forms_ids,
498
  'titles' => $forms_titles,
499
  ),
507
  $form = $this->get_form( $form_id );
508
 
509
  $this->log(
510
+ // translators: Placeholder refers to a form title (e.g. "Contact Form")
511
  __( '"%s" form exported', 'stream' ),
512
  array(
513
  'form_title' => $form['title'],
526
  $titles = wp_list_pluck( $forms, 'title' );
527
 
528
  $this->log(
529
+ // translators: Placeholder refers to a number of forms (e.g. "42")
530
+ _n( 'Export process started for %d form', 'Export process started for %d forms', count( $forms ), 'stream' ),
531
  array(
532
  'count' => count( $forms ),
533
  'ids' => $ids,
546
  $form = $this->get_form( $lead['form_id'] );
547
 
548
  $this->log(
549
+ // translators: Placeholders refer to an ID, and a form title (e.g. "42", "Contact Form")
550
  __( 'Lead #%1$d from "%2$s" deleted', 'stream' ),
551
  array(
552
  'lead_id' => $lead_id,
569
  $form = $this->get_form( $lead['form_id'] );
570
 
571
  $this->log(
572
+ // translators: Placeholders refer to an ID, another ID, and a form title (e.g. "42", "7", "Contact Form")
573
  __( 'Note #%1$d added to lead #%2$d on "%3$s" form', 'stream' ),
574
  array(
575
  'note_id' => $note_id,
588
  $form = $this->get_form( $lead['form_id'] );
589
 
590
  $this->log(
591
+ // translators: Placeholders refer to an ID, another ID, and a form title (e.g. "42", "7", "Contact Form")
592
  __( 'Note #%1$d deleted from lead #%2$d on "%3$s" form', 'stream' ),
593
  array(
594
  'note_id' => $note_id,
623
 
624
  $this->log(
625
  sprintf(
626
+ // translators: Placeholders refer to an ID, a status, and a form title (e.g. "42", "activated", "Contact Form")
627
  __( 'Lead #%1$d %2$s on "%3$s" form', 'stream' ),
628
  $lead_id,
629
  $actions[ $status ],
656
 
657
  $this->log(
658
  sprintf(
659
+ // translators: Placeholders refer to an ID, a status, and a form title (e.g. "42", "unread", "Contact Form")
660
  __( 'Entry #%1$d marked as %2$s on form #%3$d ("%4$s")', 'stream' ),
661
  $lead_id,
662
  $status,
690
 
691
  $this->log(
692
  sprintf(
693
+ // translators: Placeholders refer to an ID, a status, and a form title (e.g. "42", "starred", "Contact Form")
694
  __( 'Entry #%1$d %2$s on form #%3$d ("%4$s")', 'stream' ),
695
  $lead_id,
696
  $status,
805
 
806
  $this->log(
807
  sprintf(
808
+ // translators: Placeholders refer to an ID, a form title, and a status (e.g. "42", "Contact Form", "Activated")
809
  __( 'Form #%1$d ("%2$s") %3$s', 'stream' ),
810
  $form_id,
811
  $form['title'],
connectors/class-connector-installer.php CHANGED
@@ -81,7 +81,7 @@ class Connector_Installer extends Connector {
81
  * @return array Action links
82
  */
83
  public function action_links( $links, $record ) {
84
- if ( 'wordpress' === $record->context && 'updated' === $record->action ) {
85
  global $wp_version;
86
 
87
  $version = $record->get_meta( 'new_version', true );
@@ -168,16 +168,18 @@ class Connector_Installer extends Connector {
168
  $version = $theme->version;
169
  }
170
 
171
- $action = 'installed';
 
172
  $message = _x(
173
  'Installed %1$s: %2$s %3$s',
174
  'Plugin/theme installation. 1: Type (plugin/theme), 2: Plugin/theme name, 3: Plugin/theme version',
175
  'stream'
176
  );
177
 
178
- $logs[] = compact( 'slug', 'name', 'version', 'message', 'action' );
179
  } elseif ( 'update' === $action ) {
180
- $action = 'updated';
 
181
  $message = _x(
182
  'Updated %1$s: %2$s %3$s',
183
  'Plugin/theme update. 1: Type (plugin/theme), 2: Plugin/theme name, 3: Plugin/theme version',
@@ -211,7 +213,11 @@ class Connector_Installer extends Connector {
211
  foreach ( $slugs as $slug ) {
212
  $theme = wp_get_theme( $slug );
213
  $stylesheet = $theme['Stylesheet Dir'] . '/style.css';
214
- $theme_data = get_file_data( $stylesheet, array( 'Version' => 'Version' ) );
 
 
 
 
215
  $name = $theme['Name'];
216
  $old_version = $theme['Version'];
217
  $version = $theme_data['Version'];
@@ -251,6 +257,7 @@ class Connector_Installer extends Connector {
251
  $network_wide = $network_wide ? esc_html__( 'network wide', 'stream' ) : null;
252
 
253
  $this->log(
 
254
  _x(
255
  '"%1$s" plugin activated %2$s',
256
  '1: Plugin name, 2: Single site or network wide',
@@ -269,6 +276,7 @@ class Connector_Installer extends Connector {
269
  $network_wide = $network_wide ? esc_html__( 'network wide', 'stream' ) : null;
270
 
271
  $this->log(
 
272
  _x(
273
  '"%1$s" plugin deactivated %2$s',
274
  '1: Plugin name, 2: Single site or network wide',
@@ -284,6 +292,7 @@ class Connector_Installer extends Connector {
284
  public function callback_switch_theme( $name, $theme ) {
285
  unset( $theme );
286
  $this->log(
 
287
  __( '"%s" theme activated', 'stream' ),
288
  compact( 'name' ),
289
  null,
@@ -296,7 +305,7 @@ class Connector_Installer extends Connector {
296
  * @todo Core needs a delete_theme hook
297
  */
298
  public function callback_delete_site_transient_update_themes() {
299
- $backtrace = debug_backtrace();
300
  $delete_theme_call = null;
301
 
302
  foreach ( $backtrace as $call ) {
@@ -314,6 +323,7 @@ class Connector_Installer extends Connector {
314
  // @todo Can we get the name of the theme? Or has it already been eliminated
315
 
316
  $this->log(
 
317
  __( '"%s" theme deleted', 'stream' ),
318
  compact( 'name' ),
319
  null,
@@ -361,11 +371,8 @@ class Connector_Installer extends Connector {
361
  * @todo This does not work in WP-CLI
362
  */
363
  public function callback_pre_set_site_transient_update_plugins( $value ) {
364
- if (
365
- ! wp_stream_filter_input( INPUT_POST, 'verify-delete' )
366
- ||
367
- ! ( $plugins_to_delete = get_option( 'wp_stream_plugins_to_delete' ) )
368
- ) {
369
  return $value;
370
  }
371
 
@@ -374,6 +381,7 @@ class Connector_Installer extends Connector {
374
  $network_wide = $data['Network'] ? esc_html__( 'network wide', 'stream' ) : '';
375
 
376
  $this->log(
 
377
  __( '"%s" plugin deleted', 'stream' ),
378
  compact( 'name', 'plugin', 'network_wide' ),
379
  null,
@@ -394,8 +402,10 @@ class Connector_Installer extends Connector {
394
  $auto_updated = ( 'update-core.php' !== $pagenow );
395
 
396
  if ( $auto_updated ) {
 
397
  $message = esc_html__( 'WordPress auto-updated to %s', 'stream' );
398
  } else {
 
399
  $message = esc_html__( 'WordPress updated to %s', 'stream' );
400
  }
401
 
@@ -403,7 +413,7 @@ class Connector_Installer extends Connector {
403
  $message,
404
  compact( 'new_version', 'old_version', 'auto_updated' ),
405
  null,
406
- 'wordpress',
407
  'updated'
408
  );
409
  }
81
  * @return array Action links
82
  */
83
  public function action_links( $links, $record ) {
84
+ if ( 'WordPress' === $record->context && 'updated' === $record->action ) {
85
  global $wp_version;
86
 
87
  $version = $record->get_meta( 'new_version', true );
168
  $version = $theme->version;
169
  }
170
 
171
+ $action = 'installed';
172
+ // translators: Placeholders refer to a plugin/theme type, a plugin/theme name, and a plugin/theme version (e.g. "plugin", "Stream", "4.2")
173
  $message = _x(
174
  'Installed %1$s: %2$s %3$s',
175
  'Plugin/theme installation. 1: Type (plugin/theme), 2: Plugin/theme name, 3: Plugin/theme version',
176
  'stream'
177
  );
178
 
179
+ $logs[] = compact( 'slug', 'name', 'version', 'message', 'action' );
180
  } elseif ( 'update' === $action ) {
181
+ $action = 'updated';
182
+ // translators: Placeholders refer to a plugin/theme type, a plugin/theme name, and a plugin/theme version (e.g. "plugin", "Stream", "4.2")
183
  $message = _x(
184
  'Updated %1$s: %2$s %3$s',
185
  'Plugin/theme update. 1: Type (plugin/theme), 2: Plugin/theme name, 3: Plugin/theme version',
213
  foreach ( $slugs as $slug ) {
214
  $theme = wp_get_theme( $slug );
215
  $stylesheet = $theme['Stylesheet Dir'] . '/style.css';
216
+ $theme_data = get_file_data(
217
+ $stylesheet, array(
218
+ 'Version' => 'Version',
219
+ )
220
+ );
221
  $name = $theme['Name'];
222
  $old_version = $theme['Version'];
223
  $version = $theme_data['Version'];
257
  $network_wide = $network_wide ? esc_html__( 'network wide', 'stream' ) : null;
258
 
259
  $this->log(
260
+ // translators: Placeholders refer to a plugin name, and whether it is on a single site or network wide (e.g. "Stream", "network wide") (a single site results in a blank string)
261
  _x(
262
  '"%1$s" plugin activated %2$s',
263
  '1: Plugin name, 2: Single site or network wide',
276
  $network_wide = $network_wide ? esc_html__( 'network wide', 'stream' ) : null;
277
 
278
  $this->log(
279
+ // translators: Placeholders refer to a plugin name, and whether it is on a single site or network wide (e.g. "Stream", "network wide") (a single site results in a blank string)
280
  _x(
281
  '"%1$s" plugin deactivated %2$s',
282
  '1: Plugin name, 2: Single site or network wide',
292
  public function callback_switch_theme( $name, $theme ) {
293
  unset( $theme );
294
  $this->log(
295
+ // translators: Placeholder refers to a theme name (e.g. "Twenty Seventeen")
296
  __( '"%s" theme activated', 'stream' ),
297
  compact( 'name' ),
298
  null,
305
  * @todo Core needs a delete_theme hook
306
  */
307
  public function callback_delete_site_transient_update_themes() {
308
+ $backtrace = debug_backtrace(); // @codingStandardsIgnoreLine This is used as a hack to determine a theme was deleted.
309
  $delete_theme_call = null;
310
 
311
  foreach ( $backtrace as $call ) {
323
  // @todo Can we get the name of the theme? Or has it already been eliminated
324
 
325
  $this->log(
326
+ // translators: Placeholder refers to a theme name (e.g. "Twenty Seventeen")
327
  __( '"%s" theme deleted', 'stream' ),
328
  compact( 'name' ),
329
  null,
371
  * @todo This does not work in WP-CLI
372
  */
373
  public function callback_pre_set_site_transient_update_plugins( $value ) {
374
+ $plugins_to_delete = get_option( 'wp_stream_plugins_to_delete' );
375
+ if ( ! wp_stream_filter_input( INPUT_POST, 'verify-delete' ) || ! $plugins_to_delete ) {
 
 
 
376
  return $value;
377
  }
378
 
381
  $network_wide = $data['Network'] ? esc_html__( 'network wide', 'stream' ) : '';
382
 
383
  $this->log(
384
+ // translators: Placeholder refers to a plugin name (e.g. "Stream")
385
  __( '"%s" plugin deleted', 'stream' ),
386
  compact( 'name', 'plugin', 'network_wide' ),
387
  null,
402
  $auto_updated = ( 'update-core.php' !== $pagenow );
403
 
404
  if ( $auto_updated ) {
405
+ // translators: Placeholder refers to a version number (e.g. "4.2")
406
  $message = esc_html__( 'WordPress auto-updated to %s', 'stream' );
407
  } else {
408
+ // translators: Placeholder refers to a version number (e.g. "4.2")
409
  $message = esc_html__( 'WordPress updated to %s', 'stream' );
410
  }
411
 
413
  $message,
414
  compact( 'new_version', 'old_version', 'auto_updated' ),
415
  null,
416
+ 'WordPress',
417
  'updated'
418
  );
419
  }
connectors/class-connector-jetpack.php CHANGED
@@ -193,21 +193,21 @@ class Connector_Jetpack extends Connector {
193
  add_filter( 'wp_stream_log_data', array( $this, 'log_override' ) );
194
 
195
  $this->options = array(
196
- 'jetpack_options' => null,
197
  // Sharing module
198
- 'hide_gplus' => null,
199
- 'gplus_authors' => null,
200
- 'sharing-options' => array(
201
  'label' => esc_html__( 'Sharing options', 'stream' ),
202
  'context' => 'sharedaddy',
203
  ),
204
- 'sharedaddy_disable_resources' => null,
205
- 'jetpack-twitter-cards-site-tag' => array(
206
  'label' => esc_html__( 'Twitter site tag', 'stream' ),
207
  'context' => 'sharedaddy',
208
  ),
209
  // Stats module
210
- 'stats_options' => array(
211
  'label' => esc_html__( 'WordPress.com Stats', 'stream' ),
212
  'context' => 'stats',
213
  ),
@@ -217,16 +217,16 @@ class Connector_Jetpack extends Connector {
217
  'context' => 'jetpack-comments',
218
  ),
219
  // Likes
220
- 'disabled_likes' => array(
221
  'label' => esc_html__( 'WP.com Site-wide Likes', 'stream' ),
222
  'context' => 'likes',
223
  ),
224
  // Mobile
225
- 'wp_mobile_excerpt' => array(
226
  'label' => esc_html__( 'Excerpts appearance', 'stream' ),
227
  'context' => 'minileven',
228
  ),
229
- 'wp_mobile_app_promos' => array(
230
  'label' => esc_html__( 'App promos', 'stream' ),
231
  'context' => 'minileven',
232
  ),
@@ -234,25 +234,25 @@ class Connector_Jetpack extends Connector {
234
 
235
  $this->options_override = array(
236
  // Carousel Module
237
- 'carousel_background_color' => array(
238
  'label' => esc_html__( 'Background color', 'stream' ),
239
  'context' => 'carousel',
240
  ),
241
- 'carousel_display_exif' => array(
242
  'label' => esc_html__( 'Metadata', 'stream' ),
243
  'context' => 'carousel',
244
  ),
245
  // Subscriptions
246
- 'stb_enabled' => array(
247
  'label' => esc_html__( 'Follow blog comment form button', 'stream' ),
248
  'context' => 'subscriptions',
249
  ),
250
- 'stc_enabled' => array(
251
  'label' => esc_html__( 'Follow comments form button', 'stream' ),
252
  'context' => 'subscriptions',
253
  ),
254
  // Jetpack comments
255
- 'highlander_comment_form_prompt' => array(
256
  'label' => esc_html__( 'Greeting Text', 'stream' ),
257
  'context' => 'jetpack-comments',
258
  ),
@@ -267,16 +267,16 @@ class Connector_Jetpack extends Connector {
267
  'context' => 'protect',
268
  ),
269
  // SSO
270
- 'jetpack_sso_require_two_step' => array(
271
  'label' => esc_html__( 'Require Two-Step Authentication', 'stream' ),
272
  'context' => 'sso',
273
  ),
274
- 'jetpack_sso_match_by_email' => array(
275
  'label' => esc_html__( 'Match by Email', 'stream' ),
276
  'context' => 'sso',
277
  ),
278
  // Related posts
279
- 'jetpack_relatedposts' => array(
280
  'show_headline' => array(
281
  'label' => esc_html__( 'Show Related Posts Headline', 'stream' ),
282
  'context' => 'related-posts',
@@ -287,12 +287,12 @@ class Connector_Jetpack extends Connector {
287
  ),
288
  ),
289
  // Site verification
290
- 'verification_services_codes' => array(
291
- 'google' => array(
292
  'label' => esc_html__( 'Google Webmaster Tools Token', 'stream' ),
293
  'context' => 'verification-tools',
294
  ),
295
- 'bing' => array(
296
  'label' => esc_html__( 'Bing Webmaster Center Token', 'stream' ),
297
  'context' => 'verification-tools',
298
  ),
@@ -302,7 +302,7 @@ class Connector_Jetpack extends Connector {
302
  ),
303
  ),
304
  // Tiled galleries
305
- 'tiled_galleries' => array(
306
  'label' => esc_html__( 'Tiled Galleries', 'stream' ),
307
  'context' => 'tiled-gallery',
308
  ),
@@ -343,6 +343,7 @@ class Connector_Jetpack extends Connector {
343
  $action = $method . 'd';
344
  $meta = compact( 'module_slug' );
345
  $message = sprintf(
 
346
  __( '%1$s module %2$s', 'stream' ),
347
  $module_name,
348
  ( 'activated' === $action ) ? esc_html__( 'activated', 'stream' ) : esc_html__( 'deactivated', 'stream' )
@@ -361,6 +362,7 @@ class Connector_Jetpack extends Connector {
361
  $action = $method;
362
  $meta = compact( 'user_id', 'user_email', 'user_login' );
363
  $message = sprintf(
 
364
  __( '%1$s\'s account %2$s %3$s Jetpack', 'stream' ),
365
  $user->display_name,
366
  ( 'unlink' === $action ) ? esc_html__( 'unlinked', 'stream' ) : esc_html__( 'linked', 'stream' ),
@@ -370,7 +372,7 @@ class Connector_Jetpack extends Connector {
370
  $context = 'blogs';
371
  $action = str_replace( 'subsite', '', $method );
372
  $is_multisite = ( 0 === strpos( $method, 'subsite' ) );
373
- $blog_id = $is_multisite ? ( isset( $_GET['site_id'] ) ? intval( wp_unslash( $_GET['site_id'] ) ) : null ) : get_current_blog_id(); // phpcs: input var okay
374
 
375
  if ( empty( $blog_id ) ) {
376
  return;
@@ -378,15 +380,21 @@ class Connector_Jetpack extends Connector {
378
 
379
  if ( ! $is_multisite ) {
380
  $message = sprintf(
 
381
  __( 'Site %s Jetpack', 'stream' ),
382
  ( 'register' === $action ) ? esc_html__( 'connected to', 'stream' ) : esc_html__( 'disconnected from', 'stream' )
383
  );
384
  } else {
385
- $blog_details = get_blog_details( array( 'blog_id' => $blog_id ) );
 
 
 
 
386
  $blog_name = $blog_details->blogname;
387
  $meta += compact( 'blog_id', 'blog_name' );
388
 
389
  $message = sprintf(
 
390
  __( '"%1$s" blog %2$s Jetpack', 'stream' ),
391
  $blog_name,
392
  ( 'register' === $action ) ? esc_html__( 'connected to', 'stream' ) : esc_html__( 'disconnected from', 'stream' )
@@ -445,6 +453,7 @@ class Connector_Jetpack extends Connector {
445
  }
446
 
447
  $this->log(
 
448
  __( 'Monitor notifications %s', 'stream' ),
449
  array(
450
  'status' => $active ? esc_html__( 'activated', 'stream' ) : esc_html__( 'deactivated', 'stream' ),
@@ -482,6 +491,7 @@ class Connector_Jetpack extends Connector {
482
  $user = wp_get_current_user();
483
 
484
  $this->log(
 
485
  __( '%1$s %2$s Post by Email', 'stream' ),
486
  array(
487
  'user_displayname' => $user->display_name,
@@ -506,6 +516,7 @@ class Connector_Jetpack extends Connector {
506
  $option_title = $data['label'];
507
 
508
  $this->log(
 
509
  __( '"%s" setting updated', 'stream' ),
510
  compact( 'option_title', 'option', 'old_value', 'new_value' ),
511
  null,
@@ -561,6 +572,7 @@ class Connector_Jetpack extends Connector {
561
  }
562
 
563
  $this->log(
 
564
  __( 'G+ profile display %s', 'stream' ),
565
  array(
566
  'action' => $status ? esc_html__( 'enabled', 'stream' ) : esc_html__( 'disabled', 'stream' ),
@@ -578,6 +590,7 @@ class Connector_Jetpack extends Connector {
578
  $connected = is_array( $new_value ) && array_key_exists( $user->ID, $new_value );
579
 
580
  $this->log(
 
581
  __( '%1$s\'s Google+ account %2$s', 'stream' ),
582
  array(
583
  'display_name' => $user->display_name,
@@ -598,6 +611,7 @@ class Connector_Jetpack extends Connector {
598
  $status = ! $new_value ? 'enabled' : 'disabled'; // disabled = 1
599
 
600
  $this->log(
 
601
  __( 'Sharing CSS/JS %s', 'stream' ),
602
  compact( 'status', 'old_value', 'new_value' ),
603
  null,
@@ -657,6 +671,7 @@ class Connector_Jetpack extends Connector {
657
  $name = str_replace( 'publicize_connections::', '', $key );
658
 
659
  return array(
 
660
  'message' => esc_html__( '%1$s connection %2$s', 'stream' ),
661
  'meta' => array(
662
  'connection' => $publicize_ui->publicize->get_service_label( $name ),
@@ -681,6 +696,7 @@ class Connector_Jetpack extends Connector {
681
  }
682
 
683
  return array(
 
684
  'message' => esc_html__( '"%s" setting updated', 'stream' ),
685
  'meta' => array(
686
  'option_name' => $options[ $name ],
193
  add_filter( 'wp_stream_log_data', array( $this, 'log_override' ) );
194
 
195
  $this->options = array(
196
+ 'jetpack_options' => null,
197
  // Sharing module
198
+ 'hide_gplus' => null,
199
+ 'gplus_authors' => null,
200
+ 'sharing-options' => array(
201
  'label' => esc_html__( 'Sharing options', 'stream' ),
202
  'context' => 'sharedaddy',
203
  ),
204
+ 'sharedaddy_disable_resources' => null,
205
+ 'jetpack-twitter-cards-site-tag' => array(
206
  'label' => esc_html__( 'Twitter site tag', 'stream' ),
207
  'context' => 'sharedaddy',
208
  ),
209
  // Stats module
210
+ 'stats_options' => array(
211
  'label' => esc_html__( 'WordPress.com Stats', 'stream' ),
212
  'context' => 'stats',
213
  ),
217
  'context' => 'jetpack-comments',
218
  ),
219
  // Likes
220
+ 'disabled_likes' => array(
221
  'label' => esc_html__( 'WP.com Site-wide Likes', 'stream' ),
222
  'context' => 'likes',
223
  ),
224
  // Mobile
225
+ 'wp_mobile_excerpt' => array(
226
  'label' => esc_html__( 'Excerpts appearance', 'stream' ),
227
  'context' => 'minileven',
228
  ),
229
+ 'wp_mobile_app_promos' => array(
230
  'label' => esc_html__( 'App promos', 'stream' ),
231
  'context' => 'minileven',
232
  ),
234
 
235
  $this->options_override = array(
236
  // Carousel Module
237
+ 'carousel_background_color' => array(
238
  'label' => esc_html__( 'Background color', 'stream' ),
239
  'context' => 'carousel',
240
  ),
241
+ 'carousel_display_exif' => array(
242
  'label' => esc_html__( 'Metadata', 'stream' ),
243
  'context' => 'carousel',
244
  ),
245
  // Subscriptions
246
+ 'stb_enabled' => array(
247
  'label' => esc_html__( 'Follow blog comment form button', 'stream' ),
248
  'context' => 'subscriptions',
249
  ),
250
+ 'stc_enabled' => array(
251
  'label' => esc_html__( 'Follow comments form button', 'stream' ),
252
  'context' => 'subscriptions',
253
  ),
254
  // Jetpack comments
255
+ 'highlander_comment_form_prompt' => array(
256
  'label' => esc_html__( 'Greeting Text', 'stream' ),
257
  'context' => 'jetpack-comments',
258
  ),
267
  'context' => 'protect',
268
  ),
269
  // SSO
270
+ 'jetpack_sso_require_two_step' => array(
271
  'label' => esc_html__( 'Require Two-Step Authentication', 'stream' ),
272
  'context' => 'sso',
273
  ),
274
+ 'jetpack_sso_match_by_email' => array(
275
  'label' => esc_html__( 'Match by Email', 'stream' ),
276
  'context' => 'sso',
277
  ),
278
  // Related posts
279
+ 'jetpack_relatedposts' => array(
280
  'show_headline' => array(
281
  'label' => esc_html__( 'Show Related Posts Headline', 'stream' ),
282
  'context' => 'related-posts',
287
  ),
288
  ),
289
  // Site verification
290
+ 'verification_services_codes' => array(
291
+ 'google' => array(
292
  'label' => esc_html__( 'Google Webmaster Tools Token', 'stream' ),
293
  'context' => 'verification-tools',
294
  ),
295
+ 'bing' => array(
296
  'label' => esc_html__( 'Bing Webmaster Center Token', 'stream' ),
297
  'context' => 'verification-tools',
298
  ),
302
  ),
303
  ),
304
  // Tiled galleries
305
+ 'tiled_galleries' => array(
306
  'label' => esc_html__( 'Tiled Galleries', 'stream' ),
307
  'context' => 'tiled-gallery',
308
  ),
343
  $action = $method . 'd';
344
  $meta = compact( 'module_slug' );
345
  $message = sprintf(
346
+ // translators: Placeholders refer to a module name, and a status (e.g. "Photon", "activated")
347
  __( '%1$s module %2$s', 'stream' ),
348
  $module_name,
349
  ( 'activated' === $action ) ? esc_html__( 'activated', 'stream' ) : esc_html__( 'deactivated', 'stream' )
362
  $action = $method;
363
  $meta = compact( 'user_id', 'user_email', 'user_login' );
364
  $message = sprintf(
365
+ // translators: Placeholders refer to a user display name, a status, and the connection either "from" or "to" (e.g. "Jane Doe", "unlinked", "from")
366
  __( '%1$s\'s account %2$s %3$s Jetpack', 'stream' ),
367
  $user->display_name,
368
  ( 'unlink' === $action ) ? esc_html__( 'unlinked', 'stream' ) : esc_html__( 'linked', 'stream' ),
372
  $context = 'blogs';
373
  $action = str_replace( 'subsite', '', $method );
374
  $is_multisite = ( 0 === strpos( $method, 'subsite' ) );
375
+ $blog_id = $is_multisite ? ( isset( $_GET['site_id'] ) ? intval( wp_unslash( $_GET['site_id'] ) ) : null ) : get_current_blog_id(); // phpcs: input var okay, CSRF okay
376
 
377
  if ( empty( $blog_id ) ) {
378
  return;
380
 
381
  if ( ! $is_multisite ) {
382
  $message = sprintf(
383
+ // translators: Placeholder refers to a connection status. Either "connected to" or "disconnected from".
384
  __( 'Site %s Jetpack', 'stream' ),
385
  ( 'register' === $action ) ? esc_html__( 'connected to', 'stream' ) : esc_html__( 'disconnected from', 'stream' )
386
  );
387
  } else {
388
+ $blog_details = get_blog_details(
389
+ array(
390
+ 'blog_id' => $blog_id,
391
+ )
392
+ );
393
  $blog_name = $blog_details->blogname;
394
  $meta += compact( 'blog_id', 'blog_name' );
395
 
396
  $message = sprintf(
397
+ // translators: Placeholder refers to a connection status. Either "connected to" or "disconnected from".
398
  __( '"%1$s" blog %2$s Jetpack', 'stream' ),
399
  $blog_name,
400
  ( 'register' === $action ) ? esc_html__( 'connected to', 'stream' ) : esc_html__( 'disconnected from', 'stream' )
453
  }
454
 
455
  $this->log(
456
+ // translators: Placeholder refers to a status (e.g. "activated")
457
  __( 'Monitor notifications %s', 'stream' ),
458
  array(
459
  'status' => $active ? esc_html__( 'activated', 'stream' ) : esc_html__( 'deactivated', 'stream' ),
491
  $user = wp_get_current_user();
492
 
493
  $this->log(
494
+ // translators: Placeholders refer to a user display name, and a status (e.g. "Jane Doe", "enabled")
495
  __( '%1$s %2$s Post by Email', 'stream' ),
496
  array(
497
  'user_displayname' => $user->display_name,
516
  $option_title = $data['label'];
517
 
518
  $this->log(
519
+ // translators: Placeholder refers to a setting name (e.g. "Language")
520
  __( '"%s" setting updated', 'stream' ),
521
  compact( 'option_title', 'option', 'old_value', 'new_value' ),
522
  null,
572
  }
573
 
574
  $this->log(
575
+ // translators: Placeholder refers to a status (e.g. "enabled")
576
  __( 'G+ profile display %s', 'stream' ),
577
  array(
578
  'action' => $status ? esc_html__( 'enabled', 'stream' ) : esc_html__( 'disabled', 'stream' ),
590
  $connected = is_array( $new_value ) && array_key_exists( $user->ID, $new_value );
591
 
592
  $this->log(
593
+ // translators: Placeholders refer to a user display name, and a status (e.g. "Jane Doe", "connected")
594
  __( '%1$s\'s Google+ account %2$s', 'stream' ),
595
  array(
596
  'display_name' => $user->display_name,
611
  $status = ! $new_value ? 'enabled' : 'disabled'; // disabled = 1
612
 
613
  $this->log(
614
+ // translators: Placeholder refers to a status (e.g. "enabled")
615
  __( 'Sharing CSS/JS %s', 'stream' ),
616
  compact( 'status', 'old_value', 'new_value' ),
617
  null,
671
  $name = str_replace( 'publicize_connections::', '', $key );
672
 
673
  return array(
674
+ // translators: Placeholders refer to a service, and a status (e.g. "Facebook", "added")
675
  'message' => esc_html__( '%1$s connection %2$s', 'stream' ),
676
  'meta' => array(
677
  'connection' => $publicize_ui->publicize->get_service_label( $name ),
696
  }
697
 
698
  return array(
699
+ // translators: Placeholder refers to a setting name (e.g. "Language")
700
  'message' => esc_html__( '"%s" setting updated', 'stream' ),
701
  'meta' => array(
702
  'option_name' => $options[ $name ],
connectors/class-connector-media.php CHANGED
@@ -104,11 +104,13 @@ class Connector_Media extends Connector {
104
  */
105
  public function action_links( $links, $record ) {
106
  if ( $record->object_id ) {
107
- if ( $link = get_edit_post_link( $record->object_id ) ) {
108
- $links[ esc_html__( 'Edit Media', 'stream' ) ] = $link;
 
109
  }
110
- if ( $link = get_permalink( $record->object_id ) ) {
111
- $links[ esc_html__( 'View', 'stream' ) ] = $link;
 
112
  }
113
  }
114
 
@@ -125,12 +127,14 @@ class Connector_Media extends Connector {
125
  public function callback_add_attachment( $post_id ) {
126
  $post = get_post( $post_id );
127
  if ( $post->post_parent ) {
 
128
  $message = _x(
129
  'Attached "%1$s" to "%2$s"',
130
  '1: Attachment title, 2: Parent post title',
131
  'stream'
132
  );
133
  } else {
 
134
  $message = esc_html__( 'Added "%s" to Media library', 'stream' );
135
  }
136
 
@@ -159,10 +163,12 @@ class Connector_Media extends Connector {
159
  */
160
  public function callback_edit_attachment( $post_id ) {
161
  $post = get_post( $post_id );
162
- $message = esc_html__( 'Updated "%s"', 'stream' );
163
  $name = $post->post_title;
164
  $attachment_type = $this->get_attachment_type( $post->guid );
165
 
 
 
 
166
  $this->log(
167
  $message,
168
  compact( 'name' ),
@@ -183,11 +189,13 @@ class Connector_Media extends Connector {
183
  $post = get_post( $post_id );
184
  $parent = $post->post_parent ? get_post( $post->post_parent ) : null;
185
  $parent_id = $parent ? $parent->ID : null;
186
- $message = esc_html__( 'Deleted "%s"', 'stream' );
187
  $name = $post->post_title;
188
  $url = $post->guid;
189
  $attachment_type = $this->get_attachment_type( $post->guid );
190
 
 
 
 
191
  $this->log(
192
  $message,
193
  compact( 'name', 'parent_id', 'url' ),
@@ -219,6 +227,7 @@ class Connector_Media extends Connector {
219
  $attachment_type = $this->get_attachment_type( $post->guid );
220
 
221
  $this->log(
 
222
  __( 'Edited image "%s"', 'stream' ),
223
  compact( 'name', 'filename', 'post_id' ),
224
  $post_id,
104
  */
105
  public function action_links( $links, $record ) {
106
  if ( $record->object_id ) {
107
+ $edit_post_link = get_edit_post_link( $record->object_id );
108
+ if ( $edit_post_link ) {
109
+ $links[ esc_html__( 'Edit Media', 'stream' ) ] = $edit_post_link;
110
  }
111
+ $permalink = get_permalink( $record->object_id );
112
+ if ( $permalink ) {
113
+ $links[ esc_html__( 'View', 'stream' ) ] = $permalink;
114
  }
115
  }
116
 
127
  public function callback_add_attachment( $post_id ) {
128
  $post = get_post( $post_id );
129
  if ( $post->post_parent ) {
130
+ // translators: Placeholders refer to an attachment title, and a post title (e.g. "PIC001", "Hello World")
131
  $message = _x(
132
  'Attached "%1$s" to "%2$s"',
133
  '1: Attachment title, 2: Parent post title',
134
  'stream'
135
  );
136
  } else {
137
+ // translators: Placeholder refers to an attachment title (e.g. "PIC001")
138
  $message = esc_html__( 'Added "%s" to Media library', 'stream' );
139
  }
140
 
163
  */
164
  public function callback_edit_attachment( $post_id ) {
165
  $post = get_post( $post_id );
 
166
  $name = $post->post_title;
167
  $attachment_type = $this->get_attachment_type( $post->guid );
168
 
169
+ // translators: Placeholder refers to an attachment title (e.g. "PIC001")
170
+ $message = esc_html__( 'Updated "%s"', 'stream' );
171
+
172
  $this->log(
173
  $message,
174
  compact( 'name' ),
189
  $post = get_post( $post_id );
190
  $parent = $post->post_parent ? get_post( $post->post_parent ) : null;
191
  $parent_id = $parent ? $parent->ID : null;
 
192
  $name = $post->post_title;
193
  $url = $post->guid;
194
  $attachment_type = $this->get_attachment_type( $post->guid );
195
 
196
+ // translators: Placeholder refers to an attachment title (e.g. "PIC001")
197
+ $message = esc_html__( 'Deleted "%s"', 'stream' );
198
+
199
  $this->log(
200
  $message,
201
  compact( 'name', 'parent_id', 'url' ),
227
  $attachment_type = $this->get_attachment_type( $post->guid );
228
 
229
  $this->log(
230
+ // translators: Placeholder refers to an attachment title (e.g. "PIC001")
231
  __( 'Edited image "%s"', 'stream' ),
232
  compact( 'name', 'filename', 'post_id' ),
233
  $post_id,
connectors/class-connector-menus.php CHANGED
@@ -58,7 +58,11 @@ class Connector_Menus extends Connector {
58
  */
59
  public function get_context_labels() {
60
  $labels = array();
61
- $menus = get_terms( 'nav_menu', array( 'hide_empty' => false ) );
 
 
 
 
62
 
63
  foreach ( $menus as $menu ) {
64
  $slug = sanitize_title( $menu->name );
@@ -109,6 +113,7 @@ class Connector_Menus extends Connector {
109
  $name = $menu_data['menu-name'];
110
 
111
  $this->log(
 
112
  __( 'Created new menu "%s"', 'stream' ),
113
  compact( 'name', 'menu_id' ),
114
  $menu_id,
@@ -133,6 +138,7 @@ class Connector_Menus extends Connector {
133
  $name = $menu_data['menu-name'];
134
 
135
  $this->log(
 
136
  _x( 'Updated menu "%s"', 'Menu name', 'stream' ),
137
  compact( 'name', 'menu_id', 'menu_data' ),
138
  $menu_id,
@@ -157,6 +163,7 @@ class Connector_Menus extends Connector {
157
  $menu_id = $term->term_id;
158
 
159
  $this->log(
 
160
  _x( 'Deleted "%s"', 'Menu name', 'stream' ),
161
  compact( 'name', 'menu_id' ),
162
  $menu_id,
@@ -204,6 +211,7 @@ class Connector_Menus extends Connector {
204
  if ( empty( $new[ $key ][ $location_id ] ) ) {
205
  $action = 'unassigned';
206
  $menu_id = isset( $old[ $key ][ $location_id ] ) ? $old[ $key ][ $location_id ] : 0;
 
207
  $message = _x(
208
  '"%1$s" has been unassigned from "%2$s"',
209
  '1: Menu name, 2: Theme location',
@@ -212,6 +220,7 @@ class Connector_Menus extends Connector {
212
  } else {
213
  $action = 'assigned';
214
  $menu_id = isset( $new[ $key ][ $location_id ] ) ? $new[ $key ][ $location_id ] : 0;
 
215
  $message = _x(
216
  '"%1$s" has been assigned to "%2$s"',
217
  '1: Menu name, 2: Theme location',
58
  */
59
  public function get_context_labels() {
60
  $labels = array();
61
+ $menus = get_terms(
62
+ 'nav_menu', array(
63
+ 'hide_empty' => false,
64
+ )
65
+ );
66
 
67
  foreach ( $menus as $menu ) {
68
  $slug = sanitize_title( $menu->name );
113
  $name = $menu_data['menu-name'];
114
 
115
  $this->log(
116
+ // translators: Placeholder refers to a menu name (e.g. "Primary Menu")
117
  __( 'Created new menu "%s"', 'stream' ),
118
  compact( 'name', 'menu_id' ),
119
  $menu_id,
138
  $name = $menu_data['menu-name'];
139
 
140
  $this->log(
141
+ // translators: Placeholder refers to a menu name (e.g. "Primary Menu")
142
  _x( 'Updated menu "%s"', 'Menu name', 'stream' ),
143
  compact( 'name', 'menu_id', 'menu_data' ),
144
  $menu_id,
163
  $menu_id = $term->term_id;
164
 
165
  $this->log(
166
+ // translators: Placeholder refers to a menu name (e.g. "Primary Menu")
167
  _x( 'Deleted "%s"', 'Menu name', 'stream' ),
168
  compact( 'name', 'menu_id' ),
169
  $menu_id,
211
  if ( empty( $new[ $key ][ $location_id ] ) ) {
212
  $action = 'unassigned';
213
  $menu_id = isset( $old[ $key ][ $location_id ] ) ? $old[ $key ][ $location_id ] : 0;
214
+ // translators: Placeholders refer to a menu name, and a theme location (e.g. "Primary Menu", "primary_nav")
215
  $message = _x(
216
  '"%1$s" has been unassigned from "%2$s"',
217
  '1: Menu name, 2: Theme location',
220
  } else {
221
  $action = 'assigned';
222
  $menu_id = isset( $new[ $key ][ $location_id ] ) ? $new[ $key ][ $location_id ] : 0;
223
+ // translators: Placeholders refer to a menu name, and a theme location (e.g. "Primary Menu", "primary_nav")
224
  $message = _x(
225
  '"%1$s" has been assigned to "%2$s"',
226
  '1: Menu name, 2: Theme location',
connectors/class-connector-posts.php CHANGED
@@ -61,7 +61,7 @@ class Connector_Posts extends Connector {
61
  $post_types = wp_filter_object_list( $wp_post_types, array(), null, 'label' );
62
  $post_types = array_diff_key( $post_types, array_flip( $this->get_excluded_post_types() ) );
63
 
64
- add_action( 'registered_post_type', array( $this, '_registered_post_type' ), 10, 2 );
65
 
66
  return $post_types;
67
  }
@@ -105,12 +105,16 @@ class Connector_Posts extends Connector {
105
  sprintf( 'delete-post_%d', $post->ID )
106
  );
107
 
 
108
  $links[ sprintf( esc_html_x( 'Restore %s', 'Post type singular name', 'stream' ), $post_type_name ) ] = $untrash;
 
109
  $links[ sprintf( esc_html_x( 'Delete %s Permenantly', 'Post type singular name', 'stream' ), $post_type_name ) ] = $delete;
110
  } else {
 
111
  $links[ sprintf( esc_html_x( 'Edit %s', 'Post type singular name', 'stream' ), $post_type_name ) ] = get_edit_post_link( $post->ID );
112
 
113
- if ( $view_link = get_permalink( $post->ID ) ) {
 
114
  $links[ esc_html__( 'View', 'stream' ) ] = $view_link;
115
  }
116
 
@@ -127,14 +131,14 @@ class Connector_Posts extends Connector {
127
  }
128
 
129
  /**
130
- * Catch registeration of post_types after initial loading, to cache its labels
131
  *
132
  * @action registered_post_type
133
  *
134
  * @param string $post_type Post type slug
135
  * @param array $args Arguments used to register the post type
136
  */
137
- public function _registered_post_type( $post_type, $args ) {
138
  unset( $args );
139
 
140
  $post_type_obj = get_post_type_object( $post_type );
@@ -162,12 +166,14 @@ class Connector_Posts extends Connector {
162
  } elseif ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
163
  return;
164
  } elseif ( 'draft' === $new && 'publish' === $old ) {
 
165
  $summary = _x(
166
  '"%1$s" %2$s unpublished',
167
  '1: Post title, 2: Post type singular name',
168
  'stream'
169
  );
170
  } elseif ( 'trash' === $old && 'trash' !== $new ) {
 
171
  $summary = _x(
172
  '"%1$s" %2$s restored from trash',
173
  '1: Post title, 2: Post type singular name',
@@ -175,48 +181,56 @@ class Connector_Posts extends Connector {
175
  );
176
  $action = 'untrashed';
177
  } elseif ( 'draft' === $new && 'draft' === $old ) {
 
178
  $summary = _x(
179
  '"%1$s" %2$s draft saved',
180
  '1: Post title, 2: Post type singular name',
181
  'stream'
182
  );
183
  } elseif ( 'publish' === $new && 'draft' === $old ) {
 
184
  $summary = _x(
185
  '"%1$s" %2$s published',
186
  '1: Post title, 2: Post type singular name',
187
  'stream'
188
  );
189
  } elseif ( 'draft' === $new ) {
 
190
  $summary = _x(
191
  '"%1$s" %2$s drafted',
192
  '1: Post title, 2: Post type singular name',
193
  'stream'
194
  );
195
  } elseif ( 'pending' === $new ) {
 
196
  $summary = _x(
197
  '"%1$s" %2$s pending review',
198
  '1: Post title, 2: Post type singular name',
199
  'stream'
200
  );
201
  } elseif ( 'future' === $new ) {
 
202
  $summary = _x(
203
  '"%1$s" %2$s scheduled for %3$s',
204
  '1: Post title, 2: Post type singular name, 3: Scheduled post date',
205
  'stream'
206
  );
207
  } elseif ( 'future' === $old && 'publish' === $new ) {
 
208
  $summary = _x(
209
  '"%1$s" scheduled %2$s published',
210
  '1: Post title, 2: Post type singular name',
211
  'stream'
212
  );
213
  } elseif ( 'private' === $new ) {
 
214
  $summary = _x(
215
  '"%1$s" %2$s privately published',
216
  '1: Post title, 2: Post type singular name',
217
  'stream'
218
  );
219
  } elseif ( 'trash' === $new ) {
 
220
  $summary = _x(
221
  '"%1$s" %2$s trashed',
222
  '1: Post title, 2: Post type singular name',
@@ -224,6 +238,7 @@ class Connector_Posts extends Connector {
224
  );
225
  $action = 'trashed';
226
  } else {
 
227
  $summary = _x(
228
  '"%1$s" %2$s updated',
229
  '1: Post title, 2: Post type singular name',
@@ -289,7 +304,7 @@ class Connector_Posts extends Connector {
289
  $post = get_post( $post_id );
290
 
291
  // We check if post is an instance of WP_Post as it doesn't always resolve in unit testing
292
- if ( ! ( $post instanceof \WP_Post ) || in_array( $post->post_type, $this->get_excluded_post_types(), true ) ) {
293
  return;
294
  }
295
 
@@ -301,6 +316,7 @@ class Connector_Posts extends Connector {
301
  $post_type_name = strtolower( $this->get_post_type_name( $post->post_type ) );
302
 
303
  $this->log(
 
304
  _x(
305
  '"%1$s" %2$s deleted from trash',
306
  '1: Post title, 2: Post type singular name',
61
  $post_types = wp_filter_object_list( $wp_post_types, array(), null, 'label' );
62
  $post_types = array_diff_key( $post_types, array_flip( $this->get_excluded_post_types() ) );
63
 
64
+ add_action( 'registered_post_type', array( $this, 'registered_post_type' ), 10, 2 );
65
 
66
  return $post_types;
67
  }
105
  sprintf( 'delete-post_%d', $post->ID )
106
  );
107
 
108
+ // translators: Placeholder refers to a post type singular name (e.g. "Post")
109
  $links[ sprintf( esc_html_x( 'Restore %s', 'Post type singular name', 'stream' ), $post_type_name ) ] = $untrash;
110
+ // translators: Placeholder refers to a post type singular name (e.g. "Post")
111
  $links[ sprintf( esc_html_x( 'Delete %s Permenantly', 'Post type singular name', 'stream' ), $post_type_name ) ] = $delete;
112
  } else {
113
+ // translators: Placeholder refers to a post type singular name (e.g. "Post")
114
  $links[ sprintf( esc_html_x( 'Edit %s', 'Post type singular name', 'stream' ), $post_type_name ) ] = get_edit_post_link( $post->ID );
115
 
116
+ $view_link = get_permalink( $post->ID );
117
+ if ( $view_link ) {
118
  $links[ esc_html__( 'View', 'stream' ) ] = $view_link;
119
  }
120
 
131
  }
132
 
133
  /**
134
+ * Catch registration of post_types after initial loading, to cache its labels
135
  *
136
  * @action registered_post_type
137
  *
138
  * @param string $post_type Post type slug
139
  * @param array $args Arguments used to register the post type
140
  */
141
+ public function registered_post_type( $post_type, $args ) {
142
  unset( $args );
143
 
144
  $post_type_obj = get_post_type_object( $post_type );
166
  } elseif ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
167
  return;
168
  } elseif ( 'draft' === $new && 'publish' === $old ) {
169
+ // translators: Placeholders refer to a post title, and a post type singular name (e.g. "Hello World", "Post")
170
  $summary = _x(
171
  '"%1$s" %2$s unpublished',
172
  '1: Post title, 2: Post type singular name',
173
  'stream'
174
  );
175
  } elseif ( 'trash' === $old && 'trash' !== $new ) {
176
+ // translators: Placeholders refer to a post title, and a post type singular name (e.g. "Hello World", "Post")
177
  $summary = _x(
178
  '"%1$s" %2$s restored from trash',
179
  '1: Post title, 2: Post type singular name',
181
  );
182
  $action = 'untrashed';
183
  } elseif ( 'draft' === $new && 'draft' === $old ) {
184
+ // translators: Placeholders refer to a post title, and a post type singular name (e.g. "Hello World", "Post")
185
  $summary = _x(
186
  '"%1$s" %2$s draft saved',
187
  '1: Post title, 2: Post type singular name',
188
  'stream'
189
  );
190
  } elseif ( 'publish' === $new && 'draft' === $old ) {
191
+ // translators: Placeholders refer to a post title, and a post type singular name (e.g. "Hello World", "Post")
192
  $summary = _x(
193
  '"%1$s" %2$s published',
194
  '1: Post title, 2: Post type singular name',
195
  'stream'
196
  );
197
  } elseif ( 'draft' === $new ) {
198
+ // translators: Placeholders refer to a post title, and a post type singular name (e.g. "Hello World", "Post")
199
  $summary = _x(
200
  '"%1$s" %2$s drafted',
201
  '1: Post title, 2: Post type singular name',
202
  'stream'
203
  );
204
  } elseif ( 'pending' === $new ) {
205
+ // translators: Placeholders refer to a post title, and a post type singular name (e.g. "Hello World", "Post")
206
  $summary = _x(
207
  '"%1$s" %2$s pending review',
208
  '1: Post title, 2: Post type singular name',
209
  'stream'
210
  );
211
  } elseif ( 'future' === $new ) {
212
+ // translators: Placeholders refer to a post title, and a post type singular name (e.g. "Hello World", "Post")
213
  $summary = _x(
214
  '"%1$s" %2$s scheduled for %3$s',
215
  '1: Post title, 2: Post type singular name, 3: Scheduled post date',
216
  'stream'
217
  );
218
  } elseif ( 'future' === $old && 'publish' === $new ) {
219
+ // translators: Placeholders refer to a post title, and a post type singular name (e.g. "Hello World", "Post")
220
  $summary = _x(
221
  '"%1$s" scheduled %2$s published',
222
  '1: Post title, 2: Post type singular name',
223
  'stream'
224
  );
225
  } elseif ( 'private' === $new ) {
226
+ // translators: Placeholders refer to a post title, and a post type singular name (e.g. "Hello World", "Post")
227
  $summary = _x(
228
  '"%1$s" %2$s privately published',
229
  '1: Post title, 2: Post type singular name',
230
  'stream'
231
  );
232
  } elseif ( 'trash' === $new ) {
233
+ // translators: Placeholders refer to a post title, and a post type singular name (e.g. "Hello World", "Post")
234
  $summary = _x(
235
  '"%1$s" %2$s trashed',
236
  '1: Post title, 2: Post type singular name',
238
  );
239
  $action = 'trashed';
240
  } else {
241
+ // translators: Placeholders refer to a post title, and a post type singular name (e.g. "Hello World", "Post")
242
  $summary = _x(
243
  '"%1$s" %2$s updated',
244
  '1: Post title, 2: Post type singular name',
304
  $post = get_post( $post_id );
305
 
306
  // We check if post is an instance of WP_Post as it doesn't always resolve in unit testing
307
+ if ( ! ( $post instanceof \WP_Post ) || in_array( $post->post_type, $this->get_excluded_post_types(), true ) ) {
308
  return;
309
  }
310
 
316
  $post_type_name = strtolower( $this->get_post_type_name( $post->post_type ) );
317
 
318
  $this->log(
319
+ // translators: Placeholders refer to a post title, and a post type singular name (e.g. "Hello World", "Post")
320
  _x(
321
  '"%1$s" %2$s deleted from trash',
322
  '1: Post title, 2: Post type singular name',
connectors/class-connector-settings.php CHANGED
@@ -287,7 +287,7 @@ class Connector_Settings extends Connector {
287
  'background_attachment',
288
  'background_color',
289
  ),
290
- 'custom_header' => array(
291
  'header_image',
292
  'header_textcolor',
293
  ),
@@ -458,10 +458,10 @@ class Connector_Settings extends Connector {
458
  */
459
  public function action_links( $links, $record ) {
460
  $context_labels = $this->get_context_labels();
461
- $plugin = wp_stream_get_instance();
462
 
463
  $rules = array(
464
- 'stream' => array(
465
  'menu_slug' => 'wp_stream',
466
  'submenu_slug' => $plugin->admin->settings_page_slug,
467
  'url' => function( $rule, $record ) use ( $plugin ) {
@@ -504,7 +504,7 @@ class Connector_Settings extends Connector {
504
  return in_array( $record->context, array( 'custom_header', 'custom_background' ), true );
505
  },
506
  ),
507
- 'general' => array(
508
  'menu_slug' => 'options-general.php',
509
  'submenu_slug' => function( $record ) {
510
  return sprintf( 'options-%s.php', $record->context );
@@ -516,7 +516,7 @@ class Connector_Settings extends Connector {
516
  return ! empty( $submenu['options-general.php'] );
517
  },
518
  ),
519
- 'network' => array(
520
  'menu_slug' => 'settings.php',
521
  'submenu_slug' => function( $record ) {
522
  return 'settings.php';
@@ -553,19 +553,23 @@ class Connector_Settings extends Connector {
553
  if ( isset( $submenu[ $menu_slug ] ) ) {
554
  $found_submenus = wp_list_filter(
555
  $submenu[ $menu_slug ],
556
- array( 2 => $submenu_slug )
 
 
557
  );
558
  }
559
 
560
  if ( ! empty( $found_submenus ) ) {
561
- $target_submenu = array_pop( $found_submenus );
562
  list( $menu_title, $capability ) = $target_submenu;
563
 
564
  if ( current_user_can( $capability ) ) {
565
  $url = apply_filters( 'wp_stream_action_link_url', $url, $record );
566
- $text = sprintf( esc_html__( 'Edit %s Settings', 'stream' ), $context_labels[ $record->context ] );
567
  $field_name = $record->get_meta( 'option_key', true );
568
 
 
 
 
569
  if ( '' === $field_name ) {
570
  $field_name = $record->get_meta( 'option', true );
571
  }
@@ -682,8 +686,12 @@ class Connector_Settings extends Connector {
682
  $options = array_merge(
683
  (array) $whitelist_options,
684
  (array) $new_whitelist_options,
685
- array( 'permalink' => $this->permalink_options ),
686
- array( 'network' => $this->network_options )
 
 
 
 
687
  );
688
 
689
  foreach ( $options as $key => $opts ) {
@@ -702,7 +710,7 @@ class Connector_Settings extends Connector {
702
  if ( $this->is_option_group( $value ) ) {
703
  foreach ( $this->get_changed_keys( $old_value, $value ) as $field_key ) {
704
  if ( ! $this->is_key_ignored( $option, $field_key ) ) {
705
- $key_context = $this->get_context_by_key( $option, $field_key );
706
  $changed_options[] = array(
707
  'label' => $this->get_serialized_field_label( $option, $field_key ),
708
  'option' => $option,
@@ -725,6 +733,7 @@ class Connector_Settings extends Connector {
725
 
726
  foreach ( $changed_options as $properties ) {
727
  $this->log(
 
728
  __( '"%s" setting was updated', 'stream' ),
729
  $properties,
730
  null,
@@ -744,7 +753,7 @@ class Connector_Settings extends Connector {
744
  <script>
745
  (function ($) {
746
  $(function () {
747
- var hashPrefix = <?php echo wp_stream_json_encode( self::HIGHLIGHT_FIELD_URL_HASH_PREFIX ) // xss ok ?>,
748
  hashFieldName = "",
749
  fieldNames = [],
750
  $select2Choices = {},
@@ -830,7 +839,7 @@ class Connector_Settings extends Connector {
830
  public function sanitize_value( $value ) {
831
  if ( is_array( $value ) ) {
832
  return '';
833
- } elseif ( is_object( $value ) && ! in_array( '__toString', get_class_methods( $value ) ) ) {
834
  return '';
835
  }
836
 
287
  'background_attachment',
288
  'background_color',
289
  ),
290
+ 'custom_header' => array(
291
  'header_image',
292
  'header_textcolor',
293
  ),
458
  */
459
  public function action_links( $links, $record ) {
460
  $context_labels = $this->get_context_labels();
461
+ $plugin = wp_stream_get_instance();
462
 
463
  $rules = array(
464
+ 'stream' => array(
465
  'menu_slug' => 'wp_stream',
466
  'submenu_slug' => $plugin->admin->settings_page_slug,
467
  'url' => function( $rule, $record ) use ( $plugin ) {
504
  return in_array( $record->context, array( 'custom_header', 'custom_background' ), true );
505
  },
506
  ),
507
+ 'general' => array(
508
  'menu_slug' => 'options-general.php',
509
  'submenu_slug' => function( $record ) {
510
  return sprintf( 'options-%s.php', $record->context );
516
  return ! empty( $submenu['options-general.php'] );
517
  },
518
  ),
519
+ 'network' => array(
520
  'menu_slug' => 'settings.php',
521
  'submenu_slug' => function( $record ) {
522
  return 'settings.php';
553
  if ( isset( $submenu[ $menu_slug ] ) ) {
554
  $found_submenus = wp_list_filter(
555
  $submenu[ $menu_slug ],
556
+ array(
557
+ 2 => $submenu_slug,
558
+ )
559
  );
560
  }
561
 
562
  if ( ! empty( $found_submenus ) ) {
563
+ $target_submenu = array_pop( $found_submenus );
564
  list( $menu_title, $capability ) = $target_submenu;
565
 
566
  if ( current_user_can( $capability ) ) {
567
  $url = apply_filters( 'wp_stream_action_link_url', $url, $record );
 
568
  $field_name = $record->get_meta( 'option_key', true );
569
 
570
+ // translators: Placeholder refers to a context (e.g. "Editor")
571
+ $text = sprintf( esc_html__( 'Edit %s Settings', 'stream' ), $context_labels[ $record->context ] );
572
+
573
  if ( '' === $field_name ) {
574
  $field_name = $record->get_meta( 'option', true );
575
  }
686
  $options = array_merge(
687
  (array) $whitelist_options,
688
  (array) $new_whitelist_options,
689
+ array(
690
+ 'permalink' => $this->permalink_options,
691
+ ),
692
+ array(
693
+ 'network' => $this->network_options,
694
+ )
695
  );
696
 
697
  foreach ( $options as $key => $opts ) {
710
  if ( $this->is_option_group( $value ) ) {
711
  foreach ( $this->get_changed_keys( $old_value, $value ) as $field_key ) {
712
  if ( ! $this->is_key_ignored( $option, $field_key ) ) {
713
+ $key_context = $this->get_context_by_key( $option, $field_key );
714
  $changed_options[] = array(
715
  'label' => $this->get_serialized_field_label( $option, $field_key ),
716
  'option' => $option,
733
 
734
  foreach ( $changed_options as $properties ) {
735
  $this->log(
736
+ // translators: Placeholder refers to a setting name (e.g. "Language")
737
  __( '"%s" setting was updated', 'stream' ),
738
  $properties,
739
  null,
753
  <script>
754
  (function ($) {
755
  $(function () {
756
+ var hashPrefix = <?php echo wp_stream_json_encode( self::HIGHLIGHT_FIELD_URL_HASH_PREFIX ); // xss ok ?>,
757
  hashFieldName = "",
758
  fieldNames = [],
759
  $select2Choices = {},
839
  public function sanitize_value( $value ) {
840
  if ( is_array( $value ) ) {
841
  return '';
842
+ } elseif ( is_object( $value ) && ! in_array( '__toString', get_class_methods( $value ), true ) ) {
843
  return '';
844
  }
845
 
connectors/class-connector-taxonomies.php CHANGED
@@ -74,9 +74,9 @@ class Connector_Taxonomies extends Connector {
74
 
75
  $labels = wp_list_pluck( $wp_taxonomies, 'labels' );
76
 
77
- $this->context_labels = wp_list_pluck( $labels, 'singular_name' );
78
 
79
- add_action( 'registered_taxonomy', array( $this, '_registered_taxonomy' ), 10, 3 );
80
 
81
  return $this->context_labels;
82
  }
@@ -92,13 +92,8 @@ class Connector_Taxonomies extends Connector {
92
  * @return array Action links
93
  */
94
  public function action_links( $links, $record ) {
95
- if (
96
- $record->object_id
97
- &&
98
- 'deleted' !== $record->action
99
- &&
100
- ( $term = get_term_by( 'term_taxonomy_id', $record->object_id, $record->context ) ) // wpcom_vip_get_term_by() does not indicate support for `term_taxonomy_id`
101
- ) {
102
  if ( ! is_wp_error( $term ) ) {
103
  $tax_obj = get_taxonomy( $term->taxonomy );
104
  $tax_label = isset( $tax_obj->labels->singular_name ) ? $tax_obj->labels->singular_name : null;
@@ -109,6 +104,7 @@ class Connector_Taxonomies extends Connector {
109
 
110
  $term_id = empty( $term_id ) ? $term->term_id : $term_id;
111
 
 
112
  $links[ sprintf( _x( 'Edit %s', 'Term singular name', 'stream' ), $tax_label ) ] = get_edit_term_link( $term_id, $term->taxonomy );
113
  $links[ esc_html__( 'View', 'stream' ) ] = wp_stream_is_vip() ? \wpcom_vip_get_term_link( $term_id, $term->taxonomy ) : get_term_link( $term_id, $term->taxonomy );
114
  }
@@ -126,7 +122,7 @@ class Connector_Taxonomies extends Connector {
126
  * @param array|string $object_type Object type or array of object types
127
  * @param array|string $args Array or string of taxonomy registration arguments
128
  */
129
- public function _registered_taxonomy( $taxonomy, $object_type, $args ) {
130
  unset( $object_type );
131
 
132
  $taxonomy_obj = (object) $args;
@@ -157,6 +153,7 @@ class Connector_Taxonomies extends Connector {
157
  $term_parent = $term->parent;
158
 
159
  $this->log(
 
160
  _x(
161
  '"%1$s" %2$s created',
162
  '1: Term name, 2: Taxonomy singular label',
@@ -189,6 +186,7 @@ class Connector_Taxonomies extends Connector {
189
  $taxonomy_label = strtolower( $this->context_labels[ $taxonomy ] );
190
 
191
  $this->log(
 
192
  _x(
193
  '"%1$s" %2$s deleted',
194
  '1: Term name, 2: Taxonomy singular label',
@@ -231,6 +229,7 @@ class Connector_Taxonomies extends Connector {
231
  $term_parent = $term->parent;
232
 
233
  $this->log(
 
234
  _x(
235
  '"%1$s" %2$s updated',
236
  '1: Term name, 2: Taxonomy singular label',
74
 
75
  $labels = wp_list_pluck( $wp_taxonomies, 'labels' );
76
 
77
+ $this->context_labels = wp_list_pluck( $labels, 'singular_name' );
78
 
79
+ add_action( 'registered_taxonomy', array( $this, 'registered_taxonomy' ), 10, 3 );
80
 
81
  return $this->context_labels;
82
  }
92
  * @return array Action links
93
  */
94
  public function action_links( $links, $record ) {
95
+ $term = get_term_by( 'term_taxonomy_id', $record->object_id, $record->context ); // wpcom_vip_get_term_by() does not indicate support for `term_taxonomy_id`
96
+ if ( $record->object_id && 'deleted' !== $record->action && $term ) {
 
 
 
 
 
97
  if ( ! is_wp_error( $term ) ) {
98
  $tax_obj = get_taxonomy( $term->taxonomy );
99
  $tax_label = isset( $tax_obj->labels->singular_name ) ? $tax_obj->labels->singular_name : null;
104
 
105
  $term_id = empty( $term_id ) ? $term->term_id : $term_id;
106
 
107
+ // translators: Placeholder refers to a term singular name (e.g. "Tag")
108
  $links[ sprintf( _x( 'Edit %s', 'Term singular name', 'stream' ), $tax_label ) ] = get_edit_term_link( $term_id, $term->taxonomy );
109
  $links[ esc_html__( 'View', 'stream' ) ] = wp_stream_is_vip() ? \wpcom_vip_get_term_link( $term_id, $term->taxonomy ) : get_term_link( $term_id, $term->taxonomy );
110
  }
122
  * @param array|string $object_type Object type or array of object types
123
  * @param array|string $args Array or string of taxonomy registration arguments
124
  */
125
+ public function registered_taxonomy( $taxonomy, $object_type, $args ) {
126
  unset( $object_type );
127
 
128
  $taxonomy_obj = (object) $args;
153
  $term_parent = $term->parent;
154
 
155
  $this->log(
156
+ // translators: Placeholders refer to a term name, and a taxonomy singular label (e.g. "Tags", "Genre")
157
  _x(
158
  '"%1$s" %2$s created',
159
  '1: Term name, 2: Taxonomy singular label',
186
  $taxonomy_label = strtolower( $this->context_labels[ $taxonomy ] );
187
 
188
  $this->log(
189
+ // translators: Placeholders refer to a term name, and a taxonomy singular label (e.g. "Tags", "Genre")
190
  _x(
191
  '"%1$s" %2$s deleted',
192
  '1: Term name, 2: Taxonomy singular label',
229
  $term_parent = $term->parent;
230
 
231
  $this->log(
232
+ // translators: Placeholders refer to a term name, and a taxonomy singular label (e.g. "Tags", "Genre")
233
  _x(
234
  '"%1$s" %2$s updated',
235
  '1: Term name, 2: Taxonomy singular label',
connectors/class-connector-user-switching.php CHANGED
@@ -105,12 +105,13 @@ class Connector_User_Switching extends Connector {
105
  * @param Connectors $connectors The Connectors object instance
106
  */
107
  public function callback_wp_stream_after_connectors_registration( array $labels, Connectors $connectors ) {
 
108
 
109
- if ( ! isset( $_REQUEST['action'] ) ) {
110
  return;
111
  }
112
 
113
- switch ( $_REQUEST['action'] ) {
114
 
115
  case 'switch_to_user':
116
  $this->unhook_user_action( $connectors, 'clear_auth_cookie' );
@@ -140,7 +141,8 @@ class Connector_User_Switching extends Connector {
140
 
141
  $user = get_userdata( $user_id );
142
  $old_user = get_userdata( $old_user_id );
143
- $message = _x(
 
144
  'Switched user to %1$s (%2$s)',
145
  '1: User display name, 2: User login',
146
  'stream'
@@ -169,8 +171,9 @@ class Connector_User_Switching extends Connector {
169
  */
170
  public function callback_switch_back_user( $user_id, $old_user_id ) {
171
 
172
- $user = get_userdata( $user_id );
173
- $message = _x(
 
174
  'Switched back to %1$s (%2$s)',
175
  '1: User display name, 2: User login',
176
  'stream'
105
  * @param Connectors $connectors The Connectors object instance
106
  */
107
  public function callback_wp_stream_after_connectors_registration( array $labels, Connectors $connectors ) {
108
+ $action = wp_stream_filter_input( INPUT_GET, 'action' ) ?: wp_stream_filter_input( INPUT_POST, 'action' );
109
 
110
+ if ( ! $action ) {
111
  return;
112
  }
113
 
114
+ switch ( $action ) {
115
 
116
  case 'switch_to_user':
117
  $this->unhook_user_action( $connectors, 'clear_auth_cookie' );
141
 
142
  $user = get_userdata( $user_id );
143
  $old_user = get_userdata( $old_user_id );
144
+ // translators: Placeholders refer to a user display name, and a username (e.g. "Jane Doe", "administrator")
145
+ $message = _x(
146
  'Switched user to %1$s (%2$s)',
147
  '1: User display name, 2: User login',
148
  'stream'
171
  */
172
  public function callback_switch_back_user( $user_id, $old_user_id ) {
173
 
174
+ $user = get_userdata( $user_id );
175
+ // translators: Placeholders refer to a user display name, and a username (e.g. "Jane Doe", "administrator")
176
+ $message = _x(
177
  'Switched back to %1$s (%2$s)',
178
  '1: User display name, 2: User login',
179
  'stream'
connectors/class-connector-users.php CHANGED
@@ -86,7 +86,8 @@ class Connector_Users extends Connector {
86
  */
87
  public function action_links( $links, $record ) {
88
  if ( $record->object_id ) {
89
- if ( $link = get_edit_user_link( $record->object_id ) ) {
 
90
  $links [ esc_html__( 'Edit User', 'stream' ) ] = $link;
91
  }
92
  }
@@ -139,6 +140,7 @@ class Connector_Users extends Connector {
139
  $message = esc_html__( 'New user registration', 'stream' );
140
  $user_to_log = $registered_user->ID;
141
  } else { // Current logged-in user created a new user
 
142
  $message = _x(
143
  'New user account created for %1$s (%2$s)',
144
  '1: User display name, 2: User role',
@@ -172,6 +174,7 @@ class Connector_Users extends Connector {
172
  unset( $user_id );
173
 
174
  $this->log(
 
175
  __( '%s\'s profile was updated', 'stream' ),
176
  array(
177
  'display_name' => $user->display_name,
@@ -199,6 +202,7 @@ class Connector_Users extends Connector {
199
  global $wp_roles;
200
 
201
  $this->log(
 
202
  _x(
203
  '%1$s\'s role was changed from %2$s to %3$s',
204
  '1: User display name, 2: Old role, 3: New role',
@@ -206,8 +210,8 @@ class Connector_Users extends Connector {
206
  ),
207
  array(
208
  'display_name' => get_user_by( 'id', $user_id )->display_name,
209
- 'old_role' => translate_user_role( $wp_roles->role_names[ $old_roles[0] ] ),
210
- 'new_role' => translate_user_role( $wp_roles->role_names[ $new_role ] ),
211
  ),
212
  $user_id,
213
  'profiles',
@@ -224,6 +228,7 @@ class Connector_Users extends Connector {
224
  */
225
  public function callback_password_reset( $user ) {
226
  $this->log(
 
227
  __( '%s\'s password was reset', 'stream' ),
228
  array(
229
  'email' => $user->display_name,
@@ -250,8 +255,11 @@ class Connector_Users extends Connector {
250
  }
251
 
252
  $this->log(
 
253
  __( '%s\'s password was requested to be reset', 'stream' ),
254
- array( 'display_name' => $user->display_name ),
 
 
255
  $user->ID,
256
  'sessions',
257
  'forgot-password',
@@ -276,8 +284,11 @@ class Connector_Users extends Connector {
276
  $user = get_user_by( 'id', $user_id );
277
 
278
  $this->log(
 
279
  __( '%s logged in', 'stream' ),
280
- array( 'display_name' => $user->display_name ),
 
 
281
  $user->ID,
282
  'sessions',
283
  'login',
@@ -299,8 +310,11 @@ class Connector_Users extends Connector {
299
  }
300
 
301
  $this->log(
 
302
  __( '%s logged out', 'stream' ),
303
- array( 'display_name' => $user->display_name ),
 
 
304
  $user->ID,
305
  'sessions',
306
  'logout',
@@ -334,6 +348,7 @@ class Connector_Users extends Connector {
334
  $user = wp_get_current_user();
335
 
336
  if ( isset( $this->_users_object_pre_deleted[ $user_id ] ) ) {
 
337
  $message = _x(
338
  '%1$s\'s account was deleted (%2$s)',
339
  '1: User display name, 2: User roles',
@@ -343,6 +358,7 @@ class Connector_Users extends Connector {
343
  $deleted_user = $this->_users_object_pre_deleted[ $user_id ];
344
  unset( $this->_users_object_pre_deleted[ $user_id ] );
345
  } else {
 
346
  $message = esc_html__( 'User account #%d was deleted', 'stream' );
347
  $display_name = $user_id;
348
  $deleted_user = $user_id;
86
  */
87
  public function action_links( $links, $record ) {
88
  if ( $record->object_id ) {
89
+ $link = get_edit_user_link( $record->object_id );
90
+ if ( $link ) {
91
  $links [ esc_html__( 'Edit User', 'stream' ) ] = $link;
92
  }
93
  }
140
  $message = esc_html__( 'New user registration', 'stream' );
141
  $user_to_log = $registered_user->ID;
142
  } else { // Current logged-in user created a new user
143
+ // translators: Placeholders refer to a user display name, and a user role (e.g. "Jane Doe", "subscriber")
144
  $message = _x(
145
  'New user account created for %1$s (%2$s)',
146
  '1: User display name, 2: User role',
174
  unset( $user_id );
175
 
176
  $this->log(
177
+ // translators: Placeholder refers to a user display name (e.g. "Jane Doe")
178
  __( '%s\'s profile was updated', 'stream' ),
179
  array(
180
  'display_name' => $user->display_name,
202
  global $wp_roles;
203
 
204
  $this->log(
205
+ // translators: Placeholders refer to a user display name, a user role, and another user role (e.g. "Jane Doe", "editor", "subscriber")
206
  _x(
207
  '%1$s\'s role was changed from %2$s to %3$s',
208
  '1: User display name, 2: Old role, 3: New role',
210
  ),
211
  array(
212
  'display_name' => get_user_by( 'id', $user_id )->display_name,
213
+ 'old_role' => translate_user_role( $wp_roles->role_names[ current( $old_roles ) ] ),
214
+ 'new_role' => $new_role ? translate_user_role( $wp_roles->role_names[ $new_role ] ) : __( 'N/A', 'stream' ),
215
  ),
216
  $user_id,
217
  'profiles',
228
  */
229
  public function callback_password_reset( $user ) {
230
  $this->log(
231
+ // translators: Placeholder refers to a user display name (e.g. "Jane Doe")
232
  __( '%s\'s password was reset', 'stream' ),
233
  array(
234
  'email' => $user->display_name,
255
  }
256
 
257
  $this->log(
258
+ // translators: Placeholder refers to a user display name (e.g. "Jane Doe")
259
  __( '%s\'s password was requested to be reset', 'stream' ),
260
+ array(
261
+ 'display_name' => $user->display_name,
262
+ ),
263
  $user->ID,
264
  'sessions',
265
  'forgot-password',
284
  $user = get_user_by( 'id', $user_id );
285
 
286
  $this->log(
287
+ // translators: Placeholder refers to a user display name (e.g. "Jane Doe")
288
  __( '%s logged in', 'stream' ),
289
+ array(
290
+ 'display_name' => $user->display_name,
291
+ ),
292
  $user->ID,
293
  'sessions',
294
  'login',
310
  }
311
 
312
  $this->log(
313
+ // translators: Placeholder refers to a user display name (e.g. "Jane Doe")
314
  __( '%s logged out', 'stream' ),
315
+ array(
316
+ 'display_name' => $user->display_name,
317
+ ),
318
  $user->ID,
319
  'sessions',
320
  'logout',
348
  $user = wp_get_current_user();
349
 
350
  if ( isset( $this->_users_object_pre_deleted[ $user_id ] ) ) {
351
+ // translators: Placeholders refer to a user display name, and a user role (e.g. "Jane Doe", "subscriber")
352
  $message = _x(
353
  '%1$s\'s account was deleted (%2$s)',
354
  '1: User display name, 2: User roles',
358
  $deleted_user = $this->_users_object_pre_deleted[ $user_id ];
359
  unset( $this->_users_object_pre_deleted[ $user_id ] );
360
  } else {
361
+ // translators: Placeholders refer to a user display name, and a user role (e.g. "Jane Doe", "subscriber")
362
  $message = esc_html__( 'User account #%d was deleted', 'stream' );
363
  $display_name = $user_id;
364
  $deleted_user = $user_id;
connectors/class-connector-widgets.php CHANGED
@@ -96,7 +96,8 @@ class Connector_Widgets extends Connector {
96
  * @return array Action links
97
  */
98
  public function action_links( $links, $record ) {
99
- if ( $sidebar = $record->get_meta( 'sidebar_id', true ) ) {
 
100
  global $wp_registered_sidebars;
101
 
102
  if ( array_key_exists( $sidebar, $wp_registered_sidebars ) ) {
@@ -128,7 +129,7 @@ class Connector_Widgets extends Connector {
128
  if ( did_action( 'customize_save' ) ) {
129
  if ( is_null( $this->customizer_initial_sidebars_widgets ) ) {
130
  $this->customizer_initial_sidebars_widgets = $old;
131
- add_action( 'customize_save_after', array( $this, '_callback_customize_save_after' ) );
132
  }
133
  } else {
134
  $this->handle_sidebars_widgets_changes( $old, $new );
@@ -142,7 +143,7 @@ class Connector_Widgets extends Connector {
142
  *
143
  * @see callback_update_option_sidebars_widgets()
144
  */
145
- public function _callback_customize_save_after() {
146
  $old_sidebars_widgets = $this->customizer_initial_sidebars_widgets;
147
  $new_sidebars_widgets = get_option( 'sidebars_widgets' );
148
 
@@ -196,15 +197,19 @@ class Connector_Widgets extends Connector {
196
  $sidebar_name = isset( $labels[ $sidebar_id ] ) ? $labels[ $sidebar_id ] : $sidebar_id;
197
 
198
  if ( $name && $title ) {
 
199
  $message = _x( '%1$s widget named "%2$s" from "%3$s" deactivated', '1: Name, 2: Title, 3: Sidebar Name', 'stream' );
200
  } elseif ( $name ) {
201
  // Empty title, but we have the name
 
202
  $message = _x( '%1$s widget from "%3$s" deactivated', '1: Name, 3: Sidebar Name', 'stream' );
203
  } elseif ( $title ) {
204
  // Likely a single widget since no name is available
 
205
  $message = _x( 'Unknown widget type named "%2$s" from "%3$s" deactivated', '2: Title, 3: Sidebar Name', 'stream' );
206
  } else {
207
  // Neither a name nor a title are available, so use the widget ID
 
208
  $message = _x( '%4$s widget from "%3$s" deactivated', '4: Widget ID, 3: Sidebar Name', 'stream' );
209
  }
210
 
@@ -245,15 +250,19 @@ class Connector_Widgets extends Connector {
245
  $title = $this->get_widget_title( $widget_id );
246
 
247
  if ( $name && $title ) {
 
248
  $message = _x( '%1$s widget named "%2$s" reactivated', '1: Name, 2: Title', 'stream' );
249
  } elseif ( $name ) {
250
  // Empty title, but we have the name
 
251
  $message = _x( '%1$s widget reactivated', '1: Name', 'stream' );
252
  } elseif ( $title ) {
253
  // Likely a single widget since no name is available
 
254
  $message = _x( 'Unknown widget type named "%2$s" reactivated', '2: Title', 'stream' );
255
  } else {
256
  // Neither a name nor a title are available, so use the widget ID
 
257
  $message = _x( '%3$s widget reactivated', '3: Widget ID', 'stream' );
258
  }
259
 
@@ -301,15 +310,19 @@ class Connector_Widgets extends Connector {
301
  $sidebar_name = isset( $labels[ $sidebar_id ] ) ? $labels[ $sidebar_id ] : $sidebar_id;
302
 
303
  if ( $name && $title ) {
 
304
  $message = _x( '%1$s widget named "%2$s" removed from "%3$s"', '1: Name, 2: Title, 3: Sidebar Name', 'stream' );
305
  } elseif ( $name ) {
306
  // Empty title, but we have the name
 
307
  $message = _x( '%1$s widget removed from "%3$s"', '1: Name, 3: Sidebar Name', 'stream' );
308
  } elseif ( $title ) {
309
  // Likely a single widget since no name is available
 
310
  $message = _x( 'Unknown widget type named "%2$s" removed from "%3$s"', '2: Title, 3: Sidebar Name', 'stream' );
311
  } else {
312
  // Neither a name nor a title are available, so use the widget ID
 
313
  $message = _x( '%4$s widget removed from "%3$s"', '4: Widget ID, 3: Sidebar Name', 'stream' );
314
  }
315
 
@@ -354,15 +367,19 @@ class Connector_Widgets extends Connector {
354
  $sidebar_name = isset( $labels[ $sidebar_id ] ) ? $labels[ $sidebar_id ] : $sidebar_id;
355
 
356
  if ( $name && $title ) {
 
357
  $message = _x( '%1$s widget named "%2$s" added to "%3$s"', '1: Name, 2: Title, 3: Sidebar Name', 'stream' );
358
  } elseif ( $name ) {
359
  // Empty title, but we have the name
 
360
  $message = _x( '%1$s widget added to "%3$s"', '1: Name, 3: Sidebar Name', 'stream' );
361
  } elseif ( $title ) {
362
  // Likely a single widget since no name is available
 
363
  $message = _x( 'Unknown widget type named "%2$s" added to "%3$s"', '2: Title, 3: Sidebar Name', 'stream' );
364
  } else {
365
  // Neither a name nor a title are available, so use the widget ID
 
366
  $message = _x( '%4$s widget added to "%3$s"', '4: Widget ID, 3: Sidebar Name', 'stream' );
367
  }
368
 
@@ -405,8 +422,10 @@ class Connector_Widgets extends Connector {
405
  $labels = $this->get_context_labels();
406
  $sidebar_name = isset( $labels[ $sidebar_id ] ) ? $labels[ $sidebar_id ] : $sidebar_id;
407
  $old_widget_ids = $old[ $sidebar_id ];
408
- $message = _x( 'Widgets reordered in "%s"', 'Sidebar name', 'stream' );
409
- $message = sprintf( $message, $sidebar_name );
 
 
410
 
411
  $this->log(
412
  $message,
@@ -460,15 +479,19 @@ class Connector_Widgets extends Connector {
460
  $new_sidebar_name = isset( $labels[ $new_sidebar_id ] ) ? $labels[ $new_sidebar_id ] : $new_sidebar_id;
461
 
462
  if ( $name && $title ) {
 
463
  $message = _x( '%1$s widget named "%2$s" moved from "%4$s" to "%5$s"', '1: Name, 2: Title, 4: Old Sidebar Name, 5: New Sidebar Name', 'stream' );
464
  } elseif ( $name ) {
465
  // Empty title, but we have the name
 
466
  $message = _x( '%1$s widget moved from "%4$s" to "%5$s"', '1: Name, 4: Old Sidebar Name, 5: New Sidebar Name', 'stream' );
467
  } elseif ( $title ) {
468
  // Likely a single widget since no name is available
 
469
  $message = _x( 'Unknown widget type named "%2$s" moved from "%4$s" to "%5$s"', '2: Title, 4: Old Sidebar Name, 5: New Sidebar Name', 'stream' );
470
  } else {
471
  // Neither a name nor a title are available, so use the widget ID
 
472
  $message = _x( '%3$s widget moved from "%4$s" to "%5$s"', '3: Widget ID, 4: Old Sidebar Name, 5: New Sidebar Name', 'stream' );
473
  }
474
 
@@ -520,11 +543,11 @@ class Connector_Widgets extends Connector {
520
  $created_widget_numbers = array_diff( array_keys( $new_value ), array_keys( $old_value ) );
521
 
522
  foreach ( $created_widget_numbers as $widget_number ) {
523
- $instance = $new_value[ $widget_number ];
524
- $widget_id = sprintf( $widget_id_format, $widget_number );
525
- $name = $this->get_widget_name( $widget_id );
526
- $title = ! empty( $instance['title'] ) ? $instance['title'] : null;
527
- $sidebar_id = $this->get_widget_sidebar_id( $widget_id ); // @todo May not be assigned yet
528
 
529
  $creates[] = compact( 'name', 'title', 'widget_id', 'sidebar_id', 'instance' );
530
  }
@@ -556,11 +579,11 @@ class Connector_Widgets extends Connector {
556
  $deleted_widget_numbers = array_diff( array_keys( $old_value ), array_keys( $new_value ) );
557
 
558
  foreach ( $deleted_widget_numbers as $widget_number ) {
559
- $instance = $old_value[ $widget_number ];
560
- $widget_id = sprintf( $widget_id_format, $widget_number );
561
- $name = $this->get_widget_name( $widget_id );
562
- $title = ! empty( $instance['title'] ) ? $instance['title'] : null;
563
- $sidebar_id = $this->get_widget_sidebar_id( $widget_id ); // @todo May not be assigned anymore
564
 
565
  $deletes[] = compact( 'name', 'title', 'widget_id', 'sidebar_id', 'instance' );
566
  }
@@ -582,15 +605,19 @@ class Connector_Widgets extends Connector {
582
  */
583
  foreach ( $updates as $update ) {
584
  if ( $update['name'] && $update['title'] ) {
 
585
  $message = _x( '%1$s widget named "%2$s" in "%3$s" updated', '1: Name, 2: Title, 3: Sidebar Name', 'stream' );
586
  } elseif ( $update['name'] ) {
587
  // Empty title, but we have the name
 
588
  $message = _x( '%1$s widget in "%3$s" updated', '1: Name, 3: Sidebar Name', 'stream' );
589
  } elseif ( $update['title'] ) {
590
  // Likely a single widget since no name is available
 
591
  $message = _x( 'Unknown widget type named "%2$s" in "%3$s" updated', '2: Title, 3: Sidebar Name', 'stream' );
592
  } else {
593
  // Neither a name nor a title are available, so use the widget ID
 
594
  $message = _x( '%4$s widget in "%3$s" updated', '4: Widget ID, 3: Sidebar Name', 'stream' );
595
  }
596
 
@@ -622,15 +649,19 @@ class Connector_Widgets extends Connector {
622
  */
623
  foreach ( $creates as $create ) {
624
  if ( $create['name'] && $create['title'] ) {
 
625
  $message = _x( '%1$s widget named "%2$s" created', '1: Name, 2: Title', 'stream' );
626
  } elseif ( $create['name'] ) {
627
  // Empty title, but we have the name
 
628
  $message = _x( '%1$s widget created', '1: Name', 'stream' );
629
  } elseif ( $create['title'] ) {
630
  // Likely a single widget since no name is available
 
631
  $message = _x( 'Unknown widget type named "%2$s" created', '2: Title', 'stream' );
632
  } else {
633
  // Neither a name nor a title are available, so use the widget ID
 
634
  $message = _x( '%3$s widget created', '3: Widget ID', 'stream' );
635
  }
636
 
@@ -652,15 +683,19 @@ class Connector_Widgets extends Connector {
652
  */
653
  foreach ( $deletes as $delete ) {
654
  if ( $delete['name'] && $delete['title'] ) {
 
655
  $message = _x( '%1$s widget named "%2$s" deleted', '1: Name, 2: Title', 'stream' );
656
  } elseif ( $delete['name'] ) {
657
  // Empty title, but we have the name
 
658
  $message = _x( '%1$s widget deleted', '1: Name', 'stream' );
659
  } elseif ( $delete['title'] ) {
660
  // Likely a single widget since no name is available
 
661
  $message = _x( 'Unknown widget type named "%2$s" deleted', '2: Title', 'stream' );
662
  } else {
663
  // Neither a name nor a title are available, so use the widget ID
 
664
  $message = _x( '%3$s widget deleted', '3: Widget ID', 'stream' );
665
  }
666
 
96
  * @return array Action links
97
  */
98
  public function action_links( $links, $record ) {
99
+ $sidebar = $record->get_meta( 'sidebar_id', true );
100
+ if ( $sidebar ) {
101
  global $wp_registered_sidebars;
102
 
103
  if ( array_key_exists( $sidebar, $wp_registered_sidebars ) ) {
129
  if ( did_action( 'customize_save' ) ) {
130
  if ( is_null( $this->customizer_initial_sidebars_widgets ) ) {
131
  $this->customizer_initial_sidebars_widgets = $old;
132
+ add_action( 'customize_save_after', array( $this, 'callback_customize_save_after' ) );
133
  }
134
  } else {
135
  $this->handle_sidebars_widgets_changes( $old, $new );
143
  *
144
  * @see callback_update_option_sidebars_widgets()
145
  */
146
+ public function callback_customize_save_after() {
147
  $old_sidebars_widgets = $this->customizer_initial_sidebars_widgets;
148
  $new_sidebars_widgets = get_option( 'sidebars_widgets' );
149
 
197
  $sidebar_name = isset( $labels[ $sidebar_id ] ) ? $labels[ $sidebar_id ] : $sidebar_id;
198
 
199
  if ( $name && $title ) {
200
+ // translators: Placeholders refer to a widget name, a widget title, and a sidebar name (e.g. "Archives", "Browse", "Footer Area 1")
201
  $message = _x( '%1$s widget named "%2$s" from "%3$s" deactivated', '1: Name, 2: Title, 3: Sidebar Name', 'stream' );
202
  } elseif ( $name ) {
203
  // Empty title, but we have the name
204
+ // translators: Placeholders refer to a widget name, and a sidebar name (e.g. "Archives", "Footer Area 1")
205
  $message = _x( '%1$s widget from "%3$s" deactivated', '1: Name, 3: Sidebar Name', 'stream' );
206
  } elseif ( $title ) {
207
  // Likely a single widget since no name is available
208
+ // translators: Placeholders refer to a widget title, and a sidebar name (e.g. "Browse", "Footer Area 1")
209
  $message = _x( 'Unknown widget type named "%2$s" from "%3$s" deactivated', '2: Title, 3: Sidebar Name', 'stream' );
210
  } else {
211
  // Neither a name nor a title are available, so use the widget ID
212
+ // translators: Placeholders refer to a widget ID, and a sidebar name (e.g. "42", "Footer Area 1")
213
  $message = _x( '%4$s widget from "%3$s" deactivated', '4: Widget ID, 3: Sidebar Name', 'stream' );
214
  }
215
 
250
  $title = $this->get_widget_title( $widget_id );
251
 
252
  if ( $name && $title ) {
253
+ // translators: Placeholders refer to a widget name, and a widget title (e.g. "Archives", "Browse")
254
  $message = _x( '%1$s widget named "%2$s" reactivated', '1: Name, 2: Title', 'stream' );
255
  } elseif ( $name ) {
256
  // Empty title, but we have the name
257
+ // translators: Placeholder refers to a widget name (e.g. "Archives")
258
  $message = _x( '%1$s widget reactivated', '1: Name', 'stream' );
259
  } elseif ( $title ) {
260
  // Likely a single widget since no name is available
261
+ // translators: Placeholder refers to a widget title (e.g. "Browse")
262
  $message = _x( 'Unknown widget type named "%2$s" reactivated', '2: Title', 'stream' );
263
  } else {
264
  // Neither a name nor a title are available, so use the widget ID
265
+ // translators: Placeholder refers to a widget ID (e.g. "42")
266
  $message = _x( '%3$s widget reactivated', '3: Widget ID', 'stream' );
267
  }
268
 
310
  $sidebar_name = isset( $labels[ $sidebar_id ] ) ? $labels[ $sidebar_id ] : $sidebar_id;
311
 
312
  if ( $name && $title ) {
313
+ // translators: Placeholders refer to a widget name, a widget title, and a sidebar name (e.g. "Archives", "Browse", "Footer Area 1")
314
  $message = _x( '%1$s widget named "%2$s" removed from "%3$s"', '1: Name, 2: Title, 3: Sidebar Name', 'stream' );
315
  } elseif ( $name ) {
316
  // Empty title, but we have the name
317
+ // translators: Placeholders refer to a widget name, and a sidebar name (e.g. "Archives", "Footer Area 1")
318
  $message = _x( '%1$s widget removed from "%3$s"', '1: Name, 3: Sidebar Name', 'stream' );
319
  } elseif ( $title ) {
320
  // Likely a single widget since no name is available
321
+ // translators: Placeholders refer to a widget title, and a sidebar name (e.g. "Browse", "Footer Area 1")
322
  $message = _x( 'Unknown widget type named "%2$s" removed from "%3$s"', '2: Title, 3: Sidebar Name', 'stream' );
323
  } else {
324
  // Neither a name nor a title are available, so use the widget ID
325
+ // translators: Placeholders refer to a widget ID, and a sidebar name (e.g. "42", "Footer Area 1")
326
  $message = _x( '%4$s widget removed from "%3$s"', '4: Widget ID, 3: Sidebar Name', 'stream' );
327
  }
328
 
367
  $sidebar_name = isset( $labels[ $sidebar_id ] ) ? $labels[ $sidebar_id ] : $sidebar_id;
368
 
369
  if ( $name && $title ) {
370
+ // translators: Placeholders refer to a widget name, a widget title, and a sidebar name (e.g. "Archives", "Browse", "Footer Area 1")
371
  $message = _x( '%1$s widget named "%2$s" added to "%3$s"', '1: Name, 2: Title, 3: Sidebar Name', 'stream' );
372
  } elseif ( $name ) {
373
  // Empty title, but we have the name
374
+ // translators: Placeholders refer to a widget name, and a sidebar name (e.g. "Archives", "Footer Area 1")
375
  $message = _x( '%1$s widget added to "%3$s"', '1: Name, 3: Sidebar Name', 'stream' );
376
  } elseif ( $title ) {
377
  // Likely a single widget since no name is available
378
+ // translators: Placeholders refer to a widget title, and a sidebar name (e.g. "Browse", "Footer Area 1")
379
  $message = _x( 'Unknown widget type named "%2$s" added to "%3$s"', '2: Title, 3: Sidebar Name', 'stream' );
380
  } else {
381
  // Neither a name nor a title are available, so use the widget ID
382
+ // translators: Placeholders refer to a widget ID, and a sidebar name (e.g. "42", "Footer Area 1")
383
  $message = _x( '%4$s widget added to "%3$s"', '4: Widget ID, 3: Sidebar Name', 'stream' );
384
  }
385
 
422
  $labels = $this->get_context_labels();
423
  $sidebar_name = isset( $labels[ $sidebar_id ] ) ? $labels[ $sidebar_id ] : $sidebar_id;
424
  $old_widget_ids = $old[ $sidebar_id ];
425
+
426
+ // translators: Placeholder refers to a sidebar name (e.g. "Footer Area 1")
427
+ $message = _x( 'Widgets reordered in "%s"', 'Sidebar name', 'stream' );
428
+ $message = sprintf( $message, $sidebar_name );
429
 
430
  $this->log(
431
  $message,
479
  $new_sidebar_name = isset( $labels[ $new_sidebar_id ] ) ? $labels[ $new_sidebar_id ] : $new_sidebar_id;
480
 
481
  if ( $name && $title ) {
482
+ // translators: Placeholders refer to a widget name, a widget title, a sidebar name, and another sidebar name (e.g. "Archives", "Browse", "Footer Area 1", "Footer Area 2")
483
  $message = _x( '%1$s widget named "%2$s" moved from "%4$s" to "%5$s"', '1: Name, 2: Title, 4: Old Sidebar Name, 5: New Sidebar Name', 'stream' );
484
  } elseif ( $name ) {
485
  // Empty title, but we have the name
486
+ // translators: Placeholders refer to a widget name, a sidebar name, and another sidebar name (e.g. "Archives", "Footer Area 1", "Footer Area 2")
487
  $message = _x( '%1$s widget moved from "%4$s" to "%5$s"', '1: Name, 4: Old Sidebar Name, 5: New Sidebar Name', 'stream' );
488
  } elseif ( $title ) {
489
  // Likely a single widget since no name is available
490
+ // translators: Placeholders refer to a widget title, a sidebar name, and another sidebar name (e.g. "Browse", "Footer Area 1", "Footer Area 2")
491
  $message = _x( 'Unknown widget type named "%2$s" moved from "%4$s" to "%5$s"', '2: Title, 4: Old Sidebar Name, 5: New Sidebar Name', 'stream' );
492
  } else {
493
  // Neither a name nor a title are available, so use the widget ID
494
+ // translators: Placeholders refer to a widget ID, a sidebar name, and another sidebar name (e.g. "42", "Footer Area 1", "Footer Area 2")
495
  $message = _x( '%3$s widget moved from "%4$s" to "%5$s"', '3: Widget ID, 4: Old Sidebar Name, 5: New Sidebar Name', 'stream' );
496
  }
497
 
543
  $created_widget_numbers = array_diff( array_keys( $new_value ), array_keys( $old_value ) );
544
 
545
  foreach ( $created_widget_numbers as $widget_number ) {
546
+ $instance = $new_value[ $widget_number ];
547
+ $widget_id = sprintf( $widget_id_format, $widget_number );
548
+ $name = $this->get_widget_name( $widget_id );
549
+ $title = ! empty( $instance['title'] ) ? $instance['title'] : null;
550
+ $sidebar_id = $this->get_widget_sidebar_id( $widget_id ); // @todo May not be assigned yet
551
 
552
  $creates[] = compact( 'name', 'title', 'widget_id', 'sidebar_id', 'instance' );
553
  }
579
  $deleted_widget_numbers = array_diff( array_keys( $old_value ), array_keys( $new_value ) );
580
 
581
  foreach ( $deleted_widget_numbers as $widget_number ) {
582
+ $instance = $old_value[ $widget_number ];
583
+ $widget_id = sprintf( $widget_id_format, $widget_number );
584
+ $name = $this->get_widget_name( $widget_id );
585
+ $title = ! empty( $instance['title'] ) ? $instance['title'] : null;
586
+ $sidebar_id = $this->get_widget_sidebar_id( $widget_id ); // @todo May not be assigned anymore
587
 
588
  $deletes[] = compact( 'name', 'title', 'widget_id', 'sidebar_id', 'instance' );
589
  }
605
  */
606
  foreach ( $updates as $update ) {
607
  if ( $update['name'] && $update['title'] ) {
608
+ // translators: Placeholders refer to a widget name, a widget title, and a sidebar name (e.g. "Archives", "Browse", "Footer Area 1")
609
  $message = _x( '%1$s widget named "%2$s" in "%3$s" updated', '1: Name, 2: Title, 3: Sidebar Name', 'stream' );
610
  } elseif ( $update['name'] ) {
611
  // Empty title, but we have the name
612
+ // translators: Placeholders refer to a widget name, and a sidebar name (e.g. "Archives", "Footer Area 1")
613
  $message = _x( '%1$s widget in "%3$s" updated', '1: Name, 3: Sidebar Name', 'stream' );
614
  } elseif ( $update['title'] ) {
615
  // Likely a single widget since no name is available
616
+ // translators: Placeholders refer to a widget title, and a sidebar name (e.g. "Browse", "Footer Area 1")
617
  $message = _x( 'Unknown widget type named "%2$s" in "%3$s" updated', '2: Title, 3: Sidebar Name', 'stream' );
618
  } else {
619
  // Neither a name nor a title are available, so use the widget ID
620
+ // translators: Placeholders refer to a widget ID, and a sidebar name (e.g. "42", "Footer Area 1")
621
  $message = _x( '%4$s widget in "%3$s" updated', '4: Widget ID, 3: Sidebar Name', 'stream' );
622
  }
623
 
649
  */
650
  foreach ( $creates as $create ) {
651
  if ( $create['name'] && $create['title'] ) {
652
+ // translators: Placeholders refer to a widget name, and a widget title (e.g. "Archives", "Browse")
653
  $message = _x( '%1$s widget named "%2$s" created', '1: Name, 2: Title', 'stream' );
654
  } elseif ( $create['name'] ) {
655
  // Empty title, but we have the name
656
+ // translators: Placeholder refers to a widget name (e.g. "Archives")
657
  $message = _x( '%1$s widget created', '1: Name', 'stream' );
658
  } elseif ( $create['title'] ) {
659
  // Likely a single widget since no name is available
660
+ // translators: Placeholder refers to a widget title (e.g. "Browse")
661
  $message = _x( 'Unknown widget type named "%2$s" created', '2: Title', 'stream' );
662
  } else {
663
  // Neither a name nor a title are available, so use the widget ID
664
+ // translators: Placeholder refers to a widget ID (e.g. "42")
665
  $message = _x( '%3$s widget created', '3: Widget ID', 'stream' );
666
  }
667
 
683
  */
684
  foreach ( $deletes as $delete ) {
685
  if ( $delete['name'] && $delete['title'] ) {
686
+ // translators: Placeholders refer to a widget name, and a widget title (e.g. "Archives", "Browse")
687
  $message = _x( '%1$s widget named "%2$s" deleted', '1: Name, 2: Title', 'stream' );
688
  } elseif ( $delete['name'] ) {
689
  // Empty title, but we have the name
690
+ // translators: Placeholder refers to a widget name (e.g. "Archives")
691
  $message = _x( '%1$s widget deleted', '1: Name', 'stream' );
692
  } elseif ( $delete['title'] ) {
693
  // Likely a single widget since no name is available
694
+ // translators: Placeholder refers to a widget title (e.g. "Browse")
695
  $message = _x( 'Unknown widget type named "%2$s" deleted', '2: Title', 'stream' );
696
  } else {
697
  // Neither a name nor a title are available, so use the widget ID
698
+ // translators: Placeholder refers to a widget ID (e.g. "42")
699
  $message = _x( '%3$s widget deleted', '3: Widget ID', 'stream' );
700
  }
701
 
connectors/class-connector-woocommerce.php CHANGED
@@ -114,7 +114,7 @@ class Connector_Woocommerce extends Connector {
114
 
115
  if ( class_exists( 'Connector_Posts' ) ) {
116
  $posts_connector = new Connector_Posts();
117
- $context_labels = array_merge(
118
  $context_labels,
119
  $posts_connector->get_context_labels()
120
  );
@@ -140,21 +140,21 @@ class Connector_Woocommerce extends Connector {
140
  */
141
  public function get_custom_settings() {
142
  $custom_settings = array(
143
- 'woocommerce_frontend_css_colors' => array(
144
  'title' => esc_html__( 'Frontend Styles', 'stream' ),
145
  'page' => 'wc-settings',
146
  'tab' => 'general',
147
  'section' => '',
148
  'type' => esc_html__( 'setting', 'stream' ),
149
  ),
150
- 'woocommerce_default_gateway' => array(
151
  'title' => esc_html__( 'Gateway Display Default', 'stream' ),
152
  'page' => 'wc-settings',
153
  'tab' => 'checkout',
154
  'section' => '',
155
  'type' => esc_html__( 'setting', 'stream' ),
156
  ),
157
- 'woocommerce_gateway_order' => array(
158
  'title' => esc_html__( 'Gateway Display Order', 'stream' ),
159
  'page' => 'wc-settings',
160
  'tab' => 'checkout',
@@ -168,28 +168,28 @@ class Connector_Woocommerce extends Connector {
168
  'section' => '',
169
  'type' => esc_html__( 'setting', 'stream' ),
170
  ),
171
- 'woocommerce_shipping_method_order' => array(
172
  'title' => esc_html__( 'Shipping Methods Order', 'stream' ),
173
  'page' => 'wc-settings',
174
  'tab' => 'shipping',
175
  'section' => '',
176
  'type' => esc_html__( 'setting', 'stream' ),
177
  ),
178
- 'shipping_debug_mode' => array(
179
  'title' => esc_html__( 'Shipping Debug Mode', 'stream' ),
180
  'page' => 'wc-status',
181
  'tab' => 'tools',
182
  'section' => '',
183
  'type' => esc_html__( 'tool', 'stream' ),
184
  ),
185
- 'template_debug_mode' => array(
186
  'title' => esc_html__( 'Template Debug Mode', 'stream' ),
187
  'page' => 'wc-status',
188
  'tab' => 'tools',
189
  'section' => '',
190
  'type' => esc_html__( 'tool', 'stream' ),
191
  ),
192
- 'uninstall_data' => array(
193
  'title' => esc_html__( 'Remove post types on uninstall', 'stream' ),
194
  'page' => 'wc-status',
195
  'tab' => 'tools',
@@ -213,14 +213,17 @@ class Connector_Woocommerce extends Connector {
213
  */
214
  public function action_links( $links, $record ) {
215
  if ( in_array( $record->context, $this->post_types, true ) && get_post( $record->object_id ) ) {
216
- if ( $link = get_edit_post_link( $record->object_id ) ) {
 
217
  $posts_connector = new Connector_Posts();
218
- $post_type_name = $posts_connector->get_post_type_name( get_post_type( $record->object_id ) );
219
- $links[ sprintf( esc_html_x( 'Edit %s', 'Post type singular name', 'stream' ), $post_type_name ) ] = $link;
 
220
  }
221
 
222
- if ( post_type_exists( get_post_type( $record->object_id ) ) && $link = get_permalink( $record->object_id ) ) {
223
- $links[ esc_html__( 'View', 'stream' ) ] = $link;
 
224
  }
225
  }
226
 
@@ -231,9 +234,14 @@ class Connector_Woocommerce extends Connector {
231
  $option_section = $record->get_meta( 'section', true );
232
 
233
  if ( $option_key && $option_tab ) {
 
234
  $text = sprintf( esc_html__( 'Edit WooCommerce %s', 'stream' ), $context_labels[ $record->context ] );
235
  $url = add_query_arg(
236
- array( 'page' => $option_page, 'tab' => $option_tab, 'section' => $option_section ),
 
 
 
 
237
  admin_url( 'admin.php' ) // Not self_admin_url here, as WooCommerce doesn't exist in Network Admin
238
  );
239
 
@@ -308,6 +316,7 @@ class Connector_Woocommerce extends Connector {
308
  if ( in_array( $new, array( 'auto-draft', 'draft', 'inherit' ), true ) ) {
309
  return;
310
  } elseif ( 'auto-draft' === $old && 'publish' === $new ) {
 
311
  $message = esc_html_x(
312
  '%s created',
313
  'Order title',
@@ -315,6 +324,7 @@ class Connector_Woocommerce extends Connector {
315
  );
316
  $action = 'created';
317
  } elseif ( 'trash' === $new ) {
 
318
  $message = esc_html_x(
319
  '%s trashed',
320
  'Order title',
@@ -322,6 +332,7 @@ class Connector_Woocommerce extends Connector {
322
  );
323
  $action = 'trashed';
324
  } elseif ( 'trash' === $old && 'publish' === $new ) {
 
325
  $message = esc_html_x(
326
  '%s restored from the trash',
327
  'Order title',
@@ -329,6 +340,7 @@ class Connector_Woocommerce extends Connector {
329
  );
330
  $action = 'untrashed';
331
  } else {
 
332
  $message = esc_html_x(
333
  '%s updated',
334
  'Order title',
@@ -386,6 +398,7 @@ class Connector_Woocommerce extends Connector {
386
  $order_type_name = esc_html__( 'order', 'stream' );
387
 
388
  $this->log(
 
389
  _x(
390
  '"%s" deleted from trash',
391
  'Order title',
@@ -425,12 +438,13 @@ class Connector_Woocommerce extends Connector {
425
  $old_status_name = wc_get_order_status_name( $old );
426
  $new_status_name = wc_get_order_status_name( $new );
427
  } else {
428
- $old_status = wp_stream_is_vip() ? wpcom_vip_get_term_by( 'slug', $old, 'shop_order_status' ) : get_term_by( 'slug', $old, 'shop_order_status' );
429
- $new_status = wp_stream_is_vip() ? wpcom_vip_get_term_by( 'slug', $new, 'shop_order_status' ) : get_term_by( 'slug', $new, 'shop_order_status' );
430
  $new_status_name = $new_status->name;
431
  $old_status_name = $old_status->name;
432
  }
433
 
 
434
  $message = esc_html_x(
435
  '%1$s status changed from %2$s to %3$s',
436
  '1. Order title, 2. Old status, 3. New status',
@@ -468,6 +482,7 @@ class Connector_Woocommerce extends Connector {
468
  */
469
  public function callback_woocommerce_attribute_added( $attribute_id, $attribute ) {
470
  $this->log(
 
471
  _x(
472
  '"%s" product attribute created',
473
  'Term name',
@@ -490,6 +505,7 @@ class Connector_Woocommerce extends Connector {
490
  */
491
  public function callback_woocommerce_attribute_updated( $attribute_id, $attribute ) {
492
  $this->log(
 
493
  _x(
494
  '"%s" product attribute updated',
495
  'Term name',
@@ -512,6 +528,7 @@ class Connector_Woocommerce extends Connector {
512
  */
513
  public function callback_woocommerce_attribute_deleted( $attribute_id, $attribute_name ) {
514
  $this->log(
 
515
  _x(
516
  '"%s" product attribute deleted',
517
  'Term name',
@@ -536,6 +553,7 @@ class Connector_Woocommerce extends Connector {
536
  */
537
  public function callback_woocommerce_tax_rate_added( $tax_rate_id, $tax_rate ) {
538
  $this->log(
 
539
  _x(
540
  '"%4$s" tax rate created',
541
  'Tax rate name',
@@ -558,6 +576,7 @@ class Connector_Woocommerce extends Connector {
558
  */
559
  public function callback_woocommerce_tax_rate_updated( $tax_rate_id, $tax_rate ) {
560
  $this->log(
 
561
  _x(
562
  '"%4$s" tax rate updated',
563
  'Tax rate name',
@@ -590,6 +609,7 @@ class Connector_Woocommerce extends Connector {
590
  );
591
 
592
  $this->log(
 
593
  _x(
594
  '"%s" tax rate deleted',
595
  'Tax rate name',
@@ -651,6 +671,7 @@ class Connector_Woocommerce extends Connector {
651
  }
652
 
653
  $this->log(
 
654
  __( '"%1$s" %2$s updated', 'stream' ),
655
  array(
656
  'label' => $this->settings[ $option ]['title'],
@@ -679,8 +700,9 @@ class Connector_Woocommerce extends Connector {
679
  }
680
 
681
  $settings_cache_key = 'stream_connector_woocommerce_settings_' . sanitize_key( WC_VERSION );
 
682
 
683
- if ( $settings_transient = get_transient( $settings_cache_key ) ) {
684
  $settings = $settings_transient['settings'];
685
  $settings_pages = $settings_transient['settings_pages'];
686
  } else {
@@ -715,7 +737,7 @@ class Connector_Woocommerce extends Connector {
715
  );
716
 
717
  foreach ( $_fields as $field ) {
718
- $title = isset( $field['title'] ) ? $field['title'] : ( isset( $field['desc'] ) ? $field['desc'] : 'N/A' );
719
  $fields[ $field['id'] ] = array(
720
  'title' => $title,
721
  'page' => 'wc-settings',
@@ -731,9 +753,11 @@ class Connector_Woocommerce extends Connector {
731
  }
732
 
733
  // Provide additional context for each of the settings pages
734
- array_walk( $settings_pages, function( &$value ) {
735
- $value .= ' ' . esc_html__( 'Settings', 'stream' );
736
- });
 
 
737
 
738
  // Load Payment Gateway Settings
739
  $payment_gateway_settings = array();
114
 
115
  if ( class_exists( 'Connector_Posts' ) ) {
116
  $posts_connector = new Connector_Posts();
117
+ $context_labels = array_merge(
118
  $context_labels,
119
  $posts_connector->get_context_labels()
120
  );
140
  */
141
  public function get_custom_settings() {
142
  $custom_settings = array(
143
+ 'woocommerce_frontend_css_colors' => array(
144
  'title' => esc_html__( 'Frontend Styles', 'stream' ),
145
  'page' => 'wc-settings',
146
  'tab' => 'general',
147
  'section' => '',
148
  'type' => esc_html__( 'setting', 'stream' ),
149
  ),
150
+ 'woocommerce_default_gateway' => array(
151
  'title' => esc_html__( 'Gateway Display Default', 'stream' ),
152
  'page' => 'wc-settings',
153
  'tab' => 'checkout',
154
  'section' => '',
155
  'type' => esc_html__( 'setting', 'stream' ),
156
  ),
157
+ 'woocommerce_gateway_order' => array(
158
  'title' => esc_html__( 'Gateway Display Order', 'stream' ),
159
  'page' => 'wc-settings',
160
  'tab' => 'checkout',
168
  'section' => '',
169
  'type' => esc_html__( 'setting', 'stream' ),
170
  ),
171
+ 'woocommerce_shipping_method_order' => array(
172
  'title' => esc_html__( 'Shipping Methods Order', 'stream' ),
173
  'page' => 'wc-settings',
174
  'tab' => 'shipping',
175
  'section' => '',
176
  'type' => esc_html__( 'setting', 'stream' ),
177
  ),
178
+ 'shipping_debug_mode' => array(
179
  'title' => esc_html__( 'Shipping Debug Mode', 'stream' ),
180
  'page' => 'wc-status',
181
  'tab' => 'tools',
182
  'section' => '',
183
  'type' => esc_html__( 'tool', 'stream' ),
184
  ),
185
+ 'template_debug_mode' => array(
186
  'title' => esc_html__( 'Template Debug Mode', 'stream' ),
187
  'page' => 'wc-status',
188
  'tab' => 'tools',
189
  'section' => '',
190
  'type' => esc_html__( 'tool', 'stream' ),
191
  ),
192
+ 'uninstall_data' => array(
193
  'title' => esc_html__( 'Remove post types on uninstall', 'stream' ),
194
  'page' => 'wc-status',
195
  'tab' => 'tools',
213
  */
214
  public function action_links( $links, $record ) {
215
  if ( in_array( $record->context, $this->post_types, true ) && get_post( $record->object_id ) ) {
216
+ $edit_post_link = get_edit_post_link( $record->object_id );
217
+ if ( $edit_post_link ) {
218
  $posts_connector = new Connector_Posts();
219
+ $post_type_name = $posts_connector->get_post_type_name( get_post_type( $record->object_id ) );
220
+ // translators: Placeholder refers to a post type singular name (e.g. "Post")
221
+ $links[ sprintf( esc_html_x( 'Edit %s', 'Post type singular name', 'stream' ), $post_type_name ) ] = $edit_post_link;
222
  }
223
 
224
+ $permalink = get_permalink( $record->object_id );
225
+ if ( post_type_exists( get_post_type( $record->object_id ) ) && $permalink ) {
226
+ $links[ esc_html__( 'View', 'stream' ) ] = $permalink;
227
  }
228
  }
229
 
234
  $option_section = $record->get_meta( 'section', true );
235
 
236
  if ( $option_key && $option_tab ) {
237
+ // translators: Placeholder refers to a context (e.g. "Attribute")
238
  $text = sprintf( esc_html__( 'Edit WooCommerce %s', 'stream' ), $context_labels[ $record->context ] );
239
  $url = add_query_arg(
240
+ array(
241
+ 'page' => $option_page,
242
+ 'tab' => $option_tab,
243
+ 'section' => $option_section,
244
+ ),
245
  admin_url( 'admin.php' ) // Not self_admin_url here, as WooCommerce doesn't exist in Network Admin
246
  );
247
 
316
  if ( in_array( $new, array( 'auto-draft', 'draft', 'inherit' ), true ) ) {
317
  return;
318
  } elseif ( 'auto-draft' === $old && 'publish' === $new ) {
319
+ // translators: Placeholder refers to an order title (e.g. "Order #42")
320
  $message = esc_html_x(
321
  '%s created',
322
  'Order title',
324
  );
325
  $action = 'created';
326
  } elseif ( 'trash' === $new ) {
327
+ // translators: Placeholder refers to an order title (e.g. "Order #42")
328
  $message = esc_html_x(
329
  '%s trashed',
330
  'Order title',
332
  );
333
  $action = 'trashed';
334
  } elseif ( 'trash' === $old && 'publish' === $new ) {
335
+ // translators: Placeholder refers to an order title (e.g. "Order #42")
336
  $message = esc_html_x(
337
  '%s restored from the trash',
338
  'Order title',
340
  );
341
  $action = 'untrashed';
342
  } else {
343
+ // translators: Placeholder refers to an order title (e.g. "Order #42")
344
  $message = esc_html_x(
345
  '%s updated',
346
  'Order title',
398
  $order_type_name = esc_html__( 'order', 'stream' );
399
 
400
  $this->log(
401
+ // translators: Placeholder refers to an order title (e.g. "Order #42")
402
  _x(
403
  '"%s" deleted from trash',
404
  'Order title',
438
  $old_status_name = wc_get_order_status_name( $old );
439
  $new_status_name = wc_get_order_status_name( $new );
440
  } else {
441
+ $old_status = wp_stream_is_vip() ? wpcom_vip_get_term_by( 'slug', $old, 'shop_order_status' ) : get_term_by( 'slug', $old, 'shop_order_status' );
442
+ $new_status = wp_stream_is_vip() ? wpcom_vip_get_term_by( 'slug', $new, 'shop_order_status' ) : get_term_by( 'slug', $new, 'shop_order_status' );
443
  $new_status_name = $new_status->name;
444
  $old_status_name = $old_status->name;
445
  }
446
 
447
+ // translators: Placeholders refer to an order title, and order status, and another order status (e.g. "Order #42", "processing", "complete")
448
  $message = esc_html_x(
449
  '%1$s status changed from %2$s to %3$s',
450
  '1. Order title, 2. Old status, 3. New status',
482
  */
483
  public function callback_woocommerce_attribute_added( $attribute_id, $attribute ) {
484
  $this->log(
485
+ // translators: Placeholder refers to a term name (e.g. "color")
486
  _x(
487
  '"%s" product attribute created',
488
  'Term name',
505
  */
506
  public function callback_woocommerce_attribute_updated( $attribute_id, $attribute ) {
507
  $this->log(
508
+ // translators: Placeholder refers to a term name (e.g. "color")
509
  _x(
510
  '"%s" product attribute updated',
511
  'Term name',
528
  */
529
  public function callback_woocommerce_attribute_deleted( $attribute_id, $attribute_name ) {
530
  $this->log(
531
+ // translators: Placeholder refers to a term name (e.g. "color")
532
  _x(
533
  '"%s" product attribute deleted',
534
  'Term name',
553
  */
554
  public function callback_woocommerce_tax_rate_added( $tax_rate_id, $tax_rate ) {
555
  $this->log(
556
+ // translators: Placeholder refers to a tax rate name (e.g. "GST")
557
  _x(
558
  '"%4$s" tax rate created',
559
  'Tax rate name',
576
  */
577
  public function callback_woocommerce_tax_rate_updated( $tax_rate_id, $tax_rate ) {
578
  $this->log(
579
+ // translators: Placeholder refers to a tax rate name (e.g. "GST")
580
  _x(
581
  '"%4$s" tax rate updated',
582
  'Tax rate name',
609
  );
610
 
611
  $this->log(
612
+ // translators: Placeholder refers to a tax rate name (e.g. "GST")
613
  _x(
614
  '"%s" tax rate deleted',
615
  'Tax rate name',
671
  }
672
 
673
  $this->log(
674
+ // translators: Placeholders refer to a setting name and a setting type (e.g. "Direct Deposit", "Payment Method")
675
  __( '"%1$s" %2$s updated', 'stream' ),
676
  array(
677
  'label' => $this->settings[ $option ]['title'],
700
  }
701
 
702
  $settings_cache_key = 'stream_connector_woocommerce_settings_' . sanitize_key( WC_VERSION );
703
+ $settings_transient = get_transient( $settings_cache_key );
704
 
705
+ if ( $settings_transient ) {
706
  $settings = $settings_transient['settings'];
707
  $settings_pages = $settings_transient['settings_pages'];
708
  } else {
737
  );
738
 
739
  foreach ( $_fields as $field ) {
740
+ $title = isset( $field['title'] ) ? $field['title'] : ( isset( $field['desc'] ) ? $field['desc'] : 'N/A' );
741
  $fields[ $field['id'] ] = array(
742
  'title' => $title,
743
  'page' => 'wc-settings',
753
  }
754
 
755
  // Provide additional context for each of the settings pages
756
+ array_walk(
757
+ $settings_pages, function( &$value ) {
758
+ $value .= ' ' . esc_html__( 'Settings', 'stream' );
759
+ }
760
+ );
761
 
762
  // Load Payment Gateway Settings
763
  $payment_gateway_settings = array();
connectors/class-connector-wordpress-seo.php CHANGED
@@ -111,7 +111,8 @@ class Connector_WordPress_SEO extends Connector {
111
  */
112
  public function action_links( $links, $record ) {
113
  // Options
114
- if ( $option = $record->get_meta( 'option', true ) ) {
 
115
  $key = $record->get_meta( 'option_key', true );
116
 
117
  $links[ esc_html__( 'Edit', 'stream' ) ] = add_query_arg(
@@ -132,7 +133,7 @@ class Connector_WordPress_SEO extends Connector {
132
 
133
  if ( $post ) {
134
  $posts_connector = new Connector_Posts();
135
- $post_type_name = $posts_connector->get_post_type_name( get_post_type( $post->ID ) );
136
 
137
  if ( 'trash' === $post->post_status ) {
138
  $untrash = wp_nonce_url(
@@ -157,16 +158,21 @@ class Connector_WordPress_SEO extends Connector {
157
  sprintf( 'delete-post_%d', $post->ID )
158
  );
159
 
 
160
  $links[ sprintf( esc_html_x( 'Restore %s', 'Post type singular name', 'stream' ), $post_type_name ) ] = $untrash;
 
161
  $links[ sprintf( esc_html_x( 'Delete %s Permenantly', 'Post type singular name', 'stream' ), $post_type_name ) ] = $delete;
162
  } else {
 
163
  $links[ sprintf( esc_html_x( 'Edit %s', 'Post type singular name', 'stream' ), $post_type_name ) ] = get_edit_post_link( $post->ID );
164
 
165
- if ( $view_link = get_permalink( $post->ID ) ) {
 
166
  $links[ esc_html__( 'View', 'stream' ) ] = $view_link;
167
  }
168
 
169
- if ( $revision_id = $record->get_meta( 'revision_id', true ) ) {
 
170
  $links[ esc_html__( 'Revision', 'stream' ) ] = get_edit_post_link( $revision_id );
171
  }
172
  }
@@ -186,7 +192,7 @@ class Connector_WordPress_SEO extends Connector {
186
  /* @var $class WPSEO_Options */
187
  $this->option_groups[ $class::get_instance()->group_name ] = array(
188
  'class' => $class,
189
- 'name' => $class::get_instance()->option_name,
190
  );
191
  }
192
 
@@ -197,7 +203,7 @@ class Connector_WordPress_SEO extends Connector {
197
  public function admin_enqueue_scripts( $hook ) {
198
  if ( 0 === strpos( $hook, 'seo_page_' ) ) {
199
  $stream = wp_stream_get_instance();
200
- $src = $stream->locations['url'] . '/ui/js/wpseo-admin.js';
201
  wp_enqueue_script( 'stream-connector-wpseo', $src, array( 'jquery' ), $stream->get_version() );
202
  }
203
  }
@@ -222,12 +228,13 @@ class Connector_WordPress_SEO extends Connector {
222
  if ( isset( $opts[ $key ] ) ) {
223
  $this->log(
224
  sprintf(
 
225
  __( 'Imported settings from %1$s%2$s', 'stream' ),
226
  $name,
227
  isset( $opts['deleteolddata'] ) ? esc_html__( ', and deleted old data', 'stream' ) : ''
228
  ),
229
  array(
230
- 'key' => $key,
231
  'deleteolddata' => isset( $opts['deleteolddata'] ),
232
  ),
233
  null,
@@ -244,6 +251,7 @@ class Connector_WordPress_SEO extends Connector {
244
  if ( wp_stream_filter_input( INPUT_POST, 'wpseo_export' ) ) {
245
  $this->log(
246
  sprintf(
 
247
  __( 'Exported settings%s', 'stream' ),
248
  isset( $opts['include_taxonomy_meta'] ) ? esc_html__( ', including taxonomy meta', 'stream' ) : ''
249
  ),
@@ -257,6 +265,7 @@ class Connector_WordPress_SEO extends Connector {
257
  } elseif ( isset( $_FILES['settings_import_file']['name'] ) ) { // phpcs: input var okay
258
  $this->log(
259
  sprintf(
 
260
  __( 'Tried importing settings from "%s"', 'stream' ),
261
  sanitize_text_field( wp_unslash( $_FILES['settings_import_file']['name'] ) ) // phpcs: input var okay
262
  ),
@@ -330,17 +339,16 @@ class Connector_WordPress_SEO extends Connector {
330
 
331
  $this->log(
332
  sprintf(
 
333
  __( 'Updated "%1$s" of "%2$s" %3$s', 'stream' ),
334
  $field['title'],
335
  $post->post_title,
336
  $post_type_label
337
  ),
338
  array(
339
- // @codingStandardsIgnoreStart
340
- 'meta_key' => $meta_key,
341
  'meta_value' => $meta_value,
342
- // @codingStandardsIgnoreEnd
343
- 'post_type' => $post->post_type,
344
  ),
345
  $object_id,
346
  'wpseo_meta',
@@ -374,7 +382,9 @@ class Connector_WordPress_SEO extends Connector {
374
  return $data;
375
  }
376
 
377
- if ( ! ( $label = $this->settings_labels( $data['args']['option_key'] ) ) ) {
 
 
378
  $data['message'] = esc_html__( '%s settings updated', 'stream' );
379
  $label = $labels[ $page ];
380
  }
@@ -391,75 +401,75 @@ class Connector_WordPress_SEO extends Connector {
391
  private function settings_labels( $option ) {
392
  $labels = array(
393
  // wp-content/plugins/wordpress-seo/admin/pages/dashboard.php:
394
- 'yoast_tracking' => esc_html_x( "Allow tracking of this WordPress install's anonymous data.", 'wordpress-seo', 'stream' ), # type = checkbox
395
- 'disableadvanced_meta' => esc_html_x( 'Disable the Advanced part of the WordPress SEO meta box', 'wordpress-seo', 'stream' ), # type = checkbox
396
- 'alexaverify' => esc_html_x( 'Alexa Verification ID', 'wordpress-seo', 'stream' ), # type = textinput
397
- 'msverify' => esc_html_x( 'Bing Webmaster Tools', 'wordpress-seo', 'stream' ), # type = textinput
398
- 'googleverify' => esc_html_x( 'Google Webmaster Tools', 'wordpress-seo', 'stream' ), # type = textinput
399
- 'pinterestverify' => esc_html_x( 'Pinterest', 'wordpress-seo', 'stream' ), # type = textinput
400
- 'yandexverify' => esc_html_x( 'Yandex Webmaster Tools', 'wordpress-seo', 'stream' ), # type = textinput
401
 
402
  // wp-content/plugins/wordpress-seo/admin/pages/advanced.php:
403
- 'breadcrumbs-enable' => esc_html_x( 'Enable Breadcrumbs', 'wordpress-seo', 'stream' ), # type = checkbox
404
- 'breadcrumbs-sep' => esc_html_x( 'Separator between breadcrumbs', 'wordpress-seo', 'stream' ), # type = textinput
405
- 'breadcrumbs-home' => esc_html_x( 'Anchor text for the Homepage', 'wordpress-seo', 'stream' ), # type = textinput
406
- 'breadcrumbs-prefix' => esc_html_x( 'Prefix for the breadcrumb path', 'wordpress-seo', 'stream' ), # type = textinput
407
- 'breadcrumbs-archiveprefix' => esc_html_x( 'Prefix for Archive breadcrumbs', 'wordpress-seo', 'stream' ), # type = textinput
408
- 'breadcrumbs-searchprefix' => esc_html_x( 'Prefix for Search Page breadcrumbs', 'wordpress-seo', 'stream' ), # type = textinput
409
- 'breadcrumbs-404crumb' => esc_html_x( 'Breadcrumb for 404 Page', 'wordpress-seo', 'stream' ), # type = textinput
410
- 'breadcrumbs-blog-remove' => esc_html_x( 'Remove Blog page from Breadcrumbs', 'wordpress-seo', 'stream' ), # type = checkbox
411
- 'breadcrumbs-boldlast' => esc_html_x( 'Bold the last page in the breadcrumb', 'wordpress-seo', 'stream' ), # type = checkbox
412
- 'post_types-post-maintax' => esc_html_x( 'Taxonomy to show in breadcrumbs for post types', 'wordpress-seo', 'stream' ), # type = select
413
 
414
  // wp-content/plugins/wordpress-seo/admin/pages/metas.php:
415
- 'forcerewritetitle' => esc_html_x( 'Force rewrite titles', 'wordpress-seo', 'stream' ), # type = checkbox
416
- 'noindex-subpages-wpseo' => esc_html_x( 'Noindex subpages of archives', 'wordpress-seo', 'stream' ), # type = checkbox
417
- 'usemetakeywords' => _x( 'Use <code>meta</code> keywords tag?', 'wordpress-seo', 'stream' ), # type = checkbox
418
- 'noodp' => _x( 'Add <code>noodp</code> meta robots tag sitewide', 'wordpress-seo', 'stream' ), # type = checkbox
419
- 'noydir' => _x( 'Add <code>noydir</code> meta robots tag sitewide', 'wordpress-seo', 'stream' ), # type = checkbox
420
- 'hide-rsdlink' => esc_html_x( 'Hide RSD Links', 'wordpress-seo', 'stream' ), # type = checkbox
421
- 'hide-wlwmanifest' => esc_html_x( 'Hide WLW Manifest Links', 'wordpress-seo', 'stream' ), # type = checkbox
422
- 'hide-shortlink' => esc_html_x( 'Hide Shortlink for posts', 'wordpress-seo', 'stream' ), # type = checkbox
423
- 'hide-feedlinks' => esc_html_x( 'Hide RSS Links', 'wordpress-seo', 'stream' ), # type = checkbox
424
- 'disable-author' => esc_html_x( 'Disable the author archives', 'wordpress-seo', 'stream' ), # type = checkbox
425
- 'disable-date' => esc_html_x( 'Disable the date-based archives', 'wordpress-seo', 'stream' ), # type = checkbox
426
 
427
  // wp-content/plugins/wordpress-seo/admin/pages/network.php:
428
- 'access' => esc_html_x( 'Who should have access to the WordPress SEO settings', 'wordpress-seo', 'stream' ), # type = select
429
- 'defaultblog' => esc_html_x( 'New blogs get the SEO settings from this blog', 'wordpress-seo', 'stream' ), # type = textinput
430
- 'restoreblog' => esc_html_x( 'Blog ID', 'wordpress-seo', 'stream' ), # type = textinput
431
 
432
  // wp-content/plugins/wordpress-seo/admin/pages/permalinks.php:
433
- 'stripcategorybase' => _x( 'Strip the category base (usually <code>/category/</code>) from the category URL.', 'wordpress-seo', 'stream' ), # type = checkbox
434
- 'trailingslash' => esc_html_x( "Enforce a trailing slash on all category and tag URL's", 'wordpress-seo', 'stream' ), # type = checkbox
435
- 'cleanslugs' => esc_html_x( 'Remove stop words from slugs.', 'wordpress-seo', 'stream' ), # type = checkbox
436
- 'redirectattachment' => esc_html_x( "Redirect attachment URL's to parent post URL.", 'wordpress-seo', 'stream' ), # type = checkbox
437
- 'cleanreplytocom' => _x( 'Remove the <code>?replytocom</code> variables.', 'wordpress-seo', 'stream' ), # type = checkbox
438
- 'cleanpermalinks' => esc_html_x( "Redirect ugly URL's to clean permalinks. (Not recommended in many cases!)", 'wordpress-seo', 'stream' ), # type = checkbox
439
- 'force_transport' => esc_html_x( 'Force Transport', 'wordpress-seo', 'stream' ), # type = select
440
- 'cleanpermalink-googlesitesearch' => esc_html_x( "Prevent cleaning out Google Site Search URL's.", 'wordpress-seo', 'stream' ), # type = checkbox
441
- 'cleanpermalink-googlecampaign' => esc_html_x( 'Prevent cleaning out Google Analytics Campaign & Google AdWords Parameters.', 'wordpress-seo', 'stream' ), # type = checkbox
442
- 'cleanpermalink-extravars' => esc_html_x( 'Other variables not to clean', 'wordpress-seo', 'stream' ), # type = textinput
443
 
444
  // wp-content/plugins/wordpress-seo/admin/pages/social.php:
445
- 'opengraph' => esc_html_x( 'Add Open Graph meta data', 'wordpress-seo', 'stream' ), # type = checkbox
446
- 'facebook_site' => esc_html_x( 'Facebook Page URL', 'wordpress-seo', 'stream' ), # type = textinput
447
- 'instagram_url' => esc_html_x( 'Instagram URL', 'wordpress-seo', 'stream' ), # type = textinput
448
- 'linkedin_url' => esc_html_x( 'LinkedIn URL', 'wordpress-seo', 'stream' ), # type = textinput
449
- 'myspace_url' => esc_html_x( 'MySpace URL', 'wordpress-seo', 'stream' ), # type = textinput
450
- 'pinterest_url' => esc_html_x( 'Pinterest URL', 'wordpress-seo', 'stream' ), # type = textinput
451
- 'youtube_url' => esc_html_x( 'YouTube URL', 'wordpress-seo', 'stream' ), # type = textinput
452
- 'google_plus_url' => esc_html_x( 'Google+ URL', 'wordpress-seo', 'stream' ), # type = textinput
453
- 'og_frontpage_image' => esc_html_x( 'Image URL', 'wordpress-seo', 'stream' ), # type = textinput
454
- 'og_frontpage_desc' => esc_html_x( 'Description', 'wordpress-seo', 'stream' ), # type = textinput
455
- 'og_frontpage_title' => esc_html_x( 'Title', 'wordpress-seo', 'stream' ), # type = textinput
456
- 'og_default_image' => esc_html_x( 'Image URL', 'wordpress-seo', 'stream' ), # type = textinput
457
- 'twitter' => esc_html_x( 'Add Twitter card meta data', 'wordpress-seo', 'stream' ), # type = checkbox
458
- 'twitter_site' => esc_html_x( 'Site Twitter Username', 'wordpress-seo', 'stream' ), # type = textinput
459
- 'twitter_card_type' => esc_html_x( 'The default card type to use', 'wordpress-seo', 'stream' ), # type = select
460
- 'googleplus' => esc_html_x( 'Add Google+ specific post meta data (excluding author metadata)', 'wordpress-seo', 'stream' ), # type = checkbox
461
- 'plus-publisher' => esc_html_x( 'Google Publisher Page', 'wordpress-seo', 'stream' ), # type = textinput
462
- 'fbadminapp' => esc_html_x( 'Facebook App ID', 'wordpress-seo', 'stream' ), # type = textinput
463
 
464
  // wp-content/plugins/wordpress-seo/admin/pages/xml-sitemaps.php:
465
  'enablexmlsitemap' => esc_html_x( 'Check this box to enable XML sitemap functionality.', 'wordpress-seo', 'stream' ), # type = checkbox
@@ -481,8 +491,8 @@ class Connector_WordPress_SEO extends Connector {
481
  'taxonomies-post_tag-not_in_sitemap' => _x( 'Taxonomies Tags (<code>post_tag</code>)', 'wordpress-seo', 'stream' ), # type = checkbox
482
 
483
  // Added manually
484
- 'rssbefore' => esc_html_x( 'Content to put before each post in the feed', 'wordpress-seo', 'stream' ),
485
- 'rssafter' => esc_html_x( 'Content to put after each post', 'wordpress-seo', 'stream' ),
486
  );
487
 
488
  $ast_labels = array(
111
  */
112
  public function action_links( $links, $record ) {
113
  // Options
114
+ $option = $record->get_meta( 'option', true );
115
+ if ( $option ) {
116
  $key = $record->get_meta( 'option_key', true );
117
 
118
  $links[ esc_html__( 'Edit', 'stream' ) ] = add_query_arg(
133
 
134
  if ( $post ) {
135
  $posts_connector = new Connector_Posts();
136
+ $post_type_name = $posts_connector->get_post_type_name( get_post_type( $post->ID ) );
137
 
138
  if ( 'trash' === $post->post_status ) {
139
  $untrash = wp_nonce_url(
158
  sprintf( 'delete-post_%d', $post->ID )
159
  );
160
 
161
+ // translators: Placeholder refers to a post type singular name (e.g. "Post")
162
  $links[ sprintf( esc_html_x( 'Restore %s', 'Post type singular name', 'stream' ), $post_type_name ) ] = $untrash;
163
+ // translators: Placeholder refers to a post type singular name (e.g. "Post")
164
  $links[ sprintf( esc_html_x( 'Delete %s Permenantly', 'Post type singular name', 'stream' ), $post_type_name ) ] = $delete;
165
  } else {
166
+ // translators: Placeholder refers to a post type singular name (e.g. "Post")
167
  $links[ sprintf( esc_html_x( 'Edit %s', 'Post type singular name', 'stream' ), $post_type_name ) ] = get_edit_post_link( $post->ID );
168
 
169
+ $view_link = get_permalink( $post->ID );
170
+ if ( $view_link ) {
171
  $links[ esc_html__( 'View', 'stream' ) ] = $view_link;
172
  }
173
 
174
+ $revision_id = $record->get_meta( 'revision_id', true );
175
+ if ( $revision_id ) {
176
  $links[ esc_html__( 'Revision', 'stream' ) ] = get_edit_post_link( $revision_id );
177
  }
178
  }
192
  /* @var $class WPSEO_Options */
193
  $this->option_groups[ $class::get_instance()->group_name ] = array(
194
  'class' => $class,
195
+ 'name' => $class::get_instance()->option_name,
196
  );
197
  }
198
 
203
  public function admin_enqueue_scripts( $hook ) {
204
  if ( 0 === strpos( $hook, 'seo_page_' ) ) {
205
  $stream = wp_stream_get_instance();
206
+ $src = $stream->locations['url'] . '/ui/js/wpseo-admin.js';
207
  wp_enqueue_script( 'stream-connector-wpseo', $src, array( 'jquery' ), $stream->get_version() );
208
  }
209
  }
228
  if ( isset( $opts[ $key ] ) ) {
229
  $this->log(
230
  sprintf(
231
+ // translators: Placeholders refer to an import method, and an extra string (sometimes blank) (e.g. "HeadSpace2", ", and deleted old data")
232
  __( 'Imported settings from %1$s%2$s', 'stream' ),
233
  $name,
234
  isset( $opts['deleteolddata'] ) ? esc_html__( ', and deleted old data', 'stream' ) : ''
235
  ),
236
  array(
237
+ 'key' => $key,
238
  'deleteolddata' => isset( $opts['deleteolddata'] ),
239
  ),
240
  null,
251
  if ( wp_stream_filter_input( INPUT_POST, 'wpseo_export' ) ) {
252
  $this->log(
253
  sprintf(
254
+ // translators: Placeholder refers to an extra string (sometimes blank) (e.g. ", including taxonomy meta")
255
  __( 'Exported settings%s', 'stream' ),
256
  isset( $opts['include_taxonomy_meta'] ) ? esc_html__( ', including taxonomy meta', 'stream' ) : ''
257
  ),
265
  } elseif ( isset( $_FILES['settings_import_file']['name'] ) ) { // phpcs: input var okay
266
  $this->log(
267
  sprintf(
268
+ // translators: Placeholder refers to a filename (e.g. "test.xml")
269
  __( 'Tried importing settings from "%s"', 'stream' ),
270
  sanitize_text_field( wp_unslash( $_FILES['settings_import_file']['name'] ) ) // phpcs: input var okay
271
  ),
339
 
340
  $this->log(
341
  sprintf(
342
+ // translators: Placeholders refer to a meta field title, a post title, and a post type (e.g. "Description", "Hello World", "Post")
343
  __( 'Updated "%1$s" of "%2$s" %3$s', 'stream' ),
344
  $field['title'],
345
  $post->post_title,
346
  $post_type_label
347
  ),
348
  array(
349
+ 'meta_key' => $meta_key,
 
350
  'meta_value' => $meta_value,
351
+ 'post_type' => $post->post_type,
 
352
  ),
353
  $object_id,
354
  'wpseo_meta',
382
  return $data;
383
  }
384
 
385
+ $label = $this->settings_labels( $data['args']['option_key'] );
386
+ if ( ! $label ) {
387
+ // translators: Placeholder refers to a context (e.g. "Dashboard")
388
  $data['message'] = esc_html__( '%s settings updated', 'stream' );
389
  $label = $labels[ $page ];
390
  }
401
  private function settings_labels( $option ) {
402
  $labels = array(
403
  // wp-content/plugins/wordpress-seo/admin/pages/dashboard.php:
404
+ 'yoast_tracking' => esc_html_x( "Allow tracking of this WordPress install's anonymous data.", 'wordpress-seo', 'stream' ), # type = checkbox
405
+ 'disableadvanced_meta' => esc_html_x( 'Disable the Advanced part of the WordPress SEO meta box', 'wordpress-seo', 'stream' ), # type = checkbox
406
+ 'alexaverify' => esc_html_x( 'Alexa Verification ID', 'wordpress-seo', 'stream' ), # type = textinput
407
+ 'msverify' => esc_html_x( 'Bing Webmaster Tools', 'wordpress-seo', 'stream' ), # type = textinput
408
+ 'googleverify' => esc_html_x( 'Google Webmaster Tools', 'wordpress-seo', 'stream' ), # type = textinput
409
+ 'pinterestverify' => esc_html_x( 'Pinterest', 'wordpress-seo', 'stream' ), # type = textinput
410
+ 'yandexverify' => esc_html_x( 'Yandex Webmaster Tools', 'wordpress-seo', 'stream' ), # type = textinput
411
 
412
  // wp-content/plugins/wordpress-seo/admin/pages/advanced.php:
413
+ 'breadcrumbs-enable' => esc_html_x( 'Enable Breadcrumbs', 'wordpress-seo', 'stream' ), # type = checkbox
414
+ 'breadcrumbs-sep' => esc_html_x( 'Separator between breadcrumbs', 'wordpress-seo', 'stream' ), # type = textinput
415
+ 'breadcrumbs-home' => esc_html_x( 'Anchor text for the Homepage', 'wordpress-seo', 'stream' ), # type = textinput
416
+ 'breadcrumbs-prefix' => esc_html_x( 'Prefix for the breadcrumb path', 'wordpress-seo', 'stream' ), # type = textinput
417
+ 'breadcrumbs-archiveprefix' => esc_html_x( 'Prefix for Archive breadcrumbs', 'wordpress-seo', 'stream' ), # type = textinput
418
+ 'breadcrumbs-searchprefix' => esc_html_x( 'Prefix for Search Page breadcrumbs', 'wordpress-seo', 'stream' ), # type = textinput
419
+ 'breadcrumbs-404crumb' => esc_html_x( 'Breadcrumb for 404 Page', 'wordpress-seo', 'stream' ), # type = textinput
420
+ 'breadcrumbs-blog-remove' => esc_html_x( 'Remove Blog page from Breadcrumbs', 'wordpress-seo', 'stream' ), # type = checkbox
421
+ 'breadcrumbs-boldlast' => esc_html_x( 'Bold the last page in the breadcrumb', 'wordpress-seo', 'stream' ), # type = checkbox
422
+ 'post_types-post-maintax' => esc_html_x( 'Taxonomy to show in breadcrumbs for post types', 'wordpress-seo', 'stream' ), # type = select
423
 
424
  // wp-content/plugins/wordpress-seo/admin/pages/metas.php:
425
+ 'forcerewritetitle' => esc_html_x( 'Force rewrite titles', 'wordpress-seo', 'stream' ), # type = checkbox
426
+ 'noindex-subpages-wpseo' => esc_html_x( 'Noindex subpages of archives', 'wordpress-seo', 'stream' ), # type = checkbox
427
+ 'usemetakeywords' => _x( 'Use <code>meta</code> keywords tag?', 'wordpress-seo', 'stream' ), # type = checkbox
428
+ 'noodp' => _x( 'Add <code>noodp</code> meta robots tag sitewide', 'wordpress-seo', 'stream' ), # type = checkbox
429
+ 'noydir' => _x( 'Add <code>noydir</code> meta robots tag sitewide', 'wordpress-seo', 'stream' ), # type = checkbox
430
+ 'hide-rsdlink' => esc_html_x( 'Hide RSD Links', 'wordpress-seo', 'stream' ), # type = checkbox
431
+ 'hide-wlwmanifest' => esc_html_x( 'Hide WLW Manifest Links', 'wordpress-seo', 'stream' ), # type = checkbox
432
+ 'hide-shortlink' => esc_html_x( 'Hide Shortlink for posts', 'wordpress-seo', 'stream' ), # type = checkbox
433
+ 'hide-feedlinks' => esc_html_x( 'Hide RSS Links', 'wordpress-seo', 'stream' ), # type = checkbox
434
+ 'disable-author' => esc_html_x( 'Disable the author archives', 'wordpress-seo', 'stream' ), # type = checkbox
435
+ 'disable-date' => esc_html_x( 'Disable the date-based archives', 'wordpress-seo', 'stream' ), # type = checkbox
436
 
437
  // wp-content/plugins/wordpress-seo/admin/pages/network.php:
438
+ 'access' => esc_html_x( 'Who should have access to the WordPress SEO settings', 'wordpress-seo', 'stream' ), # type = select
439
+ 'defaultblog' => esc_html_x( 'New blogs get the SEO settings from this blog', 'wordpress-seo', 'stream' ), # type = textinput
440
+ 'restoreblog' => esc_html_x( 'Blog ID', 'wordpress-seo', 'stream' ), # type = textinput
441
 
442
  // wp-content/plugins/wordpress-seo/admin/pages/permalinks.php:
443
+ 'stripcategorybase' => _x( 'Strip the category base (usually <code>/category/</code>) from the category URL.', 'wordpress-seo', 'stream' ), # type = checkbox
444
+ 'trailingslash' => esc_html_x( "Enforce a trailing slash on all category and tag URL's", 'wordpress-seo', 'stream' ), # type = checkbox
445
+ 'cleanslugs' => esc_html_x( 'Remove stop words from slugs.', 'wordpress-seo', 'stream' ), # type = checkbox
446
+ 'redirectattachment' => esc_html_x( "Redirect attachment URL's to parent post URL.", 'wordpress-seo', 'stream' ), # type = checkbox
447
+ 'cleanreplytocom' => _x( 'Remove the <code>?replytocom</code> variables.', 'wordpress-seo', 'stream' ), # type = checkbox
448
+ 'cleanpermalinks' => esc_html_x( "Redirect ugly URL's to clean permalinks. (Not recommended in many cases!)", 'wordpress-seo', 'stream' ), # type = checkbox
449
+ 'force_transport' => esc_html_x( 'Force Transport', 'wordpress-seo', 'stream' ), # type = select
450
+ 'cleanpermalink-googlesitesearch' => esc_html_x( "Prevent cleaning out Google Site Search URL's.", 'wordpress-seo', 'stream' ), # type = checkbox
451
+ 'cleanpermalink-googlecampaign' => esc_html_x( 'Prevent cleaning out Google Analytics Campaign & Google AdWords Parameters.', 'wordpress-seo', 'stream' ), # type = checkbox
452
+ 'cleanpermalink-extravars' => esc_html_x( 'Other variables not to clean', 'wordpress-seo', 'stream' ), # type = textinput
453
 
454
  // wp-content/plugins/wordpress-seo/admin/pages/social.php:
455
+ 'opengraph' => esc_html_x( 'Add Open Graph meta data', 'wordpress-seo', 'stream' ), # type = checkbox
456
+ 'facebook_site' => esc_html_x( 'Facebook Page URL', 'wordpress-seo', 'stream' ), # type = textinput
457
+ 'instagram_url' => esc_html_x( 'Instagram URL', 'wordpress-seo', 'stream' ), # type = textinput
458
+ 'linkedin_url' => esc_html_x( 'LinkedIn URL', 'wordpress-seo', 'stream' ), # type = textinput
459
+ 'myspace_url' => esc_html_x( 'MySpace URL', 'wordpress-seo', 'stream' ), # type = textinput
460
+ 'pinterest_url' => esc_html_x( 'Pinterest URL', 'wordpress-seo', 'stream' ), # type = textinput
461
+ 'youtube_url' => esc_html_x( 'YouTube URL', 'wordpress-seo', 'stream' ), # type = textinput
462
+ 'google_plus_url' => esc_html_x( 'Google+ URL', 'wordpress-seo', 'stream' ), # type = textinput
463
+ 'og_frontpage_image' => esc_html_x( 'Image URL', 'wordpress-seo', 'stream' ), # type = textinput
464
+ 'og_frontpage_desc' => esc_html_x( 'Description', 'wordpress-seo', 'stream' ), # type = textinput
465
+ 'og_frontpage_title' => esc_html_x( 'Title', 'wordpress-seo', 'stream' ), # type = textinput
466
+ 'og_default_image' => esc_html_x( 'Image URL', 'wordpress-seo', 'stream' ), # type = textinput
467
+ 'twitter' => esc_html_x( 'Add Twitter card meta data', 'wordpress-seo', 'stream' ), # type = checkbox
468
+ 'twitter_site' => esc_html_x( 'Site Twitter Username', 'wordpress-seo', 'stream' ), # type = textinput
469
+ 'twitter_card_type' => esc_html_x( 'The default card type to use', 'wordpress-seo', 'stream' ), # type = select
470
+ 'googleplus' => esc_html_x( 'Add Google+ specific post meta data (excluding author metadata)', 'wordpress-seo', 'stream' ), # type = checkbox
471
+ 'plus-publisher' => esc_html_x( 'Google Publisher Page', 'wordpress-seo', 'stream' ), # type = textinput
472
+ 'fbadminapp' => esc_html_x( 'Facebook App ID', 'wordpress-seo', 'stream' ), # type = textinput
473
 
474
  // wp-content/plugins/wordpress-seo/admin/pages/xml-sitemaps.php:
475
  'enablexmlsitemap' => esc_html_x( 'Check this box to enable XML sitemap functionality.', 'wordpress-seo', 'stream' ), # type = checkbox
491
  'taxonomies-post_tag-not_in_sitemap' => _x( 'Taxonomies Tags (<code>post_tag</code>)', 'wordpress-seo', 'stream' ), # type = checkbox
492
 
493
  // Added manually
494
+ 'rssbefore' => esc_html_x( 'Content to put before each post in the feed', 'wordpress-seo', 'stream' ),
495
+ 'rssafter' => esc_html_x( 'Content to put after each post', 'wordpress-seo', 'stream' ),
496
  );
497
 
498
  $ast_labels = array(
includes/db-updates.php CHANGED
@@ -32,9 +32,21 @@ function wp_stream_update_302( $db_version, $current_version ) {
32
  $class = 'Connector_' . $entry->context;
33
  if ( class_exists( $class ) ) {
34
  $connector = new $class();
35
- $wpdb->update( $wpdb->base_prefix . 'stream', array( 'connector' => $connector->name ), array( 'ID' => $entry->ID ) );
 
 
 
 
 
 
36
  } else {
37
- $wpdb->update( $wpdb->base_prefix . 'stream', array( 'connector' => strtolower( $entry->connector ) ), array( 'ID' => $entry->ID ) );
 
 
 
 
 
 
38
  }
39
  }
40
 
32
  $class = 'Connector_' . $entry->context;
33
  if ( class_exists( $class ) ) {
34
  $connector = new $class();
35
+ $wpdb->update(
36
+ $wpdb->base_prefix . 'stream', array(
37
+ 'connector' => $connector->name,
38
+ ), array(
39
+ 'ID' => $entry->ID,
40
+ )
41
+ );
42
  } else {
43
+ $wpdb->update(
44
+ $wpdb->base_prefix . 'stream', array(
45
+ 'connector' => strtolower( $entry->connector ),
46
+ ), array(
47
+ 'ID' => $entry->ID,
48
+ )
49
+ );
50
  }
51
  }
52
 
includes/feeds/atom.php CHANGED
@@ -3,15 +3,15 @@ header( 'Content-Type: ' . feed_content_type( 'atom' ) . '; charset=' . get_opti
3
  printf( '<?xml version="1.0" encoding="%s"?>', esc_attr( get_option( 'blog_charset' ) ) );
4
  ?>
5
 
6
- <feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xml:lang="<?php echo esc_attr( bloginfo_rss( 'language' ) ) ?>" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" <?php do_action( 'atom_ns' ) ?>>
7
- <title><?php bloginfo_rss( 'name' ) ?> - <?php esc_html_e( 'Stream Feed', 'stream' ) ?></title>
8
- <link href="<?php self_link() ?>" rel="self" type="application/rss+xml" />
9
- <link href="<?php echo esc_url( $records_admin_url ) ?>" />
10
- <subtitle type="html"><?php esc_html( bloginfo_rss( 'description' ) ) ?></subtitle>
11
- <updated><?php echo esc_html( mysql2date( 'c', $latest_record, false ) ) ?></updated>
12
- <id><?php echo esc_url( $latest_link ) ?></id>
13
- <sy:updatePeriod><?php echo esc_html( 'hourly' ) ?></sy:updatePeriod>
14
- <sy:updateFrequency><?php echo absint( 1 ) ?></sy:updateFrequency>
15
  <?php
16
  /**
17
  * Action fires during RSS head
@@ -19,7 +19,7 @@ printf( '<?xml version="1.0" encoding="%s"?>', esc_attr( get_option( 'blog_chars
19
  do_action( 'atom_head' );
20
 
21
  foreach ( $records as $record ) :
22
- $record_link = add_query_arg(
23
  array(
24
  'record__in' => $record->ID,
25
  ),
@@ -30,18 +30,18 @@ printf( '<?xml version="1.0" encoding="%s"?>', esc_attr( get_option( 'blog_chars
30
  $display_name = isset( $author->display_name ) ? $author->display_name : 'N/A';
31
  ?>
32
  <entry>
33
- <title type="html"><![CDATA[[<?php echo esc_html( $domain ) ?>] <?php echo esc_html( $record->summary ) // xss ok ?> ]]></title>
34
- <link href="<?php echo esc_url( $record_link ) ?>" />
35
- <updated><?php echo esc_html( mysql2date( 'c', $record->created, false ) ) ?></updated>
36
  <author>
37
- <name><?php echo esc_html( $display_name ) ?></name>
38
  </author>
39
- <category term="connector" label="<?php echo esc_html( $record->connector ) ?>" />
40
- <category term="context" label="<?php echo esc_html( $record->context ) ?>"/>
41
- <category term="action" label="<?php echo esc_html( $record->action ) ?>" />
42
- <category term="ip" label="<?php echo esc_html( $record->ip ) ?>" />
43
- <id><?php echo esc_url( $record_link ) ?></id>
44
- <summary type="html"><![CDATA[- <?php echo esc_html( $display_name ) ?> ]]></summary>
45
  <?php
46
  /**
47
  * Action fires during Atom item
3
  printf( '<?xml version="1.0" encoding="%s"?>', esc_attr( get_option( 'blog_charset' ) ) );
4
  ?>
5
 
6
+ <feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xml:lang="<?php echo esc_attr( bloginfo_rss( 'language' ) ); ?>" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" <?php do_action( 'atom_ns' ); ?>>
7
+ <title><?php bloginfo_rss( 'name' ); ?> - <?php esc_html_e( 'Stream Feed', 'stream' ); ?></title>
8
+ <link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
9
+ <link href="<?php echo esc_url( $records_admin_url ); ?>" />
10
+ <subtitle type="html"><?php esc_html( bloginfo_rss( 'description' ) ); ?></subtitle>
11
+ <updated><?php echo esc_html( mysql2date( 'c', $latest_record, false ) ); ?></updated>
12
+ <id><?php echo esc_url( $latest_link ); ?></id>
13
+ <sy:updatePeriod><?php echo esc_html( 'hourly' ); ?></sy:updatePeriod>
14
+ <sy:updateFrequency><?php echo absint( 1 ); ?></sy:updateFrequency>
15
  <?php
16
  /**
17
  * Action fires during RSS head
19
  do_action( 'atom_head' );
20
 
21
  foreach ( $records as $record ) :
22
+ $record_link = add_query_arg(
23
  array(
24
  'record__in' => $record->ID,
25
  ),
30
  $display_name = isset( $author->display_name ) ? $author->display_name : 'N/A';
31
  ?>
32
  <entry>
33
+ <title type="html"><![CDATA[[<?php echo esc_html( $domain ); ?>] <?php echo esc_html( $record->summary ); // xss ok ?> ]]></title>
34
+ <link href="<?php echo esc_url( $record_link ); ?>" />
35
+ <updated><?php echo esc_html( mysql2date( 'c', $record->created, false ) ); ?></updated>
36
  <author>
37
+ <name><?php echo esc_html( $display_name ); ?></name>
38
  </author>
39
+ <category term="connector" label="<?php echo esc_html( $record->connector ); ?>" />
40
+ <category term="context" label="<?php echo esc_html( $record->context ); ?>"/>
41
+ <category term="action" label="<?php echo esc_html( $record->action ); ?>" />
42
+ <category term="ip" label="<?php echo esc_html( $record->ip ); ?>" />
43
+ <id><?php echo esc_url( $record_link ); ?></id>
44
+ <summary type="html"><![CDATA[- <?php echo esc_html( $display_name ); ?> ]]></summary>
45
  <?php
46
  /**
47
  * Action fires during Atom item
includes/feeds/rss-2.0.php CHANGED
@@ -18,14 +18,14 @@ printf( '<?xml version="1.0" encoding="%s"?>', esc_attr( get_option( 'blog_chars
18
  ?>
19
  >
20
  <channel>
21
- <title><?php bloginfo_rss( 'name' ) ?> - <?php esc_html_e( 'Stream Feed', 'stream' ) ?></title>
22
- <atom:link href="<?php self_link() ?>" rel="self" type="application/rss+xml" />
23
- <link><?php echo esc_url( $records_admin_url ) ?></link>
24
- <description><?php bloginfo_rss( 'description' ) ?></description>
25
- <lastBuildDate><?php echo esc_html( mysql2date( 'r', $latest_record, false ) ) ?></lastBuildDate>
26
- <language><?php bloginfo_rss( 'language' ) ?></language>
27
- <sy:updatePeriod><?php echo esc_html( 'hourly' ) ?></sy:updatePeriod>
28
- <sy:updateFrequency><?php echo absint( 1 ) ?></sy:updateFrequency>
29
  <?php
30
  /**
31
  * Action fires during RSS head
@@ -33,7 +33,7 @@ printf( '<?xml version="1.0" encoding="%s"?>', esc_attr( get_option( 'blog_chars
33
  do_action( 'rss2_head' );
34
 
35
  foreach ( $records as $record ) :
36
- $record_link = add_query_arg(
37
  array(
38
  'record__in' => $record->ID,
39
  ),
@@ -44,15 +44,15 @@ printf( '<?xml version="1.0" encoding="%s"?>', esc_attr( get_option( 'blog_chars
44
  $display_name = isset( $author->display_name ) ? $author->display_name : 'N/A';
45
  ?>
46
  <item>
47
- <title><![CDATA[ <?php echo esc_html( $record->summary ) // xss ok ?> ]]></title>
48
- <pubDate><?php echo esc_html( mysql2date( 'r', $record->created, false ) ) ?></pubDate>
49
- <dc:creator><?php echo esc_html( $display_name ) ?></dc:creator>
50
- <category domain="connector"><![CDATA[ <?php echo esc_html( $record->connector ) ?> ]]></category>
51
- <category domain="context"><![CDATA[ <?php echo esc_html( $record->context ) ?> ]]></category>
52
- <category domain="action"><![CDATA[ <?php echo esc_html( $record->action ) ?> ]]></category>
53
- <category domain="ip"><?php echo esc_html( $record->ip ) ?></category>
54
- <guid isPermaLink="false"><?php echo esc_url( $record_link ) ?></guid>
55
- <link><?php echo esc_url( $record_link ) ?></link>
56
  <?php
57
  /**
58
  * Action fires during RSS item
18
  ?>
19
  >
20
  <channel>
21
+ <title><?php bloginfo_rss( 'name' ); ?> - <?php esc_html_e( 'Stream Feed', 'stream' ); ?></title>
22
+ <atom:link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
23
+ <link><?php echo esc_url( $records_admin_url ); ?></link>
24
+ <description><?php bloginfo_rss( 'description' ); ?></description>
25
+ <lastBuildDate><?php echo esc_html( mysql2date( 'r', $latest_record, false ) ); ?></lastBuildDate>
26
+ <language><?php bloginfo_rss( 'language' ); ?></language>
27
+ <sy:updatePeriod><?php echo esc_html( 'hourly' ); ?></sy:updatePeriod>
28
+ <sy:updateFrequency><?php echo absint( 1 ); ?></sy:updateFrequency>
29
  <?php
30
  /**
31
  * Action fires during RSS head
33
  do_action( 'rss2_head' );
34
 
35
  foreach ( $records as $record ) :
36
+ $record_link = add_query_arg(
37
  array(
38
  'record__in' => $record->ID,
39
  ),
44
  $display_name = isset( $author->display_name ) ? $author->display_name : 'N/A';
45
  ?>
46
  <item>
47
+ <title><![CDATA[ <?php echo esc_html( $record->summary ); // xss ok ?> ]]></title>
48
+ <pubDate><?php echo esc_html( mysql2date( 'r', $record->created, false ) ); ?></pubDate>
49
+ <dc:creator><?php echo esc_html( $display_name ); ?></dc:creator>
50
+ <category domain="connector"><![CDATA[ <?php echo esc_html( $record->connector ); ?> ]]></category>
51
+ <category domain="context"><![CDATA[ <?php echo esc_html( $record->context ); ?> ]]></category>
52
+ <category domain="action"><![CDATA[ <?php echo esc_html( $record->action ); ?> ]]></category>
53
+ <category domain="ip"><?php echo esc_html( $record->ip ); ?></category>
54
+ <guid isPermaLink="false"><?php echo esc_url( $record_link ); ?></guid>
55
+ <link><?php echo esc_url( $record_link ); ?></link>
56
  <?php
57
  /**
58
  * Action fires during RSS item
includes/functions.php CHANGED
@@ -98,7 +98,7 @@ function wp_stream_get_sites( $args = array() ) {
98
  $sites = get_sites( $args );
99
  } else {
100
  $sites = array();
101
- foreach ( wp_get_sites( $args ) as $site ) {
102
  $sites[] = WP_Site::get_instance( $site['blog_id'] );
103
  }
104
  }
@@ -123,3 +123,17 @@ function wp_stream_is_vip() {
123
  function wp_stream_is_cron_enabled() {
124
  return ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) ? false : true;
125
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  $sites = get_sites( $args );
99
  } else {
100
  $sites = array();
101
+ foreach ( wp_get_sites( $args ) as $site ) { // @codingStandardsIgnoreLine Specifically for old version of WP first, in order to provide backward compatibility
102
  $sites[] = WP_Site::get_instance( $site['blog_id'] );
103
  }
104
  }
123
  function wp_stream_is_cron_enabled() {
124
  return ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) ? false : true;
125
  }
126
+
127
+ /**
128
+ * Get the asset min suffix if defined.
129
+ *
130
+ * @return string
131
+ */
132
+ function wp_stream_min_suffix() {
133
+ $min = '';
134
+ if ( ! defined( 'SCRIPT_DEBUG' ) || false === SCRIPT_DEBUG ) {
135
+ $min = 'min.';
136
+ }
137
+
138
+ return $min;
139
+ }
includes/lib/Carbon.php CHANGED
@@ -675,12 +675,13 @@ class Carbon extends DateTime
675
  * @param integer $hour
676
  * @param integer $minute
677
  * @param integer $second
 
678
  *
679
  * @return static
680
  */
681
- public function setTime($hour, $minute, $second = 0)
682
  {
683
- parent::setTime($hour, $minute, $second);
684
 
685
  return $this;
686
  }
675
  * @param integer $hour
676
  * @param integer $minute
677
  * @param integer $second
678
+ * @param integer $microseconds
679
  *
680
  * @return static
681
  */
682
+ public function setTime($hour, $minute, $second = 0, $microseconds = 0 )
683
  {
684
+ parent::setTime($hour, $minute, $second, $microseconds );
685
 
686
  return $this;
687
  }
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: lukecarbis, fjarrett, stream, xwp
3
  Tags: wp stream, stream, activity, logs, track
4
  Requires at least: 3.9
5
- Tested up to: 4.8.1
6
- Stable tag: 3.2.2
7
  License: GPLv2 or later
8
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -79,6 +79,21 @@ Thank you for wanting to make Stream better for everyone!
79
 
80
  == Changelog ==
81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  = 3.2.2 - September 13, 2017 =
83
 
84
  * Fix: Prevent fatal error when attempting to store an Object in the database.
@@ -92,6 +107,7 @@ Thank you for wanting to make Stream better for everyone!
92
  * Fix: Readme spelling fixes (localised [sic] for en_US). ([#928](https://github.com/xwp/stream/pull/928))
93
  * Fix: Undefined index ID issue when trashing post with customize-posts. ([#936](https://github.com/xwp/stream/pull/936))
94
  * Fix: Stream fails to install properly (sometimes) due to database error. ([#934](https://github.com/xwp/stream/pull/934))
 
95
 
96
  = 3.2.0 - March 15, 2017 =
97
 
@@ -520,13 +536,13 @@ Language pack for Polish. Bug fixes. Props [@powelski](https://github.com/powels
520
  Language pack for Spanish. Bug fixes. Props [omniwired](https://github.com/omniwired), [@shadyvb](https://github.com/shadyvb)
521
 
522
  = 1.0.2 - January 15, 2014 =
523
- Ensure the dashboard widget repects the Role Access setting. Props [@fjarrett](https://github.com/fjarrett)
524
 
525
  = 1.0.1 - January 15, 2014 =
526
  Require nonce for generating a new user feed key. Props [@johnregan3](https://github.com/johnregan3)
527
 
528
  = 1.0.0 - January 13, 2014 =
529
- Allow list table to be exensible. Hook added to prevent tables from being created, if desired. Props [@johnregan3](https://github.com/johnregan3), [@fjarrett](https://github.com/fjarrett), [@jonathanbardo](https://github.com/jonathanbardo)
530
 
531
  = 0.9.9 - January 8, 2014 =
532
  Updated screenshot assets and descriptions. Props [@fjarrett](https://github.com/fjarrett)
2
  Contributors: lukecarbis, fjarrett, stream, xwp
3
  Tags: wp stream, stream, activity, logs, track
4
  Requires at least: 3.9
5
+ Tested up to: 4.9.5
6
+ Stable tag: 3.2.3
7
  License: GPLv2 or later
8
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
9
 
79
 
80
  == Changelog ==
81
 
82
+ = 3.2.3 - April 23, 2018 =
83
+
84
+ * New: Use minimized assets ([#973](https://github.com/xwp/stream/pull/973))
85
+ * New: Alert type – Slack alerts! ([#970](https://github.com/xwp/stream/pull/970) [#962](https://github.com/xwp/stream/pull/962))
86
+ * Fix: PHP 7.1 compatibility fix ([#974](https://github.com/xwp/stream/pull/974))
87
+ * Fix: Make reset nonce unique ([#972](https://github.com/xwp/stream/pull/972))
88
+ * Fix: Stripped settings and alerts inputs ([#968](https://github.com/xwp/stream/pull/968))
89
+ * Fix: Update Datetime extension ([#966](https://github.com/xwp/stream/pull/966))
90
+ * Fix: WP CLI Namespace collision ([#944](https://github.com/xwp/stream/pull/944))
91
+ * Tweak: Coding standards updates ([#975](https://github.com/xwp/stream/pull/975))
92
+ * Tweak: Show real client IP (if available) when in reverse-proxy mode ([#969](https://github.com/xwp/stream/pull/969) [#963](https://github.com/xwp/stream/pull/963))
93
+ * Tweak: Performance improvement when listing roles ([#964](https://github.com/xwp/stream/pull/964))
94
+
95
+ Props [@DavidCramer](https://github.com/DavidCramer), [@lukecarbis](https://github.com/lukecarbis), [@frozzare](https://github.com/frozzare), [@fjarrett](https://github.com/fjarrett), [@shadyvb](https://github.com/shadyvb), [@valendesigns](https://github.com/valendesigns), [@robbiet480](https://github.com/robbiet480), [@cfoellmann](https://github.com/cfoellmann)
96
+
97
  = 3.2.2 - September 13, 2017 =
98
 
99
  * Fix: Prevent fatal error when attempting to store an Object in the database.
107
  * Fix: Readme spelling fixes (localised [sic] for en_US). ([#928](https://github.com/xwp/stream/pull/928))
108
  * Fix: Undefined index ID issue when trashing post with customize-posts. ([#936](https://github.com/xwp/stream/pull/936))
109
  * Fix: Stream fails to install properly (sometimes) due to database error. ([#934](https://github.com/xwp/stream/pull/934))
110
+ * Fix: Stream is network activated if it's a must-use plugin on a multisite ([#956](https://github.com/xwp/stream/pull/956))
111
 
112
  = 3.2.0 - March 15, 2017 =
113
 
536
  Language pack for Spanish. Bug fixes. Props [omniwired](https://github.com/omniwired), [@shadyvb](https://github.com/shadyvb)
537
 
538
  = 1.0.2 - January 15, 2014 =
539
+ Ensure the dashboard widget respects the Role Access setting. Props [@fjarrett](https://github.com/fjarrett)
540
 
541
  = 1.0.1 - January 15, 2014 =
542
  Require nonce for generating a new user feed key. Props [@johnregan3](https://github.com/johnregan3)
543
 
544
  = 1.0.0 - January 13, 2014 =
545
+ Allow list table to be extensible. Hook added to prevent tables from being created, if desired. Props [@johnregan3](https://github.com/johnregan3), [@fjarrett](https://github.com/fjarrett), [@jonathanbardo](https://github.com/jonathanbardo)
546
 
547
  = 0.9.9 - January 8, 2014 =
548
  Updated screenshot assets and descriptions. Props [@fjarrett](https://github.com/fjarrett)
stream.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Stream
4
  * Plugin URI: https://wp-stream.com/
5
  * Description: Stream tracks logged-in user activity so you can monitor every change made on your WordPress site in beautifully organized detail. All activity is organized by context, action and IP address for easy filtering. Developers can extend Stream with custom connectors to log any kind of action.
6
- * Version: 3.2.2
7
  * Author: XWP
8
  * Author URI: https://xwp.co/
9
  * License: GPLv2+
3
  * Plugin Name: Stream
4
  * Plugin URI: https://wp-stream.com/
5
  * Description: Stream tracks logged-in user activity so you can monitor every change made on your WordPress site in beautifully organized detail. All activity is organized by context, action and IP address for easy filtering. Developers can extend Stream with custom connectors to log any kind of action.
6
+ * Version: 3.2.3
7
  * Author: XWP
8
  * Author URI: https://xwp.co/
9
  * License: GPLv2+
ui/css/admin.css CHANGED
@@ -229,10 +229,10 @@
229
  background-color: #ffffe0 !important;
230
 
231
  -webkit-transition: background 0.5s linear;
232
- -moz-transition: background 0.5s linear;
233
- -ms-transition: background 0.5s linear;
234
- -o-transition: background 0.5s linear;
235
- transition: background 0.5s linear;
236
  }
237
 
238
  .toplevel_page_wp_stream .new-row.alternate,
@@ -397,9 +397,9 @@
397
 
398
  .post-type-wp_stream_alerts .select2-search-choice-close {
399
  -webkit-transition: none;
400
- -moz-transition: none;
401
- -o-transition: all 0 none;
402
- transition: none;
403
  }
404
 
405
  .wp-stream-select2-icon {
@@ -411,10 +411,10 @@
411
  }
412
 
413
  .select2-disabled .wp-stream-select2-icon {
414
- filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#grayscale"); /* Firefox 3.5+ */
415
- filter: gray; /* IE6-9 */
416
  -webkit-filter: grayscale(100%); /* Chrome 19+ & Safari 6+ */
417
- -moz-filter: grayscale(100%); /* Firefox < 3.5 */
418
  }
419
 
420
 
229
  background-color: #ffffe0 !important;
230
 
231
  -webkit-transition: background 0.5s linear;
232
+ -moz-transition: background 0.5s linear;
233
+ -ms-transition: background 0.5s linear;
234
+ -o-transition: background 0.5s linear;
235
+ transition: background 0.5s linear;
236
  }
237
 
238
  .toplevel_page_wp_stream .new-row.alternate,
397
 
398
  .post-type-wp_stream_alerts .select2-search-choice-close {
399
  -webkit-transition: none;
400
+ -moz-transition: none;
401
+ -o-transition: all 0 none;
402
+ transition: none;
403
  }
404
 
405
  .wp-stream-select2-icon {
411
  }
412
 
413
  .select2-disabled .wp-stream-select2-icon {
414
+ filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#grayscale"); /* Firefox 3.5+ */
415
+ filter: gray; /* IE6-9 */
416
  -webkit-filter: grayscale(100%); /* Chrome 19+ & Safari 6+ */
417
+ -moz-filter: grayscale(100%); /* Firefox < 3.5 */
418
  }
419
 
420
 
ui/css/admin.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .toplevel_page_wp_stream .tablenav{padding-top:6px}.toplevel_page_wp_stream #record-actions-form{margin-top:-32px}.toplevel_page_wp_stream #record-actions-form .button{margin-left:6px;float:left}.toplevel_page_wp_stream .tablenav .actions{padding:0;overflow:visible}.post-type-wp_stream_alerts .select2 .select2-selection,.stream-exclude-list .select2 .select2-selection{border-color:#ccc;background:#f7f7f7;-webkit-box-shadow:0 1px 0 #ccc;box-shadow:0 1px 0 #ccc}.post-type-wp_stream_alerts .select2 .select2-selection--multiple,.stream-exclude-list .select2 .select2-selection--multiple{font-size:0;min-height:28px}.post-type-wp_stream_alerts .select2-container.select2-container--focus .select2-selection--multiple,.stream-exclude-list .select2-container.select2-container--focus .select2-selection--multiple{border:solid #ccc 1px}.post-type-wp_stream_alerts .select2-container .select2-selection--multiple .select2-selection__choice,.stream-exclude-list .select2-container .select2-selection--multiple .select2-selection__choice{margin-top:4px;margin-bottom:3px}.post-type-wp_stream_alerts .select2 .select2-selection .select2-selection__rendered,.stream-exclude-list .select2 .select2-selection .select2-selection__rendered{color:#555}#record-query-reset{position:relative;margin-left:5px;line-height:28px;text-decoration:none;display:inline-block}#record-query-reset span.dashicons{position:absolute;top:.5em;left:0;font-size:13px}#record-query-reset .record-query-reset-text{margin-left:19px}.toplevel_page_wp_stream .chosen-container-single{margin-top:3px;margin-right:6px}.toplevel_page_wp_stream .view-switch{display:none}.toplevel_page_wp_stream #filter-date-range{float:left;margin-right:6px}.toplevel_page_wp_stream .manage-column{width:12%}.toplevel_page_wp_stream .column-date{min-width:10%;white-space:nowrap}.toplevel_page_wp_stream .column-date .timeago{padding-right:1em}.toplevel_page_wp_stream .column-user_id{width:18%}.toplevel_page_wp_stream .column-summary{width:auto}.toplevel_page_wp_stream .column-ip{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.toplevel_page_wp_stream .stream-filter-object-id{padding-left:5px;visibility:hidden}.toplevel_page_wp_stream td.summary:hover .stream-filter-object-id{visibility:visible}.toplevel_page_wp_stream .tablenav .stream-export-tablenav{margin-top:6px;height:28px;float:right}.toplevel_page_wp_stream .tablenav .stream-export-tablenav a{margin-top:0}@media only screen and (min-width:782px){.toplevel_page_wp_stream .tablenav .tablenav-pages{margin-bottom:4px}.toplevel_page_wp_stream .tablenav .stream-export-tablenav{margin-bottom:4px;float:left}.toplevel_page_wp_stream .tablenav .stream-export-tablenav a{margin-top:5px;display:inline-block}}@media only screen and (max-width:782px){.toplevel_page_wp_stream .tablenav.bottom .displaying-num{top:-8px}}@media only screen and (max-width:900px){.toplevel_page_wp_stream .fixed .manage-column,.toplevel_page_wp_stream .fixed tbody tr td{display:none!important}.toplevel_page_wp_stream .fixed .column-date,.toplevel_page_wp_stream .fixed .column-summary,.toplevel_page_wp_stream .fixed .column-user_id,.toplevel_page_wp_stream .fixed tbody tr.no-items td{display:table-cell!important}.toplevel_page_wp_stream .fixed .column-date{width:100px}.toplevel_page_wp_stream .fixed .column-user_id{width:50%}.toplevel_page_wp_stream .fixed .column-summary{width:100%}}@media only screen and (max-width:480px){.toplevel_page_wp_stream .fixed .column-user_id{display:none}}.toplevel_page_wp_stream .wp-list-table tr td::before{content:""!important}.toplevel_page_wp_stream .column-user_id a{vertical-align:top}.toplevel_page_wp_stream .column-user_id img{float:left;margin:1px 10px 8px 0;width:32px;height:32px}.toplevel_page_wp_stream .column-user_id .deleted{font-style:italic;color:#aaa}.toplevel_page_wp_stream .filter-date-range{margin-top:-1px}.toplevel_page_wp_stream .alignleft.actions input[type=text]{height:28px;line-height:19px}.toplevel_page_wp_stream .date-interval{display:inline}.toplevel_page_wp_stream .select2-container{margin-right:6px;margin-bottom:6px}.toplevel_page_wp_stream .select2-container.select2-allowclear .select2-choice abbr{margin-top:-2px}.toplevel_page_wp_stream .stream-live-update-checkbox .spinner{margin-top:5px}#dashboard_stream_activity .new-row,.toplevel_page_wp_stream .new-row{background-color:#ffffe0!important;-webkit-transition:background .5s linear;-moz-transition:background .5s linear;-ms-transition:background .5s linear;-o-transition:background .5s linear;transition:background .5s linear}#dashboard_stream_activity .new-row.alternate,.toplevel_page_wp_stream .new-row.alternate{background-color:#ffffcd!important}#dashboard_stream_activity .new-row.fadeout,.toplevel_page_wp_stream .new-row.fadeout{background-color:transparent!important}#dashboard_stream_activity .new-row.alternate.fadeout,.toplevel_page_wp_stream .new-row.alternate.fadeout{background-color:#f9f9f9!important}.toplevel_page_wp_stream #the-list .no-items .stream-list-table-no-items{text-align:center}.toplevel_page_wp_stream #the-list .no-items .stream-list-table-no-items p{margin:2px 0}.wp_stream_default_settings .select2.select2-container,.wp_stream_network_settings .select2.select2-container,.wp_stream_settings .select2.select2-container{min-width:160px;max-width:100%}.wp_stream_default_settings .tablenav,.wp_stream_network_settings .tablenav,.wp_stream_settings .tablenav{margin-top:16px}.wp_stream_default_settings .tablenav input,.wp_stream_network_settings .tablenav input,.wp_stream_settings .tablenav input{margin-right:1em}.post-type-wp_stream_alerts a.warning{color:#a00}.post-type-wp_stream_alerts a.warning:hover{color:red}.post-type-wp_stream_alerts .date-interval .field-predefined{width:165px;float:left;margin-right:6px;margin-bottom:6px}.post-type-wp_stream_alerts .date-interval .date-inputs{float:left;margin-right:6px;margin-bottom:6px}.post-type-wp_stream_alerts .date-interval .date-inputs .field-from,.post-type-wp_stream_alerts .date-interval .date-inputs .field-to{line-height:28px;height:28px;margin:0;width:125px}.post-type-wp_stream_alerts .date-interval .date-inputs .box{position:relative;display:block;float:left}.post-type-wp_stream_alerts .date-interval .date-inputs .box .date-remove{display:none;position:absolute;cursor:pointer;top:0;right:0;width:20px;height:28px;line-height:28px;font-size:14px;padding-top:1px;padding-right:2px}.post-type-wp_stream_alerts .date-interval .date-inputs .box .date-remove:before{content:'\f158'}.post-type-wp_stream_alerts .date-interval .date-inputs .connector{display:block;float:left;border:1px solid #ddd;border-left:0;border-right:0;height:26px;line-height:28px;padding-left:2px;width:20px;text-align:center;font-size:14px}.post-type-wp_stream_alerts .date-interval .date-inputs .connector:before{content:'\f345'}.post-type-wp_stream_alerts li.select2-no-results,.post-type-wp_stream_alerts li.select2-searching{background:0 0;padding:7px 7px 0;color:#999}.post-type-wp_stream_alerts .select2 .select2-selection .select2-selection__placeholder{color:#72777c}.post-type-wp_stream_alerts .select2-results .select2-disabled{background:0 0;color:#aaa}.post-type-wp_stream_alerts .select2 .select2-search--inline{float:none;margin-bottom:4px;margin-left:2px}.post-type-wp_stream_alerts .select2-selection .icon16{margin:-3px 1px 0 -3px;padding:0;width:16px;height:16px}.post-type-wp_stream_alerts .select2-selection .icon16{padding-right:8px}.post-type-wp_stream_alerts .select2-chosen .icon16:before,.post-type-wp_stream_alerts .select2-search-choice .icon16:before{font-size:15px!important;color:#656565}.post-type-wp_stream_alerts .select2-search-choice-close{-webkit-transition:none;-moz-transition:none;-o-transition:all 0 none;transition:none}.wp-stream-select2-icon{position:relative;top:3px;margin-right:4px;width:16px;height:16px}.select2-disabled .wp-stream-select2-icon{filter:url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#grayscale");filter:gray;-webkit-filter:grayscale(100%);-moz-filter:grayscale(100%)}.stream-exclude-list{margin-top:1em}.stream-exclude-list th{font-weight:400;padding:8px 10px;width:auto}.stream-exclude-list tbody tr.no-items{background-color:#fff}.stream-exclude-list tbody tr.no-items td{padding:8px 10px;font-size:13px}.stream-exclude-list tbody th.check-column{padding:16px 0 0 3px}.stream-exclude-list thead th.actions-column{width:3em}.stream-exclude-list tbody th.actions-column{padding:21px 10px 20px 0}.stream-exclude-list tbody th.actions-column a{display:none;color:#a00;font-size:13px}.stream-exclude-list tbody tr:hover th.actions-column a{display:block}.stream-exclude-list tbody th.actions-column a:hover{color:red}.stream-exclude-list tbody th.actions-column .dashicons{margin-top:4px}.stream-exclude-list tbody td .ip_address{width:100%}.stream-exclude-list tbody td .ip_address.invalid{border:1px solid rgba(160,0,0,.75)}.stream-exclude-list .icon-users{top:-3px!important;position:relative!important}.wp_stream_screen .select2-results__option .parent{font-weight:700}.wp_stream_screen .select2-results__option .child{padding-left:8px}@media screen and (max-width:900px){.wp_stream_network_settings .stream-exclude-list .actions-column,.wp_stream_settings .stream-exclude-list .actions-column{display:none}}@media screen and (max-width:782px){.toplevel_page_wp_stream #record-actions-form{margin-top:0;margin-bottom:35px}.wp_stream_default_settings .stream-exclude-list td,.wp_stream_network_settings .stream-exclude-list td,.wp_stream_settings .stream-exclude-list td{padding:10px 10px 0 0}.wp_stream_default_settings .stream-exclude-list th,.wp_stream_network_settings .stream-exclude-list th,.wp_stream_settings .stream-exclude-list th{display:none}.wp_stream_default_settings .stream-exclude-list .check-column,.wp_stream_network_settings .stream-exclude-list .check-column,.wp_stream_settings .stream-exclude-list .check-column{display:table-cell;padding:13px 10px 0 3px}.wp_stream_default_settings .stream-exclude-list thead .actions-column,.wp_stream_network_settings .stream-exclude-list thead .actions-column,.wp_stream_settings .stream-exclude-list thead .actions-column{display:table-cell;width:auto}.wp_stream_default_settings .stream-exclude-list thead .actions-column .hidden,.wp_stream_network_settings .stream-exclude-list thead .actions-column .hidden,.wp_stream_settings .stream-exclude-list thead .actions-column .hidden{display:block}.wp_stream_default_settings .stream-exclude-list tfoot,.wp_stream_network_settings .stream-exclude-list tfoot,.wp_stream_settings .stream-exclude-list tfoot{display:none}}.toplevel_page_wp_stream .alert-highlight.highlight-yellow{background-color:#ffffe0}.toplevel_page_wp_stream .alert-highlight.highlight-red{background-color:#fbeaea}.toplevel_page_wp_stream .alert-highlight.highlight-green{background-color:#ecf7ed}.toplevel_page_wp_stream .alert-highlight.highlight-blue{background-color:#e5f5fa}.post-type-stream_notification .view-switch{display:none}
ui/css/alerts-list.css CHANGED
@@ -76,11 +76,11 @@
76
  }
77
 
78
  .edit-php.post-type-wp_stream_alerts span.select2-selection__rendered {
79
- font-style: normal;
80
  }
81
 
82
  #add-new-alert.inline-edit-row.inline-edit-row-page .inline-edit-col-right {
83
- margin-top: 32px;
84
  }
85
  .edit-php.post-type-wp_stream_alerts #add-new-alert .select2.select2-container {
86
  display: block;
@@ -139,4 +139,4 @@
139
  .post-type-wp_stream_alerts .wp-list-table .alert_trigger_value:last-child:after {
140
  border-top: none;
141
  border-right: none;
142
- }
76
  }
77
 
78
  .edit-php.post-type-wp_stream_alerts span.select2-selection__rendered {
79
+ font-style: normal;
80
  }
81
 
82
  #add-new-alert.inline-edit-row.inline-edit-row-page .inline-edit-col-right {
83
+ margin-top: 32px;
84
  }
85
  .edit-php.post-type-wp_stream_alerts #add-new-alert .select2.select2-container {
86
  display: block;
139
  .post-type-wp_stream_alerts .wp-list-table .alert_trigger_value:last-child:after {
140
  border-top: none;
141
  border-right: none;
142
+ }
ui/css/alerts-list.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .edit-php.post-type-wp_stream_alerts .inline-edit-col,.edit-php.post-type-wp_stream_alerts .widefat .inline-edit-col,.edit-php.post-type-wp_stream_alerts .widefat p{color:#444;font-size:12px;display:block;margin:.2em 0;line-height:2.5}.edit-php.post-type-wp_stream_alerts .inline-edit-col .wp_stream_alert_type_description{line-height:1.5em;display:inline-block}.edit-php.post-type-wp_stream_alerts .inline-edit-col{padding:0 .5em!important}.edit-php.post-type-wp_stream_alerts .inline-edit-col legend{padding:.2em 0!important;color:#555}.edit-php.post-type-wp_stream_alerts .inline-edit-col .select2-container{display:block}#wp_stream_alert_type_form{max-height:14em;border:1px solid #ddd;overflow-y:scroll;padding:.2em .5em;margin:6px 0;background-color:#fff;display:none}#wp_stream_alert_type_form select{width:100%}#wp_stream_alert_type_form .dashicons{position:relative;top:7px;left:-2px;font-size:1em}.edit-php.post-type-wp_stream_alerts #misc-publishing-actions,.edit-php.post-type-wp_stream_alerts .misc-pub-section.misc-pub-post-status{padding:0}@media (min-width:781px){.wp-stream-show-mobile{display:none}.edit-php.post-type-wp_stream_alerts .inline-edit-col.inline-edit-wp_stream_alerts{float:left;width:100%;max-width:20%}}.edit-php.post-type-wp_stream_alerts .select2 .select2-selection{border-color:#ccc;background:#f7f7f7;-webkit-box-shadow:0 1px 0 #ccc;box-shadow:0 1px 0 #ccc}.edit-php.post-type-wp_stream_alerts label{font-style:italic}.edit-php.post-type-wp_stream_alerts select{width:auto}.edit-php.post-type-wp_stream_alerts span.select2-selection__rendered{font-style:normal}#add-new-alert.inline-edit-row.inline-edit-row-page .inline-edit-col-right{margin-top:32px}.edit-php.post-type-wp_stream_alerts #add-new-alert .select2.select2-container{display:block}.edit-php.post-type-wp_stream_alerts #add-new-alert .inline-edit-add-new-notifications,.edit-php.post-type-wp_stream_alerts #add-new-alert .inline-edit-add-new-status{margin-top:33px}#add-new-alert .inline-edit-add-new-triggers label{margin:.2em 0;line-height:2.5}.post-type-wp_stream_alerts .select2-container{margin-bottom:6px;min-width:165px}.post-type-wp_stream_alerts #posts-filter #post-query-submit{display:none}.post-type-wp_stream_alerts .wp-list-table .alert_trigger_value{padding:3px 20px 3px 10px;border-top:1px solid #e5e5e5;border-bottom:1px solid #e5e5e5;background:#fff;display:inline-block;height:22px;line-height:22px;box-shadow:0 1px 1px rgba(0,0,0,.04)}.post-type-wp_stream_alerts .wp-list-table .alert_trigger_value:first-child{border-left:1px solid #e5e5e5}.post-type-wp_stream_alerts .wp-list-table .alert_trigger_value:last-child{padding-right:10px;border-right:1px solid #e5e5e5}.post-type-wp_stream_alerts .wp-list-table .alert_trigger_value:after{content:"";position:absolute;margin-left:7px;margin-top:6px;display:inline-block;height:8px;width:8px;transform:rotate(45deg);border-top:2px solid #e5e5e5;border-right:2px solid #e5e5e5}.post-type-wp_stream_alerts .wp-list-table .alert_trigger_value:last-child:after{border-top:none;border-right:none}
ui/css/datepicker.css CHANGED
@@ -156,8 +156,8 @@
156
  border: 1px solid #ddd;
157
 
158
  -webkit-border-radius: 0;
159
- -moz-border-radius: 0;
160
- border-radius: 0;
161
  }
162
 
163
  #ui-datepicker-div.stream-datepicker.ui-datepicker * {
@@ -186,8 +186,8 @@
186
  cursor: pointer;
187
 
188
  -webkit-border-radius: 0;
189
- -moz-border-radius: 0;
190
- border-radius: 0;
191
  }
192
 
193
  #ui-datepicker-div.stream-datepicker.ui-datepicker thead {
@@ -197,8 +197,8 @@
197
 
198
  #ui-datepicker-div.stream-datepicker .ui-corner-all {
199
  -webkit-border-radius: 0;
200
- -moz-border-radius: 0;
201
- border-radius: 0;
202
  }
203
 
204
  #ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-title {
156
  border: 1px solid #ddd;
157
 
158
  -webkit-border-radius: 0;
159
+ -moz-border-radius: 0;
160
+ border-radius: 0;
161
  }
162
 
163
  #ui-datepicker-div.stream-datepicker.ui-datepicker * {
186
  cursor: pointer;
187
 
188
  -webkit-border-radius: 0;
189
+ -moz-border-radius: 0;
190
+ border-radius: 0;
191
  }
192
 
193
  #ui-datepicker-div.stream-datepicker.ui-datepicker thead {
197
 
198
  #ui-datepicker-div.stream-datepicker .ui-corner-all {
199
  -webkit-border-radius: 0;
200
+ -moz-border-radius: 0;
201
+ border-radius: 0;
202
  }
203
 
204
  #ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-title {
ui/css/datepicker.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-next,.ui-datepicker .ui-datepicker-prev{position:absolute;top:2px}.ui-datepicker .ui-datepicker-prev{left:12px}.ui-datepicker .ui-datepicker-next{right:12px}.ui-datepicker .ui-datepicker-next span,.ui-datepicker .ui-datepicker-prev span{font-size:0;width:12px;height:12px;display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month-year{width:100%}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:49%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:700;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td a,.ui-datepicker td span{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}#ui-datepicker-div.stream-datepicker.ui-widget{margin-top:5px;border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}#ui-datepicker-div.stream-datepicker.ui-datepicker *{padding:0;font-family:'Open Sans',sans-serif}#ui-datepicker-div.stream-datepicker.ui-datepicker{padding:0}#ui-datepicker-div.stream-datepicker.ui-datepicker table{font-size:13px}#ui-datepicker-div.stream-datepicker .ui-datepicker-header{border:none;background:#23282d;color:#fff;font-weight:400}#ui-datepicker-div.stream-datepicker .ui-datepicker-header .ui-state-hover{background:#23282d;border-color:transparent;cursor:pointer;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}#ui-datepicker-div.stream-datepicker.ui-datepicker thead{background:#23282d;color:#fff}#ui-datepicker-div.stream-datepicker .ui-corner-all{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}#ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-title{color:#fff;font-size:14px}#ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-next,#ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-next-hover,#ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-prev,#ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-prev-hover{top:.33em;border:none}#ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-next span,#ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-prev span{background-image:url(../stream-icons/datepicker-icons.png);background-position:-32px 0;margin-top:0;top:0;font-weight:400}#ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-prev span{background-position:-96px 0}#ui-datepicker-div.stream-datepicker.ui-datepicker table{margin:0}#ui-datepicker-div.stream-datepicker.ui-datepicker th{padding:.75em 0;color:#fff;font-weight:400;border:none;border-top:1px solid #32373c}#ui-datepicker-div.stream-datepicker.ui-datepicker td{background:#f1f1f1;border:none;padding:0}#ui-datepicker-div.stream-datepicker td .ui-state-default{background:0 0;border:none;text-align:center;padding:.5em;margin:0;font-weight:400;color:#32373c}#ui-datepicker-div.stream-datepicker td.ui-state-disabled .ui-state-default,#ui-datepicker-div.stream-datepicker.ui-datepicker .ui-widget-content td.ui-state-disabled,#ui-datepicker-div.stream-datepicker.ui-datepicker td.ui-state-disabled{opacity:1;color:#999}#ui-datepicker-div.stream-datepicker td .ui-state-active,#ui-datepicker-div.stream-datepicker td .ui-state-hover{background:#0074a2;color:#fff}.admin-color-light #ui-datepicker-div.stream-datepicker .ui-datepicker-header,.admin-color-light #ui-datepicker-div.stream-datepicker .ui-datepicker-header .ui-state-hover,.admin-color-light #ui-datepicker-div.stream-datepicker td .ui-state-active,.admin-color-light #ui-datepicker-div.stream-datepicker td .ui-state-hover,.admin-color-light #ui-datepicker-div.stream-datepicker.ui-datepicker thead{background:#e5e5e5}.admin-color-light #ui-datepicker-div.stream-datepicker.ui-datepicker td{background:#fff}.admin-color-light #ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-next span,.admin-color-light #ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-prev span{background-image:url(../stream-icons/datepicker-icons-gray.png)}.admin-color-light #ui-datepicker-div.stream-datepicker.ui-datepicker th{border-color:#fff}.admin-color-light #ui-datepicker-div.stream-datepicker td .ui-state-default,.admin-color-light #ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-title,.admin-color-light #ui-datepicker-div.stream-datepicker.ui-datepicker th{color:#555}.admin-color-light #ui-datepicker-div.stream-datepicker td .ui-state-active,.admin-color-light #ui-datepicker-div.stream-datepicker td .ui-state-hover{color:#fff;background:#888}.admin-color-light #ui-datepicker-div.stream-datepicker td.ui-state-disabled .ui-state-default,.admin-color-light #ui-datepicker-div.stream-datepicker.ui-datepicker .ui-widget-content td.ui-state-disabled,.admin-color-light #ui-datepicker-div.stream-datepicker.ui-datepicker td.ui-state-disabled{color:#ccc}.admin-color-blue #ui-datepicker-div.stream-datepicker .ui-datepicker-header,.admin-color-blue #ui-datepicker-div.stream-datepicker .ui-datepicker-header .ui-state-hover,.admin-color-blue #ui-datepicker-div.stream-datepicker td .ui-state-active,.admin-color-blue #ui-datepicker-div.stream-datepicker td .ui-state-hover,.admin-color-blue #ui-datepicker-div.stream-datepicker.ui-datepicker thead{background:#4796b3}.admin-color-blue #ui-datepicker-div.stream-datepicker.ui-datepicker th{border-color:#52accc}.admin-color-blue #ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-title,.admin-color-blue #ui-datepicker-div.stream-datepicker.ui-datepicker th{color:#fff}.admin-color-blue #ui-datepicker-div.stream-datepicker td .ui-state-active,.admin-color-blue #ui-datepicker-div.stream-datepicker td .ui-state-hover{background:#096484}.admin-color-coffee #ui-datepicker-div.stream-datepicker .ui-datepicker-header,.admin-color-coffee #ui-datepicker-div.stream-datepicker .ui-datepicker-header .ui-state-hover,.admin-color-coffee #ui-datepicker-div.stream-datepicker td .ui-state-active,.admin-color-coffee #ui-datepicker-div.stream-datepicker td .ui-state-hover,.admin-color-coffee #ui-datepicker-div.stream-datepicker.ui-datepicker thead{background:#46403c}.admin-color-coffee #ui-datepicker-div.stream-datepicker.ui-datepicker th{border-color:#59524c}.admin-color-coffee #ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-title,.admin-color-coffee #ui-datepicker-div.stream-datepicker.ui-datepicker th{color:#fff}.admin-color-coffee #ui-datepicker-div.stream-datepicker td .ui-state-active,.admin-color-coffee #ui-datepicker-div.stream-datepicker td .ui-state-hover{background:#c7a589}.admin-color-ectoplasm #ui-datepicker-div.stream-datepicker .ui-datepicker-header,.admin-color-ectoplasm #ui-datepicker-div.stream-datepicker .ui-datepicker-header .ui-state-hover,.admin-color-ectoplasm #ui-datepicker-div.stream-datepicker td .ui-state-active,.admin-color-ectoplasm #ui-datepicker-div.stream-datepicker td .ui-state-hover,.admin-color-ectoplasm #ui-datepicker-div.stream-datepicker.ui-datepicker thead{background:#413256}.admin-color-ectoplasm #ui-datepicker-div.stream-datepicker.ui-datepicker th{border-color:#523f6d}.admin-color-ectoplasm #ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-title,.admin-color-ectoplasm #ui-datepicker-div.stream-datepicker.ui-datepicker th{color:#fff}.admin-color-ectoplasm #ui-datepicker-div.stream-datepicker td .ui-state-active,.admin-color-ectoplasm #ui-datepicker-div.stream-datepicker td .ui-state-hover{background:#a3b745}.admin-color-midnight #ui-datepicker-div.stream-datepicker .ui-datepicker-header,.admin-color-midnight #ui-datepicker-div.stream-datepicker .ui-datepicker-header .ui-state-hover,.admin-color-midnight #ui-datepicker-div.stream-datepicker td .ui-state-active,.admin-color-midnight #ui-datepicker-div.stream-datepicker td .ui-state-hover,.admin-color-midnight #ui-datepicker-div.stream-datepicker.ui-datepicker thead{background:#26292c}.admin-color-midnight #ui-datepicker-div.stream-datepicker.ui-datepicker th{border-color:#363b3f}.admin-color-midnight #ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-title,.admin-color-midnight #ui-datepicker-div.stream-datepicker.ui-datepicker th{color:#fff}.admin-color-midnight #ui-datepicker-div.stream-datepicker td .ui-state-active,.admin-color-midnight #ui-datepicker-div.stream-datepicker td .ui-state-hover{background:#e14d43}.admin-color-ocean #ui-datepicker-div.stream-datepicker .ui-datepicker-header,.admin-color-ocean #ui-datepicker-div.stream-datepicker .ui-datepicker-header .ui-state-hover,.admin-color-ocean #ui-datepicker-div.stream-datepicker td .ui-state-active,.admin-color-ocean #ui-datepicker-div.stream-datepicker td .ui-state-hover,.admin-color-ocean #ui-datepicker-div.stream-datepicker.ui-datepicker thead{background:#627c83}.admin-color-ocean #ui-datepicker-div.stream-datepicker.ui-datepicker th{border-color:#738e96}.admin-color-ocean #ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-title,.admin-color-ocean #ui-datepicker-div.stream-datepicker.ui-datepicker th{color:#fff}.admin-color-ocean #ui-datepicker-div.stream-datepicker td .ui-state-active,.admin-color-ocean #ui-datepicker-div.stream-datepicker td .ui-state-hover{background:#9ebaa0}.admin-color-sunrise #ui-datepicker-div.stream-datepicker .ui-datepicker-header,.admin-color-sunrise #ui-datepicker-div.stream-datepicker .ui-datepicker-header .ui-state-hover,.admin-color-sunrise #ui-datepicker-div.stream-datepicker td .ui-state-active,.admin-color-sunrise #ui-datepicker-div.stream-datepicker td .ui-state-hover,.admin-color-sunrise #ui-datepicker-div.stream-datepicker.ui-datepicker thead{background:#be3631}.admin-color-sunrise #ui-datepicker-div.stream-datepicker.ui-datepicker th{border-color:#cf4944}.admin-color-sunrise #ui-datepicker-div.stream-datepicker.ui-datepicker .ui-datepicker-title,.admin-color-sunrise #ui-datepicker-div.stream-datepicker.ui-datepicker th{color:#fff}.admin-color-sunrise #ui-datepicker-div.stream-datepicker td .ui-state-active,.admin-color-sunrise #ui-datepicker-div.stream-datepicker td .ui-state-hover{background:#dd823b}
ui/js/admin.js CHANGED
@@ -1,508 +1,570 @@
1
  /* globals wp_stream, ajaxurl */
2
- jQuery( function( $ ) {
3
-
4
- // Shorter timeago strings for English locale
5
- if ( 'en' === wp_stream.locale && 'undefined' !== typeof $.timeago ) {
6
- $.timeago.settings.strings.seconds = 'seconds';
7
- $.timeago.settings.strings.minute = 'a minute';
8
- $.timeago.settings.strings.hour = 'an hour';
9
- $.timeago.settings.strings.hours = '%d hours';
10
- $.timeago.settings.strings.month = 'a month';
11
- $.timeago.settings.strings.year = 'a year';
12
- }
 
13
 
14
- $( 'li.toplevel_page_wp_stream ul li.wp-first-item.current' ).parent().parent().find( '.update-plugins' ).remove();
15
 
16
- $( '.toplevel_page_wp_stream :input.chosen-select' ).each( function( i, el ) {
17
- var args = {},
18
- templateResult = function( record ) {
19
- var $result = $( '<span>' ),
20
- $elem = $( record.element ),
21
- icon = '';
 
22
 
23
- if ( '- ' === record.text.substring( 0, 2 ) ) {
24
- record.text = record.text.substring( 2 );
25
- }
26
 
27
- if ( 'undefined' !== typeof record.id ) {
28
- if ( record.id.indexOf( 'group-' ) === 0 ) {
29
- $result.addClass( 'parent' );
30
- } else if ( $elem.hasClass( 'level-2' ) ) {
31
- $result.addClass( 'child' );
32
- }
33
- }
34
 
35
- if ( undefined !== record.icon ) {
36
- icon = record.icon;
37
- } else if ( undefined !== $elem && '' !== $elem.data( 'icon' ) ) {
38
- icon = $elem.data( 'icon' );
39
- }
40
 
41
- if ( icon ) {
42
- $result.html( '<img src="' + icon + '" class="wp-stream-select2-icon">' );
43
- }
44
- $result.append( record.text );
45
-
46
- return $result;
47
- },
48
- templateSelection = function( record ) {
49
- if ( '- ' === record.text.substring( 0, 2 ) ) {
50
- record.text = record.text.substring( 2 );
51
- }
52
- return record.text;
53
- };
54
-
55
- if ( $( el ).find( 'option' ).not( ':selected' ).not( ':empty' ).length > 0 ) {
56
- args = {
57
- minimumResultsForSearch: 10,
58
- templateResult: templateResult,
59
- templateSelection: templateSelection,
60
- allowClear: true,
61
- width: '165px'
62
- };
63
- } else {
64
- args = {
65
- minimumInputLength: 3,
66
- allowClear: true,
67
- width: '165px',
68
- ajax: {
69
- url: ajaxurl,
70
- dataType: 'json',
71
- quietMillis: 100,
72
- data: function( term ) {
73
- return {
74
- action: 'wp_stream_filters',
75
- nonce: $( '#stream_filters_user_search_nonce' ).val(),
76
- filter: $( el ).attr( 'name' ),
77
- q: term.term
78
- };
79
  },
80
- processResults: function( data ) {
81
- var results = [];
82
- $.each( data, function( index, item ){
83
- results.push({
84
- id: item.id,
85
- text: item.label
86
- });
87
- });
88
- return {
89
- results: results
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  };
91
  }
92
- },
93
- templateResult: templateResult,
94
- templateSelection: templateSelection
95
- };
96
- }
97
-
98
- $( el ).select2( args );
99
- });
100
-
101
- var $queryVars = $.streamGetQueryVars();
102
- var $contextInput = $( '.toplevel_page_wp_stream select.chosen-select[name="context"]' );
103
-
104
- if ( ( 'undefined' === typeof $queryVars.context || '' === $queryVars.context ) && 'undefined' !== typeof $queryVars.connector ) {
105
- $contextInput.val( 'group-' + $queryVars.connector );
106
- $contextInput.trigger( 'change' );
107
- }
108
-
109
- $( 'input[type=submit]', '#record-filter-form' ).click( function() {
110
- $( 'input[type=submit]', $( this ).parents( 'form' ) ).removeAttr( 'clicked' );
111
- $( this ).attr( 'clicked', 'true' );
112
- });
113
-
114
- $( '#record-filter-form' ).submit( function() {
115
- var $context = $( '.toplevel_page_wp_stream :input.chosen-select[name="context"]' ),
116
- $option = $context.find( 'option:selected' ),
117
- $connector = $context.parent().find( '.record-filter-connector' ),
118
- optionConnector = $option.data( 'group' ),
119
- optionClass = $option.prop( 'class' ),
120
- $recordAction = $( '.recordactions select' );
121
-
122
- if ( $( '#record-actions-submit' ).attr( 'clicked' ) !== 'true' ) {
123
- $recordAction.val( '' );
124
- }
125
-
126
- $connector.val( optionConnector );
127
-
128
- if ( 'level-1' === optionClass ) {
129
- $option.val( '' );
130
- }
131
- });
132
 
133
- $( window ).load( function() {
134
- $( '.toplevel_page_wp_stream input[type="search"]' ).off( 'mousedown' );
135
- });
136
-
137
- // Confirmation on some important actions
138
- $( 'body' ).on( 'click', '#wp_stream_advanced_delete_all_records, #wp_stream_network_advanced_delete_all_records', function( e ) {
139
- if ( ! window.confirm( wp_stream.i18n.confirm_purge ) ) {
140
- e.preventDefault();
141
- }
142
- });
143
 
144
- $( 'body' ).on( 'click', '#wp_stream_advanced_reset_site_settings, #wp_stream_network_advanced_reset_site_settings', function( e ) {
145
- if ( ! window.confirm( wp_stream.i18n.confirm_defaults ) ) {
146
- e.preventDefault();
147
- }
148
- });
149
 
150
- $( 'body' ).on( 'click', '#wp_stream_uninstall', function( e ) {
151
- if ( ! window.confirm( wp_stream.i18n.confirm_uninstall ) ) {
152
- e.preventDefault();
153
  }
154
- });
155
-
156
- // Admin page tabs
157
- var $tabs = $( '.wp_stream_screen .nav-tab-wrapper' ),
158
- $panels = $( '.wp_stream_screen .nav-tab-content table.form-table' ),
159
- $activeTab = $tabs.find( '.nav-tab-active' ),
160
- defaultIndex = $activeTab.length > 0 ? $tabs.find( 'a' ).index( $activeTab ) : 0,
161
- hashIndex = window.location.hash.match( /^#(\d+)$/ ),
162
- currentHash = ( null !== hashIndex ? hashIndex[ 1 ] : defaultIndex ),
163
- syncFormAction = function( index ) {
164
- var $optionsForm = $( 'input[name="option_page"][value^="wp_stream"]' ).closest( 'form' );
165
- if ( $optionsForm.length === 0 ) {
166
- return;
167
- }
168
- var currentAction = $optionsForm.attr( 'action' );
169
-
170
- $optionsForm.prop( 'action', currentAction.replace( /(^[^#]*).*$/, '$1#' + index ) );
171
- };
172
-
173
- $tabs.on( 'click', 'a', function() {
174
- var index = $tabs.find( 'a' ).index( $( this ) ),
175
- hashIndex = window.location.hash.match( /^#(\d+)$/ );
176
-
177
- $panels.hide().eq( index ).show();
178
- $tabs
179
- .find( 'a' )
180
- .removeClass( 'nav-tab-active' )
181
- .filter( $( this ) )
182
- .addClass( 'nav-tab-active' );
183
 
184
- if ( '' === window.location.hash || null !== hashIndex ) {
185
- window.location.hash = index;
186
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
 
188
- syncFormAction( index );
189
 
190
- return false;
191
- });
 
 
 
192
 
193
- $tabs.children().eq( currentHash ).trigger( 'click' );
 
 
 
 
194
 
195
- // Live Updates screen option
196
- $( document ).ready( function() {
 
 
 
 
 
 
197
 
198
- // Enable Live Updates checkbox ajax
199
- $( '#enable_live_update' ).click( function() {
200
- var nonce = $( '#stream_live_update_nonce' ).val(),
201
- user = $( '#enable_live_update_user' ).val(),
202
- checked = 'unchecked',
203
- heartbeat = 'true';
 
204
 
205
- if ( $( '#enable_live_update' ).is( ':checked' ) ) {
206
- checked = 'checked';
207
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
 
209
- heartbeat = $( '#enable_live_update' ).data( 'heartbeat' );
 
210
 
211
- $.ajax({
212
- type: 'POST',
213
- url: ajaxurl,
214
- data: {
215
- action: 'stream_enable_live_update',
216
- nonce: nonce,
217
- user: user,
218
- checked: checked,
219
- heartbeat: heartbeat
220
- },
221
- dataType: 'json',
222
- beforeSend: function() {
223
- $( '.stream-live-update-checkbox .spinner' ).show().css( { 'display': 'inline-block' } );
224
- },
225
- success: function( response ) {
226
- $( '.stream-live-update-checkbox .spinner' ).hide();
227
 
228
- if ( false === response.success ) {
229
- $( '#enable_live_update' ).prop( 'checked', false );
 
 
 
 
230
 
231
- if ( response.data ) {
232
- window.alert( response.data );
233
- }
234
  }
235
- }
236
- });
237
- });
238
-
239
- function toggle_filter_submit() {
240
- var all_hidden = true;
241
-
242
- // If all filters are hidden, hide the button
243
- if ( $( 'div.metabox-prefs [name="date-hide"]' ).is( ':checked' ) ) {
244
- all_hidden = false;
245
- }
246
 
247
- var divs = $( 'div.alignleft.actions div.select2-container' );
248
 
249
- divs.each( function() {
250
- if ( ! $( this ).is( ':hidden' ) ) {
251
- all_hidden = false;
252
  return false;
253
  }
254
- });
255
-
256
- if ( all_hidden ) {
257
- $( 'input#record-query-submit' ).hide();
258
- $( 'span.filter_info' ).show();
259
- } else {
260
- $( 'input#record-query-submit' ).show();
261
- $( 'span.filter_info' ).hide();
262
- }
263
- }
264
-
265
- if ( $( 'div.metabox-prefs [name="date-hide"]' ).is( ':checked' ) ) {
266
- $( 'div.date-interval' ).show();
267
- } else {
268
- $( 'div.date-interval' ).hide();
269
- }
270
-
271
- $( 'div.actions select.chosen-select' ).each( function() {
272
- var name = $( this ).prop( 'name' );
273
 
274
- if ( $( 'div.metabox-prefs [name="' + name + '-hide"]' ).is( ':checked' ) ) {
275
- $( this ).prev( '.select2-container' ).show();
276
- } else {
277
- $( this ).prev( '.select2-container' ).hide();
278
- }
279
- });
280
 
281
- toggle_filter_submit();
 
 
282
 
283
- $( 'div.metabox-prefs [type="checkbox"]' ).click( function() {
284
- var id = $( this ).prop( 'id' );
 
 
 
 
 
285
 
286
- if ( 'date-hide' === id ) {
287
- if ( $( this ).is( ':checked' ) ) {
288
- $( 'div.date-interval' ).show();
289
- } else {
290
- $( 'div.date-interval' ).hide();
291
- }
292
- } else {
293
- id = id.replace( '-hide', '' );
294
-
295
- if ( $( this ).is( ':checked' ) ) {
296
- $( '[name="' + id + '"]' ).prev( '.select2-container' ).show();
297
- } else {
298
- $( '[name="' + id + '"]' ).prev( '.select2-container' ).hide();
299
- }
300
- }
301
-
302
- toggle_filter_submit();
303
- });
304
-
305
- $( '#ui-datepicker-div' ).addClass( 'stream-datepicker' );
306
- });
307
-
308
- // Relative time
309
- $( 'table.wp-list-table' ).on( 'updated', function() {
310
- var timeObjects = $( this ).find( 'time.relative-time' );
311
- timeObjects.each( function( i, el ) {
312
- var timeEl = $( el );
313
- timeEl.removeClass( 'relative-time' );
314
- $( '<strong><time datetime="' + timeEl.attr( 'datetime' ) + '" class="timeago"/></time></strong><br/>' )
315
- .prependTo( timeEl.parent().parent() )
316
- .find( 'time.timeago' )
317
- .timeago();
318
- });
319
- }).trigger( 'updated' );
320
-
321
- var intervals = {
322
- init: function( $wrapper ) {
323
- this.wrapper = $wrapper;
324
- this.save_interval( this.wrapper.find( '.button-primary' ), this.wrapper );
325
-
326
- this.$ = this.wrapper.each( function( i, val ) {
327
- var container = $( val ),
328
- dateinputs = container.find( '.date-inputs' ),
329
- from = container.find( '.field-from' ),
330
- to = container.find( '.field-to' ),
331
- to_remove = to.prev( '.date-remove' ),
332
- from_remove = from.prev( '.date-remove' ),
333
- predefined = container.children( '.field-predefined' ),
334
- datepickers = $( '' ).add( to ).add( from );
335
-
336
- if ( jQuery.datepicker ) {
337
-
338
- // Apply a GMT offset due to Date() using the visitor's local time
339
- var siteGMTOffsetHours = parseFloat( wp_stream.gmt_offset ),
340
- localGMTOffsetHours = new Date().getTimezoneOffset() / 60 * -1,
341
- totalGMTOffsetHours = siteGMTOffsetHours - localGMTOffsetHours,
342
- localTime = new Date(),
343
- siteTime = new Date( localTime.getTime() + ( totalGMTOffsetHours * 60 * 60 * 1000 ) ),
344
- maxOffset = 0,
345
- minOffset = null;
346
-
347
- // Check if the site date is different from the local date, and set a day offset
348
- if ( localTime.getDate() !== siteTime.getDate() || localTime.getMonth() !== siteTime.getMonth() ) {
349
- if ( localTime.getTime() < siteTime.getTime() ) {
350
- maxOffset = '+1d';
351
- } else {
352
- maxOffset = '-1d';
353
- }
354
- }
355
 
356
- datepickers.datepicker({
357
- dateFormat: 'yy/mm/dd',
358
- minDate: minOffset,
359
- maxDate: maxOffset,
360
- defaultDate: siteTime,
361
- beforeShow: function() {
362
- $( this ).prop( 'disabled', true );
363
- },
364
- onClose: function() {
365
- $( this ).prop( 'disabled', false );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
  }
367
- });
368
 
369
- datepickers.datepicker( 'widget' ).addClass( 'stream-datepicker' );
370
- }
371
-
372
- predefined.select2({
373
- 'allowClear': true
374
- });
375
 
376
- if ( '' !== from.val() ) {
377
- from_remove.show();
378
- }
 
379
 
380
- if ( '' !== to.val() ) {
381
- to_remove.show();
382
- }
383
 
384
- predefined.on({
385
- 'change': function () {
386
- var value = $( this ).val(),
387
- option = predefined.find( '[value="' + value + '"]' ),
388
- to_val = option.data( 'to' ),
389
- from_val = option.data( 'from' );
 
 
390
 
391
- if ( 'custom' === value ) {
392
- dateinputs.show();
393
- return false;
394
  } else {
395
- dateinputs.hide();
396
- datepickers.datepicker( 'hide' );
397
  }
 
398
 
399
- from.val( from_val ).trigger( 'change', [ true ] );
400
- to.val( to_val ).trigger( 'change', [ true ] );
 
 
 
401
 
402
- if ( jQuery.datepicker && datepickers.datepicker( 'widget' ).is( ':visible' ) ) {
403
- datepickers.datepicker( 'refresh' ).datepicker( 'hide' );
404
- }
405
- },
406
- 'select2-removed': function() {
407
- predefined.val( '' ).trigger( 'change' );
408
- },
409
- 'check_options': function () {
410
- if ( '' !== to.val() && '' !== from.val() ) {
411
- var option = predefined
412
- .find( 'option' )
413
- .filter( '[data-to="' + to.val() + '"]' )
414
- .filter( '[data-from="' + from.val() + '"]' );
415
- if ( 0 !== option.length ) {
416
- predefined.val( option.attr( 'value' ) ).trigger( 'change', [ true ] );
417
  } else {
418
- predefined.val( 'custom' ).trigger( 'change', [ true ] );
419
  }
420
- } else if ( '' === to.val() && '' === from.val() ) {
421
- predefined.val( '' ).trigger( 'change', [ true ] );
422
- } else {
423
- predefined.val( 'custom' ).trigger( 'change', [ true ] );
424
  }
425
- }
426
- });
427
 
428
- from.on( 'change', function() {
429
- if ( '' !== from.val() ) {
430
- from_remove.show();
431
- to.datepicker( 'option', 'minDate', from.val() );
432
- } else {
433
- from_remove.hide();
434
- }
435
 
436
- if ( true === arguments[ arguments.length - 1 ] ) {
437
- return false;
438
- }
439
 
440
- predefined.trigger( 'check_options' );
441
- });
 
 
 
 
 
 
442
 
443
- to.on( 'change', function() {
444
- if ( '' !== to.val() ) {
445
- to_remove.show();
446
- from.datepicker( 'option', 'maxDate', to.val() );
447
- } else {
448
- to_remove.hide();
449
- }
450
 
451
- if ( true === arguments[ arguments.length - 1 ] ) {
452
- return false;
453
- }
454
 
455
- predefined.trigger( 'check_options' );
456
- });
457
-
458
- // Trigger change on load
459
- predefined.trigger( 'change' );
460
-
461
- $( '' ).add( from_remove ).add( to_remove ).on( 'click', function() {
462
- $( this ).next( 'input' ).val( '' ).trigger( 'change' );
463
- });
464
- });
465
- },
466
-
467
- save_interval: function( $btn ) {
468
- var $wrapper = this.wrapper;
469
- $btn.click( function() {
470
- var data = {
471
- key: $wrapper.find( 'select.field-predefined' ).find( ':selected' ).val(),
472
- start: $wrapper.find( '.date-inputs .field-from' ).val(),
473
- end: $wrapper.find( '.date-inputs .field-to' ).val()
474
- };
475
-
476
- // Add params to URL
477
- $( this ).attr( 'href', $( this ).attr( 'href' ) + '&' + $.param( data ) );
478
- });
479
- }
480
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
481
 
482
- $( document ).ready( function() {
483
- intervals.init( $( '.date-interval' ) );
 
 
 
484
 
485
- // Disable option groups whose children are all disabled
486
- $( 'select[name="context"] .level-1' ).each( function() {
487
- var all_disabled = true;
488
 
489
- $( this ).nextUntil( '.level-1' ).each( function() {
490
- if ( $( this ).is( ':not(:disabled)' ) ) {
491
- all_disabled = false;
492
- return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
493
  }
494
- });
495
 
496
- if ( true === all_disabled ) {
497
- $( this ).prop( 'disabled', true );
498
- }
499
- });
500
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
 
502
- });
 
503
 
504
- jQuery.extend({
505
- streamGetQueryVars: function( str ) {
506
- return ( str || document.location.search ).replace( /(^\?)/, '' ).split( '&' ).map( function( n ) { return n = n.split( '=' ), this[n[0]] = n[1], this; }.bind( {} ) )[0];
 
 
507
  }
508
- });
1
  /* globals wp_stream, ajaxurl */
2
+ jQuery(
3
+ function( $ ) {
4
+
5
+ // Shorter timeago strings for English locale
6
+ if ( 'en' === wp_stream.locale && 'undefined' !== typeof $.timeago ) {
7
+ $.timeago.settings.strings.seconds = 'seconds';
8
+ $.timeago.settings.strings.minute = 'a minute';
9
+ $.timeago.settings.strings.hour = 'an hour';
10
+ $.timeago.settings.strings.hours = '%d hours';
11
+ $.timeago.settings.strings.month = 'a month';
12
+ $.timeago.settings.strings.year = 'a year';
13
+ }
14
 
15
+ $( 'li.toplevel_page_wp_stream ul li.wp-first-item.current' ).parent().parent().find( '.update-plugins' ).remove();
16
 
17
+ $( '.toplevel_page_wp_stream :input.chosen-select' ).each(
18
+ function( i, el ) {
19
+ var args = {},
20
+ templateResult = function( record ) {
21
+ var $result = $( '<span>' ),
22
+ $elem = $( record.element ),
23
+ icon = '';
24
 
25
+ if ( '- ' === record.text.substring( 0, 2 ) ) {
26
+ record.text = record.text.substring( 2 );
27
+ }
28
 
29
+ if ( 'undefined' !== typeof record.id ) {
30
+ if ( record.id.indexOf( 'group-' ) === 0 ) {
31
+ $result.addClass( 'parent' );
32
+ } else if ( $elem.hasClass( 'level-2' ) ) {
33
+ $result.addClass( 'child' );
34
+ }
35
+ }
36
 
37
+ if ( undefined !== record.icon ) {
38
+ icon = record.icon;
39
+ } else if ( undefined !== $elem && '' !== $elem.data( 'icon' ) ) {
40
+ icon = $elem.data( 'icon' );
41
+ }
42
 
43
+ if ( icon ) {
44
+ $result.html( '<img src="' + icon + '" class="wp-stream-select2-icon">' );
45
+ }
46
+ $result.append( record.text );
47
+
48
+ return $result;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  },
50
+ templateSelection = function( record ) {
51
+ if ( '- ' === record.text.substring( 0, 2 ) ) {
52
+ record.text = record.text.substring( 2 );
53
+ }
54
+ return record.text;
55
+ };
56
+
57
+ if ( $( el ).find( 'option' ).not( ':selected' ).not( ':empty' ).length > 0 ) {
58
+ args = {
59
+ minimumResultsForSearch: 10,
60
+ templateResult: templateResult,
61
+ templateSelection: templateSelection,
62
+ allowClear: true,
63
+ width: '165px'
64
+ };
65
+ } else {
66
+ args = {
67
+ minimumInputLength: 3,
68
+ allowClear: true,
69
+ width: '165px',
70
+ ajax: {
71
+ url: ajaxurl,
72
+ dataType: 'json',
73
+ quietMillis: 100,
74
+ data: function( term ) {
75
+ return {
76
+ action: 'wp_stream_filters',
77
+ nonce: $( '#stream_filters_user_search_nonce' ).val(),
78
+ filter: $( el ).attr( 'name' ),
79
+ q: term.term
80
+ };
81
+ },
82
+ processResults: function( data ) {
83
+ var results = [];
84
+ $.each(
85
+ data, function( index, item ){
86
+ results.push(
87
+ {
88
+ id: item.id,
89
+ text: item.label
90
+ }
91
+ );
92
+ }
93
+ );
94
+ return {
95
+ results: results
96
+ };
97
+ }
98
+ },
99
+ templateResult: templateResult,
100
+ templateSelection: templateSelection
101
  };
102
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
+ $( el ).select2( args );
105
+ }
106
+ );
 
 
 
 
 
 
 
107
 
108
+ var $queryVars = $.streamGetQueryVars();
109
+ var $contextInput = $( '.toplevel_page_wp_stream select.chosen-select[name="context"]' );
 
 
 
110
 
111
+ if ( ( 'undefined' === typeof $queryVars.context || '' === $queryVars.context ) && 'undefined' !== typeof $queryVars.connector ) {
112
+ $contextInput.val( 'group-' + $queryVars.connector );
113
+ $contextInput.trigger( 'change' );
114
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
+ $( 'input[type=submit]', '#record-filter-form' ).click(
117
+ function() {
118
+ $( 'input[type=submit]', $( this ).parents( 'form' ) ).removeAttr( 'clicked' );
119
+ $( this ).attr( 'clicked', 'true' );
120
+ }
121
+ );
122
+
123
+ $( '#record-filter-form' ).submit(
124
+ function() {
125
+ var $context = $( '.toplevel_page_wp_stream :input.chosen-select[name="context"]' ),
126
+ $option = $context.find( 'option:selected' ),
127
+ $connector = $context.parent().find( '.record-filter-connector' ),
128
+ optionConnector = $option.data( 'group' ),
129
+ optionClass = $option.prop( 'class' ),
130
+ $recordAction = $( '.recordactions select' );
131
+
132
+ if ( $( '#record-actions-submit' ).attr( 'clicked' ) !== 'true' ) {
133
+ $recordAction.val( '' );
134
+ }
135
 
136
+ $connector.val( optionConnector );
137
 
138
+ if ( 'level-1' === optionClass ) {
139
+ $option.val( '' );
140
+ }
141
+ }
142
+ );
143
 
144
+ $( window ).load(
145
+ function() {
146
+ $( '.toplevel_page_wp_stream input[type="search"]' ).off( 'mousedown' );
147
+ }
148
+ );
149
 
150
+ // Confirmation on some important actions
151
+ $( 'body' ).on(
152
+ 'click', '#wp_stream_advanced_delete_all_records, #wp_stream_network_advanced_delete_all_records', function( e ) {
153
+ if ( ! window.confirm( wp_stream.i18n.confirm_purge ) ) {
154
+ e.preventDefault();
155
+ }
156
+ }
157
+ );
158
 
159
+ $( 'body' ).on(
160
+ 'click', '#wp_stream_advanced_reset_site_settings, #wp_stream_network_advanced_reset_site_settings', function( e ) {
161
+ if ( ! window.confirm( wp_stream.i18n.confirm_defaults ) ) {
162
+ e.preventDefault();
163
+ }
164
+ }
165
+ );
166
 
167
+ $( 'body' ).on(
168
+ 'click', '#wp_stream_uninstall', function( e ) {
169
+ if ( ! window.confirm( wp_stream.i18n.confirm_uninstall ) ) {
170
+ e.preventDefault();
171
+ }
172
+ }
173
+ );
174
+
175
+ // Admin page tabs
176
+ var $tabs = $( '.wp_stream_screen .nav-tab-wrapper' ),
177
+ $panels = $( '.wp_stream_screen .nav-tab-content table.form-table' ),
178
+ $activeTab = $tabs.find( '.nav-tab-active' ),
179
+ defaultIndex = $activeTab.length > 0 ? $tabs.find( 'a' ).index( $activeTab ) : 0,
180
+ hashIndex = window.location.hash.match( /^#(\d+)$/ ),
181
+ currentHash = ( null !== hashIndex ? hashIndex[ 1 ] : defaultIndex ),
182
+ syncFormAction = function( index ) {
183
+ var $optionsForm = $( 'input[name="option_page"][value^="wp_stream"]' ).closest( 'form' );
184
+ if ( $optionsForm.length === 0 ) {
185
+ return;
186
+ }
187
+ var currentAction = $optionsForm.attr( 'action' );
188
 
189
+ $optionsForm.prop( 'action', currentAction.replace( /(^[^#]*).*$/, '$1#' + index ) );
190
+ };
191
 
192
+ $tabs.on(
193
+ 'click', 'a', function() {
194
+ var index = $tabs.find( 'a' ).index( $( this ) ),
195
+ hashIndex = window.location.hash.match( /^#(\d+)$/ );
 
 
 
 
 
 
 
 
 
 
 
 
196
 
197
+ $panels.hide().eq( index ).show();
198
+ $tabs
199
+ .find( 'a' )
200
+ .removeClass( 'nav-tab-active' )
201
+ .filter( $( this ) )
202
+ .addClass( 'nav-tab-active' );
203
 
204
+ if ( '' === window.location.hash || null !== hashIndex ) {
205
+ window.location.hash = index;
 
206
  }
 
 
 
 
 
 
 
 
 
 
 
207
 
208
+ syncFormAction( index );
209
 
 
 
 
210
  return false;
211
  }
212
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
 
214
+ $tabs.children().eq( currentHash ).trigger( 'click' );
 
 
 
 
 
215
 
216
+ // Live Updates screen option
217
+ $( document ).ready(
218
+ function() {
219
 
220
+ // Enable Live Updates checkbox ajax
221
+ $( '#enable_live_update' ).click(
222
+ function() {
223
+ var nonce = $( '#stream_live_update_nonce' ).val(),
224
+ user = $( '#enable_live_update_user' ).val(),
225
+ checked = 'unchecked',
226
+ heartbeat = 'true';
227
 
228
+ if ( $( '#enable_live_update' ).is( ':checked' ) ) {
229
+ checked = 'checked';
230
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
 
232
+ heartbeat = $( '#enable_live_update' ).data( 'heartbeat' );
233
+
234
+ $.ajax(
235
+ {
236
+ type: 'POST',
237
+ url: ajaxurl,
238
+ data: {
239
+ action: 'stream_enable_live_update',
240
+ nonce: nonce,
241
+ user: user,
242
+ checked: checked,
243
+ heartbeat: heartbeat
244
+ },
245
+ dataType: 'json',
246
+ beforeSend: function() {
247
+ $( '.stream-live-update-checkbox .spinner' ).show().css( { 'display': 'inline-block' } );
248
+ },
249
+ success: function( response ) {
250
+ $( '.stream-live-update-checkbox .spinner' ).hide();
251
+
252
+ if ( false === response.success ) {
253
+ $( '#enable_live_update' ).prop( 'checked', false );
254
+
255
+ if ( response.data ) {
256
+ window.alert( response.data );
257
+ }
258
+ }
259
+ }
260
+ }
261
+ );
262
  }
263
+ );
264
 
265
+ function toggle_filter_submit() {
266
+ var all_hidden = true;
 
 
 
 
267
 
268
+ // If all filters are hidden, hide the button
269
+ if ( $( 'div.metabox-prefs [name="date-hide"]' ).is( ':checked' ) ) {
270
+ all_hidden = false;
271
+ }
272
 
273
+ var divs = $( 'div.alignleft.actions div.select2-container' );
 
 
274
 
275
+ divs.each(
276
+ function() {
277
+ if ( ! $( this ).is( ':hidden' ) ) {
278
+ all_hidden = false;
279
+ return false;
280
+ }
281
+ }
282
+ );
283
 
284
+ if ( all_hidden ) {
285
+ $( 'input#record-query-submit' ).hide();
286
+ $( 'span.filter_info' ).show();
287
  } else {
288
+ $( 'input#record-query-submit' ).show();
289
+ $( 'span.filter_info' ).hide();
290
  }
291
+ }
292
 
293
+ if ( $( 'div.metabox-prefs [name="date-hide"]' ).is( ':checked' ) ) {
294
+ $( 'div.date-interval' ).show();
295
+ } else {
296
+ $( 'div.date-interval' ).hide();
297
+ }
298
 
299
+ $( 'div.actions select.chosen-select' ).each(
300
+ function() {
301
+ var name = $( this ).prop( 'name' );
302
+
303
+ if ( $( 'div.metabox-prefs [name="' + name + '-hide"]' ).is( ':checked' ) ) {
304
+ $( this ).prev( '.select2-container' ).show();
 
 
 
 
 
 
 
 
 
305
  } else {
306
+ $( this ).prev( '.select2-container' ).hide();
307
  }
 
 
 
 
308
  }
309
+ );
 
310
 
311
+ toggle_filter_submit();
 
 
 
 
 
 
312
 
313
+ $( 'div.metabox-prefs [type="checkbox"]' ).click(
314
+ function() {
315
+ var id = $( this ).prop( 'id' );
316
 
317
+ if ( 'date-hide' === id ) {
318
+ if ( $( this ).is( ':checked' ) ) {
319
+ $( 'div.date-interval' ).show();
320
+ } else {
321
+ $( 'div.date-interval' ).hide();
322
+ }
323
+ } else {
324
+ id = id.replace( '-hide', '' );
325
 
326
+ if ( $( this ).is( ':checked' ) ) {
327
+ $( '[name="' + id + '"]' ).prev( '.select2-container' ).show();
328
+ } else {
329
+ $( '[name="' + id + '"]' ).prev( '.select2-container' ).hide();
330
+ }
331
+ }
 
332
 
333
+ toggle_filter_submit();
334
+ }
335
+ );
336
 
337
+ $( '#ui-datepicker-div' ).addClass( 'stream-datepicker' );
338
+ }
339
+ );
340
+
341
+ // Relative time
342
+ $( 'table.wp-list-table' ).on(
343
+ 'updated', function() {
344
+ var timeObjects = $( this ).find( 'time.relative-time' );
345
+ timeObjects.each(
346
+ function( i, el ) {
347
+ var timeEl = $( el );
348
+ timeEl.removeClass( 'relative-time' );
349
+ $( '<strong><time datetime="' + timeEl.attr( 'datetime' ) + '" class="timeago"/></time></strong><br/>' )
350
+ .prependTo( timeEl.parent().parent() )
351
+ .find( 'time.timeago' )
352
+ .timeago();
353
+ }
354
+ );
355
+ }
356
+ ).trigger( 'updated' );
357
+
358
+ var intervals = {
359
+ init: function( $wrapper ) {
360
+ this.wrapper = $wrapper;
361
+ this.save_interval( this.wrapper.find( '.button-primary' ), this.wrapper );
362
+
363
+ this.$ = this.wrapper.each(
364
+ function( i, val ) {
365
+ var container = $( val ),
366
+ dateinputs = container.find( '.date-inputs' ),
367
+ from = container.find( '.field-from' ),
368
+ to = container.find( '.field-to' ),
369
+ to_remove = to.prev( '.date-remove' ),
370
+ from_remove = from.prev( '.date-remove' ),
371
+ predefined = container.children( '.field-predefined' ),
372
+ datepickers = $( '' ).add( to ).add( from );
373
+
374
+ if ( jQuery.datepicker ) {
375
+
376
+ // Apply a GMT offset due to Date() using the visitor's local time
377
+ var siteGMTOffsetHours = parseFloat( wp_stream.gmt_offset ),
378
+ localGMTOffsetHours = new Date().getTimezoneOffset() / 60 * -1,
379
+ totalGMTOffsetHours = siteGMTOffsetHours - localGMTOffsetHours,
380
+ localTime = new Date(),
381
+ siteTime = new Date( localTime.getTime() + ( totalGMTOffsetHours * 60 * 60 * 1000 ) ),
382
+ maxOffset = 0,
383
+ minOffset = null;
384
+
385
+ // Check if the site date is different from the local date, and set a day offset
386
+ if ( localTime.getDate() !== siteTime.getDate() || localTime.getMonth() !== siteTime.getMonth() ) {
387
+ if ( localTime.getTime() < siteTime.getTime() ) {
388
+ maxOffset = '+1d';
389
+ } else {
390
+ maxOffset = '-1d';
391
+ }
392
+ }
393
+
394
+ datepickers.datepicker(
395
+ {
396
+ dateFormat: 'yy/mm/dd',
397
+ minDate: minOffset,
398
+ maxDate: maxOffset,
399
+ defaultDate: siteTime,
400
+ beforeShow: function() {
401
+ $( this ).prop( 'disabled', true );
402
+ },
403
+ onClose: function() {
404
+ $( this ).prop( 'disabled', false );
405
+ }
406
+ }
407
+ );
408
+
409
+ datepickers.datepicker( 'widget' ).addClass( 'stream-datepicker' );
410
+ }
411
 
412
+ predefined.select2(
413
+ {
414
+ 'allowClear': true
415
+ }
416
+ );
417
 
418
+ if ( '' !== from.val() ) {
419
+ from_remove.show();
420
+ }
421
 
422
+ if ( '' !== to.val() ) {
423
+ to_remove.show();
424
+ }
425
+
426
+ predefined.on(
427
+ {
428
+ 'change': function () {
429
+ var value = $( this ).val(),
430
+ option = predefined.find( '[value="' + value + '"]' ),
431
+ to_val = option.data( 'to' ),
432
+ from_val = option.data( 'from' );
433
+
434
+ if ( 'custom' === value ) {
435
+ dateinputs.show();
436
+ return false;
437
+ } else {
438
+ dateinputs.hide();
439
+ datepickers.datepicker( 'hide' );
440
+ }
441
+
442
+ from.val( from_val ).trigger( 'change', [ true ] );
443
+ to.val( to_val ).trigger( 'change', [ true ] );
444
+
445
+ if ( jQuery.datepicker && datepickers.datepicker( 'widget' ).is( ':visible' ) ) {
446
+ datepickers.datepicker( 'refresh' ).datepicker( 'hide' );
447
+ }
448
+ },
449
+ 'select2-removed': function() {
450
+ predefined.val( '' ).trigger( 'change' );
451
+ },
452
+ 'check_options': function () {
453
+ if ( '' !== to.val() && '' !== from.val() ) {
454
+ var option = predefined
455
+ .find( 'option' )
456
+ .filter( '[data-to="' + to.val() + '"]' )
457
+ .filter( '[data-from="' + from.val() + '"]' );
458
+ if ( 0 !== option.length ) {
459
+ predefined.val( option.attr( 'value' ) ).trigger( 'change', [ true ] );
460
+ } else {
461
+ predefined.val( 'custom' ).trigger( 'change', [ true ] );
462
+ }
463
+ } else if ( '' === to.val() && '' === from.val() ) {
464
+ predefined.val( '' ).trigger( 'change', [ true ] );
465
+ } else {
466
+ predefined.val( 'custom' ).trigger( 'change', [ true ] );
467
+ }
468
+ }
469
+ }
470
+ );
471
+
472
+ from.on(
473
+ 'change', function() {
474
+ if ( '' !== from.val() ) {
475
+ from_remove.show();
476
+ to.datepicker( 'option', 'minDate', from.val() );
477
+ } else {
478
+ from_remove.hide();
479
+ }
480
+
481
+ if ( true === arguments[ arguments.length - 1 ] ) {
482
+ return false;
483
+ }
484
+
485
+ predefined.trigger( 'check_options' );
486
+ }
487
+ );
488
+
489
+ to.on(
490
+ 'change', function() {
491
+ if ( '' !== to.val() ) {
492
+ to_remove.show();
493
+ from.datepicker( 'option', 'maxDate', to.val() );
494
+ } else {
495
+ to_remove.hide();
496
+ }
497
+
498
+ if ( true === arguments[ arguments.length - 1 ] ) {
499
+ return false;
500
+ }
501
+
502
+ predefined.trigger( 'check_options' );
503
+ }
504
+ );
505
+
506
+ // Trigger change on load
507
+ predefined.trigger( 'change' );
508
+
509
+ $( '' ).add( from_remove ).add( to_remove ).on(
510
+ 'click', function() {
511
+ $( this ).next( 'input' ).val( '' ).trigger( 'change' );
512
+ }
513
+ );
514
+ }
515
+ );
516
+ },
517
+
518
+ save_interval: function( $btn ) {
519
+ var $wrapper = this.wrapper;
520
+ $btn.click(
521
+ function() {
522
+ var data = {
523
+ key: $wrapper.find( 'select.field-predefined' ).find( ':selected' ).val(),
524
+ start: $wrapper.find( '.date-inputs .field-from' ).val(),
525
+ end: $wrapper.find( '.date-inputs .field-to' ).val()
526
+ };
527
+
528
+ // Add params to URL
529
+ $( this ).attr( 'href', $( this ).attr( 'href' ) + '&' + $.param( data ) );
530
+ }
531
+ );
532
  }
533
+ };
534
 
535
+ $( document ).ready(
536
+ function() {
537
+ intervals.init( $( '.date-interval' ) );
538
+
539
+ // Disable option groups whose children are all disabled
540
+ $( 'select[name="context"] .level-1' ).each(
541
+ function() {
542
+ var all_disabled = true;
543
+
544
+ $( this ).nextUntil( '.level-1' ).each(
545
+ function() {
546
+ if ( $( this ).is( ':not(:disabled)' ) ) {
547
+ all_disabled = false;
548
+ return false;
549
+ }
550
+ }
551
+ );
552
+
553
+ if ( true === all_disabled ) {
554
+ $( this ).prop( 'disabled', true );
555
+ }
556
+ }
557
+ );
558
+ }
559
+ );
560
 
561
+ }
562
+ );
563
 
564
+ jQuery.extend(
565
+ {
566
+ streamGetQueryVars: function( str ) {
567
+ return ( str || document.location.search ).replace( /(^\?)/, '' ).split( '&' ).map( function( n ) { return n = n.split( '=' ), this[n[0]] = n[1], this; }.bind( {} ) )[0];
568
+ }
569
  }
570
+ );
ui/js/admin.min.js ADDED
@@ -0,0 +1 @@
 
1
+ jQuery(function(e){"en"===wp_stream.locale&&void 0!==e.timeago&&(e.timeago.settings.strings.seconds="seconds",e.timeago.settings.strings.minute="a minute",e.timeago.settings.strings.hour="an hour",e.timeago.settings.strings.hours="%d hours",e.timeago.settings.strings.month="a month",e.timeago.settings.strings.year="a year"),e("li.toplevel_page_wp_stream ul li.wp-first-item.current").parent().parent().find(".update-plugins").remove(),e(".toplevel_page_wp_stream :input.chosen-select").each(function(t,a){var i={},n=function(t){var a=e("<span>"),i=e(t.element),n="";return"- "===t.text.substring(0,2)&&(t.text=t.text.substring(2)),void 0!==t.id&&(0===t.id.indexOf("group-")?a.addClass("parent"):i.hasClass("level-2")&&a.addClass("child")),void 0!==t.icon?n=t.icon:void 0!==i&&""!==i.data("icon")&&(n=i.data("icon")),n&&a.html('<img src="'+n+'" class="wp-stream-select2-icon">'),a.append(t.text),a},r=function(e){return"- "===e.text.substring(0,2)&&(e.text=e.text.substring(2)),e.text};i=e(a).find("option").not(":selected").not(":empty").length>0?{minimumResultsForSearch:10,templateResult:n,templateSelection:r,allowClear:!0,width:"165px"}:{minimumInputLength:3,allowClear:!0,width:"165px",ajax:{url:ajaxurl,dataType:"json",quietMillis:100,data:function(t){return{action:"wp_stream_filters",nonce:e("#stream_filters_user_search_nonce").val(),filter:e(a).attr("name"),q:t.term}},processResults:function(t){var a=[];return e.each(t,function(e,t){a.push({id:t.id,text:t.label})}),{results:a}}},templateResult:n,templateSelection:r},e(a).select2(i)});var t=e.streamGetQueryVars(),a=e('.toplevel_page_wp_stream select.chosen-select[name="context"]');void 0!==t.context&&""!==t.context||void 0===t.connector||(a.val("group-"+t.connector),a.trigger("change")),e("input[type=submit]","#record-filter-form").click(function(){e("input[type=submit]",e(this).parents("form")).removeAttr("clicked"),e(this).attr("clicked","true")}),e("#record-filter-form").submit(function(){var t=e('.toplevel_page_wp_stream :input.chosen-select[name="context"]'),a=t.find("option:selected"),i=t.parent().find(".record-filter-connector"),n=a.data("group"),r=a.prop("class"),s=e(".recordactions select");"true"!==e("#record-actions-submit").attr("clicked")&&s.val(""),i.val(n),"level-1"===r&&a.val("")}),e(window).load(function(){e('.toplevel_page_wp_stream input[type="search"]').off("mousedown")}),e("body").on("click","#wp_stream_advanced_delete_all_records, #wp_stream_network_advanced_delete_all_records",function(e){window.confirm(wp_stream.i18n.confirm_purge)||e.preventDefault()}),e("body").on("click","#wp_stream_advanced_reset_site_settings, #wp_stream_network_advanced_reset_site_settings",function(e){window.confirm(wp_stream.i18n.confirm_defaults)||e.preventDefault()}),e("body").on("click","#wp_stream_uninstall",function(e){window.confirm(wp_stream.i18n.confirm_uninstall)||e.preventDefault()});var i=e(".wp_stream_screen .nav-tab-wrapper"),n=e(".wp_stream_screen .nav-tab-content table.form-table"),r=i.find(".nav-tab-active"),s=r.length>0?i.find("a").index(r):0,o=window.location.hash.match(/^#(\d+)$/),c=null!==o?o[1]:s,d=function(t){var a=e('input[name="option_page"][value^="wp_stream"]').closest("form");if(0!==a.length){var i=a.attr("action");a.prop("action",i.replace(/(^[^#]*).*$/,"$1#"+t))}};i.on("click","a",function(){var t=i.find("a").index(e(this)),a=window.location.hash.match(/^#(\d+)$/);return n.hide().eq(t).show(),i.find("a").removeClass("nav-tab-active").filter(e(this)).addClass("nav-tab-active"),""!==window.location.hash&&null===a||(window.location.hash=t),d(t),!1}),i.children().eq(c).trigger("click"),e(document).ready(function(){function t(){var t=!0;e('div.metabox-prefs [name="date-hide"]').is(":checked")&&(t=!1),e("div.alignleft.actions div.select2-container").each(function(){if(!e(this).is(":hidden"))return t=!1,!1}),t?(e("input#record-query-submit").hide(),e("span.filter_info").show()):(e("input#record-query-submit").show(),e("span.filter_info").hide())}e("#enable_live_update").click(function(){var t=e("#stream_live_update_nonce").val(),a=e("#enable_live_update_user").val(),i="unchecked",n="true";e("#enable_live_update").is(":checked")&&(i="checked"),n=e("#enable_live_update").data("heartbeat"),e.ajax({type:"POST",url:ajaxurl,data:{action:"stream_enable_live_update",nonce:t,user:a,checked:i,heartbeat:n},dataType:"json",beforeSend:function(){e(".stream-live-update-checkbox .spinner").show().css({display:"inline-block"})},success:function(t){e(".stream-live-update-checkbox .spinner").hide(),!1===t.success&&(e("#enable_live_update").prop("checked",!1),t.data&&window.alert(t.data))}})}),e('div.metabox-prefs [name="date-hide"]').is(":checked")?e("div.date-interval").show():e("div.date-interval").hide(),e("div.actions select.chosen-select").each(function(){var t=e(this).prop("name");e('div.metabox-prefs [name="'+t+'-hide"]').is(":checked")?e(this).prev(".select2-container").show():e(this).prev(".select2-container").hide()}),t(),e('div.metabox-prefs [type="checkbox"]').click(function(){var a=e(this).prop("id");"date-hide"===a?e(this).is(":checked")?e("div.date-interval").show():e("div.date-interval").hide():(a=a.replace("-hide",""),e(this).is(":checked")?e('[name="'+a+'"]').prev(".select2-container").show():e('[name="'+a+'"]').prev(".select2-container").hide()),t()}),e("#ui-datepicker-div").addClass("stream-datepicker")}),e("table.wp-list-table").on("updated",function(){e(this).find("time.relative-time").each(function(t,a){var i=e(a);i.removeClass("relative-time"),e('<strong><time datetime="'+i.attr("datetime")+'" class="timeago"/></time></strong><br/>').prependTo(i.parent().parent()).find("time.timeago").timeago()})}).trigger("updated");var l={init:function(t){this.wrapper=t,this.save_interval(this.wrapper.find(".button-primary"),this.wrapper),this.$=this.wrapper.each(function(t,a){var i=e(a),n=i.find(".date-inputs"),r=i.find(".field-from"),s=i.find(".field-to"),o=s.prev(".date-remove"),c=r.prev(".date-remove"),d=i.children(".field-predefined"),l=e("").add(s).add(r);if(jQuery.datepicker){var p=parseFloat(wp_stream.gmt_offset)-(new Date).getTimezoneOffset()/60*-1,u=new Date,h=new Date(u.getTime()+60*p*60*1e3),m=0;u.getDate()===h.getDate()&&u.getMonth()===h.getMonth()||(m=u.getTime()<h.getTime()?"+1d":"-1d"),l.datepicker({dateFormat:"yy/mm/dd",minDate:null,maxDate:m,defaultDate:h,beforeShow:function(){e(this).prop("disabled",!0)},onClose:function(){e(this).prop("disabled",!1)}}),l.datepicker("widget").addClass("stream-datepicker")}d.select2({allowClear:!0}),""!==r.val()&&c.show(),""!==s.val()&&o.show(),d.on({change:function(){var t=e(this).val(),a=d.find('[value="'+t+'"]'),i=a.data("to"),o=a.data("from");if("custom"===t)return n.show(),!1;n.hide(),l.datepicker("hide"),r.val(o).trigger("change",[!0]),s.val(i).trigger("change",[!0]),jQuery.datepicker&&l.datepicker("widget").is(":visible")&&l.datepicker("refresh").datepicker("hide")},"select2-removed":function(){d.val("").trigger("change")},check_options:function(){if(""!==s.val()&&""!==r.val()){var e=d.find("option").filter('[data-to="'+s.val()+'"]').filter('[data-from="'+r.val()+'"]');0!==e.length?d.val(e.attr("value")).trigger("change",[!0]):d.val("custom").trigger("change",[!0])}else""===s.val()&&""===r.val()?d.val("").trigger("change",[!0]):d.val("custom").trigger("change",[!0])}}),r.on("change",function(){if(""!==r.val()?(c.show(),s.datepicker("option","minDate",r.val())):c.hide(),!0===arguments[arguments.length-1])return!1;d.trigger("check_options")}),s.on("change",function(){if(""!==s.val()?(o.show(),r.datepicker("option","maxDate",s.val())):o.hide(),!0===arguments[arguments.length-1])return!1;d.trigger("check_options")}),d.trigger("change"),e("").add(c).add(o).on("click",function(){e(this).next("input").val("").trigger("change")})})},save_interval:function(t){var a=this.wrapper;t.click(function(){var t={key:a.find("select.field-predefined").find(":selected").val(),start:a.find(".date-inputs .field-from").val(),end:a.find(".date-inputs .field-to").val()};e(this).attr("href",e(this).attr("href")+"&"+e.param(t))})}};e(document).ready(function(){l.init(e(".date-interval")),e('select[name="context"] .level-1').each(function(){var t=!0;e(this).nextUntil(".level-1").each(function(){if(e(this).is(":not(:disabled)"))return t=!1,!1}),!0===t&&e(this).prop("disabled",!0)})})}),jQuery.extend({streamGetQueryVars:function(e){return(e||document.location.search).replace(/(^\?)/,"").split("&").map(function(e){return e=e.split("="),this[e[0]]=e[1],this}.bind({}))[0]}});
ui/js/alerts-list.js CHANGED
@@ -1,11 +1,15 @@
1
  /* globals jQuery */
2
  ( function( $ ) {
3
- $( document ).ready( function() {
4
- $( '.inline-edit-col-left, .inline-edit-col-right, #major-publishing-actions', '.edit-php.post-type-wp_stream_alerts' ).each( function() {
5
- $( this ).remove();
6
- });
 
 
 
7
 
8
- // This is done with JS instead of CSS to override the inline styles added by Select2's JS.
9
- $( '.select2-container', '.inline-edit-col' ).css( { 'width': '100%' } );
10
- });
 
11
  })( jQuery );
1
  /* globals jQuery */
2
  ( function( $ ) {
3
+ $( document ).ready(
4
+ function() {
5
+ $( '.inline-edit-col-left, .inline-edit-col-right, #major-publishing-actions', '.edit-php.post-type-wp_stream_alerts' ).each(
6
+ function() {
7
+ $( this ).remove();
8
+ }
9
+ );
10
 
11
+ // This is done with JS instead of CSS to override the inline styles added by Select2's JS.
12
+ $( '.select2-container', '.inline-edit-col' ).css( { 'width': '100%' } );
13
+ }
14
+ );
15
  })( jQuery );
ui/js/alerts-list.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e){e(document).ready(function(){e(".inline-edit-col-left, .inline-edit-col-right, #major-publishing-actions",".edit-php.post-type-wp_stream_alerts").each(function(){e(this).remove()}),e(".select2-container",".inline-edit-col").css({width:"100%"})})}(jQuery);
ui/js/alerts.js CHANGED
@@ -1,271 +1,309 @@
1
  /* globals jQuery, streamAlerts, inlineEditPost */
2
- jQuery( function( $ ) {
3
- 'use strict';
4
- var $post_row,
5
- $edit_row;
6
- var setupSelectTwo = function setupSelectTwo( id ) {
7
- var $target = $( id );
8
- $target.find( '.select2-select.connector_or_context' ).each( function( k, el ) {
9
- $( el ).select2({
10
- allowClear: true,
11
- placeholder: streamAlerts.anyContext,
12
- templateResult: function( item ) {
13
- if ( 'undefined' === typeof item.id ) {
14
- return item.text;
15
- }
16
- if ( -1 === item.id.indexOf( '-' ) ) {
17
- return $( '<span class="parent">' + item.text + '</span>' );
18
- } else {
19
- return $( '<span class="child">' + item.text + '</span>' );
20
- }
21
- },
22
- matcher: function (params, data) {
23
- var match = $.extend( true, {}, data );
 
 
 
24
 
25
- if (null === params.term || '' === $.trim( params.term )) {
26
- return match;
27
- }
28
 
29
- var term = params.term.toLowerCase();
30
 
31
- match.id = match.id.replace( 'blogs', 'sites' );
32
- if (match.id.toLowerCase().indexOf( term ) >= 0) {
33
- return match;
34
- }
35
 
36
- if (match.children) {
37
- for (var i = match.children.length - 1; i >= 0; i--) {
38
- var child = match.children[i];
39
 
40
- // Remove term from results if it doesn't match.
41
- if (-1 === child.id.toLowerCase().indexOf( term )) {
42
- match.children.splice( i, 1 );
 
 
 
 
 
 
 
 
 
 
43
  }
44
- }
 
 
 
 
 
 
 
 
 
45
 
46
- if (match.children.length > 0) {
47
- return match;
 
 
 
 
48
  }
 
49
  }
 
50
 
51
- return null;
52
- }
53
- }).change( function() {
54
- var value = $( this ).val();
55
- if ( value ) {
56
- var parts = value.split( '-' );
57
- $( this ).siblings( '.connector' ).val( parts[0] );
58
- $( this ).siblings( '.context' ).val( parts[1] );
59
- }
60
- });
61
-
62
- var parts = [
63
- $( el ).siblings( '.connector' ).val(),
64
- $( el ).siblings( '.context' ).val()
65
- ];
66
- if ( '' === parts[1] ) {
67
- parts.splice( 1, 1 );
68
- }
69
- $( el ).val( parts.join( '-' ) ).trigger( 'change' );
70
- });
71
 
72
- $target.find( 'select.select2-select:not(.connector_or_context)' ).each( function() {
73
- var element_id_split = $( this ).attr( 'id' ).split( '_' );
74
- var select_name = element_id_split[element_id_split.length - 1].charAt( 0 ).toUpperCase() +
75
- element_id_split[element_id_split.length - 1].slice( 1 );
76
- $( this ).select2({
77
- allowClear: true,
78
- placeholder: streamAlerts.any + ' ' + select_name
79
- });
80
- });
81
- };
82
- var $alertSettingSelect = $( '#wp_stream_alert_type' );
83
 
84
- var loadAlertSettings = function( alert_type ) {
85
- var data = {
86
- 'action' : 'load_alerts_settings',
87
- 'alert_type' : alert_type
88
- };
 
 
 
 
 
 
 
 
 
 
 
89
 
90
- var $edit_row = $( '#wp_stream_alert_type' ).closest( 'tr' );
91
- var row_id = $edit_row.attr( 'id' );
92
- data.post_id = row_id.split('-')[1];
93
- $.post( window.ajaxurl, data, function( response ) {
94
- var $alert_type_settings = $( '#wp_stream_alert_type_form' );
95
- var alert_type = $( '#wp_stream_alert_type' ).val();
96
- if ('none' === alert_type) {
97
- $alert_type_settings.hide();
98
- return;
99
- }
100
- $alert_type_settings.html( response.data.html );
101
- $alert_type_settings.show();
102
- });
103
- };
104
 
105
- $( '#the-list' ).on('change', '#wp_stream_trigger_connector_or_context', function() {
106
- if ( 'wp_stream_trigger_connector_or_context' === $( this ).attr( 'id' ) ) {
107
- var connector = $( this ).val();
108
- if ( connector && 0 < connector.indexOf( '-' ) ) {
109
- var connector_split = connector.split( '-' );
110
- connector = connector_split[0];
111
- }
112
- getActions( connector );
113
- }
114
- });
115
 
116
- var getActions = function( connector ) {
117
- var trigger_action = $( '#wp_stream_trigger_action' );
118
- trigger_action.empty();
119
- trigger_action.prop( 'disabled', true );
120
 
121
- var placeholder = $( '<option/>', {value: '', text: ''} );
122
- trigger_action.append( placeholder );
 
 
123
 
124
- var data = {
125
- 'action' : 'get_actions',
126
- 'connector' : connector
127
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
- $.post( window.ajaxurl, data, function( response ) {
130
- var success = response.success,
131
- actions = response.data;
132
- if ( ! success ) {
133
- return;
134
- }
135
- for ( var key in actions ) {
136
- if ( actions.hasOwnProperty( key ) ) {
137
- var value = actions[key];
138
- var option = $( '<option/>', {value: key, text: value} );
139
- trigger_action.append( option );
140
  }
141
- }
142
- trigger_action.prop( 'disabled', false );
143
- $( document ).trigger( 'alert-actions-updated' );
144
- });
145
- };
146
 
147
- $alertSettingSelect.change( function() {
148
- loadAlertSettings( $( this ).val() );
149
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
- $( '#wpbody-content' ).on( 'click', 'a.page-title-action', function( e ) {
152
- e.preventDefault();
153
- $( '#add-new-alert' ).remove();
154
- if ( $( '.inline-edit-wp_stream_alerts' ).length > 0 ) {
155
- $( '.inline-edit-wp_stream_alerts .inline-edit-save button.button-secondary.cancel' ).trigger( 'click' );
156
- }
157
- var alert_form_html = '';
158
- var data = {
159
- 'action': 'get_new_alert_triggers_notifications'
160
- };
161
- $.post( window.ajaxurl, data, function( response ) {
162
- if ( true === response.success ) {
163
- alert_form_html = response.data.html;
164
- $( 'tbody#the-list' ).prepend( '<tr id="add-new-alert" class="inline-edit-row inline-edit-row-page inline-edit-page quick-edit-row quick-edit-row-page inline-edit-page inline-editor" style=""><td colspan="4" class="colspanchange">' + alert_form_html + '<p class="submit inline-edit-save"> <button type="button" class="button-secondary cancel alignleft">Cancel</button> <input type="hidden" id="_inline_edit" name="_inline_edit" value="3550d271fe"> <button type="button" class="button-primary save alignright">Save</button> <span class="spinner"></span><span class="error" style="display:none"></span> <br class="clear"></p></td></tr>' );
165
- var add_new_alert = $( '#add-new-alert' );
166
- var current_bg_color = add_new_alert.css( 'background-color' );
167
 
168
- // Color taken from /wp-admin/css/forms.css
169
- // #pass-strength-result.strong
170
- add_new_alert.css( 'background-color', '#C1E1B9' );
171
- setTimeout( function() {
172
- add_new_alert.css( 'background-color', current_bg_color );
173
- }, 250);
 
 
 
 
 
174
 
175
- $( '#wp_stream_alert_type' ).change( function() {
176
- loadAlertSettings( $( this ).val() );
177
- });
178
- add_new_alert.on( 'click', '.button-secondary.cancel', function() {
179
- $( '#add-new-alert' ).remove();
180
- });
181
- add_new_alert.on( 'click', '.button-primary.save', save_new_alert );
182
-
183
- setupSelectTwo( '#add-new-alert' );
184
- }
185
- });
186
 
187
- });
188
- var save_new_alert = function save_new_alert( e ) {
189
- e.preventDefault();
190
- $( '#add-new-alert' ).find( 'p.submit.inline-edit-save span.spinner' ).css( 'visibility', 'visible' );
191
- var data = {
192
- 'action': 'save_new_alert',
193
- 'wp_stream_alerts_nonce': $( '#wp_stream_alerts_nonce' ).val(),
194
- 'wp_stream_trigger_author': $( '#wp_stream_trigger_author' ).val(),
195
- 'wp_stream_trigger_context': $( '#wp_stream_trigger_connector_or_context' ).val(),
196
- 'wp_stream_trigger_action': $( '#wp_stream_trigger_action' ).val(),
197
- 'wp_stream_alert_type': $( '#wp_stream_alert_type' ).val(),
198
- 'wp_stream_alert_status': $( '#wp_stream_alert_status' ).val()
199
- };
200
- $( '#wp_stream_alert_type_form' ).find( ':input' ).each( function(){
201
- var alert_type_data_id = $( this ).attr( 'id' );
202
- if ( $( this ).val() ) {
203
- data[alert_type_data_id] = $( this ).val();
204
- }
205
- });
 
 
 
206
 
207
- $.post( window.ajaxurl, data, function( response ) {
208
- if ( true === response.success ) {
209
- $( '#add-new-alert' ).find( 'p.submit.inline-edit-save span.spinner' ).css( 'visibility', 'hidden' );
210
- location.reload();
211
- }
212
- });
213
- };
 
 
214
 
215
- // we create a copy of the WP inline edit post function
216
- var $wp_inline_edit = inlineEditPost.edit;
217
 
218
- // and then we overwrite the function with our own code
219
- inlineEditPost.edit = function( id ) {
220
- // "call" the original WP edit function
221
- // we don't want to leave WordPress hanging
222
- $wp_inline_edit.apply( this, arguments );
223
 
224
- // now we take care of our business
225
 
226
- // get the post ID
227
- var post_id = 0;
228
- if ( typeof( id ) === 'object' ) {
229
- post_id = parseInt( this.getId( id ), 10 );
230
- }
231
 
232
- if ( post_id > 0 ) {
233
- // define the edit row
234
- $edit_row = $( '#edit-' + post_id );
235
- $post_row = $( '#post-' + post_id );
236
 
237
- // get the data
238
- var alert_trigger_connector = $post_row.find( 'input[name="wp_stream_trigger_connector"]' ).val();
239
- var alert_trigger_context = $post_row.find( 'input[name="wp_stream_trigger_context"]' ).val();
240
- var alert_trigger_connector_context = alert_trigger_connector + '-' + alert_trigger_context;
241
- var alert_trigger_action = $post_row.find( 'input[name="wp_stream_trigger_action"]' ).val();
242
- var alert_status = $post_row.find( 'input[name="wp_stream_alert_status"]' ).val();
243
 
244
- // populate the data
245
- $edit_row.find( 'input[name="wp_stream_trigger_connector"]' ).attr( 'value', alert_trigger_connector );
246
- $edit_row.find( 'input[name="wp_stream_trigger_context"]' ).attr( 'value', alert_trigger_context );
247
- $edit_row.find( 'select[name="wp_stream_trigger_connector_or_context"] option[value="' + alert_trigger_connector_context + '"]' ).attr( 'selected', 'selected' );
248
- $( document ).one( 'alert-actions-updated', function() {
249
- $edit_row.find( 'input[name="wp_stream_trigger_action"]' ).attr( 'value', alert_trigger_action );
250
- $edit_row.find( 'select[name="wp_stream_trigger_action"] option[value="' + alert_trigger_action + '"]' ).attr( 'selected', 'selected' ).trigger( 'change' );
251
- });
252
- $edit_row.find( 'select[name="wp_stream_alert_status"] option[value="' + alert_status + '"]' ).attr( 'selected', 'selected' );
253
- setupSelectTwo( '#edit-' + post_id );
 
 
254
 
255
- // Alert type handling
256
- $( '#wp_stream_alert_type_form' ).hide();
257
- var alert_type = $post_row.find( 'input[name="wp_stream_alert_type"]' ).val();
258
- $edit_row.find( 'select[name="wp_stream_alert_type"] option[value="' + alert_type + '"]' ).attr( 'selected', 'selected' ).trigger( 'change' );
259
- }
260
- };
261
- if ( window.location.hash ) {
262
- var $target_post_row = $( window.location.hash );
263
- if ( $target_post_row.length ) {
264
- var scroll_to_position = $target_post_row.offset().top - $('#wpadminbar').height();
265
- $('html, body').animate({
266
- scrollTop: scroll_to_position
267
- }, 1000);
268
- $target_post_row.find('.row-actions a.editinline').trigger('click');
 
 
 
269
  }
270
  }
271
- });
1
  /* globals jQuery, streamAlerts, inlineEditPost */
2
+ jQuery(
3
+ function( $ ) {
4
+ 'use strict';
5
+ var $post_row,
6
+ $edit_row;
7
+ var setupSelectTwo = function setupSelectTwo( id ) {
8
+ var $target = $( id );
9
+ $target.find( '.select2-select.connector_or_context' ).each(
10
+ function( k, el ) {
11
+ $( el ).select2(
12
+ {
13
+ allowClear: true,
14
+ placeholder: streamAlerts.anyContext,
15
+ templateResult: function( item ) {
16
+ if ( 'undefined' === typeof item.id ) {
17
+ return item.text;
18
+ }
19
+ if ( -1 === item.id.indexOf( '-' ) ) {
20
+ return $( '<span class="parent">' + item.text + '</span>' );
21
+ } else {
22
+ return $( '<span class="child">' + item.text + '</span>' );
23
+ }
24
+ },
25
+ matcher: function (params, data) {
26
+ var match = $.extend( true, {}, data );
27
 
28
+ if (null === params.term || '' === $.trim( params.term )) {
29
+ return match;
30
+ }
31
 
32
+ var term = params.term.toLowerCase();
33
 
34
+ match.id = match.id.replace( 'blogs', 'sites' );
35
+ if (match.id.toLowerCase().indexOf( term ) >= 0) {
36
+ return match;
37
+ }
38
 
39
+ if (match.children) {
40
+ for (var i = match.children.length - 1; i >= 0; i--) {
41
+ var child = match.children[i];
42
 
43
+ // Remove term from results if it doesn't match.
44
+ if (-1 === child.id.toLowerCase().indexOf( term )) {
45
+ match.children.splice( i, 1 );
46
+ }
47
+ }
48
+
49
+ if (match.children.length > 0) {
50
+ return match;
51
+ }
52
+ }
53
+
54
+ return null;
55
+ }
56
  }
57
+ ).change(
58
+ function() {
59
+ var value = $( this ).val();
60
+ if ( value ) {
61
+ var parts = value.split( '-' );
62
+ $( this ).siblings( '.connector' ).val( parts[0] );
63
+ $( this ).siblings( '.context' ).val( parts[1] );
64
+ }
65
+ }
66
+ );
67
 
68
+ var parts = [
69
+ $( el ).siblings( '.connector' ).val(),
70
+ $( el ).siblings( '.context' ).val()
71
+ ];
72
+ if ( '' === parts[1] ) {
73
+ parts.splice( 1, 1 );
74
  }
75
+ $( el ).val( parts.join( '-' ) ).trigger( 'change' );
76
  }
77
+ );
78
 
79
+ $target.find( 'select.select2-select:not(.connector_or_context)' ).each(
80
+ function() {
81
+ var element_id_split = $( this ).attr( 'id' ).split( '_' );
82
+ var select_name = element_id_split[element_id_split.length - 1].charAt( 0 ).toUpperCase() +
83
+ element_id_split[element_id_split.length - 1].slice( 1 );
84
+ $( this ).select2(
85
+ {
86
+ allowClear: true,
87
+ placeholder: streamAlerts.any + ' ' + select_name
88
+ }
89
+ );
90
+ }
91
+ );
92
+ };
93
+ var $alertSettingSelect = $( '#wp_stream_alert_type' );
 
 
 
 
 
94
 
95
+ var loadAlertSettings = function( alert_type ) {
96
+ var data = {
97
+ 'action' : 'load_alerts_settings',
98
+ 'alert_type' : alert_type
99
+ };
 
 
 
 
 
 
100
 
101
+ var $edit_row = $( '#wp_stream_alert_type' ).closest( 'tr' );
102
+ var row_id = $edit_row.attr( 'id' );
103
+ data.post_id = row_id.split( '-' )[1];
104
+ $.post(
105
+ window.ajaxurl, data, function( response ) {
106
+ var $alert_type_settings = $( '#wp_stream_alert_type_form' );
107
+ var alert_type = $( '#wp_stream_alert_type' ).val();
108
+ if ('none' === alert_type) {
109
+ $alert_type_settings.hide();
110
+ return;
111
+ }
112
+ $alert_type_settings.html( response.data.html );
113
+ $alert_type_settings.show();
114
+ }
115
+ );
116
+ };
117
 
118
+ $( '#the-list' ).on(
119
+ 'change', '#wp_stream_trigger_connector_or_context', function() {
120
+ if ( 'wp_stream_trigger_connector_or_context' === $( this ).attr( 'id' ) ) {
121
+ var connector = $( this ).val();
122
+ if ( connector && 0 < connector.indexOf( '-' ) ) {
123
+ var connector_split = connector.split( '-' );
124
+ connector = connector_split[0];
125
+ }
126
+ getActions( connector );
127
+ }
128
+ }
129
+ );
 
 
130
 
131
+ var getActions = function( connector ) {
132
+ var trigger_action = $( '#wp_stream_trigger_action' );
133
+ trigger_action.empty();
134
+ trigger_action.prop( 'disabled', true );
 
 
 
 
 
 
135
 
136
+ var placeholder = $( '<option/>', {value: '', text: ''} );
137
+ trigger_action.append( placeholder );
 
 
138
 
139
+ var data = {
140
+ 'action' : 'get_actions',
141
+ 'connector' : connector
142
+ };
143
 
144
+ $.post(
145
+ window.ajaxurl, data, function( response ) {
146
+ var success = response.success,
147
+ actions = response.data;
148
+ if ( ! success ) {
149
+ return;
150
+ }
151
+ for ( var key in actions ) {
152
+ if ( actions.hasOwnProperty( key ) ) {
153
+ var value = actions[key];
154
+ var option = $( '<option/>', {value: key, text: value} );
155
+ trigger_action.append( option );
156
+ }
157
+ }
158
+ trigger_action.prop( 'disabled', false );
159
+ $( document ).trigger( 'alert-actions-updated' );
160
+ }
161
+ );
162
+ };
163
 
164
+ $alertSettingSelect.change(
165
+ function() {
166
+ loadAlertSettings( $( this ).val() );
 
 
 
 
 
 
 
 
167
  }
168
+ );
 
 
 
 
169
 
170
+ $( '#wpbody-content' ).on(
171
+ 'click', 'a.page-title-action', function( e ) {
172
+ e.preventDefault();
173
+ $( '#add-new-alert' ).remove();
174
+ if ( $( '.inline-edit-wp_stream_alerts' ).length > 0 ) {
175
+ $( '.inline-edit-wp_stream_alerts .inline-edit-save button.button-secondary.cancel' ).trigger( 'click' );
176
+ }
177
+ var alert_form_html = '';
178
+ var data = {
179
+ 'action': 'get_new_alert_triggers_notifications'
180
+ };
181
+ $.post(
182
+ window.ajaxurl, data, function( response ) {
183
+ if ( true === response.success ) {
184
+ alert_form_html = response.data.html;
185
+ $( 'tbody#the-list' ).prepend( '<tr id="add-new-alert" class="inline-edit-row inline-edit-row-page inline-edit-page quick-edit-row quick-edit-row-page inline-edit-page inline-editor" style=""><td colspan="4" class="colspanchange">' + alert_form_html + '<p class="submit inline-edit-save"> <button type="button" class="button-secondary cancel alignleft">Cancel</button> <input type="hidden" id="_inline_edit" name="_inline_edit" value="3550d271fe"> <button type="button" class="button-primary save alignright">Save</button> <span class="spinner"></span><span class="error" style="display:none"></span> <br class="clear"></p></td></tr>' );
186
+ var add_new_alert = $( '#add-new-alert' );
187
+ var current_bg_color = add_new_alert.css( 'background-color' );
188
 
189
+ // Color taken from /wp-admin/css/forms.css
190
+ // #pass-strength-result.strong
191
+ add_new_alert.css( 'background-color', '#C1E1B9' );
192
+ setTimeout(
193
+ function() {
194
+ add_new_alert.css( 'background-color', current_bg_color );
195
+ }, 250
196
+ );
 
 
 
 
 
 
 
 
197
 
198
+ $( '#wp_stream_alert_type' ).change(
199
+ function() {
200
+ loadAlertSettings( $( this ).val() );
201
+ }
202
+ );
203
+ add_new_alert.on(
204
+ 'click', '.button-secondary.cancel', function() {
205
+ $( '#add-new-alert' ).remove();
206
+ }
207
+ );
208
+ add_new_alert.on( 'click', '.button-primary.save', save_new_alert );
209
 
210
+ setupSelectTwo( '#add-new-alert' );
211
+ }
212
+ }
213
+ );
 
 
 
 
 
 
 
214
 
215
+ }
216
+ );
217
+ var save_new_alert = function save_new_alert( e ) {
218
+ e.preventDefault();
219
+ $( '#add-new-alert' ).find( 'p.submit.inline-edit-save span.spinner' ).css( 'visibility', 'visible' );
220
+ var data = {
221
+ 'action': 'save_new_alert',
222
+ 'wp_stream_alerts_nonce': $( '#wp_stream_alerts_nonce' ).val(),
223
+ 'wp_stream_trigger_author': $( '#wp_stream_trigger_author' ).val(),
224
+ 'wp_stream_trigger_context': $( '#wp_stream_trigger_connector_or_context' ).val(),
225
+ 'wp_stream_trigger_action': $( '#wp_stream_trigger_action' ).val(),
226
+ 'wp_stream_alert_type': $( '#wp_stream_alert_type' ).val(),
227
+ 'wp_stream_alert_status': $( '#wp_stream_alert_status' ).val()
228
+ };
229
+ $( '#wp_stream_alert_type_form' ).find( ':input' ).each(
230
+ function(){
231
+ var alert_type_data_id = $( this ).attr( 'id' );
232
+ if ( $( this ).val() ) {
233
+ data[alert_type_data_id] = $( this ).val();
234
+ }
235
+ }
236
+ );
237
 
238
+ $.post(
239
+ window.ajaxurl, data, function( response ) {
240
+ if ( true === response.success ) {
241
+ $( '#add-new-alert' ).find( 'p.submit.inline-edit-save span.spinner' ).css( 'visibility', 'hidden' );
242
+ location.reload();
243
+ }
244
+ }
245
+ );
246
+ };
247
 
248
+ // we create a copy of the WP inline edit post function
249
+ var $wp_inline_edit = inlineEditPost.edit;
250
 
251
+ // and then we overwrite the function with our own code
252
+ inlineEditPost.edit = function( id ) {
253
+ // "call" the original WP edit function
254
+ // we don't want to leave WordPress hanging
255
+ $wp_inline_edit.apply( this, arguments );
256
 
257
+ // now we take care of our business
258
 
259
+ // get the post ID
260
+ var post_id = 0;
261
+ if ( typeof( id ) === 'object' ) {
262
+ post_id = parseInt( this.getId( id ), 10 );
263
+ }
264
 
265
+ if ( post_id > 0 ) {
266
+ // define the edit row
267
+ $edit_row = $( '#edit-' + post_id );
268
+ $post_row = $( '#post-' + post_id );
269
 
270
+ // get the data
271
+ var alert_trigger_connector = $post_row.find( 'input[name="wp_stream_trigger_connector"]' ).val();
272
+ var alert_trigger_context = $post_row.find( 'input[name="wp_stream_trigger_context"]' ).val();
273
+ var alert_trigger_connector_context = alert_trigger_connector + '-' + alert_trigger_context;
274
+ var alert_trigger_action = $post_row.find( 'input[name="wp_stream_trigger_action"]' ).val();
275
+ var alert_status = $post_row.find( 'input[name="wp_stream_alert_status"]' ).val();
276
 
277
+ // populate the data
278
+ $edit_row.find( 'input[name="wp_stream_trigger_connector"]' ).attr( 'value', alert_trigger_connector );
279
+ $edit_row.find( 'input[name="wp_stream_trigger_context"]' ).attr( 'value', alert_trigger_context );
280
+ $edit_row.find( 'select[name="wp_stream_trigger_connector_or_context"] option[value="' + alert_trigger_connector_context + '"]' ).attr( 'selected', 'selected' );
281
+ $( document ).one(
282
+ 'alert-actions-updated', function() {
283
+ $edit_row.find( 'input[name="wp_stream_trigger_action"]' ).attr( 'value', alert_trigger_action );
284
+ $edit_row.find( 'select[name="wp_stream_trigger_action"] option[value="' + alert_trigger_action + '"]' ).attr( 'selected', 'selected' ).trigger( 'change' );
285
+ }
286
+ );
287
+ $edit_row.find( 'select[name="wp_stream_alert_status"] option[value="' + alert_status + '"]' ).attr( 'selected', 'selected' );
288
+ setupSelectTwo( '#edit-' + post_id );
289
 
290
+ // Alert type handling
291
+ $( '#wp_stream_alert_type_form' ).hide();
292
+ var alert_type = $post_row.find( 'input[name="wp_stream_alert_type"]' ).val();
293
+ $edit_row.find( 'select[name="wp_stream_alert_type"] option[value="' + alert_type + '"]' ).attr( 'selected', 'selected' ).trigger( 'change' );
294
+ }
295
+ };
296
+ if ( window.location.hash ) {
297
+ var $target_post_row = $( window.location.hash );
298
+ if ( $target_post_row.length ) {
299
+ var scroll_to_position = $target_post_row.offset().top - $( '#wpadminbar' ).height();
300
+ $( 'html, body' ).animate(
301
+ {
302
+ scrollTop: scroll_to_position
303
+ }, 1000
304
+ );
305
+ $target_post_row.find( '.row-actions a.editinline' ).trigger( 'click' );
306
+ }
307
  }
308
  }
309
+ );
ui/js/alerts.min.js ADDED
@@ -0,0 +1 @@
 
1
+ jQuery(function(t){"use strict";var e,n,a=function(e){var n=t(e);n.find(".select2-select.connector_or_context").each(function(e,n){t(n).select2({allowClear:!0,placeholder:streamAlerts.anyContext,templateResult:function(e){return void 0===e.id?e.text:t(-1===e.id.indexOf("-")?'<span class="parent">'+e.text+"</span>":'<span class="child">'+e.text+"</span>")},matcher:function(e,n){var a=t.extend(!0,{},n);if(null===e.term||""===t.trim(e.term))return a;var i=e.term.toLowerCase();if(a.id=a.id.replace("blogs","sites"),a.id.toLowerCase().indexOf(i)>=0)return a;if(a.children){for(var r=a.children.length-1;r>=0;r--)-1===a.children[r].id.toLowerCase().indexOf(i)&&a.children.splice(r,1);if(a.children.length>0)return a}return null}}).change(function(){var e=t(this).val();if(e){var n=e.split("-");t(this).siblings(".connector").val(n[0]),t(this).siblings(".context").val(n[1])}});var a=[t(n).siblings(".connector").val(),t(n).siblings(".context").val()];""===a[1]&&a.splice(1,1),t(n).val(a.join("-")).trigger("change")}),n.find("select.select2-select:not(.connector_or_context)").each(function(){var e=t(this).attr("id").split("_"),n=e[e.length-1].charAt(0).toUpperCase()+e[e.length-1].slice(1);t(this).select2({allowClear:!0,placeholder:streamAlerts.any+" "+n})})},i=t("#wp_stream_alert_type"),r=function(e){var n={action:"load_alerts_settings",alert_type:e},a=t("#wp_stream_alert_type").closest("tr").attr("id");n.post_id=a.split("-")[1],t.post(window.ajaxurl,n,function(e){var n=t("#wp_stream_alert_type_form");"none"!==t("#wp_stream_alert_type").val()?(n.html(e.data.html),n.show()):n.hide()})};t("#the-list").on("change","#wp_stream_trigger_connector_or_context",function(){if("wp_stream_trigger_connector_or_context"===t(this).attr("id")){var e=t(this).val();e&&0<e.indexOf("-")&&(e=e.split("-")[0]),s(e)}});var s=function(e){var n=t("#wp_stream_trigger_action");n.empty(),n.prop("disabled",!0);var a=t("<option/>",{value:"",text:""});n.append(a);var i={action:"get_actions",connector:e};t.post(window.ajaxurl,i,function(e){var a=e.success,i=e.data;if(a){for(var r in i)if(i.hasOwnProperty(r)){var s=i[r],l=t("<option/>",{value:r,text:s});n.append(l)}n.prop("disabled",!1),t(document).trigger("alert-actions-updated")}})};i.change(function(){r(t(this).val())}),t("#wpbody-content").on("click","a.page-title-action",function(e){e.preventDefault(),t("#add-new-alert").remove(),t(".inline-edit-wp_stream_alerts").length>0&&t(".inline-edit-wp_stream_alerts .inline-edit-save button.button-secondary.cancel").trigger("click");var n="",i={action:"get_new_alert_triggers_notifications"};t.post(window.ajaxurl,i,function(e){if(!0===e.success){n=e.data.html,t("tbody#the-list").prepend('<tr id="add-new-alert" class="inline-edit-row inline-edit-row-page inline-edit-page quick-edit-row quick-edit-row-page inline-edit-page inline-editor" style=""><td colspan="4" class="colspanchange">'+n+'<p class="submit inline-edit-save"> <button type="button" class="button-secondary cancel alignleft">Cancel</button> <input type="hidden" id="_inline_edit" name="_inline_edit" value="3550d271fe"> <button type="button" class="button-primary save alignright">Save</button> <span class="spinner"></span><span class="error" style="display:none"></span> <br class="clear"></p></td></tr>');var i=t("#add-new-alert"),s=i.css("background-color");i.css("background-color","#C1E1B9"),setTimeout(function(){i.css("background-color",s)},250),t("#wp_stream_alert_type").change(function(){r(t(this).val())}),i.on("click",".button-secondary.cancel",function(){t("#add-new-alert").remove()}),i.on("click",".button-primary.save",l),a("#add-new-alert")}})});var l=function(e){e.preventDefault(),t("#add-new-alert").find("p.submit.inline-edit-save span.spinner").css("visibility","visible");var n={action:"save_new_alert",wp_stream_alerts_nonce:t("#wp_stream_alerts_nonce").val(),wp_stream_trigger_author:t("#wp_stream_trigger_author").val(),wp_stream_trigger_context:t("#wp_stream_trigger_connector_or_context").val(),wp_stream_trigger_action:t("#wp_stream_trigger_action").val(),wp_stream_alert_type:t("#wp_stream_alert_type").val(),wp_stream_alert_status:t("#wp_stream_alert_status").val()};t("#wp_stream_alert_type_form").find(":input").each(function(){var e=t(this).attr("id");t(this).val()&&(n[e]=t(this).val())}),t.post(window.ajaxurl,n,function(e){!0===e.success&&(t("#add-new-alert").find("p.submit.inline-edit-save span.spinner").css("visibility","hidden"),location.reload())})},o=inlineEditPost.edit;if(inlineEditPost.edit=function(i){o.apply(this,arguments);var r=0;if("object"==typeof i&&(r=parseInt(this.getId(i),10)),r>0){n=t("#edit-"+r);var s=(e=t("#post-"+r)).find('input[name="wp_stream_trigger_connector"]').val(),l=e.find('input[name="wp_stream_trigger_context"]').val(),c=s+"-"+l,p=e.find('input[name="wp_stream_trigger_action"]').val(),_=e.find('input[name="wp_stream_alert_status"]').val();n.find('input[name="wp_stream_trigger_connector"]').attr("value",s),n.find('input[name="wp_stream_trigger_context"]').attr("value",l),n.find('select[name="wp_stream_trigger_connector_or_context"] option[value="'+c+'"]').attr("selected","selected"),t(document).one("alert-actions-updated",function(){n.find('input[name="wp_stream_trigger_action"]').attr("value",p),n.find('select[name="wp_stream_trigger_action"] option[value="'+p+'"]').attr("selected","selected").trigger("change")}),n.find('select[name="wp_stream_alert_status"] option[value="'+_+'"]').attr("selected","selected"),a("#edit-"+r),t("#wp_stream_alert_type_form").hide();var d=e.find('input[name="wp_stream_alert_type"]').val();n.find('select[name="wp_stream_alert_type"] option[value="'+d+'"]').attr("selected","selected").trigger("change")}},window.location.hash){var c=t(window.location.hash);if(c.length){var p=c.offset().top-t("#wpadminbar").height();t("html, body").animate({scrollTop:p},1e3),c.find(".row-actions a.editinline").trigger("click")}}});
ui/js/exclude.js CHANGED
@@ -1,401 +1,465 @@
1
  /* globals ajaxurl, wp_stream_regenerate_alt_rows */
2
- jQuery( function( $ ) {
3
- var initSettingsSelect2 = function() {
4
- var $input_user;
5
-
6
- $( '.stream-exclude-list tr:not(.hidden) select.select2-select.connector_or_context' ).each( function( k, el ) {
7
- $( el ).select2({
8
- allowClear: true,
9
- templateResult : function( item ) {
10
- if ( typeof item.id === 'undefined' ) {
11
- return item.text;
12
- }
13
- if ( item.id.indexOf( '-' ) === -1 ) {
14
- return $( '<span class="parent">' + item.text + '</span>' );
15
- } else {
16
- return $( '<span class="child">' + item.text + '</span>' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  }
18
- },
19
- matcher: function( params, data ) {
20
- var match = $.extend( true, {}, data );
21
 
22
- if ( params.term == null || $.trim( params.term ) === '') {
23
- return match;
 
 
 
 
 
24
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
- var term = params.term.toLowerCase();
27
-
28
- match.id = match.id.replace( 'blogs', 'sites' );
29
- if ( match.id.toLowerCase().indexOf( term ) >= 0 ) {
30
- return match;
31
  }
32
-
33
- if ( match.children ) {
34
-
35
- for ( var i = match.children.length - 1; i >= 0; i--) {
36
- var child = match.children[i];
37
-
38
- // Remove term from results if it doesn't match.
39
- if ( child.id.toLowerCase().indexOf( term ) === -1 ) {
40
- match.children.splice( i, 1 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  }
42
- }
43
-
44
- if ( match.children.length > 0 ) {
45
- return match;
46
- }
47
  }
 
 
 
 
 
48
 
49
- return null;
50
- }
51
- }).on( 'change', function() {
52
- var row = $( this ).closest( 'tr' ),
53
- connector = $( this ).val();
54
- if ( connector && 0 < connector.indexOf( '-' ) ) {
55
- var connector_split = connector.split( '-' );
56
- connector = connector_split[0];
57
- }
58
- getActions( row, connector );
59
- });
60
- });
61
-
62
- $( '.stream-exclude-list tr:not(.hidden) select.select2-select.action' ).each( function( k, el ) {
63
- $( el ).select2({
64
- allowClear: true
65
- });
66
- });
67
-
68
- $( '.stream-exclude-list tr:not(.hidden) select.select2-select.author_or_role' ).each( function( k, el ) {
69
- $input_user = $( el );
70
-
71
- $input_user.select2({
72
- ajax: {
73
- type: 'POST',
74
- url: ajaxurl,
75
- dataType: 'json',
76
- quietMillis: 500,
77
- data: function( term, page ) {
78
- return {
79
- find: term,
80
- limit: 10,
81
- pager: page,
82
- action: 'stream_get_users',
83
- nonce: $input_user.data( 'nonce' )
84
- };
85
- },
86
- processResults: function( response ) {
87
- var answer = {
88
- results: [
89
- { text: '', id: '' },
90
- { text: 'Roles', children: [] },
91
- { text: 'Users', children: [] }
92
- ]
93
- };
94
-
95
- if ( true !== response.success || undefined === response.data || true !== response.data.status ) {
96
- return answer;
97
- }
98
 
99
- if ( undefined === response.data.users || undefined === response.data.roles ) {
100
- return answer;
 
101
  }
102
-
103
- var roles = [];
104
-
105
- $.each( response.data.roles, function( id, text ) {
106
- roles.push({
107
- 'id' : id,
108
- 'text' : text
109
- });
110
- });
111
-
112
- answer.results[ 1 ].children = roles;
113
- answer.results[ 2 ].children = response.data.users;
114
-
115
- // Return the value of more so Select2 knows if more results can be loaded
116
- return answer;
117
  }
118
- },
119
- templateResult: function( object ) {
120
- var $result = $( '<div>' ).text( object.text );
121
 
122
- if ( 'undefined' !== typeof object.icon && object.icon ) {
123
- $result.prepend( $( '<img src="' + object.icon + '" class="wp-stream-select2-icon">' ) );
 
124
 
125
- // Add more info to the container
126
- $result.attr( 'title', object.tooltip );
127
- }
128
 
129
- // Add more info to the container
130
- if ( 'undefined' !== typeof object.tooltip ) {
131
- $result.attr( 'title', object.tooltip );
132
- } else if ( 'undefined' !== typeof object.user_count ) {
133
- $result.attr( 'title', object.user_count );
134
  }
 
 
135
 
136
- return $result;
137
- },
138
- templateSelection: function( object ) {
139
- var $result = $( '<div>' ).text( object.text );
140
 
141
- if ( $.isNumeric( object.id ) && object.text.indexOf( 'icon-users' ) < 0 ) {
142
- $result.append( $( '<i class="icon16 icon-users"></i>' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  }
 
 
 
144
 
145
- return $result;
146
- },
147
- allowClear: true,
148
- placeholder: $input_user.data( 'placeholder' )
149
- }).on( 'change', function() {
150
- var value = $( this ).select2( 'data' );
151
-
152
- $( this ).data( 'selected-id', value.id );
153
- $( this ).data( 'selected-text', value.text );
154
- });
155
- });
156
-
157
- $( '.stream-exclude-list tr:not(.hidden) select.select2-select.ip_address' ).each( function( k, el ) {
158
- var $input_ip = $( el ),
159
- searchTerm = '';
160
-
161
- $input_ip.select2({
162
- ajax: {
163
- type: 'POST',
164
- url: ajaxurl,
165
- dataType: 'json',
166
- quietMillis: 500,
167
- data: function( term ) {
168
- searchTerm = term.term;
169
- return {
170
- find: term,
171
- limit: 10,
172
- action: 'stream_get_ips',
173
- nonce: $input_ip.data( 'nonce' )
174
- };
175
- },
176
- processResults: function( response ) {
177
- var answer = { results: [] },
178
- ip_chunks = [];
179
-
180
- if ( true === response.success && undefined !== response.data ) {
181
- $.each( response.data, function( key, ip ) {
182
- answer.results.push({
183
- id: ip,
184
- text: ip
185
- });
186
- });
187
- }
188
-
189
- if ( undefined === searchTerm ) {
190
- return answer;
191
- }
192
-
193
- ip_chunks = searchTerm.match( /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ );
194
 
195
- if ( null === ip_chunks ) {
196
- return answer;
 
197
  }
 
198
 
199
- // remove whole match
200
- ip_chunks.shift();
201
 
202
- ip_chunks = $.grep(
203
- ip_chunks,
204
- function( chunk ) {
205
- var numeric = parseInt( chunk, 10 );
206
- return numeric <= 255 && numeric.toString() === chunk;
207
- }
208
- );
209
 
210
- if ( ip_chunks.length >= 4 ) {
211
- answer.results.push({
212
- id: searchTerm,
213
- text: searchTerm
214
- });
215
- }
216
 
217
- return answer;
218
- }
219
- },
220
- allowClear: false,
221
- multiple: true,
222
- maximumSelectionSize: 1,
223
- placeholder: $input_ip.data( 'placeholder' ),
224
- tags: true
225
- });
226
- }).on( 'change', function() {
227
- $( this ).prev( '.select2-container' ).find( 'input.select2-input' ).blur();
228
- });
229
-
230
- $( 'ul.select2-choices, ul.select2-choices li, input.select2-input', '.stream-exclude-list tr:not(.hidden) .ip_address' ).on( 'mousedown click focus', function() {
231
- var $container = $( this ).closest( '.select2-container' ),
232
- $input = $container.find( 'input.select2-input' ),
233
- value = $container.select2( 'data' );
234
-
235
- if ( value.length >= 1 ) {
236
- $input.blur();
237
- return false;
238
- }
239
- });
240
 
241
- $( '.stream-exclude-list tr:not(.hidden) .exclude_rules_remove_rule_row' ).on( 'click', function() {
242
- var $thisRow = $( this ).closest( 'tr' );
 
 
243
 
244
- $thisRow.remove();
 
 
 
245
 
246
- recalculate_rules_found();
247
- recalculate_rules_selected();
248
- });
249
- };
 
 
 
250
 
251
- initSettingsSelect2();
252
 
253
- $( '.stream-exclude-list tr:not(.hidden) select.select2-select.author_or_role' ).each( function() {
254
- var $option = $('<option selected>' + $( this ).data( 'selected-text' ) + '</option>').val( $( this ).data( 'selected-id' ) );
255
- $( this ).append( $option ).trigger( 'change' );
256
- });
257
 
258
- $( '.stream-exclude-list tr:not(.hidden) select.select2-select.connector_or_context' ).each( function() {
259
- var parts = [
260
- $( this ).siblings( '.connector' ).val(),
261
- $( this ).siblings( '.context' ).val()
262
- ];
263
- if ( parts[1] === '' ) {
264
- parts.splice( 1, 1 );
265
- }
266
- $( this ).val( parts.join( '-' ) ).trigger( 'change' );
267
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
- $( '#exclude_rules_new_rule' ).on( 'click', function() {
270
- var $excludeList = $( 'table.stream-exclude-list' );
271
 
272
- $( 'tr:not(.hidden) select.select2-select', $excludeList ).each( function() {
273
- $( this ).select2( 'destroy' );
274
- });
 
 
275
 
276
- var $lastRow = $( 'tr', $excludeList ).last(),
277
- $newRow = $lastRow.clone();
 
278
 
279
- $newRow.removeAttr( 'class' );
280
- $( '.stream-exclude-list tbody :input' ).off();
281
- $( ':input', $newRow ).off().val( '' );
282
 
283
- $lastRow.after( $newRow );
 
284
 
285
- initSettingsSelect2();
 
 
 
286
 
287
- recalculate_rules_found();
288
- recalculate_rules_selected();
289
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
 
291
- $( '#exclude_rules_remove_rules' ).on( 'click', function() {
292
- var $excludeList = $( 'table.stream-exclude-list' ),
293
- selectedRows = $( 'tbody input.cb-select:checked', $excludeList ).closest( 'tr' );
294
 
295
- if ( ( $( 'tbody tr', $excludeList ).length - selectedRows.length ) >= 2 ) {
296
- selectedRows.remove();
297
- } else {
298
- $( ':input', selectedRows ).val( '' );
299
- $( selectedRows ).not( ':first' ).remove();
300
- $( '.select2-select', selectedRows ).select2( 'val', '' );
301
  }
302
 
303
- $excludeList.find( 'input.cb-select' ).prop( 'checked', false );
304
-
305
- recalculate_rules_found();
306
- recalculate_rules_selected();
307
- });
308
-
309
- $( '.stream-exclude-list' ).closest( 'form' ).submit( function() {
310
- $( '.stream-exclude-list tbody tr.hidden', this ).each( function() {
311
- $( this ).find( ':input' ).removeAttr( 'name' );
312
- });
313
- $( '.stream-exclude-list tbody tr:not(.hidden) select.select2-select.connector_or_context', this ).each( function() {
314
- var parts = $( this ).val().split( '-' );
315
- $( this ).siblings( '.connector' ).val( parts[0] );
316
- $( this ).siblings( '.context' ).val( parts[1] );
317
- $( this ).removeAttr( 'name' );
318
- });
319
- $( '.stream-exclude-list tbody tr:not(.hidden) select.select2-select.ip_address', this ).each( function() {
320
- var firstSelected = $( 'option:selected', this ).first();
321
- $( 'option:selected:not(:first)', this ).each( function() {
322
- firstSelected.attr( 'value', firstSelected.attr( 'value' ) + ',' + $( this ).attr( 'value' ) );
323
- $( this ).removeAttr( 'selected' );
324
- });
325
- });
326
- });
327
-
328
- $( '.stream-exclude-list' ).closest( 'td' ).prev( 'th' ).hide();
329
-
330
- $( 'table.stream-exclude-list' ).on( 'click', 'input.cb-select', function() {
331
- recalculate_rules_selected();
332
- });
333
-
334
- function getActions( row, connector ) {
335
- var trigger_action = $( '.select2-select.action', row ),
336
- action_value = trigger_action.val();
337
-
338
- trigger_action.empty();
339
- trigger_action.prop( 'disabled', true );
340
-
341
- var placeholder = $( '<option/>', {value: '', text: ''} );
342
- trigger_action.append( placeholder );
343
-
344
- var data = {
345
- 'action' : 'get_actions',
346
- 'connector' : connector
347
- };
348
-
349
- $.post( window.ajaxurl, data, function( response ) {
350
- var success = response.success,
351
- actions = response.data;
352
- if ( ! success ) {
353
- return;
354
- }
355
- for ( var key in actions ) {
356
- if ( actions.hasOwnProperty( key ) ) {
357
- var value = actions[key];
358
- var option = $( '<option/>', {value: key, text: value} );
359
- trigger_action.append( option );
360
- }
361
  }
362
- trigger_action.val( action_value );
363
- trigger_action.prop( 'disabled', false );
364
- $( document ).trigger( 'alert-actions-updated' );
365
- });
366
- }
367
-
368
- function recalculate_rules_selected() {
369
- var $selectedRows = $( 'table.stream-exclude-list tbody tr:not( .hidden ) input.cb-select:checked' ),
370
- $deleteButton = $( '#exclude_rules_remove_rules' );
371
 
372
- if ( 0 === $selectedRows.length ) {
373
- $deleteButton.prop( 'disabled', true );
374
- } else {
375
- $deleteButton.prop( 'disabled', false );
376
  }
377
- }
378
 
379
- function recalculate_rules_found() {
380
- var $allRows = $( 'table.stream-exclude-list tbody tr:not( .hidden )' ),
381
- $noRulesFound = $( 'table.stream-exclude-list tbody tr.no-items' ),
382
- $selectAll = $( '.check-column.manage-column input.cb-select' ),
383
- $deleteButton = $( '#exclude_rules_remove_rules' );
384
-
385
- if ( 0 === $allRows.length ) {
386
- $noRulesFound.show();
387
- $selectAll.prop( 'disabled', true );
388
- $deleteButton.prop( 'disabled', true );
389
- } else {
390
- $noRulesFound.hide();
391
- $selectAll.prop( 'disabled', false );
392
- }
393
-
394
- wp_stream_regenerate_alt_rows( $allRows );
395
  }
396
-
397
- $( document ).ready( function() {
398
- recalculate_rules_found();
399
- recalculate_rules_selected();
400
- });
401
- });
1
  /* globals ajaxurl, wp_stream_regenerate_alt_rows */
2
+ jQuery(
3
+ function( $ ) {
4
+ var initSettingsSelect2 = function() {
5
+ var $input_user;
6
+
7
+ $( '.stream-exclude-list tr:not(.hidden) select.select2-select.connector_or_context' ).each(
8
+ function( k, el ) {
9
+ $( el ).select2(
10
+ {
11
+ allowClear: true,
12
+ templateResult : function( item ) {
13
+ if ( typeof item.id === 'undefined' ) {
14
+ return item.text;
15
+ }
16
+ if ( item.id.indexOf( '-' ) === -1 ) {
17
+ return $( '<span class="parent">' + item.text + '</span>' );
18
+ } else {
19
+ return $( '<span class="child">' + item.text + '</span>' );
20
+ }
21
+ },
22
+ matcher: function( params, data ) {
23
+ var match = $.extend( true, {}, data );
24
+
25
+ if ( params.term == null || $.trim( params.term ) === '') {
26
+ return match;
27
+ }
28
+
29
+ var term = params.term.toLowerCase();
30
+
31
+ match.id = match.id.replace( 'blogs', 'sites' );
32
+ if ( match.id.toLowerCase().indexOf( term ) >= 0 ) {
33
+ return match;
34
+ }
35
+
36
+ if ( match.children ) {
37
+
38
+ for ( var i = match.children.length - 1; i >= 0; i--) {
39
+ var child = match.children[i];
40
+
41
+ // Remove term from results if it doesn't match.
42
+ if ( child.id.toLowerCase().indexOf( term ) === -1 ) {
43
+ match.children.splice( i, 1 );
44
+ }
45
+ }
46
+
47
+ if ( match.children.length > 0 ) {
48
+ return match;
49
+ }
50
+ }
51
+
52
+ return null;
53
+ }
54
+ }
55
+ ).on(
56
+ 'change', function() {
57
+ var row = $( this ).closest( 'tr' ),
58
+ connector = $( this ).val();
59
+ if ( connector && 0 < connector.indexOf( '-' ) ) {
60
+ var connector_split = connector.split( '-' );
61
+ connector = connector_split[0];
62
+ }
63
+ getActions( row, connector );
64
+ }
65
+ );
66
  }
67
+ );
 
 
68
 
69
+ $( '.stream-exclude-list tr:not(.hidden) select.select2-select.action' ).each(
70
+ function( k, el ) {
71
+ $( el ).select2(
72
+ {
73
+ allowClear: true
74
+ }
75
+ );
76
  }
77
+ );
78
+
79
+ $( '.stream-exclude-list tr:not(.hidden) select.select2-select.author_or_role' ).each(
80
+ function( k, el ) {
81
+ $input_user = $( el );
82
+
83
+ $input_user.select2(
84
+ {
85
+ ajax: {
86
+ type: 'POST',
87
+ url: ajaxurl,
88
+ dataType: 'json',
89
+ quietMillis: 500,
90
+ data: function( term, page ) {
91
+ return {
92
+ find: term,
93
+ limit: 10,
94
+ pager: page,
95
+ action: 'stream_get_users',
96
+ nonce: $input_user.data( 'nonce' )
97
+ };
98
+ },
99
+ processResults: function( response ) {
100
+ var answer = {
101
+ results: [
102
+ { text: '', id: '' },
103
+ { text: 'Roles', children: [] },
104
+ { text: 'Users', children: [] }
105
+ ]
106
+ };
107
+
108
+ if ( true !== response.success || undefined === response.data || true !== response.data.status ) {
109
+ return answer;
110
+ }
111
+
112
+ if ( undefined === response.data.users || undefined === response.data.roles ) {
113
+ return answer;
114
+ }
115
+
116
+ var roles = [];
117
+
118
+ $.each(
119
+ response.data.roles, function( id, text ) {
120
+ roles.push(
121
+ {
122
+ 'id' : id,
123
+ 'text' : text
124
+ }
125
+ );
126
+ }
127
+ );
128
+
129
+ answer.results[ 1 ].children = roles;
130
+ answer.results[ 2 ].children = response.data.users;
131
+
132
+ // Return the value of more so Select2 knows if more results can be loaded
133
+ return answer;
134
+ }
135
+ },
136
+ templateResult: function( object ) {
137
+ var $result = $( '<div>' ).text( object.text );
138
+
139
+ if ( 'undefined' !== typeof object.icon && object.icon ) {
140
+ $result.prepend( $( '<img src="' + object.icon + '" class="wp-stream-select2-icon">' ) );
141
+
142
+ // Add more info to the container
143
+ $result.attr( 'title', object.tooltip );
144
+ }
145
+
146
+ // Add more info to the container
147
+ if ( 'undefined' !== typeof object.tooltip ) {
148
+ $result.attr( 'title', object.tooltip );
149
+ } else if ( 'undefined' !== typeof object.user_count ) {
150
+ $result.attr( 'title', object.user_count );
151
+ }
152
+
153
+ return $result;
154
+ },
155
+ templateSelection: function( object ) {
156
+ var $result = $( '<div>' ).text( object.text );
157
+
158
+ if ( $.isNumeric( object.id ) && object.text.indexOf( 'icon-users' ) < 0 ) {
159
+ $result.append( $( '<i class="icon16 icon-users"></i>' ) );
160
+ }
161
+
162
+ return $result;
163
+ },
164
+ allowClear: true,
165
+ placeholder: $input_user.data( 'placeholder' )
166
+ }
167
+ ).on(
168
+ 'change', function() {
169
+ var value = $( this ).select2( 'data' );
170
 
171
+ $( this ).data( 'selected-id', value.id );
172
+ $( this ).data( 'selected-text', value.text );
173
+ }
174
+ );
 
175
  }
176
+ );
177
+
178
+ $( '.stream-exclude-list tr:not(.hidden) select.select2-select.ip_address' ).each(
179
+ function( k, el ) {
180
+ var $input_ip = $( el ),
181
+ searchTerm = '';
182
+
183
+ $input_ip.select2(
184
+ {
185
+ ajax: {
186
+ type: 'POST',
187
+ url: ajaxurl,
188
+ dataType: 'json',
189
+ quietMillis: 500,
190
+ data: function( term ) {
191
+ searchTerm = term.term;
192
+ return {
193
+ find: term,
194
+ limit: 10,
195
+ action: 'stream_get_ips',
196
+ nonce: $input_ip.data( 'nonce' )
197
+ };
198
+ },
199
+ processResults: function( response ) {
200
+ var answer = { results: [] },
201
+ ip_chunks = [];
202
+
203
+ if ( true === response.success && undefined !== response.data ) {
204
+ $.each(
205
+ response.data, function( key, ip ) {
206
+ answer.results.push(
207
+ {
208
+ id: ip,
209
+ text: ip
210
+ }
211
+ );
212
+ }
213
+ );
214
+ }
215
+
216
+ if ( undefined === searchTerm ) {
217
+ return answer;
218
+ }
219
+
220
+ ip_chunks = searchTerm.match( /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ );
221
+
222
+ if ( null === ip_chunks ) {
223
+ return answer;
224
+ }
225
+
226
+ // remove whole match
227
+ ip_chunks.shift();
228
+
229
+ ip_chunks = $.grep(
230
+ ip_chunks,
231
+ function( chunk ) {
232
+ var numeric = parseInt( chunk, 10 );
233
+ return numeric <= 255 && numeric.toString() === chunk;
234
+ }
235
+ );
236
+
237
+ if ( ip_chunks.length >= 4 ) {
238
+ answer.results.push(
239
+ {
240
+ id: searchTerm,
241
+ text: searchTerm
242
+ }
243
+ );
244
+ }
245
+
246
+ return answer;
247
+ }
248
+ },
249
+ allowClear: false,
250
+ multiple: true,
251
+ maximumSelectionSize: 1,
252
+ placeholder: $input_ip.data( 'placeholder' ),
253
+ tags: true
254
  }
255
+ );
 
 
 
 
256
  }
257
+ ).on(
258
+ 'change', function() {
259
+ $( this ).prev( '.select2-container' ).find( 'input.select2-input' ).blur();
260
+ }
261
+ );
262
 
263
+ $( 'ul.select2-choices, ul.select2-choices li, input.select2-input', '.stream-exclude-list tr:not(.hidden) .ip_address' ).on(
264
+ 'mousedown click focus', function() {
265
+ var $container = $( this ).closest( '.select2-container' ),
266
+ $input = $container.find( 'input.select2-input' ),
267
+ value = $container.select2( 'data' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
+ if ( value.length >= 1 ) {
270
+ $input.blur();
271
+ return false;
272
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  }
274
+ );
 
 
275
 
276
+ $( '.stream-exclude-list tr:not(.hidden) .exclude_rules_remove_rule_row' ).on(
277
+ 'click', function() {
278
+ var $thisRow = $( this ).closest( 'tr' );
279
 
280
+ $thisRow.remove();
 
 
281
 
282
+ recalculate_rules_found();
283
+ recalculate_rules_selected();
 
 
 
284
  }
285
+ );
286
+ };
287
 
288
+ initSettingsSelect2();
 
 
 
289
 
290
+ $( '.stream-exclude-list tr:not(.hidden) select.select2-select.author_or_role' ).each(
291
+ function() {
292
+ var $option = $( '<option selected>' + $( this ).data( 'selected-text' ) + '</option>' ).val( $( this ).data( 'selected-id' ) );
293
+ $( this ).append( $option ).trigger( 'change' );
294
+ }
295
+ );
296
+
297
+ $( '.stream-exclude-list tr:not(.hidden) select.select2-select.connector_or_context' ).each(
298
+ function() {
299
+ var parts = [
300
+ $( this ).siblings( '.connector' ).val(),
301
+ $( this ).siblings( '.context' ).val()
302
+ ];
303
+ if ( parts[1] === '' ) {
304
+ parts.splice( 1, 1 );
305
  }
306
+ $( this ).val( parts.join( '-' ) ).trigger( 'change' );
307
+ }
308
+ );
309
 
310
+ $( '#exclude_rules_new_rule' ).on(
311
+ 'click', function() {
312
+ var $excludeList = $( 'table.stream-exclude-list' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
 
314
+ $( 'tr:not(.hidden) select.select2-select', $excludeList ).each(
315
+ function() {
316
+ $( this ).select2( 'destroy' );
317
  }
318
+ );
319
 
320
+ var $lastRow = $( 'tr', $excludeList ).last(),
321
+ $newRow = $lastRow.clone();
322
 
323
+ $newRow.removeAttr( 'class' );
324
+ $( '.stream-exclude-list tbody :input' ).off();
325
+ $( ':input', $newRow ).off().val( '' );
 
 
 
 
326
 
327
+ $lastRow.after( $newRow );
 
 
 
 
 
328
 
329
+ initSettingsSelect2();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
 
331
+ recalculate_rules_found();
332
+ recalculate_rules_selected();
333
+ }
334
+ );
335
 
336
+ $( '#exclude_rules_remove_rules' ).on(
337
+ 'click', function() {
338
+ var $excludeList = $( 'table.stream-exclude-list' ),
339
+ selectedRows = $( 'tbody input.cb-select:checked', $excludeList ).closest( 'tr' );
340
 
341
+ if ( ( $( 'tbody tr', $excludeList ).length - selectedRows.length ) >= 2 ) {
342
+ selectedRows.remove();
343
+ } else {
344
+ $( ':input', selectedRows ).val( '' );
345
+ $( selectedRows ).not( ':first' ).remove();
346
+ $( '.select2-select', selectedRows ).select2( 'val', '' );
347
+ }
348
 
349
+ $excludeList.find( 'input.cb-select' ).prop( 'checked', false );
350
 
351
+ recalculate_rules_found();
352
+ recalculate_rules_selected();
353
+ }
354
+ );
355
 
356
+ $( '.stream-exclude-list' ).closest( 'form' ).submit(
357
+ function() {
358
+ $( '.stream-exclude-list tbody tr.hidden', this ).each(
359
+ function() {
360
+ $( this ).find( ':input' ).removeAttr( 'name' );
361
+ }
362
+ );
363
+ $( '.stream-exclude-list tbody tr:not(.hidden) select.select2-select.connector_or_context', this ).each(
364
+ function() {
365
+ var parts = $( this ).val().split( '-' );
366
+ $( this ).siblings( '.connector' ).val( parts[0] );
367
+ $( this ).siblings( '.context' ).val( parts[1] );
368
+ $( this ).removeAttr( 'name' );
369
+ }
370
+ );
371
+ $( '.stream-exclude-list tbody tr:not(.hidden) select.select2-select.ip_address', this ).each(
372
+ function() {
373
+ var firstSelected = $( 'option:selected', this ).first();
374
+ $( 'option:selected:not(:first)', this ).each(
375
+ function() {
376
+ firstSelected.attr( 'value', firstSelected.attr( 'value' ) + ',' + $( this ).attr( 'value' ) );
377
+ $( this ).removeAttr( 'selected' );
378
+ }
379
+ );
380
+ }
381
+ );
382
+ }
383
+ );
384
 
385
+ $( '.stream-exclude-list' ).closest( 'td' ).prev( 'th' ).hide();
 
386
 
387
+ $( 'table.stream-exclude-list' ).on(
388
+ 'click', 'input.cb-select', function() {
389
+ recalculate_rules_selected();
390
+ }
391
+ );
392
 
393
+ function getActions( row, connector ) {
394
+ var trigger_action = $( '.select2-select.action', row ),
395
+ action_value = trigger_action.val();
396
 
397
+ trigger_action.empty();
398
+ trigger_action.prop( 'disabled', true );
 
399
 
400
+ var placeholder = $( '<option/>', {value: '', text: ''} );
401
+ trigger_action.append( placeholder );
402
 
403
+ var data = {
404
+ 'action' : 'get_actions',
405
+ 'connector' : connector
406
+ };
407
 
408
+ $.post(
409
+ window.ajaxurl, data, function( response ) {
410
+ var success = response.success,
411
+ actions = response.data;
412
+ if ( ! success ) {
413
+ return;
414
+ }
415
+ for ( var key in actions ) {
416
+ if ( actions.hasOwnProperty( key ) ) {
417
+ var value = actions[key];
418
+ var option = $( '<option/>', {value: key, text: value} );
419
+ trigger_action.append( option );
420
+ }
421
+ }
422
+ trigger_action.val( action_value );
423
+ trigger_action.prop( 'disabled', false );
424
+ $( document ).trigger( 'alert-actions-updated' );
425
+ }
426
+ );
427
+ }
428
 
429
+ function recalculate_rules_selected() {
430
+ var $selectedRows = $( 'table.stream-exclude-list tbody tr:not( .hidden ) input.cb-select:checked' ),
431
+ $deleteButton = $( '#exclude_rules_remove_rules' );
432
 
433
+ if ( 0 === $selectedRows.length ) {
434
+ $deleteButton.prop( 'disabled', true );
435
+ } else {
436
+ $deleteButton.prop( 'disabled', false );
437
+ }
 
438
  }
439
 
440
+ function recalculate_rules_found() {
441
+ var $allRows = $( 'table.stream-exclude-list tbody tr:not( .hidden )' ),
442
+ $noRulesFound = $( 'table.stream-exclude-list tbody tr.no-items' ),
443
+ $selectAll = $( '.check-column.manage-column input.cb-select' ),
444
+ $deleteButton = $( '#exclude_rules_remove_rules' );
445
+
446
+ if ( 0 === $allRows.length ) {
447
+ $noRulesFound.show();
448
+ $selectAll.prop( 'disabled', true );
449
+ $deleteButton.prop( 'disabled', true );
450
+ } else {
451
+ $noRulesFound.hide();
452
+ $selectAll.prop( 'disabled', false );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
453
  }
 
 
 
 
 
 
 
 
 
454
 
455
+ wp_stream_regenerate_alt_rows( $allRows );
 
 
 
456
  }
 
457
 
458
+ $( document ).ready(
459
+ function() {
460
+ recalculate_rules_found();
461
+ recalculate_rules_selected();
462
+ }
463
+ );
 
 
 
 
 
 
 
 
 
 
464
  }
465
+ );
 
 
 
 
 
ui/js/exclude.min.js ADDED
@@ -0,0 +1 @@
 
1
+ jQuery(function(e){function t(t,s){var n=e(".select2-select.action",t),l=n.val();n.empty(),n.prop("disabled",!0);var i=e("<option/>",{value:"",text:""});n.append(i);var c={action:"get_actions",connector:s};e.post(window.ajaxurl,c,function(t){var s=t.success,i=t.data;if(s){for(var c in i)if(i.hasOwnProperty(c)){var r=i[c],a=e("<option/>",{value:c,text:r});n.append(a)}n.val(l),n.prop("disabled",!1),e(document).trigger("alert-actions-updated")}})}function s(){var t=e("table.stream-exclude-list tbody tr:not( .hidden ) input.cb-select:checked"),s=e("#exclude_rules_remove_rules");0===t.length?s.prop("disabled",!0):s.prop("disabled",!1)}function n(){var t=e("table.stream-exclude-list tbody tr:not( .hidden )"),s=e("table.stream-exclude-list tbody tr.no-items"),n=e(".check-column.manage-column input.cb-select"),l=e("#exclude_rules_remove_rules");0===t.length?(s.show(),n.prop("disabled",!0),l.prop("disabled",!0)):(s.hide(),n.prop("disabled",!1)),wp_stream_regenerate_alt_rows(t)}var l=function(){var l;e(".stream-exclude-list tr:not(.hidden) select.select2-select.connector_or_context").each(function(s,n){e(n).select2({allowClear:!0,templateResult:function(t){return void 0===t.id?t.text:e(-1===t.id.indexOf("-")?'<span class="parent">'+t.text+"</span>":'<span class="child">'+t.text+"</span>")},matcher:function(t,s){var n=e.extend(!0,{},s);if(null==t.term||""===e.trim(t.term))return n;var l=t.term.toLowerCase();if(n.id=n.id.replace("blogs","sites"),n.id.toLowerCase().indexOf(l)>=0)return n;if(n.children){for(var i=n.children.length-1;i>=0;i--)-1===n.children[i].id.toLowerCase().indexOf(l)&&n.children.splice(i,1);if(n.children.length>0)return n}return null}}).on("change",function(){var s=e(this).closest("tr"),n=e(this).val();n&&0<n.indexOf("-")&&(n=n.split("-")[0]),t(s,n)})}),e(".stream-exclude-list tr:not(.hidden) select.select2-select.action").each(function(t,s){e(s).select2({allowClear:!0})}),e(".stream-exclude-list tr:not(.hidden) select.select2-select.author_or_role").each(function(t,s){(l=e(s)).select2({ajax:{type:"POST",url:ajaxurl,dataType:"json",quietMillis:500,data:function(e,t){return{find:e,limit:10,pager:t,action:"stream_get_users",nonce:l.data("nonce")}},processResults:function(t){var s={results:[{text:"",id:""},{text:"Roles",children:[]},{text:"Users",children:[]}]};if(!0!==t.success||void 0===t.data||!0!==t.data.status)return s;if(void 0===t.data.users||void 0===t.data.roles)return s;var n=[];return e.each(t.data.roles,function(e,t){n.push({id:e,text:t})}),s.results[1].children=n,s.results[2].children=t.data.users,s}},templateResult:function(t){var s=e("<div>").text(t.text);return void 0!==t.icon&&t.icon&&(s.prepend(e('<img src="'+t.icon+'" class="wp-stream-select2-icon">')),s.attr("title",t.tooltip)),void 0!==t.tooltip?s.attr("title",t.tooltip):void 0!==t.user_count&&s.attr("title",t.user_count),s},templateSelection:function(t){var s=e("<div>").text(t.text);return e.isNumeric(t.id)&&t.text.indexOf("icon-users")<0&&s.append(e('<i class="icon16 icon-users"></i>')),s},allowClear:!0,placeholder:l.data("placeholder")}).on("change",function(){var t=e(this).select2("data");e(this).data("selected-id",t.id),e(this).data("selected-text",t.text)})}),e(".stream-exclude-list tr:not(.hidden) select.select2-select.ip_address").each(function(t,s){var n=e(s),l="";n.select2({ajax:{type:"POST",url:ajaxurl,dataType:"json",quietMillis:500,data:function(e){return l=e.term,{find:e,limit:10,action:"stream_get_ips",nonce:n.data("nonce")}},processResults:function(t){var s={results:[]},n=[];return!0===t.success&&void 0!==t.data&&e.each(t.data,function(e,t){s.results.push({id:t,text:t})}),void 0===l?s:null===(n=l.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/))?s:(n.shift(),(n=e.grep(n,function(e){var t=parseInt(e,10);return t<=255&&t.toString()===e})).length>=4&&s.results.push({id:l,text:l}),s)}},allowClear:!1,multiple:!0,maximumSelectionSize:1,placeholder:n.data("placeholder"),tags:!0})}).on("change",function(){e(this).prev(".select2-container").find("input.select2-input").blur()}),e("ul.select2-choices, ul.select2-choices li, input.select2-input",".stream-exclude-list tr:not(.hidden) .ip_address").on("mousedown click focus",function(){var t=e(this).closest(".select2-container"),s=t.find("input.select2-input");if(t.select2("data").length>=1)return s.blur(),!1}),e(".stream-exclude-list tr:not(.hidden) .exclude_rules_remove_rule_row").on("click",function(){e(this).closest("tr").remove(),n(),s()})};l(),e(".stream-exclude-list tr:not(.hidden) select.select2-select.author_or_role").each(function(){var t=e("<option selected>"+e(this).data("selected-text")+"</option>").val(e(this).data("selected-id"));e(this).append(t).trigger("change")}),e(".stream-exclude-list tr:not(.hidden) select.select2-select.connector_or_context").each(function(){var t=[e(this).siblings(".connector").val(),e(this).siblings(".context").val()];""===t[1]&&t.splice(1,1),e(this).val(t.join("-")).trigger("change")}),e("#exclude_rules_new_rule").on("click",function(){var t=e("table.stream-exclude-list");e("tr:not(.hidden) select.select2-select",t).each(function(){e(this).select2("destroy")});var i=e("tr",t).last(),c=i.clone();c.removeAttr("class"),e(".stream-exclude-list tbody :input").off(),e(":input",c).off().val(""),i.after(c),l(),n(),s()}),e("#exclude_rules_remove_rules").on("click",function(){var t=e("table.stream-exclude-list"),l=e("tbody input.cb-select:checked",t).closest("tr");e("tbody tr",t).length-l.length>=2?l.remove():(e(":input",l).val(""),e(l).not(":first").remove(),e(".select2-select",l).select2("val","")),t.find("input.cb-select").prop("checked",!1),n(),s()}),e(".stream-exclude-list").closest("form").submit(function(){e(".stream-exclude-list tbody tr.hidden",this).each(function(){e(this).find(":input").removeAttr("name")}),e(".stream-exclude-list tbody tr:not(.hidden) select.select2-select.connector_or_context",this).each(function(){var t=e(this).val().split("-");e(this).siblings(".connector").val(t[0]),e(this).siblings(".context").val(t[1]),e(this).removeAttr("name")}),e(".stream-exclude-list tbody tr:not(.hidden) select.select2-select.ip_address",this).each(function(){var t=e("option:selected",this).first();e("option:selected:not(:first)",this).each(function(){t.attr("value",t.attr("value")+","+e(this).attr("value")),e(this).removeAttr("selected")})})}),e(".stream-exclude-list").closest("td").prev("th").hide(),e("table.stream-exclude-list").on("click","input.cb-select",function(){s()}),e(document).ready(function(){n(),s()})});
ui/js/global.js CHANGED
@@ -1,30 +1,36 @@
1
  /* globals wp_stream_global */
2
  /* exported wp_stream_regenerate_alt_rows */
3
- jQuery( function( $ ) {
4
-
5
- // List table actions, ignores filtering
6
- $( '.actions :submit:not([name="filter_action"])' ).on( 'click', function( e ) {
7
- if ( $( 'table.widefat tbody :checkbox:checked' ).length > wp_stream_global.bulk_actions.threshold ) {
8
- warning_message( e );
9
- }
10
- });
11
-
12
- // Post type empty trash
13
- $( '#delete_all' ).on( 'click', function( e ) {
14
- var trash_count = parseInt( $( 'ul.subsubsub li.trash .count' ).text().replace( /\D/g, '' ), 10 );
15
-
16
- if ( trash_count > wp_stream_global.bulk_actions.threshold ) {
17
- warning_message( e );
 
 
 
 
 
 
 
 
 
 
 
 
18
  }
19
- });
20
 
21
- function warning_message( e ) {
22
- if ( ! window.confirm( wp_stream_global.bulk_actions.i18n.confirm_action ) ) {
23
- e.preventDefault();
24
- }
25
  }
26
-
27
- });
28
 
29
  // Regenerate alternating row classes
30
  var wp_stream_regenerate_alt_rows = function( $rows ) {
@@ -34,7 +40,9 @@ var wp_stream_regenerate_alt_rows = function( $rows ) {
34
 
35
  $rows.removeClass( 'alternate' );
36
 
37
- $rows.each( function( index ) {
38
- jQuery( this ).addClass( index % 2 ? '' : 'alternate' );
39
- });
 
 
40
  };
1
  /* globals wp_stream_global */
2
  /* exported wp_stream_regenerate_alt_rows */
3
+ jQuery(
4
+ function( $ ) {
5
+
6
+ // List table actions, ignores filtering
7
+ $( '.actions :submit:not([name="filter_action"])' ).on(
8
+ 'click', function( e ) {
9
+ if ( $( 'table.widefat tbody :checkbox:checked' ).length > wp_stream_global.bulk_actions.threshold ) {
10
+ warning_message( e );
11
+ }
12
+ }
13
+ );
14
+
15
+ // Post type empty trash
16
+ $( '#delete_all' ).on(
17
+ 'click', function( e ) {
18
+ var trash_count = parseInt( $( 'ul.subsubsub li.trash .count' ).text().replace( /\D/g, '' ), 10 );
19
+
20
+ if ( trash_count > wp_stream_global.bulk_actions.threshold ) {
21
+ warning_message( e );
22
+ }
23
+ }
24
+ );
25
+
26
+ function warning_message( e ) {
27
+ if ( ! window.confirm( wp_stream_global.bulk_actions.i18n.confirm_action ) ) {
28
+ e.preventDefault();
29
+ }
30
  }
 
31
 
 
 
 
 
32
  }
33
+ );
 
34
 
35
  // Regenerate alternating row classes
36
  var wp_stream_regenerate_alt_rows = function( $rows ) {
40
 
41
  $rows.removeClass( 'alternate' );
42
 
43
+ $rows.each(
44
+ function( index ) {
45
+ jQuery( this ).addClass( index % 2 ? '' : 'alternate' );
46
+ }
47
+ );
48
  };
ui/js/global.min.js ADDED
@@ -0,0 +1 @@
 
1
+ jQuery(function(t){function e(t){window.confirm(wp_stream_global.bulk_actions.i18n.confirm_action)||t.preventDefault()}t('.actions :submit:not([name="filter_action"])').on("click",function(n){t("table.widefat tbody :checkbox:checked").length>wp_stream_global.bulk_actions.threshold&&e(n)}),t("#delete_all").on("click",function(n){parseInt(t("ul.subsubsub li.trash .count").text().replace(/\D/g,""),10)>wp_stream_global.bulk_actions.threshold&&e(n)})});var wp_stream_regenerate_alt_rows=function(t){if(!t.length)return!1;t.removeClass("alternate"),t.each(function(t){jQuery(this).addClass(t%2?"":"alternate")})};
ui/js/live-updates.js CHANGED
@@ -1,112 +1,125 @@
1
  /* globals wp_stream_live_updates, wp_stream_regenerate_alt_rows */
2
- jQuery( function( $ ) {
 
3
 
4
- $( document ).ready( function() {
 
5
 
6
- // Only run on wp_stream when page is 1 and the order is desc
7
- if ( 'toplevel_page_wp_stream' !== wp_stream_live_updates.current_screen || '1' !== wp_stream_live_updates.current_page || 'asc' === wp_stream_live_updates.current_order ) {
8
- return;
9
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
- // Do not run if there are filters in use
12
- if ( parseInt( wp_stream_live_updates.current_query_count, 10 ) > 1 ) {
13
- return;
14
- }
15
-
16
- var list_sel = '.toplevel_page_wp_stream #the-list';
17
-
18
- // Set initial beat to fast. WP is designed to slow this to 15 seconds after 2.5 minutes.
19
- wp.heartbeat.interval( 'fast' );
20
-
21
- $( document ).on( 'heartbeat-send.stream', function( e, data ) {
22
-
23
- data['wp-stream-heartbeat'] = 'live-update';
24
-
25
- var last_item = $( list_sel + ' tr:first .column-date time' ),
26
- last_time = 1;
27
-
28
- if ( 0 !== last_item.length ) {
29
- last_time = ( '' === last_item.attr( 'datetime' ) ) ? 1 : last_item.attr( 'datetime' );
30
- }
31
-
32
- data['wp-stream-heartbeat-last-time'] = last_time;
33
- data['wp-stream-heartbeat-query'] = wp_stream_live_updates.current_query;
34
- });
35
-
36
- // Listen for "heartbeat-tick" on $(document).
37
- $( document ).on( 'heartbeat-tick.stream', function( e, data ) {
38
-
39
- // If this no rows return then we kill the script
40
- if ( ! data['wp-stream-heartbeat'] || 0 === data['wp-stream-heartbeat'].length ) {
41
- return;
42
- }
43
-
44
- var show_on_screen = $( '#edit_stream_per_page' ).val(),
45
- $current_items = $( list_sel + ' tr' ),
46
- $new_items = $( data['wp-stream-heartbeat'] );
47
-
48
- // Remove all default classes and add class to highlight new rows
49
- $new_items.addClass( 'new-row' );
50
-
51
-
52
- // Check if first tr has the alternate class
53
- var has_class = ( $current_items.first().hasClass( 'alternate' ) );
54
-
55
- // Apply the good class to the list
56
- if ( 1 === $new_items.length && ! has_class ) {
57
- $new_items.addClass( 'alternate' );
58
- } else {
59
- var even_or_odd = ( 0 === $new_items.length % 2 && ! has_class ) ? 'even' : 'odd';
60
- // Add class to nth child because there is more than one element
61
- $new_items.filter( ':nth-child(' + even_or_odd + ')' ).addClass( 'alternate' );
62
- }
63
-
64
- // Add element to the dom
65
- $( list_sel ).prepend( $new_items );
66
-
67
- $( '.metabox-prefs input' ).each( function() {
68
- if ( true !== $( this ).prop( 'checked' ) ) {
69
- var label = $( this ).val();
70
- $( 'td.column-' + label ).hide();
71
  }
72
- });
73
-
74
- // Remove the number of element added to the end of the list table
75
- var slice_rows = show_on_screen - ( $new_items.length + $current_items.length );
76
-
77
- if ( slice_rows < 0 ) {
78
- $( list_sel + ' tr' ).slice( slice_rows ).remove();
79
- }
80
-
81
- // Remove the no items row
82
- $( list_sel + ' tr.no-items' ).remove();
83
-
84
- // Update pagination
85
- var total_items_i18n = data.total_items_i18n || '';
86
-
87
- if ( total_items_i18n ) {
88
- $( '.displaying-num' ).text( total_items_i18n );
89
- $( '.total-pages' ).text( data.total_pages_i18n );
90
- $( '.tablenav-pages' ).find( '.next-page, .last-page' ).toggleClass( 'disabled', data.total_pages === $( '.current-page' ).val() );
91
- $( '.tablenav-pages .last-page' ).attr( 'href', data.last_page_link );
92
- }
93
-
94
- // Allow others to hook in, ie: timeago
95
- $( list_sel ).parent().trigger( 'updated' );
96
-
97
- // Regenerate alternating row classes
98
- wp_stream_regenerate_alt_rows( $( list_sel + ' tr' ) );
99
-
100
- // Remove background after a certain amount of time
101
- setTimeout( function() {
102
- $( '.new-row' ).addClass( 'fadeout' );
103
- setTimeout( function() {
104
- $( list_sel + ' tr' ).removeClass( 'new-row fadeout' );
105
- }, 500 );
106
- }, 3000 );
107
-
108
- });
109
-
110
- });
111
 
112
- });
 
1
  /* globals wp_stream_live_updates, wp_stream_regenerate_alt_rows */
2
+ jQuery(
3
+ function( $ ) {
4
 
5
+ $( document ).ready(
6
+ function() {
7
 
8
+ // Only run on wp_stream when page is 1 and the order is desc
9
+ if ( 'toplevel_page_wp_stream' !== wp_stream_live_updates.current_screen || '1' !== wp_stream_live_updates.current_page || 'asc' === wp_stream_live_updates.current_order ) {
10
+ return;
11
+ }
12
+
13
+ // Do not run if there are filters in use
14
+ if ( parseInt( wp_stream_live_updates.current_query_count, 10 ) > 1 ) {
15
+ return;
16
+ }
17
+
18
+ var list_sel = '.toplevel_page_wp_stream #the-list';
19
+
20
+ // Set initial beat to fast. WP is designed to slow this to 15 seconds after 2.5 minutes.
21
+ wp.heartbeat.interval( 'fast' );
22
+
23
+ $( document ).on(
24
+ 'heartbeat-send.stream', function( e, data ) {
25
+
26
+ data['wp-stream-heartbeat'] = 'live-update';
27
+
28
+ var last_item = $( list_sel + ' tr:first .column-date time' ),
29
+ last_time = 1;
30
+
31
+ if ( 0 !== last_item.length ) {
32
+ last_time = ( '' === last_item.attr( 'datetime' ) ) ? 1 : last_item.attr( 'datetime' );
33
+ }
34
+
35
+ data['wp-stream-heartbeat-last-time'] = last_time;
36
+ data['wp-stream-heartbeat-query'] = wp_stream_live_updates.current_query;
37
+ }
38
+ );
39
+
40
+ // Listen for "heartbeat-tick" on $(document).
41
+ $( document ).on(
42
+ 'heartbeat-tick.stream', function( e, data ) {
43
+
44
+ // If this no rows return then we kill the script
45
+ if ( ! data['wp-stream-heartbeat'] || 0 === data['wp-stream-heartbeat'].length ) {
46
+ return;
47
+ }
48
+
49
+ var show_on_screen = $( '#edit_stream_per_page' ).val(),
50
+ $current_items = $( list_sel + ' tr' ),
51
+ $new_items = $( data['wp-stream-heartbeat'] );
52
+
53
+ // Remove all default classes and add class to highlight new rows
54
+ $new_items.addClass( 'new-row' );
55
+
56
+ // Check if first tr has the alternate class
57
+ var has_class = ( $current_items.first().hasClass( 'alternate' ) );
58
+
59
+ // Apply the good class to the list
60
+ if ( 1 === $new_items.length && ! has_class ) {
61
+ $new_items.addClass( 'alternate' );
62
+ } else {
63
+ var even_or_odd = ( 0 === $new_items.length % 2 && ! has_class ) ? 'even' : 'odd';
64
+ // Add class to nth child because there is more than one element
65
+ $new_items.filter( ':nth-child(' + even_or_odd + ')' ).addClass( 'alternate' );
66
+ }
67
+
68
+ // Add element to the dom
69
+ $( list_sel ).prepend( $new_items );
70
+
71
+ $( '.metabox-prefs input' ).each(
72
+ function() {
73
+ if ( true !== $( this ).prop( 'checked' ) ) {
74
+ var label = $( this ).val();
75
+ $( 'td.column-' + label ).hide();
76
+ }
77
+ }
78
+ );
79
+
80
+ // Remove the number of element added to the end of the list table
81
+ var slice_rows = show_on_screen - ( $new_items.length + $current_items.length );
82
+
83
+ if ( slice_rows < 0 ) {
84
+ $( list_sel + ' tr' ).slice( slice_rows ).remove();
85
+ }
86
+
87
+ // Remove the no items row
88
+ $( list_sel + ' tr.no-items' ).remove();
89
+
90
+ // Update pagination
91
+ var total_items_i18n = data.total_items_i18n || '';
92
+
93
+ if ( total_items_i18n ) {
94
+ $( '.displaying-num' ).text( total_items_i18n );
95
+ $( '.total-pages' ).text( data.total_pages_i18n );
96
+ $( '.tablenav-pages' ).find( '.next-page, .last-page' ).toggleClass( 'disabled', data.total_pages === $( '.current-page' ).val() );
97
+ $( '.tablenav-pages .last-page' ).attr( 'href', data.last_page_link );
98
+ }
99
+
100
+ // Allow others to hook in, ie: timeago
101
+ $( list_sel ).parent().trigger( 'updated' );
102
+
103
+ // Regenerate alternating row classes
104
+ wp_stream_regenerate_alt_rows( $( list_sel + ' tr' ) );
105
+
106
+ // Remove background after a certain amount of time
107
+ setTimeout(
108
+ function() {
109
+ $( '.new-row' ).addClass( 'fadeout' );
110
+ setTimeout(
111
+ function() {
112
+ $( list_sel + ' tr' ).removeClass( 'new-row fadeout' );
113
+ }, 500
114
+ );
115
+ }, 3000
116
+ );
117
+
118
+ }
119
+ );
120
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  }
122
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
+ }
125
+ );
ui/js/live-updates.min.js ADDED
@@ -0,0 +1 @@
 
1
+ jQuery(function(e){e(document).ready(function(){if("toplevel_page_wp_stream"===wp_stream_live_updates.current_screen&&"1"===wp_stream_live_updates.current_page&&"asc"!==wp_stream_live_updates.current_order&&!(parseInt(wp_stream_live_updates.current_query_count,10)>1)){var t=".toplevel_page_wp_stream #the-list";wp.heartbeat.interval("fast"),e(document).on("heartbeat-send.stream",function(a,r){r["wp-stream-heartbeat"]="live-update";var s=e(t+" tr:first .column-date time"),n=1;0!==s.length&&(n=""===s.attr("datetime")?1:s.attr("datetime")),r["wp-stream-heartbeat-last-time"]=n,r["wp-stream-heartbeat-query"]=wp_stream_live_updates.current_query}),e(document).on("heartbeat-tick.stream",function(a,r){if(r["wp-stream-heartbeat"]&&0!==r["wp-stream-heartbeat"].length){var s=e("#edit_stream_per_page").val(),n=e(t+" tr"),l=e(r["wp-stream-heartbeat"]);l.addClass("new-row");var p=n.first().hasClass("alternate");if(1!==l.length||p){var i=0!=l.length%2||p?"odd":"even";l.filter(":nth-child("+i+")").addClass("alternate")}else l.addClass("alternate");e(t).prepend(l),e(".metabox-prefs input").each(function(){if(!0!==e(this).prop("checked")){var t=e(this).val();e("td.column-"+t).hide()}});var _=s-(l.length+n.length);_<0&&e(t+" tr").slice(_).remove(),e(t+" tr.no-items").remove();var d=r.total_items_i18n||"";d&&(e(".displaying-num").text(d),e(".total-pages").text(r.total_pages_i18n),e(".tablenav-pages").find(".next-page, .last-page").toggleClass("disabled",r.total_pages===e(".current-page").val()),e(".tablenav-pages .last-page").attr("href",r.last_page_link)),e(t).parent().trigger("updated"),wp_stream_regenerate_alt_rows(e(t+" tr")),setTimeout(function(){e(".new-row").addClass("fadeout"),setTimeout(function(){e(t+" tr").removeClass("new-row fadeout")},500)},3e3)}})}})});
ui/js/settings.js CHANGED
@@ -1,75 +1,82 @@
1
- /* globals confirm, wp_stream, ajaxurl, wp_stream_regenerate_alt_rows */
2
- jQuery( function( $ ) {
3
- var network_affix;
4
- if ( 'wp_stream_network' === $( 'input[name="option_page"]' ).val() ) {
5
- network_affix = '_network_affix';
6
- } else {
7
- network_affix = '';
8
- }
9
- var keepRecordsIndefinitely = $( '#wp_stream' + network_affix + '\\[general_keep_records_indefinitely\\]' ),
10
- keepRecordsFor = $( '#wp_stream' + network_affix + '_general_records_ttl' ),
11
- keepRecordsForRow = keepRecordsFor.closest( 'tr' );
12
-
13
- function toggleKeepRecordsFor() {
14
- if ( keepRecordsIndefinitely.is( ':checked' ) ) {
15
- keepRecordsForRow.addClass( 'hidden' );
16
- keepRecordsFor.addClass( 'hidden' );
17
  } else {
18
- keepRecordsForRow.removeClass( 'hidden' );
19
- keepRecordsFor.removeClass( 'hidden' );
20
  }
21
- }
 
 
22
 
23
- keepRecordsIndefinitely.on( 'change', function() {
24
- toggleKeepRecordsFor();
25
- });
26
-
27
- toggleKeepRecordsFor();
28
- // Confirmation on some important actions
29
- $( '#wp_stream_general_reset_site_settings' ).click( function( e ) {
30
- if ( ! confirm( wp_stream.i18n.confirm_defaults ) ) {
31
- e.preventDefault();
32
  }
33
- });
34
 
35
- // Settings page tabs
36
- var $tabs = $( '.nav-tab-wrapper' ),
37
- $panels = $( '.nav-tab-content table.form-table' ),
38
- $activeTab = $tabs.find( '.nav-tab-active' ),
39
- defaultIndex = $activeTab.length > 0 ? $tabs.find( 'a' ).index( $activeTab ) : 0,
40
- hashIndex = window.location.hash.match( /^#(\d+)$/ ),
41
- currentHash = ( null !== hashIndex ? hashIndex[ 1 ] : defaultIndex ),
42
- syncFormAction = function( index ) {
43
- var $optionsForm = $( 'input[name="option_page"][value^="wp_stream"]' ).closest( 'form' );
44
- if ( $optionsForm.length === 0 ) {
45
- return;
46
  }
47
- var currentAction = $optionsForm.attr( 'action' );
48
 
49
- $optionsForm.prop( 'action', currentAction.replace( /(^[^#]*).*$/, '$1#' + index ) );
50
- };
 
 
 
 
 
 
 
51
 
52
- $tabs.on( 'click', 'a', function() {
53
- var index = $tabs.find( 'a' ).index( $( this ) ),
54
- hashIndex = window.location.hash.match( /^#(\d+)$/ );
 
 
 
 
 
 
 
 
 
 
55
 
56
- $panels.hide().eq( index ).show();
 
57
 
58
- $tabs
59
- .find( 'a' )
60
- .removeClass( 'nav-tab-active' )
61
- .filter( $( this ) )
62
- .addClass( 'nav-tab-active' );
63
 
64
- if ( '' === window.location.hash || null !== hashIndex ) {
65
- window.location.hash = index;
66
- }
67
 
68
- syncFormAction( index );
 
 
 
 
69
 
70
- return false;
71
- });
 
72
 
73
- $tabs.children().eq( currentHash ).trigger( 'click' );
74
 
75
- });
 
 
 
 
 
 
1
+ /* globals confirm, wp_stream */
2
+ jQuery(
3
+ function( $ ) {
4
+ var network_affix;
5
+ if ( 'wp_stream_network' === $( 'input[name="option_page"]' ).val() ) {
6
+ network_affix = '_network_affix';
 
 
 
 
 
 
 
 
 
 
7
  } else {
8
+ network_affix = '';
 
9
  }
10
+ var keepRecordsIndefinitely = $( '#wp_stream' + network_affix + '\\[general_keep_records_indefinitely\\]' ),
11
+ keepRecordsFor = $( '#wp_stream' + network_affix + '_general_records_ttl' ),
12
+ keepRecordsForRow = keepRecordsFor.closest( 'tr' );
13
 
14
+ function toggleKeepRecordsFor() {
15
+ if ( keepRecordsIndefinitely.is( ':checked' ) ) {
16
+ keepRecordsForRow.addClass( 'hidden' );
17
+ keepRecordsFor.addClass( 'hidden' );
18
+ } else {
19
+ keepRecordsForRow.removeClass( 'hidden' );
20
+ keepRecordsFor.removeClass( 'hidden' );
21
+ }
 
22
  }
 
23
 
24
+ keepRecordsIndefinitely.on(
25
+ 'change', function() {
26
+ toggleKeepRecordsFor();
 
 
 
 
 
 
 
 
27
  }
28
+ );
29
 
30
+ toggleKeepRecordsFor();
31
+ // Confirmation on some important actions
32
+ $( '#wp_stream_general_reset_site_settings' ).click(
33
+ function( e ) {
34
+ if ( ! confirm( wp_stream.i18n.confirm_defaults ) ) {
35
+ e.preventDefault();
36
+ }
37
+ }
38
+ );
39
 
40
+ // Settings page tabs
41
+ var $tabs = $( '.nav-tab-wrapper' ),
42
+ $panels = $( '.nav-tab-content table.form-table' ),
43
+ $activeTab = $tabs.find( '.nav-tab-active' ),
44
+ defaultIndex = $activeTab.length > 0 ? $tabs.find( 'a' ).index( $activeTab ) : 0,
45
+ hashIndex = window.location.hash.match( /^#(\d+)$/ ),
46
+ currentHash = ( null !== hashIndex ? hashIndex[ 1 ] : defaultIndex ),
47
+ syncFormAction = function( index ) {
48
+ var $optionsForm = $( 'input[name="option_page"][value^="wp_stream"]' ).closest( 'form' );
49
+ if ( $optionsForm.length === 0 ) {
50
+ return;
51
+ }
52
+ var currentAction = $optionsForm.attr( 'action' );
53
 
54
+ $optionsForm.prop( 'action', currentAction.replace( /(^[^#]*).*$/, '$1#' + index ) );
55
+ };
56
 
57
+ $tabs.on(
58
+ 'click', 'a', function() {
59
+ var index = $tabs.find( 'a' ).index( $( this ) ),
60
+ hashIndex = window.location.hash.match( /^#(\d+)$/ );
 
61
 
62
+ $panels.hide().eq( index ).show();
 
 
63
 
64
+ $tabs
65
+ .find( 'a' )
66
+ .removeClass( 'nav-tab-active' )
67
+ .filter( $( this ) )
68
+ .addClass( 'nav-tab-active' );
69
 
70
+ if ( '' === window.location.hash || null !== hashIndex ) {
71
+ window.location.hash = index;
72
+ }
73
 
74
+ syncFormAction( index );
75
 
76
+ return false;
77
+ }
78
+ );
79
+
80
+ $tabs.children().eq( currentHash ).trigger( 'click' );
81
+ }
82
+ );
ui/js/settings.min.js ADDED
@@ -0,0 +1 @@
 
1
+ jQuery(function(e){function a(){t.is(":checked")?(r.addClass("hidden"),i.addClass("hidden")):(r.removeClass("hidden"),i.removeClass("hidden"))}var n;n="wp_stream_network"===e('input[name="option_page"]').val()?"_network_affix":"";var t=e("#wp_stream"+n+"\\[general_keep_records_indefinitely\\]"),i=e("#wp_stream"+n+"_general_records_ttl"),r=i.closest("tr");t.on("change",function(){a()}),a(),e("#wp_stream_general_reset_site_settings").click(function(e){confirm(wp_stream.i18n.confirm_defaults)||e.preventDefault()});var o=e(".nav-tab-wrapper"),s=e(".nav-tab-content table.form-table"),c=o.find(".nav-tab-active"),l=c.length>0?o.find("a").index(c):0,d=window.location.hash.match(/^#(\d+)$/),h=null!==d?d[1]:l,_=function(a){var n=e('input[name="option_page"][value^="wp_stream"]').closest("form");if(0!==n.length){var t=n.attr("action");n.prop("action",t.replace(/(^[^#]*).*$/,"$1#"+a))}};o.on("click","a",function(){var a=o.find("a").index(e(this)),n=window.location.hash.match(/^#(\d+)$/);return s.hide().eq(a).show(),o.find("a").removeClass("nav-tab-active").filter(e(this)).addClass("nav-tab-active"),""!==window.location.hash&&null===n||(window.location.hash=a),_(a),!1}),o.children().eq(h).trigger("click")});
ui/js/wpseo-admin.js CHANGED
@@ -1,32 +1,38 @@
1
  // globals jQuery, $
2
- jQuery( function( $ ) {
 
3
 
4
- var highlight, input, tab;
5
 
6
- if ( window.location.hash.substr( 'stream-highlight-' ) ) {
7
- highlight = window.location.hash.replace( 'stream-highlight-', '' );
8
- input = $( ':input' + highlight );
9
 
10
- window.location.hash = '';
11
 
12
- if ( input.length ) {
13
- if ( $( '#wpseo-tabs' ).length ) {
14
- tab = input.parents( '.wpseotab' ).first().attr( 'id' );
15
- window.location.hash = '#top#' + tab;
16
- }
17
 
18
- jQuery( document ).ready( function() {
19
- setTimeout( function() {
20
- $( 'body,html' ).animate(
21
- { scrollTop: input.offset().top - 50 },
22
- 'slow',
23
- function() {
24
- input.animate( { backgroundColor: 'yellow' }, 'slow' );
25
- }
26
- );
27
- }, 500 );
28
- });
 
 
 
 
 
29
  }
30
- }
31
 
32
- });
 
1
  // globals jQuery, $
2
+ jQuery(
3
+ function( $ ) {
4
 
5
+ var highlight, input, tab;
6
 
7
+ if ( window.location.hash.substr( 'stream-highlight-' ) ) {
8
+ highlight = window.location.hash.replace( 'stream-highlight-', '' );
9
+ input = $( ':input' + highlight );
10
 
11
+ window.location.hash = '';
12
 
13
+ if ( input.length ) {
14
+ if ( $( '#wpseo-tabs' ).length ) {
15
+ tab = input.parents( '.wpseotab' ).first().attr( 'id' );
16
+ window.location.hash = '#top#' + tab;
17
+ }
18
 
19
+ jQuery( document ).ready(
20
+ function() {
21
+ setTimeout(
22
+ function() {
23
+ $( 'body,html' ).animate(
24
+ { scrollTop: input.offset().top - 50 },
25
+ 'slow',
26
+ function() {
27
+ input.animate( { backgroundColor: 'yellow' }, 'slow' );
28
+ }
29
+ );
30
+ }, 500
31
+ );
32
+ }
33
+ );
34
+ }
35
  }
 
36
 
37
+ }
38
+ );
ui/js/wpseo-admin.min.js ADDED
@@ -0,0 +1 @@
 
1
+ jQuery(function(o){var t,n,a;window.location.hash.substr("stream-highlight-")&&(t=window.location.hash.replace("stream-highlight-",""),n=o(":input"+t),window.location.hash="",n.length&&(o("#wpseo-tabs").length&&(a=n.parents(".wpseotab").first().attr("id"),window.location.hash="#top#"+a),jQuery(document).ready(function(){setTimeout(function(){o("body,html").animate({scrollTop:n.offset().top-50},"slow",function(){n.animate({backgroundColor:"yellow"},"slow")})},500)})))});
ui/lib/select2/css/select2.css CHANGED
File without changes
ui/lib/select2/css/select2.min.css CHANGED
File without changes
ui/lib/select2/js/i18n/ar.js CHANGED
File without changes
ui/lib/select2/js/i18n/az.js CHANGED
File without changes
ui/lib/select2/js/i18n/bg.js CHANGED
File without changes
ui/lib/select2/js/i18n/ca.js CHANGED
File without changes
ui/lib/select2/js/i18n/cs.js CHANGED
File without changes
ui/lib/select2/js/i18n/da.js CHANGED
File without changes
ui/lib/select2/js/i18n/de.js CHANGED
File without changes
ui/lib/select2/js/i18n/el.js CHANGED
File without changes
ui/lib/select2/js/i18n/en.js CHANGED
File without changes
ui/lib/select2/js/i18n/es.js CHANGED
File without changes
ui/lib/select2/js/i18n/et.js CHANGED
File without changes
ui/lib/select2/js/i18n/eu.js CHANGED
File without changes
ui/lib/select2/js/i18n/fa.js CHANGED
File without changes
ui/lib/select2/js/i18n/fi.js CHANGED
File without changes
ui/lib/select2/js/i18n/fr.js CHANGED
File without changes
ui/lib/select2/js/i18n/gl.js CHANGED
File without changes
ui/lib/select2/js/i18n/he.js CHANGED
File without changes
ui/lib/select2/js/i18n/hi.js CHANGED
File without changes
ui/lib/select2/js/i18n/hr.js CHANGED
File without changes
ui/lib/select2/js/i18n/hu.js CHANGED
File without changes
ui/lib/select2/js/i18n/id.js CHANGED
File without changes
ui/lib/select2/js/i18n/is.js CHANGED
File without changes
ui/lib/select2/js/i18n/it.js CHANGED
File without changes
ui/lib/select2/js/i18n/ja.js CHANGED
File without changes
ui/lib/select2/js/i18n/km.js CHANGED
File without changes
ui/lib/select2/js/i18n/ko.js CHANGED
File without changes
ui/lib/select2/js/i18n/lt.js CHANGED
File without changes
ui/lib/select2/js/i18n/lv.js CHANGED
File without changes
ui/lib/select2/js/i18n/mk.js CHANGED
File without changes
ui/lib/select2/js/i18n/ms.js CHANGED
File without changes
ui/lib/select2/js/i18n/nb.js CHANGED
File without changes
ui/lib/select2/js/i18n/nl.js CHANGED
File without changes
ui/lib/select2/js/i18n/pl.js CHANGED
File without changes
ui/lib/select2/js/i18n/pt-BR.js CHANGED
File without changes
ui/lib/select2/js/i18n/pt.js CHANGED
File without changes
ui/lib/select2/js/i18n/ro.js CHANGED
File without changes
ui/lib/select2/js/i18n/ru.js CHANGED
File without changes
ui/lib/select2/js/i18n/sk.js CHANGED
File without changes
ui/lib/select2/js/i18n/sr-Cyrl.js CHANGED
File without changes
ui/lib/select2/js/i18n/sr.js CHANGED
File without changes
ui/lib/select2/js/i18n/sv.js CHANGED
File without changes
ui/lib/select2/js/i18n/th.js CHANGED
File without changes
ui/lib/select2/js/i18n/tr.js CHANGED
File without changes
ui/lib/select2/js/i18n/uk.js CHANGED
File without changes
ui/lib/select2/js/i18n/vi.js CHANGED
File without changes
ui/lib/select2/js/i18n/zh-CN.js CHANGED
File without changes
ui/lib/select2/js/i18n/zh-TW.js CHANGED
File without changes
ui/lib/select2/js/select2.full.js CHANGED
File without changes
ui/lib/select2/js/select2.full.min.js CHANGED
File without changes
ui/lib/select2/js/select2.js CHANGED
File without changes
ui/lib/select2/js/select2.min.js CHANGED
File without changes
ui/lib/timeago/jquery.timeago.js CHANGED
File without changes
ui/lib/timeago/locales/README.md CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.ar.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.bg.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.bs.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.ca.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.cs.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.cy.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.da.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.de.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.el.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.en-short.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.en.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.es-short.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.es.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.et.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.fa.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.fi.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.fr-short.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.fr.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.gl.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.he.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.hr.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.hu.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.hy.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.id.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.is.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.it.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.ja.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.jv.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.ko.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.lt.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.mk.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.nl.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.no.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.pl.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.pt-br-short.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.pt-br.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.pt.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.ro.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.rs.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.ru.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.sk.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.sl.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.sv.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.th.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.tr.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.uk.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.uz.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.vi.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.zh-CN.js CHANGED
File without changes
ui/lib/timeago/locales/jquery.timeago.zh-TW.js CHANGED
File without changes
ui/stream-icons/font/stream.eot CHANGED
File without changes
ui/stream-icons/font/stream.svg CHANGED
File without changes
ui/stream-icons/font/stream.ttf CHANGED
File without changes
ui/stream-icons/font/stream.woff CHANGED
File without changes
ui/stream-icons/style.css CHANGED
File without changes