Stream - Version 3.1

Version Description

  • October 31, 2016 =

  • New: Stream Alerts is here! Get notified when something happens in your WP-Admin, so that you don't miss a thing. (#844)

  • Tweak: Better support for the latest version of Yoast SEO (#838)

  • Tweak: Better support for the latest version of WooCommerce (#851#864)

  • Tweak: Better taxonomy labelling (#859)

  • Fix: Fatal error caused by conflict with Yoast SEO (#879)

  • Fix: Activating Stream through WP CLI now works (#880)

  • Fix: Custom roles track properly (#836)

Props @chacha, @lukecarbis, @johnbillion, @rheinardkorf, @frozzare, @johnregan3, @jacobschweitzer, @wrongware

Download this release

Release Info

Developer lukecarbis
Plugin Icon 128x128 Stream
Version 3.1
Comparing to
See all releases

Code changes from version 3.0.7 to 3.1

Files changed (40) hide show
  1. alerts/class-alert-trigger-action.php +176 -0
  2. alerts/class-alert-trigger-author.php +155 -0
  3. alerts/class-alert-trigger-context.php +188 -0
  4. alerts/class-alert-type-die.php +44 -0
  5. alerts/class-alert-type-email.php +181 -0
  6. alerts/class-alert-type-highlight.php +301 -0
  7. alerts/class-alert-type-ifttt.php +307 -0
  8. alerts/class-alert-type-menu-alert.php +173 -0
  9. alerts/class-alert-type-none.php +41 -0
  10. alerts/js/alert-type-highlight.js +77 -0
  11. classes/class-admin.php +14 -1
  12. classes/class-alert-trigger.php +95 -0
  13. classes/class-alert-type.php +78 -0
  14. classes/class-alert.php +337 -0
  15. classes/class-alerts-list.php +374 -0
  16. classes/class-alerts.php +777 -0
  17. classes/class-connectors.php +7 -7
  18. classes/class-form-generator.php +13 -11
  19. classes/class-install.php +6 -6
  20. classes/class-list-table.php +17 -5
  21. classes/class-live-update.php +1 -1
  22. classes/class-network.php +4 -2
  23. classes/class-plugin.php +18 -5
  24. classes/class-preview-list-table.php +62 -0
  25. classes/class-settings.php +3 -3
  26. connectors/class-connector-buddypress.php +2 -2
  27. connectors/class-connector-gravityforms.php +1 -1
  28. connectors/class-connector-user-switching.php +1 -1
  29. connectors/class-connector-woocommerce.php +16 -8
  30. connectors/class-connector-wordpress-seo.php +32 -7
  31. exporters/class-exporter-csv.php +1 -1
  32. includes/db-updates.php +17 -0
  33. readme.txt +14 -2
  34. stream.php +1 -1
  35. ui/css/admin.css +45 -29
  36. ui/css/alerts-list.css +142 -0
  37. ui/js/alerts-list.js +11 -0
  38. ui/js/alerts.js +272 -0
  39. ui/js/live-updates.js +2 -1
  40. ui/js/settings.js +8 -4
alerts/class-alert-trigger-action.php ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Trigger on an Action.
4
+ *
5
+ * @package WP_Stream
6
+ */
7
+
8
+ namespace WP_Stream;
9
+
10
+ /**
11
+ * Class Alert_Trigger_Action
12
+ *
13
+ * @package WP_Stream
14
+ */
15
+ class Alert_Trigger_Action extends Alert_Trigger {
16
+
17
+ /**
18
+ * Unique identifier
19
+ *
20
+ * @var string
21
+ */
22
+ public $slug = 'action';
23
+
24
+ /**
25
+ * Meta key used in forms.
26
+ *
27
+ * @var string
28
+ */
29
+ public $meta_key = 'trigger_action';
30
+
31
+ /**
32
+ * Field key used in database
33
+ *
34
+ * @var string
35
+ */
36
+ public $field_key = 'wp_stream_trigger_action';
37
+
38
+ /**
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
+
55
+ /**
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() ) {
64
+ $value = '';
65
+ if ( is_object( $alert ) && ! empty( $alert->alert_meta['trigger_action'] ) ) {
66
+ $value = $alert->alert_meta['trigger_action'];
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
+ );
78
+ $form->add_field( 'select2', $args );
79
+ }
80
+
81
+ /**
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 ) {
89
+ $input = wp_stream_filter_input( INPUT_POST, $this->field_key );
90
+ if ( array_key_exists( $input, $this->get_values( true ) ) ) {
91
+ $alert->alert_meta['trigger_action'] = $input;
92
+ } else {
93
+ $alert->alert_meta['trigger_action'] = '';
94
+ }
95
+ }
96
+
97
+ /**
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
+
115
+ /**
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 ) {
123
+ $return_labels = array();
124
+
125
+ if ( isset( $this->plugin->connectors->term_labels[ 'stream_' . $column ] ) ) {
126
+ if ( 'context' === $column && isset( $this->plugin->connectors->term_labels['stream_connector'] ) ) {
127
+ $connectors = $this->plugin->connectors->term_labels['stream_connector'];
128
+ $contexts = $this->plugin->connectors->term_labels['stream_context'];
129
+
130
+ foreach ( $connectors as $connector => $connector_label ) {
131
+ $return_labels[ $connector ]['label'] = $connector_label;
132
+ foreach ( $contexts as $context => $context_label ) {
133
+ if ( isset( $this->plugin->connectors->contexts[ $connector ] ) && array_key_exists( $context, $this->plugin->connectors->contexts[ $connector ] ) ) {
134
+ $return_labels[ $connector ]['children'][ $context ] = $context_label;
135
+ }
136
+ }
137
+ }
138
+ } else {
139
+ $return_labels = $this->plugin->connectors->term_labels[ 'stream_' . $column ];
140
+ }
141
+
142
+ ksort( $return_labels );
143
+ }
144
+ return $return_labels;
145
+ }
146
+
147
+ /**
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 ) {
159
+ if ( empty( $action ) ) {
160
+ $action = __( 'performed any action on', 'stream' );
161
+ }
162
+ } else {
163
+ if ( empty( $action ) ) {
164
+ $action = __( 'Any Action', 'stream' );
165
+ } else {
166
+ $actions = $this->plugin->connectors->term_labels['stream_action'];
167
+ if ( ! empty( $actions[ $action ] ) ) {
168
+ $action = $actions[ $action ];
169
+ }
170
+ $action = ucfirst( $action );
171
+ }
172
+ }
173
+
174
+ return ucfirst( $action );
175
+ }
176
+ }
alerts/class-alert-trigger-author.php ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Trigger for an Author.
4
+ *
5
+ * @package WP_Stream
6
+ */
7
+ namespace WP_Stream;
8
+
9
+ /**
10
+ * Class Alert_Trigger_Author
11
+ *
12
+ * @package WP_Stream
13
+ */
14
+ class Alert_Trigger_Author extends Alert_Trigger {
15
+
16
+ /**
17
+ * Unique identifier
18
+ *
19
+ * @var string
20
+ */
21
+ public $slug = 'author';
22
+
23
+ /**
24
+ * Field key used in database
25
+ *
26
+ * @var string
27
+ */
28
+ public $field_key = 'wp_stream_trigger_author';
29
+
30
+ /**
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
+
47
+ /**
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() ) {
56
+ $value = '';
57
+ if ( is_object( $alert ) && ! empty( $alert->alert_meta['trigger_author'] ) ) {
58
+ $value = $alert->alert_meta['trigger_author'];
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
+ );
70
+ $form->add_field( 'select2', $args );
71
+ }
72
+
73
+ /**
74
+ * Generate array of possible action values
75
+ *
76
+ * @return array
77
+ */
78
+ public function get_values() {
79
+ $all_records = array();
80
+
81
+ $user_count = count_users();
82
+ $total_users = $user_count['total_users'];
83
+
84
+ if ( $total_users > $this->plugin->admin->preload_users_max ) {
85
+ return array();
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(
110
+ 'id' => $user->id,
111
+ 'text' => $user->get_display_name(),
112
+ );
113
+ }
114
+ return $all_records;
115
+ }
116
+
117
+ /**
118
+ * Validate and save Alert object
119
+ *
120
+ * @see Alert_Trigger::save_fields().
121
+ * @param Alert $alert The Alert being worked on.
122
+ * @return void
123
+ */
124
+ public function save_fields( $alert ) {
125
+ $input = wp_stream_filter_input( INPUT_POST, $this->field_key );
126
+ if ( array_key_exists( $input, $this->get_values( $alert ) ) ) {
127
+ $alert->alert_meta['trigger_author'] = $input;
128
+ } else {
129
+ $alert->alert_meta['trigger_author'] = '';
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Returns the trigger's value for the given alert.
135
+ *
136
+ * @see Alert_Trigger::get_display_value().
137
+ * @param string $context The location this data will be displayed in.
138
+ * @param Alert $alert Alert being processed.
139
+ * @return string
140
+ */
141
+ function get_display_value( $context = 'normal', $alert ) {
142
+ $author = ( ! empty( $alert->alert_meta['trigger_author'] ) ) ? $alert->alert_meta['trigger_author'] : null;
143
+ if ( empty( $author ) ) {
144
+ $author = __( 'Any User', 'stream' );
145
+ } elseif ( is_numeric( $author ) ) {
146
+ $author_data = get_userdata( $author );
147
+ if ( $author_data ) {
148
+ $author = $author_data->display_name;
149
+ } else {
150
+ $author = __( 'Unknown User', 'stream' );
151
+ }
152
+ }
153
+ return ucfirst( $author );
154
+ }
155
+ }
alerts/class-alert-trigger-context.php ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Trigger on Context.
4
+ *
5
+ * @package WP_Stream
6
+ */
7
+ namespace WP_Stream;
8
+
9
+ /**
10
+ * Class Alert_Trigger_Context
11
+ *
12
+ * @package WP_Stream
13
+ */
14
+ class Alert_Trigger_Context extends Alert_Trigger {
15
+
16
+ /**
17
+ * Unique identifier
18
+ *
19
+ * @var string
20
+ */
21
+ public $slug = 'context';
22
+
23
+ /**
24
+ * Field key used in database
25
+ *
26
+ * @var string
27
+ */
28
+ public $field_key = 'wp_stream_trigger_context';
29
+
30
+ /**
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_connector'] ) && $recordarr['connector'] !== $alert->alert_meta['trigger_connector'] ) {
42
+ return false;
43
+ }
44
+ if ( ! empty( $alert->alert_meta['trigger_context'] ) && $recordarr['context'] !== $alert->alert_meta['trigger_context'] ) {
45
+ return false;
46
+ }
47
+ return $success;
48
+ }
49
+
50
+ /**
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() ) {
59
+ $connector = '';
60
+ if ( is_object( $alert ) && ! empty( $alert->alert_meta['trigger_connector'] ) ) {
61
+ $connector = $alert->alert_meta['trigger_connector'];
62
+ }
63
+
64
+ $context = '';
65
+ if ( is_object( $alert ) && ! empty( $alert->alert_meta['trigger_context'] ) ) {
66
+ $context = $alert->alert_meta['trigger_context'];
67
+ }
68
+
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
+ /**
107
+ * Generate array of possible action values
108
+ *
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( 'id' => $context_id . '-' . $child_id, 'text' => $child_value, 'parent' => $context_id );
119
+ }
120
+ }
121
+ if ( isset( $context_data['label'] ) ) {
122
+ $context_values[] = array( 'id' => $context_id, 'text' => $context_data['label'], 'children' => $child_values );
123
+ }
124
+ } else {
125
+ $context_values[] = array( 'id' => $context_id, 'text' => $context_data );
126
+ }
127
+ }
128
+ return $context_values;
129
+ }
130
+
131
+ /**
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 ) {
139
+ $return_labels = array();
140
+
141
+ if ( isset( $this->plugin->connectors->term_labels[ 'stream_' . $column ] ) ) {
142
+ if ( 'context' === $column && isset( $this->plugin->connectors->term_labels['stream_connector'] ) ) {
143
+ $connectors = $this->plugin->connectors->term_labels['stream_connector'];
144
+ $contexts = $this->plugin->connectors->term_labels['stream_context'];
145
+
146
+ foreach ( $connectors as $connector => $connector_label ) {
147
+ $return_labels[ $connector ]['label'] = $connector_label;
148
+ foreach ( $contexts as $context => $context_label ) {
149
+ if ( isset( $this->plugin->connectors->contexts[ $connector ] ) && array_key_exists( $context, $this->plugin->connectors->contexts[ $connector ] ) ) {
150
+ $return_labels[ $connector ]['children'][ $context ] = $context_label;
151
+ }
152
+ }
153
+ }
154
+ } else {
155
+ $return_labels = $this->plugin->connectors->term_labels[ 'stream_' . $column ];
156
+ }
157
+
158
+ ksort( $return_labels );
159
+ }
160
+ return $return_labels;
161
+ }
162
+
163
+ /**
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' );
176
+ } else {
177
+ $term_labels = $this->get_terms_labels( 'context' );
178
+ if ( ! empty( $term_labels[ $connector ]['children'][ $context ] ) ) {
179
+ $context = $term_labels[ $connector ]['children'][ $context ];
180
+ } else {
181
+ if ( ! empty( $term_labels[ $connector ]['label'] ) ) {
182
+ $context = $term_labels[ $connector ]['label'];
183
+ }
184
+ }
185
+ }
186
+ return ucfirst( $context );
187
+ }
188
+ }
alerts/class-alert-type-die.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Used for debugging.
4
+ *
5
+ * @package WP_Stream
6
+ */
7
+
8
+ namespace WP_Stream;
9
+
10
+ /**
11
+ * Class Alert_Type_Die
12
+ *
13
+ * @package WP_Stream
14
+ */
15
+ class Alert_Type_Die extends Alert_Type {
16
+ /**
17
+ * Alert type name
18
+ *
19
+ * @var string
20
+ */
21
+ public $name = 'Die Notifier';
22
+
23
+ /**
24
+ * Alert type slug
25
+ *
26
+ * @var string
27
+ */
28
+ public $slug = 'die';
29
+
30
+ /**
31
+ * Triggers a script exit when an alert is triggered. Debugging use only.
32
+ *
33
+ * @param int $record_id Record that triggered notification.
34
+ * @param array $recordarr Record details.
35
+ * @param array $options Alert options.
36
+ * @return void
37
+ */
38
+ public function alert( $record_id, $recordarr, $options ) {
39
+ echo '<pre>';
40
+ print_r( $recordarr ); // @codingStandardsIgnoreLine debug not loaded in production
41
+ echo '</pre>';
42
+ die( 'You have been notified!' );
43
+ }
44
+ }
alerts/class-alert-type-email.php ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Email Alerts.
4
+ *
5
+ * Idea for future expansion: allow customization of email.
6
+ *
7
+ * @package WP_Stream
8
+ */
9
+
10
+ namespace WP_Stream;
11
+
12
+ /**
13
+ * Class Alert_Type_Email
14
+ *
15
+ * @package WP_Stream
16
+ */
17
+ class Alert_Type_Email extends Alert_Type {
18
+
19
+ /**
20
+ * Alert type name
21
+ *
22
+ * @var string
23
+ */
24
+ public $name = 'Email';
25
+
26
+ /**
27
+ * Alert type slug
28
+ *
29
+ * @var string
30
+ */
31
+ public $slug = 'email';
32
+
33
+ /**
34
+ * Class Constructor
35
+ *
36
+ * @param Plugin $plugin Plugin object.
37
+ * @return void
38
+ */
39
+ public function __construct( $plugin ) {
40
+ parent::__construct( $plugin );
41
+ $this->plugin = $plugin;
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
+ /**
49
+ * Sends an email to the given recipient.
50
+ *
51
+ * @param int $record_id Record that triggered notification.
52
+ * @param array $recordarr Record details.
53
+ * @param Alert $alert Alert options.
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 );
91
+
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
+ }
105
+
106
+ /**
107
+ * Displays a settings form for the alert type
108
+ *
109
+ * @param Alert $alert Alert object for the currently displayed alert.
110
+ * @return void
111
+ */
112
+ public function display_fields( $alert ) {
113
+ $alert_meta = array();
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
+
143
+ /**
144
+ * Validates and saves form settings for later use.
145
+ *
146
+ * @param Alert $alert Alert object for the currently displayed alert.
147
+ * @return void
148
+ */
149
+ public function save_fields( $alert ) {
150
+ check_admin_referer( 'save_alert', 'wp_stream_alerts_nonce' );
151
+
152
+ if ( isset( $_POST['wp_stream_email_recipient'] ) ) {
153
+ $alert->alert_meta['email_recipient'] = sanitize_text_field( wp_unslash( $_POST['wp_stream_email_recipient'] ) );
154
+ }
155
+
156
+ if ( isset( $_POST['wp_stream_email_subject'] ) ) {
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
+ *
163
+ * @param array $alert_meta The metadata to be inserted for this alert.
164
+ * @param string $alert_type The type of alert being added or updated.
165
+ *
166
+ * @return mixed
167
+ */
168
+ public function add_alert_meta( $alert_meta, $alert_type ) {
169
+ if ( $this->slug === $alert_type ) {
170
+ $email_recipient = wp_stream_filter_input( INPUT_POST, 'wp_stream_email_recipient' );
171
+ if ( ! empty( $email_recipient ) ) {
172
+ $alert_meta['email_recipient'] = $email_recipient;
173
+ }
174
+ $email_subject = wp_stream_filter_input( INPUT_POST, 'wp_stream_email_subject' );
175
+ if ( ! empty( $email_subject ) ) {
176
+ $alert_meta['email_subject'] = $email_subject;
177
+ }
178
+ }
179
+ return $alert_meta;
180
+ }
181
+ }
alerts/class-alert-type-highlight.php ADDED
@@ -0,0 +1,301 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Highlight Alert type.
4
+ *
5
+ * @package WP_Stream
6
+ */
7
+
8
+ namespace WP_Stream;
9
+
10
+ /**
11
+ * Class Alert_Type_Highlight
12
+ *
13
+ * @package WP_Stream
14
+ */
15
+ class Alert_Type_Highlight extends Alert_Type {
16
+ /**
17
+ * Main JS file script handle.
18
+ */
19
+ const SCRIPT_HANDLE = 'wp-stream-alert-highlight-js';
20
+
21
+ /**
22
+ * Remove Highlight Ajax action label.
23
+ */
24
+ const REMOVE_ACTION = 'stream_remove_highlight';
25
+
26
+ /**
27
+ * Remove Action nonce name.
28
+ */
29
+ const REMOVE_ACTION_NONCE = 'stream-remove-highlight';
30
+
31
+ /**
32
+ * Alert type name
33
+ *
34
+ * @var string
35
+ */
36
+ public $name = 'Highlight';
37
+
38
+ /**
39
+ * Alert type slug
40
+ *
41
+ * @var string
42
+ */
43
+ public $slug = 'highlight';
44
+
45
+ /**
46
+ * The single Alert ID.
47
+ *
48
+ * @var int|string
49
+ */
50
+ public $single_alert_id;
51
+
52
+ /**
53
+ * The Plugin
54
+ *
55
+ * @var Plugin
56
+ */
57
+ public $plugin;
58
+
59
+ /**
60
+ * Class Constructor
61
+ *
62
+ * @param Plugin $plugin Plugin object.
63
+ * @return void
64
+ */
65
+ public function __construct( $plugin ) {
66
+ parent::__construct( $plugin );
67
+ $this->plugin = $plugin;
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
+ /**
85
+ * Record that the Alert was triggered by a Record.
86
+ *
87
+ * In self::post_class() this value is checked so we can determine
88
+ * if the class should be added to the Record's display.
89
+ *
90
+ * @param int|string $record_id Record that triggered alert.
91
+ * @param array $recordarr Record details.
92
+ * @param object $alert Alert options.
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
+
103
+ /**
104
+ * Displays a settings form for the alert type
105
+ *
106
+ * @param Alert $alert Alert object for the currently displayed alert.
107
+ * @return void
108
+ */
109
+ public function display_fields( $alert ) {
110
+ $alert_meta = array();
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
+ *
133
+ * @return array List of highlight color options.
134
+ */
135
+ public function get_highlight_options() {
136
+ return array(
137
+ 'yellow' => __( 'Yellow', 'stream' ),
138
+ 'red' => __( 'Red', 'stream' ),
139
+ 'green' => __( 'Green', 'stream' ),
140
+ 'blue' => __( 'Blue', 'stream' ),
141
+ );
142
+ }
143
+
144
+ /**
145
+ * Validates and saves form settings for later use.
146
+ *
147
+ * @param Alert $alert Alert object for the currently displayed alert.
148
+ * @return void
149
+ */
150
+ public function save_fields( $alert ) {
151
+ check_admin_referer( 'save_alert', 'wp_stream_alerts_nonce' );
152
+
153
+ if ( empty( $_POST['wp_stream_highlight_color'] ) ) {
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;
161
+ }
162
+
163
+ }
164
+
165
+ /**
166
+ * Apply highlight to records
167
+ *
168
+ * @param array $classes List of classes being applied to the post.
169
+ * @param object $record Record data.
170
+ * @return array New list of classes.
171
+ */
172
+ public function post_class( $classes, $record ) {
173
+ if ( ! empty( $record->meta['wp_stream_alerts_triggered']['highlight']['highlight_color'] ) ) {
174
+ $color = $record->meta['wp_stream_alerts_triggered']['highlight']['highlight_color'];
175
+ }
176
+
177
+ if ( empty( $color ) || ! is_string( $color ) ) {
178
+ return $classes;
179
+ }
180
+ $classes[] = 'alert-highlight highlight-' . esc_attr( $color ) . ' record-id-' . $record->ID;
181
+
182
+ return $classes;
183
+ }
184
+
185
+ /**
186
+ * Maybe add the "Remove Highlight" action link.
187
+ *
188
+ * This will appear on highlighted items on
189
+ * the Record List page.
190
+ *
191
+ * This is set to run for all Connectors
192
+ * in self::__construct().
193
+ *
194
+ * @filter wp_stream_action_links_{ connector }
195
+ *
196
+ * @param array $actions Action links.
197
+ * @param object $record A record object.
198
+ *
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' ) ] = '#';
206
+ }
207
+
208
+ return $actions;
209
+ }
210
+
211
+ /**
212
+ * Ajax action to remove highlight.
213
+ *
214
+ * This is fired from the "Remove Highlight"
215
+ * action links on the Records list page.
216
+ *
217
+ * First, we validate and sanitize.
218
+ *
219
+ * Then, we get the Record meta and remove
220
+ * the "highlight" array from the
221
+ * "Alerts triggered" meta field.
222
+ *
223
+ * Finally, the meta field is updated.
224
+ *
225
+ * Note: this removes ALL highlights
226
+ * that were triggered by this record, not just
227
+ * those triggered on specific Alert (post) IDs.
228
+ *
229
+ * @action wp_ajax_stream_remove_highlight
230
+ */
231
+ public function ajax_remove_highlight() {
232
+ check_ajax_referer( self::REMOVE_ACTION_NONCE, 'security' );
233
+
234
+ $failure_message = __( 'Removing Highlight Failed', 'stream' );
235
+
236
+ if ( empty( $_POST['recordId'] ) ) {
237
+ wp_send_json_error( $failure_message );
238
+ }
239
+
240
+ $record_id = sanitize_text_field( wp_unslash( $_POST['recordId'] ) );
241
+
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 ] );
251
+ }
252
+ $record->update_meta( Alerts::ALERTS_TRIGGERED_META_KEY, $alerts_triggered );
253
+ wp_send_json_success();
254
+ }
255
+
256
+ /**
257
+ * Enqueue Highlight-specific scripts.
258
+ *
259
+ * @param string $page WP admin page.
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(
272
+ self::SCRIPT_HANDLE,
273
+ 'data',
274
+ sprintf( 'var _streamAlertTypeHighlightExports = %s;', wp_json_encode( $exports ) )
275
+ );
276
+
277
+ wp_add_inline_script( self::SCRIPT_HANDLE, 'streamAlertTypeHighlight.init();', 'after' );
278
+ wp_enqueue_script( self::SCRIPT_HANDLE );
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Add alert meta if this is a highlight alert
284
+ *
285
+ * @param array $alert_meta The metadata to be inserted for this alert.
286
+ * @param string $alert_type The type of alert being added or updated.
287
+ *
288
+ * @return mixed
289
+ */
290
+ public function add_alert_meta( $alert_meta, $alert_type ) {
291
+ if ( $this->slug === $alert_type ) {
292
+ $color = wp_stream_filter_input( INPUT_POST, 'wp_stream_highlight_color' );
293
+ if ( empty( $color ) ) {
294
+ $alert_meta['color'] = 'yellow';
295
+ } else {
296
+ $alert_meta['color'] = $color;
297
+ }
298
+ }
299
+ return $alert_meta;
300
+ }
301
+ }
alerts/class-alert-type-ifttt.php ADDED
@@ -0,0 +1,307 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * If This Then That (IFTTT) Alert type.
4
+ *
5
+ * A user must create a IFTTT Maker account,
6
+ * and get their Maker Key, found here:
7
+ *
8
+ * @link https://ifttt.com/maker
9
+ * This will be used in the Stream Alert form.
10
+ *
11
+ * Next, they create a new recipe, using the
12
+ * "Maker" > "Receive a web request" trigger.
13
+ * @link https://ifttt.com/myrecipes/personal/new
14
+ *
15
+ * Here, they will enter a label for the event name.
16
+ * This will be also need to be entered into the Stream Alert form.
17
+ *
18
+ * Finally, the Maker Key and Event Name need to be entered into
19
+ * the Alert metabox.
20
+ *
21
+ * This class uses the above data to send at POST request to IFTTT,
22
+ * which then hooks into whichever service the user has determined
23
+ * in their Recipe.
24
+ *
25
+ * Notes:
26
+ *
27
+ * In their IFTTT recipe, the Task Content field "quicktags" are
28
+ * defined as such in the unfiltered state of this Alert Type:
29
+ *
30
+ * {{EventName}} - The name the user assigned to this event.
31
+ * {{Value1}} - The Record Summary
32
+ * {{Value2}} - The User that triggered the alert
33
+ * {{Value3}} - The date/time on which the alert was triggered.
34
+ *
35
+ * There are several filters in self::notify_ifttt() that
36
+ * allow for complete customization of these values.
37
+ *
38
+ * More info:
39
+ * @link https://ifttt.com/channels/maker/triggers/1636368624-receive-a-web-request
40
+ *
41
+ * @package WP_Stream
42
+ */
43
+
44
+ namespace WP_Stream;
45
+
46
+ /**
47
+ * Class Alert_Type_IFTTT
48
+ *
49
+ * @package WP_Stream
50
+ */
51
+ class Alert_Type_IFTTT extends Alert_Type {
52
+ /**
53
+ * Alert type name
54
+ *
55
+ * @var string
56
+ */
57
+ public $name = 'IFTTT';
58
+
59
+ /**
60
+ * Alert type slug
61
+ *
62
+ * @var string
63
+ */
64
+ public $slug = 'ifttt';
65
+
66
+ /**
67
+ * Class Constructor
68
+ *
69
+ * @param Plugin $plugin Plugin object.
70
+ * @return void
71
+ */
72
+ public function __construct( $plugin ) {
73
+ parent::__construct( $plugin );
74
+ $this->plugin = $plugin;
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
+ *
83
+ * In self::post_class() this value is checked so we can determine
84
+ * if the class should be added to the Record's display.
85
+ *
86
+ * @param int|string $record_id Record that triggered alert.
87
+ * @param array $recordarr Record details.
88
+ * @param object $alert Alert options.
89
+ * @return void
90
+ */
91
+ public function alert( $record_id, $recordarr, $alert ) {
92
+ $recordarr['ID'] = $record_id;
93
+ $this->notify_ifttt( $alert, $recordarr );
94
+ }
95
+
96
+ /**
97
+ * Displays a settings form for the alert type
98
+ *
99
+ * @param Alert $alert Alert object for the currently displayed alert.
100
+ * @return void
101
+ */
102
+ public function display_fields( $alert ) {
103
+ $alert_meta = array();
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' );
116
+ echo wp_kses_post( sprintf( ' (<a href="%1$s" target="_blank">%2$s</span></a>)', 'https://youtu.be/XFWtkHXv9h0', __( 'Tutorial', 'stream' ) ) );
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>',
128
+ esc_url( 'https://ifttt.com/maker' ),
129
+ esc_html__( 'Open IFTTT Maker Channel', 'stream' ),
130
+ '<span class="dashicons dashicons-external"></span>'
131
+ );
132
+ echo '</label>';
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>',
144
+ esc_url( 'https://ifttt.com/myrecipes/personal' ),
145
+ esc_html__( 'Open IFTTT Recipes', 'stream' ),
146
+ '<span class="dashicons dashicons-external"></span>'
147
+ );
148
+ echo '</label>';
149
+ }
150
+
151
+ /**
152
+ * Validates and saves form settings for later use.
153
+ *
154
+ * @param Alert $alert Alert object for the currently displayed alert.
155
+ * @return void
156
+ */
157
+ public function save_fields( $alert ) {
158
+ check_admin_referer( 'save_alert', 'wp_stream_alerts_nonce' );
159
+ $alert->alert_meta['maker_key'] = '';
160
+
161
+ if ( ! empty( $_POST['wp_stream_ifttt_maker_key'] ) ) {
162
+ $alert->alert_meta['maker_key'] = sanitize_text_field( wp_unslash( $_POST['wp_stream_ifttt_maker_key'] ) );
163
+ }
164
+ if ( ! empty( $_POST['wp_stream_ifttt_event_name'] ) ) {
165
+ $alert->alert_meta['event_name'] = sanitize_text_field( wp_unslash( $_POST['wp_stream_ifttt_event_name'] ) );
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Send a POST request to IFTTT.
171
+ *
172
+ * This method sends Record data to IFTTT for processing.
173
+ *
174
+ * There are several filters here (documented below) that
175
+ * allow for customization of the data sent.
176
+ *
177
+ * Note that IFTTT only allows for three specifically-named
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
+ */
185
+ public function notify_ifttt( $alert, $recordarr ) {
186
+ if ( empty( $alert->alert_meta['maker_key'] ) || empty( $alert->alert_meta['event_name'] ) || empty( $recordarr ) ) {
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
201
+ *
202
+ * Defaults to 'user_login'.
203
+ *
204
+ * @param object $alert The Alert.
205
+ * @param array $recordarray The Record's data.
206
+ * @return string
207
+ */
208
+ $user_field = apply_filters( 'wp_stream_alert_ifttt_user_data_value', 'user_login', $alert, $recordarr );
209
+ $user_value = ! empty( $user->$user_field ) ? $user->$user_field : $user->user_login;
210
+
211
+ $created = $recordarr['created'];
212
+ /**
213
+ * Filter the date format string
214
+ *
215
+ * Defaults to 'Y-m-d H:i:s'.
216
+ *
217
+ * @param object $alert The Alert.
218
+ * @param array $recordarray The Record's data.
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
+
226
+ /*
227
+ * The array key labels for the request body are required by
228
+ * IFTTT; their names cannot be changed.
229
+ *
230
+ * The filter defaults are set up to send:
231
+ * value1 = Record Summary
232
+ *
233
+ * value2 = User login name
234
+ * (see the wp_stream_alert_ifttt_user_data_value filter above)
235
+ *
236
+ * value3 = Record Date
237
+ * (see the wp_stream_alert_ifttt_date_format filter above)
238
+ *
239
+ * The filters below allow complete customization of these data values.
240
+ */
241
+ $args = array(
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
+ */
255
+ 'value1' => apply_filters( 'wp_stream_alert_ifttt_value_one', $record_data['summary'], $alert, $recordarr ),
256
+
257
+ /**
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
265
+ */
266
+ 'value2' => apply_filters( 'wp_stream_alert_ifttt_value_two', $user_value, $user_id, $alert, $recordarr ),
267
+
268
+ /**
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
+ */
276
+ 'value3' => apply_filters( 'wp_stream_alert_ifttt_value_three', $date, $alert, $recordarr ),
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
+ *
289
+ * @param array $alert_meta The metadata to be inserted for this alert.
290
+ * @param string $alert_type The type of alert being added or updated.
291
+ *
292
+ * @return mixed
293
+ */
294
+ public function add_alert_meta( $alert_meta, $alert_type ) {
295
+ if ( $this->slug === $alert_type ) {
296
+ $maker_key = wp_stream_filter_input( INPUT_POST, 'wp_stream_ifttt_maker_key' );
297
+ if ( ! empty( $maker_key ) ) {
298
+ $alert_meta['maker_key'] = $maker_key;
299
+ }
300
+ $event_name = wp_stream_filter_input( INPUT_POST, 'wp_stream_ifttt_event_name' );
301
+ if ( ! empty( $event_name ) ) {
302
+ $alert_meta['event_name'] = $event_name;
303
+ }
304
+ }
305
+ return $alert_meta;
306
+ }
307
+ }
alerts/class-alert-type-menu-alert.php ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Menu Alert type.
4
+ *
5
+ * @package WP_Stream
6
+ */
7
+
8
+ namespace WP_Stream;
9
+
10
+ /**
11
+ * Class Alert_Type_Menu_Alert
12
+ *
13
+ * @package WP_Stream
14
+ */
15
+ class Alert_Type_Menu_Alert extends Alert_Type {
16
+ /**
17
+ * Alert type name
18
+ *
19
+ * @var string
20
+ */
21
+ public $name = 'Create Menu Alert';
22
+
23
+ /**
24
+ * Alert type slug
25
+ *
26
+ * @var string
27
+ */
28
+ public $slug = 'menu-alert';
29
+
30
+ /**
31
+ * Class Constructor
32
+ *
33
+ * @param Plugin $plugin Plugin object.
34
+ * @return void
35
+ */
36
+ public function __construct( $plugin ) {
37
+ parent::__construct( $plugin );
38
+ add_action( 'admin_bar_menu', array( $this, 'menu_alert' ), 99 );
39
+ }
40
+
41
+ /**
42
+ * Notify user of triggered alert.
43
+ *
44
+ * @param int $record_id Record that triggered alert.
45
+ * @param array $recordarr Record details.
46
+ * @param array $options Alert options.
47
+ * @return void
48
+ */
49
+ public function alert( $record_id, $recordarr, $options ) {
50
+ $this->add_message( $recordarr['summary'] );
51
+ return;
52
+ }
53
+
54
+ /**
55
+ * Displays a settings form for the alert type
56
+ *
57
+ * @param Alert $alert Alert object for the currently displayed alert.
58
+ * @return void
59
+ */
60
+ public function display_fields( $alert ) {
61
+ $alert_meta = array();
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
+ /**
81
+ * Validates and saves form settings for later use.
82
+ *
83
+ * @param Alert $alert Alert object for the currently displayed alert.
84
+ * @return void
85
+ */
86
+ public function save_fields( $alert ) {
87
+ check_admin_referer( 'save_alert', 'wp_stream_alerts_nonce' );
88
+
89
+ $alert->alert_meta['clear_immediate'] = ! empty( $_POST['wp_stream_menu_alert_clear_immediate'] );
90
+ }
91
+
92
+ /**
93
+ * Display and clear all unviewed menu alerts
94
+ *
95
+ * @param WP_Admin_Bar $wp_admin_bar Representation of the WP Admin Bar.
96
+ * @return bool True if notifications were displayed, false otherwise.
97
+ */
98
+ public function menu_alert( $wp_admin_bar ) {
99
+ $messages = $this->get_messages();
100
+ if ( ! $messages ) {
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
+
126
+ /**
127
+ * Get a list of all current alert messages for current user.
128
+ *
129
+ * @todo update this for VIP. (get_user_meta)
130
+ *
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
+
139
+ /**
140
+ * Adds a new alert message for the current user.
141
+ *
142
+ * @todo update this for VIP. (add_user_meta)
143
+ *
144
+ * @param string $message Alert message to add.
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
+
152
+ /**
153
+ * Clears all alert messages for the current user.
154
+ *
155
+ * @todo update this for VIP. (delete_user_meta)
156
+ *
157
+ * @param bool $global Whether to clear globally.
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
+
165
+ /**
166
+ * Returns meta key for pending alerts.
167
+ *
168
+ * @return string Meta key
169
+ */
170
+ public function get_key() {
171
+ return 'wp_stream_alerts_menu_pending';
172
+ }
173
+ }
alerts/class-alert-type-none.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * "Do nothing" Alert type.
4
+ *
5
+ * @package WP_Stream
6
+ */
7
+
8
+ namespace WP_Stream;
9
+
10
+ /**
11
+ * Class Alert_Type_None
12
+ *
13
+ * @package WP_Stream
14
+ */
15
+ class Alert_Type_None extends Alert_Type {
16
+ /**
17
+ * Notifier name
18
+ *
19
+ * @var string
20
+ */
21
+ public $name = 'Do Nothing';
22
+
23
+ /**
24
+ * Notifier slug
25
+ *
26
+ * @var string
27
+ */
28
+ public $slug = 'none';
29
+
30
+ /**
31
+ * Does not notify user.
32
+ *
33
+ * @param int $record_id Record that triggered alert.
34
+ * @param array $recordarr Record details.
35
+ * @param array $options Alert options.
36
+ * @return void
37
+ */
38
+ public function alert( $record_id, $recordarr, $options ) {
39
+ return;
40
+ }
41
+ }
alerts/js/alert-type-highlight.js ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* globals jQuery, _streamAlertTypeHighlightExports */
2
+ /* exported streamAlertTypeHighlight */
3
+ var streamAlertTypeHighlight = ( function( $ ) {
4
+ var self = {
5
+ ajaxUrl: '',
6
+ removeAction: '',
7
+ security: ''
8
+ };
9
+
10
+ if ( 'undefined' !== typeof _streamAlertTypeHighlightExports ) {
11
+ $.extend( self, _streamAlertTypeHighlightExports );
12
+ }
13
+
14
+ /**
15
+ * The primary function for this file.
16
+ *
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;
76
+
77
+ })( jQuery );
classes/class-admin.php CHANGED
@@ -299,6 +299,14 @@ class Admin {
299
  $main_menu_position
300
  );
301
 
 
 
 
 
 
 
 
 
302
  /**
303
  * Filter the Settings admin page title
304
  *
@@ -433,6 +441,11 @@ class Admin {
433
  return true;
434
  }
435
 
 
 
 
 
 
436
  return false;
437
  }
438
 
@@ -615,7 +628,7 @@ class Admin {
615
  $options = (array) get_option( 'wp_stream', array() );
616
  }
617
 
618
- if ( isset( $options['general_keep_records_indefinitely'] ) || ! isset( $options['general_records_ttl'] ) ) {
619
  return;
620
  }
621
 
299
  $main_menu_position
300
  );
301
 
302
+ /**
303
+ * Fires before submenu items are added to the Stream menu
304
+ * allowing plugins to add menu items before Settings
305
+ *
306
+ * @return void
307
+ */
308
+ do_action( 'wp_stream_admin_menu' );
309
+
310
  /**
311
  * Filter the Settings admin page title
312
  *
441
  return true;
442
  }
443
 
444
+ $screen = get_current_screen();
445
+ if ( is_admin() && 'post' === $screen->base && Alerts::POST_TYPE === $screen->post_type ) {
446
+ return true;
447
+ }
448
+
449
  return false;
450
  }
451
 
628
  $options = (array) get_option( 'wp_stream', array() );
629
  }
630
 
631
+ if ( ! empty( $options['general_keep_records_indefinitely'] ) || ! isset( $options['general_records_ttl'] ) ) {
632
  return;
633
  }
634
 
classes/class-alert-trigger.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Alert Trigger abstract class.
4
+ *
5
+ * @package WP_Stream
6
+ */
7
+
8
+ namespace WP_Stream;
9
+
10
+ /**
11
+ * Class Alert_Trigger
12
+ *
13
+ * @package WP_Stream
14
+ */
15
+ abstract class Alert_Trigger {
16
+
17
+ /**
18
+ * Hold the Plugin class
19
+ *
20
+ * @var Plugin
21
+ */
22
+ public $plugin;
23
+
24
+ /**
25
+ * Unique identifier
26
+ *
27
+ * @var string
28
+ */
29
+ public $slug;
30
+
31
+ /**
32
+ * Class constructor
33
+ *
34
+ * @param Plugin $plugin Plugin class.
35
+ */
36
+ public function __construct( $plugin ) {
37
+ $this->plugin = $plugin;
38
+
39
+ add_action( 'wp_stream_alert_trigger_form_display', array( $this, 'add_fields' ), 10, 2 );
40
+ add_action( 'wp_stream_alert_trigger_form_save', array( $this, 'save_fields' ), 10, 1 );
41
+ add_filter( 'wp_stream_alert_trigger_check', array( $this, 'check_record' ), 10, 4 );
42
+ }
43
+
44
+ /**
45
+ * Checks if a record matches the criteria from the trigger.
46
+ *
47
+ * @filter wp_stream_alert_trigger_check
48
+ *
49
+ * @param bool $success Status of previous checks.
50
+ * @param int $record_id Record ID.
51
+ * @param array $recordarr Record data.
52
+ * @param Alert $alert The Alert being worked on.
53
+ * @return bool False on failure, otherwise should return original value of $success.
54
+ */
55
+ abstract public function check_record ( $success, $record_id, $recordarr, $alert );
56
+
57
+ /**
58
+ * Adds fields to the trigger form.
59
+ *
60
+ * @action wp_stream_alert_trigger_form_display
61
+ *
62
+ * @param Form_Generator $form The Form Object to add to.
63
+ * @param Alert $alert The Alert being worked on.
64
+ * @return void
65
+ */
66
+ abstract public function add_fields( $form, $alert );
67
+
68
+ /**
69
+ * Validate and save Alert object
70
+ *
71
+ * @action wp_stream_alert_trigger_form_save
72
+ *
73
+ * @param Alert $alert The Alert being worked on.
74
+ * @return void
75
+ */
76
+ abstract public function save_fields( $alert );
77
+
78
+ /**
79
+ * Returns the trigger's value for the given alert.
80
+ *
81
+ * @param string $context The location this data will be displayed in.
82
+ * @param Alert $alert Alert being processed.
83
+ * @return string
84
+ */
85
+ abstract public function get_display_value( $context = 'normal', $alert );
86
+
87
+ /**
88
+ * Allow connectors to determine if their dependencies is satisfied or not
89
+ *
90
+ * @return bool
91
+ */
92
+ public function is_dependency_satisfied() {
93
+ return true;
94
+ }
95
+ }
classes/class-alert-type.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Alert Type abstract class.
4
+ *
5
+ * Used to register new Alert types.
6
+ *
7
+ * @package WP_Stream
8
+ */
9
+
10
+ namespace WP_Stream;
11
+
12
+ /**
13
+ * Class Alert_Type
14
+ *
15
+ * @package WP_Stream
16
+ */
17
+ abstract class Alert_Type {
18
+
19
+ /**
20
+ * Hold the Plugin class
21
+ *
22
+ * @var Plugin
23
+ */
24
+ public $plugin;
25
+
26
+ /**
27
+ * Unique identifier.
28
+ *
29
+ * @var string
30
+ */
31
+ public $slug;
32
+
33
+ /**
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.
48
+ * @param array $options Alert options.
49
+ */
50
+ abstract public function alert( $record_id, $recordarr, $options );
51
+
52
+ /**
53
+ * Display settings form for configuration of individual alerts
54
+ *
55
+ * @param Alert $alert Alert currently being worked on.
56
+ */
57
+ public function display_fields( $alert ) {
58
+ return;
59
+ }
60
+
61
+ /**
62
+ * Process settings form for configuration of individual alerts
63
+ *
64
+ * @param Alert $alert Alert currently being worked on.
65
+ */
66
+ public function save_fields( $alert ) {
67
+ return;
68
+ }
69
+
70
+ /**
71
+ * Allow connectors to determine if their dependencies are satisfied or not
72
+ *
73
+ * @return bool
74
+ */
75
+ public function is_dependency_satisfied() {
76
+ return true;
77
+ }
78
+ }
classes/class-alert.php ADDED
@@ -0,0 +1,337 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Single Alert handler.
4
+ *
5
+ * @package WP_Stream
6
+ */
7
+
8
+ namespace WP_Stream;
9
+
10
+ /**
11
+ * Class Alert
12
+ *
13
+ * @package WP_Stream
14
+ */
15
+ class Alert {
16
+
17
+ /**
18
+ * Alert post ID
19
+ *
20
+ * @var int
21
+ */
22
+ public $ID;
23
+
24
+ /**
25
+ * Creation date
26
+ *
27
+ * @var string
28
+ */
29
+ public $date;
30
+
31
+ /**
32
+ * Status
33
+ *
34
+ * @var string
35
+ */
36
+ public $status;
37
+
38
+ /**
39
+ * Alert author ID
40
+ *
41
+ * @var int
42
+ */
43
+ public $author;
44
+
45
+ /**
46
+ * Alert type
47
+ *
48
+ * @var string
49
+ */
50
+ public $alert_type;
51
+
52
+ /**
53
+ * Alert meta data
54
+ *
55
+ * @var int
56
+ */
57
+ public $alert_meta;
58
+
59
+ /**
60
+ * Hold Plugin class
61
+ *
62
+ * @var Plugin
63
+ */
64
+ public $plugin;
65
+
66
+ /**
67
+ * Class constructor
68
+ *
69
+ * @param object $item Alert data.
70
+ * @param Plugin $plugin Plugin class.
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();
83
+ }
84
+
85
+ /**
86
+ * Save state of the Alert to database
87
+ *
88
+ * @return int The Post ID of the alert.
89
+ */
90
+ public function save() {
91
+
92
+ $args = array(
93
+ 'ID' => $this->ID,
94
+ 'post_date' => $this->date,
95
+ 'post_status' => $this->status,
96
+ 'post_content' => '',
97
+ 'post_title' => $this->get_title(),
98
+ 'post_author' => $this->author,
99
+ 'post_type' => Alerts::POST_TYPE,
100
+ );
101
+
102
+ if ( empty( $args['ID'] ) ) {
103
+ unset( $args['ID'] );
104
+ }
105
+
106
+ $post_id = wp_insert_post( $args );
107
+ if ( empty( $args['ID'] ) ) {
108
+ $this->ID = $post_id;
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 ) {
117
+ $this->update_meta( $key, $value );
118
+ }
119
+
120
+ return $post_id;
121
+ }
122
+
123
+ /**
124
+ * Process settings form data
125
+ *
126
+ * @param array $data Processed post object data.
127
+ * @return array New post object data.
128
+ */
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 ) {
140
+ $data[ $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 ) {
149
+ $this->update_meta( $key, $value );
150
+ }
151
+
152
+ return $data;
153
+ }
154
+
155
+ /**
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.
163
+ */
164
+ public function get_meta( $meta_key = '', $single = false ) {
165
+ return maybe_unserialize( get_post_meta( $this->ID, $meta_key, $single ) );
166
+ }
167
+
168
+ /**
169
+ * Update record meta
170
+ *
171
+ * @param string $meta_key Meta key to update.
172
+ * @param string $meta_value Value to update with.
173
+ * @param string $prev_value Previous value to change (optional).
174
+ * @return array
175
+ */
176
+ public function update_meta( $meta_key, $meta_value, $prev_value = '' ) {
177
+ return update_post_meta( $this->ID, $meta_key, $meta_value, $prev_value );
178
+ }
179
+
180
+ /**
181
+ * Determine the title of the alert.
182
+ *
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
+
190
+ $output = array();
191
+ foreach ( array( 'action', 'author', 'context' ) as $trigger_type ) {
192
+ $output[ $trigger_type ] = $this->plugin->alerts->alert_triggers[ $trigger_type ]->get_display_value( 'list_table', $this );
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, ' > ' );
200
+ return $title;
201
+ }
202
+
203
+ /**
204
+ * Retrive current alert type object
205
+ *
206
+ * @return Alert_Type
207
+ */
208
+ public function get_alert_type_obj() {
209
+ if ( array_key_exists( $this->alert_type, $this->plugin->alerts->alert_types ) ) {
210
+ $obj = $this->plugin->alerts->alert_types[ $this->alert_type ];
211
+ } else {
212
+ $obj = new Alert_Type_None( $this->plugin );
213
+ }
214
+ return $obj;
215
+ }
216
+
217
+ /**
218
+ * Check if record matches trigger criteria.
219
+ *
220
+ * @param int $record_id Record ID.
221
+ * @param array $recordarr Record data.
222
+ * @return bool True if a positive match. False otherwise.
223
+ */
224
+ public function check_record( $record_id, $recordarr ) {
225
+ return apply_filters( 'wp_stream_alert_trigger_check', true, $record_id, $recordarr, $this );
226
+ }
227
+
228
+ /**
229
+ * Trigger alert for a specific record.
230
+ *
231
+ * @param int $record_id Record ID.
232
+ * @param int $recordarr Record Data.
233
+ * @return void
234
+ */
235
+ public function send_alert( $record_id, $recordarr ) {
236
+ $this->get_alert_type_obj()->alert( $record_id, $recordarr, $this );
237
+ }
238
+
239
+ /**
240
+ * Record Alerts triggered by a Record.
241
+ *
242
+ * This should be used any time an alert is triggered.
243
+ *
244
+ * Stores the post ID of an Alert triggered by a record.
245
+ *
246
+ * As an example, this exists so that its value(s) can then be used
247
+ * to fetch meta from that Alert in later operations.
248
+ *
249
+ * This also creates the Alert triggered meta if not found.
250
+ *
251
+ * @see Alert_Type_Highlight::alert() for an example.
252
+ *
253
+ * @param object $record The Record.
254
+ * @param string $alert_slug The Alert Type slug.
255
+ * @param array $alert_meta Alert meta.
256
+ *
257
+ * @return bool If the meta was updated successfully.
258
+ */
259
+ public static function update_record_triggered_alerts( $record, $alert_slug, $alert_meta ) {
260
+ if ( ! is_string( $alert_slug ) ) {
261
+ return false;
262
+ }
263
+
264
+ if ( is_array( $record ) ) {
265
+ $record = (object) $record;
266
+ }
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
+ }
278
+ return $record->update_meta( Alerts::ALERTS_TRIGGERED_META_KEY, $alerts_triggered );
279
+ }
280
+
281
+ /**
282
+ * Get a meta value from the Alert that a Record has triggered.
283
+ *
284
+ * If a Record has triggered an Alert (post), this fetches a specific
285
+ * Alert meta (i.e., "post meta") value from that Alert.
286
+ *
287
+ * First, it gets the array of Alerts that the Record has triggered.
288
+ * Then, using the requested Alert Type, it grabs the first item (post ID) in
289
+ * that Type.
290
+ *
291
+ * Using that ID, it fetches that Alert post's meta, then
292
+ * returns the value of the requested setting (ie., "post meta" field).
293
+ *
294
+ * @see Alert_Type_Highlight::post_class() for an example.
295
+ *
296
+ * @param object $record The Record object.
297
+ * @param string $alert_slug The slug of the Alert Type.
298
+ * @param string $setting The requested meta value of the Alert.
299
+ * @param mixed $default The default value if no value is found.
300
+ *
301
+ * @return mixed
302
+ */
303
+ public function get_single_alert_setting_from_record( $record, $alert_slug, $setting, $default = false ) {
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.
311
+ if ( empty( $alerts_triggered ) || ! is_array( $alerts_triggered ) || ! array_key_exists( $alert_slug, $alerts_triggered ) ) {
312
+ return false;
313
+ }
314
+
315
+ $values = $alerts_triggered[ $alert_slug ];
316
+ if ( empty( $values ) ) {
317
+ return false;
318
+ }
319
+
320
+ // Grab an Alert post ID.
321
+ // @todo determine which Alert post takes priority.
322
+ if ( is_array( $values ) ) {
323
+ $post_id = $values[0];
324
+ } else {
325
+ $post_id = $values;
326
+ }
327
+
328
+ if ( ! is_numeric( $post_id ) ) {
329
+ return false;
330
+ }
331
+
332
+ $alert = $this->plugin->alerts->get_alert( $post_id );
333
+
334
+ $value = ! empty( $alert->alert_meta[ $setting ] ) ? $alert->alert_meta[ $setting ] : $default;
335
+ return $value;
336
+ }
337
+ }
classes/class-alerts-list.php ADDED
@@ -0,0 +1,374 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Listing of Alerts in the WP Admin.
4
+ *
5
+ * @package WP_Stream
6
+ */
7
+
8
+ namespace WP_Stream;
9
+
10
+ /**
11
+ * Class Alerts_List
12
+ *
13
+ * @package WP_Stream
14
+ */
15
+ class Alerts_List {
16
+ /**
17
+ * Hold the Plugin class
18
+ *
19
+ * @var Plugin
20
+ */
21
+ public $plugin;
22
+
23
+ /**
24
+ * Class constructor.
25
+ *
26
+ * @param Plugin $plugin The main Plugin class.
27
+ */
28
+ public function __construct( $plugin ) {
29
+ $this->plugin = $plugin;
30
+
31
+ add_filter( 'bulk_actions-edit-wp_stream_alerts', array( $this, 'suppress_bulk_actions' ), 10, 1 );
32
+ add_filter( 'disable_months_dropdown', array( $this, 'suppress_months_dropdown' ), 10, 2 );
33
+ add_filter( 'post_row_actions', array( $this, 'suppress_quick_edit' ), 10, 1 );
34
+
35
+ // @todo Make more specific
36
+ if ( is_admin() ) {
37
+ add_filter( 'request', array( $this, 'parse_request' ), 10, 2 );
38
+ }
39
+ add_filter( 'views_edit-wp_stream_alerts', array( $this, 'manage_views' ) );
40
+
41
+ add_filter( 'manage_wp_stream_alerts_posts_columns', array( $this, 'manage_columns' ) );
42
+ add_action( 'manage_wp_stream_alerts_posts_custom_column', array( $this, 'column_data' ), 10, 2 );
43
+
44
+ add_action( 'quick_edit_custom_box', array( $this, 'display_custom_quick_edit' ), 10, 2 );
45
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
46
+
47
+ add_filter( 'wp_insert_post_data', array( $this, 'save_alert_inline_edit' ), 99, 2 );
48
+ }
49
+
50
+ /**
51
+ * Default to wp_stream_enabled and wp_stream_disabled when querying for alerts
52
+ *
53
+ * @filter request
54
+ *
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' );
62
+ }
63
+ return $query_vars;
64
+ }
65
+
66
+ /**
67
+ * Manage views on the alerts list view
68
+ *
69
+ * @filter views_edit-wp_stream_alerts
70
+ *
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'];
78
+ unset( $views['trash'] );
79
+ $views['trash'] = $trash;
80
+ }
81
+
82
+ return $views;
83
+ }
84
+
85
+ /**
86
+ * Manages columns on the alerts list view
87
+ *
88
+ * @filter manage_wp_stream_alerts_posts_columns
89
+ *
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' ),
97
+ 'alert_type' => __( 'Type', 'stream' ),
98
+ 'alert_status' => __( 'Status', 'stream' ),
99
+ );
100
+ return $columns;
101
+ }
102
+
103
+ /**
104
+ * Fills in column data for custom columns.
105
+ *
106
+ * @action manage_wp_stream_alerts_posts_custom_column
107
+ *
108
+ * @param string $column_name Column name to show data for.
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 ) {
116
+ return;
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
+ ?>
127
+ <div><?php echo wp_kses_post( join( '', $values ) ); ?></div>
128
+ <div class="row-actions wp-stream-show-mobile">
129
+ <?php echo wp_kses_post( $this->custom_column_actions( $post_id ) ); ?>
130
+ <button type="button" class="toggle-row"><span class="screen-reader-text"><?php echo esc_html__( 'Show more details', 'stream' ); ?></span></button>
131
+ </div>
132
+ <?php
133
+ if ( ! empty( $alert->alert_meta['trigger_connector'] ) ) {
134
+ $trigger_connector = $alert->alert_meta['trigger_connector'];
135
+ } else {
136
+ $trigger_connector = '';
137
+ }
138
+ if ( ! empty( $alert->alert_meta['trigger_context'] ) ) {
139
+ $trigger_context = $alert->alert_meta['trigger_context'];
140
+ } else {
141
+ $trigger_context = '';
142
+ }
143
+ if ( ! empty( $alert->alert_meta['trigger_action'] ) ) {
144
+ $trigger_action = $alert->alert_meta['trigger_action'];
145
+ } else {
146
+ $trigger_action = '';
147
+ }
148
+ ?>
149
+ <input type="hidden" name="wp_stream_trigger_connector" value="<?php echo esc_attr( $trigger_connector ); ?>" />
150
+ <input type="hidden" name="wp_stream_trigger_context" value="<?php echo esc_attr( $trigger_context ); ?>" />
151
+ <input type="hidden" name="wp_stream_trigger_action" value="<?php echo esc_attr( $trigger_action ); ?>" />
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;
159
+ } else {
160
+ $alert_name = 'Untitled Alert';
161
+ }
162
+ ?>
163
+ <input type="hidden" name="wp_stream_alert_type" value="<?php echo esc_attr( $alert->alert_type ); ?>" />
164
+ <strong class="row-title"><?php echo esc_html( $alert_name ); ?></strong>
165
+ <?php
166
+ if ( ! empty( $alert->alert_meta['color'] ) ) {
167
+ ?>
168
+ <input type="hidden" name="wp_stream_highlight_color" value="<?php echo esc_attr( $alert->alert_meta['color'] ); ?>" />
169
+ <?php
170
+ }
171
+ if ( ! empty( $alert->alert_meta['email_recipient'] ) ) {
172
+ ?>
173
+ <input type="hidden" name="wp_stream_email_recipient" value="<?php echo esc_attr( $alert->alert_meta['email_recipient'] ); ?>" />
174
+ <?php
175
+ }
176
+ if ( ! empty( $alert->alert_meta['email_subject'] ) ) {
177
+ ?>
178
+ <input type="hidden" name="wp_stream_email_subject" value="<?php echo esc_attr( $alert->alert_meta['email_subject'] ); ?>" />
179
+ <?php
180
+ }
181
+ if ( ! empty( $alert->alert_meta['event_name'] ) ) {
182
+ ?>
183
+ <input type="hidden" name="wp_stream_ifttt_event_name" value="<?php echo esc_attr( $alert->alert_meta['event_name'] ); ?>" />
184
+ <?php
185
+ }
186
+ if ( ! empty( $alert->alert_meta['maker_key'] ) ) {
187
+ ?>
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 );
196
+ }
197
+ ?>
198
+ <input type="hidden" name="wp_stream_alert_status" value="<?php echo esc_attr( $post_status_object->name ); ?>" />
199
+ <?php
200
+ break;
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Remove 'edit' action from bulk actions
206
+ *
207
+ * @filter bulk_actions-edit-wp_stream_alerts
208
+ *
209
+ * @param array $actions List of bulk actions available.
210
+ * @return array
211
+ */
212
+ public function suppress_bulk_actions( $actions ) {
213
+ unset( $actions['edit'] );
214
+ return $actions;
215
+ }
216
+
217
+ /**
218
+ * Remove quick edit action from inline edit actions
219
+ *
220
+ * @filter post_row_actions
221
+ *
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
+ }
229
+ unset( $actions['edit'] );
230
+ unset( $actions['view'] );
231
+ unset( $actions['trash'] );
232
+ unset( $actions['inline hide-if-no-js'] );
233
+ return $actions;
234
+ }
235
+
236
+ /**
237
+ * Remove months dropdown from Alerts list page
238
+ *
239
+ * @filter disable_months_dropdown
240
+ *
241
+ * @param bool $status Status of months dropdown enabling.
242
+ * @param string $post_type Post type status is related to.
243
+ * @return bool
244
+ */
245
+ public function suppress_months_dropdown( $status, $post_type ) {
246
+ if ( Alerts::POST_TYPE === $post_type ) {
247
+ $status = true;
248
+ }
249
+ return $status;
250
+ }
251
+
252
+ /**
253
+ * Custom column actions for alerts main screen
254
+ *
255
+ * @param int $post_id The current post ID.
256
+ *
257
+ * @return string
258
+ */
259
+ public function custom_column_actions( $post_id ) {
260
+ $post_status = wp_stream_filter_input( INPUT_GET, 'post_status' );
261
+ ob_start();
262
+ if ( 'trash' !== $post_status ) {
263
+ $bare_url = admin_url( 'post.php?post=' . $post_id . '&action=trash' );
264
+ $nonce_url = wp_nonce_url( $bare_url, 'trash-post_' . $post_id );
265
+ ?>
266
+ <div class="row-actions">
267
+ <span class="inline hide-if-no-js"><a href="#" class="editinline" aria-label="Quick edit “Hello world!” inline"><?php esc_html_e( 'Edit' ); ?></a>&nbsp;|&nbsp;</span>
268
+ <span class="trash">
269
+ <a href="<?php echo esc_url( $nonce_url ); ?>" class="submitdelete"><?php esc_html_e( 'Trash', 'stream' ); ?></a>
270
+ </span>
271
+ </div>
272
+ <?php
273
+ }
274
+ return ob_get_clean();
275
+ }
276
+
277
+ /**
278
+ * Display a custom quick edit form.
279
+ */
280
+ public function display_custom_quick_edit() {
281
+ static $fired = false;
282
+ if ( false !== $fired ) {
283
+ return;
284
+ }
285
+ $screen = get_current_screen();
286
+ if ( 'edit-wp_stream_alerts' !== $screen->id ) {
287
+ return;
288
+ }
289
+ wp_nonce_field( plugin_basename( __FILE__ ), Alerts::POST_TYPE . '_edit_nonce' );
290
+ $box_type = array(
291
+ 'triggers',
292
+ 'notification',
293
+ 'submit',
294
+ );
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>
306
+ <?php
307
+ endforeach;
308
+ $fired = true;
309
+ }
310
+
311
+ /**
312
+ * Enqueue scripts for the alerts list screen.
313
+ *
314
+ * @param string $page The current page name.
315
+ */
316
+ public function enqueue_scripts( $page ) {
317
+ $screen = get_current_screen();
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
+ }
327
+
328
+ /**
329
+ * Save alert meta after using the inline editor.
330
+ *
331
+ * @param array $data Filtered post data.
332
+ * @param array $postarr Raw post data.
333
+ *
334
+ * @return array
335
+ */
336
+ function save_alert_inline_edit( $data, $postarr ) {
337
+ $post_id = $postarr['ID'];
338
+ $post_type = wp_stream_filter_input( INPUT_POST, 'post_type' );
339
+ if ( Alerts::POST_TYPE !== $post_type ) {
340
+ return $data;
341
+ }
342
+ if ( ! current_user_can( 'edit_post', $post_id ) ) {
343
+ return $data;
344
+ }
345
+
346
+ $nonce = wp_stream_filter_input( INPUT_POST, Alerts::POST_TYPE . '_edit_nonce' );
347
+ if ( null === $nonce || ! wp_verify_nonce( $nonce, plugin_basename( __FILE__ ) ) ) {
348
+ return $data;
349
+ }
350
+
351
+ $trigger_author = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_author' );
352
+ $trigger_connector_and_context = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_connector_or_context' );
353
+ $trigger_connector_and_context_split = explode( '-', $trigger_connector_and_context );
354
+ $trigger_connector = $trigger_connector_and_context_split[0];
355
+ $trigger_context = $trigger_connector_and_context_split[1];
356
+
357
+ $trigger_action = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_action' );
358
+ $alert_type = wp_stream_filter_input( INPUT_POST, 'wp_stream_alert_type' );
359
+ $alert_status = wp_stream_filter_input( INPUT_POST, 'wp_stream_alert_status' );
360
+ $data['post_status'] = $alert_status;
361
+
362
+ update_post_meta( $post_id, 'alert_type', $alert_type );
363
+
364
+ $alert_meta = array(
365
+ 'trigger_author' => $trigger_author,
366
+ 'trigger_connector' => $trigger_connector,
367
+ 'trigger_action' => $trigger_action,
368
+ 'trigger_context' => $trigger_context,
369
+ );
370
+ $alert_meta = apply_filters( 'wp_stream_alerts_save_meta', $alert_meta, $alert_type );
371
+ update_post_meta( $post_id, 'alert_meta', $alert_meta );
372
+ return $data;
373
+ }
374
+ }
classes/class-alerts.php ADDED
@@ -0,0 +1,777 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Alerts feature class.
4
+ *
5
+ * @package WP_Stream
6
+ */
7
+
8
+ namespace WP_Stream;
9
+
10
+ /**
11
+ * Class Alerts
12
+ *
13
+ * @package WP_Stream
14
+ */
15
+ class Alerts {
16
+ /**
17
+ * Alerts post type slug
18
+ */
19
+ const POST_TYPE = 'wp_stream_alerts';
20
+
21
+ /**
22
+ * Triggered Alerts meta key for Records
23
+ */
24
+ const ALERTS_TRIGGERED_META_KEY = 'wp_stream_alerts_triggered';
25
+
26
+ /**
27
+ * Hold Plugin class
28
+ *
29
+ * @var Plugin
30
+ */
31
+ public $plugin;
32
+
33
+ /**
34
+ * Post meta prefix
35
+ *
36
+ * @var string
37
+ */
38
+ public $meta_prefix = 'wp_stream';
39
+
40
+ /**
41
+ * Alert Types
42
+ *
43
+ * @var array
44
+ */
45
+ public $alert_types = array();
46
+
47
+ /**
48
+ * Alert Triggers
49
+ *
50
+ * @var array
51
+ */
52
+ public $alert_triggers = array();
53
+
54
+ /**
55
+ * Class constructor.
56
+ *
57
+ * @param Plugin $plugin The main Plugin class.
58
+ */
59
+ public function __construct( $plugin ) {
60
+ $this->plugin = $plugin;
61
+
62
+ // Register custom post type.
63
+ add_action( 'init', array( $this, 'register_post_type' ) );
64
+
65
+ // Add custom post type to menu.
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
+
90
+ /**
91
+ * Load alert_type classes
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();
104
+ foreach ( $alert_types as $alert_type ) {
105
+ $file_location = $this->plugin->locations['dir'] . '/alerts/class-alert-type-' . $alert_type . '.php';
106
+ if ( file_exists( $file_location ) ) {
107
+ include_once $file_location;
108
+ $class_name = sprintf( '\WP_Stream\Alert_Type_%s', str_replace( '-', '_', $alert_type ) );
109
+ if ( ! class_exists( $class_name ) ) {
110
+ continue;
111
+ }
112
+ $class = new $class_name( $this->plugin );
113
+ if ( ! property_exists( $class, 'slug' ) ) {
114
+ continue;
115
+ }
116
+ $classes[ $class->slug ] = $class;
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Allows for adding additional alert_types via classes that extend Notifier.
122
+ *
123
+ * @param array $classes An array of Notifier objects. In the format alert_type_slug => Notifier_Class()
124
+ */
125
+ $this->alert_types = apply_filters( 'wp_stream_alert_types', $classes );
126
+
127
+ // Ensure that all alert_types extend Notifier.
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
+ }
140
+
141
+ /**
142
+ * Load alert_type classes
143
+ *
144
+ * @return void
145
+ */
146
+ function load_alert_triggers() {
147
+ $alert_triggers = array(
148
+ 'author',
149
+ 'context',
150
+ 'action',
151
+ );
152
+
153
+ $classes = array();
154
+ foreach ( $alert_triggers as $alert_trigger ) {
155
+ $file_location = $this->plugin->locations['dir'] . '/alerts/class-alert-trigger-' . $alert_trigger . '.php';
156
+ if ( file_exists( $file_location ) ) {
157
+ include_once $file_location;
158
+ $class_name = sprintf( '\WP_Stream\Alert_Trigger_%s', str_replace( '-', '_', $alert_trigger ) );
159
+ if ( ! class_exists( $class_name ) ) {
160
+ continue;
161
+ }
162
+ $class = new $class_name( $this->plugin );
163
+ if ( ! property_exists( $class, 'slug' ) ) {
164
+ continue;
165
+ }
166
+ $classes[ $class->slug ] = $class;
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Allows for adding additional alert_triggers via classes that extend Notifier.
172
+ *
173
+ * @param array $classes An array of Notifier objects. In the format alert_trigger_slug => Notifier_Class()
174
+ */
175
+ $this->alert_triggers = apply_filters( 'wp_stream_alert_triggers', $classes );
176
+
177
+ // Ensure that all alert_triggers extend Notifier.
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
+ }
190
+
191
+ /**
192
+ * Checks whether a Alert Type class is valid
193
+ *
194
+ * @param Alert_Type $alert_type The class to check.
195
+ *
196
+ * @return bool
197
+ */
198
+ public function is_valid_alert_type( $alert_type ) {
199
+ if ( ! is_a( $alert_type, 'WP_Stream\Alert_Type' ) ) {
200
+ return false;
201
+ }
202
+
203
+ if ( ! method_exists( $alert_type, 'is_dependency_satisfied' ) || ! $alert_type->is_dependency_satisfied() ) {
204
+ return false;
205
+ }
206
+
207
+ return true;
208
+ }
209
+
210
+ /**
211
+ * Checks whether a Alert Trigger class is valid
212
+ *
213
+ * @param Alert_Trigger $alert_trigger The class to check.
214
+ *
215
+ * @return bool
216
+ */
217
+ public function is_valid_alert_trigger( $alert_trigger ) {
218
+ if ( ! is_a( $alert_trigger, 'WP_Stream\Alert_Trigger' ) ) {
219
+ return false;
220
+ }
221
+
222
+ if ( ! method_exists( $alert_trigger, 'is_dependency_satisfied' ) || ! $alert_trigger->is_dependency_satisfied() ) {
223
+ return false;
224
+ }
225
+
226
+ return true;
227
+ }
228
+
229
+ /**
230
+ * Checks record being processed against active alerts.
231
+ *
232
+ * @filter wp_stream_record_inserted
233
+ *
234
+ * @param int $record_id The record being processed.
235
+ * @param array $recordarr Record data.
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',
243
+ );
244
+
245
+ $alerts = new \WP_Query( $args );
246
+ foreach ( $alerts->posts as $alert ) {
247
+ $alert = $this->get_alert( $alert->ID );
248
+
249
+ $status = $alert->check_record( $record_id, $recordarr );
250
+ if ( $status ) {
251
+ $alert->send_alert( $record_id, $recordarr ); // @todo send_alert expects int, not array.
252
+ }
253
+ }
254
+
255
+ return $recordarr;
256
+
257
+ }
258
+
259
+ /**
260
+ * Register scripts for page load
261
+ *
262
+ * @action admin_enqueue_scripts
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' ),
278
+ )
279
+ );
280
+ wp_enqueue_script( 'wp-stream-alerts' );
281
+ wp_enqueue_style( 'wp-stream-select2' );
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Register custom post type
287
+ *
288
+ * @action init
289
+ *
290
+ * @return void
291
+ */
292
+ public function register_post_type() {
293
+ $labels = array(
294
+ 'name' => _x( 'Alerts', 'post type general name', 'stream' ),
295
+ 'singular_name' => _x( 'Alert', 'post type singular name', 'stream' ),
296
+ 'menu_name' => _x( 'Alerts', 'admin menu', 'stream' ),
297
+ 'name_admin_bar' => _x( 'Alert', 'add new on admin bar', 'stream' ),
298
+ 'add_new' => _x( 'Add New', 'book', 'stream' ),
299
+ 'add_new_item' => __( 'Add New Alert', 'stream' ),
300
+ 'new_item' => __( 'New Alert', 'stream' ),
301
+ 'edit_item' => __( 'Edit Alert', 'stream' ),
302
+ 'view_item' => __( 'View Alert', 'stream' ),
303
+ 'all_items' => __( 'Alerts', 'stream' ),
304
+ 'search_items' => __( 'Search Alerts', 'stream' ),
305
+ 'parent_item_colon' => __( 'Parent Alerts:', 'stream' ),
306
+ 'not_found' => __( 'No alerts found.', 'stream' ),
307
+ 'not_found_in_trash' => __( 'No alerts found in Trash.', 'stream' ),
308
+ );
309
+
310
+ $args = array(
311
+ 'labels' => $labels,
312
+ 'description' => __( 'Alerts for Stream.', 'stream' ),
313
+ 'public' => false,
314
+ 'publicly_queryable' => false,
315
+ 'exclude_from_search' => true,
316
+ 'show_ui' => true,
317
+ 'show_in_menu' => false, // @see modify_admin_menu
318
+ 'supports' => false,
319
+ 'capabilities' => array(
320
+ 'publish_posts' => 'manage_options',
321
+ 'edit_others_posts' => 'manage_options',
322
+ 'delete_posts' => 'manage_options',
323
+ 'delete_others_posts' => 'manage_options',
324
+ 'read_private_posts' => 'manage_options',
325
+ 'edit_post' => 'manage_options',
326
+ 'delete_post' => 'manage_options',
327
+ 'read_post' => 'manage_options',
328
+ ),
329
+ );
330
+
331
+ register_post_type( self::POST_TYPE, $args );
332
+
333
+ $args = array(
334
+ 'label' => _x( 'Enabled', 'alert', 'stream' ),
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
+
341
+ register_post_status( 'wp_stream_enabled', $args );
342
+
343
+ $args = array(
344
+ 'label' => _x( 'Disabled', 'alert', 'stream' ),
345
+ 'public' => false,
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
+
352
+ register_post_status( 'wp_stream_disabled', $args );
353
+ }
354
+
355
+ /**
356
+ * Return alert object of the given ID
357
+ *
358
+ * @param string $post_id Post ID for the alert.
359
+ *
360
+ * @return Alert
361
+ */
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
+ $meta = get_post_custom( $post_id );
370
+
371
+ $obj = (object) array(
372
+ 'ID' => $post->ID,
373
+ 'status' => $post->post_status,
374
+ 'date' => $post->post_date,
375
+ 'author' => $post->post_author,
376
+ 'filter_action' => isset( $meta['filter_action'] ) ? $meta['filter_action'][0] : null,
377
+ 'filter_author' => isset( $meta['filter_author'] ) ? $meta['filter_author'][0] : null,
378
+ 'filter_context' => isset( $meta['filter_context'] ) ? $meta['filter_context'][0] : null,
379
+ 'alert_type' => isset( $meta['alert_type'] ) ? $meta['alert_type'][0] : null,
380
+ 'alert_meta' => isset( $meta['alert_meta'] ) ? (array) maybe_unserialize( $meta['alert_meta'][0] ) : array(),
381
+ );
382
+
383
+ return new Alert( $obj, $this->plugin );
384
+
385
+ }
386
+
387
+ /**
388
+ * Add custom post type to menu
389
+ *
390
+ * @action admin_menu
391
+ *
392
+ * @return void
393
+ */
394
+ function register_menu() {
395
+ add_submenu_page(
396
+ $this->plugin->admin->records_page_slug,
397
+ __( 'Alerts', 'stream' ),
398
+ __( 'Alerts', 'stream' ),
399
+ 'manage_options',
400
+ 'edit.php?post_type=wp_stream_alerts'
401
+ );
402
+ }
403
+
404
+ /**
405
+ * Modify the Stream > Alerts Network Admin Menu link.
406
+ *
407
+ * In self::register_menu(), the Alerts submenu item
408
+ * is essentially set to go to the Site's admin area.
409
+ *
410
+ * However, on the Network admin, we need to redirect
411
+ * it to the first site in the network, as this is
412
+ * where the true Network Alerts settings page is located.
413
+ *
414
+ * @action network_admin_menu
415
+ * @return bool
416
+ */
417
+ function change_menu_link_url() {
418
+ global $submenu;
419
+
420
+ $parent = 'wp_stream';
421
+ $page = 'edit.php?post_type=wp_stream_alerts';
422
+
423
+ // If we're not on the Stream menu item, return.
424
+ if ( ! isset( $submenu[ $parent ] ) ) {
425
+ return false;
426
+ }
427
+
428
+ // Get the first existing Site in the Network.
429
+ // @todo: Switch to use wp_stream_get_sites()
430
+ $sites = wp_get_sites(
431
+ array(
432
+ 'limit' => 5, // Limit the size of the query.
433
+ )
434
+ );
435
+
436
+ $site_id = '1';
437
+
438
+ // Function wp_get_sites() can return an empty array if the network is too large.
439
+ if ( ! empty( $sites ) && ! empty( $sites[0]['blog_id'] ) ) {
440
+ $site_id = $sites[0]['blog_id'];
441
+ }
442
+
443
+ $new_url = get_admin_url( $site_id, $page );
444
+
445
+ foreach ( $submenu[ $parent ] as $key => $value ) {
446
+
447
+ // Set correct URL for the menu item.
448
+ if ( $page === $value[2] ) {
449
+ $submenu[ $parent ][ $key ][2] = $new_url;
450
+ break;
451
+ }
452
+ }
453
+
454
+ return true;
455
+ }
456
+
457
+ /**
458
+ * Display Alert Type Meta Box
459
+ *
460
+ * @param \WP_Post|array $post Post object for current alert.
461
+ *
462
+ * @return void
463
+ */
464
+ function display_notification_box( $post = array() ) {
465
+ $alert_type = 'none';
466
+ if ( is_object( $post ) ) {
467
+ $alert = $this->get_alert( $post->ID );
468
+ $alert_type = $alert->alert_type;
469
+ }
470
+ $form = new Form_Generator;
471
+
472
+ $field_html = $form->render_field( 'select', array(
473
+ 'id' => 'wp_stream_alert_type',
474
+ 'name' => 'wp_stream_alert_type',
475
+ 'value' => $alert_type,
476
+ 'options' => $this->get_notification_values(),
477
+ 'placeholder' => __( 'No Alert', 'stream' ),
478
+ 'title' => 'Alert Type:',
479
+ ) );
480
+
481
+ echo '<label>' . esc_html__( 'Alert me by', 'stream' ) . '</label>';
482
+ echo $field_html; // Xss ok.
483
+
484
+ echo '<div id="wp_stream_alert_type_form">';
485
+ if ( is_object( $alert ) ) {
486
+ $alert->get_alert_type_obj()->display_fields( $alert );
487
+ } else {
488
+ $this->plugin->alerts->alert_types['none']->display_fields( array() );
489
+ }
490
+
491
+ echo '</div>';
492
+ }
493
+
494
+ /**
495
+ * Returns settings form HTML for AJAX use
496
+ *
497
+ * @action wp_ajax_load_alerts_settings
498
+ *
499
+ * @return void
500
+ */
501
+ function load_alerts_settings() {
502
+ $alert = array();
503
+ $post_id = wp_stream_filter_input( INPUT_POST, 'post_id' );
504
+ if ( ! empty( $post_id ) ) {
505
+ $alert = $this->get_alert( $post_id );
506
+ if ( ! $alert ) {
507
+ wp_send_json_error( array(
508
+ 'message' => 'Could not find alert.',
509
+ ) );
510
+ }
511
+ }
512
+
513
+ $alert_type = wp_stream_filter_input( INPUT_POST, 'alert_type' );
514
+ if ( empty( $alert_type ) ) {
515
+ $alert_type = 'none';
516
+ }
517
+ if ( ! array_key_exists( $alert_type, $this->alert_types ) ) {
518
+ wp_send_json_error( array(
519
+ 'message' => 'Could not find alert type.',
520
+ ) );
521
+ }
522
+
523
+ ob_start();
524
+ $this->alert_types[ $alert_type ]->display_fields( $alert );
525
+ $output = ob_get_contents();
526
+ ob_end_clean();
527
+
528
+ $data = array( 'html' => $output );
529
+ wp_send_json_success( $data );
530
+ }
531
+
532
+ /**
533
+ * Display Trigger Meta Box
534
+ *
535
+ * @param \WP_Post|array $post Post object for current alert.
536
+ *
537
+ * @return void
538
+ */
539
+ function display_triggers_box( $post = array() ) {
540
+ if ( is_object( $post ) ) {
541
+ $alert = $this->get_alert( $post->ID );
542
+ } else {
543
+ $alert = array();
544
+ }
545
+ $form = new Form_Generator;
546
+ do_action( 'wp_stream_alert_trigger_form_display', $form, $alert );
547
+ // @TODO use human readable text.
548
+ echo '<label>' . esc_html__( 'Alert me when', 'stream' ) . '</label>';
549
+ echo $form->render_fields(); // Xss ok.
550
+ wp_nonce_field( 'save_alert', 'wp_stream_alerts_nonce' );
551
+ }
552
+
553
+ /**
554
+ * Display Submit Box
555
+ *
556
+ * @param \WP_Post $post Post object for current alert.
557
+ *
558
+ * @return void
559
+ */
560
+ function display_submit_box( $post ) {
561
+ if ( empty( $post ) ) {
562
+ return;
563
+ }
564
+
565
+ $post_status = $post->post_status;
566
+ if ( 'auto-draft' === $post_status ) {
567
+ $post_status = 'wp_stream_enabled';
568
+ }
569
+ ?>
570
+ <div class="submitbox" id="submitpost">
571
+ <div id="minor-publishing">
572
+ <div id="misc-publishing-actions">
573
+ <div class="misc-pub-section misc-pub-post-status">
574
+ <label for="wp_stream_alert_status"><?php esc_html_e( 'Status', 'stream' ) ?></label>
575
+ <select name='wp_stream_alert_status' id='wp_stream_alert_status'>
576
+ <option<?php selected( $post_status, 'wp_stream_enabled' ); ?>
577
+ value='wp_stream_enabled'><?php esc_html_e( 'Enabled', 'stream' ) ?></option>
578
+ <option<?php selected( $post_status, 'wp_stream_disabled' ); ?>
579
+ value='wp_stream_disabled'><?php esc_html_e( 'Disabled', 'stream' ) ?></option>
580
+ </select>
581
+ </div>
582
+ </div>
583
+ <div class="clear"></div>
584
+ </div>
585
+
586
+ <div id="major-publishing-actions">
587
+ <div id="delete-action">
588
+ <?php
589
+ if ( current_user_can( 'delete_post', $post->ID ) ) {
590
+ if ( ! EMPTY_TRASH_DAYS ) {
591
+ $delete_text = __( 'Delete Permanently', 'stream' );
592
+ } else {
593
+ $delete_text = __( 'Move to Trash', 'stream' );
594
+ }
595
+ ?>
596
+ <a class="submitdelete deletion"
597
+ href="<?php echo get_delete_post_link( $post->ID ); ?>"><?php esc_html( $delete_text ); ?></a><?php
598
+ } ?>
599
+ </div>
600
+ <div id="publishing-action">
601
+ <span class="spinner"></span>
602
+ <?php submit_button( __( 'Save' ), 'primary button-large', 'publish', false ); ?>
603
+ </div>
604
+ <div class="clear"></div>
605
+ </div>
606
+ </div>
607
+ <?php
608
+ }
609
+
610
+ /**
611
+ * Display Status Box
612
+ *
613
+ * @return void
614
+ */
615
+ function display_status_box() {
616
+ ?>
617
+ <div id="minor-publishing">
618
+ <div id="misc-publishing-actions">
619
+ <div class="misc-pub-section misc-pub-post-status">
620
+ <label for="wp_stream_alert_status">
621
+ <span class="title"><?php esc_html_e( 'Status:', 'stream' ) ?></span>
622
+ <span class="input-text-wrap">
623
+ <select name='wp_stream_alert_status' id='wp_stream_alert_status'>
624
+ <option selected value='wp_stream_enabled'><?php esc_html_e( 'Enabled', 'stream' ) ?></option>
625
+ <option value='wp_stream_disabled'><?php esc_html_e( 'Disabled', 'stream' ) ?></option>
626
+ </select>
627
+ </span>
628
+ </label>
629
+ </div>
630
+ </div>
631
+ <div class="clear"></div>
632
+ </div>
633
+ <?php
634
+ }
635
+
636
+ /**
637
+ * Return all notification values
638
+ *
639
+ * @return array
640
+ */
641
+ function get_notification_values() {
642
+ $result = array();
643
+ $names = wp_list_pluck( $this->alert_types, 'name', 'slug' );
644
+ foreach ( $names as $slug => $name ) {
645
+ $result[ $slug ] = $name;
646
+ }
647
+
648
+ return $result;
649
+ }
650
+
651
+ /**
652
+ * Update actions dropdown options based on the connector selected.
653
+ */
654
+ function get_actions() {
655
+ $connector_name = wp_stream_filter_input( INPUT_POST, 'connector' );
656
+ $stream_connectors = wp_stream_get_instance()->connectors;
657
+ if ( ! empty( $connector_name ) ) {
658
+ if ( isset( $stream_connectors->connectors[ $connector_name ] ) ) {
659
+ $connector = $stream_connectors->connectors[ $connector_name ];
660
+ if ( method_exists( $connector, 'get_action_labels' ) ) {
661
+ $actions = $connector->get_action_labels();
662
+ }
663
+ }
664
+ } else {
665
+ $actions = $stream_connectors->term_labels['stream_action'];
666
+ }
667
+ ksort( $actions );
668
+ wp_send_json_success( $actions );
669
+ }
670
+
671
+ /**
672
+ * Save a new alert
673
+ */
674
+ function save_new_alert() {
675
+ check_ajax_referer( 'save_alert', 'wp_stream_alerts_nonce' );
676
+ $trigger_author = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_author' );
677
+ $trigger_connector_and_context = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_context' );
678
+ if ( false !== strpos( $trigger_connector_and_context, '-' ) ) {
679
+ // This is a connector with a context such as posts-post.
680
+ $trigger_connector_and_context_split = explode( '-', $trigger_connector_and_context );
681
+ $trigger_connector = $trigger_connector_and_context_split[0];
682
+ $trigger_context = $trigger_connector_and_context_split[1];
683
+ } else {
684
+ if ( ! empty( $trigger_connector_and_context ) ) {
685
+ // This is a parent connector with no dash such as posts.
686
+ $trigger_connector = $trigger_connector_and_context;
687
+ $trigger_context = '';
688
+ } else {
689
+ // There is no connector or context.
690
+ $trigger_connector = '';
691
+ $trigger_context = '';
692
+ }
693
+ }
694
+
695
+ $trigger_action = wp_stream_filter_input( INPUT_POST, 'wp_stream_trigger_action' );
696
+ $alert_type = wp_stream_filter_input( INPUT_POST, 'wp_stream_alert_type' );
697
+ $alert_status = wp_stream_filter_input( INPUT_POST, 'wp_stream_alert_status' );
698
+
699
+ // Insert the post into the database
700
+ $item = (object) array(
701
+ 'alert_type' => $alert_type,
702
+ 'alert_meta' => array(
703
+ 'trigger_author' => $trigger_author,
704
+ 'trigger_connector' => $trigger_connector,
705
+ 'trigger_action' => $trigger_action,
706
+ 'trigger_context' => $trigger_context,
707
+ ),
708
+ 'alert_status' => $alert_status,
709
+ );
710
+ $alert = new Alert( $item, $this->plugin );
711
+ $title = $alert->get_title();
712
+ $post_id = wp_insert_post( array(
713
+ 'post_status' => $alert_status,
714
+ 'post_type' => 'wp_stream_alerts',
715
+ 'post_title' => $title,
716
+ ) );
717
+ if ( empty( $post_id ) ) {
718
+ wp_send_json_error();
719
+ }
720
+ add_post_meta( $post_id, 'alert_type', $alert_type );
721
+
722
+ $alert_meta = array(
723
+ 'trigger_author' => $trigger_author,
724
+ 'trigger_connector' => $trigger_connector,
725
+ 'trigger_action' => $trigger_action,
726
+ 'trigger_context' => $trigger_context,
727
+ );
728
+ $alert_meta = apply_filters( 'wp_stream_alerts_save_meta', $alert_meta, $alert_type );
729
+ add_post_meta( $post_id, 'alert_meta', $alert_meta );
730
+ wp_send_json_success( array( 'success' => true ) );
731
+ }
732
+
733
+ /**
734
+ *
735
+ */
736
+ function get_new_alert_triggers_notifications() {
737
+ ob_start();
738
+ ?>
739
+ <fieldset class="inline-edit-col inline-edit-wp_stream_alerts inline-edit-add-new-triggers">
740
+ <legend class="inline-edit-legend">Add New</legend>
741
+ <?php $GLOBALS['wp_stream']->alerts->display_triggers_box(); ?>
742
+ </fieldset>
743
+ <fieldset class="inline-edit-col inline-edit-wp_stream_alerts inline-edit-add-new-notifications">
744
+ <?php $GLOBALS['wp_stream']->alerts->display_notification_box(); ?>
745
+ </fieldset>
746
+ <fieldset class="inline-edit-col inline-edit-wp_stream_alerts inline-edit-add-new-status">
747
+ <?php $GLOBALS['wp_stream']->alerts->display_status_box(); ?>
748
+ </fieldset>
749
+ <?php
750
+ $html = ob_get_clean();
751
+ wp_send_json_success( array( 'success' => true, 'html' => $html ) );
752
+ }
753
+ /**
754
+ * Add action links to Stream drop row in admin list screen
755
+ *
756
+ * @filter wp_stream_action_links_{connector}
757
+ *
758
+ * @param array $links Previous links registered
759
+ * @param Record $record Stream record
760
+ *
761
+ * @return array Action links
762
+ */
763
+ function change_alert_action_links( $links, $record ) {
764
+ $post = get_post( $record->object_id );
765
+
766
+ if ( $post && self::POST_TYPE === $post->post_type && $post->post_status === $record->get_meta( 'new_status', true ) ) {
767
+ if ( 'trash' !== $post->post_status ) {
768
+ $connector_posts = new \WP_Stream\Connector_Posts;
769
+ $post_type_name = $connector_posts->get_post_type_name( get_post_type( $post->ID ) );
770
+
771
+ $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 );
772
+ unset( $links[ esc_html__( 'View', 'stream' ) ] );
773
+ }
774
+ }
775
+ return $links;
776
+ }
777
+ }
classes/class-connectors.php CHANGED
@@ -93,7 +93,7 @@ class Connectors {
93
  continue;
94
  }
95
  if ( $class->is_dependency_satisfied() ) {
96
- $classes[] = $class;
97
  }
98
  }
99
 
@@ -120,25 +120,25 @@ class Connectors {
120
 
121
  foreach ( $this->connectors as $connector ) {
122
  if ( ! method_exists( $connector, 'get_label' ) ) {
123
- $this->plugin->admin->notice( sprintf( __( "%s class wasn't loaded because it doesn't implement the get_label method.", 'stream' ), $connector->name, 'Connector' ), true );
124
  continue;
125
  }
126
  if ( ! method_exists( $connector, 'register' ) ) {
127
- $this->plugin->admin->notice( sprintf( __( "%s class wasn't loaded because it doesn't implement the register method.", 'stream' ), $connector->name, 'Connector' ), true );
128
  continue;
129
  }
130
  if ( ! method_exists( $connector, 'get_context_labels' ) ) {
131
- $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 );
132
  continue;
133
  }
134
  if ( ! method_exists( $connector, 'get_action_labels' ) ) {
135
- $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 );
136
  continue;
137
  }
138
 
139
- // Check if the connectors extends the Connector class, if not skip it
140
  if ( ! is_subclass_of( $connector, '\WP_Stream\Connector' ) ) {
141
- $this->plugin->admin->notice( sprintf( __( "%s class wasn't loaded because it doesn't extends the %s class.", 'stream' ), $connector->name, 'Connector' ), true );
142
  continue;
143
  }
144
 
93
  continue;
94
  }
95
  if ( $class->is_dependency_satisfied() ) {
96
+ $classes[ $class->name ] = $class;
97
  }
98
  }
99
 
120
 
121
  foreach ( $this->connectors as $connector ) {
122
  if ( ! method_exists( $connector, 'get_label' ) ) {
123
+ $this->plugin->admin->notice( sprintf( __( '%s class wasn\'t loaded because it doesn\'t implement the get_label method.', 'stream' ), $connector->name, 'Connector' ), true );
124
  continue;
125
  }
126
  if ( ! method_exists( $connector, 'register' ) ) {
127
+ $this->plugin->admin->notice( sprintf( __( '%s class wasn\'t loaded because it doesn\'t implement the register method.', 'stream' ), $connector->name, 'Connector' ), true );
128
  continue;
129
  }
130
  if ( ! method_exists( $connector, 'get_context_labels' ) ) {
131
+ $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 );
132
  continue;
133
  }
134
  if ( ! method_exists( $connector, 'get_action_labels' ) ) {
135
+ $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 );
136
  continue;
137
  }
138
 
139
+ // Check if the connectors extends the Connector class, if not skip it.
140
  if ( ! is_subclass_of( $connector, '\WP_Stream\Connector' ) ) {
141
+ $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 );
142
  continue;
143
  }
144
 
classes/class-form-generator.php CHANGED
@@ -116,7 +116,7 @@ class Form_Generator {
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'] ),
122
  $this->prepare_data_attributes_string( $args['data'] ),
@@ -133,36 +133,38 @@ class Form_Generator {
133
  'text' => '',
134
  'children' => array(),
135
  ) );
 
 
 
136
  if ( is_array( $args['value'] ) ) {
137
- $selected = selected( in_array( $parent['value'], $args['value'], true ), true, false );
138
  } else {
139
- $selected = selected( $args['value'], $parent['value'], false );
140
  }
141
  $output .= sprintf(
142
  '<option class="parent" value="%1$s" %3$s>%2$s</option>',
143
- $parent['value'],
144
  $parent['text'],
145
  $selected
146
  );
147
- $values[] = $parent['value'];
148
  if ( ! empty( $parent['children'] ) ) {
149
  foreach ( $parent['children'] as $child ) {
150
  $output .= sprintf(
151
  '<option class="child" value="%1$s" %3$s>%2$s</option>',
152
- $child['value'],
153
  $child['text'],
154
- selected( $args['value'], $child['value'], false )
155
  );
156
- $values[] = $child['value'];
157
  }
158
  $output .= '</optgroup>';
159
  }
160
  }
161
 
162
  $selected_values = explode( ',', $args['value'] );
163
-
164
  foreach ( $selected_values as $selected_value ) {
165
- if ( ! empty( $selected_value ) && ! in_array( $selected_value, $values, true ) ) {
166
  $output .= sprintf(
167
  '<option value="%1$s" %2$s>%1$s</option>',
168
  $selected_value,
@@ -194,7 +196,7 @@ class Form_Generator {
194
  /**
195
  * Prepares string with HTML data attributes
196
  *
197
- * @param $data array List of key/value data pairs to prepare.
198
  * @return string
199
  */
200
  public function prepare_data_attributes_string( $data ) {
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'] ),
122
  $this->prepare_data_attributes_string( $args['data'] ),
133
  'text' => '',
134
  'children' => array(),
135
  ) );
136
+ if ( empty( $parent['id'] ) ) {
137
+ continue;
138
+ }
139
  if ( is_array( $args['value'] ) ) {
140
+ $selected = selected( in_array( $parent['id'], $args['value'], true ), true, false );
141
  } else {
142
+ $selected = selected( $args['value'], $parent['id'], false );
143
  }
144
  $output .= sprintf(
145
  '<option class="parent" value="%1$s" %3$s>%2$s</option>',
146
+ $parent['id'],
147
  $parent['text'],
148
  $selected
149
  );
150
+ $values[] = $parent['id'];
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['id'],
156
  $child['text'],
157
+ selected( $args['value'], $child['id'], false )
158
  );
159
+ $values[] = $child['id'];
160
  }
161
  $output .= '</optgroup>';
162
  }
163
  }
164
 
165
  $selected_values = explode( ',', $args['value'] );
 
166
  foreach ( $selected_values as $selected_value ) {
167
+ if ( ! empty( $selected_value ) && ! in_array( $selected_value, array_map( 'strval', $values ), true ) ) {
168
  $output .= sprintf(
169
  '<option value="%1$s" %2$s>%1$s</option>',
170
  $selected_value,
196
  /**
197
  * Prepares string with HTML data attributes
198
  *
199
+ * @param array $data List of key/value data pairs to prepare.
200
  * @return string
201
  */
202
  public function prepare_data_attributes_string( $data ) {
classes/class-install.php CHANGED
@@ -76,8 +76,6 @@ class Install {
76
  * @return void
77
  */
78
  public function check() {
79
- global $wpdb;
80
-
81
  if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
82
  return;
83
  }
@@ -97,15 +95,16 @@ class Install {
97
  if ( ! $update ) {
98
  $this->update_required = true;
99
  $this->success_db = $this->update( $this->db_version, $this->plugin->get_version(), array( 'type' => 'auto' ) );
100
- } elseif ( '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();
105
 
106
- if ( version_compare( end( $versions ), $this->db_version, '>' ) ) {
107
  add_action( 'all_admin_notices', array( $this, 'update_notice_hook' ) );
108
-
109
  return;
110
  }
111
 
@@ -337,6 +336,7 @@ class Install {
337
  $db_update_versions = array(
338
  '3.0.0' /* @version 3.0.0 Drop the stream_context table, changes to stream table */,
339
  '3.0.2' /* @version 3.0.2 Fix uppercase values in stream table, connector column */,
 
340
  );
341
 
342
  /**
@@ -399,7 +399,7 @@ class Install {
399
  blog_id bigint(20) unsigned NOT NULL DEFAULT '1',
400
  object_id bigint(20) unsigned NULL,
401
  user_id bigint(20) unsigned NOT NULL DEFAULT '0',
402
- user_role varchar(20) NOT NULL DEFAULT '',
403
  summary longtext NOT NULL,
404
  created datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
405
  connector varchar(100) NOT NULL,
76
  * @return void
77
  */
78
  public function check() {
 
 
79
  if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
80
  return;
81
  }
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();
105
 
106
+ if ( ! $this->success_db && version_compare( end( $versions ), $this->db_version, '>' ) ) {
107
  add_action( 'all_admin_notices', array( $this, 'update_notice_hook' ) );
 
108
  return;
109
  }
110
 
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
  /**
399
  blog_id bigint(20) unsigned NOT NULL DEFAULT '1',
400
  object_id bigint(20) unsigned NULL,
401
  user_id bigint(20) unsigned NOT NULL DEFAULT '0',
402
+ user_role varchar(50) NOT NULL DEFAULT '',
403
  summary longtext NOT NULL,
404
  created datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
405
  connector varchar(100) NOT NULL,
classes/class-list-table.php CHANGED
@@ -13,7 +13,7 @@ class List_Table extends \WP_List_Table {
13
  * Class constructor.
14
  *
15
  * @param Plugin $plugin The main Plugin class.
16
- * @param array $args
17
  */
18
  function __construct( $plugin, $args = array() ) {
19
  $this->plugin = $plugin;
@@ -308,8 +308,8 @@ class List_Table extends \WP_List_Table {
308
 
309
  default :
310
  /**
311
- * Registers new Columns to be inserted into the table. The cell contents of this column is set
312
- * below with 'wp_stream_inster_column_default-'
313
  *
314
  * @return array
315
  */
@@ -325,7 +325,7 @@ class List_Table extends \WP_List_Table {
325
  * Also, note that the action name must include the $column_title registered
326
  * with wp_stream_register_column_defaults
327
  */
328
- if ( $column_title === $column_name && has_filter( "wp_stream_insert_column_default-{$column_title}" ) ) {
329
  /**
330
  * Allows for the addition of content under a specified column.
331
  *
@@ -333,7 +333,7 @@ class List_Table extends \WP_List_Table {
333
  *
334
  * @return string
335
  */
336
- $out = apply_filters( "wp_stream_insert_column_default-{$column_title}", $column_name, $record );
337
  } else {
338
  $out = $column_name;
339
  }
@@ -869,6 +869,18 @@ class List_Table extends \WP_List_Table {
869
  echo '</form>';
870
  }
871
 
 
 
 
 
 
 
 
 
 
 
 
 
872
  function display_tablenav( $which ) {
873
  if ( 'top' === $which ) : ?>
874
  <div class="tablenav <?php echo esc_attr( $which ); ?>">
13
  * Class constructor.
14
  *
15
  * @param Plugin $plugin The main Plugin class.
16
+ * @param array $args
17
  */
18
  function __construct( $plugin, $args = array() ) {
19
  $this->plugin = $plugin;
308
 
309
  default :
310
  /**
311
+ * Registers new Columns to be inserted into the table. The cell contents of this column is set
312
+ * below with 'wp_stream_insert_column_default_'
313
  *
314
  * @return array
315
  */
325
  * Also, note that the action name must include the $column_title registered
326
  * with wp_stream_register_column_defaults
327
  */
328
+ if ( $column_title === $column_name && has_filter( "wp_stream_insert_column_default_{$column_title}" ) ) {
329
  /**
330
  * Allows for the addition of content under a specified column.
331
  *
333
  *
334
  * @return string
335
  */
336
+ $out = apply_filters( "wp_stream_insert_column_default_{$column_title}", $column_name, $record );
337
  } else {
338
  $out = $column_name;
339
  }
869
  echo '</form>';
870
  }
871
 
872
+ function single_row( $item ) {
873
+ $classes = apply_filters( 'wp_stream_record_classes', array(), $item );
874
+ $class_string = '';
875
+ if ( ! empty( $classes ) ) {
876
+ $class_string = ' class="' . esc_attr( join( ' ', $classes ) ) . '"';
877
+ }
878
+
879
+ echo sprintf( '<tr%s>', $class_string ); // xss ok
880
+ $this->single_row_columns( $item );
881
+ echo '</tr>';
882
+ }
883
+
884
  function display_tablenav( $which ) {
885
  if ( 'top' === $which ) : ?>
886
  <div class="tablenav <?php echo esc_attr( $which ); ?>">
classes/class-live-update.php CHANGED
@@ -184,7 +184,7 @@ class Live_Update {
184
 
185
  if ( isset( $data['wp-stream-heartbeat'] ) && isset( $total_items ) ) {
186
  $response['total_items'] = $total_items;
187
- $response['total_items_i18n'] = sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) );
188
  }
189
 
190
  if ( isset( $data['wp-stream-heartbeat'] ) && 'live-update' === $data['wp-stream-heartbeat'] && $enable_stream_update ) {
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
 
190
  if ( isset( $data['wp-stream-heartbeat'] ) && 'live-update' === $data['wp-stream-heartbeat'] && $enable_stream_update ) {
classes/class-network.php CHANGED
@@ -220,9 +220,11 @@ class Network {
220
  'wp_stream_hidden_option_fields',
221
  array(
222
  'general' => array(
223
- 'delete_all_records',
224
  'records_ttl',
225
  ),
 
 
 
226
  )
227
  );
228
 
@@ -479,7 +481,7 @@ class Network {
479
  */
480
  public function network_admin_page_title( $page_title ) {
481
  if ( is_network_admin() ) {
482
- $site_count = sprintf( _n( '1 site', '%s sites', get_blog_count(), 'stream' ), number_format( get_blog_count() ) );
483
  $page_title = sprintf( '%s (%s)', $page_title, $site_count );
484
  }
485
 
220
  'wp_stream_hidden_option_fields',
221
  array(
222
  'general' => array(
 
223
  'records_ttl',
224
  ),
225
+ 'advanced' => array(
226
+ 'delete_all_records',
227
+ ),
228
  )
229
  );
230
 
481
  */
482
  public function network_admin_page_title( $page_title ) {
483
  if ( is_network_admin() ) {
484
+ $site_count = sprintf( _n( '%d site', '%d sites', get_blog_count(), 'stream' ), number_format( get_blog_count() ) );
485
  $page_title = sprintf( '%s (%s)', $page_title, $site_count );
486
  }
487
 
classes/class-plugin.php CHANGED
@@ -7,7 +7,7 @@ class Plugin {
7
  *
8
  * @const string
9
  */
10
- const VERSION = '3.0.7';
11
 
12
  /**
13
  * WP-CLI command
@@ -21,6 +21,16 @@ class Plugin {
21
  */
22
  public $admin;
23
 
 
 
 
 
 
 
 
 
 
 
24
  /**
25
  * @var Connectors
26
  */
@@ -98,7 +108,7 @@ class Plugin {
98
  add_action( 'wp_head', array( $this, 'frontend_indicator' ) );
99
 
100
  // Load admin area classes
101
- if ( is_admin() || ( defined( 'WP_STREAM_DEV_DEBUG' ) && WP_STREAM_DEV_DEBUG ) ) {
102
  $this->admin = new Admin( $this );
103
  $this->install = new Install( $this );
104
  } elseif ( defined( 'DOING_CRON' ) && DOING_CRON ) {
@@ -150,13 +160,16 @@ class Plugin {
150
  }
151
 
152
  /*
153
- * Load Settings and Connectors
154
  *
155
  * @action init
156
  */
157
  public function init() {
158
- $this->settings = new Settings( $this );
159
- $this->connectors = new Connectors( $this );
 
 
 
160
  }
161
 
162
  /**
7
  *
8
  * @const string
9
  */
10
+ const VERSION = '3.1';
11
 
12
  /**
13
  * WP-CLI command
21
  */
22
  public $admin;
23
 
24
+ /**
25
+ * @var Alerts
26
+ */
27
+ public $alerts;
28
+
29
+ /**
30
+ * @var Alerts_List
31
+ */
32
+ public $alerts_list;
33
+
34
  /**
35
  * @var Connectors
36
  */
108
  add_action( 'wp_head', array( $this, 'frontend_indicator' ) );
109
 
110
  // Load admin area classes
111
+ if ( is_admin() || ( defined( 'WP_STREAM_DEV_DEBUG' ) && WP_STREAM_DEV_DEBUG ) || ( defined( 'WP_CLI' ) && WP_CLI ) ) {
112
  $this->admin = new Admin( $this );
113
  $this->install = new Install( $this );
114
  } elseif ( defined( 'DOING_CRON' ) && DOING_CRON ) {
160
  }
161
 
162
  /*
163
+ * Load Settings, Notifications, and Connectors
164
  *
165
  * @action init
166
  */
167
  public function init() {
168
+ $this->settings = new Settings( $this );
169
+ $this->connectors = new Connectors( $this );
170
+ $this->alerts = new Alerts( $this );
171
+ $this->alerts_list = new Alerts_List( $this );
172
+
173
  }
174
 
175
  /**
classes/class-preview-list-table.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WP_Stream;
3
+
4
+ class Preview_List_Table extends List_Table {
5
+
6
+ /**
7
+ * Class constructor.
8
+ *
9
+ * @param Plugin $plugin Plugin object.
10
+ * @return void
11
+ */
12
+ public function __construct( $plugin ) {
13
+ $this->plugin = $plugin;
14
+ parent::__construct( $plugin );
15
+ }
16
+
17
+ /**
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
+ }
33
+
34
+ /**
35
+ * Display the table
36
+ *
37
+ * @since 3.1.0
38
+ * @access public
39
+ */
40
+ public function display() {
41
+ ?>
42
+ <table class="wp-list-table <?php esc_attr( implode( ' ', $this->get_table_classes() ) ); ?>">
43
+ <thead>
44
+ <tr>
45
+ <?php $this->print_column_headers(); ?>
46
+ </tr>
47
+ </thead>
48
+
49
+ <tbody id="the-list">
50
+ <?php $this->display_rows_or_placeholder(); ?>
51
+ </tbody>
52
+
53
+ <tfoot>
54
+ <tr>
55
+ <?php $this->print_column_headers( false ); ?>
56
+ </tr>
57
+ </tfoot>
58
+
59
+ </table>
60
+ <?php
61
+ }
62
+ }
classes/class-settings.php CHANGED
@@ -143,7 +143,7 @@ class Settings {
143
 
144
  $args['tooltip'] = esc_attr(
145
  sprintf(
146
- __( "ID: %d\nUser: %s\nEmail: %s\nRole: %s", 'stream' ),
147
  $author->id,
148
  $author->user_login,
149
  $author->user_email,
@@ -263,7 +263,7 @@ class Settings {
263
  'name' => 'records_ttl',
264
  'title' => esc_html__( 'Keep Records for', 'stream' ),
265
  'type' => 'number',
266
- 'class' => 'small-text hidden',
267
  'desc' => esc_html__( 'Maximum number of days to keep activity records.', 'stream' ),
268
  'default' => 30,
269
  'min' => 1,
@@ -800,7 +800,7 @@ class Settings {
800
  $count = isset( $users['avail_roles'][ $role_id ] ) ? $users['avail_roles'][ $role_id ] : 0;
801
 
802
  if ( ! empty( $count ) ) {
803
- $args['user_count'] = sprintf( _n( '1 user', '%s users', absint( $count ), 'stream' ), absint( $count ) );
804
  }
805
 
806
  if ( $role_id === $author_or_role ) {
143
 
144
  $args['tooltip'] = esc_attr(
145
  sprintf(
146
+ __( 'ID: %1$d\nUser: %2$s\nEmail: %3$s\nRole: %4$s', 'stream' ),
147
  $author->id,
148
  $author->user_login,
149
  $author->user_email,
263
  'name' => 'records_ttl',
264
  'title' => esc_html__( 'Keep Records for', 'stream' ),
265
  'type' => 'number',
266
+ 'class' => 'small-text',
267
  'desc' => esc_html__( 'Maximum number of days to keep activity records.', 'stream' ),
268
  'default' => 30,
269
  'min' => 1,
800
  $count = isset( $users['avail_roles'][ $role_id ] ) ? $users['avail_roles'][ $role_id ] : 0;
801
 
802
  if ( ! empty( $count ) ) {
803
+ $args['user_count'] = sprintf( _n( '%d user', '%d users', absint( $count ), 'stream' ), absint( $count ) );
804
  }
805
 
806
  if ( $role_id === $author_or_role ) {
connectors/class-connector-buddypress.php CHANGED
@@ -673,7 +673,7 @@ class Connector_BuddyPress extends Connector {
673
  'mod' => esc_html_x( 'Moderator', 'buddypress', 'stream' ),
674
  );
675
  $message = sprintf(
676
- __( 'Promoted "%s" to "%s" in "%s"', 'stream' ),
677
  $user->display_name,
678
  $roles[ $status ],
679
  $group->name
@@ -685,7 +685,7 @@ class Connector_BuddyPress extends Connector {
685
  $group = \groups_get_group( array( 'group_id' => $group_id ) );
686
  $user = new \WP_User( $user_id );
687
  $message = sprintf(
688
- __( 'Demoted "%s" to "%s" in "%s"', 'stream' ),
689
  $user->display_name,
690
  _x( 'Member', 'buddypress', 'stream' ),
691
  $group->name
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 ],
679
  $group->name
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' ),
691
  $group->name
connectors/class-connector-gravityforms.php CHANGED
@@ -481,7 +481,7 @@ class Connector_GravityForms extends Connector {
481
  $forms_titles = wp_list_pluck( $forms, 'title' );
482
 
483
  $this->log(
484
- __( '%d %s imported', 'stream' ),
485
  array(
486
  'count' => $forms_total,
487
  'label' => $forms_label,
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,
connectors/class-connector-user-switching.php CHANGED
@@ -8,7 +8,7 @@ class Connector_User_Switching extends Connector {
8
  *
9
  * @var string
10
  */
11
- public $name = 'user-switching';
12
 
13
  /**
14
  * Actions registered for this connector
8
  *
9
  * @var string
10
  */
11
+ public $name = 'userswitching';
12
 
13
  /**
14
  * Actions registered for this connector
connectors/class-connector-woocommerce.php CHANGED
@@ -54,6 +54,8 @@ class Connector_Woocommerce extends Connector {
54
 
55
  private $settings = array();
56
 
 
 
57
  public function register() {
58
  parent::register();
59
 
@@ -72,6 +74,7 @@ class Connector_Woocommerce extends Connector {
72
  global $woocommerce;
73
 
74
  if ( class_exists( 'WooCommerce' ) && version_compare( $woocommerce->version, self::PLUGIN_MIN_VERSION, '>=' ) ) {
 
75
  return true;
76
  }
77
 
@@ -413,14 +416,21 @@ class Connector_Woocommerce extends Connector {
413
  return;
414
  }
415
 
416
- $old_status = wp_stream_is_vip() ? wpcom_vip_get_term_by( 'slug', $old, 'shop_order_status' ) : get_term_by( 'slug', $old, 'shop_order_status' );
417
- $new_status = wp_stream_is_vip() ? wpcom_vip_get_term_by( 'slug', $new, 'shop_order_status' ) : get_term_by( 'slug', $new, 'shop_order_status' );
418
-
419
  // Don't track new statuses
420
- if ( ! $old_status ) {
421
  return;
422
  }
423
 
 
 
 
 
 
 
 
 
 
 
424
  $message = esc_html_x(
425
  '%1$s status changed from %2$s to %3$s',
426
  '1. Order title, 2. Old status, 3. New status',
@@ -430,8 +440,6 @@ class Connector_Woocommerce extends Connector {
430
  $order = new \WC_Order( $order_id );
431
  $order_title = esc_html__( 'Order number', 'stream' ) . ' ' . esc_html( $order->get_order_number() );
432
  $order_type_name = esc_html__( 'order', 'stream' );
433
- $new_status_name = strtolower( $new_status->name );
434
- $old_status_name = strtolower( $old_status->name );
435
 
436
  $this->log(
437
  $message,
@@ -446,7 +454,7 @@ class Connector_Woocommerce extends Connector {
446
  ),
447
  $order_id,
448
  'shop_order',
449
- $new_status_name
450
  );
451
  }
452
 
@@ -751,7 +759,7 @@ class Connector_Woocommerce extends Connector {
751
  $shipping_method_settings = array();
752
  $shipping_methods = $woocommerce->shipping();
753
 
754
- foreach ( $shipping_methods->shipping_methods as $section_key => $shipping_method ) {
755
  $title = $shipping_method->title;
756
  $key = $shipping_method->plugin_id . $shipping_method->id . '_settings';
757
 
54
 
55
  private $settings = array();
56
 
57
+ private $plugin_version = null;
58
+
59
  public function register() {
60
  parent::register();
61
 
74
  global $woocommerce;
75
 
76
  if ( class_exists( 'WooCommerce' ) && version_compare( $woocommerce->version, self::PLUGIN_MIN_VERSION, '>=' ) ) {
77
+ $this->plugin_version = $woocommerce->version;
78
  return true;
79
  }
80
 
416
  return;
417
  }
418
 
 
 
 
419
  // Don't track new statuses
420
+ if ( empty( $old ) ) {
421
  return;
422
  }
423
 
424
+ if ( version_compare( $this->plugin_version, '2.2', '>=' ) ) {
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',
440
  $order = new \WC_Order( $order_id );
441
  $order_title = esc_html__( 'Order number', 'stream' ) . ' ' . esc_html( $order->get_order_number() );
442
  $order_type_name = esc_html__( 'order', 'stream' );
 
 
443
 
444
  $this->log(
445
  $message,
454
  ),
455
  $order_id,
456
  'shop_order',
457
+ 'updated'
458
  );
459
  }
460
 
759
  $shipping_method_settings = array();
760
  $shipping_methods = $woocommerce->shipping();
761
 
762
+ foreach ( (array) $shipping_methods->shipping_methods as $section_key => $shipping_method ) {
763
  $title = $shipping_method->title;
764
  $key = $shipping_method->plugin_id . $shipping_method->id . '_settings';
765
 
connectors/class-connector-wordpress-seo.php CHANGED
@@ -8,7 +8,7 @@ class Connector_WordPress_SEO extends Connector {
8
  *
9
  * @var string
10
  */
11
- public $name = 'wordpress-seo';
12
 
13
  /**
14
  * Holds tracked plugin minimum version required
@@ -89,6 +89,7 @@ class Connector_WordPress_SEO extends Connector {
89
  'wpseo_xml' => esc_html_x( 'XML Sitemaps', 'wordpress-seo', 'stream' ),
90
  'wpseo_permalinks' => esc_html_x( 'Permalinks', 'wordpress-seo', 'stream' ),
91
  'wpseo_internal-links' => esc_html_x( 'Internal Links', 'wordpress-seo', 'stream' ),
 
92
  'wpseo_rss' => esc_html_x( 'RSS', 'wordpress-seo', 'stream' ),
93
  'wpseo_import' => esc_html_x( 'Import & Export', 'wordpress-seo', 'stream' ),
94
  'wpseo_bulk-title-editor' => esc_html_x( 'Bulk Title Editor', 'wordpress-seo', 'stream' ),
@@ -176,6 +177,9 @@ class Connector_WordPress_SEO extends Connector {
176
  }
177
 
178
  public function register() {
 
 
 
179
  parent::register();
180
 
181
  foreach ( \WPSEO_Options::$options as $class ) {
@@ -395,7 +399,7 @@ class Connector_WordPress_SEO extends Connector {
395
  'pinterestverify' => esc_html_x( 'Pinterest', 'wordpress-seo', 'stream' ), # type = textinput
396
  'yandexverify' => esc_html_x( 'Yandex Webmaster Tools', 'wordpress-seo', 'stream' ), # type = textinput
397
 
398
- // wp-content/plugins/wordpress-seo/admin/pages/internal-links.php:
399
  'breadcrumbs-enable' => esc_html_x( 'Enable Breadcrumbs', 'wordpress-seo', 'stream' ), # type = checkbox
400
  'breadcrumbs-sep' => esc_html_x( 'Separator between breadcrumbs', 'wordpress-seo', 'stream' ), # type = textinput
401
  'breadcrumbs-home' => esc_html_x( 'Anchor text for the Homepage', 'wordpress-seo', 'stream' ), # type = textinput
@@ -405,6 +409,7 @@ class Connector_WordPress_SEO extends Connector {
405
  'breadcrumbs-404crumb' => esc_html_x( 'Breadcrumb for 404 Page', 'wordpress-seo', 'stream' ), # type = textinput
406
  'breadcrumbs-blog-remove' => esc_html_x( 'Remove Blog page from Breadcrumbs', 'wordpress-seo', 'stream' ), # type = checkbox
407
  'breadcrumbs-boldlast' => esc_html_x( 'Bold the last page in the breadcrumb', 'wordpress-seo', 'stream' ), # type = checkbox
 
408
 
409
  // wp-content/plugins/wordpress-seo/admin/pages/metas.php:
410
  'forcerewritetitle' => esc_html_x( 'Force rewrite titles', 'wordpress-seo', 'stream' ), # type = checkbox
@@ -439,21 +444,41 @@ class Connector_WordPress_SEO extends Connector {
439
  // wp-content/plugins/wordpress-seo/admin/pages/social.php:
440
  'opengraph' => esc_html_x( 'Add Open Graph meta data', 'wordpress-seo', 'stream' ), # type = checkbox
441
  'facebook_site' => esc_html_x( 'Facebook Page URL', 'wordpress-seo', 'stream' ), # type = textinput
 
 
 
 
 
 
442
  'og_frontpage_image' => esc_html_x( 'Image URL', 'wordpress-seo', 'stream' ), # type = textinput
443
  'og_frontpage_desc' => esc_html_x( 'Description', 'wordpress-seo', 'stream' ), # type = textinput
 
444
  'og_default_image' => esc_html_x( 'Image URL', 'wordpress-seo', 'stream' ), # type = textinput
445
  'twitter' => esc_html_x( 'Add Twitter card meta data', 'wordpress-seo', 'stream' ), # type = checkbox
446
  'twitter_site' => esc_html_x( 'Site Twitter Username', 'wordpress-seo', 'stream' ), # type = textinput
447
  'twitter_card_type' => esc_html_x( 'The default card type to use', 'wordpress-seo', 'stream' ), # type = select
448
  'googleplus' => esc_html_x( 'Add Google+ specific post meta data (excluding author metadata)', 'wordpress-seo', 'stream' ), # type = checkbox
449
  'plus-publisher' => esc_html_x( 'Google Publisher Page', 'wordpress-seo', 'stream' ), # type = textinput
 
450
 
451
  // wp-content/plugins/wordpress-seo/admin/pages/xml-sitemaps.php:
452
- 'enablexmlsitemap' => esc_html_x( 'Check this box to enable XML sitemap functionality.', 'wordpress-seo', 'stream' ), # type = checkbox
453
- 'disable_author_sitemap' => esc_html_x( 'Disable author/user sitemap', 'wordpress-seo', 'stream' ), # type = checkbox
454
- 'xml_ping_yahoo' => esc_html_x( 'Ping Yahoo!', 'wordpress-seo', 'stream' ), # type = checkbox
455
- 'xml_ping_ask' => esc_html_x( 'Ping Ask.com', 'wordpress-seo', 'stream' ), # type = checkbox
456
- 'entries-per-page' => esc_html_x( 'Max entries per sitemap page', 'wordpress-seo', 'stream' ), # type = textinput
 
 
 
 
 
 
 
 
 
 
 
 
457
 
458
  // Added manually
459
  'rssbefore' => esc_html_x( 'Content to put before each post in the feed', 'wordpress-seo', 'stream' ),
8
  *
9
  * @var string
10
  */
11
+ public $name = 'wordpressseo';
12
 
13
  /**
14
  * Holds tracked plugin minimum version required
89
  'wpseo_xml' => esc_html_x( 'XML Sitemaps', 'wordpress-seo', 'stream' ),
90
  'wpseo_permalinks' => esc_html_x( 'Permalinks', 'wordpress-seo', 'stream' ),
91
  'wpseo_internal-links' => esc_html_x( 'Internal Links', 'wordpress-seo', 'stream' ),
92
+ 'wpseo_advanced' => esc_html_x( 'Advanced', 'wordpress-seo', 'stream' ),
93
  'wpseo_rss' => esc_html_x( 'RSS', 'wordpress-seo', 'stream' ),
94
  'wpseo_import' => esc_html_x( 'Import & Export', 'wordpress-seo', 'stream' ),
95
  'wpseo_bulk-title-editor' => esc_html_x( 'Bulk Title Editor', 'wordpress-seo', 'stream' ),
177
  }
178
 
179
  public function register() {
180
+ if ( is_network_admin() && ! is_plugin_active_for_network( 'wordpress-seo/wordpress-seo-main.php' ) ) {
181
+ return;
182
+ }
183
  parent::register();
184
 
185
  foreach ( \WPSEO_Options::$options as $class ) {
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
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
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
466
+ 'disable_author_sitemap' => esc_html_x( 'Disable author/user sitemap', 'wordpress-seo', 'stream' ), # type = checkbox
467
+ 'disable_author_noposts' => esc_html_x( 'Users with zero posts', 'wordpress-seo', 'stream' ), # type = checkbox
468
+ 'user_role-administrator-not_in_sitemap' => esc_html_x( 'Filter specific user roles - Administrator', 'wordpress-seo', 'stream' ), # type = checkbox
469
+ 'user_role-editor-not_in_sitemap' => esc_html_x( 'Filter specific user roles - Editor', 'wordpress-seo', 'stream' ), # type = checkbox
470
+ 'user_role-author-not_in_sitemap' => esc_html_x( 'Filter specific user roles - Author', 'wordpress-seo', 'stream' ), # type = checkbox
471
+ 'user_role-contributor-not_in_sitemap' => esc_html_x( 'Filter specific user roles - Contributor', 'wordpress-seo', 'stream' ), # type = checkbox
472
+ 'user_role-subscriber-not_in_sitemap' => esc_html_x( 'Filter specific user roles - Subscriber', 'wordpress-seo', 'stream' ), # type = checkbox
473
+ 'xml_ping_yahoo' => esc_html_x( 'Ping Yahoo!', 'wordpress-seo', 'stream' ), # type = checkbox
474
+ 'xml_ping_ask' => esc_html_x( 'Ping Ask.com', 'wordpress-seo', 'stream' ), # type = checkbox
475
+ 'entries-per-page' => esc_html_x( 'Max entries per sitemap page', 'wordpress-seo', 'stream' ), # type = textinput
476
+ 'excluded-posts' => esc_html_x( 'Posts to exclude', 'wordpress-seo', 'stream' ), # type = textinput
477
+ 'post_types-post-not_in_sitemap' => _x( 'Post Types Posts (<code>post</code>)', 'wordpress-seo', 'stream' ), # type = checkbox
478
+ 'post_types-page-not_in_sitemap' => _x( 'Post Types Pages (<code>page</code>)', 'wordpress-seo', 'stream' ), # type = checkbox
479
+ 'post_types-attachment-not_in_sitemap' => _x( 'Post Types Media (<code>attachment</code>)', 'wordpress-seo', 'stream' ), # type = checkbox
480
+ 'taxonomies-category-not_in_sitemap' => _x( 'Taxonomies Categories (<code>category</code>)', 'wordpress-seo', 'stream' ), # type = checkbox
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' ),
exporters/class-exporter-csv.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  namespace WP_Stream;
3
 
4
- class Exporter_CSV extends Exporter{
5
  /**
6
  * Exporter name
7
  *
1
  <?php
2
  namespace WP_Stream;
3
 
4
+ class Exporter_CSV extends Exporter {
5
  /**
6
  * Exporter name
7
  *
includes/db-updates.php CHANGED
@@ -1,4 +1,21 @@
1
  <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  /**
3
  * Version 3.0.2
4
  *
1
  <?php
2
+ /**
3
+ * Version 3.0.8
4
+ *
5
+ * Force update for older versions to call \dbdelta in install() method to fix column widths.
6
+ *
7
+ * @param string $db_version
8
+ * @param string $current_version
9
+ *
10
+ * @return string
11
+ */
12
+ function wp_stream_update_auto_308( $db_version, $current_version ) {
13
+ $plugin = wp_stream_get_instance();
14
+ $plugin->install->install( $current_version );
15
+
16
+ return $current_version;
17
+ }
18
+
19
  /**
20
  * Version 3.0.2
21
  *
readme.txt CHANGED
@@ -1,9 +1,9 @@
1
  === Stream ===
2
- Contributors: fjarrett, lukecarbis, chacha102, stream, xwp
3
  Tags: actions, activity, activity log, activity logs, admin actions, analytics, audit, audit log, audit logs, blackbox, black box, change, changes, dashboard, log, logs, stream, tracking, troubleshooting, wp stream
4
  Requires at least: 3.9
5
  Tested up to: 4.6
6
- Stable tag: 3.0.7
7
  License: GPLv2 or later
8
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -79,6 +79,18 @@ Thank you for wanting to make Stream better for everyone!
79
 
80
  == Changelog ==
81
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  = 3.0.7 - June 14, 2016 =
83
 
84
  * Tweak: Use get_sites instead of wp_get_sites when available ([#856](https://github.com/xwp/stream/pull/856))
1
  === Stream ===
2
+ Contributors: fjarrett, lukecarbis, stream, xwp
3
  Tags: actions, activity, activity log, activity logs, admin actions, analytics, audit, audit log, audit logs, blackbox, black box, change, changes, dashboard, log, logs, stream, tracking, troubleshooting, wp stream
4
  Requires at least: 3.9
5
  Tested up to: 4.6
6
+ Stable tag: 3.1
7
  License: GPLv2 or later
8
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
9
 
79
 
80
  == Changelog ==
81
 
82
+ = 3.1 - October 31, 2016 =
83
+
84
+ * New: Stream Alerts is here! Get notified when something happens in your WP-Admin, so that you don't miss a thing. ([#844](https://github.com/xwp/stream/pull/844))
85
+ * Tweak: Better support for the latest version of Yoast SEO ([#838](https://github.com/xwp/stream/pull/838))
86
+ * Tweak: Better support for the latest version of WooCommerce ([#851](https://github.com/xwp/stream/pull/851)[#864](https://github.com/xwp/stream/pull/864))
87
+ * Tweak: Better taxonomy labelling ([#859](https://github.com/xwp/stream/pull/859))
88
+ * Fix: Fatal error caused by conflict with Yoast SEO ([#879](https://github.com/xwp/stream/pull/879))
89
+ * Fix: Activating Stream through WP CLI now works ([#880](https://github.com/xwp/stream/pull/880))
90
+ * Fix: Custom roles track properly ([#836](https://github.com/xwp/stream/pull/836))
91
+
92
+ Props [@chacha](https://github.com/chacha), [@lukecarbis](https://github.com/lukecarbis), [@johnbillion](https://github.com/johnbillion), [@rheinardkorf](https://github.com/rheinardkorf), [@frozzare](https://github.com/frozzare), [@johnregan3](https://github.com/johnregan3), [@jacobschweitzer](https://github.com/jacobschweitzer), [@wrongware](https://github.com/wrongware)
93
+
94
  = 3.0.7 - June 14, 2016 =
95
 
96
  * Tweak: Use get_sites instead of wp_get_sites when available ([#856](https://github.com/xwp/stream/pull/856))
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.0.7
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.1
7
  * Author: XWP
8
  * Author URI: https://xwp.co/
9
  * License: GPLv2+
ui/css/admin.css CHANGED
@@ -18,28 +18,28 @@
18
  overflow: visible;
19
  }
20
 
21
- .wp_stream_screen .select2 .select2-selection {
22
  border-color: #ccc;
23
  background: #f7f7f7;
24
  -webkit-box-shadow: 0 1px 0 #ccc;
25
  box-shadow: 0 1px 0 #ccc;
26
  }
27
 
28
- .wp_stream_screen .select2 .select2-selection--multiple {
29
  font-size: 0;
30
  min-height: 28px;
31
  }
32
 
33
- .wp_stream_screen .select2-container.select2-container--focus .select2-selection--multiple {
34
  border: solid #ccc 1px;
35
  }
36
 
37
- .wp_stream_screen .select2-container .select2-selection--multiple .select2-selection__choice {
38
  margin-top: 4px;
39
  margin-bottom: 3px;
40
  }
41
 
42
- .wp_stream_screen .select2 .select2-selection .select2-selection__rendered {
43
  color: #555;
44
  }
45
 
@@ -106,7 +106,7 @@
106
  visibility: hidden;
107
  }
108
 
109
- .toplevel_page_wp_stream td.summary:hover .stream-filter-object-id {
110
  visibility: visible;
111
  }
112
 
@@ -273,44 +273,44 @@
273
  margin-right: 1em;
274
  }
275
 
276
- .wp_stream_screen a.warning {
277
  color: #a00;
278
  }
279
 
280
- .wp_stream_screen a.warning:hover {
281
  color: #f00;
282
  }
283
 
284
  /* Date Interval Common */
285
 
286
- .wp_stream_screen .date-interval .field-predefined {
287
  width: 165px;
288
  float: left;
289
  margin-right: 6px;
290
  margin-bottom: 6px;
291
  }
292
 
293
- .wp_stream_screen .date-interval .date-inputs {
294
  float: left;
295
  margin-right: 6px;
296
  margin-bottom: 6px;
297
  }
298
 
299
- .wp_stream_screen .date-interval .date-inputs .field-to,
300
- .wp_stream_screen .date-interval .date-inputs .field-from {
301
  line-height: 28px;
302
  height: 28px;
303
  margin: 0;
304
  width: 125px;
305
  }
306
 
307
- .wp_stream_screen .date-interval .date-inputs .box {
308
  position: relative;
309
  display: block;
310
  float: left;
311
  }
312
 
313
- .wp_stream_screen .date-interval .date-inputs .box .date-remove {
314
  display: none;
315
  position: absolute;
316
  cursor: pointer;
@@ -324,11 +324,11 @@
324
  padding-right: 2px;
325
  }
326
 
327
- .wp_stream_screen .date-interval .date-inputs .box .date-remove:before {
328
  content: '\f158';
329
  }
330
 
331
- .wp_stream_screen .date-interval .date-inputs .connector {
332
  display: block;
333
  float: left;
334
  border: 1px solid #ddd;
@@ -342,53 +342,53 @@
342
  font-size: 14px;
343
  }
344
 
345
- .wp_stream_screen .date-interval .date-inputs .connector:before {
346
  content: '\f345';
347
  }
348
 
349
 
350
  /* Select2 Common */
351
 
352
- .wp_stream_screen li.select2-searching,
353
- .wp_stream_screen li.select2-no-results {
354
  background: none;
355
  padding: 7px 7px 0;
356
  color: #999;
357
  }
358
 
359
- .wp_stream_screen .select2 .select2-selection .select2-selection__placeholder {
360
  color: #72777c;
361
  }
362
 
363
- .wp_stream_screen .select2-results .select2-disabled {
364
  background: transparent;
365
  color: #aaa;
366
  }
367
 
368
- .wp_stream_screen .select2 .select2-search--inline {
369
  float: none;
370
  margin-bottom: 4px;
371
  margin-left: 2px;
372
  }
373
 
374
- .wp_stream_screen .select2-selection .icon16 {
375
  margin: -3px 1px 0 -3px;
376
  padding: 0;
377
  width: 16px;
378
  height: 16px;
379
  }
380
 
381
- .wp_stream_screen .select2-selection .icon16 {
382
  padding-right: 8px;
383
  }
384
 
385
- .wp_stream_screen .select2-chosen .icon16:before,
386
- .wp_stream_screen .select2-search-choice .icon16:before {
387
  font-size: 15px !important;
388
  color: #656565;
389
  }
390
 
391
- .wp_stream_screen .select2-search-choice-close {
392
  -webkit-transition: none;
393
  -moz-transition: none;
394
  -o-transition: all 0 none;
@@ -470,11 +470,11 @@
470
  border: 1px solid rgba(160,0,0,0.75);
471
  }
472
 
473
- .wp_stream_screen .select2-results__option .parent {
474
  font-weight: bold;
475
  }
476
 
477
- .wp_stream_screen .select2-results__option .child {
478
  padding-left: 8px;
479
  }
480
 
@@ -532,6 +532,22 @@
532
  }
533
 
534
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
535
  /* Extensions */
536
 
537
  .post-type-stream_notification .view-switch {
18
  overflow: visible;
19
  }
20
 
21
+ .post-type-wp_stream_alerts .select2 .select2-selection {
22
  border-color: #ccc;
23
  background: #f7f7f7;
24
  -webkit-box-shadow: 0 1px 0 #ccc;
25
  box-shadow: 0 1px 0 #ccc;
26
  }
27
 
28
+ .post-type-wp_stream_alerts .select2 .select2-selection--multiple {
29
  font-size: 0;
30
  min-height: 28px;
31
  }
32
 
33
+ .post-type-wp_stream_alerts .select2-container.select2-container--focus .select2-selection--multiple {
34
  border: solid #ccc 1px;
35
  }
36
 
37
+ .post-type-wp_stream_alerts .select2-container .select2-selection--multiple .select2-selection__choice {
38
  margin-top: 4px;
39
  margin-bottom: 3px;
40
  }
41
 
42
+ .post-type-wp_stream_alerts .select2 .select2-selection .select2-selection__rendered {
43
  color: #555;
44
  }
45
 
106
  visibility: hidden;
107
  }
108
 
109
+ .toplevel_page_wp_stream td.summary:hover .stream-filter-object-id{
110
  visibility: visible;
111
  }
112
 
273
  margin-right: 1em;
274
  }
275
 
276
+ .post-type-wp_stream_alerts a.warning {
277
  color: #a00;
278
  }
279
 
280
+ .post-type-wp_stream_alerts a.warning:hover {
281
  color: #f00;
282
  }
283
 
284
  /* Date Interval Common */
285
 
286
+ .post-type-wp_stream_alerts .date-interval .field-predefined {
287
  width: 165px;
288
  float: left;
289
  margin-right: 6px;
290
  margin-bottom: 6px;
291
  }
292
 
293
+ .post-type-wp_stream_alerts .date-interval .date-inputs {
294
  float: left;
295
  margin-right: 6px;
296
  margin-bottom: 6px;
297
  }
298
 
299
+ .post-type-wp_stream_alerts .date-interval .date-inputs .field-to,
300
+ .post-type-wp_stream_alerts .date-interval .date-inputs .field-from {
301
  line-height: 28px;
302
  height: 28px;
303
  margin: 0;
304
  width: 125px;
305
  }
306
 
307
+ .post-type-wp_stream_alerts .date-interval .date-inputs .box {
308
  position: relative;
309
  display: block;
310
  float: left;
311
  }
312
 
313
+ .post-type-wp_stream_alerts .date-interval .date-inputs .box .date-remove {
314
  display: none;
315
  position: absolute;
316
  cursor: pointer;
324
  padding-right: 2px;
325
  }
326
 
327
+ .post-type-wp_stream_alerts .date-interval .date-inputs .box .date-remove:before {
328
  content: '\f158';
329
  }
330
 
331
+ .post-type-wp_stream_alerts .date-interval .date-inputs .connector {
332
  display: block;
333
  float: left;
334
  border: 1px solid #ddd;
342
  font-size: 14px;
343
  }
344
 
345
+ .post-type-wp_stream_alerts .date-interval .date-inputs .connector:before {
346
  content: '\f345';
347
  }
348
 
349
 
350
  /* Select2 Common */
351
 
352
+ .post-type-wp_stream_alerts li.select2-searching,
353
+ .post-type-wp_stream_alerts li.select2-no-results {
354
  background: none;
355
  padding: 7px 7px 0;
356
  color: #999;
357
  }
358
 
359
+ .post-type-wp_stream_alerts .select2 .select2-selection .select2-selection__placeholder {
360
  color: #72777c;
361
  }
362
 
363
+ .post-type-wp_stream_alerts .select2-results .select2-disabled {
364
  background: transparent;
365
  color: #aaa;
366
  }
367
 
368
+ .post-type-wp_stream_alerts .select2 .select2-search--inline {
369
  float: none;
370
  margin-bottom: 4px;
371
  margin-left: 2px;
372
  }
373
 
374
+ .post-type-wp_stream_alerts .select2-selection .icon16 {
375
  margin: -3px 1px 0 -3px;
376
  padding: 0;
377
  width: 16px;
378
  height: 16px;
379
  }
380
 
381
+ .post-type-wp_stream_alerts .select2-selection .icon16 {
382
  padding-right: 8px;
383
  }
384
 
385
+ .post-type-wp_stream_alerts .select2-chosen .icon16:before,
386
+ .post-type-wp_stream_alerts .select2-search-choice .icon16:before {
387
  font-size: 15px !important;
388
  color: #656565;
389
  }
390
 
391
+ .post-type-wp_stream_alerts .select2-search-choice-close {
392
  -webkit-transition: none;
393
  -moz-transition: none;
394
  -o-transition: all 0 none;
470
  border: 1px solid rgba(160,0,0,0.75);
471
  }
472
 
473
+ .post-type-wp_stream_alerts .select2-results__option .parent {
474
  font-weight: bold;
475
  }
476
 
477
+ .post-type-wp_stream_alerts .select2-results__option .child {
478
  padding-left: 8px;
479
  }
480
 
532
  }
533
 
534
 
535
+ .toplevel_page_wp_stream .alert-highlight.highlight-yellow {
536
+ background-color: #ffffe0;
537
+ }
538
+
539
+ .toplevel_page_wp_stream .alert-highlight.highlight-red {
540
+ background-color: #fbeaea;
541
+ }
542
+
543
+ .toplevel_page_wp_stream .alert-highlight.highlight-green {
544
+ background-color: #ecf7ed;
545
+ }
546
+
547
+ .toplevel_page_wp_stream .alert-highlight.highlight-blue {
548
+ background-color: #e5f5fa;
549
+ }
550
+
551
  /* Extensions */
552
 
553
  .post-type-stream_notification .view-switch {
ui/css/alerts-list.css ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .edit-php.post-type-wp_stream_alerts .inline-edit-col,
2
+ .edit-php.post-type-wp_stream_alerts .widefat .inline-edit-col,
3
+ .edit-php.post-type-wp_stream_alerts .widefat p {
4
+ color: #444;
5
+ font-size: 12px;
6
+ display: block;
7
+ margin: .2em 0;
8
+ line-height: 2.5;
9
+ }
10
+ .edit-php.post-type-wp_stream_alerts .inline-edit-col .wp_stream_alert_type_description {
11
+ line-height: 1.5em;
12
+ display: inline-block;
13
+ }
14
+ .edit-php.post-type-wp_stream_alerts .inline-edit-col {
15
+ padding: 0 0.5em !important;
16
+ }
17
+
18
+ .edit-php.post-type-wp_stream_alerts .inline-edit-col legend {
19
+ padding: 0.2em 0 !important;
20
+ color: #555;
21
+ }
22
+
23
+ .edit-php.post-type-wp_stream_alerts .inline-edit-col .select2-container {
24
+ display: block;
25
+ }
26
+
27
+ #wp_stream_alert_type_form {
28
+ max-height: 14em;
29
+ border: 1px solid #ddd;
30
+ overflow-y: scroll;
31
+ padding: 0.2em 0.5em;
32
+ margin: 6px 0;
33
+ background-color: #fff;
34
+ display: none;
35
+ }
36
+
37
+ #wp_stream_alert_type_form select {
38
+ width: 100%;
39
+ }
40
+
41
+ #wp_stream_alert_type_form .dashicons {
42
+ position: relative;
43
+ top: 7px;
44
+ left: -2px;
45
+ font-size: 1em;
46
+ }
47
+
48
+ .edit-php.post-type-wp_stream_alerts .misc-pub-section.misc-pub-post-status,
49
+ .edit-php.post-type-wp_stream_alerts #misc-publishing-actions {
50
+ padding: 0;
51
+ }
52
+
53
+ @media ( min-width: 781px ) {
54
+ .wp-stream-show-mobile {
55
+ display: none;
56
+ }
57
+ .edit-php.post-type-wp_stream_alerts .inline-edit-col.inline-edit-wp_stream_alerts {
58
+ float: left;
59
+ width: 100%;
60
+ max-width: 20%
61
+ }
62
+ }
63
+
64
+ .edit-php.post-type-wp_stream_alerts .select2 .select2-selection {
65
+ border-color: #ccc;
66
+ background: #f7f7f7;
67
+ -webkit-box-shadow: 0 1px 0 #ccc;
68
+ box-shadow: 0 1px 0 #ccc;
69
+ }
70
+ .edit-php.post-type-wp_stream_alerts label {
71
+ font-style: italic;
72
+ }
73
+
74
+ .edit-php.post-type-wp_stream_alerts select {
75
+ width: auto;
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;
87
+ }
88
+ .edit-php.post-type-wp_stream_alerts #add-new-alert .inline-edit-add-new-notifications,
89
+ .edit-php.post-type-wp_stream_alerts #add-new-alert .inline-edit-add-new-status {
90
+ margin-top: 33px;
91
+ }
92
+ #add-new-alert .inline-edit-add-new-triggers label {
93
+ margin: .2em 0;
94
+ line-height: 2.5;
95
+ }
96
+ .post-type-wp_stream_alerts .select2-container {
97
+ margin-bottom: 6px;
98
+ min-width: 165px;
99
+ }
100
+
101
+ /* Alert List Page */
102
+ .post-type-wp_stream_alerts #posts-filter #post-query-submit {
103
+ display: none;
104
+ }
105
+
106
+ .post-type-wp_stream_alerts .wp-list-table .alert_trigger_value {
107
+ padding: 3px 20px 3px 10px;
108
+ border-top: 1px solid #e5e5e5;
109
+ border-bottom: 1px solid #e5e5e5;
110
+ background: #fff;
111
+ display: inline-block;
112
+ height: 22px;
113
+ line-height: 22px;
114
+ box-shadow: 0 1px 1px rgba(0, 0, 0, .04);
115
+ }
116
+
117
+ .post-type-wp_stream_alerts .wp-list-table .alert_trigger_value:first-child {
118
+ border-left: 1px solid #e5e5e5;
119
+ }
120
+
121
+ .post-type-wp_stream_alerts .wp-list-table .alert_trigger_value:last-child {
122
+ padding-right: 10px;
123
+ border-right: 1px solid #e5e5e5;
124
+ }
125
+
126
+ .post-type-wp_stream_alerts .wp-list-table .alert_trigger_value:after {
127
+ content: "";
128
+ position: absolute;
129
+ margin-left: 7px;
130
+ margin-top: 6px;
131
+ display: inline-block;
132
+ height: 8px;
133
+ width: 8px;
134
+ transform: rotate(45deg);
135
+ border-top: 2px solid #e5e5e5;
136
+ border-right: 2px solid #e5e5e5;
137
+ }
138
+
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/js/alerts-list.js ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
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 );
ui/js/alerts.js ADDED
@@ -0,0 +1,272 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ // $(this).removeAttr('name');
60
+ }
61
+ });
62
+
63
+ var parts = [
64
+ $( el ).siblings( '.connector' ).val(),
65
+ $( el ).siblings( '.context' ).val()
66
+ ];
67
+ if ('' === parts[1]) {
68
+ parts.splice( 1, 1 );
69
+ }
70
+ $( el ).val( parts.join( '-' ) ).trigger( 'change' );
71
+ });
72
+
73
+ $target.find( 'select.select2-select:not(.connector_or_context)' ).each(function () {
74
+ var element_id_split = $( this ).attr( 'id' ).split( '_' );
75
+ var select_name = element_id_split[element_id_split.length - 1].charAt( 0 ).toUpperCase() +
76
+ element_id_split[element_id_split.length - 1].slice( 1 );
77
+ $( this ).select2({
78
+ allowClear: true,
79
+ placeholder: streamAlerts.any + ' ' + select_name
80
+ });
81
+ });
82
+ };
83
+ var $alertSettingSelect = $( '#wp_stream_alert_type' );
84
+
85
+ var loadAlertSettings = function( alert_type ) {
86
+ var data = {
87
+ 'action' : 'load_alerts_settings',
88
+ 'alert_type' : alert_type
89
+ };
90
+
91
+ var $edit_row = $( '#wp_stream_alert_type' ).closest( 'tr' );
92
+ var row_id = $edit_row.attr( 'id' );
93
+ data.post_id = row_id.split('-')[1];
94
+ $.post( window.ajaxurl, data, function( response ) {
95
+ var $alert_type_settings = $( '#wp_stream_alert_type_form' );
96
+ var alert_type = $( '#wp_stream_alert_type' ).val();
97
+ if ('none' === alert_type) {
98
+ $alert_type_settings.hide();
99
+ return;
100
+ }
101
+ $alert_type_settings.html( response.data.html );
102
+ $alert_type_settings.show();
103
+ });
104
+ };
105
+
106
+ $( '#the-list' ).on('change', '#wp_stream_trigger_connector_or_context', function() {
107
+ if ( 'wp_stream_trigger_connector_or_context' === $( this ).attr( 'id' ) ) {
108
+ var connector = $( this ).val();
109
+ if ( connector && 0 < connector.indexOf( '-' ) ) {
110
+ var connector_split = connector.split( '-' );
111
+ connector = connector_split[0];
112
+ }
113
+ getActions( connector );
114
+ }
115
+ });
116
+
117
+ var getActions = function( connector ) {
118
+ var trigger_action = $( '#wp_stream_trigger_action' );
119
+ trigger_action.empty();
120
+ trigger_action.prop( 'disabled', true );
121
+
122
+ var placeholder = $( '<option/>', {value: '', text: ''} );
123
+ trigger_action.append( placeholder );
124
+
125
+ var data = {
126
+ 'action' : 'get_actions',
127
+ 'connector' : connector
128
+ };
129
+
130
+ $.post( window.ajaxurl, data, function( response ) {
131
+ var success = response.success,
132
+ actions = response.data;
133
+ if ( ! success ) {
134
+ return;
135
+ }
136
+ for ( var key in actions ) {
137
+ if ( actions.hasOwnProperty( key ) ) {
138
+ var value = actions[key];
139
+ var option = $( '<option/>', {value: key, text: value} );
140
+ trigger_action.append( option );
141
+ }
142
+ }
143
+ trigger_action.prop( 'disabled', false );
144
+ $( document ).trigger( 'alert-actions-updated' );
145
+ });
146
+ };
147
+
148
+ $alertSettingSelect.change( function() {
149
+ loadAlertSettings( $( this ).val() );
150
+ });
151
+
152
+ $( '#wpbody-content' ).on( 'click', 'a.page-title-action', function( e ) {
153
+ e.preventDefault();
154
+ $( '#add-new-alert' ).remove();
155
+ if ( $( '.inline-edit-wp_stream_alerts' ).length > 0 ) {
156
+ $( '.inline-edit-wp_stream_alerts .inline-edit-save button.button-secondary.cancel' ).trigger( 'click' );
157
+ }
158
+ var alert_form_html = '';
159
+ var data = {
160
+ 'action': 'get_new_alert_triggers_notifications'
161
+ };
162
+ $.post( window.ajaxurl, data, function( response ) {
163
+ if ( true === response.success ) {
164
+ alert_form_html = response.data.html;
165
+ $( '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>' );
166
+ var add_new_alert = $( '#add-new-alert' );
167
+ var current_bg_color = add_new_alert.css( 'background-color' );
168
+
169
+ // Color taken from /wp-admin/css/forms.css
170
+ // #pass-strength-result.strong
171
+ add_new_alert.css( 'background-color', '#C1E1B9' );
172
+ setTimeout(function () {
173
+ add_new_alert.css( 'background-color', current_bg_color );
174
+ }, 250);
175
+
176
+ $( '#wp_stream_alert_type' ).change( function () {
177
+ loadAlertSettings( $( this ).val() );
178
+ });
179
+ add_new_alert.on( 'click', '.button-secondary.cancel', function () {
180
+ $( '#add-new-alert' ).remove();
181
+ });
182
+ add_new_alert.on( 'click', '.button-primary.save', save_new_alert );
183
+
184
+ setupSelectTwo( '#add-new-alert' );
185
+ }
186
+ });
187
+
188
+ });
189
+ var save_new_alert = function save_new_alert( e ) {
190
+ e.preventDefault();
191
+ $( '#add-new-alert' ).find( 'p.submit.inline-edit-save span.spinner' ).css( 'visibility', 'visible' );
192
+ var data = {
193
+ 'action': 'save_new_alert',
194
+ 'wp_stream_alerts_nonce': $( '#wp_stream_alerts_nonce' ).val(),
195
+ 'wp_stream_trigger_author': $( '#wp_stream_trigger_author' ).val(),
196
+ 'wp_stream_trigger_context': $( '#wp_stream_trigger_connector_or_context' ).val(),
197
+ 'wp_stream_trigger_action': $( '#wp_stream_trigger_action' ).val(),
198
+ 'wp_stream_alert_type': $( '#wp_stream_alert_type' ).val(),
199
+ 'wp_stream_alert_status': $( '#wp_stream_alert_status' ).val()
200
+ };
201
+ $( '#wp_stream_alert_type_form' ).find( ':input' ).each( function(){
202
+ var alert_type_data_id = $( this ).attr( 'id' );
203
+ if ( $( this ).val() ) {
204
+ data[alert_type_data_id] = $( this ).val();
205
+ }
206
+ });
207
+
208
+ $.post( window.ajaxurl, data, function( response ) {
209
+ if ( true === response.success ) {
210
+ $( '#add-new-alert' ).find( 'p.submit.inline-edit-save span.spinner' ).css( 'visibility', 'hidden' );
211
+ location.reload();
212
+ }
213
+ });
214
+ };
215
+
216
+ // we create a copy of the WP inline edit post function
217
+ var $wp_inline_edit = inlineEditPost.edit;
218
+
219
+ // and then we overwrite the function with our own code
220
+ inlineEditPost.edit = function( id ) {
221
+ // "call" the original WP edit function
222
+ // we don't want to leave WordPress hanging
223
+ $wp_inline_edit.apply( this, arguments );
224
+
225
+ // now we take care of our business
226
+
227
+ // get the post ID
228
+ var post_id = 0;
229
+ if ( typeof( id ) === 'object' ) {
230
+ post_id = parseInt( this.getId( id ), 10 );
231
+ }
232
+
233
+ if ( post_id > 0 ) {
234
+ // define the edit row
235
+ $edit_row = $( '#edit-' + post_id );
236
+ $post_row = $( '#post-' + post_id );
237
+
238
+ // get the data
239
+ var alert_trigger_connector = $post_row.find( 'input[name="wp_stream_trigger_connector"]' ).val();
240
+ var alert_trigger_context = $post_row.find( 'input[name="wp_stream_trigger_context"]' ).val();
241
+ var alert_trigger_connector_context = alert_trigger_connector + '-' + alert_trigger_context;
242
+ var alert_trigger_action = $post_row.find( 'input[name="wp_stream_trigger_action"]' ).val();
243
+ var alert_status = $post_row.find( 'input[name="wp_stream_alert_status"]' ).val();
244
+
245
+ // populate the data
246
+ $edit_row.find( 'input[name="wp_stream_trigger_connector"]' ).attr( 'value', alert_trigger_connector );
247
+ $edit_row.find( 'input[name="wp_stream_trigger_context"]' ).attr( 'value', alert_trigger_context );
248
+ $edit_row.find( 'select[name="wp_stream_trigger_connector_or_context"] option[value="' + alert_trigger_connector_context + '"]' ).attr( 'selected', 'selected' );
249
+ $( document ).one( 'alert-actions-updated', function() {
250
+ $edit_row.find( 'input[name="wp_stream_trigger_action"]' ).attr( 'value', alert_trigger_action );
251
+ $edit_row.find( 'select[name="wp_stream_trigger_action"] option[value="' + alert_trigger_action + '"]' ).attr( 'selected', 'selected' ).trigger( 'change' );
252
+ });
253
+ $edit_row.find( 'select[name="wp_stream_alert_status"] option[value="' + alert_status + '"]' ).attr( 'selected', 'selected' );
254
+ setupSelectTwo( '#edit-' + post_id );
255
+
256
+ // Alert type handling
257
+ $( '#wp_stream_alert_type_form' ).hide();
258
+ var alert_type = $post_row.find( 'input[name="wp_stream_alert_type"]' ).val();
259
+ $edit_row.find( 'select[name="wp_stream_alert_type"] option[value="' + alert_type + '"]' ).attr( 'selected', 'selected' ).trigger( 'change' );
260
+ }
261
+ };
262
+ if ( window.location.hash ) {
263
+ var $target_post_row = $( window.location.hash );
264
+ if ( $target_post_row.length ) {
265
+ var scroll_to_position = $target_post_row.offset().top - $('#wpadminbar').height();
266
+ $('html, body').animate({
267
+ scrollTop: scroll_to_position
268
+ }, 1000);
269
+ $target_post_row.find('.row-actions a.editinline').trigger('click');
270
+ }
271
+ }
272
+ });
ui/js/live-updates.js CHANGED
@@ -46,7 +46,8 @@ jQuery( function( $ ) {
46
  $new_items = $( data['wp-stream-heartbeat'] );
47
 
48
  // Remove all default classes and add class to highlight new rows
49
- $new_items.removeClass().addClass( 'new-row' );
 
50
 
51
  // Check if first tr has the alternate class
52
  var has_class = ( $current_items.first().hasClass( 'alternate' ) );
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' ) );
ui/js/settings.js CHANGED
@@ -1,8 +1,13 @@
1
  /* globals confirm, wp_stream, ajaxurl, wp_stream_regenerate_alt_rows */
2
  jQuery( function( $ ) {
3
-
4
- var keepRecordsIndefinitely = $( '#wp_stream\\[general_keep_records_indefinitely\\]' ),
5
- keepRecordsFor = $( '#wp_stream_general_records_ttl' ),
 
 
 
 
 
6
  keepRecordsForRow = keepRecordsFor.closest( 'tr' );
7
 
8
  function toggleKeepRecordsFor() {
@@ -20,7 +25,6 @@ jQuery( function( $ ) {
20
  });
21
 
22
  toggleKeepRecordsFor();
23
-
24
  // Confirmation on some important actions
25
  $( '#wp_stream_general_reset_site_settings' ).click( function( e ) {
26
  if ( ! confirm( wp_stream.i18n.confirm_defaults ) ) {
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() {
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 ) ) {