Version Description
- Change: Minimum WordPress version is now 4.7.0.
- Enhancement: Add email notifications with initial support for new jobs, updated jobs, and expiring listings.
- Enhancement: For GDPR, scrub WPJM data from database on uninstall if option is enabled.
- Enhancement: Filter by Filled and Featured status in WP admin.
- Enhancement: Simplify the display of application URLs.
- Enhancement: When using WPML, prevent changes to page options when on a non-default language. (@vukvukovich)
- Enhancement: Include company logo in structured data. (@RajeebTheGreat)
- Enhancement: Use more efficient jQuery selectors in scripts. (@RajeebTheGreat)
- Enhancement: Use proper
<h2>
tag incontent-summary-job_listing.php
template for the job title. (@abdullah1908) - Enhancement: Hide empty categories on
[job]
filter. - Fix: Update calls to
get_terms()
to use the new format. - Fix: Maintain the current tab when saving settings in WP Admin.
- Fix: Enqueue the date picker CSS when used on the front-end.
- Fix: Remove errors when widget instance was created without setting defaults.
- REST API Pre-release: Add support for job category taxonomy endpoints.
- Dev: Add
$job_id
parameter tojob_manager_job_dashboard_do_action_{$action}
action hook. (@jonasvogel) - Dev: Add support for hidden WPJM settings in WP Admin.
Download this release
Release Info
Developer | jakeom |
Plugin | WP Job Manager |
Version | 1.31.0 |
Comparing to | |
See all releases |
Code changes from version 1.30.2 to 1.31.0
- assets/css/admin.css +1 -1
- assets/css/admin.less +35 -0
- assets/js/admin.js +3 -3
- assets/js/admin.min.js +1 -1
- assets/js/job-application.js +1 -1
- assets/js/job-application.min.js +1 -1
- assets/js/job-submission.js +2 -2
- assets/js/job-submission.min.js +1 -1
- changelog.txt +19 -0
- includes/3rd-party/wpml.php +45 -0
- includes/abstracts/abstract-wp-job-manager-email-template.php +131 -0
- includes/abstracts/abstract-wp-job-manager-email.php +223 -0
- includes/admin/class-wp-job-manager-admin.php +2 -6
- includes/admin/class-wp-job-manager-cpt.php +121 -1
- includes/admin/class-wp-job-manager-settings.php +306 -124
- includes/admin/class-wp-job-manager-writepanels.php +2 -2
- includes/class-wp-job-manager-data-cleaner.php +365 -0
- includes/class-wp-job-manager-email-notifications.php +814 -0
- includes/class-wp-job-manager-shortcodes.php +1 -1
- includes/class-wp-job-manager-usage-tracking-data.php +4 -2
- includes/class-wp-job-manager-usage-tracking.php +1 -1
- includes/class-wp-job-manager-widget.php +18 -0
- includes/emails/class-wp-job-manager-email-admin-expiring-job.php +52 -0
- includes/emails/class-wp-job-manager-email-admin-new-job.php +87 -0
- includes/emails/class-wp-job-manager-email-admin-updated-job.php +87 -0
- includes/emails/class-wp-job-manager-email-employer-expiring-job.php +155 -0
- includes/rest-api/class-wp-job-manager-controllers-status.php +1 -1
- includes/rest-api/class-wp-job-manager-models-job-categories-custom-fields.php +26 -0
- includes/rest-api/class-wp-job-manager-models-status.php +4 -1
- includes/rest-api/class-wp-job-manager-registrable-job-categories.php +42 -0
- includes/rest-api/class-wp-job-manager-registrable-job-listings.php +4 -0
- includes/rest-api/class-wp-job-manager-registrable-job-types.php +13 -184
- includes/rest-api/class-wp-job-manager-registrable-taxonomy-type.php +229 -0
- includes/rest-api/class-wp-job-manager-rest-api.php +5 -0
- includes/widgets/class-wp-job-manager-widget-featured-jobs.php +6 -4
- includes/widgets/class-wp-job-manager-widget-recent-jobs.php +4 -2
- languages/wp-job-manager.pot +399 -184
- lib/emogrifier/class-emogrifier.php +1557 -0
- readme.txt +21 -2
- templates/account-signin.php +4 -4
- templates/content-summary-job_listing.php +2 -2
- templates/emails/admin-expiring-job.php +46 -0
- templates/emails/admin-new-job.php +44 -0
- templates/emails/admin-updated-job.php +44 -0
- templates/emails/email-footer.php +30 -0
- templates/emails/email-header.php +37 -0
- templates/emails/email-job-details.php +41 -0
- templates/emails/email-styles.php +83 -0
- templates/emails/employer-expiring-job.php +45 -0
- templates/emails/plain/admin-expiring-job.php +44 -0
- templates/emails/plain/admin-new-job.php +41 -0
- templates/emails/plain/admin-updated-job.php +41 -0
- templates/emails/plain/email-footer.php +17 -0
- templates/emails/plain/email-header.php +17 -0
- templates/emails/plain/email-job-details.php +28 -0
- templates/emails/plain/employer-expiring-job.php +43 -0
- templates/form-fields/date-field.php +2 -1
- templates/job-application-url.php +2 -2
- templates/job-filters.php +5 -5
- uninstall.php +27 -32
- wp-job-manager-functions.php +21 -6
- wp-job-manager-template.php +57 -0
- wp-job-manager.php +35 -4
assets/css/admin.css
CHANGED
@@ -1 +1 @@
|
|
1 |
-
.clearfix{zoom:1}.clearfix:after,.clearfix:before{content:"";display:table}.clearfix:after{clear:both}@font-face{font-family:job-manager;src:url(../font/job-manager.eot?4963673);src:url(../font/job-manager.eot?4963673#iefix) format('embedded-opentype'),url(../font/job-manager.woff?4963673) format('woff'),url(../font/job-manager.ttf?4963673) format('truetype'),url(../font/job-manager.svg?4963673#job-manager) format('svg');font-weight:400;font-style:normal}@font-face{font-family:jm-logo;src:url(../font/jm-logo/jm.eot?ycsbky);src:url(../font/jm-logo/jm.eot?#iefixycsbky) format('embedded-opentype'),url(../font/jm-logo/jm.woff?ycsbky) format('woff'),url(../font/jm-logo/jm.ttf?ycsbky) format('truetype'),url(../font/jm-logo/jm.svg?ycsbky#icomoon) format('svg');font-weight:400;font-style:normal}.jm-icon{font-family:job-manager!important;font-style:normal;font-weight:400;speak:none;display:inline-block;text-decoration:inherit;width:1em;text-align:center;font-variant:normal;text-transform:none;line-height:1em}.job-manager-settings-wrap .updated{display:none}.job-manager-settings-wrap .job-manager-updated{display:block;margin:1em 0 0}a.wpjm-activate-licence-link,a.wpjm-activate-licence-link:active,a.wpjm-activate-licence-link:hover,a.wpjm-activate-licence-link:link,a.wpjm-activate-licence-link:visited{color:#ff4500}.wpjm-licences{margin-top:10px}.wpjm-licences .licence-row{align-items:center;border:solid 1px #e2e0e2;display:flex;background-color:#fff;flex-wrap:wrap;min-height:82px;margin-bottom:20px;position:relative}.wpjm-licences .plugin-info{font-size:18px;flex-basis:320px;padding:0 20px;margin-right:10px}.wpjm-licences .plugin-info .plugin-author{font-size:12px}.wpjm-licences .plugin-licence{flex:1;flex-basis:40%;padding-bottom:5px}.wpjm-licences .plugin-licence label{white-space:nowrap}.widefat td.column-featured_job,.widefat td.column-filled,.widefat td.column-job_status{width:46px;text-align:left;padding-left:11px}.widefat th.column-featured_job,.widefat th.column-filled,.widefat th.column-job_status{width:1em}.widefat th.column-featured_job span,.widefat th.column-filled span,.widefat th.column-job_status span{display:block;width:1em;height:1em;line-height:1em;padding:1px 0 0 0;overflow:hidden}.widefat th.column-featured_job span:before,.widefat th.column-filled span:before,.widefat th.column-job_status span:before{content:'\e803';font-family:job-manager!important;font-style:normal;font-weight:400;speak:none;display:inline-block;text-decoration:inherit;width:1em;text-align:center;font-variant:normal;text-transform:none;line-height:1em}.widefat th.column-filled span:before{content:'\e807'}.widefat th.column-job_status span:before{content:'\e828'}.widefat .column-job_posted strong{display:block;margin-bottom:.2em}.widefat td.column-job_status span{position:relative;font-size:1em;line-height:1.5em;width:1em;height:0;padding:2em 0 0 0;overflow:hidden;display:block}.widefat td.column-job_status span:before{font-family:job-manager!important;font-style:normal;font-weight:400;speak:none;display:inline-block;text-decoration:inherit;width:1em;text-align:center;font-variant:normal;text-transform:none;line-height:1em;position:absolute;top:0;left:0;line-height:1.5em;vertical-align:middle;color:#999;content:'\e829'}.widefat td.column-job_status .status-trash:before{content:'\e82b';color:#a00}.widefat td.column-job_status .status-pending:before{content:'\e82c';color:#ffba00}.widefat td.column-job_status .status-publish:before{content:'\e82f';color:#73a724}.widefat td.column-job_status .status-expired:before{content:'\e82e';color:#a00}.widefat .column-job_listing_type{text-align:left;width:6em;word-wrap:normal!important}.widefat .column-job_listing_type .job-type{color:#fff;padding:4px;font-size:11px;-webkit-border-radius:2px;border-radius:2px;display:block;background-color:#f08d3c;text-align:center}.widefat .column-job_listing_type .full-time{background-color:#90da36}.widefat .column-job_listing_type .part-time{background-color:#f08d3c}.widefat .column-job_listing_type .temporary{background-color:#d93674}.widefat .column-job_listing_type .freelance{background-color:#39c}.widefat .column-job_listing_type .internship{background-color:#6033cc}.widefat th.column-job_position{width:20%}.widefat td.column-job_position{width:20%;height:34px}.widefat td.column-job_position .job_position{position:relative;padding-right:50px!important}.widefat td.column-job_position a.job_title{font-weight:700}.widefat td.column-job_position img{width:32px;height:32px;position:absolute;right:7px;top:0;-webkit-border-radius:50%;border-radius:50%;box-shadow:0 1px 0 1px rgba(0,0,0,.1);-webkit-box-shadow:0 1px 0 1px rgba(0,0,0,.1);-moz-box-shadow:0 1px 0 1px rgba(0,0,0,.1);border:1px solid #fff}.widefat td.column-job_position .company{margin-top:.2em;display:block;padding-top:2px;color:#bbb}.widefat .column-job_location{width:10%}.widefat .column-job_actions{text-align:right;width:128px}.widefat .column-job_actions strong{display:block;margin-bottom:.2em}.widefat .column-job_actions .actions{padding-top:2px}.widefat .column-job_actions a.button{display:inline-block;margin:0 0 2px 4px;cursor:pointer;padding:0 6px!important;font-size:1em!important;line-height:2em!important;overflow:hidden}.widefat .column-job_actions a.button-icon{width:2em!important;padding:0!important}.widefat .column-job_actions a.button-icon:before{font-family:job-manager!important;font-style:normal;font-weight:400;speak:none;display:inline-block;text-decoration:inherit;width:1em;text-align:center;font-variant:normal;text-transform:none;line-height:1em;float:left;width:2em!important;line-height:2em}.widefat .column-job_actions .icon-view:before{content:'\e805'}.widefat .column-job_actions .icon-edit:before{content:'\e804'}.widefat .column-job_actions .icon-delete:before{content:'\e82b'}.widefat .column-job_actions .icon-approve:before{content:'\e802'}.wp_job_manager_meta_data{zoom:1}.wp_job_manager_meta_data:after,.wp_job_manager_meta_data:before{content:"";display:table}.wp_job_manager_meta_data:after{clear:both}.wp_job_manager_meta_data .form-field{width:50%;line-height:2em;float:left;box-sizing:border-box;padding:0 12px 0 0;margin:0 0 12px;clear:both}.wp_job_manager_meta_data .form-field:nth-child(even){float:right;padding:0 0 0 12px;clear:right}.wp_job_manager_meta_data .form-field:nth-last-child(-n+2){margin-bottom:0;padding-bottom:0;border-bottom:0}.wp_job_manager_meta_data .form-field label{vertical-align:middle;display:block;font-weight:700;margin:0}.wp_job_manager_meta_data .form-field .tips{cursor:help;float:right;font-weight:400;color:#999}.wp_job_manager_meta_data .form-field input{width:100%;margin:1px 0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;vertical-align:middle}.wp_job_manager_meta_data .form-field input.checkbox,.wp_job_manager_meta_data .form-field input.radio{width:auto;margin:4px 2px;display:inline-block}.wp_job_manager_meta_data .form-field .description{display:block;color:#999}.wp_job_manager_meta_data .form-field.form-field-checkbox .description{display:inline}.wp_job_manager_meta_data .form-field .file_url input{width:75%}.wp_job_manager_meta_data .form-field .button{margin-left:4px}.wp_job_manager_meta_data .form-field .file_no_url{-o-animation:flash .3s linear infinite alternate;-webkit-animation:flash .3s linear infinite alternate;-moz-animation:flash .3s linear infinite alternate;animation:flash .3s linear infinite alternate}@-o-keyframes flash{from{background-color:unset}to{background-color:#dc3232}}@-ms-keyframes flash{from{background-color:unset}to{background-color:#dc3232}}@-moz-keyframes flash{from{background-color:unset}to{background-color:#dc3232}}@-webkit-keyframes flash{from{background-color:unset}to{background-color:#dc3232}}@keyframes flash{from{background-color:unset}to{background-color:#dc3232}}#tiptip_holder{display:none;position:absolute;top:0;left:0;z-index:99999}#tiptip_holder.tip_top{padding-bottom:5px}#tiptip_holder.tip_bottom{padding-top:5px}#tiptip_holder.tip_right{padding-left:5px}#tiptip_holder.tip_left{padding-right:5px}#tiptip_content{font-size:11px;color:#fff;padding:4px 8px;background:#464646;border-radius:3px;-webkit-border-radius:3px;-moz-border-radius:3px;box-shadow:1px 1px 3px rgba(0,0,0,.1);-webkit-box-shadow:1px 1px 3px rgba(0,0,0,.1);-moz-box-shadow:1px 1px 3px rgba(0,0,0,.1);text-align:center}#tiptip_content code{background:#999;padding:1px}#tiptip_arrow,#tiptip_arrow_inner{position:absolute;border-color:transparent;border-style:solid;border-width:6px;height:0;width:0}#tiptip_holder.tip_top #tiptip_arrow_inner{margin-top:-7px;margin-left:-6px;border-top-color:#464646}#tiptip_holder.tip_bottom #tiptip_arrow_inner{margin-top:-5px;margin-left:-6px;border-bottom-color:#464646}#tiptip_holder.tip_right #tiptip_arrow_inner{margin-top:-6px;margin-left:-5px;border-right-color:#464646}#tiptip_holder.tip_left #tiptip_arrow_inner{margin-top:-6px;margin-left:-7px;border-left-color:#464646}.wp_job_manager_add_ons_wrap .products{overflow:hidden}.wp_job_manager_add_ons_wrap .products li{float:left;margin:0 1em 1em 0!important;padding:0;vertical-align:top;width:350px}.wp_job_manager_add_ons_wrap .products li a{text-decoration:none;color:inherit;border:1px solid #ddd;display:block;min-height:220px;overflow:hidden;background:#f6f6f6;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),inset 0 -1px 0 rgba(0,0,0,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),inset 0 -1px 0 rgba(0,0,0,.1)}.wp_job_manager_add_ons_wrap .products li img{display:inline-block;width:auto;max-height:60px;max-width:90px;margin:0 5px 0 0;float:left}.wp_job_manager_add_ons_wrap .products li h2{margin:0!important;padding:20px!important;background:#fff;height:20px;font-size:16px}.wp_job_manager_add_ons_wrap .products li p{padding:20px!important;margin:0!important;border-top:1px solid #f1f1f1}.wp_job_manager_add_ons_wrap .products li .price{display:none}.rtl .widefat .column-job_actions a.button-icon:before{float:right}.rtl .wp_job_manager_meta_data p{padding:0 20% 0 0}.rtl .wp_job_manager_meta_data label{left:auto;right:0}@media only screen and (max-width:782px){.wpjm-licences .plugin-info{padding:10px}.wpjm-licences .plugin-licence{padding:10px}.widefat .job_position.column-primary{display:table-cell!important}.widefat .toggle-row:before{top:5px}.widefat .column-job_actions{text-align:left}.widefat .column-job_actions a.button-icon:before{float:left}.rtl .widefat .column-job_actions{text-align:right}.rtl .widefat .column-job_actions a.button-icon:before{float:right}.wp_job_manager_meta_data .form-field{width:100%;padding:0}.wp_job_manager_meta_data .form-field:nth-child(even){float:none;padding:0;margin-bottom:12px;clear:both}.wp_job_manager_meta_data .form-field:nth-last-child(-n+2){float:none;padding:0;margin-bottom:12px;clear:both}}
|
1 |
+
.clearfix{zoom:1}.clearfix:after,.clearfix:before{content:"";display:table}.clearfix:after{clear:both}@font-face{font-family:job-manager;src:url(../font/job-manager.eot?4963673);src:url(../font/job-manager.eot?4963673#iefix) format('embedded-opentype'),url(../font/job-manager.woff?4963673) format('woff'),url(../font/job-manager.ttf?4963673) format('truetype'),url(../font/job-manager.svg?4963673#job-manager) format('svg');font-weight:400;font-style:normal}@font-face{font-family:jm-logo;src:url(../font/jm-logo/jm.eot?ycsbky);src:url(../font/jm-logo/jm.eot?#iefixycsbky) format('embedded-opentype'),url(../font/jm-logo/jm.woff?ycsbky) format('woff'),url(../font/jm-logo/jm.ttf?ycsbky) format('truetype'),url(../font/jm-logo/jm.svg?ycsbky#icomoon) format('svg');font-weight:400;font-style:normal}.jm-icon{font-family:job-manager!important;font-style:normal;font-weight:400;speak:none;display:inline-block;text-decoration:inherit;width:1em;text-align:center;font-variant:normal;text-transform:none;line-height:1em}.job-manager-settings-wrap .updated{display:none}.job-manager-settings-wrap .job-manager-updated{display:block;margin:1em 0 0}a.wpjm-activate-licence-link,a.wpjm-activate-licence-link:active,a.wpjm-activate-licence-link:hover,a.wpjm-activate-licence-link:link,a.wpjm-activate-licence-link:visited{color:#ff4500}.wpjm-licences{margin-top:10px}.wpjm-licences .licence-row{align-items:center;border:solid 1px #e2e0e2;display:flex;background-color:#fff;flex-wrap:wrap;min-height:82px;margin-bottom:20px;position:relative}.wpjm-licences .plugin-info{font-size:18px;flex-basis:320px;padding:0 20px;margin-right:10px}.wpjm-licences .plugin-info .plugin-author{font-size:12px}.wpjm-licences .plugin-licence{flex:1;flex-basis:40%;padding-bottom:5px}.wpjm-licences .plugin-licence label{white-space:nowrap}.widefat td.column-featured_job,.widefat td.column-filled,.widefat td.column-job_status{width:46px;text-align:left;padding-left:11px}.widefat th.column-featured_job,.widefat th.column-filled,.widefat th.column-job_status{width:1em}.widefat th.column-featured_job span,.widefat th.column-filled span,.widefat th.column-job_status span{display:block;width:1em;height:1em;line-height:1em;padding:1px 0 0 0;overflow:hidden}.widefat th.column-featured_job span:before,.widefat th.column-filled span:before,.widefat th.column-job_status span:before{content:'\e803';font-family:job-manager!important;font-style:normal;font-weight:400;speak:none;display:inline-block;text-decoration:inherit;width:1em;text-align:center;font-variant:normal;text-transform:none;line-height:1em}.widefat th.column-filled span:before{content:'\e807'}.widefat th.column-job_status span:before{content:'\e828'}.widefat .column-job_posted strong{display:block;margin-bottom:.2em}.widefat td.column-job_status span{position:relative;font-size:1em;line-height:1.5em;width:1em;height:0;padding:2em 0 0 0;overflow:hidden;display:block}.widefat td.column-job_status span:before{font-family:job-manager!important;font-style:normal;font-weight:400;speak:none;display:inline-block;text-decoration:inherit;width:1em;text-align:center;font-variant:normal;text-transform:none;line-height:1em;position:absolute;top:0;left:0;line-height:1.5em;vertical-align:middle;color:#999;content:'\e829'}.widefat td.column-job_status .status-trash:before{content:'\e82b';color:#a00}.widefat td.column-job_status .status-pending:before{content:'\e82c';color:#ffba00}.widefat td.column-job_status .status-publish:before{content:'\e82f';color:#73a724}.widefat td.column-job_status .status-expired:before{content:'\e82e';color:#a00}.widefat .column-job_listing_type{text-align:left;width:6em;word-wrap:normal!important}.widefat .column-job_listing_type .job-type{color:#fff;padding:4px;font-size:11px;-webkit-border-radius:2px;border-radius:2px;display:block;background-color:#f08d3c;text-align:center}.widefat .column-job_listing_type .full-time{background-color:#90da36}.widefat .column-job_listing_type .part-time{background-color:#f08d3c}.widefat .column-job_listing_type .temporary{background-color:#d93674}.widefat .column-job_listing_type .freelance{background-color:#39c}.widefat .column-job_listing_type .internship{background-color:#6033cc}.widefat th.column-job_position{width:20%}.widefat td.column-job_position{width:20%;height:34px}.widefat td.column-job_position .job_position{position:relative;padding-right:50px!important}.widefat td.column-job_position a.job_title{font-weight:700}.widefat td.column-job_position img{width:32px;height:32px;position:absolute;right:7px;top:0;-webkit-border-radius:50%;border-radius:50%;box-shadow:0 1px 0 1px rgba(0,0,0,.1);-webkit-box-shadow:0 1px 0 1px rgba(0,0,0,.1);-moz-box-shadow:0 1px 0 1px rgba(0,0,0,.1);border:1px solid #fff}.widefat td.column-job_position .company{margin-top:.2em;display:block;padding-top:2px;color:#bbb}.widefat .column-job_location{width:10%}.widefat .column-job_actions{text-align:right;width:128px}.widefat .column-job_actions strong{display:block;margin-bottom:.2em}.widefat .column-job_actions .actions{padding-top:2px}.widefat .column-job_actions a.button{display:inline-block;margin:0 0 2px 4px;cursor:pointer;padding:0 6px!important;font-size:1em!important;line-height:2em!important;overflow:hidden}.widefat .column-job_actions a.button-icon{width:2em!important;padding:0!important}.widefat .column-job_actions a.button-icon:before{font-family:job-manager!important;font-style:normal;font-weight:400;speak:none;display:inline-block;text-decoration:inherit;width:1em;text-align:center;font-variant:normal;text-transform:none;line-height:1em;float:left;width:2em!important;line-height:2em}.widefat .column-job_actions .icon-view:before{content:'\e805'}.widefat .column-job_actions .icon-edit:before{content:'\e804'}.widefat .column-job_actions .icon-delete:before{content:'\e82b'}.widefat .column-job_actions .icon-approve:before{content:'\e802'}.wp_job_manager_meta_data{zoom:1}.wp_job_manager_meta_data:after,.wp_job_manager_meta_data:before{content:"";display:table}.wp_job_manager_meta_data:after{clear:both}.wp_job_manager_meta_data .form-field{width:50%;line-height:2em;float:left;box-sizing:border-box;padding:0 12px 0 0;margin:0 0 12px;clear:both}.wp_job_manager_meta_data .form-field:nth-child(even){float:right;padding:0 0 0 12px;clear:right}.wp_job_manager_meta_data .form-field:nth-last-child(-n+2){margin-bottom:0;padding-bottom:0;border-bottom:0}.wp_job_manager_meta_data .form-field label{vertical-align:middle;display:block;font-weight:700;margin:0}.wp_job_manager_meta_data .form-field .tips{cursor:help;float:right;font-weight:400;color:#999}.wp_job_manager_meta_data .form-field input{width:100%;margin:1px 0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;vertical-align:middle}.wp_job_manager_meta_data .form-field input.checkbox,.wp_job_manager_meta_data .form-field input.radio{width:auto;margin:4px 2px;display:inline-block}.wp_job_manager_meta_data .form-field .description{display:block;color:#999}.wp_job_manager_meta_data .form-field.form-field-checkbox .description{display:inline}.wp_job_manager_meta_data .form-field .file_url input{width:75%}.wp_job_manager_meta_data .form-field .button{margin-left:4px}.wp_job_manager_meta_data .form-field .file_no_url{-o-animation:flash .3s linear infinite alternate;-webkit-animation:flash .3s linear infinite alternate;-moz-animation:flash .3s linear infinite alternate;animation:flash .3s linear infinite alternate}@-o-keyframes flash{from{background-color:unset}to{background-color:#dc3232}}@-ms-keyframes flash{from{background-color:unset}to{background-color:#dc3232}}@-moz-keyframes flash{from{background-color:unset}to{background-color:#dc3232}}@-webkit-keyframes flash{from{background-color:unset}to{background-color:#dc3232}}@keyframes flash{from{background-color:unset}to{background-color:#dc3232}}#tiptip_holder{display:none;position:absolute;top:0;left:0;z-index:99999}#tiptip_holder.tip_top{padding-bottom:5px}#tiptip_holder.tip_bottom{padding-top:5px}#tiptip_holder.tip_right{padding-left:5px}#tiptip_holder.tip_left{padding-right:5px}#tiptip_content{font-size:11px;color:#fff;padding:4px 8px;background:#464646;border-radius:3px;-webkit-border-radius:3px;-moz-border-radius:3px;box-shadow:1px 1px 3px rgba(0,0,0,.1);-webkit-box-shadow:1px 1px 3px rgba(0,0,0,.1);-moz-box-shadow:1px 1px 3px rgba(0,0,0,.1);text-align:center}#tiptip_content code{background:#999;padding:1px}#tiptip_arrow,#tiptip_arrow_inner{position:absolute;border-color:transparent;border-style:solid;border-width:6px;height:0;width:0}#tiptip_holder.tip_top #tiptip_arrow_inner{margin-top:-7px;margin-left:-6px;border-top-color:#464646}#tiptip_holder.tip_bottom #tiptip_arrow_inner{margin-top:-5px;margin-left:-6px;border-bottom-color:#464646}#tiptip_holder.tip_right #tiptip_arrow_inner{margin-top:-6px;margin-left:-5px;border-right-color:#464646}#tiptip_holder.tip_left #tiptip_arrow_inner{margin-top:-6px;margin-left:-7px;border-left-color:#464646}.wp_job_manager_add_ons_wrap .products{overflow:hidden}.wp_job_manager_add_ons_wrap .products li{float:left;margin:0 1em 1em 0!important;padding:0;vertical-align:top;width:350px}.wp_job_manager_add_ons_wrap .products li a{text-decoration:none;color:inherit;border:1px solid #ddd;display:block;min-height:220px;overflow:hidden;background:#f6f6f6;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),inset 0 -1px 0 rgba(0,0,0,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),inset 0 -1px 0 rgba(0,0,0,.1)}.wp_job_manager_add_ons_wrap .products li img{display:inline-block;width:auto;max-height:60px;max-width:90px;margin:0 5px 0 0;float:left}.wp_job_manager_add_ons_wrap .products li h2{margin:0!important;padding:20px!important;background:#fff;height:20px;font-size:16px}.wp_job_manager_add_ons_wrap .products li p{padding:20px!important;margin:0!important;border-top:1px solid #f1f1f1}.wp_job_manager_add_ons_wrap .products li .price{display:none}.rtl .widefat .column-job_actions a.button-icon:before{float:right}.rtl .wp_job_manager_meta_data p{padding:0 20% 0 0}.rtl .wp_job_manager_meta_data label{left:auto;right:0}table.form-table.settings tr{border-bottom:1px solid rgba(0,0,0,.1)}table.form-table.settings tr.no-separator,table.form-table.settings tr:last-child{border-bottom:0}div.setting-enable-expand{border:1px solid rgba(0,0,0,.1);padding:15px 10px}div.setting-enable-expand .sub-settings-expandable{display:none;padding-left:25px}div.setting-enable-expand .sub-settings-expandable.expanded{display:block}tr.email-setting-row td{padding:5px}@media only screen and (max-width:782px){.wpjm-licences .plugin-info{padding:10px}.wpjm-licences .plugin-licence{padding:10px}.widefat .job_position.column-primary{display:table-cell!important}.widefat .toggle-row:before{top:5px}.widefat .column-job_actions{text-align:left}.widefat .column-job_actions a.button-icon:before{float:left}.rtl .widefat .column-job_actions{text-align:right}.rtl .widefat .column-job_actions a.button-icon:before{float:right}.wp_job_manager_meta_data .form-field{width:100%;padding:0}.wp_job_manager_meta_data .form-field:nth-child(even){float:none;padding:0;margin-bottom:12px;clear:both}.wp_job_manager_meta_data .form-field:nth-last-child(-n+2){float:none;padding:0;margin-bottom:12px;clear:both}}
|
assets/css/admin.less
CHANGED
@@ -451,6 +451,41 @@ a.wpjm-activate-licence-link:active {
|
|
451 |
}
|
452 |
}
|
453 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
454 |
/**
|
455 |
* Mobile styles
|
456 |
*/
|
451 |
}
|
452 |
}
|
453 |
|
454 |
+
/**
|
455 |
+
* Settings
|
456 |
+
*/
|
457 |
+
|
458 |
+
table.form-table.settings {
|
459 |
+
tr {
|
460 |
+
border-bottom: 1px solid rgba( 0, 0, 0, .1);
|
461 |
+
|
462 |
+
&:last-child,
|
463 |
+
&.no-separator {
|
464 |
+
border-bottom: 0;
|
465 |
+
}
|
466 |
+
}
|
467 |
+
}
|
468 |
+
|
469 |
+
div.setting-enable-expand {
|
470 |
+
border: 1px solid rgba( 0, 0, 0, .1);
|
471 |
+
padding: 15px 10px;
|
472 |
+
|
473 |
+
.sub-settings-expandable {
|
474 |
+
display: none;
|
475 |
+
padding-left: 25px;
|
476 |
+
|
477 |
+
&.expanded {
|
478 |
+
display: block;
|
479 |
+
}
|
480 |
+
}
|
481 |
+
}
|
482 |
+
|
483 |
+
tr.email-setting-row {
|
484 |
+
td {
|
485 |
+
padding: 5px;
|
486 |
+
}
|
487 |
+
}
|
488 |
+
|
489 |
/**
|
490 |
* Mobile styles
|
491 |
*/
|
assets/js/admin.js
CHANGED
@@ -19,7 +19,7 @@ jQuery(document).ready(function($) {
|
|
19 |
var file_target_input;
|
20 |
var file_target_wrapper;
|
21 |
|
22 |
-
$(document).on('click', '.wp_job_manager_add_another_file_button', function( event ){
|
23 |
event.preventDefault();
|
24 |
|
25 |
var field_name = $( this ).data( 'field_name' );
|
@@ -31,7 +31,7 @@ jQuery(document).ready(function($) {
|
|
31 |
$( this ).before( '<span class="file_url"><input type="text" name="' + field_name + '[]" placeholder="' + field_placeholder + '" /><button class="button button-small wp_job_manager_upload_file_button" data-uploader_button_text="' + button_text + '">' + button + '</button><button class="button button-small wp_job_manager_view_file_button">' + view_button + '</button></span>' );
|
32 |
} );
|
33 |
|
34 |
-
$(document).on('click', '.wp_job_manager_view_file_button', function ( event ) {
|
35 |
event.preventDefault();
|
36 |
|
37 |
file_target_wrapper = $( this ).closest( '.file_url' );
|
@@ -50,7 +50,7 @@ jQuery(document).ready(function($) {
|
|
50 |
|
51 |
});
|
52 |
|
53 |
-
$(document).on('click', '.wp_job_manager_upload_file_button', function( event ){
|
54 |
event.preventDefault();
|
55 |
|
56 |
file_target_wrapper = $( this ).closest('.file_url');
|
19 |
var file_target_input;
|
20 |
var file_target_wrapper;
|
21 |
|
22 |
+
$( document.body ).on('click', '.wp_job_manager_add_another_file_button', function( event ){
|
23 |
event.preventDefault();
|
24 |
|
25 |
var field_name = $( this ).data( 'field_name' );
|
31 |
$( this ).before( '<span class="file_url"><input type="text" name="' + field_name + '[]" placeholder="' + field_placeholder + '" /><button class="button button-small wp_job_manager_upload_file_button" data-uploader_button_text="' + button_text + '">' + button + '</button><button class="button button-small wp_job_manager_view_file_button">' + view_button + '</button></span>' );
|
32 |
} );
|
33 |
|
34 |
+
$( document.body ).on('click', '.wp_job_manager_view_file_button', function ( event ) {
|
35 |
event.preventDefault();
|
36 |
|
37 |
file_target_wrapper = $( this ).closest( '.file_url' );
|
50 |
|
51 |
});
|
52 |
|
53 |
+
$( document.body ).on('click', '.wp_job_manager_upload_file_button', function( event ){
|
54 |
event.preventDefault();
|
55 |
|
56 |
file_target_wrapper = $( this ).closest('.file_url');
|
assets/js/admin.min.js
CHANGED
@@ -1 +1 @@
|
|
1 |
-
jQuery(document).ready(function(t){t(".tips, .help_tip").tipTip({attribute:"data-tip",fadeIn:50,fadeOut:50,delay:200}),t("p.form-field-author").on("click","a.change-author",function(){return t(this).closest("p").find(".current-author").hide(),t(this).closest("p").find(".change-author").show(),!1});var e,a,
|
1 |
+
jQuery(document).ready(function(t){t(".tips, .help_tip").tipTip({attribute:"data-tip",fadeIn:50,fadeOut:50,delay:200}),t("p.form-field-author").on("click","a.change-author",function(){return t(this).closest("p").find(".current-author").hide(),t(this).closest("p").find(".change-author").show(),!1});var e,a,o;t(document.body).on("click",".wp_job_manager_add_another_file_button",function(e){e.preventDefault();var a=t(this).data("field_name"),o=t(this).data("field_placeholder"),i=t(this).data("uploader_button_text"),n=t(this).data("uploader_button"),l=t(this).data("view_button");t(this).before('<span class="file_url"><input type="text" name="'+a+'[]" placeholder="'+o+'" /><button class="button button-small wp_job_manager_upload_file_button" data-uploader_button_text="'+i+'">'+n+'</button><button class="button button-small wp_job_manager_view_file_button">'+l+"</button></span>")}),t(document.body).on("click",".wp_job_manager_view_file_button",function(e){e.preventDefault(),o=t(this).closest(".file_url");var i=(a=o.find("input")).val();i.indexOf("://")>-1?window.open(i,"_blank"):(a.addClass("file_no_url"),setTimeout(function(){a.removeClass("file_no_url")},1e3))}),t(document.body).on("click",".wp_job_manager_upload_file_button",function(i){i.preventDefault(),o=t(this).closest(".file_url"),a=o.find("input"),e?e.open():((e=wp.media.frames.file_frame=wp.media({title:t(this).data("uploader_title"),button:{text:t(this).data("uploader_button_text")},multiple:!1})).on("select",function(){var o=e.state().get("selection").first().toJSON();t(a).val(o.url)}),e.open())})}),jQuery(document).ready(function(t){var e="job_listing_type";t("#"+e+"checklist li :radio, #"+e+"checklist-pop :radio").live("click",function(){var a=t(this),o=a.is(":checked"),i=a.val();t("#"+e+"checklist li :radio, #"+e+"checklist-pop :radio").prop("checked",!1),t("#in-"+e+"-"+i+", #in-popular-"+e+"-"+i).prop("checked",o)})});
|
assets/js/job-application.js
CHANGED
@@ -4,7 +4,7 @@ jQuery(document).ready(function($) {
|
|
4 |
$( '.application_details' ).hide();
|
5 |
}
|
6 |
|
7 |
-
$(
|
8 |
var $details = $(this).siblings('.application_details').first();
|
9 |
var $button = $(this);
|
10 |
$details.slideToggle( 400, function() {
|
4 |
$( '.application_details' ).hide();
|
5 |
}
|
6 |
|
7 |
+
$( document.body ).on( 'click', '.job_application .application_button', function() {
|
8 |
var $details = $(this).siblings('.application_details').first();
|
9 |
var $button = $(this);
|
10 |
$details.slideToggle( 400, function() {
|
assets/js/job-application.min.js
CHANGED
@@ -1 +1 @@
|
|
1 |
-
jQuery(document).ready(function(i){i("body").hasClass("job-application-details-keep-open")||i(".application_details").hide(),i(
|
1 |
+
jQuery(document).ready(function(i){i("body").hasClass("job-application-details-keep-open")||i(".application_details").hide(),i(document.body).on("click",".job_application .application_button",function(){var t=i(this).siblings(".application_details").first(),o=i(this);t.slideToggle(400,function(){if(i(this).is(":visible")){var e=Math.max(Math.min(t.outerHeight(),200),.33*t.outerHeight()),a=t.offset().top+e,n=5;i("#wpadminbar").length>0&&"fixed"===i("#wpadminbar").css("position")&&(n+=i("#wpadminbar").outerHeight()),i("header").length>0&&"fixed"===i("header").css("position")&&(n+=i("header").outerHeight());var s=i(window).scrollTop()+window.innerHeight,l=t.offset().top+t.outerHeight()-s,d=window.innerHeight-n;l>0&&t.outerHeight()<.9*d?i("html, body").animate({scrollTop:i(window).scrollTop()+l+5},400):s<a&&i("html, body").animate({scrollTop:o.offset().top-n},600)}})})});
|
assets/js/job-submission.js
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
jQuery(document).ready(function($) {
|
2 |
-
$(
|
3 |
$(this).closest( '.job-manager-uploaded-file' ).remove();
|
4 |
return false;
|
5 |
});
|
6 |
-
$(
|
7 |
$(this).find( '.spinner' ).addClass( 'is-active' );
|
8 |
$(this).find( 'input[type=submit]' ).addClass( 'disabled' ).on( 'click', function() { return false; } );
|
9 |
});
|
1 |
jQuery(document).ready(function($) {
|
2 |
+
$( document.body ).on( 'click', '.job-manager-remove-uploaded-file', function() {
|
3 |
$(this).closest( '.job-manager-uploaded-file' ).remove();
|
4 |
return false;
|
5 |
});
|
6 |
+
$( document.body ).on( 'submit', '.job-manager-form:not(.prevent-spinner-behavior)', function() {
|
7 |
$(this).find( '.spinner' ).addClass( 'is-active' );
|
8 |
$(this).find( 'input[type=submit]' ).addClass( 'disabled' ).on( 'click', function() { return false; } );
|
9 |
});
|
assets/js/job-submission.min.js
CHANGED
@@ -1 +1 @@
|
|
1 |
-
jQuery(document).ready(function(n){n(
|
1 |
+
jQuery(document).ready(function(n){n(document.body).on("click",".job-manager-remove-uploaded-file",function(){return n(this).closest(".job-manager-uploaded-file").remove(),!1}),n(document.body).on("submit",".job-manager-form:not(.prevent-spinner-behavior)",function(){n(this).find(".spinner").addClass("is-active"),n(this).find("input[type=submit]").addClass("disabled").on("click",function(){return!1})})});
|
changelog.txt
CHANGED
@@ -1,3 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
= 1.30.2 =
|
2 |
* Enhancement: Show notice when user is using an older version of WordPress.
|
3 |
* Enhancement: Hide unnecessary view mode in WP Admin's Job Listings page. (@RajeebTheGreat)
|
1 |
+
= 1.31.0 =
|
2 |
+
* Change: Minimum WordPress version is now 4.7.0.
|
3 |
+
* Enhancement: Add email notifications with initial support for new jobs, updated jobs, and expiring listings.
|
4 |
+
* Enhancement: For GDPR, scrub WPJM data from database on uninstall if option is enabled.
|
5 |
+
* Enhancement: Filter by Filled and Featured status in WP admin.
|
6 |
+
* Enhancement: Simplify the display of application URLs.
|
7 |
+
* Enhancement: When using WPML, prevent changes to page options when on a non-default language. (@vukvukovich)
|
8 |
+
* Enhancement: Include company logo in structured data. (@RajeebTheGreat)
|
9 |
+
* Enhancement: Use more efficient jQuery selectors in scripts. (@RajeebTheGreat)
|
10 |
+
* Enhancement: Use proper `<h2>` tag in `content-summary-job_listing.php` template for the job title. (@abdullah1908)
|
11 |
+
* Enhancement: Hide empty categories on `[job]` filter.
|
12 |
+
* Fix: Update calls to `get_terms()` to use the new format.
|
13 |
+
* Fix: Maintain the current tab when saving settings in WP Admin.
|
14 |
+
* Fix: Enqueue the date picker CSS when used on the front-end.
|
15 |
+
* Fix: Remove errors when widget instance was created without setting defaults.
|
16 |
+
* REST API Pre-release: Add support for job category taxonomy endpoints.
|
17 |
+
* Dev: Add `$job_id` parameter to `job_manager_job_dashboard_do_action_{$action}` action hook. (@jonasvogel)
|
18 |
+
* Dev: Add support for hidden WPJM settings in WP Admin.
|
19 |
+
|
20 |
= 1.30.2 =
|
21 |
* Enhancement: Show notice when user is using an older version of WordPress.
|
22 |
* Enhancement: Hide unnecessary view mode in WP Admin's Job Listings page. (@RajeebTheGreat)
|
includes/3rd-party/wpml.php
CHANGED
@@ -12,7 +12,16 @@ function wpml_wpjm_init() {
|
|
12 |
add_action( 'get_job_listings_init', 'wpml_wpjm_set_language' );
|
13 |
add_filter( 'wpjm_lang', 'wpml_wpjm_get_job_listings_lang' );
|
14 |
add_filter( 'wpjm_page_id', 'wpml_wpjm_page_id' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
}
|
|
|
16 |
add_action( 'wpml_loaded', 'wpml_wpjm_init' );
|
17 |
add_action( 'wpml_loaded', 'wpml_wpjm_set_language' );
|
18 |
|
@@ -37,6 +46,7 @@ function wpml_wpjm_set_language() {
|
|
37 |
* @since 1.26.0
|
38 |
*
|
39 |
* @param string $lang
|
|
|
40 |
* @return string
|
41 |
*/
|
42 |
function wpml_wpjm_get_job_listings_lang( $lang ) {
|
@@ -47,8 +57,43 @@ function wpml_wpjm_get_job_listings_lang( $lang ) {
|
|
47 |
* Returns the page ID for the current language.
|
48 |
*
|
49 |
* @param int $page_id
|
|
|
50 |
* @return int
|
51 |
*/
|
52 |
function wpml_wpjm_page_id( $page_id ) {
|
53 |
return apply_filters( 'wpml_object_id', $page_id, 'page', true );
|
54 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
add_action( 'get_job_listings_init', 'wpml_wpjm_set_language' );
|
13 |
add_filter( 'wpjm_lang', 'wpml_wpjm_get_job_listings_lang' );
|
14 |
add_filter( 'wpjm_page_id', 'wpml_wpjm_page_id' );
|
15 |
+
|
16 |
+
$default_lang = apply_filters( 'wpml_default_language', null );
|
17 |
+
$current_lang = apply_filters( 'wpml_current_language', null );
|
18 |
+
|
19 |
+
// Add filter only for non default languages.
|
20 |
+
if ( $current_lang !== $default_lang ) {
|
21 |
+
add_filter( 'job_manager_settings', 'wpml_wpjm_hide_page_selection' );
|
22 |
+
}
|
23 |
}
|
24 |
+
|
25 |
add_action( 'wpml_loaded', 'wpml_wpjm_init' );
|
26 |
add_action( 'wpml_loaded', 'wpml_wpjm_set_language' );
|
27 |
|
46 |
* @since 1.26.0
|
47 |
*
|
48 |
* @param string $lang
|
49 |
+
*
|
50 |
* @return string
|
51 |
*/
|
52 |
function wpml_wpjm_get_job_listings_lang( $lang ) {
|
57 |
* Returns the page ID for the current language.
|
58 |
*
|
59 |
* @param int $page_id
|
60 |
+
*
|
61 |
* @return int
|
62 |
*/
|
63 |
function wpml_wpjm_page_id( $page_id ) {
|
64 |
return apply_filters( 'wpml_object_id', $page_id, 'page', true );
|
65 |
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Set WPJM page options to hidden for non default languages.
|
69 |
+
*
|
70 |
+
* @since 1.31.0
|
71 |
+
*
|
72 |
+
* @param array $settings
|
73 |
+
*
|
74 |
+
* @return array
|
75 |
+
*/
|
76 |
+
function wpml_wpjm_hide_page_selection( $settings ) {
|
77 |
+
foreach ( $settings['job_pages'][1] as $key => $setting ) {
|
78 |
+
if ( 'page' !== $setting['type'] ) {
|
79 |
+
continue;
|
80 |
+
}
|
81 |
+
$setting['type'] = 'hidden';
|
82 |
+
$setting['human_value'] = __( 'Page Not Set', 'wp-job-manager' );
|
83 |
+
$current_value = get_option( $setting['name'] );
|
84 |
+
if ( $current_value ) {
|
85 |
+
$page = get_post( apply_filters( 'wpml_object_id', $current_value, 'page' ) );
|
86 |
+
|
87 |
+
if ( $page ) {
|
88 |
+
$setting['human_value'] = $page->post_title;
|
89 |
+
}
|
90 |
+
}
|
91 |
+
|
92 |
+
$default_lang = apply_filters( 'wpml_default_language', null );
|
93 |
+
$url_to_edit_page = admin_url( 'edit.php?post_type=job_listing&page=job-manager-settings&lang=' . $default_lang . '#settings-job_pages' );
|
94 |
+
$setting['desc'] = sprintf( __( '<a href="%s">Switch to primary language</a> to edit this setting.', 'wp-job-manager' ), $url_to_edit_page );
|
95 |
+
$settings['job_pages'][1][ $key ] = $setting;
|
96 |
+
}
|
97 |
+
|
98 |
+
return $settings;
|
99 |
+
}
|
includes/abstracts/abstract-wp-job-manager-email-template.php
ADDED
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
4 |
+
exit; // Exit if accessed directly
|
5 |
+
}
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Abstract class for an email notification built using templates.
|
9 |
+
*
|
10 |
+
* @package wp-job-manager
|
11 |
+
*
|
12 |
+
* @since 1.31.0
|
13 |
+
*/
|
14 |
+
|
15 |
+
abstract class WP_Job_Manager_Email_Template extends WP_Job_Manager_Email {
|
16 |
+
/**
|
17 |
+
* Get the template path for overriding templates.
|
18 |
+
*
|
19 |
+
* @type abstract
|
20 |
+
* @return string
|
21 |
+
*/
|
22 |
+
public static function get_template_path() {
|
23 |
+
return 'job_manager';
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Get the default template path that WP Job Manager should look for the templates.
|
28 |
+
*
|
29 |
+
* @type abstract
|
30 |
+
* @return string
|
31 |
+
*/
|
32 |
+
public static function get_template_default_path() {
|
33 |
+
return '';
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Get the rich text version of the email content.
|
38 |
+
*
|
39 |
+
* @return string
|
40 |
+
*/
|
41 |
+
public function get_rich_content() {
|
42 |
+
return $this->get_template( false );
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Get the plaintext version of the email content.
|
47 |
+
*
|
48 |
+
* @return string
|
49 |
+
*/
|
50 |
+
public function get_plain_content() {
|
51 |
+
if ( $this->has_template( true ) ) {
|
52 |
+
return $this->get_template( true );
|
53 |
+
}
|
54 |
+
return parent::get_plain_content();
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Get the contents of a template.
|
59 |
+
*
|
60 |
+
* @param bool $plain_text
|
61 |
+
* @return string
|
62 |
+
*/
|
63 |
+
public function get_template( $plain_text = false ) {
|
64 |
+
$template_file = $this->locate_template( $plain_text );
|
65 |
+
if ( ! $template_file ) {
|
66 |
+
return '';
|
67 |
+
}
|
68 |
+
$args = $this->get_args();
|
69 |
+
$email = $this;
|
70 |
+
|
71 |
+
ob_start();
|
72 |
+
include $template_file;
|
73 |
+
return ob_get_clean();
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Check to see if a template exists for this email.
|
78 |
+
*
|
79 |
+
* @param bool $plain_text
|
80 |
+
* @return bool
|
81 |
+
*/
|
82 |
+
public function has_template( $plain_text = false ) {
|
83 |
+
$template_file = $this->locate_template( $plain_text );
|
84 |
+
return $template_file && file_exists( $template_file );
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Locate template for this email.
|
89 |
+
*
|
90 |
+
* @param bool $plain_text
|
91 |
+
* @return string
|
92 |
+
*/
|
93 |
+
protected function locate_template( $plain_text ) {
|
94 |
+
$class_name = get_class( $this );
|
95 |
+
$template_path = call_user_func( array( $class_name, 'get_template_path' ) );
|
96 |
+
$template_default_path = call_user_func( array( $class_name, 'get_template_default_path' ) );
|
97 |
+
return locate_job_manager_template( $this->get_template_file_name( $plain_text ), $template_path, $template_default_path );
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Generate the file name for the email template.
|
102 |
+
*
|
103 |
+
* @param bool $plain_text
|
104 |
+
* @return string
|
105 |
+
*/
|
106 |
+
protected function get_template_file_name( $plain_text = false ) {
|
107 |
+
$class_name = get_class( $this );
|
108 |
+
// PHP 5.2: Using `call_user_func()` but `$class_name::get_key()` preferred.
|
109 |
+
$email_notification_key = call_user_func( array( $class_name, 'get_key') );
|
110 |
+
$template_name = str_replace( '_', '-', $email_notification_key );
|
111 |
+
return self::generate_template_file_name( $template_name, $plain_text );
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Generate the file name for the email template.
|
116 |
+
*
|
117 |
+
* @param string $template_name
|
118 |
+
* @param bool $plain_text
|
119 |
+
* @return string
|
120 |
+
*/
|
121 |
+
public static function generate_template_file_name( $template_name, $plain_text = false ) {
|
122 |
+
$file_name_parts = array( 'emails' );
|
123 |
+
if ( $plain_text ) {
|
124 |
+
$file_name_parts[] = 'plain';
|
125 |
+
}
|
126 |
+
|
127 |
+
$file_name_parts[] = $template_name . '.php';
|
128 |
+
|
129 |
+
return implode( '/', $file_name_parts );
|
130 |
+
}
|
131 |
+
}
|
includes/abstracts/abstract-wp-job-manager-email.php
ADDED
@@ -0,0 +1,223 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
4 |
+
exit; // Exit if accessed directly
|
5 |
+
}
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Abstract class for an email notification.
|
9 |
+
*
|
10 |
+
* Do not rely on WordPress global variables or functions that rely on global variables such as `wp_get_current_user()`.
|
11 |
+
* Email might be generated when no longer in scope. Instead, pass the values on as an argument when initiating the email
|
12 |
+
* notification.
|
13 |
+
*
|
14 |
+
* Additionally, inside of plugins and themes, load email notification files based on this class inside the
|
15 |
+
* `job_manager_email_init` hook. This will prevent unnecessary loading and won't include the files if this abstract
|
16 |
+
* class isn't available.
|
17 |
+
*
|
18 |
+
* Example:
|
19 |
+
* ```
|
20 |
+
* add_action( 'job_manager_email_init', 'custom_plugin_include_emails' );
|
21 |
+
* function custom_plugin_include_emails() {
|
22 |
+
* include_once 'emails/custom-plugin-sent-resume.php`;
|
23 |
+
* }
|
24 |
+
* ```
|
25 |
+
*
|
26 |
+
* @package wp-job-manager
|
27 |
+
*
|
28 |
+
* @since 1.31.0
|
29 |
+
*/
|
30 |
+
|
31 |
+
abstract class WP_Job_Manager_Email {
|
32 |
+
/**
|
33 |
+
* @var array
|
34 |
+
*/
|
35 |
+
private $args = array();
|
36 |
+
|
37 |
+
/**
|
38 |
+
* @var array
|
39 |
+
*/
|
40 |
+
private $settings = array();
|
41 |
+
|
42 |
+
/**
|
43 |
+
* WP_Job_Manager_Email constructor.
|
44 |
+
*
|
45 |
+
* @param array $args Arguments used in forming email notification.
|
46 |
+
* @param array $settings Settings for this notification.
|
47 |
+
*/
|
48 |
+
final public function __construct( $args, $settings ) {
|
49 |
+
$this->args = $this->prepare_args( (array) $args );
|
50 |
+
$this->settings = (array) $settings;
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Get the unique email notification key.
|
55 |
+
*
|
56 |
+
* @type abstract
|
57 |
+
*
|
58 |
+
* @return string
|
59 |
+
*/
|
60 |
+
public static function get_key() {
|
61 |
+
return false;
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Get the friendly name for this email notification.
|
66 |
+
*
|
67 |
+
* @type abstract
|
68 |
+
* @return string
|
69 |
+
*/
|
70 |
+
public static function get_name() {
|
71 |
+
return false;
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Get the description for this email notification.
|
76 |
+
*
|
77 |
+
* @type abstract
|
78 |
+
* @return string
|
79 |
+
*/
|
80 |
+
public static function get_description() {
|
81 |
+
return '';
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Get the context for where this email notification is used. Used to direct which admin settings to show.
|
86 |
+
*
|
87 |
+
* @type abstract
|
88 |
+
* @return string
|
89 |
+
*/
|
90 |
+
public static function get_context() {
|
91 |
+
return 'job_manager';
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Get the email subject.
|
96 |
+
*
|
97 |
+
* @return string
|
98 |
+
*/
|
99 |
+
abstract public function get_subject();
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Get `From:` address header value. Can be simple email or formatted `Firstname Lastname <email@example.com>`.
|
103 |
+
*
|
104 |
+
* @return string|bool Email from value or false to use WordPress' default.
|
105 |
+
*/
|
106 |
+
abstract public function get_from();
|
107 |
+
|
108 |
+
/**
|
109 |
+
* Get array or comma-separated list of email addresses to send message.
|
110 |
+
*
|
111 |
+
* @return string|array
|
112 |
+
*/
|
113 |
+
abstract public function get_to();
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Get the rich text version of the email content.
|
117 |
+
*
|
118 |
+
* @return string
|
119 |
+
*/
|
120 |
+
abstract public function get_rich_content();
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Expand arguments as necessary for the generation of the email.
|
124 |
+
*
|
125 |
+
* @param $args
|
126 |
+
* @return mixed
|
127 |
+
*/
|
128 |
+
protected function prepare_args( $args ) {
|
129 |
+
if ( isset( $args['job_id'] ) ) {
|
130 |
+
$job = get_post( $args['job_id'] );
|
131 |
+
if ( $job instanceof WP_Post ) {
|
132 |
+
$args['job'] = $job;
|
133 |
+
}
|
134 |
+
}
|
135 |
+
if ( isset( $args['job'] ) && $args['job'] instanceof WP_Post ) {
|
136 |
+
$author = get_user_by( 'ID', $args['job']->post_author );
|
137 |
+
if ( $author instanceof WP_User ) {
|
138 |
+
$args['author'] = $author;
|
139 |
+
}
|
140 |
+
}
|
141 |
+
|
142 |
+
return $args;
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Checks the arguments and returns whether the email notification is properly set up.
|
147 |
+
*
|
148 |
+
* @return bool
|
149 |
+
*/
|
150 |
+
abstract public function is_valid();
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Returns the list of file paths to attach to an email.
|
154 |
+
*
|
155 |
+
* @return array
|
156 |
+
*/
|
157 |
+
public function get_attachments() {
|
158 |
+
return array();
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* Returns the value of the CC header, if needed.
|
163 |
+
*
|
164 |
+
* @return string|null
|
165 |
+
*/
|
166 |
+
public function get_cc() {
|
167 |
+
return null;
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Get the base headers for the email. No need to add CC or From headers. Content-type is added when sending rich-text.
|
172 |
+
*
|
173 |
+
* @return array
|
174 |
+
*/
|
175 |
+
public function get_headers() {
|
176 |
+
return array();
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* Get the plaintext version of the email content.
|
181 |
+
*
|
182 |
+
* @return string
|
183 |
+
*/
|
184 |
+
public function get_plain_content() {
|
185 |
+
return strip_tags( $this->get_rich_content() );
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* Get the settings for this email notifications.
|
190 |
+
*
|
191 |
+
* @return array
|
192 |
+
*/
|
193 |
+
public static function get_setting_fields() {
|
194 |
+
return array();
|
195 |
+
}
|
196 |
+
|
197 |
+
/**
|
198 |
+
* Is this email notification enabled by default?
|
199 |
+
*
|
200 |
+
* @return bool
|
201 |
+
*/
|
202 |
+
public static function is_default_enabled() {
|
203 |
+
return true;
|
204 |
+
}
|
205 |
+
|
206 |
+
/**
|
207 |
+
* Returns the args that the email notification was sent with.
|
208 |
+
*
|
209 |
+
* @return array
|
210 |
+
*/
|
211 |
+
final protected function get_args() {
|
212 |
+
return $this->args;
|
213 |
+
}
|
214 |
+
|
215 |
+
/**
|
216 |
+
* Returns the settings values.
|
217 |
+
*
|
218 |
+
* @return array
|
219 |
+
*/
|
220 |
+
final protected function get_settings() {
|
221 |
+
return $this->settings;
|
222 |
+
}
|
223 |
+
}
|
includes/admin/class-wp-job-manager-admin.php
CHANGED
@@ -83,7 +83,7 @@ class WP_Job_Manager_Admin {
|
|
83 |
}
|
84 |
|
85 |
echo '<div class="error">';
|
86 |
-
echo '<p>' . sprintf( __( '
|
87 |
echo '</div>';
|
88 |
}
|
89 |
|
@@ -116,14 +116,10 @@ class WP_Job_Manager_Admin {
|
|
116 |
* Enqueues CSS and JS assets.
|
117 |
*/
|
118 |
public function admin_enqueue_scripts() {
|
119 |
-
global $wp_scripts;
|
120 |
-
|
121 |
$screen = get_current_screen();
|
122 |
|
123 |
if ( in_array( $screen->id, apply_filters( 'job_manager_admin_screen_ids', array( 'edit-job_listing', 'plugins', 'job_listing', 'job_listing_page_job-manager-settings', 'job_listing_page_job-manager-addons' ) ) ) ) {
|
124 |
-
|
125 |
-
|
126 |
-
wp_enqueue_style( 'jquery-ui-style', '//code.jquery.com/ui/' . $jquery_version . '/themes/smoothness/jquery-ui.css', array(), $jquery_version );
|
127 |
wp_enqueue_style( 'job_manager_admin_css', JOB_MANAGER_PLUGIN_URL . '/assets/css/admin.css', array(), JOB_MANAGER_VERSION );
|
128 |
wp_register_script( 'jquery-tiptip', JOB_MANAGER_PLUGIN_URL. '/assets/js/jquery-tiptip/jquery.tipTip.min.js', array( 'jquery' ), JOB_MANAGER_VERSION, true );
|
129 |
wp_enqueue_script( 'job_manager_datepicker_js', JOB_MANAGER_PLUGIN_URL. '/assets/js/datepicker.min.js', array( 'jquery', 'jquery-ui-datepicker' ), JOB_MANAGER_VERSION, true );
|
83 |
}
|
84 |
|
85 |
echo '<div class="error">';
|
86 |
+
echo '<p>' . sprintf( __( '<strong>WP Job Manager</strong> requires a more recent version of WordPress. <a href="%s">Please update WordPresse</a> to avoid issues.', 'wp-job-manager' ), esc_url( self_admin_url( 'update-core.php' ) ) ) . '</p>';
|
87 |
echo '</div>';
|
88 |
}
|
89 |
|
116 |
* Enqueues CSS and JS assets.
|
117 |
*/
|
118 |
public function admin_enqueue_scripts() {
|
|
|
|
|
119 |
$screen = get_current_screen();
|
120 |
|
121 |
if ( in_array( $screen->id, apply_filters( 'job_manager_admin_screen_ids', array( 'edit-job_listing', 'plugins', 'job_listing', 'job_listing_page_job-manager-settings', 'job_listing_page_job-manager-addons' ) ) ) ) {
|
122 |
+
wp_enqueue_style( 'jquery-ui' );
|
|
|
|
|
123 |
wp_enqueue_style( 'job_manager_admin_css', JOB_MANAGER_PLUGIN_URL . '/assets/css/admin.css', array(), JOB_MANAGER_VERSION );
|
124 |
wp_register_script( 'jquery-tiptip', JOB_MANAGER_PLUGIN_URL. '/assets/js/jquery-tiptip/jquery.tipTip.min.js', array( 'jquery' ), JOB_MANAGER_VERSION, true );
|
125 |
wp_enqueue_script( 'job_manager_datepicker_js', JOB_MANAGER_PLUGIN_URL. '/assets/js/datepicker.min.js', array( 'jquery', 'jquery-ui-datepicker' ), JOB_MANAGER_VERSION, true );
|
includes/admin/class-wp-job-manager-cpt.php
CHANGED
@@ -44,6 +44,7 @@ class WP_Job_Manager_CPT {
|
|
44 |
add_filter( 'manage_edit-job_listing_sortable_columns', array( $this, 'sortable_columns' ) );
|
45 |
add_filter( 'request', array( $this, 'sort_columns' ) );
|
46 |
add_action( 'parse_query', array( $this, 'search_meta' ) );
|
|
|
47 |
add_filter( 'get_search_query', array( $this, 'search_meta_label' ) );
|
48 |
add_filter( 'post_updated_messages', array( $this, 'post_updated_messages' ) );
|
49 |
add_action( 'bulk_actions-edit-job_listing', array( $this, 'add_bulk_actions' ) );
|
@@ -55,6 +56,7 @@ class WP_Job_Manager_CPT {
|
|
55 |
if ( get_option( 'job_manager_enable_categories' ) ) {
|
56 |
add_action( "restrict_manage_posts", array( $this, "jobs_by_category" ) );
|
57 |
}
|
|
|
58 |
|
59 |
foreach ( array( 'post', 'post-new' ) as $hook ) {
|
60 |
add_action( "admin_footer-{$hook}.php", array( $this,'extend_submitdiv_post_status' ) );
|
@@ -282,13 +284,14 @@ class WP_Job_Manager_CPT {
|
|
282 |
include_once( JOB_MANAGER_PLUGIN_DIR . '/includes/class-wp-job-manager-category-walker.php' );
|
283 |
|
284 |
$r = array();
|
|
|
285 |
$r['pad_counts'] = 1;
|
286 |
$r['hierarchical'] = 1;
|
287 |
$r['hide_empty'] = 0;
|
288 |
$r['show_count'] = 1;
|
289 |
$r['selected'] = ( isset( $wp_query->query['job_listing_category'] ) ) ? $wp_query->query['job_listing_category'] : '';
|
290 |
$r['menu_order'] = false;
|
291 |
-
$terms = get_terms(
|
292 |
$walker = new WP_Job_Manager_Category_Walker;
|
293 |
|
294 |
if ( ! $terms ) {
|
@@ -303,6 +306,84 @@ class WP_Job_Manager_CPT {
|
|
303 |
echo $output;
|
304 |
}
|
305 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
306 |
/**
|
307 |
* Filters page title placeholder text to show custom label.
|
308 |
*
|
@@ -597,6 +678,45 @@ class WP_Job_Manager_CPT {
|
|
597 |
$wp->query_vars['post__in'] = $post_ids;
|
598 |
}
|
599 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
600 |
/**
|
601 |
* Changes the label when searching meta.
|
602 |
*
|
44 |
add_filter( 'manage_edit-job_listing_sortable_columns', array( $this, 'sortable_columns' ) );
|
45 |
add_filter( 'request', array( $this, 'sort_columns' ) );
|
46 |
add_action( 'parse_query', array( $this, 'search_meta' ) );
|
47 |
+
add_action( 'parse_query', array( $this, 'filter_meta' ) );
|
48 |
add_filter( 'get_search_query', array( $this, 'search_meta_label' ) );
|
49 |
add_filter( 'post_updated_messages', array( $this, 'post_updated_messages' ) );
|
50 |
add_action( 'bulk_actions-edit-job_listing', array( $this, 'add_bulk_actions' ) );
|
56 |
if ( get_option( 'job_manager_enable_categories' ) ) {
|
57 |
add_action( "restrict_manage_posts", array( $this, "jobs_by_category" ) );
|
58 |
}
|
59 |
+
add_action( "restrict_manage_posts", array( $this, "jobs_meta_filters" ) );
|
60 |
|
61 |
foreach ( array( 'post', 'post-new' ) as $hook ) {
|
62 |
add_action( "admin_footer-{$hook}.php", array( $this,'extend_submitdiv_post_status' ) );
|
284 |
include_once( JOB_MANAGER_PLUGIN_DIR . '/includes/class-wp-job-manager-category-walker.php' );
|
285 |
|
286 |
$r = array();
|
287 |
+
$r['taxonomy'] = 'job_listing_category';
|
288 |
$r['pad_counts'] = 1;
|
289 |
$r['hierarchical'] = 1;
|
290 |
$r['hide_empty'] = 0;
|
291 |
$r['show_count'] = 1;
|
292 |
$r['selected'] = ( isset( $wp_query->query['job_listing_category'] ) ) ? $wp_query->query['job_listing_category'] : '';
|
293 |
$r['menu_order'] = false;
|
294 |
+
$terms = get_terms( $r );
|
295 |
$walker = new WP_Job_Manager_Category_Walker;
|
296 |
|
297 |
if ( ! $terms ) {
|
306 |
echo $output;
|
307 |
}
|
308 |
|
309 |
+
/**
|
310 |
+
* Output dropdowns for filters based on post meta.
|
311 |
+
*
|
312 |
+
* @since 1.31.0
|
313 |
+
*/
|
314 |
+
public function jobs_meta_filters() {
|
315 |
+
global $typenow;
|
316 |
+
|
317 |
+
// Only add the filters for job_listings
|
318 |
+
if ( 'job_listing' !== $typenow ) {
|
319 |
+
return;
|
320 |
+
}
|
321 |
+
|
322 |
+
// Filter by Filled.
|
323 |
+
$this->jobs_filter_dropdown( 'job_listing_filled', array(
|
324 |
+
array(
|
325 |
+
'value' => '',
|
326 |
+
'text' => __( 'Select Filled', 'wp-job-manager' ),
|
327 |
+
),
|
328 |
+
array(
|
329 |
+
'value' => '1',
|
330 |
+
'text' => __( 'Filled', 'wp-job-manager' ),
|
331 |
+
),
|
332 |
+
array(
|
333 |
+
'value' => '0',
|
334 |
+
'text' => __( 'Not Filled', 'wp-job-manager' ),
|
335 |
+
),
|
336 |
+
) );
|
337 |
+
|
338 |
+
// Filter by Featured.
|
339 |
+
$this->jobs_filter_dropdown( 'job_listing_featured', array(
|
340 |
+
array(
|
341 |
+
'value' => '',
|
342 |
+
'text' => __( 'Select Featured', 'wp-job-manager' ),
|
343 |
+
),
|
344 |
+
array(
|
345 |
+
'value' => '1',
|
346 |
+
'text' => __( 'Featured', 'wp-job-manager' ),
|
347 |
+
),
|
348 |
+
array(
|
349 |
+
'value' => '0',
|
350 |
+
'text' => __( 'Not Featured', 'wp-job-manager' ),
|
351 |
+
),
|
352 |
+
) );
|
353 |
+
}
|
354 |
+
|
355 |
+
/**
|
356 |
+
* Shows dropdown to filter by the given URL parameter. The dropdown will
|
357 |
+
* have three options: "Select $name", "$name", and "Not $name".
|
358 |
+
*
|
359 |
+
* The $options element should be an array of arrays, each with the
|
360 |
+
* attributes needed to create an <option> HTML element. The attributes are
|
361 |
+
* as follows:
|
362 |
+
*
|
363 |
+
* $options[i]['value'] The value for the <option> HTML element.
|
364 |
+
* $options[i]['text'] The text for the <option> HTML element.
|
365 |
+
*
|
366 |
+
* @since 1.31.0
|
367 |
+
*
|
368 |
+
* @param string $param The URL parameter.
|
369 |
+
* @param array $options The options for the dropdown. See the
|
370 |
+
* description above.
|
371 |
+
*/
|
372 |
+
private function jobs_filter_dropdown( $param, $options ) {
|
373 |
+
$selected = isset( $_GET[ $param ] ) ? $_GET[ $param ] : '';
|
374 |
+
|
375 |
+
$output = "<select name=\"$param\" id=\"dropdown_$param\">";
|
376 |
+
|
377 |
+
foreach ( $options as $option ) {
|
378 |
+
$output .= '<option value="' . esc_attr( $option['value'] ) . '"'
|
379 |
+
. ( $selected === $option['value'] ? ' selected' : '' )
|
380 |
+
. '>' . esc_html( $option['text'] ) . '</option>';
|
381 |
+
}
|
382 |
+
$output .= '</select>';
|
383 |
+
|
384 |
+
echo $output;
|
385 |
+
}
|
386 |
+
|
387 |
/**
|
388 |
* Filters page title placeholder text to show custom label.
|
389 |
*
|
678 |
$wp->query_vars['post__in'] = $post_ids;
|
679 |
}
|
680 |
|
681 |
+
/**
|
682 |
+
* Filters by meta fields.
|
683 |
+
*
|
684 |
+
* @param WP_Query $wp
|
685 |
+
*/
|
686 |
+
public function filter_meta( $wp ) {
|
687 |
+
global $pagenow;
|
688 |
+
|
689 |
+
if ( 'edit.php' !== $pagenow || 'job_listing' !== $wp->query_vars['post_type'] ) {
|
690 |
+
return;
|
691 |
+
}
|
692 |
+
|
693 |
+
$meta_query = $wp->get( 'meta_query' );
|
694 |
+
if ( ! is_array( $meta_query ) ) {
|
695 |
+
$meta_query = array();
|
696 |
+
}
|
697 |
+
|
698 |
+
// Filter on _filled meta.
|
699 |
+
if ( isset( $_GET['job_listing_filled'] ) && '' !== $_GET['job_listing_filled'] ) {
|
700 |
+
$meta_query[] = array(
|
701 |
+
'key' => '_filled',
|
702 |
+
'value' => $_GET['job_listing_filled'],
|
703 |
+
);
|
704 |
+
}
|
705 |
+
|
706 |
+
// Filter on _featured meta.
|
707 |
+
if ( isset( $_GET['job_listing_featured'] ) && '' !== $_GET['job_listing_featured'] ) {
|
708 |
+
$meta_query[] = array(
|
709 |
+
'key' => '_featured',
|
710 |
+
'value' => $_GET['job_listing_featured'],
|
711 |
+
);
|
712 |
+
}
|
713 |
+
|
714 |
+
// Set new meta query.
|
715 |
+
if ( ! empty( $meta_query ) ) {
|
716 |
+
$wp->set( 'meta_query', $meta_query );
|
717 |
+
}
|
718 |
+
}
|
719 |
+
|
720 |
/**
|
721 |
* Changes the label when searching meta.
|
722 |
*
|
includes/admin/class-wp-job-manager-settings.php
CHANGED
@@ -99,6 +99,15 @@ class WP_Job_Manager_Settings {
|
|
99 |
'desc' => sprintf( __( 'Google requires an API key to retrieve location information for job listings. Acquire an API key from the <a href="%s">Google Maps API developer site</a>.', 'wp-job-manager' ), 'https://developers.google.com/maps/documentation/geocoding/get-api-key' ),
|
100 |
'attributes' => array()
|
101 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
),
|
103 |
),
|
104 |
'job_listings' => array(
|
@@ -378,17 +387,17 @@ class WP_Job_Manager_Settings {
|
|
378 |
$this->init_settings();
|
379 |
?>
|
380 |
<div class="wrap job-manager-settings-wrap">
|
381 |
-
<form method="post" action="options.php">
|
382 |
|
383 |
<?php settings_fields( $this->settings_group ); ?>
|
384 |
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
|
393 |
<?php
|
394 |
if ( ! empty( $_GET['settings-updated'] ) ) {
|
@@ -397,142 +406,48 @@ class WP_Job_Manager_Settings {
|
|
397 |
}
|
398 |
|
399 |
foreach ( $this->settings as $key => $section ) {
|
400 |
-
|
401 |
echo '<div id="settings-' . sanitize_title( $key ) . '" class="settings_panel">';
|
402 |
-
|
403 |
-
|
|
|
|
|
404 |
|
405 |
foreach ( $section[1] as $option ) {
|
406 |
-
|
407 |
-
$
|
408 |
-
$class = ! empty( $option['class'] ) ? $option['class'] : '';
|
409 |
-
$value = get_option( $option['name'] );
|
410 |
-
$option['type'] = ! empty( $option['type'] ) ? $option['type'] : '';
|
411 |
-
$attributes = array();
|
412 |
-
|
413 |
-
if ( ! empty( $option['attributes'] ) && is_array( $option['attributes'] ) )
|
414 |
-
foreach ( $option['attributes'] as $attribute_name => $attribute_value )
|
415 |
-
$attributes[] = esc_attr( $attribute_name ) . '="' . esc_attr( $attribute_value ) . '"';
|
416 |
-
|
417 |
-
echo '<tr valign="top" class="' . $class . '"><th scope="row"><label for="setting-' . $option['name'] . '">' . $option['label'] . '</a></th><td>';
|
418 |
-
|
419 |
-
switch ( $option['type'] ) {
|
420 |
-
|
421 |
-
case "checkbox" :
|
422 |
-
|
423 |
-
?><label><input id="setting-<?php echo $option['name']; ?>" name="<?php echo $option['name']; ?>" type="checkbox" value="1" <?php echo implode( ' ', $attributes ); ?> <?php checked( '1', $value ); ?> /> <?php echo $option['cb_label']; ?></label><?php
|
424 |
-
|
425 |
-
if ( $option['desc'] )
|
426 |
-
echo ' <p class="description">' . $option['desc'] . '</p>';
|
427 |
-
|
428 |
-
break;
|
429 |
-
case "textarea" :
|
430 |
-
|
431 |
-
?><textarea id="setting-<?php echo $option['name']; ?>" class="large-text" cols="50" rows="3" name="<?php echo $option['name']; ?>" <?php echo implode( ' ', $attributes ); ?> <?php echo $placeholder; ?>><?php echo esc_textarea( $value ); ?></textarea><?php
|
432 |
-
|
433 |
-
if ( $option['desc'] )
|
434 |
-
echo ' <p class="description">' . $option['desc'] . '</p>';
|
435 |
-
|
436 |
-
break;
|
437 |
-
case "select" :
|
438 |
-
|
439 |
-
?><select id="setting-<?php echo $option['name']; ?>" class="regular-text" name="<?php echo $option['name']; ?>" <?php echo implode( ' ', $attributes ); ?>><?php
|
440 |
-
foreach( $option['options'] as $key => $name )
|
441 |
-
echo '<option value="' . esc_attr( $key ) . '" ' . selected( $value, $key, false ) . '>' . esc_html( $name ) . '</option>';
|
442 |
-
?></select><?php
|
443 |
-
|
444 |
-
if ( $option['desc'] ) {
|
445 |
-
echo ' <p class="description">' . $option['desc'] . '</p>';
|
446 |
-
}
|
447 |
-
|
448 |
-
break;
|
449 |
-
case "radio":
|
450 |
-
?><fieldset>
|
451 |
-
<legend class="screen-reader-text">
|
452 |
-
<span><?php echo esc_html( $option['label'] ); ?></span>
|
453 |
-
</legend><?php
|
454 |
-
|
455 |
-
if ( $option['desc'] ) {
|
456 |
-
echo '<p class="description">' . $option['desc'] . '</p>';
|
457 |
-
}
|
458 |
-
|
459 |
-
foreach( $option['options'] as $key => $name )
|
460 |
-
echo '<label><input name="' . esc_attr( $option['name'] ) . '" type="radio" value="' . esc_attr( $key ) . '" ' . checked( $value, $key, false ) . ' />' . esc_html( $name ) . '</label><br>';
|
461 |
-
|
462 |
-
?></fieldset><?php
|
463 |
-
|
464 |
-
break;
|
465 |
-
case "page" :
|
466 |
-
|
467 |
-
$args = array(
|
468 |
-
'name' => $option['name'],
|
469 |
-
'id' => $option['name'],
|
470 |
-
'sort_column' => 'menu_order',
|
471 |
-
'sort_order' => 'ASC',
|
472 |
-
'show_option_none' => __( '--no page--', 'wp-job-manager' ),
|
473 |
-
'echo' => false,
|
474 |
-
'selected' => absint( $value )
|
475 |
-
);
|
476 |
-
|
477 |
-
echo str_replace(' id=', " data-placeholder='" . __( 'Select a page…', 'wp-job-manager' ) . "' id=", wp_dropdown_pages( $args ) );
|
478 |
-
|
479 |
-
if ( $option['desc'] ) {
|
480 |
-
echo ' <p class="description">' . $option['desc'] . '</p>';
|
481 |
-
}
|
482 |
-
|
483 |
-
break;
|
484 |
-
case "password" :
|
485 |
-
|
486 |
-
?><input id="setting-<?php echo $option['name']; ?>" class="regular-text" type="password" name="<?php echo $option['name']; ?>" value="<?php echo esc_attr( $value ); ?>" <?php echo implode( ' ', $attributes ); ?> <?php echo $placeholder; ?> /><?php
|
487 |
-
|
488 |
-
if ( $option['desc'] ) {
|
489 |
-
echo ' <p class="description">' . $option['desc'] . '</p>';
|
490 |
-
}
|
491 |
-
|
492 |
-
break;
|
493 |
-
case "number" :
|
494 |
-
?><input id="setting-<?php echo $option['name']; ?>" class="regular-text" type="number" name="<?php echo $option['name']; ?>" value="<?php echo esc_attr( $value ); ?>" <?php echo implode( ' ', $attributes ); ?> <?php echo $placeholder; ?> /><?php
|
495 |
-
|
496 |
-
if ( $option['desc'] ) {
|
497 |
-
echo ' <p class="description">' . $option['desc'] . '</p>';
|
498 |
-
}
|
499 |
-
break;
|
500 |
-
case "" :
|
501 |
-
case "input" :
|
502 |
-
case "text" :
|
503 |
-
?><input id="setting-<?php echo $option['name']; ?>" class="regular-text" type="text" name="<?php echo $option['name']; ?>" value="<?php echo esc_attr( $value ); ?>" <?php echo implode( ' ', $attributes ); ?> <?php echo $placeholder; ?> /><?php
|
504 |
-
|
505 |
-
if ( $option['desc'] ) {
|
506 |
-
echo ' <p class="description">' . $option['desc'] . '</p>';
|
507 |
-
}
|
508 |
-
break;
|
509 |
-
default :
|
510 |
-
do_action( 'wp_job_manager_admin_field_' . $option['type'], $option, $attributes, $value, $placeholder );
|
511 |
-
break;
|
512 |
-
|
513 |
-
}
|
514 |
-
|
515 |
-
echo '</td></tr>';
|
516 |
}
|
517 |
|
518 |
-
echo '</table
|
|
|
|
|
|
|
|
|
519 |
|
520 |
}
|
521 |
?>
|
522 |
<p class="submit">
|
523 |
<input type="submit" class="button-primary" value="<?php _e( 'Save Changes', 'wp-job-manager' ); ?>" />
|
524 |
</p>
|
525 |
-
|
526 |
</div>
|
527 |
<script type="text/javascript">
|
528 |
jQuery('.nav-tab-wrapper a').click(function() {
|
|
|
|
|
|
|
529 |
jQuery('.settings_panel').hide();
|
530 |
jQuery('.nav-tab-active').removeClass('nav-tab-active');
|
531 |
jQuery( jQuery(this).attr('href') ).show();
|
532 |
jQuery(this).addClass('nav-tab-active');
|
|
|
|
|
533 |
return false;
|
534 |
});
|
535 |
var goto_hash = window.location.hash;
|
|
|
|
|
|
|
536 |
if ( goto_hash ) {
|
537 |
var the_tab = jQuery( 'a[href="' + goto_hash + '"]' );
|
538 |
if ( the_tab.length > 0 ) {
|
@@ -567,7 +482,7 @@ class WP_Job_Manager_Settings {
|
|
567 |
|
568 |
$generate_username_from_email.change(function() {
|
569 |
if ( jQuery( this ).is(':checked') ) {
|
570 |
-
|
571 |
} else {
|
572 |
$use_standard_password_setup_email.prop('disabled', false);
|
573 |
if ( undefined !== $use_standard_password_setup_email.data('original-state') ) {
|
@@ -575,7 +490,274 @@ class WP_Job_Manager_Settings {
|
|
575 |
}
|
576 |
}
|
577 |
}).change();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
578 |
</script>
|
579 |
<?php
|
580 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
581 |
}
|
99 |
'desc' => sprintf( __( 'Google requires an API key to retrieve location information for job listings. Acquire an API key from the <a href="%s">Google Maps API developer site</a>.', 'wp-job-manager' ), 'https://developers.google.com/maps/documentation/geocoding/get-api-key' ),
|
100 |
'attributes' => array()
|
101 |
),
|
102 |
+
array(
|
103 |
+
'name' => 'job_manager_delete_data_on_uninstall',
|
104 |
+
'std' => '0',
|
105 |
+
'label' => __( 'Delete Data On Uninstall', 'wp-job-manager' ),
|
106 |
+
'cb_label' => __( 'Delete WP Job Manager data when the plugin is deleted. Once removed, this data cannot be restored.', 'wp-job-manager' ),
|
107 |
+
'desc' => '',
|
108 |
+
'type' => 'checkbox',
|
109 |
+
'attributes' => array()
|
110 |
+
),
|
111 |
),
|
112 |
),
|
113 |
'job_listings' => array(
|
387 |
$this->init_settings();
|
388 |
?>
|
389 |
<div class="wrap job-manager-settings-wrap">
|
390 |
+
<form class="job-manager-options" method="post" action="options.php">
|
391 |
|
392 |
<?php settings_fields( $this->settings_group ); ?>
|
393 |
|
394 |
+
<h2 class="nav-tab-wrapper">
|
395 |
+
<?php
|
396 |
+
foreach ( $this->settings as $key => $section ) {
|
397 |
+
echo '<a href="#settings-' . sanitize_title( $key ) . '" class="nav-tab">' . esc_html( $section[0] ) . '</a>';
|
398 |
+
}
|
399 |
+
?>
|
400 |
+
</h2>
|
401 |
|
402 |
<?php
|
403 |
if ( ! empty( $_GET['settings-updated'] ) ) {
|
406 |
}
|
407 |
|
408 |
foreach ( $this->settings as $key => $section ) {
|
409 |
+
$section_args = isset( $section[2] ) ? (array) $section[2] : array();
|
410 |
echo '<div id="settings-' . sanitize_title( $key ) . '" class="settings_panel">';
|
411 |
+
if ( ! empty( $section_args['before'] ) ) {
|
412 |
+
echo '<p class="before-settings">' . $section_args['before'] . '</p>';
|
413 |
+
}
|
414 |
+
echo '<table class="form-table settings parent-settings">';
|
415 |
|
416 |
foreach ( $section[1] as $option ) {
|
417 |
+
$value = get_option( $option['name'] );
|
418 |
+
$this->output_field( $option, $value );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
419 |
}
|
420 |
|
421 |
+
echo '</table>';
|
422 |
+
if ( ! empty( $section_args['after'] ) ) {
|
423 |
+
echo '<p class="after-settings">' . $section_args['after'] . '</p>';
|
424 |
+
}
|
425 |
+
echo '</div>';
|
426 |
|
427 |
}
|
428 |
?>
|
429 |
<p class="submit">
|
430 |
<input type="submit" class="button-primary" value="<?php _e( 'Save Changes', 'wp-job-manager' ); ?>" />
|
431 |
</p>
|
432 |
+
</form>
|
433 |
</div>
|
434 |
<script type="text/javascript">
|
435 |
jQuery('.nav-tab-wrapper a').click(function() {
|
436 |
+
if ( '#' !== jQuery(this).attr( 'href' ).substr( 0, 1 ) ) {
|
437 |
+
return false;
|
438 |
+
}
|
439 |
jQuery('.settings_panel').hide();
|
440 |
jQuery('.nav-tab-active').removeClass('nav-tab-active');
|
441 |
jQuery( jQuery(this).attr('href') ).show();
|
442 |
jQuery(this).addClass('nav-tab-active');
|
443 |
+
window.location.hash = jQuery(this).attr('href');
|
444 |
+
jQuery( 'form.job-manager-options' ).attr( 'action', 'options.php' + jQuery(this).attr( 'href' ) );
|
445 |
return false;
|
446 |
});
|
447 |
var goto_hash = window.location.hash;
|
448 |
+
if ( '#' === goto_hash.substr( 0, 1 ) ) {
|
449 |
+
jQuery( 'form.job-manager-options' ).attr( 'action', 'options.php' + jQuery(this).attr( 'href' ) );
|
450 |
+
}
|
451 |
if ( goto_hash ) {
|
452 |
var the_tab = jQuery( 'a[href="' + goto_hash + '"]' );
|
453 |
if ( the_tab.length > 0 ) {
|
482 |
|
483 |
$generate_username_from_email.change(function() {
|
484 |
if ( jQuery( this ).is(':checked') ) {
|
485 |
+
$use_standard_password_setup_email.data('original-state', $use_standard_password_setup_email.is(':checked')).prop('checked', true).prop('disabled', true);
|
486 |
} else {
|
487 |
$use_standard_password_setup_email.prop('disabled', false);
|
488 |
if ( undefined !== $use_standard_password_setup_email.data('original-state') ) {
|
490 |
}
|
491 |
}
|
492 |
}).change();
|
493 |
+
|
494 |
+
jQuery( '.sub-settings-expander' ).on( 'change', function() {
|
495 |
+
var $expandable = jQuery(this).parent().siblings( '.sub-settings-expandable' );
|
496 |
+
var checked = jQuery(this).is( ':checked' );
|
497 |
+
if ( checked ) {
|
498 |
+
$expandable.addClass( 'expanded' );
|
499 |
+
} else {
|
500 |
+
$expandable.removeClass( 'expanded' );
|
501 |
+
}
|
502 |
+
} ).trigger( 'change' );
|
503 |
</script>
|
504 |
<?php
|
505 |
}
|
506 |
+
|
507 |
+
/**
|
508 |
+
* Checkbox input field.
|
509 |
+
*
|
510 |
+
* @param array $option
|
511 |
+
* @param array $attributes
|
512 |
+
* @param mixed $value
|
513 |
+
* @param string $placeholder (Ignored).
|
514 |
+
*/
|
515 |
+
protected function input_checkbox( $option, $attributes, $value, $placeholder ) {
|
516 |
+
?><label>
|
517 |
+
<input type="hidden" name="<?php echo $option['name']; ?>" value="0" />
|
518 |
+
<input id="setting-<?php echo $option['name']; ?>" name="<?php echo $option['name']; ?>" type="checkbox" value="1" <?php echo implode( ' ', $attributes ); ?> <?php checked( '1', $value ); ?> /> <?php echo $option['cb_label']; ?></label><?php
|
519 |
+
|
520 |
+
if ( ! empty( $option['desc'] ) ) {
|
521 |
+
echo ' <p class="description">' . $option['desc'] . '</p>';
|
522 |
+
}
|
523 |
+
}
|
524 |
+
|
525 |
+
/**
|
526 |
+
* Text area input field.
|
527 |
+
*
|
528 |
+
* @param array $option
|
529 |
+
* @param array $attributes
|
530 |
+
* @param mixed $value
|
531 |
+
* @param string $placeholder
|
532 |
+
*/
|
533 |
+
protected function input_textarea ( $option, $attributes, $value, $placeholder ) {
|
534 |
+
?><textarea id="setting-<?php echo $option['name']; ?>" class="large-text" cols="50" rows="3" name="<?php echo $option['name']; ?>" <?php echo implode( ' ', $attributes ); ?> <?php echo $placeholder; ?>><?php echo esc_textarea( $value ); ?></textarea><?php
|
535 |
+
|
536 |
+
if ( ! empty( $option['desc'] ) ) {
|
537 |
+
echo ' <p class="description">' . $option['desc'] . '</p>';
|
538 |
+
}
|
539 |
+
}
|
540 |
+
|
541 |
+
/**
|
542 |
+
* Select input field.
|
543 |
+
*
|
544 |
+
* @param array $option
|
545 |
+
* @param array $attributes
|
546 |
+
* @param mixed $value
|
547 |
+
* @param string $placeholder (Ignored).
|
548 |
+
*/
|
549 |
+
protected function input_select( $option, $attributes, $value, $placeholder ) {
|
550 |
+
?><select id="setting-<?php echo $option['name']; ?>" class="regular-text" name="<?php echo $option['name']; ?>" <?php echo implode( ' ', $attributes ); ?>><?php
|
551 |
+
foreach( $option['options'] as $key => $name )
|
552 |
+
echo '<option value="' . esc_attr( $key ) . '" ' . selected( $value, $key, false ) . '>' . esc_html( $name ) . '</option>';
|
553 |
+
?></select><?php
|
554 |
+
|
555 |
+
if ( ! empty( $option['desc'] ) ) {
|
556 |
+
echo ' <p class="description">' . $option['desc'] . '</p>';
|
557 |
+
}
|
558 |
+
}
|
559 |
+
|
560 |
+
/**
|
561 |
+
* Radio input field.
|
562 |
+
*
|
563 |
+
* @param array $option
|
564 |
+
* @param array $attributes
|
565 |
+
* @param mixed $value
|
566 |
+
* @param string $placeholder (Ignored).
|
567 |
+
*/
|
568 |
+
protected function input_radio( $option, $attributes, $value, $placeholder ) {
|
569 |
+
?><fieldset>
|
570 |
+
<legend class="screen-reader-text">
|
571 |
+
<span><?php echo esc_html( $option['label'] ); ?></span>
|
572 |
+
</legend><?php
|
573 |
+
if ( ! empty( $option['desc'] ) ) {
|
574 |
+
echo ' <p class="description">' . $option['desc'] . '</p>';
|
575 |
+
}
|
576 |
+
|
577 |
+
foreach( $option['options'] as $key => $name ) {
|
578 |
+
echo '<label><input name="' . esc_attr( $option['name'] ) . '" type="radio" value="' . esc_attr( $key ) . '" ' . checked( $value, $key, false ) . ' />' . esc_html( $name ) . '</label><br>';
|
579 |
+
}
|
580 |
+
?></fieldset><?php
|
581 |
+
}
|
582 |
+
|
583 |
+
/**
|
584 |
+
* Page input field.
|
585 |
+
*
|
586 |
+
* @param array $option
|
587 |
+
* @param array $attributes
|
588 |
+
* @param mixed $value
|
589 |
+
* @param string $placeholder (Ignored).
|
590 |
+
*/
|
591 |
+
protected function input_page( $option, $attributes, $value, $placeholder ) {
|
592 |
+
$args = array(
|
593 |
+
'name' => $option['name'],
|
594 |
+
'id' => $option['name'],
|
595 |
+
'sort_column' => 'menu_order',
|
596 |
+
'sort_order' => 'ASC',
|
597 |
+
'show_option_none' => __( '--no page--', 'wp-job-manager' ),
|
598 |
+
'echo' => false,
|
599 |
+
'selected' => absint( $value )
|
600 |
+
);
|
601 |
+
|
602 |
+
echo str_replace(' id=', " data-placeholder='" . __( 'Select a page…', 'wp-job-manager' ) . "' id=", wp_dropdown_pages( $args ) );
|
603 |
+
|
604 |
+
if ( ! empty( $option['desc'] ) ) {
|
605 |
+
echo ' <p class="description">' . $option['desc'] . '</p>';
|
606 |
+
}
|
607 |
+
}
|
608 |
+
|
609 |
+
/**
|
610 |
+
* Hidden input field.
|
611 |
+
*
|
612 |
+
* @param array $option
|
613 |
+
* @param array $attributes
|
614 |
+
* @param mixed $value
|
615 |
+
* @param string $placeholder (Ignored).
|
616 |
+
*/
|
617 |
+
protected function input_hidden( $option, $attributes, $value, $placeholder ) {
|
618 |
+
$human_value = $value;
|
619 |
+
if( $option['human_value'] ) {
|
620 |
+
$human_value = $option['human_value'];
|
621 |
+
}
|
622 |
+
?><input id="setting-<?php echo $option['name']; ?>" type="hidden" name="<?php echo $option['name']; ?>" value="<?php echo esc_attr( $value ); ?>" <?php echo implode( ' ', $attributes ); ?> /><strong><?php echo esc_html( $human_value ) ?></strong><?php
|
623 |
+
|
624 |
+
if ( ! empty( $option['desc'] ) ) {
|
625 |
+
echo ' <p class="description">' . $option['desc'] . '</p>';
|
626 |
+
}
|
627 |
+
}
|
628 |
+
|
629 |
+
/**
|
630 |
+
* Password input field.
|
631 |
+
*
|
632 |
+
* @param array $option
|
633 |
+
* @param array $attributes
|
634 |
+
* @param mixed $value
|
635 |
+
* @param string $placeholder
|
636 |
+
*/
|
637 |
+
protected function input_password( $option, $attributes, $value, $placeholder ) {
|
638 |
+
?><input id="setting-<?php echo $option['name']; ?>" class="regular-text" type="password" name="<?php echo $option['name']; ?>" value="<?php echo esc_attr( $value ); ?>" <?php echo implode( ' ', $attributes ); ?> <?php echo $placeholder; ?> /><?php
|
639 |
+
|
640 |
+
if ( ! empty( $option['desc'] ) ) {
|
641 |
+
echo ' <p class="description">' . $option['desc'] . '</p>';
|
642 |
+
}
|
643 |
+
}
|
644 |
+
|
645 |
+
/**
|
646 |
+
* Number input field.
|
647 |
+
*
|
648 |
+
* @param array $option
|
649 |
+
* @param array $attributes
|
650 |
+
* @param mixed $value
|
651 |
+
* @param string $placeholder
|
652 |
+
*/
|
653 |
+
protected function input_number( $option, $attributes, $value, $placeholder ) {
|
654 |
+
echo isset( $option['before'] ) ? $option['before'] : '';
|
655 |
+
?><input id="setting-<?php echo $option['name']; ?>" class="small-text" type="number" name="<?php echo $option['name']; ?>" value="<?php echo esc_attr( $value ); ?>" <?php echo implode( ' ', $attributes ); ?> <?php echo $placeholder; ?> /><?php
|
656 |
+
echo isset( $option['after'] ) ? $option['after'] : '';
|
657 |
+
if ( ! empty( $option['desc'] ) ) {
|
658 |
+
echo ' <p class="description">' . $option['desc'] . '</p>';
|
659 |
+
}
|
660 |
+
}
|
661 |
+
|
662 |
+
/**
|
663 |
+
* Text input field.
|
664 |
+
*
|
665 |
+
* @param array $option
|
666 |
+
* @param array $attributes
|
667 |
+
* @param mixed $value
|
668 |
+
* @param string $placeholder
|
669 |
+
*/
|
670 |
+
protected function input_text( $option, $attributes, $value, $placeholder ) {
|
671 |
+
?><input id="setting-<?php echo $option['name']; ?>" class="regular-text" type="text" name="<?php echo $option['name']; ?>" value="<?php echo esc_attr( $value ); ?>" <?php echo implode( ' ', $attributes ); ?> <?php echo $placeholder; ?> /><?php
|
672 |
+
|
673 |
+
if ( ! empty( $option['desc'] ) ) {
|
674 |
+
echo ' <p class="description">' . $option['desc'] . '</p>';
|
675 |
+
}
|
676 |
+
}
|
677 |
+
|
678 |
+
/**
|
679 |
+
* Outputs the field row.
|
680 |
+
*
|
681 |
+
* @param array $option
|
682 |
+
* @param mixed $value
|
683 |
+
*/
|
684 |
+
protected function output_field( $option, $value ) {
|
685 |
+
$placeholder = ( ! empty( $option['placeholder'] ) ) ? 'placeholder="' . $option['placeholder'] . '"' : '';
|
686 |
+
$class = ! empty( $option['class'] ) ? $option['class'] : '';
|
687 |
+
$option['type'] = ! empty( $option['type'] ) ? $option['type'] : 'text';
|
688 |
+
$attributes = array();
|
689 |
+
if ( ! empty( $option['attributes'] ) && is_array( $option['attributes'] ) ) {
|
690 |
+
foreach ( $option['attributes'] as $attribute_name => $attribute_value ) {
|
691 |
+
$attributes[] = esc_attr( $attribute_name ) . '="' . esc_attr( $attribute_value ) . '"';
|
692 |
+
}
|
693 |
+
}
|
694 |
+
|
695 |
+
echo '<tr valign="top" class="' . $class . '">';
|
696 |
+
|
697 |
+
if ( ! empty( $option['label'] ) ) {
|
698 |
+
echo '<th scope="row"><label for="setting-' . $option[ 'name' ] . '">' . $option[ 'label' ] . '</a></th><td>';
|
699 |
+
} else {
|
700 |
+
echo '<td colspan="2">';
|
701 |
+
}
|
702 |
+
|
703 |
+
$method_name = 'input_' . $option['type'];
|
704 |
+
if ( method_exists( $this, $method_name ) ) {
|
705 |
+
$this->$method_name( $option, $attributes, $value, $placeholder );
|
706 |
+
} else {
|
707 |
+
do_action( 'wp_job_manager_admin_field_' . $option['type'], $option, $attributes, $value, $placeholder );
|
708 |
+
}
|
709 |
+
echo '</td></tr>';
|
710 |
+
}
|
711 |
+
|
712 |
+
/**
|
713 |
+
* Multiple settings stored in one setting array that are shown when the `enable` setting is checked.
|
714 |
+
*
|
715 |
+
* @param array $option
|
716 |
+
* @param array $attributes
|
717 |
+
* @param array $values
|
718 |
+
* @param string $placeholder
|
719 |
+
*/
|
720 |
+
protected function input_multi_enable_expand( $option, $attributes, $values, $placeholder ) {
|
721 |
+
echo '<div class="setting-enable-expand">';
|
722 |
+
$enable_option = $option['enable_field'];
|
723 |
+
$enable_option['name'] = $option['name'] . '[' . $enable_option['name'] . ']';
|
724 |
+
$enable_option['type'] = 'checkbox';
|
725 |
+
$enable_option['attributes'] = array( 'class="sub-settings-expander"' );
|
726 |
+
$this->input_checkbox( $enable_option, $enable_option['attributes'], $values[ $option['enable_field']['name'] ], null );
|
727 |
+
|
728 |
+
echo '<div class="sub-settings-expandable">';
|
729 |
+
$this->input_multi( $option, $attributes, $values, $placeholder );
|
730 |
+
echo '</div>';
|
731 |
+
echo '</div>';
|
732 |
+
}
|
733 |
+
|
734 |
+
/**
|
735 |
+
* Multiple settings stored in one setting array.
|
736 |
+
*
|
737 |
+
* @param array $option
|
738 |
+
* @param array $attributes
|
739 |
+
* @param array $values
|
740 |
+
* @param string $placeholder
|
741 |
+
*/
|
742 |
+
protected function input_multi( $option, $attributes, $values, $placeholder ) {
|
743 |
+
echo '<table class="form-table settings child-settings">';
|
744 |
+
foreach ( $option['settings'] as $sub_option ) {
|
745 |
+
$value = isset( $values[ $sub_option['name'] ] ) ? $values[ $sub_option['name'] ] : $sub_option['std'];
|
746 |
+
$sub_option['name'] = $option['name'] . '[' . $sub_option['name'] . ']';
|
747 |
+
$this->output_field( $sub_option, $value );
|
748 |
+
}
|
749 |
+
echo '</table>';
|
750 |
+
}
|
751 |
+
|
752 |
+
/**
|
753 |
+
* Proxy for text input field.
|
754 |
+
*
|
755 |
+
* @param array $option
|
756 |
+
* @param array $attributes
|
757 |
+
* @param mixed $value
|
758 |
+
* @param string $placeholder
|
759 |
+
*/
|
760 |
+
protected function input_input( $option, $attributes, $value, $placeholder ) {
|
761 |
+
$this->input_text( $option, $attributes, $value, $placeholder );
|
762 |
+
}
|
763 |
}
|
includes/admin/class-wp-job-manager-writepanels.php
CHANGED
@@ -181,12 +181,12 @@ class WP_Job_Manager_Writepanels {
|
|
181 |
$name = 'tax_input[' . $taxonomy . ']';
|
182 |
|
183 |
// Get all the terms for this taxonomy
|
184 |
-
$terms = get_terms( $taxonomy,
|
185 |
$postterms = get_the_terms( $post->ID, $taxonomy );
|
186 |
$current = ( $postterms ? array_pop( $postterms ) : false );
|
187 |
$current = ( $current ? $current->term_id : 0 );
|
188 |
// Get current and popular terms
|
189 |
-
$popular = get_terms( $taxonomy,
|
190 |
$postterms = get_the_terms( $post->ID,$taxonomy );
|
191 |
$current = ($postterms ? array_pop($postterms) : false);
|
192 |
$current = ($current ? $current->term_id : 0);
|
181 |
$name = 'tax_input[' . $taxonomy . ']';
|
182 |
|
183 |
// Get all the terms for this taxonomy
|
184 |
+
$terms = get_terms( array( 'taxonomy' => $taxonomy, 'hide_empty' => 0 ) );
|
185 |
$postterms = get_the_terms( $post->ID, $taxonomy );
|
186 |
$current = ( $postterms ? array_pop( $postterms ) : false );
|
187 |
$current = ( $current ? $current->term_id : 0 );
|
188 |
// Get current and popular terms
|
189 |
+
$popular = get_terms( array( 'taxonomy' => $taxonomy, 'orderby' => 'count', 'order' => 'DESC', 'number' => 10, 'hierarchical' => false ) );
|
190 |
$postterms = get_the_terms( $post->ID,$taxonomy );
|
191 |
$current = ($postterms ? array_pop($postterms) : false);
|
192 |
$current = ($current ? $current->term_id : 0);
|
includes/class-wp-job-manager-data-cleaner.php
ADDED
@@ -0,0 +1,365 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Defines a class with methods for cleaning up plugin data. To be used when
|
4 |
+
* the plugin is deleted.
|
5 |
+
*
|
6 |
+
* @package Core
|
7 |
+
*/
|
8 |
+
|
9 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
+
// Exit if accessed directly.
|
11 |
+
exit;
|
12 |
+
}
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Methods for cleaning up all plugin data.
|
16 |
+
*
|
17 |
+
* @author Automattic
|
18 |
+
* @since 1.31.0
|
19 |
+
*/
|
20 |
+
class WP_Job_Manager_Data_Cleaner {
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Custom post types to be deleted.
|
24 |
+
*
|
25 |
+
* @var $custom_post_types
|
26 |
+
*/
|
27 |
+
private static $custom_post_types = array(
|
28 |
+
'job_listing',
|
29 |
+
);
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Taxonomies to be deleted.
|
33 |
+
*
|
34 |
+
* @var $taxonomies
|
35 |
+
*/
|
36 |
+
private static $taxonomies = array(
|
37 |
+
'job_listing_category',
|
38 |
+
'job_listing_type',
|
39 |
+
);
|
40 |
+
|
41 |
+
/** Cron jobs to be unscheduled.
|
42 |
+
*
|
43 |
+
* @var $cron_jobs
|
44 |
+
*/
|
45 |
+
private static $cron_jobs = array(
|
46 |
+
'job_manager_check_for_expired_jobs',
|
47 |
+
'job_manager_delete_old_previews',
|
48 |
+
'job_manager_clear_expired_transients',
|
49 |
+
'job_manager_email_daily_notices',
|
50 |
+
'job_manager_usage_tracking_send_usage_data',
|
51 |
+
);
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Options to be deleted.
|
55 |
+
*
|
56 |
+
* @var $options
|
57 |
+
*/
|
58 |
+
private static $options = array(
|
59 |
+
'wp_job_manager_version',
|
60 |
+
'job_manager_installed_terms',
|
61 |
+
'wpjm_permalinks',
|
62 |
+
'job_manager_helper',
|
63 |
+
'job_manager_date_format',
|
64 |
+
'job_manager_google_maps_api_key',
|
65 |
+
'job_manager_usage_tracking_enabled',
|
66 |
+
'job_manager_usage_tracking_opt_in_hide',
|
67 |
+
'job_manager_per_page',
|
68 |
+
'job_manager_hide_filled_positions',
|
69 |
+
'job_manager_hide_expired',
|
70 |
+
'job_manager_hide_expired_content',
|
71 |
+
'job_manager_enable_categories',
|
72 |
+
'job_manager_enable_default_category_multiselect',
|
73 |
+
'job_manager_category_filter_type',
|
74 |
+
'job_manager_enable_types',
|
75 |
+
'job_manager_multi_job_type',
|
76 |
+
'job_manager_user_requires_account',
|
77 |
+
'job_manager_enable_registration',
|
78 |
+
'job_manager_generate_username_from_email',
|
79 |
+
'job_manager_use_standard_password_setup_email',
|
80 |
+
'job_manager_registration_role',
|
81 |
+
'job_manager_submission_requires_approval',
|
82 |
+
'job_manager_user_can_edit_pending_submissions',
|
83 |
+
'job_manager_user_edit_published_submissions',
|
84 |
+
'job_manager_submission_duration',
|
85 |
+
'job_manager_allowed_application_method',
|
86 |
+
'job_manager_recaptcha_label',
|
87 |
+
'job_manager_recaptcha_site_key',
|
88 |
+
'job_manager_recaptcha_secret_key',
|
89 |
+
'job_manager_enable_recaptcha_job_submission',
|
90 |
+
'job_manager_submit_job_form_page_id',
|
91 |
+
'job_manager_job_dashboard_page_id',
|
92 |
+
'job_manager_jobs_page_id',
|
93 |
+
'job_manager_submit_page_slug',
|
94 |
+
'job_manager_job_dashboard_page_slug',
|
95 |
+
'job_manager_delete_data_on_uninstall',
|
96 |
+
'job_manager_email_admin_updated_job',
|
97 |
+
'job_manager_email_admin_new_job',
|
98 |
+
'job_manager_email_admin_expiring_job',
|
99 |
+
'job_manager_email_employer_expiring_job',
|
100 |
+
);
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Site options to be deleted.
|
104 |
+
*
|
105 |
+
* @var $site_options
|
106 |
+
*/
|
107 |
+
private static $site_options = array(
|
108 |
+
'job_manager_helper',
|
109 |
+
);
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Transient names (as MySQL regexes) to be deleted. The prefixes
|
113 |
+
* "_transient_" and "_transient_timeout_" will be prepended.
|
114 |
+
*
|
115 |
+
* @var $transients
|
116 |
+
*/
|
117 |
+
private static $transients = array(
|
118 |
+
'_job_manager_activation_redirect',
|
119 |
+
'get_job_listings-transient-version',
|
120 |
+
'jm_.*',
|
121 |
+
);
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Role to be removed.
|
125 |
+
*
|
126 |
+
* @var $role
|
127 |
+
*/
|
128 |
+
private static $role = 'employer';
|
129 |
+
|
130 |
+
/**
|
131 |
+
* Capabilities to be deleted.
|
132 |
+
*
|
133 |
+
* @var $caps
|
134 |
+
*/
|
135 |
+
private static $caps = array(
|
136 |
+
'manage_job_listings',
|
137 |
+
'edit_job_listing',
|
138 |
+
'read_job_listing',
|
139 |
+
'delete_job_listing',
|
140 |
+
'edit_job_listings',
|
141 |
+
'edit_others_job_listings',
|
142 |
+
'publish_job_listings',
|
143 |
+
'read_private_job_listings',
|
144 |
+
'delete_job_listings',
|
145 |
+
'delete_private_job_listings',
|
146 |
+
'delete_published_job_listings',
|
147 |
+
'delete_others_job_listings',
|
148 |
+
'edit_private_job_listings',
|
149 |
+
'edit_published_job_listings',
|
150 |
+
'manage_job_listing_terms',
|
151 |
+
'edit_job_listing_terms',
|
152 |
+
'delete_job_listing_terms',
|
153 |
+
'assign_job_listing_terms',
|
154 |
+
);
|
155 |
+
|
156 |
+
/**
|
157 |
+
* User meta key names to be deleted.
|
158 |
+
*
|
159 |
+
* @var array $user_meta_keys
|
160 |
+
*/
|
161 |
+
private static $user_meta_keys = array(
|
162 |
+
'_company_logo',
|
163 |
+
'_company_name',
|
164 |
+
'_company_website',
|
165 |
+
'_company_tagline',
|
166 |
+
'_company_twitter',
|
167 |
+
'_company_video',
|
168 |
+
);
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Cleanup all data.
|
172 |
+
*
|
173 |
+
* @access public
|
174 |
+
*/
|
175 |
+
public static function cleanup_all() {
|
176 |
+
self::cleanup_custom_post_types();
|
177 |
+
self::cleanup_taxonomies();
|
178 |
+
self::cleanup_pages();
|
179 |
+
self::cleanup_cron_jobs();
|
180 |
+
self::cleanup_roles_and_caps();
|
181 |
+
self::cleanup_transients();
|
182 |
+
self::cleanup_user_meta();
|
183 |
+
self::cleanup_options();
|
184 |
+
self::cleanup_site_options();
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Cleanup data for custom post types.
|
189 |
+
*
|
190 |
+
* @access private
|
191 |
+
*/
|
192 |
+
private static function cleanup_custom_post_types() {
|
193 |
+
foreach ( self::$custom_post_types as $post_type ) {
|
194 |
+
$items = get_posts( array(
|
195 |
+
'post_type' => $post_type,
|
196 |
+
'post_status' => 'any',
|
197 |
+
'numberposts' => -1,
|
198 |
+
'fields' => 'ids',
|
199 |
+
) );
|
200 |
+
|
201 |
+
foreach ( $items as $item ) {
|
202 |
+
wp_trash_post( $item );
|
203 |
+
}
|
204 |
+
}
|
205 |
+
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* Cleanup data for taxonomies.
|
209 |
+
*
|
210 |
+
* @access private
|
211 |
+
*/
|
212 |
+
private static function cleanup_taxonomies() {
|
213 |
+
global $wpdb;
|
214 |
+
|
215 |
+
foreach ( self::$taxonomies as $taxonomy ) {
|
216 |
+
$terms = $wpdb->get_results(
|
217 |
+
$wpdb->prepare(
|
218 |
+
"SELECT term_id, term_taxonomy_id FROM $wpdb->term_taxonomy WHERE taxonomy = %s",
|
219 |
+
$taxonomy
|
220 |
+
)
|
221 |
+
);
|
222 |
+
|
223 |
+
// Delete all data for each term.
|
224 |
+
foreach ( $terms as $term ) {
|
225 |
+
$wpdb->delete( $wpdb->term_relationships, array( 'term_taxonomy_id' => $term->term_taxonomy_id ) );
|
226 |
+
$wpdb->delete( $wpdb->term_taxonomy, array( 'term_taxonomy_id' => $term->term_taxonomy_id ) );
|
227 |
+
$wpdb->delete( $wpdb->terms, array( 'term_id' => $term->term_id ) );
|
228 |
+
$wpdb->delete( $wpdb->termmeta, array( 'term_id' => $term->term_id ) );
|
229 |
+
}
|
230 |
+
|
231 |
+
if ( function_exists( 'clean_taxonomy_cache' ) ) {
|
232 |
+
clean_taxonomy_cache( $taxonomy );
|
233 |
+
}
|
234 |
+
}
|
235 |
+
}
|
236 |
+
|
237 |
+
/**
|
238 |
+
* Cleanup data for pages.
|
239 |
+
*
|
240 |
+
* @access private
|
241 |
+
*/
|
242 |
+
private static function cleanup_pages() {
|
243 |
+
// Trash the Submit Job page.
|
244 |
+
$submit_job_form_page_id = get_option( 'job_manager_submit_job_form_page_id' );
|
245 |
+
if ( $submit_job_form_page_id ) {
|
246 |
+
wp_trash_post( $submit_job_form_page_id );
|
247 |
+
}
|
248 |
+
|
249 |
+
// Trash the Job Dashboard page.
|
250 |
+
$job_dashboard_page_id = get_option( 'job_manager_job_dashboard_page_id' );
|
251 |
+
if ( $job_dashboard_page_id ) {
|
252 |
+
wp_trash_post( $job_dashboard_page_id );
|
253 |
+
}
|
254 |
+
|
255 |
+
// Trash the Jobs page.
|
256 |
+
$jobs_page_id = get_option( 'job_manager_jobs_page_id' );
|
257 |
+
if ( $jobs_page_id ) {
|
258 |
+
wp_trash_post( $jobs_page_id );
|
259 |
+
}
|
260 |
+
}
|
261 |
+
|
262 |
+
/**
|
263 |
+
* Cleanup data for options.
|
264 |
+
*
|
265 |
+
* @access private
|
266 |
+
*/
|
267 |
+
private static function cleanup_options() {
|
268 |
+
foreach ( self::$options as $option ) {
|
269 |
+
delete_option( $option );
|
270 |
+
}
|
271 |
+
}
|
272 |
+
|
273 |
+
/**
|
274 |
+
* Cleanup data for site options.
|
275 |
+
*
|
276 |
+
* @access private
|
277 |
+
*/
|
278 |
+
private static function cleanup_site_options() {
|
279 |
+
foreach ( self::$site_options as $option ) {
|
280 |
+
delete_site_option( $option );
|
281 |
+
}
|
282 |
+
}
|
283 |
+
|
284 |
+
/**
|
285 |
+
* Cleanup transients from the database.
|
286 |
+
*
|
287 |
+
* @access private
|
288 |
+
*/
|
289 |
+
private static function cleanup_transients() {
|
290 |
+
global $wpdb;
|
291 |
+
|
292 |
+
foreach ( array( '_transient_', '_transient_timeout_' ) as $prefix ) {
|
293 |
+
foreach ( self::$transients as $transient ) {
|
294 |
+
$wpdb->query(
|
295 |
+
$wpdb->prepare(
|
296 |
+
"DELETE FROM $wpdb->options WHERE option_name RLIKE %s",
|
297 |
+
$prefix . $transient
|
298 |
+
)
|
299 |
+
);
|
300 |
+
}
|
301 |
+
}
|
302 |
+
}
|
303 |
+
|
304 |
+
/**
|
305 |
+
* Cleanup data for roles and caps.
|
306 |
+
*
|
307 |
+
* @access private
|
308 |
+
*/
|
309 |
+
private static function cleanup_roles_and_caps() {
|
310 |
+
global $wp_roles;
|
311 |
+
|
312 |
+
// Remove caps from roles.
|
313 |
+
$role_names = array_keys( $wp_roles->roles );
|
314 |
+
foreach ( $role_names as $role_name ) {
|
315 |
+
$role = get_role( $role_name );
|
316 |
+
self::remove_all_job_manager_caps( $role );
|
317 |
+
}
|
318 |
+
|
319 |
+
// Remove caps and role from users.
|
320 |
+
$users = get_users( array() );
|
321 |
+
foreach ( $users as $user ) {
|
322 |
+
self::remove_all_job_manager_caps( $user );
|
323 |
+
$user->remove_role( self::$role );
|
324 |
+
}
|
325 |
+
|
326 |
+
// Remove role.
|
327 |
+
remove_role( self::$role );
|
328 |
+
}
|
329 |
+
|
330 |
+
/**
|
331 |
+
* Helper method to remove WPJM caps from a user or role object.
|
332 |
+
*
|
333 |
+
* @param (WP_User|WP_Role) $object the user or role object.
|
334 |
+
*/
|
335 |
+
private static function remove_all_job_manager_caps( $object ) {
|
336 |
+
foreach ( self::$caps as $cap ) {
|
337 |
+
$object->remove_cap( $cap );
|
338 |
+
}
|
339 |
+
}
|
340 |
+
|
341 |
+
/**
|
342 |
+
* Cleanup user meta from the database.
|
343 |
+
*
|
344 |
+
* @access private
|
345 |
+
*/
|
346 |
+
private static function cleanup_user_meta() {
|
347 |
+
global $wpdb;
|
348 |
+
|
349 |
+
foreach ( self::$user_meta_keys as $meta_key ) {
|
350 |
+
$wpdb->delete( $wpdb->usermeta, array( 'meta_key' => $meta_key ) );
|
351 |
+
}
|
352 |
+
}
|
353 |
+
|
354 |
+
/**
|
355 |
+
* Cleanup cron jobs. Note that this should be done on deactivation, but
|
356 |
+
* doing it here as well for safety.
|
357 |
+
*
|
358 |
+
* @access private
|
359 |
+
*/
|
360 |
+
private static function cleanup_cron_jobs() {
|
361 |
+
foreach ( self::$cron_jobs as $job ) {
|
362 |
+
wp_clear_scheduled_hook( $job );
|
363 |
+
}
|
364 |
+
}
|
365 |
+
}
|
includes/class-wp-job-manager-email-notifications.php
ADDED
@@ -0,0 +1,814 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
4 |
+
exit; // Exit if accessed directly
|
5 |
+
}
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Base class for WP Job Manager's email notification system.
|
9 |
+
*
|
10 |
+
* @package wp-job-manager
|
11 |
+
* @since 1.31.0
|
12 |
+
*/
|
13 |
+
final class WP_Job_Manager_Email_Notifications {
|
14 |
+
const EMAIL_SETTING_PREFIX = 'job_manager_email_';
|
15 |
+
const EMAIL_SETTING_ENABLED = 'enabled';
|
16 |
+
const EMAIL_SETTING_PLAIN_TEXT = 'plain_text';
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var array
|
20 |
+
*/
|
21 |
+
private static $deferred_notifications = array();
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Sets up initial hooks.
|
25 |
+
*
|
26 |
+
* @static
|
27 |
+
*/
|
28 |
+
public static function init() {
|
29 |
+
add_action( 'job_manager_send_notification', array( __CLASS__, '_schedule_notification' ), 10, 2 );
|
30 |
+
add_action( 'job_manager_email_init', array( __CLASS__, '_lazy_init' ) );
|
31 |
+
add_action( 'job_manager_email_job_details', array( __CLASS__, 'output_job_details' ), 10, 4 );
|
32 |
+
add_action( 'job_manager_email_header', array( __CLASS__, 'output_header' ), 10, 3 );
|
33 |
+
add_action( 'job_manager_email_footer', array( __CLASS__, 'output_footer' ), 10, 3 );
|
34 |
+
add_action( 'job_manager_email_daily_notices', array( __CLASS__, 'send_employer_expiring_notice' ) );
|
35 |
+
add_action( 'job_manager_email_daily_notices', array( __CLASS__, 'send_admin_expiring_notice' ) );
|
36 |
+
add_filter( 'job_manager_settings', array( __CLASS__, 'add_job_manager_email_settings' ), 1 );
|
37 |
+
add_action( 'job_manager_job_submitted', array( __CLASS__, 'send_new_job_notification' ) );
|
38 |
+
add_action( 'job_manager_user_edit_job_listing', array( __CLASS__, 'send_updated_job_notification' ) );
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Gets list of email notifications handled by WP Job Manager core.
|
43 |
+
*
|
44 |
+
* @return array
|
45 |
+
*/
|
46 |
+
public static function core_email_notifications() {
|
47 |
+
return array(
|
48 |
+
'WP_Job_Manager_Email_Admin_New_Job',
|
49 |
+
'WP_Job_Manager_Email_Admin_Updated_Job',
|
50 |
+
'WP_Job_Manager_Email_Admin_Expiring_Job',
|
51 |
+
'WP_Job_Manager_Email_Employer_Expiring_Job',
|
52 |
+
);
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Sets up an email notification to be sent at the end of the script's execution.
|
57 |
+
*
|
58 |
+
* @param string $notification
|
59 |
+
* @param array $args
|
60 |
+
*/
|
61 |
+
public static function _schedule_notification( $notification, $args = array() ) {
|
62 |
+
self::maybe_init();
|
63 |
+
|
64 |
+
self::$deferred_notifications[] = array( $notification, $args );
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Sends all notifications collected during execution.
|
69 |
+
*
|
70 |
+
* Do not call manually.
|
71 |
+
*
|
72 |
+
* @access private
|
73 |
+
*/
|
74 |
+
public static function _send_deferred_notifications() {
|
75 |
+
$email_notifications = self::get_email_notifications( true );
|
76 |
+
foreach ( self::$deferred_notifications as $email ) {
|
77 |
+
if (
|
78 |
+
! is_string( $email[0] )
|
79 |
+
|| ! isset( $email_notifications[ $email[0] ] )
|
80 |
+
) {
|
81 |
+
continue;
|
82 |
+
}
|
83 |
+
|
84 |
+
$email_class = $email_notifications[ $email[0] ];
|
85 |
+
$email_notification_key = $email[0];
|
86 |
+
$email_args = is_array( $email[1] ) ? $email[1] : array();
|
87 |
+
|
88 |
+
self::send_email( $email[0], new $email_class( $email_args, self::get_email_settings( $email_notification_key ) ) );
|
89 |
+
}
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Initialize if necessary.
|
94 |
+
*/
|
95 |
+
public static function maybe_init() {
|
96 |
+
if ( 0 === did_action( 'job_manager_email_init' ) ) {
|
97 |
+
/**
|
98 |
+
* Lazily load remaining files needed for email notifications. Do this here instead of in
|
99 |
+
* `shutdown` for proper logging in case of syntax errors.
|
100 |
+
*
|
101 |
+
* @since 1.31.0
|
102 |
+
*/
|
103 |
+
do_action( 'job_manager_email_init' );
|
104 |
+
}
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Include email files.
|
109 |
+
*
|
110 |
+
* Do not call manually.
|
111 |
+
*
|
112 |
+
* @access private
|
113 |
+
*/
|
114 |
+
public static function _lazy_init() {
|
115 |
+
add_action( 'shutdown', array( __CLASS__, '_send_deferred_notifications' ) );
|
116 |
+
|
117 |
+
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/emails/class-wp-job-manager-email-admin-new-job.php';
|
118 |
+
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/emails/class-wp-job-manager-email-admin-updated-job.php';
|
119 |
+
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/emails/class-wp-job-manager-email-employer-expiring-job.php';
|
120 |
+
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/emails/class-wp-job-manager-email-admin-expiring-job.php';
|
121 |
+
|
122 |
+
if ( ! class_exists( 'Emogrifier' ) && class_exists( 'DOMDocument' ) ) {
|
123 |
+
include_once JOB_MANAGER_PLUGIN_DIR . '/lib/emogrifier/class-emogrifier.php';
|
124 |
+
}
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Clear the deferred notifications email array.
|
129 |
+
*
|
130 |
+
* Do not call manually. Only for help with tests.
|
131 |
+
*
|
132 |
+
* @access private
|
133 |
+
*/
|
134 |
+
public static function _clear_deferred_notifications() {
|
135 |
+
if ( ! defined( 'PHPUNIT_WPJM_TESTSUITE' ) || ! PHPUNIT_WPJM_TESTSUITE ) {
|
136 |
+
die( "This is just for use while testing" );
|
137 |
+
}
|
138 |
+
self::$deferred_notifications = array();
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Gets a list of all email notifications that WP Job Manager handles.
|
143 |
+
*
|
144 |
+
* @param bool $enabled_notifications_only
|
145 |
+
* @return array
|
146 |
+
*/
|
147 |
+
public static function get_email_notifications( $enabled_notifications_only = false ) {
|
148 |
+
self::maybe_init();
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Retrieves all email notifications to be sent.
|
152 |
+
*
|
153 |
+
* @since 1.31.0
|
154 |
+
*
|
155 |
+
* @param array $email_notifications All the email notifications to be registered.
|
156 |
+
*/
|
157 |
+
$email_notification_classes = array_unique( apply_filters( 'job_manager_email_notifications', self::core_email_notifications() ) );
|
158 |
+
$email_notifications = array();
|
159 |
+
|
160 |
+
/**
|
161 |
+
* @var WP_Job_Manager_Email $email_class
|
162 |
+
*/
|
163 |
+
foreach ( $email_notification_classes as $email_class ) {
|
164 |
+
// Check to make sure email notification is valid.
|
165 |
+
if ( ! self::is_email_notification_valid( $email_class ) ) {
|
166 |
+
continue;
|
167 |
+
}
|
168 |
+
|
169 |
+
// PHP 5.2: Using `call_user_func()` but `$email_class::get_key()` preferred.
|
170 |
+
$email_notification_key = call_user_func( array( $email_class, 'get_key') );
|
171 |
+
if (
|
172 |
+
isset( $email_notifications[ $email_notification_key ] )
|
173 |
+
|| ( $enabled_notifications_only && ! self::is_email_notification_enabled( $email_notification_key ) )
|
174 |
+
) {
|
175 |
+
continue;
|
176 |
+
}
|
177 |
+
|
178 |
+
$email_notifications[ $email_notification_key ] = $email_class;
|
179 |
+
}
|
180 |
+
|
181 |
+
return $email_notifications;
|
182 |
+
}
|
183 |
+
|
184 |
+
/**
|
185 |
+
* Show details about the job listing.
|
186 |
+
*
|
187 |
+
* @param WP_Post $job The job listing to show details for.
|
188 |
+
* @param WP_Job_Manager_Email $email Email object for the notification.
|
189 |
+
* @param bool $sent_to_admin True if this is being sent to an administrator.
|
190 |
+
* @param bool $plain_text True if the email is being sent as plain text.
|
191 |
+
*/
|
192 |
+
public static function output_job_details( $job, $email, $sent_to_admin, $plain_text = false ) {
|
193 |
+
$template_segment = self::locate_template_file( 'email-job-details', $plain_text );
|
194 |
+
if ( ! file_exists( $template_segment ) ) {
|
195 |
+
return;
|
196 |
+
}
|
197 |
+
|
198 |
+
$fields = self::get_job_detail_fields( $job, $sent_to_admin, $plain_text );
|
199 |
+
|
200 |
+
include $template_segment;
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* Get the job fields to show in email templates.
|
205 |
+
*
|
206 |
+
* @param WP_Post $job
|
207 |
+
* @param bool $sent_to_admin
|
208 |
+
* @param bool $plain_text
|
209 |
+
* @return array
|
210 |
+
*/
|
211 |
+
private static function get_job_detail_fields( WP_Post $job, $sent_to_admin, $plain_text = false ) {
|
212 |
+
$fields = array();
|
213 |
+
|
214 |
+
$fields['job_title'] = array(
|
215 |
+
'label' => __( 'Job title', 'wp-job-manager' ),
|
216 |
+
'value' => $job->post_title,
|
217 |
+
);
|
218 |
+
|
219 |
+
if ( $sent_to_admin || 'publish' === $job->post_status ) {
|
220 |
+
$fields['job_title']['url'] = get_permalink( $job );
|
221 |
+
}
|
222 |
+
|
223 |
+
$job_location = get_the_job_location( $job );
|
224 |
+
if ( ! empty( $job_location ) ) {
|
225 |
+
$fields['job_location'] = array(
|
226 |
+
'label' => __( 'Location', 'wp-job-manager' ),
|
227 |
+
'value' => $job_location,
|
228 |
+
);
|
229 |
+
}
|
230 |
+
|
231 |
+
if ( get_option( 'job_manager_enable_types' ) && wp_count_terms( 'job_listing_type' ) > 0 ) {
|
232 |
+
$job_types = wpjm_get_the_job_types( $job );
|
233 |
+
if ( ! empty( $job_types ) ) {
|
234 |
+
$fields[ 'job_type' ] = array(
|
235 |
+
'label' => __( 'Job type', 'wp-job-manager' ),
|
236 |
+
'value' => implode( ', ', wp_list_pluck( $job_types, 'name' ) ),
|
237 |
+
);
|
238 |
+
}
|
239 |
+
}
|
240 |
+
|
241 |
+
if ( get_option( 'job_manager_enable_categories' ) && wp_count_terms( 'job_listing_category' ) > 0 ) {
|
242 |
+
$job_categories = wpjm_get_the_job_categories( $job );
|
243 |
+
if ( ! empty( $job_categories ) ) {
|
244 |
+
$fields[ 'job_category' ] = array(
|
245 |
+
'label' => __( 'Job category', 'wp-job-manager' ),
|
246 |
+
'value' => implode( ', ', wp_list_pluck( $job_categories, 'name' ) ),
|
247 |
+
);
|
248 |
+
}
|
249 |
+
}
|
250 |
+
|
251 |
+
$company_name = get_the_company_name( $job );
|
252 |
+
if ( ! empty( $company_name ) ) {
|
253 |
+
$fields['company_name'] = array(
|
254 |
+
'label' => __( 'Company name', 'wp-job-manager' ),
|
255 |
+
'value' => $company_name,
|
256 |
+
);
|
257 |
+
}
|
258 |
+
|
259 |
+
$company_website = get_the_company_website( $job );
|
260 |
+
if ( ! empty( $company_website ) ) {
|
261 |
+
$fields['company_website'] = array(
|
262 |
+
'label' => __( 'Company website', 'wp-job-manager' ),
|
263 |
+
'value' => $plain_text ? $company_website : sprintf( '<a href="%1$s">%1$s</a>', esc_url( $company_website, array( 'http', 'https' ) ) ),
|
264 |
+
);
|
265 |
+
}
|
266 |
+
|
267 |
+
$job_expires = get_post_meta( $job->ID, '_job_expires', true );
|
268 |
+
if ( ! empty( $job_expires ) ) {
|
269 |
+
$job_expires_str = date_i18n( get_option( 'date_format' ), strtotime( $job_expires ) );
|
270 |
+
$fields['job_expires'] = array(
|
271 |
+
'label' => __( 'Listing expires', 'wp-job-manager' ),
|
272 |
+
'value' => $job_expires_str,
|
273 |
+
);
|
274 |
+
}
|
275 |
+
|
276 |
+
if ( $sent_to_admin ) {
|
277 |
+
$author = get_user_by( 'ID', $job->post_author );
|
278 |
+
if ( $author instanceof WP_User ) {
|
279 |
+
$fields['author'] = array(
|
280 |
+
'label' => __( 'Posted by', 'wp-job-manager' ),
|
281 |
+
'value' => $author->user_nicename,
|
282 |
+
'url' => 'mailto:' . $author->user_email,
|
283 |
+
);
|
284 |
+
}
|
285 |
+
}
|
286 |
+
|
287 |
+
/**
|
288 |
+
* Modify the fields shown in email notifications in the details summary a job listing.
|
289 |
+
*
|
290 |
+
* @since 1.31.0
|
291 |
+
*
|
292 |
+
* @param array $fields {
|
293 |
+
* Array of fields. Each field is keyed with a unique identifier.
|
294 |
+
* {
|
295 |
+
* @type string $label Label to show next to field.
|
296 |
+
* @type string $value Value for field.
|
297 |
+
* @type string $url URL to provide with the value (optional).
|
298 |
+
* }
|
299 |
+
* }
|
300 |
+
* @param WP_Post $job Job listing.
|
301 |
+
* @param bool $sent_to_admin True if being sent in an admin notification.
|
302 |
+
* @param bool $plain_text True if being sent as plain text.
|
303 |
+
*/
|
304 |
+
return apply_filters( 'job_manager_emails_job_detail_fields', $fields, $job, $sent_to_admin, $plain_text );
|
305 |
+
}
|
306 |
+
|
307 |
+
/**
|
308 |
+
* Output email header.
|
309 |
+
*
|
310 |
+
* @param string $email_notification_key Email notification key for email being sent.
|
311 |
+
* @param bool $sent_to_admin True if this is being sent to an administrator.
|
312 |
+
* @param bool $plain_text True if the email is being sent as plain text.
|
313 |
+
*/
|
314 |
+
public static function output_header( $email_notification_key, $sent_to_admin, $plain_text = false ) {
|
315 |
+
$template_segment = self::email_template_path_alternative( $email_notification_key, 'email-header', $plain_text );
|
316 |
+
if ( false === $template_segment ) {
|
317 |
+
$template_segment = self::locate_template_file( 'email-header', $plain_text );
|
318 |
+
}
|
319 |
+
if ( ! $template_segment || ! file_exists( $template_segment ) ) {
|
320 |
+
return;
|
321 |
+
}
|
322 |
+
include $template_segment;
|
323 |
+
}
|
324 |
+
|
325 |
+
/**
|
326 |
+
* Output email footer.
|
327 |
+
*
|
328 |
+
* @param string $email_notification_key Email notification key for email being sent.
|
329 |
+
* @param bool $sent_to_admin True if this is being sent to an administrator.
|
330 |
+
* @param bool $plain_text True if the email is being sent as plain text.
|
331 |
+
*/
|
332 |
+
public static function output_footer( $email_notification_key, $sent_to_admin, $plain_text = false ) {
|
333 |
+
$template_segment = self::email_template_path_alternative( $email_notification_key, 'email-footer', $plain_text );
|
334 |
+
if ( false === $template_segment ) {
|
335 |
+
$template_segment = self::locate_template_file( 'email-footer', $plain_text );
|
336 |
+
}
|
337 |
+
if ( ! $template_segment || ! file_exists( $template_segment ) ) {
|
338 |
+
return;
|
339 |
+
}
|
340 |
+
include $template_segment;
|
341 |
+
}
|
342 |
+
|
343 |
+
/**
|
344 |
+
* Checks for an alternative email template segment in the template path specified by the current email.
|
345 |
+
* Useful to provide alternative email headers and footers for a specific WPJM extension plugin.
|
346 |
+
*
|
347 |
+
* @param string $email_notification_key Email notification key for email being sent.
|
348 |
+
* @param string $template_name Name of the template to check
|
349 |
+
* @param bool $plain_text True if the email is being sent as plain text.
|
350 |
+
* @return bool|string Returns path to template path alternative or false if none exists.
|
351 |
+
*/
|
352 |
+
private static function email_template_path_alternative( $email_notification_key, $template_name, $plain_text ) {
|
353 |
+
$email_class = self::get_email_class( $email_notification_key );
|
354 |
+
if ( ! $email_class || ! is_subclass_of( $email_class, 'WP_Job_Manager_Email_Template' ) ) {
|
355 |
+
return false;
|
356 |
+
}
|
357 |
+
|
358 |
+
$template_default_path = call_user_func( array( $email_class, 'get_template_default_path' ) );
|
359 |
+
if ( '' === $template_default_path ) {
|
360 |
+
return false;
|
361 |
+
}
|
362 |
+
|
363 |
+
$template_path = call_user_func( array( $email_class, 'get_template_path' ) );
|
364 |
+
$template = self::locate_template_file( $template_name, $plain_text, $template_path, $template_default_path );
|
365 |
+
if ( '' === $template ) {
|
366 |
+
return false;
|
367 |
+
}
|
368 |
+
|
369 |
+
return $template;
|
370 |
+
}
|
371 |
+
|
372 |
+
/**
|
373 |
+
* Locate template file.
|
374 |
+
*
|
375 |
+
* @param string $template_name
|
376 |
+
* @param bool $plain_text
|
377 |
+
* @param string $template_path
|
378 |
+
* @param string $default_path
|
379 |
+
* @return string
|
380 |
+
*/
|
381 |
+
public static function locate_template_file( $template_name, $plain_text = false, $template_path = 'job_manager', $default_path = '' ) {
|
382 |
+
return locate_job_manager_template( WP_Job_Manager_Email_Template::generate_template_file_name( $template_name, $plain_text ), $template_path, $default_path );
|
383 |
+
}
|
384 |
+
|
385 |
+
/**
|
386 |
+
* Add email notification settings for the job manager context.
|
387 |
+
*
|
388 |
+
* @param array $settings
|
389 |
+
* @return array
|
390 |
+
*/
|
391 |
+
public static function add_job_manager_email_settings( $settings ) {
|
392 |
+
return self::add_email_settings( $settings, WP_Job_Manager_Email::get_context() );
|
393 |
+
}
|
394 |
+
|
395 |
+
/**
|
396 |
+
* Add email notification settings for a context.
|
397 |
+
*
|
398 |
+
* @param array $settings
|
399 |
+
* @param string $context
|
400 |
+
* @return array
|
401 |
+
*/
|
402 |
+
public static function add_email_settings( $settings, $context ) {
|
403 |
+
$email_notifications = self::get_email_notifications( false );
|
404 |
+
$email_settings = array();
|
405 |
+
|
406 |
+
foreach ( $email_notifications as $email_notification_key => $email_class ) {
|
407 |
+
$email_notification_context = call_user_func( array( $email_class, 'get_context' ) );
|
408 |
+
if ( $context !== $email_notification_context ) { continue; }
|
409 |
+
|
410 |
+
$email_settings[] = array(
|
411 |
+
'type' => 'multi_enable_expand',
|
412 |
+
'class' => 'email-setting-row no-separator',
|
413 |
+
'name' => self::EMAIL_SETTING_PREFIX . call_user_func( array( $email_class, 'get_key' ) ),
|
414 |
+
'enable_field' => array(
|
415 |
+
'name' => self::EMAIL_SETTING_ENABLED,
|
416 |
+
'cb_label' => call_user_func( array( $email_class, 'get_name' ) ),
|
417 |
+
'desc' => call_user_func( array( $email_class, 'get_description' ) ),
|
418 |
+
),
|
419 |
+
'label' => false,
|
420 |
+
'std' => self::get_email_setting_defaults( $email_notification_key ),
|
421 |
+
'settings' => self::get_email_setting_fields( $email_notification_key ),
|
422 |
+
);
|
423 |
+
}
|
424 |
+
|
425 |
+
if ( ! empty( $email_settings ) ) {
|
426 |
+
$settings['email_notifications'] = array(
|
427 |
+
__( 'Email Notifications', 'wp-job-manager' ),
|
428 |
+
$email_settings,
|
429 |
+
array(
|
430 |
+
'before' => __( 'Select the email notifications to enable.', 'wp-job-manager' ),
|
431 |
+
),
|
432 |
+
);
|
433 |
+
}
|
434 |
+
|
435 |
+
return $settings;
|
436 |
+
}
|
437 |
+
|
438 |
+
/**
|
439 |
+
* Checks if a particular notification is enabled or not.
|
440 |
+
*
|
441 |
+
* @param string $email_notification_key
|
442 |
+
* @return bool
|
443 |
+
*/
|
444 |
+
public static function is_email_notification_enabled( $email_notification_key ) {
|
445 |
+
$settings = self::get_email_settings( $email_notification_key );
|
446 |
+
|
447 |
+
$is_email_notification_enabled = ! empty( $settings[ self::EMAIL_SETTING_ENABLED ] );
|
448 |
+
|
449 |
+
/**
|
450 |
+
* Filter whether an notification email is enabled.
|
451 |
+
*
|
452 |
+
* @since 1.31.0
|
453 |
+
*
|
454 |
+
* @param bool $is_email_notification_enabled
|
455 |
+
* @param string $email_notification_key
|
456 |
+
*/
|
457 |
+
return apply_filters( 'job_manager_email_is_email_notification_enabled', $is_email_notification_enabled, $email_notification_key );
|
458 |
+
}
|
459 |
+
|
460 |
+
/**
|
461 |
+
* Checks if we should send emails using plain text.
|
462 |
+
*
|
463 |
+
* @param string $email_notification_key
|
464 |
+
* @return bool
|
465 |
+
*/
|
466 |
+
public static function send_as_plain_text( $email_notification_key ) {
|
467 |
+
$settings = self::get_email_settings( $email_notification_key );
|
468 |
+
|
469 |
+
$send_as_plain_text = ! empty( $settings[ self::EMAIL_SETTING_PLAIN_TEXT ] );
|
470 |
+
|
471 |
+
/**
|
472 |
+
* Filter whether to send emails as plain text.
|
473 |
+
*
|
474 |
+
* @since 1.31.0
|
475 |
+
*
|
476 |
+
* @param bool $send_as_plain_text
|
477 |
+
* @param string $email_notification_key
|
478 |
+
*/
|
479 |
+
return apply_filters( 'job_manager_email_send_as_plain_text', $send_as_plain_text, $email_notification_key );
|
480 |
+
}
|
481 |
+
|
482 |
+
/**
|
483 |
+
* Sending notices to employers for expiring job listings.
|
484 |
+
*/
|
485 |
+
public static function send_employer_expiring_notice() {
|
486 |
+
self::maybe_init();
|
487 |
+
|
488 |
+
$email_key = WP_Job_Manager_Email_Employer_Expiring_Job::get_key();
|
489 |
+
if ( ! self::is_email_notification_enabled( $email_key ) ) {
|
490 |
+
return;
|
491 |
+
}
|
492 |
+
$settings = self::get_email_settings( $email_key );
|
493 |
+
$days_notice = WP_Job_Manager_Email_Employer_Expiring_Job::get_notice_period( $settings );
|
494 |
+
self::send_expiring_notice( $email_key, $days_notice );
|
495 |
+
}
|
496 |
+
|
497 |
+
/**
|
498 |
+
* Sending notices to the site administrator for expiring job listings.
|
499 |
+
*/
|
500 |
+
public static function send_admin_expiring_notice() {
|
501 |
+
self::maybe_init();
|
502 |
+
|
503 |
+
$email_key = WP_Job_Manager_Email_Admin_Expiring_Job::get_key();
|
504 |
+
if ( ! self::is_email_notification_enabled( $email_key ) ) {
|
505 |
+
return;
|
506 |
+
}
|
507 |
+
$settings = self::get_email_settings( $email_key );
|
508 |
+
$days_notice = WP_Job_Manager_Email_Admin_Expiring_Job::get_notice_period( $settings );
|
509 |
+
self::send_expiring_notice( $email_key, $days_notice );
|
510 |
+
}
|
511 |
+
|
512 |
+
/**
|
513 |
+
* Fire the action to send a new job notification to the admin.
|
514 |
+
*
|
515 |
+
* @param int $job_id
|
516 |
+
*/
|
517 |
+
public static function send_new_job_notification( $job_id ) {
|
518 |
+
do_action( 'job_manager_send_notification', 'admin_new_job', array( 'job_id' => $job_id ) );
|
519 |
+
}
|
520 |
+
|
521 |
+
/**
|
522 |
+
* Fire the action to send a updated job notification to the admin.
|
523 |
+
*
|
524 |
+
* @param int $job_id
|
525 |
+
*/
|
526 |
+
public static function send_updated_job_notification( $job_id ) {
|
527 |
+
do_action( 'job_manager_send_notification', 'admin_updated_job', array( 'job_id' => $job_id ) );
|
528 |
+
}
|
529 |
+
|
530 |
+
/**
|
531 |
+
* Send notice based on job expiration date.
|
532 |
+
*
|
533 |
+
* @param string $email_notification_key
|
534 |
+
* @param int $days_notice
|
535 |
+
*/
|
536 |
+
private static function send_expiring_notice( $email_notification_key, $days_notice ) {
|
537 |
+
global $wpdb;
|
538 |
+
|
539 |
+
$notice_before_ts = current_time( 'timestamp' ) + ( DAY_IN_SECONDS * $days_notice );
|
540 |
+
$job_ids_sql = $wpdb->prepare( "
|
541 |
+
SELECT postmeta.post_id FROM {$wpdb->postmeta} as postmeta
|
542 |
+
LEFT JOIN {$wpdb->posts} as posts ON postmeta.post_id = posts.ID
|
543 |
+
WHERE postmeta.meta_key = '_job_expires'
|
544 |
+
AND postmeta.meta_value = %s
|
545 |
+
AND posts.post_status = 'publish'
|
546 |
+
AND posts.post_type = 'job_listing'
|
547 |
+
", date( 'Y-m-d', $notice_before_ts ) );
|
548 |
+
$job_ids = $wpdb->get_col( $job_ids_sql );
|
549 |
+
|
550 |
+
if ( $job_ids ) {
|
551 |
+
foreach ( $job_ids as $job_id ) {
|
552 |
+
do_action( 'job_manager_send_notification', $email_notification_key, array( 'job_id' => $job_id ) );
|
553 |
+
}
|
554 |
+
}
|
555 |
+
}
|
556 |
+
|
557 |
+
/**
|
558 |
+
* Get the setting fields for an email.
|
559 |
+
*
|
560 |
+
* @param string $email_notification_key
|
561 |
+
* @return array
|
562 |
+
*/
|
563 |
+
private static function get_email_setting_fields( $email_notification_key ) {
|
564 |
+
$email_class = self::get_email_class( $email_notification_key );
|
565 |
+
$core_settings = array(
|
566 |
+
array(
|
567 |
+
'name' => 'plain_text',
|
568 |
+
'std' => '0',
|
569 |
+
'label' => __( 'Format', 'wp-job-manager' ),
|
570 |
+
'type' => 'radio',
|
571 |
+
'options' => array(
|
572 |
+
'1' => __( 'Send plain text email', 'wp-job-manager' ),
|
573 |
+
'0' => __( 'Send rich text email', 'wp-job-manager' ),
|
574 |
+
),
|
575 |
+
),
|
576 |
+
);
|
577 |
+
$email_settings = call_user_func( array( $email_class, 'get_setting_fields' ) );
|
578 |
+
return array_merge( $core_settings, $email_settings );
|
579 |
+
}
|
580 |
+
|
581 |
+
/**
|
582 |
+
* Get the settings for the email.
|
583 |
+
*
|
584 |
+
* @param string $email_notification_key
|
585 |
+
* @return array
|
586 |
+
*/
|
587 |
+
private static function get_email_settings( $email_notification_key ) {
|
588 |
+
$option_name = self::EMAIL_SETTING_PREFIX . $email_notification_key;
|
589 |
+
$option_value = get_option( $option_name );
|
590 |
+
if ( empty( $option_value ) || ! is_array( $option_value ) ) {
|
591 |
+
$option_value = array();
|
592 |
+
}
|
593 |
+
$default_settings = self::get_email_setting_defaults( $email_notification_key );
|
594 |
+
|
595 |
+
return array_merge( $default_settings, $option_value );
|
596 |
+
}
|
597 |
+
|
598 |
+
/**
|
599 |
+
* Gets the default values for the email notification.
|
600 |
+
*
|
601 |
+
* @param string $email_notification_key
|
602 |
+
* @return array
|
603 |
+
*/
|
604 |
+
private static function get_email_setting_defaults( $email_notification_key ) {
|
605 |
+
$settings = self::get_email_setting_fields( $email_notification_key );
|
606 |
+
$email_class = self::get_email_class( $email_notification_key );
|
607 |
+
|
608 |
+
$defaults = array();
|
609 |
+
$defaults[ self::EMAIL_SETTING_ENABLED ] = call_user_func( array( $email_class, 'is_default_enabled' ) ) ? '1' : '0';
|
610 |
+
|
611 |
+
foreach ( $settings as $setting ) {
|
612 |
+
$defaults[ $setting['name'] ] = null;
|
613 |
+
if ( isset( $setting['std'] ) ) {
|
614 |
+
$defaults[ $setting['name'] ] = $setting['std'];
|
615 |
+
}
|
616 |
+
}
|
617 |
+
|
618 |
+
return $defaults;
|
619 |
+
}
|
620 |
+
|
621 |
+
/**
|
622 |
+
* Get the email class from the unique key.
|
623 |
+
*
|
624 |
+
* @param string $email_notification_key
|
625 |
+
* @return bool|string
|
626 |
+
*/
|
627 |
+
private static function get_email_class( $email_notification_key ) {
|
628 |
+
$email_notifications = self::get_email_notifications( false );
|
629 |
+
|
630 |
+
return isset( $email_notifications[ $email_notification_key ] ) ? $email_notifications[ $email_notification_key ] : false;
|
631 |
+
}
|
632 |
+
|
633 |
+
/**
|
634 |
+
* Returns the total number of deferred notifications to be sent. Used in unit tests.
|
635 |
+
*
|
636 |
+
* @access private
|
637 |
+
*
|
638 |
+
* @return int
|
639 |
+
*/
|
640 |
+
public static function _get_deferred_notification_count() {
|
641 |
+
return count( self::$deferred_notifications );
|
642 |
+
}
|
643 |
+
|
644 |
+
/**
|
645 |
+
* Confirms an email notification is valid.
|
646 |
+
*
|
647 |
+
* @access private
|
648 |
+
*
|
649 |
+
* @param string $email_class
|
650 |
+
* @return bool
|
651 |
+
*/
|
652 |
+
private static function is_email_notification_valid( $email_class ) {
|
653 |
+
// PHP 5.2: Using `call_user_func()` but `$email_class::get_key()` preferred.
|
654 |
+
return is_string( $email_class )
|
655 |
+
&& class_exists( $email_class )
|
656 |
+
&& is_subclass_of( $email_class, 'WP_Job_Manager_Email' )
|
657 |
+
&& false !== call_user_func( array( $email_class, 'get_key') )
|
658 |
+
&& false !== call_user_func( array( $email_class, 'get_name') );
|
659 |
+
}
|
660 |
+
|
661 |
+
/**
|
662 |
+
* Sends an email notification.
|
663 |
+
*
|
664 |
+
* @access private
|
665 |
+
*
|
666 |
+
* @param string $email_notification_key
|
667 |
+
* @param WP_Job_Manager_Email $email
|
668 |
+
* @return bool
|
669 |
+
*/
|
670 |
+
private static function send_email( $email_notification_key, WP_Job_Manager_Email $email ) {
|
671 |
+
if ( ! $email->is_valid() ) {
|
672 |
+
return false;
|
673 |
+
}
|
674 |
+
|
675 |
+
$fields = array( 'to', 'from', 'subject', 'rich_content', 'plain_content', 'attachments', 'cc', 'headers' );
|
676 |
+
$args = array();
|
677 |
+
foreach ( $fields as $field ) {
|
678 |
+
$method = 'get_' . $field;
|
679 |
+
|
680 |
+
/**
|
681 |
+
* Filter email values for job manager notifications.
|
682 |
+
*
|
683 |
+
* @since 1.31.0
|
684 |
+
*
|
685 |
+
* @param mixed $email_field_value Value to be filtered.
|
686 |
+
* @param WP_Job_Manager_Email $email Email notification object.
|
687 |
+
*/
|
688 |
+
$args[ $field ] = apply_filters( "job_manager_email_{$email_notification_key}_{$field}", $email->$method(), $email );
|
689 |
+
}
|
690 |
+
|
691 |
+
$headers = is_array( $args['headers'] ) ? $args['headers'] : array();
|
692 |
+
|
693 |
+
if ( ! empty( $args['from'] ) ) {
|
694 |
+
$headers[] = 'From: ' . $args['from'];
|
695 |
+
}
|
696 |
+
|
697 |
+
if ( ! self::send_as_plain_text( $email_notification_key ) ) {
|
698 |
+
$headers[] = 'Content-Type: text/html';
|
699 |
+
}
|
700 |
+
|
701 |
+
$content = self::get_email_content( $email_notification_key, $args );
|
702 |
+
|
703 |
+
/**
|
704 |
+
* Allows for short-circuiting the actual sending of email notifications.
|
705 |
+
*
|
706 |
+
* @since 1.31.0
|
707 |
+
*
|
708 |
+
* @param bool $do_send_notification True if we should send the notification.
|
709 |
+
* @param WP_Job_Manager_Email $email Email notification object.
|
710 |
+
* @param array $args Email arguments for generating email.
|
711 |
+
* @param string $content Email content.
|
712 |
+
* @param array $headers Email headers.
|
713 |
+
* @param
|
714 |
+
*/
|
715 |
+
if ( ! apply_filters( 'job_manager_email_do_send_notification', true, $email, $args, $content, $headers ) ) {
|
716 |
+
return false;
|
717 |
+
}
|
718 |
+
return wp_mail( $args['to'], $args['subject'], $content, $headers, $args['attachments'] );
|
719 |
+
}
|
720 |
+
|
721 |
+
/**
|
722 |
+
* Generates the content for an email.
|
723 |
+
*
|
724 |
+
* @access private
|
725 |
+
*
|
726 |
+
* @param string $email_notification_key
|
727 |
+
* @param array $args
|
728 |
+
* @return string
|
729 |
+
*/
|
730 |
+
private static function get_email_content( $email_notification_key, $args ) {
|
731 |
+
$plain_text = self::send_as_plain_text( $email_notification_key );
|
732 |
+
|
733 |
+
ob_start();
|
734 |
+
|
735 |
+
/**
|
736 |
+
* Output the header for all job manager emails.
|
737 |
+
*
|
738 |
+
* @since 1.31.0
|
739 |
+
*
|
740 |
+
* @param string $email_notification_key Unique email notification key.
|
741 |
+
* @param array $args Arguments passed for generating email.
|
742 |
+
* @param bool $plain_text True if sending plain text email.
|
743 |
+
*/
|
744 |
+
do_action( 'job_manager_email_header', $email_notification_key, $args, $plain_text );
|
745 |
+
|
746 |
+
if ( $plain_text ) {
|
747 |
+
echo html_entity_decode( wptexturize( $args['plain_content'] ) );
|
748 |
+
} else {
|
749 |
+
echo wpautop( wptexturize( $args['rich_content'] ) );
|
750 |
+
}
|
751 |
+
|
752 |
+
/**
|
753 |
+
* Output the footer for all job manager emails.
|
754 |
+
*
|
755 |
+
* @since 1.31.0
|
756 |
+
*
|
757 |
+
* @param string $email_notification_key Unique email notification key.
|
758 |
+
* @param array $args Arguments passed for generating email.
|
759 |
+
* @param bool $plain_text True if sending plain text email.
|
760 |
+
*/
|
761 |
+
do_action( 'job_manager_email_footer', $email_notification_key, $args, $plain_text );
|
762 |
+
|
763 |
+
$content = ob_get_clean();
|
764 |
+
if ( ! $plain_text ) {
|
765 |
+
$content = self::inject_styles( $content );
|
766 |
+
}
|
767 |
+
|
768 |
+
/**
|
769 |
+
* Filter the content of the email.
|
770 |
+
*
|
771 |
+
* @since 1.31.0
|
772 |
+
*
|
773 |
+
* @param string $content Email content.
|
774 |
+
* @param string $email_notification_key Unique email notification key.
|
775 |
+
* @param array $args Arguments passed for generating email.
|
776 |
+
* @param bool $plain_text True if sending plain text email.
|
777 |
+
*/
|
778 |
+
return apply_filters( 'job_manager_email_content', $content, $email_notification_key, $args, $plain_text );
|
779 |
+
}
|
780 |
+
|
781 |
+
/**
|
782 |
+
* Inject inline styles into email content.
|
783 |
+
*
|
784 |
+
* @param string $content
|
785 |
+
* @return string
|
786 |
+
*/
|
787 |
+
private static function inject_styles( $content ) {
|
788 |
+
if ( class_exists( 'Emogrifier' ) ) {
|
789 |
+
try {
|
790 |
+
$emogrifier = new Emogrifier( $content, self::get_styles() );
|
791 |
+
$content = $emogrifier->emogrify();
|
792 |
+
} catch ( Exception $e ) {
|
793 |
+
trigger_error( 'Unable to inject styles into email notification: ' . $e->getMessage() );
|
794 |
+
}
|
795 |
+
}
|
796 |
+
return $content;
|
797 |
+
}
|
798 |
+
|
799 |
+
/**
|
800 |
+
* Gets the CSS styles to be used in email notifications.
|
801 |
+
*
|
802 |
+
* @return bool|string
|
803 |
+
*/
|
804 |
+
private static function get_styles() {
|
805 |
+
$email_styles_template = self::locate_template_file( 'email-styles' );
|
806 |
+
if ( ! file_exists( $email_styles_template ) ) {
|
807 |
+
return false;
|
808 |
+
}
|
809 |
+
ob_start();
|
810 |
+
include $email_styles_template;
|
811 |
+
return ob_get_clean();
|
812 |
+
}
|
813 |
+
|
814 |
+
}
|
includes/class-wp-job-manager-shortcodes.php
CHANGED
@@ -152,7 +152,7 @@ class WP_Job_Manager_Shortcodes {
|
|
152 |
|
153 |
break;
|
154 |
default :
|
155 |
-
do_action( 'job_manager_job_dashboard_do_action_' . $action );
|
156 |
break;
|
157 |
}
|
158 |
|
152 |
|
153 |
break;
|
154 |
default :
|
155 |
+
do_action( 'job_manager_job_dashboard_do_action_' . $action, $job_id );
|
156 |
break;
|
157 |
}
|
158 |
|
includes/class-wp-job-manager-usage-tracking-data.php
CHANGED
@@ -102,7 +102,8 @@ class WP_Job_Manager_Usage_Tracking_Data {
|
|
102 |
|
103 |
$count = 0;
|
104 |
$terms = get_terms(
|
105 |
-
|
|
|
106 |
'hide_empty' => false,
|
107 |
)
|
108 |
);
|
@@ -128,7 +129,8 @@ class WP_Job_Manager_Usage_Tracking_Data {
|
|
128 |
private static function get_job_type_has_description_count() {
|
129 |
$count = 0;
|
130 |
$terms = get_terms(
|
131 |
-
|
|
|
132 |
'hide_empty' => false,
|
133 |
)
|
134 |
);
|
102 |
|
103 |
$count = 0;
|
104 |
$terms = get_terms(
|
105 |
+
array(
|
106 |
+
'taxonomy' => 'job_listing_category',
|
107 |
'hide_empty' => false,
|
108 |
)
|
109 |
);
|
129 |
private static function get_job_type_has_description_count() {
|
130 |
$count = 0;
|
131 |
$terms = get_terms(
|
132 |
+
array(
|
133 |
+
'taxonomy' => 'job_listing_type',
|
134 |
'hide_empty' => false,
|
135 |
)
|
136 |
);
|
includes/class-wp-job-manager-usage-tracking.php
CHANGED
@@ -123,7 +123,7 @@ class WP_Job_Manager_Usage_Tracking extends WP_Job_Manager_Usage_Tracking_Base {
|
|
123 |
'std' => '0',
|
124 |
'type' => 'checkbox',
|
125 |
'desc' => '',
|
126 |
-
'label' => __( 'Enable
|
127 |
'cb_label' => $this->opt_in_checkbox_text(),
|
128 |
);
|
129 |
|
123 |
'std' => '0',
|
124 |
'type' => 'checkbox',
|
125 |
'desc' => '',
|
126 |
+
'label' => __( 'Enable Usage Tracking', 'wp-job-manager' ),
|
127 |
'cb_label' => $this->opt_in_checkbox_text(),
|
128 |
);
|
129 |
|
includes/class-wp-job-manager-widget.php
CHANGED
@@ -182,6 +182,24 @@ class WP_Job_Manager_Widget extends WP_Widget {
|
|
182 |
}
|
183 |
}
|
184 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
185 |
/**
|
186 |
* Echoes the widget content.
|
187 |
*
|
182 |
}
|
183 |
}
|
184 |
|
185 |
+
/**
|
186 |
+
* Gets the instance with the default values for all settings.
|
187 |
+
*
|
188 |
+
* @return array
|
189 |
+
*/
|
190 |
+
protected function get_default_instance() {
|
191 |
+
$defaults = array();
|
192 |
+
if ( ! empty( $this->settings ) ) {
|
193 |
+
foreach ( $this->settings as $key => $setting ) {
|
194 |
+
$defaults[ $key ] = null;
|
195 |
+
if ( isset( $setting['std'] ) ) {
|
196 |
+
$defaults[ $key ] = $setting['std'];
|
197 |
+
}
|
198 |
+
}
|
199 |
+
}
|
200 |
+
return $defaults;
|
201 |
+
}
|
202 |
+
|
203 |
/**
|
204 |
* Echoes the widget content.
|
205 |
*
|
includes/emails/class-wp-job-manager-email-admin-expiring-job.php
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
4 |
+
exit; // Exit if accessed directly
|
5 |
+
}
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Email notification to the site administrator when a job is expiring.
|
9 |
+
*
|
10 |
+
* @package wp-job-manager
|
11 |
+
* @since 1.31.0
|
12 |
+
* @extends WP_Job_Manager_Email
|
13 |
+
*/
|
14 |
+
class WP_Job_Manager_Email_Admin_Expiring_Job extends WP_Job_Manager_Email_Employer_Expiring_Job {
|
15 |
+
/**
|
16 |
+
* Get the unique email notification key.
|
17 |
+
*
|
18 |
+
* @return string
|
19 |
+
*/
|
20 |
+
public static function get_key() {
|
21 |
+
return 'admin_expiring_job';
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Get the friendly name for this email notification.
|
26 |
+
*
|
27 |
+
* @return string
|
28 |
+
*/
|
29 |
+
public static function get_name() {
|
30 |
+
return __( 'Admin Notice of Expiring Job Listings', 'wp-job-manager' );
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Get the description for this email notification.
|
35 |
+
*
|
36 |
+
* @type abstract
|
37 |
+
* @return string
|
38 |
+
*/
|
39 |
+
public static function get_description() {
|
40 |
+
return __( 'Send notices to the site administrator before a job listing expires.', 'wp-job-manager' );
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Get array or comma-separated list of email addresses to send message.
|
45 |
+
*
|
46 |
+
* @return string|array
|
47 |
+
*/
|
48 |
+
public function get_to() {
|
49 |
+
return get_option( 'admin_email', false );
|
50 |
+
}
|
51 |
+
|
52 |
+
}
|
includes/emails/class-wp-job-manager-email-admin-new-job.php
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
4 |
+
exit; // Exit if accessed directly
|
5 |
+
}
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Email notification to administrator when a new job is submitted.
|
9 |
+
*
|
10 |
+
* @package wp-job-manager
|
11 |
+
* @since 1.31.0
|
12 |
+
* @extends WP_Job_Manager_Email
|
13 |
+
*/
|
14 |
+
class WP_Job_Manager_Email_Admin_New_Job extends WP_Job_Manager_Email_Template {
|
15 |
+
/**
|
16 |
+
* Get the unique email notification key.
|
17 |
+
*
|
18 |
+
* @return string
|
19 |
+
*/
|
20 |
+
public static function get_key() {
|
21 |
+
return 'admin_new_job';
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Get the friendly name for this email notification.
|
26 |
+
*
|
27 |
+
* @return string
|
28 |
+
*/
|
29 |
+
public static function get_name() {
|
30 |
+
return __( 'Admin Notice of New Listing', 'wp-job-manager' );
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Get the description for this email notification.
|
35 |
+
*
|
36 |
+
* @type abstract
|
37 |
+
* @return string
|
38 |
+
*/
|
39 |
+
public static function get_description() {
|
40 |
+
return __( 'Send a notice to the site administrator when a new job is submitted on the frontend.', 'wp-job-manager' );
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Get the email subject.
|
45 |
+
*
|
46 |
+
* @return string
|
47 |
+
*/
|
48 |
+
public function get_subject() {
|
49 |
+
$args = $this->get_args();
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @var WP_Post $job
|
53 |
+
*/
|
54 |
+
$job = $args['job'];
|
55 |
+
return sprintf( __( 'New Job Listing Submitted: %s', 'wp-job-manager' ), $job->post_title );
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Get `From:` address header value. Can be simple email or formatted `Firstname Lastname <email@example.com>`.
|
60 |
+
*
|
61 |
+
* @return string|bool Email from value or false to use WordPress' default.
|
62 |
+
*/
|
63 |
+
public function get_from() {
|
64 |
+
return false;
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Get array or comma-separated list of email addresses to send message.
|
69 |
+
*
|
70 |
+
* @return string|array
|
71 |
+
*/
|
72 |
+
public function get_to() {
|
73 |
+
return get_option( 'admin_email', false );
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Checks the arguments and returns whether the email notification is properly set up.
|
78 |
+
*
|
79 |
+
* @return bool
|
80 |
+
*/
|
81 |
+
public function is_valid() {
|
82 |
+
$args = $this->get_args();
|
83 |
+
return isset( $args['job'] )
|
84 |
+
&& $args['job'] instanceof WP_Post
|
85 |
+
&& $this->get_to();
|
86 |
+
}
|
87 |
+
}
|
includes/emails/class-wp-job-manager-email-admin-updated-job.php
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
4 |
+
exit; // Exit if accessed directly
|
5 |
+
}
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Email notification to administrator when a job is updated.
|
9 |
+
*
|
10 |
+
* @package wp-job-manager
|
11 |
+
* @since 1.31.0
|
12 |
+
* @extends WP_Job_Manager_Email
|
13 |
+
*/
|
14 |
+
class WP_Job_Manager_Email_Admin_Updated_Job extends WP_Job_Manager_Email_Template {
|
15 |
+
/**
|
16 |
+
* Get the unique email notification key.
|
17 |
+
*
|
18 |
+
* @return string
|
19 |
+
*/
|
20 |
+
public static function get_key() {
|
21 |
+
return 'admin_updated_job';
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Get the friendly name for this email notification.
|
26 |
+
*
|
27 |
+
* @return string
|
28 |
+
*/
|
29 |
+
public static function get_name() {
|
30 |
+
return __( 'Admin Notice of Updated Listing', 'wp-job-manager' );
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Get the description for this email notification.
|
35 |
+
*
|
36 |
+
* @type abstract
|
37 |
+
* @return string
|
38 |
+
*/
|
39 |
+
public static function get_description() {
|
40 |
+
return __( 'Send a notice to the site administrator when a job is updated on the frontend.', 'wp-job-manager' );
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Get the email subject.
|
45 |
+
*
|
46 |
+
* @return string
|
47 |
+
*/
|
48 |
+
public function get_subject() {
|
49 |
+
$args = $this->get_args();
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @var WP_Post $job
|
53 |
+
*/
|
54 |
+
$job = $args['job'];
|
55 |
+
return sprintf( __( 'Job Listing Updated: %s', 'wp-job-manager' ), $job->post_title );
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Get `From:` address header value. Can be simple email or formatted `Firstname Lastname <email@example.com>`.
|
60 |
+
*
|
61 |
+
* @return string|bool Email from value or false to use WordPress' default.
|
62 |
+
*/
|
63 |
+
public function get_from() {
|
64 |
+
return false;
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Get array or comma-separated list of email addresses to send message.
|
69 |
+
*
|
70 |
+
* @return string|array
|
71 |
+
*/
|
72 |
+
public function get_to() {
|
73 |
+
return get_option( 'admin_email', false );
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Checks the arguments and returns whether the email notification is properly set up.
|
78 |
+
*
|
79 |
+
* @return bool
|
80 |
+
*/
|
81 |
+
public function is_valid() {
|
82 |
+
$args = $this->get_args();
|
83 |
+
return isset( $args['job'] )
|
84 |
+
&& $args['job'] instanceof WP_Post
|
85 |
+
&& $this->get_to();
|
86 |
+
}
|
87 |
+
}
|
includes/emails/class-wp-job-manager-email-employer-expiring-job.php
ADDED
@@ -0,0 +1,155 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
4 |
+
exit; // Exit if accessed directly
|
5 |
+
}
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Email notification to employers when a job is expiring.
|
9 |
+
*
|
10 |
+
* @package wp-job-manager
|
11 |
+
* @since 1.31.0
|
12 |
+
* @extends WP_Job_Manager_Email
|
13 |
+
*/
|
14 |
+
class WP_Job_Manager_Email_Employer_Expiring_Job extends WP_Job_Manager_Email_Template {
|
15 |
+
const SETTING_NOTICE_PERIOD_NAME = 'notice_period_days';
|
16 |
+
const SETTING_NOTICE_PERIOD_DEFAULT = '1';
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Get the unique email notification key.
|
20 |
+
*
|
21 |
+
* @return string
|
22 |
+
*/
|
23 |
+
public static function get_key() {
|
24 |
+
return 'employer_expiring_job';
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Get the friendly name for this email notification.
|
29 |
+
*
|
30 |
+
* @return string
|
31 |
+
*/
|
32 |
+
public static function get_name() {
|
33 |
+
return __( 'Employer Notice of Expiring Job Listings', 'wp-job-manager' );
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Get the description for this email notification.
|
38 |
+
*
|
39 |
+
* @type abstract
|
40 |
+
* @return string
|
41 |
+
*/
|
42 |
+
public static function get_description() {
|
43 |
+
return __( 'Send notices to employers before a job listing expires.', 'wp-job-manager' );
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Get the notice period in days from the notification settings.
|
48 |
+
*
|
49 |
+
* @param array $settings
|
50 |
+
* @return int
|
51 |
+
*/
|
52 |
+
public static function get_notice_period( $settings ) {
|
53 |
+
if ( isset( $settings[ self::SETTING_NOTICE_PERIOD_NAME ] ) ) {
|
54 |
+
return absint( $settings[ self::SETTING_NOTICE_PERIOD_NAME ] );
|
55 |
+
}
|
56 |
+
return absint( self::SETTING_NOTICE_PERIOD_DEFAULT );
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Get the email subject.
|
61 |
+
*
|
62 |
+
* @return string
|
63 |
+
*/
|
64 |
+
public function get_subject() {
|
65 |
+
$args = $this->get_args();
|
66 |
+
|
67 |
+
/**
|
68 |
+
* @var WP_Post $job
|
69 |
+
*/
|
70 |
+
$job = $args['job'];
|
71 |
+
return sprintf( __( 'Job Listing Expiring: %s', 'wp-job-manager' ), $job->post_title );
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Get `From:` address header value. Can be simple email or formatted `Firstname Lastname <email@example.com>`.
|
76 |
+
*
|
77 |
+
* @return string|bool Email from value or false to use WordPress' default.
|
78 |
+
*/
|
79 |
+
public function get_from() {
|
80 |
+
return false;
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Get array or comma-separated list of email addresses to send message.
|
85 |
+
*
|
86 |
+
* @return string|array
|
87 |
+
*/
|
88 |
+
public function get_to() {
|
89 |
+
$args = $this->get_args();
|
90 |
+
return $args['author']->user_email;
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Expand arguments as necessary for the generation of the email.
|
95 |
+
*
|
96 |
+
* @param $args
|
97 |
+
* @return mixed
|
98 |
+
*/
|
99 |
+
protected function prepare_args( $args ) {
|
100 |
+
$args = parent::prepare_args( $args );
|
101 |
+
|
102 |
+
if ( isset( $args['job'] ) ) {
|
103 |
+
$args['expiring_today'] = false;
|
104 |
+
$today = date( 'Y-m-d', current_time( 'timestamp' ) );
|
105 |
+
$expiring_date = date( 'Y-m-d', strtotime( $args['job']->_job_expires ) );
|
106 |
+
if ( ! empty( $args['job']->_job_expires ) && $today === $expiring_date ) {
|
107 |
+
$args['expiring_today'] = true;
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
return $args;
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Get the settings for this email notifications.
|
116 |
+
*
|
117 |
+
* @return array
|
118 |
+
*/
|
119 |
+
public static function get_setting_fields() {
|
120 |
+
$fields = parent::get_setting_fields();
|
121 |
+
$fields[] = array(
|
122 |
+
'name' => self::SETTING_NOTICE_PERIOD_NAME,
|
123 |
+
'std' => self::SETTING_NOTICE_PERIOD_DEFAULT,
|
124 |
+
'label' => __( 'Notice Period', 'wp-job-manager' ),
|
125 |
+
'type' => 'number',
|
126 |
+
'after' => ' ' . __( 'days', 'wp-job-manager' ),
|
127 |
+
'attributes' => array( 'min' => 0 ),
|
128 |
+
);
|
129 |
+
return $fields;
|
130 |
+
}
|
131 |
+
|
132 |
+
/**
|
133 |
+
* Is this email notification enabled by default?
|
134 |
+
*
|
135 |
+
* @return bool
|
136 |
+
*/
|
137 |
+
public static function is_default_enabled() {
|
138 |
+
return false;
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Checks the arguments and returns whether the email notification is properly set up.
|
143 |
+
*
|
144 |
+
* @return bool
|
145 |
+
*/
|
146 |
+
public function is_valid() {
|
147 |
+
$args = $this->get_args();
|
148 |
+
return isset( $args['job'] )
|
149 |
+
&& $args['job'] instanceof WP_Post
|
150 |
+
&& isset( $args['author'] )
|
151 |
+
&& $args['author'] instanceof WP_User
|
152 |
+
&& ! empty( $args['author']->user_email );
|
153 |
+
}
|
154 |
+
|
155 |
+
}
|
includes/rest-api/class-wp-job-manager-controllers-status.php
CHANGED
@@ -95,7 +95,7 @@ class WP_Job_Manager_Controllers_Status extends WP_Job_Manager_REST_Controller_M
|
|
95 |
public function update( $request ) {
|
96 |
$key = $request->get_param( 'key' );
|
97 |
$value = $request->get_param( 'value' );
|
98 |
-
if (
|
99 |
if ( ! function_exists( 'json_decode' ) ) {
|
100 |
include_once ABSPATH . WPINC . 'compat.php';
|
101 |
}
|
95 |
public function update( $request ) {
|
96 |
$key = $request->get_param( 'key' );
|
97 |
$value = $request->get_param( 'value' );
|
98 |
+
if ( ! isset( $value ) ) {
|
99 |
if ( ! function_exists( 'json_decode' ) ) {
|
100 |
include_once ABSPATH . WPINC . 'compat.php';
|
101 |
}
|
includes/rest-api/class-wp-job-manager-models-job-categories-custom-fields.php
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Declaration of Job Categories Custom Fields Model
|
4 |
+
*
|
5 |
+
* @package WPJM/REST
|
6 |
+
*/
|
7 |
+
|
8 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
9 |
+
exit;
|
10 |
+
}
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Class WP_Job_Manager_Models_Job_Categories_Custom_Fields
|
14 |
+
*/
|
15 |
+
class WP_Job_Manager_Models_Job_Categories_Custom_Fields extends WP_Job_Manager_REST_Model
|
16 |
+
implements WP_Job_Manager_REST_Interfaces_Model {
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Declare Fields.
|
20 |
+
*
|
21 |
+
* @return array
|
22 |
+
*/
|
23 |
+
public function declare_fields() {
|
24 |
+
return array();
|
25 |
+
}
|
26 |
+
}
|
includes/rest-api/class-wp-job-manager-models-status.php
CHANGED
@@ -38,8 +38,11 @@ class WP_Job_Manager_Models_Status extends WP_Job_Manager_REST_Model
|
|
38 |
* @return bool
|
39 |
*/
|
40 |
public function permissions_check( $request, $action ) {
|
|
|
|
|
|
|
41 |
if ( in_array( $action, array( 'index', 'show' ), true ) ) {
|
42 |
-
return
|
43 |
}
|
44 |
return current_user_can( 'manage_options' );
|
45 |
}
|
38 |
* @return bool
|
39 |
*/
|
40 |
public function permissions_check( $request, $action ) {
|
41 |
+
if ( ! is_user_logged_in() ) {
|
42 |
+
return false;
|
43 |
+
}
|
44 |
if ( in_array( $action, array( 'index', 'show' ), true ) ) {
|
45 |
+
return current_user_can( 'manage_job_listings' );
|
46 |
}
|
47 |
return current_user_can( 'manage_options' );
|
48 |
}
|
includes/rest-api/class-wp-job-manager-registrable-job-categories.php
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Exposes Job Categories Taxonomy REST Api
|
4 |
+
*
|
5 |
+
* @package WPJM/REST
|
6 |
+
*/
|
7 |
+
|
8 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
9 |
+
exit;
|
10 |
+
}
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Class WP_Job_Manager_Registrable_Job_Categories
|
14 |
+
*/
|
15 |
+
class WP_Job_Manager_Registrable_Job_Categories extends WP_Job_Manager_Registrable_Taxonomy_Type {
|
16 |
+
/**
|
17 |
+
* Gets the taxonomy type to register.
|
18 |
+
*
|
19 |
+
* @return string Taxonomy type to expose.
|
20 |
+
*/
|
21 |
+
public function get_taxonomy_type() {
|
22 |
+
return 'job_listing_category';
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Gets the REST API base slug.
|
27 |
+
*
|
28 |
+
* @return string Slug for REST API base.
|
29 |
+
*/
|
30 |
+
public function get_rest_base() {
|
31 |
+
return 'job-categories';
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Gets the REST API model class name.
|
36 |
+
*
|
37 |
+
* @return string Class name for the taxonomy type's model.
|
38 |
+
*/
|
39 |
+
public function get_model_class_name() {
|
40 |
+
return 'WP_Job_Manager_Models_Job_Categories_Custom_Fields';
|
41 |
+
}
|
42 |
+
}
|
includes/rest-api/class-wp-job-manager-registrable-job-listings.php
CHANGED
@@ -7,6 +7,10 @@
|
|
7 |
* @package WPJM/REST
|
8 |
*/
|
9 |
|
|
|
|
|
|
|
|
|
10 |
/**
|
11 |
* Class MT_Controller_Extension
|
12 |
*/
|
7 |
* @package WPJM/REST
|
8 |
*/
|
9 |
|
10 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
11 |
+
exit;
|
12 |
+
}
|
13 |
+
|
14 |
/**
|
15 |
* Class MT_Controller_Extension
|
16 |
*/
|
includes/rest-api/class-wp-job-manager-registrable-job-types.php
CHANGED
@@ -12,202 +12,31 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
12 |
/**
|
13 |
* Class WP_Job_Manager_Registrable_Job_Types
|
14 |
*/
|
15 |
-
class WP_Job_Manager_Registrable_Job_Types
|
16 |
-
|
17 |
-
/**
|
18 |
-
* The Model Prototype
|
19 |
-
*
|
20 |
-
* @var WP_Job_Manager_REST_Model
|
21 |
-
*/
|
22 |
-
private $model_prototype;
|
23 |
-
|
24 |
-
/**
|
25 |
-
* Rest Field Name
|
26 |
-
*
|
27 |
-
* @var string
|
28 |
-
*/
|
29 |
-
private $rest_field_name;
|
30 |
-
|
31 |
/**
|
32 |
-
*
|
33 |
*
|
34 |
-
* @
|
35 |
*/
|
36 |
-
|
37 |
-
|
38 |
-
/**
|
39 |
-
* Register Job Types
|
40 |
-
*
|
41 |
-
* @param WP_Job_Manager_REST_Environment $environment The Environment to use.
|
42 |
-
* @throws WP_Job_Manager_REST_Exception Throws.
|
43 |
-
*
|
44 |
-
* @return bool|WP_Error true if valid otherwise error.
|
45 |
-
*/
|
46 |
-
public function register( $environment ) {
|
47 |
-
global $wp_taxonomies;
|
48 |
-
$this->taxonomy_type = 'job_listing_type';
|
49 |
-
$this->rest_field_name = 'fields';
|
50 |
-
|
51 |
-
if ( ! isset( $wp_taxonomies[ $this->taxonomy_type ] ) ) {
|
52 |
-
return false;
|
53 |
-
}
|
54 |
-
|
55 |
-
if ( $wp_taxonomies[ $this->taxonomy_type ]->show_in_rest ) {
|
56 |
-
return true;
|
57 |
-
}
|
58 |
-
|
59 |
-
$wp_taxonomies[ $this->taxonomy_type ]->show_in_rest = true;
|
60 |
-
$wp_taxonomies[ $this->taxonomy_type ]->rest_base = 'job-types';
|
61 |
-
|
62 |
-
$this->model_prototype = $environment->model( 'WP_Job_Manager_Models_Job_Types_Custom_Fields' );
|
63 |
-
|
64 |
-
if ( ! $this->model_prototype ) {
|
65 |
-
return new WP_Error( 'model-not-found' );
|
66 |
-
}
|
67 |
-
register_rest_field( $this->taxonomy_type, $this->rest_field_name, array(
|
68 |
-
'get_callback' => array( $this, 'get_employment_type' ),
|
69 |
-
'update_callback' => array( $this, 'update_employment_type' ),
|
70 |
-
'schema' => $this->get_item_schema(),
|
71 |
-
) );
|
72 |
-
|
73 |
-
return true;
|
74 |
}
|
75 |
|
76 |
/**
|
77 |
-
*
|
78 |
*
|
79 |
-
* @return
|
80 |
*/
|
81 |
-
public function
|
82 |
-
|
83 |
-
$properties = array();
|
84 |
-
$required = array();
|
85 |
-
foreach ( $fields as $field_declaration ) {
|
86 |
-
/**
|
87 |
-
* Our declaration
|
88 |
-
*
|
89 |
-
* @var WP_Job_Manager_REST_Field_Declaration $field_declaration
|
90 |
-
*/
|
91 |
-
$properties[ $field_declaration->get_data_transfer_name() ] = $field_declaration->as_item_schema_property();
|
92 |
-
if ( $field_declaration->is_required() ) {
|
93 |
-
$required[] = $field_declaration->get_data_transfer_name();
|
94 |
-
}
|
95 |
-
}
|
96 |
-
$schema = array(
|
97 |
-
'$schema' => 'http://json-schema.org/schema#',
|
98 |
-
'title' => $this->model_prototype->get_name(),
|
99 |
-
'type' => 'object',
|
100 |
-
'properties' => (array) apply_filters( 'mixtape_rest_api_schema_properties', $properties, $this->model_prototype ),
|
101 |
-
);
|
102 |
-
|
103 |
-
if ( ! empty( $required ) ) {
|
104 |
-
$schema['required'] = $required;
|
105 |
-
}
|
106 |
-
|
107 |
-
return $schema;
|
108 |
}
|
109 |
|
110 |
/**
|
111 |
-
*
|
112 |
-
*
|
113 |
-
* @param array $object Object.
|
114 |
-
* @param string $field_name Field Name.
|
115 |
-
* @param WP_REST_Request $request Request.
|
116 |
-
* @param string $object_type Object Type.
|
117 |
*
|
118 |
-
* @return
|
119 |
-
* @throws WP_Job_Manager_REST_Exception If type not there.
|
120 |
*/
|
121 |
-
public function
|
122 |
-
|
123 |
-
return null;
|
124 |
-
}
|
125 |
-
|
126 |
-
if ( $this->rest_field_name !== $field_name ) {
|
127 |
-
return null;
|
128 |
-
}
|
129 |
-
|
130 |
-
$object_id = absint( $object['id'] );
|
131 |
-
$model = $this->get_model( $object_id );
|
132 |
-
return $model->to_dto();
|
133 |
-
}
|
134 |
-
|
135 |
-
/**
|
136 |
-
* Get a model if exists
|
137 |
-
*
|
138 |
-
* @param int $object_id Object ID.
|
139 |
-
* @return WP_Job_Manager_REST_Interfaces_Model
|
140 |
-
* @throws WP_Job_Manager_REST_Exception On Error.
|
141 |
-
*/
|
142 |
-
private function get_model( $object_id ) {
|
143 |
-
$data = array();
|
144 |
-
foreach ( $this->model_prototype->get_fields( WP_Job_Manager_REST_Field_Declaration::META ) as $field_declaration ) {
|
145 |
-
$field_name = $field_declaration->get_name();
|
146 |
-
if ( metadata_exists( 'term', $object_id, $field_name ) ) {
|
147 |
-
$meta = get_term_meta( $object_id, $field_name, true );
|
148 |
-
$data[ $field_name ] = $meta;
|
149 |
-
}
|
150 |
-
}
|
151 |
-
|
152 |
-
return $this->model_prototype->create( $data, array(
|
153 |
-
'deserialize' => true,
|
154 |
-
) );
|
155 |
-
}
|
156 |
-
|
157 |
-
/**
|
158 |
-
* Our Reader.
|
159 |
-
*
|
160 |
-
* @param mixed $data Data.
|
161 |
-
* @param object $object Object.
|
162 |
-
* @param string $field_name Field Name.
|
163 |
-
* @param WP_REST_Request $request Request.
|
164 |
-
* @param string $object_type Object Type.
|
165 |
-
*
|
166 |
-
* @return mixed|string
|
167 |
-
* @throws WP_Job_Manager_REST_Exception If type not there.
|
168 |
-
*/
|
169 |
-
public function update_employment_type( $data, $object, $field_name, $request, $object_type ) {
|
170 |
-
if ( $this->taxonomy_type !== $object_type ) {
|
171 |
-
return null;
|
172 |
-
}
|
173 |
-
|
174 |
-
if ( $this->rest_field_name !== $field_name ) {
|
175 |
-
return null;
|
176 |
-
}
|
177 |
-
|
178 |
-
if ( ! is_a( $object, 'WP_Term' ) ) {
|
179 |
-
return null;
|
180 |
-
}
|
181 |
-
|
182 |
-
$term_id = absint( $object->term_id );
|
183 |
-
if ( ! $term_id ) {
|
184 |
-
// No way to update this. Bail.
|
185 |
-
return new WP_Error( 'job-types-error-invalid-id', 'job-types-error-invalid-id', array(
|
186 |
-
'status' => 400,
|
187 |
-
) );
|
188 |
-
}
|
189 |
-
$existing_model = $this->get_model( $term_id );
|
190 |
-
|
191 |
-
$updated = $existing_model->update_from_array( $data );
|
192 |
-
if ( is_wp_error( $updated ) ) {
|
193 |
-
return $updated;
|
194 |
-
}
|
195 |
-
|
196 |
-
$maybe_validation_error = $updated->sanitize()->validate();
|
197 |
-
if ( is_wp_error( $maybe_validation_error ) ) {
|
198 |
-
return $maybe_validation_error;
|
199 |
-
}
|
200 |
-
|
201 |
-
$serialized_data = $updated->serialize( WP_Job_Manager_REST_Field_Declaration::META );
|
202 |
-
|
203 |
-
foreach ( $serialized_data as $field_name => $val ) {
|
204 |
-
if ( metadata_exists( 'term', $term_id, $field_name ) ) {
|
205 |
-
update_term_meta( $term_id, $field_name, $val );
|
206 |
-
} else {
|
207 |
-
add_term_meta( $term_id, $field_name, $val );
|
208 |
-
}
|
209 |
-
}
|
210 |
-
|
211 |
-
return true;
|
212 |
}
|
213 |
}
|
12 |
/**
|
13 |
* Class WP_Job_Manager_Registrable_Job_Types
|
14 |
*/
|
15 |
+
class WP_Job_Manager_Registrable_Job_Types extends WP_Job_Manager_Registrable_Taxonomy_Type {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
/**
|
17 |
+
* Gets the taxonomy type to register.
|
18 |
*
|
19 |
+
* @return string Taxonomy type to expose.
|
20 |
*/
|
21 |
+
public function get_taxonomy_type() {
|
22 |
+
return 'job_listing_type';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
}
|
24 |
|
25 |
/**
|
26 |
+
* Gets the REST API base slug.
|
27 |
*
|
28 |
+
* @return string Slug for REST API base.
|
29 |
*/
|
30 |
+
public function get_rest_base() {
|
31 |
+
return 'job-types';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
}
|
33 |
|
34 |
/**
|
35 |
+
* Gets the REST API model class name.
|
|
|
|
|
|
|
|
|
|
|
36 |
*
|
37 |
+
* @return string Class name for the taxonomy type's model.
|
|
|
38 |
*/
|
39 |
+
public function get_model_class_name() {
|
40 |
+
return 'WP_Job_Manager_Models_Job_Types_Custom_Fields';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
}
|
42 |
}
|
includes/rest-api/class-wp-job-manager-registrable-taxonomy-type.php
ADDED
@@ -0,0 +1,229 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Exposes Taxonomy REST Api
|
4 |
+
*
|
5 |
+
* @package WPJM/REST
|
6 |
+
*/
|
7 |
+
|
8 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
9 |
+
exit;
|
10 |
+
}
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Class WP_Job_Manager_Registrable_Taxonomy_Type
|
14 |
+
*/
|
15 |
+
abstract class WP_Job_Manager_Registrable_Taxonomy_Type implements WP_Job_Manager_REST_Interfaces_Registrable {
|
16 |
+
|
17 |
+
/**
|
18 |
+
* The Model Prototype
|
19 |
+
*
|
20 |
+
* @var WP_Job_Manager_REST_Model
|
21 |
+
*/
|
22 |
+
private $model_prototype;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* REST Field Name
|
26 |
+
*
|
27 |
+
* @var string
|
28 |
+
*/
|
29 |
+
private $rest_field_name;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Gets the taxonomy type to register.
|
33 |
+
*
|
34 |
+
* @return string Taxonomy type to expose.
|
35 |
+
*/
|
36 |
+
abstract public function get_taxonomy_type();
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Gets the REST API base slug.
|
40 |
+
*
|
41 |
+
* @return string Slug for REST API base.
|
42 |
+
*/
|
43 |
+
abstract public function get_rest_base();
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Gets the REST API model class name.
|
47 |
+
*
|
48 |
+
* @return string Class name for the taxonomy type's model.
|
49 |
+
*/
|
50 |
+
abstract public function get_model_class_name();
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Register Job Categories
|
54 |
+
*
|
55 |
+
* @param WP_Job_Manager_REST_Environment $environment The Environment to use.
|
56 |
+
* @throws WP_Job_Manager_REST_Exception Throws.
|
57 |
+
*
|
58 |
+
* @return bool|WP_Error true if valid otherwise error.
|
59 |
+
*/
|
60 |
+
public function register( $environment ) {
|
61 |
+
global $wp_taxonomies;
|
62 |
+
|
63 |
+
$taxonomy_type = $this->get_taxonomy_type();
|
64 |
+
$this->rest_field_name = 'fields';
|
65 |
+
|
66 |
+
if ( ! isset( $wp_taxonomies[ $taxonomy_type ] ) ) {
|
67 |
+
return false;
|
68 |
+
}
|
69 |
+
|
70 |
+
if ( $wp_taxonomies[ $taxonomy_type ]->show_in_rest ) {
|
71 |
+
return true;
|
72 |
+
}
|
73 |
+
|
74 |
+
$wp_taxonomies[ $taxonomy_type ]->show_in_rest = true;
|
75 |
+
$wp_taxonomies[ $taxonomy_type ]->rest_base = $this->get_rest_base();
|
76 |
+
|
77 |
+
$this->model_prototype = $environment->model( $this->get_model_class_name() );
|
78 |
+
|
79 |
+
if ( ! $this->model_prototype ) {
|
80 |
+
return new WP_Error( 'model-not-found' );
|
81 |
+
}
|
82 |
+
register_rest_field( $taxonomy_type, $this->rest_field_name, array(
|
83 |
+
'get_callback' => array( $this, 'get_taxonomy_term' ),
|
84 |
+
'update_callback' => array( $this, 'update_taxonomy_term' ),
|
85 |
+
'schema' => $this->get_item_schema(),
|
86 |
+
) );
|
87 |
+
|
88 |
+
return true;
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Get Item Schema
|
93 |
+
*
|
94 |
+
* @return array
|
95 |
+
*/
|
96 |
+
public function get_item_schema() {
|
97 |
+
$fields = $this->model_prototype->get_fields();
|
98 |
+
$properties = array();
|
99 |
+
$required = array();
|
100 |
+
foreach ( $fields as $field_declaration ) {
|
101 |
+
/**
|
102 |
+
* Our declaration
|
103 |
+
*
|
104 |
+
* @var WP_Job_Manager_REST_Field_Declaration $field_declaration
|
105 |
+
*/
|
106 |
+
$properties[ $field_declaration->get_data_transfer_name() ] = $field_declaration->as_item_schema_property();
|
107 |
+
if ( $field_declaration->is_required() ) {
|
108 |
+
$required[] = $field_declaration->get_data_transfer_name();
|
109 |
+
}
|
110 |
+
}
|
111 |
+
$schema = array(
|
112 |
+
'$schema' => 'http://json-schema.org/schema#',
|
113 |
+
'title' => $this->model_prototype->get_name(),
|
114 |
+
'type' => 'object',
|
115 |
+
'properties' => (array) apply_filters( 'mixtape_rest_api_schema_properties', $properties, $this->model_prototype ),
|
116 |
+
);
|
117 |
+
|
118 |
+
if ( ! empty( $required ) ) {
|
119 |
+
$schema['required'] = $required;
|
120 |
+
}
|
121 |
+
|
122 |
+
return $schema;
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Our Get Fields.
|
127 |
+
*
|
128 |
+
* @param array $object Object.
|
129 |
+
* @param string $field_name Field Name.
|
130 |
+
* @param WP_REST_Request $request Request.
|
131 |
+
* @param string $object_type Object Type.
|
132 |
+
*
|
133 |
+
* @return mixed|string
|
134 |
+
* @throws WP_Job_Manager_REST_Exception If type not there.
|
135 |
+
*/
|
136 |
+
public function get_taxonomy_term( $object, $field_name, $request, $object_type ) {
|
137 |
+
if ( $this->get_taxonomy_type() !== $object_type ) {
|
138 |
+
return null;
|
139 |
+
}
|
140 |
+
|
141 |
+
if ( $this->rest_field_name !== $field_name ) {
|
142 |
+
return null;
|
143 |
+
}
|
144 |
+
|
145 |
+
$object_id = absint( $object['id'] );
|
146 |
+
$model = $this->get_model( $object_id );
|
147 |
+
return $model->to_dto();
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Get a model if exists
|
152 |
+
*
|
153 |
+
* @param int $object_id Object ID.
|
154 |
+
* @return WP_Job_Manager_REST_Interfaces_Model
|
155 |
+
* @throws WP_Job_Manager_REST_Exception On Error.
|
156 |
+
*/
|
157 |
+
private function get_model( $object_id ) {
|
158 |
+
$data = array();
|
159 |
+
foreach ( $this->model_prototype->get_fields( WP_Job_Manager_REST_Field_Declaration::META ) as $field_declaration ) {
|
160 |
+
$field_name = $field_declaration->get_name();
|
161 |
+
if ( metadata_exists( 'term', $object_id, $field_name ) ) {
|
162 |
+
$meta = get_term_meta( $object_id, $field_name, true );
|
163 |
+
$data[ $field_name ] = $meta;
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
return $this->model_prototype->create( $data, array(
|
168 |
+
'deserialize' => true,
|
169 |
+
) );
|
170 |
+
}
|
171 |
+
|
172 |
+
/**
|
173 |
+
* Our Reader.
|
174 |
+
*
|
175 |
+
* @param mixed $data Data.
|
176 |
+
* @param object $object Object.
|
177 |
+
* @param string $field_name Field Name.
|
178 |
+
* @param WP_REST_Request $request Request.
|
179 |
+
* @param string $object_type Object Type.
|
180 |
+
*
|
181 |
+
* @return mixed|string
|
182 |
+
* @throws WP_Job_Manager_REST_Exception If type not there.
|
183 |
+
*/
|
184 |
+
public function update_taxonomy_term( $data, $object, $field_name, $request, $object_type ) {
|
185 |
+
if ( $this->get_taxonomy_type() !== $object_type ) {
|
186 |
+
return null;
|
187 |
+
}
|
188 |
+
|
189 |
+
if ( $this->rest_field_name !== $field_name ) {
|
190 |
+
return null;
|
191 |
+
}
|
192 |
+
|
193 |
+
if ( ! is_a( $object, 'WP_Term' ) ) {
|
194 |
+
return null;
|
195 |
+
}
|
196 |
+
|
197 |
+
$rest_base = $this->get_rest_base();
|
198 |
+
$term_id = absint( $object->term_id );
|
199 |
+
if ( ! $term_id ) {
|
200 |
+
// No way to update this. Bail.
|
201 |
+
return new WP_Error( $rest_base . '-error-invalid-id', $rest_base . '-error-invalid-id', array(
|
202 |
+
'status' => 400,
|
203 |
+
) );
|
204 |
+
}
|
205 |
+
$existing_model = $this->get_model( $term_id );
|
206 |
+
|
207 |
+
$updated = $existing_model->update_from_array( $data );
|
208 |
+
if ( is_wp_error( $updated ) ) {
|
209 |
+
return $updated;
|
210 |
+
}
|
211 |
+
|
212 |
+
$maybe_validation_error = $updated->sanitize()->validate();
|
213 |
+
if ( is_wp_error( $maybe_validation_error ) ) {
|
214 |
+
return $maybe_validation_error;
|
215 |
+
}
|
216 |
+
|
217 |
+
$serialized_data = $updated->serialize( WP_Job_Manager_REST_Field_Declaration::META );
|
218 |
+
|
219 |
+
foreach ( $serialized_data as $field_name => $val ) {
|
220 |
+
if ( metadata_exists( 'term', $term_id, $field_name ) ) {
|
221 |
+
update_term_meta( $term_id, $field_name, $val );
|
222 |
+
} else {
|
223 |
+
add_term_meta( $term_id, $field_name, $val );
|
224 |
+
}
|
225 |
+
}
|
226 |
+
|
227 |
+
return true;
|
228 |
+
}
|
229 |
+
}
|
includes/rest-api/class-wp-job-manager-rest-api.php
CHANGED
@@ -92,8 +92,11 @@ class WP_Job_Manager_REST_API {
|
|
92 |
include_once 'class-wp-job-manager-controllers-status.php';
|
93 |
include_once 'class-wp-job-manager-models-job-listings-custom-fields.php';
|
94 |
include_once 'class-wp-job-manager-models-job-types-custom-fields.php';
|
|
|
95 |
include_once 'class-wp-job-manager-registrable-job-listings.php';
|
|
|
96 |
include_once 'class-wp-job-manager-registrable-job-types.php';
|
|
|
97 |
|
98 |
// Models.
|
99 |
$env->define_model( 'WP_Job_Manager_Models_Settings' )
|
@@ -103,6 +106,7 @@ class WP_Job_Manager_REST_API {
|
|
103 |
$env->define_model( 'WP_Job_Manager_Filters_Status' );
|
104 |
$env->define_model( 'WP_Job_Manager_Models_Job_Listings_Custom_Fields' );
|
105 |
$env->define_model( 'WP_Job_Manager_Models_Job_Types_Custom_Fields' );
|
|
|
106 |
|
107 |
// Endpoints.
|
108 |
$env->rest_api( 'wpjm/v1' )
|
@@ -113,6 +117,7 @@ class WP_Job_Manager_REST_API {
|
|
113 |
'WP_Job_Manager_Models_Job_Listings_Custom_Fields',
|
114 |
'fields' ) );
|
115 |
$env->add_registrable( new WP_Job_Manager_Registrable_Job_Types() );
|
|
|
116 |
}
|
117 |
}
|
118 |
|
92 |
include_once 'class-wp-job-manager-controllers-status.php';
|
93 |
include_once 'class-wp-job-manager-models-job-listings-custom-fields.php';
|
94 |
include_once 'class-wp-job-manager-models-job-types-custom-fields.php';
|
95 |
+
include_once 'class-wp-job-manager-models-job-categories-custom-fields.php';
|
96 |
include_once 'class-wp-job-manager-registrable-job-listings.php';
|
97 |
+
include_once 'class-wp-job-manager-registrable-taxonomy-type.php';
|
98 |
include_once 'class-wp-job-manager-registrable-job-types.php';
|
99 |
+
include_once 'class-wp-job-manager-registrable-job-categories.php';
|
100 |
|
101 |
// Models.
|
102 |
$env->define_model( 'WP_Job_Manager_Models_Settings' )
|
106 |
$env->define_model( 'WP_Job_Manager_Filters_Status' );
|
107 |
$env->define_model( 'WP_Job_Manager_Models_Job_Listings_Custom_Fields' );
|
108 |
$env->define_model( 'WP_Job_Manager_Models_Job_Types_Custom_Fields' );
|
109 |
+
$env->define_model( 'WP_Job_Manager_Models_Job_Categories_Custom_Fields' );
|
110 |
|
111 |
// Endpoints.
|
112 |
$env->rest_api( 'wpjm/v1' )
|
117 |
'WP_Job_Manager_Models_Job_Listings_Custom_Fields',
|
118 |
'fields' ) );
|
119 |
$env->add_registrable( new WP_Job_Manager_Registrable_Job_Types() );
|
120 |
+
$env->add_registrable( new WP_Job_Manager_Registrable_Job_Categories() );
|
121 |
}
|
122 |
}
|
123 |
|
includes/widgets/class-wp-job-manager-widget-featured-jobs.php
CHANGED
@@ -73,13 +73,15 @@ class WP_Job_Manager_Widget_Featured_Jobs extends WP_Job_Manager_Widget {
|
|
73 |
return;
|
74 |
}
|
75 |
|
|
|
|
|
76 |
ob_start();
|
77 |
|
78 |
extract( $args );
|
79 |
-
$titleInstance =
|
80 |
-
$number =
|
81 |
-
$orderby =
|
82 |
-
$order =
|
83 |
$title = apply_filters( 'widget_title', $titleInstance, $instance, $this->id_base );
|
84 |
$jobs = get_job_listings( array(
|
85 |
'posts_per_page' => $number,
|
73 |
return;
|
74 |
}
|
75 |
|
76 |
+
$instance = array_merge( $this->get_default_instance(), $instance );
|
77 |
+
|
78 |
ob_start();
|
79 |
|
80 |
extract( $args );
|
81 |
+
$titleInstance = esc_attr( $instance['title'] );
|
82 |
+
$number = absint( $instance['number'] );
|
83 |
+
$orderby = esc_attr( $instance['orderby'] );
|
84 |
+
$order = esc_attr( $instance['order'] );
|
85 |
$title = apply_filters( 'widget_title', $titleInstance, $instance, $this->id_base );
|
86 |
$jobs = get_job_listings( array(
|
87 |
'posts_per_page' => $number,
|
includes/widgets/class-wp-job-manager-widget-recent-jobs.php
CHANGED
@@ -63,6 +63,8 @@ class WP_Job_Manager_Widget_Recent_Jobs extends WP_Job_Manager_Widget {
|
|
63 |
return;
|
64 |
}
|
65 |
|
|
|
|
|
66 |
ob_start();
|
67 |
|
68 |
extract( $args );
|
@@ -70,8 +72,8 @@ class WP_Job_Manager_Widget_Recent_Jobs extends WP_Job_Manager_Widget {
|
|
70 |
$title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
|
71 |
$number = absint( $instance['number'] );
|
72 |
$jobs = get_job_listings( array(
|
73 |
-
'search_location' =>
|
74 |
-
'search_keywords' =>
|
75 |
'posts_per_page' => $number,
|
76 |
'orderby' => 'date',
|
77 |
'order' => 'DESC',
|
63 |
return;
|
64 |
}
|
65 |
|
66 |
+
$instance = array_merge( $this->get_default_instance(), $instance );
|
67 |
+
|
68 |
ob_start();
|
69 |
|
70 |
extract( $args );
|
72 |
$title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
|
73 |
$number = absint( $instance['number'] );
|
74 |
$jobs = get_job_listings( array(
|
75 |
+
'search_location' => $instance['location'],
|
76 |
+
'search_keywords' => $instance['keyword'],
|
77 |
'posts_per_page' => $number,
|
78 |
'orderby' => 'date',
|
79 |
'order' => 'DESC',
|
languages/wp-job-manager.pot
CHANGED
@@ -2,9 +2,9 @@
|
|
2 |
# This file is distributed under the GPL2+.
|
3 |
msgid ""
|
4 |
msgstr ""
|
5 |
-
"Project-Id-Version: WP Job Manager 1.
|
6 |
"Report-Msgid-Bugs-To: https://github.com/Automattic/WP-Job-Manager/issues\n"
|
7 |
-
"POT-Creation-Date: 2018-
|
8 |
"MIME-Version: 1.0\n"
|
9 |
"Content-Type: text/plain; charset=utf-8\n"
|
10 |
"Content-Transfer-Encoding: 8bit\n"
|
@@ -13,13 +13,21 @@ msgstr ""
|
|
13 |
"Language-Team: LANGUAGE <EMAIL@ADDRESS>\n"
|
14 |
"X-Generator: grunt-wp-i18n 0.5.4\n"
|
15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
#: includes/abstracts/abstract-wp-job-manager-form.php:312
|
17 |
#: includes/abstracts/abstract-wp-job-manager-form.php:325
|
18 |
msgid "\"%s\" check failed. Please try again."
|
19 |
msgstr ""
|
20 |
|
21 |
#: includes/admin/class-wp-job-manager-addons.php:111
|
22 |
-
#: includes/admin/class-wp-job-manager-admin.php:
|
23 |
#: includes/admin/views/html-admin-page-addons.php:2
|
24 |
msgid "WP Job Manager Add-ons"
|
25 |
msgstr ""
|
@@ -31,178 +39,202 @@ msgstr ""
|
|
31 |
|
32 |
#: includes/admin/class-wp-job-manager-admin.php:86
|
33 |
msgid ""
|
34 |
-
"
|
35 |
-
"
|
36 |
-
"WordPress</a> before updating WP Job Manager."
|
37 |
msgstr ""
|
38 |
|
39 |
#: includes/admin/class-wp-job-manager-admin.php:97
|
40 |
msgid "<a href=\"%s\" style=\"color: red\">WordPress Update Required</a>"
|
41 |
msgstr ""
|
42 |
|
43 |
-
#: includes/admin/class-wp-job-manager-admin.php:
|
44 |
msgid "Settings"
|
45 |
msgstr ""
|
46 |
|
47 |
-
#: includes/admin/class-wp-job-manager-admin.php:
|
48 |
msgid "Add-ons"
|
49 |
msgstr ""
|
50 |
|
51 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
52 |
msgid "Approve %s"
|
53 |
msgstr ""
|
54 |
|
55 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
56 |
msgid "%s approved"
|
57 |
msgstr ""
|
58 |
|
59 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
60 |
msgid "Expire %s"
|
61 |
msgstr ""
|
62 |
|
63 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
64 |
msgid "%s expired"
|
65 |
msgstr ""
|
66 |
|
67 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
68 |
msgid "Mark %s Filled"
|
69 |
msgstr ""
|
70 |
|
71 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
72 |
msgid "%s marked as filled"
|
73 |
msgstr ""
|
74 |
|
75 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
76 |
msgid "Mark %s Not Filled"
|
77 |
msgstr ""
|
78 |
|
79 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
80 |
msgid "%s marked as not filled"
|
81 |
msgstr ""
|
82 |
|
83 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
84 |
msgid "Select category"
|
85 |
msgstr ""
|
86 |
|
87 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
88 |
-
|
89 |
-
msgid "Position"
|
90 |
msgstr ""
|
91 |
|
92 |
#: includes/admin/class-wp-job-manager-cpt.php:330
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
msgid "%s updated. <a href=\"%s\">View</a>"
|
94 |
msgstr ""
|
95 |
|
96 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
97 |
msgid "Custom field updated."
|
98 |
msgstr ""
|
99 |
|
100 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
101 |
msgid "Custom field deleted."
|
102 |
msgstr ""
|
103 |
|
104 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
105 |
msgid "%s updated."
|
106 |
msgstr ""
|
107 |
|
108 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
109 |
msgid "%s restored to revision from %s"
|
110 |
msgstr ""
|
111 |
|
112 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
113 |
msgid "%s published. <a href=\"%s\">View</a>"
|
114 |
msgstr ""
|
115 |
|
116 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
117 |
msgid "%s saved."
|
118 |
msgstr ""
|
119 |
|
120 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
121 |
msgid "%s submitted. <a target=\"_blank\" href=\"%s\">Preview</a>"
|
122 |
msgstr ""
|
123 |
|
124 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
125 |
msgid ""
|
126 |
"%s scheduled for: <strong>%1$s</strong>. <a target=\"_blank\" "
|
127 |
"href=\"%2$s\">Preview</a>"
|
128 |
msgstr ""
|
129 |
|
130 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
131 |
msgid "%s draft updated. <a target=\"_blank\" href=\"%s\">Preview</a>"
|
132 |
msgstr ""
|
133 |
|
134 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
135 |
msgid "Type"
|
136 |
msgstr ""
|
137 |
|
138 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
139 |
#: includes/admin/class-wp-job-manager-writepanels.php:55
|
|
|
140 |
#: includes/forms/class-wp-job-manager-form-submit-job.php:178
|
141 |
#: includes/widgets/class-wp-job-manager-widget-recent-jobs.php:38
|
142 |
#: templates/job-filters.php:35 templates/job-filters.php:36
|
143 |
msgid "Location"
|
144 |
msgstr ""
|
145 |
|
146 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
147 |
msgid "Status"
|
148 |
msgstr ""
|
149 |
|
150 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
151 |
msgid "Posted"
|
152 |
msgstr ""
|
153 |
|
154 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
155 |
msgid "Expires"
|
156 |
msgstr ""
|
157 |
|
158 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
159 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
160 |
msgid "Categories"
|
161 |
msgstr ""
|
162 |
|
163 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
164 |
msgid "Featured?"
|
165 |
msgstr ""
|
166 |
|
167 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
168 |
#: includes/class-wp-job-manager-shortcodes.php:218
|
169 |
msgid "Filled?"
|
170 |
msgstr ""
|
171 |
|
172 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
173 |
msgid "Actions"
|
174 |
msgstr ""
|
175 |
|
176 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
177 |
msgid "ID: %d"
|
178 |
msgstr ""
|
179 |
|
180 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
181 |
msgid "by a guest"
|
182 |
msgstr ""
|
183 |
|
184 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
185 |
msgid "by %s"
|
186 |
msgstr ""
|
187 |
|
188 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
189 |
msgid "Approve"
|
190 |
msgstr ""
|
191 |
|
192 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
193 |
#: includes/admin/class-wp-job-manager-writepanels.php:257
|
194 |
#: includes/admin/class-wp-job-manager-writepanels.php:260
|
195 |
#: includes/admin/class-wp-job-manager-writepanels.php:263
|
196 |
msgid "View"
|
197 |
msgstr ""
|
198 |
|
199 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
200 |
#: includes/class-wp-job-manager-post-types.php:221
|
201 |
#: templates/job-dashboard.php:52 templates/job-dashboard.php:70
|
202 |
msgid "Edit"
|
203 |
msgstr ""
|
204 |
|
205 |
-
#: includes/admin/class-wp-job-manager-cpt.php:
|
206 |
#: templates/job-dashboard.php:75
|
207 |
msgid "Delete"
|
208 |
msgstr ""
|
@@ -253,387 +285,397 @@ msgid ""
|
|
253 |
msgstr ""
|
254 |
|
255 |
#: includes/admin/class-wp-job-manager-settings.php:105
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
256 |
#: includes/class-wp-job-manager-post-types.php:217
|
257 |
#: includes/class-wp-job-manager-post-types.php:293
|
258 |
msgid "Job Listings"
|
259 |
msgstr ""
|
260 |
|
261 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
262 |
msgid "Listings Per Page"
|
263 |
msgstr ""
|
264 |
|
265 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
266 |
msgid "Number of job listings to display per page."
|
267 |
msgstr ""
|
268 |
|
269 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
270 |
msgid "Filled Positions"
|
271 |
msgstr ""
|
272 |
|
273 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
274 |
msgid "Hide filled positions"
|
275 |
msgstr ""
|
276 |
|
277 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
278 |
msgid "Filled positions will not display in your archives."
|
279 |
msgstr ""
|
280 |
|
281 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
282 |
msgid "Hide Expired Listings"
|
283 |
msgstr ""
|
284 |
|
285 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
286 |
msgid "Hide expired listings in job archives/search"
|
287 |
msgstr ""
|
288 |
|
289 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
290 |
msgid "Expired job listings will not be searchable."
|
291 |
msgstr ""
|
292 |
|
293 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
294 |
msgid "Hide Expired Listings Content"
|
295 |
msgstr ""
|
296 |
|
297 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
298 |
msgid "Hide content in expired single job listings"
|
299 |
msgstr ""
|
300 |
|
301 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
302 |
msgid ""
|
303 |
"Your site will display the titles of expired listings, but not the content "
|
304 |
"of the listings. Otherwise, expired listings display their full content "
|
305 |
"minus the application area."
|
306 |
msgstr ""
|
307 |
|
308 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
309 |
msgid "Enable listing categories"
|
310 |
msgstr ""
|
311 |
|
312 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
313 |
msgid ""
|
314 |
"This lets users select from a list of categories when submitting a job. "
|
315 |
"Note: an admin has to create categories before site users can select them."
|
316 |
msgstr ""
|
317 |
|
318 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
319 |
msgid "Multi-select Categories"
|
320 |
msgstr ""
|
321 |
|
322 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
323 |
msgid "Default to category multiselect"
|
324 |
msgstr ""
|
325 |
|
326 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
327 |
msgid ""
|
328 |
"The category selection box will default to allowing multiple selections on "
|
329 |
"the [jobs] shortcode. Without this, users will only be able to select a "
|
330 |
"single category when submitting jobs."
|
331 |
msgstr ""
|
332 |
|
333 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
334 |
msgid "Category Filter Type"
|
335 |
msgstr ""
|
336 |
|
337 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
338 |
msgid ""
|
339 |
"Determines the logic used to display jobs when selecting multiple "
|
340 |
"categories."
|
341 |
msgstr ""
|
342 |
|
343 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
344 |
msgid "Jobs will be shown if within ANY selected category"
|
345 |
msgstr ""
|
346 |
|
347 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
348 |
msgid "Jobs will be shown if within ALL selected categories"
|
349 |
msgstr ""
|
350 |
|
351 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
352 |
msgid "Types"
|
353 |
msgstr ""
|
354 |
|
355 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
356 |
msgid "Enable listing types"
|
357 |
msgstr ""
|
358 |
|
359 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
360 |
msgid ""
|
361 |
"This lets users select from a list of types when submitting a job. Note: an "
|
362 |
"admin has to create types before site users can select them."
|
363 |
msgstr ""
|
364 |
|
365 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
366 |
msgid "Multi-select Listing Types"
|
367 |
msgstr ""
|
368 |
|
369 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
370 |
msgid "Allow multiple types for listings"
|
371 |
msgstr ""
|
372 |
|
373 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
374 |
msgid ""
|
375 |
"This allows users to select more than one type when submitting a job. The "
|
376 |
"metabox on the post editor and the selection box on the front-end job "
|
377 |
"submission form will both reflect this."
|
378 |
msgstr ""
|
379 |
|
380 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
381 |
msgid "Job Submission"
|
382 |
msgstr ""
|
383 |
|
384 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
385 |
msgid "Account Required"
|
386 |
msgstr ""
|
387 |
|
388 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
389 |
msgid "Require an account to submit listings"
|
390 |
msgstr ""
|
391 |
|
392 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
393 |
msgid "Limits job listing submissions to registered, logged-in users."
|
394 |
msgstr ""
|
395 |
|
396 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
397 |
msgid "Account Creation"
|
398 |
msgstr ""
|
399 |
|
400 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
401 |
msgid "Enable account creation during submission"
|
402 |
msgstr ""
|
403 |
|
404 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
405 |
msgid ""
|
406 |
"Includes account creation on the listing submission form, to allow "
|
407 |
"non-registered users to create an account and submit a job listing "
|
408 |
"simultaneously."
|
409 |
msgstr ""
|
410 |
|
411 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
412 |
msgid "Account Username"
|
413 |
msgstr ""
|
414 |
|
415 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
416 |
msgid "Generate usernames from email addresses"
|
417 |
msgstr ""
|
418 |
|
419 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
420 |
msgid ""
|
421 |
"Automatically generates usernames for new accounts from the registrant's "
|
422 |
"email address. If this is not enabled, a \"username\" field will display "
|
423 |
"instead."
|
424 |
msgstr ""
|
425 |
|
426 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
427 |
msgid "Account Password"
|
428 |
msgstr ""
|
429 |
|
430 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
431 |
msgid "Email new users a link to set a password"
|
432 |
msgstr ""
|
433 |
|
434 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
435 |
msgid ""
|
436 |
"Sends an email to the user with their username and a link to set their "
|
437 |
"password. If this is not enabled, a \"password\" field will display "
|
438 |
"instead, and their email address won't be verified."
|
439 |
msgstr ""
|
440 |
|
441 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
442 |
msgid "Account Role"
|
443 |
msgstr ""
|
444 |
|
445 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
446 |
msgid ""
|
447 |
"Any new accounts created during submission will have this role. If you "
|
448 |
"haven't enabled account creation during submission in the options above, "
|
449 |
"your own method of assigning roles will apply."
|
450 |
msgstr ""
|
451 |
|
452 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
453 |
msgid "Moderate New Listings"
|
454 |
msgstr ""
|
455 |
|
456 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
457 |
msgid "Require admin approval of all new listing submissions"
|
458 |
msgstr ""
|
459 |
|
460 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
461 |
msgid ""
|
462 |
"Sets all new submissions to \"pending.\" They will not appear on your site "
|
463 |
"until an admin approves them."
|
464 |
msgstr ""
|
465 |
|
466 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
467 |
msgid "Allow Pending Edits"
|
468 |
msgstr ""
|
469 |
|
470 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
471 |
msgid "Allow editing of pending listings"
|
472 |
msgstr ""
|
473 |
|
474 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
475 |
msgid ""
|
476 |
"Users can continue to edit pending listings until they are approved by an "
|
477 |
"admin."
|
478 |
msgstr ""
|
479 |
|
480 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
481 |
msgid "Allow Published Edits"
|
482 |
msgstr ""
|
483 |
|
484 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
485 |
msgid "Allow editing of published listings"
|
486 |
msgstr ""
|
487 |
|
488 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
489 |
msgid ""
|
490 |
"Choose whether published job listings can be edited and if edits require "
|
491 |
"admin approval. When moderation is required, the original job listings will "
|
492 |
"be unpublished while edits await admin approval."
|
493 |
msgstr ""
|
494 |
|
495 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
496 |
msgid "Users cannot edit"
|
497 |
msgstr ""
|
498 |
|
499 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
500 |
msgid "Users can edit without admin approval"
|
501 |
msgstr ""
|
502 |
|
503 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
504 |
msgid "Users can edit, but edits require admin approval"
|
505 |
msgstr ""
|
506 |
|
507 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
508 |
msgid "Listing Duration"
|
509 |
msgstr ""
|
510 |
|
511 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
512 |
msgid ""
|
513 |
"Listings will display for the set number of days, then expire. Leave this "
|
514 |
"field blank if you don't want listings to have an expiration date."
|
515 |
msgstr ""
|
516 |
|
517 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
518 |
msgid "Application Method"
|
519 |
msgstr ""
|
520 |
|
521 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
522 |
msgid ""
|
523 |
"Choose the application method job listers will need to provide. Specify URL "
|
524 |
"or email address only, or allow listers to choose which they prefer."
|
525 |
msgstr ""
|
526 |
|
527 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
528 |
msgid "Email address or website URL"
|
529 |
msgstr ""
|
530 |
|
531 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
532 |
msgid "Email addresses only"
|
533 |
msgstr ""
|
534 |
|
535 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
536 |
msgid "Website URLs only"
|
537 |
msgstr ""
|
538 |
|
539 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
540 |
msgid "reCAPTCHA"
|
541 |
msgstr ""
|
542 |
|
543 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
544 |
msgid "Are you human?"
|
545 |
msgstr ""
|
546 |
|
547 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
548 |
msgid "Field Label"
|
549 |
msgstr ""
|
550 |
|
551 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
552 |
msgid "The label used for the reCAPTCHA field on forms."
|
553 |
msgstr ""
|
554 |
|
555 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
556 |
msgid "Site Key"
|
557 |
msgstr ""
|
558 |
|
559 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
560 |
msgid ""
|
561 |
"You can retrieve your site key from <a href=\"%s\">Google's reCAPTCHA admin "
|
562 |
"dashboard</a>."
|
563 |
msgstr ""
|
564 |
|
565 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
566 |
msgid "Secret Key"
|
567 |
msgstr ""
|
568 |
|
569 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
570 |
msgid ""
|
571 |
"You can retrieve your secret key from <a href=\"%s\">Google's reCAPTCHA "
|
572 |
"admin dashboard</a>."
|
573 |
msgstr ""
|
574 |
|
575 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
576 |
msgid "Job Submission Form"
|
577 |
msgstr ""
|
578 |
|
579 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
580 |
msgid "Display a reCAPTCHA field on job submission form."
|
581 |
msgstr ""
|
582 |
|
583 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
584 |
msgid ""
|
585 |
"This will help prevent bots from submitting job listings. You must have "
|
586 |
"entered a valid site key and secret key above."
|
587 |
msgstr ""
|
588 |
|
589 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
590 |
msgid "Pages"
|
591 |
msgstr ""
|
592 |
|
593 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
594 |
msgid "Submit Job Form Page"
|
595 |
msgstr ""
|
596 |
|
597 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
598 |
msgid ""
|
599 |
"Select the page where you've used the [submit_job_form] shortcode. This "
|
600 |
"lets the plugin know the location of the form."
|
601 |
msgstr ""
|
602 |
|
603 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
604 |
msgid "Job Dashboard Page"
|
605 |
msgstr ""
|
606 |
|
607 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
608 |
msgid ""
|
609 |
"Select the page where you've used the [job_dashboard] shortcode. This lets "
|
610 |
"the plugin know the location of the dashboard."
|
611 |
msgstr ""
|
612 |
|
613 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
614 |
msgid "Job Listings Page"
|
615 |
msgstr ""
|
616 |
|
617 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
618 |
msgid ""
|
619 |
"Select the page where you've used the [jobs] shortcode. This lets the "
|
620 |
"plugin know the location of the job listings page."
|
621 |
msgstr ""
|
622 |
|
623 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
624 |
msgid "Settings successfully saved"
|
625 |
msgstr ""
|
626 |
|
627 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
628 |
-
msgid "
|
629 |
msgstr ""
|
630 |
|
631 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
632 |
-
msgid "
|
633 |
msgstr ""
|
634 |
|
635 |
-
#: includes/admin/class-wp-job-manager-settings.php:
|
636 |
-
msgid "
|
637 |
msgstr ""
|
638 |
|
639 |
#: includes/admin/class-wp-job-manager-setup.php:51
|
@@ -908,6 +950,7 @@ msgid "Listing Expiry Date"
|
|
908 |
msgstr ""
|
909 |
|
910 |
#: includes/admin/class-wp-job-manager-writepanels.php:118
|
|
|
911 |
#: includes/rest-api/class-wp-job-manager-models-job-listings-custom-fields.php:75
|
912 |
msgid "Posted by"
|
913 |
msgstr ""
|
@@ -970,6 +1013,55 @@ msgstr[1] ""
|
|
970 |
msgid "You must be logged in to upload files using this method."
|
971 |
msgstr ""
|
972 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
973 |
#: includes/class-wp-job-manager-geocode.php:221
|
974 |
msgid "No results found"
|
975 |
msgstr ""
|
@@ -988,11 +1080,6 @@ msgstr ""
|
|
988 |
msgid "Employer"
|
989 |
msgstr ""
|
990 |
|
991 |
-
#: includes/class-wp-job-manager-post-types.php:90
|
992 |
-
#: includes/forms/class-wp-job-manager-form-submit-job.php:195
|
993 |
-
msgid "Job category"
|
994 |
-
msgstr ""
|
995 |
-
|
996 |
#: includes/class-wp-job-manager-post-types.php:91
|
997 |
msgid "Job categories"
|
998 |
msgstr ""
|
@@ -1041,11 +1128,6 @@ msgstr ""
|
|
1041 |
msgid "New %s Name"
|
1042 |
msgstr ""
|
1043 |
|
1044 |
-
#: includes/class-wp-job-manager-post-types.php:139
|
1045 |
-
#: includes/forms/class-wp-job-manager-form-submit-job.php:186
|
1046 |
-
msgid "Job type"
|
1047 |
-
msgstr ""
|
1048 |
-
|
1049 |
#: includes/class-wp-job-manager-post-types.php:140
|
1050 |
msgid "Job types"
|
1051 |
msgstr ""
|
@@ -1183,7 +1265,64 @@ msgid ""
|
|
1183 |
msgstr ""
|
1184 |
|
1185 |
#: includes/class-wp-job-manager-usage-tracking.php:126
|
1186 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1187 |
msgstr ""
|
1188 |
|
1189 |
#: includes/forms/class-wp-job-manager-form-edit-job.php:93
|
@@ -1231,7 +1370,7 @@ msgid "Application email"
|
|
1231 |
msgstr ""
|
1232 |
|
1233 |
#: includes/forms/class-wp-job-manager-form-submit-job.php:148
|
1234 |
-
#: wp-job-manager-template.php:
|
1235 |
msgid "you@yourdomain.com"
|
1236 |
msgstr ""
|
1237 |
|
@@ -1268,10 +1407,6 @@ msgstr ""
|
|
1268 |
msgid "Description"
|
1269 |
msgstr ""
|
1270 |
|
1271 |
-
#: includes/forms/class-wp-job-manager-form-submit-job.php:220
|
1272 |
-
msgid "Company name"
|
1273 |
-
msgstr ""
|
1274 |
-
|
1275 |
#: includes/forms/class-wp-job-manager-form-submit-job.php:223
|
1276 |
msgid "Enter the name of the company"
|
1277 |
msgstr ""
|
@@ -1318,7 +1453,7 @@ msgid "%s is invalid"
|
|
1318 |
msgstr ""
|
1319 |
|
1320 |
#: includes/forms/class-wp-job-manager-form-submit-job.php:332
|
1321 |
-
#: wp-job-manager-functions.php:
|
1322 |
msgid "\"%s\" (filetype %s) needs to be one of the following file types: %s"
|
1323 |
msgstr ""
|
1324 |
|
@@ -1497,10 +1632,6 @@ msgstr ""
|
|
1497 |
msgid "Every Two Weeks"
|
1498 |
msgstr ""
|
1499 |
|
1500 |
-
#: lib/usage-tracking/class-usage-tracking-base.php:472
|
1501 |
-
msgid "Enable Usage Tracking"
|
1502 |
-
msgstr ""
|
1503 |
-
|
1504 |
#: lib/usage-tracking/class-usage-tracking-base.php:475
|
1505 |
msgid "Disable Usage Tracking"
|
1506 |
msgstr ""
|
@@ -1552,8 +1683,8 @@ msgstr ""
|
|
1552 |
|
1553 |
#: templates/account-signin.php:45
|
1554 |
msgid ""
|
1555 |
-
"If you don
|
1556 |
-
"
|
1557 |
msgstr ""
|
1558 |
|
1559 |
#: templates/account-signin.php:45
|
@@ -1593,17 +1724,101 @@ msgstr ""
|
|
1593 |
msgid "This listing has expired."
|
1594 |
msgstr ""
|
1595 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1596 |
#: templates/form-fields/file-field.php:45
|
1597 |
msgid "Maximum file size: %s."
|
1598 |
msgstr ""
|
1599 |
|
1600 |
#: templates/form-fields/multiselect-field.php:20
|
1601 |
-
#: wp-job-manager-functions.php:
|
1602 |
msgid "No results match"
|
1603 |
msgstr ""
|
1604 |
|
1605 |
#: templates/form-fields/multiselect-field.php:20
|
1606 |
-
#: wp-job-manager-functions.php:
|
1607 |
msgid "Select Some Options"
|
1608 |
msgstr ""
|
1609 |
|
@@ -1619,7 +1834,7 @@ msgid ""
|
|
1619 |
msgstr ""
|
1620 |
|
1621 |
#: templates/job-application-url.php:18
|
1622 |
-
msgid "To apply for this job please visit
|
1623 |
msgstr ""
|
1624 |
|
1625 |
#: templates/job-application.php:24
|
@@ -1704,67 +1919,67 @@ msgstr ""
|
|
1704 |
msgid "%s submitted successfully. Your listing will be visible once approved."
|
1705 |
msgstr ""
|
1706 |
|
1707 |
-
#: wp-job-manager-functions.php:
|
1708 |
msgid "Reset"
|
1709 |
msgstr ""
|
1710 |
|
1711 |
-
#: wp-job-manager-functions.php:
|
1712 |
msgid "RSS"
|
1713 |
msgstr ""
|
1714 |
|
1715 |
-
#: wp-job-manager-functions.php:
|
1716 |
msgid "Invalid email address."
|
1717 |
msgstr ""
|
1718 |
|
1719 |
-
#: wp-job-manager-functions.php:
|
1720 |
msgid "Your email address isn’t correct."
|
1721 |
msgstr ""
|
1722 |
|
1723 |
-
#: wp-job-manager-functions.php:
|
1724 |
msgid "This email is already registered, please choose another one."
|
1725 |
msgstr ""
|
1726 |
|
1727 |
-
#: wp-job-manager-functions.php:
|
1728 |
msgid "Full Time"
|
1729 |
msgstr ""
|
1730 |
|
1731 |
-
#: wp-job-manager-functions.php:
|
1732 |
msgid "Part Time"
|
1733 |
msgstr ""
|
1734 |
|
1735 |
-
#: wp-job-manager-functions.php:
|
1736 |
msgid "Contractor"
|
1737 |
msgstr ""
|
1738 |
|
1739 |
-
#: wp-job-manager-functions.php:
|
1740 |
msgid "Temporary"
|
1741 |
msgstr ""
|
1742 |
|
1743 |
-
#: wp-job-manager-functions.php:
|
1744 |
msgid "Intern"
|
1745 |
msgstr ""
|
1746 |
|
1747 |
-
#: wp-job-manager-functions.php:
|
1748 |
msgid "Volunteer"
|
1749 |
msgstr ""
|
1750 |
|
1751 |
-
#: wp-job-manager-functions.php:
|
1752 |
msgid "Per Diem"
|
1753 |
msgstr ""
|
1754 |
|
1755 |
-
#: wp-job-manager-functions.php:
|
1756 |
msgid "Other"
|
1757 |
msgstr ""
|
1758 |
|
1759 |
-
#: wp-job-manager-functions.php:
|
1760 |
msgid "Passwords must be at least 8 characters long."
|
1761 |
msgstr ""
|
1762 |
|
1763 |
-
#: wp-job-manager-functions.php:
|
1764 |
msgid "Choose a category…"
|
1765 |
msgstr ""
|
1766 |
|
1767 |
-
#: wp-job-manager-functions.php:
|
1768 |
msgid "Uploaded files need to be one of the following file types: %s"
|
1769 |
msgstr ""
|
1770 |
|
@@ -1776,43 +1991,43 @@ msgstr ""
|
|
1776 |
msgid "Application via \"%s\" listing on %s"
|
1777 |
msgstr ""
|
1778 |
|
1779 |
-
#: wp-job-manager-template.php:
|
1780 |
msgid "Username"
|
1781 |
msgstr ""
|
1782 |
|
1783 |
-
#: wp-job-manager-template.php:
|
1784 |
msgid "Password"
|
1785 |
msgstr ""
|
1786 |
|
1787 |
-
#: wp-job-manager-template.php:
|
1788 |
msgid "Verify Password"
|
1789 |
msgstr ""
|
1790 |
|
1791 |
-
#: wp-job-manager-template.php:
|
1792 |
msgid "Your email"
|
1793 |
msgstr ""
|
1794 |
|
1795 |
-
#: wp-job-manager-template.php:
|
1796 |
msgid "Posted on "
|
1797 |
msgstr ""
|
1798 |
|
1799 |
-
#: wp-job-manager-template.php:
|
1800 |
msgid "Posted %s ago"
|
1801 |
msgstr ""
|
1802 |
|
1803 |
-
#: wp-job-manager-template.php:
|
1804 |
msgid "Anywhere"
|
1805 |
msgstr ""
|
1806 |
|
1807 |
-
#: wp-job-manager.php:
|
1808 |
msgid "Load previous listings"
|
1809 |
msgstr ""
|
1810 |
|
1811 |
-
#: wp-job-manager.php:
|
1812 |
msgid "Invalid file type. Accepted types:"
|
1813 |
msgstr ""
|
1814 |
|
1815 |
-
#: wp-job-manager.php:
|
1816 |
msgid "Are you sure you want to delete this listing?"
|
1817 |
msgstr ""
|
1818 |
|
@@ -1834,7 +2049,7 @@ msgstr ""
|
|
1834 |
msgid "Automattic"
|
1835 |
msgstr ""
|
1836 |
|
1837 |
-
#: includes/admin/class-wp-job-manager-admin.php:
|
1838 |
#: includes/forms/class-wp-job-manager-form-submit-job.php:401
|
1839 |
#. translators: jQuery date format, see
|
1840 |
#. http:api.jqueryui.com/datepicker/#utility-formatDate
|
2 |
# This file is distributed under the GPL2+.
|
3 |
msgid ""
|
4 |
msgstr ""
|
5 |
+
"Project-Id-Version: WP Job Manager 1.31.0\n"
|
6 |
"Report-Msgid-Bugs-To: https://github.com/Automattic/WP-Job-Manager/issues\n"
|
7 |
+
"POT-Creation-Date: 2018-04-26 10:23:25+00:00\n"
|
8 |
"MIME-Version: 1.0\n"
|
9 |
"Content-Type: text/plain; charset=utf-8\n"
|
10 |
"Content-Transfer-Encoding: 8bit\n"
|
13 |
"Language-Team: LANGUAGE <EMAIL@ADDRESS>\n"
|
14 |
"X-Generator: grunt-wp-i18n 0.5.4\n"
|
15 |
|
16 |
+
#: includes/3rd-party/wpml.php:82
|
17 |
+
msgid "Page Not Set"
|
18 |
+
msgstr ""
|
19 |
+
|
20 |
+
#: includes/3rd-party/wpml.php:94
|
21 |
+
msgid "<a href=\"%s\">Switch to primary language</a> to edit this setting."
|
22 |
+
msgstr ""
|
23 |
+
|
24 |
#: includes/abstracts/abstract-wp-job-manager-form.php:312
|
25 |
#: includes/abstracts/abstract-wp-job-manager-form.php:325
|
26 |
msgid "\"%s\" check failed. Please try again."
|
27 |
msgstr ""
|
28 |
|
29 |
#: includes/admin/class-wp-job-manager-addons.php:111
|
30 |
+
#: includes/admin/class-wp-job-manager-admin.php:146
|
31 |
#: includes/admin/views/html-admin-page-addons.php:2
|
32 |
msgid "WP Job Manager Add-ons"
|
33 |
msgstr ""
|
39 |
|
40 |
#: includes/admin/class-wp-job-manager-admin.php:86
|
41 |
msgid ""
|
42 |
+
"<strong>WP Job Manager</strong> requires a more recent version of "
|
43 |
+
"WordPress. <a href=\"%s\">Please update WordPresse</a> to avoid issues."
|
|
|
44 |
msgstr ""
|
45 |
|
46 |
#: includes/admin/class-wp-job-manager-admin.php:97
|
47 |
msgid "<a href=\"%s\" style=\"color: red\">WordPress Update Required</a>"
|
48 |
msgstr ""
|
49 |
|
50 |
+
#: includes/admin/class-wp-job-manager-admin.php:143
|
51 |
msgid "Settings"
|
52 |
msgstr ""
|
53 |
|
54 |
+
#: includes/admin/class-wp-job-manager-admin.php:146
|
55 |
msgid "Add-ons"
|
56 |
msgstr ""
|
57 |
|
58 |
+
#: includes/admin/class-wp-job-manager-cpt.php:74
|
59 |
msgid "Approve %s"
|
60 |
msgstr ""
|
61 |
|
62 |
+
#: includes/admin/class-wp-job-manager-cpt.php:75
|
63 |
msgid "%s approved"
|
64 |
msgstr ""
|
65 |
|
66 |
+
#: includes/admin/class-wp-job-manager-cpt.php:79
|
67 |
msgid "Expire %s"
|
68 |
msgstr ""
|
69 |
|
70 |
+
#: includes/admin/class-wp-job-manager-cpt.php:80
|
71 |
msgid "%s expired"
|
72 |
msgstr ""
|
73 |
|
74 |
+
#: includes/admin/class-wp-job-manager-cpt.php:84
|
75 |
msgid "Mark %s Filled"
|
76 |
msgstr ""
|
77 |
|
78 |
+
#: includes/admin/class-wp-job-manager-cpt.php:85
|
79 |
msgid "%s marked as filled"
|
80 |
msgstr ""
|
81 |
|
82 |
+
#: includes/admin/class-wp-job-manager-cpt.php:89
|
83 |
msgid "Mark %s Not Filled"
|
84 |
msgstr ""
|
85 |
|
86 |
+
#: includes/admin/class-wp-job-manager-cpt.php:90
|
87 |
msgid "%s marked as not filled"
|
88 |
msgstr ""
|
89 |
|
90 |
+
#: includes/admin/class-wp-job-manager-cpt.php:302
|
91 |
msgid "Select category"
|
92 |
msgstr ""
|
93 |
|
94 |
+
#: includes/admin/class-wp-job-manager-cpt.php:326
|
95 |
+
msgid "Select Filled"
|
|
|
96 |
msgstr ""
|
97 |
|
98 |
#: includes/admin/class-wp-job-manager-cpt.php:330
|
99 |
+
msgid "Filled"
|
100 |
+
msgstr ""
|
101 |
+
|
102 |
+
#: includes/admin/class-wp-job-manager-cpt.php:334
|
103 |
+
msgid "Not Filled"
|
104 |
+
msgstr ""
|
105 |
+
|
106 |
+
#: includes/admin/class-wp-job-manager-cpt.php:342
|
107 |
+
msgid "Select Featured"
|
108 |
+
msgstr ""
|
109 |
+
|
110 |
+
#: includes/admin/class-wp-job-manager-cpt.php:346
|
111 |
+
msgid "Featured"
|
112 |
+
msgstr ""
|
113 |
+
|
114 |
+
#: includes/admin/class-wp-job-manager-cpt.php:350
|
115 |
+
msgid "Not Featured"
|
116 |
+
msgstr ""
|
117 |
+
|
118 |
+
#: includes/admin/class-wp-job-manager-cpt.php:396
|
119 |
+
#: includes/admin/class-wp-job-manager-cpt.php:440
|
120 |
+
msgid "Position"
|
121 |
+
msgstr ""
|
122 |
+
|
123 |
+
#: includes/admin/class-wp-job-manager-cpt.php:411
|
124 |
msgid "%s updated. <a href=\"%s\">View</a>"
|
125 |
msgstr ""
|
126 |
|
127 |
+
#: includes/admin/class-wp-job-manager-cpt.php:412
|
128 |
msgid "Custom field updated."
|
129 |
msgstr ""
|
130 |
|
131 |
+
#: includes/admin/class-wp-job-manager-cpt.php:413
|
132 |
msgid "Custom field deleted."
|
133 |
msgstr ""
|
134 |
|
135 |
+
#: includes/admin/class-wp-job-manager-cpt.php:414
|
136 |
msgid "%s updated."
|
137 |
msgstr ""
|
138 |
|
139 |
+
#: includes/admin/class-wp-job-manager-cpt.php:415
|
140 |
msgid "%s restored to revision from %s"
|
141 |
msgstr ""
|
142 |
|
143 |
+
#: includes/admin/class-wp-job-manager-cpt.php:416
|
144 |
msgid "%s published. <a href=\"%s\">View</a>"
|
145 |
msgstr ""
|
146 |
|
147 |
+
#: includes/admin/class-wp-job-manager-cpt.php:417
|
148 |
msgid "%s saved."
|
149 |
msgstr ""
|
150 |
|
151 |
+
#: includes/admin/class-wp-job-manager-cpt.php:418
|
152 |
msgid "%s submitted. <a target=\"_blank\" href=\"%s\">Preview</a>"
|
153 |
msgstr ""
|
154 |
|
155 |
+
#: includes/admin/class-wp-job-manager-cpt.php:419
|
156 |
msgid ""
|
157 |
"%s scheduled for: <strong>%1$s</strong>. <a target=\"_blank\" "
|
158 |
"href=\"%2$s\">Preview</a>"
|
159 |
msgstr ""
|
160 |
|
161 |
+
#: includes/admin/class-wp-job-manager-cpt.php:421
|
162 |
msgid "%s draft updated. <a target=\"_blank\" href=\"%s\">Preview</a>"
|
163 |
msgstr ""
|
164 |
|
165 |
+
#: includes/admin/class-wp-job-manager-cpt.php:441
|
166 |
msgid "Type"
|
167 |
msgstr ""
|
168 |
|
169 |
+
#: includes/admin/class-wp-job-manager-cpt.php:442
|
170 |
#: includes/admin/class-wp-job-manager-writepanels.php:55
|
171 |
+
#: includes/class-wp-job-manager-email-notifications.php:226
|
172 |
#: includes/forms/class-wp-job-manager-form-submit-job.php:178
|
173 |
#: includes/widgets/class-wp-job-manager-widget-recent-jobs.php:38
|
174 |
#: templates/job-filters.php:35 templates/job-filters.php:36
|
175 |
msgid "Location"
|
176 |
msgstr ""
|
177 |
|
178 |
+
#: includes/admin/class-wp-job-manager-cpt.php:443
|
179 |
msgid "Status"
|
180 |
msgstr ""
|
181 |
|
182 |
+
#: includes/admin/class-wp-job-manager-cpt.php:444
|
183 |
msgid "Posted"
|
184 |
msgstr ""
|
185 |
|
186 |
+
#: includes/admin/class-wp-job-manager-cpt.php:445
|
187 |
msgid "Expires"
|
188 |
msgstr ""
|
189 |
|
190 |
+
#: includes/admin/class-wp-job-manager-cpt.php:446
|
191 |
+
#: includes/admin/class-wp-job-manager-settings.php:154
|
192 |
msgid "Categories"
|
193 |
msgstr ""
|
194 |
|
195 |
+
#: includes/admin/class-wp-job-manager-cpt.php:447
|
196 |
msgid "Featured?"
|
197 |
msgstr ""
|
198 |
|
199 |
+
#: includes/admin/class-wp-job-manager-cpt.php:448
|
200 |
#: includes/class-wp-job-manager-shortcodes.php:218
|
201 |
msgid "Filled?"
|
202 |
msgstr ""
|
203 |
|
204 |
+
#: includes/admin/class-wp-job-manager-cpt.php:449
|
205 |
msgid "Actions"
|
206 |
msgstr ""
|
207 |
|
208 |
+
#: includes/admin/class-wp-job-manager-cpt.php:513
|
209 |
msgid "ID: %d"
|
210 |
msgstr ""
|
211 |
|
212 |
+
#: includes/admin/class-wp-job-manager-cpt.php:543
|
213 |
msgid "by a guest"
|
214 |
msgstr ""
|
215 |
|
216 |
+
#: includes/admin/class-wp-job-manager-cpt.php:543
|
217 |
msgid "by %s"
|
218 |
msgstr ""
|
219 |
|
220 |
+
#: includes/admin/class-wp-job-manager-cpt.php:561
|
221 |
msgid "Approve"
|
222 |
msgstr ""
|
223 |
|
224 |
+
#: includes/admin/class-wp-job-manager-cpt.php:569
|
225 |
#: includes/admin/class-wp-job-manager-writepanels.php:257
|
226 |
#: includes/admin/class-wp-job-manager-writepanels.php:260
|
227 |
#: includes/admin/class-wp-job-manager-writepanels.php:263
|
228 |
msgid "View"
|
229 |
msgstr ""
|
230 |
|
231 |
+
#: includes/admin/class-wp-job-manager-cpt.php:576
|
232 |
#: includes/class-wp-job-manager-post-types.php:221
|
233 |
#: templates/job-dashboard.php:52 templates/job-dashboard.php:70
|
234 |
msgid "Edit"
|
235 |
msgstr ""
|
236 |
|
237 |
+
#: includes/admin/class-wp-job-manager-cpt.php:583
|
238 |
#: templates/job-dashboard.php:75
|
239 |
msgid "Delete"
|
240 |
msgstr ""
|
285 |
msgstr ""
|
286 |
|
287 |
#: includes/admin/class-wp-job-manager-settings.php:105
|
288 |
+
msgid "Delete Data On Uninstall"
|
289 |
+
msgstr ""
|
290 |
+
|
291 |
+
#: includes/admin/class-wp-job-manager-settings.php:106
|
292 |
+
msgid ""
|
293 |
+
"Delete WP Job Manager data when the plugin is deleted. Once removed, this "
|
294 |
+
"data cannot be restored."
|
295 |
+
msgstr ""
|
296 |
+
|
297 |
+
#: includes/admin/class-wp-job-manager-settings.php:114
|
298 |
#: includes/class-wp-job-manager-post-types.php:217
|
299 |
#: includes/class-wp-job-manager-post-types.php:293
|
300 |
msgid "Job Listings"
|
301 |
msgstr ""
|
302 |
|
303 |
+
#: includes/admin/class-wp-job-manager-settings.php:120
|
304 |
msgid "Listings Per Page"
|
305 |
msgstr ""
|
306 |
|
307 |
+
#: includes/admin/class-wp-job-manager-settings.php:121
|
308 |
msgid "Number of job listings to display per page."
|
309 |
msgstr ""
|
310 |
|
311 |
+
#: includes/admin/class-wp-job-manager-settings.php:127
|
312 |
msgid "Filled Positions"
|
313 |
msgstr ""
|
314 |
|
315 |
+
#: includes/admin/class-wp-job-manager-settings.php:128
|
316 |
msgid "Hide filled positions"
|
317 |
msgstr ""
|
318 |
|
319 |
+
#: includes/admin/class-wp-job-manager-settings.php:129
|
320 |
msgid "Filled positions will not display in your archives."
|
321 |
msgstr ""
|
322 |
|
323 |
+
#: includes/admin/class-wp-job-manager-settings.php:136
|
324 |
msgid "Hide Expired Listings"
|
325 |
msgstr ""
|
326 |
|
327 |
+
#: includes/admin/class-wp-job-manager-settings.php:137
|
328 |
msgid "Hide expired listings in job archives/search"
|
329 |
msgstr ""
|
330 |
|
331 |
+
#: includes/admin/class-wp-job-manager-settings.php:138
|
332 |
msgid "Expired job listings will not be searchable."
|
333 |
msgstr ""
|
334 |
|
335 |
+
#: includes/admin/class-wp-job-manager-settings.php:145
|
336 |
msgid "Hide Expired Listings Content"
|
337 |
msgstr ""
|
338 |
|
339 |
+
#: includes/admin/class-wp-job-manager-settings.php:146
|
340 |
msgid "Hide content in expired single job listings"
|
341 |
msgstr ""
|
342 |
|
343 |
+
#: includes/admin/class-wp-job-manager-settings.php:147
|
344 |
msgid ""
|
345 |
"Your site will display the titles of expired listings, but not the content "
|
346 |
"of the listings. Otherwise, expired listings display their full content "
|
347 |
"minus the application area."
|
348 |
msgstr ""
|
349 |
|
350 |
+
#: includes/admin/class-wp-job-manager-settings.php:155
|
351 |
msgid "Enable listing categories"
|
352 |
msgstr ""
|
353 |
|
354 |
+
#: includes/admin/class-wp-job-manager-settings.php:156
|
355 |
msgid ""
|
356 |
"This lets users select from a list of categories when submitting a job. "
|
357 |
"Note: an admin has to create categories before site users can select them."
|
358 |
msgstr ""
|
359 |
|
360 |
+
#: includes/admin/class-wp-job-manager-settings.php:163
|
361 |
msgid "Multi-select Categories"
|
362 |
msgstr ""
|
363 |
|
364 |
+
#: includes/admin/class-wp-job-manager-settings.php:164
|
365 |
msgid "Default to category multiselect"
|
366 |
msgstr ""
|
367 |
|
368 |
+
#: includes/admin/class-wp-job-manager-settings.php:165
|
369 |
msgid ""
|
370 |
"The category selection box will default to allowing multiple selections on "
|
371 |
"the [jobs] shortcode. Without this, users will only be able to select a "
|
372 |
"single category when submitting jobs."
|
373 |
msgstr ""
|
374 |
|
375 |
+
#: includes/admin/class-wp-job-manager-settings.php:172
|
376 |
msgid "Category Filter Type"
|
377 |
msgstr ""
|
378 |
|
379 |
+
#: includes/admin/class-wp-job-manager-settings.php:173
|
380 |
msgid ""
|
381 |
"Determines the logic used to display jobs when selecting multiple "
|
382 |
"categories."
|
383 |
msgstr ""
|
384 |
|
385 |
+
#: includes/admin/class-wp-job-manager-settings.php:176
|
386 |
msgid "Jobs will be shown if within ANY selected category"
|
387 |
msgstr ""
|
388 |
|
389 |
+
#: includes/admin/class-wp-job-manager-settings.php:177
|
390 |
msgid "Jobs will be shown if within ALL selected categories"
|
391 |
msgstr ""
|
392 |
|
393 |
+
#: includes/admin/class-wp-job-manager-settings.php:183
|
394 |
msgid "Types"
|
395 |
msgstr ""
|
396 |
|
397 |
+
#: includes/admin/class-wp-job-manager-settings.php:184
|
398 |
msgid "Enable listing types"
|
399 |
msgstr ""
|
400 |
|
401 |
+
#: includes/admin/class-wp-job-manager-settings.php:185
|
402 |
msgid ""
|
403 |
"This lets users select from a list of types when submitting a job. Note: an "
|
404 |
"admin has to create types before site users can select them."
|
405 |
msgstr ""
|
406 |
|
407 |
+
#: includes/admin/class-wp-job-manager-settings.php:192
|
408 |
msgid "Multi-select Listing Types"
|
409 |
msgstr ""
|
410 |
|
411 |
+
#: includes/admin/class-wp-job-manager-settings.php:193
|
412 |
msgid "Allow multiple types for listings"
|
413 |
msgstr ""
|
414 |
|
415 |
+
#: includes/admin/class-wp-job-manager-settings.php:194
|
416 |
msgid ""
|
417 |
"This allows users to select more than one type when submitting a job. The "
|
418 |
"metabox on the post editor and the selection box on the front-end job "
|
419 |
"submission form will both reflect this."
|
420 |
msgstr ""
|
421 |
|
422 |
+
#: includes/admin/class-wp-job-manager-settings.php:201
|
423 |
msgid "Job Submission"
|
424 |
msgstr ""
|
425 |
|
426 |
+
#: includes/admin/class-wp-job-manager-settings.php:206
|
427 |
msgid "Account Required"
|
428 |
msgstr ""
|
429 |
|
430 |
+
#: includes/admin/class-wp-job-manager-settings.php:207
|
431 |
msgid "Require an account to submit listings"
|
432 |
msgstr ""
|
433 |
|
434 |
+
#: includes/admin/class-wp-job-manager-settings.php:208
|
435 |
msgid "Limits job listing submissions to registered, logged-in users."
|
436 |
msgstr ""
|
437 |
|
438 |
+
#: includes/admin/class-wp-job-manager-settings.php:215
|
439 |
msgid "Account Creation"
|
440 |
msgstr ""
|
441 |
|
442 |
+
#: includes/admin/class-wp-job-manager-settings.php:216
|
443 |
msgid "Enable account creation during submission"
|
444 |
msgstr ""
|
445 |
|
446 |
+
#: includes/admin/class-wp-job-manager-settings.php:217
|
447 |
msgid ""
|
448 |
"Includes account creation on the listing submission form, to allow "
|
449 |
"non-registered users to create an account and submit a job listing "
|
450 |
"simultaneously."
|
451 |
msgstr ""
|
452 |
|
453 |
+
#: includes/admin/class-wp-job-manager-settings.php:224
|
454 |
msgid "Account Username"
|
455 |
msgstr ""
|
456 |
|
457 |
+
#: includes/admin/class-wp-job-manager-settings.php:225
|
458 |
msgid "Generate usernames from email addresses"
|
459 |
msgstr ""
|
460 |
|
461 |
+
#: includes/admin/class-wp-job-manager-settings.php:226
|
462 |
msgid ""
|
463 |
"Automatically generates usernames for new accounts from the registrant's "
|
464 |
"email address. If this is not enabled, a \"username\" field will display "
|
465 |
"instead."
|
466 |
msgstr ""
|
467 |
|
468 |
+
#: includes/admin/class-wp-job-manager-settings.php:233
|
469 |
msgid "Account Password"
|
470 |
msgstr ""
|
471 |
|
472 |
+
#: includes/admin/class-wp-job-manager-settings.php:234
|
473 |
msgid "Email new users a link to set a password"
|
474 |
msgstr ""
|
475 |
|
476 |
+
#: includes/admin/class-wp-job-manager-settings.php:235
|
477 |
msgid ""
|
478 |
"Sends an email to the user with their username and a link to set their "
|
479 |
"password. If this is not enabled, a \"password\" field will display "
|
480 |
"instead, and their email address won't be verified."
|
481 |
msgstr ""
|
482 |
|
483 |
+
#: includes/admin/class-wp-job-manager-settings.php:242
|
484 |
msgid "Account Role"
|
485 |
msgstr ""
|
486 |
|
487 |
+
#: includes/admin/class-wp-job-manager-settings.php:243
|
488 |
msgid ""
|
489 |
"Any new accounts created during submission will have this role. If you "
|
490 |
"haven't enabled account creation during submission in the options above, "
|
491 |
"your own method of assigning roles will apply."
|
492 |
msgstr ""
|
493 |
|
494 |
+
#: includes/admin/class-wp-job-manager-settings.php:250
|
495 |
msgid "Moderate New Listings"
|
496 |
msgstr ""
|
497 |
|
498 |
+
#: includes/admin/class-wp-job-manager-settings.php:251
|
499 |
msgid "Require admin approval of all new listing submissions"
|
500 |
msgstr ""
|
501 |
|
502 |
+
#: includes/admin/class-wp-job-manager-settings.php:252
|
503 |
msgid ""
|
504 |
"Sets all new submissions to \"pending.\" They will not appear on your site "
|
505 |
"until an admin approves them."
|
506 |
msgstr ""
|
507 |
|
508 |
+
#: includes/admin/class-wp-job-manager-settings.php:259
|
509 |
msgid "Allow Pending Edits"
|
510 |
msgstr ""
|
511 |
|
512 |
+
#: includes/admin/class-wp-job-manager-settings.php:260
|
513 |
msgid "Allow editing of pending listings"
|
514 |
msgstr ""
|
515 |
|
516 |
+
#: includes/admin/class-wp-job-manager-settings.php:261
|
517 |
msgid ""
|
518 |
"Users can continue to edit pending listings until they are approved by an "
|
519 |
"admin."
|
520 |
msgstr ""
|
521 |
|
522 |
+
#: includes/admin/class-wp-job-manager-settings.php:268
|
523 |
msgid "Allow Published Edits"
|
524 |
msgstr ""
|
525 |
|
526 |
+
#: includes/admin/class-wp-job-manager-settings.php:269
|
527 |
msgid "Allow editing of published listings"
|
528 |
msgstr ""
|
529 |
|
530 |
+
#: includes/admin/class-wp-job-manager-settings.php:270
|
531 |
msgid ""
|
532 |
"Choose whether published job listings can be edited and if edits require "
|
533 |
"admin approval. When moderation is required, the original job listings will "
|
534 |
"be unpublished while edits await admin approval."
|
535 |
msgstr ""
|
536 |
|
537 |
+
#: includes/admin/class-wp-job-manager-settings.php:273
|
538 |
msgid "Users cannot edit"
|
539 |
msgstr ""
|
540 |
|
541 |
+
#: includes/admin/class-wp-job-manager-settings.php:274
|
542 |
msgid "Users can edit without admin approval"
|
543 |
msgstr ""
|
544 |
|
545 |
+
#: includes/admin/class-wp-job-manager-settings.php:275
|
546 |
msgid "Users can edit, but edits require admin approval"
|
547 |
msgstr ""
|
548 |
|
549 |
+
#: includes/admin/class-wp-job-manager-settings.php:282
|
550 |
msgid "Listing Duration"
|
551 |
msgstr ""
|
552 |
|
553 |
+
#: includes/admin/class-wp-job-manager-settings.php:283
|
554 |
msgid ""
|
555 |
"Listings will display for the set number of days, then expire. Leave this "
|
556 |
"field blank if you don't want listings to have an expiration date."
|
557 |
msgstr ""
|
558 |
|
559 |
+
#: includes/admin/class-wp-job-manager-settings.php:289
|
560 |
msgid "Application Method"
|
561 |
msgstr ""
|
562 |
|
563 |
+
#: includes/admin/class-wp-job-manager-settings.php:290
|
564 |
msgid ""
|
565 |
"Choose the application method job listers will need to provide. Specify URL "
|
566 |
"or email address only, or allow listers to choose which they prefer."
|
567 |
msgstr ""
|
568 |
|
569 |
+
#: includes/admin/class-wp-job-manager-settings.php:293
|
570 |
msgid "Email address or website URL"
|
571 |
msgstr ""
|
572 |
|
573 |
+
#: includes/admin/class-wp-job-manager-settings.php:294
|
574 |
msgid "Email addresses only"
|
575 |
msgstr ""
|
576 |
|
577 |
+
#: includes/admin/class-wp-job-manager-settings.php:295
|
578 |
msgid "Website URLs only"
|
579 |
msgstr ""
|
580 |
|
581 |
+
#: includes/admin/class-wp-job-manager-settings.php:301
|
582 |
msgid "reCAPTCHA"
|
583 |
msgstr ""
|
584 |
|
585 |
+
#: includes/admin/class-wp-job-manager-settings.php:305
|
586 |
msgid "Are you human?"
|
587 |
msgstr ""
|
588 |
|
589 |
+
#: includes/admin/class-wp-job-manager-settings.php:307
|
590 |
msgid "Field Label"
|
591 |
msgstr ""
|
592 |
|
593 |
+
#: includes/admin/class-wp-job-manager-settings.php:308
|
594 |
msgid "The label used for the reCAPTCHA field on forms."
|
595 |
msgstr ""
|
596 |
|
597 |
+
#: includes/admin/class-wp-job-manager-settings.php:315
|
598 |
msgid "Site Key"
|
599 |
msgstr ""
|
600 |
|
601 |
+
#: includes/admin/class-wp-job-manager-settings.php:316
|
602 |
msgid ""
|
603 |
"You can retrieve your site key from <a href=\"%s\">Google's reCAPTCHA admin "
|
604 |
"dashboard</a>."
|
605 |
msgstr ""
|
606 |
|
607 |
+
#: includes/admin/class-wp-job-manager-settings.php:323
|
608 |
msgid "Secret Key"
|
609 |
msgstr ""
|
610 |
|
611 |
+
#: includes/admin/class-wp-job-manager-settings.php:324
|
612 |
msgid ""
|
613 |
"You can retrieve your secret key from <a href=\"%s\">Google's reCAPTCHA "
|
614 |
"admin dashboard</a>."
|
615 |
msgstr ""
|
616 |
|
617 |
+
#: includes/admin/class-wp-job-manager-settings.php:330
|
618 |
msgid "Job Submission Form"
|
619 |
msgstr ""
|
620 |
|
621 |
+
#: includes/admin/class-wp-job-manager-settings.php:331
|
622 |
msgid "Display a reCAPTCHA field on job submission form."
|
623 |
msgstr ""
|
624 |
|
625 |
+
#: includes/admin/class-wp-job-manager-settings.php:332
|
626 |
msgid ""
|
627 |
"This will help prevent bots from submitting job listings. You must have "
|
628 |
"entered a valid site key and secret key above."
|
629 |
msgstr ""
|
630 |
|
631 |
+
#: includes/admin/class-wp-job-manager-settings.php:339
|
632 |
msgid "Pages"
|
633 |
msgstr ""
|
634 |
|
635 |
+
#: includes/admin/class-wp-job-manager-settings.php:344
|
636 |
msgid "Submit Job Form Page"
|
637 |
msgstr ""
|
638 |
|
639 |
+
#: includes/admin/class-wp-job-manager-settings.php:345
|
640 |
msgid ""
|
641 |
"Select the page where you've used the [submit_job_form] shortcode. This "
|
642 |
"lets the plugin know the location of the form."
|
643 |
msgstr ""
|
644 |
|
645 |
+
#: includes/admin/class-wp-job-manager-settings.php:351
|
646 |
msgid "Job Dashboard Page"
|
647 |
msgstr ""
|
648 |
|
649 |
+
#: includes/admin/class-wp-job-manager-settings.php:352
|
650 |
msgid ""
|
651 |
"Select the page where you've used the [job_dashboard] shortcode. This lets "
|
652 |
"the plugin know the location of the dashboard."
|
653 |
msgstr ""
|
654 |
|
655 |
+
#: includes/admin/class-wp-job-manager-settings.php:358
|
656 |
msgid "Job Listings Page"
|
657 |
msgstr ""
|
658 |
|
659 |
+
#: includes/admin/class-wp-job-manager-settings.php:359
|
660 |
msgid ""
|
661 |
"Select the page where you've used the [jobs] shortcode. This lets the "
|
662 |
"plugin know the location of the job listings page."
|
663 |
msgstr ""
|
664 |
|
665 |
+
#: includes/admin/class-wp-job-manager-settings.php:405
|
666 |
msgid "Settings successfully saved"
|
667 |
msgstr ""
|
668 |
|
669 |
+
#: includes/admin/class-wp-job-manager-settings.php:430
|
670 |
+
msgid "Save Changes"
|
671 |
msgstr ""
|
672 |
|
673 |
+
#: includes/admin/class-wp-job-manager-settings.php:597
|
674 |
+
msgid "--no page--"
|
675 |
msgstr ""
|
676 |
|
677 |
+
#: includes/admin/class-wp-job-manager-settings.php:602
|
678 |
+
msgid "Select a page…"
|
679 |
msgstr ""
|
680 |
|
681 |
#: includes/admin/class-wp-job-manager-setup.php:51
|
950 |
msgstr ""
|
951 |
|
952 |
#: includes/admin/class-wp-job-manager-writepanels.php:118
|
953 |
+
#: includes/class-wp-job-manager-email-notifications.php:280
|
954 |
#: includes/rest-api/class-wp-job-manager-models-job-listings-custom-fields.php:75
|
955 |
msgid "Posted by"
|
956 |
msgstr ""
|
1013 |
msgid "You must be logged in to upload files using this method."
|
1014 |
msgstr ""
|
1015 |
|
1016 |
+
#: includes/class-wp-job-manager-email-notifications.php:215
|
1017 |
+
msgid "Job title"
|
1018 |
+
msgstr ""
|
1019 |
+
|
1020 |
+
#: includes/class-wp-job-manager-email-notifications.php:235
|
1021 |
+
#: includes/class-wp-job-manager-post-types.php:139
|
1022 |
+
#: includes/forms/class-wp-job-manager-form-submit-job.php:186
|
1023 |
+
msgid "Job type"
|
1024 |
+
msgstr ""
|
1025 |
+
|
1026 |
+
#: includes/class-wp-job-manager-email-notifications.php:245
|
1027 |
+
#: includes/class-wp-job-manager-post-types.php:90
|
1028 |
+
#: includes/forms/class-wp-job-manager-form-submit-job.php:195
|
1029 |
+
msgid "Job category"
|
1030 |
+
msgstr ""
|
1031 |
+
|
1032 |
+
#: includes/class-wp-job-manager-email-notifications.php:254
|
1033 |
+
#: includes/forms/class-wp-job-manager-form-submit-job.php:220
|
1034 |
+
msgid "Company name"
|
1035 |
+
msgstr ""
|
1036 |
+
|
1037 |
+
#: includes/class-wp-job-manager-email-notifications.php:262
|
1038 |
+
msgid "Company website"
|
1039 |
+
msgstr ""
|
1040 |
+
|
1041 |
+
#: includes/class-wp-job-manager-email-notifications.php:271
|
1042 |
+
msgid "Listing expires"
|
1043 |
+
msgstr ""
|
1044 |
+
|
1045 |
+
#: includes/class-wp-job-manager-email-notifications.php:427
|
1046 |
+
msgid "Email Notifications"
|
1047 |
+
msgstr ""
|
1048 |
+
|
1049 |
+
#: includes/class-wp-job-manager-email-notifications.php:430
|
1050 |
+
msgid "Select the email notifications to enable."
|
1051 |
+
msgstr ""
|
1052 |
+
|
1053 |
+
#: includes/class-wp-job-manager-email-notifications.php:569
|
1054 |
+
msgid "Format"
|
1055 |
+
msgstr ""
|
1056 |
+
|
1057 |
+
#: includes/class-wp-job-manager-email-notifications.php:572
|
1058 |
+
msgid "Send plain text email"
|
1059 |
+
msgstr ""
|
1060 |
+
|
1061 |
+
#: includes/class-wp-job-manager-email-notifications.php:573
|
1062 |
+
msgid "Send rich text email"
|
1063 |
+
msgstr ""
|
1064 |
+
|
1065 |
#: includes/class-wp-job-manager-geocode.php:221
|
1066 |
msgid "No results found"
|
1067 |
msgstr ""
|
1080 |
msgid "Employer"
|
1081 |
msgstr ""
|
1082 |
|
|
|
|
|
|
|
|
|
|
|
1083 |
#: includes/class-wp-job-manager-post-types.php:91
|
1084 |
msgid "Job categories"
|
1085 |
msgstr ""
|
1128 |
msgid "New %s Name"
|
1129 |
msgstr ""
|
1130 |
|
|
|
|
|
|
|
|
|
|
|
1131 |
#: includes/class-wp-job-manager-post-types.php:140
|
1132 |
msgid "Job types"
|
1133 |
msgstr ""
|
1265 |
msgstr ""
|
1266 |
|
1267 |
#: includes/class-wp-job-manager-usage-tracking.php:126
|
1268 |
+
#: lib/usage-tracking/class-usage-tracking-base.php:472
|
1269 |
+
msgid "Enable Usage Tracking"
|
1270 |
+
msgstr ""
|
1271 |
+
|
1272 |
+
#: includes/emails/class-wp-job-manager-email-admin-expiring-job.php:30
|
1273 |
+
msgid "Admin Notice of Expiring Job Listings"
|
1274 |
+
msgstr ""
|
1275 |
+
|
1276 |
+
#: includes/emails/class-wp-job-manager-email-admin-expiring-job.php:40
|
1277 |
+
msgid "Send notices to the site administrator before a job listing expires."
|
1278 |
+
msgstr ""
|
1279 |
+
|
1280 |
+
#: includes/emails/class-wp-job-manager-email-admin-new-job.php:30
|
1281 |
+
msgid "Admin Notice of New Listing"
|
1282 |
+
msgstr ""
|
1283 |
+
|
1284 |
+
#: includes/emails/class-wp-job-manager-email-admin-new-job.php:40
|
1285 |
+
msgid ""
|
1286 |
+
"Send a notice to the site administrator when a new job is submitted on the "
|
1287 |
+
"frontend."
|
1288 |
+
msgstr ""
|
1289 |
+
|
1290 |
+
#: includes/emails/class-wp-job-manager-email-admin-new-job.php:55
|
1291 |
+
msgid "New Job Listing Submitted: %s"
|
1292 |
+
msgstr ""
|
1293 |
+
|
1294 |
+
#: includes/emails/class-wp-job-manager-email-admin-updated-job.php:30
|
1295 |
+
msgid "Admin Notice of Updated Listing"
|
1296 |
+
msgstr ""
|
1297 |
+
|
1298 |
+
#: includes/emails/class-wp-job-manager-email-admin-updated-job.php:40
|
1299 |
+
msgid ""
|
1300 |
+
"Send a notice to the site administrator when a job is updated on the "
|
1301 |
+
"frontend."
|
1302 |
+
msgstr ""
|
1303 |
+
|
1304 |
+
#: includes/emails/class-wp-job-manager-email-admin-updated-job.php:55
|
1305 |
+
msgid "Job Listing Updated: %s"
|
1306 |
+
msgstr ""
|
1307 |
+
|
1308 |
+
#: includes/emails/class-wp-job-manager-email-employer-expiring-job.php:33
|
1309 |
+
msgid "Employer Notice of Expiring Job Listings"
|
1310 |
+
msgstr ""
|
1311 |
+
|
1312 |
+
#: includes/emails/class-wp-job-manager-email-employer-expiring-job.php:43
|
1313 |
+
msgid "Send notices to employers before a job listing expires."
|
1314 |
+
msgstr ""
|
1315 |
+
|
1316 |
+
#: includes/emails/class-wp-job-manager-email-employer-expiring-job.php:71
|
1317 |
+
msgid "Job Listing Expiring: %s"
|
1318 |
+
msgstr ""
|
1319 |
+
|
1320 |
+
#: includes/emails/class-wp-job-manager-email-employer-expiring-job.php:124
|
1321 |
+
msgid "Notice Period"
|
1322 |
+
msgstr ""
|
1323 |
+
|
1324 |
+
#: includes/emails/class-wp-job-manager-email-employer-expiring-job.php:126
|
1325 |
+
msgid "days"
|
1326 |
msgstr ""
|
1327 |
|
1328 |
#: includes/forms/class-wp-job-manager-form-edit-job.php:93
|
1370 |
msgstr ""
|
1371 |
|
1372 |
#: includes/forms/class-wp-job-manager-form-submit-job.php:148
|
1373 |
+
#: wp-job-manager-template.php:703
|
1374 |
msgid "you@yourdomain.com"
|
1375 |
msgstr ""
|
1376 |
|
1407 |
msgid "Description"
|
1408 |
msgstr ""
|
1409 |
|
|
|
|
|
|
|
|
|
1410 |
#: includes/forms/class-wp-job-manager-form-submit-job.php:223
|
1411 |
msgid "Enter the name of the company"
|
1412 |
msgstr ""
|
1453 |
msgstr ""
|
1454 |
|
1455 |
#: includes/forms/class-wp-job-manager-form-submit-job.php:332
|
1456 |
+
#: wp-job-manager-functions.php:1257
|
1457 |
msgid "\"%s\" (filetype %s) needs to be one of the following file types: %s"
|
1458 |
msgstr ""
|
1459 |
|
1632 |
msgid "Every Two Weeks"
|
1633 |
msgstr ""
|
1634 |
|
|
|
|
|
|
|
|
|
1635 |
#: lib/usage-tracking/class-usage-tracking-base.php:475
|
1636 |
msgid "Disable Usage Tracking"
|
1637 |
msgstr ""
|
1683 |
|
1684 |
#: templates/account-signin.php:45
|
1685 |
msgid ""
|
1686 |
+
"If you don't have an account you can %screate one below by entering your "
|
1687 |
+
"email address/username."
|
1688 |
msgstr ""
|
1689 |
|
1690 |
#: templates/account-signin.php:45
|
1724 |
msgid "This listing has expired."
|
1725 |
msgstr ""
|
1726 |
|
1727 |
+
#: templates/emails/admin-expiring-job.php:30
|
1728 |
+
#: templates/emails/employer-expiring-job.php:30
|
1729 |
+
msgid "The following job listing is expiring today from <a href=\"%s\">%s</a>."
|
1730 |
+
msgstr ""
|
1731 |
+
|
1732 |
+
#: templates/emails/admin-expiring-job.php:32
|
1733 |
+
#: templates/emails/employer-expiring-job.php:32
|
1734 |
+
msgid "The following job listing is expiring soon from <a href=\"%s\">%s</a>."
|
1735 |
+
msgstr ""
|
1736 |
+
|
1737 |
+
#: templates/emails/admin-expiring-job.php:35
|
1738 |
+
msgid "Visit <a href=\"%s\">WordPress admin</a> to manage the listing."
|
1739 |
+
msgstr ""
|
1740 |
+
|
1741 |
+
#: templates/emails/admin-new-job.php:24
|
1742 |
+
msgid "A new job listing has been submitted to <a href=\"%s\">%s</a>."
|
1743 |
+
msgstr ""
|
1744 |
+
|
1745 |
+
#: templates/emails/admin-new-job.php:27
|
1746 |
+
#: templates/emails/plain/admin-new-job.php:26
|
1747 |
+
msgid "It has been published and is now available to the public."
|
1748 |
+
msgstr ""
|
1749 |
+
|
1750 |
+
#: templates/emails/admin-new-job.php:30
|
1751 |
+
msgid ""
|
1752 |
+
"It is awaiting approval by an administrator in <a href=\"%s\">WordPress "
|
1753 |
+
"admin</a>."
|
1754 |
+
msgstr ""
|
1755 |
+
|
1756 |
+
#: templates/emails/admin-updated-job.php:24
|
1757 |
+
msgid "A job listing has been updated on <a href=\"%s\">%s</a>."
|
1758 |
+
msgstr ""
|
1759 |
+
|
1760 |
+
#: templates/emails/admin-updated-job.php:27
|
1761 |
+
#: templates/emails/plain/admin-updated-job.php:26
|
1762 |
+
msgid "The changes have been published and are now available to the public."
|
1763 |
+
msgstr ""
|
1764 |
+
|
1765 |
+
#: templates/emails/admin-updated-job.php:30
|
1766 |
+
msgid ""
|
1767 |
+
"The job listing is not publicly available until the changes are approved by "
|
1768 |
+
"an administrator in the site's <a href=\"%s\">WordPress admin</a>."
|
1769 |
+
msgstr ""
|
1770 |
+
|
1771 |
+
#: templates/emails/employer-expiring-job.php:34
|
1772 |
+
msgid "Visit the <a href=\"%s\">job listing dashboard</a> to manage the listing."
|
1773 |
+
msgstr ""
|
1774 |
+
|
1775 |
+
#: templates/emails/plain/admin-expiring-job.php:29
|
1776 |
+
#: templates/emails/plain/employer-expiring-job.php:29
|
1777 |
+
msgid "The following job listing is expiring today from %s (%s)."
|
1778 |
+
msgstr ""
|
1779 |
+
|
1780 |
+
#: templates/emails/plain/admin-expiring-job.php:31
|
1781 |
+
#: templates/emails/plain/employer-expiring-job.php:31
|
1782 |
+
msgid "The following job listing is expiring soon from %s (%s)."
|
1783 |
+
msgstr ""
|
1784 |
+
|
1785 |
+
#: templates/emails/plain/admin-expiring-job.php:34
|
1786 |
+
msgid "Visit WordPress admin (%s) to manage the listing."
|
1787 |
+
msgstr ""
|
1788 |
+
|
1789 |
+
#: templates/emails/plain/admin-new-job.php:23
|
1790 |
+
msgid "A new job listing has been submitted to %s (%s)."
|
1791 |
+
msgstr ""
|
1792 |
+
|
1793 |
+
#: templates/emails/plain/admin-new-job.php:29
|
1794 |
+
msgid "It is awaiting approval by an administrator in WordPress admin (%s)."
|
1795 |
+
msgstr ""
|
1796 |
+
|
1797 |
+
#: templates/emails/plain/admin-updated-job.php:23
|
1798 |
+
msgid "A job listing has been updated on %s (%s)."
|
1799 |
+
msgstr ""
|
1800 |
+
|
1801 |
+
#: templates/emails/plain/admin-updated-job.php:29
|
1802 |
+
msgid ""
|
1803 |
+
"The job listing is not publicly available until the changes are approved by "
|
1804 |
+
"an administrator in the site's WordPress admin (%s)."
|
1805 |
+
msgstr ""
|
1806 |
+
|
1807 |
+
#: templates/emails/plain/employer-expiring-job.php:33
|
1808 |
+
msgid "Visit the job listing dashboard (%s) to manage the listing."
|
1809 |
+
msgstr ""
|
1810 |
+
|
1811 |
#: templates/form-fields/file-field.php:45
|
1812 |
msgid "Maximum file size: %s."
|
1813 |
msgstr ""
|
1814 |
|
1815 |
#: templates/form-fields/multiselect-field.php:20
|
1816 |
+
#: wp-job-manager-functions.php:1043
|
1817 |
msgid "No results match"
|
1818 |
msgstr ""
|
1819 |
|
1820 |
#: templates/form-fields/multiselect-field.php:20
|
1821 |
+
#: wp-job-manager-functions.php:1044
|
1822 |
msgid "Select Some Options"
|
1823 |
msgstr ""
|
1824 |
|
1834 |
msgstr ""
|
1835 |
|
1836 |
#: templates/job-application-url.php:18
|
1837 |
+
msgid "To apply for this job please visit"
|
1838 |
msgstr ""
|
1839 |
|
1840 |
#: templates/job-application.php:24
|
1919 |
msgid "%s submitted successfully. Your listing will be visible once approved."
|
1920 |
msgstr ""
|
1921 |
|
1922 |
+
#: wp-job-manager-functions.php:435
|
1923 |
msgid "Reset"
|
1924 |
msgstr ""
|
1925 |
|
1926 |
+
#: wp-job-manager-functions.php:439
|
1927 |
msgid "RSS"
|
1928 |
msgstr ""
|
1929 |
|
1930 |
+
#: wp-job-manager-functions.php:539
|
1931 |
msgid "Invalid email address."
|
1932 |
msgstr ""
|
1933 |
|
1934 |
+
#: wp-job-manager-functions.php:547
|
1935 |
msgid "Your email address isn’t correct."
|
1936 |
msgstr ""
|
1937 |
|
1938 |
+
#: wp-job-manager-functions.php:551
|
1939 |
msgid "This email is already registered, please choose another one."
|
1940 |
msgstr ""
|
1941 |
|
1942 |
+
#: wp-job-manager-functions.php:849
|
1943 |
msgid "Full Time"
|
1944 |
msgstr ""
|
1945 |
|
1946 |
+
#: wp-job-manager-functions.php:850
|
1947 |
msgid "Part Time"
|
1948 |
msgstr ""
|
1949 |
|
1950 |
+
#: wp-job-manager-functions.php:851
|
1951 |
msgid "Contractor"
|
1952 |
msgstr ""
|
1953 |
|
1954 |
+
#: wp-job-manager-functions.php:852
|
1955 |
msgid "Temporary"
|
1956 |
msgstr ""
|
1957 |
|
1958 |
+
#: wp-job-manager-functions.php:853
|
1959 |
msgid "Intern"
|
1960 |
msgstr ""
|
1961 |
|
1962 |
+
#: wp-job-manager-functions.php:854
|
1963 |
msgid "Volunteer"
|
1964 |
msgstr ""
|
1965 |
|
1966 |
+
#: wp-job-manager-functions.php:855
|
1967 |
msgid "Per Diem"
|
1968 |
msgstr ""
|
1969 |
|
1970 |
+
#: wp-job-manager-functions.php:856
|
1971 |
msgid "Other"
|
1972 |
msgstr ""
|
1973 |
|
1974 |
+
#: wp-job-manager-functions.php:923
|
1975 |
msgid "Passwords must be at least 8 characters long."
|
1976 |
msgstr ""
|
1977 |
|
1978 |
+
#: wp-job-manager-functions.php:1042
|
1979 |
msgid "Choose a category…"
|
1980 |
msgstr ""
|
1981 |
|
1982 |
+
#: wp-job-manager-functions.php:1259
|
1983 |
msgid "Uploaded files need to be one of the following file types: %s"
|
1984 |
msgstr ""
|
1985 |
|
1991 |
msgid "Application via \"%s\" listing on %s"
|
1992 |
msgstr ""
|
1993 |
|
1994 |
+
#: wp-job-manager-template.php:677
|
1995 |
msgid "Username"
|
1996 |
msgstr ""
|
1997 |
|
1998 |
+
#: wp-job-manager-template.php:685
|
1999 |
msgid "Password"
|
2000 |
msgstr ""
|
2001 |
|
2002 |
+
#: wp-job-manager-template.php:695
|
2003 |
msgid "Verify Password"
|
2004 |
msgstr ""
|
2005 |
|
2006 |
+
#: wp-job-manager-template.php:702
|
2007 |
msgid "Your email"
|
2008 |
msgstr ""
|
2009 |
|
2010 |
+
#: wp-job-manager-template.php:729
|
2011 |
msgid "Posted on "
|
2012 |
msgstr ""
|
2013 |
|
2014 |
+
#: wp-job-manager-template.php:731 wp-job-manager-template.php:751
|
2015 |
msgid "Posted %s ago"
|
2016 |
msgstr ""
|
2017 |
|
2018 |
+
#: wp-job-manager-template.php:774
|
2019 |
msgid "Anywhere"
|
2020 |
msgstr ""
|
2021 |
|
2022 |
+
#: wp-job-manager.php:301
|
2023 |
msgid "Load previous listings"
|
2024 |
msgstr ""
|
2025 |
|
2026 |
+
#: wp-job-manager.php:364
|
2027 |
msgid "Invalid file type. Accepted types:"
|
2028 |
msgstr ""
|
2029 |
|
2030 |
+
#: wp-job-manager.php:375
|
2031 |
msgid "Are you sure you want to delete this listing?"
|
2032 |
msgstr ""
|
2033 |
|
2049 |
msgid "Automattic"
|
2050 |
msgstr ""
|
2051 |
|
2052 |
+
#: includes/admin/class-wp-job-manager-admin.php:131
|
2053 |
#: includes/forms/class-wp-job-manager-form-submit-job.php:401
|
2054 |
#. translators: jQuery date format, see
|
2055 |
#. http:api.jqueryui.com/datepicker/#utility-formatDate
|
lib/emogrifier/class-emogrifier.php
ADDED
@@ -0,0 +1,1557 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This class provides functions for converting CSS styles into inline style attributes in your HTML code.
|
4 |
+
*
|
5 |
+
* For more information, please see the README.md file.
|
6 |
+
*
|
7 |
+
* @version 1.2.0
|
8 |
+
*
|
9 |
+
* @author Cameron Brooks
|
10 |
+
* @author Jaime Prado
|
11 |
+
* @author Oliver Klee <typo3-coding@oliverklee.de>
|
12 |
+
* @author Roman Ožana <ozana@omdesign.cz>
|
13 |
+
* @author Sander Kruger <s.kruger@invessel.com>
|
14 |
+
*
|
15 |
+
* @see https://raw.githubusercontent.com/MyIntervals/emogrifier/V1.2.0/Classes/Emogrifier.php
|
16 |
+
*/
|
17 |
+
// @codingStandardsIgnoreFile
|
18 |
+
class Emogrifier
|
19 |
+
{
|
20 |
+
/**
|
21 |
+
* @var int
|
22 |
+
*/
|
23 |
+
const CACHE_KEY_CSS = 0;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var int
|
27 |
+
*/
|
28 |
+
const CACHE_KEY_SELECTOR = 1;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* @var int
|
32 |
+
*/
|
33 |
+
const CACHE_KEY_XPATH = 2;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @var int
|
37 |
+
*/
|
38 |
+
const CACHE_KEY_CSS_DECLARATIONS_BLOCK = 3;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @var int
|
42 |
+
*/
|
43 |
+
const CACHE_KEY_COMBINED_STYLES = 4;
|
44 |
+
|
45 |
+
/**
|
46 |
+
* for calculating nth-of-type and nth-child selectors
|
47 |
+
*
|
48 |
+
* @var int
|
49 |
+
*/
|
50 |
+
const INDEX = 0;
|
51 |
+
|
52 |
+
/**
|
53 |
+
* for calculating nth-of-type and nth-child selectors
|
54 |
+
*
|
55 |
+
* @var int
|
56 |
+
*/
|
57 |
+
const MULTIPLIER = 1;
|
58 |
+
|
59 |
+
/**
|
60 |
+
* @var string
|
61 |
+
*/
|
62 |
+
const ID_ATTRIBUTE_MATCHER = '/(\\w+)?\\#([\\w\\-]+)/';
|
63 |
+
|
64 |
+
/**
|
65 |
+
* @var string
|
66 |
+
*/
|
67 |
+
const CLASS_ATTRIBUTE_MATCHER = '/(\\w+|[\\*\\]])?((\\.[\\w\\-]+)+)/';
|
68 |
+
|
69 |
+
/**
|
70 |
+
* @var string
|
71 |
+
*/
|
72 |
+
const CONTENT_TYPE_META_TAG = '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">';
|
73 |
+
|
74 |
+
/**
|
75 |
+
* @var string
|
76 |
+
*/
|
77 |
+
const DEFAULT_DOCUMENT_TYPE = '<!DOCTYPE html>';
|
78 |
+
|
79 |
+
/**
|
80 |
+
* @var string
|
81 |
+
*/
|
82 |
+
private $html = '';
|
83 |
+
|
84 |
+
/**
|
85 |
+
* @var string
|
86 |
+
*/
|
87 |
+
private $css = '';
|
88 |
+
|
89 |
+
/**
|
90 |
+
* @var bool[]
|
91 |
+
*/
|
92 |
+
private $excludedSelectors = array();
|
93 |
+
|
94 |
+
/**
|
95 |
+
* @var string[]
|
96 |
+
*/
|
97 |
+
private $unprocessableHtmlTags = array( 'wbr' );
|
98 |
+
|
99 |
+
/**
|
100 |
+
* @var bool[]
|
101 |
+
*/
|
102 |
+
private $allowedMediaTypes = array( 'all' => true, 'screen' => true, 'print' => true );
|
103 |
+
|
104 |
+
/**
|
105 |
+
* @var mixed[]
|
106 |
+
*/
|
107 |
+
private $caches = array(
|
108 |
+
self::CACHE_KEY_CSS => array(),
|
109 |
+
self::CACHE_KEY_SELECTOR => array(),
|
110 |
+
self::CACHE_KEY_XPATH => array(),
|
111 |
+
self::CACHE_KEY_CSS_DECLARATIONS_BLOCK => array(),
|
112 |
+
self::CACHE_KEY_COMBINED_STYLES => array(),
|
113 |
+
);
|
114 |
+
|
115 |
+
/**
|
116 |
+
* the visited nodes with the XPath paths as array keys
|
117 |
+
*
|
118 |
+
* @var \DOMElement[]
|
119 |
+
*/
|
120 |
+
private $visitedNodes = array();
|
121 |
+
|
122 |
+
/**
|
123 |
+
* the styles to apply to the nodes with the XPath paths as array keys for the outer array
|
124 |
+
* and the attribute names/values as key/value pairs for the inner array
|
125 |
+
*
|
126 |
+
* @var string[][]
|
127 |
+
*/
|
128 |
+
private $styleAttributesForNodes = array();
|
129 |
+
|
130 |
+
/**
|
131 |
+
* Determines whether the "style" attributes of tags in the the HTML passed to this class should be preserved.
|
132 |
+
* If set to false, the value of the style attributes will be discarded.
|
133 |
+
*
|
134 |
+
* @var bool
|
135 |
+
*/
|
136 |
+
private $isInlineStyleAttributesParsingEnabled = true;
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Determines whether the <style> blocks in the HTML passed to this class should be parsed.
|
140 |
+
*
|
141 |
+
* If set to true, the <style> blocks will be removed from the HTML and their contents will be applied to the HTML
|
142 |
+
* via inline styles.
|
143 |
+
*
|
144 |
+
* If set to false, the <style> blocks will be left as they are in the HTML.
|
145 |
+
*
|
146 |
+
* @var bool
|
147 |
+
*/
|
148 |
+
private $isStyleBlocksParsingEnabled = true;
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Determines whether elements with the `display: none` property are
|
152 |
+
* removed from the DOM.
|
153 |
+
*
|
154 |
+
* @var bool
|
155 |
+
*/
|
156 |
+
private $shouldKeepInvisibleNodes = true;
|
157 |
+
|
158 |
+
/**
|
159 |
+
* @var string[]
|
160 |
+
*/
|
161 |
+
private $xPathRules = array(
|
162 |
+
// child
|
163 |
+
'/\\s*>\\s*/' => '/',
|
164 |
+
// adjacent sibling
|
165 |
+
'/\\s+\\+\\s+/' => '/following-sibling::*[1]/self::',
|
166 |
+
// descendant
|
167 |
+
'/\\s+(?=.*[^\\]]{1}$)/' => '//',
|
168 |
+
// :first-child
|
169 |
+
'/([^\\/]+):first-child/i' => '*[1]/self::\\1',
|
170 |
+
// :last-child
|
171 |
+
'/([^\\/]+):last-child/i' => '*[last()]/self::\\1',
|
172 |
+
// attribute only
|
173 |
+
'/^\\[(\\w+|\\w+\\=[\'"]?\\w+[\'"]?)\\]/' => '*[@\\1]',
|
174 |
+
// attribute
|
175 |
+
'/(\\w)\\[(\\w+)\\]/' => '\\1[@\\2]',
|
176 |
+
// exact attribute
|
177 |
+
'/(\\w)\\[(\\w+)\\=[\'"]?([\\w\\s]+)[\'"]?\\]/' => '\\1[@\\2="\\3"]',
|
178 |
+
// element attribute~=
|
179 |
+
'/([\\w\\*]+)\\[(\\w+)[\\s]*\\~\\=[\\s]*[\'"]?([\\w-_\\/]+)[\'"]?\\]/'
|
180 |
+
=> '\\1[contains(concat(" ", @\\2, " "), concat(" ", "\\3", " "))]',
|
181 |
+
// element attribute^=
|
182 |
+
'/([\\w\\*]+)\\[(\\w+)[\\s]*\\^\\=[\\s]*[\'"]?([\\w-_\\/]+)[\'"]?\\]/' => '\\1[starts-with(@\\2, "\\3")]',
|
183 |
+
// element attribute*=
|
184 |
+
'/([\\w\\*]+)\\[(\\w+)[\\s]*\\*\\=[\\s]*[\'"]?([\\w-_\\s\\/:;]+)[\'"]?\\]/' => '\\1[contains(@\\2, "\\3")]',
|
185 |
+
// element attribute$=
|
186 |
+
'/([\\w\\*]+)\\[(\\w+)[\\s]*\\$\\=[\\s]*[\'"]?([\\w-_\\s\\/]+)[\'"]?\\]/'
|
187 |
+
=> '\\1[substring(@\\2, string-length(@\\2) - string-length("\\3") + 1) = "\\3"]',
|
188 |
+
// element attribute|=
|
189 |
+
'/([\\w\\*]+)\\[(\\w+)[\\s]*\\|\\=[\\s]*[\'"]?([\\w-_\\s\\/]+)[\'"]?\\]/'
|
190 |
+
=> '\\1[@\\2="\\3" or starts-with(@\\2, concat("\\3", "-"))]',
|
191 |
+
);
|
192 |
+
|
193 |
+
/**
|
194 |
+
* Determines whether CSS styles that have an equivalent HTML attribute
|
195 |
+
* should be mapped and attached to those elements.
|
196 |
+
*
|
197 |
+
* @var bool
|
198 |
+
*/
|
199 |
+
private $shouldMapCssToHtml = false;
|
200 |
+
|
201 |
+
/**
|
202 |
+
* This multi-level array contains simple mappings of CSS properties to
|
203 |
+
* HTML attributes. If a mapping only applies to certain HTML nodes or
|
204 |
+
* only for certain values, the mapping is an object with a whitelist
|
205 |
+
* of nodes and values.
|
206 |
+
*
|
207 |
+
* @var mixed[][]
|
208 |
+
*/
|
209 |
+
private $cssToHtmlMap = array(
|
210 |
+
'background-color' => array(
|
211 |
+
'attribute' => 'bgcolor',
|
212 |
+
),
|
213 |
+
'text-align' => array(
|
214 |
+
'attribute' => 'align',
|
215 |
+
'nodes' => array('p', 'div', 'td'),
|
216 |
+
'values' => array('left', 'right', 'center', 'justify'),
|
217 |
+
),
|
218 |
+
'float' => array(
|
219 |
+
'attribute' => 'align',
|
220 |
+
'nodes' => array('table', 'img'),
|
221 |
+
'values' => array('left', 'right'),
|
222 |
+
),
|
223 |
+
'border-spacing' => array(
|
224 |
+
'attribute' => 'cellspacing',
|
225 |
+
'nodes' => array('table'),
|
226 |
+
),
|
227 |
+
);
|
228 |
+
|
229 |
+
public static $_media = '';
|
230 |
+
|
231 |
+
/**
|
232 |
+
* The constructor.
|
233 |
+
*
|
234 |
+
* @param string $html the HTML to emogrify, must be UTF-8-encoded
|
235 |
+
* @param string $css the CSS to merge, must be UTF-8-encoded
|
236 |
+
*/
|
237 |
+
public function __construct($html = '', $css = '')
|
238 |
+
{
|
239 |
+
$this->setHtml($html);
|
240 |
+
$this->setCss($css);
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* The destructor.
|
245 |
+
*/
|
246 |
+
public function __destruct()
|
247 |
+
{
|
248 |
+
$this->purgeVisitedNodes();
|
249 |
+
}
|
250 |
+
|
251 |
+
/**
|
252 |
+
* Sets the HTML to emogrify.
|
253 |
+
*
|
254 |
+
* @param string $html the HTML to emogrify, must be UTF-8-encoded
|
255 |
+
*
|
256 |
+
* @return void
|
257 |
+
*/
|
258 |
+
public function setHtml($html)
|
259 |
+
{
|
260 |
+
$this->html = $html;
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* Sets the CSS to merge with the HTML.
|
265 |
+
*
|
266 |
+
* @param string $css the CSS to merge, must be UTF-8-encoded
|
267 |
+
*
|
268 |
+
* @return void
|
269 |
+
*/
|
270 |
+
public function setCss($css)
|
271 |
+
{
|
272 |
+
$this->css = $css;
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Applies $this->css to $this->html and returns the HTML with the CSS
|
277 |
+
* applied.
|
278 |
+
*
|
279 |
+
* This method places the CSS inline.
|
280 |
+
*
|
281 |
+
* @return string
|
282 |
+
*
|
283 |
+
* @throws \BadMethodCallException
|
284 |
+
*/
|
285 |
+
public function emogrify()
|
286 |
+
{
|
287 |
+
if ($this->html === '') {
|
288 |
+
throw new BadMethodCallException('Please set some HTML first before calling emogrify.', 1390393096);
|
289 |
+
}
|
290 |
+
|
291 |
+
self::$_media = ''; // reset.
|
292 |
+
|
293 |
+
$xmlDocument = $this->createXmlDocument();
|
294 |
+
$this->process($xmlDocument);
|
295 |
+
|
296 |
+
return $xmlDocument->saveHTML();
|
297 |
+
}
|
298 |
+
|
299 |
+
/**
|
300 |
+
* Applies $this->css to $this->html and returns only the HTML content
|
301 |
+
* within the <body> tag.
|
302 |
+
*
|
303 |
+
* This method places the CSS inline.
|
304 |
+
*
|
305 |
+
* @return string
|
306 |
+
*
|
307 |
+
* @throws \BadMethodCallException
|
308 |
+
*/
|
309 |
+
public function emogrifyBodyContent()
|
310 |
+
{
|
311 |
+
if ($this->html === '') {
|
312 |
+
throw new BadMethodCallException('Please set some HTML first before calling emogrify.', 1390393096);
|
313 |
+
}
|
314 |
+
|
315 |
+
$xmlDocument = $this->createXmlDocument();
|
316 |
+
$this->process($xmlDocument);
|
317 |
+
|
318 |
+
$innerDocument = new DOMDocument();
|
319 |
+
foreach ($xmlDocument->documentElement->getElementsByTagName('body')->item(0)->childNodes as $childNode) {
|
320 |
+
$innerDocument->appendChild($innerDocument->importNode($childNode, true));
|
321 |
+
}
|
322 |
+
|
323 |
+
return html_entity_decode($innerDocument->saveHTML());
|
324 |
+
}
|
325 |
+
|
326 |
+
/**
|
327 |
+
* Applies $this->css to $xmlDocument.
|
328 |
+
*
|
329 |
+
* This method places the CSS inline.
|
330 |
+
*
|
331 |
+
* @param \DOMDocument $xmlDocument
|
332 |
+
*
|
333 |
+
* @return void
|
334 |
+
*
|
335 |
+
* @throws \InvalidArgumentException
|
336 |
+
*/
|
337 |
+
protected function process(DOMDocument $xmlDocument)
|
338 |
+
{
|
339 |
+
$xPath = new DOMXPath($xmlDocument);
|
340 |
+
$this->clearAllCaches();
|
341 |
+
|
342 |
+
// Before be begin processing the CSS file, parse the document and normalize all existing CSS attributes.
|
343 |
+
// This changes 'DISPLAY: none' to 'display: none'.
|
344 |
+
// We wouldn't have to do this if DOMXPath supported XPath 2.0.
|
345 |
+
// Also store a reference of nodes with existing inline styles so we don't overwrite them.
|
346 |
+
$this->purgeVisitedNodes();
|
347 |
+
|
348 |
+
set_error_handler(array($this, 'handleXpathError'), E_WARNING);
|
349 |
+
|
350 |
+
$nodesWithStyleAttributes = $xPath->query('//*[@style]');
|
351 |
+
if ($nodesWithStyleAttributes !== false) {
|
352 |
+
/** @var \DOMElement $node */
|
353 |
+
foreach ($nodesWithStyleAttributes as $node) {
|
354 |
+
if ($this->isInlineStyleAttributesParsingEnabled) {
|
355 |
+
$this->normalizeStyleAttributes($node);
|
356 |
+
} else {
|
357 |
+
$node->removeAttribute('style');
|
358 |
+
}
|
359 |
+
}
|
360 |
+
}
|
361 |
+
|
362 |
+
// grab any existing style blocks from the html and append them to the existing CSS
|
363 |
+
// (these blocks should be appended so as to have precedence over conflicting styles in the existing CSS)
|
364 |
+
$allCss = $this->css;
|
365 |
+
|
366 |
+
if ($this->isStyleBlocksParsingEnabled) {
|
367 |
+
$allCss .= $this->getCssFromAllStyleNodes($xPath);
|
368 |
+
}
|
369 |
+
|
370 |
+
$cssParts = $this->splitCssAndMediaQuery($allCss);
|
371 |
+
$excludedNodes = $this->getNodesToExclude($xPath);
|
372 |
+
$cssRules = $this->parseCssRules($cssParts['css']);
|
373 |
+
foreach ($cssRules as $cssRule) {
|
374 |
+
// query the body for the xpath selector
|
375 |
+
$nodesMatchingCssSelectors = $xPath->query($this->translateCssToXpath($cssRule['selector']));
|
376 |
+
// ignore invalid selectors
|
377 |
+
if ($nodesMatchingCssSelectors === false) {
|
378 |
+
continue;
|
379 |
+
}
|
380 |
+
|
381 |
+
/** @var \DOMElement $node */
|
382 |
+
foreach ($nodesMatchingCssSelectors as $node) {
|
383 |
+
if (in_array($node, $excludedNodes, true)) {
|
384 |
+
continue;
|
385 |
+
}
|
386 |
+
|
387 |
+
// if it has a style attribute, get it, process it, and append (overwrite) new stuff
|
388 |
+
if ($node->hasAttribute('style')) {
|
389 |
+
// break it up into an associative array
|
390 |
+
$oldStyleDeclarations = $this->parseCssDeclarationsBlock($node->getAttribute('style'));
|
391 |
+
} else {
|
392 |
+
$oldStyleDeclarations = array();
|
393 |
+
}
|
394 |
+
$newStyleDeclarations = $this->parseCssDeclarationsBlock($cssRule['declarationsBlock']);
|
395 |
+
if ($this->shouldMapCssToHtml) {
|
396 |
+
$this->mapCssToHtmlAttributes($newStyleDeclarations, $node);
|
397 |
+
}
|
398 |
+
$node->setAttribute(
|
399 |
+
'style',
|
400 |
+
$this->generateStyleStringFromDeclarationsArrays($oldStyleDeclarations, $newStyleDeclarations)
|
401 |
+
);
|
402 |
+
}
|
403 |
+
}
|
404 |
+
|
405 |
+
restore_error_handler();
|
406 |
+
|
407 |
+
if ($this->isInlineStyleAttributesParsingEnabled) {
|
408 |
+
$this->fillStyleAttributesWithMergedStyles();
|
409 |
+
}
|
410 |
+
|
411 |
+
if ($this->shouldKeepInvisibleNodes) {
|
412 |
+
$this->removeInvisibleNodes($xPath);
|
413 |
+
}
|
414 |
+
|
415 |
+
$this->copyCssWithMediaToStyleNode($xmlDocument, $xPath, $cssParts['media']);
|
416 |
+
}
|
417 |
+
|
418 |
+
/**
|
419 |
+
* Applies $styles to $node.
|
420 |
+
*
|
421 |
+
* This method maps CSS styles to HTML attributes and adds those to the
|
422 |
+
* node.
|
423 |
+
*
|
424 |
+
* @param string[] $styles the new CSS styles taken from the global styles to be applied to this node
|
425 |
+
* @param \DOMNode $node node to apply styles to
|
426 |
+
*
|
427 |
+
* @return void
|
428 |
+
*/
|
429 |
+
private function mapCssToHtmlAttributes(array $styles, DOMNode $node)
|
430 |
+
{
|
431 |
+
foreach ($styles as $property => $value) {
|
432 |
+
// Strip !important indicator
|
433 |
+
$value = trim(str_replace('!important', '', $value));
|
434 |
+
$this->mapCssToHtmlAttribute($property, $value, $node);
|
435 |
+
}
|
436 |
+
}
|
437 |
+
|
438 |
+
/**
|
439 |
+
* Tries to apply the CSS style to $node as an attribute.
|
440 |
+
*
|
441 |
+
* This method maps a CSS rule to HTML attributes and adds those to the node.
|
442 |
+
*
|
443 |
+
* @param string $property the name of the CSS property to map
|
444 |
+
* @param string $value the value of the style rule to map
|
445 |
+
* @param \DOMNode $node node to apply styles to
|
446 |
+
*
|
447 |
+
* @return void
|
448 |
+
*/
|
449 |
+
private function mapCssToHtmlAttribute($property, $value, DOMNode $node)
|
450 |
+
{
|
451 |
+
if (!$this->mapSimpleCssProperty($property, $value, $node)) {
|
452 |
+
$this->mapComplexCssProperty($property, $value, $node);
|
453 |
+
}
|
454 |
+
}
|
455 |
+
|
456 |
+
/**
|
457 |
+
* Looks up the CSS property in the mapping table and maps it if it matches the conditions.
|
458 |
+
*
|
459 |
+
* @param string $property the name of the CSS property to map
|
460 |
+
* @param string $value the value of the style rule to map
|
461 |
+
* @param \DOMNode $node node to apply styles to
|
462 |
+
*
|
463 |
+
* @return bool true if the property cab be mapped using the simple mapping table
|
464 |
+
*/
|
465 |
+
private function mapSimpleCssProperty($property, $value, DOMNode $node)
|
466 |
+
{
|
467 |
+
if (!isset($this->cssToHtmlMap[$property])) {
|
468 |
+
return false;
|
469 |
+
}
|
470 |
+
|
471 |
+
$mapping = $this->cssToHtmlMap[$property];
|
472 |
+
$nodesMatch = !isset($mapping['nodes']) || in_array($node->nodeName, $mapping['nodes'], true);
|
473 |
+
$valuesMatch = !isset($mapping['values']) || in_array($value, $mapping['values'], true);
|
474 |
+
if (!$nodesMatch || !$valuesMatch) {
|
475 |
+
return false;
|
476 |
+
}
|
477 |
+
|
478 |
+
$node->setAttribute($mapping['attribute'], $value);
|
479 |
+
|
480 |
+
return true;
|
481 |
+
}
|
482 |
+
|
483 |
+
/**
|
484 |
+
* Maps CSS properties that need special transformation to an HTML attribute.
|
485 |
+
*
|
486 |
+
* @param string $property the name of the CSS property to map
|
487 |
+
* @param string $value the value of the style rule to map
|
488 |
+
* @param \DOMNode $node node to apply styles to
|
489 |
+
*
|
490 |
+
* @return void
|
491 |
+
*/
|
492 |
+
private function mapComplexCssProperty($property, $value, DOMNode $node)
|
493 |
+
{
|
494 |
+
$nodeName = $node->nodeName;
|
495 |
+
$isTable = $nodeName === 'table';
|
496 |
+
$isImage = $nodeName === 'img';
|
497 |
+
$isTableOrImage = $isTable || $isImage;
|
498 |
+
|
499 |
+
switch ($property) {
|
500 |
+
case 'background':
|
501 |
+
// Parse out the color, if any
|
502 |
+
$styles = explode(' ', $value);
|
503 |
+
$first = $styles[0];
|
504 |
+
if (!is_numeric(substr($first, 0, 1)) && substr($first, 0, 3) !== 'url') {
|
505 |
+
// This is not a position or image, assume it's a color
|
506 |
+
$node->setAttribute('bgcolor', $first);
|
507 |
+
}
|
508 |
+
break;
|
509 |
+
case 'width':
|
510 |
+
// intentional fall-through
|
511 |
+
case 'height':
|
512 |
+
// Only parse values in px and %, but not values like "auto".
|
513 |
+
if (preg_match('/^\d+(px|%)$/', $value)) {
|
514 |
+
// Remove 'px'. This regex only conserves numbers and %
|
515 |
+
$number = preg_replace('/[^0-9.%]/', '', $value);
|
516 |
+
$node->setAttribute($property, $number);
|
517 |
+
}
|
518 |
+
break;
|
519 |
+
case 'margin':
|
520 |
+
if ($isTableOrImage) {
|
521 |
+
$margins = $this->parseCssShorthandValue($value);
|
522 |
+
if ($margins['left'] === 'auto' && $margins['right'] === 'auto') {
|
523 |
+
$node->setAttribute('align', 'center');
|
524 |
+
}
|
525 |
+
}
|
526 |
+
break;
|
527 |
+
case 'border':
|
528 |
+
if ($isTableOrImage) {
|
529 |
+
if ($value === 'none' || $value === '0') {
|
530 |
+
$node->setAttribute('border', '0');
|
531 |
+
}
|
532 |
+
}
|
533 |
+
break;
|
534 |
+
default:
|
535 |
+
}
|
536 |
+
}
|
537 |
+
|
538 |
+
/**
|
539 |
+
* Parses a shorthand CSS value and splits it into individual values
|
540 |
+
*
|
541 |
+
* @param string $value a string of CSS value with 1, 2, 3 or 4 sizes
|
542 |
+
* For example: padding: 0 auto;
|
543 |
+
* '0 auto' is split into top: 0, left: auto, bottom: 0,
|
544 |
+
* right: auto.
|
545 |
+
*
|
546 |
+
* @return string[] an array of values for top, right, bottom and left (using these as associative array keys)
|
547 |
+
*/
|
548 |
+
private function parseCssShorthandValue($value)
|
549 |
+
{
|
550 |
+
$values = preg_split('/\\s+/', $value);
|
551 |
+
|
552 |
+
$css = array();
|
553 |
+
$css['top'] = $values[0];
|
554 |
+
$css['right'] = (count($values) > 1) ? $values[1] : $css['top'];
|
555 |
+
$css['bottom'] = (count($values) > 2) ? $values[2] : $css['top'];
|
556 |
+
$css['left'] = (count($values) > 3) ? $values[3] : $css['right'];
|
557 |
+
|
558 |
+
return $css;
|
559 |
+
}
|
560 |
+
|
561 |
+
/**
|
562 |
+
* Extracts and parses the individual rules from a CSS string.
|
563 |
+
*
|
564 |
+
* @param string $css a string of raw CSS code
|
565 |
+
*
|
566 |
+
* @return string[][] an array of string sub-arrays with the keys
|
567 |
+
* "selector" (the CSS selector(s), e.g., "*" or "h1"),
|
568 |
+
* "declarationsBLock" (the semicolon-separated CSS declarations for that selector(s),
|
569 |
+
* e.g., "color: red; height: 4px;"),
|
570 |
+
* and "line" (the line number e.g. 42)
|
571 |
+
*/
|
572 |
+
private function parseCssRules($css)
|
573 |
+
{
|
574 |
+
$cssKey = md5($css);
|
575 |
+
if (!isset($this->caches[self::CACHE_KEY_CSS][$cssKey])) {
|
576 |
+
// process the CSS file for selectors and definitions
|
577 |
+
preg_match_all('/(?:^|[\\s^{}]*)([^{]+){([^}]*)}/mis', $css, $matches, PREG_SET_ORDER);
|
578 |
+
|
579 |
+
$cssRules = array();
|
580 |
+
/** @var string[] $cssRule */
|
581 |
+
foreach ($matches as $key => $cssRule) {
|
582 |
+
$cssDeclaration = trim($cssRule[2]);
|
583 |
+
if ($cssDeclaration === '') {
|
584 |
+
continue;
|
585 |
+
}
|
586 |
+
|
587 |
+
$selectors = explode(',', $cssRule[1]);
|
588 |
+
foreach ($selectors as $selector) {
|
589 |
+
// don't process pseudo-elements and behavioral (dynamic) pseudo-classes;
|
590 |
+
// only allow structural pseudo-classes
|
591 |
+
$hasPseudoElement = strpos($selector, '::') !== false;
|
592 |
+
$hasAnyPseudoClass = (bool) preg_match('/:[a-zA-Z]/', $selector);
|
593 |
+
$hasSupportedPseudoClass = (bool) preg_match('/:\\S+\\-(child|type\\()/i', $selector);
|
594 |
+
if ($hasPseudoElement || ($hasAnyPseudoClass && !$hasSupportedPseudoClass)) {
|
595 |
+
continue;
|
596 |
+
}
|
597 |
+
|
598 |
+
$cssRules[] = array(
|
599 |
+
'selector' => trim($selector),
|
600 |
+
'declarationsBlock' => $cssDeclaration,
|
601 |
+
// keep track of where it appears in the file, since order is important
|
602 |
+
'line' => $key,
|
603 |
+
);
|
604 |
+
}
|
605 |
+
}
|
606 |
+
|
607 |
+
usort($cssRules, array($this, 'sortBySelectorPrecedence'));
|
608 |
+
|
609 |
+
$this->caches[self::CACHE_KEY_CSS][$cssKey] = $cssRules;
|
610 |
+
}
|
611 |
+
|
612 |
+
return $this->caches[self::CACHE_KEY_CSS][$cssKey];
|
613 |
+
}
|
614 |
+
|
615 |
+
/**
|
616 |
+
* Disables the parsing of inline styles.
|
617 |
+
*
|
618 |
+
* @return void
|
619 |
+
*/
|
620 |
+
public function disableInlineStyleAttributesParsing()
|
621 |
+
{
|
622 |
+
$this->isInlineStyleAttributesParsingEnabled = false;
|
623 |
+
}
|
624 |
+
|
625 |
+
/**
|
626 |
+
* Disables the parsing of <style> blocks.
|
627 |
+
*
|
628 |
+
* @return void
|
629 |
+
*/
|
630 |
+
public function disableStyleBlocksParsing()
|
631 |
+
{
|
632 |
+
$this->isStyleBlocksParsingEnabled = false;
|
633 |
+
}
|
634 |
+
|
635 |
+
/**
|
636 |
+
* Disables the removal of elements with `display: none` properties.
|
637 |
+
*
|
638 |
+
* @return void
|
639 |
+
*/
|
640 |
+
public function disableInvisibleNodeRemoval()
|
641 |
+
{
|
642 |
+
$this->shouldKeepInvisibleNodes = false;
|
643 |
+
}
|
644 |
+
|
645 |
+
/**
|
646 |
+
* Enables the attachment/override of HTML attributes for which a
|
647 |
+
* corresponding CSS property has been set.
|
648 |
+
*
|
649 |
+
* @return void
|
650 |
+
*/
|
651 |
+
public function enableCssToHtmlMapping()
|
652 |
+
{
|
653 |
+
$this->shouldMapCssToHtml = true;
|
654 |
+
}
|
655 |
+
|
656 |
+
/**
|
657 |
+
* Clears all caches.
|
658 |
+
*
|
659 |
+
* @return void
|
660 |
+
*/
|
661 |
+
private function clearAllCaches()
|
662 |
+
{
|
663 |
+
$this->clearCache(self::CACHE_KEY_CSS);
|
664 |
+
$this->clearCache(self::CACHE_KEY_SELECTOR);
|
665 |
+
$this->clearCache(self::CACHE_KEY_XPATH);
|
666 |
+
$this->clearCache(self::CACHE_KEY_CSS_DECLARATIONS_BLOCK);
|
667 |
+
$this->clearCache(self::CACHE_KEY_COMBINED_STYLES);
|
668 |
+
}
|
669 |
+
|
670 |
+
/**
|
671 |
+
* Clears a single cache by key.
|
672 |
+
*
|
673 |
+
* @param int $key the cache key, must be CACHE_KEY_CSS, CACHE_KEY_SELECTOR, CACHE_KEY_XPATH
|
674 |
+
* or CACHE_KEY_CSS_DECLARATION_BLOCK
|
675 |
+
*
|
676 |
+
* @return void
|
677 |
+
*
|
678 |
+
* @throws \InvalidArgumentException
|
679 |
+
*/
|
680 |
+
private function clearCache($key)
|
681 |
+
{
|
682 |
+
$allowedCacheKeys = array(
|
683 |
+
self::CACHE_KEY_CSS,
|
684 |
+
self::CACHE_KEY_SELECTOR,
|
685 |
+
self::CACHE_KEY_XPATH,
|
686 |
+
self::CACHE_KEY_CSS_DECLARATIONS_BLOCK,
|
687 |
+
self::CACHE_KEY_COMBINED_STYLES,
|
688 |
+
);
|
689 |
+
if (!in_array($key, $allowedCacheKeys, true)) {
|
690 |
+
throw new InvalidArgumentException('Invalid cache key: ' . $key, 1391822035);
|
691 |
+
}
|
692 |
+
|
693 |
+
$this->caches[$key] = array();
|
694 |
+
}
|
695 |
+
|
696 |
+
/**
|
697 |
+
* Purges the visited nodes.
|
698 |
+
*
|
699 |
+
* @return void
|
700 |
+
*/
|
701 |
+
private function purgeVisitedNodes()
|
702 |
+
{
|
703 |
+
$this->visitedNodes = array();
|
704 |
+
$this->styleAttributesForNodes = array();
|
705 |
+
}
|
706 |
+
|
707 |
+
/**
|
708 |
+
* Marks a tag for removal.
|
709 |
+
*
|
710 |
+
* There are some HTML tags that DOMDocument cannot process, and it will throw an error if it encounters them.
|
711 |
+
* In particular, DOMDocument will complain if you try to use HTML5 tags in an XHTML document.
|
712 |
+
*
|
713 |
+
* Note: The tags will not be removed if they have any content.
|
714 |
+
*
|
715 |
+
* @param string $tagName the tag name, e.g., "p"
|
716 |
+
*
|
717 |
+
* @return void
|
718 |
+
*/
|
719 |
+
public function addUnprocessableHtmlTag($tagName)
|
720 |
+
{
|
721 |
+
$this->unprocessableHtmlTags[] = $tagName;
|
722 |
+
}
|
723 |
+
|
724 |
+
/**
|
725 |
+
* Drops a tag from the removal list.
|
726 |
+
*
|
727 |
+
* @param string $tagName the tag name, e.g., "p"
|
728 |
+
*
|
729 |
+
* @return void
|
730 |
+
*/
|
731 |
+
public function removeUnprocessableHtmlTag($tagName)
|
732 |
+
{
|
733 |
+
$key = array_search($tagName, $this->unprocessableHtmlTags, true);
|
734 |
+
if ($key !== false) {
|
735 |
+
unset($this->unprocessableHtmlTags[$key]);
|
736 |
+
}
|
737 |
+
}
|
738 |
+
|
739 |
+
/**
|
740 |
+
* Marks a media query type to keep.
|
741 |
+
*
|
742 |
+
* @param string $mediaName the media type name, e.g., "braille"
|
743 |
+
*
|
744 |
+
* @return void
|
745 |
+
*/
|
746 |
+
public function addAllowedMediaType($mediaName)
|
747 |
+
{
|
748 |
+
$this->allowedMediaTypes[$mediaName] = true;
|
749 |
+
}
|
750 |
+
|
751 |
+
/**
|
752 |
+
* Drops a media query type from the allowed list.
|
753 |
+
*
|
754 |
+
* @param string $mediaName the tag name, e.g., "braille"
|
755 |
+
*
|
756 |
+
* @return void
|
757 |
+
*/
|
758 |
+
public function removeAllowedMediaType($mediaName)
|
759 |
+
{
|
760 |
+
if (isset($this->allowedMediaTypes[$mediaName])) {
|
761 |
+
unset($this->allowedMediaTypes[$mediaName]);
|
762 |
+
}
|
763 |
+
}
|
764 |
+
|
765 |
+
/**
|
766 |
+
* Adds a selector to exclude nodes from emogrification.
|
767 |
+
*
|
768 |
+
* Any nodes that match the selector will not have their style altered.
|
769 |
+
*
|
770 |
+
* @param string $selector the selector to exclude, e.g., ".editor"
|
771 |
+
*
|
772 |
+
* @return void
|
773 |
+
*/
|
774 |
+
public function addExcludedSelector($selector)
|
775 |
+
{
|
776 |
+
$this->excludedSelectors[$selector] = true;
|
777 |
+
}
|
778 |
+
|
779 |
+
/**
|
780 |
+
* No longer excludes the nodes matching this selector from emogrification.
|
781 |
+
*
|
782 |
+
* @param string $selector the selector to no longer exclude, e.g., ".editor"
|
783 |
+
*
|
784 |
+
* @return void
|
785 |
+
*/
|
786 |
+
public function removeExcludedSelector($selector)
|
787 |
+
{
|
788 |
+
if (isset($this->excludedSelectors[$selector])) {
|
789 |
+
unset($this->excludedSelectors[$selector]);
|
790 |
+
}
|
791 |
+
}
|
792 |
+
|
793 |
+
/**
|
794 |
+
* This removes styles from your email that contain display:none.
|
795 |
+
* We need to look for display:none, but we need to do a case-insensitive search. Since DOMDocument only
|
796 |
+
* supports XPath 1.0, lower-case() isn't available to us. We've thus far only set attributes to lowercase,
|
797 |
+
* not attribute values. Consequently, we need to translate() the letters that would be in 'NONE' ("NOE")
|
798 |
+
* to lowercase.
|
799 |
+
*
|
800 |
+
* @param \DOMXPath $xPath
|
801 |
+
*
|
802 |
+
* @return void
|
803 |
+
*/
|
804 |
+
private function removeInvisibleNodes(DOMXPath $xPath)
|
805 |
+
{
|
806 |
+
$nodesWithStyleDisplayNone = $xPath->query(
|
807 |
+
'//*[contains(translate(translate(@style," ",""),"NOE","noe"),"display:none")]'
|
808 |
+
);
|
809 |
+
if ($nodesWithStyleDisplayNone->length === 0) {
|
810 |
+
return;
|
811 |
+
}
|
812 |
+
|
813 |
+
// The checks on parentNode and is_callable below ensure that if we've deleted the parent node,
|
814 |
+
// we don't try to call removeChild on a nonexistent child node
|
815 |
+
/** @var \DOMNode $node */
|
816 |
+
foreach ($nodesWithStyleDisplayNone as $node) {
|
817 |
+
if ($node->parentNode && is_callable(array($node->parentNode, 'removeChild'))) {
|
818 |
+
$node->parentNode->removeChild($node);
|
819 |
+
}
|
820 |
+
}
|
821 |
+
}
|
822 |
+
|
823 |
+
private function normalizeStyleAttributes_callback( $m ) {
|
824 |
+
return strtolower( $m[0] );
|
825 |
+
}
|
826 |
+
|
827 |
+
/**
|
828 |
+
* Normalizes the value of the "style" attribute and saves it.
|
829 |
+
*
|
830 |
+
* @param \DOMElement $node
|
831 |
+
*
|
832 |
+
* @return void
|
833 |
+
*/
|
834 |
+
private function normalizeStyleAttributes(DOMElement $node)
|
835 |
+
{
|
836 |
+
$normalizedOriginalStyle = preg_replace_callback(
|
837 |
+
'/[A-z\\-]+(?=\\:)/S',
|
838 |
+
array( $this, 'normalizeStyleAttributes_callback' ),
|
839 |
+
$node->getAttribute('style')
|
840 |
+
);
|
841 |
+
|
842 |
+
// in order to not overwrite existing style attributes in the HTML, we
|
843 |
+
// have to save the original HTML styles
|
844 |
+
$nodePath = $node->getNodePath();
|
845 |
+
if (!isset($this->styleAttributesForNodes[$nodePath])) {
|
846 |
+
$this->styleAttributesForNodes[$nodePath] = $this->parseCssDeclarationsBlock($normalizedOriginalStyle);
|
847 |
+
$this->visitedNodes[$nodePath] = $node;
|
848 |
+
}
|
849 |
+
|
850 |
+
$node->setAttribute('style', $normalizedOriginalStyle);
|
851 |
+
}
|
852 |
+
|
853 |
+
/**
|
854 |
+
* Merges styles from styles attributes and style nodes and applies them to the attribute nodes
|
855 |
+
*
|
856 |
+
* @return void
|
857 |
+
*/
|
858 |
+
private function fillStyleAttributesWithMergedStyles()
|
859 |
+
{
|
860 |
+
foreach ($this->styleAttributesForNodes as $nodePath => $styleAttributesForNode) {
|
861 |
+
$node = $this->visitedNodes[$nodePath];
|
862 |
+
$currentStyleAttributes = $this->parseCssDeclarationsBlock($node->getAttribute('style'));
|
863 |
+
$node->setAttribute(
|
864 |
+
'style',
|
865 |
+
$this->generateStyleStringFromDeclarationsArrays(
|
866 |
+
$currentStyleAttributes,
|
867 |
+
$styleAttributesForNode
|
868 |
+
)
|
869 |
+
);
|
870 |
+
}
|
871 |
+
}
|
872 |
+
|
873 |
+
/**
|
874 |
+
* This method merges old or existing name/value array with new name/value array
|
875 |
+
* and then generates a string of the combined style suitable for placing inline.
|
876 |
+
* This becomes the single point for CSS string generation allowing for consistent
|
877 |
+
* CSS output no matter where the CSS originally came from.
|
878 |
+
*
|
879 |
+
* @param string[] $oldStyles
|
880 |
+
* @param string[] $newStyles
|
881 |
+
*
|
882 |
+
* @return string
|
883 |
+
*/
|
884 |
+
private function generateStyleStringFromDeclarationsArrays(array $oldStyles, array $newStyles)
|
885 |
+
{
|
886 |
+
$combinedStyles = array_merge($oldStyles, $newStyles);
|
887 |
+
$cacheKey = serialize($combinedStyles);
|
888 |
+
if (isset($this->caches[self::CACHE_KEY_COMBINED_STYLES][$cacheKey])) {
|
889 |
+
return $this->caches[self::CACHE_KEY_COMBINED_STYLES][$cacheKey];
|
890 |
+
}
|
891 |
+
|
892 |
+
foreach ($oldStyles as $attributeName => $attributeValue) {
|
893 |
+
if (!isset($newStyles[$attributeName])) {
|
894 |
+
continue;
|
895 |
+
}
|
896 |
+
|
897 |
+
$newAttributeValue = $newStyles[$attributeName];
|
898 |
+
if ($this->attributeValueIsImportant($attributeValue)
|
899 |
+
&& !$this->attributeValueIsImportant($newAttributeValue)
|
900 |
+
) {
|
901 |
+
$combinedStyles[$attributeName] = $attributeValue;
|
902 |
+
}
|
903 |
+
}
|
904 |
+
|
905 |
+
$style = '';
|
906 |
+
foreach ($combinedStyles as $attributeName => $attributeValue) {
|
907 |
+
$style .= strtolower(trim($attributeName)) . ': ' . trim($attributeValue) . '; ';
|
908 |
+
}
|
909 |
+
$trimmedStyle = rtrim($style);
|
910 |
+
|
911 |
+
$this->caches[self::CACHE_KEY_COMBINED_STYLES][$cacheKey] = $trimmedStyle;
|
912 |
+
|
913 |
+
return $trimmedStyle;
|
914 |
+
}
|
915 |
+
|
916 |
+
/**
|
917 |
+
* Checks whether $attributeValue is marked as !important.
|
918 |
+
*
|
919 |
+
* @param string $attributeValue
|
920 |
+
*
|
921 |
+
* @return bool
|
922 |
+
*/
|
923 |
+
private function attributeValueIsImportant($attributeValue)
|
924 |
+
{
|
925 |
+
return strtolower(substr(trim($attributeValue), -10)) === '!important';
|
926 |
+
}
|
927 |
+
|
928 |
+
/**
|
929 |
+
* Applies $css to $xmlDocument, limited to the media queries that actually apply to the document.
|
930 |
+
*
|
931 |
+
* @param \DOMDocument $xmlDocument the document to match against
|
932 |
+
* @param \DOMXPath $xPath
|
933 |
+
* @param string $css a string of CSS
|
934 |
+
*
|
935 |
+
* @return void
|
936 |
+
*/
|
937 |
+
private function copyCssWithMediaToStyleNode(DOMDocument $xmlDocument, DOMXPath $xPath, $css)
|
938 |
+
{
|
939 |
+
if ($css === '') {
|
940 |
+
return;
|
941 |
+
}
|
942 |
+
|
943 |
+
$mediaQueriesRelevantForDocument = array();
|
944 |
+
|
945 |
+
foreach ($this->extractMediaQueriesFromCss($css) as $mediaQuery) {
|
946 |
+
foreach ($this->parseCssRules($mediaQuery['css']) as $selector) {
|
947 |
+
if ($this->existsMatchForCssSelector($xPath, $selector['selector'])) {
|
948 |
+
$mediaQueriesRelevantForDocument[] = $mediaQuery['query'];
|
949 |
+
break;
|
950 |
+
}
|
951 |
+
}
|
952 |
+
}
|
953 |
+
|
954 |
+
$this->addStyleElementToDocument($xmlDocument, implode($mediaQueriesRelevantForDocument));
|
955 |
+
}
|
956 |
+
|
957 |
+
/**
|
958 |
+
* Extracts the media queries from $css while skipping empty media queries.
|
959 |
+
*
|
960 |
+
* @param string $css
|
961 |
+
*
|
962 |
+
* @return string[][] numeric array with string sub-arrays with the keys "css" and "query"
|
963 |
+
*/
|
964 |
+
private function extractMediaQueriesFromCss($css)
|
965 |
+
{
|
966 |
+
preg_match_all('/@media\\b[^{]*({((?:[^{}]+|(?1))*)})/', $css, $rawMediaQueries, PREG_SET_ORDER);
|
967 |
+
$parsedQueries = array();
|
968 |
+
|
969 |
+
foreach ($rawMediaQueries as $mediaQuery) {
|
970 |
+
if ($mediaQuery[2] !== '') {
|
971 |
+
$parsedQueries[] = array(
|
972 |
+
'css' => $mediaQuery[2],
|
973 |
+
'query' => $mediaQuery[0],
|
974 |
+
);
|
975 |
+
}
|
976 |
+
}
|
977 |
+
|
978 |
+
return $parsedQueries;
|
979 |
+
}
|
980 |
+
|
981 |
+
/**
|
982 |
+
* Checks whether there is at least one matching element for $cssSelector.
|
983 |
+
*
|
984 |
+
* @param \DOMXPath $xPath
|
985 |
+
* @param string $cssSelector
|
986 |
+
*
|
987 |
+
* @return bool
|
988 |
+
*/
|
989 |
+
private function existsMatchForCssSelector(DOMXPath $xPath, $cssSelector)
|
990 |
+
{
|
991 |
+
$nodesMatchingSelector = $xPath->query($this->translateCssToXpath($cssSelector));
|
992 |
+
|
993 |
+
return $nodesMatchingSelector !== false && $nodesMatchingSelector->length !== 0;
|
994 |
+
}
|
995 |
+
|
996 |
+
/**
|
997 |
+
* Returns CSS content.
|
998 |
+
*
|
999 |
+
* @param \DOMXPath $xPath
|
1000 |
+
*
|
1001 |
+
* @return string
|
1002 |
+
*/
|
1003 |
+
private function getCssFromAllStyleNodes(DOMXPath $xPath)
|
1004 |
+
{
|
1005 |
+
$styleNodes = $xPath->query('//style');
|
1006 |
+
|
1007 |
+
if ($styleNodes === false) {
|
1008 |
+
return '';
|
1009 |
+
}
|
1010 |
+
|
1011 |
+
$css = '';
|
1012 |
+
/** @var \DOMNode $styleNode */
|
1013 |
+
foreach ($styleNodes as $styleNode) {
|
1014 |
+
$css .= "\n\n" . $styleNode->nodeValue;
|
1015 |
+
$styleNode->parentNode->removeChild($styleNode);
|
1016 |
+
}
|
1017 |
+
|
1018 |
+
return $css;
|
1019 |
+
}
|
1020 |
+
|
1021 |
+
/**
|
1022 |
+
* Adds a style element with $css to $document.
|
1023 |
+
*
|
1024 |
+
* This method is protected to allow overriding.
|
1025 |
+
*
|
1026 |
+
* @see https://github.com/jjriv/emogrifier/issues/103
|
1027 |
+
*
|
1028 |
+
* @param \DOMDocument $document
|
1029 |
+
* @param string $css
|
1030 |
+
*
|
1031 |
+
* @return void
|
1032 |
+
*/
|
1033 |
+
protected function addStyleElementToDocument(DOMDocument $document, $css)
|
1034 |
+
{
|
1035 |
+
$styleElement = $document->createElement('style', $css);
|
1036 |
+
$styleAttribute = $document->createAttribute('type');
|
1037 |
+
$styleAttribute->value = 'text/css';
|
1038 |
+
$styleElement->appendChild($styleAttribute);
|
1039 |
+
|
1040 |
+
$head = $this->getOrCreateHeadElement($document);
|
1041 |
+
$head->appendChild($styleElement);
|
1042 |
+
}
|
1043 |
+
|
1044 |
+
/**
|
1045 |
+
* Returns the existing or creates a new head element in $document.
|
1046 |
+
*
|
1047 |
+
* @param \DOMDocument $document
|
1048 |
+
*
|
1049 |
+
* @return \DOMNode the head element
|
1050 |
+
*/
|
1051 |
+
private function getOrCreateHeadElement(DOMDocument $document)
|
1052 |
+
{
|
1053 |
+
$head = $document->getElementsByTagName('head')->item(0);
|
1054 |
+
|
1055 |
+
if ($head === null) {
|
1056 |
+
$head = $document->createElement('head');
|
1057 |
+
$html = $document->getElementsByTagName('html')->item(0);
|
1058 |
+
$html->insertBefore($head, $document->getElementsByTagName('body')->item(0));
|
1059 |
+
}
|
1060 |
+
|
1061 |
+
return $head;
|
1062 |
+
}
|
1063 |
+
|
1064 |
+
/**
|
1065 |
+
* Splits input CSS code to an array where:
|
1066 |
+
*
|
1067 |
+
* - key "css" will be contains clean CSS code
|
1068 |
+
* - key "media" will be contains all valuable media queries
|
1069 |
+
*
|
1070 |
+
* Example:
|
1071 |
+
*
|
1072 |
+
* The CSS code
|
1073 |
+
*
|
1074 |
+
* "@import "file.css"; h1 { color:red; } @media { h1 {}} @media tv { h1 {}}"
|
1075 |
+
*
|
1076 |
+
* will be parsed into the following array:
|
1077 |
+
*
|
1078 |
+
* "css" => "h1 { color:red; }"
|
1079 |
+
* "media" => "@media { h1 {}}"
|
1080 |
+
*
|
1081 |
+
* @param string $css
|
1082 |
+
*
|
1083 |
+
* @return string[]
|
1084 |
+
*/
|
1085 |
+
private function splitCssAndMediaQuery($css)
|
1086 |
+
{
|
1087 |
+
$cssWithoutComments = preg_replace('/\\/\\*.*\\*\\//sU', '', $css);
|
1088 |
+
|
1089 |
+
$mediaTypesExpression = '';
|
1090 |
+
if (!empty($this->allowedMediaTypes)) {
|
1091 |
+
$mediaTypesExpression = '|' . implode('|', array_keys($this->allowedMediaTypes));
|
1092 |
+
}
|
1093 |
+
|
1094 |
+
$cssForAllowedMediaTypes = preg_replace_callback(
|
1095 |
+
'#@media\\s+(?:only\\s)?(?:[\\s{\\(]' . $mediaTypesExpression . ')\\s?[^{]+{.*}\\s*}\\s*#misU',
|
1096 |
+
array( $this, '_media_concat' ),
|
1097 |
+
$cssWithoutComments
|
1098 |
+
);
|
1099 |
+
|
1100 |
+
// filter the CSS
|
1101 |
+
$search = array(
|
1102 |
+
'import directives' => '/^\\s*@import\\s[^;]+;/misU',
|
1103 |
+
'remaining media enclosures' => '/^\\s*@media\\s[^{]+{(.*)}\\s*}\\s/misU',
|
1104 |
+
);
|
1105 |
+
|
1106 |
+
$cleanedCss = preg_replace($search, '', $cssForAllowedMediaTypes);
|
1107 |
+
|
1108 |
+
return array('css' => $cleanedCss, 'media' => self::$_media);
|
1109 |
+
}
|
1110 |
+
|
1111 |
+
private function _media_concat( $matches ) {
|
1112 |
+
self::$_media .= $matches[0];
|
1113 |
+
}
|
1114 |
+
|
1115 |
+
/**
|
1116 |
+
* Creates a DOMDocument instance with the current HTML.
|
1117 |
+
*
|
1118 |
+
* @return \DOMDocument
|
1119 |
+
*/
|
1120 |
+
private function createXmlDocument()
|
1121 |
+
{
|
1122 |
+
$xmlDocument = new DOMDocument;
|
1123 |
+
$xmlDocument->encoding = 'UTF-8';
|
1124 |
+
$xmlDocument->strictErrorChecking = false;
|
1125 |
+
$xmlDocument->formatOutput = true;
|
1126 |
+
$libXmlState = libxml_use_internal_errors(true);
|
1127 |
+
$xmlDocument->loadHTML($this->getUnifiedHtml());
|
1128 |
+
libxml_clear_errors();
|
1129 |
+
libxml_use_internal_errors($libXmlState);
|
1130 |
+
$xmlDocument->normalizeDocument();
|
1131 |
+
|
1132 |
+
return $xmlDocument;
|
1133 |
+
}
|
1134 |
+
|
1135 |
+
/**
|
1136 |
+
* Returns the HTML with the unprocessable HTML tags removed and
|
1137 |
+
* with added document type and Content-Type meta tag if needed.
|
1138 |
+
*
|
1139 |
+
* @return string the unified HTML
|
1140 |
+
*
|
1141 |
+
* @throws \BadMethodCallException
|
1142 |
+
*/
|
1143 |
+
private function getUnifiedHtml()
|
1144 |
+
{
|
1145 |
+
$htmlWithoutUnprocessableTags = $this->removeUnprocessableTags($this->html);
|
1146 |
+
$htmlWithDocumentType = $this->ensureDocumentType($htmlWithoutUnprocessableTags);
|
1147 |
+
|
1148 |
+
return $this->addContentTypeMetaTag($htmlWithDocumentType);
|
1149 |
+
}
|
1150 |
+
|
1151 |
+
/**
|
1152 |
+
* Removes the unprocessable tags from $html (if this feature is enabled).
|
1153 |
+
*
|
1154 |
+
* @param string $html
|
1155 |
+
*
|
1156 |
+
* @return string the reworked HTML with the unprocessable tags removed
|
1157 |
+
*/
|
1158 |
+
private function removeUnprocessableTags($html)
|
1159 |
+
{
|
1160 |
+
if (empty($this->unprocessableHtmlTags)) {
|
1161 |
+
return $html;
|
1162 |
+
}
|
1163 |
+
|
1164 |
+
$unprocessableHtmlTags = implode('|', $this->unprocessableHtmlTags);
|
1165 |
+
|
1166 |
+
return preg_replace(
|
1167 |
+
'/<\\/?(' . $unprocessableHtmlTags . ')[^>]*>/i',
|
1168 |
+
'',
|
1169 |
+
$html
|
1170 |
+
);
|
1171 |
+
}
|
1172 |
+
|
1173 |
+
/**
|
1174 |
+
* Makes sure that the passed HTML has a document type.
|
1175 |
+
*
|
1176 |
+
* @param string $html
|
1177 |
+
*
|
1178 |
+
* @return string HTML with document type
|
1179 |
+
*/
|
1180 |
+
private function ensureDocumentType($html)
|
1181 |
+
{
|
1182 |
+
$hasDocumentType = stripos($html, '<!DOCTYPE') !== false;
|
1183 |
+
if ($hasDocumentType) {
|
1184 |
+
return $html;
|
1185 |
+
}
|
1186 |
+
|
1187 |
+
return self::DEFAULT_DOCUMENT_TYPE . $html;
|
1188 |
+
}
|
1189 |
+
|
1190 |
+
/**
|
1191 |
+
* Adds a Content-Type meta tag for the charset.
|
1192 |
+
*
|
1193 |
+
* @param string $html
|
1194 |
+
*
|
1195 |
+
* @return string the HTML with the meta tag added
|
1196 |
+
*/
|
1197 |
+
private function addContentTypeMetaTag($html)
|
1198 |
+
{
|
1199 |
+
$hasContentTypeMetaTag = stristr($html, 'Content-Type') !== false;
|
1200 |
+
if ($hasContentTypeMetaTag) {
|
1201 |
+
return $html;
|
1202 |
+
}
|
1203 |
+
|
1204 |
+
// We are trying to insert the meta tag to the right spot in the DOM.
|
1205 |
+
// If we just prepended it to the HTML, we would lose attributes set to the HTML tag.
|
1206 |
+
$hasHeadTag = stripos($html, '<head') !== false;
|
1207 |
+
$hasHtmlTag = stripos($html, '<html') !== false;
|
1208 |
+
|
1209 |
+
if ($hasHeadTag) {
|
1210 |
+
$reworkedHtml = preg_replace('/<head(.*?)>/i', '<head$1>' . self::CONTENT_TYPE_META_TAG, $html);
|
1211 |
+
} elseif ($hasHtmlTag) {
|
1212 |
+
$reworkedHtml = preg_replace(
|
1213 |
+
'/<html(.*?)>/i',
|
1214 |
+
'<html$1><head>' . self::CONTENT_TYPE_META_TAG . '</head>',
|
1215 |
+
$html
|
1216 |
+
);
|
1217 |
+
} else {
|
1218 |
+
$reworkedHtml = self::CONTENT_TYPE_META_TAG . $html;
|
1219 |
+
}
|
1220 |
+
|
1221 |
+
return $reworkedHtml;
|
1222 |
+
}
|
1223 |
+
|
1224 |
+
/**
|
1225 |
+
* @param string[] $a
|
1226 |
+
* @param string[] $b
|
1227 |
+
*
|
1228 |
+
* @return int
|
1229 |
+
*/
|
1230 |
+
private function sortBySelectorPrecedence(array $a, array $b)
|
1231 |
+
{
|
1232 |
+
$precedenceA = $this->getCssSelectorPrecedence($a['selector']);
|
1233 |
+
$precedenceB = $this->getCssSelectorPrecedence($b['selector']);
|
1234 |
+
|
1235 |
+
// We want these sorted in ascending order so selectors with lesser precedence get processed first and
|
1236 |
+
// selectors with greater precedence get sorted last.
|
1237 |
+
$precedenceForEquals = ($a['line'] < $b['line'] ? -1 : 1);
|
1238 |
+
$precedenceForNotEquals = ($precedenceA < $precedenceB ? -1 : 1);
|
1239 |
+
return ($precedenceA === $precedenceB) ? $precedenceForEquals : $precedenceForNotEquals;
|
1240 |
+
}
|
1241 |
+
|
1242 |
+
/**
|
1243 |
+
* @param string $selector
|
1244 |
+
*
|
1245 |
+
* @return int
|
1246 |
+
*/
|
1247 |
+
private function getCssSelectorPrecedence($selector)
|
1248 |
+
{
|
1249 |
+
$selectorKey = md5($selector);
|
1250 |
+
if (!isset($this->caches[self::CACHE_KEY_SELECTOR][$selectorKey])) {
|
1251 |
+
$precedence = 0;
|
1252 |
+
$value = 100;
|
1253 |
+
// ids: worth 100, classes: worth 10, elements: worth 1
|
1254 |
+
$search = array('\\#','\\.','');
|
1255 |
+
|
1256 |
+
foreach ($search as $s) {
|
1257 |
+
if (trim($selector) === '') {
|
1258 |
+
break;
|
1259 |
+
}
|
1260 |
+
$number = 0;
|
1261 |
+
$selector = preg_replace('/' . $s . '\\w+/', '', $selector, -1, $number);
|
1262 |
+
$precedence += ($value * $number);
|
1263 |
+
$value /= 10;
|
1264 |
+
}
|
1265 |
+
$this->caches[self::CACHE_KEY_SELECTOR][$selectorKey] = $precedence;
|
1266 |
+
}
|
1267 |
+
|
1268 |
+
return $this->caches[self::CACHE_KEY_SELECTOR][$selectorKey];
|
1269 |
+
}
|
1270 |
+
|
1271 |
+
private function translateCssToXpath_callback( $matches ) {
|
1272 |
+
return strtolower($matches[0]);
|
1273 |
+
}
|
1274 |
+
|
1275 |
+
/**
|
1276 |
+
* Maps a CSS selector to an XPath query string.
|
1277 |
+
*
|
1278 |
+
* @see http://plasmasturm.org/log/444/
|
1279 |
+
*
|
1280 |
+
* @param string $cssSelector a CSS selector
|
1281 |
+
*
|
1282 |
+
* @return string the corresponding XPath selector
|
1283 |
+
*/
|
1284 |
+
private function translateCssToXpath($cssSelector)
|
1285 |
+
{
|
1286 |
+
$paddedSelector = ' ' . $cssSelector . ' ';
|
1287 |
+
$lowercasePaddedSelector = preg_replace_callback(
|
1288 |
+
'/\\s+\\w+\\s+/',
|
1289 |
+
array( $this, 'translateCssToXpath_callback' ),
|
1290 |
+
$paddedSelector
|
1291 |
+
);
|
1292 |
+
|
1293 |
+
$trimmedLowercaseSelector = trim($lowercasePaddedSelector);
|
1294 |
+
$xPathKey = md5($trimmedLowercaseSelector);
|
1295 |
+
if (!isset($this->caches[self::CACHE_KEY_XPATH][$xPathKey])) {
|
1296 |
+
$roughXpath = '//' . preg_replace(
|
1297 |
+
array_keys($this->xPathRules),
|
1298 |
+
$this->xPathRules,
|
1299 |
+
$trimmedLowercaseSelector
|
1300 |
+
);
|
1301 |
+
$xPathWithIdAttributeMatchers = preg_replace_callback(
|
1302 |
+
self::ID_ATTRIBUTE_MATCHER,
|
1303 |
+
array($this, 'matchIdAttributes'),
|
1304 |
+
$roughXpath
|
1305 |
+
);
|
1306 |
+
$xPathWithIdAttributeAndClassMatchers = preg_replace_callback(
|
1307 |
+
self::CLASS_ATTRIBUTE_MATCHER,
|
1308 |
+
array($this, 'matchClassAttributes'),
|
1309 |
+
$xPathWithIdAttributeMatchers
|
1310 |
+
);
|
1311 |
+
|
1312 |
+
// Advanced selectors are going to require a bit more advanced emogrification.
|
1313 |
+
// When we required PHP 5.3, we could do this with closures.
|
1314 |
+
$xPathWithIdAttributeAndClassMatchers = preg_replace_callback(
|
1315 |
+
'/([^\\/]+):nth-child\\(\\s*(odd|even|[+\\-]?\\d|[+\\-]?\\d?n(\\s*[+\\-]\\s*\\d)?)\\s*\\)/i',
|
1316 |
+
array($this, 'translateNthChild'),
|
1317 |
+
$xPathWithIdAttributeAndClassMatchers
|
1318 |
+
);
|
1319 |
+
$finalXpath = preg_replace_callback(
|
1320 |
+
'/([^\\/]+):nth-of-type\\(\s*(odd|even|[+\\-]?\\d|[+\\-]?\\d?n(\\s*[+\\-]\\s*\\d)?)\\s*\\)/i',
|
1321 |
+
array($this, 'translateNthOfType'),
|
1322 |
+
$xPathWithIdAttributeAndClassMatchers
|
1323 |
+
);
|
1324 |
+
|
1325 |
+
$this->caches[self::CACHE_KEY_SELECTOR][$xPathKey] = $finalXpath;
|
1326 |
+
}
|
1327 |
+
return $this->caches[self::CACHE_KEY_SELECTOR][$xPathKey];
|
1328 |
+
}
|
1329 |
+
|
1330 |
+
/**
|
1331 |
+
* @param string[] $match
|
1332 |
+
*
|
1333 |
+
* @return string
|
1334 |
+
*/
|
1335 |
+
private function matchIdAttributes(array $match)
|
1336 |
+
{
|
1337 |
+
return ($match[1] !== '' ? $match[1] : '*') . '[@id="' . $match[2] . '"]';
|
1338 |
+
}
|
1339 |
+
|
1340 |
+
/**
|
1341 |
+
* @param string[] $match
|
1342 |
+
*
|
1343 |
+
* @return string
|
1344 |
+
*/
|
1345 |
+
private function matchClassAttributes(array $match)
|
1346 |
+
{
|
1347 |
+
return ($match[1] !== '' ? $match[1] : '*') . '[contains(concat(" ",@class," "),concat(" ","' .
|
1348 |
+
implode(
|
1349 |
+
'"," "))][contains(concat(" ",@class," "),concat(" ","',
|
1350 |
+
explode('.', substr($match[2], 1))
|
1351 |
+
) . '"," "))]';
|
1352 |
+
}
|
1353 |
+
|
1354 |
+
/**
|
1355 |
+
* @param string[] $match
|
1356 |
+
*
|
1357 |
+
* @return string
|
1358 |
+
*/
|
1359 |
+
private function translateNthChild(array $match)
|
1360 |
+
{
|
1361 |
+
$parseResult = $this->parseNth($match);
|
1362 |
+
|
1363 |
+
if (isset($parseResult[self::MULTIPLIER])) {
|
1364 |
+
if ($parseResult[self::MULTIPLIER] < 0) {
|
1365 |
+
$parseResult[self::MULTIPLIER] = abs($parseResult[self::MULTIPLIER]);
|
1366 |
+
$xPathExpression = sprintf(
|
1367 |
+
'*[(last() - position()) mod %u = %u]/self::%s',
|
1368 |
+
$parseResult[self::MULTIPLIER],
|
1369 |
+
$parseResult[self::INDEX],
|
1370 |
+
$match[1]
|
1371 |
+
);
|
1372 |
+
} else {
|
1373 |
+
$xPathExpression = sprintf(
|
1374 |
+
'*[position() mod %u = %u]/self::%s',
|
1375 |
+
$parseResult[self::MULTIPLIER],
|
1376 |
+
$parseResult[self::INDEX],
|
1377 |
+
$match[1]
|
1378 |
+
);
|
1379 |
+
}
|
1380 |
+
} else {
|
1381 |
+
$xPathExpression = sprintf('*[%u]/self::%s', $parseResult[self::INDEX], $match[1]);
|
1382 |
+
}
|
1383 |
+
|
1384 |
+
return $xPathExpression;
|
1385 |
+
}
|
1386 |
+
|
1387 |
+
/**
|
1388 |
+
* @param string[] $match
|
1389 |
+
*
|
1390 |
+
* @return string
|
1391 |
+
*/
|
1392 |
+
private function translateNthOfType(array $match)
|
1393 |
+
{
|
1394 |
+
$parseResult = $this->parseNth($match);
|
1395 |
+
|
1396 |
+
if (isset($parseResult[self::MULTIPLIER])) {
|
1397 |
+
if ($parseResult[self::MULTIPLIER] < 0) {
|
1398 |
+
$parseResult[self::MULTIPLIER] = abs($parseResult[self::MULTIPLIER]);
|
1399 |
+
$xPathExpression = sprintf(
|
1400 |
+
'%s[(last() - position()) mod %u = %u]',
|
1401 |
+
$match[1],
|
1402 |
+
$parseResult[self::MULTIPLIER],
|
1403 |
+
$parseResult[self::INDEX]
|
1404 |
+
);
|
1405 |
+
} else {
|
1406 |
+
$xPathExpression = sprintf(
|
1407 |
+
'%s[position() mod %u = %u]',
|
1408 |
+
$match[1],
|
1409 |
+
$parseResult[self::MULTIPLIER],
|
1410 |
+
$parseResult[self::INDEX]
|
1411 |
+
);
|
1412 |
+
}
|
1413 |
+
} else {
|
1414 |
+
$xPathExpression = sprintf('%s[%u]', $match[1], $parseResult[self::INDEX]);
|
1415 |
+
}
|
1416 |
+
|
1417 |
+
return $xPathExpression;
|
1418 |
+
}
|
1419 |
+
|
1420 |
+
/**
|
1421 |
+
* @param string[] $match
|
1422 |
+
*
|
1423 |
+
* @return int[]
|
1424 |
+
*/
|
1425 |
+
private function parseNth(array $match)
|
1426 |
+
{
|
1427 |
+
if (in_array(strtolower($match[2]), array('even', 'odd'), true)) {
|
1428 |
+
// we have "even" or "odd"
|
1429 |
+
$index = strtolower($match[2]) === 'even' ? 0 : 1;
|
1430 |
+
return array(self::MULTIPLIER => 2, self::INDEX => $index);
|
1431 |
+
}
|
1432 |
+
if (stripos($match[2], 'n') === false) {
|
1433 |
+
// if there is a multiplier
|
1434 |
+
$index = (int) str_replace(' ', '', $match[2]);
|
1435 |
+
return array(self::INDEX => $index);
|
1436 |
+
}
|
1437 |
+
|
1438 |
+
if (isset($match[3])) {
|
1439 |
+
$multipleTerm = str_replace($match[3], '', $match[2]);
|
1440 |
+
$index = (int) str_replace(' ', '', $match[3]);
|
1441 |
+
} else {
|
1442 |
+
$multipleTerm = $match[2];
|
1443 |
+
$index = 0;
|
1444 |
+
}
|
1445 |
+
|
1446 |
+
$multiplier = str_ireplace('n', '', $multipleTerm);
|
1447 |
+
|
1448 |
+
if ($multiplier === '') {
|
1449 |
+
$multiplier = 1;
|
1450 |
+
} elseif ($multiplier === '0') {
|
1451 |
+
return array(self::INDEX => $index);
|
1452 |
+
} else {
|
1453 |
+
$multiplier = (int) $multiplier;
|
1454 |
+
}
|
1455 |
+
|
1456 |
+
while ($index < 0) {
|
1457 |
+
$index += abs($multiplier);
|
1458 |
+
}
|
1459 |
+
|
1460 |
+
return array(self::MULTIPLIER => $multiplier, self::INDEX => $index);
|
1461 |
+
}
|
1462 |
+
|
1463 |
+
/**
|
1464 |
+
* Parses a CSS declaration block into property name/value pairs.
|
1465 |
+
*
|
1466 |
+
* Example:
|
1467 |
+
*
|
1468 |
+
* The declaration block
|
1469 |
+
*
|
1470 |
+
* "color: #000; font-weight: bold;"
|
1471 |
+
*
|
1472 |
+
* will be parsed into the following array:
|
1473 |
+
*
|
1474 |
+
* "color" => "#000"
|
1475 |
+
* "font-weight" => "bold"
|
1476 |
+
*
|
1477 |
+
* @param string $cssDeclarationsBlock the CSS declarations block without the curly braces, may be empty
|
1478 |
+
*
|
1479 |
+
* @return string[]
|
1480 |
+
* the CSS declarations with the property names as array keys and the property values as array values
|
1481 |
+
*/
|
1482 |
+
private function parseCssDeclarationsBlock($cssDeclarationsBlock)
|
1483 |
+
{
|
1484 |
+
if (isset($this->caches[self::CACHE_KEY_CSS_DECLARATIONS_BLOCK][$cssDeclarationsBlock])) {
|
1485 |
+
return $this->caches[self::CACHE_KEY_CSS_DECLARATIONS_BLOCK][$cssDeclarationsBlock];
|
1486 |
+
}
|
1487 |
+
|
1488 |
+
$properties = array();
|
1489 |
+
$declarations = preg_split('/;(?!base64|charset)/', $cssDeclarationsBlock);
|
1490 |
+
|
1491 |
+
foreach ($declarations as $declaration) {
|
1492 |
+
$matches = array();
|
1493 |
+
if (!preg_match('/^([A-Za-z\\-]+)\\s*:\\s*(.+)$/', trim($declaration), $matches)) {
|
1494 |
+
continue;
|
1495 |
+
}
|
1496 |
+
|
1497 |
+
$propertyName = strtolower($matches[1]);
|
1498 |
+
$propertyValue = $matches[2];
|
1499 |
+
$properties[$propertyName] = $propertyValue;
|
1500 |
+
}
|
1501 |
+
$this->caches[self::CACHE_KEY_CSS_DECLARATIONS_BLOCK][$cssDeclarationsBlock] = $properties;
|
1502 |
+
|
1503 |
+
return $properties;
|
1504 |
+
}
|
1505 |
+
|
1506 |
+
/**
|
1507 |
+
* Find the nodes that are not to be emogrified.
|
1508 |
+
*
|
1509 |
+
* @param \DOMXPath $xPath
|
1510 |
+
*
|
1511 |
+
* @return \DOMElement[]
|
1512 |
+
*/
|
1513 |
+
private function getNodesToExclude(DOMXPath $xPath)
|
1514 |
+
{
|
1515 |
+
$excludedNodes = array();
|
1516 |
+
foreach (array_keys($this->excludedSelectors) as $selectorToExclude) {
|
1517 |
+
foreach ($xPath->query($this->translateCssToXpath($selectorToExclude)) as $node) {
|
1518 |
+
$excludedNodes[] = $node;
|
1519 |
+
}
|
1520 |
+
}
|
1521 |
+
|
1522 |
+
return $excludedNodes;
|
1523 |
+
}
|
1524 |
+
|
1525 |
+
/**
|
1526 |
+
* Handles invalid xPath expression warnings, generated by process() method,
|
1527 |
+
* during querying \DOMDocument and trigger \InvalidArgumentException
|
1528 |
+
* with invalid selector.
|
1529 |
+
*
|
1530 |
+
* @param int $type
|
1531 |
+
* @param string $message
|
1532 |
+
* @param string $file
|
1533 |
+
* @param int $line
|
1534 |
+
* @param array $context
|
1535 |
+
*
|
1536 |
+
* @return bool always false
|
1537 |
+
*
|
1538 |
+
* @throws \InvalidArgumentException
|
1539 |
+
*/
|
1540 |
+
public function handleXpathError($type, $message, $file, $line, array $context)
|
1541 |
+
{
|
1542 |
+
if ($type === E_WARNING && isset($context['cssRule']['selector'])) {
|
1543 |
+
throw new InvalidArgumentException(
|
1544 |
+
sprintf(
|
1545 |
+
'%s in selector >> %s << in %s on line %s',
|
1546 |
+
$message,
|
1547 |
+
$context['cssRule']['selector'],
|
1548 |
+
$file,
|
1549 |
+
$line
|
1550 |
+
)
|
1551 |
+
);
|
1552 |
+
}
|
1553 |
+
|
1554 |
+
// the normal error handling continues when handler return false
|
1555 |
+
return false;
|
1556 |
+
}
|
1557 |
+
}
|
readme.txt
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
=== WP Job Manager ===
|
2 |
Contributors: mikejolley, automattic, adamkheckler, alexsanford1, annezazu, cena, chaselivingston, csonnek, davor.altman, donnapep, donncha, drawmyface, erania-pinnera, jacobshere, jakeom, jeherve, jenhooks, jgs, jonryan, kraftbj, lamdayap, lschuyler, macmanx, nancythanki, orangesareorange, rachelsquirrel, ryancowles, richardmtl, scarstocea
|
3 |
Tags: job manager, job listing, job board, job management, job lists, job list, job, jobs, company, hiring, employment, employer, employees, candidate, freelance, internship, job listings, positions, board, application, hiring, listing, manager, recruiting, recruitment, talent
|
4 |
-
Requires at least: 4.
|
5 |
Tested up to: 4.9
|
6 |
-
Stable tag: 1.
|
7 |
License: GPLv3
|
8 |
License URI: http://www.gnu.org/licenses/gpl-3.0.html
|
9 |
|
@@ -141,6 +141,25 @@ You can view (and contribute) translations via the [translate.wordpress.org](htt
|
|
141 |
|
142 |
== Changelog ==
|
143 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
144 |
= 1.30.2 =
|
145 |
* Enhancement: Show notice when user is using an older version of WordPress.
|
146 |
* Enhancement: Hide unnecessary view mode in WP Admin's Job Listings page. (@RajeebTheGreat)
|
1 |
=== WP Job Manager ===
|
2 |
Contributors: mikejolley, automattic, adamkheckler, alexsanford1, annezazu, cena, chaselivingston, csonnek, davor.altman, donnapep, donncha, drawmyface, erania-pinnera, jacobshere, jakeom, jeherve, jenhooks, jgs, jonryan, kraftbj, lamdayap, lschuyler, macmanx, nancythanki, orangesareorange, rachelsquirrel, ryancowles, richardmtl, scarstocea
|
3 |
Tags: job manager, job listing, job board, job management, job lists, job list, job, jobs, company, hiring, employment, employer, employees, candidate, freelance, internship, job listings, positions, board, application, hiring, listing, manager, recruiting, recruitment, talent
|
4 |
+
Requires at least: 4.7.0
|
5 |
Tested up to: 4.9
|
6 |
+
Stable tag: 1.31.0
|
7 |
License: GPLv3
|
8 |
License URI: http://www.gnu.org/licenses/gpl-3.0.html
|
9 |
|
141 |
|
142 |
== Changelog ==
|
143 |
|
144 |
+
= 1.31.0 =
|
145 |
+
* Change: Minimum WordPress version is now 4.7.0.
|
146 |
+
* Enhancement: Add email notifications with initial support for new jobs, updated jobs, and expiring listings.
|
147 |
+
* Enhancement: For GDPR, scrub WPJM data from database on uninstall if option is enabled.
|
148 |
+
* Enhancement: Filter by Filled and Featured status in WP admin.
|
149 |
+
* Enhancement: Simplify the display of application URLs.
|
150 |
+
* Enhancement: When using WPML, prevent changes to page options when on a non-default language. (@vukvukovich)
|
151 |
+
* Enhancement: Include company logo in structured data. (@RajeebTheGreat)
|
152 |
+
* Enhancement: Use more efficient jQuery selectors in scripts. (@RajeebTheGreat)
|
153 |
+
* Enhancement: Use proper `<h2>` tag in `content-summary-job_listing.php` template for the job title. (@abdullah1908)
|
154 |
+
* Enhancement: Hide empty categories on `[job]` filter.
|
155 |
+
* Fix: Update calls to `get_terms()` to use the new format.
|
156 |
+
* Fix: Maintain the current tab when saving settings in WP Admin.
|
157 |
+
* Fix: Enqueue the date picker CSS when used on the front-end.
|
158 |
+
* Fix: Remove errors when widget instance was created without setting defaults.
|
159 |
+
* REST API Pre-release: Add support for job category taxonomy endpoints.
|
160 |
+
* Dev: Add `$job_id` parameter to `job_manager_job_dashboard_do_action_{$action}` action hook. (@jonasvogel)
|
161 |
+
* Dev: Add support for hidden WPJM settings in WP Admin.
|
162 |
+
|
163 |
= 1.30.2 =
|
164 |
* Enhancement: Show notice when user is using an older version of WordPress.
|
165 |
* Enhancement: Hide unnecessary view mode in WP Admin's Job Listings page. (@RajeebTheGreat)
|
templates/account-signin.php
CHANGED
@@ -8,7 +8,7 @@
|
|
8 |
* @author Automattic
|
9 |
* @package WP Job Manager
|
10 |
* @category Template
|
11 |
-
* @version 1.
|
12 |
*/
|
13 |
|
14 |
if ( ! defined( 'ABSPATH' ) ) {
|
@@ -17,7 +17,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
17 |
?>
|
18 |
<?php if ( is_user_logged_in() ) : ?>
|
19 |
|
20 |
-
<fieldset>
|
21 |
<label><?php _e( 'Your account', 'wp-job-manager' ); ?></label>
|
22 |
<div class="field account-sign-in">
|
23 |
<?php
|
@@ -35,14 +35,14 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
35 |
$registration_fields = wpjm_get_registration_fields();
|
36 |
$use_standard_password_email = wpjm_use_standard_password_setup_email();
|
37 |
?>
|
38 |
-
<fieldset>
|
39 |
<label><?php _e( 'Have an account?', 'wp-job-manager' ); ?></label>
|
40 |
<div class="field account-sign-in">
|
41 |
<a class="button" href="<?php echo apply_filters( 'submit_job_form_login_url', wp_login_url( get_permalink() ) ); ?>"><?php _e( 'Sign in', 'wp-job-manager' ); ?></a>
|
42 |
|
43 |
<?php if ( $registration_enabled ) : ?>
|
44 |
|
45 |
-
<?php printf( __( 'If you don
|
46 |
<?php if ( $use_standard_password_email ) : ?>
|
47 |
<?php printf( __( 'Your account details will be confirmed via email.', 'wp-job-manager' ) ); ?>
|
48 |
<?php endif; ?>
|
8 |
* @author Automattic
|
9 |
* @package WP Job Manager
|
10 |
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
*/
|
13 |
|
14 |
if ( ! defined( 'ABSPATH' ) ) {
|
17 |
?>
|
18 |
<?php if ( is_user_logged_in() ) : ?>
|
19 |
|
20 |
+
<fieldset class="fieldset-logged_in">
|
21 |
<label><?php _e( 'Your account', 'wp-job-manager' ); ?></label>
|
22 |
<div class="field account-sign-in">
|
23 |
<?php
|
35 |
$registration_fields = wpjm_get_registration_fields();
|
36 |
$use_standard_password_email = wpjm_use_standard_password_setup_email();
|
37 |
?>
|
38 |
+
<fieldset class="fieldset-login_required">
|
39 |
<label><?php _e( 'Have an account?', 'wp-job-manager' ); ?></label>
|
40 |
<div class="field account-sign-in">
|
41 |
<a class="button" href="<?php echo apply_filters( 'submit_job_form_login_url', wp_login_url( get_permalink() ) ); ?>"><?php _e( 'Sign in', 'wp-job-manager' ); ?></a>
|
42 |
|
43 |
<?php if ( $registration_enabled ) : ?>
|
44 |
|
45 |
+
<?php printf( __( 'If you don\'t have an account you can %screate one below by entering your email address/username.', 'wp-job-manager' ), $account_required ? '' : __( 'optionally', 'wp-job-manager' ) . ' ' ); ?>
|
46 |
<?php if ( $use_standard_password_email ) : ?>
|
47 |
<?php printf( __( 'Your account details will be confirmed via email.', 'wp-job-manager' ) ); ?>
|
48 |
<?php endif; ?>
|
templates/content-summary-job_listing.php
CHANGED
@@ -8,7 +8,7 @@
|
|
8 |
* @author Automattic
|
9 |
* @package WP Job Manager
|
10 |
* @category Template
|
11 |
-
* @version 1.
|
12 |
*/
|
13 |
|
14 |
if ( ! defined( 'ABSPATH' ) ) {
|
@@ -34,7 +34,7 @@ global $job_manager;
|
|
34 |
|
35 |
<div class="job_summary_content">
|
36 |
|
37 |
-
<
|
38 |
|
39 |
<p class="meta"><?php the_job_location( false ); ?> — <?php the_job_publish_date(); ?></p>
|
40 |
|
8 |
* @author Automattic
|
9 |
* @package WP Job Manager
|
10 |
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
*/
|
13 |
|
14 |
if ( ! defined( 'ABSPATH' ) ) {
|
34 |
|
35 |
<div class="job_summary_content">
|
36 |
|
37 |
+
<h2 class="job_summary_title"><?php wpjm_the_job_title(); ?></h2>
|
38 |
|
39 |
<p class="meta"><?php the_job_location( false ); ?> — <?php the_job_publish_date(); ?></p>
|
40 |
|
templates/emails/admin-expiring-job.php
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Email content when notifying the administrator of an expiring job listing.
|
4 |
+
*
|
5 |
+
* This template can be overridden by copying it to yourtheme/job_manager/emails/employer-expiring-job.php.
|
6 |
+
*
|
7 |
+
* @see https://wpjobmanager.com/document/template-overrides/
|
8 |
+
* @author Automattic
|
9 |
+
* @package WP Job Manager
|
10 |
+
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
+
*/
|
13 |
+
|
14 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
+
exit; // Exit if accessed directly
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var WP_Post $job
|
20 |
+
*/
|
21 |
+
$job = $args['job'];
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var bool
|
25 |
+
*/
|
26 |
+
$expiring_today = $args['expiring_today'];
|
27 |
+
|
28 |
+
echo '<p>';
|
29 |
+
if ( $expiring_today ) {
|
30 |
+
printf( __( 'The following job listing is expiring today from <a href="%s">%s</a>.', 'wp-job-manager' ), home_url(), get_bloginfo( 'name' ) );
|
31 |
+
} else {
|
32 |
+
printf( __( 'The following job listing is expiring soon from <a href="%s">%s</a>.', 'wp-job-manager' ), home_url(), get_bloginfo( 'name' ) );
|
33 |
+
}
|
34 |
+
$edit_post_link = admin_url( sprintf( 'post.php?post=%d&action=edit', $job->ID ) );
|
35 |
+
printf( ' ' . __( 'Visit <a href="%s">WordPress admin</a> to manage the listing.', 'wp-job-manager' ), esc_url( $edit_post_link ) );
|
36 |
+
echo '</p>';
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Show details about the job listing.
|
40 |
+
*
|
41 |
+
* @param WP_Post $job The job listing to show details for.
|
42 |
+
* @param WP_Job_Manager_Email $email Email object for the notification.
|
43 |
+
* @param bool $sent_to_admin True if this is being sent to an administrator.
|
44 |
+
* @param bool $plain_text True if the email is being sent as plain text.
|
45 |
+
*/
|
46 |
+
do_action( 'job_manager_email_job_details', $job, $email, true, $plain_text );
|
templates/emails/admin-new-job.php
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Email content when notifying admin of a new job listing.
|
4 |
+
*
|
5 |
+
* This template can be overridden by copying it to yourtheme/job_manager/emails/admin-new-job.php.
|
6 |
+
*
|
7 |
+
* @see https://wpjobmanager.com/document/template-overrides/
|
8 |
+
* @author Automattic
|
9 |
+
* @package WP Job Manager
|
10 |
+
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
+
*/
|
13 |
+
|
14 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
+
exit; // Exit if accessed directly
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var WP_Post $job
|
20 |
+
*/
|
21 |
+
$job = $args['job'];
|
22 |
+
?>
|
23 |
+
<p><?php
|
24 |
+
printf( __( 'A new job listing has been submitted to <a href="%s">%s</a>.', 'wp-job-manager' ), home_url(), get_bloginfo( 'name' ) );
|
25 |
+
switch ( $job->post_status ) {
|
26 |
+
case 'publish':
|
27 |
+
printf( ' ' . __( 'It has been published and is now available to the public.', 'wp-job-manager' ) );
|
28 |
+
break;
|
29 |
+
case 'pending':
|
30 |
+
printf( ' ' . __( 'It is awaiting approval by an administrator in <a href="%s">WordPress admin</a>.', 'wp-job-manager' ), esc_url( admin_url( 'edit.php?post_type=job_listing' ) ) );
|
31 |
+
break;
|
32 |
+
}
|
33 |
+
?></p>
|
34 |
+
<?php
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Show details about the job listing.
|
38 |
+
*
|
39 |
+
* @param WP_Post $job The job listing to show details for.
|
40 |
+
* @param WP_Job_Manager_Email $email Email object for the notification.
|
41 |
+
* @param bool $sent_to_admin True if this is being sent to an administrator.
|
42 |
+
* @param bool $plain_text True if the email is being sent as plain text.
|
43 |
+
*/
|
44 |
+
do_action( 'job_manager_email_job_details', $job, $email, true, $plain_text );
|
templates/emails/admin-updated-job.php
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Email content when notifying admin of an updated job listing.
|
4 |
+
*
|
5 |
+
* This template can be overridden by copying it to yourtheme/job_manager/emails/admin-updated-job.php.
|
6 |
+
*
|
7 |
+
* @see https://wpjobmanager.com/document/template-overrides/
|
8 |
+
* @author Automattic
|
9 |
+
* @package WP Job Manager
|
10 |
+
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
+
*/
|
13 |
+
|
14 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
+
exit; // Exit if accessed directly
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var WP_Post $job
|
20 |
+
*/
|
21 |
+
$job = $args['job'];
|
22 |
+
?>
|
23 |
+
<p><?php
|
24 |
+
printf( __( 'A job listing has been updated on <a href="%s">%s</a>.', 'wp-job-manager' ), home_url(), get_bloginfo( 'name' ) );
|
25 |
+
switch ( $job->post_status ) {
|
26 |
+
case 'publish':
|
27 |
+
printf( ' ' . __( 'The changes have been published and are now available to the public.', 'wp-job-manager' ) );
|
28 |
+
break;
|
29 |
+
case 'pending':
|
30 |
+
printf( ' ' . __( 'The job listing is not publicly available until the changes are approved by an administrator in the site\'s <a href="%s">WordPress admin</a>.', 'wp-job-manager' ), esc_url( admin_url( 'edit.php?post_type=job_listing' ) ) );
|
31 |
+
break;
|
32 |
+
}
|
33 |
+
?></p>
|
34 |
+
<?php
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Show details about the job listing.
|
38 |
+
*
|
39 |
+
* @param WP_Post $job The job listing to show details for.
|
40 |
+
* @param WP_Job_Manager_Email $email Email object for the notification.
|
41 |
+
* @param bool $sent_to_admin True if this is being sent to an administrator.
|
42 |
+
* @param bool $plain_text True if the email is being sent as plain text.
|
43 |
+
*/
|
44 |
+
do_action( 'job_manager_email_job_details', $job, $email, true, $plain_text );
|
templates/emails/email-footer.php
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Footer for email notifications.
|
4 |
+
*
|
5 |
+
* This template can be overridden by copying it to yourtheme/job_manager/emails/email-footer.php.
|
6 |
+
*
|
7 |
+
* @see https://wpjobmanager.com/document/template-overrides/
|
8 |
+
* @author Automattic
|
9 |
+
* @package WP Job Manager
|
10 |
+
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
+
*/
|
13 |
+
|
14 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
+
exit; // Exit if accessed directly
|
16 |
+
}
|
17 |
+
?>
|
18 |
+
</div>
|
19 |
+
</td>
|
20 |
+
</tr>
|
21 |
+
</table>
|
22 |
+
</td>
|
23 |
+
</tr>
|
24 |
+
</table>
|
25 |
+
</td>
|
26 |
+
</tr>
|
27 |
+
</table>
|
28 |
+
</div>
|
29 |
+
</body>
|
30 |
+
</html>
|
templates/emails/email-header.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Header for email notifications.
|
4 |
+
*
|
5 |
+
* This template can be overridden by copying it to yourtheme/job_manager/emails/email-header.php.
|
6 |
+
*
|
7 |
+
* @see https://wpjobmanager.com/document/template-overrides/
|
8 |
+
* @author Automattic
|
9 |
+
* @package WP Job Manager
|
10 |
+
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
+
*/
|
13 |
+
|
14 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
+
exit; // Exit if accessed directly
|
16 |
+
}
|
17 |
+
?>
|
18 |
+
<!DOCTYPE html>
|
19 |
+
<html <?php language_attributes(); ?>>
|
20 |
+
<head>
|
21 |
+
<meta http-equiv="Content-Type" content="text/html; charset=<?php bloginfo( 'charset' ); ?>" />
|
22 |
+
<title><?php echo get_bloginfo( 'name', 'display' ); ?></title>
|
23 |
+
</head>
|
24 |
+
<body <?php echo is_rtl() ? 'rightmargin' : 'leftmargin'; ?>="0" marginwidth="0" topmargin="0" marginheight="0" offset="0">
|
25 |
+
<div id="wrapper" dir="<?php echo is_rtl() ? 'rtl' : 'ltr'?>">
|
26 |
+
<table border="0" cellpadding="0" cellspacing="0" height="100%" width="100%">
|
27 |
+
<tr>
|
28 |
+
<td align="center" valign="top">
|
29 |
+
<!-- Body -->
|
30 |
+
<table border="0" cellpadding="0" cellspacing="0" width="600" id="template_body">
|
31 |
+
<tr>
|
32 |
+
<td valign="top" id="body_content">
|
33 |
+
<!-- Content -->
|
34 |
+
<table border="0" cellpadding="20" cellspacing="0" width="100%">
|
35 |
+
<tr>
|
36 |
+
<td valign="top">
|
37 |
+
<div id="body_content_inner">
|
templates/emails/email-job-details.php
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Email content for showing job details.
|
4 |
+
*
|
5 |
+
* This template can be overridden by copying it to yourtheme/job_manager/emails/email-job-details.php.
|
6 |
+
*
|
7 |
+
* @see https://wpjobmanager.com/document/template-overrides/
|
8 |
+
* @author Automattic
|
9 |
+
* @package WP Job Manager
|
10 |
+
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
+
*/
|
13 |
+
|
14 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
+
exit; // Exit if accessed directly
|
16 |
+
}
|
17 |
+
|
18 |
+
$text_align = is_rtl() ? 'right' : 'left';
|
19 |
+
|
20 |
+
if ( ! empty( $fields ) ) : ?>
|
21 |
+
<div class="job-manager-email-job-details-container email-container">
|
22 |
+
<table border="0" cellpadding="10" cellspacing="0" width="100%" class="job-manager-email-job-details details">
|
23 |
+
<?php foreach ( $fields as $field ) : ?>
|
24 |
+
<tr>
|
25 |
+
<td class="detail-label" style="text-align:<?php echo $text_align; ?>;">
|
26 |
+
<?php echo wp_kses_post( $field['label'] ); ?>
|
27 |
+
</td>
|
28 |
+
<td class="detail-value" style="text-align:<?php echo $text_align; ?>;">
|
29 |
+
<?php
|
30 |
+
if ( ! empty( $field['url'] ) ) {
|
31 |
+
echo sprintf( '<a href="%s">%s</a>', esc_url( $field['url'] ), wp_kses_post( $field['value'] ) );
|
32 |
+
} else {
|
33 |
+
echo wp_kses_post( $field['value'] );
|
34 |
+
}
|
35 |
+
?>
|
36 |
+
</td>
|
37 |
+
</tr>
|
38 |
+
<?php endforeach; ?>
|
39 |
+
</table>
|
40 |
+
</div>
|
41 |
+
<?php endif; ?>
|
templates/emails/email-styles.php
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Email stylesheet.
|
4 |
+
*
|
5 |
+
* This template can be overridden by copying it to yourtheme/job_manager/emails/email-styles.php.
|
6 |
+
*
|
7 |
+
* @see https://wpjobmanager.com/document/template-overrides/
|
8 |
+
* @author Automattic
|
9 |
+
* @package WP Job Manager
|
10 |
+
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
+
*/
|
13 |
+
|
14 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
+
exit; // Exit if accessed directly
|
16 |
+
}
|
17 |
+
|
18 |
+
$style_vars = array();
|
19 |
+
$style_vars['color_bg'] = '#fff';
|
20 |
+
$style_vars['color_fg'] = '#000';
|
21 |
+
$style_vars['color_light'] = '#eee';
|
22 |
+
$style_vars['color_link'] = '#036fa9';
|
23 |
+
$style_vars['font_family'] = '"Helvetica Neue", Helvetica, Roboto, Arial, sans-serif';
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Change the style vars used in email generation stylesheet.
|
27 |
+
*
|
28 |
+
* @since 1.31.0
|
29 |
+
*
|
30 |
+
* @param array $style_vars Variables used in style generation.
|
31 |
+
*/
|
32 |
+
$style_vars = apply_filters( 'job_manager_email_style_vars', $style_vars );
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Inject styles before the core styles.
|
36 |
+
*
|
37 |
+
* @since 1.31.0
|
38 |
+
*
|
39 |
+
* @param array $style_vars Variables used in style generation.
|
40 |
+
*/
|
41 |
+
do_action( 'job_manager_email_style_before', $style_vars );
|
42 |
+
?>
|
43 |
+
|
44 |
+
#wrapper {
|
45 |
+
background-color: <?php echo esc_attr( $style_vars['color_bg'] ); ?>;
|
46 |
+
color: <?php echo esc_attr( $style_vars['color_fg'] ); ?>;
|
47 |
+
margin: 0;
|
48 |
+
padding: 70px 0 70px 0;
|
49 |
+
-webkit-text-size-adjust: none !important;
|
50 |
+
width: 100%;
|
51 |
+
font-family: <?php echo esc_attr( $style_vars['font_family'] ); ?>;
|
52 |
+
}
|
53 |
+
|
54 |
+
a {
|
55 |
+
color: <?php echo esc_attr( $style_vars['color_link'] ); ?>;
|
56 |
+
font-weight: normal;
|
57 |
+
text-decoration: underline;
|
58 |
+
}
|
59 |
+
|
60 |
+
.email-container {
|
61 |
+
margin-bottom: 10px;
|
62 |
+
}
|
63 |
+
|
64 |
+
td.detail-label,
|
65 |
+
td.detail-value {
|
66 |
+
vertical-align: middle;
|
67 |
+
border: 1px solid <?php echo esc_attr( $style_vars['color_light'] ); ?>;
|
68 |
+
}
|
69 |
+
|
70 |
+
td.detail-label {
|
71 |
+
word-wrap: break-word;
|
72 |
+
width: 40%;
|
73 |
+
}
|
74 |
+
|
75 |
+
<?php
|
76 |
+
/**
|
77 |
+
* Inject styles after the core styles.
|
78 |
+
*
|
79 |
+
* @since 1.31.0
|
80 |
+
*
|
81 |
+
* @param array $style_vars Variables used in style generation.
|
82 |
+
*/
|
83 |
+
do_action( 'job_manager_email_style_after', $style_vars );
|
templates/emails/employer-expiring-job.php
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Email content when notifying employers of an expiring job listing.
|
4 |
+
*
|
5 |
+
* This template can be overridden by copying it to yourtheme/job_manager/emails/employer-expiring-job.php.
|
6 |
+
*
|
7 |
+
* @see https://wpjobmanager.com/document/template-overrides/
|
8 |
+
* @author Automattic
|
9 |
+
* @package WP Job Manager
|
10 |
+
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
+
*/
|
13 |
+
|
14 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
+
exit; // Exit if accessed directly
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var WP_Post $job
|
20 |
+
*/
|
21 |
+
$job = $args['job'];
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var bool
|
25 |
+
*/
|
26 |
+
$expiring_today = $args['expiring_today'];
|
27 |
+
|
28 |
+
echo '<p>';
|
29 |
+
if ( $expiring_today ) {
|
30 |
+
printf( __( 'The following job listing is expiring today from <a href="%s">%s</a>.', 'wp-job-manager' ), home_url(), get_bloginfo( 'name' ) );
|
31 |
+
} else {
|
32 |
+
printf( __( 'The following job listing is expiring soon from <a href="%s">%s</a>.', 'wp-job-manager' ), home_url(), get_bloginfo( 'name' ) );
|
33 |
+
}
|
34 |
+
printf( ' ' . __( 'Visit the <a href="%s">job listing dashboard</a> to manage the listing.', 'wp-job-manager' ), esc_url( job_manager_get_permalink( 'job_dashboard' ) ) );
|
35 |
+
echo '</p>';
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Show details about the job listing.
|
39 |
+
*
|
40 |
+
* @param WP_Post $job The job listing to show details for.
|
41 |
+
* @param WP_Job_Manager_Email $email Email object for the notification.
|
42 |
+
* @param bool $sent_to_admin True if this is being sent to an administrator.
|
43 |
+
* @param bool $plain_text True if the email is being sent as plain text.
|
44 |
+
*/
|
45 |
+
do_action( 'job_manager_email_job_details', $job, $email, false, $plain_text );
|
templates/emails/plain/admin-expiring-job.php
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Email content when notifying the administrator of an expiring job listing.
|
4 |
+
*
|
5 |
+
* This template can be overridden by copying it to yourtheme/job_manager/emails/plain/employer-expiring-job.php.
|
6 |
+
*
|
7 |
+
* @see https://wpjobmanager.com/document/template-overrides/
|
8 |
+
* @author Automattic
|
9 |
+
* @package WP Job Manager
|
10 |
+
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
+
*/
|
13 |
+
|
14 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
+
exit; // Exit if accessed directly
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var WP_Post $job
|
20 |
+
*/
|
21 |
+
$job = $args['job'];
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var bool
|
25 |
+
*/
|
26 |
+
$expiring_today = $args['expiring_today'];
|
27 |
+
|
28 |
+
if ( $expiring_today ) {
|
29 |
+
printf( __( 'The following job listing is expiring today from %s (%s).', 'wp-job-manager' ), get_bloginfo( 'name' ), home_url() );
|
30 |
+
} else {
|
31 |
+
printf( __( 'The following job listing is expiring soon from %s (%s).', 'wp-job-manager' ), get_bloginfo( 'name' ), home_url() );
|
32 |
+
}
|
33 |
+
$edit_post_link = admin_url( sprintf( 'post.php?post=%d&action=edit', $job->ID ) );
|
34 |
+
printf( ' ' . __( 'Visit WordPress admin (%s) to manage the listing.', 'wp-job-manager' ), esc_url( $edit_post_link ) );
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Show details about the job listing.
|
38 |
+
*
|
39 |
+
* @param WP_Post $job The job listing to show details for.
|
40 |
+
* @param WP_Job_Manager_Email $email Email object for the notification.
|
41 |
+
* @param bool $sent_to_admin True if this is being sent to an administrator.
|
42 |
+
* @param bool $plain_text True if the email is being sent as plain text.
|
43 |
+
*/
|
44 |
+
do_action( 'job_manager_email_job_details', $job, $email, true, $plain_text );
|
templates/emails/plain/admin-new-job.php
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Email content when notifying admin of a new job listing.
|
4 |
+
*
|
5 |
+
* This template can be overridden by copying it to yourtheme/job_manager/emails/plain/admin-new-job.php.
|
6 |
+
*
|
7 |
+
* @see https://wpjobmanager.com/document/template-overrides/
|
8 |
+
* @author Automattic
|
9 |
+
* @package WP Job Manager
|
10 |
+
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
+
*/
|
13 |
+
|
14 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
+
exit; // Exit if accessed directly
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var WP_Post $job
|
20 |
+
*/
|
21 |
+
$job = $args['job'];
|
22 |
+
|
23 |
+
printf( __( 'A new job listing has been submitted to %s (%s).', 'wp-job-manager' ), get_bloginfo( 'name' ), home_url() );
|
24 |
+
switch ( $job->post_status ) {
|
25 |
+
case 'publish':
|
26 |
+
printf( ' ' . __( 'It has been published and is now available to the public.', 'wp-job-manager' ) );
|
27 |
+
break;
|
28 |
+
case 'pending':
|
29 |
+
printf( ' ' . __( 'It is awaiting approval by an administrator in WordPress admin (%s).', 'wp-job-manager' ), esc_url( admin_url( 'edit.php?post_type=job_listing' ) ) );
|
30 |
+
break;
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Show details about the job listing.
|
35 |
+
*
|
36 |
+
* @param WP_Post $job The job listing to show details for.
|
37 |
+
* @param WP_Job_Manager_Email $email Email object for the notification.
|
38 |
+
* @param bool $sent_to_admin True if this is being sent to an administrator.
|
39 |
+
* @param bool $plain_text True if the email is being sent as plain text.
|
40 |
+
*/
|
41 |
+
do_action( 'job_manager_email_job_details', $job, $email, true, $plain_text );
|
templates/emails/plain/admin-updated-job.php
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Email content when notifying admin of an updated job listing.
|
4 |
+
*
|
5 |
+
* This template can be overridden by copying it to yourtheme/job_manager/emails/plain/admin-updated-job.php.
|
6 |
+
*
|
7 |
+
* @see https://wpjobmanager.com/document/template-overrides/
|
8 |
+
* @author Automattic
|
9 |
+
* @package WP Job Manager
|
10 |
+
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
+
*/
|
13 |
+
|
14 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
+
exit; // Exit if accessed directly
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var WP_Post $job
|
20 |
+
*/
|
21 |
+
$job = $args['job'];
|
22 |
+
|
23 |
+
printf( __( 'A job listing has been updated on %s (%s).', 'wp-job-manager' ), get_bloginfo( 'name' ), home_url() );
|
24 |
+
switch ( $job->post_status ) {
|
25 |
+
case 'publish':
|
26 |
+
printf( ' ' . __( 'The changes have been published and are now available to the public.', 'wp-job-manager' ) );
|
27 |
+
break;
|
28 |
+
case 'pending':
|
29 |
+
printf( ' ' . __( 'The job listing is not publicly available until the changes are approved by an administrator in the site\'s WordPress admin (%s).', 'wp-job-manager' ), esc_url( admin_url( 'edit.php?post_type=job_listing' ) ) );
|
30 |
+
break;
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Show details about the job listing.
|
35 |
+
*
|
36 |
+
* @param WP_Post $job The job listing to show details for.
|
37 |
+
* @param WP_Job_Manager_Email $email Email object for the notification.
|
38 |
+
* @param bool $sent_to_admin True if this is being sent to an administrator.
|
39 |
+
* @param bool $plain_text True if the email is being sent as plain text.
|
40 |
+
*/
|
41 |
+
do_action( 'job_manager_email_job_details', $job, $email, true, $plain_text );
|
templates/emails/plain/email-footer.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Footer for email notifications.
|
4 |
+
*
|
5 |
+
* This template can be overridden by copying it to yourtheme/job_manager/emails/plain/email-footer.php.
|
6 |
+
*
|
7 |
+
* @see https://wpjobmanager.com/document/template-overrides/
|
8 |
+
* @author Automattic
|
9 |
+
* @package WP Job Manager
|
10 |
+
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
+
*/
|
13 |
+
|
14 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
+
exit; // Exit if accessed directly
|
16 |
+
}
|
17 |
+
?>
|
templates/emails/plain/email-header.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Header for email notifications.
|
4 |
+
*
|
5 |
+
* This template can be overridden by copying it to yourtheme/job_manager/emails/plain/email-header.php.
|
6 |
+
*
|
7 |
+
* @see https://wpjobmanager.com/document/template-overrides/
|
8 |
+
* @author Automattic
|
9 |
+
* @package WP Job Manager
|
10 |
+
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
+
*/
|
13 |
+
|
14 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
+
exit; // Exit if accessed directly
|
16 |
+
}
|
17 |
+
?>
|
templates/emails/plain/email-job-details.php
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Email content for showing job details.
|
4 |
+
*
|
5 |
+
* This template can be overridden by copying it to yourtheme/job_manager/emails/plain/email-job-details.php.
|
6 |
+
*
|
7 |
+
* @see https://wpjobmanager.com/document/template-overrides/
|
8 |
+
* @author Automattic
|
9 |
+
* @package WP Job Manager
|
10 |
+
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
+
*/
|
13 |
+
|
14 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
+
exit; // Exit if accessed directly
|
16 |
+
}
|
17 |
+
|
18 |
+
echo "\n\n";
|
19 |
+
|
20 |
+
if ( ! empty( $fields ) ) {
|
21 |
+
foreach ( $fields as $field ) {
|
22 |
+
echo strip_tags( $field[ 'label' ] ) .': '. strip_tags( $field[ 'value' ] );
|
23 |
+
if ( ! empty( $field['url'] ) ) {
|
24 |
+
echo ' (' . esc_url( $field['url'] ) . ')';
|
25 |
+
}
|
26 |
+
echo "\n";
|
27 |
+
}
|
28 |
+
}
|
templates/emails/plain/employer-expiring-job.php
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Email content when notifying employers of an expiring job listing.
|
4 |
+
*
|
5 |
+
* This template can be overridden by copying it to yourtheme/job_manager/emails/plain/employer-expiring-job.php.
|
6 |
+
*
|
7 |
+
* @see https://wpjobmanager.com/document/template-overrides/
|
8 |
+
* @author Automattic
|
9 |
+
* @package WP Job Manager
|
10 |
+
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
+
*/
|
13 |
+
|
14 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
+
exit; // Exit if accessed directly
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var WP_Post $job
|
20 |
+
*/
|
21 |
+
$job = $args['job'];
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var bool
|
25 |
+
*/
|
26 |
+
$expiring_today = $args['expiring_today'];
|
27 |
+
|
28 |
+
if ( $expiring_today ) {
|
29 |
+
printf( __( 'The following job listing is expiring today from %s (%s).', 'wp-job-manager' ), get_bloginfo( 'name' ), home_url() );
|
30 |
+
} else {
|
31 |
+
printf( __( 'The following job listing is expiring soon from %s (%s).', 'wp-job-manager' ), get_bloginfo( 'name' ), home_url() );
|
32 |
+
}
|
33 |
+
printf( ' ' . __( 'Visit the job listing dashboard (%s) to manage the listing.', 'wp-job-manager' ), esc_url( job_manager_get_permalink( 'job_dashboard' ) ) );
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Show details about the job listing.
|
37 |
+
*
|
38 |
+
* @param WP_Post $job The job listing to show details for.
|
39 |
+
* @param WP_Job_Manager_Email $email Email object for the notification.
|
40 |
+
* @param bool $sent_to_admin True if this is being sent to an administrator.
|
41 |
+
* @param bool $plain_text True if the email is being sent as plain text.
|
42 |
+
*/
|
43 |
+
do_action( 'job_manager_email_job_details', $job, $email, false, $plain_text );
|
templates/form-fields/date-field.php
CHANGED
@@ -8,7 +8,7 @@
|
|
8 |
* @author Automattic
|
9 |
* @package WP Job Manager
|
10 |
* @category Template
|
11 |
-
* @version 1.30.
|
12 |
*/
|
13 |
|
14 |
if ( ! defined( 'ABSPATH' ) ) {
|
@@ -16,6 +16,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
16 |
}
|
17 |
|
18 |
wp_enqueue_script( 'wp-job-manager-datepicker' );
|
|
|
19 |
|
20 |
?>
|
21 |
<input type="text" class="input-date job-manager-datepicker" name="<?php echo esc_attr( isset( $field['name'] ) ? $field['name'] : $key ); ?>"<?php if ( isset( $field['autocomplete'] ) && false === $field['autocomplete'] ) { echo ' autocomplete="off"'; } ?> id="<?php echo esc_attr( $key ); ?>" placeholder="<?php echo empty( $field['placeholder'] ) ? '' : esc_attr( $field['placeholder'] ); ?>" value="<?php echo isset( $field['value'] ) ? esc_attr( $field['value'] ) : ''; ?>" <?php if ( ! empty( $field['required'] ) ) echo 'required'; ?> />
|
8 |
* @author Automattic
|
9 |
* @package WP Job Manager
|
10 |
* @category Template
|
11 |
+
* @version 1.30.2
|
12 |
*/
|
13 |
|
14 |
if ( ! defined( 'ABSPATH' ) ) {
|
16 |
}
|
17 |
|
18 |
wp_enqueue_script( 'wp-job-manager-datepicker' );
|
19 |
+
wp_enqueue_style( 'jquery-ui' );
|
20 |
|
21 |
?>
|
22 |
<input type="text" class="input-date job-manager-datepicker" name="<?php echo esc_attr( isset( $field['name'] ) ? $field['name'] : $key ); ?>"<?php if ( isset( $field['autocomplete'] ) && false === $field['autocomplete'] ) { echo ' autocomplete="off"'; } ?> id="<?php echo esc_attr( $key ); ?>" placeholder="<?php echo empty( $field['placeholder'] ) ? '' : esc_attr( $field['placeholder'] ); ?>" value="<?php echo isset( $field['value'] ) ? esc_attr( $field['value'] ) : ''; ?>" <?php if ( ! empty( $field['required'] ) ) echo 'required'; ?> />
|
templates/job-application-url.php
CHANGED
@@ -8,11 +8,11 @@
|
|
8 |
* @author Automattic
|
9 |
* @package WP Job Manager
|
10 |
* @category Template
|
11 |
-
* @version 1.
|
12 |
*/
|
13 |
|
14 |
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
exit; // Exit if accessed directly
|
16 |
}
|
17 |
?>
|
18 |
-
<p><?php
|
8 |
* @author Automattic
|
9 |
* @package WP Job Manager
|
10 |
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
*/
|
13 |
|
14 |
if ( ! defined( 'ABSPATH' ) ) {
|
15 |
exit; // Exit if accessed directly
|
16 |
}
|
17 |
?>
|
18 |
+
<p><?php esc_html_e( 'To apply for this job please visit', 'wp-job-manager' ); ?> <a href="<?php echo esc_url( $apply->url ); ?>" target="_blank" rel="nofollow"><?php echo esc_html( wp_parse_url( $apply->url, PHP_URL_HOST ) ); ?></a>.</p>
|
templates/job-filters.php
CHANGED
@@ -8,7 +8,7 @@
|
|
8 |
* @author Automattic
|
9 |
* @package WP Job Manager
|
10 |
* @category Template
|
11 |
-
* @version 1.
|
12 |
*/
|
13 |
|
14 |
if ( ! defined( 'ABSPATH' ) ) {
|
@@ -40,13 +40,13 @@ do_action( 'job_manager_job_filters_before', $atts );
|
|
40 |
<?php foreach ( $categories as $category ) : ?>
|
41 |
<input type="hidden" name="search_categories[]" value="<?php echo sanitize_title( $category ); ?>" />
|
42 |
<?php endforeach; ?>
|
43 |
-
<?php elseif ( $show_categories && ! is_tax( 'job_listing_category' ) && get_terms( 'job_listing_category' ) ) : ?>
|
44 |
<div class="search_categories">
|
45 |
<label for="search_categories"><?php _e( 'Category', 'wp-job-manager' ); ?></label>
|
46 |
<?php if ( $show_category_multiselect ) : ?>
|
47 |
-
<?php job_manager_dropdown_categories( array( 'taxonomy' => 'job_listing_category', 'hierarchical' => 1, 'name' => 'search_categories', 'orderby' => 'name', 'selected' => $selected_category, 'hide_empty' =>
|
48 |
<?php else : ?>
|
49 |
-
<?php job_manager_dropdown_categories( array( 'taxonomy' => 'job_listing_category', 'hierarchical' => 1, 'show_option_all' => __( 'Any category', 'wp-job-manager' ), 'name' => 'search_categories', 'orderby' => 'name', 'selected' => $selected_category, 'multiple' => false ) ); ?>
|
50 |
<?php endif; ?>
|
51 |
</div>
|
52 |
<?php endif; ?>
|
@@ -59,4 +59,4 @@ do_action( 'job_manager_job_filters_before', $atts );
|
|
59 |
|
60 |
<?php do_action( 'job_manager_job_filters_after', $atts ); ?>
|
61 |
|
62 |
-
<noscript><?php _e( 'Your browser does not support JavaScript, or it is disabled. JavaScript must be enabled in order to view listings.', 'wp-job-manager' ); ?></noscript>
|
8 |
* @author Automattic
|
9 |
* @package WP Job Manager
|
10 |
* @category Template
|
11 |
+
* @version 1.31.0
|
12 |
*/
|
13 |
|
14 |
if ( ! defined( 'ABSPATH' ) ) {
|
40 |
<?php foreach ( $categories as $category ) : ?>
|
41 |
<input type="hidden" name="search_categories[]" value="<?php echo sanitize_title( $category ); ?>" />
|
42 |
<?php endforeach; ?>
|
43 |
+
<?php elseif ( $show_categories && ! is_tax( 'job_listing_category' ) && get_terms( array( 'taxonomy' => 'job_listing_category' ) ) ) : ?>
|
44 |
<div class="search_categories">
|
45 |
<label for="search_categories"><?php _e( 'Category', 'wp-job-manager' ); ?></label>
|
46 |
<?php if ( $show_category_multiselect ) : ?>
|
47 |
+
<?php job_manager_dropdown_categories( array( 'taxonomy' => 'job_listing_category', 'hierarchical' => 1, 'name' => 'search_categories', 'orderby' => 'name', 'selected' => $selected_category, 'hide_empty' => true ) ); ?>
|
48 |
<?php else : ?>
|
49 |
+
<?php job_manager_dropdown_categories( array( 'taxonomy' => 'job_listing_category', 'hierarchical' => 1, 'show_option_all' => __( 'Any category', 'wp-job-manager' ), 'name' => 'search_categories', 'orderby' => 'name', 'selected' => $selected_category, 'multiple' => false, 'hide_empty' => true ) ); ?>
|
50 |
<?php endif; ?>
|
51 |
</div>
|
52 |
<?php endif; ?>
|
59 |
|
60 |
<?php do_action( 'job_manager_job_filters_after', $atts ); ?>
|
61 |
|
62 |
+
<noscript><?php _e( 'Your browser does not support JavaScript, or it is disabled. JavaScript must be enabled in order to view listings.', 'wp-job-manager' ); ?></noscript>
|
uninstall.php
CHANGED
@@ -3,38 +3,33 @@ if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
|
|
3 |
exit();
|
4 |
}
|
5 |
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
$
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
'job_manager_google_maps_api_key',
|
34 |
-
);
|
35 |
-
|
36 |
-
foreach ( $options as $option ) {
|
37 |
-
delete_option( $option );
|
38 |
}
|
39 |
|
40 |
include dirname( __FILE__ ) . '/includes/class-wp-job-manager-usage-tracking.php';
|
3 |
exit();
|
4 |
}
|
5 |
|
6 |
+
// Cleanup all data.
|
7 |
+
require 'includes/class-wp-job-manager-data-cleaner.php';
|
8 |
+
|
9 |
+
if ( ! is_multisite() ) {
|
10 |
+
|
11 |
+
// Only do deletion if the setting is true.
|
12 |
+
$do_deletion = get_option( 'job_manager_delete_data_on_uninstall' );
|
13 |
+
if ( $do_deletion ) {
|
14 |
+
WP_Job_Manager_Data_Cleaner::cleanup_all();
|
15 |
+
}
|
16 |
+
} else {
|
17 |
+
global $wpdb;
|
18 |
+
|
19 |
+
$blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
|
20 |
+
$original_blog_id = get_current_blog_id();
|
21 |
+
|
22 |
+
foreach ( $blog_ids as $blog_id ) {
|
23 |
+
switch_to_blog( $blog_id );
|
24 |
+
|
25 |
+
// Only do deletion if the setting is true.
|
26 |
+
$do_deletion = get_option( 'job_manager_delete_data_on_uninstall' );
|
27 |
+
if ( $do_deletion ) {
|
28 |
+
WP_Job_Manager_Data_Cleaner::cleanup_all();
|
29 |
+
}
|
30 |
+
}
|
31 |
+
|
32 |
+
switch_to_blog( $original_blog_id );
|
|
|
|
|
|
|
|
|
|
|
33 |
}
|
34 |
|
35 |
include dirname( __FILE__ ) . '/includes/class-wp-job-manager-usage-tracking.php';
|
wp-job-manager-functions.php
CHANGED
@@ -382,11 +382,25 @@ function get_job_listing_categories() {
|
|
382 |
return array();
|
383 |
}
|
384 |
|
385 |
-
|
386 |
'orderby' => 'name',
|
387 |
-
|
388 |
-
|
389 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
390 |
}
|
391 |
endif;
|
392 |
|
@@ -1046,14 +1060,15 @@ function job_manager_dropdown_categories( $args = '' ) {
|
|
1046 |
$categories = get_transient( $categories_hash );
|
1047 |
|
1048 |
if ( empty( $categories ) ) {
|
1049 |
-
$categories = get_terms(
|
|
|
1050 |
'orderby' => $r['orderby'],
|
1051 |
'order' => $r['order'],
|
1052 |
'hide_empty' => $r['hide_empty'],
|
1053 |
'parent' => $r['parent'],
|
1054 |
'child_of' => $r['child_of'],
|
1055 |
'exclude' => $r['exclude'],
|
1056 |
-
'hierarchical' => $r['hierarchical']
|
1057 |
) );
|
1058 |
set_transient( $categories_hash, $categories, DAY_IN_SECONDS * 7 );
|
1059 |
}
|
382 |
return array();
|
383 |
}
|
384 |
|
385 |
+
$args = array(
|
386 |
'orderby' => 'name',
|
387 |
+
'order' => 'ASC',
|
388 |
+
'hide_empty' => false,
|
389 |
+
);
|
390 |
+
|
391 |
+
/**
|
392 |
+
* Change the category query arguments.
|
393 |
+
*
|
394 |
+
* @since 1.31.0
|
395 |
+
*
|
396 |
+
* @param array $args
|
397 |
+
*/
|
398 |
+
$args = apply_filters( 'get_job_listing_category_args', $args );
|
399 |
+
|
400 |
+
// Prevent users from filtering the taxonomy
|
401 |
+
$args['taxonomy'] = 'job_listing_category';
|
402 |
+
|
403 |
+
return get_terms( $args );
|
404 |
}
|
405 |
endif;
|
406 |
|
1060 |
$categories = get_transient( $categories_hash );
|
1061 |
|
1062 |
if ( empty( $categories ) ) {
|
1063 |
+
$categories = get_terms( array(
|
1064 |
+
'taxonomy' => $r['taxonomy'],
|
1065 |
'orderby' => $r['orderby'],
|
1066 |
'order' => $r['order'],
|
1067 |
'hide_empty' => $r['hide_empty'],
|
1068 |
'parent' => $r['parent'],
|
1069 |
'child_of' => $r['child_of'],
|
1070 |
'exclude' => $r['exclude'],
|
1071 |
+
'hierarchical' => $r['hierarchical'],
|
1072 |
) );
|
1073 |
set_transient( $categories_hash, $categories, DAY_IN_SECONDS * 7 );
|
1074 |
}
|
wp-job-manager-template.php
CHANGED
@@ -380,6 +380,9 @@ function wpjm_get_job_listing_structured_data( $post = null ) {
|
|
380 |
$data['hiringOrganization']['sameAs'] = $company_website;
|
381 |
$data['hiringOrganization']['url'] = $company_website;
|
382 |
}
|
|
|
|
|
|
|
383 |
|
384 |
$data['identifier'] = array();
|
385 |
$data['identifier']['@type'] = 'PropertyValue';
|
@@ -600,6 +603,60 @@ function wpjm_get_the_job_types( $post = null ) {
|
|
600 |
return apply_filters( 'wpjm_the_job_types', $types, $post );
|
601 |
}
|
602 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
603 |
/**
|
604 |
* Returns the registration fields used when an account is required.
|
605 |
*
|
380 |
$data['hiringOrganization']['sameAs'] = $company_website;
|
381 |
$data['hiringOrganization']['url'] = $company_website;
|
382 |
}
|
383 |
+
if ( $company_logo = get_the_company_logo( $post, 'full' ) ) {
|
384 |
+
$data['hiringOrganization']['logo'] = $company_logo;
|
385 |
+
}
|
386 |
|
387 |
$data['identifier'] = array();
|
388 |
$data['identifier']['@type'] = 'PropertyValue';
|
603 |
return apply_filters( 'wpjm_the_job_types', $types, $post );
|
604 |
}
|
605 |
|
606 |
+
/**
|
607 |
+
* Displays job categories for the listing.
|
608 |
+
*
|
609 |
+
* @since 1.31.0
|
610 |
+
*
|
611 |
+
* @param int|WP_Post $post Current post object.
|
612 |
+
* @param string $separator String to join the term names with.
|
613 |
+
*/
|
614 |
+
function wpjm_the_job_categories( $post = null, $separator = ', ' ) {
|
615 |
+
if ( ! get_option( 'job_manager_enable_categories' ) ) {
|
616 |
+
return;
|
617 |
+
}
|
618 |
+
|
619 |
+
$job_categories = wpjm_get_the_job_categories( $post );
|
620 |
+
|
621 |
+
if ( $job_categories ) {
|
622 |
+
$names = wp_list_pluck( $job_categories, 'name' );
|
623 |
+
|
624 |
+
echo esc_html( implode( $separator, $names ) );
|
625 |
+
}
|
626 |
+
}
|
627 |
+
|
628 |
+
/**
|
629 |
+
* Gets the job type for the listing.
|
630 |
+
*
|
631 |
+
* @since 1.31.0
|
632 |
+
*
|
633 |
+
* @param int|WP_Post $post (default: null).
|
634 |
+
* @return bool|array
|
635 |
+
*/
|
636 |
+
function wpjm_get_the_job_categories( $post = null ) {
|
637 |
+
$post = get_post( $post );
|
638 |
+
|
639 |
+
if ( ! $post || 'job_listing' !== $post->post_type ) {
|
640 |
+
return false;
|
641 |
+
}
|
642 |
+
|
643 |
+
$categories = get_the_terms( $post->ID, 'job_listing_category' );
|
644 |
+
|
645 |
+
if ( empty( $categories ) || is_wp_error( $categories ) ) {
|
646 |
+
$categories = array();
|
647 |
+
}
|
648 |
+
|
649 |
+
/**
|
650 |
+
* Filter the returned job categories for a post.
|
651 |
+
*
|
652 |
+
* @since 1.31.0
|
653 |
+
*
|
654 |
+
* @param array $types
|
655 |
+
* @param WP_Post $post
|
656 |
+
*/
|
657 |
+
return apply_filters( 'wpjm_the_job_categories', $categories, $post );
|
658 |
+
}
|
659 |
+
|
660 |
/**
|
661 |
* Returns the registration fields used when an account is required.
|
662 |
*
|
wp-job-manager.php
CHANGED
@@ -3,10 +3,10 @@
|
|
3 |
* Plugin Name: WP Job Manager
|
4 |
* Plugin URI: https://wpjobmanager.com/
|
5 |
* Description: Manage job listings from the WordPress admin panel, and allow users to post jobs directly to your site.
|
6 |
-
* Version: 1.
|
7 |
* Author: Automattic
|
8 |
* Author URI: https://wpjobmanager.com/
|
9 |
-
* Requires at least: 4.
|
10 |
* Tested up to: 4.9
|
11 |
* Text Domain: wp-job-manager
|
12 |
* Domain Path: /languages/
|
@@ -58,8 +58,8 @@ class WP_Job_Manager {
|
|
58 |
*/
|
59 |
public function __construct() {
|
60 |
// Define constants
|
61 |
-
define( 'JOB_MANAGER_VERSION', '1.
|
62 |
-
define( 'JOB_MANAGER_MINIMUM_WP_VERSION', '4.7' );
|
63 |
define( 'JOB_MANAGER_PLUGIN_DIR', untrailingslashit( plugin_dir_path( __FILE__ ) ) );
|
64 |
define( 'JOB_MANAGER_PLUGIN_URL', untrailingslashit( plugins_url( basename( plugin_dir_path( __FILE__ ) ), basename( __FILE__ ) ) ) );
|
65 |
define( 'JOB_MANAGER_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
|
@@ -74,6 +74,9 @@ class WP_Job_Manager {
|
|
74 |
include_once( JOB_MANAGER_PLUGIN_DIR . '/includes/class-wp-job-manager-geocode.php' );
|
75 |
include_once( JOB_MANAGER_PLUGIN_DIR . '/includes/class-wp-job-manager-cache-helper.php' );
|
76 |
include_once( JOB_MANAGER_PLUGIN_DIR . '/includes/helper/class-wp-job-manager-helper.php' );
|
|
|
|
|
|
|
77 |
|
78 |
add_action( 'rest_api_init', array( $this, 'rest_api' ) );
|
79 |
|
@@ -103,13 +106,18 @@ class WP_Job_Manager {
|
|
103 |
add_action( 'after_setup_theme', array( $this, 'load_plugin_textdomain' ) );
|
104 |
add_action( 'after_setup_theme', array( $this, 'include_template_functions' ), 11 );
|
105 |
add_action( 'widgets_init', array( $this, 'widgets_init' ) );
|
|
|
106 |
add_action( 'wp_enqueue_scripts', array( $this, 'frontend_scripts' ) );
|
107 |
add_action( 'admin_init', array( $this, 'updater' ) );
|
108 |
add_action( 'wp_logout', array( $this, 'cleanup_job_posting_cookies' ) );
|
|
|
109 |
|
110 |
add_action( 'init', array( $this, 'usage_tracking_init' ) );
|
111 |
register_deactivation_hook( __FILE__, array( $this, 'usage_tracking_cleanup' ) );
|
112 |
|
|
|
|
|
|
|
113 |
// Defaults for WPJM core actions
|
114 |
add_action( 'wpjm_notify_new_user', 'wp_job_manager_notify_new_user', 10, 2 );
|
115 |
}
|
@@ -209,6 +217,19 @@ class WP_Job_Manager {
|
|
209 |
if ( ! wp_next_scheduled( 'job_manager_clear_expired_transients' ) ) {
|
210 |
wp_schedule_event( time(), 'twicedaily', 'job_manager_clear_expired_transients' );
|
211 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
}
|
213 |
|
214 |
/**
|
@@ -223,6 +244,16 @@ class WP_Job_Manager {
|
|
223 |
}
|
224 |
}
|
225 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
226 |
/**
|
227 |
* Registers and enqueues scripts and CSS.
|
228 |
*/
|
3 |
* Plugin Name: WP Job Manager
|
4 |
* Plugin URI: https://wpjobmanager.com/
|
5 |
* Description: Manage job listings from the WordPress admin panel, and allow users to post jobs directly to your site.
|
6 |
+
* Version: 1.31.0
|
7 |
* Author: Automattic
|
8 |
* Author URI: https://wpjobmanager.com/
|
9 |
+
* Requires at least: 4.7.0
|
10 |
* Tested up to: 4.9
|
11 |
* Text Domain: wp-job-manager
|
12 |
* Domain Path: /languages/
|
58 |
*/
|
59 |
public function __construct() {
|
60 |
// Define constants
|
61 |
+
define( 'JOB_MANAGER_VERSION', '1.31.0' );
|
62 |
+
define( 'JOB_MANAGER_MINIMUM_WP_VERSION', '4.7.0' );
|
63 |
define( 'JOB_MANAGER_PLUGIN_DIR', untrailingslashit( plugin_dir_path( __FILE__ ) ) );
|
64 |
define( 'JOB_MANAGER_PLUGIN_URL', untrailingslashit( plugins_url( basename( plugin_dir_path( __FILE__ ) ), basename( __FILE__ ) ) ) );
|
65 |
define( 'JOB_MANAGER_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
|
74 |
include_once( JOB_MANAGER_PLUGIN_DIR . '/includes/class-wp-job-manager-geocode.php' );
|
75 |
include_once( JOB_MANAGER_PLUGIN_DIR . '/includes/class-wp-job-manager-cache-helper.php' );
|
76 |
include_once( JOB_MANAGER_PLUGIN_DIR . '/includes/helper/class-wp-job-manager-helper.php' );
|
77 |
+
include_once( JOB_MANAGER_PLUGIN_DIR . '/includes/abstracts/abstract-wp-job-manager-email.php' );
|
78 |
+
include_once( JOB_MANAGER_PLUGIN_DIR . '/includes/abstracts/abstract-wp-job-manager-email-template.php' );
|
79 |
+
include_once( JOB_MANAGER_PLUGIN_DIR . '/includes/class-wp-job-manager-email-notifications.php' );
|
80 |
|
81 |
add_action( 'rest_api_init', array( $this, 'rest_api' ) );
|
82 |
|
106 |
add_action( 'after_setup_theme', array( $this, 'load_plugin_textdomain' ) );
|
107 |
add_action( 'after_setup_theme', array( $this, 'include_template_functions' ), 11 );
|
108 |
add_action( 'widgets_init', array( $this, 'widgets_init' ) );
|
109 |
+
add_action( 'wp_loaded', array( $this, 'register_shared_assets' ) );
|
110 |
add_action( 'wp_enqueue_scripts', array( $this, 'frontend_scripts' ) );
|
111 |
add_action( 'admin_init', array( $this, 'updater' ) );
|
112 |
add_action( 'wp_logout', array( $this, 'cleanup_job_posting_cookies' ) );
|
113 |
+
add_action( 'init', array( 'WP_Job_Manager_Email_Notifications', 'init' ) );
|
114 |
|
115 |
add_action( 'init', array( $this, 'usage_tracking_init' ) );
|
116 |
register_deactivation_hook( __FILE__, array( $this, 'usage_tracking_cleanup' ) );
|
117 |
|
118 |
+
// Other cleanup
|
119 |
+
register_deactivation_hook( __FILE__, array( $this, 'unschedule_cron_jobs' ) );
|
120 |
+
|
121 |
// Defaults for WPJM core actions
|
122 |
add_action( 'wpjm_notify_new_user', 'wp_job_manager_notify_new_user', 10, 2 );
|
123 |
}
|
217 |
if ( ! wp_next_scheduled( 'job_manager_clear_expired_transients' ) ) {
|
218 |
wp_schedule_event( time(), 'twicedaily', 'job_manager_clear_expired_transients' );
|
219 |
}
|
220 |
+
if ( ! wp_next_scheduled( 'job_manager_email_daily_notices' ) ) {
|
221 |
+
wp_schedule_event( time(), 'daily', 'job_manager_email_daily_notices' );
|
222 |
+
}
|
223 |
+
}
|
224 |
+
|
225 |
+
/**
|
226 |
+
* Unschedule cron jobs. This is run on plugin deactivation.
|
227 |
+
*/
|
228 |
+
public static function unschedule_cron_jobs() {
|
229 |
+
wp_clear_scheduled_hook( 'job_manager_check_for_expired_jobs' );
|
230 |
+
wp_clear_scheduled_hook( 'job_manager_delete_old_previews' );
|
231 |
+
wp_clear_scheduled_hook( 'job_manager_clear_expired_transients' );
|
232 |
+
wp_clear_scheduled_hook( 'job_manager_email_daily_notices' );
|
233 |
}
|
234 |
|
235 |
/**
|
244 |
}
|
245 |
}
|
246 |
|
247 |
+
/**
|
248 |
+
* Registers assets used in both the frontend and WP admin.
|
249 |
+
*/
|
250 |
+
public function register_shared_assets() {
|
251 |
+
global $wp_scripts;
|
252 |
+
|
253 |
+
$jquery_version = isset( $wp_scripts->registered['jquery-ui-core']->ver ) ? $wp_scripts->registered['jquery-ui-core']->ver : '1.9.2';
|
254 |
+
wp_register_style( 'jquery-ui', '//code.jquery.com/ui/' . $jquery_version . '/themes/smoothness/jquery-ui.css', array(), $jquery_version );
|
255 |
+
}
|
256 |
+
|
257 |
/**
|
258 |
* Registers and enqueues scripts and CSS.
|
259 |
*/
|