Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Sensei_Messages often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Sensei_Messages, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
14 | class Sensei_Messages { |
||
15 | public $token; |
||
16 | public $post_type; |
||
17 | public $meta_fields; |
||
18 | |||
19 | /** |
||
20 | * Constructor. |
||
21 | * @since 1.6.0 |
||
22 | */ |
||
23 | public function __construct () { |
||
24 | $this->token = 'messages'; |
||
25 | $this->post_type = 'sensei_message'; |
||
26 | $this->meta_fields = array( 'sender', 'receiver' ); |
||
27 | |||
28 | // Add Messages page to admin menu |
||
29 | add_action( 'admin_menu', array( $this, 'add_menu_item' ), 40 ); |
||
30 | add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ), 10, 2 ); |
||
31 | add_action( 'admin_menu', array( $this, 'remove_meta_box' ) ); |
||
32 | |||
33 | // Save new private message |
||
34 | add_action( 'init', array( $this, 'save_new_message' ), 1 ); |
||
35 | |||
36 | // Monitor when new reply is posted |
||
37 | add_action( 'comment_post', array( $this, 'message_reply_received' ), 10, 1 ); |
||
38 | |||
39 | // Block WordPress from sending comment update emails for the messages post type |
||
40 | add_filter('comment_notification_recipients', array( $this, 'stop_wp_comment_emails' ), 20, 2 ); |
||
41 | |||
42 | // Block WordPress from sending comment moderator emails on the sensei messages post types |
||
43 | add_filter('comment_moderation_recipients', array( $this, 'stop_wp_comment_emails' ), 20, 2 ); |
||
44 | |||
45 | // Process saving of message posts |
||
46 | add_action( 'save_post', array( $this, 'save_message' ) ); |
||
47 | |||
48 | // Add message links to courses & lessons |
||
49 | add_action( 'sensei_single_course_content_inside_before', array( $this, 'send_message_link' ), 35 ); |
||
50 | |||
51 | // add message link to lesson |
||
52 | add_action( 'sensei_single_lesson_content_inside_before', array( $this, 'send_message_link' ), 30, 2 ); |
||
53 | |||
54 | // add message link to lesson |
||
55 | add_action( 'sensei_single_quiz_questions_before', array( $this, 'send_message_link' ), 10, 2 ); |
||
56 | |||
57 | // Hide messages and replies from users who do not have access |
||
58 | add_action( 'template_redirect', array( $this, 'message_login' ), 10, 1 ); |
||
59 | add_action( 'pre_get_posts', array( $this, 'message_list' ), 10, 1 ); |
||
60 | add_filter( 'the_title', array( $this, 'message_title' ), 10, 2 ); |
||
61 | add_filter( 'the_content', array( $this, 'message_content' ), 10, 1 ); |
||
62 | add_filter( 'comments_array', array( $this, 'message_replies' ), 100, 1 ); |
||
63 | add_filter( 'get_comments_number', array( $this, 'message_reply_count' ), 100, 2 ); |
||
64 | add_filter( 'comments_open', array( $this, 'message_replies_open' ), 100, 2 ); |
||
65 | } // End __construct() |
||
66 | |||
67 | public function add_menu_item() { |
||
68 | |||
69 | if( ! isset( Sensei()->settings->settings['messages_disable'] ) || ! Sensei()->settings->settings['messages_disable'] ) { |
||
70 | add_submenu_page( 'sensei', __( 'Messages', 'woothemes-sensei'), __( 'Messages', 'woothemes-sensei') , 'edit_courses', 'edit.php?post_type=sensei_message' ); |
||
71 | } |
||
72 | } |
||
73 | |||
74 | public function add_meta_box( $post_type, $post ) { |
||
75 | |||
76 | if( ! $post_type == $this->post_type ) return; |
||
77 | |||
78 | add_meta_box( $this->post_type . '-data', __( 'Message Information', 'woothemes-sensei' ), array( $this, 'meta_box_content' ), $this->post_type, 'normal', 'default' ); |
||
79 | |||
80 | } |
||
81 | |||
82 | public function meta_box_content() { |
||
83 | global $post; |
||
84 | |||
85 | $settings = array( |
||
86 | array( |
||
87 | 'id' => 'sender', |
||
88 | 'label' => __( 'Message sent by:', 'woothemes-sensei' ), |
||
89 | 'description' => __( 'The username of the learner who sent this message.', 'woothemes-sensei' ), |
||
90 | 'type' => 'text', |
||
91 | 'default' => '', |
||
92 | 'placeholder' => __( 'Learner username', 'woothemes-sensei' ), |
||
93 | ), |
||
94 | array( |
||
95 | 'id' => 'receiver', |
||
96 | 'label' => __( 'Message received by:', 'woothemes-sensei' ), |
||
97 | 'description' => __( 'The username of the teacher who received this message.', 'woothemes-sensei' ), |
||
98 | 'type' => 'text', |
||
99 | 'default' => '', |
||
100 | 'placeholder' => __( 'Teacher username', 'woothemes-sensei' ), |
||
101 | ), |
||
102 | ); |
||
103 | |||
104 | $message_posttype = get_post_meta( $post->ID, '_posttype', true ); |
||
105 | |||
106 | if( isset( $message_posttype ) && $message_posttype ) { |
||
107 | |||
108 | $args = array( |
||
109 | 'post_type' => $message_posttype, |
||
110 | 'posts_per_page' => -1, |
||
111 | 'orderby' => 'name', |
||
112 | 'order' => 'ASC', |
||
113 | 'post_status' => 'publish', |
||
114 | ); |
||
115 | |||
116 | $posts = get_posts( $args ); |
||
117 | |||
118 | $post_options[0] = sprintf( __( 'Select %1$s', 'woothemes-sensei' ), $message_posttype ); |
||
119 | foreach( $posts as $post_item ) { |
||
120 | $post_options[ $post_item->ID ] = $post_item->post_title; |
||
121 | } |
||
122 | |||
123 | $settings[] = array( |
||
124 | 'id' => 'post', |
||
125 | 'label' => sprintf( __( 'Message from %1$s:', 'woothemes-sensei' ), $message_posttype ), |
||
126 | 'description' => sprintf( __( 'The %1$s to which this message relates.', 'woothemes-sensei' ), $message_posttype ), |
||
127 | 'type' => 'select', |
||
128 | 'default' => 0, |
||
129 | 'options' => $post_options, |
||
130 | ); |
||
131 | } |
||
132 | |||
133 | $html = Sensei()->admin->render_settings( $settings, $post->ID, 'message-info' ); |
||
134 | |||
135 | echo $html; |
||
136 | } |
||
137 | |||
138 | public function save_message( $post_id = 0 ) { |
||
139 | global $post; |
||
140 | |||
141 | if( $this->post_type != get_post_type() ) return; |
||
142 | |||
143 | if( isset( $_POST['sender'] ) && $_POST['sender'] ) { |
||
144 | update_post_meta( $post_id, '_sender', $_POST['sender'] ); |
||
145 | } |
||
146 | |||
147 | if( isset( $_POST['receiver'] ) && $_POST['receiver'] ) { |
||
148 | update_post_meta( $post_id, '_receiver', $_POST['receiver'] ); |
||
149 | } |
||
150 | |||
151 | if( isset( $_POST['post'] ) && $_POST['post'] ) { |
||
152 | update_post_meta( $post_id, '_post', $_POST['post'] ); |
||
153 | } |
||
154 | |||
155 | remove_action( 'save_post', array( $this, 'save_message' ) ); |
||
156 | |||
157 | wp_update_post( array( 'ID' => $post_id, 'comment_status' => 'open' ) ); |
||
158 | |||
159 | add_action( 'save_post', array( $this, 'save_message' ) ); |
||
160 | } |
||
161 | |||
162 | public function send_message_link( $post_id = 0, $user_id = 0 ) { |
||
163 | global $post; |
||
164 | |||
165 | // only show the link for the allowed post types: |
||
166 | $allowed_post_types = array('lesson', 'course', 'quiz'); |
||
167 | if ( ! in_array( get_post_type() , $allowed_post_types ) ) { |
||
168 | |||
169 | return; |
||
170 | |||
171 | } |
||
172 | |||
173 | $html = ''; |
||
174 | |||
175 | if( ! isset( Sensei()->settings->settings['messages_disable'] ) || ! Sensei()->settings->settings['messages_disable'] ) { |
||
176 | |||
177 | if( ! is_user_logged_in() ) return; |
||
178 | |||
179 | if( isset( $_GET['contact'] ) ) { |
||
180 | $html .= $this->teacher_contact_form( $post ); |
||
181 | } else { |
||
182 | $href = add_query_arg( array( 'contact' => $post->post_type ) ); |
||
183 | |||
184 | if( 'lesson' == $post->post_type ) { |
||
185 | $contact_button_text = __( 'Contact Lesson Teacher', 'woothemes-sensei' ); |
||
186 | } elseif( 'course' == $post->post_type ) { |
||
187 | $contact_button_text = __( 'Contact Course Teacher', 'woothemes-sensei' ); |
||
188 | }else{ |
||
189 | $contact_button_text = __( 'Contact Teacher', 'woothemes-sensei' ); |
||
190 | } |
||
191 | |||
192 | $html .= '<p><a class="button send-message-button" href="' . esc_url($href) . '#private_message">' . $contact_button_text . '</a></p>'; |
||
193 | } |
||
194 | |||
195 | if( isset( $this->message_notice ) && isset( $this->message_notice['type'] ) && isset( $this->message_notice['notice'] ) ) { |
||
196 | $html .= '<div class="sensei-message ' . $this->message_notice['type'] . '">' . $this->message_notice['notice'] . '</div>'; |
||
197 | } |
||
198 | |||
199 | } |
||
200 | |||
201 | echo $html; |
||
202 | } |
||
203 | |||
204 | public function teacher_contact_form( $post ) { |
||
205 | |||
206 | if( ! is_user_logged_in() ) return; |
||
207 | |||
208 | global $current_user; |
||
209 | wp_get_current_user(); |
||
210 | |||
211 | $html = ''; |
||
212 | |||
213 | if( ! isset( $post->ID ) ) return $html; |
||
214 | |||
215 | //confirm private message |
||
216 | $confirmation = ''; |
||
217 | if( isset( $_GET[ 'send' ] ) && 'complete' == $_GET[ 'send' ] ) { |
||
218 | |||
219 | $confirmation_message = __('Your private message has been sent.', 'woothemes-sensei'); |
||
220 | $confirmation = '<div class="sensei-message tick">' . $confirmation_message . '</div>'; |
||
221 | |||
222 | } |
||
223 | |||
224 | $html .= '<h3 id="private_message">' . __( 'Send Private Message', 'woothemes-sensei' ) . '</h3>'; |
||
225 | $html .= '<p>'; |
||
226 | $html .= $confirmation; |
||
227 | $html .= '</p>'; |
||
228 | $html .= '<form name="contact-teacher" action="" method="post" class="contact-teacher">'; |
||
229 | $html .= '<p class="form-row form-row-wide">'; |
||
230 | $html .= '<textarea name="contact_message" placeholder="' . __( 'Enter your private message.', 'woothemes-sensei' ) . '"></textarea>'; |
||
231 | $html .= '</p>'; |
||
232 | $html .= '<p class="form-row">'; |
||
233 | $html .= '<input type="hidden" name="post_id" value="' . $post->ID . '" />'; |
||
234 | $html .= '<input type="hidden" name="sender_id" value="' . $current_user->ID . '" />'; |
||
235 | $html .= '<input type="hidden" name="receiver_id" value="' . $post->post_author . '" />'; |
||
236 | $html .= wp_nonce_field( 'message_teacher', 'sensei_message_teacher_nonce', true, false ); |
||
237 | $html .= '<input type="submit" class="send_message" value="' . __( 'Send Message', 'woothemes-sensei' ) . '" />'; |
||
238 | $html .= '</p>'; |
||
239 | $html .= '<div class="fix"></div>'; |
||
240 | $html .= '</form>'; |
||
241 | |||
242 | return $html; |
||
243 | } |
||
244 | |||
245 | public function save_new_message() { |
||
246 | |||
247 | if( ! isset( $_POST['sensei_message_teacher_nonce'] ) ) return; |
||
248 | |||
249 | if( ! wp_verify_nonce( $_POST['sensei_message_teacher_nonce'], 'message_teacher' ) ) return; |
||
250 | |||
251 | $message_id = $this->save_new_message_post( $_POST['sender_id'], $_POST['receiver_id'], $_POST['contact_message'], $_POST['post_id'] ); |
||
252 | |||
253 | } |
||
254 | |||
255 | public function message_reply_received( $comment_id = 0 ) { |
||
256 | |||
257 | // Get comment object |
||
258 | $comment = get_comment( $comment_id ); |
||
259 | |||
260 | if( is_null( $comment ) ) return; |
||
261 | |||
262 | // Get message post object |
||
263 | $message = get_post( $comment->comment_post_ID ); |
||
264 | |||
265 | if( $message->post_type != $this->post_type ) return; |
||
266 | |||
267 | // Force comment to be approved |
||
268 | wp_set_comment_status( $comment_id, 'approve' ); |
||
269 | |||
270 | do_action( 'sensei_private_message_reply', $comment, $message ); |
||
271 | } |
||
272 | |||
273 | /** |
||
274 | * This function stops WordPress from sending the default comment update emails. |
||
275 | * |
||
276 | * This function is hooked into comment_notification_recipients. It will simply return |
||
277 | * an empty array if the current passed in comment is on a message post type. |
||
278 | * |
||
279 | * @param array $emails |
||
280 | * @param int $comment_id |
||
281 | * @return array; |
||
282 | */ |
||
283 | public function stop_wp_comment_emails( $emails , $comment_id ){ |
||
284 | |||
285 | $comment = get_comment( $comment_id ); |
||
286 | if( isset( $comment->comment_post_ID ) && |
||
287 | 'sensei_message' == get_post_type( $comment->comment_post_ID ) ){ |
||
288 | |||
289 | // empty the emails array to ensure no emails are sent for this comment |
||
290 | $emails = array(); |
||
291 | |||
292 | } |
||
293 | return $emails; |
||
294 | |||
295 | }// end stop_wp_comment_emails |
||
296 | |||
297 | /** |
||
298 | * Save new message post |
||
299 | * @param integer $sender_id ID of sender |
||
300 | * @param integer $receiver_id ID of receiver |
||
301 | * @param string $message Message content |
||
302 | * @param string $post_id ID of post related to message |
||
303 | * @return mixed Message ID on success, boolean false on failure |
||
304 | */ |
||
305 | private function save_new_message_post( $sender_id = 0, $receiver_id = 0, $message = '', $post_id = 0 ) { |
||
306 | |||
307 | $message_id = false; |
||
308 | |||
309 | if( $sender_id && $receiver_id && $message && $post_id ) { |
||
310 | |||
311 | $title = wp_trim_words( $message, 8, '...' ); |
||
312 | |||
313 | // Set up post data for message |
||
314 | $message_data = array( |
||
315 | 'post_type' => $this->post_type, |
||
316 | 'post_title' => esc_html( $title ), |
||
317 | 'post_content' => esc_html( $message ), |
||
318 | 'post_status' => 'publish', |
||
319 | 'ping_status' => 'closed', |
||
320 | 'comment_status' => 'open', |
||
321 | 'post_excerpt' => '', |
||
322 | 'post_author' => intval( $sender_id ) |
||
323 | ); |
||
324 | |||
325 | // Insert post |
||
326 | $message_id = wp_insert_post( $message_data ); |
||
327 | |||
328 | if( ! is_wp_error( $message_id ) ) { |
||
329 | |||
330 | // Add sender to message meta |
||
331 | $sender = get_userdata( $sender_id ); |
||
332 | add_post_meta( $message_id, '_sender', $sender->user_login ); |
||
333 | |||
334 | // Add receiver to message meta |
||
335 | $receiver = get_userdata( $receiver_id ); |
||
336 | add_post_meta( $message_id, '_receiver', $receiver->user_login ); |
||
337 | |||
338 | // Add lesson/course ID to message meta |
||
339 | $post = get_post( $post_id ); |
||
340 | add_post_meta( $message_id, '_posttype', $post->post_type ); |
||
341 | add_post_meta( $message_id, '_post', $post->ID ); |
||
342 | |||
343 | do_action( 'sensei_new_private_message', $message_id ); |
||
344 | |||
345 | } else { |
||
346 | |||
347 | $message_id = false; |
||
348 | |||
349 | } |
||
350 | } |
||
351 | |||
352 | return $message_id; |
||
353 | } |
||
354 | |||
355 | /** |
||
356 | * Check if user has access to view this message |
||
357 | * @param integer $message_id Post ID of message |
||
358 | * @param integer $user_id ID of user |
||
359 | * @return boolean True if user has access to this message |
||
360 | */ |
||
361 | private function view_message( $message_id, $user_id = 0) { |
||
362 | |||
363 | if( ! is_user_logged_in() ) return false; |
||
364 | |||
365 | if( $user_id == 0 ) { |
||
366 | global $current_user; |
||
367 | wp_get_current_user(); |
||
368 | $user_login = $current_user->user_login; |
||
369 | } |
||
370 | |||
371 | // Get allowed users |
||
372 | $receiver = get_post_meta( $message_id, '_receiver', true ); |
||
373 | $sender = get_post_meta( $message_id, '_sender', true ); |
||
374 | |||
375 | // Check if user is allowed to view the message |
||
376 | if( in_array( $user_login, array( $receiver, $sender ) ) ) { |
||
377 | return true; |
||
378 | } |
||
379 | |||
380 | // Return false if user is not allowed access |
||
381 | return false; |
||
382 | } |
||
383 | |||
384 | /** |
||
385 | * Remove unneeded meta boxes from Messages posts |
||
386 | * @return void |
||
387 | */ |
||
388 | public function remove_meta_box() { |
||
391 | |||
392 | /** |
||
393 | * Function message_login() |
||
394 | * |
||
395 | * Only show /messages/* to logged in users, and |
||
396 | * redirect logged out users to wp-login.php |
||
397 | * |
||
398 | * @since 1.9.0 |
||
399 | * @param none |
||
400 | * @return void |
||
401 | */ |
||
402 | |||
403 | public function message_login () { |
||
437 | /** |
||
438 | * Only show allowed messages in messages archive |
||
439 | * @param WP_Query $query Original query |
||
440 | * @return void |
||
441 | */ |
||
442 | public function message_list( $query ) { |
||
470 | |||
471 | /** |
||
472 | * Hide message title |
||
473 | * @param string $title Original message title |
||
474 | * @param integer $post_id ID of post |
||
475 | * @return string Modified string if user does not have access to this message |
||
476 | */ |
||
477 | public function message_title( $title = '', $post_id = null ) { |
||
487 | |||
488 | /** |
||
489 | * Hide content of message |
||
490 | * @param string $content Original message content |
||
491 | * @return string Empty string if user does not have access to this message |
||
492 | */ |
||
493 | View Code Duplication | public function message_content( $content ) { |
|
504 | |||
505 | /** |
||
506 | * Hide all replies |
||
507 | * @param array $comments Array of replies |
||
508 | * @return array Empty array if user does not have access to this message |
||
509 | */ |
||
510 | View Code Duplication | public function message_replies( $comments ) { |
|
521 | |||
522 | /** |
||
523 | * Set message reply count to 0 |
||
524 | * @param integer $count Default count |
||
525 | * @param integer $post_id ID of post |
||
526 | * @return integer 0 if user does not have access to this message |
||
527 | */ |
||
528 | View Code Duplication | public function message_reply_count( $count, $post_id ) { |
|
539 | |||
540 | /** |
||
541 | * Close replies for messages |
||
542 | * @param boolean $open Current comment open status |
||
543 | * @param integer $post_id ID of post |
||
544 | * @return boolean False if user does not have access to this message |
||
545 | */ |
||
546 | View Code Duplication | public function message_replies_open( $open, $post_id ) { |
|
557 | |||
558 | /** |
||
559 | * Print outthe message was sent by $sender_username on the |
||
560 | * |
||
561 | * @since 1.9.0 |
||
562 | */ |
||
563 | public static function the_message_sent_by_title(){ |
||
581 | |||
582 | /** |
||
583 | * sensei_single_title output for single page title |
||
584 | * @since 1.1.0 |
||
585 | * @return void |
||
586 | * @deprecate |
||
587 | */ |
||
588 | public static function the_title() { |
||
623 | |||
624 | /** |
||
625 | * Generates the my messages |
||
626 | * archive header. |
||
627 | * |
||
628 | * @since 1.9.0 |
||
629 | * |
||
630 | * @return string |
||
631 | */ |
||
632 | public static function the_archive_header( ){ |
||
646 | |||
647 | /** |
||
648 | * Output the title for a message given the post_id. |
||
649 | * |
||
650 | * @since 1.9.0 |
||
651 | * @param $post_id |
||
652 | */ |
||
653 | public static function the_message_title( $message_post_id ){ |
||
677 | |||
678 | /** |
||
679 | * Output the message sender given the post id. |
||
680 | * |
||
681 | * @param $message_post_id |
||
682 | */ |
||
683 | public static function the_message_sender( $message_post_id ){ |
||
701 | |||
702 | /** |
||
703 | * Link to the users my messages page |
||
704 | * |
||
705 | * @since 1.9.0 |
||
706 | */ |
||
707 | public static function the_my_messages_link(){ |
||
719 | |||
720 | } // End Class |
||
721 | |||
728 |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.