├── templates ├── talk-plugin.php ├── talk-footer.php ├── archive.php ├── talk-header.php ├── signup.php ├── user-infos.php ├── talk-entry.php ├── talk-form.php ├── talk-loop.php ├── user-profile.php └── user-comments.php ├── includes ├── comments │ ├── classes.php │ ├── wordcamp-talks-loop-comments.php │ ├── functions.php │ ├── tags.php │ └── wordcamp-talks-comments.php ├── core │ ├── status │ │ ├── view.php │ │ ├── controller.php │ │ ├── views │ │ │ ├── posts-list.php │ │ │ └── publish-box.php │ │ ├── post-status.php │ │ └── taxonomy.php │ ├── classes.php │ ├── poststatus.php │ ├── upgrade.php │ ├── actions.php │ ├── rewrites.php │ ├── filters.php │ ├── widgets.php │ ├── wordcamp-talks-template-loader.php │ ├── wordcamp-talks-rewrites.php │ ├── wordcamp-talks-loop.php │ ├── options.php │ └── capabilities.php ├── talks │ ├── wordcamp-talks-loop-talks.php │ ├── wordcamp-talk-metas.php │ └── wordcamp-talks-talk.php ├── users │ └── tags.php ├── wordcamp-talks.php └── admin │ └── comments.php ├── wordcamp-talks.php ├── js ├── script.min.js ├── script.js ├── tagging.min.js └── jquery.raty.min.js └── README.md /templates/talk-plugin.php: -------------------------------------------------------------------------------- 1 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | -------------------------------------------------------------------------------- /templates/talk-footer.php: -------------------------------------------------------------------------------- 1 | 11 | 20 | -------------------------------------------------------------------------------- /templates/archive.php: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | -------------------------------------------------------------------------------- /templates/talk-header.php: -------------------------------------------------------------------------------- 1 | 11 |
12 | 13 | 14 | 15 | 16 |
17 | -------------------------------------------------------------------------------- /includes/comments/classes.php: -------------------------------------------------------------------------------- 1 | posts_list = $posts_list; 15 | $this->publish_box = $publish_box; 16 | } 17 | 18 | /** 19 | * After the object is created, this tells it to start doing work 20 | * 21 | * @return void 22 | */ 23 | public function run() { 24 | $this->posts_list->run(); 25 | $this->publish_box->run(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /templates/signup.php: -------------------------------------------------------------------------------- 1 | 11 |
12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 |
30 | 31 |
32 | 33 | 34 | 35 |
36 | -------------------------------------------------------------------------------- /templates/user-infos.php: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 | 14 | 21 | 22 |
23 | 24 |
25 |
26 | 27 |
28 | 29 | 36 | 37 |
38 | -------------------------------------------------------------------------------- /wordcamp-talks.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 |
14 | 15 |
16 | 17 | 18 |
19 | 20 | 21 | 22 |
23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 | -------------------------------------------------------------------------------- /includes/core/status/controller.php: -------------------------------------------------------------------------------- 1 | post_status = $post_status; 22 | $this->taxonomy = $taxonomy; 23 | $this->view = $view; 24 | } 25 | 26 | /** 27 | * After the object is created, this tells it to start doing work 28 | * 29 | * @return void 30 | */ 31 | public function run() { 32 | $this->post_status->run(); 33 | $this->taxonomy->run(); 34 | $this->view->run(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /includes/core/status/views/posts-list.php: -------------------------------------------------------------------------------- 1 | post_type ) { 38 | return false; 39 | } 40 | return $post_states; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /templates/talk-form.php: -------------------------------------------------------------------------------- 1 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 |
30 | 31 |
32 | 33 | 34 | 35 |
36 | 37 | 38 | 39 |
40 | 41 | 42 | 43 |
44 | 45 |
46 | 47 | 48 | 49 |
50 |

51 |
52 | 53 | 54 | 55 | 56 | 57 |
58 | -------------------------------------------------------------------------------- /includes/core/classes.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 29 | 30 | 41 | 42 | 57 | 58 | 59 | 60 | 61 | 62 |
63 |

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /includes/core/status/post-status.php: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
31 | 32 |

33 | 34 | 35 | 36 |
37 | 38 | 39 | 40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
67 | -------------------------------------------------------------------------------- /includes/core/poststatus.php: -------------------------------------------------------------------------------- 1 | statuses ) ) { 22 | $wct->statuses = new self; 23 | } 24 | 25 | return $wct->statuses; 26 | } 27 | 28 | public function __construct() { 29 | // register the various hooks 30 | add_action( 'admin_head-post-new.php', array( $this, 'set_publishing_actions' ) ); 31 | add_action( 'pre_get_posts', array( $this, 'filter_talks' ) ); 32 | } 33 | 34 | /** 35 | * Filter which talks are seen by who 36 | * 37 | * @param \WP_Query $q the query being filtered 38 | * @return void nothing, the query is passed by reference 39 | */ 40 | function filter_talks( \WP_Query $q ) { 41 | if ( 'talks' === $q->get( 'post_type' ) ) { 42 | /** 43 | * logged out users should only be able to see those talks that have been picked 44 | */ 45 | if ( ! is_user_logged_in() ) { 46 | // logged out users shouldn't be able to see any of these, 47 | // that's what sessions are for 48 | $q->set( 'post__in', [ 0 ] ); 49 | return; 50 | } 51 | 52 | // are we on the frontend? 53 | if ( ! is_admin() ) { 54 | /** 55 | * This use is either a speaker or someone selecting, so show more post statuses. 56 | * We wouldn't want to do this in the admin area, else trashed posts and 57 | * published/private posts would be impossible to see, which would be difficult 58 | * to debug 59 | */ 60 | $q->set( 'post_status', array( 'private' ) ); 61 | } 62 | 63 | // If the user cannot select talks, they must be a speaker, only show talks belonging to them 64 | if ( ! wct_user_can( 'list_all_talks' ) ) { 65 | $q->set( 'author', get_current_user_id() ); 66 | } 67 | } 68 | } 69 | } 70 | endif; 71 | -------------------------------------------------------------------------------- /templates/user-comments.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 28 | 29 | 64 | 65 | 80 | 81 | 82 | 83 | 84 | 85 |
86 |

87 |
88 | 89 | 90 | -------------------------------------------------------------------------------- /includes/core/upgrade.php: -------------------------------------------------------------------------------- 1 | $value ) { 94 | add_option( $key, $value ); 95 | } 96 | 97 | /** 98 | * Hook here if you need to perform actions when plugin 99 | * is installed for the first time 100 | * 101 | * @since 1.0.0 102 | */ 103 | do_action( 'wct_installed' ); 104 | } 105 | -------------------------------------------------------------------------------- /includes/core/actions.php: -------------------------------------------------------------------------------- 1 | _x( 'Talk Status', 'Taxonomy General Name', 'wordcamp-talks' ), 26 | 'singular_name' => _x( 'Talk Status', 'Taxonomy Singular Name', 'wordcamp-talks' ), 27 | 'menu_name' => __( 'Talk Statuses', 'wordcamp-talks' ), 28 | 'all_items' => __( 'All Statuses', 'wordcamp-talks' ), 29 | 'parent_item' => __( 'Parent Status', 'wordcamp-talks' ), 30 | 'parent_item_colon' => __( 'Parent Item:', 'wordcamp-talks' ), 31 | 'new_item_name' => __( 'New Status Name', 'wordcamp-talks' ), 32 | 'add_new_item' => __( 'Add New Status', 'wordcamp-talks' ), 33 | 'edit_item' => __( 'Edit Status', 'wordcamp-talks' ), 34 | 'update_item' => __( 'Update Status', 'wordcamp-talks' ), 35 | 'view_item' => __( 'View Status', 'wordcamp-talks' ), 36 | 'separate_items_with_commas' => __( 'Separate statuses with commas', 'wordcamp-talks' ), 37 | 'add_or_remove_items' => __( 'Add or remove statuses', 'wordcamp-talks' ), 38 | 'choose_from_most_used' => __( 'Choose from the most used', 'wordcamp-talks' ), 39 | 'popular_items' => __( 'Popular Statuses', 'wordcamp-talks' ), 40 | 'search_items' => __( 'Search Statuses', 'wordcamp-talks' ), 41 | 'not_found' => __( 'Not Found', 'wordcamp-talks' ), 42 | 'no_terms' => __( 'No statuses', 'wordcamp-talks' ), 43 | 'items_list' => __( 'Status list', 'wordcamp-talks' ), 44 | 'items_list_navigation' => __( 'Status list navigation', 'wordcamp-talks' ), 45 | ); 46 | $args = array( 47 | 'labels' => $labels, 48 | 'hierarchical' => false, 49 | 'public' => false, 50 | 'show_ui' => false, 51 | 'show_admin_column' => true, 52 | 'show_in_nav_menus' => false, 53 | 'show_tagcloud' => false, 54 | 'rewrite' => false, 55 | 'show_in_rest' => true, 56 | ); 57 | register_taxonomy( 'wordcamp-talks-status', array( 'talks' ), $args ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /js/script.min.js: -------------------------------------------------------------------------------- 1 | /*! wordcamp-talks - v1.0.0-beta2 - 2016-12-01 5:39:14 PM UTC - https://github.com/imath/wordcamp-talks */ 2 | !function(a){function b(b){a(".rating-info").html(wct_vars.wait_msg);var d={action:"wct_rate",rate:b,wpnonce:wct_vars.wpnonce,talk:a("#rate").data("talk")};a.post(wct_vars.ajaxurl,d,function(b){b&&b>0?a(".rating-info").html(wct_vars.success_msg+" "+b).fadeOut(2e3,function(){c(1),a(this).show()}):(a(".rating-info").html(wct_vars.error_msg),a.fn.raty("readOnly",!1,"#rate"))})}function c(b){var c,d=Number(wct_vars.rate_nb)+b;c=1===d?wct_vars.one_rate:0===d?wct_vars.not_rated:wct_vars.x_rate.replace("%",d),a(".rating-info").html(""+c+"")}if(wct_vars.canonical&&window.history.replaceState&&window.history.replaceState(null,null,wct_vars.canonical+window.location.hash),"undefined"!=typeof wct_vars.raty_loaded&&(c(0),a("div#rate").raty({cancel:!1,half:!1,halfShow:!0,starType:"i",readOnly:wct_vars.readonly,score:wct_vars.average_rate,targetKeep:!1,noRatedMsg:wct_vars.not_rated,hints:wct_vars.hints,number:wct_vars.hints_nb,click:function(c){wct_vars.can_rate&&(a.fn.raty("readOnly",!0,"#rate"),b(c))}})),a("#wordcamp-talks-form ul.category-list").on("click",":checkbox",function(b){a.each(a(b.delegateTarget).find(":checked"),function(c,d){a(d).prop("id")!==a(b.target).prop("id")&&a(d).prop("checked",!1)})}),"undefined"!=typeof wct_vars.tagging_loaded&&(a("#_wct_the_tags").tagging({"tags-input-name":"wct[_the_tags]","edit-on-delete":!1,"tag-char":"","forbidden-chars":[".","_","?","<",">"],"forbidden-words":["<",">"],"no-duplicate-text":wct_vars.duplicate_tag,"forbidden-chars-text":wct_vars.forbidden_chars,"forbidden-words-text":wct_vars.forbidden_words}),a("#_wct_the_title").focus(),a("#wct_most_used_tags .tag-items a").on("click",function(b){b.preventDefault(),a("#_wct_the_tags").tagging("add",a(this).html())}),a("#wordcamp-talks-form").on("reset",function(){a("#_wct_the_tags").tagging("reset")})),"undefined"!=typeof wp&&"undefined"!=typeof wp.heartbeat&&"undefined"!=typeof wct_vars.pulse&&(wp.heartbeat.interval(wct_vars.pulse),a.fn.extend({"heartbeat-send":function(){return this.bind("heartbeat-send.wc_talks")}})),a(document).on("heartbeat-send.wc_talks",function(a,b){b.wc_talks_heartbeat_current_talk=wct_vars.talk_id}),a(document).on("heartbeat-tick",function(b,c){c.wc_talks_heartbeat_response&&(a("#wordcamp-talks .message").length?(a("#wordcamp-talks .message").removeClass("error").addClass("info"),a("#wordcamp-talks .message p").html(wct_vars.warning)):a("#wordcamp-talks").prepend('

'+wct_vars.warning+"

"),a('#wordcamp-talks .submit input[name="wct[save]"]').remove())}),"undefined"!=typeof wct_vars.is_profile){var d=a("#item-header-content").innerHeight()||0;a(".wp-embed-share-input").on("click",function(a){a.target.select()}),a(".wp-embed-share-dialog-open").on("click",function(){a("#item-header-content").css({width:"600px",height:"200px"}),a(".wp-embed-share-dialog").removeClass("hidden"),a('.wp-embed-share-tab-button [aria-selected="true"]').focus()}),a(".wp-embed-share-dialog-close").on("click",function(){a(".wp-embed-share-dialog").addClass("hidden"),a(".wp-embed-share-dialog-open").focus(),a("#item-header-content").css({width:"auto",height:d+"px"})}),a(".wp-embed-share-tab-button button").on("click",function(b){var c=a(b.target).attr("aria-controls");a(".wp-embed-share-tab").each(function(b,d){c===a(d).prop("id")?a(d).attr("aria-hidden","false"):a(d).attr("aria-hidden","true")})})}}(jQuery); -------------------------------------------------------------------------------- /includes/comments/wordcamp-talks-loop-comments.php: -------------------------------------------------------------------------------- 1 | 'publish', 27 | 'status' => 'approve', 28 | 'user_id' => 0, 29 | 'number' => wct_talks_per_page(), 30 | ); 31 | 32 | // All post status if user is viewing his profile 33 | if ( wct_is_current_user_profile() || current_user_can( 'read_private_talks' ) ) { 34 | $default['post_status'] = ''; 35 | } 36 | 37 | //Merge default with requested 38 | $r = wp_parse_args( $args, $default ); 39 | 40 | // Set which pagination page 41 | if ( get_query_var( wct_cpage_rewrite_id() ) ) { 42 | $paged = get_query_var( wct_cpage_rewrite_id() ); 43 | 44 | } else if ( ! empty( $_GET[ wct_cpage_rewrite_id() ] ) ) { 45 | $paged = absint( $_GET[ wct_cpage_rewrite_id() ] ); 46 | 47 | } else if ( ! empty( $r['page'] ) ) { 48 | $paged = absint( $r['page'] ); 49 | 50 | // Set default page (first page) 51 | } else { 52 | $paged = 1; 53 | } 54 | 55 | $comments_args = array( 56 | 'post_type' => 'talks', 57 | 'post_status' => $r['post_status'], 58 | 'status' => $r['status'], 59 | 'user_id' => (int) $r['user_id'], 60 | 'number' => (int) $r['number'], 61 | 'offset' => intval( ( $paged - 1 ) * $r['number'] ), 62 | 'page' => (int) $paged, 63 | ); 64 | 65 | if ( ! empty( $comments_args ) ) { 66 | foreach ( $comments_args as $key => $value ) { 67 | $this->{$key} = $value; 68 | } 69 | } else { 70 | return false; 71 | } 72 | 73 | if ( empty( $this->user_id ) ) { 74 | $comment_count = 0; 75 | } else { 76 | $comment_count = wct_comments_count_comments( $this->user_id ); 77 | } 78 | 79 | // Get the comments 80 | $comments = get_comments( $comments_args ); 81 | 82 | if ( ! empty( $comments ) ) { 83 | $post_ids = wp_list_pluck( $comments, 'comment_post_ID' ); 84 | 85 | // Get all posts in the object cache. 86 | $posts = get_posts( array( 'include' => $post_ids, 'post_type' => 'talks' ) ); 87 | 88 | // Reset will need to be done at the end of the loop 89 | wct_set_global( 'needs_reset', true ); 90 | 91 | // Build a new post array indexed by post ID 92 | $p = array(); 93 | foreach ( $posts as $post ) { 94 | $p[ $post->ID ] = $post; 95 | } 96 | 97 | // Attach the corresponding post to each comment 98 | foreach ( $comments as $key => $comment ) { 99 | if ( ! empty( $p[ $comment->comment_post_ID ] ) ) { 100 | $comments[ $key ]->talk = $p[ $comment->comment_post_ID ]; 101 | } 102 | } 103 | } 104 | 105 | $params = array( 106 | 'plugin_prefix' => 'wct', 107 | 'item_name' => 'comment', 108 | 'item_name_plural' => 'comments', 109 | 'items' => $comments, 110 | 'total_item_count' => $comment_count, 111 | 'page' => $this->page, 112 | 'per_page' => $this->number, 113 | ); 114 | 115 | $paginate_args = array(); 116 | 117 | $paginate_args['base'] = trailingslashit( wct_users_get_displayed_profile_url( 'comments') ) . '%_%'; 118 | $paginate_args['format'] = 'cpage/%#%/'; 119 | 120 | parent::start( $params, apply_filters( 'wct_comments_pagination_args', $paginate_args ) ); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WordCamp Talks 2 | ============== 3 | 4 | This WP Plugin is no more supported and this repository is now archived. 5 | 6 | If you need to manage a "Call for Speakers" for a WordCamp you are an organizer of, this plugin can make you save some time and ease your life. 7 | 8 | Candidates will be able to sign up to your WordPress site using a form including the custom contact methods you can easily generate from the "Talks > Settings" Administration screen as shown below: 9 | 10 | ![Signup Form](https://cldup.com/CdigRMNoFE.png) 11 | 12 | As you can see, you just need to add a comma separated list of contact method labels into the Public or Private user profile fields, save the settings and then you'll be able to activate the checkboxes of your choice in the "Fields to add to the signup form" option. 13 | 14 | NB: if you are using WordPress 4.7 and you have more than one language available in your WordPress config, candidates will also be able to select their prefered language for the strings used in the plugin from this signup form. 15 | 16 | Then, once logged in they will be able to submit as many talk as they wish using the [submit form](https://cldup.com/oCAOf9VED0.png). You can create new categories to tidy all this creativity. 17 | 18 | ![Regular categories](https://cldup.com/nnMX5CIqE3.png) 19 | 20 | Using "flat" (without a hierarchy) categories will display them into a section called "Categories" of the submit form. But you can also choose to use a one level hierarchy in which the parent will be used as the section and the children as checkboxes like shown below: 21 | 22 | ![Hierarchical categories](https://cldup.com/R1z5y2oTJ8.png) 23 | 24 | As you can see the title section is using the parent category name and the additional information is using the parent category description. 25 | 26 | The talks will be published privately so that only the candidate, the Site Administrator or users having one of the 2 specific rating roles can read and evaluate the talks. These 2 roles are: 27 | 28 | + The Rater: users having this role cannot publish talks but can only evaluate them (comment/rate) and view the candidates profiles. 29 | + The Blind Rater: users having this role cannot publish talks, they can evaluate them (comment/rate) *but* they *cannot* view the candidates profiles. 30 | 31 | Raters and Admins can use the comments to discuss directly on the talk, the talk's author won't see them. Raters can also use the built in star rating system to progressively build a list of the most promising talks for your WordCamp. From the main archive page of the plugin (usually available at site.url/talks), you will be able to reorder talks according to the number of comments or the average rate they got. 32 | 33 | ![Workflow](https://cldup.com/YTbC6TQB6o.png) 34 | 35 | Admins can also use a mini workflow to build their selection. It's also possible to export a comma separated list of talks clicking on the spreadsheet dashicon next to the Status views. 36 | 37 | When you begin to have a lot of submissions, it can be tricky to remember the talks you already rated. That's the reason why @jennybeaumont had the idea to include a specific tab into the Rater's user profile: the "To rate" tab: 38 | 39 | ![Workflow](https://cldup.com/BeMdU2rb1B.png) 40 | 41 | Thanks to her you won't miss a talk! 42 | 43 | Requirements 44 | ------------ 45 | 46 | It's a WordPress plugin requiring at least WordPress 4.6.1. 47 | Tested up to WordPress 4.7. 48 | 49 | Cloning/Downloading 50 | ------------------- 51 | 52 | If you're familiar with git you can simply go at the root of you plugins directory and do: 53 | 54 | ``` 55 | git clone https://github.com/imath/wordcamp-talks.git 56 | 57 | ``` 58 | 59 | If you downloaded the master archive or one of the release tags. Make sure to rename the main folder of the plugin to 'wordcamp-talks' before activating it. 60 | -------------------------------------------------------------------------------- /includes/core/rewrites.php: -------------------------------------------------------------------------------- 1 | pagination_base; 39 | } 40 | 41 | /** 42 | * Rewrite id for the user's profile 43 | * 44 | * @package WordCamp Talks 45 | * @subpackage core/rewrites 46 | * 47 | * @since 1.0.0 48 | * 49 | * @return string The user's profile rewrite id 50 | */ 51 | function wct_user_rewrite_id( $default = 'is_user' ) { 52 | return apply_filters( 'wct_user_rewrite_id', $default ); 53 | } 54 | 55 | /** 56 | * Rewrite id for the user's rates 57 | * 58 | * @package WordCamp Talks 59 | * @subpackage core/rewrites 60 | * 61 | * @since 1.0.0 62 | * 63 | * @return string The user's rates rewrite id 64 | */ 65 | function wct_user_rates_rewrite_id( $default = 'is_rates' ) { 66 | return apply_filters( 'wct_user_rates_rewrite_id', $default ); 67 | } 68 | 69 | /** 70 | * Rewrite id for the user's to rate 71 | * 72 | * @package WordCamp Talks 73 | * @subpackage core/rewrites 74 | * 75 | * @since 1.0.0 76 | * 77 | * @return string The user's to rate rewrite id 78 | */ 79 | function wct_user_to_rate_rewrite_id( $default = 'is_to_rate' ) { 80 | return apply_filters( 'wct_user_to_rate_rewrite_id', $default ); 81 | } 82 | 83 | /** 84 | * Rewrite id for the user's talks 85 | * 86 | * @package WordCamp Talks 87 | * @subpackage core/rewrites 88 | * 89 | * @since 1.0.0 90 | * 91 | * @return string The user's talks rewrite id 92 | */ 93 | function wct_user_talks_rewrite_id( $default = 'is_user_talks' ) { 94 | return apply_filters( 'wct_user_talks_rewrite_id', $default ); 95 | } 96 | 97 | /** 98 | * Rewrite id for the user's comments 99 | * 100 | * @package WordCamp Talks 101 | * @subpackage core/rewrites 102 | * 103 | * @since 1.0.0 104 | * 105 | * @return string The user's comments rewrite id 106 | */ 107 | function wct_user_comments_rewrite_id( $default = 'is_comments' ) { 108 | return apply_filters( 'wct_user_comments_rewrite_id', $default ); 109 | } 110 | 111 | /** 112 | * Rewrite id for actions 113 | * 114 | * @package WordCamp Talks 115 | * @subpackage core/rewrites 116 | * 117 | * @since 1.0.0 118 | * 119 | * @return string The actions rewrite id 120 | */ 121 | function wct_action_rewrite_id( $default = 'is_action' ) { 122 | return apply_filters( 'wct_action_rewrite_id', $default ); 123 | } 124 | 125 | /** 126 | * Rewrite id for searching in talks 127 | * 128 | * @package WordCamp Talks 129 | * @subpackage core/rewrites 130 | * 131 | * @since 1.0.0 132 | * 133 | * @return string Searching in talks rewrite id 134 | */ 135 | function wct_search_rewrite_id( $default = 'talk_search' ) { 136 | return apply_filters( 'wct_search_rewrite_id', $default ); 137 | } 138 | 139 | /** 140 | * Rewrite id for user's comments pagination 141 | * 142 | * @package WordCamp Talks 143 | * @subpackage core/rewrites 144 | * 145 | * @since 1.0.0 146 | * 147 | * @return string User's comments pagination rewrite id 148 | */ 149 | function wct_cpage_rewrite_id( $default = 'cpaged' ) { 150 | return apply_filters( 'wct_cpage_rewrite_id', $default ); 151 | } 152 | 153 | /** 154 | * Delete a blogs rewrite rules, so that they are automatically rebuilt on 155 | * the subsequent page load. 156 | * 157 | * @package WordCamp Talks 158 | * @subpackage core/rewrites 159 | * 160 | * @since 1.0.0 161 | */ 162 | function wct_delete_rewrite_rules() { 163 | delete_option( 'rewrite_rules' ); 164 | } 165 | -------------------------------------------------------------------------------- /includes/core/status/views/publish-box.php: -------------------------------------------------------------------------------- 1 | statuses = [ 18 | '' => 'Newly submitted', 19 | 'rejected' => 'Rejected', 20 | 'shortlisted' => 'Shortlisted', 21 | 'selected' => 'Selected', 22 | 'backup' => 'Backup', 23 | ]; 24 | } 25 | 26 | /** 27 | * After the object is created, this tells it to start doing work 28 | * 29 | * @return void 30 | */ 31 | public function run() { 32 | add_action( 'post_submitbox_misc_actions', [ $this, 'set_publishing_actions' ] ); 33 | add_action( 'post_submitbox_misc_actions', [ $this, 'selection_control' ] ); 34 | add_action( 'update_post', [ $this, 'save_post' ] ); 35 | add_action( 'save_post', [ $this, 'save_post' ], 1, 1 ); 36 | } 37 | 38 | function set_publishing_actions() { 39 | global $post; 40 | if ( 'talks' === $post->post_type ) { 41 | echo ''; 60 | } 61 | } 62 | 63 | function selection_control() { 64 | global $post; 65 | if ( 'talks' !== get_post_type( $post ) ) { 66 | return; 67 | } 68 | if ( true === self::$ran ) { 69 | return; 70 | } 71 | self::$ran = true; 72 | wp_nonce_field( plugin_basename( __FILE__ ), 'wct_publish_box_nonce' ); 73 | $terms = get_the_terms( $post->ID, 'wordcamp-talks-status' ); 74 | $status = ''; 75 | if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) { 76 | $status = $terms[0]->slug; 77 | } 78 | ?> 79 |
80 | 92 |
93 |
'; 95 | echo ' ';*/ 96 | } 97 | 98 | function save_post( $post_id ) { 99 | //wct_publish_box_nonce 100 | if ( ! isset( $_POST['post_type'] ) ) { 101 | return $post_id; 102 | } 103 | 104 | if ( ! wp_verify_nonce( $_POST['wct_publish_box_nonce'], plugin_basename( __FILE__ ) ) ) { 105 | return $post_id; 106 | } 107 | 108 | if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { 109 | return $post_id; 110 | } 111 | 112 | if ( 'talks' == $_POST['post_type'] && ! current_user_can( 'edit_post', $post_id ) ) { 113 | return $post_id; 114 | } 115 | 116 | if ( ! isset( $_POST['wct_talk_status'] ) ) { 117 | return $post_id; 118 | } 119 | $status = sanitize_title( wp_unslash( $_POST['wct_talk_status'] ) ); 120 | if ( empty( $status ) ) { 121 | return $post_id; 122 | } 123 | if ( ! array_key_exists( $status, $this->statuses ) ) { 124 | wp_die( 'Error: Illegal talk status "'.esc_html( $status ).'"' ); 125 | } 126 | // if the term doesn't exist, create it with the appropriate details 127 | if ( ! term_exists( $status, 'wordcamp-talks-status' ) ) { 128 | wp_insert_term( $this->statuses[ $status ], 'wordcamp-talks-status', [ 129 | 'slug' => $status, 130 | ] ); 131 | } 132 | wp_set_object_terms( $post_id, $status, 'wordcamp-talks-status', false ); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /includes/core/filters.php: -------------------------------------------------------------------------------- 1 | 0 ){ 53 | $( '.rating-info' ).html( wct_vars.success_msg + ' ' + response ).fadeOut( 2000, function() { 54 | wpis_update_rate_num( 1 ); 55 | $(this).show(); 56 | } ); 57 | } else { 58 | $( '.rating-info' ).html( wct_vars.error_msg ); 59 | $.fn.raty( 'readOnly', false, '#rate' ); 60 | } 61 | }); 62 | } 63 | 64 | function wpis_update_rate_num( rate ) { 65 | var number = Number( wct_vars.rate_nb ) + rate, 66 | msg; 67 | 68 | if ( 1 === number ) { 69 | msg = wct_vars.one_rate; 70 | } else if( 0 === number ) { 71 | msg = wct_vars.not_rated; 72 | } else { 73 | msg = wct_vars.x_rate.replace( '%', number ); 74 | } 75 | 76 | $( '.rating-info' ).html( '' + msg + '' ); 77 | } 78 | 79 | // Checkbox are radio groups! 80 | $( '#wordcamp-talks-form ul.category-list' ).on( 'click', ':checkbox', function( event ) { 81 | $.each( $( event.delegateTarget ).find( ':checked' ), function( cb, checkbox ) { 82 | if ( $( checkbox ).prop( 'id' ) !== $( event.target ).prop( 'id' ) ) { 83 | $( checkbox ).prop( 'checked', false ); 84 | } 85 | } ); 86 | } ); 87 | 88 | if ( typeof wct_vars.tagging_loaded !== 'undefined' ) { 89 | $( '#_wct_the_tags' ).tagging( { 90 | 'tags-input-name' : 'wct[_the_tags]', 91 | 'edit-on-delete' : false, 92 | 'tag-char' : '', 93 | 'forbidden-chars' : [ '.', '_', '?', '<', '>' ], 94 | 'forbidden-words' : [ '<', '>' ], 95 | 'no-duplicate-text' : wct_vars.duplicate_tag, 96 | 'forbidden-chars-text' : wct_vars.forbidden_chars, 97 | 'forbidden-words-text' : wct_vars.forbidden_words 98 | } ); 99 | 100 | // Make sure the title gets the focus 101 | $( '#_wct_the_title' ).focus(); 102 | 103 | // Add most used tags 104 | $( '#wct_most_used_tags .tag-items a' ).on( 'click', function( event ) { 105 | event.preventDefault(); 106 | 107 | $( '#_wct_the_tags' ).tagging( 'add', $( this ).html() ); 108 | } ); 109 | 110 | // Reset tags 111 | $( '#wordcamp-talks-form' ).on( 'reset', function() { 112 | $( '#_wct_the_tags' ).tagging( 'reset' ); 113 | } ); 114 | } 115 | 116 | // Set the interval and the namespace event 117 | if ( typeof wp !== 'undefined' && typeof wp.heartbeat !== 'undefined' && typeof wct_vars.pulse !== 'undefined' ) { 118 | wp.heartbeat.interval( wct_vars.pulse ); 119 | 120 | $.fn.extend( { 121 | 'heartbeat-send': function() { 122 | return this.bind( 'heartbeat-send.wc_talks' ); 123 | } 124 | } ); 125 | } 126 | 127 | // Send the current talk ID being edited 128 | $( document ).on( 'heartbeat-send.wc_talks', function( e, data ) { 129 | data.wc_talks_heartbeat_current_talk = wct_vars.talk_id; 130 | } ); 131 | 132 | // Inform the user if data has been returned 133 | $( document ).on( 'heartbeat-tick', function( e, data ) { 134 | 135 | // Only proceed if an admin took the lead 136 | if ( ! data.wc_talks_heartbeat_response ) { 137 | return; 138 | } 139 | 140 | if ( ! $( '#wordcamp-talks .message' ).length ) { 141 | $( '#wordcamp-talks' ).prepend( 142 | '
' + 143 | '

' + wct_vars.warning + '

' + 144 | '
' 145 | ); 146 | } else { 147 | $( '#wordcamp-talks .message' ).removeClass( 'error' ).addClass( 'info' ); 148 | $( '#wordcamp-talks .message p' ).html( wct_vars.warning ); 149 | } 150 | 151 | $( '#wordcamp-talks .submit input[name="wct[save]"]' ).remove(); 152 | } ); 153 | 154 | })( jQuery ); 155 | -------------------------------------------------------------------------------- /includes/talks/wordcamp-talks-loop-talks.php: -------------------------------------------------------------------------------- 1 | talk ) ) { 52 | $talk = wct_talks_get_talk_by_name( $args['talk_name'] ); 53 | } else { 54 | $talk = $query_loop->talk; 55 | } 56 | 57 | // can't do this too ealy 58 | $reset_data = array_merge( (array) $talk, array( 'is_page' => true ) ); 59 | wct_reset_post( $reset_data ); 60 | 61 | // this needs a "reset postdata"! 62 | wct_set_global( 'needs_reset', true ); 63 | 64 | $talks = array( 65 | 'talks' => array( $talk ), 66 | 'total' => 1, 67 | 'get_args' => array( 68 | 'page' => 1, 69 | 'per_page' => 1, 70 | ), 71 | ); 72 | 73 | // Get the talks 74 | } else { 75 | $talks = wct_talks_get_talks( $args ); 76 | } 77 | 78 | if ( ! empty( $talks['get_args'] ) ) { 79 | foreach ( $talks['get_args'] as $key => $value ) { 80 | $this->{$key} = $value; 81 | } 82 | } else { 83 | return false; 84 | } 85 | 86 | $params = array( 87 | 'plugin_prefix' => 'wct', 88 | 'item_name' => 'talk', 89 | 'item_name_plural' => 'talks', 90 | 'items' => $talks['talks'], 91 | 'total_item_count' => $talks['total'], 92 | 'page' => $this->page, 93 | 'per_page' => $this->per_page, 94 | ); 95 | 96 | $paginate_args = array(); 97 | 98 | // Is it the main archive page ? 99 | if ( wct_is_talks_archive() ) { 100 | $base = trailingslashit( wct_get_root_url() ) . '%_%'; 101 | 102 | // Or the category archive page ? 103 | } else if ( wct_is_category() ) { 104 | $base = trailingslashit( wct_get_category_url() ) . '%_%'; 105 | 106 | // Or the tag archive page ? 107 | } else if ( wct_is_tag() ) { 108 | $base = trailingslashit( wct_get_tag_url() ) . '%_%'; 109 | 110 | // Or the displayed user rated talks ? 111 | } else if ( wct_is_user_profile_rates() ) { 112 | $base = trailingslashit( wct_users_get_displayed_profile_url( 'rates' ) ) . '%_%'; 113 | 114 | // Or the displayed user talks "to rate" ? 115 | } else if ( wct_is_user_profile_to_rate() ) { 116 | $base = trailingslashit( wct_users_get_displayed_profile_url( 'to_rate' ) ) . '%_%'; 117 | 118 | // Or the displayed user published talks ? 119 | } else if ( wct_is_user_profile_talks() ) { 120 | $base = trailingslashit( wct_users_get_displayed_profile_url( 'talks' ) ) . '%_%'; 121 | 122 | // Or the displayed user home page ? 123 | } else if ( wct_is_user_profile_home() ) { 124 | $base = trailingslashit( wct_users_get_displayed_profile_url() ) . '%_%'; 125 | 126 | // Or nothing i've planed ? 127 | } else { 128 | 129 | /** 130 | * Create your own pagination base if not handled by the plugin 131 | * 132 | * @param string empty string 133 | */ 134 | $base = apply_filters( 'wct_talks_pagination_base', '' ); 135 | } 136 | 137 | $paginate_args['base'] = $base; 138 | $paginate_args['format'] = wct_paged_slug() . '/%#%/'; 139 | 140 | // Is this a search ? 141 | if ( wct_get_global( 'is_search' ) ) { 142 | $paginate_args['add_args'] = array( wct_search_rewrite_id() => $_GET[ wct_search_rewrite_id() ] ); 143 | } 144 | 145 | // Do we have a specific order to use ? 146 | $orderby = wct_get_global( 'orderby' ); 147 | 148 | if ( ! empty( $orderby ) && 'date' != $orderby ) { 149 | $merge = array(); 150 | 151 | if ( ! empty( $paginate_args['add_args'] ) ) { 152 | $merge = $paginate_args['add_args']; 153 | } 154 | $paginate_args['add_args'] = array_merge( $merge, array( 'orderby' => $orderby ) ); 155 | } 156 | 157 | /** 158 | * Use this filter to override the pagination 159 | * 160 | * @param array $paginate_args the pagination arguments 161 | */ 162 | parent::start( $params, apply_filters( 'wct_talks_pagination_args', $paginate_args ) ); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /js/tagging.min.js: -------------------------------------------------------------------------------- 1 | /*! wordcamp-talks - v1.0.0-beta2 - 2016-12-01 5:39:14 PM UTC - https://github.com/imath/wordcamp-talks */ 2 | !function(a,b,c,d){var e=function(b,c){this.elem=b,this.$elem=a(b),this.options=c};e.prototype={tags:[],keys:{add:{comma:188,enter:13,spacebar:32},remove:{del:46,backspace:8}},defaults:{"case-sensitive":!1,"close-char":"×","close-class":"tag-i","edit-on-delete":!0,"forbidden-chars":[".","_","?"],"forbidden-chars-callback":b.alert,"forbidden-chars-text":"Forbidden character:","forbidden-words":[],"forbidden-words-callback":b.alert,"forbidden-words-text":"Forbidden word:","no-backspace":!1,"no-comma":!1,"no-del":!1,"no-duplicate":!0,"no-duplicate-callback":b.alert,"no-duplicate-text":"Duplicate tag:","no-enter":!1,"no-spacebar":!1,"pre-tags-separator":", ","tag-box-class":"tagging","tag-char":"#","tag-class":"tag","tags-input-name":"tag","type-zone-class":"type-zone"},add:function(b){var d,e,f,g,h,i,j;if(f=this,a.isArray(b))return a.each(b,function(){f.add(this+"")});if(h=f.config["forbidden-words"],b||(b=f.valInput(),f.emptyInput()),!b||!b.length)return!1;for(f.config["case-sensitive"]||(b=b.toLowerCase()),e=h.length;e--;)if(g=b.indexOf(h[e]),g>=0)return f.emptyInput(),i=f.config["forbidden-words-callback"],j=f.config["forbidden-words-text"],f.throwError(i,j,b);if(f.config["no-duplicate"])for(e=f.tags.length;e--;)if(f.tags[e].pure_text===b)return f.emptyInput(),i=f.config["no-duplicate-callback"],j=f.config["no-duplicate-text"],f.throwError(i,j,b);return d=a(c.createElement("div")).addClass(f.config["tag-class"]).html(""+f.config["tag-char"]+" "+b),a(c.createElement("input")).attr("type","hidden").attr("name",f.config["tags-input-name"]+"[]").val(b).appendTo(d),a(c.createElement("a")).attr("role","button").addClass(f.config["close-class"]).html(f.config["close-char"]).click(function(){f.remove(d)}).appendTo(d),d.pure_text=b,f.tags.push(d),f.$type_zone.before(d),!0},addSpecialKeys:function(b){var c,d,e,f,g;if(c=this,g=b[0],f=b[1],e={},a.isArray(f))return a.each(f,function(){c.addSpecialKeys([g,this])});if(!f&&f.constructor!==Object)return"Error -> The second argument is not an Object!";for(d in f)f.hasOwnProperty(d)&&f[d]===+f[d]&&f[d]===(0|f[d])&&a.extend(e,f);return c.keys[g]=a.extend({},e,c.keys[g]),c.keys[g]},destroy:function(){return this.$elem.find("."+this.config["type-zone-class"]).remove(),this.$elem.find("."+this.config["tag-class"]).remove(),this.$elem.data("tag-box",null),!0},emptyInput:function(){return this.focusInput(),this.valInput("")},focusInput:function(){return this.$type_zone.focus()},getDataOptions:function(){var a,b,c;c={};for(a in this.defaults)b=this.$elem.data(a),b&&(c[a]=b);return c},getSpecialKeys:function(){return a.extend({},this.keys.add,this.keys.remove)},getSpecialKeysD:function(){return this.keys},getTags:function(){var a,b,c;for(c=this.tags.length,a=[],b=0;b=0)return a.preventDefault(),i=i.replace(h[e],""),d.focusInput(),d.valInput(i),j=d.config["forbidden-chars-callback"],k=d.config["forbidden-chars-text"],d.throwError(j,k,h[e]);for(b in d.keys.add)if(f===d.keys.add[b]&&!d.config["no-"+b])return a.preventDefault(),d.add()}else for(b in g)if(f===g[b]){if(d.keys.add[b])return a.preventDefault(),!0;if(d.keys.remove[b]&&!d.config["no-"+b])return a.preventDefault(),d.remove()}return!0}),d.$elem.on("click",function(){d.focusInput()}),d.refresh(b),d},refresh:function(b){var c,d;return c=this,d=c.config["pre-tags-separator"],b=b||c.getTags().join(d),c.reset(),a.each(b.split(d),function(){c.add(this+"")}),!0},remove:function(b){var c,d,e;if(c=this,a.isArray(b))return a.each(b,function(){c.remove(this+"")});if("string"==typeof b&&(d=b,b=c.$elem.find("input[value="+d+"]").parent(),!b.length))return"Error -> Tag not found";if(b)for(e=c.tags.length;e--;)c.tags[e][0].innerHTML===b[0].innerHTML&&c.tags.splice(e,1);else b=c.tags.pop();return d=d||b.pure_text,b.remove(),c.config["edit-on-delete"]&&(c.emptyInput(),c.valInput(b.pure_text)),b},removeAll:function(){return this.reset()},removeSpecialKeys:function(b){var c,e,f,g,h;if(c=this,h=b[0],g=b[1],f={},a.isArray(g))return a.each(g,function(){c.removeSpecialKeys([h,this])});for(e in c.keys[h])c.keys[h].hasOwnProperty(e)&&c.keys[h][e]===g&&(c.keys[h][e]=d);return c.keys[h]},reset:function(){var a;for(a=this.tags.length;a--;)this.remove(this.tags[a]);return this.emptyInput(),this.tags},throwError:function(a,b,c){return a([b+" '"+c+"'."])},valInput:function(a){return null==a?this.$type_zone.val():this.$type_zone.val(a)}},a.fn.tagging=function(b,c){var d=[];return this.each(function(){var f,g,h;f=a(this),g=f.data("tag-box"),g?(h=g[b](c),h&&d.push(h)):(g=new e(this,b),f.data("tag-box",g),g.init(),d.push(g.$elem))}),"string"==typeof b?d.length>1?d:d[0]:d}}(window.jQuery,window,document); -------------------------------------------------------------------------------- /includes/core/widgets.php: -------------------------------------------------------------------------------- 1 | __( 'Add WordCamp Talks's nav to your sidebar.', 'wordcamp-talks' ) ); 36 | parent::__construct( false, $name = __( 'WordCamp Talks Nav', 'wordcamp-talks' ), $widget_ops ); 37 | 38 | // We need to wait for the talks post type to be registered 39 | add_action( 'init', array( $this, 'set_available_nav_items' ) ); 40 | } 41 | 42 | /** 43 | * Register the widget 44 | * 45 | * @package WordCamp Talks 46 | * @subpackage core/widgets 47 | * 48 | * @since 1.0.0 49 | */ 50 | public static function register_widget() { 51 | register_widget( 'WordCamp_Talks_Navig' ); 52 | } 53 | 54 | /** 55 | * Setup available nav items 56 | * 57 | * @package WordCamp Talks 58 | * @subpackage core/widgets 59 | */ 60 | public function set_available_nav_items() { 61 | // construct nav 62 | $this->nav_items_available = array( 63 | 'talk_archive' => array( 64 | 'url' => wct_get_root_url(), 65 | 'name' => wct_archive_title() 66 | ), 67 | 'addnew' => array( 68 | 'url' => wct_get_form_url(), 69 | 'name' => __( 'New talk', 'wordcamp-talks' ) 70 | ) 71 | ); 72 | 73 | if ( is_user_logged_in() ) { 74 | $this->nav_items_available['current_user_profile'] = array( 75 | 'url' => wct_users_get_logged_in_profile_url(), 76 | 'name' => __( 'My profile', 'wordcamp-talks' ) 77 | ); 78 | } 79 | 80 | if ( wct_is_signup_allowed_for_current_blog() ) { 81 | $this->nav_items_available['signup'] = array( 82 | 'url' => wct_users_get_signup_url(), 83 | 'name' => __( 'Sign up', 'wordcamp-talks' ) 84 | ); 85 | } 86 | 87 | /** 88 | * @param array the available nav items 89 | * @param string the widget's id base 90 | */ 91 | $this->nav_items_available = apply_filters( 'wct_widget_nav_items', $this->nav_items_available, $this->id_base ); 92 | } 93 | 94 | /** 95 | * Display the widget on front end 96 | * 97 | * @package WordCamp Talks 98 | * @subpackage core/widgets 99 | * 100 | * @since 1.0.0 101 | */ 102 | public function widget( $args = array(), $instance = array() ) { 103 | // Default to all items 104 | $nav_items = array( 'talk_archive', 'addnew', 'current_user_profile' ); 105 | 106 | if ( ! empty( $instance['nav_items'] ) ) { 107 | $nav_items = (array) $instance['nav_items']; 108 | } 109 | 110 | // No nav items to show !? Stop! 111 | if ( empty( $nav_items ) ) { 112 | return; 113 | } 114 | 115 | // Get selected Nav items 116 | $nav_items = array_intersect_key( $this->nav_items_available, array_flip( $nav_items ) ); 117 | 118 | // Default to nothing 119 | $title = ''; 120 | 121 | if ( ! empty( $instance['title'] ) ) { 122 | $title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ); 123 | } 124 | 125 | echo $args['before_widget']; 126 | 127 | if ( ! empty( $title ) ) { 128 | echo $args['before_title'] . $title . $args['after_title']; 129 | } 130 | 131 | // Display the Nav 132 | ?> 133 | 160 | 208 | 209 |

210 | 211 | 212 |

213 | 214 |

215 | 216 | nav_items_available as $key_item => $item ) : ?> 217 | 218 | id="get_field_id( 'nav_items' ) . '-' . $key_item; ?>" name="get_field_name( 'nav_items' ); ?>[]" value="" /> 219 |
220 | 221 | 222 | 223 |

224 | 225 | get_template_file_names( $slug, $name ); 58 | 59 | // Return the part that is found 60 | return $this->locate_template( $templates, $load, $require_once ); 61 | } 62 | 63 | /** 64 | * Given a slug and optional name, create the file names of templates. 65 | * 66 | * @package WordCamp Talks 67 | * @subpackage core/classes 68 | * 69 | * @since 1.0.0 70 | * 71 | * @param string $slug 72 | * @param string $name 73 | * @return array 74 | */ 75 | protected function get_template_file_names( $slug, $name, $ext = 'php' ) { 76 | $templates = array(); 77 | if ( isset( $name ) ) { 78 | $templates[] = $slug . '-' . $name . '.' . $ext; 79 | } 80 | $templates[] = $slug . '.' . $ext; 81 | 82 | /** 83 | * Allow template choices to be filtered. 84 | */ 85 | return apply_filters( $this->filter_prefix . '_get_template_part', $templates, $slug, $name ); 86 | } 87 | 88 | /** 89 | * Retrieve the name of the highest priority template file that exists. 90 | * 91 | * @package WordCamp Talks 92 | * @subpackage core/classes 93 | * 94 | * @since 1.0.0 95 | * 96 | * @param string|array $template_names Template file(s) to search for, in order. 97 | * @param bool $load If true the template file will be loaded if it is found. 98 | * @param bool $require_once Whether to require_once or require. Default true. 99 | * Has no effect if $load is false. 100 | * @return string The template filename if one is located. 101 | */ 102 | public function locate_template( $template_names, $load = false, $require_once = true ) { 103 | // No file found yet 104 | $located = false; 105 | 106 | // Remove empty entries 107 | $template_names = array_filter( (array) $template_names ); 108 | $template_paths = $this->get_template_paths(); 109 | 110 | // Try to find a template file 111 | foreach ( $template_names as $template_name ) { 112 | // Trim off any slashes from the template name 113 | $template_name = ltrim( $template_name, '/' ); 114 | 115 | // Try locating this template file by looping through the template paths 116 | foreach ( $template_paths as $template_path ) { 117 | if ( file_exists( $template_path . $template_name ) ) { 118 | $located = $template_path . $template_name; 119 | break 2; 120 | } 121 | } 122 | } 123 | 124 | if ( $load && $located ) { 125 | load_template( $located, $require_once ); 126 | } 127 | 128 | return $located; 129 | } 130 | 131 | /** 132 | * Return a list of paths to check for template locations. 133 | * 134 | * @package WordCamp Talks 135 | * @subpackage core/classes 136 | * 137 | * @since 1.0.0 138 | * 139 | * @return mixed|void 140 | */ 141 | protected function get_template_paths() { 142 | $theme_directory = trailingslashit( $this->theme_template_directory ); 143 | 144 | $file_paths = array( 145 | 10 => trailingslashit( get_template_directory() ) . $theme_directory, 146 | 100 => $this->get_templates_dir(), 147 | ); 148 | 149 | // Only add this conditionally, so non-child themes don't redundantly check active theme twice. 150 | if ( is_child_theme() ) { 151 | $file_paths[1] = trailingslashit( get_stylesheet_directory() ) . $theme_directory; 152 | } 153 | 154 | /** 155 | * Allow ordered list of template paths to be amended. 156 | */ 157 | $file_paths = apply_filters( $this->filter_prefix . '_template_paths', $file_paths ); 158 | 159 | // sort the file paths based on priority 160 | ksort( $file_paths, SORT_NUMERIC ); 161 | 162 | return array_map( 'trailingslashit', $file_paths ); 163 | } 164 | 165 | /** 166 | * Return the path to the templates directory in this plugin. 167 | * 168 | * @package WordCamp Talks 169 | * @subpackage core/classes 170 | * 171 | * @since 1.0.0 172 | * 173 | * @return string 174 | */ 175 | protected function get_templates_dir() { 176 | return untrailingslashit( wct_get_templates_dir() ); 177 | } 178 | 179 | /** 180 | * Return the url to the plugin's stylesheet. 181 | * 182 | * That's my little "extend" of the Original GamaJo Class 183 | * The goal is to also benefit of the template location feature 184 | * for the css file. This way, a theme can override the plugin's 185 | * stylesheet from the wordcamp-talks theme's folder as soon as 186 | * the custom css file is named style.css 187 | * 188 | * @package WordCamp Talks 189 | * @subpackage core/classes 190 | * 191 | * @since 1.0.0 192 | * 193 | * @return string 194 | */ 195 | public function get_stylesheet( $css = 'style' ) { 196 | $styles = $this->get_template_file_names( $css, null, 'css' ); 197 | 198 | $located = $this->locate_template( $styles ); 199 | 200 | // Microsoft is annoying... 201 | $slashed_located = str_replace( '\\', '/', $located ); 202 | $slashed_content_dir = str_replace( '\\', '/', WP_CONTENT_DIR ); 203 | $slashed_plugin_dir = str_replace( '\\', '/', wct_get_plugin_dir() ); 204 | 205 | // Should allways be the case for regular configs 206 | if ( false !== strpos( $slashed_located, $slashed_content_dir ) ) { 207 | $located = str_replace( $slashed_content_dir, content_url(), $slashed_located ); 208 | 209 | // If not, WordCamp Talks might be symlinked, so let's try this 210 | } else { 211 | $located = str_replace( $slashed_plugin_dir, wct_get_plugin_url(), $slashed_located ); 212 | } 213 | 214 | return $located; 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /includes/core/wordcamp-talks-rewrites.php: -------------------------------------------------------------------------------- 1 | setup_globals(); 23 | $this->hooks(); 24 | } 25 | 26 | /** 27 | * Start the rewrites 28 | * 29 | * @package WordCamp Talks 30 | * @subpackage core/classes 31 | * 32 | * @since 1.0.0 33 | */ 34 | public static function start() { 35 | $wct = wct(); 36 | 37 | if ( empty( $wct->rewrites ) ) { 38 | $wct->rewrites = new self; 39 | } 40 | 41 | return $wct->rewrites; 42 | } 43 | 44 | /** 45 | * Setup the rewrite ids and slugs 46 | * 47 | * @package WordCamp Talks 48 | * @subpackage core/classes 49 | * 50 | * @since 1.0.0 51 | */ 52 | private function setup_globals() { 53 | /** Rewrite ids ***************************************************************/ 54 | 55 | $this->page_rid = 'paged'; // WordPress built-in global var 56 | $this->user_rid = wct_user_rewrite_id(); 57 | $this->user_comments_rid = wct_user_comments_rewrite_id(); 58 | $this->user_rates_rid = wct_user_rates_rewrite_id(); 59 | $this->user_to_rate_rid = wct_user_to_rate_rewrite_id(); 60 | $this->user_talks_rid = wct_user_talks_rewrite_id(); 61 | $this->cpage_rid = wct_cpage_rewrite_id(); 62 | $this->action_rid = wct_action_rewrite_id(); 63 | $this->search_rid = wct_search_rewrite_id(); 64 | 65 | /** Rewrite slugs *************************************************************/ 66 | 67 | $this->user_slug = 'talks/user'; 68 | $this->user_comments_slug = 'comments'; 69 | $this->user_rates_slug = 'ratings'; 70 | $this->user_to_rate_slug = 'to-rate'; 71 | $this->user_talks_slug = 'talks'; 72 | $this->cpage_slug = 'cpage'; 73 | $this->action_slug = 'talks/action'; 74 | } 75 | 76 | /** 77 | * Hooks to load the register methods 78 | * 79 | * @package WordCamp Talks 80 | * @subpackage core/classes 81 | * 82 | * @since 1.0.0 83 | */ 84 | private function hooks() { 85 | // Register rewrite tags. 86 | add_action( 'init', array( $this, 'add_rewrite_tags' ) ); 87 | 88 | // Register the rewrite rules 89 | add_action( 'init', array( $this, 'add_rewrite_rules' ) ); 90 | 91 | // Register the permastructs 92 | add_action( 'init', array( $this, 'add_permastructs' ) ); 93 | } 94 | 95 | /** 96 | * Register the rewrite tags 97 | * 98 | * @package WordCamp Talks 99 | * @subpackage core/classes 100 | * 101 | * @since 1.0.0 102 | */ 103 | public function add_rewrite_tags() { 104 | add_rewrite_tag( '%' . $this->user_rid . '%', '([^/]+)' ); 105 | add_rewrite_tag( '%' . $this->user_comments_rid . '%', '([1]{1,})' ); 106 | add_rewrite_tag( '%' . $this->user_rates_rid . '%', '([1]{1,})' ); 107 | add_rewrite_tag( '%' . $this->user_to_rate_rid . '%', '([1]{1,})' ); 108 | add_rewrite_tag( '%' . $this->user_talks_rid . '%', '([1]{1,})' ); 109 | add_rewrite_tag( '%' . $this->cpage_rid . '%', '([^/]+)' ); 110 | add_rewrite_tag( '%' . $this->action_rid . '%', '([^/]+)' ); 111 | add_rewrite_tag( '%' . $this->search_rid . '%', '([^/]+)' ); 112 | } 113 | 114 | /** 115 | * Register the rewrite rules 116 | * 117 | * @package WordCamp Talks 118 | * @subpackage core/classes 119 | * 120 | * @since 1.0.0 121 | */ 122 | public function add_rewrite_rules() { 123 | $priority = 'top'; 124 | $root_rule = '/([^/]+)/?$'; 125 | 126 | $page_slug = wct_paged_slug(); 127 | $paged_rule = '/([^/]+)/' . $page_slug . '/?([0-9]{1,})/?$'; 128 | 129 | // User Comments 130 | $user_comments_rule = '/([^/]+)/' . $this->user_comments_slug . '/?$'; 131 | $user_comments_paged_rule = '/([^/]+)/' . $this->user_comments_slug . '/' . $this->cpage_slug . '/?([0-9]{1,})/?$'; 132 | 133 | // User Rates 134 | $user_rates_rule = '/([^/]+)/' . $this->user_rates_slug . '/?$'; 135 | $user_rates_paged_rule = '/([^/]+)/' . $this->user_rates_slug . '/' . $page_slug . '/?([0-9]{1,})/?$'; 136 | 137 | // User to rate 138 | $user_to_rate_rule = '/([^/]+)/' . $this->user_to_rate_slug . '/?$'; 139 | $user_to_rate_paged_rule = '/([^/]+)/' . $this->user_to_rate_slug . '/' . $page_slug . '/?([0-9]{1,})/?$'; 140 | 141 | // User talks 142 | $user_talks_rule = '/([^/]+)/' . $this->user_talks_slug . '/?$'; 143 | $user_talks_paged_rule = '/([^/]+)/' . $this->user_talks_slug . '/' . $page_slug . '/?([0-9]{1,})/?$'; 144 | 145 | // User rules 146 | add_rewrite_rule( $this->user_slug . $user_comments_paged_rule, 'index.php?' . $this->user_rid . '=$matches[1]&' . $this->user_comments_rid . '=1&' . $this->cpage_rid . '=$matches[2]', $priority ); 147 | add_rewrite_rule( $this->user_slug . $user_comments_rule, 'index.php?' . $this->user_rid . '=$matches[1]&' . $this->user_comments_rid . '=1', $priority ); 148 | add_rewrite_rule( $this->user_slug . $user_rates_paged_rule, 'index.php?' . $this->user_rid . '=$matches[1]&' . $this->user_rates_rid . '=1&' . $this->page_rid . '=$matches[2]', $priority ); 149 | add_rewrite_rule( $this->user_slug . $user_rates_rule, 'index.php?' . $this->user_rid . '=$matches[1]&' . $this->user_rates_rid . '=1', $priority ); 150 | add_rewrite_rule( $this->user_slug . $user_to_rate_paged_rule, 'index.php?' . $this->user_rid . '=$matches[1]&' . $this->user_to_rate_rid . '=1&' . $this->page_rid . '=$matches[2]', $priority ); 151 | add_rewrite_rule( $this->user_slug . $user_to_rate_rule, 'index.php?' . $this->user_rid . '=$matches[1]&' . $this->user_to_rate_rid . '=1', $priority ); 152 | add_rewrite_rule( $this->user_slug . $user_talks_paged_rule, 'index.php?' . $this->user_rid . '=$matches[1]&' . $this->user_talks_rid . '=1&' . $this->page_rid . '=$matches[2]', $priority ); 153 | add_rewrite_rule( $this->user_slug . $user_talks_rule, 'index.php?' . $this->user_rid . '=$matches[1]&' . $this->user_talks_rid . '=1', $priority ); 154 | add_rewrite_rule( $this->user_slug . $root_rule, 'index.php?' . $this->user_rid . '=$matches[1]', $priority ); 155 | 156 | // Action rules (only add a new talk right now) 157 | add_rewrite_rule( $this->action_slug . $root_rule, 'index.php?' . $this->action_rid . '=$matches[1]', $priority ); 158 | } 159 | 160 | /** 161 | * Register the permastructs 162 | * 163 | * @package WordCamp Talks 164 | * @subpackage core/classes 165 | * 166 | * @since 1.0.0 167 | */ 168 | public function add_permastructs() { 169 | // User Permastruct 170 | add_permastruct( $this->user_rid, $this->user_slug . '/%' . $this->user_rid . '%', array( 171 | 'with_front' => false, 172 | 'ep_mask' => EP_NONE, 173 | 'paged' => true, 174 | 'feed' => false, 175 | 'forcomments' => false, 176 | 'walk_dirs' => true, 177 | 'endpoints' => false, 178 | ) ); 179 | 180 | // Action Permastruct 181 | add_permastruct( $this->action_rid, $this->action_slug . '/%' . $this->action_rid . '%', array( 182 | 'with_front' => false, 183 | 'ep_mask' => EP_NONE, 184 | 'paged' => true, 185 | 'feed' => false, 186 | 'forcomments' => false, 187 | 'walk_dirs' => true, 188 | 'endpoints' => false, 189 | ) ); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /includes/core/wordcamp-talks-loop.php: -------------------------------------------------------------------------------- 1 | loop_vars = array( 96 | 'item_name' => 'item', 97 | 'item_name_plural' => 'items', 98 | ); 99 | 100 | $custom_vars = array_intersect_key( (array) $params, $this->loop_vars ); 101 | 102 | if ( ! empty( $custom_vars ) && 2 == count( $custom_vars ) ) { 103 | $this->loop_vars = $custom_vars; 104 | } 105 | 106 | $this->loop_vars = array_merge( $this->loop_vars, array( 107 | 'total_item_count' => 'total_' . $this->loop_vars['item_name'] . '_count', 108 | 'item_count' => $this->loop_vars['item_name'] .'_count', 109 | 'current_item' => 'current_' . $this->loop_vars['item_name'], 110 | ) ); 111 | 112 | $this->{$this->loop_vars['current_item']} = -1; 113 | 114 | // Parsing other params 115 | if ( ! empty( $params ) ) { 116 | foreach ( (array) $params as $key => $value ) { 117 | // This will be set in $this->{$this->loop_vars['item_name_plural']} 118 | if ( 'items' == $key ) { 119 | continue; 120 | } 121 | 122 | $this->{$key} = $value; 123 | } 124 | } else { 125 | return false; 126 | } 127 | 128 | // Setup the Items to loop through 129 | $this->{$this->loop_vars['item_name_plural']} = $params['items']; 130 | $this->{$this->loop_vars['total_item_count']} = $params['total_item_count']; 131 | 132 | if ( empty( $this->{$this->loop_vars['item_name_plural']} ) ) { 133 | $this->{$this->loop_vars['item_count']} = 0; 134 | $this->{$this->loop_vars['total_item_count']} = 0; 135 | } else { 136 | $this->{$this->loop_vars['item_count']} = count( $this->{$this->loop_vars['item_name_plural']} ); 137 | } 138 | 139 | if ( (int) $this->{$this->loop_vars['total_item_count']} && ! empty( $this->per_page ) ) { 140 | $default_paginate_args = array( 141 | 'total' => ceil( (int) $this->{$this->loop_vars['total_item_count']} / (int) $this->per_page ), 142 | 'current' => (int) $this->page, 143 | ); 144 | 145 | $custom_paginate_args = wp_parse_args( $paginate_args, array( 146 | 'base' => '', 147 | 'format' => '', 148 | 'prev_text' => _x( '←', 'pagination previous text', 'wordcamp-talks' ), 149 | 'next_text' => _x( '→', 'pagination next text', 'wordcamp-talks' ), 150 | 'mid_size' => 1, 151 | ) ); 152 | 153 | $this->pag_links = paginate_links( array_merge( $default_paginate_args, $custom_paginate_args ) ); 154 | 155 | // Remove first page from pagination 156 | $this->pag_links = str_replace( '?paged=1', '', $this->pag_links ); 157 | $this->pag_links = str_replace( '&paged=1', '', $this->pag_links ); 158 | } 159 | } 160 | 161 | /** 162 | * Whether there are Items available in the loop. 163 | * 164 | * @package WordCamp Talks 165 | * @subpackage core/classes 166 | * 167 | * @since 1.0.0 168 | * 169 | * @return bool True if there are items in the loop, otherwise false. 170 | */ 171 | public function has_items() { 172 | if ( $this->{$this->loop_vars['item_count']} ) { 173 | return true; 174 | } 175 | 176 | return false; 177 | } 178 | 179 | /** 180 | * Set up the next item and iterate index. 181 | * 182 | * @package WordCamp Talks 183 | * @subpackage core/classes 184 | * 185 | * @since 1.0.0 186 | * 187 | * @return object The next item to iterate over. 188 | */ 189 | public function next_item() { 190 | 191 | $this->{$this->loop_vars['current_item']}++; 192 | 193 | $this->{$this->loop_vars['item_name']} = $this->{$this->loop_vars['item_name_plural']}[ $this->{$this->loop_vars['current_item']} ]; 194 | 195 | return $this->{$this->loop_vars['item_name']}; 196 | } 197 | 198 | /** 199 | * Rewind the items and reset items index. 200 | * 201 | * @package WordCamp Talks 202 | * @subpackage classes 203 | * 204 | * @since 1.0.0 205 | */ 206 | public function rewind_items() { 207 | 208 | $this->{$this->loop_vars['current_item']} = -1; 209 | 210 | if ( $this->{$this->loop_vars['item_count']} > 0 ) { 211 | $this->{$this->loop_vars['item_name']} = $this->{$this->loop_vars['item_name_plural']}[0]; 212 | } 213 | } 214 | 215 | /** 216 | * Whether there are items left in the loop to iterate over. 217 | * 218 | * @package WordCamp Talks 219 | * @subpackage core/classes 220 | * 221 | * @since 1.0.0 222 | * 223 | * @return bool True if there are more items to show, 224 | * otherwise false. 225 | */ 226 | public function items() { 227 | 228 | if ( $this->{$this->loop_vars['current_item']} + 1 < $this->{$this->loop_vars['item_count']} ) { 229 | return true; 230 | 231 | } elseif ( $this->{$this->loop_vars['current_item']} + 1 == $this->{$this->loop_vars['item_count']} ) { 232 | do_action( "{$this->plugin_prefix}_{$this->item_name}_loop_end" ); 233 | 234 | $this->rewind_items(); 235 | } 236 | 237 | $this->in_the_loop = false; 238 | return false; 239 | } 240 | 241 | /** 242 | * Set up the current item inside the loop. 243 | * 244 | * @package WordCamp Talks 245 | * @subpackage core/classes 246 | * 247 | * @since 1.0.0 248 | */ 249 | public function the_item() { 250 | $this->in_the_loop = true; 251 | $this->{$this->loop_vars['item_name']} = $this->next_item(); 252 | 253 | // loop has just started 254 | if ( 0 === $this->{$this->loop_vars['current_item']} ) { 255 | do_action( "{$this->plugin_prefix}_{$this->item_name}_start" ); 256 | } 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /includes/talks/wordcamp-talk-metas.php: -------------------------------------------------------------------------------- 1 | do_metas(); 35 | } 36 | 37 | /** 38 | * Starts the class 39 | * 40 | * @package WordCamp Talks 41 | * @subpackage talks/classes 42 | * 43 | * @since 1.0.0 44 | */ 45 | public static function start() { 46 | $wct = wct(); 47 | 48 | if ( empty( $wct->talk_metas ) ) { 49 | $wct->talk_metas = new self; 50 | } 51 | 52 | return $wct->talk_metas; 53 | } 54 | 55 | /** 56 | * Checks if talk metas are registered and hooks to some key actions/filters 57 | * 58 | * @package WordCamp Talks 59 | * @subpackage talks/classes 60 | * 61 | * @since 1.0.0 62 | */ 63 | private function do_metas() { 64 | $this->metas = wct_get_global( 'wc_talks_metas' ); 65 | 66 | if ( empty( $this->metas ) || ! is_array( $this->metas ) ) { 67 | return; 68 | } 69 | 70 | /** Admin *********************************************************************/ 71 | add_filter( 'wct_admin_get_meta_boxes', array( $this, 'register_metabox' ), 10, 1 ); 72 | add_action( 'wct_save_metaboxes', array( $this, 'save_metabox' ), 10, 3 ); 73 | 74 | /** Front *********************************************************************/ 75 | add_action( 'wct_talks_the_talk_meta_edit', array( $this, 'front_output' ) ); 76 | add_action( 'wct_before_talk_footer', array( $this, 'single_output' ) ); 77 | } 78 | 79 | /** 80 | * Registers a new metabox for custom fields 81 | * 82 | * @package WordCamp Talks 83 | * @subpackage talks/classes 84 | * 85 | * @since 1.0.0 86 | * 87 | * @param array $metaboxes the metabox list 88 | * @return array the new list 89 | */ 90 | public function register_metabox( $metaboxes = array() ) { 91 | $metas_metabox = array( 92 | 'wc_talks_metas' => array( 93 | 'id' => 'wct_metas_box', 94 | 'title' => __( 'Custom fields', 'wordcamp-talks' ), 95 | 'callback' => array( $this, 'do_metabox' ), 96 | 'context' => 'advanced', 97 | 'priority' => 'high' 98 | ) ); 99 | 100 | return array_merge( $metaboxes, $metas_metabox ); 101 | } 102 | 103 | /** 104 | * Outputs the fields in the Custom Field Talk metabox 105 | * 106 | * @package WordCamp Talks 107 | * @subpackage talks/classes 108 | * 109 | * @since 1.0.0 110 | * 111 | * @param WP_Post $talk the talk object 112 | * @return string HTML output 113 | */ 114 | public function do_metabox( $talk = null ) { 115 | if ( empty( $talk->ID ) || ! is_array( $this->metas ) ) { 116 | esc_html_e( 'No custom fields available', 'wordcamp-talks' ); 117 | return; 118 | } 119 | 120 | $meta_list = array_keys( $this->metas ); 121 | ?> 122 |
123 | 128 | 129 | 130 |
131 | meta_key ) ) { 153 | return; 154 | } 155 | 156 | $meta_object->field_name = 'wct[_the_metas]['. $meta_object->meta_key .']'; 157 | $meta_object->field_value = false; 158 | $meta_object->talk_id = $talk_id; 159 | $display_meta = ''; 160 | 161 | if ( empty( $meta_object->label ) ) { 162 | $meta_object->label = ucfirst( str_replace( '_', ' ', $meta_object->meta_key ) ); 163 | } 164 | 165 | if ( ! empty( $talk_id ) ) { 166 | $meta_object->field_value = wct_talks_get_meta( $talk_id, $meta_object->meta_key ); 167 | } 168 | 169 | if ( empty( $meta_object->form ) ) { 170 | $meta_object->form = $meta_object->admin; 171 | } 172 | 173 | if ( 'single' == $context && empty( $meta_object->field_value ) ) { 174 | return; 175 | } 176 | 177 | if ( ! is_callable( $meta_object->{$context} ) ) { 178 | return; 179 | } 180 | 181 | // We apply the callback as an action 182 | add_action( 'wct_talks_meta_display', $meta_object->{$context}, 10, 3 ); 183 | 184 | // Generate the output for the meta object 185 | do_action( 'wct_talks_meta_display', $display_meta, $meta_object, $context ); 186 | 187 | // Remove the action for other metas 188 | remove_action( 'wct_talks_meta_display', $meta_object->{$context}, 10, 3 ); 189 | } 190 | 191 | /** 192 | * Saves the custom fields when edited from the admin screens (edit/post new) 193 | * 194 | * @package WordCamp Talks 195 | * @subpackage talks/classes 196 | * 197 | * @since 1.0.0 198 | * 199 | * @param int $id the talk ID 200 | * @param WP_Post $talk the talk object 201 | * @param bool $update whether it's an update or not 202 | * @return int the ID of the talk 203 | */ 204 | public function save_metabox( $id = 0, $talk = null, $update = false ) { 205 | // Bail if no meta to save 206 | if ( empty( $_POST['wct']['meta_keys'] ) ) { 207 | return $id; 208 | } 209 | 210 | check_admin_referer( 'admin-wc-talks-metas', '_admin_wc_talks_metas' ); 211 | 212 | $the_metas = array(); 213 | if ( ! empty( $_POST['wct']['_the_metas'] ) ) { 214 | $the_metas = $_POST['wct']['_the_metas']; 215 | } 216 | 217 | $meta_keys = explode( ',', $_POST['wct']['meta_keys'] ); 218 | $meta_keys = array_map( 'sanitize_key', (array) $meta_keys ); 219 | 220 | foreach ( $meta_keys as $meta_key ) { 221 | if ( empty( $the_metas[ $meta_key ] ) && wct_talks_get_meta( $id, $meta_key ) ) { 222 | wct_talks_delete_meta( $id, $meta_key ); 223 | } else if ( ! empty( $the_metas[ $meta_key ] ) ) { 224 | wct_talks_update_meta( $id, $meta_key, $the_metas[ $meta_key ] ); 225 | } 226 | } 227 | 228 | return $id; 229 | } 230 | 231 | /** 232 | * Displays metas for form/single display 233 | * 234 | * @package WordCamp Talks 235 | * @subpackage talks/classes 236 | * 237 | * @since 1.0.0 238 | * 239 | * @param string $context the context (single/form) 240 | * @return string HTML Output 241 | */ 242 | public function front_output( $context = '' ) { 243 | if ( empty( $this->metas ) ) { 244 | return; 245 | } 246 | 247 | if ( empty( $context ) ) { 248 | $context = 'form'; 249 | } 250 | 251 | $wct = wct(); 252 | 253 | $talk_id = 0; 254 | 255 | if ( ! empty( $wct->query_loop->talk->ID ) ) { 256 | $talk_id = $wct->query_loop->talk->ID; 257 | } 258 | 259 | foreach ( $this->metas as $meta_object ) { 260 | $this->display_meta( $talk_id, $meta_object, $context ); 261 | } 262 | } 263 | 264 | /** 265 | * Displays metas for single display 266 | * 267 | * @package WordCamp Talks 268 | * @subpackage talks/classes 269 | * 270 | * @since 1.0.0 271 | * 272 | * @return string HTML Output 273 | */ 274 | public function single_output() { 275 | if ( ! wct_is_single_talk() ) { 276 | return; 277 | } 278 | 279 | return $this->front_output( 'single' ); 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /includes/talks/wordcamp-talks-talk.php: -------------------------------------------------------------------------------- 1 | meta_value 72 | * 73 | * @access public 74 | * @var array 75 | */ 76 | public $metas; 77 | 78 | /** 79 | * Constructor. 80 | * 81 | * @package WordCamp Talks 82 | * @subpackage talks/classes 83 | * 84 | * @since 1.0.0 85 | * 86 | * @param mixed int|string ID or name of the talk 87 | */ 88 | function __construct( $id = 0 ){ 89 | if ( ! empty( $id ) ) { 90 | if ( is_numeric( $id ) ) { 91 | $this->id = $id; 92 | } else { 93 | $this->name = $id; 94 | } 95 | $this->populate(); 96 | } 97 | } 98 | 99 | /** 100 | * Get an talk 101 | * 102 | * @package WordCamp Talks 103 | * @subpackage talks/classes 104 | * 105 | * @since 1.0.0) 106 | */ 107 | public function populate() { 108 | 109 | if ( empty( $this->id ) ) { 110 | // Let's try to get an ID thanks to its name. 111 | if ( ! empty( $this->name ) ) { 112 | $this->talk = self::get_talk_by_name( $this->name ); 113 | } 114 | } else { 115 | $this->talk = get_post( $this->id ); 116 | } 117 | 118 | $this->id = $this->talk->ID; 119 | $this->author = $this->talk->post_author; 120 | $this->title = $this->talk->post_title; 121 | $this->description = $this->talk->post_content; 122 | $this->status = $this->talk->post_status; 123 | 124 | // Build an array of taxonomies 125 | $this->taxonomies = array(); 126 | 127 | // Look in categories 128 | $categories = wp_get_object_terms( $this->id, wct_get_category(), array( 'fields' => 'ids' ) ); 129 | 130 | if ( ! empty( $categories ) ) { 131 | $this->taxonomies = array_merge( $this->taxonomies, array( 132 | wct_get_category() => $categories, 133 | ) ); 134 | } 135 | 136 | // Look in tags 137 | $tags = wp_get_object_terms( $this->id, wct_get_tag(), array( 'fields' => 'slugs' ) ); 138 | 139 | if ( ! empty( $tags ) ) { 140 | $this->taxonomies = array_merge( $this->taxonomies, array( 141 | wct_get_tag() => join( ',', $tags ) 142 | ) ); 143 | } 144 | 145 | // Build an array of post metas 146 | $this->metas = array(); 147 | 148 | $metas = get_post_custom( $this->id ); 149 | 150 | foreach ( $metas as $key => $meta ) { 151 | if ( false === strpos( $key, '_wc_talks_' ) ) { 152 | continue; 153 | } 154 | 155 | $wctalks_key = str_replace( '_wc_talks_', '', $key ); 156 | 157 | if ( count( $meta ) == 1 ) { 158 | $this->metas[ $wctalks_key ] = maybe_unserialize( $meta[0] ); 159 | } else { 160 | $this->metas[ $wctalks_key ] = array_map( 'maybe_unserialize', $meta ); 161 | } 162 | 163 | $this->metas['keys'][] = $wctalks_key; 164 | } 165 | } 166 | 167 | /** 168 | * Save an talk. 169 | * 170 | * @package WordCamp Talks 171 | * @subpackage talks/classes 172 | * 173 | * @since 1.0.0 174 | */ 175 | public function save() { 176 | $this->id = apply_filters_ref_array( 'wct_id_before_save', array( $this->id, &$this ) ); 177 | $this->author = apply_filters_ref_array( 'wct_author_before_save', array( $this->author, &$this ) ); 178 | $this->title = apply_filters_ref_array( 'wct_title_before_save', array( $this->title, &$this ) ); 179 | $this->description = apply_filters_ref_array( 'wct_description_before_save', array( $this->description, &$this ) ); 180 | $this->status = apply_filters_ref_array( 'wct_status_before_save', array( $this->status, &$this ) ); 181 | $this->taxonomies = apply_filters_ref_array( 'wct_taxonomies_before_save', array( $this->taxonomies, &$this ) ); 182 | $this->metas = apply_filters_ref_array( 'wct_metas_before_save', array( $this->metas, &$this ) ); 183 | 184 | // Use this, not the filters above 185 | do_action_ref_array( 'wct_before_save', array( &$this ) ); 186 | 187 | if ( empty( $this->author ) || empty( $this->title ) ) { 188 | return false; 189 | } 190 | 191 | if ( empty( $this->status ) ) { 192 | $this->status = 'publish'; 193 | } 194 | 195 | $post_args = array( 196 | 'post_author' => $this->author, 197 | 'post_title' => $this->title, 198 | 'post_type' => 'talks', 199 | 'post_content' => $this->description, 200 | 'post_status' => $this->status, 201 | 'tax_input' => $this->taxonomies, 202 | ); 203 | 204 | // Update. 205 | if ( $this->id ) { 206 | $post_args = array_merge( array( 207 | 'ID' => $this->id, 208 | ), $post_args ); 209 | 210 | $result = wp_update_post( $post_args ); 211 | // Insert. 212 | } else { 213 | $result = wp_insert_post( $post_args ); 214 | } 215 | 216 | if ( ! empty( $result ) && ! empty( $this->metas ) ) { 217 | 218 | foreach ( $this->metas as $meta_key => $meta_value ) { 219 | // Do not update these keys. 220 | $skip_keys = apply_filters( 'wct_meta_key_skip_save', array( 'keys', 'rates', 'average_rate', 'workflow_state' ) ); 221 | if ( in_array( $meta_key, $skip_keys ) ) { 222 | continue; 223 | } 224 | 225 | if ( empty( $meta_value ) ) { 226 | wct_talks_delete_meta( $result, $meta_key ); 227 | } else { 228 | wct_talks_update_meta( $result, $meta_key, $meta_value ); 229 | } 230 | } 231 | } 232 | 233 | do_action_ref_array( 'wct_after_save', array( $result, &$this ) ); 234 | 235 | return $result; 236 | } 237 | 238 | /** 239 | * The selection query 240 | * 241 | * @package WordCamp Talks 242 | * @subpackage talks/classes 243 | * 244 | * @since 1.0.0 245 | * 246 | * @param array $args arguments to customize the query 247 | * @return array associative array containing talks and total count. 248 | */ 249 | public static function get( $args = array() ) { 250 | 251 | $defaults = array( 252 | 'author' => 0, 253 | 'per_page' => 10, 254 | 'page' => 1, 255 | 'search' => '', 256 | 'exclude' => '', 257 | 'include' => '', 258 | 'orderby' => 'date', 259 | 'order' => 'DESC', 260 | 'meta_query' => array(), 261 | 'tax_query' => array(), 262 | ); 263 | 264 | $r = wp_parse_args( $args, $defaults ); 265 | 266 | /** 267 | * Allow default status to be filtered 268 | * @see wct_talks_get_status() 269 | */ 270 | $talks_status = wct_talks_get_status(); 271 | 272 | // Reviewers can see all talks 273 | if ( wct_user_can( 'list_all_talks' ) ) { 274 | $talks_status = array( 'draft', 'pending', 'rejected', 'selected', 'shortlisted' ); 275 | 276 | // Speakers can only see their talks. 277 | } elseif ( is_user_logged_in() ) { 278 | $r['author'] = get_current_user_id(); 279 | $talks_status = 'any'; 280 | } 281 | 282 | $query_args = array( 283 | 'post_status' => $talks_status, 284 | 'post_type' => 'talks', 285 | 'posts_per_page' => $r['per_page'], 286 | 'paged' => $r['page'], 287 | 'orderby' => $r['orderby'], 288 | 'order' => $r['order'], 289 | 's' => $r['search'], 290 | ); 291 | 292 | if ( ! empty( $r['author'] ) ) { 293 | $query_args['author'] = $r['author']; 294 | } 295 | 296 | if ( ! empty( $r['exclude'] ) ) { 297 | $query_args['post__not_in'] = wp_parse_id_list( $r['exclude'] ); 298 | } 299 | 300 | if ( ! empty( $r['include'] ) ) { 301 | $query_args['post__in'] = wp_parse_id_list( $r['include'] ); 302 | } 303 | 304 | if ( 'rates_count' == $r['orderby'] ) { 305 | $r['meta_query'][] = array( 306 | 'key' => '_wc_talks_average_rate', 307 | 'compare' => 'EXISTS' 308 | ); 309 | } 310 | 311 | if ( ! empty( $r['meta_query'] ) ) { 312 | $query_args['meta_query'] = $r['meta_query']; 313 | } 314 | 315 | if ( ! empty( $r['tax_query'] ) ) { 316 | $query_args['tax_query'] = $r['tax_query']; 317 | } 318 | 319 | // Get the main order 320 | $main_order = wct_get_global( 'orderby' ); 321 | 322 | // Apply the one requested 323 | wct_set_global( 'orderby', $r['orderby'] ); 324 | 325 | $talks = new WP_Query( $query_args ); 326 | 327 | // Reset to main order 328 | wct_set_global( 'orderby', $main_order ); 329 | 330 | return array( 'talks' => $talks->posts, 'total' => $talks->found_posts ); 331 | } 332 | 333 | /** 334 | * Get an talk using its post name. 335 | * 336 | * @package WordCamp Talks 337 | * @subpackage talks/classes 338 | * 339 | * @since 1.0.0 340 | * 341 | * @global $wpdb 342 | * @param string name of the talk 343 | * @return WP_Post the talk object 344 | */ 345 | public static function get_talk_by_name( $name = '' ) { 346 | global $wpdb; 347 | 348 | $where = $wpdb->prepare( 'post_name = %s AND post_type = %s', $name, 'talks' ); 349 | $id = $wpdb->get_var( "SELECT ID FROM {$wpdb->posts} WHERE {$where}" ); 350 | 351 | return get_post( $id ); 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /includes/core/options.php: -------------------------------------------------------------------------------- 1 | wct_get_version(), 37 | 38 | /** Core Settings **********************************************************/ 39 | '_wc_talks_archive_title' => 'Talks', 40 | '_wc_talks_closing_date' => '', 41 | '_wc_talks_editor_link' => 1, 42 | '_wc_talks_moderation_message' => '', 43 | '_wc_talks_login_message' => '', 44 | '_wc_talks_hint_list' => array(), 45 | '_wc_talks_private_fields_list' => array(), 46 | '_wc_talks_public_fields_list' => array(), 47 | '_wc_talks_signup_fields' => array(), 48 | '_wc_talks_allow_comments' => 1, 49 | '_wc_talks_to_rate_disabled' => 0, 50 | '_wc_talks_autolog_enabled' => 0, 51 | ); 52 | 53 | // Multisite options 54 | if ( is_multisite() ) { 55 | $default_options = array_merge( $default_options, array( 56 | '_wc_talks_allow_signups' => 0, 57 | '_wc_talks_user_default_role' => 0, 58 | ) ); 59 | } 60 | 61 | /** 62 | * @param array $default_options list of options 63 | */ 64 | return apply_filters( 'wct_get_default_options', $default_options ); 65 | } 66 | 67 | /** 68 | * Add default plugin's options 69 | * 70 | * @package WordCamp Talks 71 | * @subpackage core/options 72 | * 73 | * @since 1.0.0 74 | */ 75 | function wct_add_options() { 76 | 77 | // Add default options 78 | foreach ( wct_get_default_options() as $key => $value ) { 79 | add_option( $key, $value ); 80 | } 81 | 82 | // Allow plugins to append their own options. 83 | do_action( 'wct_add_options' ); 84 | } 85 | 86 | /** 87 | * Main archive page title 88 | * 89 | * @package WordCamp Talks 90 | * @subpackage core/options 91 | * 92 | * @since 1.0.0 93 | * 94 | * @param string $default default value 95 | * @return string default value or customized one 96 | */ 97 | function wct_archive_title( $default = 'Talks' ) { 98 | return apply_filters( 'wct_archive_title', get_option( '_wc_talks_archive_title', $default ) ); 99 | } 100 | 101 | /** 102 | * Gets the timestamp or mysql date closing limit 103 | * 104 | * @since 1.0.0 105 | * 106 | * @param bool $timestamp true to get the timestamp 107 | * @return mixed int|string timestamp or mysql date closing limit 108 | */ 109 | function wct_get_closing_date( $timestamp = false ) { 110 | $closing = get_option( '_wc_talks_closing_date', '' ); 111 | 112 | if ( ! empty( $timestamp ) ) { 113 | return $closing; 114 | } 115 | 116 | if ( is_numeric( $closing ) ) { 117 | $closing = date_i18n( 'Y-m-d H:i', $closing ); 118 | } 119 | 120 | return $closing; 121 | } 122 | 123 | /** 124 | * Should the editor include the add/remove link buttons ? 125 | * 126 | * @package WordCamp Talks 127 | * @subpackage core/options 128 | * 129 | * @since 1.0.0 130 | * 131 | * @param int $default default value 132 | * @return bool True if enabled, false otherwise 133 | */ 134 | function wct_talk_editor_link( $default = 1 ) { 135 | return (bool) apply_filters( 'wct_talk_editor_link', (bool) get_option( '_wc_talks_editor_link', $default ) ); 136 | } 137 | 138 | /** 139 | * Use a custom moderation message ? 140 | * 141 | * This option depends on the default publish status one. If pending 142 | * is set, it will be possible to customize a moderation message. 143 | * 144 | * @package WordCamp Talks 145 | * @subpackage core/options 146 | * 147 | * @since 1.0.0 148 | * 149 | * @param string $default default value 150 | * @return string the moderation message 151 | */ 152 | function wct_moderation_message( $default = '' ) { 153 | return apply_filters( 'wct_moderation_message', get_option( '_wc_talks_moderation_message', $default ) ); 154 | } 155 | 156 | /** 157 | * Use a custom login message ? 158 | * 159 | * @package WordCamp Talks 160 | * @subpackage core/options 161 | * 162 | * @since 1.0.0 163 | * 164 | * @param string $default default value 165 | * @return string the moderation message 166 | */ 167 | function wct_login_message( $default = '' ) { 168 | return apply_filters( 'wct_login_message', get_option( '_wc_talks_login_message', $default ) ); 169 | } 170 | 171 | /** 172 | * Use a custom captions for rating stars ? 173 | * 174 | * @package WordCamp Talks 175 | * @subpackage core/options 176 | * 177 | * @since 1.0.0 178 | * 179 | * @param array $default default value 180 | * @return array the list of rating stars captions. 181 | */ 182 | function wct_hint_list( $default = array() ) { 183 | if ( ! $default ) { 184 | $default = array( 'poor', 'good', 'great' ); 185 | } 186 | 187 | return apply_filters( 'wct_hint_list', get_option( '_wc_talks_hint_list', $default ) ); 188 | } 189 | 190 | /** 191 | * Are Private profile fields set? 192 | * 193 | * @package WordCamp Talks 194 | * @subpackage core/options 195 | * 196 | * @since 1.0.0 197 | * 198 | * @param array $default Default value 199 | * @return array The list of private profile fields. 200 | */ 201 | function wct_user_private_fields_list( $default = array() ) { 202 | return (array) apply_filters( 'wct_user_private_fields_list', get_option( '_wc_talks_private_fields_list', $default ) ); 203 | } 204 | 205 | /** 206 | * Are Public profile fields set? 207 | * 208 | * @package WordCamp Talks 209 | * @subpackage core/options 210 | * 211 | * @since 1.0.0 212 | * 213 | * @param array $default Default value 214 | * @return array The list of private profile fields. 215 | */ 216 | function wct_user_public_fields_list( $default = array() ) { 217 | return (array) apply_filters( 'wct_user_public_fields_list', get_option( '_wc_talks_public_fields_list', $default ) ); 218 | } 219 | 220 | /** 221 | * Get the signup fields. 222 | * 223 | * @package WordCamp Talks 224 | * @subpackage core/options 225 | * 226 | * @since 1.0.0 227 | * 228 | * @param array $default Default value 229 | * @return array The list of fields to display into the signup form. 230 | */ 231 | function wct_user_signup_fields( $default = array() ) { 232 | return (array) apply_filters( 'wct_user_signup_fields', get_option( '_wc_talks_signup_fields', $default ) ); 233 | } 234 | 235 | /** 236 | * Should the user be automagically logged in after a successful signup ? 237 | * 238 | * @package WordCamp Talks 239 | * @subpackage core/options 240 | * 241 | * @since 1.0.0 242 | * 243 | * @param bool $default default value 244 | * @return bool True if enabled, false otherwise 245 | */ 246 | function wct_user_autolog_after_signup( $default = 0 ) { 247 | return (bool) apply_filters( 'wct_user_autolog_after_signup', (bool) get_option( '_wc_talks_autolog_enabled', $default ) ); 248 | } 249 | 250 | /** 251 | * Are comments about talks globally allowed 252 | * 253 | * Thanks to this option, plugin will be able to neutralize comments about 254 | * talks without having to rely on WordPress discussion settings. If this 255 | * option is enabled, it's still possible from the edit Administration screen 256 | * of the talk to neutralize for each specific talk the comments. 257 | * 258 | * @package WordCamp Talks 259 | * @subpackage core/options 260 | * 261 | * @since 1.0.0 262 | * 263 | * @param bool $default default value 264 | * @return bool True if enabled, false otherwise 265 | */ 266 | function wct_is_comments_allowed( $default = 1 ) { 267 | return (bool) apply_filters( 'wct_is_comments_allowed', (bool) get_option( '_wc_talks_allow_comments', $default ) ); 268 | } 269 | 270 | /** 271 | * Is user's to rate profile tab disabled ? 272 | * 273 | * @since 1.0.0 274 | * 275 | * @param int $default default value 276 | * @param null|bool $rates_disabled Whether built-in rating system is disabled or not. 277 | * @return bool True if disabled, false otherwise. 278 | */ 279 | function wct_is_user_to_rate_disabled( $default = 0, $rates_disabled = null ) { 280 | if ( is_null( $rates_disabled ) ) { 281 | $rates_disabled = wct_is_rating_disabled(); 282 | } 283 | 284 | if ( $rates_disabled ) { 285 | return true; 286 | } 287 | 288 | return (bool) apply_filters( 'wct_is_user_to_rate_disabled', (bool) get_option( '_wc_talks_to_rate_disabled', $default ) ); 289 | } 290 | 291 | /** 292 | * Should the plugin manage signups for the blog? 293 | * 294 | * @package WordCamp Talks 295 | * @subpackage core/options 296 | * 297 | * @since 1.0.0 298 | * 299 | * @param int $default default value 300 | * @return bool True if enabled, false otherwise 301 | */ 302 | function wct_allow_signups( $default = 0 ) { 303 | return (bool) apply_filters( 'wct_allow_signups', get_option( '_wc_talks_allow_signups', $default ) ); 304 | } 305 | 306 | /** 307 | * Should we make sure the user posting an talk on the site has the default role ? 308 | * 309 | * @package WordCamp Talks 310 | * @subpackage core/options 311 | * 312 | * @since 1.0.0 313 | * 314 | * @param int $default default value 315 | * @return bool True if enabled, false otherwise 316 | */ 317 | function wct_get_user_default_role( $default = 0 ) { 318 | return (bool) apply_filters( 'wct_get_user_default_role', get_option( '_wc_talks_user_default_role', $default ) ); 319 | } 320 | -------------------------------------------------------------------------------- /js/jquery.raty.min.js: -------------------------------------------------------------------------------- 1 | /*! wordcamp-talks - v1.0.0-beta2 - 2016-12-01 5:39:14 PM UTC - https://github.com/imath/wordcamp-talks */ 2 | !function(a){"use strict";var b={init:function(c){return this.each(function(){this.self=a(this),b.destroy.call(this.self),this.opt=a.extend(!0,{},a.fn.raty.defaults,c),b._adjustCallback.call(this),b._adjustNumber.call(this),b._adjustHints.call(this),this.opt.score=b._adjustedScore.call(this,this.opt.score),"img"!==this.opt.starType&&b._adjustStarType.call(this),b._adjustPath.call(this),b._createStars.call(this),this.opt.cancel&&b._createCancel.call(this),this.opt.precision&&b._adjustPrecision.call(this),b._createScore.call(this),b._apply.call(this,this.opt.score),b._setTitle.call(this,this.opt.score),b._target.call(this,this.opt.score),this.opt.readOnly?b._lock.call(this):(this.style.cursor="pointer",b._binds.call(this))})},_adjustCallback:function(){for(var a=["number","readOnly","score","scoreName","target"],b=0;b0&&this.score.val(a),b._roundStars.call(this,a))},_between:function(a,b,c){return Math.min(Math.max(parseFloat(a),b),c)},_binds:function(){this.cancel&&(b._bindOverCancel.call(this),b._bindClickCancel.call(this),b._bindOutCancel.call(this)),b._bindOver.call(this),b._bindClick.call(this),b._bindOut.call(this)},_bindClick:function(){var c=this;c.stars.on("click.raty",function(d){var e=!0,f=c.opt.half||c.opt.precision?c.self.data("score"):this.alt||a(this).data("alt");c.opt.click&&(e=c.opt.click.call(c,+f,d)),(e||void 0===e)&&(c.opt.half&&!c.opt.precision&&(f=b._roundHalfScore.call(c,f)),b._apply.call(c,f))})},_bindClickCancel:function(){var a=this;a.cancel.on("click.raty",function(b){a.score.removeAttr("value"),a.opt.click&&a.opt.click.call(a,null,b)})},_bindOut:function(){var a=this;a.self.on("mouseleave.raty",function(c){var d=+a.score.val()||void 0;b._apply.call(a,d),b._target.call(a,d,c),b._resetTitle.call(a),a.opt.mouseout&&a.opt.mouseout.call(a,d,c)})},_bindOutCancel:function(){var a=this;a.cancel.on("mouseleave.raty",function(c){var d=a.opt.cancelOff;if("img"!==a.opt.starType&&(d=a.opt.cancelClass+" "+d),b._setIcon.call(a,this,d),a.opt.mouseout){var e=+a.score.val()||void 0;a.opt.mouseout.call(a,e,c)}})},_bindOver:function(){var a=this,c=a.opt.half?"mousemove.raty":"mouseover.raty";a.stars.on(c,function(c){var d=b._getScoreByPosition.call(a,c,this);b._fill.call(a,d),a.opt.half&&(b._roundStars.call(a,d,c),b._setTitle.call(a,d,c),a.self.data("score",d)),b._target.call(a,d,c),a.opt.mouseover&&a.opt.mouseover.call(a,d,c)})},_bindOverCancel:function(){var a=this;a.cancel.on("mouseover.raty",function(c){var d=a.opt.path+a.opt.starOff,e=a.opt.cancelOn;"img"===a.opt.starType?a.stars.attr("src",d):(e=a.opt.cancelClass+" "+e,a.stars.attr("class",d)),b._setIcon.call(a,this,e),b._target.call(a,null,c),a.opt.mouseover&&a.opt.mouseover.call(a,null)})},_buildScoreField:function(){return a("",{name:this.opt.scoreName,type:"hidden"}).appendTo(this)},_createCancel:function(){var b=this.opt.path+this.opt.cancelOff,c=a("<"+this.opt.starType+" />",{title:this.opt.cancelHint,class:this.opt.cancelClass});"img"===this.opt.starType?c.attr({src:b,alt:"x"}):c.attr("data-alt","x").addClass(b),"left"===this.opt.cancelPlace?this.self.prepend(" ").prepend(c):this.self.append(" ").append(c),this.cancel=c},_createScore:function(){var c=a(this.opt.targetScore);this.score=c.length?c:b._buildScoreField.call(this)},_createStars:function(){for(var c=1;c<=this.opt.number;c++){var d=b._nameForIndex.call(this,c),e={alt:c,src:this.opt.path+this.opt[d]};"img"!==this.opt.starType&&(e={"data-alt":c,class:e.src}),e.title=b._getHint.call(this,c),a("<"+this.opt.starType+" />",e).appendTo(this),this.opt.space&&this.self.append(cc){var h=this.opt.iconRange[c];e=b._getRangeIcon.call(this,h,g),d<=h.range&&b._setIcon.call(this,f,e),d===h.range&&c++}else e=this.opt[g?"starOn":"starOff"],b._setIcon.call(this,f,e)}},_getFirstDecimal:function(a){var b=a.toString().split(".")[1],c=0;return b&&(c=parseInt(b.charAt(0),10),"9999"===b.slice(1,5)&&c++),c},_getRangeIcon:function(a,b){return b?a.on||this.opt.starOn:a.off||this.opt.starOff},_getScoreByPosition:function(c,d){var e=parseInt(d.alt||d.getAttribute("data-alt"),10);if(this.opt.half){var f=b._getWidth.call(this),g=parseFloat((c.pageX-a(d).offset().left)/f);e=e-1+g}return e},_getHint:function(a,c){if(0!==a&&!a)return this.opt.noRatedMsg;var d=b._getFirstDecimal.call(this,a),e=Math.ceil(a),f=this.opt.hints[(e||1)-1],g=f,h=!c||this.move;return this.opt.precision?(h&&(d=0===d?9:d-1),g=f[d]):(this.opt.halfShow||this.opt.half)&&(d=h&&0===d?1:d>5?1:0,"undefined"!=typeof f&&(g=f[d])),""===g?"":g||a},_getWidth:function(){var a=this.stars[0].width||parseFloat(this.stars.eq(0).css("font-size"));return a||b._error.call(this,"Could not get the icon width!"),a},_lock:function(){var a=b._getHint.call(this,this.score.val());this.style.cursor="",this.title=a,this.score.prop("readonly",!0),this.stars.prop("title",a),this.cancel&&this.cancel.hide(),this.self.data("readonly",!0)},_nameForIndex:function(a){return this.opt.score&&this.opt.score>=a?"starOn":"starOff"},_resetTitle:function(a){for(var c=0;c5?1:.5),c+d},_roundStars:function(a,c){var d,e=(a%1).toFixed(2);if(c||this.move?d=e>.5?"starOn":"starHalf":e>this.opt.round.down&&(d="starOn",this.opt.halfShow&&e1?b:b[0]},move:function(c){return this.each(function(){var d=parseInt(c,10),e=b._getFirstDecimal.call(this,c);d>=this.opt.number&&(d=this.opt.number-1,e=10);var f=b._getWidth.call(this),g=f/10,h=a(this.stars[d]),i=h.offset().left+g*e,j=a.Event("mousemove",{pageX:i});this.move=!0,h.trigger(j),this.move=!1})},readOnly:function(c,d){return a(d).each(function(){var d=a(this);d.data("readonly")!==c&&(c?(d.off(".raty").children("img","i").off(".raty"),d.off(".raty").children("i").off(".raty"),b._lock.call(this)):(b._binds.call(this),b._unlock.call(this)),d.data("readonly",c))})},reload:function(){return b.set.call(this,{})},score:function(){var c=a(this);return arguments.length?b.setScore.apply(c,arguments):b.getScore.call(c)},set:function(b){return this.each(function(){a(this).raty(a.extend({},this.opt,b))})},setScore:function(c){return this.each(function(){a(this).data("readonly")!==!0&&(c=b._adjustedScore.call(this,c),b._apply.call(this,c),b._target.call(this,c))})}};a.fn.raty=function(c){return b[c]?b[c].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof c&&c?void a.error("Method "+c+" does not exist!"):b.init.apply(this,arguments)},a.fn.raty.defaults={cancel:!1,cancelClass:"raty-cancel",cancelHint:"Cancel this rating!",cancelOff:"cancel-off.png",cancelOn:"cancel-on.png",cancelPlace:"left",click:void 0,half:!1,halfShow:!0,hints:["bad","poor","regular","good","gorgeous"],iconRange:void 0,mouseout:void 0,mouseover:void 0,noRatedMsg:"Not rated yet!",number:5,numberMax:20,path:void 0,precision:!1,readOnly:!1,round:{down:.25,full:.6,up:.76},score:void 0,scoreName:"score",single:!1,space:!0,starHalf:"star-half.png",starOff:"star-off.png",starOn:"star-on.png",starType:"img",target:void 0,targetFormat:"{score}",targetKeep:!1,targetScore:void 0,targetText:"",targetType:"hint"}}(jQuery); -------------------------------------------------------------------------------- /includes/core/capabilities.php: -------------------------------------------------------------------------------- 1 | 'edit_talk', 27 | 'read_post' => 'read_talk', 28 | 'delete_post' => 'delete_talk', 29 | 'edit_posts' => 'edit_talks', 30 | 'edit_others_posts' => 'edit_others_talks', 31 | 'publish_posts' => 'publish_talks', 32 | 'read_private_posts' => 'read_private_talks', 33 | 'delete_posts' => 'delete_talks', 34 | 'delete_private_posts' => 'delete_private_talks', 35 | 'delete_published_posts' => 'delete_published_talks', 36 | 'delete_others_posts' => 'delete_others_talks', 37 | 'edit_private_posts' => 'edit_private_talks', 38 | 'edit_published_posts' => 'edit_published_talks', 39 | 'create_posts' => 'create_talks', 40 | ) ); 41 | } 42 | 43 | /** 44 | * Return Talks tag capabilities 45 | * 46 | * @package WordCamp Talks 47 | * @subpackage core/capabilities 48 | * 49 | * @since 1.0.0 50 | * 51 | * @return array Talks tag capabilities 52 | */ 53 | function wct_get_tag_caps() { 54 | return apply_filters( 'wct_get_tag_caps', array ( 55 | 'manage_terms' => 'manage_talk_tags', 56 | 'edit_terms' => 'edit_talk_tags', 57 | 'delete_terms' => 'delete_talk_tags', 58 | 'assign_terms' => 'assign_talk_tags' 59 | ) ); 60 | } 61 | 62 | /** 63 | * Return Talks category capabilities 64 | * 65 | * @package WordCamp Talks 66 | * @subpackage core/capabilities 67 | * 68 | * @since 1.0.0 69 | * 70 | * @return array Talks category capabilities 71 | */ 72 | function wct_get_category_caps() { 73 | return apply_filters( 'wct_get_category_caps', array ( 74 | 'manage_terms' => 'manage_talk_categories', 75 | 'edit_terms' => 'edit_talk_categories', 76 | 'delete_terms' => 'delete_talk_categories', 77 | 'assign_terms' => 'assign_talk_categories' 78 | ) ); 79 | } 80 | 81 | /** 82 | * Get Raters capabilities 83 | * 84 | * @since 1.0.0 85 | * 86 | * @return array The list of caps for the rater roles. 87 | */ 88 | function wct_get_rater_caps() { 89 | $caps = array( 90 | 'view_other_profiles' => true, 91 | 'comment_talks' => true, 92 | 'rate_talks' => true, 93 | 'view_talk_rates' => true, 94 | 'view_talk_comments' => true, 95 | 'list_all_talks' => true, 96 | ); 97 | 98 | $post_type_object = get_post_type_object( 'talks' ); 99 | 100 | if ( ! empty( $post_type_object->cap ) ) { 101 | $caps = array_merge( $caps, array( 102 | $post_type_object->cap->publish_posts => false, 103 | $post_type_object->cap->read_post => true, 104 | $post_type_object->cap->read_private_posts => true, 105 | ) ); 106 | } 107 | 108 | /** 109 | * Filter here to edit rater caps. 110 | * 111 | * @since 1.0.0 112 | * 113 | * @param array $caps The list of caps for the rater roles. 114 | */ 115 | return apply_filters( 'wct_get_rater_caps', $caps ); 116 | } 117 | 118 | /** 119 | * Register Rater roles. 120 | * 121 | * @since 1.0.0 122 | */ 123 | function wct_register_roles() { 124 | $rater = get_role( 'rater' ); 125 | $blind_rater = get_role( 'blind_rater' ); 126 | $juror = get_role( 'juror' ); 127 | 128 | $caps = array( 129 | 'read' => true, 130 | ); 131 | 132 | if ( is_null( $rater ) || is_null( $blind_rater || is_null( $juror ) ) ) { 133 | $caps = $caps + wct_get_rater_caps(); 134 | } 135 | 136 | // Create the role if not already done. 137 | if ( is_null( $rater ) ) { 138 | add_role( 'rater', __( 'Rater', 'wordcamp-talks' ), $caps ); 139 | } 140 | 141 | // Create the role if not already done. 142 | if ( is_null( $blind_rater ) ) { 143 | $blind_rater_caps = array_diff_key( $caps, array( 144 | 'view_other_profiles' => false, 145 | 'view_talk_rates' => false, 146 | 'view_talk_comments' => false, 147 | ) ); 148 | 149 | add_role( 'blind_rater', __( 'Blind Rater', 'wordcamp-talks' ), $blind_rater_caps ); 150 | } 151 | 152 | if ( is_null( $juror ) ) { 153 | $juror_caps = array_fill_keys( wct_get_post_type_caps(), true ); 154 | $juror_caps = array_merge( $caps, $juror_caps, array( 155 | 'assign_talk_categories' => true, 156 | 'assign_talk_tags' => true, 157 | 'select_talks' => true, 158 | 'list_all_talks' => true, 159 | ) ); 160 | 161 | add_role( 'juror', __( 'Juror', 'wordcamp-talks' ), $juror_caps ); 162 | } 163 | } 164 | add_action( 'admin_init', 'wct_register_roles' ); 165 | 166 | /** 167 | * Maps Talks capabilities 168 | * 169 | * @package WordCamp Talks 170 | * @subpackage core/capabilities 171 | * 172 | * @since 1.0.0 173 | * 174 | * @param array $caps Capabilities for meta capability 175 | * @param string $cap Capability name 176 | * @param int $user_id User id 177 | * @param mixed $args Arguments 178 | * @return array Actual capabilities for meta capability 179 | */ 180 | function wct_map_meta_caps( $caps = array(), $cap = '', $user_id = 0, $args = array() ) { 181 | $user = wp_get_current_user(); 182 | 183 | if ( $user->ID !== $user_id && $user_id ) { 184 | $user = get_user_by( 'id', $user_id ); 185 | } 186 | 187 | // The user has the cap set (rater, blind_rater or juror) 188 | if ( isset( $user->allcaps[ $cap ] ) ) { 189 | if ( ! $user->allcaps[ $cap ] ) { 190 | $caps = array( 'do_not_allow' ); 191 | } 192 | 193 | // For any other cases, use the caps mapping! 194 | } else { 195 | switch ( $cap ) { 196 | 197 | case 'publish_talks' : 198 | if ( ! empty( $user_id ) ) { 199 | $closing = (int) wct_get_closing_date( true ); 200 | $is_closed = false; 201 | 202 | // No closing date defined, free to post if you can! 203 | if ( ! empty( $closing ) ) { 204 | $now = strtotime( date_i18n( 'Y-m-d H:i' ) ); 205 | 206 | if ( $closing < $now ) { 207 | $is_closed = true; 208 | } 209 | } 210 | 211 | if ( ! $is_closed ) { 212 | $caps = array( 'exist' ); 213 | } else { 214 | $caps = array( 'manage_options' ); 215 | } 216 | 217 | } else { 218 | $caps = array( 'manage_options' ); 219 | } 220 | 221 | break; 222 | 223 | case 'read_talk' : 224 | case 'edit_talk' : 225 | 226 | // Get the post 227 | $_post = get_post( $args[0] ); 228 | 229 | if ( ! empty( $_post ) ) { 230 | 231 | $caps = array(); 232 | 233 | if ( ! is_admin() && ( (int) $user_id === (int) $_post->post_author ) ) { 234 | $caps = array( 'exist' ); 235 | 236 | // Unknown, so map to manage_options 237 | } else { 238 | $caps[] = 'manage_options'; 239 | } 240 | } 241 | 242 | break; 243 | 244 | // Specific to talks' workflow 245 | case 'edit_talks' : 246 | case 'edit_others_talks' : 247 | case 'edit_private_talks' : 248 | case 'edit_published_talks' : 249 | case 'read_private_talks' : 250 | case 'create_talks' : 251 | $caps = array( 'manage_options' ); 252 | break; 253 | 254 | // Specific to review 255 | case 'comment_talks' : 256 | case 'rate_talks' : 257 | case 'view_talk_rates' : 258 | case 'view_talk_comments' : 259 | case 'select_talks' : 260 | case 'list_all_talks' : 261 | $caps = array( 'manage_options' ); 262 | break; 263 | 264 | case 'view_other_profiles' : 265 | $caps = array( 'exist' ); 266 | break; 267 | 268 | case 'edit_comment' : 269 | 270 | // Get the comment 271 | $_comment = get_comment( $args[0] ); 272 | 273 | if ( ! is_admin() ) { 274 | 275 | if ( ! empty( $_comment ) && 'talks' == get_post_type( $_comment->comment_post_ID ) ) { 276 | $caps = array( 'manage_options' ); 277 | } 278 | 279 | // Specific case for jurors. 280 | } elseif ( isset( $user->allcaps[ 'juror' ] ) ) { 281 | if ( ! empty( $_comment ) && (int) $_comment->user_id !== (int) $user->ID ) { 282 | $caps = array( 'do_not_allow' ); 283 | } 284 | } 285 | 286 | break; 287 | 288 | case 'delete_talk' : 289 | case 'delete_talks' : 290 | case 'delete_others_talks' : 291 | case 'delete_private_talks' : 292 | case 'delete_published_talks' : 293 | $caps = array( 'manage_options' ); 294 | break; 295 | 296 | /** Taxonomies ****************************************************************/ 297 | 298 | case 'manage_talk_tags' : 299 | case 'edit_talk_tags' : 300 | case 'delete_talk_tags' : 301 | case 'manage_talk_categories' : 302 | case 'edit_talk_categories' : 303 | case 'delete_talk_categories' : 304 | $caps = array( 'manage_options' ); 305 | break; 306 | 307 | // Open to all users that have an ID 308 | case 'assign_talk_tags' : 309 | case 'assign_talk_categories' : 310 | if ( ! empty( $user_id ) ) { 311 | $caps = array( 'exist' ); 312 | } else { 313 | $caps = array( 'manage_options' ); 314 | } 315 | break; 316 | 317 | /** Admin *********************************************************************/ 318 | 319 | case 'wct_talks_admin' : 320 | $caps = array( 'manage_options' ); 321 | break; 322 | } 323 | } 324 | 325 | /** 326 | * @param array $caps Capabilities for meta capability 327 | * @param string $cap Capability name 328 | * @param int $user_id User id 329 | * @param mixed $args Arguments 330 | */ 331 | return apply_filters( 'wct_map_meta_caps', $caps, $cap, $user_id, $args ); 332 | } 333 | 334 | /** 335 | * Check wether a user has the capability to perform an action 336 | * 337 | * @package WordCamp Talks 338 | * @subpackage core/capabilities 339 | * 340 | * @since 1.0.0 341 | * 342 | * @param string $capability Capability to check 343 | * @param array $args additional args to help 344 | * @return bool True|False 345 | */ 346 | function wct_user_can( $capability = '', $args = false ) { 347 | $can = false; 348 | 349 | if ( ! empty( $args ) ) { 350 | $can = current_user_can( $capability, $args ); 351 | } else { 352 | $can = current_user_can( $capability ); 353 | } 354 | 355 | return apply_filters( 'wct_user_can', $can, $capability ); 356 | } 357 | -------------------------------------------------------------------------------- /includes/users/tags.php: -------------------------------------------------------------------------------- 1 | '; 54 | 55 | foreach ( $nav_items as $nav_item ) { 56 | $class = ! empty( $nav_item['current'] ) ? ' class="current"' : ''; 57 | $user_nav .= ''; 58 | $user_nav .= '' . esc_html( $nav_item['title'] ) . ''; 59 | $user_nav .= ''; 60 | } 61 | 62 | $user_nav .= ''; 63 | 64 | /** 65 | * Filter the user nav output 66 | * 67 | * @param string $user_nav User nav output 68 | * @param int $user_id the user ID 69 | * @param string $user_nicename the username 70 | */ 71 | return apply_filters( 'wct_users_get_user_nav', $user_nav, $user_id, $username ); 72 | } 73 | 74 | /** 75 | * Outputs user's profile avatar 76 | * 77 | * @package WordCamp Talks 78 | * @subpackage users/tags 79 | * 80 | * @since 1.0.0 81 | */ 82 | function wct_users_the_user_profile_avatar() { 83 | echo wct_users_get_user_profile_avatar(); 84 | } 85 | 86 | /** 87 | * Gets user's profile avatar 88 | * 89 | * @package WordCamp Talks 90 | * @subpackage users/tags 91 | * 92 | * @since 1.0.0 93 | */ 94 | function wct_users_get_user_profile_avatar() { 95 | return apply_filters( 'wct_users_get_user_profile_avatar', get_avatar( wct_users_displayed_user_id(), '150' ) ); 96 | } 97 | 98 | /** 99 | * Outputs user's profile display name 100 | * 101 | * @since 1.0.0 102 | */ 103 | function wct_users_user_profile_display_name() { 104 | echo wct_users_get_user_profile_display_name(); 105 | } 106 | 107 | /** 108 | * Gets user's profile display name 109 | * 110 | * @since 1.0.0 111 | */ 112 | function wct_users_get_user_profile_display_name() { 113 | return esc_html( apply_filters( 'wct_users_get_user_profile_display_name', wct_users_get_displayed_user_displayname() ) ); 114 | } 115 | 116 | 117 | /** 118 | * Append displayed user's rating in talks header when viewing his rates profile 119 | * 120 | * @package WordCamp Talks 121 | * @subpackage users/tags 122 | * 123 | * @since 1.0.0 124 | * 125 | * @param int $id the talk ID 126 | * @param int $user_id the user ID 127 | */ 128 | function wct_users_the_user_talk_rating( $id = 0, $user_id = 0 ) { 129 | if ( ! wct_user_can( 'view_talk_rates' ) ) { 130 | return; 131 | } 132 | 133 | echo wct_users_get_user_talk_rating( $id, $user_id ); 134 | } 135 | 136 | /** 137 | * Gets displayed user's rating for a given talk 138 | * 139 | * @package WordCamp Talks 140 | * @subpackage users/tags 141 | * 142 | * @since 1.0.0 143 | * 144 | * @param int $id the talk ID 145 | * @param int $user_id the user ID 146 | */ 147 | function wct_users_get_user_talk_rating( $id = 0, $user_id = 0 ) { 148 | if ( ! wct_is_user_profile_rates() ) { 149 | return; 150 | } 151 | 152 | if ( empty( $id ) ) { 153 | $query_loop = wct_get_global( 'query_loop' ); 154 | 155 | if ( ! empty( $query_loop->talk->ID ) ) { 156 | $id = $query_loop->talk->ID; 157 | } 158 | } 159 | 160 | if ( empty( $user_id ) ) { 161 | $user_id = wct_users_displayed_user_id(); 162 | } 163 | 164 | if ( empty( $user_id ) || empty( $id ) ) { 165 | return; 166 | } 167 | 168 | $user_rating = wct_count_ratings( $id, $user_id ); 169 | 170 | if ( empty( $user_rating ) || is_array( $user_rating ) ) { 171 | return false; 172 | } 173 | 174 | $username = wct_users_get_displayed_user_username(); 175 | 176 | $output = ''; 177 | $output .= get_avatar( $user_id, 20 ) . sprintf( _n( 'rated 1 star', 'rated %s stars', $user_rating, 'wordcamp-talks' ), $user_rating ) . ''; 178 | 179 | /** 180 | * Filter the user talk rating output 181 | * 182 | * @param string $output the rating 183 | * @param int $id the talk ID 184 | * @param int $user_id the user ID 185 | */ 186 | return apply_filters( 'wct_users_get_user_talk_rating', $output, $id, $user_id ); 187 | } 188 | 189 | function wct_users_the_signup_fields() { 190 | echo wct_users_get_signup_fields(); 191 | } 192 | 193 | function wct_users_get_signup_fields() { 194 | $output = ''; 195 | 196 | foreach ( (array) wct_user_get_fields() as $key => $label ) { 197 | // reset 198 | $sanitized = array( 199 | 'key' => sanitize_key( $key ), 200 | 'label' => esc_html( $label ), 201 | 'value' => '', 202 | ); 203 | 204 | if ( ! empty( $_POST['wct_signup'][ $sanitized['key'] ] ) ) { 205 | $sanitized['value'] = apply_filters( "wct_users_get_signup_field_{$key}", $_POST['wct_signup'][ $sanitized['key'] ] ); 206 | } 207 | 208 | $required = apply_filters( 'wct_users_is_signup_field_required', false, $key ); 209 | $required_output = false; 210 | 211 | if ( ! empty( $required ) || in_array( $key, array( 'user_login', 'user_email' ) ) ) { 212 | $required_output = '*'; 213 | } 214 | 215 | $output .= ''; 216 | 217 | // Description is a text area. 218 | if ( 'description' === $sanitized['key'] ) { 219 | $output .= ''; 220 | 221 | // Language is a select box. 222 | } elseif ( 'locale' === $sanitized['key'] ) { 223 | $languages = get_available_languages(); 224 | 225 | if ( empty( $languages ) ) { 226 | continue; 227 | } 228 | 229 | $output .= wp_dropdown_languages( array( 230 | 'name' => 'wct_signup[' . esc_attr( $sanitized['key'] ) . ']', 231 | 'id' => '_wct_signup_' . esc_attr( $sanitized['key'] ), 232 | 'selected' => get_locale(), 233 | 'languages' => $languages, 234 | 'show_available_translations' => false, 235 | 'echo' => 0, 236 | ) ); 237 | 238 | // Default is text field. 239 | } else { 240 | $output .= ''; 241 | } 242 | 243 | $output .= apply_filters( 'wct_users_after_signup_field', '', $sanitized ); 244 | } 245 | 246 | return apply_filters( 'wct_users_get_signup_fields', $output ); 247 | } 248 | 249 | function wct_users_the_signup_submit() { 250 | $wct = wct(); 251 | 252 | wp_nonce_field( 'wct_signup' ); 253 | 254 | do_action( 'wct_users_the_signup_submit' ); ?> 255 | 256 | 257 | 258 | 275 |
276 | 277 | 278 | 279 |
280 | __( 'Biographical Info', 'wordcamp-talks' ), 302 | 'user_url' => __( 'Website', 'wordcamp-talks' ), 303 | ), wct_users_contactmethods( array(), 'public' ) ) ); 304 | 305 | wct_set_global( 'public_profile_labels', $public_labels ); 306 | 307 | return array_keys( $public_labels ); 308 | } 309 | 310 | /** 311 | * Check if a field's key has a corresponding value for the user. 312 | * 313 | * @since 1.0.0 314 | * 315 | * @param string $info The field key. 316 | * @return bool True if the user has filled the field. False otherwise. 317 | */ 318 | function wct_users_public_profile_has_info( $info = '' ) { 319 | if ( empty( $info ) ) { 320 | return false; 321 | } 322 | 323 | return ! empty( wct()->displayed_user->{$info} ); 324 | } 325 | 326 | /** 327 | * While Iterating fields, count the empty ones. 328 | * 329 | * @since 1.0.0 330 | */ 331 | function wct_users_public_empty_info() { 332 | $empty_info = (int) wct_get_global( 'empty_info' ); 333 | 334 | wct_set_global( 'empty_info', $empty_info + 1 ); 335 | } 336 | 337 | /** 338 | * Displays the field label. 339 | * 340 | * @since 1.0.0 341 | * 342 | * @param string $info The field key. 343 | */ 344 | function wct_users_public_profile_label( $info = '' ) { 345 | if ( empty( $info ) ) { 346 | return; 347 | } 348 | 349 | $labels = wct_get_global( 'public_profile_labels' ); 350 | 351 | if ( ! isset( $labels[ $info ] ) ) { 352 | return; 353 | } 354 | 355 | echo esc_html( apply_filters( 'wct_users_public_label', $labels[ $info ], $info ) ); 356 | } 357 | 358 | /** 359 | * Displays the field value. 360 | * 361 | * @since 1.0.0 362 | * 363 | * @param string $info The field key. 364 | */ 365 | function wct_users_public_profile_value( $info = '' ) { 366 | if ( empty( $info ) ) { 367 | return; 368 | } 369 | 370 | echo apply_filters( 'wct_users_public_value', wct()->displayed_user->{$info}, $info ); 371 | } 372 | 373 | /** 374 | * Check if no fields were filled by the user. 375 | * 376 | * @since 1.0.0 377 | * 378 | * @return bool True if the user didn't filled any fields. False otherwise. 379 | */ 380 | function wct_users_public_empty_profile() { 381 | $empty_info = (int) wct_get_global( 'empty_info' ); 382 | $labels = wct_get_global( 'public_profile_labels' ); 383 | 384 | if ( $empty_info && $empty_info === count( $labels ) ) { 385 | $feedback = array( 'info' => array( 3 ) ); 386 | 387 | if ( wct_is_current_user_profile() ) { 388 | $feedback = array( 'info' => array( 4 ) ); 389 | } 390 | 391 | wct_set_global( 'feedback', $feedback ); 392 | 393 | return true; 394 | } 395 | 396 | return false; 397 | } 398 | -------------------------------------------------------------------------------- /includes/wordcamp-talks.php: -------------------------------------------------------------------------------- 1 | setup_globals( $file ); 30 | $this->includes(); 31 | $this->setup_hooks(); 32 | 33 | $posts_list = new Talk_Status_View_Posts_List(); 34 | $publish_box = new Talk_Status_View_Publish_Box(); 35 | 36 | $post_status = new Talk_Status_Post_Status(); 37 | $taxonomy = new Talk_Status_Taxonomy(); 38 | $view = new Talk_Status_View( $posts_list, $publish_box ); 39 | 40 | $this->status_controller = new Talk_Status_Controller( $post_status, $taxonomy, $view ); 41 | 42 | $this->status_controller->run(); 43 | } 44 | 45 | /** 46 | * Return an instance of this class. 47 | * 48 | * @package WordCamp Talks 49 | * 50 | * @since 1.0.0 51 | * 52 | * @return object A single instance of this class. 53 | */ 54 | public static function start( $file ) { 55 | 56 | // If the single instance hasn't been set, set it now. 57 | if ( empty( self::$instance ) ) { 58 | self::$instance = new self( $file ); 59 | } 60 | 61 | return self::$instance; 62 | } 63 | 64 | /** 65 | * Setups plugin's globals 66 | * 67 | * @package WordCamp Talks 68 | * 69 | * @since 1.0.0 70 | */ 71 | private function setup_globals( $file = '' ) { 72 | // Version 73 | $this->version = '1.0.0-beta2'; 74 | 75 | // Domain 76 | $this->domain = 'wordcamp-talks'; 77 | 78 | // Base name 79 | $this->file = $file; 80 | $this->basename = apply_filters( 'wct_plugin_basename', plugin_basename( $this->file ) ); 81 | 82 | // Path and URL 83 | $this->plugin_dir = apply_filters( 'wct_plugin_dir_path', plugin_dir_path( $this->file ) ); 84 | $this->plugin_url = apply_filters( 'wct_plugin_dir_url', plugin_dir_url ( $this->file ) ); 85 | $this->js_url = apply_filters( 'wct_js_url', trailingslashit( $this->plugin_url . 'js' ) ); 86 | $this->lang_dir = apply_filters( 'wct_lang_dir', trailingslashit( $this->plugin_dir . 'languages' ) ); 87 | 88 | // Includes 89 | $this->includes_dir = apply_filters( 'wct_includes_dir_path', trailingslashit( $this->plugin_dir . 'includes' ) ); 90 | $this->includes_url = apply_filters( 'wct_includes_dir_url', trailingslashit( $this->plugin_url . 'includes' ) ); 91 | 92 | // Default templates location (can be overridden from theme or child theme) 93 | $this->templates_dir = apply_filters( 'wct_templates_dir_path', trailingslashit( $this->plugin_dir . 'templates' ) ); 94 | 95 | // Post types / taxonomies default ids 96 | $this->post_type = 'talks'; 97 | $this->category = 'talk_categories'; 98 | $this->tag = 'talk_tags'; 99 | 100 | // template globals 101 | $this->is_talks = false; 102 | $this->template_file = false; 103 | $this->main_query = array(); 104 | $this->query_loop = false; 105 | $this->per_page = get_option( 'posts_per_page' ); 106 | $this->is_talks_archive = false; 107 | $this->is_category = false; 108 | $this->is_tag = false; 109 | $this->current_term = false; 110 | $this->is_user = false; 111 | $this->is_user_rates = false; 112 | $this->is_user_comments = false; 113 | $this->is_action = false; 114 | $this->is_new = false; 115 | $this->is_edit = false; 116 | $this->is_search = false; 117 | $this->orderby = false; 118 | $this->needs_reset = false; 119 | 120 | // User globals 121 | $this->displayed_user = new WP_User(); 122 | $this->current_user = new WP_User(); 123 | $this->feedback = array(); 124 | } 125 | 126 | /** 127 | * Includes plugin's needed files 128 | * 129 | * @package WordCamp Talks 130 | * 131 | * @since 1.0.0 132 | * 133 | * @uses is_admin() to check for WordPress Administration 134 | */ 135 | private function includes() { 136 | require_once( $this->includes_dir . 'core/options.php' ); 137 | require_once( $this->includes_dir . 'core/functions.php' ); 138 | require_once( $this->includes_dir . 'core/rewrites.php' ); 139 | require_once( $this->includes_dir . 'core/classes.php' ); 140 | require_once( $this->includes_dir . 'core/capabilities.php' ); 141 | require_once( $this->includes_dir . 'core/upgrade.php' ); 142 | require_once( $this->includes_dir . 'core/template-functions.php' ); 143 | require_once( $this->includes_dir . 'core/template-loader.php' ); 144 | require_once( $this->includes_dir . 'core/widgets.php' ); 145 | 146 | require_once( $this->includes_dir . 'comments/functions.php' ); 147 | require_once( $this->includes_dir . 'comments/classes.php' ); 148 | require_once( $this->includes_dir . 'comments/tags.php' ); 149 | 150 | require_once( $this->includes_dir . 'talks/functions.php' ); 151 | 152 | /** Talk Class ****************************************************************/ 153 | 154 | if ( ! class_exists( 'WordCamp_Talks_Talk' ) ) : 155 | require_once( $this->includes_dir . 'talks/wordcamp-talks-talk.php' ); 156 | endif; 157 | 158 | /** Talks Loop ****************************************************************/ 159 | 160 | if ( ! class_exists( 'WordCamp_Talks_Loop_Talks' ) ) : 161 | require_once( $this->includes_dir . 'talks/wordcamp-talks-loop-talks.php' ); 162 | endif; 163 | 164 | if ( ! class_exists( 'WordCamp_Talk_Metas' ) ) : 165 | require_once( $this->includes_dir . 'talks/wordcamp-talk-metas.php' ); 166 | endif; 167 | 168 | require_once( $this->includes_dir . 'talks/tags.php' ); 169 | 170 | require_once( $this->includes_dir . 'users/functions.php' ); 171 | require_once( $this->includes_dir . 'users/tags.php' ); 172 | 173 | require_once( $this->includes_dir . 'core/actions.php' ); 174 | require_once( $this->includes_dir . 'core/filters.php' ); 175 | 176 | if ( is_admin() ) { 177 | require_once( $this->includes_dir . 'admin/admin.php' ); 178 | } 179 | 180 | /** 181 | * Add specific functions for the current site 182 | */ 183 | if ( file_exists( WP_PLUGIN_DIR . '/wct-functions.php' ) ) { 184 | require_once( WP_PLUGIN_DIR . '/wct-functions.php' ); 185 | } 186 | 187 | /** 188 | * On multisite configs, load current blog's specific functions 189 | */ 190 | if ( is_multisite() && file_exists( WP_PLUGIN_DIR . '/wct-' . get_current_blog_id() . '- functions.php' ) ) { 191 | require_once( WP_PLUGIN_DIR . '/wct-' . get_current_blog_id() . '- functions.php' ); 192 | } 193 | } 194 | 195 | /** 196 | * Setups some hooks to register post type stuff, scripts, set 197 | * the current user. 198 | * 199 | * @package WordCamp Talks 200 | * 201 | * @since 1.0.0 202 | * 203 | * @uses add_action() to perform custom actions at key points 204 | */ 205 | private function setup_hooks() { 206 | // Main hooks 207 | add_action( 'plugins_loaded', array( $this, 'load_textdomain' ), 0 ); 208 | add_action( 'init', array( $this, 'register_post_type' ) ); 209 | add_action( 'init', array( $this, 'register_taxonomies' ) ); 210 | add_action( 'set_current_user', array( $this, 'setup_current_user' ) ); 211 | add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ), 1 ); 212 | } 213 | 214 | /** 215 | * Registers the Talks post type 216 | * 217 | * @package WordCamp Talks 218 | * 219 | * @since 1.0.0 220 | */ 221 | public function register_post_type() { 222 | //register the Talks post-type 223 | // 224 | $supports = array( 'title', 'editor', 'author', 'comments', 'revisions' ); 225 | 226 | $args = apply_filters( 'wct_post_type_register_args', array( 227 | 'public' => true, 228 | 'query_var' => 'talks', 229 | 'rewrite' => array( 230 | 'slug' => 'talks/talk', 231 | 'with_front' => false 232 | ), 233 | 'has_archive' => 'talks', 234 | 'exclude_from_search' => true, 235 | 'show_in_nav_menus' => false, 236 | 'show_in_admin_bar' => wct_user_can( 'wct_talks_admin' ), 237 | 'menu_icon' => 'dashicons-megaphone', 238 | 'supports' => $supports, 239 | 'taxonomies' => array( 240 | wct_get_category(), 241 | wct_get_tag() 242 | ), 243 | 'capability_type' => array( 'talk', 'talks' ), 244 | 'capabilities' => wct_get_post_type_caps(), 245 | 'delete_with_user' => true, 246 | 'can_export' => true, 247 | ) ); 248 | register_post_type( 249 | $this->post_type, 250 | array_merge( 251 | wct_post_type_register_labels(), 252 | $args 253 | ) 254 | ); 255 | } 256 | 257 | /** 258 | * Registers the Talks taxonomies 259 | * 260 | * @package WordCamp Talks 261 | * 262 | * @since 1.0.0 263 | */ 264 | public function register_taxonomies() { 265 | 266 | // Register the category taxonomy 267 | register_taxonomy( 268 | wct_get_category(), 269 | 'talks', 270 | array_merge( 271 | wct_category_register_labels(), 272 | wct_category_register_args() 273 | ) 274 | ); 275 | 276 | // Register the tag taxonomy 277 | register_taxonomy( 278 | wct_get_tag(), 279 | 'talks', 280 | array_merge( 281 | wct_tag_register_labels(), 282 | wct_tag_register_args() 283 | ) 284 | ); 285 | } 286 | 287 | /** 288 | * Setups the loggedin user 289 | * 290 | * @package WordCamp Talks 291 | * 292 | * @since 1.0.0 293 | */ 294 | public function setup_current_user() { 295 | $this->current_user = wp_get_current_user(); 296 | } 297 | 298 | /** 299 | * Setups a globalized var for a later use 300 | * 301 | * @package WordCamp Talks 302 | * 303 | * @since 1.0.0 304 | * 305 | * @param string $var The key to access to the globalized var 306 | * @param mixed $value The value of the globalized var 307 | */ 308 | public function set_global( $var = '', $value = null ) { 309 | if ( empty( $var ) || empty( $value ) ) { 310 | return false; 311 | } 312 | 313 | $this->{$var} = $value; 314 | } 315 | 316 | /** 317 | * Gets a globalized var 318 | * 319 | * @package WordCamp Talks 320 | * 321 | * @since 1.0.0 322 | * 323 | * @param string $var the key to access to the globalized var 324 | * @return mixed the value of the globalized var 325 | */ 326 | public function get_global( $var = '' ) { 327 | if ( empty( $var ) || empty( $this->{$var} ) ) { 328 | return false; 329 | } 330 | 331 | return $this->{$var}; 332 | } 333 | 334 | /** 335 | * Registers external javascript libraries to be linked later 336 | * using the wp_enqueue_script() function, & adds the plugin's stylesheet 337 | * 338 | * @package WordCamp Talks 339 | * 340 | * @since 1.0.0 341 | */ 342 | public function enqueue_scripts() { 343 | if ( ! wct_is_talks() ) { 344 | return; 345 | } 346 | 347 | // Register jquery Raty 348 | wp_register_script( 'jquery-raty', wct_get_js_script( 'jquery.raty' ), array( 'jquery' ), '2.7.0.imath', true ); 349 | 350 | // Register tagging 351 | wp_register_script( 'tagging', wct_get_js_script( 'tagging' ), array( 'jquery' ), '1.3.1', true ); 352 | 353 | wct_enqueue_style(); 354 | } 355 | 356 | /** 357 | * Loads the translation files 358 | * 359 | * @package WordCamp Talks 360 | * 361 | * @since 1.0.0 362 | */ 363 | public function load_textdomain() { 364 | // Use regular locale 365 | if ( ! function_exists( 'get_user_locale' ) ) { 366 | // Look in global /wp-content/languages/plugins/ 367 | load_plugin_textdomain( $this->domain ); 368 | 369 | // Use user locale instead 370 | } else { 371 | /** 372 | * Filter here to edit this plugin locale. 373 | * 374 | * @since 1.0.0 375 | * 376 | * @param string $value The locale. 377 | * @param string $domain The plugin domain. 378 | */ 379 | $locale = apply_filters( 'wordcamp_talks_locale', get_user_locale(), $this->domain ); 380 | 381 | if ( empty( $locale ) ) { 382 | $mofile = $this->domain . '.mo'; 383 | } else { 384 | $mofile = sprintf( '%1$s-%2$s.mo', $this->domain, $locale ); 385 | } 386 | 387 | /** 388 | * Filter here to use another dir than the regular plugin lang dir 389 | * 390 | * @since 1.0.0 391 | * 392 | * @param string $value Absolute path to the mo file. 393 | * @param string $mofile The mofile file name. 394 | * @param string $locale The current locale. 395 | */ 396 | $mofile_dir = apply_filters( 'wordcamp_talks_lang_dir', $this->lang_dir . $mofile, $mofile, $locale ); 397 | 398 | // Try to see if a GlotPress generated language is available first. 399 | if ( ! load_textdomain( $this->domain, WP_LANG_DIR . '/plugins/' . $mofile ) ) { 400 | load_textdomain( $this->domain, $mofile_dir ); 401 | } 402 | } 403 | } 404 | } 405 | 406 | -------------------------------------------------------------------------------- /includes/admin/comments.php: -------------------------------------------------------------------------------- 1 | setup_globals(); 57 | $this->hooks(); 58 | } 59 | 60 | /** 61 | * Starts the class 62 | * 63 | * @package WordCamp Talks 64 | * @subpackage admin/comments 65 | * 66 | * @since 1.0.0 67 | */ 68 | public static function start() { 69 | if ( ! is_admin() ) { 70 | return; 71 | } 72 | 73 | $wct_admin = wct()->admin; 74 | 75 | if ( empty( $wct_admin->comments ) ) { 76 | $wct_admin->comments = new self; 77 | } 78 | 79 | return $wct_admin->comments; 80 | } 81 | 82 | /** 83 | * Sets some globals 84 | * 85 | * @package WordCamp Talks 86 | * @subpackage admin/comments 87 | * 88 | * @since 1.0.0 89 | */ 90 | private function setup_globals() { 91 | $this->post_type = 'talks'; 92 | $this->talk_comment_count = false; 93 | } 94 | 95 | /** 96 | * Sets up the hooks to extend the plugin's Administration 97 | * 98 | * @package WordCamp Talks 99 | * @subpackage admin/comments 100 | * 101 | * @since 1.0.0 102 | */ 103 | private function hooks() { 104 | 105 | /** Actions *******************************************************************/ 106 | 107 | // Add a bubble to plugin's parent menu if some talk comments are pending 108 | add_action( 'admin_head', array( $this, 'admin_head' ), 10 ); 109 | 110 | // Check the post type if actions were made clicking on a moderation link from an email 111 | add_action( 'load-edit-comments.php', array( $this, 'maybe_force_post_type' ) ); 112 | 113 | // Load some script to also disjoin bubbles 114 | add_action( 'admin_footer-edit-comments.php', array( $this, 'disjoin_post_bubbles' ) ); 115 | add_action( 'admin_footer-edit.php', array( $this, 'disjoin_post_bubbles' ) ); 116 | 117 | /** Filters *******************************************************************/ 118 | 119 | // Add a comment submenu to plugin's menu. 120 | add_filter( 'wct_admin_menus', array( $this, 'comments_menu' ), 10, 1 ); 121 | 122 | // Adjust comment views (count) and comment row actions 123 | add_filter( 'comment_status_links', array( $this, 'adjust_comment_status_links' ), 10, 1 ); 124 | add_filter( 'comment_row_actions', array( $this, 'adjust_row_actions' ), 10, 2 ); 125 | } 126 | 127 | /** 128 | * Adds a bubble to menu title to show how many comments are pending 129 | * 130 | * @package WordCamp Talks 131 | * @subpackage admin/comments 132 | * 133 | * @since 1.0.0 134 | * 135 | * @param string $menu_title the text for the menu 136 | * @param int $count the number of comments 137 | * @return string the title menu output 138 | */ 139 | public function bubbled_menu( $menu_title = '', $count = 0 ) { 140 | return sprintf( 141 | _x( '%1$s %2$s', 'wordcamp-talks admin menu bubble', 'wordcamp-talks' ), 142 | $menu_title, 143 | "" . number_format_i18n( $count ) . "" 144 | ); 145 | } 146 | 147 | /** 148 | * Creates a comments submenu for the plugin's menu 149 | * 150 | * @package WordCamp Talks 151 | * @subpackage admin/comments 152 | * 153 | * @since 1.0.0 154 | * 155 | * @param array $menus list of menu items to add 156 | * @return array the new menu items 157 | */ 158 | public function comments_menu( $menus = array() ) { 159 | // Comments menu title 160 | $comments_menu_title = esc_html__( 'Comments', 'wordcamp-talks' ); 161 | 162 | $this->talk_comment_count = wct_get_global( 'talk_comment_count' ); 163 | 164 | if ( empty( $this->talk_comment_count ) ) { 165 | $this->talk_comment_count = wct_comments_count_comments(); 166 | } 167 | 168 | $comments_menu_title = $this->bubbled_menu( $comments_menu_title . ' ', $this->talk_comment_count->moderated ); 169 | 170 | $menus[0] = array( 171 | 'type' => 'comments', 172 | 'parent_slug' => wct()->admin->parent_slug, 173 | 'page_title' => esc_html__( 'Comments', 'wordcamp-talks' ), 174 | 'menu_title' => $comments_menu_title, 175 | 'capability' => 'edit_posts', // Unfortunately We cannot use 'edit_talks' here see edit-comments.php. 176 | 'slug' => add_query_arg( 'post_type', $this->post_type, 'edit-comments.php' ), 177 | 'function' => '', 178 | 'alt_screen_id' => 'edit-comments.php', 179 | 'actions' => array( 180 | 'admin_head-%page%' => array( $this, 'comments_menu_highlight' ) 181 | ), 182 | ); 183 | 184 | return $menus; 185 | } 186 | 187 | /** 188 | * Adds a bubble to plugin's menu title and make sure it's the highlighted parent 189 | * when talk comments screens are displayed 190 | * 191 | * @package WordCamp Talks 192 | * @subpackage admin/comments 193 | * 194 | * @since 1.0.0 195 | * 196 | * @global $menu 197 | * @global $submenu 198 | * @global $parent_file 199 | * @global $submenu_file 200 | */ 201 | public function admin_head() { 202 | global $menu, $submenu, $parent_file, $submenu_file; 203 | 204 | $menu_title = _x( 'Talks', 'Main Plugin menu', 'wordcamp-talks' ); 205 | 206 | // Eventually add a bubble in plugin's Menu 207 | foreach ( $menu as $position => $data ) { 208 | if ( strpos( $data[0], $menu_title ) !== false ) { 209 | $menu[ $position ][0] = $this->bubbled_menu( $menu_title . ' ', $this->talk_comment_count->moderated ); 210 | } 211 | } 212 | 213 | if ( $this->post_type == get_current_screen()->post_type && 'comment' == get_current_screen()->id ) { 214 | $parent_file = add_query_arg( 'post_type', $this->post_type, 'edit.php' ); 215 | $submenu_file = add_query_arg( 'post_type', $this->post_type, 'edit-comments.php' ); 216 | } 217 | } 218 | 219 | /** 220 | * Make sure the comments plugin's submenu is the highlighted submenu 221 | * if its content is displayed 222 | * 223 | * @package WordCamp Talks 224 | * @subpackage admin/comments 225 | * 226 | * @since 1.0.0 227 | * 228 | * @global $submenu_file 229 | * @uses add_query_arg() to build menu item slugs 230 | */ 231 | public function comments_menu_highlight() { 232 | global $submenu_file; 233 | 234 | if( ! wct_is_admin() ) { 235 | return; 236 | } 237 | 238 | $submenu_file = add_query_arg( 'post_type', $this->post_type, 'edit-comments.php' ); 239 | } 240 | 241 | /** 242 | * Replaces the comment count by the talk comments count in the screen views when 243 | * managing comments about talks 244 | * 245 | * @package WordCamp Talks 246 | * @subpackage admin/comments 247 | * 248 | * @since 1.0.0 249 | * 250 | * @param array $status_links list of WP Liste Comments Table views 251 | * @return array list of views with a new count if needed 252 | */ 253 | public function adjust_comment_status_links( $status_links = array() ) { 254 | // Bail if not in Talk Comments area 255 | if ( ! wct_is_admin() ) { 256 | return $status_links; 257 | } 258 | 259 | foreach ( $status_links as $key => $link ) { 260 | 261 | if ( isset( $this->talk_comment_count->{$key} ) ) { 262 | $prefix = $key; 263 | 264 | if ( 'moderated' == $key ) { 265 | $prefix = 'pending'; 266 | } 267 | 268 | $link = preg_replace( 269 | '/\d<\/span>/', 270 | '' . $this->talk_comment_count->{$key} . '', 271 | $link 272 | ); 273 | } 274 | 275 | $link = preg_replace( '/\?/', '?post_type=' . $this->post_type . '&', $link ); 276 | 277 | if ( preg_match( '/class=\"pending-count\"/', $link ) ) { 278 | $link = preg_replace( '/class=\"pending-count\"/', 'class="pending-count-talk"', $link ); 279 | } 280 | 281 | $status_links[$key] = $link; 282 | } 283 | 284 | return $status_links; 285 | } 286 | 287 | /** 288 | * Adds a post_type query var to the edit action link 289 | * 290 | * @package WordCamp Talks 291 | * @subpackage admin/comments 292 | * 293 | * @since 1.0.0 294 | * 295 | * @param array $actions the list of row actions 296 | * @param object $comment the comment object 297 | * @return array the list of row actions 298 | */ 299 | public function adjust_row_actions( $actions = array(), $comment = null ) { 300 | // Default is unknown... 301 | $post_type = ''; 302 | 303 | if ( ! empty( $comment->post_type ) ) { 304 | $post_type = $comment->post_type; 305 | 306 | // Ajax Listing comments in the edit talk screen will 307 | // fail in getting the post type. 308 | } else { 309 | $post_type = get_post_type( get_the_ID() ); 310 | } 311 | 312 | // Bail if not the talks post type 313 | if ( $this->post_type != $post_type ) { 314 | return $actions; 315 | } 316 | 317 | if ( ! empty( $actions['edit'] ) ) { 318 | // get the url 319 | preg_match( '/]*?href=[\'"](.+?)[\'"]/is', $actions['edit'], $matches ); 320 | 321 | // and add the post type query var to it. 322 | if ( ! empty( $matches[1] ) ) { 323 | $actions['edit'] = str_replace( $matches[1], $matches[1] . '&post_type=' . $this->post_type, $actions['edit'] ); 324 | } 325 | } 326 | 327 | return $actions; 328 | } 329 | 330 | /** 331 | * Sets the post type attribute of the screen when the comments 332 | * was made on an talk 333 | * 334 | * When clicking on a moderation link within a moderation email, the post type 335 | * is not set, as a result, the highlighted menu is not the good one. This make 336 | * sure the typenow global and the post type attribute of the screen are set 337 | * to the talks post type if needed. 338 | * 339 | * @package WordCamp Talks 340 | * @subpackage admin/comments 341 | * 342 | * @since 1.0.0 343 | * 344 | * @global $typenow 345 | */ 346 | function maybe_force_post_type() { 347 | global $typenow; 348 | 349 | if ( empty( $_GET['post_type'] ) ) { 350 | 351 | $get_keys = array_keys( $_GET ); 352 | $did_keys = array( 'approved', 'trashed', 'spammed' ); 353 | 354 | $match_keys = array_intersect( $get_keys, $did_keys); 355 | 356 | if ( ! $match_keys ) { 357 | return; 358 | } 359 | 360 | if ( ! in_array( 'p', $get_keys ) ) { 361 | return; 362 | } 363 | 364 | $post_type = get_post_type( absint( $_GET['p'] ) ); 365 | 366 | if ( empty( $post_type ) ) { 367 | return; 368 | } 369 | 370 | $typenow = $post_type; 371 | get_current_screen()->post_type = $post_type; 372 | } 373 | } 374 | 375 | /** 376 | * Disjoin comment count bubbles 377 | * 378 | * The goal here is to make sure the ajax bubbles count update 379 | * are dissociated between posts and talks 380 | * 381 | * @package WordCamp Talks 382 | * @subpackage admin/comments 383 | * 384 | * @since 1.0.0 385 | * 386 | * @return string JS output 387 | */ 388 | public function disjoin_post_bubbles() { 389 | if ( ! wct_is_admin() ) { 390 | return; 391 | } 392 | ?> 393 | 422 | comment_post_ID ); 47 | 48 | if ( 'talks' === $post->post_type ) { 49 | $comment->comment_post_type = $post->post_type; 50 | $comment->comment_post_author = $post->post_author; 51 | $comment->comment_post_title = $post->post_title; 52 | } 53 | 54 | /** 55 | * @param object $comment the comment object 56 | * @param WP_Post $post the post the comment is linked to 57 | */ 58 | return apply_filters( 'wct_comments_get_comment', $comment, $post ); 59 | } 60 | 61 | /** 62 | * Gets comments matching arguments 63 | * 64 | * @package WordCamp Talks 65 | * @subpackage comments/functions 66 | * 67 | * @since 1.0.0 68 | * 69 | * @param array $args the arguments of the comments query 70 | * @return array the list of comments matching arguments 71 | */ 72 | function wct_comments_get_comments( $args = array() ) { 73 | $comments_args = wp_parse_args( $args, array( 74 | 'post_type' => 'talks', 75 | 'post_status' => 'publish', 76 | 'status' => 'approve', 77 | 'number' => false, 78 | 'offset' => false, 79 | 'fields' => false, 80 | 'post_id' => 0 81 | ) ); 82 | 83 | return get_comments( $comments_args ); 84 | } 85 | 86 | /** 87 | * Clean the talk's comment count cache 88 | * 89 | * @package WordCamp Talks 90 | * @subpackage comments/functions 91 | * 92 | * @since 1.0.0 93 | * 94 | * @param int $comment_id the comment ID 95 | * @param string $status its status 96 | */ 97 | function wct_comments_clean_count_cache( $comment_id = 0, $status = '' ) { 98 | if ( 'wp_insert_comment' === current_action() && is_a( $status, 'WP_Comment' ) ) { 99 | $status = $status->comment_approved; 100 | 101 | // Bail if the comment is not approved 102 | if ( 1 !== (int) $status ) { 103 | return; 104 | } 105 | } 106 | 107 | // Bail if no comment id or the status is delete 108 | if ( empty( $comment_id ) || ( ! empty( $status ) && 'delete' == $status ) ) { 109 | return; 110 | } 111 | 112 | $comment = wct_comments_get_comment( $comment_id ); 113 | 114 | // Make sure the comment has been made on an talk post type 115 | if ( empty( $comment->comment_post_type ) || 'talks' !== $comment->comment_post_type ) { 116 | return; 117 | } 118 | 119 | wp_cache_delete( "talk_comment_count_0", 'wct' ); 120 | 121 | // Clean user count cache 122 | if ( ! empty( $comment->user_id ) ) { 123 | wp_cache_delete( "talk_comment_count_{$comment->user_id}", 'wct' ); 124 | } 125 | } 126 | 127 | /** 128 | * Retrieve total comments about talks for blog or user. 129 | * 130 | * @package WordCamp Talks 131 | * @subpackage comments/functions 132 | * 133 | * @since 1.0.0 134 | * 135 | * @param int $user_id Optional. User ID. 136 | * @return object Comment stats. 137 | */ 138 | function wct_comments_count_comments( $user_id = 0 ) { 139 | 140 | $user_id = (int) $user_id; 141 | 142 | $count = wp_cache_get( "talk_comment_count_{$user_id}", 'wct' ); 143 | 144 | if ( false !== $count ) { 145 | return $count; 146 | } 147 | 148 | // Counting for one user 149 | if ( ! empty( $user_id ) ) { 150 | $stats = WordCamp_Talks_Comments::count_user_comments( $user_id ); 151 | 152 | // Counting for comments on talks 153 | } else { 154 | $stats = WordCamp_Talks_Comments::count_talks_comments(); 155 | } 156 | 157 | wp_cache_set( "talk_comment_count_{$user_id}", $stats, 'wct' ); 158 | 159 | return $stats; 160 | } 161 | 162 | /** Comments urls *************************************************************/ 163 | 164 | /** 165 | * Builds the talk's comment permalink 166 | * 167 | * @package WordCamp Talks 168 | * @subpackage comments/functions 169 | * 170 | * @since 1.0.0 171 | * 172 | * @param integer $comment_id the comment ID 173 | * @return string the comment link 174 | */ 175 | function wct_comments_get_comment_link( $comment_id = 0 ) { 176 | if ( empty( $comment_id ) ) { 177 | return false; 178 | } 179 | 180 | $comment = get_comment( $comment_id ); 181 | 182 | /** 183 | * Check if the Talk still exists. 184 | */ 185 | if ( empty( $comment->comment_post_ID ) ) { 186 | $comment_link = false; 187 | } else { 188 | $comment_link = get_comment_link( $comment ); 189 | } 190 | 191 | /** 192 | * @param string $comment_link the comment permalink 193 | * @param int $comment_id the comment ID 194 | */ 195 | return apply_filters( 'wct_comments_get_comment_link', $comment_link, $comment_id ); 196 | } 197 | 198 | /** 199 | * Make sure the comment edit link about talks post type will 200 | * open the plugin's Comments Submenu once cliked on. 201 | * 202 | * @package WordCamp Talks 203 | * @subpackage comments/functions 204 | * 205 | * @since 1.0.0 206 | * 207 | * @param string $location the comment edit link 208 | * @return string the new comment edit link if about an talk, unchanged otherwise 209 | */ 210 | function wct_edit_comment_link( $location = '' ) { 211 | if ( empty( $location ) ) { 212 | return $location; 213 | } 214 | 215 | // Too bad WordPres is not sending the comment object or ID in the filter :( 216 | if ( ! preg_match( '/[&|&]c=(\d+)/', $location, $matches ) ) { 217 | return $location; 218 | } 219 | 220 | if ( empty( $matches[1] ) ) { 221 | return $location; 222 | } 223 | 224 | $comment_id = absint( $matches[1] ); 225 | $comment = wct_comments_get_comment( $comment_id ); 226 | 227 | if ( empty( $comment->comment_post_type ) || 'talks' !== $comment->comment_post_type ) { 228 | return $location; 229 | } 230 | 231 | $new_location = add_query_arg( 'post_type', 'talks', $location ); 232 | 233 | /** 234 | * @param string $new_location the new comment edit link 235 | * @param string $location the original comment edit link 236 | * @param object $comment the talk's comment object 237 | */ 238 | return apply_filters( 'wct_edit_comment_link', $new_location, $location, $comment ); 239 | } 240 | 241 | /** Template functions ********************************************************/ 242 | 243 | /** 244 | * Builds the loop query arguments for user comments 245 | * 246 | * @package WordCamp Talks 247 | * @subpackage comments/functions 248 | * 249 | * @since 1.0.0 250 | * 251 | * @param string $type is this a single talk ? 252 | * @return array the loop args 253 | */ 254 | function wct_comments_query_args() { 255 | /** 256 | * Use this filter to overide loop args 257 | * @see wct_comments_has_comments() for the list of available ones 258 | * 259 | * @param array by default an empty array 260 | */ 261 | return apply_filters( 'wct_comments_query_args', array() ); 262 | } 263 | 264 | /** 265 | * Should we display the comments form ? 266 | * 267 | * @package WordCamp Talks 268 | * @subpackage comments/functions 269 | * 270 | * @since 1.0.0 271 | * 272 | * @param bool $open true if comments are opened, false otherwise 273 | * @param int $talk_id the ID of the talk 274 | * @return bool true if comments are opened, false otherwise 275 | */ 276 | function wct_comments_open( $open = true, $talk_id = 0 ) { 277 | if ( ! wct_is_talks() ) { 278 | return $open; 279 | } 280 | 281 | if ( $open !== wct_is_comments_allowed() ) { 282 | $open = false; 283 | } else { 284 | $open = wct_user_can( 'comment_talks' ); 285 | } 286 | 287 | /** 288 | * @param bool $open true if comments are opened, false otherwise 289 | * @param int $talk_id the ID of the talk 290 | */ 291 | return apply_filters( 'wct_comments_open', $open, $talk_id ); 292 | } 293 | 294 | /** 295 | * Replace or Add the user's profile link to the comment authors 296 | * 297 | * @package WordCamp Talks 298 | * @subpackage comments/functions 299 | * 300 | * @since 1.0.0 301 | * 302 | * @param array $comments the list of comments in an array 303 | * @param int $talk_id the ID of the talk 304 | * @return array the list of comments, author links replaced by the plugin's profile if needed 305 | */ 306 | function wct_comments_array( $comments = array(), $talk_id = 0 ) { 307 | // Only filter comments arry if on a single talk 308 | if ( ! wct_is_single_talk() || empty( $comments ) || empty( $talk_id ) ) { 309 | return $comments; 310 | } 311 | 312 | // If the user can't comment 313 | if ( ! wct_user_can( 'comment_talks' ) ) { 314 | return array(); 315 | } 316 | 317 | foreach ( $comments as $key => $comment ) { 318 | if ( empty( $comment->user_id ) || ! wct_user_can( 'view_other_profiles', $comment->user_id ) ) { 319 | continue; 320 | } 321 | 322 | $comments[ $key ]->comment_author_url = esc_url( wct_users_get_user_profile_url( $comment->user_id ) ); 323 | } 324 | 325 | return $comments; 326 | } 327 | 328 | /** 329 | * Filter the comments query in case of a private call for speakers 330 | * (when the default talk status is private). 331 | * 332 | * @since 1.0.0 333 | * 334 | * @param array $comment_query_args The Comments loop query arguments. 335 | * @return array The Comments loop query arguments. 336 | */ 337 | function wct_comments_template_query_args( $comment_query_args = array() ) { 338 | if ( ! wct_is_talks() ) { 339 | return $comment_query_args; 340 | } 341 | 342 | // This case should never happened as the talk is private. 343 | if ( ! is_user_logged_in() ) { 344 | $comment_query_args['type__not_in'] = array( 'comment' ); 345 | 346 | // if the user can't view any talk comments, only show him the ones he posted. 347 | } elseif ( ! wct_user_can( 'view_talk_comments' ) ) { 348 | $comment_query_args['user_id'] = get_current_user_id(); 349 | } 350 | 351 | return $comment_query_args; 352 | } 353 | 354 | /** 355 | * Filter the comments count in case of a private call for speakers 356 | * (when the default talk status is private). 357 | * 358 | * @since 1.0.0 359 | * 360 | * @param int $count The comment count. 361 | * @param int $post_id The current Post ID. 362 | * @return int The comment count. 363 | */ 364 | function wct_edit_comments_number( $count = 0, $post_id = 0 ) { 365 | if ( empty( $count ) || empty( $post_id ) ) { 366 | return $count; 367 | } 368 | 369 | $post_type = get_post_type( $post_id ); 370 | 371 | if ( 'talks' !== $post_type ) { 372 | return $count; 373 | } 374 | 375 | if ( wct_user_can( 'view_talk_comments' ) ) { 376 | return $count; 377 | } 378 | 379 | // When listing comments on a single post, we only fetch the current user comments. 380 | if ( ! empty( $GLOBALS['wp_query']->comments ) ) { 381 | return $GLOBALS['wp_query']->comment_count; 382 | } 383 | 384 | // Otherwise there are no comments the user can see. 385 | return 0; 386 | } 387 | 388 | /** 389 | * Edit the comment reply link for blind raters, as they can only view their own comments. 390 | * 391 | * @since 1.0.0 392 | * 393 | * @param string $reply_link HTML output for the reply link. 394 | * @param array $args An array of arguments overriding the defaults. 395 | * @param WP_Comment $comment The object of the comment being replied. 396 | * @param WP_Post $post The WP_Post object. 397 | * @return string HTML output for the reply link. 398 | */ 399 | function wct_comment_reply_link( $reply_link = '', $args = array(), $comment = null, $post = null ) { 400 | if ( empty( $comment->user_id ) ) { 401 | return $reply_link; 402 | } 403 | 404 | $post_type = get_post_type( $post ); 405 | 406 | if ( 'talks' !== $post_type ) { 407 | return $reply_link; 408 | } 409 | 410 | if ( user_can( $comment->user_id, 'view_talk_comments' ) ) { 411 | return $reply_link; 412 | } 413 | 414 | $blind_rater_reply_link = ''; 415 | 416 | if ( (int) $comment->user_id !== wct_users_current_user_id() ) { 417 | $blind_rater_reply_link = sprintf( 418 | esc_html__( '%1$s%2$s Blind raters can only view their own comments.%3$s', 'wordcamp-talks' ), 419 | $args['before'] . '', 420 | '', 421 | '' . $args['after'] 422 | ); 423 | } 424 | 425 | /** 426 | * Filter here if you want to edit the comment reply link for blind raters. 427 | * 428 | * @since 1.0.0 429 | * 430 | * @param string $blind_rater_reply_link HTML output for the reply link of the blind rater. 431 | * @param string $reply_link Original HTML output for the reply link. 432 | * @param array $args An array of arguments overriding the defaults. 433 | * @param WP_Comment $comment The object of the comment being replied. 434 | * @param WP_Post $post The WP_Post object. 435 | */ 436 | return apply_filters( 'wct_comment_reply_link', $blind_rater_reply_link, $reply_link, $args, $comment, $post ); 437 | } 438 | 439 | /** 440 | * Make sure user can see comment feeds. 441 | * 442 | * @package WordCamp Talks 443 | * @subpackage comments/functions 444 | * 445 | * @since 1.0.0 446 | * 447 | * @param string $limit the limit args of the WP_Query comment subquery. 448 | * @param WP_Query $wp_query WordPress main query. 449 | * @return string the limit args of the WP_Query comment subquery. 450 | */ 451 | function wct_comment_feed_limits( $limit = '', $wp_query = null ) { 452 | // Force the comments query to return nothing 453 | if ( ! empty( $wp_query->query['post_type'] ) && 'talks' === $wp_query->query['post_type'] && ! wct_user_can( 'comment_talks' ) ) { 454 | $limit = 'LIMIT 0'; 455 | } 456 | 457 | return $limit; 458 | } 459 | -------------------------------------------------------------------------------- /includes/comments/tags.php: -------------------------------------------------------------------------------- 1 | wct_users_displayed_user_id(), 38 | 'status' => 'approve', 39 | 'number' => wct_talks_per_page(), 40 | 'page' => 1, 41 | ) ); 42 | 43 | // Get the WordCamp Talks 44 | $comment_query_loop = new WordCamp_Talks_Loop_Comments( array( 45 | 'user_id' => (int) $r['user_id'], 46 | 'status' => $r['status'], 47 | 'number' => (int) $r['number'], 48 | 'page' => (int) $r['page'], 49 | ) ); 50 | 51 | // Setup the global query loop 52 | wct()->comment_query_loop = $comment_query_loop; 53 | 54 | return apply_filters( 'wct_comments_has_comments', $comment_query_loop->has_items(), $comment_query_loop ); 55 | } 56 | 57 | /** 58 | * Get the comments returned by the template loop. 59 | * 60 | * @package WordCamp Talks 61 | * @subpackage comments/tags 62 | * 63 | * @since 1.0.0 64 | * 65 | * @return array List of comments. 66 | */ 67 | function wct_comments_the_comments() { 68 | return wct()->comment_query_loop->items(); 69 | } 70 | 71 | /** 72 | * Get the current comment object in the loop. 73 | * 74 | * @package WordCamp Talks 75 | * @subpackage comments/tags 76 | * 77 | * @since 1.0.0 78 | * 79 | * @return object The current comment within the loop. 80 | */ 81 | function wct_comments_the_comment() { 82 | return wct()->comment_query_loop->the_item(); 83 | } 84 | 85 | /** Loop Output ***************************************************************/ 86 | // Mainly inspired by The BuddyPress notifications loop 87 | 88 | /** 89 | * Displays a message if no comments were found 90 | * 91 | * @package WordCamp Talks 92 | * @subpackage comments/tags 93 | * 94 | * @since 1.0.0 95 | */ 96 | function wct_comments_no_comment_found() { 97 | echo wct_comments_get_no_comment_found(); 98 | } 99 | 100 | /** 101 | * Gets a message if no comments were found 102 | * 103 | * @package WordCamp Talks 104 | * @subpackage comments/tags 105 | * 106 | * @since 1.0.0 107 | * 108 | * @return string the message if no comments were found 109 | */ 110 | function wct_comments_get_no_comment_found() { 111 | $output = sprintf( 112 | __( 'It looks like %s has not commented on any talks yet', 'wordcamp-talks' ), 113 | wct_users_get_displayed_user_displayname() 114 | ); 115 | 116 | /** 117 | * @param string $output the message if no comments were found 118 | */ 119 | return apply_filters( 'wct_comments_get_no_comment_found', $output ); 120 | } 121 | 122 | /** 123 | * Output the pagination count for the current comments loop. 124 | * 125 | * @package WordCamp Talks 126 | * @subpackage comments/tags 127 | * 128 | * @since 1.0.0 129 | */ 130 | function wct_comments_pagination_count() { 131 | echo wct_comments_get_pagination_count(); 132 | } 133 | 134 | /** 135 | * Return the pagination count for the current comments loop. 136 | * 137 | * @package WordCamp Talks 138 | * @subpackage comments/tags 139 | * 140 | * @since 1.0.0 141 | * 142 | * @return string HTML for the pagination count. 143 | */ 144 | function wct_comments_get_pagination_count() { 145 | $query_loop = wct()->comment_query_loop; 146 | $start_num = intval( ( $query_loop->page - 1 ) * $query_loop->per_page ) + 1; 147 | $from_num = number_format_i18n( $start_num ); 148 | $to_num = number_format_i18n( ( $start_num + ( $query_loop->per_page - 1 ) > $query_loop->total_comment_count ) ? $query_loop->total_comment_count : $start_num + ( $query_loop->number - 1 ) ); 149 | $total = number_format_i18n( $query_loop->total_comment_count ); 150 | $pag = sprintf( _n( 'Viewing %1$s to %2$s (of %3$s comments)', 'Viewing %1$s to %2$s (of %3$s comments)', $total, 'wordcamp-talks' ), $from_num, $to_num, $total ); 151 | 152 | /** 153 | * @param string $pag the pagination count to output 154 | */ 155 | return apply_filters( 'wct_comments_get_pagination_count', $pag ); 156 | } 157 | 158 | /** 159 | * Output the pagination links for the current comments loop. 160 | * 161 | * @package WordCamp Talks 162 | * @subpackage comments/tags 163 | * 164 | * @since 1.0.0 165 | */ 166 | function wct_comments_pagination_links() { 167 | echo wct_comments_get_pagination_links(); 168 | } 169 | 170 | /** 171 | * Return the pagination links for the current comments loop. 172 | * 173 | * @package WordCamp Talks 174 | * @subpackage comments/tags 175 | * 176 | * @since 1.0.0 177 | * 178 | * @return string HTML for the pagination links. 179 | */ 180 | function wct_comments_get_pagination_links() { 181 | /** 182 | * @param string the pagination links to output 183 | */ 184 | return apply_filters( 'wct_comments_get_pagination_links', wct()->comment_query_loop->pag_links ); 185 | } 186 | 187 | /** 188 | * Output the ID of the comment currently being iterated on. 189 | * 190 | * @package WordCamp Talks 191 | * @subpackage comments/tags 192 | * 193 | * @since 1.0.0 194 | */ 195 | function wct_comments_the_comment_id() { 196 | echo wct_comments_get_comment_id(); 197 | } 198 | 199 | /** 200 | * Return the ID of the comment currently being iterated on. 201 | * 202 | * @package WordCamp Talks 203 | * @subpackage comments/tags 204 | * 205 | * @since 1.0.0 206 | * 207 | * @return int ID of the current comment. 208 | */ 209 | function wct_comments_get_comment_id() { 210 | /** 211 | * @param int the comment ID to output 212 | */ 213 | return apply_filters( 'wct_comments_get_comment_id', wct()->comment_query_loop->comment->comment_ID ); 214 | } 215 | 216 | /** 217 | * Output the avatar of the author of the comment currently being iterated on. 218 | * 219 | * @package WordCamp Talks 220 | * @subpackage comments/tags 221 | * 222 | * @since 1.0.0 223 | */ 224 | function wct_comments_the_comment_author_avatar() { 225 | echo wct_comments_get_comment_author_avatar(); 226 | } 227 | 228 | /** 229 | * Return the avatar of the author of the comment currently being iterated on. 230 | * 231 | * @package WordCamp Talks 232 | * @subpackage comments/tags 233 | * 234 | * @since 1.0.0 235 | * 236 | * @return string the avatar. 237 | */ 238 | function wct_comments_get_comment_author_avatar() { 239 | $author = wct()->comment_query_loop->comment->user_id; 240 | $avatar = get_avatar( $author ); 241 | $avatar_link = '' . $avatar . ''; 242 | 243 | /** 244 | * @param string $avatar_link the avatar output 245 | * @param int $author the author ID 246 | * @param string $avatar the avatar 247 | */ 248 | return apply_filters( 'wct_comments_get_comment_author_avatar', $avatar_link, $author, $avatar ); 249 | } 250 | 251 | /** 252 | * Output the mention to add before the title of the comment currently being iterated on. 253 | * 254 | * @package WordCamp Talks 255 | * @subpackage comments/tags 256 | * 257 | * @since 1.0.0 258 | */ 259 | function wct_comments_before_comment_title() { 260 | echo wct_comments_get_before_comment_title(); 261 | } 262 | 263 | /** 264 | * Return the mention to add before the title of the comment currently being iterated on. 265 | * 266 | * @package WordCamp Talks 267 | * @subpackage comments/tags 268 | * 269 | * @since 1.0.0 270 | * 271 | * @return string the mention to prefix the title with. 272 | */ 273 | function wct_comments_get_before_comment_title() { 274 | /** 275 | * @param string the mention output 276 | */ 277 | return apply_filters( 'wct_comments_get_before_comment_title', esc_html__( 'In reply to:', 'wordcamp-talks' ) ); 278 | } 279 | 280 | /** 281 | * Output the permalink of the comment currently being iterated on. 282 | * 283 | * @package WordCamp Talks 284 | * @subpackage comments/tags 285 | * 286 | * @since 1.0.0 287 | */ 288 | function wct_comments_the_comment_permalink() { 289 | echo wct_comments_get_comment_permalink(); 290 | } 291 | 292 | /** 293 | * Return the permalink of the comment currently being iterated on. 294 | * 295 | * @package WordCamp Talks 296 | * @subpackage comments/tags 297 | * 298 | * @since 1.0.0 299 | * 300 | * @return string the comment's permalink. 301 | */ 302 | function wct_comments_get_comment_permalink() { 303 | $comment = wct()->comment_query_loop->comment; 304 | $comment_link = wct_comments_get_comment_link( $comment ); 305 | 306 | /** 307 | * @param string $comment_link the comment link 308 | * @param object $comment the comment object 309 | */ 310 | return apply_filters( 'wct_comments_get_comment_permalink', esc_url( $comment_link ), $comment ); 311 | } 312 | 313 | /** 314 | * Output the title attribute of the comment currently being iterated on. 315 | * 316 | * @package WordCamp Talks 317 | * @subpackage comments/tags 318 | * 319 | * @since 1.0.0 320 | */ 321 | function wct_comments_the_comment_title_attribute() { 322 | echo wct_comments_get_comment_title_attribute(); 323 | } 324 | 325 | /** 326 | * Return the title attribute of the comment currently being iterated on. 327 | * 328 | * @package WordCamp Talks 329 | * @subpackage comments/tags 330 | * 331 | * @since 1.0.0 332 | * 333 | * @return string the title attribute. 334 | */ 335 | function wct_comments_get_comment_title_attribute() { 336 | $comment = wct()->comment_query_loop->comment; 337 | $title = ''; 338 | 339 | $talk = $comment->comment_post_ID; 340 | 341 | if ( ! empty( $comment->talk ) ) { 342 | $talk = $comment->talk; 343 | } 344 | 345 | $talk = get_post( $talk ); 346 | 347 | if ( ! empty( $talk->post_password ) ) { 348 | $title = _x( 'Protected:', 'talk permalink title protected attribute', 'wordcamp-talks' ) . ' '; 349 | } 350 | 351 | $title .= $talk->post_title; 352 | 353 | /** 354 | * @param string $title the title attribute 355 | * @param WP_Post $talk the talk object 356 | * @param object $comment the comment object 357 | */ 358 | return apply_filters( 'wct_comments_get_comment_title_attribute', esc_attr( $title ), $talk, $comment ); 359 | } 360 | 361 | /** 362 | * Output the title of the comment currently being iterated on. 363 | * 364 | * @package WordCamp Talks 365 | * @subpackage comments/tags 366 | * 367 | * @since 1.0.0 368 | */ 369 | function wct_comments_the_comment_title() { 370 | echo wct_comments_get_comment_title(); 371 | } 372 | 373 | /** 374 | * Return the title of the comment currently being iterated on. 375 | * 376 | * @package WordCamp Talks 377 | * @subpackage comments/tags 378 | * 379 | * @since 1.0.0 380 | * 381 | * @return string the title. 382 | */ 383 | function wct_comments_get_comment_title() { 384 | $comment = wct()->comment_query_loop->comment; 385 | 386 | /** 387 | * When the talk has a private status, we're applying a dashicon to a span 388 | * So we need to only allow this tag when sanitizing the output 389 | */ 390 | if ( isset( $comment->post_status ) && 'publish' !== $comment->post_status ) { 391 | $title = wp_kses( get_the_title( $comment->comment_post_ID ), array( 'span' => array( 'class' => array() ) ) ); 392 | } else { 393 | $title = esc_html( get_the_title( $comment->comment_post_ID ) ); 394 | } 395 | 396 | /** 397 | * @param string the title of the talk, the comment is linked to 398 | * @param object $comment the comment object 399 | */ 400 | return apply_filters( 'wct_comments_get_comment_title', $title, $comment ); 401 | } 402 | 403 | /** 404 | * Output the excerpt of the comment currently being iterated on. 405 | * 406 | * @package WordCamp Talks 407 | * @subpackage comments/tags 408 | * 409 | * @since 1.0.0 410 | */ 411 | function wct_comments_the_comment_excerpt() { 412 | echo wp_kses_post( wct_comments_get_comment_excerpt() ); 413 | } 414 | 415 | /** 416 | * Return the excerpt of the comment currently being iterated on. 417 | * 418 | * @package WordCamp Talks 419 | * @subpackage comments/tags 420 | * 421 | * @since 1.0.0 422 | * 423 | * @return string the excerpt. 424 | */ 425 | function wct_comments_get_comment_excerpt() { 426 | $comment = wct()->comment_query_loop->comment; 427 | $title = ''; 428 | 429 | $talk = $comment->comment_post_ID; 430 | 431 | if ( ! empty( $comment->talk ) ) { 432 | $talk = $comment->talk; 433 | } 434 | 435 | $talk = get_post( $talk ); 436 | 437 | if ( post_password_required( $talk ) ) { 438 | $excerpt = __( 'The talk the comment was posted on is password protected: you will need the password to view its content.', 'wordcamp-talks' ); 439 | 440 | // Private 441 | } else if ( ! empty( $talk->post_status ) && 'publish' !== $talk->post_status && ! wct_user_can( 'read_talk', $talk->ID ) ) { 442 | $excerpt = __( 'The talk the comment was posted on is private: you cannot view its content.', 'wordcamp-talks' ); 443 | 444 | // Public 445 | } else { 446 | $excerpt = get_comment_excerpt( wct()->comment_query_loop->comment->comment_ID ); 447 | } 448 | 449 | /** 450 | * @param string $excerpt the comment excerpt 451 | */ 452 | return apply_filters( 'wct_comments_get_comment_excerpt', $excerpt ); 453 | } 454 | 455 | /** 456 | * Output the footer of the comment currently being iterated on. 457 | * 458 | * @package WordCamp Talks 459 | * @subpackage comments/tags 460 | * 461 | * @since 1.0.0 462 | */ 463 | function wct_comments_the_comment_footer() { 464 | echo wp_kses_post( wct_comments_get_comment_footer() ); 465 | } 466 | 467 | /** 468 | * Return the footer of the comment currently being iterated on. 469 | * 470 | * @package WordCamp Talks 471 | * @subpackage comments/tags 472 | * 473 | * @since 1.0.0 474 | * 475 | * @return string the footer. 476 | */ 477 | function wct_comments_get_comment_footer() { 478 | $posted_on = sprintf( esc_html__( 'This comment was posted on %s', 'wordcamp-talks' ), get_comment_date( '', wct()->comment_query_loop->comment->comment_ID ) ); 479 | 480 | /** 481 | * @param string $posted_on the comment footer 482 | * @param object the comment object 483 | */ 484 | return apply_filters( 'wct_comments_get_comment_footer', $posted_on, wct()->comment_query_loop->comment ); 485 | } 486 | -------------------------------------------------------------------------------- /includes/comments/wordcamp-talks-comments.php: -------------------------------------------------------------------------------- 1 | setup_globals(); 27 | $this->hooks(); 28 | } 29 | 30 | /** 31 | * Starts the class 32 | * 33 | * @package WordCamp Talks 34 | * @subpackage comments/classes 35 | * 36 | * @since 1.0.0 37 | */ 38 | public static function start() { 39 | $wct = wct(); 40 | 41 | if ( empty( $wct->comments ) ) { 42 | $wct->comments = new self; 43 | } 44 | 45 | return $wct->comments; 46 | } 47 | 48 | /** 49 | * Setups some globals 50 | * 51 | * @package WordCamp Talks 52 | * @subpackage comments/classes 53 | * 54 | * @since 1.0.0 55 | */ 56 | private function setup_globals() { 57 | /** Rewrite ids ***************************************************************/ 58 | $this->post_type = 'talks'; 59 | $this->comments_count = false; 60 | $this->talk_comments_count = false; 61 | } 62 | 63 | /** 64 | * Hooks to disjoin comments about talks 65 | * & to filter the email notifications 66 | * 67 | * @package WordCamp Talks 68 | * @subpackage comments/classes 69 | * 70 | * @since 1.0.0 71 | */ 72 | private function hooks() { 73 | add_action( 'pre_get_comments', array( $this, 'maybe_talk_comments' ), 10, 1 ); 74 | 75 | add_action( 'init', array( $this, 'cache_comments_count' ) ); 76 | add_filter( 'wp_count_comments', array( $this, 'adjust_comment_count' ), 10, 1 ); 77 | add_filter( 'widget_comments_args', array( $this, 'comments_widget_dummy_var' ), 10, 1 ); 78 | add_filter( 'comments_clauses', array( $this, 'maybe_alter_comments_query'), 10, 2 ); 79 | 80 | // Make sure the comment notifications respect talk authors capability 81 | add_filter( 'comment_moderation_recipients', array( $this, 'moderation_recipients' ), 10, 2 ); 82 | add_filter( 'comment_notification_text', array( $this, 'comment_notification' ), 10, 2 ); 83 | add_filter( 'comment_moderation_text', array( $this, 'comment_notification' ), 10, 2 ); 84 | } 85 | 86 | /** 87 | * Makes sure the post type is set to talks when in Talks 88 | * administration screens 89 | * 90 | * @package WordCamp Talks 91 | * @subpackage comments/classes 92 | * 93 | * @since 1.0.0 94 | * 95 | * @param WP_Comment_Query $wp_comment_query 96 | */ 97 | function maybe_talk_comments( $wp_comment_query = null ) { 98 | // Bail if Ajax 99 | if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { 100 | return; 101 | } 102 | 103 | if ( wct_is_admin() ) { 104 | $wp_comment_query->query_vars['post_type'] = $this->post_type; 105 | } 106 | } 107 | 108 | /** 109 | * Catches the "all comments" count 110 | * 111 | * @package WordCamp Talks 112 | * @subpackage comments/classes 113 | * 114 | * @since 1.0.0 115 | */ 116 | public function cache_comments_count() { 117 | $this->comment_count = wp_cache_get( 'comments-0', 'counts' ); 118 | 119 | if ( empty( $this->comment_count ) ) { 120 | $this->comment_count = wp_count_comments(); 121 | } 122 | 123 | // For internal use only, please don't use this action. 124 | do_action( 'wct_cache_comments_count' ); 125 | } 126 | 127 | /** 128 | * Adjust the comment count 129 | * by counting comments about talks 130 | * by removing this count to the global comment count 131 | * 132 | * @package WordCamp Talks 133 | * @subpackage comments/classes 134 | * 135 | * @since 1.0.0 136 | * 137 | * @param array $stats empty array to override in the method 138 | * @return array adjusted comment count stats 139 | */ 140 | public function adjust_comment_count( $stats = array() ) { 141 | if ( did_action( 'wct_cache_comments_count' ) ) { 142 | $this->talk_comment_count = wct_comments_count_comments(); 143 | 144 | // Catch this count 145 | wct_set_global( 'talk_comment_count', $this->talk_comment_count ); 146 | 147 | if ( ! did_action( 'wct_comments_count_cached' ) ) { 148 | $talk_comment_count = clone $this->talk_comment_count; 149 | 150 | foreach ( $this->comment_count as $key => $count ) { 151 | if ( ! empty( $talk_comment_count->{$key} ) ) { 152 | $this->comment_count->{$key} = $count - $talk_comment_count->{$key}; 153 | unset( $talk_comment_count->{$key} ); 154 | } 155 | } 156 | 157 | // For internal use only, please don't use this action. 158 | do_action( 'wct_comments_count_cached' ); 159 | } 160 | 161 | $stats = $this->comment_count; 162 | } 163 | 164 | return $stats; 165 | } 166 | 167 | /** 168 | * Adds a dummy argument to comments widget in order 169 | * to be able to remove a bit later comments about talks 170 | * 171 | * @package WordCamp Talks 172 | * @subpackage comments/classes 173 | * 174 | * @since 1.0.0 175 | */ 176 | public function comments_widget_dummy_var( $comment_args = array() ) { 177 | if ( empty( $comment_args['post_type' ] ) || $this->post_type != $comment_args['post_type' ] ) { 178 | $comment_args['strip_talks'] = true; 179 | } 180 | 181 | /** 182 | * @param array $comment_args the arguments of the comment query of the widget 183 | */ 184 | return apply_filters( 'wct_comments_widget_disjoin_talks', $comment_args ); 185 | } 186 | 187 | /** 188 | * Make sure talks comments are not mixed with posts ones 189 | * 190 | * @package WordCamp Talks 191 | * @subpackage comments/classes 192 | * 193 | * @since 1.0.0 194 | * 195 | * @param array $pieces 196 | * @param WP_Comment_Query $wp_comment_query 197 | * @return array $pieces 198 | */ 199 | public function maybe_alter_comments_query( $pieces = array(), $wp_comment_query = null ) { 200 | 201 | // Bail if Ajax 202 | if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { 203 | return $pieces; 204 | } 205 | 206 | /* Bail if not the talks post type */ 207 | if ( $this->post_type == $wp_comment_query->query_vars['post_type'] || wct_is_admin() ) { 208 | return $pieces; 209 | } 210 | 211 | /* Bail if strip talks query var is not set on front */ 212 | if ( ! is_admin() && empty( $wp_comment_query->query_vars['strip_talks'] ) ) { 213 | return $pieces; 214 | } 215 | 216 | // Override pieces 217 | return array_merge( $pieces, self::comments_query_pieces( $pieces ) ); 218 | } 219 | 220 | /** 221 | * Removes recipients from the moderation notification if needed 222 | * 223 | * @package WordCamp Talks 224 | * @subpackage comments/classes 225 | * 226 | * @since 1.0.0 227 | * 228 | * @param array $emails list of emails that will receive the moderation notification 229 | * @param integer $comment_id the comment ID 230 | * @return array the emails, without the author 231 | */ 232 | public function moderation_recipients( $emails = array(), $comment_id = 0 ) { 233 | // Return if no comment ID 234 | if ( empty( $comment_id ) ) { 235 | return $emails; 236 | } 237 | 238 | // Get the comment 239 | $comment = wct_comments_get_comment( $comment_id ); 240 | 241 | // check if it relates to an talk 242 | if ( empty( $comment->comment_post_type ) || 'talks' != $comment->comment_post_type ) { 243 | return $emails; 244 | } 245 | 246 | // We have a comment about an talk, catch it for a later use 247 | $this->{'comment_post_' . $comment_id} = $comment; 248 | 249 | /** 250 | * Talk's author will receive a moderation email but won't be able 251 | * to moderate it in WordPress Admin, so we need to remove their 252 | * email from recipients list. 253 | */ 254 | $author_email = wct_users_get_user_data( 'id', $comment->comment_post_author, 'user_email' ); 255 | 256 | // Found author's email in the list ? If so, let's remove it. 257 | if ( ! empty( $author_email ) && in_array( $author_email, $emails ) ) { 258 | $emails = array_diff( $emails, array( $author_email ) ); 259 | } 260 | 261 | return $emails; 262 | } 263 | 264 | /** 265 | * Edit the new comment notification message 266 | * 267 | * @package WordCamp Talks 268 | * @subpackage comments/classes 269 | * 270 | * @since 1.0.0 271 | * 272 | * @param string $message the content of the notification 273 | * @param integer $comment_id the comment ID 274 | * @return string the content, edited if needed 275 | */ 276 | public function comment_notification( $message = '', $comment_id = 0 ) { 277 | // Return if no comment ID 278 | if ( empty( $comment_id ) ) { 279 | return $message; 280 | } 281 | 282 | // Check caught value 283 | if ( ! empty( $this->{'comment_post_' . $comment_id} ) ) { 284 | $comment = $this->{'comment_post_' . $comment_id}; 285 | 286 | // Get the comment to check if it relates to an talk 287 | } else { 288 | $comment = wct_comments_get_comment( $comment_id ); 289 | } 290 | 291 | // Return if no user_id or the comment does not relate to an talk 292 | if ( empty( $comment->comment_post_author ) || empty( $comment->comment_post_type ) || 'talks' != $comment->comment_post_type ) { 293 | return $message; 294 | } 295 | 296 | // First add a post type var at the end of the links 297 | preg_match_all( '/(comment|comments).php\?(.*)\\r\\n/', $message, $matches ); 298 | 299 | if ( ! empty( $matches[2] ) ) { 300 | foreach ( $matches[2] as $action ) { 301 | $message = str_replace( $action, $action . '&post_type=talks', $message ); 302 | } 303 | } 304 | 305 | // It's not a notification to author return the message 306 | if ( empty( $comment->comment_approved ) ) { 307 | return $message; 308 | } 309 | 310 | /** 311 | * If we arrive here, then WordPress is notifying the author of the talk 312 | * that a new comment has been posted and approuved on his talk. So if the 313 | * talk's author does not have the capability to moderate comments, we need 314 | * to make sure he won't receive the links to delete|trash|spam the comment 315 | * The easiest way is to completely replace the content of the message sent. 316 | */ 317 | if ( ! user_can( $comment->comment_post_author, 'moderate_comments' ) ) { 318 | // reset the message 319 | $message = sprintf( __( 'New comment on your talk "%s"', 'wordcamp-talks' ), $comment->comment_post_title ) . "\r\n"; 320 | $message .= __( 'Comment: ', 'wordcamp-talks' ) . "\r\n" . $comment->comment_content . "\r\n\r\n"; 321 | $message .= sprintf( __( 'Permalink to the comment: %s', 'wordcamp-talks' ), wct_comments_get_comment_link( $comment_id ) ) . "\r\n"; 322 | } 323 | 324 | /** 325 | * @param object $comment the comment object 326 | */ 327 | do_action( 'wct_comments_notify_author', $comment ); 328 | 329 | return $message; 330 | } 331 | 332 | /** 333 | * Build pieces to remove comments about talks 334 | * 335 | * @package WordCamp Talks 336 | * @subpackage comments/classes 337 | * 338 | * @since 1.0.0 339 | * 340 | * @global $wpdb 341 | * @param array $pieces the comment sql query pieces 342 | * @return array $pieces 343 | */ 344 | public static function comments_query_pieces( $pieces = array() ) { 345 | global $wpdb; 346 | 347 | if ( ! empty( $pieces ) ) { 348 | $pieces = array( 349 | 'join' => "JOIN {$wpdb->posts} ON {$wpdb->posts}.ID = {$wpdb->comments}.comment_post_ID", 350 | 'where' => $pieces['where'] . ' ' . $wpdb->prepare( "AND {$wpdb->posts}.post_type != %s", 'talks' ), 351 | ); 352 | } 353 | 354 | return $pieces; 355 | } 356 | 357 | /** 358 | * Count user's comments about talks 359 | * 360 | * @package WordCamp Talks 361 | * @subpackage comments/classes 362 | * 363 | * @since 1.0.0 364 | * 365 | * @global $wpdb 366 | * @param int $user_id 367 | * @return int $stats number of comments for the user 368 | */ 369 | public static function count_user_comments( $user_id = 0 ) { 370 | global $wpdb; 371 | 372 | // Initialize vars 373 | $stats = 0; 374 | $sql = array(); 375 | 376 | if ( empty( $user_id ) ) { 377 | return $stats; 378 | } 379 | 380 | $sql['select'] = 'SELECT COUNT( * )'; 381 | $sql['from'] = "FROM {$wpdb->comments} LEFT JOIN {$wpdb->posts} ON ( {$wpdb->posts}.ID = {$wpdb->comments}.comment_post_ID )"; 382 | $sql['where'][] = $wpdb->prepare( "{$wpdb->posts}.post_type = %s", 'talks' ); 383 | $sql['where'][] = $wpdb->prepare( "{$wpdb->comments}.user_id = %d", $user_id ); 384 | $sql['where'][] = $wpdb->prepare( "{$wpdb->comments}.comment_approved = %d", 1 ); 385 | 386 | //Merge where clauses 387 | $sql['where'] = 'WHERE ' . join( ' AND ', $sql['where'] ); 388 | 389 | $query = apply_filters( 'wct_count_user_comments_query', join( ' ', $sql ), $sql ); 390 | 391 | $stats = (int) $wpdb->get_var( $query ); 392 | 393 | return $stats; 394 | } 395 | 396 | /** 397 | * Count comments about talks 398 | * 399 | * @package WordCamp Talks 400 | * @subpackage comments/classes 401 | * 402 | * @since 1.0.0 403 | * 404 | * @global $wpdb 405 | * @return object $stats list of comments by type (approved, pending, spam, trash...) 406 | */ 407 | public static function count_talks_comments() { 408 | global $wpdb; 409 | 410 | // Initialize vars 411 | $stats = array(); 412 | $sql = array(); 413 | 414 | $sql['select'] = 'SELECT comment_approved, COUNT( * ) AS num_comments'; 415 | $sql['from'] = "FROM {$wpdb->comments} LEFT JOIN {$wpdb->posts} ON ( {$wpdb->posts}.ID = {$wpdb->comments}.comment_post_ID )"; 416 | $sql['where'] = $wpdb->prepare( "WHERE {$wpdb->posts}.post_type = %s", 'talks' ); 417 | $sql['groupby'] = 'GROUP BY comment_approved'; 418 | 419 | $query = apply_filters( 'wct_count_talks_comments_query', join( ' ', $sql ), $sql ); 420 | $count = $wpdb->get_results( $query, ARRAY_A ); 421 | 422 | $stats = array( 423 | 'approved' => 0, 424 | 'awaiting_moderation' => 0, 425 | 'spam' => 0, 426 | 'trash' => 0, 427 | 'post-trashed' => 0, 428 | 'total_comments' => 0, 429 | 'all' => 0, 430 | ); 431 | 432 | foreach ( $count as $row ) { 433 | switch ( $row['comment_approved'] ) { 434 | case 'trash': 435 | $stats['trash'] = $row['num_comments']; 436 | break; 437 | case 'post-trashed': 438 | $stats['post-trashed'] = $row['num_comments']; 439 | break; 440 | case 'spam': 441 | $stats['spam'] = $row['num_comments']; 442 | $stats['total_comments'] += $row['num_comments']; 443 | break; 444 | case '1': 445 | $stats['approved'] = $row['num_comments']; 446 | $stats['total_comments'] += $row['num_comments']; 447 | $stats['all'] += $row['num_comments']; 448 | break; 449 | case '0': 450 | $stats['awaiting_moderation'] = $row['num_comments']; 451 | $stats['total_comments'] += $row['num_comments']; 452 | $stats['all'] += $row['num_comments']; 453 | break; 454 | default: 455 | break; 456 | } 457 | } 458 | 459 | $stats['moderated'] = $stats['awaiting_moderation']; 460 | unset( $stats['awaiting_moderation'] ); 461 | 462 | return (object) $stats; 463 | } 464 | 465 | /** 466 | * Make sure speakers won't be notified in case a comment has been added to their talks 467 | * 468 | * @since 1.0.0 469 | * 470 | * @param array $emails list of emails 471 | * @param int $comment_id the comment id 472 | * @return array list of emails without the speaker one 473 | */ 474 | function donot_notify_talk_authors( $emails = array(), $comment_id = 0 ) { 475 | if ( empty( $comment_id ) ) { 476 | return $emails; 477 | } 478 | 479 | $comment = wct_comments_get_comment( $comment_id ); 480 | 481 | // check if it relates to a talk 482 | if ( empty( $comment->comment_post_type ) || 'talks' !== $comment->comment_post_type ) { 483 | return $emails; 484 | } 485 | 486 | // Get the speaker email 487 | $author_email = wct_users_get_user_data( 'id', $comment->comment_post_author, 'user_email' ); 488 | 489 | // Found speaker's email in the list ? If so, let's remove it. 490 | if ( ! empty( $author_email ) && in_array( $author_email, $emails ) ) { 491 | $emails = array_diff( $emails, array( $author_email ) ); 492 | } 493 | 494 | return $emails; 495 | } 496 | } 497 | --------------------------------------------------------------------------------