├── classes └── class.pmproseries.php ├── css ├── pmpro-series-admin.css ├── pmpro_series.css └── select2.css ├── email └── new_content.html ├── js ├── pmpro-series.js └── select2.js ├── languages ├── pmpro-series-en_EN.mo ├── pmpro-series-en_EN.po ├── pmpro-series-fr_FR.mo ├── pmpro-series-fr_FR.po ├── pmpro-series.mo ├── pmpro-series.po └── pmpro-series.pot ├── pmpro-series.php ├── readme.txt └── scheduled └── crons.php /classes/class.pmproseries.php: -------------------------------------------------------------------------------- 1 | getSeriesByID( $id ); 42 | } else { 43 | return true; 44 | } 45 | } 46 | 47 | /** 48 | * [getSeriesByID] Populate series data by post id passed. 49 | * 50 | * @param integer $id 51 | * @return integer 52 | */ 53 | function getSeriesByID( $id ) { 54 | $this->post = get_post( $id ); 55 | if ( ! empty( $this->post->ID ) ) { 56 | $this->id = $id; 57 | } else { 58 | $this->id = false; 59 | } 60 | 61 | return $this->id; 62 | } 63 | 64 | /** 65 | * [addPost] Add a post to this series. 66 | * 67 | * @param integer $post_id 68 | * @param integer $delay 69 | */ 70 | function addPost( $post_id, $delay ) { 71 | if ( empty( $post_id ) || ! isset( $delay ) ) { 72 | $this->error = esc_html__( 'Please enter a value for post and delay.', 'pmpro-series' ); 73 | return false; 74 | } 75 | 76 | $post = get_post( $post_id ); 77 | 78 | if ( empty( $post->ID ) ) { 79 | $this->error = esc_html__( 'A post with that id does not exist.', 'pmpro-series' ); 80 | return false; 81 | } 82 | 83 | $this->getPosts(); 84 | 85 | if ( empty( $this->posts ) ) { 86 | $this->posts = array(); 87 | } 88 | 89 | // remove any old post with this id 90 | if ( $this->hasPost( $post_id ) ) { 91 | $this->removePost( $post_id ); 92 | } 93 | 94 | // add post 95 | $temp = new stdClass(); 96 | $temp->id = $post_id; 97 | $temp->delay = $delay; 98 | $this->posts[] = $temp; 99 | 100 | // sort 101 | usort( $this->posts, array( 'PMProSeries', 'sortByDelay' ) ); 102 | 103 | // save 104 | update_post_meta( $this->id, '_series_posts', $this->posts ); 105 | 106 | // add series to post 107 | $post_series = get_post_meta( $post_id, '_post_series', true ); 108 | if ( ! is_array( $post_series ) ) { 109 | $post_series = array( $this->id ); 110 | } else { 111 | $post_series[] = $this->id; 112 | } 113 | 114 | // save 115 | update_post_meta( $post_id, '_post_series', $post_series ); 116 | } 117 | 118 | /** 119 | * [removePost] Remove a post from this series. 120 | * 121 | * @param integer $post_id 122 | * @param integer $delay 123 | */ 124 | function removePost( $post_id ) { 125 | if ( empty( $post_id ) ) { 126 | return false; 127 | } 128 | 129 | $this->getPosts(); 130 | 131 | if ( empty( $this->posts ) ) { 132 | return true; 133 | } 134 | 135 | // remove this post from the series 136 | foreach ( $this->posts as $i => $post ) { 137 | if ( $post->id == $post_id ) { 138 | unset( $this->posts[ $i ] ); 139 | $this->posts = array_values( $this->posts ); 140 | update_post_meta( $this->id, '_series_posts', $this->posts ); 141 | break; // assume there is only one 142 | } 143 | } 144 | 145 | // remove this series from the post 146 | $post_series = get_post_meta( $post_id, '_post_series', true ); 147 | if ( is_array( $post_series ) && ( $key = array_search( $this->id, $post_series ) ) !== false ) { 148 | unset( $post_series[ $key ] ); 149 | update_post_meta( $post_id, '_post_series', $post_series ); 150 | } 151 | 152 | return true; 153 | } 154 | 155 | /** 156 | * [getPosts] Get array of all posts in this series. 157 | * force = ignore cache and get data from DB. 158 | * 159 | * @param boolean $force update from database. 160 | * @param null|string $status of posts to return. 161 | * @return array of posts with status, if applicable. $this->posts will not be filtered by $status. 162 | */ 163 | function getPosts( $force = false, $status = null ) { 164 | if ( ! isset( $this->posts ) || $force ) { 165 | $this->posts = get_post_meta( $this->id, '_series_posts', true ); 166 | if ( ! is_array( $this->posts ) ) { 167 | $this->posts = array(); 168 | } 169 | } 170 | 171 | if ( ! empty( $status ) ) { 172 | $posts_with_status = array(); 173 | foreach ($this->posts as $key => $post) { 174 | if ( $status === get_post_status( $post->id ) ) { 175 | $posts_with_status[] = $post; 176 | } 177 | } 178 | return $posts_with_status; 179 | } 180 | return $this->posts; 181 | } 182 | 183 | /** 184 | * [hasPost] Does this series include post with id = post_id. 185 | * 186 | * @param [type] $post_id 187 | * @return boolean 188 | */ 189 | function hasPost( $post_id ) { 190 | $this->getPosts(); 191 | 192 | if ( empty( $this->posts ) ) { 193 | return false; 194 | } 195 | 196 | foreach ( $this->posts as $key => $post ) { 197 | if ( $post->id == $post_id ) { 198 | return true; 199 | } 200 | } 201 | 202 | return false; 203 | } 204 | 205 | /** 206 | * [getPostKey] Get key of post with id = $post_id. 207 | * 208 | * @param [type] $post_id 209 | * @return [type] 210 | */ 211 | function getPostKey( $post_id ) { 212 | $this->getPosts(); 213 | 214 | if ( empty( $this->posts ) ) { 215 | return false; 216 | } 217 | 218 | foreach ( $this->posts as $key => $post ) { 219 | if ( $post->id == $post_id ) { 220 | return $key; 221 | } 222 | } 223 | 224 | return false; 225 | } 226 | 227 | /** 228 | * [getDelayForPost] 229 | * 230 | * @param [type] $post_id 231 | * @return [type] 232 | */ 233 | function getDelayForPost( $post_id ) { 234 | $key = $this->getPostKey( $post_id ); 235 | 236 | if ( $key === false ) { 237 | return false; 238 | } else { 239 | return $this->posts[ $key ]->delay; 240 | } 241 | } 242 | 243 | /** 244 | * [getLongestPostDelay] 245 | * 246 | * @param null|string $status of posts to consider. 247 | * @return int longest post delay. 248 | */ 249 | function getLongestPostDelay( $status = null ) { 250 | $posts = $this->getPosts( false, $status ); 251 | $delay = 0; 252 | foreach ( $posts as $post ) { 253 | if ( ! empty( $post->delay ) && $post->delay > $delay ) { 254 | $delay = $post->delay; 255 | } 256 | } 257 | return $delay; 258 | } 259 | 260 | /** 261 | * [sortByDelay] Sort posts by delay. 262 | * 263 | * @param [type] $a 264 | * @param [type] $b 265 | * @return [type] 266 | */ 267 | function sortByDelay( $a, $b ) { 268 | if ( $a->delay == $b->delay ) { 269 | return 0; 270 | } 271 | return ( $a->delay < $b->delay ) ? -1 : 1; 272 | } 273 | 274 | /** 275 | * [sendEmail] Send an email RE new access to post_id to email of user_id. 276 | * 277 | * @param [type] $post_ids 278 | * @param [type] $user_id 279 | * @return [type] 280 | */ 281 | function sendEmail( $post_ids, $user_id ) { 282 | if ( ! class_exists( 'PMProEmail' ) ) { 283 | return; 284 | } 285 | 286 | $email = new PMProEmail(); 287 | 288 | $user = get_user_by( 'id', $user_id ); 289 | 290 | // build list of posts 291 | $post_list = "\n"; 296 | 297 | $email->email = $user->user_email; 298 | $subject = sprintf( esc_html__( 'New content is available at %s', 'pmpro-series' ), esc_html( get_option( 'blogname' ) ) ); 299 | $email->subject = apply_filters( 'pmpros_new_content_subject', $subject, $user, $post_ids ); 300 | $email->template = 'new_content'; 301 | 302 | // check for custom email template 303 | if ( file_exists( get_stylesheet_directory() . '/paid-memberships-pro/series/new_content.html' ) ) { 304 | $template_path = get_stylesheet_directory() . '/paid-memberships-pro/series/new_content.html'; 305 | } elseif ( file_exists( get_template_directory() . '/paid-memberships-pro/series/new_content.html' ) ) { 306 | $template_path = get_template_directory() . '/paid-memberships-pro/series/new_content.html'; 307 | } else { 308 | $template_path = plugins_url( 'email/new_content.html', dirname( __FILE__ ) ); 309 | } 310 | 311 | $email->email = $user->user_email; 312 | $email->subject = sprintf( esc_html__( 'New content is available at %s', 'pmpro-series' ), esc_html( get_option( 'blogname' ) ) ); 313 | $email->template = 'new_content'; 314 | 315 | $email->body .= file_get_contents( $template_path ); 316 | 317 | $email->data = array( 318 | 'name' => $user->display_name, 319 | 'sitename' => get_option( 'blogname' ), 320 | 'post_list' => $post_list, 321 | 'login_link' => wp_login_url(), 322 | ); 323 | 324 | if ( ! empty( $post->post_excerpt ) ) { 325 | $email->data['excerpt'] = '

' . esc_html__( 'An excerpt of the post is below.', 'pmpro-series' ) . '

' . esc_html( $post->post_excerpt ) . '

'; 326 | } else { 327 | $email->data['excerpt'] = ''; 328 | } 329 | 330 | $email->sendEmail(); 331 | } 332 | 333 | /** 334 | * [createCPT] Create the Custom Post Type for Series. 335 | * 336 | * @return [type] 337 | */ 338 | static function createCPT() { 339 | // don't want to do this when deactivating 340 | global $pmpros_deactivating; 341 | if ( ! empty( $pmpros_deactivating ) ) { 342 | return false; 343 | } 344 | 345 | $labels = apply_filters( 346 | 'pmpros_series_labels', 347 | array( 348 | 'name' => esc_html__( 'Series', 'pmpro-series' ), 349 | 'singular_name' => esc_html__( 'Series', 'pmpro-series' ), 350 | 'slug' => 'series', 351 | 'add_new' => esc_html__( 'New Series', 'pmpro-series' ), 352 | 'add_new_item' => esc_html__( 'New Series', 'pmpro-series' ), 353 | 'edit' => esc_html__( 'Edit Series', 'pmpro-series' ), 354 | 'edit_item' => esc_html__( 'Edit Series', 'pmpro-series' ), 355 | 'new_item' => esc_html__( 'Add New', 'pmpro-series' ), 356 | 'view' => esc_html__( 'View This Series', 'pmpro-series' ), 357 | 'view_item' => esc_html__( 'View This Series', 'pmpro-series' ), 358 | 'search_items' => esc_html__( 'Search Series', 'pmpro-series' ), 359 | 'not_found' => esc_html__( 'No Series Found', 'pmpro-series' ), 360 | 'not_found_in_trash' => esc_html__( 'No Series Found In Trash', 'pmpro-series' ), 361 | ) 362 | ); 363 | 364 | register_post_type( 365 | 'pmpro_series', 366 | apply_filters( 367 | 'pmpros_series_registration', 368 | array( 369 | 'labels' => $labels, 370 | 'public' => true, 371 | 'menu_icon' => 'dashicons-clock', 372 | 'show_ui' => true, 373 | 'show_in_menu' => true, 374 | 'show_in_rest' => true, 375 | 'publicly_queryable' => true, 376 | 'hierarchical' => true, 377 | 'supports' => array( 'title', 'editor', 'thumbnail', 'custom-fields', 'author' ), 378 | 'can_export' => true, 379 | 'show_in_nav_menus' => true, 380 | 'rewrite' => array( 381 | 'slug' => 'series', 382 | 'with_front' => false, 383 | ), 384 | 'has_archive' => 'series', 385 | ) 386 | ) 387 | ); 388 | } 389 | 390 | /** 391 | * [checkForMetaBoxes] Meta boxes 392 | * 393 | * @return [type] 394 | */ 395 | static function checkForMetaBoxes() { 396 | // add meta boxes 397 | if ( is_admin() ) { 398 | add_action( 'admin_menu', array( 'PMProSeries', 'defineMetaBoxes' ) ); 399 | } 400 | } 401 | 402 | /** 403 | * [defineMetaBoxes] Meta boxes 404 | * 405 | * @return [type] 406 | */ 407 | static function defineMetaBoxes() { 408 | // Add "Require Membership" meta box if Paid Memberships Pro is active. 409 | if ( defined( 'PMPRO_VERSION' ) ) { 410 | add_meta_box( 'pmpro_page_meta', esc_html__( 'Require Membership', 'pmpro-series' ), 'pmpro_page_meta', 'pmpro_series', 'side' ); 411 | } 412 | // series meta box 413 | add_meta_box( 'pmpro_series_meta', esc_html__( 'Manage Series', 'pmpro-series'), array( 'PMProSeries', 'seriesMetaBox' ), 'pmpro_series', 'normal' ); 414 | } 415 | 416 | /** 417 | * [seriesMetaBox] This is the actual series meta box. 418 | * 419 | * @return [type] 420 | */ 421 | static function seriesMetaBox() { 422 | global $post; 423 | $series = new PMProSeries( $post->ID ); 424 | ?> 425 |
426 | getPostListForMetaBox(); ?> 427 |
428 | getPosts( false, 'publish' ); 439 | if ( ! empty( $posts ) ) { 440 | ob_start(); 441 | ?> 442 | 482 | addPost( $pmpros_post, $delay ); 525 | } 526 | 527 | // removing a post 528 | if ( ! empty( $remove ) ) { 529 | $this->removePost( $remove ); 530 | } 531 | 532 | // show posts 533 | $this->getPosts(); 534 | 535 | ?> 536 | 537 | 538 | error ) ) { ?> 539 |

error; ?>

540 | 541 |

542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | posts ) ) { 554 | ?> 555 | posts as $post ) { 558 | ?> 559 | 560 | 561 | 562 | 563 | 567 | 568 | 573 | 574 |
.id ); ?>delay; ?> 564 | 565 | 566 |
575 |

576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 608 | 609 | 610 | 611 | 612 |
587 | 607 |
613 | get_col( "SELECT membership_id FROM $wpdb->pmpro_memberships_pages WHERE page_id = '" . $this->id . "'" ); 634 | 635 | // If series is not restricted, we just need to check pmpro_getMemberDays() without passing a level. 636 | if ( empty( $series_levels ) ) { 637 | return pmpro_getMemberDays( $user_id ); 638 | } 639 | 640 | // If series is restricted, we need to find the maximum number of days that the user has been a member of any of the restricted levels. 641 | $member_days = 0; 642 | foreach ( $series_levels as $level_id ) { 643 | $days = pmpro_getMemberDays( $user_id, $level_id ); 644 | if ( $days > $member_days ) { 645 | $member_days = $days; 646 | } 647 | } 648 | 649 | return $member_days; 650 | } 651 | } 652 | -------------------------------------------------------------------------------- /css/pmpro-series-admin.css: -------------------------------------------------------------------------------- 1 | #newmeta > tbody > tr { 2 | vertical-align: middle; 3 | } 4 | #postcustomstuff table input { 5 | margin: 2px; 6 | } 7 | 8 | #newmeta .select2 { 9 | width: 100%; 10 | } -------------------------------------------------------------------------------- /css/pmpro_series.css: -------------------------------------------------------------------------------- 1 | 2 | .pmpro { 3 | 4 | .pmpro_series_list { 5 | border-top: 1px solid var(--pmpro--color--border--variation); 6 | margin-top: var(--pmpro--base--spacing--medium); 7 | } 8 | 9 | .pmpro_series_list .pmpro_list_item { 10 | border-bottom: 1px solid var(--pmpro--color--border--variation); 11 | display: grid; 12 | grid-gap: var(--pmpro--base--spacing--small); 13 | grid-template-columns: 4fr 2fr; 14 | margin: 0; 15 | padding: var(--pmpro--base--spacing--small) 0; 16 | } 17 | 18 | .pmpro_series_list .pmpro_list_item:last-child { 19 | border-bottom: none; 20 | padding-bottom: 0; 21 | } 22 | 23 | .pmpro_series_list .pmpro_list_item > * { 24 | align-self: center; 25 | } 26 | 27 | .pmpro_series_list li.pmpro_series_item-li-unavailable { 28 | font-style: italic; 29 | opacity: 0.65; 30 | } 31 | 32 | .pmpro_series_list .pmpro_btn { 33 | display: block; 34 | } 35 | 36 | .pmpro_series_item-unavailable { 37 | text-align: right; 38 | } 39 | 40 | } 41 | 42 | @media screen and (max-width: 767px) { 43 | 44 | .pmpro { 45 | 46 | .pmpro_series_list .pmpro_list_item { 47 | grid-template-columns: 1fr; 48 | } 49 | 50 | .pmpro_series_item-unavailable { 51 | text-align: left; 52 | } 53 | 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /css/select2.css: -------------------------------------------------------------------------------- 1 | .select2-container { 2 | box-sizing: border-box; 3 | display: inline-block; 4 | margin: 0; 5 | position: relative; 6 | vertical-align: middle; } 7 | .select2-container .select2-selection--single { 8 | box-sizing: border-box; 9 | cursor: pointer; 10 | display: block; 11 | height: 28px; 12 | user-select: none; 13 | -webkit-user-select: none; } 14 | .select2-container .select2-selection--single .select2-selection__rendered { 15 | display: block; 16 | padding-left: 8px; 17 | padding-right: 20px; 18 | overflow: hidden; 19 | text-overflow: ellipsis; 20 | white-space: nowrap; } 21 | .select2-container .select2-selection--single .select2-selection__clear { 22 | position: relative; } 23 | .select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered { 24 | padding-right: 8px; 25 | padding-left: 20px; } 26 | .select2-container .select2-selection--multiple { 27 | box-sizing: border-box; 28 | cursor: pointer; 29 | display: block; 30 | min-height: 32px; 31 | user-select: none; 32 | -webkit-user-select: none; } 33 | .select2-container .select2-selection--multiple .select2-selection__rendered { 34 | display: inline-block; 35 | overflow: hidden; 36 | padding-left: 8px; 37 | text-overflow: ellipsis; 38 | white-space: nowrap; } 39 | .select2-container .select2-search--inline { 40 | float: left; } 41 | .select2-container .select2-search--inline .select2-search__field { 42 | box-sizing: border-box; 43 | border: none; 44 | font-size: 100%; 45 | margin-top: 5px; 46 | padding: 0; } 47 | .select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button { 48 | -webkit-appearance: none; } 49 | 50 | .select2-dropdown { 51 | background-color: white; 52 | border: 1px solid #aaa; 53 | border-radius: 4px; 54 | box-sizing: border-box; 55 | display: block; 56 | position: absolute; 57 | left: -100000px; 58 | width: 100%; 59 | z-index: 1051; } 60 | 61 | .select2-results { 62 | display: block; } 63 | 64 | .select2-results__options { 65 | list-style: none; 66 | margin: 0; 67 | padding: 0; } 68 | 69 | .select2-results__option { 70 | padding: 6px; 71 | user-select: none; 72 | -webkit-user-select: none; } 73 | .select2-results__option[aria-selected] { 74 | cursor: pointer; } 75 | 76 | .select2-container--open .select2-dropdown { 77 | left: 0; } 78 | 79 | .select2-container--open .select2-dropdown--above { 80 | border-bottom: none; 81 | border-bottom-left-radius: 0; 82 | border-bottom-right-radius: 0; } 83 | 84 | .select2-container--open .select2-dropdown--below { 85 | border-top: none; 86 | border-top-left-radius: 0; 87 | border-top-right-radius: 0; } 88 | 89 | .select2-search--dropdown { 90 | display: block; 91 | padding: 4px; } 92 | .select2-search--dropdown .select2-search__field { 93 | padding: 4px; 94 | width: 100%; 95 | box-sizing: border-box; } 96 | .select2-search--dropdown .select2-search__field::-webkit-search-cancel-button { 97 | -webkit-appearance: none; } 98 | .select2-search--dropdown.select2-search--hide { 99 | display: none; } 100 | 101 | .select2-close-mask { 102 | border: 0; 103 | margin: 0; 104 | padding: 0; 105 | display: block; 106 | position: fixed; 107 | left: 0; 108 | top: 0; 109 | min-height: 100%; 110 | min-width: 100%; 111 | height: auto; 112 | width: auto; 113 | opacity: 0; 114 | z-index: 99; 115 | background-color: #fff; 116 | filter: alpha(opacity=0); } 117 | 118 | .select2-hidden-accessible { 119 | border: 0 !important; 120 | clip: rect(0 0 0 0) !important; 121 | -webkit-clip-path: inset(50%) !important; 122 | clip-path: inset(50%) !important; 123 | height: 1px !important; 124 | overflow: hidden !important; 125 | padding: 0 !important; 126 | position: absolute !important; 127 | width: 1px !important; 128 | white-space: nowrap !important; } 129 | 130 | .select2-container--default .select2-selection--single { 131 | background-color: #fff; 132 | border: 1px solid #aaa; 133 | border-radius: 4px; } 134 | .select2-container--default .select2-selection--single .select2-selection__rendered { 135 | color: #444; 136 | line-height: 28px; } 137 | .select2-container--default .select2-selection--single .select2-selection__clear { 138 | cursor: pointer; 139 | float: right; 140 | font-weight: bold; } 141 | .select2-container--default .select2-selection--single .select2-selection__placeholder { 142 | color: #999; } 143 | .select2-container--default .select2-selection--single .select2-selection__arrow { 144 | height: 26px; 145 | position: absolute; 146 | top: 1px; 147 | right: 1px; 148 | width: 20px; } 149 | .select2-container--default .select2-selection--single .select2-selection__arrow b { 150 | border-color: #888 transparent transparent transparent; 151 | border-style: solid; 152 | border-width: 5px 4px 0 4px; 153 | height: 0; 154 | left: 50%; 155 | margin-left: -4px; 156 | margin-top: -2px; 157 | position: absolute; 158 | top: 50%; 159 | width: 0; } 160 | 161 | .select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear { 162 | float: left; } 163 | 164 | .select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow { 165 | left: 1px; 166 | right: auto; } 167 | 168 | .select2-container--default.select2-container--disabled .select2-selection--single { 169 | background-color: #eee; 170 | cursor: default; } 171 | .select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear { 172 | display: none; } 173 | 174 | .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b { 175 | border-color: transparent transparent #888 transparent; 176 | border-width: 0 4px 5px 4px; } 177 | 178 | .select2-container--default .select2-selection--multiple { 179 | background-color: white; 180 | border: 1px solid #aaa; 181 | border-radius: 4px; 182 | cursor: text; } 183 | .select2-container--default .select2-selection--multiple .select2-selection__rendered { 184 | box-sizing: border-box; 185 | list-style: none; 186 | margin: 0; 187 | padding: 0 5px; 188 | width: 100%; } 189 | .select2-container--default .select2-selection--multiple .select2-selection__rendered li { 190 | list-style: none; } 191 | .select2-container--default .select2-selection--multiple .select2-selection__clear { 192 | cursor: pointer; 193 | float: right; 194 | font-weight: bold; 195 | margin-top: 5px; 196 | margin-right: 10px; 197 | padding: 1px; } 198 | .select2-container--default .select2-selection--multiple .select2-selection__choice { 199 | background-color: #e4e4e4; 200 | border: 1px solid #aaa; 201 | border-radius: 4px; 202 | cursor: default; 203 | float: left; 204 | margin-right: 5px; 205 | margin-top: 5px; 206 | padding: 0 5px; } 207 | .select2-container--default .select2-selection--multiple .select2-selection__choice__remove { 208 | color: #999; 209 | cursor: pointer; 210 | display: inline-block; 211 | font-weight: bold; 212 | margin-right: 2px; } 213 | .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover { 214 | color: #333; } 215 | 216 | .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline { 217 | float: right; } 218 | 219 | .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice { 220 | margin-left: 5px; 221 | margin-right: auto; } 222 | 223 | .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove { 224 | margin-left: 2px; 225 | margin-right: auto; } 226 | 227 | .select2-container--default.select2-container--focus .select2-selection--multiple { 228 | border: solid black 1px; 229 | outline: 0; } 230 | 231 | .select2-container--default.select2-container--disabled .select2-selection--multiple { 232 | background-color: #eee; 233 | cursor: default; } 234 | 235 | .select2-container--default.select2-container--disabled .select2-selection__choice__remove { 236 | display: none; } 237 | 238 | .select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple { 239 | border-top-left-radius: 0; 240 | border-top-right-radius: 0; } 241 | 242 | .select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple { 243 | border-bottom-left-radius: 0; 244 | border-bottom-right-radius: 0; } 245 | 246 | .select2-container--default .select2-search--dropdown .select2-search__field { 247 | border: 1px solid #aaa; } 248 | 249 | .select2-container--default .select2-search--inline .select2-search__field { 250 | background: transparent; 251 | border: none; 252 | outline: 0; 253 | box-shadow: none; 254 | -webkit-appearance: textfield; } 255 | 256 | .select2-container--default .select2-results > .select2-results__options { 257 | max-height: 200px; 258 | overflow-y: auto; } 259 | 260 | .select2-container--default .select2-results__option[role=group] { 261 | padding: 0; } 262 | 263 | .select2-container--default .select2-results__option[aria-disabled=true] { 264 | color: #999; } 265 | 266 | .select2-container--default .select2-results__option[aria-selected=true] { 267 | background-color: #ddd; } 268 | 269 | .select2-container--default .select2-results__option .select2-results__option { 270 | padding-left: 1em; } 271 | .select2-container--default .select2-results__option .select2-results__option .select2-results__group { 272 | padding-left: 0; } 273 | .select2-container--default .select2-results__option .select2-results__option .select2-results__option { 274 | margin-left: -1em; 275 | padding-left: 2em; } 276 | .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option { 277 | margin-left: -2em; 278 | padding-left: 3em; } 279 | .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { 280 | margin-left: -3em; 281 | padding-left: 4em; } 282 | .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { 283 | margin-left: -4em; 284 | padding-left: 5em; } 285 | .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { 286 | margin-left: -5em; 287 | padding-left: 6em; } 288 | 289 | .select2-container--default .select2-results__option--highlighted[aria-selected] { 290 | background-color: #5897fb; 291 | color: white; } 292 | 293 | .select2-container--default .select2-results__group { 294 | cursor: default; 295 | display: block; 296 | padding: 6px; } 297 | 298 | .select2-container--classic .select2-selection--single { 299 | background-color: #f7f7f7; 300 | border: 1px solid #aaa; 301 | border-radius: 4px; 302 | outline: 0; 303 | background-image: -webkit-linear-gradient(top, white 50%, #eeeeee 100%); 304 | background-image: -o-linear-gradient(top, white 50%, #eeeeee 100%); 305 | background-image: linear-gradient(to bottom, white 50%, #eeeeee 100%); 306 | background-repeat: repeat-x; 307 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); } 308 | .select2-container--classic .select2-selection--single:focus { 309 | border: 1px solid #5897fb; } 310 | .select2-container--classic .select2-selection--single .select2-selection__rendered { 311 | color: #444; 312 | line-height: 28px; } 313 | .select2-container--classic .select2-selection--single .select2-selection__clear { 314 | cursor: pointer; 315 | float: right; 316 | font-weight: bold; 317 | margin-right: 10px; } 318 | .select2-container--classic .select2-selection--single .select2-selection__placeholder { 319 | color: #999; } 320 | .select2-container--classic .select2-selection--single .select2-selection__arrow { 321 | background-color: #ddd; 322 | border: none; 323 | border-left: 1px solid #aaa; 324 | border-top-right-radius: 4px; 325 | border-bottom-right-radius: 4px; 326 | height: 26px; 327 | position: absolute; 328 | top: 1px; 329 | right: 1px; 330 | width: 20px; 331 | background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%); 332 | background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%); 333 | background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%); 334 | background-repeat: repeat-x; 335 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0); } 336 | .select2-container--classic .select2-selection--single .select2-selection__arrow b { 337 | border-color: #888 transparent transparent transparent; 338 | border-style: solid; 339 | border-width: 5px 4px 0 4px; 340 | height: 0; 341 | left: 50%; 342 | margin-left: -4px; 343 | margin-top: -2px; 344 | position: absolute; 345 | top: 50%; 346 | width: 0; } 347 | 348 | .select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear { 349 | float: left; } 350 | 351 | .select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow { 352 | border: none; 353 | border-right: 1px solid #aaa; 354 | border-radius: 0; 355 | border-top-left-radius: 4px; 356 | border-bottom-left-radius: 4px; 357 | left: 1px; 358 | right: auto; } 359 | 360 | .select2-container--classic.select2-container--open .select2-selection--single { 361 | border: 1px solid #5897fb; } 362 | .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow { 363 | background: transparent; 364 | border: none; } 365 | .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b { 366 | border-color: transparent transparent #888 transparent; 367 | border-width: 0 4px 5px 4px; } 368 | 369 | .select2-container--classic.select2-container--open.select2-container--above .select2-selection--single { 370 | border-top: none; 371 | border-top-left-radius: 0; 372 | border-top-right-radius: 0; 373 | background-image: -webkit-linear-gradient(top, white 0%, #eeeeee 50%); 374 | background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%); 375 | background-image: linear-gradient(to bottom, white 0%, #eeeeee 50%); 376 | background-repeat: repeat-x; 377 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); } 378 | 379 | .select2-container--classic.select2-container--open.select2-container--below .select2-selection--single { 380 | border-bottom: none; 381 | border-bottom-left-radius: 0; 382 | border-bottom-right-radius: 0; 383 | background-image: -webkit-linear-gradient(top, #eeeeee 50%, white 100%); 384 | background-image: -o-linear-gradient(top, #eeeeee 50%, white 100%); 385 | background-image: linear-gradient(to bottom, #eeeeee 50%, white 100%); 386 | background-repeat: repeat-x; 387 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0); } 388 | 389 | .select2-container--classic .select2-selection--multiple { 390 | background-color: white; 391 | border: 1px solid #aaa; 392 | border-radius: 4px; 393 | cursor: text; 394 | outline: 0; } 395 | .select2-container--classic .select2-selection--multiple:focus { 396 | border: 1px solid #5897fb; } 397 | .select2-container--classic .select2-selection--multiple .select2-selection__rendered { 398 | list-style: none; 399 | margin: 0; 400 | padding: 0 5px; } 401 | .select2-container--classic .select2-selection--multiple .select2-selection__clear { 402 | display: none; } 403 | .select2-container--classic .select2-selection--multiple .select2-selection__choice { 404 | background-color: #e4e4e4; 405 | border: 1px solid #aaa; 406 | border-radius: 4px; 407 | cursor: default; 408 | float: left; 409 | margin-right: 5px; 410 | margin-top: 5px; 411 | padding: 0 5px; } 412 | .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove { 413 | color: #888; 414 | cursor: pointer; 415 | display: inline-block; 416 | font-weight: bold; 417 | margin-right: 2px; } 418 | .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover { 419 | color: #555; } 420 | 421 | .select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice { 422 | float: right; 423 | margin-left: 5px; 424 | margin-right: auto; } 425 | 426 | .select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove { 427 | margin-left: 2px; 428 | margin-right: auto; } 429 | 430 | .select2-container--classic.select2-container--open .select2-selection--multiple { 431 | border: 1px solid #5897fb; } 432 | 433 | .select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple { 434 | border-top: none; 435 | border-top-left-radius: 0; 436 | border-top-right-radius: 0; } 437 | 438 | .select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple { 439 | border-bottom: none; 440 | border-bottom-left-radius: 0; 441 | border-bottom-right-radius: 0; } 442 | 443 | .select2-container--classic .select2-search--dropdown .select2-search__field { 444 | border: 1px solid #aaa; 445 | outline: 0; } 446 | 447 | .select2-container--classic .select2-search--inline .select2-search__field { 448 | outline: 0; 449 | box-shadow: none; } 450 | 451 | .select2-container--classic .select2-dropdown { 452 | background-color: white; 453 | border: 1px solid transparent; } 454 | 455 | .select2-container--classic .select2-dropdown--above { 456 | border-bottom: none; } 457 | 458 | .select2-container--classic .select2-dropdown--below { 459 | border-top: none; } 460 | 461 | .select2-container--classic .select2-results > .select2-results__options { 462 | max-height: 200px; 463 | overflow-y: auto; } 464 | 465 | .select2-container--classic .select2-results__option[role=group] { 466 | padding: 0; } 467 | 468 | .select2-container--classic .select2-results__option[aria-disabled=true] { 469 | color: grey; } 470 | 471 | .select2-container--classic .select2-results__option--highlighted[aria-selected] { 472 | background-color: #3875d7; 473 | color: white; } 474 | 475 | .select2-container--classic .select2-results__group { 476 | cursor: default; 477 | display: block; 478 | padding: 6px; } 479 | 480 | .select2-container--classic.select2-container--open .select2-dropdown { 481 | border-color: #5897fb; } 482 | -------------------------------------------------------------------------------- /email/new_content.html: -------------------------------------------------------------------------------- 1 |

New content is available at !!sitename!!.

2 | 3 | !!post_list!! -------------------------------------------------------------------------------- /js/pmpro-series.js: -------------------------------------------------------------------------------- 1 | 2 | function pmpros_editPost(post_id, delay){ 3 | jQuery('#pmpros_post').val(post_id).trigger("change"); 4 | jQuery('#pmpros_delay').val(delay); 5 | jQuery('#pmpros_save').html('Save'); 6 | location.href = "#pmpros_edit_post"; 7 | } 8 | 9 | function pmpros_removePost(post_id) { 10 | var seriesid = jQuery('#post_ID').val(); 11 | jQuery.ajax({ 12 | url: ajaxurl, 13 | type:'GET', 14 | timeout:2000, 15 | dataType: 'html', 16 | data: "pmpros_add_post=1&pmpros_series=" + seriesid + "&pmpros_remove="+post_id, 17 | error: function(xml){ 18 | alert('Error removing series post [1]'); 19 | //enable save button 20 | jQuery('#pmpros_save').removeAttr('disabled'); 21 | }, 22 | success: function(responseHTML){ 23 | if (responseHTML == 'error'){ 24 | alert('Error removing series post [2]'); 25 | //enable save button 26 | jQuery('#pmpros_save').removeAttr('disabled'); 27 | }else{ 28 | jQuery('#pmpros_series_posts').html(responseHTML); 29 | pmpros_Setup(); 30 | } 31 | } 32 | }); 33 | } 34 | 35 | function pmpros_updatePost() { 36 | var seriesid = jQuery('#post_ID').val(); 37 | jQuery.ajax({ 38 | url: ajaxurl, 39 | type: 'GET', 40 | dataType: 'html', 41 | data: "pmpros_add_post=1&pmpros_series=" + seriesid + "&pmpros_post=" + jQuery('#pmpros_post').val() + '&pmpros_delay=' + jQuery('#pmpros_delay').val(), 42 | error: function(xml){ 43 | alert('Error saving series post [1]'); 44 | //enable save button 45 | jQuery('#pmpros_save').html('Save'); 46 | }, 47 | success: function(responseHTML){ 48 | if (responseHTML == 'error'){ 49 | alert('Error saving series post [2]'); 50 | //enable save button 51 | jQuery('#pmpros_save').html('Save'); 52 | }else{ 53 | jQuery('#pmpros_series_posts').html(responseHTML); 54 | pmpros_Setup(); 55 | } 56 | } 57 | }); 58 | } 59 | 60 | function pmpros_Setup() { 61 | jQuery('#pmpros_post').select2({width: 'elements'}); 62 | 63 | jQuery('#pmpros_save').click(function() { 64 | if(jQuery(this).html() != 'Saving...'){ 65 | pmpros_updatePost(); 66 | } 67 | }); 68 | } 69 | 70 | jQuery(document).ready(function(jQuery) { 71 | pmpros_Setup(); 72 | }); 73 | -------------------------------------------------------------------------------- /languages/pmpro-series-en_EN.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strangerstudios/pmpro-series/a28e2eaa1b9fbda8ce5d97830e6c60b51fbc929c/languages/pmpro-series-en_EN.mo -------------------------------------------------------------------------------- /languages/pmpro-series-en_EN.po: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 Paid Memberships Pro 2 | # This file is distributed under the same license as the Paid Memberships Pro - Series plugin. 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: Paid Memberships Pro - Series 0.7\n" 6 | "Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" 7 | "Last-Translator: Paid Memberships Pro \n" 8 | "Language-Team: Paid Memberships Pro \n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "POT-Creation-Date: 2024-07-19T18:33:55+00:00\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "X-Generator: WP-CLI 2.10.0\n" 15 | "X-Domain: pmpro-series\n" 16 | 17 | #: classes/class.pmproseries.php:30 18 | #: classes/class.pmproseries.php:43 19 | #: classes/class.pmproseries.php:72 20 | msgid "Please enter a value for post and delay." 21 | msgstr "Please enter a value for post and delay." 22 | 23 | #: classes/class.pmproseries.php:38 24 | #: classes/class.pmproseries.php:50 25 | #: classes/class.pmproseries.php:79 26 | msgid "A post with that id does not exist." 27 | msgstr "A post with that id does not exist." 28 | 29 | #: classes/class.pmproseries.php:178 30 | #: classes/class.pmproseries.php:269 31 | #: classes/class.pmproseries.php:283 32 | #: classes/class.pmproseries.php:298 33 | #: classes/class.pmproseries.php:312 34 | msgid "New content is available at %s" 35 | msgstr "New content is available at %s" 36 | 37 | #: classes/class.pmproseries.php:189 38 | #: classes/class.pmproseries.php:296 39 | #: classes/class.pmproseries.php:325 40 | msgid "An excerpt of the post is below." 41 | msgstr "An excerpt of the post is below." 42 | 43 | #: classes/class.pmproseries.php:209 44 | #: classes/class.pmproseries.php:319 45 | #: classes/class.pmproseries.php:320 46 | #: classes/class.pmproseries.php:348 47 | #: classes/class.pmproseries.php:349 48 | msgid "Series" 49 | msgstr "Series" 50 | 51 | #: classes/class.pmproseries.php:210 52 | msgid "Serie" 53 | msgstr "Série" 54 | 55 | #: classes/class.pmproseries.php:212 56 | #: classes/class.pmproseries.php:213 57 | #: classes/class.pmproseries.php:322 58 | #: classes/class.pmproseries.php:323 59 | #: classes/class.pmproseries.php:351 60 | #: classes/class.pmproseries.php:352 61 | msgid "New Series" 62 | msgstr "New Series" 63 | 64 | #: classes/class.pmproseries.php:214 65 | #: classes/class.pmproseries.php:215 66 | #: classes/class.pmproseries.php:324 67 | #: classes/class.pmproseries.php:325 68 | #: classes/class.pmproseries.php:353 69 | #: classes/class.pmproseries.php:354 70 | msgid "Edit Series" 71 | msgstr "Edit Series" 72 | 73 | #: classes/class.pmproseries.php:216 74 | #: classes/class.pmproseries.php:326 75 | #: classes/class.pmproseries.php:355 76 | msgid "Add New" 77 | msgstr "Add New" 78 | 79 | #: classes/class.pmproseries.php:217 80 | #: classes/class.pmproseries.php:218 81 | #: classes/class.pmproseries.php:327 82 | #: classes/class.pmproseries.php:328 83 | #: classes/class.pmproseries.php:356 84 | #: classes/class.pmproseries.php:357 85 | msgid "View This Series" 86 | msgstr "View This Series" 87 | 88 | #: classes/class.pmproseries.php:219 89 | #: classes/class.pmproseries.php:329 90 | #: classes/class.pmproseries.php:358 91 | msgid "Search Series" 92 | msgstr "Search Series" 93 | 94 | #: classes/class.pmproseries.php:220 95 | #: classes/class.pmproseries.php:330 96 | #: classes/class.pmproseries.php:359 97 | msgid "No Series Found" 98 | msgstr "No Series Found" 99 | 100 | #: classes/class.pmproseries.php:221 101 | #: classes/class.pmproseries.php:331 102 | #: classes/class.pmproseries.php:360 103 | msgid "No Series Found In Trash" 104 | msgstr "No Series Found In Trash" 105 | 106 | #: classes/class.pmproseries.php:257 107 | #: classes/class.pmproseries.php:381 108 | #: classes/class.pmproseries.php:410 109 | msgid "Require Membership" 110 | msgstr "Require Membership" 111 | 112 | #: classes/class.pmproseries.php:260 113 | #: classes/class.pmproseries.php:512 114 | #: classes/class.pmproseries.php:541 115 | msgid "Posts in this Series" 116 | msgstr "Posts in this Series" 117 | 118 | #: classes/class.pmproseries.php:292 119 | #: classes/class.pmproseries.php:438 120 | #: classes/class.pmproseries.php:467 121 | msgid "Available Now" 122 | msgstr "Available Now" 123 | 124 | #: classes/class.pmproseries.php:295 125 | msgid "available on day %s" 126 | msgstr "available on day %s" 127 | 128 | #: classes/class.pmproseries.php:354 129 | #: classes/class.pmproseries.php:515 130 | #: classes/class.pmproseries.php:544 131 | msgid "Order" 132 | msgstr "Order" 133 | 134 | #: classes/class.pmproseries.php:355 135 | #: classes/class.pmproseries.php:516 136 | #: classes/class.pmproseries.php:545 137 | msgid "Title" 138 | msgstr "Title" 139 | 140 | #: classes/class.pmproseries.php:356 141 | #: classes/class.pmproseries.php:399 142 | #: classes/class.pmproseries.php:517 143 | #: classes/class.pmproseries.php:551 144 | #: classes/class.pmproseries.php:546 145 | #: classes/class.pmproseries.php:580 146 | msgid "Delay (# of days)" 147 | msgstr "Delay (# of days)" 148 | 149 | #: classes/class.pmproseries.php:379 150 | msgid "Edit" 151 | msgstr "Edit" 152 | 153 | #: classes/class.pmproseries.php:382 154 | msgid "Remove" 155 | msgstr "Remove" 156 | 157 | #: classes/class.pmproseries.php:394 158 | msgid "Add/Edit Posts:" 159 | msgstr "Add/Edit Posts:" 160 | 161 | #: classes/class.pmproseries.php:398 162 | #: classes/class.pmproseries.php:550 163 | #: classes/class.pmproseries.php:579 164 | msgid "Post/Page" 165 | msgstr "Post/Page" 166 | 167 | #: classes/class.pmproseries.php:427 168 | #: classes/class.pmproseries.php:580 169 | #: classes/class.pmproseries.php:609 170 | msgid "Add to Series" 171 | msgstr "Add to Series" 172 | 173 | #: pmpro-series.php:117 174 | #: pmpro-series.php:119 175 | #: pmpro-series.php:128 176 | msgid "You are on day %d of your membership." 177 | msgstr "Please enter a value for post and delay." 178 | 179 | #: pmpro-series.php:225 180 | msgid "This content is part of the %s series. You will gain access on day %s of your membership." 181 | msgstr "This content is part of the %s series. You will gain access on day %s of your membership." 182 | 183 | #: pmpro-series.php:232 184 | #: pmpro-series.php:348 185 | #: pmpro-series.php:400 186 | msgid "This content is part of the %s series." 187 | msgstr "This content is part of the %s series." 188 | 189 | #: pmpro-series.php:236 190 | msgid "This content is part of the following series: " 191 | msgstr "This content is part of the following series: " 192 | 193 | #. Plugin Name of the plugin/theme 194 | msgid "PMPro Series" 195 | msgstr "PMPro Series" 196 | 197 | #. Plugin URI of the plugin/theme 198 | msgid "http://www.paidmembershipspro.com/pmpro-series/" 199 | msgstr "http://www.paidmembershipspro.com/pmpro-series/" 200 | 201 | #. Description of the plugin/theme 202 | #. Description of the plugin 203 | #: pmpro-series.php 204 | msgid "Offer serialized (drip feed) content to your PMPro members." 205 | msgstr "Offer serialized (drip feed) content to your PMPro members." 206 | 207 | #. Author of the plugin/theme 208 | msgid "Stranger Studios" 209 | msgstr "Stranger Studios" 210 | 211 | #. Author URI of the plugin/theme 212 | msgid "http://www.strangerstudios.com" 213 | msgstr "http://www.strangerstudios.com" 214 | 215 | #, fuzzy 216 | #~ msgid "This content is part of the %s series." 217 | #~ msgstr "This content is part of the following series: " 218 | 219 | #~ msgid "Saving..." 220 | #~ msgstr "Saving..." 221 | 222 | #~ msgid "Error saving series post [1]" 223 | #~ msgstr "Error saving series post [1]" 224 | 225 | #: pmpro-series.php:61 226 | #~ msgid "Save" 227 | #~ msgstr "Save" 228 | 229 | #~ msgid "Error saving series post [2]" 230 | #~ msgstr "Error saving series post [2]" 231 | 232 | #~ msgid "Error removing series post [1]" 233 | #~ msgstr "Error removing series post [1]" 234 | 235 | #~ msgid "Error removing series post [2]" 236 | #~ msgstr "Error removing series post [2]" 237 | 238 | #. Plugin Name of the plugin 239 | #: pmpro-series.php 240 | msgid "Paid Memberships Pro - Series Add On" 241 | msgstr "" 242 | 243 | #. Plugin URI of the plugin 244 | #: pmpro-series.php 245 | msgid "https://www.paidmembershipspro.com/add-ons/pmpro-series-for-drip-feed-content/" 246 | msgstr "" 247 | 248 | #. Author of the plugin 249 | #: pmpro-series.php 250 | msgid "Paid Memberships Pro" 251 | msgstr "" 252 | 253 | #. Author URI of the plugin 254 | #: pmpro-series.php 255 | msgid "https://www.paidmembershipspro.com" 256 | msgstr "" 257 | 258 | #: classes/class.pmproseries.php:384 259 | #: classes/class.pmproseries.php:413 260 | msgid "Manage Series" 261 | msgstr "" 262 | 263 | #. translators: %s: series post available date 264 | #: classes/class.pmproseries.php:444 265 | msgid "available on %s." 266 | msgstr "" 267 | 268 | #: classes/class.pmproseries.php:518 269 | #: classes/class.pmproseries.php:547 270 | msgid "Actions" 271 | msgstr "" 272 | 273 | #: classes/class.pmproseries.php:535 274 | #: classes/class.pmproseries.php:564 275 | msgid "edit" 276 | msgstr "" 277 | 278 | #: classes/class.pmproseries.php:536 279 | #: classes/class.pmproseries.php:565 280 | msgid "remove" 281 | msgstr "" 282 | 283 | #: classes/class.pmproseries.php:546 284 | #: classes/class.pmproseries.php:575 285 | msgid "Add/Edit Posts" 286 | msgstr "" 287 | 288 | #: pmpro-series.php:62 289 | msgid "Saving" 290 | msgstr "" 291 | 292 | #: pmpro-series.php:63 293 | #: pmpro-series.php:64 294 | msgid "Error saving series post" 295 | msgstr "" 296 | 297 | #: pmpro-series.php:65 298 | #: pmpro-series.php:66 299 | msgid "Error removing series post" 300 | msgstr "" 301 | 302 | #: pmpro-series.php:117 303 | #: pmpro-series.php:124 304 | msgid "All posts in this series are now available." 305 | msgstr "" 306 | 307 | #. translators: 1: series link, 2: date 308 | #: pmpro-series.php:341 309 | #: pmpro-series.php:393 310 | msgid "This content is part of the %1$s series. You will gain access on %2$s." 311 | msgstr "" 312 | 313 | #: pmpro-series.php:358 314 | #: pmpro-series.php:410 315 | msgid "This content is part of the following series: %s" 316 | msgstr "" 317 | 318 | #: pmpro-series.php:445 319 | #: pmpro-series.php:516 320 | msgid "New content is available at !!sitename!!" 321 | msgstr "" 322 | 323 | #: pmpro-series.php:446 324 | #: pmpro-series.php:517 325 | msgid "New Series Content Notification" 326 | msgstr "" 327 | 328 | #: pmpro-series.php:477 329 | #: pmpro-series.php:548 330 | msgid "Settings" 331 | msgstr "" 332 | 333 | #: pmpro-series.php:494 334 | #: pmpro-series.php:565 335 | msgid "View Documentation" 336 | msgstr "" 337 | 338 | #: pmpro-series.php:494 339 | #: pmpro-series.php:565 340 | msgid "Docs" 341 | msgstr "" 342 | 343 | #: pmpro-series.php:495 344 | #: pmpro-series.php:566 345 | msgid "Visit Customer Support Forum" 346 | msgstr "" 347 | 348 | #: pmpro-series.php:495 349 | #: pmpro-series.php:566 350 | msgid "Support" 351 | msgstr "" 352 | 353 | #. Plugin Name of the plugin 354 | #: pmpro-series.php 355 | msgid "Paid Memberships Pro - Series" 356 | msgstr "" 357 | 358 | #. Description of the plugin 359 | #: pmpro-series.php 360 | msgid "Drip feed content to your members over the course of their membership. Serializes content by # of days post-registration." 361 | msgstr "" 362 | 363 | #. translators: %s: series post available date 364 | #: classes/class.pmproseries.php:473 365 | msgid "available on %s" 366 | msgstr "" 367 | 368 | #: pmpro-series.php:119 369 | msgid "Content in This Series" 370 | msgstr "" 371 | 372 | #: pmpro-series.php:345 373 | msgid "Waiting For Access" 374 | msgstr "" 375 | 376 | #: pmpro-series.php:347 377 | msgid "Membership Required" 378 | msgstr "" 379 | -------------------------------------------------------------------------------- /languages/pmpro-series-fr_FR.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strangerstudios/pmpro-series/a28e2eaa1b9fbda8ce5d97830e6c60b51fbc929c/languages/pmpro-series-fr_FR.mo -------------------------------------------------------------------------------- /languages/pmpro-series-fr_FR.po: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 Paid Memberships Pro 2 | # This file is distributed under the same license as the Paid Memberships Pro - Series plugin. 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: Paid Memberships Pro - Series 0.7\n" 6 | "Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" 7 | "Last-Translator: Paid Memberships Pro \n" 8 | "Language-Team: Paid Memberships Pro \n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "POT-Creation-Date: 2024-07-19T18:33:56+00:00\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "X-Generator: WP-CLI 2.10.0\n" 15 | "X-Domain: pmpro-series\n" 16 | 17 | #: classes/class.pmproseries.php:30 18 | #: classes/class.pmproseries.php:43 19 | #: classes/class.pmproseries.php:72 20 | msgid "Please enter a value for post and delay." 21 | msgstr "Entrez une valeur pour l'article et le délai." 22 | 23 | #: classes/class.pmproseries.php:38 24 | #: classes/class.pmproseries.php:50 25 | #: classes/class.pmproseries.php:79 26 | msgid "A post with that id does not exist." 27 | msgstr "Cet ID ne correspond à aucun article existant." 28 | 29 | #: classes/class.pmproseries.php:178 30 | #: classes/class.pmproseries.php:269 31 | #: classes/class.pmproseries.php:283 32 | #: classes/class.pmproseries.php:298 33 | #: classes/class.pmproseries.php:312 34 | msgid "New content is available at %s" 35 | msgstr "Nouveau contenu disponible à %s" 36 | 37 | #: classes/class.pmproseries.php:189 38 | #: classes/class.pmproseries.php:296 39 | #: classes/class.pmproseries.php:325 40 | msgid "An excerpt of the post is below." 41 | msgstr "Ci-dessous un extrait de l'article." 42 | 43 | #: classes/class.pmproseries.php:209 44 | #: classes/class.pmproseries.php:319 45 | #: classes/class.pmproseries.php:320 46 | #: classes/class.pmproseries.php:348 47 | #: classes/class.pmproseries.php:349 48 | msgid "Series" 49 | msgstr "Séries" 50 | 51 | #: classes/class.pmproseries.php:210 52 | msgid "Serie" 53 | msgstr "Serie" 54 | 55 | #: classes/class.pmproseries.php:212 56 | #: classes/class.pmproseries.php:213 57 | #: classes/class.pmproseries.php:322 58 | #: classes/class.pmproseries.php:323 59 | #: classes/class.pmproseries.php:351 60 | #: classes/class.pmproseries.php:352 61 | msgid "New Series" 62 | msgstr "Nouvelle série" 63 | 64 | #: classes/class.pmproseries.php:214 65 | #: classes/class.pmproseries.php:215 66 | #: classes/class.pmproseries.php:324 67 | #: classes/class.pmproseries.php:325 68 | #: classes/class.pmproseries.php:353 69 | #: classes/class.pmproseries.php:354 70 | msgid "Edit Series" 71 | msgstr "Modifier la série" 72 | 73 | #: classes/class.pmproseries.php:216 74 | #: classes/class.pmproseries.php:326 75 | #: classes/class.pmproseries.php:355 76 | msgid "Add New" 77 | msgstr "Ajouter une série" 78 | 79 | #: classes/class.pmproseries.php:217 80 | #: classes/class.pmproseries.php:218 81 | #: classes/class.pmproseries.php:327 82 | #: classes/class.pmproseries.php:328 83 | #: classes/class.pmproseries.php:356 84 | #: classes/class.pmproseries.php:357 85 | msgid "View This Series" 86 | msgstr "Voir cette série" 87 | 88 | #: classes/class.pmproseries.php:219 89 | #: classes/class.pmproseries.php:329 90 | #: classes/class.pmproseries.php:358 91 | msgid "Search Series" 92 | msgstr "Rechercher les séries" 93 | 94 | #: classes/class.pmproseries.php:220 95 | #: classes/class.pmproseries.php:330 96 | #: classes/class.pmproseries.php:359 97 | msgid "No Series Found" 98 | msgstr "Aucune série trouvée" 99 | 100 | #: classes/class.pmproseries.php:221 101 | #: classes/class.pmproseries.php:331 102 | #: classes/class.pmproseries.php:360 103 | msgid "No Series Found In Trash" 104 | msgstr "Aucune série trouvée dans la corbeille" 105 | 106 | #: classes/class.pmproseries.php:257 107 | #: classes/class.pmproseries.php:381 108 | #: classes/class.pmproseries.php:410 109 | msgid "Require Membership" 110 | msgstr "Nécessite un abonnement" 111 | 112 | #: classes/class.pmproseries.php:260 113 | #: classes/class.pmproseries.php:512 114 | #: classes/class.pmproseries.php:541 115 | msgid "Posts in this Series" 116 | msgstr "Articles de cette série" 117 | 118 | #: classes/class.pmproseries.php:292 119 | #: classes/class.pmproseries.php:438 120 | #: classes/class.pmproseries.php:467 121 | msgid "Available Now" 122 | msgstr "Disponible" 123 | 124 | #: classes/class.pmproseries.php:295 125 | msgid "available on day %s" 126 | msgstr "Disponible le jour %s" 127 | 128 | #: classes/class.pmproseries.php:354 129 | #: classes/class.pmproseries.php:515 130 | #: classes/class.pmproseries.php:544 131 | msgid "Order" 132 | msgstr "Ordre" 133 | 134 | #: classes/class.pmproseries.php:355 135 | #: classes/class.pmproseries.php:516 136 | #: classes/class.pmproseries.php:545 137 | msgid "Title" 138 | msgstr "Titre" 139 | 140 | #: classes/class.pmproseries.php:356 141 | #: classes/class.pmproseries.php:399 142 | #: classes/class.pmproseries.php:517 143 | #: classes/class.pmproseries.php:551 144 | #: classes/class.pmproseries.php:546 145 | #: classes/class.pmproseries.php:580 146 | msgid "Delay (# of days)" 147 | msgstr "Délai (nombre de jours)" 148 | 149 | #: classes/class.pmproseries.php:379 150 | msgid "Edit" 151 | msgstr "Modifier" 152 | 153 | #: classes/class.pmproseries.php:382 154 | msgid "Remove" 155 | msgstr "Supprimer" 156 | 157 | #: classes/class.pmproseries.php:394 158 | msgid "Add/Edit Posts:" 159 | msgstr "Ajouter/modifier des articles" 160 | 161 | #: classes/class.pmproseries.php:398 162 | #: classes/class.pmproseries.php:550 163 | #: classes/class.pmproseries.php:579 164 | msgid "Post/Page" 165 | msgstr "Articles/pages" 166 | 167 | #: classes/class.pmproseries.php:427 168 | #: classes/class.pmproseries.php:580 169 | #: classes/class.pmproseries.php:609 170 | msgid "Add to Series" 171 | msgstr "Ajouter à la série" 172 | 173 | #: pmpro-series.php:117 174 | #: pmpro-series.php:119 175 | #: pmpro-series.php:128 176 | msgid "You are on day %d of your membership." 177 | msgstr "Vous êtes au jour #%d de votre abonnement." 178 | 179 | #: pmpro-series.php:225 180 | msgid "This content is part of the %s series. You will gain access on day %s of your membership." 181 | msgstr "Ce contenu fait partie de la série %s. Vous y aurez accès au jour #%s de votre abonnement." 182 | 183 | #: pmpro-series.php:232 184 | #: pmpro-series.php:348 185 | #: pmpro-series.php:400 186 | msgid "This content is part of the %s series." 187 | msgstr "Ce contenu fait partie de la séries %s." 188 | 189 | #: pmpro-series.php:236 190 | msgid "This content is part of the following series: " 191 | msgstr "Ce contenu fait partie des séries :" 192 | 193 | #. Plugin Name of the plugin/theme 194 | msgid "PMPro Series" 195 | msgstr "PMPro Series" 196 | 197 | #. Plugin URI of the plugin/theme 198 | msgid "http://www.paidmembershipspro.com/pmpro-series/" 199 | msgstr "http://www.paidmembershipspro.com/pmpro-series/" 200 | 201 | #. Description of the plugin/theme 202 | #. Description of the plugin 203 | #: pmpro-series.php 204 | msgid "Offer serialized (drip feed) content to your PMPro members." 205 | msgstr "Donne accès à des contenus au compte-gouttes (drip feed) à vos abonnés PMPro." 206 | 207 | #. Author of the plugin/theme 208 | msgid "Stranger Studios" 209 | msgstr "Stranger Studios" 210 | 211 | #. Author URI of the plugin/theme 212 | msgid "http://www.strangerstudios.com" 213 | msgstr "http://www.strangerstudios.com" 214 | 215 | #~ msgid "This content is part of the %s series." 216 | #~ msgstr "This content is part of the %s series." 217 | 218 | #~ msgid "Saving..." 219 | #~ msgstr "Sauvegarde…" 220 | 221 | #~ msgid "Error saving series post [1]" 222 | #~ msgstr "Erreur lors de l'enregistrement [1]" 223 | 224 | #: pmpro-series.php:61 225 | #~ msgid "Save" 226 | #~ msgstr "Sauvegarder" 227 | 228 | #~ msgid "Error saving series post [2]" 229 | #~ msgstr "Erreur lors de l'enregistrement [2]" 230 | 231 | #~ msgid "Error removing series post [1]" 232 | #~ msgstr "Erreur lors de la suppression [1]" 233 | 234 | #~ msgid "Error removing series post [2]" 235 | #~ msgstr "Erreur lors de la suppression [2]" 236 | 237 | #. Plugin Name of the plugin 238 | #: pmpro-series.php 239 | msgid "Paid Memberships Pro - Series Add On" 240 | msgstr "" 241 | 242 | #. Plugin URI of the plugin 243 | #: pmpro-series.php 244 | msgid "https://www.paidmembershipspro.com/add-ons/pmpro-series-for-drip-feed-content/" 245 | msgstr "" 246 | 247 | #. Author of the plugin 248 | #: pmpro-series.php 249 | msgid "Paid Memberships Pro" 250 | msgstr "" 251 | 252 | #. Author URI of the plugin 253 | #: pmpro-series.php 254 | msgid "https://www.paidmembershipspro.com" 255 | msgstr "" 256 | 257 | #: classes/class.pmproseries.php:384 258 | #: classes/class.pmproseries.php:413 259 | msgid "Manage Series" 260 | msgstr "" 261 | 262 | #. translators: %s: series post available date 263 | #: classes/class.pmproseries.php:444 264 | msgid "available on %s." 265 | msgstr "" 266 | 267 | #: classes/class.pmproseries.php:518 268 | #: classes/class.pmproseries.php:547 269 | msgid "Actions" 270 | msgstr "" 271 | 272 | #: classes/class.pmproseries.php:535 273 | #: classes/class.pmproseries.php:564 274 | msgid "edit" 275 | msgstr "" 276 | 277 | #: classes/class.pmproseries.php:536 278 | #: classes/class.pmproseries.php:565 279 | msgid "remove" 280 | msgstr "" 281 | 282 | #: classes/class.pmproseries.php:546 283 | #: classes/class.pmproseries.php:575 284 | msgid "Add/Edit Posts" 285 | msgstr "" 286 | 287 | #: pmpro-series.php:62 288 | msgid "Saving" 289 | msgstr "" 290 | 291 | #: pmpro-series.php:63 292 | #: pmpro-series.php:64 293 | msgid "Error saving series post" 294 | msgstr "" 295 | 296 | #: pmpro-series.php:65 297 | #: pmpro-series.php:66 298 | msgid "Error removing series post" 299 | msgstr "" 300 | 301 | #: pmpro-series.php:117 302 | #: pmpro-series.php:124 303 | msgid "All posts in this series are now available." 304 | msgstr "" 305 | 306 | #. translators: 1: series link, 2: date 307 | #: pmpro-series.php:341 308 | #: pmpro-series.php:393 309 | msgid "This content is part of the %1$s series. You will gain access on %2$s." 310 | msgstr "" 311 | 312 | #: pmpro-series.php:358 313 | #: pmpro-series.php:410 314 | msgid "This content is part of the following series: %s" 315 | msgstr "" 316 | 317 | #: pmpro-series.php:445 318 | #: pmpro-series.php:516 319 | msgid "New content is available at !!sitename!!" 320 | msgstr "" 321 | 322 | #: pmpro-series.php:446 323 | #: pmpro-series.php:517 324 | msgid "New Series Content Notification" 325 | msgstr "" 326 | 327 | #: pmpro-series.php:477 328 | #: pmpro-series.php:548 329 | msgid "Settings" 330 | msgstr "" 331 | 332 | #: pmpro-series.php:494 333 | #: pmpro-series.php:565 334 | msgid "View Documentation" 335 | msgstr "" 336 | 337 | #: pmpro-series.php:494 338 | #: pmpro-series.php:565 339 | msgid "Docs" 340 | msgstr "" 341 | 342 | #: pmpro-series.php:495 343 | #: pmpro-series.php:566 344 | msgid "Visit Customer Support Forum" 345 | msgstr "" 346 | 347 | #: pmpro-series.php:495 348 | #: pmpro-series.php:566 349 | msgid "Support" 350 | msgstr "" 351 | 352 | #. Plugin Name of the plugin 353 | #: pmpro-series.php 354 | msgid "Paid Memberships Pro - Series" 355 | msgstr "" 356 | 357 | #. Description of the plugin 358 | #: pmpro-series.php 359 | msgid "Drip feed content to your members over the course of their membership. Serializes content by # of days post-registration." 360 | msgstr "" 361 | 362 | #. translators: %s: series post available date 363 | #: classes/class.pmproseries.php:473 364 | msgid "available on %s" 365 | msgstr "" 366 | 367 | #: pmpro-series.php:119 368 | msgid "Content in This Series" 369 | msgstr "" 370 | 371 | #: pmpro-series.php:345 372 | msgid "Waiting For Access" 373 | msgstr "" 374 | 375 | #: pmpro-series.php:347 376 | msgid "Membership Required" 377 | msgstr "" 378 | -------------------------------------------------------------------------------- /languages/pmpro-series.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strangerstudios/pmpro-series/a28e2eaa1b9fbda8ce5d97830e6c60b51fbc929c/languages/pmpro-series.mo -------------------------------------------------------------------------------- /languages/pmpro-series.po: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 Paid Memberships Pro 2 | # This file is distributed under the same license as the Paid Memberships Pro - Series plugin. 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: Paid Memberships Pro - Series 0.7\n" 6 | "Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" 7 | "Last-Translator: Paid Memberships Pro \n" 8 | "Language-Team: Paid Memberships Pro \n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "POT-Creation-Date: 2024-07-19T18:33:55+00:00\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "X-Generator: WP-CLI 2.10.0\n" 15 | "X-Domain: pmpro-series\n" 16 | 17 | #. Plugin Name of the plugin 18 | #: pmpro-series.php 19 | msgid "Paid Memberships Pro - Series Add On" 20 | msgstr "" 21 | 22 | #. Plugin URI of the plugin 23 | #: pmpro-series.php 24 | msgid "https://www.paidmembershipspro.com/add-ons/pmpro-series-for-drip-feed-content/" 25 | msgstr "" 26 | 27 | #. Description of the plugin 28 | #: pmpro-series.php 29 | msgid "Offer serialized (drip feed) content to your PMPro members." 30 | msgstr "" 31 | 32 | #. Author of the plugin 33 | #: pmpro-series.php 34 | msgid "Paid Memberships Pro" 35 | msgstr "" 36 | 37 | #. Author URI of the plugin 38 | #: pmpro-series.php 39 | msgid "https://www.paidmembershipspro.com" 40 | msgstr "" 41 | 42 | #: classes/class.pmproseries.php:43 43 | #: classes/class.pmproseries.php:72 44 | msgid "Please enter a value for post and delay." 45 | msgstr "" 46 | 47 | #: classes/class.pmproseries.php:50 48 | #: classes/class.pmproseries.php:79 49 | msgid "A post with that id does not exist." 50 | msgstr "" 51 | 52 | #: classes/class.pmproseries.php:269 53 | #: classes/class.pmproseries.php:283 54 | #: classes/class.pmproseries.php:298 55 | #: classes/class.pmproseries.php:312 56 | msgid "New content is available at %s" 57 | msgstr "" 58 | 59 | #: classes/class.pmproseries.php:296 60 | #: classes/class.pmproseries.php:325 61 | msgid "An excerpt of the post is below." 62 | msgstr "" 63 | 64 | #: classes/class.pmproseries.php:319 65 | #: classes/class.pmproseries.php:320 66 | #: classes/class.pmproseries.php:348 67 | #: classes/class.pmproseries.php:349 68 | msgid "Series" 69 | msgstr "" 70 | 71 | #: classes/class.pmproseries.php:322 72 | #: classes/class.pmproseries.php:323 73 | #: classes/class.pmproseries.php:351 74 | #: classes/class.pmproseries.php:352 75 | msgid "New Series" 76 | msgstr "" 77 | 78 | #: classes/class.pmproseries.php:324 79 | #: classes/class.pmproseries.php:325 80 | #: classes/class.pmproseries.php:353 81 | #: classes/class.pmproseries.php:354 82 | msgid "Edit Series" 83 | msgstr "" 84 | 85 | #: classes/class.pmproseries.php:326 86 | #: classes/class.pmproseries.php:355 87 | msgid "Add New" 88 | msgstr "" 89 | 90 | #: classes/class.pmproseries.php:327 91 | #: classes/class.pmproseries.php:328 92 | #: classes/class.pmproseries.php:356 93 | #: classes/class.pmproseries.php:357 94 | msgid "View This Series" 95 | msgstr "" 96 | 97 | #: classes/class.pmproseries.php:329 98 | #: classes/class.pmproseries.php:358 99 | msgid "Search Series" 100 | msgstr "" 101 | 102 | #: classes/class.pmproseries.php:330 103 | #: classes/class.pmproseries.php:359 104 | msgid "No Series Found" 105 | msgstr "" 106 | 107 | #: classes/class.pmproseries.php:331 108 | #: classes/class.pmproseries.php:360 109 | msgid "No Series Found In Trash" 110 | msgstr "" 111 | 112 | #: classes/class.pmproseries.php:381 113 | #: classes/class.pmproseries.php:410 114 | msgid "Require Membership" 115 | msgstr "" 116 | 117 | #: classes/class.pmproseries.php:384 118 | #: classes/class.pmproseries.php:413 119 | msgid "Manage Series" 120 | msgstr "" 121 | 122 | #: classes/class.pmproseries.php:438 123 | #: classes/class.pmproseries.php:467 124 | msgid "Available Now" 125 | msgstr "" 126 | 127 | #. translators: %s: series post available date 128 | #: classes/class.pmproseries.php:444 129 | msgid "available on %s." 130 | msgstr "" 131 | 132 | #: classes/class.pmproseries.php:512 133 | #: classes/class.pmproseries.php:541 134 | msgid "Posts in this Series" 135 | msgstr "" 136 | 137 | #: classes/class.pmproseries.php:515 138 | #: classes/class.pmproseries.php:544 139 | msgid "Order" 140 | msgstr "" 141 | 142 | #: classes/class.pmproseries.php:516 143 | #: classes/class.pmproseries.php:545 144 | msgid "Title" 145 | msgstr "" 146 | 147 | #: classes/class.pmproseries.php:517 148 | #: classes/class.pmproseries.php:551 149 | #: classes/class.pmproseries.php:546 150 | #: classes/class.pmproseries.php:580 151 | msgid "Delay (# of days)" 152 | msgstr "" 153 | 154 | #: classes/class.pmproseries.php:518 155 | #: classes/class.pmproseries.php:547 156 | msgid "Actions" 157 | msgstr "" 158 | 159 | #: classes/class.pmproseries.php:535 160 | #: classes/class.pmproseries.php:564 161 | msgid "edit" 162 | msgstr "" 163 | 164 | #: classes/class.pmproseries.php:536 165 | #: classes/class.pmproseries.php:565 166 | msgid "remove" 167 | msgstr "" 168 | 169 | #: classes/class.pmproseries.php:546 170 | #: classes/class.pmproseries.php:575 171 | msgid "Add/Edit Posts" 172 | msgstr "" 173 | 174 | #: classes/class.pmproseries.php:550 175 | #: classes/class.pmproseries.php:579 176 | msgid "Post/Page" 177 | msgstr "" 178 | 179 | #: classes/class.pmproseries.php:580 180 | #: classes/class.pmproseries.php:609 181 | msgid "Add to Series" 182 | msgstr "" 183 | 184 | #: pmpro-series.php:61 185 | msgid "Save" 186 | msgstr "" 187 | 188 | #: pmpro-series.php:62 189 | msgid "Saving" 190 | msgstr "" 191 | 192 | #: pmpro-series.php:63 193 | #: pmpro-series.php:64 194 | msgid "Error saving series post" 195 | msgstr "" 196 | 197 | #: pmpro-series.php:65 198 | #: pmpro-series.php:66 199 | msgid "Error removing series post" 200 | msgstr "" 201 | 202 | #: pmpro-series.php:117 203 | #: pmpro-series.php:124 204 | msgid "All posts in this series are now available." 205 | msgstr "" 206 | 207 | #: pmpro-series.php:119 208 | #: pmpro-series.php:128 209 | msgid "You are on day %d of your membership." 210 | msgstr "" 211 | 212 | #. translators: 1: series link, 2: date 213 | #: pmpro-series.php:341 214 | #: pmpro-series.php:393 215 | msgid "This content is part of the %1$s series. You will gain access on %2$s." 216 | msgstr "" 217 | 218 | #: pmpro-series.php:348 219 | #: pmpro-series.php:400 220 | msgid "This content is part of the %s series." 221 | msgstr "" 222 | 223 | #: pmpro-series.php:358 224 | #: pmpro-series.php:410 225 | msgid "This content is part of the following series: %s" 226 | msgstr "" 227 | 228 | #: pmpro-series.php:445 229 | #: pmpro-series.php:516 230 | msgid "New content is available at !!sitename!!" 231 | msgstr "" 232 | 233 | #: pmpro-series.php:446 234 | #: pmpro-series.php:517 235 | msgid "New Series Content Notification" 236 | msgstr "" 237 | 238 | #: pmpro-series.php:477 239 | #: pmpro-series.php:548 240 | msgid "Settings" 241 | msgstr "" 242 | 243 | #: pmpro-series.php:494 244 | #: pmpro-series.php:565 245 | msgid "View Documentation" 246 | msgstr "" 247 | 248 | #: pmpro-series.php:494 249 | #: pmpro-series.php:565 250 | msgid "Docs" 251 | msgstr "" 252 | 253 | #: pmpro-series.php:495 254 | #: pmpro-series.php:566 255 | msgid "Visit Customer Support Forum" 256 | msgstr "" 257 | 258 | #: pmpro-series.php:495 259 | #: pmpro-series.php:566 260 | msgid "Support" 261 | msgstr "" 262 | 263 | #. Plugin Name of the plugin 264 | #: pmpro-series.php 265 | msgid "Paid Memberships Pro - Series" 266 | msgstr "" 267 | 268 | #. Description of the plugin 269 | #: pmpro-series.php 270 | msgid "Drip feed content to your members over the course of their membership. Serializes content by # of days post-registration." 271 | msgstr "" 272 | 273 | #. translators: %s: series post available date 274 | #: classes/class.pmproseries.php:473 275 | msgid "available on %s" 276 | msgstr "" 277 | 278 | #: pmpro-series.php:119 279 | msgid "Content in This Series" 280 | msgstr "" 281 | 282 | #: pmpro-series.php:345 283 | msgid "Waiting For Access" 284 | msgstr "" 285 | 286 | #: pmpro-series.php:347 287 | msgid "Membership Required" 288 | msgstr "" 289 | -------------------------------------------------------------------------------- /languages/pmpro-series.pot: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 Paid Memberships Pro 2 | # This file is distributed under the same license as the Paid Memberships Pro - Series plugin. 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: Paid Memberships Pro - Series 0.7\n" 6 | "Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" 7 | "Last-Translator: Paid Memberships Pro \n" 8 | "Language-Team: Paid Memberships Pro \n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "POT-Creation-Date: 2024-07-19T18:33:54+00:00\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "X-Generator: WP-CLI 2.10.0\n" 15 | "X-Domain: pmpro-series\n" 16 | 17 | #. Plugin Name of the plugin 18 | #: pmpro-series.php 19 | msgid "Paid Memberships Pro - Series" 20 | msgstr "" 21 | 22 | #. Plugin URI of the plugin 23 | #: pmpro-series.php 24 | msgid "https://www.paidmembershipspro.com/add-ons/pmpro-series-for-drip-feed-content/" 25 | msgstr "" 26 | 27 | #. Description of the plugin 28 | #: pmpro-series.php 29 | msgid "Drip feed content to your members over the course of their membership. Serializes content by # of days post-registration." 30 | msgstr "" 31 | 32 | #. Author of the plugin 33 | #: pmpro-series.php 34 | msgid "Paid Memberships Pro" 35 | msgstr "" 36 | 37 | #. Author URI of the plugin 38 | #: pmpro-series.php 39 | msgid "https://www.paidmembershipspro.com" 40 | msgstr "" 41 | 42 | #: classes/class.pmproseries.php:72 43 | msgid "Please enter a value for post and delay." 44 | msgstr "" 45 | 46 | #: classes/class.pmproseries.php:79 47 | msgid "A post with that id does not exist." 48 | msgstr "" 49 | 50 | #: classes/class.pmproseries.php:298 51 | #: classes/class.pmproseries.php:312 52 | msgid "New content is available at %s" 53 | msgstr "" 54 | 55 | #: classes/class.pmproseries.php:325 56 | msgid "An excerpt of the post is below." 57 | msgstr "" 58 | 59 | #: classes/class.pmproseries.php:348 60 | #: classes/class.pmproseries.php:349 61 | msgid "Series" 62 | msgstr "" 63 | 64 | #: classes/class.pmproseries.php:351 65 | #: classes/class.pmproseries.php:352 66 | msgid "New Series" 67 | msgstr "" 68 | 69 | #: classes/class.pmproseries.php:353 70 | #: classes/class.pmproseries.php:354 71 | msgid "Edit Series" 72 | msgstr "" 73 | 74 | #: classes/class.pmproseries.php:355 75 | msgid "Add New" 76 | msgstr "" 77 | 78 | #: classes/class.pmproseries.php:356 79 | #: classes/class.pmproseries.php:357 80 | msgid "View This Series" 81 | msgstr "" 82 | 83 | #: classes/class.pmproseries.php:358 84 | msgid "Search Series" 85 | msgstr "" 86 | 87 | #: classes/class.pmproseries.php:359 88 | msgid "No Series Found" 89 | msgstr "" 90 | 91 | #: classes/class.pmproseries.php:360 92 | msgid "No Series Found In Trash" 93 | msgstr "" 94 | 95 | #: classes/class.pmproseries.php:410 96 | msgid "Require Membership" 97 | msgstr "" 98 | 99 | #: classes/class.pmproseries.php:413 100 | msgid "Manage Series" 101 | msgstr "" 102 | 103 | #: classes/class.pmproseries.php:467 104 | msgid "Available Now" 105 | msgstr "" 106 | 107 | #. translators: %s: series post available date 108 | #: classes/class.pmproseries.php:473 109 | msgid "available on %s" 110 | msgstr "" 111 | 112 | #: classes/class.pmproseries.php:541 113 | msgid "Posts in this Series" 114 | msgstr "" 115 | 116 | #: classes/class.pmproseries.php:544 117 | msgid "Order" 118 | msgstr "" 119 | 120 | #: classes/class.pmproseries.php:545 121 | msgid "Title" 122 | msgstr "" 123 | 124 | #: classes/class.pmproseries.php:546 125 | #: classes/class.pmproseries.php:580 126 | msgid "Delay (# of days)" 127 | msgstr "" 128 | 129 | #: classes/class.pmproseries.php:547 130 | msgid "Actions" 131 | msgstr "" 132 | 133 | #: classes/class.pmproseries.php:564 134 | msgid "edit" 135 | msgstr "" 136 | 137 | #: classes/class.pmproseries.php:565 138 | msgid "remove" 139 | msgstr "" 140 | 141 | #: classes/class.pmproseries.php:575 142 | msgid "Add/Edit Posts" 143 | msgstr "" 144 | 145 | #: classes/class.pmproseries.php:579 146 | msgid "Post/Page" 147 | msgstr "" 148 | 149 | #: classes/class.pmproseries.php:609 150 | msgid "Add to Series" 151 | msgstr "" 152 | 153 | #: pmpro-series.php:61 154 | msgid "Save" 155 | msgstr "" 156 | 157 | #: pmpro-series.php:62 158 | msgid "Saving" 159 | msgstr "" 160 | 161 | #: pmpro-series.php:63 162 | #: pmpro-series.php:64 163 | msgid "Error saving series post" 164 | msgstr "" 165 | 166 | #: pmpro-series.php:65 167 | #: pmpro-series.php:66 168 | msgid "Error removing series post" 169 | msgstr "" 170 | 171 | #: pmpro-series.php:119 172 | msgid "Content in This Series" 173 | msgstr "" 174 | 175 | #: pmpro-series.php:124 176 | msgid "All posts in this series are now available." 177 | msgstr "" 178 | 179 | #: pmpro-series.php:128 180 | msgid "You are on day %d of your membership." 181 | msgstr "" 182 | 183 | #: pmpro-series.php:345 184 | msgid "Waiting For Access" 185 | msgstr "" 186 | 187 | #: pmpro-series.php:347 188 | msgid "Membership Required" 189 | msgstr "" 190 | 191 | #. translators: 1: series link, 2: date 192 | #: pmpro-series.php:393 193 | msgid "This content is part of the %1$s series. You will gain access on %2$s." 194 | msgstr "" 195 | 196 | #: pmpro-series.php:400 197 | msgid "This content is part of the %s series." 198 | msgstr "" 199 | 200 | #: pmpro-series.php:410 201 | msgid "This content is part of the following series: %s" 202 | msgstr "" 203 | 204 | #: pmpro-series.php:516 205 | msgid "New content is available at !!sitename!!" 206 | msgstr "" 207 | 208 | #: pmpro-series.php:517 209 | msgid "New Series Content Notification" 210 | msgstr "" 211 | 212 | #: pmpro-series.php:548 213 | msgid "Settings" 214 | msgstr "" 215 | 216 | #: pmpro-series.php:565 217 | msgid "View Documentation" 218 | msgstr "" 219 | 220 | #: pmpro-series.php:565 221 | msgid "Docs" 222 | msgstr "" 223 | 224 | #: pmpro-series.php:566 225 | msgid "Visit Customer Support Forum" 226 | msgstr "" 227 | 228 | #: pmpro-series.php:566 229 | msgid "Support" 230 | msgstr "" 231 | -------------------------------------------------------------------------------- /pmpro-series.php: -------------------------------------------------------------------------------- 1 | $post_id, 61 | 'save' => esc_html__( 'Save', 'pmpro-series' ), 62 | 'saving' => esc_html__( 'Saving', 'pmpro-series' ) . '...', 63 | 'saving_error_1' => esc_html__( 'Error saving series post', 'pmpro-series' ) . ' [1]', 64 | 'saving_error_2' => esc_html__( 'Error saving series post', 'pmpro-series' ) . ' [2]', 65 | 'remove_error_1' => esc_html__( 'Error removing series post', 'pmpro-series' ) . ' [1]', 66 | 'remove_error_2' => esc_html__( 'Error removing series post', 'pmpro-series' ) . ' [2]', 67 | ); 68 | 69 | wp_localize_script( 'pmpros_pmpro', 'pmpro_series', $localize ); 70 | wp_enqueue_script( 'pmpros_pmpro' ); 71 | } 72 | } 73 | add_action( 'admin_enqueue_scripts', 'pmpros_admin_scripts' ); 74 | 75 | /* 76 | PMPro Series CPT 77 | */ 78 | add_action( 'init', array( 'PMProSeries', 'createCPT' ) ); 79 | 80 | /* 81 | Add the PMPro meta box and the meta box to add posts/pages to series 82 | */ 83 | add_action( 'init', array( 'PMProSeries', 'checkForMetaBoxes' ), 20 ); 84 | 85 | 86 | /* 87 | Detect AJAX calls 88 | */ 89 | function pmpros_ajax() { 90 | if ( isset( $_REQUEST['pmpros_add_post'] ) ) { 91 | $series_id = $_REQUEST['pmpros_series']; 92 | $series = new PMProSeries( $series_id ); 93 | $series->getPostListForMetaBox(); 94 | exit; 95 | } 96 | } 97 | add_action( 'init', 'pmpros_ajax' ); 98 | 99 | 100 | /** 101 | * [pmpros_the_content] Show list of series pages at end of series. 102 | * 103 | * @param [type] $content 104 | * @return [type] 105 | */ 106 | function pmpros_the_content( $content ) { 107 | global $post; 108 | 109 | if ( ! empty( $post ) && $post->post_type == 'pmpro_series' ) { 110 | 111 | // Display the Series if Paid Memberships Pro is active. 112 | if ( !function_exists( 'pmpro_has_membership_access' ) || pmpro_has_membership_access() ) { 113 | ob_start(); 114 | $series = new PMProSeries( $post->ID ); 115 | $member_days = intval( $series->get_member_days() ); 116 | ?> 117 |
118 |
119 |

120 |
121 | = $series->getLongestPostDelay( 'publish' ) ) { 123 | ?> 124 |

125 | 128 |

129 | 132 | getPostList(); ?> 133 |
134 |
135 |
136 | getDelayForPost( $post_id ); 222 | $member_days = intval( $series->get_member_days( $user_id ) ); 223 | if ( empty( $post_delay ) || $member_days >= $post_delay ) { 224 | return true; // user has access to this post 225 | } 226 | } 227 | } 228 | 229 | // haven't found anything yet. so must not have access 230 | return false; 231 | } 232 | 233 | /** 234 | * [pmpros_pmpro_has_membership_access_filter] Filter pmpro_has_membership_access based on series access. 235 | * 236 | * @param [type] $hasaccess 237 | * @param [type] $mypost 238 | * @param [type] $myuser 239 | * @param [type] $post_membership_levels 240 | * @return [type] 241 | */ 242 | function pmpros_pmpro_has_membership_access_filter( $hasaccess, $post, $user, $post_membership_levels ) { 243 | // If the user doesn't have access already, we won't change that. So only check if they already have access. 244 | if ( $hasaccess && !empty( $post ) ) { 245 | // okay check if the user has access 246 | if ( pmpros_hasAccess( $user->ID, $post->ID ) ) { 247 | $hasaccess = true; 248 | } else { 249 | $hasaccess = false; 250 | } 251 | } 252 | 253 | return $hasaccess; 254 | } 255 | add_filter( 'pmpro_has_membership_access_filter', 'pmpros_pmpro_has_membership_access_filter', 10, 4 ); 256 | 257 | /** 258 | * Get the series a post is in. 259 | * @param $post_id int ID of the post to check series for. 260 | * NOTE: When getting/setting the _post_series post meta, use get_post_meta 261 | * to get the value directly. 262 | */ 263 | function pmpros_getPostSeries( $post_id = NULL ) { 264 | // Default to the global post. 265 | if ( empty( $post_id ) ) { 266 | global $post; 267 | 268 | if ( ! empty( $post ) && ! empty( $post->ID ) ) { 269 | $post_id = $post->ID; 270 | } 271 | } 272 | 273 | // Get ID from post object if passed in. 274 | if ( is_object( $post_id ) && ! empty( $post_id->ID ) ) { 275 | $post_id = $post_id->ID; 276 | } 277 | 278 | // Bail if no post. 279 | if ( empty( $post_id ) ) { 280 | return array(); 281 | } 282 | 283 | // If this is a series itself, bail. 284 | if ( get_post_type( $post_id ) == 'pmpro_series' ) { 285 | return array(); 286 | } 287 | 288 | // Get series from post meta. 289 | $post_series = get_post_meta( $post_id, '_post_series', true ); 290 | 291 | // Make sure it's an array. 292 | if ( empty( $post_series ) ) { 293 | $post_series = array(); 294 | } elseif ( ! is_array( $post_series ) ) { 295 | $post_series = array( $post_series ); 296 | } 297 | 298 | // Make sure the posts are published. 299 | $new_post_series = array(); 300 | $deleted_post_series = array(); 301 | foreach( $post_series as $series_id ) { 302 | if ( ! empty( $series_id ) ) { 303 | $post_status = get_post_status( $series_id ); 304 | if ( 'publish' === $post_status ) { 305 | $new_post_series[] = $series_id; 306 | } elseif ( 'trash' === $post_status || false === $post_status ) { 307 | $deleted_post_series[] = $series_id; 308 | } 309 | } 310 | } 311 | 312 | if ( ! empty( $deleted_post_series ) ) { 313 | update_post_meta( $post_id, '_post_series', array_diff( $post_series, $deleted_post_series ) ); 314 | } 315 | 316 | return $new_post_series; 317 | } 318 | 319 | /** 320 | * Filter the header message for the no access message. 321 | * 322 | * @since TBD 323 | * 324 | * @param string $header The header message for the no access message. 325 | * @return string The filtered header message for the no access message. 326 | */ 327 | function pmpros_no_access_message_header( $header ) { 328 | global $current_user, $post; 329 | 330 | // We are running PMPro v3.1+, so make sure that deprecated filters don't run later. 331 | remove_filter( 'pmpro_non_member_text_filter', 'pmpros_pmpro_text_filter' ); 332 | remove_filter( 'pmpro_not_logged_in_text_filter', 'pmpros_pmpro_text_filter' ); 333 | 334 | // Check if the post is locked in a series. 335 | if ( ! empty( $current_user ) && ! empty( $post ) && ! pmpros_hasAccess( $current_user->ID, $post->ID ) ) { 336 | // Check if the user has access to any series containing this post. 337 | $post_series = pmpros_getPostSeries( $post->ID ); 338 | $inseries = array(); 339 | foreach ( $post_series as $ps ) { 340 | if ( !function_exists('pmpro_has_membership_access') || pmpro_has_membership_access( $ps ) ) { 341 | $inseries[] = $ps; 342 | } 343 | } 344 | if ( ! empty( $inseries ) ) { 345 | $header = __( 'Waiting For Access', 'pmpro-series' ); 346 | } else { 347 | $header = __( 'Membership Required', 'pmpro-series' ); 348 | } 349 | } 350 | 351 | return $header; 352 | } 353 | add_filter( 'pmpro_no_access_message_header', 'pmpros_no_access_message_header' ); // PMPro v3.1+. 354 | 355 | /** 356 | * [pmpros_pmpro_text_filter] Filter the message for users without access. 357 | * 358 | * @param string $text 359 | * @return string 360 | */ 361 | function pmpros_pmpro_text_filter( $text ) { 362 | global $current_user, $post; 363 | 364 | if ( ! empty( $current_user ) && ! empty( $post ) ) { 365 | if ( ! pmpros_hasAccess( $current_user->ID, $post->ID ) ) { 366 | // Update text. The either have to wait or sign up. 367 | $post_series = pmpros_getPostSeries( $post->ID ); 368 | $inseries = array(); 369 | foreach ( $post_series as $ps ) { 370 | if ( !function_exists('pmpro_has_membership_access') || pmpro_has_membership_access( $ps ) ) { 371 | $inseries[] = $ps; 372 | } 373 | } 374 | 375 | if ( ! empty( $inseries ) ) { 376 | // User is a part of a series that has this post. Figure out which series will give them access soonest and tell them. 377 | $days_left = null; 378 | $soonest_series = null; 379 | foreach ( $inseries as $series_id ) { 380 | $series = new PMProSeries( $series_id ); 381 | $post_delay = $series->getDelayForPost( $post->ID ); 382 | $member_days = intval( $series->get_member_days() ); 383 | if ( empty( $soonest_series ) || $post_delay - $member_days < $days_left ) { 384 | $days_left = $post_delay - $member_days; 385 | $soonest_series = $series; 386 | } 387 | } 388 | 389 | // Show the user when they will have access. 390 | $series_date_text = date_i18n( get_option( 'date_format' ), strtotime( "+ $days_left Days", current_time( 'timestamp' ) ) ); 391 | $series_link_text = '' . esc_html( get_the_title( $soonest_series->id ) ) . ''; 392 | /* translators: 1: series link, 2: date */ 393 | $text = '

' . sprintf( esc_html__( 'This content is part of the %1$s series. You will gain access on %2$s.', 'pmpro-series' ), $series_link_text, esc_html( $series_date_text ) ) . '

'; 394 | 395 | $text = apply_filters( 'pmpros_days_left_message', $text, $member_days, $days_left, $current_user->ID ); 396 | } else { 397 | // user has to sign up for one of the series 398 | if ( count( $post_series ) == 1 ) { 399 | $series_link_text = '' . esc_html( get_the_title( $post_series[0] ) ) . ''; 400 | $text = '

' . sprintf( esc_html__( 'This content is part of the %s series.', 'pmpro-series' ), $series_link_text ) . '

'; 401 | 402 | $text = apply_filters( 'pmpros_content_access_message_single_item', $text, $post_series ); 403 | } else { 404 | $series = array(); 405 | foreach ( $post_series as $series_id ) { 406 | $series[] = "" . esc_html( get_the_title( $series_id ) ) . ''; 407 | } 408 | $series_list_text = implode( ', ', $series ) . '.'; 409 | 410 | $text = '

' . sprintf( esc_html__( 'This content is part of the following series: %s', 'pmpro-series' ), $series_list_text ) . '

'; 411 | 412 | $text = apply_filters( 'pmpros_content_access_message_many_items', $text, $post_series ); 413 | } 414 | } 415 | } 416 | } 417 | 418 | return $text; 419 | } 420 | add_filter( 'pmpro_no_access_message_body', 'pmpros_pmpro_text_filter' ); // PMPro v3.1+. 421 | add_filter( 'pmpro_non_member_text_filter', 'pmpros_pmpro_text_filter' ); // Pre-PMPro v3.1. 422 | add_filter( 'pmpro_not_logged_in_text_filter', 'pmpros_pmpro_text_filter' ); // Pre-PMPro v3.1. 423 | 424 | /* 425 | We need to flush rewrite rules on activation/etc for the CPTs. 426 | Register/unregister crons on activation/deactivation. 427 | */ 428 | /** 429 | * [pmpros_activation description] 430 | * 431 | * @return [type] 432 | */ 433 | function pmpros_activation() { 434 | // flush rewrite rules 435 | PMProSeries::createCPT(); 436 | flush_rewrite_rules(); 437 | 438 | // setup cron 439 | wp_schedule_event( current_time( 'timestamp' ), 'daily', 'pmpros_check_for_new_content' ); 440 | } 441 | register_activation_hook( __FILE__, 'pmpros_activation' ); 442 | function pmpros_deactivation() { 443 | // flush rewrite rules 444 | global $pmpros_deactivating; 445 | $pmpros_deactivating = true; 446 | flush_rewrite_rules(); 447 | 448 | // remove cron 449 | wp_clear_scheduled_hook( 'pmpros_check_for_new_content' ); 450 | } 451 | register_deactivation_hook( __FILE__, 'pmpros_deactivation' ); 452 | 453 | /* 454 | Add series post links to account page 455 | */ 456 | function pmpros_member_links_bottom() { 457 | global $wpdb, $current_user; 458 | 459 | // get all series 460 | $all_series = $wpdb->get_results( 461 | " 462 | SELECT * 463 | FROM $wpdb->posts 464 | WHERE post_type = 'pmpro_series' 465 | " 466 | ); 467 | 468 | if ( empty( $all_series ) ) { 469 | return; 470 | } 471 | 472 | // Build our list of series posts to show. 473 | $series_posts_list = array(); 474 | 475 | foreach ( $all_series as $s ) { 476 | $series = new PMProSeries( $s->ID ); 477 | $series_posts = $series->getPosts(); 478 | 479 | if ( ! empty( $series_posts ) ) { 480 | foreach ( $series_posts as $series_post ) { 481 | if ( pmpros_hasAccess( $current_user->ID, $series_post->id ) ) { 482 | $series_posts_list[$series_post->id] = array( 483 | 'title' => get_the_title( $series_post->id ), 484 | 'permalink' => get_permalink( $series_post->id ), 485 | ); 486 | } 487 | } 488 | } 489 | } 490 | 491 | if ( empty( $series_posts_list ) ) { 492 | return; 493 | } 494 | 495 | // Remove duplicate array keys. 496 | $series_posts_list = array_values( $series_posts_list ); 497 | 498 | // Show the list. 499 | foreach ( $series_posts_list as $series_post ) { 500 | ?> 501 |
  • 502 | esc_html__( 'New content is available at !!sitename!!', 'pmpro-series' ), 517 | 'description' => esc_html__( 'New Series Content Notification', 'pmpro-series' ), 518 | 'body' => file_get_contents( dirname( __FILE__ ) . '/email/new_content.html' ), 519 | ); 520 | return $templates; 521 | } 522 | add_filter( 'pmproet_templates', 'pmpros_email_templates', 10, 1 ); 523 | 524 | /** 525 | * [pmpros_add_email_template] 526 | * 527 | * @param [type] $templates 528 | * @param [type] $page_name 529 | * @param string $type 530 | * @param string $where 531 | * @param string $ext 532 | * @return [type] 533 | */ 534 | function pmpros_add_email_template( $templates, $page_name, $type = 'emails', $where = 'local', $ext = 'html' ) { 535 | $templates[] = dirname( __FILE__ ) . '/email/new_content.html'; 536 | return $templates; 537 | } 538 | add_filter( 'pmpro_email_custom_template_path', 'pmpros_add_email_template', 10, 5 ); 539 | 540 | /** 541 | * [pmpros_plugin_action_links] Function to add links to the plugin action links 542 | * 543 | * @param array $links Array of links to be shown in plugin action links. 544 | */ 545 | function pmpros_plugin_action_links( $links ) { 546 | if ( current_user_can( 'manage_options' ) ) { 547 | $new_links = array( 548 | '' . esc_html__( 'Settings', 'pmpro-series' ) . '', 549 | ); 550 | return array_merge( $new_links, $links ); 551 | } 552 | return $links; 553 | } 554 | add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'pmpros_plugin_action_links' ); 555 | 556 | /** 557 | * [pmpros_plugin_row_meta] Function to add links to the plugin row meta 558 | * 559 | * @param array $links Array of links to be shown in plugin meta. 560 | * @param string $file Filename of the plugin meta is being shown for. 561 | */ 562 | function pmpros_plugin_row_meta( $links, $file ) { 563 | if ( strpos( $file, 'pmpro-series.php' ) !== false ) { 564 | $new_links = array( 565 | '' . __( 'Docs', 'pmpro-series' ) . '', 566 | '' . __( 'Support', 'pmpro-series' ) . '', 567 | ); 568 | $links = array_merge( $links, $new_links ); 569 | } 570 | return $links; 571 | } 572 | add_filter( 'plugin_row_meta', 'pmpros_plugin_row_meta', 10, 2 ); 573 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Paid Memberships Pro - Series === 2 | Contributors: strangerstudios 3 | Tags: series, drip feed, serial, delayed, limited, memberships 4 | Requires at least: 5.2 5 | Tested up to: 6.6 6 | Stable tag: 0.7 7 | License: GPLv3 8 | License URI: https://www.gnu.org/licenses/gpl-3.0.html 9 | 10 | Drip feed content to your members over the course of their membership. Serializes content by # of days post-registration. 11 | 12 | == Description == 13 | This plugin currently requires Paid Memberships Pro. 14 | 15 | How this plugin works: 16 | 17 | 1. There will be a new "Series" tab in the Memberships menu of the WP dashboard. 18 | 2. Admins can create a new "Series". 19 | 3. Admins can add a page or post to a series along with a # of days after signup. 20 | 4. Admins can add a series to a membership level. 21 | 5. Admins can adjust the email template via an added page to their active theme. 22 | 23 | Then... 24 | 25 | 1. User signs up for a membership level that gives him access to Series A. 26 | 2. User gets access to any "0 days after" series content. 27 | 3. Each day a script checks if a user should gain access to any new content, if so: 28 | - User is given access to the content. 29 | - An email is sent to the user letting them know that content is available. 30 | 31 | Checking for access: 32 | * Is a membership level required? 33 | * If so, does the user have one of those levels? 34 | * Is the user's level "assigned" to a series? 35 | * If so, does the user have access to that content yet? (count days) 36 | * If not, then the user will have access. (e.g. Pro members get access to everything right away.) 37 | 38 | Checking to send emails: 39 | * For all members with series levels. 40 | * What day of the membership is it? 41 | * For all series. 42 | * Get content. 43 | * Send content for this day. 44 | * Email update. 45 | 46 | Data Structure 47 | * Series is a CPT 48 | * Use wp_pmpro_memberships_pages to link to membership levels 49 | * wp_pmpro_series_content (series_id, post_id, day) stored in post meta 50 | 51 | == Installation == 52 | 53 | 1. Upload the `pmpro-series` directory to the `/wp-content/plugins/` directory of your site. 54 | 1. Activate the plugin through the 'Plugins' menu in WordPress. 55 | 1. Navigate to the Series menu in the WordPress dashboard to create a new series. 56 | 1. Add posts to series using the "Posts in this Series" meta box under the post content. 57 | 58 | == Frequently Asked Questions == 59 | 60 | = I found a bug in the plugin. = 61 | 62 | Please post it in the issues section of GitHub and we'll fix it as soon as we can. Thanks for helping. https://github.com/strangerstudios/pmpro-series/issues 63 | 64 | == Changelog == 65 | = 0.7 - 2024-07-19 = 66 | * ENHANCEMENT: Updated the frontend UI for compatibility with PMPro v3.1. #108, #109 (@dparker1005, @kimcoleman) 67 | * BUG FIX: Fixed some PHP 8.2+ warnings. #109 (@kimcoleman) 68 | 69 | = 0.6.1 -2024-06-20 = 70 | * BUG FIX: Fix an issue where the new content emails would not send to customers when content becomes available. (@dparker1005,@JarrydLong) 71 | 72 | = 0.6 - 2023-08-16 = 73 | * BUG FIX/ENHANCEMENT: Improving compatibility with PMPro Multiple Memberships Per User Add On. #92 (@dparker1005) 74 | * BUG FIX/ENHANCEMENT: Improving compatibility with Avada theme to prevent some content from being duplicated. #101 (@JarrydLong) 75 | * BUG FIX/ENHANCEMENT: Updated localization and escaping of strings. #104 (@dparker1005) 76 | * BUG FIX: Now only sending “new content” emails for series in “publish” status. #83 (@andrewlimaza) 77 | * BUG FIX: Fixed PHP8 compatibility issue with the plugin action links. #99 (@JarrydLong) 78 | * BUG FIX: Fixed an error on archive pages when the `$post` global is not set. #100 (@JarrydLong) 79 | * BUG FIX: Fixed incorrect class attributes for `
  • ` items in the series list. #93 (@kimcoleman) 80 | * BUG FIX: Fixed issue where crons would not be cleared on deactivation. #105 (@dparker1005) 81 | * BUG FIX: Fixed the version of select2 that is being enqueued to match included select2 files. #103 (@dparker1005) 82 | * REFACTOR: Removing functions that have been merged into the core Paid Memberships Pro plugin. #91 (@dparker1005) 83 | 84 | = 0.5 - 2020-10-16= 85 | * ENHANCEMENT: Now showing a different message at the top of a series when all content is now available. 86 | * ENHANCEMENT: Now wrapping content of series pages in a
    so that it can be targeted with CSS. 87 | * ENHANCEMENT: Added classes around text at the top of series pages so that it can be targeted with CSS. 88 | * ENHANCEMENT: Now using date_i18n() instead of date(). 89 | * BUG FIX/ENHANCEMENT: Posts that have not yet been published will no longer be shown in series on frontend. 90 | * BUG FIX/ENHANCEMENT: Updated select2 library. 91 | * BUG FIX: Fixed issue where custom "new_content.html" files would not be found. 92 | * BUG FIX: Now updating "_post_series" postmeta when a series is deleted. 93 | * BUG FIX: Fixed PHP warning when sending the "new_content.html" email. 94 | * BUG FIX: Fixed issue where startdates may have been shifted based on SQL timezone. 95 | 96 | = .4.1 - 2020-01-09= 97 | * BUG FIX: Fixed issues where posts belonging to more than one series would show up multiple times in the new content email. 98 | * BUG FIX: Fixed JavaScript issues of posts/pages not being added to series. 99 | 100 | = .4 = 101 | * BUG FIX: Fixed issues that came up if PMPro was not active. 102 | * BUG FIX: Fixes issues when running certain versions of PHP. 103 | * BUG FIX: Fixed issue where users couldn't access pages that were part of a deleted series. (Thanks, Thomas Sjolshagen) 104 | * BUG FIX/ENHANCEMENT: Works with Gutenberg and PMPro 2.0+ now. 105 | * BUG FIX/ENHANCEMENT: You can now add posts into a series before saving/refreshing a new post. 106 | * BUG FIX/ENHANCEMENT: Fixed menu_icon for Series CPT to use dashicons-clock in place of custom image. 107 | * BUG FIX/ENHANCEMENT: Updated to the latest version of select2. 108 | * BUG FIX/ENHANCEMENT: Using current_time('timestamp') instead of time(). (Thanks, Thomas Sjolshagen) 109 | * ENHANCEMENT: Wrapped strings for translation and added French language files. (Thanks, Charlie Merland) 110 | * ENHANCEMENT: Added pmpros_days_left_message, pmpros_content_access_message_single_item, and pmpros_content_access_message_many_items filters to filter the text shown in certain cases via hooks. (Thanks, Curtis McHale) 111 | * ENHANCEMENT: Added pmpros_new_content_subject filter. (Thanks, Curtis McHale) 112 | * ENHANCEMENT: Improved styling of the series list. 113 | * ENHANCEMENT: Updating to add support for the Email Templates Admin Editor Add On. 114 | 115 | = .3.7 = 116 | * Fixed new content email cron to remember which posts members were notified of. 117 | 118 | = .3.6 = 119 | * Updated format of the notification email. Will now send 1 email with a list of all new content instead of 1 email for each post. 120 | 121 | = .3.5 = 122 | * Added pmpros_series_registration filter to change PMPro Series post type settings. e.g. https://gist.github.com/strangerstudios/4fc33f3ee209b9dca00f/ 123 | 124 | = .3.4 = 125 | * Can now use custom email templates for new content notifications. (.../your_theme/paid-memberships-pro/series/new_content.html) 126 | * Fixed bug in code that adds links to series posts in the member links section of the membership account page. 127 | * Fixed bug with Day 0 content if timezones/etc aren't lined up on server/site. 128 | * Changed series list to show dates of availability instead of "on day X". 129 | 130 | = .3.3 = 131 | * Now showing the date content in a series will become available instead of the number of days. 132 | * Fixed bug where pmpros_check_for_new_content() function was not being fired by the cron. 133 | 134 | = .3.2 = 135 | * Fixed a warning. (Thanks, Karmyn) 136 | 137 | = .3.1 = 138 | * Fixed some warnings. (Thanks, Virtualced) 139 | * Added pmpros_series_labels filter 140 | 141 | = .3 = 142 | * Added account page links 143 | * Added emails when new content is available 144 | 145 | = .2.3.1 = 146 | * Fixed bug in pmpro_getMemberDays that sometimes reported more days than the user had really been a member. (Thanks, surefireweb) 147 | 148 | = .2.3 = 149 | * Added pmpro_member_startdate filter to pmpro_getMemberStartdate function. 150 | 151 | = .2.2 = 152 | * Flushing rewrite rules on activation and deactivation so /series/ URLs work without having to update permalink settings. 153 | * Added pmpro_series_get_post_list filter. 154 | 155 | = .2.1 = 156 | * Fixed a warning message (Thanks, moneysharp on the WP.org forums.) 157 | 158 | = .2 = 159 | * Styled the post list on a series page. 160 | * Hiding the post list if a user doesn't have access to the series page. 161 | * Hiding links to posts user doesn't have access to yet due to delay. 162 | * Fixed bug with editing the first post in the series. 163 | * Now allowing 0 day content. Forcing delay (and post ids) to be integers. 164 | 165 | = .1.1 = 166 | * Fixed code for hasAccess logic. 167 | * Updating the message shown when a user doesn't have access. 168 | * Fixed edit link in the "Posts in this Series" meta box. 169 | 170 | = .1 = 171 | * This is the initial version of the plugin. 172 | -------------------------------------------------------------------------------- /scheduled/crons.php: -------------------------------------------------------------------------------- 1 | get_results(" 10 | SELECT * 11 | FROM $wpdb->pmpro_memberships_users 12 | WHERE status = 'active' 13 | "); 14 | 15 | //get all series 16 | $series = $wpdb->get_results(" 17 | SELECT * 18 | FROM $wpdb->posts 19 | WHERE post_type = 'pmpro_series' 20 | AND post_status = 'publish' 21 | "); 22 | 23 | //store emails to send 24 | $emails = array(); 25 | 26 | //search through series looking for emails to send 27 | foreach($series as $s) { 28 | $series = new PMProSeries($s->ID); 29 | $series_posts = $series->getPosts(); 30 | 31 | if(empty($series_posts)) 32 | continue; 33 | 34 | foreach($series_posts as $series_post) { 35 | foreach($users as $user) { 36 | $notified = get_user_meta($user->user_id,'pmpros_notified', true); 37 | if(empty($notified)) 38 | $notified = array(); 39 | if(pmpros_hasAccess($user->user_id, $series_post->id) && !in_array($series_post->id, $notified)) { 40 | //add email to array to send 41 | if(empty($emails[$user->user_id])) 42 | $emails[$user->user_id] = array(); 43 | //don't add to email if we're already emailing about this post 44 | if( !in_array($series_post->id, $emails[$user->user_id]) ) 45 | $emails[$user->user_id][] = $series_post->id; 46 | //$series->sendEmail($series_post->id, $user->user_id); 47 | } 48 | } 49 | } 50 | } 51 | 52 | //send emails 53 | foreach($emails as $user_id => $posts) 54 | { 55 | //send email 56 | $series->sendEmail($posts, $user_id); 57 | 58 | //remember that we emailed about these posts 59 | $notified = get_user_meta($user_id, "pmpros_notified", true); 60 | if(!is_array($notified)) 61 | $notified = array(); 62 | $notified = array_unique(array_merge($posts, $notified)); 63 | update_user_meta($user_id, "pmpros_notified", $notified); 64 | } 65 | } --------------------------------------------------------------------------------