Essential Content Types - Version 1.8.6

Version Description

(Released: August 05, 2021) = * Bug Fixed: Deprecated - Required parameter $atts follows optional parameter * Compatibility check up to version 5.8

Download this release

Release Info

Developer catchthemes
Plugin Icon Essential Content Types
Version 1.8.6
Comparing to
See all releases

Code changes from version 1.8.5 to 1.8.6

README.txt CHANGED
@@ -1,293 +1,297 @@
1
- === Essential Content Types ===
2
- Contributors: catchplugins, catchthemes, sakinshrestha, pratikshrestha, maheshmaharjan, dreamsapana
3
- Donate link: https://catchplugins.com/plugins/essential-content-types-pro/
4
- Tags: custom post types, CPT, CMS, post, types, post type, taxonomy, tax, custom, content types, post types, custom content types, testimonial, portfolio, featured content, service
5
- Requires at least: 4.8
6
- Tested up to: 5.6
7
- Stable tag: trunk
8
- License: GPLv3 or later
9
- License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
-
11
- Essential Content Types allows you to feature the impressive content through different content/post types on your website just the way you want it. These content/post types are missed by the themes in WordPress Theme Directory as the feature falls more towards the plugins’ territory.
12
-
13
- == Description ==
14
-
15
- **Essential Content Types** allows you to feature the impressive content through different content/post types on your website just the way you want it. These content/post types are missed by the themes in WordPress Theme Directory as the feature falls more towards the plugins' territory.
16
-
17
- Content is at the forefront of any website. Changing the layout of your important content changes the way your website looks, which may not be a plus point if your current website layout is loved by your users.
18
-
19
- Additionally, switching themes changes your website layout completely. Therefore, to keep things looking spic-and- span in your website, we bring you Essential Content.
20
-
21
- Essential Content allows you to add up to three content/post types:
22
- * Portfolio – Create and display your portfolio on your website
23
- * Testimonials – Add customer testimonials to your website
24
- * Featured Content – Display the content you want as featured content on your website to attract visitors' attention
25
- * Services – Add your services on your website
26
-
27
- Features of Essential Content:
28
- * Enable/Disable any content/post type as needed
29
- * Light-weight
30
- * Supports all themes on WordPress
31
-
32
- Essential Content is inspired by *Jetpack's Custom Content Types feature*.
33
-
34
- However, not everyone wants to have a plugin that “does-it- all”. Some may want plugins to be niche focused and concentrate on smaller areas.
35
-
36
- That is precisely what we have done with Essential Content. Essential content, true to its name, has only the essential elements. We have added the features that WordPressers use most. We have ruled out all other elements to make it non-bloated and clean. It takes up lesser space and does the job well.
37
-
38
- If you think we have missed any essential content/post types, please let us know. We’ll review the frequency of usage and add your suggestions.
39
-
40
- ***Portfolio Shortcode***
41
-
42
- You can use shortcodes to embed portfolio projects on posts and pages.
43
-
44
- **Embedding Portfolio Projects**
45
-
46
- To embed portfolio projects on posts and pages, first activate the Portfolio custom content type on your site and add some projects to your portfolio.
47
-
48
- Next, add the `[portfolio]` shortcode to a post or page. The shortcode will display projects in different ways, depending on how you use the optional attributes to customize the portfolio layout.
49
-
50
- **Attributes**
51
- * display_types: display Project Types. (true/false)
52
- * display_tags: display Project Tags. (true/false)
53
- * display_content: display project content. (true/false)
54
- * include_type: display specific Project Types. Defaults to all. (comma-separated list of Project Type slugs)
55
- * include_tag: display specific Project Tags. Defaults to all. (comma-separated list of Project Tag slugs)
56
- * columns: number of columns in shortcode. Defaults to 2. (number, 1-6)
57
- * showposts: number of projects to display. Defaults to all. (number)
58
- * order: display projects in ascending or descending order. Defaults to ASC for sorting in ascending order, but you can reverse the order by using DESC to display projects in descending order instead. (ASC/DESC)
59
- * orderby: sort projects by different criteria, including author name, project title, and even rand to display in a random order. Defaults to sorting by date. (author, date, title, rand)
60
-
61
- **Example**
62
- `
63
- [portfolio display_types="true" display_tags="false" include_type="grooming-tips,best-kitties" columns="2" showposts="10" orderby="title"]
64
- `
65
- The example will display up to ten portfolio projects in two columns, in ascending alphabetical order by project title. It will display Project Types, hide Project Tags, and only display projects that are assigned the “Grooming Tips” or “Best Kitties” Project Types.
66
-
67
-
68
- ***Featured Content Shortcode***
69
-
70
- You can use shortcodes to embed featured content on posts and pages.
71
-
72
- **Embedding Featured Content Projects**
73
-
74
- To embed featured content on posts and pages, first activate the Featured Content content type on your site and add some projects to your featured content.
75
-
76
- Next, add the `[featured_content]` shortcode to a post or page. The shortcode will display projects in different ways, depending on how you use the optional attributes to customize the featured content layout.
77
-
78
- **Attributes**
79
- * display_types: display Content Types. (true/false)
80
- * display_tags: display Content Tags. (true/false)
81
- * display_content: display project content. (true/false)
82
- * include_type: display specific Content Types. Defaults to all. (comma-separated list of Content Type slugs)
83
- * include_tag: display specific Content Tags. Defaults to all. (comma-separated list of Content Tag slugs)
84
- * columns: number of columns in shortcode. Defaults to 2. (number, 1-6)
85
- * showposts: number of projects to display. Defaults to all. (number)
86
- * order: display projects in ascending or descending order. Defaults to ASC for sorting in ascending order, but you can reverse the order by using DESC to display projects in descending order instead. (ASC/DESC)
87
- * orderby: sort projects by different criteria, including author name, project title, and even rand to display in a random order. Defaults to sorting by date. (author, date, title, rand)
88
-
89
- **Example**
90
- `
91
- [featured_content display_types="true" display_tags="false" include_type="grooming-tips,best-kitties" columns="2" showposts="10" orderby="title"]
92
- `
93
- The example will display up to ten featured content in two columns, in ascending alphabetical order by project title. It will display Content Types, hide Content Tags, and only display projects that are assigned the “Grooming Tips” or “Best Kitties” Content Types.
94
-
95
- ***Testimonials Shortcode***
96
-
97
- You can use shortcodes to embed testimonials on posts and pages.
98
- Embedding Testimonials
99
-
100
- To embed testimonials on posts and pages, first activate the Testimonial custom content type on your site and add some testimonials.
101
-
102
- Next, add the `[testimonials]` shortcode to a post or page. The shortcode will display testimonials in different ways, depending on how you use the optional attributes to customize the testimonials layout.
103
-
104
- **Attributes**
105
- * display_content: display testimonial content. (full/true/false)
106
- * image: display the featured image. (true/false) Defaults to true.
107
- * columns: number of columns in shortcode. Defaults to 1. (number, 1-6)
108
- * showposts: number of testimonials to display. Defaults to all. (number)
109
- * order: display testimonials in ascending or descending chronological order. Defaults to ASC for sorting in ascending order, but you can reverse the order by using DESC to display testimonials in descending order instead. (ASC/DESC)
110
- * orderby: sort testimonials by different criteria, including author name, testimonial title, and even rand to display in a random order. Defaults to sorting by date. (author, date, title, rand)
111
-
112
- **Example**
113
- `
114
- [testimonials columns="2" showposts="10" orderby="title"]
115
- `
116
- The example will display up to ten testimonials in two columns, in ascending alphabetical order by testimonial title.
117
-
118
- ***Service Shortcode***
119
-
120
- You can use shortcodes to embed service on posts and pages.
121
-
122
- **Embedding Service Projects**
123
-
124
- To embed service on posts and pages, first activate the Service content type on your site and add some projects to your Service.
125
-
126
- Next, add the `[services]` shortcode to a post or page. The shortcode will display projects in different ways, depending on how you use the optional attributes to customize the featured content layout.
127
-
128
- **Attributes**
129
- * display_types: display Content Types. (true/false)
130
- * display_tags: display Content Tags. (true/false)
131
- * display_content: display project content. (true/false)
132
- * include_type: display specific Content Types. Defaults to all. (comma-separated list of Content Type slugs)
133
- * include_tag: display specific Content Tags. Defaults to all. (comma-separated list of Content Tag slugs)
134
- * columns: number of columns in shortcode. Defaults to 2. (number, 1-6)
135
- * showposts: number of projects to display. Defaults to all. (number)
136
- * order: display projects in ascending or descending order. Defaults to ASC for sorting in ascending order, but you can reverse the order by using DESC to display projects in descending order instead. (ASC/DESC)
137
- * orderby: sort projects by different criteria, including author name, project title, and even rand to display in a random order. Defaults to sorting by date. (author, date, title, rand)
138
-
139
- **Example**
140
- `
141
- [services display_types="true" display_tags="false" include_type="grooming-tips,best-kitties" columns="2" showposts="10" orderby="title"]
142
- `
143
- The example will display up to ten featured content in two columns, in ascending alphabetical order by project title. It will display Content Types, hide Content Tags, and only display projects that are assigned the “Grooming Tips” or “Best Kitties” Content Types.
144
-
145
- ***Food Menu Shortcode***
146
-
147
- You can use shortcodes to embed Food Menu on posts and pages.
148
- Embedding Food Menu
149
-
150
- **Embedding Food Menu**
151
-
152
- To embed food menus on posts and pages, first activate the Food Menu custom content type on your site and add some food menus.
153
-
154
- Next, add the `[food_menu]` shortcode to a post or page. The shortcode will display food menu in the selected post or page.
155
-
156
- **Attributes**
157
- * showposts: number of menu items to display. Defaults to all. (number)
158
- * include_type: display specific Content Types. Defaults to all. (comma-separated list of Content Type slugs)
159
- * include_tag: display specific Content Tags. Defaults to all. (comma-separated list of Content Tag slugs)
160
-
161
- **Example**
162
-
163
- `[food_menu showposts="10" include_type="pizza,burger,breakfast"]`
164
- The example will display up to ten menu items. It will only display menu items in “Pizza”, “Burger” or “Breakfast” sections.
165
-
166
- == Translations ==
167
-
168
- To translate the plugin, use translate.wordpress.org (GlotPress). You only need your WordPress.org account to join the collaborative translation project.
169
-
170
- You can translate Essential Widgets on [translate.wordpress.org](https://translate.wordpress.org/projects/wp-plugins/essential-content-types/).
171
-
172
- == Installation ==
173
-
174
- The easy way (via Dashboard) :
175
-
176
- * Go to Plugins > Add New
177
- * Type in the **Essential Content Types** in Search Plugins box
178
- * Click Install Now to install the plugin
179
- * After Installation click activate to start using the **Essential Content Types**
180
- * Go to **Essential Content Types** from Dashboard menu
181
- * Use Shortcodes in your posts/pages/templates
182
-
183
- Not so easy way (via FTP) :
184
-
185
- * Download the **Essential Content Types**
186
- * Unarchive **Essential Content Types** plugin
187
- * Copy folder with `essential-content-types.zip`
188
- * Open the ftp `\wp-content\plugins\`
189
- * Paste the plug-ins folder in the folder
190
- * Go to admin panel => open item "Plugins" => activate **Essential Content Types**
191
- * Go to **Essential Content Types** from Dashboard menu
192
- * Use Shortcodes in your posts/pages/templates
193
-
194
- == Screenshots ==
195
-
196
- 1. Main Dashboard
197
- 2. Customizer: Portfolio Archive Options
198
- 3. Customizer: Testimonial Archive Options
199
- 4. Customizer: Featured Content Archive Options
200
- 5. Customizer: Services Archive Options
201
-
202
- == Changelog ==
203
-
204
- = 1.8.5 (Released: December 14, 2020) =
205
- * Bug Fixed: ECT archive title messing with other NON-ECT archive title
206
-
207
- = 1.8.4 (Released: December 07, 2020) =
208
- * Bug Fixed: ECT archive title and description via customizer
209
- * Compatibility check up to version 5.6
210
-
211
- = 1.8.3 (Released: September 21, 2020) =
212
- * Bug Fixed: Check if thumbnail exists on templates
213
-
214
- = 1.8.2 (Released: Aug 19, 2020) =
215
- * Bug Fixed: Issue in add new theme page
216
-
217
- = 1.8.1 (Released: May 07, 2020) =
218
- * Added: French translation by Charles GIRARDIN
219
-
220
- = 1.8 (Released: March 17, 2020) =
221
- * Compatibility check up to version 5.4
222
-
223
- = 1.7 (Released: November 12, 2019) =
224
- * Compatibility check up to version 5.3
225
-
226
- = 1.6 (Released: August 20, 2019) =
227
- * Added: Option to turn off Catch Themes and Catch Plugins tabs
228
- * Compatibility check up to version 5.2
229
- * Updated: Adjust CPT archive and custom taxonomies to obey CPT reading setting
230
-
231
-
232
- = 1.5.1 (Released: April 09, 2019) =
233
- * Added: Excerpt support for CPT
234
- * Updated: Catch Themes and Catch Plugins tabs displaying code
235
-
236
- = 1.5 (Released: February 21, 2019) =
237
- * Compatibility check up to version 5.1
238
-
239
- = 1.4 (Released: December 12, 2018) =
240
- * Added: Catch Themes and Catch Plugins tabs in Add themes and Add plugins page respectively
241
- * Added: Themes by Catch Themes section under Themes panel in customizer
242
- * Bug Fixed: Undefined index when no section is selected for food items
243
- * Compatibility check up to version 5.0
244
- * Updated: How to use link
245
-
246
- = 1.3 (Released: July 05, 2018) =
247
- * Added: Default featured-image-size
248
- * Bug fixed: settings_page function found
249
- * Changed: function name changed to settings_page
250
- * Updated: Html structure
251
-
252
- = 1.2 (Released: May 07, 2018) =
253
- * Updated: Moved domain from catchthemes.com to catchplugins.com
254
- * Compatibility check up to version 4.9.5
255
-
256
- = 1.1 =
257
- * Removed: Unnecessary code hiding menu price in Food Menu CPT
258
-
259
- = 1.0.9 =
260
- * Added: CPT-Food Menu Items
261
- * Compatibility check up to version 4.9.4
262
- * Update: Moved all plugin customizer options to new panel Essential Content Types Plugin Options
263
-
264
- = 1.0.8 =
265
- * Compatibility check up to version 4.9
266
-
267
- = 1.0.7 =
268
- * Added: Action links in plugin page
269
- * Added: Restrict activation if Pro version is active
270
- * Updated: Plugin page and reivew links
271
-
272
- = 1.0.6 =
273
- * Added: Screenshots: Services Archive Option
274
- * Bug Fixed: Services Archive option in Customizer
275
- * Bug Fixed: Link to Services Archive option in Dashboard
276
-
277
- = 1.0.5 =
278
- * Added: Custom Post Type: Service
279
- * Added: Position in Testimonials
280
-
281
- = 1.0.3 & 1.0.4 =
282
- * Shortcode instruction link added
283
-
284
- = 1.0.2 =
285
- * Checked: Version compatibility WordPress 4.8
286
- * Renamed Featured Content to ECT: Featured Content
287
-
288
- = 1.0.1 =
289
- * Bug Fixed: Featured Content compatibility with themes with Jetpack: Featured Content Support
290
- * Bug Fixed: Admin CSS
291
-
292
- = 1.0.0 =
293
- * Initial Release
 
 
 
 
1
+ === Essential Content Types ===
2
+ Contributors: catchplugins, catchthemes, sakinshrestha, pratikshrestha, maheshmaharjan, dreamsapana
3
+ Donate link: https://catchplugins.com/plugins/essential-content-types-pro/
4
+ Tags: custom post types, CPT, CMS, post, types, post type, taxonomy, tax, custom, content types, post types, custom content types, testimonial, portfolio, featured content, service
5
+ Requires at least: 4.8
6
+ Tested up to: 5.8
7
+ Stable tag: trunk
8
+ License: GPLv3 or later
9
+ License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
+
11
+ Essential Content Types allows you to feature the impressive content through different content/post types on your website just the way you want it. These content/post types are missed by the themes in WordPress Theme Directory as the feature falls more towards the plugins’ territory.
12
+
13
+ == Description ==
14
+
15
+ **Essential Content Types** allows you to feature the impressive content through different content/post types on your website just the way you want it. These content/post types are missed by the themes in WordPress Theme Directory as the feature falls more towards the plugins' territory.
16
+
17
+ Content is at the forefront of any website. Changing the layout of your important content changes the way your website looks, which may not be a plus point if your current website layout is loved by your users.
18
+
19
+ Additionally, switching themes changes your website layout completely. Therefore, to keep things looking spic-and- span in your website, we bring you Essential Content.
20
+
21
+ Essential Content allows you to add up to three content/post types:
22
+ * Portfolio – Create and display your portfolio on your website
23
+ * Testimonials – Add customer testimonials to your website
24
+ * Featured Content – Display the content you want as featured content on your website to attract visitors' attention
25
+ * Services – Add your services on your website
26
+
27
+ Features of Essential Content:
28
+ * Enable/Disable any content/post type as needed
29
+ * Light-weight
30
+ * Supports all themes on WordPress
31
+
32
+ Essential Content is inspired by *Jetpack's Custom Content Types feature*.
33
+
34
+ However, not everyone wants to have a plugin that “does-it- all”. Some may want plugins to be niche focused and concentrate on smaller areas.
35
+
36
+ That is precisely what we have done with Essential Content. Essential content, true to its name, has only the essential elements. We have added the features that WordPressers use most. We have ruled out all other elements to make it non-bloated and clean. It takes up lesser space and does the job well.
37
+
38
+ If you think we have missed any essential content/post types, please let us know. We’ll review the frequency of usage and add your suggestions.
39
+
40
+ ***Portfolio Shortcode***
41
+
42
+ You can use shortcodes to embed portfolio projects on posts and pages.
43
+
44
+ **Embedding Portfolio Projects**
45
+
46
+ To embed portfolio projects on posts and pages, first activate the Portfolio custom content type on your site and add some projects to your portfolio.
47
+
48
+ Next, add the `[portfolio]` shortcode to a post or page. The shortcode will display projects in different ways, depending on how you use the optional attributes to customize the portfolio layout.
49
+
50
+ **Attributes**
51
+ * display_types: display Project Types. (true/false)
52
+ * display_tags: display Project Tags. (true/false)
53
+ * display_content: display project content. (true/false)
54
+ * include_type: display specific Project Types. Defaults to all. (comma-separated list of Project Type slugs)
55
+ * include_tag: display specific Project Tags. Defaults to all. (comma-separated list of Project Tag slugs)
56
+ * columns: number of columns in shortcode. Defaults to 2. (number, 1-6)
57
+ * showposts: number of projects to display. Defaults to all. (number)
58
+ * order: display projects in ascending or descending order. Defaults to ASC for sorting in ascending order, but you can reverse the order by using DESC to display projects in descending order instead. (ASC/DESC)
59
+ * orderby: sort projects by different criteria, including author name, project title, and even rand to display in a random order. Defaults to sorting by date. (author, date, title, rand)
60
+
61
+ **Example**
62
+ `
63
+ [portfolio display_types="true" display_tags="false" include_type="grooming-tips,best-kitties" columns="2" showposts="10" orderby="title"]
64
+ `
65
+ The example will display up to ten portfolio projects in two columns, in ascending alphabetical order by project title. It will display Project Types, hide Project Tags, and only display projects that are assigned the “Grooming Tips” or “Best Kitties” Project Types.
66
+
67
+
68
+ ***Featured Content Shortcode***
69
+
70
+ You can use shortcodes to embed featured content on posts and pages.
71
+
72
+ **Embedding Featured Content Projects**
73
+
74
+ To embed featured content on posts and pages, first activate the Featured Content content type on your site and add some projects to your featured content.
75
+
76
+ Next, add the `[featured_content]` shortcode to a post or page. The shortcode will display projects in different ways, depending on how you use the optional attributes to customize the featured content layout.
77
+
78
+ **Attributes**
79
+ * display_types: display Content Types. (true/false)
80
+ * display_tags: display Content Tags. (true/false)
81
+ * display_content: display project content. (true/false)
82
+ * include_type: display specific Content Types. Defaults to all. (comma-separated list of Content Type slugs)
83
+ * include_tag: display specific Content Tags. Defaults to all. (comma-separated list of Content Tag slugs)
84
+ * columns: number of columns in shortcode. Defaults to 2. (number, 1-6)
85
+ * showposts: number of projects to display. Defaults to all. (number)
86
+ * order: display projects in ascending or descending order. Defaults to ASC for sorting in ascending order, but you can reverse the order by using DESC to display projects in descending order instead. (ASC/DESC)
87
+ * orderby: sort projects by different criteria, including author name, project title, and even rand to display in a random order. Defaults to sorting by date. (author, date, title, rand)
88
+
89
+ **Example**
90
+ `
91
+ [featured_content display_types="true" display_tags="false" include_type="grooming-tips,best-kitties" columns="2" showposts="10" orderby="title"]
92
+ `
93
+ The example will display up to ten featured content in two columns, in ascending alphabetical order by project title. It will display Content Types, hide Content Tags, and only display projects that are assigned the “Grooming Tips” or “Best Kitties” Content Types.
94
+
95
+ ***Testimonials Shortcode***
96
+
97
+ You can use shortcodes to embed testimonials on posts and pages.
98
+ Embedding Testimonials
99
+
100
+ To embed testimonials on posts and pages, first activate the Testimonial custom content type on your site and add some testimonials.
101
+
102
+ Next, add the `[testimonials]` shortcode to a post or page. The shortcode will display testimonials in different ways, depending on how you use the optional attributes to customize the testimonials layout.
103
+
104
+ **Attributes**
105
+ * display_content: display testimonial content. (full/true/false)
106
+ * image: display the featured image. (true/false) Defaults to true.
107
+ * columns: number of columns in shortcode. Defaults to 1. (number, 1-6)
108
+ * showposts: number of testimonials to display. Defaults to all. (number)
109
+ * order: display testimonials in ascending or descending chronological order. Defaults to ASC for sorting in ascending order, but you can reverse the order by using DESC to display testimonials in descending order instead. (ASC/DESC)
110
+ * orderby: sort testimonials by different criteria, including author name, testimonial title, and even rand to display in a random order. Defaults to sorting by date. (author, date, title, rand)
111
+
112
+ **Example**
113
+ `
114
+ [testimonials columns="2" showposts="10" orderby="title"]
115
+ `
116
+ The example will display up to ten testimonials in two columns, in ascending alphabetical order by testimonial title.
117
+
118
+ ***Service Shortcode***
119
+
120
+ You can use shortcodes to embed service on posts and pages.
121
+
122
+ **Embedding Service Projects**
123
+
124
+ To embed service on posts and pages, first activate the Service content type on your site and add some projects to your Service.
125
+
126
+ Next, add the `[services]` shortcode to a post or page. The shortcode will display projects in different ways, depending on how you use the optional attributes to customize the featured content layout.
127
+
128
+ **Attributes**
129
+ * display_types: display Content Types. (true/false)
130
+ * display_tags: display Content Tags. (true/false)
131
+ * display_content: display project content. (true/false)
132
+ * include_type: display specific Content Types. Defaults to all. (comma-separated list of Content Type slugs)
133
+ * include_tag: display specific Content Tags. Defaults to all. (comma-separated list of Content Tag slugs)
134
+ * columns: number of columns in shortcode. Defaults to 2. (number, 1-6)
135
+ * showposts: number of projects to display. Defaults to all. (number)
136
+ * order: display projects in ascending or descending order. Defaults to ASC for sorting in ascending order, but you can reverse the order by using DESC to display projects in descending order instead. (ASC/DESC)
137
+ * orderby: sort projects by different criteria, including author name, project title, and even rand to display in a random order. Defaults to sorting by date. (author, date, title, rand)
138
+
139
+ **Example**
140
+ `
141
+ [services display_types="true" display_tags="false" include_type="grooming-tips,best-kitties" columns="2" showposts="10" orderby="title"]
142
+ `
143
+ The example will display up to ten featured content in two columns, in ascending alphabetical order by project title. It will display Content Types, hide Content Tags, and only display projects that are assigned the “Grooming Tips” or “Best Kitties” Content Types.
144
+
145
+ ***Food Menu Shortcode***
146
+
147
+ You can use shortcodes to embed Food Menu on posts and pages.
148
+ Embedding Food Menu
149
+
150
+ **Embedding Food Menu**
151
+
152
+ To embed food menus on posts and pages, first activate the Food Menu custom content type on your site and add some food menus.
153
+
154
+ Next, add the `[food_menu]` shortcode to a post or page. The shortcode will display food menu in the selected post or page.
155
+
156
+ **Attributes**
157
+ * showposts: number of menu items to display. Defaults to all. (number)
158
+ * include_type: display specific Content Types. Defaults to all. (comma-separated list of Content Type slugs)
159
+ * include_tag: display specific Content Tags. Defaults to all. (comma-separated list of Content Tag slugs)
160
+
161
+ **Example**
162
+
163
+ `[food_menu showposts="10" include_type="pizza,burger,breakfast"]`
164
+ The example will display up to ten menu items. It will only display menu items in “Pizza”, “Burger” or “Breakfast” sections.
165
+
166
+ == Translations ==
167
+
168
+ To translate the plugin, use translate.wordpress.org (GlotPress). You only need your WordPress.org account to join the collaborative translation project.
169
+
170
+ You can translate Essential Widgets on [translate.wordpress.org](https://translate.wordpress.org/projects/wp-plugins/essential-content-types/).
171
+
172
+ == Installation ==
173
+
174
+ The easy way (via Dashboard) :
175
+
176
+ * Go to Plugins > Add New
177
+ * Type in the **Essential Content Types** in Search Plugins box
178
+ * Click Install Now to install the plugin
179
+ * After Installation click activate to start using the **Essential Content Types**
180
+ * Go to **Essential Content Types** from Dashboard menu
181
+ * Use Shortcodes in your posts/pages/templates
182
+
183
+ Not so easy way (via FTP) :
184
+
185
+ * Download the **Essential Content Types**
186
+ * Unarchive **Essential Content Types** plugin
187
+ * Copy folder with `essential-content-types.zip`
188
+ * Open the ftp `\wp-content\plugins\`
189
+ * Paste the plug-ins folder in the folder
190
+ * Go to admin panel => open item "Plugins" => activate **Essential Content Types**
191
+ * Go to **Essential Content Types** from Dashboard menu
192
+ * Use Shortcodes in your posts/pages/templates
193
+
194
+ == Screenshots ==
195
+
196
+ 1. Main Dashboard
197
+ 2. Customizer: Portfolio Archive Options
198
+ 3. Customizer: Testimonial Archive Options
199
+ 4. Customizer: Featured Content Archive Options
200
+ 5. Customizer: Services Archive Options
201
+
202
+ == Changelog ==
203
+
204
+ = 1.8.6 (Released: August 05, 2021) =
205
+ * Bug Fixed: Deprecated - Required parameter $atts follows optional parameter
206
+ * Compatibility check up to version 5.8
207
+
208
+ = 1.8.5 (Released: December 14, 2020) =
209
+ * Bug Fixed: ECT archive title messing with other NON-ECT archive title
210
+
211
+ = 1.8.4 (Released: December 07, 2020) =
212
+ * Bug Fixed: ECT archive title and description via customizer
213
+ * Compatibility check up to version 5.6
214
+
215
+ = 1.8.3 (Released: September 21, 2020) =
216
+ * Bug Fixed: Check if thumbnail exists on templates
217
+
218
+ = 1.8.2 (Released: Aug 19, 2020) =
219
+ * Bug Fixed: Issue in add new theme page
220
+
221
+ = 1.8.1 (Released: May 07, 2020) =
222
+ * Added: French translation by Charles GIRARDIN
223
+
224
+ = 1.8 (Released: March 17, 2020) =
225
+ * Compatibility check up to version 5.4
226
+
227
+ = 1.7 (Released: November 12, 2019) =
228
+ * Compatibility check up to version 5.3
229
+
230
+ = 1.6 (Released: August 20, 2019) =
231
+ * Added: Option to turn off Catch Themes and Catch Plugins tabs
232
+ * Compatibility check up to version 5.2
233
+ * Updated: Adjust CPT archive and custom taxonomies to obey CPT reading setting
234
+
235
+
236
+ = 1.5.1 (Released: April 09, 2019) =
237
+ * Added: Excerpt support for CPT
238
+ * Updated: Catch Themes and Catch Plugins tabs displaying code
239
+
240
+ = 1.5 (Released: February 21, 2019) =
241
+ * Compatibility check up to version 5.1
242
+
243
+ = 1.4 (Released: December 12, 2018) =
244
+ * Added: Catch Themes and Catch Plugins tabs in Add themes and Add plugins page respectively
245
+ * Added: Themes by Catch Themes section under Themes panel in customizer
246
+ * Bug Fixed: Undefined index when no section is selected for food items
247
+ * Compatibility check up to version 5.0
248
+ * Updated: How to use link
249
+
250
+ = 1.3 (Released: July 05, 2018) =
251
+ * Added: Default featured-image-size
252
+ * Bug fixed: settings_page function found
253
+ * Changed: function name changed to settings_page
254
+ * Updated: Html structure
255
+
256
+ = 1.2 (Released: May 07, 2018) =
257
+ * Updated: Moved domain from catchthemes.com to catchplugins.com
258
+ * Compatibility check up to version 4.9.5
259
+
260
+ = 1.1 =
261
+ * Removed: Unnecessary code hiding menu price in Food Menu CPT
262
+
263
+ = 1.0.9 =
264
+ * Added: CPT-Food Menu Items
265
+ * Compatibility check up to version 4.9.4
266
+ * Update: Moved all plugin customizer options to new panel Essential Content Types Plugin Options
267
+
268
+ = 1.0.8 =
269
+ * Compatibility check up to version 4.9
270
+
271
+ = 1.0.7 =
272
+ * Added: Action links in plugin page
273
+ * Added: Restrict activation if Pro version is active
274
+ * Updated: Plugin page and reivew links
275
+
276
+ = 1.0.6 =
277
+ * Added: Screenshots: Services Archive Option
278
+ * Bug Fixed: Services Archive option in Customizer
279
+ * Bug Fixed: Link to Services Archive option in Dashboard
280
+
281
+ = 1.0.5 =
282
+ * Added: Custom Post Type: Service
283
+ * Added: Position in Testimonials
284
+
285
+ = 1.0.3 & 1.0.4 =
286
+ * Shortcode instruction link added
287
+
288
+ = 1.0.2 =
289
+ * Checked: Version compatibility WordPress 4.8
290
+ * Renamed Featured Content to ECT: Featured Content
291
+
292
+ = 1.0.1 =
293
+ * Bug Fixed: Featured Content compatibility with themes with Jetpack: Featured Content Support
294
+ * Bug Fixed: Admin CSS
295
+
296
+ = 1.0.0 =
297
+ * Initial Release
admin/class-essential-content-types-admin.php CHANGED
@@ -1,259 +1,259 @@
1
- <?php
2
-
3
- /**
4
- * The admin-specific functionality of the plugin.
5
- *
6
- * @link https://catchplugins.com
7
- * @since 1.0.0
8
- *
9
- * @package Essential_Content_Types
10
- * @subpackage Essential_Content_Types/admin
11
- */
12
-
13
- /**
14
- * The admin-specific functionality of the plugin.
15
- *
16
- * Defines the plugin name, version, and two examples hooks for how to
17
- * enqueue the admin-specific stylesheet and JavaScript.
18
- *
19
- * @package Essential_Content_Types
20
- * @subpackage Essential_Content_Types/admin
21
- * @author Catch Plugins <info@catchplugins.com>
22
- */
23
- class Essential_Content_Types_Admin {
24
-
25
- /**
26
- * The ID of this plugin.
27
- *
28
- * @since 1.0.0
29
- * @access private
30
- * @var string $plugin_name The ID of this plugin.
31
- */
32
- private $plugin_name;
33
-
34
- /**
35
- * The version of this plugin.
36
- *
37
- * @since 1.0.0
38
- * @access private
39
- * @var string $version The current version of this plugin.
40
- */
41
- private $version;
42
-
43
- /**
44
- * Initialize the class and set its properties.
45
- *
46
- * @since 1.0.0
47
- * @param string $plugin_name The name of this plugin.
48
- * @param string $version The version of this plugin.
49
- */
50
- public function __construct( $plugin_name, $version ) {
51
-
52
- $this->plugin_name = $plugin_name;
53
- $this->version = $version;
54
-
55
- $this->load_dependencies();
56
-
57
- }
58
-
59
- /**
60
- * Load the required dependencies for this plugin.
61
- *
62
- * Include the following files that make up the plugin:
63
- *
64
- * - Essential_Content_Types_Loader. Orchestrates the hooks of the plugin.
65
- * - Essential_Content_Types_i18n. Defines internationalization functionality.
66
- * - Essential_Content_Types_Admin. Defines all hooks for the admin area.
67
- * - Essential_Content_Types_Public. Defines all hooks for the public side of the site.
68
- *
69
- * Create an instance of the loader which will be used to register the hooks
70
- * with WordPress.
71
- *
72
- * @since 1.0.0
73
- * @access private
74
- */
75
- private function load_dependencies() {
76
-
77
- $portfolio_options = get_option( 'ect_portfolio' );
78
- if ( isset($portfolio_options['status']) && $portfolio_options['status'] ) {
79
- /**
80
- * Load Portfolio Content Type
81
- */
82
- require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-portfolio.php';
83
- }
84
-
85
- $testimonial_options = get_option( 'ect_testimonial' );
86
- if ( isset($testimonial_options['status']) && $testimonial_options['status'] ) {
87
- /**
88
- * Load Testimonial Content Type
89
- */
90
- require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-testimonial.php';
91
- }
92
-
93
- $featured_content_options = get_option( 'ect_featured_content' );
94
- if ( isset($featured_content_options['status']) && $featured_content_options['status'] ) {
95
- /**
96
- * Load Featured Content Type
97
- */
98
- require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-featured-content.php';
99
- }
100
-
101
- $service_options = get_option( 'ect_service' );
102
- if ( isset($service_options['status']) && $service_options['status'] ) {
103
- /**
104
- * Load Service Type
105
- */
106
- require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-service.php';
107
- }
108
-
109
- $food_menu_options = get_option( 'ect_food_menu' );
110
- if ( isset($food_menu_options['status']) && $food_menu_options['status'] ) {
111
- /**
112
- * Load Food Menu Type
113
- */
114
- require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-food-menu.php';
115
- }
116
-
117
- }
118
-
119
-
120
- /**
121
- * Register the stylesheets for the admin area.
122
- *
123
- * @since 1.0.0
124
- */
125
- public function enqueue_styles( $hook_suffix ) {
126
- $allowed_pages = array(
127
- 'toplevel_page_essential-content-types' => 1,
128
- 'essential-content-types_page_ect-portfolio' => 1,
129
- 'essential-content-types_page_ect-testimonial' => 1,
130
- 'essential-content-types_page_ect-featured-content' => 1,
131
- 'essential-content-types_page_ect-service' => 1,
132
- 'essential-content-types_page_ect-food-menu' => 1,
133
- );
134
-
135
- if ( ! isset( $allowed_pages[ $hook_suffix ] ) ){
136
- return;
137
- }
138
-
139
- wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/essential-content-types-admin.css', array(), $this->version, 'all' );
140
-
141
- wp_enqueue_style( $this->plugin_name.'-tabs', plugin_dir_url( __FILE__ ) . 'css/admin-dashboard.css', array(), $this->version, 'all' );
142
-
143
- }
144
-
145
- /**
146
- * Register the JavaScript for the admin area.
147
- *
148
- * @since 1.0.0
149
- */
150
- public function enqueue_scripts( $hook_suffix ) {
151
- $allowed_pages = array(
152
- 'toplevel_page_essential-content-types' => 1,
153
- 'essential-content-types_page_ect-portfolio' => 1,
154
- 'essential-content-types_page_ect-testimonial' => 1,
155
- 'essential-content-types_page_ect-featured-content' => 1,
156
- 'essential-content-types_page_ect-service' => 1,
157
- 'essential-content-types_page_ect-food-menu' => 1,
158
- );
159
-
160
- if ( ! isset( $allowed_pages[ $hook_suffix ] ) ){
161
- return;
162
- }
163
-
164
- wp_enqueue_script( 'minHeight', plugin_dir_url( __FILE__ ) . 'js/jquery.matchHeight.min.js', array( 'jquery' ), $this->version, false );
165
- wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/essential-content-types-admin.js', array( 'minHeight', 'jquery' ), $this->version, false );
166
-
167
-
168
- }
169
-
170
- /**
171
- * Essential Content Types: action_links
172
- * Essential Content Types Settings Link function callback
173
- *
174
- * @param arrray $links Link url.
175
- *
176
- * @param arrray $file File name.
177
- */
178
- public function action_links( $links, $file ) {
179
- if ( $file === $this->plugin_name . '/' . $this->plugin_name . '.php' ) {
180
- $settings_link = '<a href="' . esc_url( admin_url( 'admin.php?page=essential-content-types' ) ) . '">' . esc_html__( 'Settings', 'essential-content-types' ) . '</a>';
181
-
182
- array_unshift( $links, $settings_link );
183
- }
184
- return $links;
185
- }
186
-
187
- /**
188
- * Add settings menu
189
- */
190
- function settings_menu() {
191
- // Add Main Menu
192
- add_menu_page(
193
- esc_html__( 'Essential Content Types', 'essential-content-types' ), //$page_title
194
- esc_html__( 'Essential Content Types', 'essential-content-types' ), //$menu_title
195
- 'manage_options', //$capability
196
- 'essential-content-types', //$menu_slug
197
- array( $this, 'settings_page' ), //$function
198
- 'dashicons-layout', //$icon_url
199
- '99.01564' //$position
200
- );
201
- }
202
-
203
- /**
204
- * Dashboard Page include
205
- */
206
- function settings_page() {
207
- if ( !current_user_can( 'manage_options' ) ) {
208
- wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
209
- }
210
-
211
- //require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/partials/dashboard-display.php';
212
- require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/partials/essential-content-types-admin-display.php';
213
- }
214
-
215
- function dashboard_switch() {
216
- $value = ( 'true' == $_POST['value'] ) ? 1 : 0;
217
-
218
- $option_name = $_POST['option_name'];
219
-
220
- $option_value = get_option( $option_name );
221
-
222
- $option_value['status'] = $value;
223
-
224
- if( update_option( $option_name, $option_value ) ) {
225
- echo $value;
226
- } else {
227
- esc_html_e( 'Connection Error. Please try again.', 'essential-content-types' );
228
- }
229
-
230
- wp_die(); // this is required to terminate immediately and return a proper response
231
- }
232
- function add_plugin_meta_links( $meta_fields, $file ){
233
-
234
- if( ESSENTIAL_CONTENT_TYPES_BASENAME == $file ) {
235
-
236
- $meta_fields[] = "<a href='https://catchplugins.com/support-forum/forum/essential-content-type/' target='_blank'>Support Forum</a>";
237
- $meta_fields[] = "<a href='https://wordpress.org/support/plugin/essential-content-types/#reviews' target='_blank' title='Rate'>
238
- <i class='ct-rate-stars'>"
239
- . "<svg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-star'><polygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2'/></svg>"
240
- . "<svg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-star'><polygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2'/></svg>"
241
- . "<svg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-star'><polygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2'/></svg>"
242
- . "<svg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-star'><polygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2'/></svg>"
243
- . "<svg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-star'><polygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2'/></svg>"
244
- . "</i></a>";
245
-
246
- $stars_color = "#ffb900";
247
-
248
- echo "<style>"
249
- . ".ct-rate-stars{display:inline-block;color:" . $stars_color . ";position:relative;top:3px;}"
250
- . ".ct-rate-stars svg{fill:" . $stars_color . ";}"
251
- . ".ct-rate-stars svg:hover{fill:" . $stars_color . "}"
252
- . ".ct-rate-stars svg:hover ~ svg{fill:none;}"
253
- . "</style>";
254
- }
255
-
256
- return $meta_fields;
257
- }
258
-
259
- }
1
+ <?php
2
+
3
+ /**
4
+ * The admin-specific functionality of the plugin.
5
+ *
6
+ * @link https://catchplugins.com
7
+ * @since 1.0.0
8
+ *
9
+ * @package Essential_Content_Types
10
+ * @subpackage Essential_Content_Types/admin
11
+ */
12
+
13
+ /**
14
+ * The admin-specific functionality of the plugin.
15
+ *
16
+ * Defines the plugin name, version, and two examples hooks for how to
17
+ * enqueue the admin-specific stylesheet and JavaScript.
18
+ *
19
+ * @package Essential_Content_Types
20
+ * @subpackage Essential_Content_Types/admin
21
+ * @author Catch Plugins <info@catchplugins.com>
22
+ */
23
+ class Essential_Content_Types_Admin {
24
+
25
+ /**
26
+ * The ID of this plugin.
27
+ *
28
+ * @since 1.0.0
29
+ * @access private
30
+ * @var string $plugin_name The ID of this plugin.
31
+ */
32
+ private $plugin_name;
33
+
34
+ /**
35
+ * The version of this plugin.
36
+ *
37
+ * @since 1.0.0
38
+ * @access private
39
+ * @var string $version The current version of this plugin.
40
+ */
41
+ private $version;
42
+
43
+ /**
44
+ * Initialize the class and set its properties.
45
+ *
46
+ * @since 1.0.0
47
+ * @param string $plugin_name The name of this plugin.
48
+ * @param string $version The version of this plugin.
49
+ */
50
+ public function __construct( $plugin_name, $version ) {
51
+
52
+ $this->plugin_name = $plugin_name;
53
+ $this->version = $version;
54
+
55
+ $this->load_dependencies();
56
+
57
+ }
58
+
59
+ /**
60
+ * Load the required dependencies for this plugin.
61
+ *
62
+ * Include the following files that make up the plugin:
63
+ *
64
+ * - Essential_Content_Types_Loader. Orchestrates the hooks of the plugin.
65
+ * - Essential_Content_Types_i18n. Defines internationalization functionality.
66
+ * - Essential_Content_Types_Admin. Defines all hooks for the admin area.
67
+ * - Essential_Content_Types_Public. Defines all hooks for the public side of the site.
68
+ *
69
+ * Create an instance of the loader which will be used to register the hooks
70
+ * with WordPress.
71
+ *
72
+ * @since 1.0.0
73
+ * @access private
74
+ */
75
+ private function load_dependencies() {
76
+
77
+ $portfolio_options = get_option( 'ect_portfolio' );
78
+ if ( isset($portfolio_options['status']) && $portfolio_options['status'] ) {
79
+ /**
80
+ * Load Portfolio Content Type
81
+ */
82
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-portfolio.php';
83
+ }
84
+
85
+ $testimonial_options = get_option( 'ect_testimonial' );
86
+ if ( isset($testimonial_options['status']) && $testimonial_options['status'] ) {
87
+ /**
88
+ * Load Testimonial Content Type
89
+ */
90
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-testimonial.php';
91
+ }
92
+
93
+ $featured_content_options = get_option( 'ect_featured_content' );
94
+ if ( isset($featured_content_options['status']) && $featured_content_options['status'] ) {
95
+ /**
96
+ * Load Featured Content Type
97
+ */
98
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-featured-content.php';
99
+ }
100
+
101
+ $service_options = get_option( 'ect_service' );
102
+ if ( isset($service_options['status']) && $service_options['status'] ) {
103
+ /**
104
+ * Load Service Type
105
+ */
106
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-service.php';
107
+ }
108
+
109
+ $food_menu_options = get_option( 'ect_food_menu' );
110
+ if ( isset($food_menu_options['status']) && $food_menu_options['status'] ) {
111
+ /**
112
+ * Load Food Menu Type
113
+ */
114
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-food-menu.php';
115
+ }
116
+
117
+ }
118
+
119
+
120
+ /**
121
+ * Register the stylesheets for the admin area.
122
+ *
123
+ * @since 1.0.0
124
+ */
125
+ public function enqueue_styles( $hook_suffix ) {
126
+ $allowed_pages = array(
127
+ 'toplevel_page_essential-content-types' => 1,
128
+ 'essential-content-types_page_ect-portfolio' => 1,
129
+ 'essential-content-types_page_ect-testimonial' => 1,
130
+ 'essential-content-types_page_ect-featured-content' => 1,
131
+ 'essential-content-types_page_ect-service' => 1,
132
+ 'essential-content-types_page_ect-food-menu' => 1,
133
+ );
134
+
135
+ if ( ! isset( $allowed_pages[ $hook_suffix ] ) ){
136
+ return;
137
+ }
138
+
139
+ wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/essential-content-types-admin.css', array(), $this->version, 'all' );
140
+
141
+ wp_enqueue_style( $this->plugin_name.'-tabs', plugin_dir_url( __FILE__ ) . 'css/admin-dashboard.css', array(), $this->version, 'all' );
142
+
143
+ }
144
+
145
+ /**
146
+ * Register the JavaScript for the admin area.
147
+ *
148
+ * @since 1.0.0
149
+ */
150
+ public function enqueue_scripts( $hook_suffix ) {
151
+ $allowed_pages = array(
152
+ 'toplevel_page_essential-content-types' => 1,
153
+ 'essential-content-types_page_ect-portfolio' => 1,
154
+ 'essential-content-types_page_ect-testimonial' => 1,
155
+ 'essential-content-types_page_ect-featured-content' => 1,
156
+ 'essential-content-types_page_ect-service' => 1,
157
+ 'essential-content-types_page_ect-food-menu' => 1,
158
+ );
159
+
160
+ if ( ! isset( $allowed_pages[ $hook_suffix ] ) ){
161
+ return;
162
+ }
163
+
164
+ wp_enqueue_script( 'minHeight', plugin_dir_url( __FILE__ ) . 'js/jquery.matchHeight.min.js', array( 'jquery' ), $this->version, false );
165
+ wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/essential-content-types-admin.js', array( 'minHeight', 'jquery' ), $this->version, false );
166
+
167
+
168
+ }
169
+
170
+ /**
171
+ * Essential Content Types: action_links
172
+ * Essential Content Types Settings Link function callback
173
+ *
174
+ * @param arrray $links Link url.
175
+ *
176
+ * @param arrray $file File name.
177
+ */
178
+ public function action_links( $links, $file ) {
179
+ if ( $file === $this->plugin_name . '/' . $this->plugin_name . '.php' ) {
180
+ $settings_link = '<a href="' . esc_url( admin_url( 'admin.php?page=essential-content-types' ) ) . '">' . esc_html__( 'Settings', 'essential-content-types' ) . '</a>';
181
+
182
+ array_unshift( $links, $settings_link );
183
+ }
184
+ return $links;
185
+ }
186
+
187
+ /**
188
+ * Add settings menu
189
+ */
190
+ function settings_menu() {
191
+ // Add Main Menu
192
+ add_menu_page(
193
+ esc_html__( 'Essential Content Types', 'essential-content-types' ), //$page_title
194
+ esc_html__( 'Essential Content Types', 'essential-content-types' ), //$menu_title
195
+ 'manage_options', //$capability
196
+ 'essential-content-types', //$menu_slug
197
+ array( $this, 'settings_page' ), //$function
198
+ 'dashicons-layout', //$icon_url
199
+ '99.01564' //$position
200
+ );
201
+ }
202
+
203
+ /**
204
+ * Dashboard Page include
205
+ */
206
+ function settings_page() {
207
+ if ( !current_user_can( 'manage_options' ) ) {
208
+ wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
209
+ }
210
+
211
+ //require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/partials/dashboard-display.php';
212
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/partials/essential-content-types-admin-display.php';
213
+ }
214
+
215
+ function dashboard_switch() {
216
+ $value = ( 'true' == $_POST['value'] ) ? 1 : 0;
217
+
218
+ $option_name = $_POST['option_name'];
219
+
220
+ $option_value = get_option( $option_name );
221
+
222
+ $option_value['status'] = $value;
223
+
224
+ if( update_option( $option_name, $option_value ) ) {
225
+ echo $value;
226
+ } else {
227
+ esc_html_e( 'Connection Error. Please try again.', 'essential-content-types' );
228
+ }
229
+
230
+ wp_die(); // this is required to terminate immediately and return a proper response
231
+ }
232
+ function add_plugin_meta_links( $meta_fields, $file ){
233
+
234
+ if( ESSENTIAL_CONTENT_TYPES_BASENAME == $file ) {
235
+
236
+ $meta_fields[] = "<a href='https://catchplugins.com/support-forum/forum/essential-content-type/' target='_blank'>Support Forum</a>";
237
+ $meta_fields[] = "<a href='https://wordpress.org/support/plugin/essential-content-types/#reviews' target='_blank' title='Rate'>
238
+ <i class='ct-rate-stars'>"
239
+ . "<svg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-star'><polygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2'/></svg>"
240
+ . "<svg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-star'><polygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2'/></svg>"
241
+ . "<svg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-star'><polygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2'/></svg>"
242
+ . "<svg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-star'><polygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2'/></svg>"
243
+ . "<svg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-star'><polygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2'/></svg>"
244
+ . "</i></a>";
245
+
246
+ $stars_color = "#ffb900";
247
+
248
+ echo "<style>"
249
+ . ".ct-rate-stars{display:inline-block;color:" . $stars_color . ";position:relative;top:3px;}"
250
+ . ".ct-rate-stars svg{fill:" . $stars_color . ";}"
251
+ . ".ct-rate-stars svg:hover{fill:" . $stars_color . "}"
252
+ . ".ct-rate-stars svg:hover ~ svg{fill:none;}"
253
+ . "</style>";
254
+ }
255
+
256
+ return $meta_fields;
257
+ }
258
+
259
+ }
admin/class-featured-content.php CHANGED
@@ -1,784 +1,784 @@
1
- <?php
2
-
3
- /**
4
- * Support JetPack Featured Content
5
- */
6
- class Essential_Content_Featured_Content {
7
- const CUSTOM_POST_TYPE = 'featured-content';
8
- const CUSTOM_TAXONOMY_TYPE = 'featured-content-type';
9
- const CUSTOM_TAXONOMY_TAG = 'featured-content-tag';
10
- const OPTION_NAME = 'featured_content';
11
- const OPTION_READING_SETTING = 'featured_content_posts_per_page';
12
-
13
- public $version = ESSENTIAL_CONTENT_TYPES_VERSION;
14
-
15
- static function init() {
16
- static $instance = false;
17
-
18
- if ( ! $instance ) {
19
- $instance = new Essential_Content_Featured_Content;
20
- }
21
-
22
- return $instance;
23
- }
24
-
25
- /**
26
- * Conditionally hook into WordPress.
27
- *
28
- * Setup user option for enabling CPT
29
- * If user has CPT enabled, show in admin
30
- */
31
- function __construct() {
32
- // Make sure the post types are loaded for imports
33
- add_action( 'import_start', array( $this, 'register_post_types' ) );
34
-
35
- // Add to REST API post type whitelist
36
- add_filter( 'rest_api_allowed_post_types', array( $this, 'allow_featured_content_rest_api_type' ) );
37
-
38
- // CPT magic
39
- $this->register_post_types();
40
- add_action( sprintf( 'add_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 );
41
- add_action( sprintf( 'update_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 );
42
- add_action( sprintf( 'publish_%s', self::CUSTOM_POST_TYPE ), array( $this, 'flush_rules_on_first_content' ) );
43
- add_action( 'after_switch_theme', array( $this, 'flush_rules_on_switch' ) );
44
-
45
- // Admin Customization
46
- add_filter( 'post_updated_messages', array( $this, 'updated_messages' ) );
47
- add_filter( sprintf( 'manage_%s_posts_columns', self::CUSTOM_POST_TYPE ), array( $this, 'edit_admin_columns' ) );
48
- add_filter( sprintf( 'manage_%s_posts_custom_column', self::CUSTOM_POST_TYPE ), array( $this, 'image_column' ), 10, 2 );
49
- add_action( 'customize_register', array( $this, 'customize_register' ) );
50
-
51
- add_image_size( 'featured-content-admin-thumb', 50, 50, true );
52
- add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
53
-
54
- // register featured_content shortcode and featured_content shortcode (legacy)
55
- add_shortcode( 'featured_content', array( $this, 'featured_content_shortcode' ) );
56
- add_shortcode( 'ect_featured_content', array( $this, 'featured_content_shortcode' ) );
57
-
58
- // Adjust CPT archive and custom taxonomies to obey CPT reading setting
59
- add_filter( 'pre_get_posts', array( $this, 'query_reading_setting' ) );
60
- }
61
-
62
- /*
63
- * Flush permalinks when CPT option is turned on/off
64
- */
65
- function flush_rules_on_enable() {
66
- flush_rewrite_rules();
67
- }
68
-
69
- /*
70
- * Count published contents and flush permalinks when first contents is published
71
- */
72
- function flush_rules_on_first_content() {
73
- $contents = get_transient( 'featured-content-count-cache' );
74
-
75
- if ( false === $contents ) {
76
- flush_rewrite_rules();
77
- $contents = (int) wp_count_posts( self::CUSTOM_POST_TYPE )->publish;
78
-
79
- if ( ! empty( $contents ) ) {
80
- set_transient( 'featured-content-count-cache', $contents, HOUR_IN_SECONDS * 12 );
81
- }
82
- }
83
- }
84
-
85
- /*
86
- * Flush permalinks when CPT supported theme is activated
87
- */
88
- function flush_rules_on_switch() {
89
- flush_rewrite_rules();
90
- }
91
-
92
- /**
93
- * Register Post Type
94
- */
95
- function register_post_types() {
96
- if ( post_type_exists( self::CUSTOM_POST_TYPE ) ) {
97
- return;
98
- }
99
-
100
- $args = array(
101
- 'description' => esc_html__( 'Featured Content Items', 'essential-content-types' ),
102
- 'labels' => array(
103
- 'name' => esc_html__( 'Featured Contents', 'essential-content-types' ),
104
- 'singular_name' => esc_html__( 'Featured Content', 'essential-content-types' ),
105
- 'menu_name' => esc_html__( 'Featured Content', 'essential-content-types' ),
106
- 'all_items' => esc_html__( 'All Contents', 'essential-content-types' ),
107
- 'add_new' => esc_html__( 'Add New', 'essential-content-types' ),
108
- 'add_new_item' => esc_html__( 'Add New Content', 'essential-content-types' ),
109
- 'edit_item' => esc_html__( 'Edit Content', 'essential-content-types' ),
110
- 'new_item' => esc_html__( 'New Content', 'essential-content-types' ),
111
- 'view_item' => esc_html__( 'View Content', 'essential-content-types' ),
112
- 'search_items' => esc_html__( 'Search Contents', 'essential-content-types' ),
113
- 'not_found' => esc_html__( 'No Contents found', 'essential-content-types' ),
114
- 'not_found_in_trash' => esc_html__( 'No Contents found in Trash', 'essential-content-types' ),
115
- 'filter_items_list' => esc_html__( 'Filter contents list', 'essential-content-types' ),
116
- 'items_list_navigation' => esc_html__( 'Content list navigation', 'essential-content-types' ),
117
- 'items_list' => esc_html__( 'Contents list', 'essential-content-types' ),
118
- ),
119
- 'supports' => array(
120
- 'title',
121
- 'editor',
122
- 'excerpt',
123
- 'thumbnail',
124
- 'author',
125
- 'comments',
126
- ),
127
- 'rewrite' => array(
128
- 'slug' => 'featured-content',
129
- 'with_front' => false,
130
- 'feeds' => true,
131
- 'pages' => true,
132
- ),
133
- 'public' => true,
134
- 'show_ui' => true,
135
- 'menu_position' => 20, // below Pages
136
- 'menu_icon' => 'dashicons-grid-view', // 3.8+ dashicon option
137
- 'capability_type' => 'page',
138
- 'map_meta_cap' => true,
139
- 'taxonomies' => array( self::CUSTOM_TAXONOMY_TYPE, self::CUSTOM_TAXONOMY_TAG ),
140
- 'has_archive' => true,
141
- 'query_var' => 'featured_content',
142
- 'show_in_rest' => true,
143
- );
144
-
145
- $description = get_option( 'featured_content_content' );
146
- if ( '' !== $description ) {
147
- $args['description'] = $description;
148
- }
149
-
150
- register_post_type(
151
- self::CUSTOM_POST_TYPE,
152
- $args
153
- );
154
-
155
- register_taxonomy(
156
- self::CUSTOM_TAXONOMY_TYPE,
157
- self::CUSTOM_POST_TYPE,
158
- array(
159
- 'hierarchical' => true,
160
- 'labels' => array(
161
- 'name' => esc_html__( 'Content Types', 'essential-content-types' ),
162
- 'singular_name' => esc_html__( 'Content Type', 'essential-content-types' ),
163
- 'menu_name' => esc_html__( 'Content Types', 'essential-content-types' ),
164
- 'all_items' => esc_html__( 'All Content Types', 'essential-content-types' ),
165
- 'edit_item' => esc_html__( 'Edit Content Type', 'essential-content-types' ),
166
- 'view_item' => esc_html__( 'View Content Type', 'essential-content-types' ),
167
- 'update_item' => esc_html__( 'Update Content Type', 'essential-content-types' ),
168
- 'add_new_item' => esc_html__( 'Add New Content Type', 'essential-content-types' ),
169
- 'new_item_name' => esc_html__( 'New Content Type Name', 'essential-content-types' ),
170
- 'parent_item' => esc_html__( 'Parent Content Type', 'essential-content-types' ),
171
- 'parent_item_colon' => esc_html__( 'Parent Content Type:', 'essential-content-types' ),
172
- 'search_items' => esc_html__( 'Search Content Types', 'essential-content-types' ),
173
- 'items_list_navigation' => esc_html__( 'Content type list navigation', 'essential-content-types' ),
174
- 'items_list' => esc_html__( 'Content type list', 'essential-content-types' ),
175
- ),
176
- 'public' => true,
177
- 'show_ui' => true,
178
- 'show_in_nav_menus' => true,
179
- 'show_in_rest' => true,
180
- 'show_admin_column' => true,
181
- 'query_var' => true,
182
- 'rewrite' => array( 'slug' => 'content-type' ),
183
- )
184
- );
185
-
186
- register_taxonomy(
187
- self::CUSTOM_TAXONOMY_TAG,
188
- self::CUSTOM_POST_TYPE,
189
- array(
190
- 'hierarchical' => false,
191
- 'labels' => array(
192
- 'name' => esc_html__( 'Content Tags', 'essential-content-types' ),
193
- 'singular_name' => esc_html__( 'Content Tag', 'essential-content-types' ),
194
- 'menu_name' => esc_html__( 'Content Tags', 'essential-content-types' ),
195
- 'all_items' => esc_html__( 'All Content Tags', 'essential-content-types' ),
196
- 'edit_item' => esc_html__( 'Edit Content Tag', 'essential-content-types' ),
197
- 'view_item' => esc_html__( 'View Content Tag', 'essential-content-types' ),
198
- 'update_item' => esc_html__( 'Update Content Tag', 'essential-content-types' ),
199
- 'add_new_item' => esc_html__( 'Add New Content Tag', 'essential-content-types' ),
200
- 'new_item_name' => esc_html__( 'New Content Tag Name', 'essential-content-types' ),
201
- 'search_items' => esc_html__( 'Search Content Tags', 'essential-content-types' ),
202
- 'popular_items' => esc_html__( 'Popular Content Tags', 'essential-content-types' ),
203
- 'separate_items_with_commas' => esc_html__( 'Separate tags with commas', 'essential-content-types' ),
204
- 'add_or_remove_items' => esc_html__( 'Add or remove tags', 'essential-content-types' ),
205
- 'choose_from_most_used' => esc_html__( 'Choose from the most used tags', 'essential-content-types' ),
206
- 'not_found' => esc_html__( 'No tags found.', 'essential-content-types' ),
207
- 'items_list_navigation' => esc_html__( 'Content tag list navigation', 'essential-content-types' ),
208
- 'items_list' => esc_html__( 'Content tag list', 'essential-content-types' ),
209
- ),
210
- 'public' => true,
211
- 'show_ui' => true,
212
- 'show_in_nav_menus' => true,
213
- 'show_in_rest' => true,
214
- 'show_admin_column' => true,
215
- 'query_var' => true,
216
- 'rewrite' => array( 'slug' => 'content-tag' ),
217
- )
218
- );
219
- }
220
-
221
- /**
222
- * Update messages for the Featured Content admin.
223
- */
224
- function updated_messages( $messages ) {
225
- global $post;
226
-
227
- $messages[ self::CUSTOM_POST_TYPE ] = array(
228
- 0 => '', // Unused. Messages start at index 1.
229
- 1 => sprintf( __( 'Content updated. <a href="%s">View item</a>', 'essential-content-types' ), esc_url( get_permalink( $post->ID ) ) ),
230
- 2 => esc_html__( 'Custom field updated.', 'essential-content-types' ),
231
- 3 => esc_html__( 'Custom field deleted.', 'essential-content-types' ),
232
- 4 => esc_html__( 'Content updated.', 'essential-content-types' ),
233
- /* translators: %s: date and time of the revision */
234
- 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Content restored to revision from %s', 'essential-content-types' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
235
- 6 => sprintf( __( 'Content published. <a href="%s">View content</a>', 'essential-content-types' ), esc_url( get_permalink( $post->ID ) ) ),
236
- 7 => esc_html__( 'Content saved.', 'essential-content-types' ),
237
- 8 => sprintf( __( 'Content submitted. <a target="_blank" href="%s">Preview content</a>', 'essential-content-types' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
238
- 9 => sprintf(
239
- __( 'Content scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview content</a>', 'essential-content-types' ),
240
- // translators: Publish box date format, see http://php.net/date
241
- date_i18n( __( 'M j, Y @ G:i', 'essential-content-types' ), strtotime( $post->post_date ) ),
242
- esc_url( get_permalink( $post->ID ) )
243
- ),
244
- 10 => sprintf( __( 'Content item draft updated. <a target="_blank" href="%s">Preview content</a>', 'essential-content-types' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
245
- );
246
-
247
- return $messages;
248
- }
249
-
250
- /**
251
- * Change ‘Title’ column label
252
- * Add Featured Image column
253
- */
254
- function edit_admin_columns( $columns ) {
255
- // change 'Title' to 'Content'
256
- $columns['title'] = __( 'Content', 'essential-content-types' );
257
- if ( current_theme_supports( 'post-thumbnails' ) ) {
258
- // add featured image before 'Content'
259
- $columns = array_slice( $columns, 0, 1, true ) + array( 'thumbnail' => '' ) + array_slice( $columns, 1, null, true );
260
- }
261
-
262
- return $columns;
263
- }
264
-
265
- /**
266
- * Add featured image to column
267
- */
268
- function image_column( $column, $post_id ) {
269
- global $post;
270
- switch ( $column ) {
271
- case 'thumbnail':
272
- echo get_the_post_thumbnail( $post_id, 'featured-content-admin-thumb' );
273
- break;
274
- }
275
- }
276
-
277
- /**
278
- * Adjust image column width
279
- */
280
- function enqueue_admin_styles( $hook ) {
281
- $screen = get_current_screen();
282
-
283
- if ( 'edit.php' == $hook && self::CUSTOM_POST_TYPE == $screen->post_type && current_theme_supports( 'post-thumbnails' ) ) {
284
- wp_add_inline_style( 'wp-admin', '.column-thumbnail img:nth-of-type(2) { display: none; } .manage-column.column-thumbnail { width: 50px; } @media screen and (max-width: 360px) { .column-thumbnail{ display:none; } }' );
285
- }
286
- }
287
-
288
- /**
289
- * Adds featured_content section to the Customizer.
290
- */
291
- function customize_register( $wp_customize ) {
292
- $wp_customize->add_panel(
293
- 'ect_plugin_options',
294
- array(
295
- 'title' => esc_html__( 'Essential Content Types Plugin Options', 'essential-content-types' ),
296
- 'priority' => 1,
297
- )
298
- );
299
-
300
- $wp_customize->add_section(
301
- 'ect_featured_content',
302
- array(
303
- 'title' => esc_html__( 'Featured Content', 'essential-content-types' ),
304
- 'panel' => 'ect_plugin_options',
305
- )
306
- );
307
-
308
- $wp_customize->add_setting(
309
- 'featured_content_title',
310
- array(
311
- 'default' => esc_html__( 'Contents', 'essential-content-types' ),
312
- 'type' => 'option',
313
- 'sanitize_callback' => 'sanitize_text_field',
314
- 'sanitize_js_callback' => 'sanitize_text_field',
315
- )
316
- );
317
-
318
- $wp_customize->add_control(
319
- 'featured_content_title',
320
- array(
321
- 'section' => 'ect_featured_content',
322
- 'label' => esc_html__( 'Featured Content Archive Title', 'essential-content-types' ),
323
- 'type' => 'text',
324
- )
325
- );
326
-
327
- $wp_customize->add_setting(
328
- 'featured_content_content',
329
- array(
330
- 'default' => '',
331
- 'type' => 'option',
332
- 'sanitize_callback' => 'wp_kses_post',
333
- 'sanitize_js_callback' => 'wp_kses_post',
334
- )
335
- );
336
-
337
- $wp_customize->add_control(
338
- 'featured_content_content',
339
- array(
340
- 'section' => 'ect_featured_content',
341
- 'label' => esc_html__( 'Featured Content Archive Content', 'essential-content-types' ),
342
- 'type' => 'textarea',
343
- )
344
- );
345
-
346
- $wp_customize->add_setting(
347
- 'featured_content_featured_image',
348
- array(
349
- 'default' => '',
350
- 'type' => 'option',
351
- 'sanitize_callback' => 'attachment_url_to_postid',
352
- 'sanitize_js_callback' => 'attachment_url_to_postid',
353
- 'theme_supports' => 'post-thumbnails',
354
- )
355
- );
356
-
357
- $wp_customize->add_control(
358
- new WP_Customize_Image_Control(
359
- $wp_customize,
360
- 'featured_content_featured_image',
361
- array(
362
- 'section' => 'ect_featured_content',
363
- 'label' => esc_html__( 'Featured Content Archive Featured Image', 'essential-content-types' ),
364
- )
365
- )
366
- );
367
- }
368
-
369
- /**
370
- * Follow CPT reading setting on CPT archive and taxonomy pages
371
- */
372
- function query_reading_setting( $query ) {
373
- if ( ! is_admin() &&
374
- $query->is_main_query() &&
375
- ( $query->is_post_type_archive( self::CUSTOM_POST_TYPE ) || $query->is_tax( self::CUSTOM_TAXONOMY_TYPE ) || $query->is_tax( self::CUSTOM_TAXONOMY_TAG ) )
376
- ) {
377
- $post_per_page = ( null != get_option( 'posts_per_page' ) ) ? get_option( 'posts_per_page' ) : '10';
378
- $query->set( 'posts_per_page', get_option( self::OPTION_READING_SETTING, $post_per_page ) );
379
- }
380
- }
381
-
382
- /**
383
- * Add to REST API post type whitelist
384
- */
385
- function allow_featured_content_rest_api_type( $post_types ) {
386
- $post_types[] = self::CUSTOM_POST_TYPE;
387
-
388
- return $post_types;
389
- }
390
-
391
- /**
392
- * Our [featured_content] shortcode.
393
- * Prints Featured Content data styled to look good on *any* theme.
394
- *
395
- * @return featured_content_shortcode_html
396
- */
397
- static function featured_content_shortcode( $atts ) {
398
- // Default attributes
399
- $atts = shortcode_atts(
400
- array(
401
- 'image' => true,
402
- 'display_types' => true,
403
- 'display_tags' => true,
404
- 'display_content' => true,
405
- 'display_author' => false,
406
- 'show_filter' => false,
407
- 'include_type' => false,
408
- 'include_tag' => false,
409
- 'columns' => 2,
410
- 'showposts' => -1,
411
- 'order' => 'asc',
412
- 'orderby' => 'date',
413
- ),
414
- $atts,
415
- 'featured_content'
416
- );
417
-
418
- // A little sanitization
419
- if ( $atts['image'] && 'true' != $atts['image'] ) {
420
- $atts['image'] = false;
421
- }
422
-
423
- if ( $atts['display_types'] && 'true' != $atts['display_types'] ) {
424
- $atts['display_types'] = false;
425
- }
426
-
427
- if ( $atts['display_tags'] && 'true' != $atts['display_tags'] ) {
428
- $atts['display_tags'] = false;
429
- }
430
-
431
- if ( $atts['display_author'] && 'true' != $atts['display_author'] ) {
432
- $atts['display_author'] = false;
433
- }
434
-
435
- if ( $atts['display_content'] && 'true' != $atts['display_content'] && 'full' != $atts['display_content'] ) {
436
- $atts['display_content'] = false;
437
- }
438
-
439
- if ( $atts['include_type'] ) {
440
- $atts['include_type'] = explode( ',', str_replace( ' ', '', $atts['include_type'] ) );
441
- }
442
-
443
- if ( $atts['include_tag'] ) {
444
- $atts['include_tag'] = explode( ',', str_replace( ' ', '', $atts['include_tag'] ) );
445
- }
446
-
447
- // Check if column value is set to valid numbers or else set default value as 2
448
- if ( 1 <= $atts['columns'] && 6 >= $atts['columns'] ) {
449
- $atts['columns'] = absint( $atts['columns'] );
450
- } else {
451
- $atts['columns'] = 2;
452
- }
453
-
454
- $atts['showposts'] = intval( $atts['showposts'] );
455
-
456
- if ( $atts['order'] ) {
457
- $atts['order'] = urldecode( $atts['order'] );
458
- $atts['order'] = strtoupper( $atts['order'] );
459
- if ( 'DESC' != $atts['order'] ) {
460
- $atts['order'] = 'ASC';
461
- }
462
- }
463
-
464
- if ( $atts['orderby'] ) {
465
- $atts['orderby'] = urldecode( $atts['orderby'] );
466
- $atts['orderby'] = strtolower( $atts['orderby'] );
467
- $allowed_keys = array( 'author', 'date', 'title', 'rand' );
468
-
469
- $parsed = array();
470
- foreach ( explode( ',', $atts['orderby'] ) as $featured_content_index_number => $orderby ) {
471
- if ( ! in_array( $orderby, $allowed_keys ) ) {
472
- continue;
473
- }
474
- $parsed[] = $orderby;
475
- }
476
-
477
- if ( empty( $parsed ) ) {
478
- unset( $atts['orderby'] );
479
- } else {
480
- $atts['orderby'] = implode( ' ', $parsed );
481
- }
482
- }
483
-
484
- // enqueue shortcode styles when shortcode is used
485
- wp_enqueue_style( 'featured-content-style', plugins_url( 'css/featured-content-shortcode.css', __FILE__ ), array(), '20140326' );
486
-
487
- return self::featured_content_shortcode_html( $atts );
488
- }
489
-
490
- /**
491
- * Query to retrieve entries from the Featured Content post_type.
492
- *
493
- * @return object
494
- */
495
- static function featured_content_query( $atts ) {
496
- // Default query arguments
497
- $default = array(
498
- 'order' => $atts['order'],
499
- 'orderby' => $atts['orderby'],
500
- 'posts_per_page' => $atts['showposts'],
501
- );
502
-
503
- $args = wp_parse_args( $atts, $default );
504
- $args['post_type'] = self::CUSTOM_POST_TYPE; // Force this post type
505
-
506
- if ( false != $atts['include_type'] || false != $atts['include_tag'] ) {
507
- $args['tax_query'] = array();
508
- }
509
-
510
- // If 'include_type' has been set use it on the main query
511
- if ( false != $atts['include_type'] ) {
512
- array_push(
513
- $args['tax_query'],
514
- array(
515
- 'taxonomy' => self::CUSTOM_TAXONOMY_TYPE,
516
- 'field' => 'slug',
517
- 'terms' => $atts['include_type'],
518
- )
519
- );
520
- }
521
-
522
- // If 'include_tag' has been set use it on the main query
523
- if ( false != $atts['include_tag'] ) {
524
- array_push(
525
- $args['tax_query'],
526
- array(
527
- 'taxonomy' => self::CUSTOM_TAXONOMY_TAG,
528
- 'field' => 'slug',
529
- 'terms' => $atts['include_tag'],
530
- )
531
- );
532
- }
533
-
534
- if ( false != $atts['include_type'] && false != $atts['include_tag'] ) {
535
- $args['tax_query']['relation'] = 'AND';
536
- }
537
-
538
- // Run the query and return
539
- $query = new WP_Query( $args );
540
- return $query;
541
- }
542
-
543
- /**
544
- * The Featured Content shortcode loop.
545
- *
546
- * @todo add theme color styles
547
- * @return html
548
- */
549
- static function featured_content_shortcode_html( $atts ) {
550
-
551
- $query = self::featured_content_query( $atts );
552
-
553
- ob_start();
554
-
555
- // If we have posts, create the html
556
- // with featured-content markup
557
- if ( $query->have_posts() ) {
558
-
559
- /**
560
- * Hook: ect_before_featured_content_loop.
561
- *
562
- * @hooked ect_featured_content_section_open
563
- */
564
- $layout = ect_get_layout();
565
- do_action( 'ect_before_featured_content_loop', $layout[ $atts['columns'] ] );
566
-
567
- ?>
568
- <?php
569
- while ( $query->have_posts() ) {
570
- $query->the_post();
571
- ect_get_template_part( 'content', 'featured-content', $atts );
572
- }
573
- wp_reset_postdata();
574
- ?>
575
-
576
- <?php
577
-
578
- /**
579
- * Hook: ect_after_featured_content_loop.
580
- *
581
- * @hooked essential_content_pro_featured_content_section_close
582
- */
583
- do_action( 'ect_after_featured_content_loop' );
584
-
585
- } else {
586
- /**
587
- * Hook: ect_no_articles_found.
588
- *
589
- * @hooked ect_no_articles_found
590
- */
591
- do_action( 'ect_no_featured_content_found' );
592
- }
593
-
594
- $html = ob_get_clean();
595
-
596
- // If there is a [featured-content] within a [featured-content], remove the shortcode
597
- if ( has_shortcode( $html, 'featured-content' ) ) {
598
- remove_shortcode( 'featured-content' );
599
- }
600
-
601
- // Return the HTML block
602
- return $html;
603
- }
604
-
605
- /**
606
- * Displays the content type that a content belongs to.
607
- *
608
- * @return html
609
- */
610
- static function get_content_type( $post_id ) {
611
- $content_types = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TYPE );
612
-
613
- // If no types, return empty string
614
- if ( empty( $content_types ) || is_wp_error( $content_types ) ) {
615
- return;
616
- }
617
-
618
- $html = '<div class="content-types"><span>' . __( 'Types', 'essential-content-types' ) . ':</span>';
619
- $types = array();
620
- // Loop thorugh all the types
621
- foreach ( $content_types as $content_type ) {
622
- $content_type_link = get_term_link( $content_type, self::CUSTOM_TAXONOMY_TYPE );
623
-
624
- if ( is_wp_error( $content_type_link ) ) {
625
- return $content_type_link;
626
- }
627
-
628
- $types[] = '<a href="' . esc_url( $content_type_link ) . '" rel="tag">' . esc_html( $content_type->name ) . '</a>';
629
- }
630
- $html .= ' ' . implode( ', ', $types );
631
- $html .= '</div>';
632
-
633
- return $html;
634
- }
635
-
636
- /**
637
- * Displays the content tags that a content belongs to.
638
- *
639
- * @return html
640
- */
641
- static function get_content_tags( $post_id ) {
642
- $content_tags = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TAG );
643
-
644
- // If no tags, return empty string
645
- if ( empty( $content_tags ) || is_wp_error( $content_tags ) ) {
646
- return false;
647
- }
648
-
649
- $html = '<div class="content-tags"><span>' . __( 'Tags', 'essential-content-types' ) . ':</span>';
650
- $tags = array();
651
- // Loop thorugh all the tags
652
- foreach ( $content_tags as $content_tag ) {
653
- $content_tag_link = get_term_link( $content_tag, self::CUSTOM_TAXONOMY_TYPE );
654
-
655
- if ( is_wp_error( $content_tag_link ) ) {
656
- return $content_tag_link;
657
- }
658
-
659
- $tags[] = '<a href="' . esc_url( $content_tag_link ) . '" rel="tag">' . esc_html( $content_tag->name ) . '</a>';
660
- }
661
- $html .= ' ' . implode( ', ', $tags );
662
- $html .= '</div>';
663
-
664
- return $html;
665
- }
666
-
667
- /**
668
- * Displays the author of the current featured_content content.
669
- *
670
- * @return html
671
- */
672
- static function get_content_author() {
673
- $html = '<div class="content-author">';
674
- /* translators: %1$s is link to author posts, %2$s is author display name */
675
- $html .= sprintf(
676
- __( '<span>Author:</span> <a href="%1$s">%2$s</a>', 'essential-content-types' ),
677
- esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ),
678
- esc_html( get_the_author() )
679
- );
680
- $html .= '</div>';
681
-
682
- return $html;
683
- }
684
- }
685
- add_action( 'init', array( 'Essential_Content_Featured_Content', 'init' ) );
686
-
687
- /**
688
- * Add Featured Content support
689
- */
690
- function essential_content_featured_content_support() {
691
- /*
692
- * Adding theme support for Jetpack Featured Content CPT.
693
- */
694
- add_image_size( 'ect-featured-content', 640, 640, true );
695
- }
696
- add_action( 'after_setup_theme', 'essential_content_featured_content_support' );
697
-
698
-
699
- if ( ! function_exists( 'essential_content_get_featured_content_thumbnail_link' ) ) :
700
- /**
701
- * Display the featured image if it's available
702
- *
703
- * @return html
704
- */
705
- function essential_content_get_featured_content_thumbnail_link( $post_id, $size ) {
706
- if ( has_post_thumbnail( $post_id ) ) {
707
- /**
708
- * Change the Featured Content thumbnail size.
709
- *
710
- * @module custom-content-types
711
- *
712
- * @since 3.4.0
713
- *
714
- * @param string|array $var Either a registered size keyword or size array.
715
- */
716
- return '<a class="featured-content-featured-image" href="' . esc_url( get_permalink( $post_id ) ) . '">' . get_the_post_thumbnail( $post_id, apply_filters( 'featured_content_thumbnail_size', $size ) ) . '</a>';
717
- }
718
- }
719
- endif;
720
-
721
-
722
- if ( ! function_exists( 'essential_content_no_featured_content_found' ) ) :
723
- /**
724
- * No items found text
725
- *
726
- * @return html
727
- */
728
- function essential_content_no_featured_content_found() {
729
- echo '<p><em>' . esc_html__( 'Your Featured Content Archive currently has no entries. You can start creating them on your dashboard.', 'essential-content-types-pro' ) . '</em></p>';
730
- }
731
- add_action( 'ect_no_featured_content_found', 'essential_content_no_featured_content_found', 10 );
732
- endif;
733
-
734
-
735
- if ( ! function_exists( 'essential_content_featured_content_section_open' ) ) :
736
- /**
737
- * Open section
738
- *
739
- * @return html
740
- */
741
- function essential_content_featured_content_section_open( $layout ) {
742
- echo '<div class="ect-featured-content ect-section ' . $layout . '">';
743
- echo '<div class="ect-wrapper">';
744
- }
745
- endif;
746
- add_action( 'ect_before_featured_content_loop', 'essential_content_featured_content_section_open', 10, 1 );
747
-
748
-
749
- if ( ! function_exists( 'essential_content_featured_content_loop_start' ) ) :
750
- /**
751
- * open wrapper before loop
752
- *
753
- */
754
- function essential_content_featured_content_loop_start( $layout = null ) {
755
- echo '<div class="section-content-wrapper featured-content-wrapper ' . $layout . '">';
756
- }
757
- endif;
758
- add_action( 'ect_before_featured_content_loop', 'essential_content_featured_content_loop_start', 30 );
759
-
760
-
761
- if ( ! function_exists( 'ect_featured_content_loop_end' ) ) :
762
- /**
763
- * close wrapper after loop
764
- *
765
- */
766
- function essential_content_featured_content_loop_end() {
767
- echo '</div><!-- .featured-content-wrapper -->';
768
- }
769
- endif;
770
- add_action( 'ect_after_featured_content_loop', 'essential_content_featured_content_loop_end', 10 );
771
-
772
-
773
- if ( ! function_exists( 'ect_featured_content_section_close' ) ) :
774
- /**
775
- * Close section
776
- *
777
- * @return html
778
- */
779
- function essential_content_featured_content_section_close() {
780
- echo '</div><!-- .ect-wrapper -->';
781
- echo '</div><!-- .ect-section -->';
782
- }
783
- endif;
784
- add_action( 'ect_after_featured_content_loop', 'essential_content_featured_content_section_close', 20 );
1
+ <?php
2
+
3
+ /**
4
+ * Support JetPack Featured Content
5
+ */
6
+ class Essential_Content_Featured_Content {
7
+ const CUSTOM_POST_TYPE = 'featured-content';
8
+ const CUSTOM_TAXONOMY_TYPE = 'featured-content-type';
9
+ const CUSTOM_TAXONOMY_TAG = 'featured-content-tag';
10
+ const OPTION_NAME = 'featured_content';
11
+ const OPTION_READING_SETTING = 'featured_content_posts_per_page';
12
+
13
+ public $version = ESSENTIAL_CONTENT_TYPES_VERSION;
14
+
15
+ static function init() {
16
+ static $instance = false;
17
+
18
+ if ( ! $instance ) {
19
+ $instance = new Essential_Content_Featured_Content;
20
+ }
21
+
22
+ return $instance;
23
+ }
24
+
25
+ /**
26
+ * Conditionally hook into WordPress.
27
+ *
28
+ * Setup user option for enabling CPT
29
+ * If user has CPT enabled, show in admin
30
+ */
31
+ function __construct() {
32
+ // Make sure the post types are loaded for imports
33
+ add_action( 'import_start', array( $this, 'register_post_types' ) );
34
+
35
+ // Add to REST API post type whitelist
36
+ add_filter( 'rest_api_allowed_post_types', array( $this, 'allow_featured_content_rest_api_type' ) );
37
+
38
+ // CPT magic
39
+ $this->register_post_types();
40
+ add_action( sprintf( 'add_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 );
41
+ add_action( sprintf( 'update_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 );
42
+ add_action( sprintf( 'publish_%s', self::CUSTOM_POST_TYPE ), array( $this, 'flush_rules_on_first_content' ) );
43
+ add_action( 'after_switch_theme', array( $this, 'flush_rules_on_switch' ) );
44
+
45
+ // Admin Customization
46
+ add_filter( 'post_updated_messages', array( $this, 'updated_messages' ) );
47
+ add_filter( sprintf( 'manage_%s_posts_columns', self::CUSTOM_POST_TYPE ), array( $this, 'edit_admin_columns' ) );
48
+ add_filter( sprintf( 'manage_%s_posts_custom_column', self::CUSTOM_POST_TYPE ), array( $this, 'image_column' ), 10, 2 );
49
+ add_action( 'customize_register', array( $this, 'customize_register' ) );
50
+
51
+ add_image_size( 'featured-content-admin-thumb', 50, 50, true );
52
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
53
+
54
+ // register featured_content shortcode and featured_content shortcode (legacy)
55
+ add_shortcode( 'featured_content', array( $this, 'featured_content_shortcode' ) );
56
+ add_shortcode( 'ect_featured_content', array( $this, 'featured_content_shortcode' ) );
57
+
58
+ // Adjust CPT archive and custom taxonomies to obey CPT reading setting
59
+ add_filter( 'pre_get_posts', array( $this, 'query_reading_setting' ) );
60
+ }
61
+
62
+ /*
63
+ * Flush permalinks when CPT option is turned on/off
64
+ */
65
+ function flush_rules_on_enable() {
66
+ flush_rewrite_rules();
67
+ }
68
+
69
+ /*
70
+ * Count published contents and flush permalinks when first contents is published
71
+ */
72
+ function flush_rules_on_first_content() {
73
+ $contents = get_transient( 'featured-content-count-cache' );
74
+
75
+ if ( false === $contents ) {
76
+ flush_rewrite_rules();
77
+ $contents = (int) wp_count_posts( self::CUSTOM_POST_TYPE )->publish;
78
+
79
+ if ( ! empty( $contents ) ) {
80
+ set_transient( 'featured-content-count-cache', $contents, HOUR_IN_SECONDS * 12 );
81
+ }
82
+ }
83
+ }
84
+
85
+ /*
86
+ * Flush permalinks when CPT supported theme is activated
87
+ */
88
+ function flush_rules_on_switch() {
89
+ flush_rewrite_rules();
90
+ }
91
+
92
+ /**
93
+ * Register Post Type
94
+ */
95
+ function register_post_types() {
96
+ if ( post_type_exists( self::CUSTOM_POST_TYPE ) ) {
97
+ return;
98
+ }
99
+
100
+ $args = array(
101
+ 'description' => esc_html__( 'Featured Content Items', 'essential-content-types' ),
102
+ 'labels' => array(
103
+ 'name' => esc_html__( 'Featured Contents', 'essential-content-types' ),
104
+ 'singular_name' => esc_html__( 'Featured Content', 'essential-content-types' ),
105
+ 'menu_name' => esc_html__( 'Featured Content', 'essential-content-types' ),
106
+ 'all_items' => esc_html__( 'All Contents', 'essential-content-types' ),
107
+ 'add_new' => esc_html__( 'Add New', 'essential-content-types' ),
108
+ 'add_new_item' => esc_html__( 'Add New Content', 'essential-content-types' ),
109
+ 'edit_item' => esc_html__( 'Edit Content', 'essential-content-types' ),
110
+ 'new_item' => esc_html__( 'New Content', 'essential-content-types' ),
111
+ 'view_item' => esc_html__( 'View Content', 'essential-content-types' ),
112
+ 'search_items' => esc_html__( 'Search Contents', 'essential-content-types' ),
113
+ 'not_found' => esc_html__( 'No Contents found', 'essential-content-types' ),
114
+ 'not_found_in_trash' => esc_html__( 'No Contents found in Trash', 'essential-content-types' ),
115
+ 'filter_items_list' => esc_html__( 'Filter contents list', 'essential-content-types' ),
116
+ 'items_list_navigation' => esc_html__( 'Content list navigation', 'essential-content-types' ),
117
+ 'items_list' => esc_html__( 'Contents list', 'essential-content-types' ),
118
+ ),
119
+ 'supports' => array(
120
+ 'title',
121
+ 'editor',
122
+ 'excerpt',
123
+ 'thumbnail',
124
+ 'author',
125
+ 'comments',
126
+ ),
127
+ 'rewrite' => array(
128
+ 'slug' => 'featured-content',
129
+ 'with_front' => false,
130
+ 'feeds' => true,
131
+ 'pages' => true,
132
+ ),
133
+ 'public' => true,
134
+ 'show_ui' => true,
135
+ 'menu_position' => 20, // below Pages
136
+ 'menu_icon' => 'dashicons-grid-view', // 3.8+ dashicon option
137
+ 'capability_type' => 'page',
138
+ 'map_meta_cap' => true,
139
+ 'taxonomies' => array( self::CUSTOM_TAXONOMY_TYPE, self::CUSTOM_TAXONOMY_TAG ),
140
+ 'has_archive' => true,
141
+ 'query_var' => 'featured_content',
142
+ 'show_in_rest' => true,
143
+ );
144
+
145
+ $description = get_option( 'featured_content_content' );
146
+ if ( '' !== $description ) {
147
+ $args['description'] = $description;
148
+ }
149
+
150
+ register_post_type(
151
+ self::CUSTOM_POST_TYPE,
152
+ $args
153
+ );
154
+
155
+ register_taxonomy(
156
+ self::CUSTOM_TAXONOMY_TYPE,
157
+ self::CUSTOM_POST_TYPE,
158
+ array(
159
+ 'hierarchical' => true,
160
+ 'labels' => array(
161
+ 'name' => esc_html__( 'Content Types', 'essential-content-types' ),
162
+ 'singular_name' => esc_html__( 'Content Type', 'essential-content-types' ),
163
+ 'menu_name' => esc_html__( 'Content Types', 'essential-content-types' ),
164
+ 'all_items' => esc_html__( 'All Content Types', 'essential-content-types' ),
165
+ 'edit_item' => esc_html__( 'Edit Content Type', 'essential-content-types' ),
166
+ 'view_item' => esc_html__( 'View Content Type', 'essential-content-types' ),
167
+ 'update_item' => esc_html__( 'Update Content Type', 'essential-content-types' ),
168
+ 'add_new_item' => esc_html__( 'Add New Content Type', 'essential-content-types' ),
169
+ 'new_item_name' => esc_html__( 'New Content Type Name', 'essential-content-types' ),
170
+ 'parent_item' => esc_html__( 'Parent Content Type', 'essential-content-types' ),
171
+ 'parent_item_colon' => esc_html__( 'Parent Content Type:', 'essential-content-types' ),
172
+ 'search_items' => esc_html__( 'Search Content Types', 'essential-content-types' ),
173
+ 'items_list_navigation' => esc_html__( 'Content type list navigation', 'essential-content-types' ),
174
+ 'items_list' => esc_html__( 'Content type list', 'essential-content-types' ),
175
+ ),
176
+ 'public' => true,
177
+ 'show_ui' => true,
178
+ 'show_in_nav_menus' => true,
179
+ 'show_in_rest' => true,
180
+ 'show_admin_column' => true,
181
+ 'query_var' => true,
182
+ 'rewrite' => array( 'slug' => 'content-type' ),
183
+ )
184
+ );
185
+
186
+ register_taxonomy(
187
+ self::CUSTOM_TAXONOMY_TAG,
188
+ self::CUSTOM_POST_TYPE,
189
+ array(
190
+ 'hierarchical' => false,
191
+ 'labels' => array(
192
+ 'name' => esc_html__( 'Content Tags', 'essential-content-types' ),
193
+ 'singular_name' => esc_html__( 'Content Tag', 'essential-content-types' ),
194
+ 'menu_name' => esc_html__( 'Content Tags', 'essential-content-types' ),
195
+ 'all_items' => esc_html__( 'All Content Tags', 'essential-content-types' ),
196
+ 'edit_item' => esc_html__( 'Edit Content Tag', 'essential-content-types' ),
197
+ 'view_item' => esc_html__( 'View Content Tag', 'essential-content-types' ),
198
+ 'update_item' => esc_html__( 'Update Content Tag', 'essential-content-types' ),
199
+ 'add_new_item' => esc_html__( 'Add New Content Tag', 'essential-content-types' ),
200
+ 'new_item_name' => esc_html__( 'New Content Tag Name', 'essential-content-types' ),
201
+ 'search_items' => esc_html__( 'Search Content Tags', 'essential-content-types' ),
202
+ 'popular_items' => esc_html__( 'Popular Content Tags', 'essential-content-types' ),
203
+ 'separate_items_with_commas' => esc_html__( 'Separate tags with commas', 'essential-content-types' ),
204
+ 'add_or_remove_items' => esc_html__( 'Add or remove tags', 'essential-content-types' ),
205
+ 'choose_from_most_used' => esc_html__( 'Choose from the most used tags', 'essential-content-types' ),
206
+ 'not_found' => esc_html__( 'No tags found.', 'essential-content-types' ),
207
+ 'items_list_navigation' => esc_html__( 'Content tag list navigation', 'essential-content-types' ),
208
+ 'items_list' => esc_html__( 'Content tag list', 'essential-content-types' ),
209
+ ),
210
+ 'public' => true,
211
+ 'show_ui' => true,
212
+ 'show_in_nav_menus' => true,
213
+ 'show_in_rest' => true,
214
+ 'show_admin_column' => true,
215
+ 'query_var' => true,
216
+ 'rewrite' => array( 'slug' => 'content-tag' ),
217
+ )
218
+ );
219
+ }
220
+
221
+ /**
222
+ * Update messages for the Featured Content admin.
223
+ */
224
+ function updated_messages( $messages ) {
225
+ global $post;
226
+
227
+ $messages[ self::CUSTOM_POST_TYPE ] = array(
228
+ 0 => '', // Unused. Messages start at index 1.
229
+ 1 => sprintf( __( 'Content updated. <a href="%s">View item</a>', 'essential-content-types' ), esc_url( get_permalink( $post->ID ) ) ),
230
+ 2 => esc_html__( 'Custom field updated.', 'essential-content-types' ),
231
+ 3 => esc_html__( 'Custom field deleted.', 'essential-content-types' ),
232
+ 4 => esc_html__( 'Content updated.', 'essential-content-types' ),
233
+ /* translators: %s: date and time of the revision */
234
+ 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Content restored to revision from %s', 'essential-content-types' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
235
+ 6 => sprintf( __( 'Content published. <a href="%s">View content</a>', 'essential-content-types' ), esc_url( get_permalink( $post->ID ) ) ),
236
+ 7 => esc_html__( 'Content saved.', 'essential-content-types' ),
237
+ 8 => sprintf( __( 'Content submitted. <a target="_blank" href="%s">Preview content</a>', 'essential-content-types' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
238
+ 9 => sprintf(
239
+ __( 'Content scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview content</a>', 'essential-content-types' ),
240
+ // translators: Publish box date format, see http://php.net/date
241
+ date_i18n( __( 'M j, Y @ G:i', 'essential-content-types' ), strtotime( $post->post_date ) ),
242
+ esc_url( get_permalink( $post->ID ) )
243
+ ),
244
+ 10 => sprintf( __( 'Content item draft updated. <a target="_blank" href="%s">Preview content</a>', 'essential-content-types' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
245
+ );
246
+
247
+ return $messages;
248
+ }
249
+
250
+ /**
251
+ * Change ‘Title’ column label
252
+ * Add Featured Image column
253
+ */
254
+ function edit_admin_columns( $columns ) {
255
+ // change 'Title' to 'Content'
256
+ $columns['title'] = __( 'Content', 'essential-content-types' );
257
+ if ( current_theme_supports( 'post-thumbnails' ) ) {
258
+ // add featured image before 'Content'
259
+ $columns = array_slice( $columns, 0, 1, true ) + array( 'thumbnail' => '' ) + array_slice( $columns, 1, null, true );
260
+ }
261
+
262
+ return $columns;
263
+ }
264
+
265
+ /**
266
+ * Add featured image to column
267
+ */
268
+ function image_column( $column, $post_id ) {
269
+ global $post;
270
+ switch ( $column ) {
271
+ case 'thumbnail':
272
+ echo get_the_post_thumbnail( $post_id, 'featured-content-admin-thumb' );
273
+ break;
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Adjust image column width
279
+ */
280
+ function enqueue_admin_styles( $hook ) {
281
+ $screen = get_current_screen();
282
+
283
+ if ( 'edit.php' == $hook && self::CUSTOM_POST_TYPE == $screen->post_type && current_theme_supports( 'post-thumbnails' ) ) {
284
+ wp_add_inline_style( 'wp-admin', '.column-thumbnail img:nth-of-type(2) { display: none; } .manage-column.column-thumbnail { width: 50px; } @media screen and (max-width: 360px) { .column-thumbnail{ display:none; } }' );
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Adds featured_content section to the Customizer.
290
+ */
291
+ function customize_register( $wp_customize ) {
292
+ $wp_customize->add_panel(
293
+ 'ect_plugin_options',
294
+ array(
295
+ 'title' => esc_html__( 'Essential Content Types Plugin Options', 'essential-content-types' ),
296
+ 'priority' => 1,
297
+ )
298
+ );
299
+
300
+ $wp_customize->add_section(
301
+ 'ect_featured_content',
302
+ array(
303
+ 'title' => esc_html__( 'Featured Content', 'essential-content-types' ),
304
+ 'panel' => 'ect_plugin_options',
305
+ )
306
+ );
307
+
308
+ $wp_customize->add_setting(
309
+ 'featured_content_title',
310
+ array(
311
+ 'default' => esc_html__( 'Contents', 'essential-content-types' ),
312
+ 'type' => 'option',
313
+ 'sanitize_callback' => 'sanitize_text_field',
314
+ 'sanitize_js_callback' => 'sanitize_text_field',
315
+ )
316
+ );
317
+
318
+ $wp_customize->add_control(
319
+ 'featured_content_title',
320
+ array(
321
+ 'section' => 'ect_featured_content',
322
+ 'label' => esc_html__( 'Featured Content Archive Title', 'essential-content-types' ),
323
+ 'type' => 'text',
324
+ )
325
+ );
326
+
327
+ $wp_customize->add_setting(
328
+ 'featured_content_content',
329
+ array(
330
+ 'default' => '',
331
+ 'type' => 'option',
332
+ 'sanitize_callback' => 'wp_kses_post',
333
+ 'sanitize_js_callback' => 'wp_kses_post',
334
+ )
335
+ );
336
+
337
+ $wp_customize->add_control(
338
+ 'featured_content_content',
339
+ array(
340
+ 'section' => 'ect_featured_content',
341
+ 'label' => esc_html__( 'Featured Content Archive Content', 'essential-content-types' ),
342
+ 'type' => 'textarea',
343
+ )
344
+ );
345
+
346
+ $wp_customize->add_setting(
347
+ 'featured_content_featured_image',
348
+ array(
349
+ 'default' => '',
350
+ 'type' => 'option',
351
+ 'sanitize_callback' => 'attachment_url_to_postid',
352
+ 'sanitize_js_callback' => 'attachment_url_to_postid',
353
+ 'theme_supports' => 'post-thumbnails',
354
+ )
355
+ );
356
+
357
+ $wp_customize->add_control(
358
+ new WP_Customize_Image_Control(
359
+ $wp_customize,
360
+ 'featured_content_featured_image',
361
+ array(
362
+ 'section' => 'ect_featured_content',
363
+ 'label' => esc_html__( 'Featured Content Archive Featured Image', 'essential-content-types' ),
364
+ )
365
+ )
366
+ );
367
+ }
368
+
369
+ /**
370
+ * Follow CPT reading setting on CPT archive and taxonomy pages
371
+ */
372
+ function query_reading_setting( $query ) {
373
+ if ( ! is_admin() &&
374
+ $query->is_main_query() &&
375
+ ( $query->is_post_type_archive( self::CUSTOM_POST_TYPE ) || $query->is_tax( self::CUSTOM_TAXONOMY_TYPE ) || $query->is_tax( self::CUSTOM_TAXONOMY_TAG ) )
376
+ ) {
377
+ $post_per_page = ( null != get_option( 'posts_per_page' ) ) ? get_option( 'posts_per_page' ) : '10';
378
+ $query->set( 'posts_per_page', get_option( self::OPTION_READING_SETTING, $post_per_page ) );
379
+ }
380
+ }
381
+
382
+ /**
383
+ * Add to REST API post type whitelist
384
+ */
385
+ function allow_featured_content_rest_api_type( $post_types ) {
386
+ $post_types[] = self::CUSTOM_POST_TYPE;
387
+
388
+ return $post_types;
389
+ }
390
+
391
+ /**
392
+ * Our [featured_content] shortcode.
393
+ * Prints Featured Content data styled to look good on *any* theme.
394
+ *
395
+ * @return featured_content_shortcode_html
396
+ */
397
+ static function featured_content_shortcode( $atts ) {
398
+ // Default attributes
399
+ $atts = shortcode_atts(
400
+ array(
401
+ 'image' => true,
402
+ 'display_types' => true,
403
+ 'display_tags' => true,
404
+ 'display_content' => true,
405
+ 'display_author' => false,
406
+ 'show_filter' => false,
407
+ 'include_type' => false,
408
+ 'include_tag' => false,
409
+ 'columns' => 2,
410
+ 'showposts' => -1,
411
+ 'order' => 'asc',
412
+ 'orderby' => 'date',
413
+ ),
414
+ $atts,
415
+ 'featured_content'
416
+ );
417
+
418
+ // A little sanitization
419
+ if ( $atts['image'] && 'true' != $atts['image'] ) {
420
+ $atts['image'] = false;
421
+ }
422
+
423
+ if ( $atts['display_types'] && 'true' != $atts['display_types'] ) {
424
+ $atts['display_types'] = false;
425
+ }
426
+
427
+ if ( $atts['display_tags'] && 'true' != $atts['display_tags'] ) {
428
+ $atts['display_tags'] = false;
429
+ }
430
+
431
+ if ( $atts['display_author'] && 'true' != $atts['display_author'] ) {
432
+ $atts['display_author'] = false;
433
+ }
434
+
435
+ if ( $atts['display_content'] && 'true' != $atts['display_content'] && 'full' != $atts['display_content'] ) {
436
+ $atts['display_content'] = false;
437
+ }
438
+
439
+ if ( $atts['include_type'] ) {
440
+ $atts['include_type'] = explode( ',', str_replace( ' ', '', $atts['include_type'] ) );
441
+ }
442
+
443
+ if ( $atts['include_tag'] ) {
444
+ $atts['include_tag'] = explode( ',', str_replace( ' ', '', $atts['include_tag'] ) );
445
+ }
446
+
447
+ // Check if column value is set to valid numbers or else set default value as 2
448
+ if ( 1 <= $atts['columns'] && 6 >= $atts['columns'] ) {
449
+ $atts['columns'] = absint( $atts['columns'] );
450
+ } else {
451
+ $atts['columns'] = 2;
452
+ }
453
+
454
+ $atts['showposts'] = intval( $atts['showposts'] );
455
+
456
+ if ( $atts['order'] ) {
457
+ $atts['order'] = urldecode( $atts['order'] );
458
+ $atts['order'] = strtoupper( $atts['order'] );
459
+ if ( 'DESC' != $atts['order'] ) {
460
+ $atts['order'] = 'ASC';
461
+ }
462
+ }
463
+
464
+ if ( $atts['orderby'] ) {
465
+ $atts['orderby'] = urldecode( $atts['orderby'] );
466
+ $atts['orderby'] = strtolower( $atts['orderby'] );
467
+ $allowed_keys = array( 'author', 'date', 'title', 'rand' );
468
+
469
+ $parsed = array();
470
+ foreach ( explode( ',', $atts['orderby'] ) as $featured_content_index_number => $orderby ) {
471
+ if ( ! in_array( $orderby, $allowed_keys ) ) {
472
+ continue;
473
+ }
474
+ $parsed[] = $orderby;
475
+ }
476
+
477
+ if ( empty( $parsed ) ) {
478
+ unset( $atts['orderby'] );
479
+ } else {
480
+ $atts['orderby'] = implode( ' ', $parsed );
481
+ }
482
+ }
483
+
484
+ // enqueue shortcode styles when shortcode is used
485
+ wp_enqueue_style( 'featured-content-style', plugins_url( 'css/featured-content-shortcode.css', __FILE__ ), array(), '20140326' );
486
+
487
+ return self::featured_content_shortcode_html( $atts );
488
+ }
489
+
490
+ /**
491
+ * Query to retrieve entries from the Featured Content post_type.
492
+ *
493
+ * @return object
494
+ */
495
+ static function featured_content_query( $atts ) {
496
+ // Default query arguments
497
+ $default = array(
498
+ 'order' => $atts['order'],
499
+ 'orderby' => $atts['orderby'],
500
+ 'posts_per_page' => $atts['showposts'],
501
+ );
502
+
503
+ $args = wp_parse_args( $atts, $default );
504
+ $args['post_type'] = self::CUSTOM_POST_TYPE; // Force this post type
505
+
506
+ if ( false != $atts['include_type'] || false != $atts['include_tag'] ) {
507
+ $args['tax_query'] = array();
508
+ }
509
+
510
+ // If 'include_type' has been set use it on the main query
511
+ if ( false != $atts['include_type'] ) {
512
+ array_push(
513
+ $args['tax_query'],
514
+ array(
515
+ 'taxonomy' => self::CUSTOM_TAXONOMY_TYPE,
516
+ 'field' => 'slug',
517
+ 'terms' => $atts['include_type'],
518
+ )
519
+ );
520
+ }
521
+
522
+ // If 'include_tag' has been set use it on the main query
523
+ if ( false != $atts['include_tag'] ) {
524
+ array_push(
525
+ $args['tax_query'],
526
+ array(
527
+ 'taxonomy' => self::CUSTOM_TAXONOMY_TAG,
528
+ 'field' => 'slug',
529
+ 'terms' => $atts['include_tag'],
530
+ )
531
+ );
532
+ }
533
+
534
+ if ( false != $atts['include_type'] && false != $atts['include_tag'] ) {
535
+ $args['tax_query']['relation'] = 'AND';
536
+ }
537
+
538
+ // Run the query and return
539
+ $query = new WP_Query( $args );
540
+ return $query;
541
+ }
542
+
543
+ /**
544
+ * The Featured Content shortcode loop.
545
+ *
546
+ * @todo add theme color styles
547
+ * @return html
548
+ */
549
+ static function featured_content_shortcode_html( $atts ) {
550
+
551
+ $query = self::featured_content_query( $atts );
552
+
553
+ ob_start();
554
+
555
+ // If we have posts, create the html
556
+ // with featured-content markup
557
+ if ( $query->have_posts() ) {
558
+
559
+ /**
560
+ * Hook: ect_before_featured_content_loop.
561
+ *
562
+ * @hooked ect_featured_content_section_open
563
+ */
564
+ $layout = ect_get_layout();
565
+ do_action( 'ect_before_featured_content_loop', $layout[ $atts['columns'] ] );
566
+
567
+ ?>
568
+ <?php
569
+ while ( $query->have_posts() ) {
570
+ $query->the_post();
571
+ ect_get_template_part( 'content', 'featured-content', $atts );
572
+ }
573
+ wp_reset_postdata();
574
+ ?>
575
+
576
+ <?php
577
+
578
+ /**
579
+ * Hook: ect_after_featured_content_loop.
580
+ *
581
+ * @hooked essential_content_pro_featured_content_section_close
582
+ */
583
+ do_action( 'ect_after_featured_content_loop' );
584
+
585
+ } else {
586
+ /**
587
+ * Hook: ect_no_articles_found.
588
+ *
589
+ * @hooked ect_no_articles_found
590
+ */
591
+ do_action( 'ect_no_featured_content_found' );
592
+ }
593
+
594
+ $html = ob_get_clean();
595
+
596
+ // If there is a [featured-content] within a [featured-content], remove the shortcode
597
+ if ( has_shortcode( $html, 'featured-content' ) ) {
598
+ remove_shortcode( 'featured-content' );
599
+ }
600
+
601
+ // Return the HTML block
602
+ return $html;
603
+ }
604
+
605
+ /**
606
+ * Displays the content type that a content belongs to.
607
+ *
608
+ * @return html
609
+ */
610
+ static function get_content_type( $post_id ) {
611
+ $content_types = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TYPE );
612
+
613
+ // If no types, return empty string
614
+ if ( empty( $content_types ) || is_wp_error( $content_types ) ) {
615
+ return;
616
+ }
617
+
618
+ $html = '<div class="content-types"><span>' . __( 'Types', 'essential-content-types' ) . ':</span>';
619
+ $types = array();
620
+ // Loop thorugh all the types
621
+ foreach ( $content_types as $content_type ) {
622
+ $content_type_link = get_term_link( $content_type, self::CUSTOM_TAXONOMY_TYPE );
623
+
624
+ if ( is_wp_error( $content_type_link ) ) {
625
+ return $content_type_link;
626
+ }
627
+
628
+ $types[] = '<a href="' . esc_url( $content_type_link ) . '" rel="tag">' . esc_html( $content_type->name ) . '</a>';
629
+ }
630
+ $html .= ' ' . implode( ', ', $types );
631
+ $html .= '</div>';
632
+
633
+ return $html;
634
+ }
635
+
636
+ /**
637
+ * Displays the content tags that a content belongs to.
638
+ *
639
+ * @return html
640
+ */
641
+ static function get_content_tags( $post_id ) {
642
+ $content_tags = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TAG );
643
+
644
+ // If no tags, return empty string
645
+ if ( empty( $content_tags ) || is_wp_error( $content_tags ) ) {
646
+ return false;
647
+ }
648
+
649
+ $html = '<div class="content-tags"><span>' . __( 'Tags', 'essential-content-types' ) . ':</span>';
650
+ $tags = array();
651
+ // Loop thorugh all the tags
652
+ foreach ( $content_tags as $content_tag ) {
653
+ $content_tag_link = get_term_link( $content_tag, self::CUSTOM_TAXONOMY_TYPE );
654
+
655
+ if ( is_wp_error( $content_tag_link ) ) {
656
+ return $content_tag_link;
657
+ }
658
+
659
+ $tags[] = '<a href="' . esc_url( $content_tag_link ) . '" rel="tag">' . esc_html( $content_tag->name ) . '</a>';
660
+ }
661
+ $html .= ' ' . implode( ', ', $tags );
662
+ $html .= '</div>';
663
+
664
+ return $html;
665
+ }
666
+
667
+ /**
668
+ * Displays the author of the current featured_content content.
669
+ *
670
+ * @return html
671
+ */
672
+ static function get_content_author() {
673
+ $html = '<div class="content-author">';
674
+ /* translators: %1$s is link to author posts, %2$s is author display name */
675
+ $html .= sprintf(
676
+ __( '<span>Author:</span> <a href="%1$s">%2$s</a>', 'essential-content-types' ),
677
+ esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ),
678
+ esc_html( get_the_author() )
679
+ );
680
+ $html .= '</div>';
681
+
682
+ return $html;
683
+ }
684
+ }
685
+ add_action( 'init', array( 'Essential_Content_Featured_Content', 'init' ) );
686
+
687
+ /**
688
+ * Add Featured Content support
689
+ */
690
+ function essential_content_featured_content_support() {
691
+ /*
692
+ * Adding theme support for Jetpack Featured Content CPT.
693
+ */
694
+ add_image_size( 'ect-featured-content', 640, 640, true );
695
+ }
696
+ add_action( 'after_setup_theme', 'essential_content_featured_content_support' );
697
+
698
+
699
+ if ( ! function_exists( 'essential_content_get_featured_content_thumbnail_link' ) ) :
700
+ /**
701
+ * Display the featured image if it's available
702
+ *
703
+ * @return html
704
+ */
705
+ function essential_content_get_featured_content_thumbnail_link( $post_id, $size ) {
706
+ if ( has_post_thumbnail( $post_id ) ) {
707
+ /**
708
+ * Change the Featured Content thumbnail size.
709
+ *
710
+ * @module custom-content-types
711
+ *
712
+ * @since 3.4.0
713
+ *
714
+ * @param string|array $var Either a registered size keyword or size array.
715
+ */
716
+ return '<a class="featured-content-featured-image" href="' . esc_url( get_permalink( $post_id ) ) . '">' . get_the_post_thumbnail( $post_id, apply_filters( 'featured_content_thumbnail_size', $size ) ) . '</a>';
717
+ }
718
+ }
719
+ endif;
720
+
721
+
722
+ if ( ! function_exists( 'essential_content_no_featured_content_found' ) ) :
723
+ /**
724
+ * No items found text
725
+ *
726
+ * @return html
727
+ */
728
+ function essential_content_no_featured_content_found() {
729
+ echo '<p><em>' . esc_html__( 'Your Featured Content Archive currently has no entries. You can start creating them on your dashboard.', 'essential-content-types-pro' ) . '</em></p>';
730
+ }
731
+ add_action( 'ect_no_featured_content_found', 'essential_content_no_featured_content_found', 10 );
732
+ endif;
733
+
734
+
735
+ if ( ! function_exists( 'essential_content_featured_content_section_open' ) ) :
736
+ /**
737
+ * Open section
738
+ *
739
+ * @return html
740
+ */
741
+ function essential_content_featured_content_section_open( $layout ) {
742
+ echo '<div class="ect-featured-content ect-section ' . $layout . '">';
743
+ echo '<div class="ect-wrapper">';
744
+ }
745
+ endif;
746
+ add_action( 'ect_before_featured_content_loop', 'essential_content_featured_content_section_open', 10, 1 );
747
+
748
+
749
+ if ( ! function_exists( 'essential_content_featured_content_loop_start' ) ) :
750
+ /**
751
+ * open wrapper before loop
752
+ *
753
+ */
754
+ function essential_content_featured_content_loop_start( $layout = null ) {
755
+ echo '<div class="section-content-wrapper featured-content-wrapper ' . $layout . '">';
756
+ }
757
+ endif;
758
+ add_action( 'ect_before_featured_content_loop', 'essential_content_featured_content_loop_start', 30 );
759
+
760
+
761
+ if ( ! function_exists( 'ect_featured_content_loop_end' ) ) :
762
+ /**
763
+ * close wrapper after loop
764
+ *
765
+ */
766
+ function essential_content_featured_content_loop_end() {
767
+ echo '</div><!-- .featured-content-wrapper -->';
768
+ }
769
+ endif;
770
+ add_action( 'ect_after_featured_content_loop', 'essential_content_featured_content_loop_end', 10 );
771
+
772
+
773
+ if ( ! function_exists( 'ect_featured_content_section_close' ) ) :
774
+ /**
775
+ * Close section
776
+ *
777
+ * @return html
778
+ */
779
+ function essential_content_featured_content_section_close() {
780
+ echo '</div><!-- .ect-wrapper -->';
781
+ echo '</div><!-- .ect-section -->';
782
+ }
783
+ endif;
784
+ add_action( 'ect_after_featured_content_loop', 'essential_content_featured_content_section_close', 20 );
admin/class-food-menu.php CHANGED
@@ -1,1538 +1,1538 @@
1
- <?php
2
- /*
3
- * Put the following code in your theme's Food Menu Page Template to customize the markup of the menu.
4
-
5
- if ( class_exists( 'Essential_Content_Food_Menu' ) ) {
6
- Essential_Content_Food_Menu::init( array(
7
- 'menu_tag' => 'section',
8
- 'menu_class' => 'menu-items',
9
- 'menu_header_tag' => 'header',
10
- 'menu_header_class' => 'menu-group-header',
11
- 'menu_title_tag' => 'h1',
12
- 'menu_title_class' => 'menu-group-title',
13
- 'menu_description_tag' => 'div',
14
- 'menu_description_class' => 'menu-group-description',
15
- ) );
16
- }
17
-
18
- */
19
-
20
- /* @todo
21
-
22
- Bulk/Quick edit response of Menu Item rows is broken.
23
-
24
- Drag and Drop reordering.
25
- */
26
-
27
- class Essential_Content_Food_Menu {
28
- const MENU_ITEM_POST_TYPE = 'ect_food_menu_item';
29
- const MENU_ITEM_LABEL_TAX = 'ect_food_menu_item_label';
30
- const MENU_TAX = 'ect_food_menu';
31
-
32
- public $version = ESSENTIAL_CONTENT_TYPES_VERSION;
33
-
34
- protected $default_menu_item_loop_markup = array(
35
- 'menu_tag' => 'section',
36
- 'menu_class' => 'menu-items',
37
- 'menu_header_tag' => 'header',
38
- 'menu_header_class' => 'menu-group-header',
39
- 'menu_title_tag' => 'h1',
40
- 'menu_title_class' => 'menu-group-title',
41
- 'menu_description_tag' => 'div',
42
- 'menu_description_class' => 'menu-group-description',
43
- );
44
-
45
- protected $menu_item_loop_markup = array();
46
- protected $menu_item_loop_last_term_id = false;
47
- protected $menu_item_loop_current_term = false;
48
-
49
- static function init( $menu_item_loop_markup = array() ) {
50
- static $instance = false;
51
-
52
- if ( !$instance ) {
53
- $instance = new Essential_Content_Food_Menu;
54
- }
55
-
56
- if ( $menu_item_loop_markup ) {
57
- $instance->menu_item_loop_markup = wp_parse_args( $menu_item_loop_markup, $instance->default_menu_item_loop_markup );
58
- }
59
-
60
- return $instance;
61
- }
62
-
63
- function __construct() {
64
- if ( ! $this->site_supports_food_menu() )
65
- return;
66
-
67
- $this->register_taxonomies();
68
- $this->register_post_types();
69
- add_action( 'admin_menu', array( $this, 'add_admin_menus' ) );
70
- add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_ect_food_styles' ) );
71
- add_action( 'admin_head', array( $this, 'set_custom_font_icon' ) );
72
-
73
- // Enable Omnisearch for Menu Items.
74
- if ( class_exists( 'Jetpack_Omnisearch_Posts' ) )
75
- new Jetpack_Omnisearch_Posts( self::MENU_ITEM_POST_TYPE );
76
-
77
- // Always sort menu items correctly
78
- add_action( 'parse_query', array( $this, 'sort_menu_item_queries_by_menu_order' ) );
79
- add_filter( 'posts_results', array( $this, 'sort_menu_item_queries_by_menu_taxonomy' ), 10, 2 );
80
-
81
- add_action( 'wp_insert_post', array( $this, 'add_post_meta' ) );
82
-
83
- $this->menu_item_loop_markup = $this->default_menu_item_loop_markup;
84
-
85
- // Only output our Menu Item Loop Markup on a real blog view. Not feeds, XML-RPC, admin, etc.
86
- add_filter( 'template_include', array( $this, 'setup_menu_item_loop_markup__in_filter' ) );
87
-
88
- add_filter( 'enter_title_here', array( $this, 'change_default_title' ) );
89
- add_filter( 'post_updated_messages', array( $this, 'updated_messages' ) );
90
- add_filter( 'dashboard_glance_items', array( $this, 'add_to_dashboard' ) );
91
-
92
- add_filter( 'body_class', array( $this, 'custom_class' ) );
93
-
94
- // register food_menu shortcode (legacy)
95
- add_shortcode( 'food_menu', array( $this, 'ect_food_shortcode' ) );
96
- }
97
-
98
- public function custom_class( $classes ) {
99
- global $post;
100
- if( isset( $post->post_content ) && has_shortcode( $post->post_content, 'food_menu' ) ) {
101
- $classes[] = 'page-template-menu-page';
102
- }
103
- return $classes;
104
- }
105
-
106
- /**
107
- * Should this Custom Post Type be made available?
108
- */
109
- function site_supports_food_menu() {
110
- // If we're on WordPress.com, and it has the menu site vertical.
111
- if ( function_exists( 'site_vertical' ) && 'ect_food_menu' == site_vertical() )
112
- return true;
113
-
114
- // Else, if the current theme requests it.
115
- if ( current_theme_supports( self::MENU_ITEM_POST_TYPE ) )
116
- return true;
117
- }
118
-
119
- /* Setup */
120
-
121
- /**
122
- * Register Taxonomies and Post Type
123
- */
124
- function register_taxonomies() {
125
- if ( ! taxonomy_exists( self::MENU_ITEM_LABEL_TAX ) ) {
126
- register_taxonomy( self::MENU_ITEM_LABEL_TAX, self::MENU_ITEM_POST_TYPE, array(
127
- 'labels' => array(
128
- /* translators: this is about a food menu */
129
- 'name' => __( 'Menu Item Labels', 'essential-content-types' ),
130
- /* translators: this is about a food menu */
131
- 'singular_name' => __( 'Menu Item Label', 'essential-content-types' ),
132
- /* translators: this is about a food menu */
133
- 'search_items' => __( 'Search Menu Item Labels', 'essential-content-types' ),
134
- 'popular_items' => __( 'Popular Labels', 'essential-content-types' ),
135
- /* translators: this is about a food menu */
136
- 'all_items' => __( 'All Menu Item Labels', 'essential-content-types' ),
137
- /* translators: this is about a food menu */
138
- 'edit_item' => __( 'Edit Menu Item Label', 'essential-content-types' ),
139
- /* translators: this is about a food menu */
140
- 'view_item' => __( 'View Menu Item Label', 'essential-content-types' ),
141
- /* translators: this is about a food menu */
142
- 'update_item' => __( 'Update Menu Item Label', 'essential-content-types' ),
143
- /* translators: this is about a food menu */
144
- 'add_new_item' => __( 'Add New Menu Item Label', 'essential-content-types' ),
145
- /* translators: this is about a food menu */
146
- 'new_item_name' => __( 'New Menu Item Label Name', 'essential-content-types' ),
147
- 'separate_items_with_commas' => __( 'For example, spicy, favorite, etc. <br /> Separate Labels with commas', 'essential-content-types' ),
148
- 'add_or_remove_items' => __( 'Add or remove Labels', 'essential-content-types' ),
149
- 'choose_from_most_used' => __( 'Choose from the most used Labels', 'essential-content-types' ),
150
- 'items_list_navigation' => __( 'Menu item label list navigation', 'essential-content-types' ),
151
- 'items_list' => __( 'Menu item labels list', 'essential-content-types' ),
152
- ),
153
- 'no_tagcloud' => __( 'No Labels found', 'essential-content-types' ),
154
- 'hierarchical' => false,
155
- ) );
156
- }
157
-
158
- if ( ! taxonomy_exists( self::MENU_TAX ) ) {
159
- register_taxonomy( self::MENU_TAX, self::MENU_ITEM_POST_TYPE, array(
160
- 'labels' => array(
161
- /* translators: this is about a food menu */
162
- 'name' => __( 'Menu Sections', 'essential-content-types' ),
163
- /* translators: this is about a food menu */
164
- 'singular_name' => __( 'Menu Section', 'essential-content-types' ),
165
- /* translators: this is about a food menu */
166
- 'search_items' => __( 'Search Menu Sections', 'essential-content-types' ),
167
- /* translators: this is about a food menu */
168
- 'all_items' => __( 'All Menu Sections', 'essential-content-types' ),
169
- /* translators: this is about a food menu */
170
- 'parent_item' => __( 'Parent Menu Section', 'essential-content-types' ),
171
- /* translators: this is about a food menu */
172
- 'parent_item_colon' => __( 'Parent Menu Section:', 'essential-content-types' ),
173
- /* translators: this is about a food menu */
174
- 'edit_item' => __( 'Edit Menu Section', 'essential-content-types' ),
175
- /* translators: this is about a food menu */
176
- 'view_item' => __( 'View Menu Section', 'essential-content-types' ),
177
- /* translators: this is about a food menu */
178
- 'update_item' => __( 'Update Menu Section', 'essential-content-types' ),
179
- /* translators: this is about a food menu */
180
- 'add_new_item' => __( 'Add New Menu Section', 'essential-content-types' ),
181
- /* translators: this is about a food menu */
182
- 'new_item_name' => __( 'New Menu Sections Name', 'essential-content-types' ),
183
- 'items_list_navigation' => __( 'Menu section list navigation', 'essential-content-types' ),
184
- 'items_list' => __( 'Menu section list', 'essential-content-types' ),
185
- ),
186
- 'rewrite' => array(
187
- 'slug' => 'menu',
188
- 'with_front' => false,
189
- 'hierarchical' => true,
190
- ),
191
- 'hierarchical' => true,
192
- 'show_tagcloud' => false,
193
- 'query_var' => 'menu',
194
- ) );
195
- }
196
- }
197
-
198
- function register_post_types() {
199
- if ( post_type_exists( self::MENU_ITEM_POST_TYPE ) ) {
200
- return;
201
- }
202
-
203
- register_post_type( self::MENU_ITEM_POST_TYPE, array(
204
- 'description' => __( "Items on your restaurant's menu", 'essential-content-types' ),
205
-
206
- 'labels' => array(
207
- /* translators: this is about a food menu */
208
- 'name' => __( 'Menu Items', 'essential-content-types' ),
209
- /* translators: this is about a food menu */
210
- 'singular_name' => __( 'Menu Item', 'essential-content-types' ),
211
- /* translators: this is about a food menu */
212
- 'menu_name' => __( 'Food Menus', 'essential-content-types' ),
213
- /* translators: this is about a food menu */
214
- 'all_items' => __( 'Menu Items', 'essential-content-types' ),
215
- /* translators: this is about a food menu */
216
- 'add_new' => __( 'Add One Item', 'essential-content-types' ),
217
- /* translators: this is about a food menu */
218
- 'add_new_item' => __( 'Add Menu Item', 'essential-content-types' ),
219
- /* translators: this is about a food menu */
220
- 'edit_item' => __( 'Edit Menu Item', 'essential-content-types' ),
221
- /* translators: this is about a food menu */
222
- 'new_item' => __( 'New Menu Item', 'essential-content-types' ),
223
- /* translators: this is about a food menu */
224
- 'view_item' => __( 'View Menu Item', 'essential-content-types' ),
225
- /* translators: this is about a food menu */
226
- 'search_items' => __( 'Search Menu Items', 'essential-content-types' ),
227
- /* translators: this is about a food menu */
228
- 'not_found' => __( 'No Menu Items found', 'essential-content-types' ),
229
- /* translators: this is about a food menu */
230
- 'not_found_in_trash' => __( 'No Menu Items found in Trash', 'essential-content-types' ),
231
- 'filter_items_list' => __( 'Filter menu items list', 'essential-content-types' ),
232
- 'items_list_navigation' => __( 'Menu item list navigation', 'essential-content-types' ),
233
- 'items_list' => __( 'Menu items list', 'essential-content-types' ),
234
- ),
235
- 'supports' => array(
236
- 'title',
237
- 'editor',
238
- 'excerpt',
239
- ),
240
- 'rewrite' => array(
241
- 'slug' => 'item',
242
- 'with_front' => false,
243
- 'feeds' => false,
244
- 'pages' => false,
245
- ),
246
- 'register_meta_box_cb' => array( $this, 'register_menu_item_meta_boxes' ),
247
-
248
- 'public' => true,
249
- 'show_ui' => true, // set to false to replace with custom UI
250
- 'menu_position' => 20, // below Pages
251
- 'capability_type' => 'page',
252
- 'map_meta_cap' => true,
253
- 'has_archive' => false,
254
- 'query_var' => 'item',
255
- ) );
256
- }
257
-
258
-
259
- /**
260
- * Update messages for the Menu Item admin.
261
- */
262
- function updated_messages( $messages ) {
263
- global $post;
264
-
265
- $messages[self::MENU_ITEM_POST_TYPE] = array(
266
- 0 => '', // Unused. Messages start at index 1.
267
- /* translators: this is about a food menu */
268
- 1 => sprintf( __( 'Menu item updated. <a href="%s">View item</a>', 'essential-content-types' ), esc_url( get_permalink( $post->ID ) ) ),
269
- 2 => esc_html__( 'Custom field updated.', 'essential-content-types' ),
270
- 3 => esc_html__( 'Custom field deleted.', 'essential-content-types' ),
271
- /* translators: this is about a food menu */
272
- 4 => esc_html__( 'Menu item updated.', 'essential-content-types' ),
273
- /* translators: %s: date and time of the revision */
274
- 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Menu item restored to revision from %s', 'essential-content-types' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
275
- /* translators: this is about a food menu */
276
- 6 => sprintf( __( 'Menu item published. <a href="%s">View item</a>', 'essential-content-types' ), esc_url( get_permalink( $post->ID ) ) ),
277
- /* translators: this is about a food menu */
278
- 7 => esc_html__( 'Menu item saved.', 'essential-content-types' ),
279
- /* translators: this is about a food menu */
280
- 8 => sprintf( __( 'Menu item submitted. <a target="_blank" href="%s">Preview item</a>', 'essential-content-types' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
281
- /* translators: this is about a food menu */
282
- 9 => sprintf( __( 'Menu item scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview item</a>', 'essential-content-types' ),
283
- // translators: Publish box date format, see http://php.net/date
284
- date_i18n( __( 'M j, Y @ G:i', 'essential-content-types' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post->ID) ) ),
285
- /* translators: this is about a food menu */
286
- 10 => sprintf( __( 'Menu item draft updated. <a target="_blank" href="%s">Preview item</a>', 'essential-content-types' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
287
- );
288
-
289
- return $messages;
290
- }
291
-
292
-
293
- /**
294
- * Nova Styles and Scripts
295
- */
296
- function enqueue_ect_food_styles( $hook ) {
297
- global $post_type;
298
- $pages = array( 'edit.php', 'post.php', 'post-new.php' );
299
-
300
- if ( in_array( $hook, $pages ) && $post_type == self::MENU_ITEM_POST_TYPE ) {
301
- wp_enqueue_style( 'ect-food-menu-style', plugins_url( 'css/ect-food-menu.css', __FILE__ ), array(), $this->version );
302
- }
303
-
304
- wp_enqueue_style( 'ect-food-menu-font', plugins_url( 'css/ect-food-menu-font.css', __FILE__ ), array(), $this->version );
305
- }
306
-
307
-
308
- /**
309
- * Change ‘Enter Title Here’ text for the Menu Item.
310
- */
311
- function change_default_title( $title ) {
312
- $screen = get_current_screen();
313
-
314
- if ( self::MENU_ITEM_POST_TYPE == $screen->post_type )
315
- /* translators: this is about a food menu */
316
- $title = esc_html__( "Enter the menu item's name here", 'essential-content-types' );
317
-
318
- return $title;
319
- }
320
-
321
-
322
- /**
323
- * Add to Dashboard At A Glance
324
- */
325
- function add_to_dashboard() {
326
- $number_menu_items = wp_count_posts( self::MENU_ITEM_POST_TYPE );
327
-
328
- if ( current_user_can( 'administrator' ) ) {
329
- $number_menu_items_published = sprintf( '<a href="%1$s">%2$s</a>',
330
- esc_url( get_admin_url( get_current_blog_id(), 'edit.php?post_type=' . self::MENU_ITEM_POST_TYPE ) ),
331
- sprintf( _n( '%1$d Food Menu Item', '%1$d Food Menu Items', intval( $number_menu_items->publish ), 'essential-content-types' ), number_format_i18n( $number_menu_items->publish ) )
332
- );
333
- }
334
- else {
335
- $number_menu_items_published = sprintf( '<span>%1$s</span>',
336
- sprintf( _n( '%1$d Food Menu Item', '%1$d Food Menu Items', intval( $number_menu_items->publish ), 'essential-content-types' ), number_format_i18n( $number_menu_items->publish ) )
337
- );
338
- }
339
-
340
- echo '<li class="ect-food-menu-count">' . $number_menu_items_published . '</li>';
341
- }
342
-
343
-
344
- /**
345
- * Query
346
- */
347
- function is_menu_item_query( $query ) {
348
- if (
349
- ( isset( $query->query_vars['taxonomy'] ) && self::MENU_TAX == $query->query_vars['taxonomy'] )
350
- ||
351
- ( isset( $query->query_vars['post_type'] ) && self::MENU_ITEM_POST_TYPE == $query->query_vars['post_type'] )
352
- ) {
353
- return true;
354
- }
355
-
356
- return false;
357
- }
358
-
359
- function sort_menu_item_queries_by_menu_order( $query ) {
360
- if ( ! $this->is_menu_item_query( $query ) ) {
361
- return;
362
- }
363
-
364
- $query->query_vars['orderby'] = 'menu_order';
365
- $query->query_vars['order'] = 'ASC';
366
-
367
- // For now, just turn off paging so we can sort by taxonmy later
368
- // If we want paging in the future, we'll need to add the taxonomy sort here (or at least before the DB query is made)
369
- $query->query_vars['posts_per_page'] = -1;
370
- }
371
-
372
- function sort_menu_item_queries_by_menu_taxonomy( $posts, $query ) {
373
- if ( !$posts ) {
374
- return $posts;
375
- }
376
-
377
- if ( !$this->is_menu_item_query( $query ) ) {
378
- return $posts;
379
- }
380
-
381
- $grouped_by_term = array();
382
-
383
- foreach ( $posts as $post ) {
384
- $term = $this->get_menu_item_menu_leaf( $post->ID );
385
- if ( !$term || is_wp_error( $term ) ) {
386
- $term_id = 0;
387
- } else {
388
- $term_id = $term->term_id;
389
- }
390
-
391
- if ( !isset( $grouped_by_term["$term_id"] ) ) {
392
- $grouped_by_term["$term_id"] = array();
393
- }
394
-
395
- $grouped_by_term["$term_id"][] = $post;
396
- }
397
-
398
- $term_order = get_option( 'ect_food_menu_order', array() );
399
-
400
- $return = array();
401
- foreach ( $term_order as $term_id ) {
402
- if ( isset( $grouped_by_term["$term_id"] ) ) {
403
- $return = array_merge( $return, $grouped_by_term["$term_id"] );
404
- unset( $grouped_by_term["$term_id"] );
405
- }
406
- }
407
-
408
- foreach ( $grouped_by_term as $term_id => $posts ) {
409
- $return = array_merge( $return, $posts );
410
- }
411
-
412
- return $return;
413
- }
414
-
415
-
416
- /**
417
- * Add Many Items
418
- */
419
- function add_admin_menus() {
420
- $hook = add_submenu_page(
421
- 'edit.php?post_type=' . self::MENU_ITEM_POST_TYPE,
422
- __( 'Add Many Items', 'essential-content-types' ),
423
- __( 'Add Many Items', 'essential-content-types' ),
424
- 'edit_pages',
425
- 'add_many_ect_food_items',
426
- array( $this, 'add_many_new_items_page' )
427
- );
428
-
429
- add_action( "load-$hook", array( $this, 'add_many_new_items_page_load' ) );
430
-
431
- add_action( 'current_screen', array( $this, 'current_screen_load' ) );
432
-
433
- //Adjust 'Add Many Items' submenu position
434
- if ( isset( $GLOBALS['submenu']['edit.php?post_type=' . self::MENU_ITEM_POST_TYPE] ) ) {
435
- $submenu_item = array_pop( $GLOBALS['submenu']['edit.php?post_type=' . self::MENU_ITEM_POST_TYPE] );
436
- $GLOBALS['submenu']['edit.php?post_type=' . self::MENU_ITEM_POST_TYPE][11] = $submenu_item;
437
- ksort( $GLOBALS['submenu']['edit.php?post_type=' . self::MENU_ITEM_POST_TYPE] );
438
- }
439
-
440
-
441
- $this->setup_menu_item_columns();
442
-
443
- wp_register_script(
444
- 'ect-food-menu-checkboxes',
445
- plugin_dir_url( __FILE__ ) . 'js/ect-food-menu-menu-checkboxes.js',
446
- array( 'jquery' ),
447
- $this->version,
448
- false
449
- );
450
- }
451
-
452
-
453
- /**
454
- * Custom Nova Icon CSS
455
- */
456
- function set_custom_font_icon() {
457
- ?>
458
- <style type="text/css">
459
- #menu-posts-ect_food_menu_item .wp-menu-image:before {
460
- font-family: 'nova-font' !important;
461
- content: '\e603' !important;
462
- }
463
- </style>
464
- <?php
465
- }
466
-
467
- function current_screen_load() {
468
- $screen = get_current_screen();
469
- if ( 'edit-ect_food_menu_item' !== $screen->id ) {
470
- return;
471
- }
472
-
473
- $this->edit_menu_items_page_load();
474
- add_filter( 'admin_notices', array( $this, 'admin_notices' ) );
475
- }
476
-
477
- /* Edit Items List */
478
-
479
- function admin_notices() {
480
- if ( isset( $_GET['ect_food_reordered'] ) )
481
- /* translators: this is about a food menu */
482
- printf( '<div class="updated"><p>%s</p></div>', __( 'Menu Items re-ordered.', 'essential-content-types' ) );
483
- }
484
-
485
- function no_title_sorting( $columns ) {
486
- if ( isset( $columns['title'] ) )
487
- unset( $columns['title'] );
488
- return $columns;
489
- }
490
-
491
- function setup_menu_item_columns() {
492
- add_filter( sprintf( 'manage_edit-%s_sortable_columns', self::MENU_ITEM_POST_TYPE ), array( $this, 'no_title_sorting' ) );
493
- add_filter( sprintf( 'manage_%s_posts_columns', self::MENU_ITEM_POST_TYPE ), array( $this, 'menu_item_columns' ) );
494
-
495
- add_action( sprintf( 'manage_%s_posts_custom_column', self::MENU_ITEM_POST_TYPE ), array( $this, 'menu_item_column_callback' ), 10, 2 );
496
- }
497
-
498
- function menu_item_columns( $columns ) {
499
- unset( $columns['date'], $columns['likes'] );
500
-
501
- $columns['thumbnail'] = __( 'Thumbnail', 'essential-content-types' );
502
- $columns['labels'] = __( 'Labels', 'essential-content-types' );
503
- $columns['price'] = __( 'Price', 'essential-content-types' );
504
- $columns['order'] = __( 'Order', 'essential-content-types' );
505
-
506
- return $columns;
507
- }
508
-
509
- function menu_item_column_callback( $column, $post_id ) {
510
- $screen = get_current_screen();
511
-
512
- switch ( $column ) {
513
- case 'thumbnail':
514
- echo get_the_post_thumbnail( $post_id, array( 50, 50 ) );
515
- break;
516
- case 'labels' :
517
- $this->list_admin_labels( $post_id );
518
- break;
519
- case 'price' :
520
- $this->display_price( $post_id );
521
- break;
522
- case 'order' :
523
- $url = admin_url( $screen->parent_file );
524
-
525
- $up_url = add_query_arg( array(
526
- 'action' => 'move-item-up',
527
- 'post_id' => (int) $post_id,
528
- ), wp_nonce_url( $url, 'ect_food_move_item_up_' . $post_id ) );
529
-
530
- $down_url = add_query_arg( array(
531
- 'action' => 'move-item-down',
532
- 'post_id' => (int) $post_id,
533
- ), wp_nonce_url( $url, 'ect_food_move_item_down_' . $post_id ) );
534
- $menu_item = get_post($post_id);
535
- $this->get_menu_by_post_id( $post_id );
536
- if ( $term_id = $this->get_menu_by_post_id( $post_id ) ) {
537
- $term_id = $term_id->term_id;
538
- }
539
- ?>
540
- <input type="hidden" class="menu-order-value" name="ect_food_order[<?php echo (int) $post_id ?>]" value="<?php echo esc_attr( $menu_item->menu_order ) ?>" />
541
- <input type="hidden" class='ect-food-menu-term' name="ect_food_menu_term[<?php echo (int) $post_id ?>]" value="<?php echo esc_attr( $term_id ); ?>">
542
-
543
- <span class="hide-if-js">
544
- &nbsp; &nbsp; &mdash; <a class="ect-food-move-item-up" data-post-id="<?php echo (int) $post_id; ?>" href="<?php echo esc_url( $up_url ); ?>">up</a>
545
- <br />
546
- &nbsp; &nbsp; &mdash; <a class="ect-food-move-item-down" data-post-id="<?php echo (int) $post_id; ?>" href="<?php echo esc_url( $down_url ); ?>">down</a>
547
- </span>
548
- <?php
549
- break;
550
- }
551
- }
552
-
553
- function get_menu_by_post_id( $post_id = null ) {
554
- if ( ! $post_id )
555
- return false;
556
-
557
- $terms = get_the_terms( $post_id, self::MENU_TAX );
558
-
559
- if ( ! is_array( $terms ) )
560
- return false;
561
-
562
- return array_pop( $terms );
563
- }
564
-
565
- /**
566
- * Fires on a menu edit page. We might have drag-n-drop reordered
567
- */
568
- function maybe_reorder_menu_items() {
569
- // make sure we clicked our button
570
- if ( ! ( isset( $_REQUEST['menu_reorder_submit'] ) && $_REQUEST['menu_reorder_submit'] === __( 'Save New Order', 'essential-content-types' ) ) )
571
- return;
572
- ;
573
-
574
- // make sure we have the nonce
575
- if ( ! ( isset( $_REQUEST['drag-drop-reorder'] ) && wp_verify_nonce( $_REQUEST['drag-drop-reorder'], 'drag-drop-reorder' ) ) )
576
- return;
577
-
578
- $term_pairs = array_map( 'absint', $_REQUEST['ect_food_menu_term'] );
579
- $order_pairs = array_map( 'absint', $_REQUEST['ect_food_order'] );
580
-
581
- foreach( $order_pairs as $ID => $menu_order ) {
582
- $ID = absint( $ID );
583
- unset( $order_pairs[$ID] );
584
- if ( $ID < 0 )
585
- continue;
586
-
587
- $post = get_post( $ID );
588
- if ( ! $post )
589
- continue;
590
-
591
- // save a write if the order hasn't changed
592
- if ( $menu_order != $post->menu_order )
593
- wp_update_post( compact( 'ID', 'menu_order' ) );
594
-
595
- // save a write if the term hasn't changed
596
- if ( $term_pairs[$ID] != $this->get_menu_by_post_id( $ID )->term_id )
597
- wp_set_object_terms( $ID, $term_pairs[$ID], self::MENU_TAX );
598
-
599
- }
600
-
601
- $redirect = add_query_arg( array(
602
- 'post_type' => self::MENU_ITEM_POST_TYPE,
603
- 'ect_food_reordered' => '1'
604
- ), admin_url( 'edit.php' ) );
605
- wp_safe_redirect( $redirect );
606
- exit;
607
-
608
- }
609
-
610
- function edit_menu_items_page_load() {
611
- if ( isset( $_GET['action'] ) ) {
612
- $this->handle_menu_item_actions();
613
- }
614
-
615
- $this->maybe_reorder_menu_items();
616
-
617
- wp_enqueue_script(
618
- 'ect-food-drag-drop',
619
- plugin_dir_url( __FILE__ ) . 'js/ect-food-menu-drag-drop.js',
620
- array( 'jquery-ui-sortable' ),
621
- $this->version,
622
- true
623
- );
624
-
625
- wp_localize_script( 'ect-food-drag-drop', '_novaDragDrop', array(
626
- 'nonce' => wp_create_nonce( 'drag-drop-reorder' ),
627
- 'nonceName' => 'drag-drop-reorder',
628
- 'reorder' => __( 'Save New Order', 'essential-content-types' ),
629
- 'reorderName' => 'menu_reorder_submit'
630
- ) );
631
- add_action( 'the_post', array( $this, 'show_menu_titles_in_menu_item_list' ) );
632
- }
633
-
634
- function handle_menu_item_actions() {
635
- $action = (string) $_GET['action'];
636
-
637
- switch ( $action ) {
638
- case 'move-item-up' :
639
- case 'move-item-down' :
640
- $reorder = false;
641
-
642
- $post_id = (int) $_GET['post_id'];
643
-
644
- $term = $this->get_menu_item_menu_leaf( $post_id );
645
-
646
- // Get all posts in that term
647
- $query = new WP_Query( array(
648
- 'taxonomy' => self::MENU_TAX,
649
- 'term' => $term->slug,
650
- ) );
651
-
652
- $order = array();
653
- foreach ( $query->posts as $post ) {
654
- $order[] = $post->ID;
655
- }
656
-
657
- if ( 'move-item-up' == $action ) {
658
- check_admin_referer( 'ect_food_move_item_up_' . $post_id );
659
-
660
- $first_post_id = $order[0];
661
- if ( $post_id == $first_post_id ) {
662
- break;
663
- }
664
-
665
- foreach ( $order as $menu_order => $order_post_id ) {
666
- if ( $post_id != $order_post_id ) {
667
- continue;
668
- }
669
-
670
- $swap_post_id = $order[$menu_order - 1];
671
- $order[$menu_order - 1] = $post_id;
672
- $order[$menu_order] = $swap_post_id;
673
-
674
- $reorder = true;
675
- break;
676
- }
677
- } else {
678
- check_admin_referer( 'ect_food_move_item_down_' . $post_id );
679
-
680
- $last_post_id = end( $order );
681
- if ( $post_id == $last_post_id ) {
682
- break;
683
- }
684
-
685
- foreach ( $order as $menu_order => $order_post_id ) {
686
- if ( $post_id != $order_post_id ) {
687
- continue;
688
- }
689
-
690
- $swap_post_id = $order[$menu_order + 1];
691
- $order[$menu_order + 1] = $post_id;
692
- $order[$menu_order] = $swap_post_id;
693
-
694
- $reorder = true;
695
- }
696
- }
697
-
698
- if ( $reorder ) {
699
- foreach ( $order as $menu_order => $ID ) {
700
- wp_update_post( compact( 'ID', 'menu_order' ) );
701
- }
702
- }
703
-
704
- break;
705
- case 'move-menu-up' :
706
- case 'move-menu-down' :
707
- $reorder = false;
708
-
709
- $term_id = (int) $_GET['term_id'];
710
-
711
- $terms = $this->get_menus();
712
-
713
- $order = array();
714
- foreach ( $terms as $term ) {
715
- $order[] = $term->term_id;
716
- }
717
-
718
- if ( 'move-menu-up' == $action ) {
719
- check_admin_referer( 'ect_food_move_menu_up_' . $term_id );
720
-
721
- $first_term_id = $order[0];
722
- if ( $term_id == $first_term_id ) {
723
- break;
724
- }
725
-
726
- foreach ( $order as $menu_order => $order_term_id ) {
727
- if ( $term_id != $order_term_id ) {
728
- continue;
729
- }
730
-
731
- $swap_term_id = $order[$menu_order - 1];
732
- $order[$menu_order - 1] = $term_id;
733
- $order[$menu_order] = $swap_term_id;
734
-
735
- $reorder = true;
736
- break;
737
- }
738
- } else {
739
- check_admin_referer( 'ect_food_move_menu_down_' . $term_id );
740
-
741
- $last_term_id = end( $order );
742
- if ( $term_id == $last_term_id ) {
743
- break;
744
- }
745
-
746
- foreach ( $order as $menu_order => $order_term_id ) {
747
- if ( $term_id != $order_term_id ) {
748
- continue;
749
- }
750
-
751
- $swap_term_id = $order[$menu_order + 1];
752
- $order[$menu_order + 1] = $term_id;
753
- $order[$menu_order] = $swap_term_id;
754
-
755
- $reorder = true;
756
- }
757
- }
758
-
759
- if ( $reorder ) {
760
- update_option( 'ect_food_menu_order', $order );
761
- }
762
-
763
- break;
764
- default :
765
- return;
766
- }
767
-
768
- $redirect = add_query_arg( array(
769
- 'post_type' => self::MENU_ITEM_POST_TYPE,
770
- 'ect_food_reordered' => '1'
771
- ), admin_url( 'edit.php' ) );
772
- wp_safe_redirect( $redirect );
773
- exit;
774
- }
775
-
776
- /*
777
- * Add menu title rows to the list table
778
- */
779
- function show_menu_titles_in_menu_item_list( $post ) {
780
- global $wp_list_table;
781
-
782
- static $last_term_id = false;
783
-
784
- $term = $this->get_menu_item_menu_leaf( $post->ID );
785
-
786
- $term_id = $term instanceof WP_Term ? $term->term_id : null;
787
-
788
- if ( false !== $last_term_id && $last_term_id === $term_id ) {
789
- return;
790
- }
791
-
792
- if ( is_null( $term_id ) ) {
793
- $last_term_id = null;
794
- $term_name = '';
795
- $parent_count = 0;
796
- } else {
797
- $last_term_id = $term->term_id;
798
- $term_name = $term->name;
799
- $parent_count = 0;
800
- $current_term = $term;
801
- while ( $current_term->parent ) {
802
- $parent_count++;
803
- $current_term = get_term( $current_term->parent, self::MENU_TAX );
804
- }
805
- }
806
-
807
- $non_order_column_count = $wp_list_table->get_column_count() - 1;
808
-
809
- $screen = get_current_screen();
810
-
811
- $url = admin_url( $screen->parent_file );
812
-
813
- $up_url = add_query_arg( array(
814
- 'action' => 'move-menu-up',
815
- 'term_id' => (int) $term_id,
816
- ), wp_nonce_url( $url, 'ect_food_move_menu_up_' . $term_id ) );
817
-
818
- $down_url = add_query_arg( array(
819
- 'action' => 'move-menu-down',
820
- 'term_id' => (int) $term_id,
821
- ), wp_nonce_url( $url, 'ect_food_move_menu_down_' . $term_id ) );
822
-
823
- ?>
824
- <tr class="no-items menu-label-row" data-term_id="<?php echo esc_attr( $term_id ) ?>">
825
- <td class="colspanchange" colspan="<?php echo (int) $non_order_column_count; ?>">
826
- <h3><?php
827
- echo str_repeat( ' &mdash; ', (int) $parent_count );
828
-
829
- if ( $term instanceof WP_Term ) {
830
- echo esc_html( sanitize_term_field( 'name', $term_name, $term_id, self::MENU_TAX, 'display' ) );
831
- edit_term_link( __( 'edit', 'essential-content-types' ), '<span class="edit-ect-food-section"><span class="dashicon dashicon-edit"></span>', '</span>', $term );
832
-
833
- } else {
834
- _e( 'Uncategorized' , 'essential-content-types' );
835
- }
836
- ?></h3>
837
- </td>
838
- <td>
839
- <?php if ( $term instanceof WP_Term ) { ?>
840
- <a class="ect-food-move-menu-up" title="<?php esc_attr_e( 'Move menu section up', 'essential-content-types' ); ?>" href="<?php echo esc_url( $up_url ); ?>"><?php esc_html_e( 'UP', 'essential-content-types' ); ?></a>
841
- <br />
842
- <a class="ect-food-move-menu-down" title="<?php esc_attr_e( 'Move menu section down', 'essential-content-types' ); ?>" href="<?php echo esc_url( $down_url ); ?>"><?php esc_html_e( 'DOWN', 'essential-content-types' ); ?></a>
843
- <?php } ?>
844
- </td>
845
- </tr>
846
- <?php
847
- }
848
-
849
- /* Edit Many Items */
850
-
851
- function add_many_new_items_page_load() {
852
- if ( 'POST' === strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
853
- $this->process_form_request();
854
- exit;
855
- }
856
-
857
- $this->enqueue_many_items_scripts();
858
- }
859
-
860
- function enqueue_many_items_scripts() {
861
- wp_enqueue_script(
862
- 'ect-food-many-items',
863
- plugin_dir_url( __FILE__ ) . 'js/ect-food-menu-many-items.js',
864
- array( 'jquery' ),
865
- $this->version,
866
- true
867
- );
868
- }
869
-
870
- function process_form_request() {
871
- if ( !isset( $_POST['ect_food_title'] ) || !is_array( $_POST['ect_food_title'] ) ) {
872
- return;
873
- }
874
-
875
- $is_ajax = !empty( $_POST['ajax'] );
876
-
877
- if ( $is_ajax ) {
878
- check_ajax_referer( 'ect_food_many_items' );
879
- } else {
880
- check_admin_referer( 'ect_food_many_items' );
881
- }
882
-
883
- foreach ( array_keys( $_POST['ect_food_title'] ) as $key ) :
884
- // $_POST is already slashed
885
- $post_details = array(
886
- 'post_status' => 'publish',
887
- 'post_type' => self::MENU_ITEM_POST_TYPE,
888
- 'post_content' => $_POST['ect_food_content'][$key],
889
- 'post_title' => $_POST['ect_food_title'][$key],
890
- 'tax_input' => array(
891
- self::MENU_ITEM_LABEL_TAX => $_POST['ect_food_labels'][$key],
892
- self::MENU_TAX => isset( $_POST['ect_food_menu_tax'] ) ? $_POST['ect_food_menu_tax'] : null,
893
- ),
894
- );
895
-
896
- $post_id = wp_insert_post( $post_details );
897
- if ( !$post_id || is_wp_error( $post_id ) ) {
898
- continue;
899
- }
900
-
901
- $this->set_price( $post_id, isset( $_POST['ect_food_price'][$key] ) ? stripslashes( $_POST['ect_food_price'][$key] ) : '' );
902
-
903
- if ( $is_ajax ) :
904
- $post = get_post( $post_id );
905
- $GLOBALS['post'] = $post;
906
- setup_postdata( $post );
907
-
908
- ?>
909
- <td><?php the_title(); ?></td>
910
- <td class="ect-food-price"><?php $this->display_price(); ?></td>
911
- <td><?php $this->list_labels( $post_id ); ?></td>
912
- <td><?php the_content(); ?></td>
913
- <?php
914
- endif;
915
-
916
- endforeach;
917
-
918
- if ( $is_ajax ) {
919
- exit;
920
- }
921
-
922
- wp_safe_redirect( admin_url( 'edit.php?post_type=' . self::MENU_ITEM_POST_TYPE ) );
923
- exit;
924
- }
925
-
926
- function add_many_new_items_page() {
927
- ?>
928
- <div class="wrap">
929
- <h2><?php esc_html_e( 'Add Many Items', 'essential-content-types' ); ?></h2>
930
-
931
- <p><?php _e( 'Use the <kbd>TAB</kbd> key on your keyboard to move between colums and the <kbd>ENTER</kbd> or <kbd>RETURN</kbd> key to save each row and move on to the next.', 'essential-content-types' ); ?></p>
932
-
933
- <form method="post" action="" enctype="multipart/form-data">
934
- <p><h3><?php esc_html_e( 'Add to section:', 'essential-content-types' ); ?> <?php wp_dropdown_categories( array(
935
- 'id' => 'ect-food-menu-tax',
936
- 'name' => 'ect_food_menu_tax',
937
- 'taxonomy' => self::MENU_TAX,
938
- 'hide_empty' => false,
939
- 'hierarchical' => true,
940
- ) ); ?></h3></p>
941
-
942
- <table class="many-items-table wp-list-table widefat">
943
- <thead>
944
- <tr>
945
- <th scope="col"><?php esc_html_e( 'Name', 'essential-content-types' ); ?></th>
946
- <th scope="col" class="ect-food-price"><?php esc_html_e( 'Price', 'essential-content-types' ); ?></th>
947
- <th scope="col"><?php _e( 'Labels: <small>spicy, favorite, etc. <em>Separate Labels with commas</em></small>', 'essential-content-types' ); ?></th>
948
- <th scope="col"><?php esc_html_e( 'Description', 'essential-content-types' ); ?></th>
949
- </tr>
950
- </thead>
951
- <tbody>
952
- <tr>
953
- <td><input type="text" name="ect_food_title[]" aria-required="true" /></td>
954
- <td class="ect-food-price"><input type="text" name="ect_food_price[]" /></td>
955
- <td><input type="text" name="ect_food_labels[]" /></td>
956
- <td><textarea name="ect_food_content[]" cols="20" rows="1"></textarea>
957
- </tr>
958
- </tbody>
959
- <tbody>
960
- <tr>
961
- <td><input type="text" name="ect_food_title[]" aria-required="true" /></td>
962
- <td class="ect-food-price"><input type="text" name="ect_food_price[]" /></td>
963
- <td><input type="text" name="ect_food_labels[]" /></td>
964
- <td><textarea name="ect_food_content[]" cols="20" rows="1"></textarea>
965
- </tr>
966
- </tbody>
967
- <tfoot>
968
- <tr>
969
- <th><a class="button button-secondary ect-food-new-row"><span class="dashicon dashicon-plus"></span> <?php esc_html_e( 'New Row' , 'essential-content-types' ); ?></a></th>
970
- <th class="ect-food-price"></th>
971
- <th></th>
972
- <th></th>
973
- </tr>
974
- </tfoot>
975
- </table>
976
-
977
- <p class="submit">
978
- <input type="submit" class="button-primary" value="<?php esc_attr_e( 'Add These New Menu Items', 'essential-content-types' ); ?>" />
979
- <?php wp_nonce_field( 'ect_food_many_items' ); ?>
980
- </p>
981
- </form>
982
- </div>
983
- <?php
984
- }
985
-
986
- /* Edit One Item */
987
-
988
- function register_menu_item_meta_boxes() {
989
- wp_enqueue_script( 'ect-food-menu-checkboxes' );
990
-
991
- add_meta_box( 'menu_item_price', __( 'Price', 'essential-content-types' ), array( $this, 'menu_item_price_meta_box' ), null, 'side', 'high' );
992
- }
993
-
994
- function menu_item_price_meta_box( $post, $meta_box ) {
995
- $price = $this->get_price( $post->ID );
996
- ?>
997
- <label for="ect-food-price-<?php echo (int) $post->ID; ?>" class="screen-reader-text"><?php esc_html_e( 'Price', 'essential-content-types' ); ?></label>
998
- <input type="text" id="ect-food-price-<?php echo (int) $post->ID; ?>" class="widefat" name="ect_food_price[<?php echo (int) $post->ID; ?>]" value="<?php echo esc_attr( $price ); ?>" />
999
- <?php
1000
- }
1001
-
1002
- function add_post_meta( $post_id ) {
1003
- if ( !isset( $_POST['ect_food_price'][$post_id] ) ) {
1004
- return;
1005
- }
1006
-
1007
- $this->set_price( $post_id, stripslashes( $_POST['ect_food_price'][$post_id] ) );
1008
- }
1009
-
1010
- /* Data */
1011
-
1012
- function get_menus( $args = array() ) {
1013
- $args = wp_parse_args( $args, array(
1014
- 'hide_empty' => false,
1015
- ) );
1016
-
1017
- $terms = get_terms( self::MENU_TAX, $args );
1018
- if ( !$terms || is_wp_error( $terms ) ) {
1019
- return array();
1020
- }
1021
-
1022
- $terms_by_id = array();
1023
- foreach ( $terms as $term ) {
1024
- $terms_by_id["{$term->term_id}"] = $term;
1025
- }
1026
-
1027
- $term_order = get_option( 'ect_food_menu_order', array() );
1028
-
1029
- $return = array();
1030
- foreach ( $term_order as $term_id ) {
1031
- if ( isset( $terms_by_id["$term_id"] ) ) {
1032
- $return[] = $terms_by_id["$term_id"];
1033
- unset( $terms_by_id["$term_id"] );
1034
- }
1035
- }
1036
-
1037
- foreach ( $terms_by_id as $term_id => $term ) {
1038
- $return[] = $term;
1039
- }
1040
-
1041
- return $return;
1042
- }
1043
-
1044
- function get_menu_item_menu_leaf( $post_id ) {
1045
- // Get first menu taxonomy "leaf"
1046
- $term_ids = wp_get_object_terms( $post_id, self::MENU_TAX, array( 'fields' => 'ids' ) );
1047
-
1048
- foreach ( $term_ids as $term_id ) {
1049
- $children = get_term_children( $term_id, self::MENU_TAX );
1050
- if ( ! $children ) {
1051
- break;
1052
- }
1053
- }
1054
-
1055
- if ( ! isset( $term_id ) ) {
1056
- return false;
1057
- }
1058
-
1059
- return get_term( $term_id, self::MENU_TAX );
1060
-
1061
- }
1062
-
1063
- function list_labels( $post_id = 0 ) {
1064
- $post = get_post( $post_id );
1065
- echo get_the_term_list( $post->ID, self::MENU_ITEM_LABEL_TAX, '', _x( ', ', 'Nova label separator', 'essential-content-types' ), '' );
1066
- }
1067
-
1068
- function list_admin_labels( $post_id = 0 ) {
1069
- $post = get_post( $post_id );
1070
- $labels = get_the_terms( $post->ID, self::MENU_ITEM_LABEL_TAX );
1071
- if ( !empty( $labels ) ) {
1072
- $out = array();
1073
- foreach ( $labels as $label ) {
1074
- $out[] = sprintf( '<a href="%s">%s</a>',
1075
- esc_url( add_query_arg( array(
1076
- 'post_type' => self::MENU_ITEM_POST_TYPE,
1077
- 'taxonomy' => self::MENU_ITEM_LABEL_TAX,
1078
- 'term' => $label->slug
1079
- ), 'edit.php' ) ),
1080
- esc_html( sanitize_term_field( 'name', $label->name, $label->term_id, self::MENU_ITEM_LABEL_TAX, 'display' ) )
1081
- );
1082
- }
1083
-
1084
- echo join( _x( ', ', 'Nova label separator', 'essential-content-types' ), $out );
1085
- } else {
1086
- esc_html_e( 'No Labels', 'essential-content-types' );
1087
- }
1088
- }
1089
-
1090
- function set_price( $post_id = 0, $price = '' ) {
1091
- $post = get_post( $post_id );
1092
-
1093
- return update_post_meta( $post->ID, 'ect_food_price', $price );
1094
- }
1095
-
1096
- function get_price( $post_id = 0 ) {
1097
- $post = get_post( $post_id );
1098
-
1099
- return get_post_meta( $post->ID, 'ect_food_price', true );
1100
- }
1101
-
1102
- function display_price( $post_id = 0 ) {
1103
- echo esc_html( $this->get_price( $post_id ) );
1104
- }
1105
-
1106
- /* Menu Item Loop Markup */
1107
-
1108
- /* Does not support nested loops */
1109
-
1110
- function get_menu_item_loop_markup( $field = null ) {
1111
- return $this->menu_item_loop_markup;
1112
- }
1113
-
1114
- /**
1115
- * Sets up the loop markup.
1116
- * Attached to the 'template_include' *filter*,
1117
- * which fires only during a real blog view (not in admin, feeds, etc.)
1118
- *
1119
- * @param string Template File
1120
- * @return string Template File. VERY Important.
1121
- */
1122
- function setup_menu_item_loop_markup__in_filter( $template ) {
1123
- add_action( 'loop_start', array( $this, 'start_menu_item_loop' ) );
1124
-
1125
- return $template;
1126
- }
1127
-
1128
- /**
1129
- * If the Query is a Menu Item Query, start outputing the Menu Item Loop Marku
1130
- * Attached to the 'loop_start' action.
1131
- *
1132
- * @param WP_Query
1133
- */
1134
- function start_menu_item_loop( $query ) {
1135
- if ( !$this->is_menu_item_query( $query ) ) {
1136
- return;
1137
- }
1138
-
1139
- $this->menu_item_loop_last_term_id = false;
1140
- $this->menu_item_loop_current_term = false;
1141
-
1142
- add_action( 'the_post', array( $this, 'menu_item_loop_each_post' ) );
1143
- add_action( 'loop_end', array( $this, 'stop_menu_item_loop' ) );
1144
- }
1145
-
1146
- /**
1147
- * Outputs the Menu Item Loop Marku
1148
- * Attached to the 'the_post' action.
1149
- *
1150
- * @param WP_Post
1151
- */
1152
- function menu_item_loop_each_post( $post ) {
1153
- $this->menu_item_loop_current_term = $this->get_menu_item_menu_leaf( $post->ID );
1154
-
1155
- if ( false === $this->menu_item_loop_last_term_id ) {
1156
- // We're at the very beginning of the loop
1157
-
1158
- $this->menu_item_loop_open_element( 'menu' ); // Start a new menu section
1159
- $this->menu_item_loop_header(); // Output the menu's header
1160
- } elseif ( $this->menu_item_loop_last_term_id != $this->menu_item_loop_current_term->term_id ) {
1161
- // We're not at the very beginning but still need to start a new menu section. End the previous menu section first.
1162
-
1163
- $this->menu_item_loop_close_element( 'menu' ); // End the previous menu section
1164
- $this->menu_item_loop_open_element( 'menu' ); // Start a new menu section
1165
- $this->menu_item_loop_header(); // Output the menu's header
1166
- }
1167
-
1168
- $this->menu_item_loop_last_term_id = isset( $this->menu_item_loop_current_term->term_id ) ? $this->menu_item_loop_current_term->term_id : '' ;
1169
- }
1170
-
1171
- /**
1172
- * If the Query is a Menu Item Query, stop outputing the Menu Item Loop Marku
1173
- * Attached to the 'loop_end' action.
1174
- *
1175
- * @param WP_Query
1176
- */
1177
- function stop_menu_item_loop( $query ) {
1178
- if ( !$this->is_menu_item_query( $query ) ) {
1179
- return;
1180
- }
1181
-
1182
- remove_action( 'the_post', array( $this, 'menu_item_loop_each_post' ) );
1183
- remove_action( 'loop_start', array( $this, 'start_menu_item_loop' ) );
1184
- remove_action( 'loop_end', array( $this, 'stop_menu_item_loop' ) );
1185
-
1186
- $this->menu_item_loop_close_element( 'menu' ); // End the last menu section
1187
- }
1188
-
1189
- /**
1190
- * Outputs the Menu Group Header
1191
- */
1192
- function menu_item_loop_header() {
1193
- $this->menu_item_loop_open_element( 'menu_header' );
1194
- $this->menu_item_loop_open_element( 'menu_title' );
1195
- echo isset( $this->menu_item_loop_current_term->name ) ? esc_html( $this->menu_item_loop_current_term->name ) : ''; // @todo tax filter
1196
- $this->menu_item_loop_close_element( 'menu_title' );
1197
- if ( isset( $this->menu_item_loop_current_term->description ) && $this->menu_item_loop_current_term->description ) :
1198
- $this->menu_item_loop_open_element( 'menu_description' );
1199
- echo esc_html( $this->menu_item_loop_current_term->description ); // @todo kses, tax filter
1200
- $this->menu_item_loop_close_element( 'menu_description' );
1201
- endif;
1202
- $this->menu_item_loop_close_element( 'menu_header' );
1203
- }
1204
-
1205
- /**
1206
- * Outputs a Menu Item Markup element opening tag
1207
- *
1208
- * @param string $field - Menu Item Markup settings field.
1209
- */
1210
- function menu_item_loop_open_element( $field ) {
1211
- $markup = $this->get_menu_item_loop_markup();
1212
- /**
1213
- * Filter a menu item's element opening tag.
1214
- *
1215
- * @module custom-content-types
1216
- *
1217
- * @since 4.4.0
1218
- *
1219
- * @param string $tag Menu item's element opening tag.
1220
- * @param string $field Menu Item Markup settings field.
1221
- * @param array $markup Array of markup elements for the menu item.
1222
- * @param false|object $term Taxonomy term for current menu item.
1223
- */
1224
- echo apply_filters(
1225
- 'essential-content-types_ect_food_menu_item_loop_open_element',
1226
- '<' . tag_escape( $markup["{$field}_tag"] ) . $this->menu_item_loop_class( $markup["{$field}_class"] ) . ">\n",
1227
- $field,
1228
- $markup,
1229
- $this->menu_item_loop_current_term
1230
- );
1231
- }
1232
-
1233
- /**
1234
- * Outputs a Menu Item Markup element closing tag
1235
- *
1236
- * @param string $field - Menu Item Markup settings field
1237
- */
1238
- function menu_item_loop_close_element( $field ) {
1239
- $markup = $this->get_menu_item_loop_markup();
1240
- /**
1241
- * Filter a menu item's element closing tag.
1242
- *
1243
- * @module custom-content-types
1244
- *
1245
- * @since 4.4.0
1246
- *
1247
- * @param string $tag Menu item's element closing tag.
1248
- * @param string $field Menu Item Markup settings field.
1249
- * @param array $markup Array of markup elements for the menu item.
1250
- * @param false|object $term Taxonomy term for current menu item.
1251
- */
1252
- echo apply_filters(
1253
- 'essential-content-types_ect_food_menu_item_loop_close_element',
1254
- '</' . tag_escape( $markup["{$field}_tag"] ) . ">\n",
1255
- $field,
1256
- $markup,
1257
- $this->menu_item_loop_current_term
1258
- );
1259
- }
1260
-
1261
- /**
1262
- * Returns a Menu Item Markup element's class attribute.
1263
- *
1264
- * @param string $class Class name.
1265
- * @return string HTML class attribute with leading whitespace.
1266
- */
1267
- function menu_item_loop_class( $class ) {
1268
- if ( ! $class ) {
1269
- return '';
1270
- }
1271
-
1272
- /**
1273
- * Filter a menu Item Markup element's class attribute.
1274
- *
1275
- * @module custom-content-types
1276
- *
1277
- * @since 4.4.0
1278
- *
1279
- * @param string $tag Menu Item Markup element's class attribute.
1280
- * @param string $class Menu Item Class name.
1281
- * @param false|object $term Taxonomy term for current menu item.
1282
- */
1283
- return apply_filters(
1284
- 'essential-content-types_ect_food_menu_item_loop_class',
1285
- ' class="' . esc_attr( $class ) . '"',
1286
- $class,
1287
- $this->menu_item_loop_current_term
1288
- );
1289
- }
1290
-
1291
- /**
1292
- * Our [food_menu] shortcode.
1293
- * Prints Food Menu data styled to look good on *any* theme.
1294
- *
1295
- * @return ect_food_shortcode_html
1296
- */
1297
- static function ect_food_shortcode( $atts ) {
1298
- // Default attributes
1299
- $atts = shortcode_atts( array(
1300
- 'include_type' => false,
1301
- 'include_tag' => false,
1302
- 'showposts' => -1,
1303
- ), $atts, 'food_menu' );
1304
-
1305
- // A little sanitization
1306
- if ( $atts['include_type'] ) {
1307
- $atts['include_type'] = explode( ',', str_replace( ' ', '', $atts['include_type'] ) );
1308
- }
1309
-
1310
- if ( $atts['include_tag'] ) {
1311
- $atts['include_tag'] = explode( ',', str_replace( ' ', '', $atts['include_tag'] ) );
1312
- }
1313
-
1314
- $atts['showposts'] = intval( $atts['showposts'] );
1315
-
1316
- // enqueue shortcode styles when shortcode is used
1317
- wp_enqueue_style( 'ect-food-menu-style', plugins_url( 'css/food-menu-shortcode.css', __FILE__ ), array(), '20140326' );
1318
- wp_enqueue_script( 'ect-food-menu-script', plugins_url( 'js/food-menu-shortcode.js', __FILE__ ) , array( 'jquery' ), '20180530', false );
1319
-
1320
- return self::ect_food_shortcode_html( $atts );
1321
- }
1322
-
1323
- /**
1324
- * Query to retrieve entries from the Food Menu post_type.
1325
- *
1326
- * @return object
1327
- */
1328
- static function ect_food_query( $atts ) {
1329
- // Default query arguments
1330
- $default = array(
1331
- 'posts_per_page' => $atts['showposts'],
1332
- );
1333
-
1334
- $args = wp_parse_args( $atts, $default );
1335
- $args['post_type'] = self::MENU_ITEM_POST_TYPE; // Force this post type
1336
-
1337
- if ( false != $atts['include_type'] || false != $atts['include_tag'] ) {
1338
- $args['tax_query'] = array();
1339
- }
1340
-
1341
- // If 'include_type' has been set use it on the main query
1342
- if ( false != $atts['include_type'] ) {
1343
- array_push( $args['tax_query'], array(
1344
- 'taxonomy' => self::MENU_TAX,
1345
- 'field' => 'slug',
1346
- 'terms' => $atts['include_type'],
1347
- ) );
1348
- }
1349
-
1350
- // If 'include_tag' has been set use it on the main query
1351
- if ( false != $atts['include_tag'] ) {
1352
- array_push( $args['tax_query'], array(
1353
- 'taxonomy' => self::MENU_ITEM_LABEL_TAX,
1354
- 'field' => 'slug',
1355
- 'terms' => $atts['include_tag'],
1356
- ) );
1357
- }
1358
-
1359
- if ( false != $atts['include_type'] && false != $atts['include_tag'] ) {
1360
- $args['tax_query']['relation'] = 'AND';
1361
- }
1362
-
1363
- // Run the query and return
1364
- $query = new WP_Query( $args );
1365
- return $query;
1366
- }
1367
-
1368
- /**
1369
- * The Food Menu shortcode loop.
1370
- *
1371
- * @return html
1372
- */
1373
- static function ect_food_shortcode_html( $atts ) {
1374
-
1375
- $query = self::ect_food_query( $atts );
1376
- ob_start();
1377
-
1378
- // If we have posts, create the html
1379
- // with hect_food markup
1380
- if ( $query->have_posts() ) {
1381
-
1382
- /**
1383
- * Hook: ect_before_food_menu_loop.
1384
- *
1385
- * @hooked ect_food_menu_section
1386
- */
1387
- $layout = ect_get_layout();
1388
- do_action( 'ect_before_food_menu_loop' );
1389
- ?>
1390
- <?php
1391
-
1392
- ect_get_template_part( 'ect', 'menu', $atts );
1393
-
1394
- // If comments are open or we have at least one comment, load up the comment template
1395
- if ( comments_open() || '0' != get_comments_number() ) {
1396
- comments_template( '', true );
1397
- }
1398
- ?>
1399
- <?php
1400
-
1401
- /**
1402
- * Hook: ect_after_food_menu_loop.
1403
- *
1404
- * @hooked
1405
- */
1406
- do_action( 'ect_after_food_menu_loop' );
1407
-
1408
- } else {
1409
- /**
1410
- * Hook: ect_no_food_menu_found.
1411
- *
1412
- * @hooked ect_no_food_menu_found
1413
- */
1414
- do_action( 'ect_no_food_menu_found' );
1415
- }
1416
-
1417
- $html = ob_get_clean();
1418
-
1419
- // If there is a [food-menu] within a [food-menu], remove the shortcode
1420
- if ( has_shortcode( $html, 'food-menu' ) ){
1421
- remove_shortcode( 'food-menu' );
1422
- }
1423
-
1424
- // Return the HTML block
1425
- return $html;
1426
- }
1427
- }
1428
-
1429
- add_action( 'init', array( 'Essential_Content_Food_Menu', 'init' ) );
1430
-
1431
-
1432
- /**
1433
- * Add Food Menu support
1434
- */
1435
- function essential_content_food_menu_support() {
1436
- /*
1437
- * Adding theme support for Food Menu Item CPT.
1438
- */
1439
- add_theme_support( 'ect_food_menu_item' );
1440
- }
1441
- add_action( 'after_setup_theme', 'essential_content_food_menu_support' );
1442
-
1443
-
1444
- if( ! function_exists( 'essential_content_no_food_menu_found' ) ):
1445
- /**
1446
- * No items found text
1447
- *
1448
- * @return html
1449
- */
1450
- function essential_content_no_food_menu_found() {
1451
- echo "<p><em>" . esc_html__( 'Your Food Menu Archive currently has no entries. You can start creating them on your dashboard.', 'essential-content-types-pro' ) . "</em></p>";
1452
- }
1453
- endif;
1454
- add_action( 'ect_no_food_menu_found', 'essential_content_no_food_menu_found', 10 );
1455
-
1456
-
1457
- if( ! function_exists( 'essential_content_food_menu_section_open' ) ):
1458
- /**
1459
- * Open section
1460
- *
1461
- * @return html
1462
- */
1463
- function essential_content_food_menu_section_open() {
1464
- echo '<div class="ect-menu ect-section">';
1465
- echo '<div class="ect-wrapper">';
1466
- }
1467
- endif;
1468
- add_action( 'ect_before_food_menu_loop', 'essential_content_food_menu_section_open', 10, 1 );
1469
-
1470
-
1471
- if( ! function_exists( 'essential_content_food_menu_loop_start' ) ):
1472
- /**
1473
- * open wrapper before loop
1474
- *
1475
- */
1476
- function essential_content_food_menu_loop_start(){
1477
- echo '<div class="section-content-wrapper menu-content-wrapper">';
1478
- }
1479
- endif;
1480
- add_action( 'ect_before_food_menu_loop', 'essential_content_food_menu_loop_start', 30 );
1481
-
1482
-
1483
- if( ! function_exists( 'essential_content_food_menu_loop_end' ) ):
1484
- /**
1485
- * close wrapper after loop
1486
- *
1487
- */
1488
- function essential_content_food_menu_loop_end(){
1489
- echo '</div><!-- .menu-content-wrapper -->';
1490
- }
1491
- endif;
1492
- add_action( 'ect_after_food_menu_loop', 'essential_content_food_menu_loop_end', 10 );
1493
-
1494
-
1495
- if( ! function_exists( 'essential_content_food_menu_section_close' ) ):
1496
- /**
1497
- * Close section
1498
- *
1499
- * @return html
1500
- */
1501
- function essential_content_food_menu_section_close() {
1502
- echo '</div><!-- .ect-wrapper -->';
1503
- echo '</div><!-- .ect-section -->';
1504
- }
1505
- endif;
1506
- add_action( 'ect_after_food_menu_loop', 'essential_content_food_menu_section_close', 20 );
1507
-
1508
- function essential_content_food_menu_get_menus( $args = array() ) {
1509
- $args = wp_parse_args( $args, array(
1510
- 'hide_empty' => false,
1511
- ) );
1512
-
1513
- $terms = get_terms( 'ect_food_menu', $args );
1514
- if ( !$terms || is_wp_error( $terms ) ) {
1515
- return array();
1516
- }
1517
-
1518
- $terms_by_id = array();
1519
- foreach ( $terms as $term ) {
1520
- $terms_by_id["{$term->term_id}"] = $term;
1521
- }
1522
-
1523
- $term_order = get_option( 'ect_food_menu_order', array() );
1524
-
1525
- $return = array();
1526
- foreach ( $term_order as $term_id ) {
1527
- if ( isset( $terms_by_id["$term_id"] ) ) {
1528
- $return[] = $terms_by_id["$term_id"];
1529
- unset( $terms_by_id["$term_id"] );
1530
- }
1531
- }
1532
-
1533
- foreach ( $terms_by_id as $term_id => $term ) {
1534
- $return[] = $term;
1535
- }
1536
-
1537
- return $return;
1538
  }
1
+ <?php
2
+ /*
3
+ * Put the following code in your theme's Food Menu Page Template to customize the markup of the menu.
4
+
5
+ if ( class_exists( 'Essential_Content_Food_Menu' ) ) {
6
+ Essential_Content_Food_Menu::init( array(
7
+ 'menu_tag' => 'section',
8
+ 'menu_class' => 'menu-items',
9
+ 'menu_header_tag' => 'header',
10
+ 'menu_header_class' => 'menu-group-header',
11
+ 'menu_title_tag' => 'h1',
12
+ 'menu_title_class' => 'menu-group-title',
13
+ 'menu_description_tag' => 'div',
14
+ 'menu_description_class' => 'menu-group-description',
15
+ ) );
16
+ }
17
+
18
+ */
19
+
20
+ /* @todo
21
+
22
+ Bulk/Quick edit response of Menu Item rows is broken.
23
+
24
+ Drag and Drop reordering.
25
+ */
26
+
27
+ class Essential_Content_Food_Menu {
28
+ const MENU_ITEM_POST_TYPE = 'ect_food_menu_item';
29
+ const MENU_ITEM_LABEL_TAX = 'ect_food_menu_item_label';
30
+ const MENU_TAX = 'ect_food_menu';
31
+
32
+ public $version = ESSENTIAL_CONTENT_TYPES_VERSION;
33
+
34
+ protected $default_menu_item_loop_markup = array(
35
+ 'menu_tag' => 'section',
36
+ 'menu_class' => 'menu-items',
37
+ 'menu_header_tag' => 'header',
38
+ 'menu_header_class' => 'menu-group-header',
39
+ 'menu_title_tag' => 'h1',
40
+ 'menu_title_class' => 'menu-group-title',
41
+ 'menu_description_tag' => 'div',
42
+ 'menu_description_class' => 'menu-group-description',
43
+ );
44
+
45
+ protected $menu_item_loop_markup = array();
46
+ protected $menu_item_loop_last_term_id = false;
47
+ protected $menu_item_loop_current_term = false;
48
+
49
+ static function init( $menu_item_loop_markup = array() ) {
50
+ static $instance = false;
51
+
52
+ if ( !$instance ) {
53
+ $instance = new Essential_Content_Food_Menu;
54
+ }
55
+
56
+ if ( $menu_item_loop_markup ) {
57
+ $instance->menu_item_loop_markup = wp_parse_args( $menu_item_loop_markup, $instance->default_menu_item_loop_markup );
58
+ }
59
+
60
+ return $instance;
61
+ }
62
+
63
+ function __construct() {
64
+ if ( ! $this->site_supports_food_menu() )
65
+ return;
66
+
67
+ $this->register_taxonomies();
68
+ $this->register_post_types();
69
+ add_action( 'admin_menu', array( $this, 'add_admin_menus' ) );
70
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_ect_food_styles' ) );
71
+ add_action( 'admin_head', array( $this, 'set_custom_font_icon' ) );
72
+
73
+ // Enable Omnisearch for Menu Items.
74
+ if ( class_exists( 'Jetpack_Omnisearch_Posts' ) )
75
+ new Jetpack_Omnisearch_Posts( self::MENU_ITEM_POST_TYPE );
76
+
77
+ // Always sort menu items correctly
78
+ add_action( 'parse_query', array( $this, 'sort_menu_item_queries_by_menu_order' ) );
79
+ add_filter( 'posts_results', array( $this, 'sort_menu_item_queries_by_menu_taxonomy' ), 10, 2 );
80
+
81
+ add_action( 'wp_insert_post', array( $this, 'add_post_meta' ) );
82
+
83
+ $this->menu_item_loop_markup = $this->default_menu_item_loop_markup;
84
+
85
+ // Only output our Menu Item Loop Markup on a real blog view. Not feeds, XML-RPC, admin, etc.
86
+ add_filter( 'template_include', array( $this, 'setup_menu_item_loop_markup__in_filter' ) );
87
+
88
+ add_filter( 'enter_title_here', array( $this, 'change_default_title' ) );
89
+ add_filter( 'post_updated_messages', array( $this, 'updated_messages' ) );
90
+ add_filter( 'dashboard_glance_items', array( $this, 'add_to_dashboard' ) );
91
+
92
+ add_filter( 'body_class', array( $this, 'custom_class' ) );
93
+
94
+ // register food_menu shortcode (legacy)
95
+ add_shortcode( 'food_menu', array( $this, 'ect_food_shortcode' ) );
96
+ }
97
+
98
+ public function custom_class( $classes ) {
99
+ global $post;
100
+ if( isset( $post->post_content ) && has_shortcode( $post->post_content, 'food_menu' ) ) {
101
+ $classes[] = 'page-template-menu-page';
102
+ }
103
+ return $classes;
104
+ }
105
+
106
+ /**
107
+ * Should this Custom Post Type be made available?
108
+ */
109
+ function site_supports_food_menu() {
110
+ // If we're on WordPress.com, and it has the menu site vertical.
111
+ if ( function_exists( 'site_vertical' ) && 'ect_food_menu' == site_vertical() )
112
+ return true;
113
+
114
+ // Else, if the current theme requests it.
115
+ if ( current_theme_supports( self::MENU_ITEM_POST_TYPE ) )
116
+ return true;
117
+ }
118
+
119
+ /* Setup */
120
+
121
+ /**
122
+ * Register Taxonomies and Post Type
123
+ */
124
+ function register_taxonomies() {
125
+ if ( ! taxonomy_exists( self::MENU_ITEM_LABEL_TAX ) ) {
126
+ register_taxonomy( self::MENU_ITEM_LABEL_TAX, self::MENU_ITEM_POST_TYPE, array(
127
+ 'labels' => array(
128
+ /* translators: this is about a food menu */
129
+ 'name' => __( 'Menu Item Labels', 'essential-content-types' ),
130
+ /* translators: this is about a food menu */
131
+ 'singular_name' => __( 'Menu Item Label', 'essential-content-types' ),
132
+ /* translators: this is about a food menu */
133
+ 'search_items' => __( 'Search Menu Item Labels', 'essential-content-types' ),
134
+ 'popular_items' => __( 'Popular Labels', 'essential-content-types' ),
135
+ /* translators: this is about a food menu */
136
+ 'all_items' => __( 'All Menu Item Labels', 'essential-content-types' ),
137
+ /* translators: this is about a food menu */
138
+ 'edit_item' => __( 'Edit Menu Item Label', 'essential-content-types' ),
139
+ /* translators: this is about a food menu */
140
+ 'view_item' => __( 'View Menu Item Label', 'essential-content-types' ),
141
+ /* translators: this is about a food menu */
142
+ 'update_item' => __( 'Update Menu Item Label', 'essential-content-types' ),
143
+ /* translators: this is about a food menu */
144
+ 'add_new_item' => __( 'Add New Menu Item Label', 'essential-content-types' ),
145
+ /* translators: this is about a food menu */
146
+ 'new_item_name' => __( 'New Menu Item Label Name', 'essential-content-types' ),
147
+ 'separate_items_with_commas' => __( 'For example, spicy, favorite, etc. <br /> Separate Labels with commas', 'essential-content-types' ),
148
+ 'add_or_remove_items' => __( 'Add or remove Labels', 'essential-content-types' ),
149
+ 'choose_from_most_used' => __( 'Choose from the most used Labels', 'essential-content-types' ),
150
+ 'items_list_navigation' => __( 'Menu item label list navigation', 'essential-content-types' ),
151
+ 'items_list' => __( 'Menu item labels list', 'essential-content-types' ),
152
+ ),
153
+ 'no_tagcloud' => __( 'No Labels found', 'essential-content-types' ),
154
+ 'hierarchical' => false,
155
+ ) );
156
+ }
157
+
158
+ if ( ! taxonomy_exists( self::MENU_TAX ) ) {
159
+ register_taxonomy( self::MENU_TAX, self::MENU_ITEM_POST_TYPE, array(
160
+ 'labels' => array(
161
+ /* translators: this is about a food menu */
162
+ 'name' => __( 'Menu Sections', 'essential-content-types' ),
163
+ /* translators: this is about a food menu */
164
+ 'singular_name' => __( 'Menu Section', 'essential-content-types' ),
165
+ /* translators: this is about a food menu */
166
+ 'search_items' => __( 'Search Menu Sections', 'essential-content-types' ),
167
+ /* translators: this is about a food menu */
168
+ 'all_items' => __( 'All Menu Sections', 'essential-content-types' ),
169
+ /* translators: this is about a food menu */
170
+ 'parent_item' => __( 'Parent Menu Section', 'essential-content-types' ),
171
+ /* translators: this is about a food menu */
172
+ 'parent_item_colon' => __( 'Parent Menu Section:', 'essential-content-types' ),
173
+ /* translators: this is about a food menu */
174
+ 'edit_item' => __( 'Edit Menu Section', 'essential-content-types' ),
175
+ /* translators: this is about a food menu */
176
+ 'view_item' => __( 'View Menu Section', 'essential-content-types' ),
177
+ /* translators: this is about a food menu */
178
+ 'update_item' => __( 'Update Menu Section', 'essential-content-types' ),
179
+ /* translators: this is about a food menu */
180
+ 'add_new_item' => __( 'Add New Menu Section', 'essential-content-types' ),
181
+ /* translators: this is about a food menu */
182
+ 'new_item_name' => __( 'New Menu Sections Name', 'essential-content-types' ),
183
+ 'items_list_navigation' => __( 'Menu section list navigation', 'essential-content-types' ),
184
+ 'items_list' => __( 'Menu section list', 'essential-content-types' ),
185
+ ),
186
+ 'rewrite' => array(
187
+ 'slug' => 'menu',
188
+ 'with_front' => false,
189
+ 'hierarchical' => true,
190
+ ),
191
+ 'hierarchical' => true,
192
+ 'show_tagcloud' => false,
193
+ 'query_var' => 'menu',
194
+ ) );
195
+ }
196
+ }
197
+
198
+ function register_post_types() {
199
+ if ( post_type_exists( self::MENU_ITEM_POST_TYPE ) ) {
200
+ return;
201
+ }
202
+
203
+ register_post_type( self::MENU_ITEM_POST_TYPE, array(
204
+ 'description' => __( "Items on your restaurant's menu", 'essential-content-types' ),
205
+
206
+ 'labels' => array(
207
+ /* translators: this is about a food menu */
208
+ 'name' => __( 'Menu Items', 'essential-content-types' ),
209
+ /* translators: this is about a food menu */
210
+ 'singular_name' => __( 'Menu Item', 'essential-content-types' ),
211
+ /* translators: this is about a food menu */
212
+ 'menu_name' => __( 'Food Menus', 'essential-content-types' ),
213
+ /* translators: this is about a food menu */
214
+ 'all_items' => __( 'Menu Items', 'essential-content-types' ),
215
+ /* translators: this is about a food menu */
216
+ 'add_new' => __( 'Add One Item', 'essential-content-types' ),
217
+ /* translators: this is about a food menu */
218
+ 'add_new_item' => __( 'Add Menu Item', 'essential-content-types' ),
219
+ /* translators: this is about a food menu */
220
+ 'edit_item' => __( 'Edit Menu Item', 'essential-content-types' ),
221
+ /* translators: this is about a food menu */
222
+ 'new_item' => __( 'New Menu Item', 'essential-content-types' ),
223
+ /* translators: this is about a food menu */
224
+ 'view_item' => __( 'View Menu Item', 'essential-content-types' ),
225
+ /* translators: this is about a food menu */
226
+ 'search_items' => __( 'Search Menu Items', 'essential-content-types' ),
227
+ /* translators: this is about a food menu */
228
+ 'not_found' => __( 'No Menu Items found', 'essential-content-types' ),
229
+ /* translators: this is about a food menu */
230
+ 'not_found_in_trash' => __( 'No Menu Items found in Trash', 'essential-content-types' ),
231
+ 'filter_items_list' => __( 'Filter menu items list', 'essential-content-types' ),
232
+ 'items_list_navigation' => __( 'Menu item list navigation', 'essential-content-types' ),
233
+ 'items_list' => __( 'Menu items list', 'essential-content-types' ),
234
+ ),
235
+ 'supports' => array(
236
+ 'title',
237
+ 'editor',
238
+ 'excerpt',
239
+ ),
240
+ 'rewrite' => array(
241
+ 'slug' => 'item',
242
+ 'with_front' => false,
243
+ 'feeds' => false,
244
+ 'pages' => false,
245
+ ),
246
+ 'register_meta_box_cb' => array( $this, 'register_menu_item_meta_boxes' ),
247
+
248
+ 'public' => true,
249
+ 'show_ui' => true, // set to false to replace with custom UI
250
+ 'menu_position' => 20, // below Pages
251
+ 'capability_type' => 'page',
252
+ 'map_meta_cap' => true,
253
+ 'has_archive' => false,
254
+ 'query_var' => 'item',
255
+ ) );
256
+ }
257
+
258
+
259
+ /**
260
+ * Update messages for the Menu Item admin.
261
+ */
262
+ function updated_messages( $messages ) {
263
+ global $post;
264
+
265
+ $messages[self::MENU_ITEM_POST_TYPE] = array(
266
+ 0 => '', // Unused. Messages start at index 1.
267
+ /* translators: this is about a food menu */
268
+ 1 => sprintf( __( 'Menu item updated. <a href="%s">View item</a>', 'essential-content-types' ), esc_url( get_permalink( $post->ID ) ) ),
269
+ 2 => esc_html__( 'Custom field updated.', 'essential-content-types' ),
270
+ 3 => esc_html__( 'Custom field deleted.', 'essential-content-types' ),
271
+ /* translators: this is about a food menu */
272
+ 4 => esc_html__( 'Menu item updated.', 'essential-content-types' ),
273
+ /* translators: %s: date and time of the revision */
274
+ 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Menu item restored to revision from %s', 'essential-content-types' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
275
+ /* translators: this is about a food menu */
276
+ 6 => sprintf( __( 'Menu item published. <a href="%s">View item</a>', 'essential-content-types' ), esc_url( get_permalink( $post->ID ) ) ),
277
+ /* translators: this is about a food menu */
278
+ 7 => esc_html__( 'Menu item saved.', 'essential-content-types' ),
279
+ /* translators: this is about a food menu */
280
+ 8 => sprintf( __( 'Menu item submitted. <a target="_blank" href="%s">Preview item</a>', 'essential-content-types' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
281
+ /* translators: this is about a food menu */
282
+ 9 => sprintf( __( 'Menu item scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview item</a>', 'essential-content-types' ),
283
+ // translators: Publish box date format, see http://php.net/date
284
+ date_i18n( __( 'M j, Y @ G:i', 'essential-content-types' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post->ID) ) ),
285
+ /* translators: this is about a food menu */
286
+ 10 => sprintf( __( 'Menu item draft updated. <a target="_blank" href="%s">Preview item</a>', 'essential-content-types' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
287
+ );
288
+
289
+ return $messages;
290
+ }
291
+
292
+
293
+ /**
294
+ * Nova Styles and Scripts
295
+ */
296
+ function enqueue_ect_food_styles( $hook ) {
297
+ global $post_type;
298
+ $pages = array( 'edit.php', 'post.php', 'post-new.php' );
299
+
300
+ if ( in_array( $hook, $pages ) && $post_type == self::MENU_ITEM_POST_TYPE ) {
301
+ wp_enqueue_style( 'ect-food-menu-style', plugins_url( 'css/ect-food-menu.css', __FILE__ ), array(), $this->version );
302
+ }
303
+
304
+ wp_enqueue_style( 'ect-food-menu-font', plugins_url( 'css/ect-food-menu-font.css', __FILE__ ), array(), $this->version );
305
+ }
306
+
307
+
308
+ /**
309
+ * Change ‘Enter Title Here’ text for the Menu Item.
310
+ */
311
+ function change_default_title( $title ) {
312
+ $screen = get_current_screen();
313
+
314
+ if ( self::MENU_ITEM_POST_TYPE == $screen->post_type )
315
+ /* translators: this is about a food menu */
316
+ $title = esc_html__( "Enter the menu item's name here", 'essential-content-types' );
317
+
318
+ return $title;
319
+ }
320
+
321
+
322
+ /**
323
+ * Add to Dashboard At A Glance
324
+ */
325
+ function add_to_dashboard() {
326
+ $number_menu_items = wp_count_posts( self::MENU_ITEM_POST_TYPE );
327
+
328
+ if ( current_user_can( 'administrator' ) ) {
329
+ $number_menu_items_published = sprintf( '<a href="%1$s">%2$s</a>',
330
+ esc_url( get_admin_url( get_current_blog_id(), 'edit.php?post_type=' . self::MENU_ITEM_POST_TYPE ) ),
331
+ sprintf( _n( '%1$d Food Menu Item', '%1$d Food Menu Items', intval( $number_menu_items->publish ), 'essential-content-types' ), number_format_i18n( $number_menu_items->publish ) )
332
+ );
333
+ }
334
+ else {
335
+ $number_menu_items_published = sprintf( '<span>%1$s</span>',
336
+ sprintf( _n( '%1$d Food Menu Item', '%1$d Food Menu Items', intval( $number_menu_items->publish ), 'essential-content-types' ), number_format_i18n( $number_menu_items->publish ) )
337
+ );
338
+ }
339
+
340
+ echo '<li class="ect-food-menu-count">' . $number_menu_items_published . '</li>';
341
+ }
342
+
343
+
344
+ /**
345
+ * Query
346
+ */
347
+ function is_menu_item_query( $query ) {
348
+ if (
349
+ ( isset( $query->query_vars['taxonomy'] ) && self::MENU_TAX == $query->query_vars['taxonomy'] )
350
+ ||
351
+ ( isset( $query->query_vars['post_type'] ) && self::MENU_ITEM_POST_TYPE == $query->query_vars['post_type'] )
352
+ ) {
353
+ return true;
354
+ }
355
+
356
+ return false;
357
+ }
358
+
359
+ function sort_menu_item_queries_by_menu_order( $query ) {
360
+ if ( ! $this->is_menu_item_query( $query ) ) {
361
+ return;
362
+ }
363
+
364
+ $query->query_vars['orderby'] = 'menu_order';
365
+ $query->query_vars['order'] = 'ASC';
366
+
367
+ // For now, just turn off paging so we can sort by taxonmy later
368
+ // If we want paging in the future, we'll need to add the taxonomy sort here (or at least before the DB query is made)
369
+ $query->query_vars['posts_per_page'] = -1;
370
+ }
371
+
372
+ function sort_menu_item_queries_by_menu_taxonomy( $posts, $query ) {
373
+ if ( !$posts ) {
374
+ return $posts;
375
+ }
376
+
377
+ if ( !$this->is_menu_item_query( $query ) ) {
378
+ return $posts;
379
+ }
380
+
381
+ $grouped_by_term = array();
382
+
383
+ foreach ( $posts as $post ) {
384
+ $term = $this->get_menu_item_menu_leaf( $post->ID );
385
+ if ( !$term || is_wp_error( $term ) ) {
386
+ $term_id = 0;
387
+ } else {
388
+ $term_id = $term->term_id;
389
+ }
390
+
391
+ if ( !isset( $grouped_by_term["$term_id"] ) ) {
392
+ $grouped_by_term["$term_id"] = array();
393
+ }
394
+
395
+ $grouped_by_term["$term_id"][] = $post;
396
+ }
397
+
398
+ $term_order = get_option( 'ect_food_menu_order', array() );
399
+
400
+ $return = array();
401
+ foreach ( $term_order as $term_id ) {
402
+ if ( isset( $grouped_by_term["$term_id"] ) ) {
403
+ $return = array_merge( $return, $grouped_by_term["$term_id"] );
404
+ unset( $grouped_by_term["$term_id"] );
405
+ }
406
+ }
407
+
408
+ foreach ( $grouped_by_term as $term_id => $posts ) {
409
+ $return = array_merge( $return, $posts );
410
+ }
411
+
412
+ return $return;
413
+ }
414
+
415
+
416
+ /**
417
+ * Add Many Items
418
+ */
419
+ function add_admin_menus() {
420
+ $hook = add_submenu_page(
421
+ 'edit.php?post_type=' . self::MENU_ITEM_POST_TYPE,
422
+ __( 'Add Many Items', 'essential-content-types' ),
423
+ __( 'Add Many Items', 'essential-content-types' ),
424
+ 'edit_pages',
425
+ 'add_many_ect_food_items',
426
+ array( $this, 'add_many_new_items_page' )
427
+ );
428
+
429
+ add_action( "load-$hook", array( $this, 'add_many_new_items_page_load' ) );
430
+
431
+ add_action( 'current_screen', array( $this, 'current_screen_load' ) );
432
+
433
+ //Adjust 'Add Many Items' submenu position
434
+ if ( isset( $GLOBALS['submenu']['edit.php?post_type=' . self::MENU_ITEM_POST_TYPE] ) ) {
435
+ $submenu_item = array_pop( $GLOBALS['submenu']['edit.php?post_type=' . self::MENU_ITEM_POST_TYPE] );
436
+ $GLOBALS['submenu']['edit.php?post_type=' . self::MENU_ITEM_POST_TYPE][11] = $submenu_item;
437
+ ksort( $GLOBALS['submenu']['edit.php?post_type=' . self::MENU_ITEM_POST_TYPE] );
438
+ }
439
+
440
+
441
+ $this->setup_menu_item_columns();
442
+
443
+ wp_register_script(
444
+ 'ect-food-menu-checkboxes',
445
+ plugin_dir_url( __FILE__ ) . 'js/ect-food-menu-menu-checkboxes.js',
446
+ array( 'jquery' ),
447
+ $this->version,
448
+ false
449
+ );
450
+ }
451
+
452
+
453
+ /**
454
+ * Custom Nova Icon CSS
455
+ */
456
+ function set_custom_font_icon() {
457
+ ?>
458
+ <style type="text/css">
459
+ #menu-posts-ect_food_menu_item .wp-menu-image:before {
460
+ font-family: 'nova-font' !important;
461
+ content: '\e603' !important;
462
+ }
463
+ </style>
464
+ <?php
465
+ }
466
+
467
+ function current_screen_load() {
468
+ $screen = get_current_screen();
469
+ if ( 'edit-ect_food_menu_item' !== $screen->id ) {
470
+ return;
471
+ }
472
+
473
+ $this->edit_menu_items_page_load();
474
+ add_filter( 'admin_notices', array( $this, 'admin_notices' ) );
475
+ }
476
+
477
+ /* Edit Items List */
478
+
479
+ function admin_notices() {
480
+ if ( isset( $_GET['ect_food_reordered'] ) )
481
+ /* translators: this is about a food menu */
482
+ printf( '<div class="updated"><p>%s</p></div>', __( 'Menu Items re-ordered.', 'essential-content-types' ) );
483
+ }
484
+
485
+ function no_title_sorting( $columns ) {
486
+ if ( isset( $columns['title'] ) )
487
+ unset( $columns['title'] );
488
+ return $columns;
489
+ }
490
+
491
+ function setup_menu_item_columns() {
492
+ add_filter( sprintf( 'manage_edit-%s_sortable_columns', self::MENU_ITEM_POST_TYPE ), array( $this, 'no_title_sorting' ) );
493
+ add_filter( sprintf( 'manage_%s_posts_columns', self::MENU_ITEM_POST_TYPE ), array( $this, 'menu_item_columns' ) );
494
+
495
+ add_action( sprintf( 'manage_%s_posts_custom_column', self::MENU_ITEM_POST_TYPE ), array( $this, 'menu_item_column_callback' ), 10, 2 );
496
+ }
497
+
498
+ function menu_item_columns( $columns ) {
499
+ unset( $columns['date'], $columns['likes'] );
500
+
501
+ $columns['thumbnail'] = __( 'Thumbnail', 'essential-content-types' );
502
+ $columns['labels'] = __( 'Labels', 'essential-content-types' );
503
+ $columns['price'] = __( 'Price', 'essential-content-types' );
504
+ $columns['order'] = __( 'Order', 'essential-content-types' );
505
+
506
+ return $columns;
507
+ }
508
+
509
+ function menu_item_column_callback( $column, $post_id ) {
510
+ $screen = get_current_screen();
511
+
512
+ switch ( $column ) {
513
+ case 'thumbnail':
514
+ echo get_the_post_thumbnail( $post_id, array( 50, 50 ) );
515
+ break;
516
+ case 'labels' :
517
+ $this->list_admin_labels( $post_id );
518
+ break;
519
+ case 'price' :
520
+ $this->display_price( $post_id );
521
+ break;
522
+ case 'order' :
523
+ $url = admin_url( $screen->parent_file );
524
+
525
+ $up_url = add_query_arg( array(
526
+ 'action' => 'move-item-up',
527
+ 'post_id' => (int) $post_id,
528
+ ), wp_nonce_url( $url, 'ect_food_move_item_up_' . $post_id ) );
529
+
530
+ $down_url = add_query_arg( array(
531
+ 'action' => 'move-item-down',
532
+ 'post_id' => (int) $post_id,
533
+ ), wp_nonce_url( $url, 'ect_food_move_item_down_' . $post_id ) );
534
+ $menu_item = get_post($post_id);
535
+ $this->get_menu_by_post_id( $post_id );
536
+ if ( $term_id = $this->get_menu_by_post_id( $post_id ) ) {
537
+ $term_id = $term_id->term_id;
538
+ }
539
+ ?>
540
+ <input type="hidden" class="menu-order-value" name="ect_food_order[<?php echo (int) $post_id ?>]" value="<?php echo esc_attr( $menu_item->menu_order ) ?>" />
541
+ <input type="hidden" class='ect-food-menu-term' name="ect_food_menu_term[<?php echo (int) $post_id ?>]" value="<?php echo esc_attr( $term_id ); ?>">
542
+
543
+ <span class="hide-if-js">
544
+ &nbsp; &nbsp; &mdash; <a class="ect-food-move-item-up" data-post-id="<?php echo (int) $post_id; ?>" href="<?php echo esc_url( $up_url ); ?>">up</a>
545
+ <br />
546
+ &nbsp; &nbsp; &mdash; <a class="ect-food-move-item-down" data-post-id="<?php echo (int) $post_id; ?>" href="<?php echo esc_url( $down_url ); ?>">down</a>
547
+ </span>
548
+ <?php
549
+ break;
550
+ }
551
+ }
552
+
553
+ function get_menu_by_post_id( $post_id = null ) {
554
+ if ( ! $post_id )
555
+ return false;
556
+
557
+ $terms = get_the_terms( $post_id, self::MENU_TAX );
558
+
559
+ if ( ! is_array( $terms ) )
560
+ return false;
561
+
562
+ return array_pop( $terms );
563
+ }
564
+
565
+ /**
566
+ * Fires on a menu edit page. We might have drag-n-drop reordered
567
+ */
568
+ function maybe_reorder_menu_items() {
569
+ // make sure we clicked our button
570
+ if ( ! ( isset( $_REQUEST['menu_reorder_submit'] ) && $_REQUEST['menu_reorder_submit'] === __( 'Save New Order', 'essential-content-types' ) ) )
571
+ return;
572
+ ;
573
+
574
+ // make sure we have the nonce
575
+ if ( ! ( isset( $_REQUEST['drag-drop-reorder'] ) && wp_verify_nonce( $_REQUEST['drag-drop-reorder'], 'drag-drop-reorder' ) ) )
576
+ return;
577
+
578
+ $term_pairs = array_map( 'absint', $_REQUEST['ect_food_menu_term'] );
579
+ $order_pairs = array_map( 'absint', $_REQUEST['ect_food_order'] );
580
+
581
+ foreach( $order_pairs as $ID => $menu_order ) {
582
+ $ID = absint( $ID );
583
+ unset( $order_pairs[$ID] );
584
+ if ( $ID < 0 )
585
+ continue;
586
+
587
+ $post = get_post( $ID );
588
+ if ( ! $post )
589
+ continue;
590
+
591
+ // save a write if the order hasn't changed
592
+ if ( $menu_order != $post->menu_order )
593
+ wp_update_post( compact( 'ID', 'menu_order' ) );
594
+
595
+ // save a write if the term hasn't changed
596
+ if ( $term_pairs[$ID] != $this->get_menu_by_post_id( $ID )->term_id )
597
+ wp_set_object_terms( $ID, $term_pairs[$ID], self::MENU_TAX );
598
+
599
+ }
600
+
601
+ $redirect = add_query_arg( array(
602
+ 'post_type' => self::MENU_ITEM_POST_TYPE,
603
+ 'ect_food_reordered' => '1'
604
+ ), admin_url( 'edit.php' ) );
605
+ wp_safe_redirect( $redirect );
606
+ exit;
607
+
608
+ }
609
+
610
+ function edit_menu_items_page_load() {
611
+ if ( isset( $_GET['action'] ) ) {
612
+ $this->handle_menu_item_actions();
613
+ }
614
+
615
+ $this->maybe_reorder_menu_items();
616
+
617
+ wp_enqueue_script(
618
+ 'ect-food-drag-drop',
619
+ plugin_dir_url( __FILE__ ) . 'js/ect-food-menu-drag-drop.js',
620
+ array( 'jquery-ui-sortable' ),
621
+ $this->version,
622
+ true
623
+ );
624
+
625
+ wp_localize_script( 'ect-food-drag-drop', '_novaDragDrop', array(
626
+ 'nonce' => wp_create_nonce( 'drag-drop-reorder' ),
627
+ 'nonceName' => 'drag-drop-reorder',
628
+ 'reorder' => __( 'Save New Order', 'essential-content-types' ),
629
+ 'reorderName' => 'menu_reorder_submit'
630
+ ) );
631
+ add_action( 'the_post', array( $this, 'show_menu_titles_in_menu_item_list' ) );
632
+ }
633
+
634
+ function handle_menu_item_actions() {
635
+ $action = (string) $_GET['action'];
636
+
637
+ switch ( $action ) {
638
+ case 'move-item-up' :
639
+ case 'move-item-down' :
640
+ $reorder = false;
641
+
642
+ $post_id = (int) $_GET['post_id'];
643
+
644
+ $term = $this->get_menu_item_menu_leaf( $post_id );
645
+
646
+ // Get all posts in that term
647
+ $query = new WP_Query( array(
648
+ 'taxonomy' => self::MENU_TAX,
649
+ 'term' => $term->slug,
650
+ ) );
651
+
652
+ $order = array();
653
+ foreach ( $query->posts as $post ) {
654
+ $order[] = $post->ID;
655
+ }
656
+
657
+ if ( 'move-item-up' == $action ) {
658
+ check_admin_referer( 'ect_food_move_item_up_' . $post_id );
659
+
660
+ $first_post_id = $order[0];
661
+ if ( $post_id == $first_post_id ) {
662
+ break;
663
+ }
664
+
665
+ foreach ( $order as $menu_order => $order_post_id ) {
666
+ if ( $post_id != $order_post_id ) {
667
+ continue;
668
+ }
669
+
670
+ $swap_post_id = $order[$menu_order - 1];
671
+ $order[$menu_order - 1] = $post_id;
672
+ $order[$menu_order] = $swap_post_id;
673
+
674
+ $reorder = true;
675
+ break;
676
+ }
677
+ } else {
678
+ check_admin_referer( 'ect_food_move_item_down_' . $post_id );
679
+
680
+ $last_post_id = end( $order );
681
+ if ( $post_id == $last_post_id ) {
682
+ break;
683
+ }
684
+
685
+ foreach ( $order as $menu_order => $order_post_id ) {
686
+ if ( $post_id != $order_post_id ) {
687
+ continue;
688
+ }
689
+
690
+ $swap_post_id = $order[$menu_order + 1];
691
+ $order[$menu_order + 1] = $post_id;
692
+ $order[$menu_order] = $swap_post_id;
693
+
694
+ $reorder = true;
695
+ }
696
+ }
697
+
698
+ if ( $reorder ) {
699
+ foreach ( $order as $menu_order => $ID ) {
700
+ wp_update_post( compact( 'ID', 'menu_order' ) );
701
+ }
702
+ }
703
+
704
+ break;
705
+ case 'move-menu-up' :
706
+ case 'move-menu-down' :
707
+ $reorder = false;
708
+
709
+ $term_id = (int) $_GET['term_id'];
710
+
711
+ $terms = $this->get_menus();
712
+
713
+ $order = array();
714
+ foreach ( $terms as $term ) {
715
+ $order[] = $term->term_id;
716
+ }
717
+
718
+ if ( 'move-menu-up' == $action ) {
719
+ check_admin_referer( 'ect_food_move_menu_up_' . $term_id );
720
+
721
+ $first_term_id = $order[0];
722
+ if ( $term_id == $first_term_id ) {
723
+ break;
724
+ }
725
+
726
+ foreach ( $order as $menu_order => $order_term_id ) {
727
+ if ( $term_id != $order_term_id ) {
728
+ continue;
729
+ }
730
+
731
+ $swap_term_id = $order[$menu_order - 1];
732
+ $order[$menu_order - 1] = $term_id;
733
+ $order[$menu_order] = $swap_term_id;
734
+
735
+ $reorder = true;
736
+ break;
737
+ }
738
+ } else {
739
+ check_admin_referer( 'ect_food_move_menu_down_' . $term_id );
740
+
741
+ $last_term_id = end( $order );
742
+ if ( $term_id == $last_term_id ) {
743
+ break;
744
+ }
745
+
746
+ foreach ( $order as $menu_order => $order_term_id ) {
747
+ if ( $term_id != $order_term_id ) {
748
+ continue;
749
+ }
750
+
751
+ $swap_term_id = $order[$menu_order + 1];
752
+ $order[$menu_order + 1] = $term_id;
753
+ $order[$menu_order] = $swap_term_id;
754
+
755
+ $reorder = true;
756
+ }
757
+ }
758
+
759
+ if ( $reorder ) {
760
+ update_option( 'ect_food_menu_order', $order );
761
+ }
762
+
763
+ break;
764
+ default :
765
+ return;
766
+ }
767
+
768
+ $redirect = add_query_arg( array(
769
+ 'post_type' => self::MENU_ITEM_POST_TYPE,
770
+ 'ect_food_reordered' => '1'
771
+ ), admin_url( 'edit.php' ) );
772
+ wp_safe_redirect( $redirect );
773
+ exit;
774
+ }
775
+
776
+ /*
777
+ * Add menu title rows to the list table
778
+ */
779
+ function show_menu_titles_in_menu_item_list( $post ) {
780
+ global $wp_list_table;
781
+
782
+ static $last_term_id = false;
783
+
784
+ $term = $this->get_menu_item_menu_leaf( $post->ID );
785
+
786
+ $term_id = $term instanceof WP_Term ? $term->term_id : null;
787
+
788
+ if ( false !== $last_term_id && $last_term_id === $term_id ) {
789
+ return;
790
+ }
791
+
792
+ if ( is_null( $term_id ) ) {
793
+ $last_term_id = null;
794
+ $term_name = '';
795
+ $parent_count = 0;
796
+ } else {
797
+ $last_term_id = $term->term_id;
798
+ $term_name = $term->name;
799
+ $parent_count = 0;
800
+ $current_term = $term;
801
+ while ( $current_term->parent ) {
802
+ $parent_count++;
803
+ $current_term = get_term( $current_term->parent, self::MENU_TAX );
804
+ }
805
+ }
806
+
807
+ $non_order_column_count = $wp_list_table->get_column_count() - 1;
808
+
809
+ $screen = get_current_screen();
810
+
811
+ $url = admin_url( $screen->parent_file );
812
+
813
+ $up_url = add_query_arg( array(
814
+ 'action' => 'move-menu-up',
815
+ 'term_id' => (int) $term_id,
816
+ ), wp_nonce_url( $url, 'ect_food_move_menu_up_' . $term_id ) );
817
+
818
+ $down_url = add_query_arg( array(
819
+ 'action' => 'move-menu-down',
820
+ 'term_id' => (int) $term_id,
821
+ ), wp_nonce_url( $url, 'ect_food_move_menu_down_' . $term_id ) );
822
+
823
+ ?>
824
+ <tr class="no-items menu-label-row" data-term_id="<?php echo esc_attr( $term_id ) ?>">
825
+ <td class="colspanchange" colspan="<?php echo (int) $non_order_column_count; ?>">
826
+ <h3><?php
827
+ echo str_repeat( ' &mdash; ', (int) $parent_count );
828
+
829
+ if ( $term instanceof WP_Term ) {
830
+ echo esc_html( sanitize_term_field( 'name', $term_name, $term_id, self::MENU_TAX, 'display' ) );
831
+ edit_term_link( __( 'edit', 'essential-content-types' ), '<span class="edit-ect-food-section"><span class="dashicon dashicon-edit"></span>', '</span>', $term );
832
+
833
+ } else {
834
+ _e( 'Uncategorized' , 'essential-content-types' );
835
+ }
836
+ ?></h3>
837
+ </td>
838
+ <td>
839
+ <?php if ( $term instanceof WP_Term ) { ?>
840
+ <a class="ect-food-move-menu-up" title="<?php esc_attr_e( 'Move menu section up', 'essential-content-types' ); ?>" href="<?php echo esc_url( $up_url ); ?>"><?php esc_html_e( 'UP', 'essential-content-types' ); ?></a>
841
+ <br />
842
+ <a class="ect-food-move-menu-down" title="<?php esc_attr_e( 'Move menu section down', 'essential-content-types' ); ?>" href="<?php echo esc_url( $down_url ); ?>"><?php esc_html_e( 'DOWN', 'essential-content-types' ); ?></a>
843
+ <?php } ?>
844
+ </td>
845
+ </tr>
846
+ <?php
847
+ }
848
+
849
+ /* Edit Many Items */
850
+
851
+ function add_many_new_items_page_load() {
852
+ if ( 'POST' === strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
853
+ $this->process_form_request();
854
+ exit;
855
+ }
856
+
857
+ $this->enqueue_many_items_scripts();
858
+ }
859
+
860
+ function enqueue_many_items_scripts() {
861
+ wp_enqueue_script(
862
+ 'ect-food-many-items',
863
+ plugin_dir_url( __FILE__ ) . 'js/ect-food-menu-many-items.js',
864
+ array( 'jquery' ),
865
+ $this->version,
866
+ true
867
+ );
868
+ }
869
+
870
+ function process_form_request() {
871
+ if ( !isset( $_POST['ect_food_title'] ) || !is_array( $_POST['ect_food_title'] ) ) {
872
+ return;
873
+ }
874
+
875
+ $is_ajax = !empty( $_POST['ajax'] );
876
+
877
+ if ( $is_ajax ) {
878
+ check_ajax_referer( 'ect_food_many_items' );
879
+ } else {
880
+ check_admin_referer( 'ect_food_many_items' );
881
+ }
882
+
883
+ foreach ( array_keys( $_POST['ect_food_title'] ) as $key ) :
884
+ // $_POST is already slashed
885
+ $post_details = array(
886
+ 'post_status' => 'publish',
887
+ 'post_type' => self::MENU_ITEM_POST_TYPE,
888
+ 'post_content' => $_POST['ect_food_content'][$key],
889
+ 'post_title' => $_POST['ect_food_title'][$key],
890
+ 'tax_input' => array(
891
+ self::MENU_ITEM_LABEL_TAX => $_POST['ect_food_labels'][$key],
892
+ self::MENU_TAX => isset( $_POST['ect_food_menu_tax'] ) ? $_POST['ect_food_menu_tax'] : null,
893
+ ),
894
+ );
895
+
896
+ $post_id = wp_insert_post( $post_details );
897
+ if ( !$post_id || is_wp_error( $post_id ) ) {
898
+ continue;
899
+ }
900
+
901
+ $this->set_price( $post_id, isset( $_POST['ect_food_price'][$key] ) ? stripslashes( $_POST['ect_food_price'][$key] ) : '' );
902
+
903
+ if ( $is_ajax ) :
904
+ $post = get_post( $post_id );
905
+ $GLOBALS['post'] = $post;
906
+ setup_postdata( $post );
907
+
908
+ ?>
909
+ <td><?php the_title(); ?></td>
910
+ <td class="ect-food-price"><?php $this->display_price(); ?></td>
911
+ <td><?php $this->list_labels( $post_id ); ?></td>
912
+ <td><?php the_content(); ?></td>
913
+ <?php
914
+ endif;
915
+
916
+ endforeach;
917
+
918
+ if ( $is_ajax ) {
919
+ exit;
920
+ }
921
+
922
+ wp_safe_redirect( admin_url( 'edit.php?post_type=' . self::MENU_ITEM_POST_TYPE ) );
923
+ exit;
924
+ }
925
+
926
+ function add_many_new_items_page() {
927
+ ?>
928
+ <div class="wrap">
929
+ <h2><?php esc_html_e( 'Add Many Items', 'essential-content-types' ); ?></h2>
930
+
931
+ <p><?php _e( 'Use the <kbd>TAB</kbd> key on your keyboard to move between colums and the <kbd>ENTER</kbd> or <kbd>RETURN</kbd> key to save each row and move on to the next.', 'essential-content-types' ); ?></p>
932
+
933
+ <form method="post" action="" enctype="multipart/form-data">
934
+ <p><h3><?php esc_html_e( 'Add to section:', 'essential-content-types' ); ?> <?php wp_dropdown_categories( array(
935
+ 'id' => 'ect-food-menu-tax',
936
+ 'name' => 'ect_food_menu_tax',
937
+ 'taxonomy' => self::MENU_TAX,
938
+ 'hide_empty' => false,
939
+ 'hierarchical' => true,
940
+ ) ); ?></h3></p>
941
+
942
+ <table class="many-items-table wp-list-table widefat">
943
+ <thead>
944
+ <tr>
945
+ <th scope="col"><?php esc_html_e( 'Name', 'essential-content-types' ); ?></th>
946
+ <th scope="col" class="ect-food-price"><?php esc_html_e( 'Price', 'essential-content-types' ); ?></th>
947
+ <th scope="col"><?php _e( 'Labels: <small>spicy, favorite, etc. <em>Separate Labels with commas</em></small>', 'essential-content-types' ); ?></th>
948
+ <th scope="col"><?php esc_html_e( 'Description', 'essential-content-types' ); ?></th>
949
+ </tr>
950
+ </thead>
951
+ <tbody>
952
+ <tr>
953
+ <td><input type="text" name="ect_food_title[]" aria-required="true" /></td>
954
+ <td class="ect-food-price"><input type="text" name="ect_food_price[]" /></td>
955
+ <td><input type="text" name="ect_food_labels[]" /></td>
956
+ <td><textarea name="ect_food_content[]" cols="20" rows="1"></textarea>
957
+ </tr>
958
+ </tbody>
959
+ <tbody>
960
+ <tr>
961
+ <td><input type="text" name="ect_food_title[]" aria-required="true" /></td>
962
+ <td class="ect-food-price"><input type="text" name="ect_food_price[]" /></td>
963
+ <td><input type="text" name="ect_food_labels[]" /></td>
964
+ <td><textarea name="ect_food_content[]" cols="20" rows="1"></textarea>
965
+ </tr>
966
+ </tbody>
967
+ <tfoot>
968
+ <tr>
969
+ <th><a class="button button-secondary ect-food-new-row"><span class="dashicon dashicon-plus"></span> <?php esc_html_e( 'New Row' , 'essential-content-types' ); ?></a></th>
970
+ <th class="ect-food-price"></th>
971
+ <th></th>
972
+ <th></th>
973
+ </tr>
974
+ </tfoot>
975
+ </table>
976
+
977
+ <p class="submit">
978
+ <input type="submit" class="button-primary" value="<?php esc_attr_e( 'Add These New Menu Items', 'essential-content-types' ); ?>" />
979
+ <?php wp_nonce_field( 'ect_food_many_items' ); ?>
980
+ </p>
981
+ </form>
982
+ </div>
983
+ <?php
984
+ }
985
+
986
+ /* Edit One Item */
987
+
988
+ function register_menu_item_meta_boxes() {
989
+ wp_enqueue_script( 'ect-food-menu-checkboxes' );
990
+
991
+ add_meta_box( 'menu_item_price', __( 'Price', 'essential-content-types' ), array( $this, 'menu_item_price_meta_box' ), null, 'side', 'high' );
992
+ }
993
+
994
+ function menu_item_price_meta_box( $post, $meta_box ) {
995
+ $price = $this->get_price( $post->ID );
996
+ ?>
997
+ <label for="ect-food-price-<?php echo (int) $post->ID; ?>" class="screen-reader-text"><?php esc_html_e( 'Price', 'essential-content-types' ); ?></label>
998
+ <input type="text" id="ect-food-price-<?php echo (int) $post->ID; ?>" class="widefat" name="ect_food_price[<?php echo (int) $post->ID; ?>]" value="<?php echo esc_attr( $price ); ?>" />
999
+ <?php
1000
+ }
1001
+
1002
+ function add_post_meta( $post_id ) {
1003
+ if ( !isset( $_POST['ect_food_price'][$post_id] ) ) {
1004
+ return;
1005
+ }
1006
+
1007
+ $this->set_price( $post_id, stripslashes( $_POST['ect_food_price'][$post_id] ) );
1008
+ }
1009
+
1010
+ /* Data */
1011
+
1012
+ function get_menus( $args = array() ) {
1013
+ $args = wp_parse_args( $args, array(
1014
+ 'hide_empty' => false,
1015
+ ) );
1016
+
1017
+ $terms = get_terms( self::MENU_TAX, $args );
1018
+ if ( !$terms || is_wp_error( $terms ) ) {
1019
+ return array();
1020
+ }
1021
+
1022
+ $terms_by_id = array();
1023
+ foreach ( $terms as $term ) {
1024
+ $terms_by_id["{$term->term_id}"] = $term;
1025
+ }
1026
+
1027
+ $term_order = get_option( 'ect_food_menu_order', array() );
1028
+
1029
+ $return = array();
1030
+ foreach ( $term_order as $term_id ) {
1031
+ if ( isset( $terms_by_id["$term_id"] ) ) {
1032
+ $return[] = $terms_by_id["$term_id"];
1033
+ unset( $terms_by_id["$term_id"] );
1034
+ }
1035
+ }
1036
+
1037
+ foreach ( $terms_by_id as $term_id => $term ) {
1038
+ $return[] = $term;
1039
+ }
1040
+
1041
+ return $return;
1042
+ }
1043
+
1044
+ function get_menu_item_menu_leaf( $post_id ) {
1045
+ // Get first menu taxonomy "leaf"
1046
+ $term_ids = wp_get_object_terms( $post_id, self::MENU_TAX, array( 'fields' => 'ids' ) );
1047
+
1048
+ foreach ( $term_ids as $term_id ) {
1049
+ $children = get_term_children( $term_id, self::MENU_TAX );
1050
+ if ( ! $children ) {
1051
+ break;
1052
+ }
1053
+ }
1054
+
1055
+ if ( ! isset( $term_id ) ) {
1056
+ return false;
1057
+ }
1058
+
1059
+ return get_term( $term_id, self::MENU_TAX );
1060
+
1061
+ }
1062
+
1063
+ function list_labels( $post_id = 0 ) {
1064
+ $post = get_post( $post_id );
1065
+ echo get_the_term_list( $post->ID, self::MENU_ITEM_LABEL_TAX, '', _x( ', ', 'Nova label separator', 'essential-content-types' ), '' );
1066
+ }
1067
+
1068
+ function list_admin_labels( $post_id = 0 ) {
1069
+ $post = get_post( $post_id );
1070
+ $labels = get_the_terms( $post->ID, self::MENU_ITEM_LABEL_TAX );
1071
+ if ( !empty( $labels ) ) {
1072
+ $out = array();
1073
+ foreach ( $labels as $label ) {
1074
+ $out[] = sprintf( '<a href="%s">%s</a>',
1075
+ esc_url( add_query_arg( array(
1076
+ 'post_type' => self::MENU_ITEM_POST_TYPE,
1077
+ 'taxonomy' => self::MENU_ITEM_LABEL_TAX,
1078
+ 'term' => $label->slug
1079
+ ), 'edit.php' ) ),
1080
+ esc_html( sanitize_term_field( 'name', $label->name, $label->term_id, self::MENU_ITEM_LABEL_TAX, 'display' ) )
1081
+ );
1082
+ }
1083
+
1084
+ echo join( _x( ', ', 'Nova label separator', 'essential-content-types' ), $out );
1085
+ } else {
1086
+ esc_html_e( 'No Labels', 'essential-content-types' );
1087
+ }
1088
+ }
1089
+
1090
+ function set_price( $post_id = 0, $price = '' ) {
1091
+ $post = get_post( $post_id );
1092
+
1093
+ return update_post_meta( $post->ID, 'ect_food_price', $price );
1094
+ }
1095
+
1096
+ function get_price( $post_id = 0 ) {
1097
+ $post = get_post( $post_id );
1098
+
1099
+ return get_post_meta( $post->ID, 'ect_food_price', true );
1100
+ }
1101
+
1102
+ function display_price( $post_id = 0 ) {
1103
+ echo esc_html( $this->get_price( $post_id ) );
1104
+ }
1105
+
1106
+ /* Menu Item Loop Markup */
1107
+
1108
+ /* Does not support nested loops */
1109
+
1110
+ function get_menu_item_loop_markup( $field = null ) {
1111
+ return $this->menu_item_loop_markup;
1112
+ }
1113
+
1114
+ /**
1115
+ * Sets up the loop markup.
1116
+ * Attached to the 'template_include' *filter*,
1117
+ * which fires only during a real blog view (not in admin, feeds, etc.)
1118
+ *
1119
+ * @param string Template File
1120
+ * @return string Template File. VERY Important.
1121
+ */
1122
+ function setup_menu_item_loop_markup__in_filter( $template ) {
1123
+ add_action( 'loop_start', array( $this, 'start_menu_item_loop' ) );
1124
+
1125
+ return $template;
1126
+ }
1127
+
1128
+ /**
1129
+ * If the Query is a Menu Item Query, start outputing the Menu Item Loop Marku
1130
+ * Attached to the 'loop_start' action.
1131
+ *
1132
+ * @param WP_Query
1133
+ */
1134
+ function start_menu_item_loop( $query ) {
1135
+ if ( !$this->is_menu_item_query( $query ) ) {
1136
+ return;
1137
+ }
1138
+
1139
+ $this->menu_item_loop_last_term_id = false;
1140
+ $this->menu_item_loop_current_term = false;
1141
+
1142
+ add_action( 'the_post', array( $this, 'menu_item_loop_each_post' ) );
1143
+ add_action( 'loop_end', array( $this, 'stop_menu_item_loop' ) );
1144
+ }
1145
+
1146
+ /**
1147
+ * Outputs the Menu Item Loop Marku
1148
+ * Attached to the 'the_post' action.
1149
+ *
1150
+ * @param WP_Post
1151
+ */
1152
+ function menu_item_loop_each_post( $post ) {
1153
+ $this->menu_item_loop_current_term = $this->get_menu_item_menu_leaf( $post->ID );
1154
+
1155
+ if ( false === $this->menu_item_loop_last_term_id ) {
1156
+ // We're at the very beginning of the loop
1157
+
1158
+ $this->menu_item_loop_open_element( 'menu' ); // Start a new menu section
1159
+ $this->menu_item_loop_header(); // Output the menu's header
1160
+ } elseif ( $this->menu_item_loop_last_term_id != $this->menu_item_loop_current_term->term_id ) {
1161
+ // We're not at the very beginning but still need to start a new menu section. End the previous menu section first.
1162
+
1163
+ $this->menu_item_loop_close_element( 'menu' ); // End the previous menu section
1164
+ $this->menu_item_loop_open_element( 'menu' ); // Start a new menu section
1165
+ $this->menu_item_loop_header(); // Output the menu's header
1166
+ }
1167
+
1168
+ $this->menu_item_loop_last_term_id = isset( $this->menu_item_loop_current_term->term_id ) ? $this->menu_item_loop_current_term->term_id : '' ;
1169
+ }
1170
+
1171
+ /**
1172
+ * If the Query is a Menu Item Query, stop outputing the Menu Item Loop Marku
1173
+ * Attached to the 'loop_end' action.
1174
+ *
1175
+ * @param WP_Query
1176
+ */
1177
+ function stop_menu_item_loop( $query ) {
1178
+ if ( !$this->is_menu_item_query( $query ) ) {
1179
+ return;
1180
+ }
1181
+
1182
+ remove_action( 'the_post', array( $this, 'menu_item_loop_each_post' ) );
1183
+ remove_action( 'loop_start', array( $this, 'start_menu_item_loop' ) );
1184
+ remove_action( 'loop_end', array( $this, 'stop_menu_item_loop' ) );
1185
+
1186
+ $this->menu_item_loop_close_element( 'menu' ); // End the last menu section
1187
+ }
1188
+
1189
+ /**
1190
+ * Outputs the Menu Group Header
1191
+ */
1192
+ function menu_item_loop_header() {
1193
+ $this->menu_item_loop_open_element( 'menu_header' );
1194
+ $this->menu_item_loop_open_element( 'menu_title' );
1195
+ echo isset( $this->menu_item_loop_current_term->name ) ? esc_html( $this->menu_item_loop_current_term->name ) : ''; // @todo tax filter
1196
+ $this->menu_item_loop_close_element( 'menu_title' );
1197
+ if ( isset( $this->menu_item_loop_current_term->description ) && $this->menu_item_loop_current_term->description ) :
1198
+ $this->menu_item_loop_open_element( 'menu_description' );
1199
+ echo esc_html( $this->menu_item_loop_current_term->description ); // @todo kses, tax filter
1200
+ $this->menu_item_loop_close_element( 'menu_description' );
1201
+ endif;
1202
+ $this->menu_item_loop_close_element( 'menu_header' );
1203
+ }
1204
+
1205
+ /**
1206
+ * Outputs a Menu Item Markup element opening tag
1207
+ *
1208
+ * @param string $field - Menu Item Markup settings field.
1209
+ */
1210
+ function menu_item_loop_open_element( $field ) {
1211
+ $markup = $this->get_menu_item_loop_markup();
1212
+ /**
1213
+ * Filter a menu item's element opening tag.
1214
+ *
1215
+ * @module custom-content-types
1216
+ *
1217
+ * @since 4.4.0
1218
+ *
1219
+ * @param string $tag Menu item's element opening tag.
1220
+ * @param string $field Menu Item Markup settings field.
1221
+ * @param array $markup Array of markup elements for the menu item.
1222
+ * @param false|object $term Taxonomy term for current menu item.
1223
+ */
1224
+ echo apply_filters(
1225
+ 'essential-content-types_ect_food_menu_item_loop_open_element',
1226
+ '<' . tag_escape( $markup["{$field}_tag"] ) . $this->menu_item_loop_class( $markup["{$field}_class"] ) . ">\n",
1227
+ $field,
1228
+ $markup,
1229
+ $this->menu_item_loop_current_term
1230
+ );
1231
+ }
1232
+
1233
+ /**
1234
+ * Outputs a Menu Item Markup element closing tag
1235
+ *
1236
+ * @param string $field - Menu Item Markup settings field
1237
+ */
1238
+ function menu_item_loop_close_element( $field ) {
1239
+ $markup = $this->get_menu_item_loop_markup();
1240
+ /**
1241
+ * Filter a menu item's element closing tag.
1242
+ *
1243
+ * @module custom-content-types
1244
+ *
1245
+ * @since 4.4.0
1246
+ *
1247
+ * @param string $tag Menu item's element closing tag.
1248
+ * @param string $field Menu Item Markup settings field.
1249
+ * @param array $markup Array of markup elements for the menu item.
1250
+ * @param false|object $term Taxonomy term for current menu item.
1251
+ */
1252
+ echo apply_filters(
1253
+ 'essential-content-types_ect_food_menu_item_loop_close_element',
1254
+ '</' . tag_escape( $markup["{$field}_tag"] ) . ">\n",
1255
+ $field,
1256
+ $markup,
1257
+ $this->menu_item_loop_current_term
1258
+ );
1259
+ }
1260
+
1261
+ /**
1262
+ * Returns a Menu Item Markup element's class attribute.
1263
+ *
1264
+ * @param string $class Class name.
1265
+ * @return string HTML class attribute with leading whitespace.
1266
+ */
1267
+ function menu_item_loop_class( $class ) {
1268
+ if ( ! $class ) {
1269
+ return '';
1270
+ }
1271
+
1272
+ /**
1273
+ * Filter a menu Item Markup element's class attribute.
1274
+ *
1275
+ * @module custom-content-types
1276
+ *
1277
+ * @since 4.4.0
1278
+ *
1279
+ * @param string $tag Menu Item Markup element's class attribute.
1280
+ * @param string $class Menu Item Class name.
1281
+ * @param false|object $term Taxonomy term for current menu item.
1282
+ */
1283
+ return apply_filters(
1284
+ 'essential-content-types_ect_food_menu_item_loop_class',
1285
+ ' class="' . esc_attr( $class ) . '"',
1286
+ $class,
1287
+ $this->menu_item_loop_current_term
1288
+ );
1289
+ }
1290
+
1291
+ /**
1292
+ * Our [food_menu] shortcode.
1293
+ * Prints Food Menu data styled to look good on *any* theme.
1294
+ *
1295
+ * @return ect_food_shortcode_html
1296
+ */
1297
+ static function ect_food_shortcode( $atts ) {
1298
+ // Default attributes
1299
+ $atts = shortcode_atts( array(
1300
+ 'include_type' => false,
1301
+ 'include_tag' => false,
1302
+ 'showposts' => -1,
1303
+ ), $atts, 'food_menu' );
1304
+
1305
+ // A little sanitization
1306
+ if ( $atts['include_type'] ) {
1307
+ $atts['include_type'] = explode( ',', str_replace( ' ', '', $atts['include_type'] ) );
1308
+ }
1309
+
1310
+ if ( $atts['include_tag'] ) {
1311
+ $atts['include_tag'] = explode( ',', str_replace( ' ', '', $atts['include_tag'] ) );
1312
+ }
1313
+
1314
+ $atts['showposts'] = intval( $atts['showposts'] );
1315
+
1316
+ // enqueue shortcode styles when shortcode is used
1317
+ wp_enqueue_style( 'ect-food-menu-style', plugins_url( 'css/food-menu-shortcode.css', __FILE__ ), array(), '20140326' );
1318
+ wp_enqueue_script( 'ect-food-menu-script', plugins_url( 'js/food-menu-shortcode.js', __FILE__ ) , array( 'jquery' ), '20180530', false );
1319
+
1320
+ return self::ect_food_shortcode_html( $atts );
1321
+ }
1322
+
1323
+ /**
1324
+ * Query to retrieve entries from the Food Menu post_type.
1325
+ *
1326
+ * @return object
1327
+ */
1328
+ static function ect_food_query( $atts ) {
1329
+ // Default query arguments
1330
+ $default = array(
1331
+ 'posts_per_page' => $atts['showposts'],
1332
+ );
1333
+
1334
+ $args = wp_parse_args( $atts, $default );
1335
+ $args['post_type'] = self::MENU_ITEM_POST_TYPE; // Force this post type
1336
+
1337
+ if ( false != $atts['include_type'] || false != $atts['include_tag'] ) {
1338
+ $args['tax_query'] = array();
1339
+ }
1340
+
1341
+ // If 'include_type' has been set use it on the main query
1342
+ if ( false != $atts['include_type'] ) {
1343
+ array_push( $args['tax_query'], array(
1344
+ 'taxonomy' => self::MENU_TAX,
1345
+ 'field' => 'slug',
1346
+ 'terms' => $atts['include_type'],
1347
+ ) );
1348
+ }
1349
+
1350
+ // If 'include_tag' has been set use it on the main query
1351
+ if ( false != $atts['include_tag'] ) {
1352
+ array_push( $args['tax_query'], array(
1353
+ 'taxonomy' => self::MENU_ITEM_LABEL_TAX,
1354
+ 'field' => 'slug',
1355
+ 'terms' => $atts['include_tag'],
1356
+ ) );
1357
+ }
1358
+
1359
+ if ( false != $atts['include_type'] && false != $atts['include_tag'] ) {
1360
+ $args['tax_query']['relation'] = 'AND';
1361
+ }
1362
+
1363
+ // Run the query and return
1364
+ $query = new WP_Query( $args );
1365
+ return $query;
1366
+ }
1367
+
1368
+ /**
1369
+ * The Food Menu shortcode loop.
1370
+ *
1371
+ * @return html
1372
+ */
1373
+ static function ect_food_shortcode_html( $atts ) {
1374
+
1375
+ $query = self::ect_food_query( $atts );
1376
+ ob_start();
1377
+
1378
+ // If we have posts, create the html
1379
+ // with hect_food markup
1380
+ if ( $query->have_posts() ) {
1381
+
1382
+ /**
1383
+ * Hook: ect_before_food_menu_loop.
1384
+ *
1385
+ * @hooked ect_food_menu_section
1386
+ */
1387
+ $layout = ect_get_layout();
1388
+ do_action( 'ect_before_food_menu_loop' );
1389
+ ?>
1390
+ <?php
1391
+
1392
+ ect_get_template_part( 'ect', 'menu', $atts );
1393
+
1394
+ // If comments are open or we have at least one comment, load up the comment template
1395
+ if ( comments_open() || '0' != get_comments_number() ) {
1396
+ comments_template( '', true );
1397
+ }
1398
+ ?>
1399
+ <?php
1400
+
1401
+ /**
1402
+ * Hook: ect_after_food_menu_loop.
1403
+ *
1404
+ * @hooked
1405
+ */
1406
+ do_action( 'ect_after_food_menu_loop' );
1407
+
1408
+ } else {
1409
+ /**
1410
+ * Hook: ect_no_food_menu_found.
1411
+ *
1412
+ * @hooked ect_no_food_menu_found
1413
+ */
1414
+ do_action( 'ect_no_food_menu_found' );
1415
+ }
1416
+
1417
+ $html = ob_get_clean();
1418
+
1419
+ // If there is a [food-menu] within a [food-menu], remove the shortcode
1420
+ if ( has_shortcode( $html, 'food-menu' ) ){
1421
+ remove_shortcode( 'food-menu' );
1422
+ }
1423
+
1424
+ // Return the HTML block
1425
+ return $html;
1426
+ }
1427
+ }
1428
+
1429
+ add_action( 'init', array( 'Essential_Content_Food_Menu', 'init' ) );
1430
+
1431
+
1432
+ /**
1433
+ * Add Food Menu support
1434
+ */
1435
+ function essential_content_food_menu_support() {
1436
+ /*
1437
+ * Adding theme support for Food Menu Item CPT.
1438
+ */
1439
+ add_theme_support( 'ect_food_menu_item' );
1440
+ }
1441
+ add_action( 'after_setup_theme', 'essential_content_food_menu_support' );
1442
+
1443
+
1444
+ if( ! function_exists( 'essential_content_no_food_menu_found' ) ):
1445
+ /**
1446
+ * No items found text
1447
+ *
1448
+ * @return html
1449
+ */
1450
+ function essential_content_no_food_menu_found() {
1451
+ echo "<p><em>" . esc_html__( 'Your Food Menu Archive currently has no entries. You can start creating them on your dashboard.', 'essential-content-types-pro' ) . "</em></p>";
1452
+ }
1453
+ endif;
1454
+ add_action( 'ect_no_food_menu_found', 'essential_content_no_food_menu_found', 10 );
1455
+
1456
+
1457
+ if( ! function_exists( 'essential_content_food_menu_section_open' ) ):
1458
+ /**
1459
+ * Open section
1460
+ *
1461
+ * @return html
1462
+ */
1463
+ function essential_content_food_menu_section_open() {
1464
+ echo '<div class="ect-menu ect-section">';
1465
+ echo '<div class="ect-wrapper">';
1466
+ }
1467
+ endif;
1468
+ add_action( 'ect_before_food_menu_loop', 'essential_content_food_menu_section_open', 10, 1 );
1469
+
1470
+
1471
+ if( ! function_exists( 'essential_content_food_menu_loop_start' ) ):
1472
+ /**
1473
+ * open wrapper before loop
1474
+ *
1475
+ */
1476
+ function essential_content_food_menu_loop_start(){
1477
+ echo '<div class="section-content-wrapper menu-content-wrapper">';
1478
+ }
1479
+ endif;
1480
+ add_action( 'ect_before_food_menu_loop', 'essential_content_food_menu_loop_start', 30 );
1481
+
1482
+
1483
+ if( ! function_exists( 'essential_content_food_menu_loop_end' ) ):
1484
+ /**
1485
+ * close wrapper after loop
1486
+ *
1487
+ */
1488
+ function essential_content_food_menu_loop_end(){
1489
+ echo '</div><!-- .menu-content-wrapper -->';
1490
+ }
1491
+ endif;
1492
+ add_action( 'ect_after_food_menu_loop', 'essential_content_food_menu_loop_end', 10 );
1493
+
1494
+
1495
+ if( ! function_exists( 'essential_content_food_menu_section_close' ) ):
1496
+ /**
1497
+ * Close section
1498
+ *
1499
+ * @return html
1500
+ */
1501
+ function essential_content_food_menu_section_close() {
1502
+ echo '</div><!-- .ect-wrapper -->';
1503
+ echo '</div><!-- .ect-section -->';
1504
+ }
1505
+ endif;
1506
+ add_action( 'ect_after_food_menu_loop', 'essential_content_food_menu_section_close', 20 );
1507
+
1508
+ function essential_content_food_menu_get_menus( $args = array() ) {
1509
+ $args = wp_parse_args( $args, array(
1510
+ 'hide_empty' => false,
1511
+ ) );
1512
+
1513
+ $terms = get_terms( 'ect_food_menu', $args );
1514
+ if ( !$terms || is_wp_error( $terms ) ) {
1515
+ return array();
1516
+ }
1517
+
1518
+ $terms_by_id = array();
1519
+ foreach ( $terms as $term ) {
1520
+ $terms_by_id["{$term->term_id}"] = $term;
1521
+ }
1522
+
1523
+ $term_order = get_option( 'ect_food_menu_order', array() );
1524
+
1525
+ $return = array();
1526
+ foreach ( $term_order as $term_id ) {
1527
+ if ( isset( $terms_by_id["$term_id"] ) ) {
1528
+ $return[] = $terms_by_id["$term_id"];
1529
+ unset( $terms_by_id["$term_id"] );
1530
+ }
1531
+ }
1532
+
1533
+ foreach ( $terms_by_id as $term_id => $term ) {
1534
+ $return[] = $term;
1535
+ }
1536
+
1537
+ return $return;
1538
  }
admin/class-portfolio.php CHANGED
@@ -1,882 +1,882 @@
1
- <?php
2
-
3
- /**
4
- * Support JetPack Portfolio
5
- */
6
- class Essential_Content_Jetpack_Portfolio {
7
- const CUSTOM_POST_TYPE = 'jetpack-portfolio';
8
- const CUSTOM_TAXONOMY_TYPE = 'jetpack-portfolio-type';
9
- const CUSTOM_TAXONOMY_TAG = 'jetpack-portfolio-tag';
10
- const OPTION_NAME = 'jetpack_portfolio';
11
- const OPTION_READING_SETTING = 'jetpack_portfolio_posts_per_page';
12
-
13
- public $version = ESSENTIAL_CONTENT_TYPES_VERSION;
14
-
15
- static function init() {
16
- static $instance = false;
17
-
18
- if ( ! $instance ) {
19
- $instance = new Essential_Content_Jetpack_Portfolio;
20
- }
21
-
22
- return $instance;
23
- }
24
-
25
- /**
26
- * Conditionally hook into WordPress.
27
- *
28
- * Setup user option for enabling CPT
29
- * If user has CPT enabled, show in admin
30
- */
31
- function __construct() {
32
- // Add an option to enable the CPT
33
- add_action( 'admin_init', array( $this, 'settings_api_init' ) );
34
-
35
- // Check on theme switch if theme supports CPT and setting is disabled
36
- add_action( 'after_switch_theme', array( $this, 'activation_post_type_support' ) );
37
-
38
- // Make sure the post types are loaded for imports
39
- add_action( 'import_start', array( $this, 'register_post_types' ) );
40
-
41
- // Add to REST API post type whitelist
42
- add_filter( 'rest_api_allowed_post_types', array( $this, 'allow_portfolio_rest_api_type' ) );
43
-
44
- $setting = 1;
45
-
46
- if ( class_exists( 'Jetpack_Options' ) ) {
47
- $setting = Jetpack_Options::get_option_and_ensure_autoload( self::OPTION_NAME, '0' );
48
- }
49
-
50
- // Bail early if Portfolio option is not set and the theme doesn't declare support
51
- if ( empty( $setting ) && ! $this->site_supports_custom_post_type() ) {
52
- return;
53
- }
54
-
55
- // Enable Omnisearch for Portfolio Items.
56
- if ( class_exists( 'Jetpack_Omnisearch_Posts' ) )
57
- new Jetpack_Omnisearch_Posts( self::CUSTOM_POST_TYPE );
58
-
59
- // CPT magic
60
- $this->register_post_types();
61
- add_action( sprintf( 'add_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 );
62
- add_action( sprintf( 'update_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 );
63
- add_action( sprintf( 'publish_%s', self::CUSTOM_POST_TYPE), array( $this, 'flush_rules_on_first_project' ) );
64
- add_action( 'after_switch_theme', array( $this, 'flush_rules_on_switch' ) );
65
-
66
- // Admin Customization
67
- add_filter( 'post_updated_messages', array( $this, 'updated_messages' ) );
68
- add_filter( sprintf( 'manage_%s_posts_columns', self::CUSTOM_POST_TYPE), array( $this, 'edit_admin_columns' ) );
69
- add_filter( sprintf( 'manage_%s_posts_custom_column', self::CUSTOM_POST_TYPE), array( $this, 'image_column' ), 10, 2 );
70
- add_action( 'customize_register', array( $this, 'customize_register' ) );
71
-
72
- add_image_size( 'jetpack-portfolio-admin-thumb', 50, 50, true );
73
- add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
74
-
75
- // register jetpack_portfolio shortcode and portfolio shortcode (legacy)
76
- add_shortcode( 'portfolio', array( $this, 'portfolio_shortcode' ) );
77
- add_shortcode( 'jetpack_portfolio', array( $this, 'portfolio_shortcode' ) );
78
-
79
- // Adjust CPT archive and custom taxonomies to obey CPT reading setting
80
- add_filter( 'pre_get_posts', array( $this, 'query_reading_setting' ) );
81
-
82
- // If CPT was enabled programatically and no CPT items exist when user switches away, disable
83
- if ( $setting && $this->site_supports_custom_post_type() ) {
84
- add_action( 'switch_theme', array( $this, 'deactivation_post_type_support' ) );
85
- }
86
- }
87
-
88
- /**
89
- * Add a checkbox field in 'Settings' > 'Writing'
90
- * for enabling CPT functionality.
91
- *
92
- * @return null
93
- */
94
- function settings_api_init() {
95
- add_settings_field(
96
- self::OPTION_NAME,
97
- '<span class="cpt-options">' . esc_html__( 'Portfolio Projects', 'essential-content-types' ) . '</span>',
98
- array( $this, 'setting_html' ),
99
- 'writing',
100
- 'jetpack_cpt_section'
101
- );
102
- register_setting(
103
- 'writing',
104
- self::OPTION_NAME,
105
- 'intval'
106
- );
107
-
108
- // Check if CPT is enabled first so that intval doesn't get set to NULL on re-registering
109
- if ( get_option( self::OPTION_NAME, '0' ) || current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
110
- register_setting(
111
- 'writing',
112
- self::OPTION_READING_SETTING,
113
- 'intval'
114
- );
115
- }
116
- }
117
-
118
- /**
119
- * HTML code to display a checkbox true/false option
120
- * for the Portfolio CPT setting.
121
- *
122
- * @return html
123
- */
124
- function setting_html() {
125
- if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) : ?>
126
- <p><?php printf( /* translators: %s is the name of a custom post type such as "jetpack-portfolio" */ __( 'Your theme supports <strong>%s</strong>', 'essential-content-types' ), self::CUSTOM_POST_TYPE ); ?></p>
127
- <?php else : ?>
128
- <label for="<?php echo esc_attr( self::OPTION_NAME ); ?>">
129
- <input name="<?php echo esc_attr( self::OPTION_NAME ); ?>" id="<?php echo esc_attr( self::OPTION_NAME ); ?>" <?php echo checked( get_option( self::OPTION_NAME, '0' ), true, false ); ?> type="checkbox" value="1" />
130
- <?php esc_html_e( 'Enable Portfolio Projects for this site.', 'essential-content-types' ); ?>
131
- <a target="_blank" href="http://en.support.wordpress.com/portfolios/"><?php esc_html_e( 'Learn More', 'essential-content-types' ); ?></a>
132
- </label>
133
- <?php endif;
134
- if ( get_option( self::OPTION_NAME, '0' ) || current_theme_supports( self::CUSTOM_POST_TYPE ) ) :
135
- printf( '<p><label for="%1$s">%2$s</label></p>',
136
- esc_attr( self::OPTION_READING_SETTING ),
137
- /* translators: %1$s is replaced with an input field for numbers */
138
- sprintf( __( 'Portfolio pages display at most %1$s projects', 'essential-content-types' ),
139
- sprintf( '<input name="%1$s" id="%1$s" type="number" step="1" min="1" value="%2$s" class="small-text" />',
140
- esc_attr( self::OPTION_READING_SETTING ),
141
- esc_attr( get_option( self::OPTION_READING_SETTING, '10' ) )
142
- )
143
- )
144
- );
145
- endif;
146
- }
147
-
148
- /**
149
- * Should this Custom Post Type be made available?
150
- */
151
- function site_supports_custom_post_type() {
152
- // If the current theme requests it.
153
- if ( current_theme_supports( self::CUSTOM_POST_TYPE ) || get_option( self::OPTION_NAME, '0' ) ) {
154
- return true;
155
- }
156
-
157
- // Otherwise, say no unless something wants to filter us to say yes.
158
- /** This action is documented in modules/custom-post-types/nova.php */
159
- return (bool) apply_filters( 'jetpack_enable_cpt', false, self::CUSTOM_POST_TYPE );
160
- }
161
-
162
- /*
163
- * Flush permalinks when CPT option is turned on/off
164
- */
165
- function flush_rules_on_enable() {
166
- flush_rewrite_rules();
167
- }
168
-
169
- /*
170
- * Count published projects and flush permalinks when first projects is published
171
- */
172
- function flush_rules_on_first_project() {
173
- $projects = get_transient( 'jetpack-portfolio-count-cache' );
174
-
175
- if ( false === $projects ) {
176
- flush_rewrite_rules();
177
- $projects = (int) wp_count_posts( self::CUSTOM_POST_TYPE )->publish;
178
-
179
- if ( ! empty( $projects ) ) {
180
- set_transient( 'jetpack-portfolio-count-cache', $projects, HOUR_IN_SECONDS * 12 );
181
- }
182
- }
183
- }
184
-
185
- /*
186
- * Flush permalinks when CPT supported theme is activated
187
- */
188
- function flush_rules_on_switch() {
189
- if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
190
- flush_rewrite_rules();
191
- }
192
- }
193
-
194
- /**
195
- * On plugin/theme activation, check if current theme supports CPT
196
- */
197
- static function activation_post_type_support() {
198
- if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
199
- update_option( self::OPTION_NAME, '1' );
200
- }
201
- }
202
-
203
- /**
204
- * On theme switch, check if CPT item exists and disable if not
205
- */
206
- function deactivation_post_type_support() {
207
- $portfolios = get_posts( array(
208
- 'fields' => 'ids',
209
- 'posts_per_page' => 1,
210
- 'post_type' => self::CUSTOM_POST_TYPE,
211
- 'suppress_filters' => false
212
- ) );
213
-
214
- if ( empty( $portfolios ) ) {
215
- update_option( self::OPTION_NAME, '0' );
216
- }
217
- }
218
-
219
- /**
220
- * Register Post Type
221
- */
222
- function register_post_types() {
223
- if ( post_type_exists( self::CUSTOM_POST_TYPE ) ) {
224
- return;
225
- }
226
-
227
- $args = array(
228
- 'description' => __( 'Portfolio Items', 'essential-content-types' ),
229
- 'labels' => array(
230
- 'name' => esc_html__( 'Projects', 'essential-content-types' ),
231
- 'singular_name' => esc_html__( 'Project', 'essential-content-types' ),
232
- 'menu_name' => esc_html__( 'Portfolio', 'essential-content-types' ),
233
- 'all_items' => esc_html__( 'All Projects', 'essential-content-types' ),
234
- 'add_new' => esc_html__( 'Add New', 'essential-content-types' ),
235
- 'add_new_item' => esc_html__( 'Add New Project', 'essential-content-types' ),
236
- 'edit_item' => esc_html__( 'Edit Project', 'essential-content-types' ),
237
- 'new_item' => esc_html__( 'New Project', 'essential-content-types' ),
238
- 'view_item' => esc_html__( 'View Project', 'essential-content-types' ),
239
- 'search_items' => esc_html__( 'Search Projects', 'essential-content-types' ),
240
- 'not_found' => esc_html__( 'No Projects found', 'essential-content-types' ),
241
- 'not_found_in_trash' => esc_html__( 'No Projects found in Trash', 'essential-content-types' ),
242
- 'filter_items_list' => esc_html__( 'Filter projects list', 'essential-content-types' ),
243
- 'items_list_navigation' => esc_html__( 'Project list navigation', 'essential-content-types' ),
244
- 'items_list' => esc_html__( 'Projects list', 'essential-content-types' ),
245
- ),
246
- 'supports' => array(
247
- 'title',
248
- 'editor',
249
- 'excerpt',
250
- 'thumbnail',
251
- 'author',
252
- 'comments',
253
- 'publicize',
254
- ),
255
- 'rewrite' => array(
256
- 'slug' => 'portfolio',
257
- 'with_front' => false,
258
- 'feeds' => true,
259
- 'pages' => true,
260
- ),
261
- 'public' => true,
262
- 'show_ui' => true,
263
- 'menu_position' => 20, // below Pages
264
- 'menu_icon' => 'dashicons-portfolio', // 3.8+ dashicon option
265
- 'capability_type' => 'page',
266
- 'map_meta_cap' => true,
267
- 'taxonomies' => array( self::CUSTOM_TAXONOMY_TYPE, self::CUSTOM_TAXONOMY_TAG ),
268
- 'has_archive' => true,
269
- 'query_var' => 'portfolio',
270
- 'show_in_rest' => true,
271
- );
272
-
273
- $description = get_option( 'jetpack_portfolio_content' );
274
- if ( '' !== $description ) {
275
- $args['description'] = $description;
276
- }
277
-
278
- register_post_type( self::CUSTOM_POST_TYPE, $args );
279
-
280
- register_taxonomy( self::CUSTOM_TAXONOMY_TYPE, self::CUSTOM_POST_TYPE, array(
281
- 'hierarchical' => true,
282
- 'labels' => array(
283
- 'name' => esc_html__( 'Project Types', 'essential-content-types' ),
284
- 'singular_name' => esc_html__( 'Project Type', 'essential-content-types' ),
285
- 'menu_name' => esc_html__( 'Project Types', 'essential-content-types' ),
286
- 'all_items' => esc_html__( 'All Project Types', 'essential-content-types' ),
287
- 'edit_item' => esc_html__( 'Edit Project Type', 'essential-content-types' ),
288
- 'view_item' => esc_html__( 'View Project Type', 'essential-content-types' ),
289
- 'update_item' => esc_html__( 'Update Project Type', 'essential-content-types' ),
290
- 'add_new_item' => esc_html__( 'Add New Project Type', 'essential-content-types' ),
291
- 'new_item_name' => esc_html__( 'New Project Type Name', 'essential-content-types' ),
292
- 'parent_item' => esc_html__( 'Parent Project Type', 'essential-content-types' ),
293
- 'parent_item_colon' => esc_html__( 'Parent Project Type:', 'essential-content-types' ),
294
- 'search_items' => esc_html__( 'Search Project Types', 'essential-content-types' ),
295
- 'items_list_navigation' => esc_html__( 'Project type list navigation', 'essential-content-types' ),
296
- 'items_list' => esc_html__( 'Project type list', 'essential-content-types' ),
297
- ),
298
- 'public' => true,
299
- 'show_ui' => true,
300
- 'show_in_nav_menus' => true,
301
- 'show_in_rest' => true,
302
- 'show_admin_column' => true,
303
- 'query_var' => true,
304
- 'rewrite' => array( 'slug' => 'project-type' ),
305
- ) );
306
-
307
- register_taxonomy( self::CUSTOM_TAXONOMY_TAG, self::CUSTOM_POST_TYPE, array(
308
- 'hierarchical' => false,
309
- 'labels' => array(
310
- 'name' => esc_html__( 'Project Tags', 'essential-content-types' ),
311
- 'singular_name' => esc_html__( 'Project Tag', 'essential-content-types' ),
312
- 'menu_name' => esc_html__( 'Project Tags', 'essential-content-types' ),
313
- 'all_items' => esc_html__( 'All Project Tags', 'essential-content-types' ),
314
- 'edit_item' => esc_html__( 'Edit Project Tag', 'essential-content-types' ),
315
- 'view_item' => esc_html__( 'View Project Tag', 'essential-content-types' ),
316
- 'update_item' => esc_html__( 'Update Project Tag', 'essential-content-types' ),
317
- 'add_new_item' => esc_html__( 'Add New Project Tag', 'essential-content-types' ),
318
- 'new_item_name' => esc_html__( 'New Project Tag Name', 'essential-content-types' ),
319
- 'search_items' => esc_html__( 'Search Project Tags', 'essential-content-types' ),
320
- 'popular_items' => esc_html__( 'Popular Project Tags', 'essential-content-types' ),
321
- 'separate_items_with_commas' => esc_html__( 'Separate tags with commas', 'essential-content-types' ),
322
- 'add_or_remove_items' => esc_html__( 'Add or remove tags', 'essential-content-types' ),
323
- 'choose_from_most_used' => esc_html__( 'Choose from the most used tags', 'essential-content-types' ),
324
- 'not_found' => esc_html__( 'No tags found.', 'essential-content-types' ),
325
- 'items_list_navigation' => esc_html__( 'Project tag list navigation', 'essential-content-types' ),
326
- 'items_list' => esc_html__( 'Project tag list', 'essential-content-types' ),
327
- ),
328
- 'public' => true,
329
- 'show_ui' => true,
330
- 'show_in_nav_menus' => true,
331
- 'show_in_rest' => true,
332
- 'show_admin_column' => true,
333
- 'query_var' => true,
334
- 'rewrite' => array( 'slug' => 'project-tag' ),
335
- ) );
336
- }
337
-
338
- /**
339
- * Update messages for the Portfolio admin.
340
- */
341
- function updated_messages( $messages ) {
342
- global $post;
343
-
344
- $messages[self::CUSTOM_POST_TYPE] = array(
345
- 0 => '', // Unused. Messages start at index 1.
346
- 1 => sprintf( __( 'Project updated. <a href="%s">View item</a>', 'essential-content-types'), esc_url( get_permalink( $post->ID ) ) ),
347
- 2 => esc_html__( 'Custom field updated.', 'essential-content-types' ),
348
- 3 => esc_html__( 'Custom field deleted.', 'essential-content-types' ),
349
- 4 => esc_html__( 'Project updated.', 'essential-content-types' ),
350
- /* translators: %s: date and time of the revision */
351
- 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Project restored to revision from %s', 'essential-content-types'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
352
- 6 => sprintf( __( 'Project published. <a href="%s">View project</a>', 'essential-content-types' ), esc_url( get_permalink( $post->ID ) ) ),
353
- 7 => esc_html__( 'Project saved.', 'essential-content-types' ),
354
- 8 => sprintf( __( 'Project submitted. <a target="_blank" href="%s">Preview project</a>', 'essential-content-types'), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
355
- 9 => sprintf( __( 'Project scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview project</a>', 'essential-content-types' ),
356
- // translators: Publish box date format, see http://php.net/date
357
- date_i18n( __( 'M j, Y @ G:i', 'essential-content-types' ), strtotime( $post->post_date ) ), esc_url( get_permalink( $post->ID ) ) ),
358
- 10 => sprintf( __( 'Project item draft updated. <a target="_blank" href="%s">Preview project</a>', 'essential-content-types' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
359
- );
360
-
361
- return $messages;
362
- }
363
-
364
- /**
365
- * Change ‘Title’ column label
366
- * Add Featured Image column
367
- */
368
- function edit_admin_columns( $columns ) {
369
- // change 'Title' to 'Project'
370
- $columns['title'] = __( 'Project', 'essential-content-types' );
371
- if ( current_theme_supports( 'post-thumbnails' ) ) {
372
- // add featured image before 'Project'
373
- $columns = array_slice( $columns, 0, 1, true ) + array( 'thumbnail' => '' ) + array_slice( $columns, 1, NULL, true );
374
- }
375
-
376
- return $columns;
377
- }
378
-
379
- /**
380
- * Add featured image to column
381
- */
382
- function image_column( $column, $post_id ) {
383
- global $post;
384
- switch ( $column ) {
385
- case 'thumbnail':
386
- echo get_the_post_thumbnail( $post_id, 'jetpack-portfolio-admin-thumb' );
387
- break;
388
- }
389
- }
390
-
391
- /**
392
- * Adjust image column width
393
- */
394
- function enqueue_admin_styles( $hook ) {
395
- $screen = get_current_screen();
396
-
397
- if ( 'edit.php' == $hook && self::CUSTOM_POST_TYPE == $screen->post_type && current_theme_supports( 'post-thumbnails' ) ) {
398
- wp_add_inline_style( 'wp-admin', '.column-thumbnail img:nth-of-type(2) { display: none; } .manage-column.column-thumbnail { width: 50px; } @media screen and (max-width: 360px) { .column-thumbnail{ display:none; } }' );
399
- }
400
- }
401
-
402
- /**
403
- * Adds portfolio section to the Customizer.
404
- */
405
- function customize_register( $wp_customize ) {
406
- $options = get_theme_support( self::CUSTOM_POST_TYPE );
407
-
408
- if ( ( ! isset( $options[0]['title'] ) || true !== $options[0]['title'] ) && ( ! isset( $options[0]['content'] ) || true !== $options[0]['content'] ) && ( ! isset( $options[0]['featured-image'] ) || true !== $options[0]['featured-image'] ) ) {
409
- return;
410
- }
411
-
412
- $wp_customize->add_panel( 'ect_plugin_options', array(
413
- 'title' => esc_html__( 'Essential Content Types Plugin Options', 'essential-content-types' ),
414
- 'priority' => 1,
415
- ) );
416
-
417
- $wp_customize->add_section( 'jetpack_portfolio', array(
418
- 'title' => esc_html__( 'Portfolio', 'essential-content-types' ),
419
- 'theme_supports' => self::CUSTOM_POST_TYPE,
420
- 'priority' => 130,
421
- 'panel' => 'ect_plugin_options',
422
- ) );
423
-
424
- if ( isset( $options[0]['title'] ) && true === $options[0]['title'] ) {
425
- $wp_customize->add_setting( 'jetpack_portfolio_title', array(
426
- 'default' => esc_html__( 'Projects', 'essential-content-types' ),
427
- 'type' => 'option',
428
- 'sanitize_callback' => 'sanitize_text_field',
429
- 'sanitize_js_callback' => 'sanitize_text_field',
430
- ) );
431
-
432
- $wp_customize->add_control( 'jetpack_portfolio_title', array(
433
- 'section' => 'jetpack_portfolio',
434
- 'label' => esc_html__( 'Portfolio Archive Title', 'essential-content-types' ),
435
- 'type' => 'text',
436
- ) );
437
- }
438
-
439
- if ( isset( $options[0]['content'] ) && true === $options[0]['content'] ) {
440
- $wp_customize->add_setting( 'jetpack_portfolio_content', array(
441
- 'default' => '',
442
- 'type' => 'option',
443
- 'sanitize_callback' => 'wp_kses_post',
444
- 'sanitize_js_callback' => 'wp_kses_post',
445
- ) );
446
-
447
- $wp_customize->add_control( 'jetpack_portfolio_content', array(
448
- 'section' => 'jetpack_portfolio',
449
- 'label' => esc_html__( 'Portfolio Archive Content', 'essential-content-types' ),
450
- 'type' => 'textarea',
451
- ) );
452
- }
453
-
454
- if ( isset( $options[0]['featured-image'] ) && true === $options[0]['featured-image'] ) {
455
- $wp_customize->add_setting( 'jetpack_portfolio_featured_image', array(
456
- 'default' => '',
457
- 'type' => 'option',
458
- 'sanitize_callback' => 'attachment_url_to_postid',
459
- 'sanitize_js_callback' => 'attachment_url_to_postid',
460
- 'theme_supports' => 'post-thumbnails',
461
- ) );
462
-
463
- $wp_customize->add_control( new WP_Customize_Image_Control( $wp_customize, 'jetpack_portfolio_featured_image', array(
464
- 'section' => 'jetpack_portfolio',
465
- 'label' => esc_html__( 'Portfolio Archive Featured Image', 'essential-content-types' ),
466
- ) ) );
467
- }
468
- }
469
-
470
- /**
471
- * Follow CPT reading setting on CPT archive and taxonomy pages
472
- */
473
- function query_reading_setting( $query ) {
474
- if ( ! is_admin() &&
475
- $query->is_main_query() &&
476
- ( $query->is_post_type_archive( self::CUSTOM_POST_TYPE ) || $query->is_tax( self::CUSTOM_TAXONOMY_TYPE ) || $query->is_tax( self::CUSTOM_TAXONOMY_TAG ) )
477
- ) {
478
- $post_per_page = ( null != get_option('posts_per_page') ) ? get_option('posts_per_page') : '10';
479
- $query->set( 'posts_per_page', get_option( self::OPTION_READING_SETTING, $post_per_page ) );
480
- }
481
- }
482
-
483
- /**
484
- * Add to REST API post type whitelist
485
- */
486
- function allow_portfolio_rest_api_type( $post_types ) {
487
- $post_types[] = self::CUSTOM_POST_TYPE;
488
-
489
- return $post_types;
490
- }
491
-
492
- /**
493
- * Our [portfolio] shortcode.
494
- * Prints Portfolio data styled to look good on *any* theme.
495
- *
496
- * @return portfolio_shortcode_html
497
- */
498
- static function portfolio_shortcode( $atts ) {
499
- // Default attributes
500
- $atts = shortcode_atts( array(
501
- 'display_types' => true,
502
- 'display_tags' => true,
503
- 'display_content' => true,
504
- 'display_author' => false,
505
- 'show_filter' => false,
506
- 'include_type' => false,
507
- 'include_tag' => false,
508
- 'columns' => 2,
509
- 'showposts' => -1,
510
- 'order' => 'asc',
511
- 'orderby' => 'date',
512
- ), $atts, 'portfolio' );
513
-
514
- // A little sanitization
515
- if ( $atts['display_types'] && 'true' != $atts['display_types'] ) {
516
- $atts['display_types'] = false;
517
- }
518
-
519
- if ( $atts['display_tags'] && 'true' != $atts['display_tags'] ) {
520
- $atts['display_tags'] = false;
521
- }
522
-
523
- if ( $atts['display_author'] && 'true' != $atts['display_author'] ) {
524
- $atts['display_author'] = false;
525
- }
526
-
527
- if ( $atts['display_content'] && 'true' != $atts['display_content'] && 'full' != $atts['display_content'] ) {
528
- $atts['display_content'] = false;
529
- }
530
-
531
- if ( $atts['include_type'] ) {
532
- $atts['include_type'] = explode( ',', str_replace( ' ', '', $atts['include_type'] ) );
533
- }
534
-
535
- if ( $atts['include_tag'] ) {
536
- $atts['include_tag'] = explode( ',', str_replace( ' ', '', $atts['include_tag'] ) );
537
- }
538
-
539
- // Check if column value is set to valid numbers or else set default value as 2
540
- if( 1 <= $atts['columns'] && 6 >= $atts['columns'] ) {
541
- $atts['columns'] = absint( $atts['columns'] );
542
- } else {
543
- $atts['columns'] = 2;
544
- }
545
-
546
- $atts['showposts'] = intval( $atts['showposts'] );
547
-
548
-
549
- if ( $atts['order'] ) {
550
- $atts['order'] = urldecode( $atts['order'] );
551
- $atts['order'] = strtoupper( $atts['order'] );
552
- if ( 'DESC' != $atts['order'] ) {
553
- $atts['order'] = 'ASC';
554
- }
555
- }
556
-
557
- if ( $atts['orderby'] ) {
558
- $atts['orderby'] = urldecode( $atts['orderby'] );
559
- $atts['orderby'] = strtolower( $atts['orderby'] );
560
- $allowed_keys = array( 'author', 'date', 'title', 'rand' );
561
-
562
- $parsed = array();
563
- foreach ( explode( ',', $atts['orderby'] ) as $portfolio_index_number => $orderby ) {
564
- if ( ! in_array( $orderby, $allowed_keys ) ) {
565
- continue;
566
- }
567
- $parsed[] = $orderby;
568
- }
569
-
570
- if ( empty( $parsed ) ) {
571
- unset( $atts['orderby'] );
572
- } else {
573
- $atts['orderby'] = implode( ' ', $parsed );
574
- }
575
- }
576
-
577
- // enqueue shortcode styles when shortcode is used
578
- wp_enqueue_style( 'jetpack-portfolio-style', plugins_url( 'css/portfolio-shortcode.css', __FILE__ ), array(), '20140326' );
579
-
580
- return self::portfolio_shortcode_html( $atts );
581
- }
582
-
583
- /**
584
- * Query to retrieve entries from the Portfolio post_type.
585
- *
586
- * @return object
587
- */
588
- static function portfolio_query( $atts ) {
589
- // Default query arguments
590
- $default = array(
591
- 'order' => $atts['order'],
592
- 'orderby' => $atts['orderby'],
593
- 'posts_per_page' => $atts['showposts'],
594
- );
595
-
596
- $args = wp_parse_args( $atts, $default );
597
- $args['post_type'] = self::CUSTOM_POST_TYPE; // Force this post type
598
-
599
- if ( false != $atts['include_type'] || false != $atts['include_tag'] ) {
600
- $args['tax_query'] = array();
601
- }
602
-
603
- // If 'include_type' has been set use it on the main query
604
- if ( false != $atts['include_type'] ) {
605
- array_push( $args['tax_query'], array(
606
- 'taxonomy' => self::CUSTOM_TAXONOMY_TYPE,
607
- 'field' => 'slug',
608
- 'terms' => $atts['include_type'],
609
- ) );
610
- }
611
-
612
- // If 'include_tag' has been set use it on the main query
613
- if ( false != $atts['include_tag'] ) {
614
- array_push( $args['tax_query'], array(
615
- 'taxonomy' => self::CUSTOM_TAXONOMY_TAG,
616
- 'field' => 'slug',
617
- 'terms' => $atts['include_tag'],
618
- ) );
619
- }
620
-
621
- if ( false != $atts['include_type'] && false != $atts['include_tag'] ) {
622
- $args['tax_query']['relation'] = 'AND';
623
- }
624
-
625
- // Run the query and return
626
- $query = new WP_Query( $args );
627
- return $query;
628
- }
629
-
630
- /**
631
- * The Portfolio shortcode loop.
632
- *
633
- * @todo add theme color styles
634
- * @return html
635
- */
636
-
637
- static function portfolio_shortcode_html( $atts ) {
638
-
639
- $query = self::portfolio_query( $atts );
640
-
641
- ob_start();
642
-
643
- // If we have posts, create the html
644
- // with hportfolio markup
645
- if ( $query->have_posts() ) {
646
-
647
-
648
- /**
649
- * Hook: ect_before_portfolio_loop.
650
- *
651
- * @hooked ect_portfolio_section
652
- */
653
- $layout = ect_get_layout();
654
- do_action( 'ect_before_portfolio_loop', $layout[$atts['columns']] );
655
- ?>
656
- <?php
657
- //ect_portfolio_loop_start( $layout[$atts['columns']] );
658
- while ( $query->have_posts() ) {
659
- $query->the_post();
660
- ect_get_template_part( 'content', 'portfolio', $atts );
661
- /**
662
- * Hook: ect_portfolio_loop.
663
- *
664
- * @hooked
665
- */
666
- }
667
- wp_reset_postdata();
668
- //ect_portfolio_loop_end();
669
- ?>
670
- <?php
671
-
672
- /**
673
- * Hook: ect_after_portfolio_loop.
674
- *
675
- * @hooked
676
- */
677
- do_action( 'ect_after_portfolio_loop' );
678
-
679
- } else {
680
- /**
681
- * Hook: ect_no_portfolio_found.
682
- *
683
- * @hooked ect_no_portfolio_found
684
- */
685
- do_action( 'ect_no_portfolio_found' );
686
- }
687
-
688
- $html = ob_get_clean();
689
-
690
- // If there is a [portfolio] within a [portfolio], remove the shortcode
691
- if ( has_shortcode( $html, 'portfolio' ) ){
692
- remove_shortcode( 'portfolio' );
693
- }
694
-
695
- // Return the HTML block
696
- return $html;
697
- }
698
-
699
- /**
700
- * Displays the project type that a project belongs to.
701
- *
702
- * @return html
703
- */
704
- static function get_project_type( $post_id ) {
705
- $project_types = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TYPE );
706
-
707
- // If no types, return empty string
708
- if ( empty( $project_types ) || is_wp_error( $project_types ) ) {
709
- return;
710
- }
711
-
712
- $html = '<div class="project-types"><span>' . __( 'Types', 'essential-content-types' ) . ':</span>';
713
- $types = array();
714
- // Loop thorugh all the types
715
- foreach ( $project_types as $project_type ) {
716
- $project_type_link = get_term_link( $project_type, self::CUSTOM_TAXONOMY_TYPE );
717
-
718
- if ( is_wp_error( $project_type_link ) ) {
719
- return $project_type_link;
720
- }
721
-
722
- $types[] = '<a href="' . esc_url( $project_type_link ) . '" rel="tag">' . esc_html( $project_type->name ) . '</a>';
723
- }
724
- $html .= ' '.implode( ', ', $types );
725
- $html .= '</div>';
726
-
727
- return $html;
728
- }
729
-
730
- /**
731
- * Displays the project tags that a project belongs to.
732
- *
733
- * @return html
734
- */
735
- static function get_project_tags( $post_id ) {
736
- $project_tags = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TAG );
737
-
738
- // If no tags, return empty string
739
- if ( empty( $project_tags ) || is_wp_error( $project_tags ) ) {
740
- return false;
741
- }
742
-
743
- $html = '<div class="project-tags"><span>' . __( 'Tags', 'essential-content-types' ) . ':</span>';
744
- $tags = array();
745
- // Loop thorugh all the tags
746
- foreach ( $project_tags as $project_tag ) {
747
- $project_tag_link = get_term_link( $project_tag, self::CUSTOM_TAXONOMY_TYPE );
748
-
749
- if ( is_wp_error( $project_tag_link ) ) {
750
- return $project_tag_link;
751
- }
752
-
753
- $tags[] = '<a href="' . esc_url( $project_tag_link ) . '" rel="tag">' . esc_html( $project_tag->name ) . '</a>';
754
- }
755
- $html .= ' '. implode( ', ', $tags );
756
- $html .= '</div>';
757
-
758
- return $html;
759
- }
760
-
761
- /**
762
- * Displays the author of the current portfolio project.
763
- *
764
- * @return html
765
- */
766
- static function get_project_author() {
767
- $html = '<div class="project-author">';
768
- /* translators: %1$s is link to author posts, %2$s is author display name */
769
- $html .= sprintf( __( '<span>Author:</span> <a href="%1$s">%2$s</a>', 'essential-content-types' ),
770
- esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ),
771
- esc_html( get_the_author() )
772
- );
773
- $html .= '</div>';
774
-
775
- return $html;
776
- }
777
- }
778
- add_action( 'init', array( 'Essential_Content_Jetpack_Portfolio', 'init' ) );
779
-
780
- /**
781
- * Add Portfolio support
782
- */
783
- function essential_content_portfolio_support() {
784
- /*
785
- * Adding theme support for Jetpack Portfolio CPT.
786
- */
787
- add_theme_support( 'jetpack-portfolio', array(
788
- 'title' => true,
789
- 'content' => true,
790
- 'featured-image' => true,
791
- ) );
792
- add_image_size( 'ect-jetpack-portfolio-featured', 640, 640, true );
793
- }
794
- add_action( 'after_setup_theme', 'essential_content_portfolio_support' );
795
-
796
-
797
- if( ! function_exists( 'essential_content_get_portfolio_thumbnail_link' ) ):
798
- /**
799
- * Display the featured image if it's available
800
- *
801
- * @return html
802
- */
803
- function essential_content_get_portfolio_thumbnail_link( $post_id ) {
804
- if ( has_post_thumbnail( $post_id ) ) {
805
- /**
806
- * Change the Portfolio thumbnail size.
807
- *
808
- * @module custom-content-types
809
- *
810
- * @since 3.4.0
811
- *
812
- * @param string|array $var Either a registered size keyword or size array.
813
- */
814
- return '<a class="portfolio-featured-image" href="' . esc_url( get_permalink( $post_id ) ) . '">' . get_the_post_thumbnail( $post_id, apply_filters( 'jetpack_portfolio_thumbnail_size', 'ect-jetpack-portfolio-featured' ) ) . '</a>';
815
- }
816
- }
817
- endif;
818
-
819
-
820
- if( ! function_exists( 'essential_content_no_portfolio_found' ) ):
821
- /**
822
- * No items found text
823
- *
824
- * @return html
825
- */
826
- function essential_content_no_portfolio_found() {
827
- echo "<p><em>" . esc_html__( 'Your Portfolio Archive currently has no entries. You can start creating them on your dashboard.', 'essential-content-types-pro' ) . "</em></p>";
828
- }
829
- endif;
830
- add_action( 'ect_no_portfolio_found', 'essential_content_no_portfolio_found', 10 );
831
-
832
-
833
- if( ! function_exists( 'essential_content_portfolio_section_open' ) ):
834
- /**
835
- * Open section
836
- *
837
- * @return html
838
- */
839
- function essential_content_portfolio_section_open( $layout ) {
840
- echo '<div class="ect-portfolio ect-section">';
841
- echo '<div class="ect-wrapper">';
842
- }
843
- endif;
844
- add_action( 'ect_before_portfolio_loop', 'essential_content_portfolio_section_open', 10, 1 );
845
-
846
-
847
- if( ! function_exists( 'essential_content_portfolio_loop_start' ) ):
848
- /**
849
- * open wrapper before loop
850
- *
851
- */
852
- function essential_content_portfolio_loop_start( $layout = null ){
853
- echo '<div class="section-content-wrapper portfolio-content-wrapper ' . $layout . '">';
854
- }
855
- endif;
856
- add_action( 'ect_before_portfolio_loop', 'essential_content_portfolio_loop_start', 30 );
857
-
858
-
859
- if( ! function_exists( 'essential_content_portfolio_loop_end' ) ):
860
- /**
861
- * close wrapper after loop
862
- *
863
- */
864
- function essential_content_portfolio_loop_end(){
865
- echo '</div><!-- .portfolio-content-wrapper -->';
866
- }
867
- endif;
868
- add_action( 'ect_after_portfolio_loop', 'essential_content_portfolio_loop_end', 10 );
869
-
870
-
871
- if( ! function_exists( 'essential_content_portfolio_section_close' ) ):
872
- /**
873
- * Close section
874
- *
875
- * @return html
876
- */
877
- function essential_content_portfolio_section_close() {
878
- echo '</div><!-- .ect-wrapper -->';
879
- echo '</div><!-- .ect-section -->';
880
- }
881
- endif;
882
  add_action( 'ect_after_portfolio_loop', 'essential_content_portfolio_section_close', 20 );
1
+ <?php
2
+
3
+ /**
4
+ * Support JetPack Portfolio
5
+ */
6
+ class Essential_Content_Jetpack_Portfolio {
7
+ const CUSTOM_POST_TYPE = 'jetpack-portfolio';
8
+ const CUSTOM_TAXONOMY_TYPE = 'jetpack-portfolio-type';
9
+ const CUSTOM_TAXONOMY_TAG = 'jetpack-portfolio-tag';
10
+ const OPTION_NAME = 'jetpack_portfolio';
11
+ const OPTION_READING_SETTING = 'jetpack_portfolio_posts_per_page';
12
+
13
+ public $version = ESSENTIAL_CONTENT_TYPES_VERSION;
14
+
15
+ static function init() {
16
+ static $instance = false;
17
+
18
+ if ( ! $instance ) {
19
+ $instance = new Essential_Content_Jetpack_Portfolio;
20
+ }
21
+
22
+ return $instance;
23
+ }
24
+
25
+ /**
26
+ * Conditionally hook into WordPress.
27
+ *
28
+ * Setup user option for enabling CPT
29
+ * If user has CPT enabled, show in admin
30
+ */
31
+ function __construct() {
32
+ // Add an option to enable the CPT
33
+ add_action( 'admin_init', array( $this, 'settings_api_init' ) );
34
+
35
+ // Check on theme switch if theme supports CPT and setting is disabled
36
+ add_action( 'after_switch_theme', array( $this, 'activation_post_type_support' ) );
37
+
38
+ // Make sure the post types are loaded for imports
39
+ add_action( 'import_start', array( $this, 'register_post_types' ) );
40
+
41
+ // Add to REST API post type whitelist
42
+ add_filter( 'rest_api_allowed_post_types', array( $this, 'allow_portfolio_rest_api_type' ) );
43
+
44
+ $setting = 1;
45
+
46
+ if ( class_exists( 'Jetpack_Options' ) ) {
47
+ $setting = Jetpack_Options::get_option_and_ensure_autoload( self::OPTION_NAME, '0' );
48
+ }
49
+
50
+ // Bail early if Portfolio option is not set and the theme doesn't declare support
51
+ if ( empty( $setting ) && ! $this->site_supports_custom_post_type() ) {
52
+ return;
53
+ }
54
+
55
+ // Enable Omnisearch for Portfolio Items.
56
+ if ( class_exists( 'Jetpack_Omnisearch_Posts' ) )
57
+ new Jetpack_Omnisearch_Posts( self::CUSTOM_POST_TYPE );
58
+
59
+ // CPT magic
60
+ $this->register_post_types();
61
+ add_action( sprintf( 'add_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 );
62
+ add_action( sprintf( 'update_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 );
63
+ add_action( sprintf( 'publish_%s', self::CUSTOM_POST_TYPE), array( $this, 'flush_rules_on_first_project' ) );
64
+ add_action( 'after_switch_theme', array( $this, 'flush_rules_on_switch' ) );
65
+
66
+ // Admin Customization
67
+ add_filter( 'post_updated_messages', array( $this, 'updated_messages' ) );
68
+ add_filter( sprintf( 'manage_%s_posts_columns', self::CUSTOM_POST_TYPE), array( $this, 'edit_admin_columns' ) );
69
+ add_filter( sprintf( 'manage_%s_posts_custom_column', self::CUSTOM_POST_TYPE), array( $this, 'image_column' ), 10, 2 );
70
+ add_action( 'customize_register', array( $this, 'customize_register' ) );
71
+
72
+ add_image_size( 'jetpack-portfolio-admin-thumb', 50, 50, true );
73
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
74
+
75
+ // register jetpack_portfolio shortcode and portfolio shortcode (legacy)
76
+ add_shortcode( 'portfolio', array( $this, 'portfolio_shortcode' ) );
77
+ add_shortcode( 'jetpack_portfolio', array( $this, 'portfolio_shortcode' ) );
78
+
79
+ // Adjust CPT archive and custom taxonomies to obey CPT reading setting
80
+ add_filter( 'pre_get_posts', array( $this, 'query_reading_setting' ) );
81
+
82
+ // If CPT was enabled programatically and no CPT items exist when user switches away, disable
83
+ if ( $setting && $this->site_supports_custom_post_type() ) {
84
+ add_action( 'switch_theme', array( $this, 'deactivation_post_type_support' ) );
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Add a checkbox field in 'Settings' > 'Writing'
90
+ * for enabling CPT functionality.
91
+ *
92
+ * @return null
93
+ */
94
+ function settings_api_init() {
95
+ add_settings_field(
96
+ self::OPTION_NAME,
97
+ '<span class="cpt-options">' . esc_html__( 'Portfolio Projects', 'essential-content-types' ) . '</span>',
98
+ array( $this, 'setting_html' ),
99
+ 'writing',
100
+ 'jetpack_cpt_section'
101
+ );
102
+ register_setting(
103
+ 'writing',
104
+ self::OPTION_NAME,
105
+ 'intval'
106
+ );
107
+
108
+ // Check if CPT is enabled first so that intval doesn't get set to NULL on re-registering
109
+ if ( get_option( self::OPTION_NAME, '0' ) || current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
110
+ register_setting(
111
+ 'writing',
112
+ self::OPTION_READING_SETTING,
113
+ 'intval'
114
+ );
115
+ }
116
+ }
117
+
118
+ /**
119
+ * HTML code to display a checkbox true/false option
120
+ * for the Portfolio CPT setting.
121
+ *
122
+ * @return html
123
+ */
124
+ function setting_html() {
125
+ if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) : ?>
126
+ <p><?php printf( /* translators: %s is the name of a custom post type such as "jetpack-portfolio" */ __( 'Your theme supports <strong>%s</strong>', 'essential-content-types' ), self::CUSTOM_POST_TYPE ); ?></p>
127
+ <?php else : ?>
128
+ <label for="<?php echo esc_attr( self::OPTION_NAME ); ?>">
129
+ <input name="<?php echo esc_attr( self::OPTION_NAME ); ?>" id="<?php echo esc_attr( self::OPTION_NAME ); ?>" <?php echo checked( get_option( self::OPTION_NAME, '0' ), true, false ); ?> type="checkbox" value="1" />
130
+ <?php esc_html_e( 'Enable Portfolio Projects for this site.', 'essential-content-types' ); ?>
131
+ <a target="_blank" href="http://en.support.wordpress.com/portfolios/"><?php esc_html_e( 'Learn More', 'essential-content-types' ); ?></a>
132
+ </label>
133
+ <?php endif;
134
+ if ( get_option( self::OPTION_NAME, '0' ) || current_theme_supports( self::CUSTOM_POST_TYPE ) ) :
135
+ printf( '<p><label for="%1$s">%2$s</label></p>',
136
+ esc_attr( self::OPTION_READING_SETTING ),
137
+ /* translators: %1$s is replaced with an input field for numbers */
138
+ sprintf( __( 'Portfolio pages display at most %1$s projects', 'essential-content-types' ),
139
+ sprintf( '<input name="%1$s" id="%1$s" type="number" step="1" min="1" value="%2$s" class="small-text" />',
140
+ esc_attr( self::OPTION_READING_SETTING ),
141
+ esc_attr( get_option( self::OPTION_READING_SETTING, '10' ) )
142
+ )
143
+ )
144
+ );
145
+ endif;
146
+ }
147
+
148
+ /**
149
+ * Should this Custom Post Type be made available?
150
+ */
151
+ function site_supports_custom_post_type() {
152
+ // If the current theme requests it.
153
+ if ( current_theme_supports( self::CUSTOM_POST_TYPE ) || get_option( self::OPTION_NAME, '0' ) ) {
154
+ return true;
155
+ }
156
+
157
+ // Otherwise, say no unless something wants to filter us to say yes.
158
+ /** This action is documented in modules/custom-post-types/nova.php */
159
+ return (bool) apply_filters( 'jetpack_enable_cpt', false, self::CUSTOM_POST_TYPE );
160
+ }
161
+
162
+ /*
163
+ * Flush permalinks when CPT option is turned on/off
164
+ */
165
+ function flush_rules_on_enable() {
166
+ flush_rewrite_rules();
167
+ }
168
+
169
+ /*
170
+ * Count published projects and flush permalinks when first projects is published
171
+ */
172
+ function flush_rules_on_first_project() {
173
+ $projects = get_transient( 'jetpack-portfolio-count-cache' );
174
+
175
+ if ( false === $projects ) {
176
+ flush_rewrite_rules();
177
+ $projects = (int) wp_count_posts( self::CUSTOM_POST_TYPE )->publish;
178
+
179
+ if ( ! empty( $projects ) ) {
180
+ set_transient( 'jetpack-portfolio-count-cache', $projects, HOUR_IN_SECONDS * 12 );
181
+ }
182
+ }
183
+ }
184
+
185
+ /*
186
+ * Flush permalinks when CPT supported theme is activated
187
+ */
188
+ function flush_rules_on_switch() {
189
+ if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
190
+ flush_rewrite_rules();
191
+ }
192
+ }
193
+
194
+ /**
195
+ * On plugin/theme activation, check if current theme supports CPT
196
+ */
197
+ static function activation_post_type_support() {
198
+ if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
199
+ update_option( self::OPTION_NAME, '1' );
200
+ }
201
+ }
202
+
203
+ /**
204
+ * On theme switch, check if CPT item exists and disable if not
205
+ */
206
+ function deactivation_post_type_support() {
207
+ $portfolios = get_posts( array(
208
+ 'fields' => 'ids',
209
+ 'posts_per_page' => 1,
210
+ 'post_type' => self::CUSTOM_POST_TYPE,
211
+ 'suppress_filters' => false
212
+ ) );
213
+
214
+ if ( empty( $portfolios ) ) {
215
+ update_option( self::OPTION_NAME, '0' );
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Register Post Type
221
+ */
222
+ function register_post_types() {
223
+ if ( post_type_exists( self::CUSTOM_POST_TYPE ) ) {
224
+ return;
225
+ }
226
+
227
+ $args = array(
228
+ 'description' => __( 'Portfolio Items', 'essential-content-types' ),
229
+ 'labels' => array(
230
+ 'name' => esc_html__( 'Projects', 'essential-content-types' ),
231
+ 'singular_name' => esc_html__( 'Project', 'essential-content-types' ),
232
+ 'menu_name' => esc_html__( 'Portfolio', 'essential-content-types' ),
233
+ 'all_items' => esc_html__( 'All Projects', 'essential-content-types' ),
234
+ 'add_new' => esc_html__( 'Add New', 'essential-content-types' ),
235
+ 'add_new_item' => esc_html__( 'Add New Project', 'essential-content-types' ),
236
+ 'edit_item' => esc_html__( 'Edit Project', 'essential-content-types' ),
237
+ 'new_item' => esc_html__( 'New Project', 'essential-content-types' ),
238
+ 'view_item' => esc_html__( 'View Project', 'essential-content-types' ),
239
+ 'search_items' => esc_html__( 'Search Projects', 'essential-content-types' ),
240
+ 'not_found' => esc_html__( 'No Projects found', 'essential-content-types' ),
241
+ 'not_found_in_trash' => esc_html__( 'No Projects found in Trash', 'essential-content-types' ),
242
+ 'filter_items_list' => esc_html__( 'Filter projects list', 'essential-content-types' ),
243
+ 'items_list_navigation' => esc_html__( 'Project list navigation', 'essential-content-types' ),
244
+ 'items_list' => esc_html__( 'Projects list', 'essential-content-types' ),
245
+ ),
246
+ 'supports' => array(
247
+ 'title',
248
+ 'editor',
249
+ 'excerpt',
250
+ 'thumbnail',
251
+ 'author',
252
+ 'comments',
253
+ 'publicize',
254
+ ),
255
+ 'rewrite' => array(
256
+ 'slug' => 'portfolio',
257
+ 'with_front' => false,
258
+ 'feeds' => true,
259
+ 'pages' => true,
260
+ ),
261
+ 'public' => true,
262
+ 'show_ui' => true,
263
+ 'menu_position' => 20, // below Pages
264
+ 'menu_icon' => 'dashicons-portfolio', // 3.8+ dashicon option
265
+ 'capability_type' => 'page',
266
+ 'map_meta_cap' => true,
267
+ 'taxonomies' => array( self::CUSTOM_TAXONOMY_TYPE, self::CUSTOM_TAXONOMY_TAG ),
268
+ 'has_archive' => true,
269
+ 'query_var' => 'portfolio',
270
+ 'show_in_rest' => true,
271
+ );
272
+
273
+ $description = get_option( 'jetpack_portfolio_content' );
274
+ if ( '' !== $description ) {
275
+ $args['description'] = $description;
276
+ }
277
+
278
+ register_post_type( self::CUSTOM_POST_TYPE, $args );
279
+
280
+ register_taxonomy( self::CUSTOM_TAXONOMY_TYPE, self::CUSTOM_POST_TYPE, array(
281
+ 'hierarchical' => true,
282
+ 'labels' => array(
283
+ 'name' => esc_html__( 'Project Types', 'essential-content-types' ),
284
+ 'singular_name' => esc_html__( 'Project Type', 'essential-content-types' ),
285
+ 'menu_name' => esc_html__( 'Project Types', 'essential-content-types' ),
286
+ 'all_items' => esc_html__( 'All Project Types', 'essential-content-types' ),
287
+ 'edit_item' => esc_html__( 'Edit Project Type', 'essential-content-types' ),
288
+ 'view_item' => esc_html__( 'View Project Type', 'essential-content-types' ),
289
+ 'update_item' => esc_html__( 'Update Project Type', 'essential-content-types' ),
290
+ 'add_new_item' => esc_html__( 'Add New Project Type', 'essential-content-types' ),
291
+ 'new_item_name' => esc_html__( 'New Project Type Name', 'essential-content-types' ),
292
+ 'parent_item' => esc_html__( 'Parent Project Type', 'essential-content-types' ),
293
+ 'parent_item_colon' => esc_html__( 'Parent Project Type:', 'essential-content-types' ),
294
+ 'search_items' => esc_html__( 'Search Project Types', 'essential-content-types' ),
295
+ 'items_list_navigation' => esc_html__( 'Project type list navigation', 'essential-content-types' ),
296
+ 'items_list' => esc_html__( 'Project type list', 'essential-content-types' ),
297
+ ),
298
+ 'public' => true,
299
+ 'show_ui' => true,
300
+ 'show_in_nav_menus' => true,
301
+ 'show_in_rest' => true,
302
+ 'show_admin_column' => true,
303
+ 'query_var' => true,
304
+ 'rewrite' => array( 'slug' => 'project-type' ),
305
+ ) );
306
+
307
+ register_taxonomy( self::CUSTOM_TAXONOMY_TAG, self::CUSTOM_POST_TYPE, array(
308
+ 'hierarchical' => false,
309
+ 'labels' => array(
310
+ 'name' => esc_html__( 'Project Tags', 'essential-content-types' ),
311
+ 'singular_name' => esc_html__( 'Project Tag', 'essential-content-types' ),
312
+ 'menu_name' => esc_html__( 'Project Tags', 'essential-content-types' ),
313
+ 'all_items' => esc_html__( 'All Project Tags', 'essential-content-types' ),
314
+ 'edit_item' => esc_html__( 'Edit Project Tag', 'essential-content-types' ),
315
+ 'view_item' => esc_html__( 'View Project Tag', 'essential-content-types' ),
316
+ 'update_item' => esc_html__( 'Update Project Tag', 'essential-content-types' ),
317
+ 'add_new_item' => esc_html__( 'Add New Project Tag', 'essential-content-types' ),
318
+ 'new_item_name' => esc_html__( 'New Project Tag Name', 'essential-content-types' ),
319
+ 'search_items' => esc_html__( 'Search Project Tags', 'essential-content-types' ),
320
+ 'popular_items' => esc_html__( 'Popular Project Tags', 'essential-content-types' ),
321
+ 'separate_items_with_commas' => esc_html__( 'Separate tags with commas', 'essential-content-types' ),
322
+ 'add_or_remove_items' => esc_html__( 'Add or remove tags', 'essential-content-types' ),
323
+ 'choose_from_most_used' => esc_html__( 'Choose from the most used tags', 'essential-content-types' ),
324
+ 'not_found' => esc_html__( 'No tags found.', 'essential-content-types' ),
325
+ 'items_list_navigation' => esc_html__( 'Project tag list navigation', 'essential-content-types' ),
326
+ 'items_list' => esc_html__( 'Project tag list', 'essential-content-types' ),
327
+ ),
328
+ 'public' => true,
329
+ 'show_ui' => true,
330
+ 'show_in_nav_menus' => true,
331
+ 'show_in_rest' => true,
332
+ 'show_admin_column' => true,
333
+ 'query_var' => true,
334
+ 'rewrite' => array( 'slug' => 'project-tag' ),
335
+ ) );
336
+ }
337
+
338
+ /**
339
+ * Update messages for the Portfolio admin.
340
+ */
341
+ function updated_messages( $messages ) {
342
+ global $post;
343
+
344
+ $messages[self::CUSTOM_POST_TYPE] = array(
345
+ 0 => '', // Unused. Messages start at index 1.
346
+ 1 => sprintf( __( 'Project updated. <a href="%s">View item</a>', 'essential-content-types'), esc_url( get_permalink( $post->ID ) ) ),
347
+ 2 => esc_html__( 'Custom field updated.', 'essential-content-types' ),
348
+ 3 => esc_html__( 'Custom field deleted.', 'essential-content-types' ),
349
+ 4 => esc_html__( 'Project updated.', 'essential-content-types' ),
350
+ /* translators: %s: date and time of the revision */
351
+ 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Project restored to revision from %s', 'essential-content-types'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
352
+ 6 => sprintf( __( 'Project published. <a href="%s">View project</a>', 'essential-content-types' ), esc_url( get_permalink( $post->ID ) ) ),
353
+ 7 => esc_html__( 'Project saved.', 'essential-content-types' ),
354
+ 8 => sprintf( __( 'Project submitted. <a target="_blank" href="%s">Preview project</a>', 'essential-content-types'), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
355
+ 9 => sprintf( __( 'Project scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview project</a>', 'essential-content-types' ),
356
+ // translators: Publish box date format, see http://php.net/date
357
+ date_i18n( __( 'M j, Y @ G:i', 'essential-content-types' ), strtotime( $post->post_date ) ), esc_url( get_permalink( $post->ID ) ) ),
358
+ 10 => sprintf( __( 'Project item draft updated. <a target="_blank" href="%s">Preview project</a>', 'essential-content-types' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
359
+ );
360
+
361
+ return $messages;
362
+ }
363
+
364
+ /**
365
+ * Change ‘Title’ column label
366
+ * Add Featured Image column
367
+ */
368
+ function edit_admin_columns( $columns ) {
369
+ // change 'Title' to 'Project'
370
+ $columns['title'] = __( 'Project', 'essential-content-types' );
371
+ if ( current_theme_supports( 'post-thumbnails' ) ) {
372
+ // add featured image before 'Project'
373
+ $columns = array_slice( $columns, 0, 1, true ) + array( 'thumbnail' => '' ) + array_slice( $columns, 1, NULL, true );
374
+ }
375
+
376
+ return $columns;
377
+ }
378
+
379
+ /**
380
+ * Add featured image to column
381
+ */
382
+ function image_column( $column, $post_id ) {
383
+ global $post;
384
+ switch ( $column ) {
385
+ case 'thumbnail':
386
+ echo get_the_post_thumbnail( $post_id, 'jetpack-portfolio-admin-thumb' );
387
+ break;
388
+ }
389
+ }
390
+
391
+ /**
392
+ * Adjust image column width
393
+ */
394
+ function enqueue_admin_styles( $hook ) {
395
+ $screen = get_current_screen();
396
+
397
+ if ( 'edit.php' == $hook && self::CUSTOM_POST_TYPE == $screen->post_type && current_theme_supports( 'post-thumbnails' ) ) {
398
+ wp_add_inline_style( 'wp-admin', '.column-thumbnail img:nth-of-type(2) { display: none; } .manage-column.column-thumbnail { width: 50px; } @media screen and (max-width: 360px) { .column-thumbnail{ display:none; } }' );
399
+ }
400
+ }
401
+
402
+ /**
403
+ * Adds portfolio section to the Customizer.
404
+ */
405
+ function customize_register( $wp_customize ) {
406
+ $options = get_theme_support( self::CUSTOM_POST_TYPE );
407
+
408
+ if ( ( ! isset( $options[0]['title'] ) || true !== $options[0]['title'] ) && ( ! isset( $options[0]['content'] ) || true !== $options[0]['content'] ) && ( ! isset( $options[0]['featured-image'] ) || true !== $options[0]['featured-image'] ) ) {
409
+ return;
410
+ }
411
+
412
+ $wp_customize->add_panel( 'ect_plugin_options', array(
413
+ 'title' => esc_html__( 'Essential Content Types Plugin Options', 'essential-content-types' ),
414
+ 'priority' => 1,
415
+ ) );
416
+
417
+ $wp_customize->add_section( 'jetpack_portfolio', array(
418
+ 'title' => esc_html__( 'Portfolio', 'essential-content-types' ),
419
+ 'theme_supports' => self::CUSTOM_POST_TYPE,
420
+ 'priority' => 130,
421
+ 'panel' => 'ect_plugin_options',
422
+ ) );
423
+
424
+ if ( isset( $options[0]['title'] ) && true === $options[0]['title'] ) {
425
+ $wp_customize->add_setting( 'jetpack_portfolio_title', array(
426
+ 'default' => esc_html__( 'Projects', 'essential-content-types' ),
427
+ 'type' => 'option',
428
+ 'sanitize_callback' => 'sanitize_text_field',
429
+ 'sanitize_js_callback' => 'sanitize_text_field',
430
+ ) );
431
+
432
+ $wp_customize->add_control( 'jetpack_portfolio_title', array(
433
+ 'section' => 'jetpack_portfolio',
434
+ 'label' => esc_html__( 'Portfolio Archive Title', 'essential-content-types' ),
435
+ 'type' => 'text',
436
+ ) );
437
+ }
438
+
439
+ if ( isset( $options[0]['content'] ) && true === $options[0]['content'] ) {
440
+ $wp_customize->add_setting( 'jetpack_portfolio_content', array(
441
+ 'default' => '',
442
+ 'type' => 'option',
443
+ 'sanitize_callback' => 'wp_kses_post',
444
+ 'sanitize_js_callback' => 'wp_kses_post',
445
+ ) );
446
+
447
+ $wp_customize->add_control( 'jetpack_portfolio_content', array(
448
+ 'section' => 'jetpack_portfolio',
449
+ 'label' => esc_html__( 'Portfolio Archive Content', 'essential-content-types' ),
450
+ 'type' => 'textarea',
451
+ ) );
452
+ }
453
+
454
+ if ( isset( $options[0]['featured-image'] ) && true === $options[0]['featured-image'] ) {
455
+ $wp_customize->add_setting( 'jetpack_portfolio_featured_image', array(
456
+ 'default' => '',
457
+ 'type' => 'option',
458
+ 'sanitize_callback' => 'attachment_url_to_postid',
459
+ 'sanitize_js_callback' => 'attachment_url_to_postid',
460
+ 'theme_supports' => 'post-thumbnails',
461
+ ) );
462
+
463
+ $wp_customize->add_control( new WP_Customize_Image_Control( $wp_customize, 'jetpack_portfolio_featured_image', array(
464
+ 'section' => 'jetpack_portfolio',
465
+ 'label' => esc_html__( 'Portfolio Archive Featured Image', 'essential-content-types' ),
466
+ ) ) );
467
+ }
468
+ }
469
+
470
+ /**
471
+ * Follow CPT reading setting on CPT archive and taxonomy pages
472
+ */
473
+ function query_reading_setting( $query ) {
474
+ if ( ! is_admin() &&
475
+ $query->is_main_query() &&
476
+ ( $query->is_post_type_archive( self::CUSTOM_POST_TYPE ) || $query->is_tax( self::CUSTOM_TAXONOMY_TYPE ) || $query->is_tax( self::CUSTOM_TAXONOMY_TAG ) )
477
+ ) {
478
+ $post_per_page = ( null != get_option('posts_per_page') ) ? get_option('posts_per_page') : '10';
479
+ $query->set( 'posts_per_page', get_option( self::OPTION_READING_SETTING, $post_per_page ) );
480
+ }
481
+ }
482
+
483
+ /**
484
+ * Add to REST API post type whitelist
485
+ */
486
+ function allow_portfolio_rest_api_type( $post_types ) {
487
+ $post_types[] = self::CUSTOM_POST_TYPE;
488
+
489
+ return $post_types;
490
+ }
491
+
492
+ /**
493
+ * Our [portfolio] shortcode.
494
+ * Prints Portfolio data styled to look good on *any* theme.
495
+ *
496
+ * @return portfolio_shortcode_html
497
+ */
498
+ static function portfolio_shortcode( $atts ) {
499
+ // Default attributes
500
+ $atts = shortcode_atts( array(
501
+ 'display_types' => true,
502
+ 'display_tags' => true,
503
+ 'display_content' => true,
504
+ 'display_author' => false,
505
+ 'show_filter' => false,
506
+ 'include_type' => false,
507
+ 'include_tag' => false,
508
+ 'columns' => 2,
509
+ 'showposts' => -1,
510
+ 'order' => 'asc',
511
+ 'orderby' => 'date',
512
+ ), $atts, 'portfolio' );
513
+
514
+ // A little sanitization
515
+ if ( $atts['display_types'] && 'true' != $atts['display_types'] ) {
516
+ $atts['display_types'] = false;
517
+ }
518
+
519
+ if ( $atts['display_tags'] && 'true' != $atts['display_tags'] ) {
520
+ $atts['display_tags'] = false;
521
+ }
522
+
523
+ if ( $atts['display_author'] && 'true' != $atts['display_author'] ) {
524
+ $atts['display_author'] = false;
525
+ }
526
+
527
+ if ( $atts['display_content'] && 'true' != $atts['display_content'] && 'full' != $atts['display_content'] ) {
528
+ $atts['display_content'] = false;
529
+ }
530
+
531
+ if ( $atts['include_type'] ) {
532
+ $atts['include_type'] = explode( ',', str_replace( ' ', '', $atts['include_type'] ) );
533
+ }
534
+
535
+ if ( $atts['include_tag'] ) {
536
+ $atts['include_tag'] = explode( ',', str_replace( ' ', '', $atts['include_tag'] ) );
537
+ }
538
+
539
+ // Check if column value is set to valid numbers or else set default value as 2
540
+ if( 1 <= $atts['columns'] && 6 >= $atts['columns'] ) {
541
+ $atts['columns'] = absint( $atts['columns'] );
542
+ } else {
543
+ $atts['columns'] = 2;
544
+ }
545
+
546
+ $atts['showposts'] = intval( $atts['showposts'] );
547
+
548
+
549
+ if ( $atts['order'] ) {
550
+ $atts['order'] = urldecode( $atts['order'] );
551
+ $atts['order'] = strtoupper( $atts['order'] );
552
+ if ( 'DESC' != $atts['order'] ) {
553
+ $atts['order'] = 'ASC';
554
+ }
555
+ }
556
+
557
+ if ( $atts['orderby'] ) {
558
+ $atts['orderby'] = urldecode( $atts['orderby'] );
559
+ $atts['orderby'] = strtolower( $atts['orderby'] );
560
+ $allowed_keys = array( 'author', 'date', 'title', 'rand' );
561
+
562
+ $parsed = array();
563
+ foreach ( explode( ',', $atts['orderby'] ) as $portfolio_index_number => $orderby ) {
564
+ if ( ! in_array( $orderby, $allowed_keys ) ) {
565
+ continue;
566
+ }
567
+ $parsed[] = $orderby;
568
+ }
569
+
570
+ if ( empty( $parsed ) ) {
571
+ unset( $atts['orderby'] );
572
+ } else {
573
+ $atts['orderby'] = implode( ' ', $parsed );
574
+ }
575
+ }
576
+
577
+ // enqueue shortcode styles when shortcode is used
578
+ wp_enqueue_style( 'jetpack-portfolio-style', plugins_url( 'css/portfolio-shortcode.css', __FILE__ ), array(), '20140326' );
579
+
580
+ return self::portfolio_shortcode_html( $atts );
581
+ }
582
+
583
+ /**
584
+ * Query to retrieve entries from the Portfolio post_type.
585
+ *
586
+ * @return object
587
+ */
588
+ static function portfolio_query( $atts ) {
589
+ // Default query arguments
590
+ $default = array(
591
+ 'order' => $atts['order'],
592
+ 'orderby' => $atts['orderby'],
593
+ 'posts_per_page' => $atts['showposts'],
594
+ );
595
+
596
+ $args = wp_parse_args( $atts, $default );
597
+ $args['post_type'] = self::CUSTOM_POST_TYPE; // Force this post type
598
+
599
+ if ( false != $atts['include_type'] || false != $atts['include_tag'] ) {
600
+ $args['tax_query'] = array();
601
+ }
602
+
603
+ // If 'include_type' has been set use it on the main query
604
+ if ( false != $atts['include_type'] ) {
605
+ array_push( $args['tax_query'], array(
606
+ 'taxonomy' => self::CUSTOM_TAXONOMY_TYPE,
607
+ 'field' => 'slug',
608
+ 'terms' => $atts['include_type'],
609
+ ) );
610
+ }
611
+
612
+ // If 'include_tag' has been set use it on the main query
613
+ if ( false != $atts['include_tag'] ) {
614
+ array_push( $args['tax_query'], array(
615
+ 'taxonomy' => self::CUSTOM_TAXONOMY_TAG,
616
+ 'field' => 'slug',
617
+ 'terms' => $atts['include_tag'],
618
+ ) );
619
+ }
620
+
621
+ if ( false != $atts['include_type'] && false != $atts['include_tag'] ) {
622
+ $args['tax_query']['relation'] = 'AND';
623
+ }
624
+
625
+ // Run the query and return
626
+ $query = new WP_Query( $args );
627
+ return $query;
628
+ }
629
+
630
+ /**
631
+ * The Portfolio shortcode loop.
632
+ *
633
+ * @todo add theme color styles
634
+ * @return html
635
+ */
636
+
637
+ static function portfolio_shortcode_html( $atts ) {
638
+
639
+ $query = self::portfolio_query( $atts );
640
+
641
+ ob_start();
642
+
643
+ // If we have posts, create the html
644
+ // with hportfolio markup
645
+ if ( $query->have_posts() ) {
646
+
647
+
648
+ /**
649
+ * Hook: ect_before_portfolio_loop.
650
+ *
651
+ * @hooked ect_portfolio_section
652
+ */
653
+ $layout = ect_get_layout();
654
+ do_action( 'ect_before_portfolio_loop', $layout[$atts['columns']] );
655
+ ?>
656
+ <?php
657
+ //ect_portfolio_loop_start( $layout[$atts['columns']] );
658
+ while ( $query->have_posts() ) {
659
+ $query->the_post();
660
+ ect_get_template_part( 'content', 'portfolio', $atts );
661
+ /**
662
+ * Hook: ect_portfolio_loop.
663
+ *
664
+ * @hooked
665
+ */
666
+ }
667
+ wp_reset_postdata();
668
+ //ect_portfolio_loop_end();
669
+ ?>
670
+ <?php
671
+
672
+ /**
673
+ * Hook: ect_after_portfolio_loop.
674
+ *
675
+ * @hooked
676
+ */
677
+ do_action( 'ect_after_portfolio_loop' );
678
+
679
+ } else {
680
+ /**
681
+ * Hook: ect_no_portfolio_found.
682
+ *
683
+ * @hooked ect_no_portfolio_found
684
+ */
685
+ do_action( 'ect_no_portfolio_found' );
686
+ }
687
+
688
+ $html = ob_get_clean();
689
+
690
+ // If there is a [portfolio] within a [portfolio], remove the shortcode
691
+ if ( has_shortcode( $html, 'portfolio' ) ){
692
+ remove_shortcode( 'portfolio' );
693
+ }
694
+
695
+ // Return the HTML block
696
+ return $html;
697
+ }
698
+
699
+ /**
700
+ * Displays the project type that a project belongs to.
701
+ *
702
+ * @return html
703
+ */
704
+ static function get_project_type( $post_id ) {
705
+ $project_types = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TYPE );
706
+
707
+ // If no types, return empty string
708
+ if ( empty( $project_types ) || is_wp_error( $project_types ) ) {
709
+ return;
710
+ }
711
+
712
+ $html = '<div class="project-types"><span>' . __( 'Types', 'essential-content-types' ) . ':</span>';
713
+ $types = array();
714
+ // Loop thorugh all the types
715
+ foreach ( $project_types as $project_type ) {
716
+ $project_type_link = get_term_link( $project_type, self::CUSTOM_TAXONOMY_TYPE );
717
+
718
+ if ( is_wp_error( $project_type_link ) ) {
719
+ return $project_type_link;
720
+ }
721
+
722
+ $types[] = '<a href="' . esc_url( $project_type_link ) . '" rel="tag">' . esc_html( $project_type->name ) . '</a>';
723
+ }
724
+ $html .= ' '.implode( ', ', $types );
725
+ $html .= '</div>';
726
+
727
+ return $html;
728
+ }
729
+
730
+ /**
731
+ * Displays the project tags that a project belongs to.
732
+ *
733
+ * @return html
734
+ */
735
+ static function get_project_tags( $post_id ) {
736
+ $project_tags = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TAG );
737
+
738
+ // If no tags, return empty string
739
+ if ( empty( $project_tags ) || is_wp_error( $project_tags ) ) {
740
+ return false;
741
+ }
742
+
743
+ $html = '<div class="project-tags"><span>' . __( 'Tags', 'essential-content-types' ) . ':</span>';
744
+ $tags = array();
745
+ // Loop thorugh all the tags
746
+ foreach ( $project_tags as $project_tag ) {
747
+ $project_tag_link = get_term_link( $project_tag, self::CUSTOM_TAXONOMY_TYPE );
748
+
749
+ if ( is_wp_error( $project_tag_link ) ) {
750
+ return $project_tag_link;
751
+ }
752
+
753
+ $tags[] = '<a href="' . esc_url( $project_tag_link ) . '" rel="tag">' . esc_html( $project_tag->name ) . '</a>';
754
+ }
755
+ $html .= ' '. implode( ', ', $tags );
756
+ $html .= '</div>';
757
+
758
+ return $html;
759
+ }
760
+
761
+ /**
762
+ * Displays the author of the current portfolio project.
763
+ *
764
+ * @return html
765
+ */
766
+ static function get_project_author() {
767
+ $html = '<div class="project-author">';
768
+ /* translators: %1$s is link to author posts, %2$s is author display name */
769
+ $html .= sprintf( __( '<span>Author:</span> <a href="%1$s">%2$s</a>', 'essential-content-types' ),
770
+ esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ),
771
+ esc_html( get_the_author() )
772
+ );
773
+ $html .= '</div>';
774
+
775
+ return $html;
776
+ }
777
+ }
778
+ add_action( 'init', array( 'Essential_Content_Jetpack_Portfolio', 'init' ) );
779
+
780
+ /**
781
+ * Add Portfolio support
782
+ */
783
+ function essential_content_portfolio_support() {
784
+ /*
785
+ * Adding theme support for Jetpack Portfolio CPT.
786
+ */
787
+ add_theme_support( 'jetpack-portfolio', array(
788
+ 'title' => true,
789
+ 'content' => true,
790
+ 'featured-image' => true,
791
+ ) );
792
+ add_image_size( 'ect-jetpack-portfolio-featured', 640, 640, true );
793
+ }
794
+ add_action( 'after_setup_theme', 'essential_content_portfolio_support' );
795
+
796
+
797
+ if( ! function_exists( 'essential_content_get_portfolio_thumbnail_link' ) ):
798
+ /**
799
+ * Display the featured image if it's available
800
+ *
801
+ * @return html
802
+ */
803
+ function essential_content_get_portfolio_thumbnail_link( $post_id ) {
804
+ if ( has_post_thumbnail( $post_id ) ) {
805
+ /**
806
+ * Change the Portfolio thumbnail size.
807
+ *
808
+ * @module custom-content-types
809
+ *
810
+ * @since 3.4.0
811
+ *
812
+ * @param string|array $var Either a registered size keyword or size array.
813
+ */
814
+ return '<a class="portfolio-featured-image" href="' . esc_url( get_permalink( $post_id ) ) . '">' . get_the_post_thumbnail( $post_id, apply_filters( 'jetpack_portfolio_thumbnail_size', 'ect-jetpack-portfolio-featured' ) ) . '</a>';
815
+ }
816
+ }
817
+ endif;
818
+
819
+
820
+ if( ! function_exists( 'essential_content_no_portfolio_found' ) ):
821
+ /**
822
+ * No items found text
823
+ *
824
+ * @return html
825
+ */
826
+ function essential_content_no_portfolio_found() {
827
+ echo "<p><em>" . esc_html__( 'Your Portfolio Archive currently has no entries. You can start creating them on your dashboard.', 'essential-content-types-pro' ) . "</em></p>";
828
+ }
829
+ endif;
830
+ add_action( 'ect_no_portfolio_found', 'essential_content_no_portfolio_found', 10 );
831
+
832
+
833
+ if( ! function_exists( 'essential_content_portfolio_section_open' ) ):
834
+ /**
835
+ * Open section
836
+ *
837
+ * @return html
838
+ */
839
+ function essential_content_portfolio_section_open( $layout ) {
840
+ echo '<div class="ect-portfolio ect-section">';
841
+ echo '<div class="ect-wrapper">';
842
+ }
843
+ endif;
844
+ add_action( 'ect_before_portfolio_loop', 'essential_content_portfolio_section_open', 10, 1 );
845
+
846
+
847
+ if( ! function_exists( 'essential_content_portfolio_loop_start' ) ):
848
+ /**
849
+ * open wrapper before loop
850
+ *
851
+ */
852
+ function essential_content_portfolio_loop_start( $layout = null ){
853
+ echo '<div class="section-content-wrapper portfolio-content-wrapper ' . $layout . '">';
854
+ }
855
+ endif;
856
+ add_action( 'ect_before_portfolio_loop', 'essential_content_portfolio_loop_start', 30 );
857
+
858
+
859
+ if( ! function_exists( 'essential_content_portfolio_loop_end' ) ):
860
+ /**
861
+ * close wrapper after loop
862
+ *
863
+ */
864
+ function essential_content_portfolio_loop_end(){
865
+ echo '</div><!-- .portfolio-content-wrapper -->';
866
+ }
867
+ endif;
868
+ add_action( 'ect_after_portfolio_loop', 'essential_content_portfolio_loop_end', 10 );
869
+
870
+
871
+ if( ! function_exists( 'essential_content_portfolio_section_close' ) ):
872
+ /**
873
+ * Close section
874
+ *
875
+ * @return html
876
+ */
877
+ function essential_content_portfolio_section_close() {
878
+ echo '</div><!-- .ect-wrapper -->';
879
+ echo '</div><!-- .ect-section -->';
880
+ }
881
+ endif;
882
  add_action( 'ect_after_portfolio_loop', 'essential_content_portfolio_section_close', 20 );
admin/class-service.php CHANGED
@@ -1,736 +1,736 @@
1
- <?php
2
-
3
- /**
4
- * Support JetPack Service
5
- */
6
- class Essential_Content_Service {
7
- const CUSTOM_POST_TYPE = 'ect-service';
8
- const CUSTOM_TAXONOMY_TYPE = 'ect-service-type';
9
- const CUSTOM_TAXONOMY_TAG = 'ect-service-tag';
10
- const OPTION_NAME = 'ect-service';
11
- const OPTION_READING_SETTING = 'ect_service_posts_per_page';
12
-
13
- public $version = ESSENTIAL_CONTENT_TYPES_VERSION;
14
-
15
- static function init() {
16
- static $instance = false;
17
-
18
- if ( ! $instance ) {
19
- $instance = new Essential_Content_Service;
20
- }
21
-
22
- return $instance;
23
- }
24
-
25
- /**
26
- * Conditionally hook into WordPress.
27
- *
28
- * Setup user option for enabling CPT
29
- * If user has CPT enabled, show in admin
30
- */
31
- function __construct() {
32
- // Make sure the post types are loaded for imports
33
- add_action( 'import_start', array( $this, 'register_post_types' ) );
34
-
35
- // Add to REST API post type whitelist
36
- add_filter( 'rest_api_allowed_post_types', array( $this, 'allow_service_rest_api_type' ) );
37
-
38
- // CPT magic
39
- $this->register_post_types();
40
- add_action( sprintf( 'add_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 );
41
- add_action( sprintf( 'update_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 );
42
- add_action( sprintf( 'publish_%s', self::CUSTOM_POST_TYPE), array( $this, 'flush_rules_on_first_content' ) );
43
- add_action( 'after_switch_theme', array( $this, 'flush_rules_on_switch' ) );
44
-
45
- // Admin Customization
46
- add_filter( 'post_updated_messages', array( $this, 'updated_messages' ) );
47
- add_filter( sprintf( 'manage_%s_posts_columns', self::CUSTOM_POST_TYPE), array( $this, 'edit_admin_columns' ) );
48
- add_filter( sprintf( 'manage_%s_posts_custom_column', self::CUSTOM_POST_TYPE), array( $this, 'image_column' ), 10, 2 );
49
- add_action( 'customize_register', array( $this, 'customize_register' ) );
50
-
51
- add_image_size( 'service-admin-thumb', 50, 50, true );
52
- add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
53
-
54
- // register service shortcode and services shortcode (legacy)
55
- add_shortcode( 'services', array( $this, 'service_shortcode' ) );
56
- add_shortcode( 'ect_services', array( $this, 'service_shortcode' ) );
57
-
58
- // Adjust CPT archive and custom taxonomies to obey CPT reading setting
59
- add_filter( 'pre_get_posts', array( $this, 'query_reading_setting' ) );
60
- }
61
-
62
- /*
63
- * Flush permalinks when CPT option is turned on/off
64
- */
65
- function flush_rules_on_enable() {
66
- flush_rewrite_rules();
67
- }
68
-
69
- /*
70
- * Count published contents and flush permalinks when first contents is published
71
- */
72
- function flush_rules_on_first_content() {
73
- $contents = get_transient( 'service-count-cache' );
74
-
75
- if ( false === $contents ) {
76
- flush_rewrite_rules();
77
- $contents = (int) wp_count_posts( self::CUSTOM_POST_TYPE )->publish;
78
-
79
- if ( ! empty( $contents ) ) {
80
- set_transient( 'service-count-cache', $contents, HOUR_IN_SECONDS * 12 );
81
- }
82
- }
83
- }
84
-
85
- /*
86
- * Flush permalinks when CPT supported theme is activated
87
- */
88
- function flush_rules_on_switch() {
89
- flush_rewrite_rules();
90
- }
91
-
92
- /**
93
- * Register Post Type
94
- */
95
- function register_post_types() {
96
- if ( post_type_exists( self::CUSTOM_POST_TYPE ) ) {
97
- return;
98
- }
99
-
100
- $args = array(
101
- 'description' => esc_html__( 'Service Items', 'essential-content-types' ),
102
- 'labels' => array(
103
- 'name' => esc_html__( 'Services', 'essential-content-types' ),
104
- 'singular_name' => esc_html__( 'Service', 'essential-content-types' ),
105
- 'menu_name' => esc_html__( 'Service', 'essential-content-types' ),
106
- 'all_items' => esc_html__( 'All Services', 'essential-content-types' ),
107
- 'add_new' => esc_html__( 'Add New', 'essential-content-types' ),
108
- 'add_new_item' => esc_html__( 'Add New Service', 'essential-content-types' ),
109
- 'edit_item' => esc_html__( 'Edit Service', 'essential-content-types' ),
110
- 'new_item' => esc_html__( 'New Service', 'essential-content-types' ),
111
- 'view_item' => esc_html__( 'View Service', 'essential-content-types' ),
112
- 'search_items' => esc_html__( 'Search Services', 'essential-content-types' ),
113
- 'not_found' => esc_html__( 'No Services found', 'essential-content-types' ),
114
- 'not_found_in_trash' => esc_html__( 'No Services found in Trash', 'essential-content-types' ),
115
- 'filter_items_list' => esc_html__( 'Filter contents list', 'essential-content-types' ),
116
- 'items_list_navigation' => esc_html__( 'Service list navigation', 'essential-content-types' ),
117
- 'items_list' => esc_html__( 'Services list', 'essential-content-types' ),
118
- ),
119
- 'supports' => array(
120
- 'title',
121
- 'editor',
122
- 'excerpt',
123
- 'thumbnail',
124
- 'author',
125
- 'comments',
126
- ),
127
- 'rewrite' => array(
128
- 'slug' => 'service',
129
- 'with_front' => false,
130
- 'feeds' => true,
131
- 'pages' => true,
132
- ),
133
- 'public' => true,
134
- 'show_ui' => true,
135
- 'menu_position' => 20, // below Pages
136
- 'menu_icon' => 'dashicons-exerpt-view', // 3.8+ dashicon option
137
- 'capability_type' => 'page',
138
- 'map_meta_cap' => true,
139
- 'taxonomies' => array( self::CUSTOM_TAXONOMY_TYPE, self::CUSTOM_TAXONOMY_TAG ),
140
- 'has_archive' => true,
141
- 'query_var' => 'service',
142
- 'show_in_rest' => true,
143
- );
144
-
145
- $description = get_option( 'ect_service_content' );
146
- if ( '' !== $description ) {
147
- $args['description'] = $description;
148
- }
149
-
150
- register_post_type( self::CUSTOM_POST_TYPE, $args );
151
-
152
- register_taxonomy( self::CUSTOM_TAXONOMY_TYPE, self::CUSTOM_POST_TYPE, array(
153
- 'hierarchical' => true,
154
- 'labels' => array(
155
- 'name' => esc_html__( 'Service Types', 'essential-content-types' ),
156
- 'singular_name' => esc_html__( 'Service Type', 'essential-content-types' ),
157
- 'menu_name' => esc_html__( 'Service Types', 'essential-content-types' ),
158
- 'all_items' => esc_html__( 'All Service Types', 'essential-content-types' ),
159
- 'edit_item' => esc_html__( 'Edit Service Type', 'essential-content-types' ),
160
- 'view_item' => esc_html__( 'View Service Type', 'essential-content-types' ),
161
- 'update_item' => esc_html__( 'Update Service Type', 'essential-content-types' ),
162
- 'add_new_item' => esc_html__( 'Add New Service Type', 'essential-content-types' ),
163
- 'new_item_name' => esc_html__( 'New Service Type Name', 'essential-content-types' ),
164
- 'parent_item' => esc_html__( 'Parent Service Type', 'essential-content-types' ),
165
- 'parent_item_colon' => esc_html__( 'Parent Service Type:', 'essential-content-types' ),
166
- 'search_items' => esc_html__( 'Search Service Types', 'essential-content-types' ),
167
- 'items_list_navigation' => esc_html__( 'Service type list navigation', 'essential-content-types' ),
168
- 'items_list' => esc_html__( 'Service type list', 'essential-content-types' ),
169
- ),
170
- 'public' => true,
171
- 'show_ui' => true,
172
- 'show_in_nav_menus' => true,
173
- 'show_in_rest' => true,
174
- 'show_admin_column' => true,
175
- 'query_var' => true,
176
- 'rewrite' => array( 'slug' => 'service-type' ),
177
- ) );
178
-
179
- register_taxonomy( self::CUSTOM_TAXONOMY_TAG, self::CUSTOM_POST_TYPE, array(
180
- 'hierarchical' => false,
181
- 'labels' => array(
182
- 'name' => esc_html__( 'Service Tags', 'essential-content-types' ),
183
- 'singular_name' => esc_html__( 'Service Tag', 'essential-content-types' ),
184
- 'menu_name' => esc_html__( 'Service Tags', 'essential-content-types' ),
185
- 'all_items' => esc_html__( 'All Service Tags', 'essential-content-types' ),
186
- 'edit_item' => esc_html__( 'Edit Service Tag', 'essential-content-types' ),
187
- 'view_item' => esc_html__( 'View Service Tag', 'essential-content-types' ),
188
- 'update_item' => esc_html__( 'Update Service Tag', 'essential-content-types' ),
189
- 'add_new_item' => esc_html__( 'Add New Service Tag', 'essential-content-types' ),
190
- 'new_item_name' => esc_html__( 'New Service Tag Name', 'essential-content-types' ),
191
- 'search_items' => esc_html__( 'Search Service Tags', 'essential-content-types' ),
192
- 'popular_items' => esc_html__( 'Popular Service Tags', 'essential-content-types' ),
193
- 'separate_items_with_commas' => esc_html__( 'Separate tags with commas', 'essential-content-types' ),
194
- 'add_or_remove_items' => esc_html__( 'Add or remove tags', 'essential-content-types' ),
195
- 'choose_from_most_used' => esc_html__( 'Choose from the most used tags', 'essential-content-types' ),
196
- 'not_found' => esc_html__( 'No tags found.', 'essential-content-types' ),
197
- 'items_list_navigation' => esc_html__( 'Service tag list navigation', 'essential-content-types' ),
198
- 'items_list' => esc_html__( 'Service tag list', 'essential-content-types' ),
199
- ),
200
- 'public' => true,
201
- 'show_ui' => true,
202
- 'show_in_nav_menus' => true,
203
- 'show_in_rest' => true,
204
- 'show_admin_column' => true,
205
- 'query_var' => true,
206
- 'rewrite' => array( 'slug' => 'service-tag' ),
207
- ) );
208
- }
209
-
210
- /**
211
- * Update messages for the Services admin.
212
- */
213
- function updated_messages( $messages ) {
214
- global $post;
215
-
216
- $messages[self::CUSTOM_POST_TYPE] = array(
217
- 0 => '', // Unused. Messages start at index 1.
218
- 1 => sprintf( __( 'Service updated. <a href="%s">View item</a>', 'essential-content-types'), esc_url( get_permalink( $post->ID ) ) ),
219
- 2 => esc_html__( 'Custom field updated.', 'essential-content-types' ),
220
- 3 => esc_html__( 'Custom field deleted.', 'essential-content-types' ),
221
- 4 => esc_html__( 'Service updated.', 'essential-content-types' ),
222
- /* translators: %s: date and time of the revision */
223
- 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Service restored to revision from %s', 'essential-content-types'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
224
- 6 => sprintf( __( 'Service published. <a href="%s">View content</a>', 'essential-content-types' ), esc_url( get_permalink( $post->ID ) ) ),
225
- 7 => esc_html__( 'Service saved.', 'essential-content-types' ),
226
- 8 => sprintf( __( 'Service submitted. <a target="_blank" href="%s">Preview content</a>', 'essential-content-types'), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
227
- 9 => sprintf( __( 'Service scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview service</a>', 'essential-content-types' ),
228
- // translators: Publish box date format, see http://php.net/date
229
- date_i18n( __( 'M j, Y @ G:i', 'essential-content-types' ), strtotime( $post->post_date ) ), esc_url( get_permalink( $post->ID ) ) ),
230
- 10 => sprintf( __( 'Service item draft updated. <a target="_blank" href="%s">Preview content</a>', 'essential-content-types' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
231
- );
232
-
233
- return $messages;
234
- }
235
-
236
- /**
237
- * Change ‘Title’ column label
238
- * Add Featured Image column
239
- */
240
- function edit_admin_columns( $columns ) {
241
- // change 'Title' to 'Content'
242
- $columns['title'] = __( 'Service', 'essential-content-types' );
243
- if ( current_theme_supports( 'post-thumbnails' ) ) {
244
- // add featured image before 'Content'
245
- $columns = array_slice( $columns, 0, 1, true ) + array( 'thumbnail' => '' ) + array_slice( $columns, 1, NULL, true );
246
- }
247
-
248
- return $columns;
249
- }
250
-
251
- /**
252
- * Add featured image to column
253
- */
254
- function image_column( $column, $post_id ) {
255
- global $post;
256
- switch ( $column ) {
257
- case 'thumbnail':
258
- echo get_the_post_thumbnail( $post_id, 'service-admin-thumb' );
259
- break;
260
- }
261
- }
262
-
263
- /**
264
- * Adjust image column width
265
- */
266
- function enqueue_admin_styles( $hook ) {
267
- $screen = get_current_screen();
268
-
269
- if ( 'edit.php' == $hook && self::CUSTOM_POST_TYPE == $screen->post_type && current_theme_supports( 'post-thumbnails' ) ) {
270
- wp_add_inline_style( 'wp-admin', '.column-thumbnail img:nth-of-type(2) { display: none; } .manage-column.column-thumbnail { width: 50px; } @media screen and (max-width: 360px) { .column-thumbnail{ display:none; } }' );
271
- }
272
- }
273
-
274
- /**
275
- * Adds service section to the Customizer.
276
- */
277
- function customize_register( $wp_customize ) {
278
- $wp_customize->add_panel( 'ect_plugin_options', array(
279
- 'title' => esc_html__( 'Essential Content Types Plugin Options', 'essential-content-types' ),
280
- 'priority' => 1,
281
- ) );
282
-
283
- $wp_customize->add_section( 'ect_service', array(
284
- 'title' => esc_html__( 'Services', 'essential-content-types' ),
285
- 'priority' => 130,
286
- 'panel' => 'ect_plugin_options',
287
- ) );
288
-
289
- $wp_customize->add_setting( 'ect_service_title', array(
290
- 'default' => esc_html__( 'Services', 'essential-content-types' ),
291
- 'type' => 'option',
292
- 'sanitize_callback' => 'sanitize_text_field',
293
- 'sanitize_js_callback' => 'sanitize_text_field',
294
- ) );
295
-
296
- $wp_customize->add_control( 'ect_service_title', array(
297
- 'section' => 'ect_service',
298
- 'label' => esc_html__( 'Service Archive Title', 'essential-content-types' ),
299
- 'type' => 'text',
300
- ) );
301
-
302
- $wp_customize->add_setting( 'ect_service_content', array(
303
- 'default' => '',
304
- 'type' => 'option',
305
- 'sanitize_callback' => 'wp_kses_post',
306
- 'sanitize_js_callback' => 'wp_kses_post',
307
- ) );
308
-
309
- $wp_customize->add_control( 'ect_service_content', array(
310
- 'section' => 'ect_service',
311
- 'label' => esc_html__( 'Service Archive Content', 'essential-content-types' ),
312
- 'type' => 'textarea',
313
- ) );
314
-
315
- $wp_customize->add_setting( 'ect_service_featured_image', array(
316
- 'default' => '',
317
- 'type' => 'option',
318
- 'sanitize_callback' => 'attachment_url_to_postid',
319
- 'sanitize_js_callback' => 'attachment_url_to_postid',
320
- 'theme_supports' => 'post-thumbnails',
321
- ) );
322
-
323
- $wp_customize->add_control( new WP_Customize_Image_Control( $wp_customize, 'ect_service_featured_image', array(
324
- 'section' => 'ect_service',
325
- 'label' => esc_html__( 'Service Archive Featured Image', 'essential-content-types' ),
326
- ) ) );
327
- }
328
-
329
- /**
330
- * Follow CPT reading setting on CPT archive and taxonomy pages
331
- */
332
- function query_reading_setting( $query ) {
333
- if ( ! is_admin() &&
334
- $query->is_main_query() &&
335
- ( $query->is_post_type_archive( self::CUSTOM_POST_TYPE ) || $query->is_tax( self::CUSTOM_TAXONOMY_TYPE ) || $query->is_tax( self::CUSTOM_TAXONOMY_TAG ) )
336
- ) {
337
- $post_per_page = ( null != get_option('posts_per_page') ) ? get_option('posts_per_page') : '10';
338
- $query->set( 'posts_per_page', get_option( self::OPTION_READING_SETTING, $post_per_page ) );
339
- }
340
- }
341
-
342
- /**
343
- * Add to REST API post type whitelist
344
- */
345
- function allow_service_rest_api_type( $post_types ) {
346
- $post_types[] = self::CUSTOM_POST_TYPE;
347
-
348
- return $post_types;
349
- }
350
-
351
- /**
352
- * Our [services] shortcode.
353
- * Prints Service data styled to look good on *any* theme.
354
- *
355
- * @return service_shortcode_html
356
- */
357
- static function service_shortcode( $atts ) {
358
- // Default attributes
359
- $atts = shortcode_atts( array(
360
- 'image' => true,
361
- 'display_types' => true,
362
- 'display_tags' => true,
363
- 'display_content' => true,
364
- 'display_author' => false,
365
- 'show_filter' => false,
366
- 'include_type' => false,
367
- 'include_tag' => false,
368
- 'columns' => 2,
369
- 'showposts' => -1,
370
- 'order' => 'asc',
371
- 'orderby' => 'date',
372
- ), $atts, 'services' );
373
-
374
- // A little sanitization
375
- if ( $atts['image'] && 'true' != $atts['image'] ) {
376
- $atts['image'] = false;
377
- }
378
-
379
- if ( $atts['display_types'] && 'true' != $atts['display_types'] ) {
380
- $atts['display_types'] = false;
381
- }
382
-
383
- if ( $atts['display_tags'] && 'true' != $atts['display_tags'] ) {
384
- $atts['display_tags'] = false;
385
- }
386
-
387
- if ( $atts['display_author'] && 'true' != $atts['display_author'] ) {
388
- $atts['display_author'] = false;
389
- }
390
-
391
- if ( $atts['display_content'] && 'true' != $atts['display_content'] && 'full' != $atts['display_content'] ) {
392
- $atts['display_content'] = false;
393
- }
394
-
395
- if ( $atts['include_type'] ) {
396
- $atts['include_type'] = explode( ',', str_replace( ' ', '', $atts['include_type'] ) );
397
- }
398
-
399
- if ( $atts['include_tag'] ) {
400
- $atts['include_tag'] = explode( ',', str_replace( ' ', '', $atts['include_tag'] ) );
401
- }
402
-
403
- // Check if column value is set to valid numbers or else set default value as 2
404
- if( 1 <= $atts['columns'] && 6 >= $atts['columns'] ) {
405
- $atts['columns'] = absint( $atts['columns'] );
406
- } else {
407
- $atts['columns'] = 2;
408
- }
409
-
410
- $atts['showposts'] = intval( $atts['showposts'] );
411
-
412
-
413
- if ( $atts['order'] ) {
414
- $atts['order'] = urldecode( $atts['order'] );
415
- $atts['order'] = strtoupper( $atts['order'] );
416
- if ( 'DESC' != $atts['order'] ) {
417
- $atts['order'] = 'ASC';
418
- }
419
- }
420
-
421
- if ( $atts['orderby'] ) {
422
- $atts['orderby'] = urldecode( $atts['orderby'] );
423
- $atts['orderby'] = strtolower( $atts['orderby'] );
424
- $allowed_keys = array( 'author', 'date', 'title', 'rand' );
425
-
426
- $parsed = array();
427
- foreach ( explode( ',', $atts['orderby'] ) as $service_index_number => $orderby ) {
428
- if ( ! in_array( $orderby, $allowed_keys ) ) {
429
- continue;
430
- }
431
- $parsed[] = $orderby;
432
- }
433
-
434
- if ( empty( $parsed ) ) {
435
- unset( $atts['orderby'] );
436
- } else {
437
- $atts['orderby'] = implode( ' ', $parsed );
438
- }
439
- }
440
-
441
- // enqueue shortcode styles when shortcode is used
442
- wp_enqueue_style( 'service-style', plugins_url( 'css/service-shortcode.css', __FILE__ ), array(), '20140326' );
443
-
444
- return self::service_shortcode_html( $atts );
445
- }
446
-
447
- /**
448
- * Query to retrieve entries from the Service post_type.
449
- *
450
- * @return object
451
- */
452
- static function service_query( $atts ) {
453
- // Default query arguments
454
- $default = array(
455
- 'order' => $atts['order'],
456
- 'orderby' => $atts['orderby'],
457
- 'posts_per_page' => $atts['showposts'],
458
- );
459
-
460
- $args = wp_parse_args( $atts, $default );
461
- $args['post_type'] = self::CUSTOM_POST_TYPE; // Force this post type
462
-
463
- if ( false != $atts['include_type'] || false != $atts['include_tag'] ) {
464
- $args['tax_query'] = array();
465
- }
466
-
467
- // If 'include_type' has been set use it on the main query
468
- if ( false != $atts['include_type'] ) {
469
- array_push( $args['tax_query'], array(
470
- 'taxonomy' => self::CUSTOM_TAXONOMY_TYPE,
471
- 'field' => 'slug',
472
- 'terms' => $atts['include_type'],
473
- ) );
474
- }
475
-
476
- // If 'include_tag' has been set use it on the main query
477
- if ( false != $atts['include_tag'] ) {
478
- array_push( $args['tax_query'], array(
479
- 'taxonomy' => self::CUSTOM_TAXONOMY_TAG,
480
- 'field' => 'slug',
481
- 'terms' => $atts['include_tag'],
482
- ) );
483
- }
484
-
485
- if ( false != $atts['include_type'] && false != $atts['include_tag'] ) {
486
- $args['tax_query']['relation'] = 'AND';
487
- }
488
-
489
- // Run the query and return
490
- $query = new WP_Query( $args );
491
- return $query;
492
- }
493
-
494
- /**
495
- * The Service shortcode loop.
496
- *
497
- * @todo add theme color styles
498
- * @return html
499
- */
500
-
501
- static function service_shortcode_html( $atts ) {
502
-
503
- $query = self::service_query( $atts );
504
- $atts['service_index_number'] = 0;
505
-
506
- ob_start();
507
-
508
- // If we have posts, create the html
509
- // with service markup
510
- if ( $query->have_posts() ) {
511
-
512
- /**
513
- * Hook: ect_before_service_loop.
514
- *
515
- * @hooked
516
- */
517
- $layout = ect_get_layout();
518
- do_action( 'ect_before_service_loop', $layout[$atts['columns']] );
519
-
520
- ?>
521
- <?php
522
- while ( $query->have_posts() ) {
523
- $query->the_post();
524
- ect_get_template_part( 'content', 'service', $atts );
525
- /**
526
- * Hook: ect_service_loop.
527
- *
528
- * @hooked
529
- */
530
- }
531
- wp_reset_postdata();
532
- ?>
533
- <?php
534
-
535
- /**
536
- * Hook: ect_after_service_loop.
537
- *
538
- * @hooked
539
- */
540
- do_action( 'ect_after_service_loop' );
541
-
542
- } else {
543
- /**
544
- * Hook: ect_no_service_found.
545
- *
546
- * @hooked ect_no_service_found
547
- */
548
- do_action( 'ect_no_service_found' );
549
- }
550
-
551
- $html = ob_get_clean();
552
-
553
- // If there is a [services] within a [services], remove the shortcode
554
- if ( has_shortcode( $html, 'services' ) ){
555
- remove_shortcode( 'services' );
556
- }
557
-
558
- // Return the HTML block
559
- return $html;
560
- }
561
-
562
- /**
563
- * Displays the content type that a content belongs to.
564
- *
565
- * @return html
566
- */
567
- static function get_content_type( $post_id ) {
568
- $content_types = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TYPE );
569
-
570
- // If no types, return empty string
571
- if ( empty( $content_types ) || is_wp_error( $content_types ) ) {
572
- return;
573
- }
574
-
575
- $html = '<div class="content-types"><span>' . __( 'Types', 'essential-content-types' ) . ':</span>';
576
- $types = array();
577
- // Loop thorugh all the types
578
- foreach ( $content_types as $content_type ) {
579
- $content_type_link = get_term_link( $content_type, self::CUSTOM_TAXONOMY_TYPE );
580
-
581
- if ( is_wp_error( $content_type_link ) ) {
582
- return $content_type_link;
583
- }
584
-
585
- $types[] = '<a href="' . esc_url( $content_type_link ) . '" rel="tag">' . esc_html( $content_type->name ) . '</a>';
586
- }
587
- $html .= ' '.implode( ', ', $types );
588
- $html .= '</div>';
589
-
590
- return $html;
591
- }
592
-
593
- /**
594
- * Displays the content tags that a content belongs to.
595
- *
596
- * @return html
597
- */
598
- static function get_content_tags( $post_id ) {
599
- $content_tags = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TAG );
600
-
601
- // If no tags, return empty string
602
- if ( empty( $content_tags ) || is_wp_error( $content_tags ) ) {
603
- return false;
604
- }
605
-
606
- $html = '<div class="content-tags"><span>' . __( 'Tags', 'essential-content-types' ) . ':</span>';
607
- $tags = array();
608
- // Loop thorugh all the tags
609
- foreach ( $content_tags as $content_tag ) {
610
- $content_tag_link = get_term_link( $content_tag, self::CUSTOM_TAXONOMY_TYPE );
611
-
612
- if ( is_wp_error( $content_tag_link ) ) {
613
- return $content_tag_link;
614
- }
615
-
616
- $tags[] = '<a href="' . esc_url( $content_tag_link ) . '" rel="tag">' . esc_html( $content_tag->name ) . '</a>';
617
- }
618
- $html .= ' '. implode( ', ', $tags );
619
- $html .= '</div>';
620
-
621
- return $html;
622
- }
623
-
624
- /**
625
- * Displays the author of the current service content.
626
- *
627
- * @return html
628
- */
629
- static function get_content_author() {
630
- $html = '<div class="content-author">';
631
- /* translators: %1$s is link to author posts, %2$s is author display name */
632
- $html .= sprintf( __( '<span>Author:</span> <a href="%1$s">%2$s</a>', 'essential-content-types' ),
633
- esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ),
634
- esc_html( get_the_author() )
635
- );
636
- $html .= '</div>';
637
-
638
- return $html;
639
- }
640
- }
641
- add_action( 'init', array( 'Essential_Content_Service', 'init' ) );
642
-
643
-
644
- /**
645
- * Add Service support
646
- */
647
- function essential_content_service_support() {
648
- /*
649
- * Adding theme support for Jetpack Service CPT.
650
- */
651
- add_image_size( 'ect-service', 640, 640, true );
652
- }
653
- add_action( 'after_setup_theme', 'essential_content_service_support' );
654
-
655
-
656
- if( ! function_exists( 'essential_content_get_service_thumbnail_link' ) ):
657
- function essential_content_get_service_thumbnail_link( $post_id, $size ) {
658
- if ( has_post_thumbnail( $post_id ) ) {
659
- /**
660
- * Change the Service thumbnail size.
661
- *
662
- * @module custom-content-types
663
- *
664
- * @since 3.4.0
665
- *
666
- * @param string|array $var Either a registered size keyword or size array.
667
- */
668
- return '<a class="service-featured-image" href="' . esc_url( get_permalink( $post_id ) ) . '">' . get_the_post_thumbnail( $post_id, apply_filters( 'service_thumbnail_size', $size ) ) . '</a>';
669
- }
670
- }
671
- endif;
672
-
673
-
674
- if( ! function_exists( 'essential_content_no_service_found' ) ):
675
- /**
676
- * No items found text
677
- *
678
- * @return html
679
- */
680
- function essential_content_no_service_found() {
681
- echo "<p><em>" . esc_html__( 'Your Service Archive currently has no entries. You can start creating them on your dashboard.', 'essential-content-types-pro' ) . "</em></p>";
682
- }
683
- add_action( 'ect_no_service_found', 'essential_content_no_service_found', 10 );
684
- endif;
685
-
686
-
687
- if( ! function_exists( 'essential_content_service_section_open' ) ):
688
- /**
689
- * Open section
690
- *
691
- * @return html
692
- */
693
- function essential_content_service_section_open( $layout ) {
694
- echo '<div class="ect-service ect-section">';
695
- echo '<div class="ect-wrapper">';
696
- }
697
- endif;
698
- add_action( 'ect_before_service_loop', 'essential_content_service_section_open', 10, 1 );
699
-
700
-
701
- if( ! function_exists( 'essential_content_service_loop_start' ) ):
702
- /**
703
- * open wrapper before loop
704
- *
705
- */
706
- function essential_content_service_loop_start( $layout = null ){
707
- echo '<div class="section-content-wrapper service-content-wrapper ' . $layout . '">';
708
- }
709
- endif;
710
- add_action( 'ect_before_service_loop', 'essential_content_service_loop_start', 30 );
711
-
712
-
713
- if( ! function_exists( 'essential_content_service_loop_end' ) ):
714
- /**
715
- * close wrapper after loop
716
- *
717
- */
718
- function essential_content_service_loop_end(){
719
- echo '</div><!-- .service-content-wrapper -->';
720
- }
721
- endif;
722
- add_action( 'ect_after_service_loop', 'essential_content_service_loop_end', 10 );
723
-
724
-
725
- if( ! function_exists( 'essential_content_service_section_close' ) ):
726
- /**
727
- * Close section
728
- *
729
- * @return html
730
- */
731
- function essential_content_service_section_close() {
732
- echo '</div><!-- .ect-wrapper -->';
733
- echo '</div><!-- .ect-section -->';
734
- }
735
- endif;
736
  add_action( 'ect_after_service_loop', 'essential_content_service_section_close', 20 );
1
+ <?php
2
+
3
+ /**
4
+ * Support JetPack Service
5
+ */
6
+ class Essential_Content_Service {
7
+ const CUSTOM_POST_TYPE = 'ect-service';
8
+ const CUSTOM_TAXONOMY_TYPE = 'ect-service-type';
9
+ const CUSTOM_TAXONOMY_TAG = 'ect-service-tag';
10
+ const OPTION_NAME = 'ect-service';
11
+ const OPTION_READING_SETTING = 'ect_service_posts_per_page';
12
+
13
+ public $version = ESSENTIAL_CONTENT_TYPES_VERSION;
14
+
15
+ static function init() {
16
+ static $instance = false;
17
+
18
+ if ( ! $instance ) {
19
+ $instance = new Essential_Content_Service;
20
+ }
21
+
22
+ return $instance;
23
+ }
24
+
25
+ /**
26
+ * Conditionally hook into WordPress.
27
+ *
28
+ * Setup user option for enabling CPT
29
+ * If user has CPT enabled, show in admin
30
+ */
31
+ function __construct() {
32
+ // Make sure the post types are loaded for imports
33
+ add_action( 'import_start', array( $this, 'register_post_types' ) );
34
+
35
+ // Add to REST API post type whitelist
36
+ add_filter( 'rest_api_allowed_post_types', array( $this, 'allow_service_rest_api_type' ) );
37
+
38
+ // CPT magic
39
+ $this->register_post_types();
40
+ add_action( sprintf( 'add_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 );
41
+ add_action( sprintf( 'update_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 );
42
+ add_action( sprintf( 'publish_%s', self::CUSTOM_POST_TYPE), array( $this, 'flush_rules_on_first_content' ) );
43
+ add_action( 'after_switch_theme', array( $this, 'flush_rules_on_switch' ) );
44
+
45
+ // Admin Customization
46
+ add_filter( 'post_updated_messages', array( $this, 'updated_messages' ) );
47
+ add_filter( sprintf( 'manage_%s_posts_columns', self::CUSTOM_POST_TYPE), array( $this, 'edit_admin_columns' ) );
48
+ add_filter( sprintf( 'manage_%s_posts_custom_column', self::CUSTOM_POST_TYPE), array( $this, 'image_column' ), 10, 2 );
49
+ add_action( 'customize_register', array( $this, 'customize_register' ) );
50
+
51
+ add_image_size( 'service-admin-thumb', 50, 50, true );
52
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
53
+
54
+ // register service shortcode and services shortcode (legacy)
55
+ add_shortcode( 'services', array( $this, 'service_shortcode' ) );
56
+ add_shortcode( 'ect_services', array( $this, 'service_shortcode' ) );
57
+
58
+ // Adjust CPT archive and custom taxonomies to obey CPT reading setting
59
+ add_filter( 'pre_get_posts', array( $this, 'query_reading_setting' ) );
60
+ }
61
+
62
+ /*
63
+ * Flush permalinks when CPT option is turned on/off
64
+ */
65
+ function flush_rules_on_enable() {
66
+ flush_rewrite_rules();
67
+ }
68
+
69
+ /*
70
+ * Count published contents and flush permalinks when first contents is published
71
+ */
72
+ function flush_rules_on_first_content() {
73
+ $contents = get_transient( 'service-count-cache' );
74
+
75
+ if ( false === $contents ) {
76
+ flush_rewrite_rules();
77
+ $contents = (int) wp_count_posts( self::CUSTOM_POST_TYPE )->publish;
78
+
79
+ if ( ! empty( $contents ) ) {
80
+ set_transient( 'service-count-cache', $contents, HOUR_IN_SECONDS * 12 );
81
+ }
82
+ }
83
+ }
84
+
85
+ /*
86
+ * Flush permalinks when CPT supported theme is activated
87
+ */
88
+ function flush_rules_on_switch() {
89
+ flush_rewrite_rules();
90
+ }
91
+
92
+ /**
93
+ * Register Post Type
94
+ */
95
+ function register_post_types() {
96
+ if ( post_type_exists( self::CUSTOM_POST_TYPE ) ) {
97
+ return;
98
+ }
99
+
100
+ $args = array(
101
+ 'description' => esc_html__( 'Service Items', 'essential-content-types' ),
102
+ 'labels' => array(
103
+ 'name' => esc_html__( 'Services', 'essential-content-types' ),
104
+ 'singular_name' => esc_html__( 'Service', 'essential-content-types' ),
105
+ 'menu_name' => esc_html__( 'Service', 'essential-content-types' ),
106
+ 'all_items' => esc_html__( 'All Services', 'essential-content-types' ),
107
+ 'add_new' => esc_html__( 'Add New', 'essential-content-types' ),
108
+ 'add_new_item' => esc_html__( 'Add New Service', 'essential-content-types' ),
109
+ 'edit_item' => esc_html__( 'Edit Service', 'essential-content-types' ),
110
+ 'new_item' => esc_html__( 'New Service', 'essential-content-types' ),
111
+ 'view_item' => esc_html__( 'View Service', 'essential-content-types' ),
112
+ 'search_items' => esc_html__( 'Search Services', 'essential-content-types' ),
113
+ 'not_found' => esc_html__( 'No Services found', 'essential-content-types' ),
114
+ 'not_found_in_trash' => esc_html__( 'No Services found in Trash', 'essential-content-types' ),
115
+ 'filter_items_list' => esc_html__( 'Filter contents list', 'essential-content-types' ),
116
+ 'items_list_navigation' => esc_html__( 'Service list navigation', 'essential-content-types' ),
117
+ 'items_list' => esc_html__( 'Services list', 'essential-content-types' ),
118
+ ),
119
+ 'supports' => array(
120
+ 'title',
121
+ 'editor',
122
+ 'excerpt',
123
+ 'thumbnail',
124
+ 'author',
125
+ 'comments',
126
+ ),
127
+ 'rewrite' => array(
128
+ 'slug' => 'service',
129
+ 'with_front' => false,
130
+ 'feeds' => true,
131
+ 'pages' => true,
132
+ ),
133
+ 'public' => true,
134
+ 'show_ui' => true,
135
+ 'menu_position' => 20, // below Pages
136
+ 'menu_icon' => 'dashicons-exerpt-view', // 3.8+ dashicon option
137
+ 'capability_type' => 'page',
138
+ 'map_meta_cap' => true,
139
+ 'taxonomies' => array( self::CUSTOM_TAXONOMY_TYPE, self::CUSTOM_TAXONOMY_TAG ),
140
+ 'has_archive' => true,
141
+ 'query_var' => 'service',
142
+ 'show_in_rest' => true,
143
+ );
144
+
145
+ $description = get_option( 'ect_service_content' );
146
+ if ( '' !== $description ) {
147
+ $args['description'] = $description;
148
+ }
149
+
150
+ register_post_type( self::CUSTOM_POST_TYPE, $args );
151
+
152
+ register_taxonomy( self::CUSTOM_TAXONOMY_TYPE, self::CUSTOM_POST_TYPE, array(
153
+ 'hierarchical' => true,
154
+ 'labels' => array(
155
+ 'name' => esc_html__( 'Service Types', 'essential-content-types' ),
156
+ 'singular_name' => esc_html__( 'Service Type', 'essential-content-types' ),
157
+ 'menu_name' => esc_html__( 'Service Types', 'essential-content-types' ),
158
+ 'all_items' => esc_html__( 'All Service Types', 'essential-content-types' ),
159
+ 'edit_item' => esc_html__( 'Edit Service Type', 'essential-content-types' ),
160
+ 'view_item' => esc_html__( 'View Service Type', 'essential-content-types' ),
161
+ 'update_item' => esc_html__( 'Update Service Type', 'essential-content-types' ),
162
+ 'add_new_item' => esc_html__( 'Add New Service Type', 'essential-content-types' ),
163
+ 'new_item_name' => esc_html__( 'New Service Type Name', 'essential-content-types' ),
164
+ 'parent_item' => esc_html__( 'Parent Service Type', 'essential-content-types' ),
165
+ 'parent_item_colon' => esc_html__( 'Parent Service Type:', 'essential-content-types' ),
166
+ 'search_items' => esc_html__( 'Search Service Types', 'essential-content-types' ),
167
+ 'items_list_navigation' => esc_html__( 'Service type list navigation', 'essential-content-types' ),
168
+ 'items_list' => esc_html__( 'Service type list', 'essential-content-types' ),
169
+ ),
170
+ 'public' => true,
171
+ 'show_ui' => true,
172
+ 'show_in_nav_menus' => true,
173
+ 'show_in_rest' => true,
174
+ 'show_admin_column' => true,
175
+ 'query_var' => true,
176
+ 'rewrite' => array( 'slug' => 'service-type' ),
177
+ ) );
178
+
179
+ register_taxonomy( self::CUSTOM_TAXONOMY_TAG, self::CUSTOM_POST_TYPE, array(
180
+ 'hierarchical' => false,
181
+ 'labels' => array(
182
+ 'name' => esc_html__( 'Service Tags', 'essential-content-types' ),
183
+ 'singular_name' => esc_html__( 'Service Tag', 'essential-content-types' ),
184
+ 'menu_name' => esc_html__( 'Service Tags', 'essential-content-types' ),
185
+ 'all_items' => esc_html__( 'All Service Tags', 'essential-content-types' ),
186
+ 'edit_item' => esc_html__( 'Edit Service Tag', 'essential-content-types' ),
187
+ 'view_item' => esc_html__( 'View Service Tag', 'essential-content-types' ),
188
+ 'update_item' => esc_html__( 'Update Service Tag', 'essential-content-types' ),
189
+ 'add_new_item' => esc_html__( 'Add New Service Tag', 'essential-content-types' ),
190
+ 'new_item_name' => esc_html__( 'New Service Tag Name', 'essential-content-types' ),
191
+ 'search_items' => esc_html__( 'Search Service Tags', 'essential-content-types' ),
192
+ 'popular_items' => esc_html__( 'Popular Service Tags', 'essential-content-types' ),
193
+ 'separate_items_with_commas' => esc_html__( 'Separate tags with commas', 'essential-content-types' ),
194
+ 'add_or_remove_items' => esc_html__( 'Add or remove tags', 'essential-content-types' ),
195
+ 'choose_from_most_used' => esc_html__( 'Choose from the most used tags', 'essential-content-types' ),
196
+ 'not_found' => esc_html__( 'No tags found.', 'essential-content-types' ),
197
+ 'items_list_navigation' => esc_html__( 'Service tag list navigation', 'essential-content-types' ),
198
+ 'items_list' => esc_html__( 'Service tag list', 'essential-content-types' ),
199
+ ),
200
+ 'public' => true,
201
+ 'show_ui' => true,
202
+ 'show_in_nav_menus' => true,
203
+ 'show_in_rest' => true,
204
+ 'show_admin_column' => true,
205
+ 'query_var' => true,
206
+ 'rewrite' => array( 'slug' => 'service-tag' ),
207
+ ) );
208
+ }
209
+
210
+ /**
211
+ * Update messages for the Services admin.
212
+ */
213
+ function updated_messages( $messages ) {
214
+ global $post;
215
+
216
+ $messages[self::CUSTOM_POST_TYPE] = array(
217
+ 0 => '', // Unused. Messages start at index 1.
218
+ 1 => sprintf( __( 'Service updated. <a href="%s">View item</a>', 'essential-content-types'), esc_url( get_permalink( $post->ID ) ) ),
219
+ 2 => esc_html__( 'Custom field updated.', 'essential-content-types' ),
220
+ 3 => esc_html__( 'Custom field deleted.', 'essential-content-types' ),
221
+ 4 => esc_html__( 'Service updated.', 'essential-content-types' ),
222
+ /* translators: %s: date and time of the revision */
223
+ 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Service restored to revision from %s', 'essential-content-types'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
224
+ 6 => sprintf( __( 'Service published. <a href="%s">View content</a>', 'essential-content-types' ), esc_url( get_permalink( $post->ID ) ) ),
225
+ 7 => esc_html__( 'Service saved.', 'essential-content-types' ),
226
+ 8 => sprintf( __( 'Service submitted. <a target="_blank" href="%s">Preview content</a>', 'essential-content-types'), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
227
+ 9 => sprintf( __( 'Service scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview service</a>', 'essential-content-types' ),
228
+ // translators: Publish box date format, see http://php.net/date
229
+ date_i18n( __( 'M j, Y @ G:i', 'essential-content-types' ), strtotime( $post->post_date ) ), esc_url( get_permalink( $post->ID ) ) ),
230
+ 10 => sprintf( __( 'Service item draft updated. <a target="_blank" href="%s">Preview content</a>', 'essential-content-types' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
231
+ );
232
+
233
+ return $messages;
234
+ }
235
+
236
+ /**
237
+ * Change ‘Title’ column label
238
+ * Add Featured Image column
239
+ */
240
+ function edit_admin_columns( $columns ) {
241
+ // change 'Title' to 'Content'
242
+ $columns['title'] = __( 'Service', 'essential-content-types' );
243
+ if ( current_theme_supports( 'post-thumbnails' ) ) {
244
+ // add featured image before 'Content'
245
+ $columns = array_slice( $columns, 0, 1, true ) + array( 'thumbnail' => '' ) + array_slice( $columns, 1, NULL, true );
246
+ }
247
+
248
+ return $columns;
249
+ }
250
+
251
+ /**
252
+ * Add featured image to column
253
+ */
254
+ function image_column( $column, $post_id ) {
255
+ global $post;
256
+ switch ( $column ) {
257
+ case 'thumbnail':
258
+ echo get_the_post_thumbnail( $post_id, 'service-admin-thumb' );
259
+ break;
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Adjust image column width
265
+ */
266
+ function enqueue_admin_styles( $hook ) {
267
+ $screen = get_current_screen();
268
+
269
+ if ( 'edit.php' == $hook && self::CUSTOM_POST_TYPE == $screen->post_type && current_theme_supports( 'post-thumbnails' ) ) {
270
+ wp_add_inline_style( 'wp-admin', '.column-thumbnail img:nth-of-type(2) { display: none; } .manage-column.column-thumbnail { width: 50px; } @media screen and (max-width: 360px) { .column-thumbnail{ display:none; } }' );
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Adds service section to the Customizer.
276
+ */
277
+ function customize_register( $wp_customize ) {
278
+ $wp_customize->add_panel( 'ect_plugin_options', array(
279
+ 'title' => esc_html__( 'Essential Content Types Plugin Options', 'essential-content-types' ),
280
+ 'priority' => 1,
281
+ ) );
282
+
283
+ $wp_customize->add_section( 'ect_service', array(
284
+ 'title' => esc_html__( 'Services', 'essential-content-types' ),
285
+ 'priority' => 130,
286
+ 'panel' => 'ect_plugin_options',
287
+ ) );
288
+
289
+ $wp_customize->add_setting( 'ect_service_title', array(
290
+ 'default' => esc_html__( 'Services', 'essential-content-types' ),
291
+ 'type' => 'option',
292
+ 'sanitize_callback' => 'sanitize_text_field',
293
+ 'sanitize_js_callback' => 'sanitize_text_field',
294
+ ) );
295
+
296
+ $wp_customize->add_control( 'ect_service_title', array(
297
+ 'section' => 'ect_service',
298
+ 'label' => esc_html__( 'Service Archive Title', 'essential-content-types' ),
299
+ 'type' => 'text',
300
+ ) );
301
+
302
+ $wp_customize->add_setting( 'ect_service_content', array(
303
+ 'default' => '',
304
+ 'type' => 'option',
305
+ 'sanitize_callback' => 'wp_kses_post',
306
+ 'sanitize_js_callback' => 'wp_kses_post',
307
+ ) );
308
+
309
+ $wp_customize->add_control( 'ect_service_content', array(
310
+ 'section' => 'ect_service',
311
+ 'label' => esc_html__( 'Service Archive Content', 'essential-content-types' ),
312
+ 'type' => 'textarea',
313
+ ) );
314
+
315
+ $wp_customize->add_setting( 'ect_service_featured_image', array(
316
+ 'default' => '',
317
+ 'type' => 'option',
318
+ 'sanitize_callback' => 'attachment_url_to_postid',
319
+ 'sanitize_js_callback' => 'attachment_url_to_postid',
320
+ 'theme_supports' => 'post-thumbnails',
321
+ ) );
322
+
323
+ $wp_customize->add_control( new WP_Customize_Image_Control( $wp_customize, 'ect_service_featured_image', array(
324
+ 'section' => 'ect_service',
325
+ 'label' => esc_html__( 'Service Archive Featured Image', 'essential-content-types' ),
326
+ ) ) );
327
+ }
328
+
329
+ /**
330
+ * Follow CPT reading setting on CPT archive and taxonomy pages
331
+ */
332
+ function query_reading_setting( $query ) {
333
+ if ( ! is_admin() &&
334
+ $query->is_main_query() &&
335
+ ( $query->is_post_type_archive( self::CUSTOM_POST_TYPE ) || $query->is_tax( self::CUSTOM_TAXONOMY_TYPE ) || $query->is_tax( self::CUSTOM_TAXONOMY_TAG ) )
336
+ ) {
337
+ $post_per_page = ( null != get_option('posts_per_page') ) ? get_option('posts_per_page') : '10';
338
+ $query->set( 'posts_per_page', get_option( self::OPTION_READING_SETTING, $post_per_page ) );
339
+ }
340
+ }
341
+
342
+ /**
343
+ * Add to REST API post type whitelist
344
+ */
345
+ function allow_service_rest_api_type( $post_types ) {
346
+ $post_types[] = self::CUSTOM_POST_TYPE;
347
+
348
+ return $post_types;
349
+ }
350
+
351
+ /**
352
+ * Our [services] shortcode.
353
+ * Prints Service data styled to look good on *any* theme.
354
+ *
355
+ * @return service_shortcode_html
356
+ */
357
+ static function service_shortcode( $atts ) {
358
+ // Default attributes
359
+ $atts = shortcode_atts( array(
360
+ 'image' => true,
361
+ 'display_types' => true,
362
+ 'display_tags' => true,
363
+ 'display_content' => true,
364
+ 'display_author' => false,
365
+ 'show_filter' => false,
366
+ 'include_type' => false,
367
+ 'include_tag' => false,
368
+ 'columns' => 2,
369
+ 'showposts' => -1,
370
+ 'order' => 'asc',
371
+ 'orderby' => 'date',
372
+ ), $atts, 'services' );
373
+
374
+ // A little sanitization
375
+ if ( $atts['image'] && 'true' != $atts['image'] ) {
376
+ $atts['image'] = false;
377
+ }
378
+
379
+ if ( $atts['display_types'] && 'true' != $atts['display_types'] ) {
380
+ $atts['display_types'] = false;
381
+ }
382
+
383
+ if ( $atts['display_tags'] && 'true' != $atts['display_tags'] ) {
384
+ $atts['display_tags'] = false;
385
+ }
386
+
387
+ if ( $atts['display_author'] && 'true' != $atts['display_author'] ) {
388
+ $atts['display_author'] = false;
389
+ }
390
+
391
+ if ( $atts['display_content'] && 'true' != $atts['display_content'] && 'full' != $atts['display_content'] ) {
392
+ $atts['display_content'] = false;
393
+ }
394
+
395
+ if ( $atts['include_type'] ) {
396
+ $atts['include_type'] = explode( ',', str_replace( ' ', '', $atts['include_type'] ) );
397
+ }
398
+
399
+ if ( $atts['include_tag'] ) {
400
+ $atts['include_tag'] = explode( ',', str_replace( ' ', '', $atts['include_tag'] ) );
401
+ }
402
+
403
+ // Check if column value is set to valid numbers or else set default value as 2
404
+ if( 1 <= $atts['columns'] && 6 >= $atts['columns'] ) {
405
+ $atts['columns'] = absint( $atts['columns'] );
406
+ } else {
407
+ $atts['columns'] = 2;
408
+ }
409
+
410
+ $atts['showposts'] = intval( $atts['showposts'] );
411
+
412
+
413
+ if ( $atts['order'] ) {
414
+ $atts['order'] = urldecode( $atts['order'] );
415
+ $atts['order'] = strtoupper( $atts['order'] );
416
+ if ( 'DESC' != $atts['order'] ) {
417
+ $atts['order'] = 'ASC';
418
+ }
419
+ }
420
+
421
+ if ( $atts['orderby'] ) {
422
+ $atts['orderby'] = urldecode( $atts['orderby'] );
423
+ $atts['orderby'] = strtolower( $atts['orderby'] );
424
+ $allowed_keys = array( 'author', 'date', 'title', 'rand' );
425
+
426
+ $parsed = array();
427
+ foreach ( explode( ',', $atts['orderby'] ) as $service_index_number => $orderby ) {
428
+ if ( ! in_array( $orderby, $allowed_keys ) ) {
429
+ continue;
430
+ }
431
+ $parsed[] = $orderby;
432
+ }
433
+
434
+ if ( empty( $parsed ) ) {
435
+ unset( $atts['orderby'] );
436
+ } else {
437
+ $atts['orderby'] = implode( ' ', $parsed );
438
+ }
439
+ }
440
+
441
+ // enqueue shortcode styles when shortcode is used
442
+ wp_enqueue_style( 'service-style', plugins_url( 'css/service-shortcode.css', __FILE__ ), array(), '20140326' );
443
+
444
+ return self::service_shortcode_html( $atts );
445
+ }
446
+
447
+ /**
448
+ * Query to retrieve entries from the Service post_type.
449
+ *
450
+ * @return object
451
+ */
452
+ static function service_query( $atts ) {
453
+ // Default query arguments
454
+ $default = array(
455
+ 'order' => $atts['order'],
456
+ 'orderby' => $atts['orderby'],
457
+ 'posts_per_page' => $atts['showposts'],
458
+ );
459
+
460
+ $args = wp_parse_args( $atts, $default );
461
+ $args['post_type'] = self::CUSTOM_POST_TYPE; // Force this post type
462
+
463
+ if ( false != $atts['include_type'] || false != $atts['include_tag'] ) {
464
+ $args['tax_query'] = array();
465
+ }
466
+
467
+ // If 'include_type' has been set use it on the main query
468
+ if ( false != $atts['include_type'] ) {
469
+ array_push( $args['tax_query'], array(
470
+ 'taxonomy' => self::CUSTOM_TAXONOMY_TYPE,
471
+ 'field' => 'slug',
472
+ 'terms' => $atts['include_type'],
473
+ ) );
474
+ }
475
+
476
+ // If 'include_tag' has been set use it on the main query
477
+ if ( false != $atts['include_tag'] ) {
478
+ array_push( $args['tax_query'], array(
479
+ 'taxonomy' => self::CUSTOM_TAXONOMY_TAG,
480
+ 'field' => 'slug',
481
+ 'terms' => $atts['include_tag'],
482
+ ) );
483
+ }
484
+
485
+ if ( false != $atts['include_type'] && false != $atts['include_tag'] ) {
486
+ $args['tax_query']['relation'] = 'AND';
487
+ }
488
+
489
+ // Run the query and return
490
+ $query = new WP_Query( $args );
491
+ return $query;
492
+ }
493
+
494
+ /**
495
+ * The Service shortcode loop.
496
+ *
497
+ * @todo add theme color styles
498
+ * @return html
499
+ */
500
+
501
+ static function service_shortcode_html( $atts ) {
502
+
503
+ $query = self::service_query( $atts );
504
+ $atts['service_index_number'] = 0;
505
+
506
+ ob_start();
507
+
508
+ // If we have posts, create the html
509
+ // with service markup
510
+ if ( $query->have_posts() ) {
511
+
512
+ /**
513
+ * Hook: ect_before_service_loop.
514
+ *
515
+ * @hooked
516
+ */
517
+ $layout = ect_get_layout();
518
+ do_action( 'ect_before_service_loop', $layout[$atts['columns']] );
519
+
520
+ ?>
521
+ <?php
522
+ while ( $query->have_posts() ) {
523
+ $query->the_post();
524
+ ect_get_template_part( 'content', 'service', $atts );
525
+ /**
526
+ * Hook: ect_service_loop.
527
+ *
528
+ * @hooked
529
+ */
530
+ }
531
+ wp_reset_postdata();
532
+ ?>
533
+ <?php
534
+
535
+ /**
536
+ * Hook: ect_after_service_loop.
537
+ *
538
+ * @hooked
539
+ */
540
+ do_action( 'ect_after_service_loop' );
541
+
542
+ } else {
543
+ /**
544
+ * Hook: ect_no_service_found.
545
+ *
546
+ * @hooked ect_no_service_found
547
+ */
548
+ do_action( 'ect_no_service_found' );
549
+ }
550
+
551
+ $html = ob_get_clean();
552
+
553
+ // If there is a [services] within a [services], remove the shortcode
554
+ if ( has_shortcode( $html, 'services' ) ){
555
+ remove_shortcode( 'services' );
556
+ }
557
+
558
+ // Return the HTML block
559
+ return $html;
560
+ }
561
+
562
+ /**
563
+ * Displays the content type that a content belongs to.
564
+ *
565
+ * @return html
566
+ */
567
+ static function get_content_type( $post_id ) {
568
+ $content_types = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TYPE );
569
+
570
+ // If no types, return empty string
571
+ if ( empty( $content_types ) || is_wp_error( $content_types ) ) {
572
+ return;
573
+ }
574
+
575
+ $html = '<div class="content-types"><span>' . __( 'Types', 'essential-content-types' ) . ':</span>';
576
+ $types = array();
577
+ // Loop thorugh all the types
578
+ foreach ( $content_types as $content_type ) {
579
+ $content_type_link = get_term_link( $content_type, self::CUSTOM_TAXONOMY_TYPE );
580
+
581
+ if ( is_wp_error( $content_type_link ) ) {
582
+ return $content_type_link;
583
+ }
584
+
585
+ $types[] = '<a href="' . esc_url( $content_type_link ) . '" rel="tag">' . esc_html( $content_type->name ) . '</a>';
586
+ }
587
+ $html .= ' '.implode( ', ', $types );
588
+ $html .= '</div>';
589
+
590
+ return $html;
591
+ }
592
+
593
+ /**
594
+ * Displays the content tags that a content belongs to.
595
+ *
596
+ * @return html
597
+ */
598
+ static function get_content_tags( $post_id ) {
599
+ $content_tags = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TAG );
600
+
601
+ // If no tags, return empty string
602
+ if ( empty( $content_tags ) || is_wp_error( $content_tags ) ) {
603
+ return false;
604
+ }
605
+
606
+ $html = '<div class="content-tags"><span>' . __( 'Tags', 'essential-content-types' ) . ':</span>';
607
+ $tags = array();
608
+ // Loop thorugh all the tags
609
+ foreach ( $content_tags as $content_tag ) {
610
+ $content_tag_link = get_term_link( $content_tag, self::CUSTOM_TAXONOMY_TYPE );
611
+
612
+ if ( is_wp_error( $content_tag_link ) ) {
613
+ return $content_tag_link;
614
+ }
615
+
616
+ $tags[] = '<a href="' . esc_url( $content_tag_link ) . '" rel="tag">' . esc_html( $content_tag->name ) . '</a>';
617
+ }
618
+ $html .= ' '. implode( ', ', $tags );
619
+ $html .= '</div>';
620
+
621
+ return $html;
622
+ }
623
+
624
+ /**
625
+ * Displays the author of the current service content.
626
+ *
627
+ * @return html
628
+ */
629
+ static function get_content_author() {
630
+ $html = '<div class="content-author">';
631
+ /* translators: %1$s is link to author posts, %2$s is author display name */
632
+ $html .= sprintf( __( '<span>Author:</span> <a href="%1$s">%2$s</a>', 'essential-content-types' ),
633
+ esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ),
634
+ esc_html( get_the_author() )
635
+ );
636
+ $html .= '</div>';
637
+
638
+ return $html;
639
+ }
640
+ }
641
+ add_action( 'init', array( 'Essential_Content_Service', 'init' ) );
642
+
643
+
644
+ /**
645
+ * Add Service support
646
+ */
647
+ function essential_content_service_support() {
648
+ /*
649
+ * Adding theme support for Jetpack Service CPT.
650
+ */
651
+ add_image_size( 'ect-service', 640, 640, true );
652
+ }
653
+ add_action( 'after_setup_theme', 'essential_content_service_support' );
654
+
655
+
656
+ if( ! function_exists( 'essential_content_get_service_thumbnail_link' ) ):
657
+ function essential_content_get_service_thumbnail_link( $post_id, $size ) {
658
+ if ( has_post_thumbnail( $post_id ) ) {
659
+ /**
660
+ * Change the Service thumbnail size.
661
+ *
662
+ * @module custom-content-types
663
+ *
664
+ * @since 3.4.0
665
+ *
666
+ * @param string|array $var Either a registered size keyword or size array.
667
+ */
668
+ return '<a class="service-featured-image" href="' . esc_url( get_permalink( $post_id ) ) . '">' . get_the_post_thumbnail( $post_id, apply_filters( 'service_thumbnail_size', $size ) ) . '</a>';
669
+ }
670
+ }
671
+ endif;
672
+
673
+
674
+ if( ! function_exists( 'essential_content_no_service_found' ) ):
675
+ /**
676
+ * No items found text
677
+ *
678
+ * @return html
679
+ */
680
+ function essential_content_no_service_found() {
681
+ echo "<p><em>" . esc_html__( 'Your Service Archive currently has no entries. You can start creating them on your dashboard.', 'essential-content-types-pro' ) . "</em></p>";
682
+ }
683
+ add_action( 'ect_no_service_found', 'essential_content_no_service_found', 10 );
684
+ endif;
685
+
686
+
687
+ if( ! function_exists( 'essential_content_service_section_open' ) ):
688
+ /**
689
+ * Open section
690
+ *
691
+ * @return html
692
+ */
693
+ function essential_content_service_section_open( $layout ) {
694
+ echo '<div class="ect-service ect-section">';
695
+ echo '<div class="ect-wrapper">';
696
+ }
697
+ endif;
698
+ add_action( 'ect_before_service_loop', 'essential_content_service_section_open', 10, 1 );
699
+
700
+
701
+ if( ! function_exists( 'essential_content_service_loop_start' ) ):
702
+ /**
703
+ * open wrapper before loop
704
+ *
705
+ */
706
+ function essential_content_service_loop_start( $layout = null ){
707
+ echo '<div class="section-content-wrapper service-content-wrapper ' . $layout . '">';
708
+ }
709
+ endif;
710
+ add_action( 'ect_before_service_loop', 'essential_content_service_loop_start', 30 );
711
+
712
+
713
+ if( ! function_exists( 'essential_content_service_loop_end' ) ):
714
+ /**
715
+ * close wrapper after loop
716
+ *
717
+ */
718
+ function essential_content_service_loop_end(){
719
+ echo '</div><!-- .service-content-wrapper -->';
720
+ }
721
+ endif;
722
+ add_action( 'ect_after_service_loop', 'essential_content_service_loop_end', 10 );
723
+
724
+
725
+ if( ! function_exists( 'essential_content_service_section_close' ) ):
726
+ /**
727
+ * Close section
728
+ *
729
+ * @return html
730
+ */
731
+ function essential_content_service_section_close() {
732
+ echo '</div><!-- .ect-wrapper -->';
733
+ echo '</div><!-- .ect-section -->';
734
+ }
735
+ endif;
736
  add_action( 'ect_after_service_loop', 'essential_content_service_section_close', 20 );
admin/class-testimonial.php CHANGED
@@ -1,1075 +1,1075 @@
1
- <?php
2
-
3
- /**
4
- * Support JetPack Testimonial
5
- */
6
- class Essential_Content_Jetpack_Testimonial {
7
- const CUSTOM_POST_TYPE = 'jetpack-testimonial';
8
- const OPTION_NAME = 'jetpack_testimonial';
9
- const OPTION_READING_SETTING = 'jetpack_testimonial_posts_per_page';
10
-
11
- public $version = ESSENTIAL_CONTENT_TYPES_VERSION;
12
-
13
- static function init() {
14
- static $instance = false;
15
-
16
- if ( ! $instance ) {
17
- $instance = new Essential_Content_Jetpack_Testimonial;
18
- }
19
-
20
- return $instance;
21
- }
22
-
23
- /**
24
- * Conditionally hook into WordPress.
25
- *
26
- * Setup user option for enabling CPT.
27
- * If user has CPT enabled, show in admin.
28
- */
29
- function __construct() {
30
- // Make sure the post types are loaded for imports
31
- add_action( 'import_start', array( $this, 'register_post_types' ) );
32
-
33
- // If called via REST API, we need to register later in lifecycle
34
- add_action( 'restapi_theme_init', array( $this, 'maybe_register_cpt' ) );
35
-
36
- // Add to REST API post type whitelist
37
- add_filter( 'rest_api_allowed_post_types', array( $this, 'allow_cpt_rest_api_type' ) );
38
-
39
- $this->maybe_register_cpt();
40
- }
41
-
42
- /**
43
- * Registers the custom post types and adds action/filter handlers, but
44
- * only if the site supports it
45
- */
46
- function maybe_register_cpt() {
47
- // Add an option to enable the CPT
48
- add_action( 'admin_init', array( $this, 'settings_api_init' ) );
49
-
50
- // Check on theme switch if theme supports CPT and setting is disabled
51
- add_action( 'after_switch_theme', array( $this, 'activation_post_type_support' ) );
52
-
53
- $setting = 1;
54
-
55
- if ( class_exists( 'Jetpack_Options' ) ) {
56
- $setting = Jetpack_Options::get_option_and_ensure_autoload( self::OPTION_NAME, '0' );
57
- }
58
-
59
- // Bail early if Testimonial option is not set and the theme doesn't declare support
60
- if ( empty( $setting ) && ! $this->site_supports_custom_post_type() ) {
61
- return;
62
- }
63
-
64
- // Enable Omnisearch for CPT.
65
- if ( class_exists( 'Jetpack_Omnisearch_Posts' ) ) {
66
- new Jetpack_Omnisearch_Posts( self::CUSTOM_POST_TYPE );
67
- }
68
-
69
- // CPT magic
70
- $this->register_post_types();
71
- add_action( sprintf( 'add_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 );
72
- add_action( sprintf( 'update_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 );
73
- add_action( sprintf( 'publish_%s', self::CUSTOM_POST_TYPE ), array( $this, 'flush_rules_on_first_testimonial' ) );
74
- add_action( 'after_switch_theme', array( $this, 'flush_rules_on_switch' ) );
75
-
76
- // Admin Customization
77
- add_filter( 'enter_title_here', array( $this, 'change_default_title' ) );
78
- add_filter( sprintf( 'manage_%s_posts_columns', self::CUSTOM_POST_TYPE ), array( $this, 'edit_title_column_label' ) );
79
- add_filter( 'post_updated_messages', array( $this, 'updated_messages' ) );
80
- add_action( 'customize_register', array( $this, 'customize_register' ) );
81
-
82
- // Only add the 'Customize' sub-menu if the theme supports it.
83
- $num_testimonials = self::count_testimonials();
84
- if ( ! empty( $num_testimonials ) && current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
85
- add_action( 'admin_menu', array( $this, 'add_customize_page' ) );
86
- }
87
-
88
- // Add to Jetpack XML sitemap
89
- add_filter( 'jetpack_sitemap_post_types', array( $this, 'add_to_sitemap' ) );
90
-
91
- // Adjust CPT archive and custom taxonomies to obey CPT reading setting
92
- add_filter( 'pre_get_posts', array( $this, 'query_reading_setting' ), 20 );
93
- add_filter( 'infinite_scroll_settings', array( $this, 'infinite_scroll_click_posts_per_page' ) );
94
-
95
- // Register [jetpack_testimonials] always and
96
- // register [testimonials] if [testimonials] isn't already set
97
- add_shortcode( 'jetpack_testimonials', array( $this, 'jetpack_testimonial_shortcode' ) );
98
-
99
- if ( ! shortcode_exists( 'testimonials' ) ) {
100
- add_shortcode( 'testimonials', array( $this, 'jetpack_testimonial_shortcode' ) );
101
- }
102
-
103
- // If CPT was enabled programatically and no CPT items exist when user switches away, disable
104
- if ( $setting && $this->site_supports_custom_post_type() ) {
105
- add_action( 'switch_theme', array( $this, 'deactivation_post_type_support' ) );
106
- }
107
- }
108
-
109
- /**
110
- * Add a checkbox field in 'Settings' > 'Writing'
111
- * for enabling CPT functionality.
112
- *
113
- * @return null
114
- */
115
- function settings_api_init() {
116
- add_settings_field(
117
- self::OPTION_NAME,
118
- '<span class="cpt-options">' . __( 'Testimonials', 'essential-content-types' ) . '</span>',
119
- array( $this, 'setting_html' ),
120
- 'writing',
121
- 'jetpack_cpt_section'
122
- );
123
-
124
- register_setting(
125
- 'writing',
126
- self::OPTION_NAME,
127
- 'intval'
128
- );
129
-
130
- // Check if CPT is enabled first so that intval doesn't get set to NULL on re-registering
131
- if ( $this->site_supports_custom_post_type() ) {
132
- register_setting(
133
- 'writing',
134
- self::OPTION_READING_SETTING,
135
- 'intval'
136
- );
137
- }
138
- }
139
-
140
- /**
141
- * HTML code to display a checkbox true/false option
142
- * for the CPT setting.
143
- *
144
- * @return html
145
- */
146
- function setting_html() {
147
- if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) : ?>
148
- <p><?php printf( __( 'Your theme supports Testimonials', 'essential-content-types' ) ); ?></p>
149
- <?php else : ?>
150
- <label for="<?php echo esc_attr( self::OPTION_NAME ); ?>">
151
- <input name="<?php echo esc_attr( self::OPTION_NAME ); ?>" id="<?php echo esc_attr( self::OPTION_NAME ); ?>" <?php echo checked( get_option( self::OPTION_NAME, '0' ), true, false ); ?> type="checkbox" value="1" />
152
- <?php esc_html_e( 'Enable Testimonials for this site.', 'essential-content-types' ); ?>
153
- <a target="_blank" href="http://en.support.wordpress.com/testimonials/"><?php esc_html_e( 'Learn More', 'essential-content-types' ); ?></a>
154
- </label>
155
- <?php
156
- endif;
157
-
158
- if ( $this->site_supports_custom_post_type() ) :
159
- printf(
160
- '<p><label for="%1$s">%2$s</label></p>',
161
- esc_attr( self::OPTION_READING_SETTING ),
162
- /* translators: %1$s is replaced with an input field for numbers */
163
- sprintf(
164
- __( 'Testimonial pages display at most %1$s testimonials', 'essential-content-types' ),
165
- sprintf(
166
- '<input name="%1$s" id="%1$s" type="number" step="1" min="1" value="%2$s" class="small-text" />',
167
- esc_attr( self::OPTION_READING_SETTING ),
168
- esc_attr( get_option( self::OPTION_READING_SETTING, '10' ) )
169
- )
170
- )
171
- );
172
- endif;
173
- }
174
-
175
- /**
176
- * Should this Custom Post Type be made available?
177
- */
178
- function site_supports_custom_post_type() {
179
- // If the current theme requests it.
180
- if ( current_theme_supports( self::CUSTOM_POST_TYPE ) || get_option( self::OPTION_NAME, '0' ) ) {
181
- return true;
182
- }
183
-
184
- // Otherwise, say no unless something wants to filter us to say yes.
185
- /** This action is documented in modules/custom-post-types/nova.php */
186
- return (bool) apply_filters( 'jetpack_enable_cpt', false, self::CUSTOM_POST_TYPE );
187
- }
188
-
189
- /**
190
- * Add to REST API post type whitelist
191
- */
192
- function allow_cpt_rest_api_type( $post_types ) {
193
- $post_types[] = self::CUSTOM_POST_TYPE;
194
-
195
- return $post_types;
196
- }
197
-
198
-
199
- /*
200
- * Flush permalinks when CPT option is turned on/off
201
- */
202
- function flush_rules_on_enable() {
203
- flush_rewrite_rules();
204
- }
205
-
206
- /*
207
- * Count published testimonials and flush permalinks when first testimonial is published
208
- */
209
- function flush_rules_on_first_testimonial() {
210
- $testimonials = get_transient( 'jetpack-testimonial-count-cache' );
211
-
212
- if ( false === $testimonials ) {
213
- flush_rewrite_rules();
214
- $testimonials = (int) wp_count_posts( self::CUSTOM_POST_TYPE )->publish;
215
-
216
- if ( ! empty( $testimonials ) ) {
217
- set_transient( 'jetpack-testimonial-count-cache', $testimonials, HOUR_IN_SECONDS * 12 );
218
- }
219
- }
220
- }
221
-
222
- /*
223
- * Flush permalinks when CPT supported theme is activated
224
- */
225
- function flush_rules_on_switch() {
226
- if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
227
- flush_rewrite_rules();
228
- }
229
- }
230
-
231
- /**
232
- * On plugin/theme activation, check if current theme supports CPT
233
- */
234
- static function activation_post_type_support() {
235
- if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
236
- update_option( self::OPTION_NAME, '1' );
237
- }
238
- }
239
-
240
- /**
241
- * On theme switch, check if CPT item exists and disable if not
242
- */
243
- function deactivation_post_type_support() {
244
- $testimonials = get_posts(
245
- array(
246
- 'fields' => 'ids',
247
- 'posts_per_page' => 1,
248
- 'post_type' => self::CUSTOM_POST_TYPE,
249
- 'suppress_filters' => false,
250
- )
251
- );
252
-
253
- if ( empty( $testimonials ) ) {
254
- update_option( self::OPTION_NAME, '0' );
255
- }
256
- }
257
-
258
- /**
259
- * Register Post Type
260
- */
261
- function register_post_types() {
262
- if ( post_type_exists( self::CUSTOM_POST_TYPE ) ) {
263
- return;
264
- }
265
-
266
- $args = array(
267
- 'description' => __( 'Customer Testimonials', 'essential-content-types' ),
268
- 'labels' => array(
269
- 'name' => esc_html__( 'Testimonials', 'essential-content-types' ),
270
- 'singular_name' => esc_html__( 'Testimonial', 'essential-content-types' ),
271
- 'menu_name' => esc_html__( 'Testimonials', 'essential-content-types' ),
272
- 'all_items' => esc_html__( 'All Testimonials', 'essential-content-types' ),
273
- 'add_new' => esc_html__( 'Add New', 'essential-content-types' ),
274
- 'add_new_item' => esc_html__( 'Add New Testimonial', 'essential-content-types' ),
275
- 'edit_item' => esc_html__( 'Edit Testimonial', 'essential-content-types' ),
276
- 'new_item' => esc_html__( 'New Testimonial', 'essential-content-types' ),
277
- 'view_item' => esc_html__( 'View Testimonial', 'essential-content-types' ),
278
- 'search_items' => esc_html__( 'Search Testimonials', 'essential-content-types' ),
279
- 'not_found' => esc_html__( 'No Testimonials found', 'essential-content-types' ),
280
- 'not_found_in_trash' => esc_html__( 'No Testimonials found in Trash', 'essential-content-types' ),
281
- 'filter_items_list' => esc_html__( 'Filter Testimonials list', 'essential-content-types' ),
282
- 'items_list_navigation' => esc_html__( 'Testimonial list navigation', 'essential-content-types' ),
283
- 'items_list' => esc_html__( 'Testimonials list', 'essential-content-types' ),
284
- ),
285
- 'supports' => array(
286
- 'title',
287
- 'editor',
288
- 'excerpt',
289
- 'thumbnail',
290
- 'page-attributes',
291
- 'revisions',
292
- ),
293
- 'rewrite' => array(
294
- 'slug' => 'testimonial',
295
- 'with_front' => false,
296
- 'feeds' => false,
297
- 'pages' => true,
298
- ),
299
- 'public' => true,
300
- 'show_ui' => true,
301
- 'menu_position' => 20, // below Pages
302
- 'menu_icon' => 'dashicons-testimonial',
303
- 'capability_type' => 'page',
304
- 'map_meta_cap' => true,
305
- 'has_archive' => true,
306
- 'query_var' => 'testimonial',
307
- 'show_in_rest' => true,
308
- );
309
-
310
- $description = get_option( 'jetpack_testimonial_content' );
311
- if ( '' !== $description ) {
312
- $args['description'] = $description;
313
- }
314
-
315
- register_post_type( self::CUSTOM_POST_TYPE, $args );
316
- }
317
-
318
- /**
319
- * Update messages for the Testimonial admin.
320
- */
321
- function updated_messages( $messages ) {
322
- global $post;
323
-
324
- $messages[ self::CUSTOM_POST_TYPE ] = array(
325
- 0 => '', // Unused. Messages start at index 1.
326
- 1 => sprintf( __( 'Testimonial updated. <a href="%s">View testimonial</a>', 'essential-content-types' ), esc_url( get_permalink( $post->ID ) ) ),
327
- 2 => esc_html__( 'Custom field updated.', 'essential-content-types' ),
328
- 3 => esc_html__( 'Custom field deleted.', 'essential-content-types' ),
329
- 4 => esc_html__( 'Testimonial updated.', 'essential-content-types' ),
330
- /* translators: %s: date and time of the revision */
331
- 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Testimonial restored to revision from %s', 'essential-content-types' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
332
- 6 => sprintf( __( 'Testimonial published. <a href="%s">View testimonial</a>', 'essential-content-types' ), esc_url( get_permalink( $post->ID ) ) ),
333
- 7 => esc_html__( 'Testimonial saved.', 'essential-content-types' ),
334
- 8 => sprintf( __( 'Testimonial submitted. <a target="_blank" href="%s">Preview testimonial</a>', 'essential-content-types' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
335
- 9 => sprintf(
336
- __( 'Testimonial scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview testimonial</a>', 'essential-content-types' ),
337
- // translators: Publish box date format, see http://php.net/date
338
- date_i18n( __( 'M j, Y @ G:i', 'essential-content-types' ), strtotime( $post->post_date ) ),
339
- esc_url( get_permalink( $post->ID ) )
340
- ),
341
- 10 => sprintf( __( 'Testimonial draft updated. <a target="_blank" href="%s">Preview testimonial</a>', 'essential-content-types' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
342
- );
343
-
344
- return $messages;
345
- }
346
-
347
- /**
348
- * Change ‘Enter Title Here’ text for the Testimonial.
349
- */
350
- function change_default_title( $title ) {
351
- $screen = get_current_screen();
352
-
353
- if ( self::CUSTOM_POST_TYPE == $screen->post_type ) {
354
- $title = esc_html__( "Enter the customer's name here", 'essential-content-types' );
355
- }
356
-
357
- return $title;
358
- }
359
-
360
- /**
361
- * Change ‘Title’ column label on all Testimonials page.
362
- */
363
- function edit_title_column_label( $columns ) {
364
- $columns['title'] = esc_html__( 'Customer Name', 'essential-content-types' );
365
-
366
- return $columns;
367
- }
368
-
369
- /**
370
- * Follow CPT reading setting on CPT archive page
371
- */
372
- function query_reading_setting( $query ) {
373
- if ( ! is_admin()
374
- && $query->is_main_query()
375
- && $query->is_post_type_archive( self::CUSTOM_POST_TYPE )
376
- ) {
377
- $post_per_page = ( null != get_option( 'posts_per_page' ) ) ? get_option( 'posts_per_page' ) : '10';
378
- $query->set( 'posts_per_page', get_option( self::OPTION_READING_SETTING, $post_per_page ) );
379
- }
380
- }
381
-
382
- /*
383
- * If Infinite Scroll is set to 'click', use our custom reading setting instead of core's `posts_per_page`.
384
- */
385
- function infinite_scroll_click_posts_per_page( $settings ) {
386
- global $wp_query;
387
-
388
- if ( ! is_admin() && true === $settings['click_handle'] && $wp_query->is_post_type_archive( self::CUSTOM_POST_TYPE ) ) {
389
- $settings['posts_per_page'] = get_option( self::OPTION_READING_SETTING, $settings['posts_per_page'] );
390
- }
391
-
392
- return $settings;
393
- }
394
-
395
- /**
396
- * Add CPT to Dotcom sitemap
397
- */
398
- function add_to_sitemap( $post_types ) {
399
- $post_types[] = self::CUSTOM_POST_TYPE;
400
-
401
- return $post_types;
402
- }
403
-
404
- function set_testimonial_option() {
405
- $testimonials = wp_count_posts( self::CUSTOM_POST_TYPE );
406
- $published_testimonials = $testimonials->publish;
407
-
408
- update_option( self::OPTION_NAME, $published_testimonials );
409
- }
410
-
411
- function count_testimonials() {
412
- $testimonials = get_transient( 'jetpack-testimonial-count-cache' );
413
-
414
- if ( false === $testimonials ) {
415
- $testimonials = (int) wp_count_posts( self::CUSTOM_POST_TYPE )->publish;
416
-
417
- if ( ! empty( $testimonials ) ) {
418
- set_transient( 'jetpack-testimonial-count-cache', $testimonials, 60 * 60 * 12 );
419
- }
420
- }
421
-
422
- return $testimonials;
423
- }
424
-
425
- /**
426
- * Adds a submenu link to the Customizer.
427
- */
428
- function add_customize_page() {
429
- add_submenu_page(
430
- 'edit.php?post_type=' . self::CUSTOM_POST_TYPE,
431
- esc_html__( 'Customize Testimonials Archive', 'essential-content-types' ),
432
- esc_html__( 'Customize', 'essential-content-types' ),
433
- 'edit_theme_options',
434
- add_query_arg(
435
- array(
436
- 'url' => urlencode( home_url( '/testimonial/' ) ),
437
- 'autofocus[section]' => 'jetpack_testimonials',
438
- ),
439
- 'customize.php'
440
- )
441
- );
442
- }
443
-
444
- /**
445
- * Adds testimonial section to the Customizer.
446
- */
447
- function customize_register( $wp_customize ) {
448
- essential_content_testimonial_custom_control_classes();
449
-
450
- $wp_customize->add_panel(
451
- 'ect_plugin_options',
452
- array(
453
- 'title' => esc_html__( 'Essential Content Types Plugin Options', 'essential-content-types' ),
454
- 'priority' => 1,
455
- )
456
- );
457
-
458
- $wp_customize->add_section(
459
- 'jetpack_testimonials',
460
- array(
461
- 'title' => esc_html__( 'Testimonials', 'essential-content-types' ),
462
- 'theme_supports' => self::CUSTOM_POST_TYPE,
463
- 'priority' => 130,
464
- 'panel' => 'ect_plugin_options',
465
- )
466
- );
467
-
468
- $wp_customize->add_setting(
469
- 'jetpack_testimonial_title',
470
- array(
471
- 'default' => esc_html__( 'Testimonials', 'essential-content-types' ),
472
- 'type' => 'option',
473
- 'sanitize_callback' => 'sanitize_text_field',
474
- 'sanitize_js_callback' => 'sanitize_text_field',
475
- )
476
- );
477
- $wp_customize->add_control(
478
- 'jetpack_testimonial_title',
479
- array(
480
- 'section' => 'jetpack_testimonials',
481
- 'label' => esc_html__( 'Testimonial Archive Title', 'essential-content-types' ),
482
- 'type' => 'text',
483
- )
484
- );
485
-
486
- $wp_customize->add_setting(
487
- 'jetpack_testimonial_content',
488
- array(
489
- 'default' => '',
490
- 'type' => 'option',
491
- 'sanitize_callback' => 'wp_kses_post',
492
- 'sanitize_js_callback' => 'wp_kses_post',
493
- )
494
- );
495
- $wp_customize->add_control(
496
- new Essential_Content_Jetpack_Testimonial_Textarea_Control(
497
- $wp_customize,
498
- 'jetpack_testimonial_content',
499
- array(
500
- 'section' => 'jetpack_testimonials',
501
- 'label' => esc_html__( 'Testimonial Archive Content', 'essential-content-types' ),
502
- 'type' => 'textarea',
503
- )
504
- )
505
- );
506
-
507
- $wp_customize->add_setting(
508
- 'jetpack_testimonial_featured_image',
509
- array(
510
- 'default' => '',
511
- 'type' => 'option',
512
- 'sanitize_callback' => 'attachment_url_to_postid',
513
- 'sanitize_js_callback' => 'attachment_url_to_postid',
514
- 'theme_supports' => 'post-thumbnails',
515
- )
516
- );
517
- $wp_customize->add_control(
518
- new WP_Customize_Image_Control(
519
- $wp_customize,
520
- 'jetpack_testimonial_featured_image',
521
- array(
522
- 'section' => 'jetpack_testimonials',
523
- 'label' => esc_html__( 'Testimonial Archive Featured Image', 'essential-content-types' ),
524
- )
525
- )
526
- );
527
-
528
- // The featured image control doesn't display properly in the Customizer unless we coerce
529
- // it back into a URL sooner, since that's what WP_Customize_Upload_Control::to_json() expects
530
- if ( is_admin() ) {
531
- add_filter( 'theme_mod_jetpack_testimonials', array( $this, 'coerce_testimonial_image_to_url' ) );
532
- }
533
- }
534
-
535
- public function coerce_testimonial_image_to_url( $opt ) {
536
- if ( ! $opt || ! is_array( $opt ) ) {
537
- return $opt;
538
- }
539
- if ( ! isset( $opt['featured-image'] ) || ! is_scalar( $opt['featured-image'] ) ) {
540
- return $opt;
541
- }
542
- $url = wp_get_attachment_url( $opt['featured-image'] );
543
- if ( $url ) {
544
- $opt['featured-image'] = $url;
545
- }
546
- return $opt;
547
- }
548
-
549
- /**
550
- * Our [testimonial] shortcode.
551
- * Prints Testimonial data styled to look good on *any* theme.
552
- *
553
- * @return jetpack_testimonial_shortcode_html
554
- */
555
- static function jetpack_testimonial_shortcode( $atts ) {
556
- // Default attributes
557
- $atts = shortcode_atts(
558
- array(
559
- 'display_content' => true,
560
- 'image' => true,
561
- 'columns' => 1,
562
- 'showposts' => -1,
563
- 'order' => 'asc',
564
- 'orderby' => 'date',
565
- ),
566
- $atts,
567
- 'testimonial'
568
- );
569
-
570
- // A little sanitization
571
- if ( $atts['display_content'] && 'true' != $atts['display_content'] && 'full' != $atts['display_content'] ) {
572
- $atts['display_content'] = false;
573
- }
574
-
575
- if ( $atts['image'] && 'true' != $atts['image'] ) {
576
- $atts['image'] = false;
577
- }
578
-
579
- // Check if column value is set to valid numbers or else set default value as 1
580
- if ( 1 == $atts['columns'] || 2 == $atts['columns'] ) {
581
- $atts['columns'] = absint( $atts['columns'] );
582
- } else {
583
- $atts['columns'] = 1;
584
- }
585
-
586
- $atts['showposts'] = intval( $atts['showposts'] );
587
-
588
- if ( $atts['order'] ) {
589
- $atts['order'] = urldecode( $atts['order'] );
590
- $atts['order'] = strtoupper( $atts['order'] );
591
- if ( 'DESC' != $atts['order'] ) {
592
- $atts['order'] = 'ASC';
593
- }
594
- }
595
-
596
- if ( $atts['orderby'] ) {
597
- $atts['orderby'] = urldecode( $atts['orderby'] );
598
- $atts['orderby'] = strtolower( $atts['orderby'] );
599
- $allowed_keys = array( 'author', 'date', 'title', 'rand' );
600
-
601
- $parsed = array();
602
- foreach ( explode( ',', $atts['orderby'] ) as $testimonial_index_number => $orderby ) {
603
- if ( ! in_array( $orderby, $allowed_keys ) ) {
604
- continue;
605
- }
606
- $parsed[] = $orderby;
607
- }
608
-
609
- if ( empty( $parsed ) ) {
610
- unset( $atts['orderby'] );
611
- } else {
612
- $atts['orderby'] = implode( ' ', $parsed );
613
- }
614
- }
615
-
616
- // enqueue shortcode styles when shortcode is used
617
- wp_enqueue_style( 'jetpack-testimonial-style', plugins_url( 'css/testimonial-shortcode.css', __FILE__ ), array(), '20140326' );
618
-
619
- return self::jetpack_testimonial_shortcode_html( $atts );
620
- }
621
-
622
- /**
623
- * The Testimonial shortcode loop.
624
- *
625
- * @todo add theme color styles
626
- * @return html
627
- */
628
-
629
- static function jetpack_testimonial_shortcode_html( $atts ) {
630
-
631
- // Default query arguments
632
- $defaults = array(
633
- 'order' => $atts['order'],
634
- 'orderby' => $atts['orderby'],
635
- 'posts_per_page' => $atts['showposts'],
636
- );
637
-
638
- $args = wp_parse_args( $atts, $defaults );
639
- $args['post_type'] = self::CUSTOM_POST_TYPE; // Force this post type
640
- $query = new WP_Query( $args );
641
-
642
- ob_start();
643
-
644
- // If we have posts, create the html
645
- // with testimonial markup
646
- if ( $query->have_posts() ) {
647
-
648
- /**
649
- * Hook: ect_before_testimonial_loop.
650
- *
651
- * @hooked
652
- */
653
- $layout = ect_get_layout();
654
- do_action( 'ect_before_testimonial_loop', $layout[ $atts['columns'] ] );
655
- ?>
656
- <?php
657
- while ( $query->have_posts() ) {
658
-
659
- $query->the_post();
660
- ect_get_template_part( 'content', 'testimonial', $atts );
661
-
662
- }
663
- wp_reset_postdata();
664
- ?>
665
- <?php
666
-
667
- /**
668
- * Hook: ect_after_testimonial_loop.
669
- *
670
- * @hooked
671
- */
672
- do_action( 'ect_after_testimonial_loop' );
673
-
674
- } else {
675
- /**
676
- * Hook: ect_no_testimonial_found.
677
- *
678
- * @hooked ect_no_testimonial_found
679
- */
680
- do_action( 'ect_no_testimonial_found' );
681
- }
682
-
683
- $html = ob_get_clean();
684
-
685
- // If there is a [testimonials] within a [testimonials], remove the shortcode
686
- if ( has_shortcode( $html, 'testimonials' ) ) {
687
- remove_shortcode( 'testimonials' );
688
- }
689
-
690
- // Return the HTML block
691
- return $html;
692
- }
693
- }
694
- add_action( 'init', array( 'Essential_Content_Jetpack_Testimonial', 'init' ) );
695
-
696
-
697
- function essential_content_testimonial_custom_control_classes() {
698
- class Essential_Content_Jetpack_Testimonial_Title_Control extends WP_Customize_Control {
699
- public static function sanitize_content( $value ) {
700
- if ( '' != $value ) {
701
- $value = trim( convert_chars( wptexturize( $value ) ) );
702
- }
703
-
704
- return $value;
705
- }
706
- }
707
-
708
- class Essential_Content_Jetpack_Testimonial_Textarea_Control extends WP_Customize_Control {
709
- public $type = 'textarea';
710
-
711
- public function render_content() {
712
- ?>
713
- <label>
714
- <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
715
- <textarea rows="5" style="width:100%;" <?php $this->link(); ?>><?php echo esc_textarea( $this->value() ); ?></textarea>
716
- </label>
717
- <?php
718
- }
719
-
720
- public static function sanitize_content( $value ) {
721
- if ( ! empty( $value ) ) {
722
- /** This filter is already documented in core. wp-includes/post-template.php */
723
- $value = apply_filters( 'the_content', $value );
724
- }
725
-
726
- $value = preg_replace( '@<div id="jp-post-flair"([^>]+)?>(.+)?</div>@is', '', $value ); // Strip WPCOM and Jetpack post flair if included in content
727
-
728
- return $value;
729
- }
730
- }
731
- }
732
-
733
- /**
734
- * Add Testimonial support
735
- */
736
- function essential_content_testimonial_support() {
737
- /*
738
- * Adding theme support for Jetpack Testimonial CPT.
739
- */
740
- add_theme_support( 'jetpack-testimonial' );
741
- }
742
- add_action( 'after_setup_theme', 'essential_content_testimonial_support' );
743
-
744
-
745
- /**
746
- * Class to Renders and save metabox options
747
- *
748
- */
749
- class Essential_Content_Jetpack_Testimonial_Metabox {
750
- private $meta_box;
751
-
752
- private $fields;
753
-
754
- /**
755
- * Constructor
756
- *
757
- *
758
- * @access public
759
- *
760
- */
761
- public function __construct( $meta_box_id, $meta_box_title, $post_type ) {
762
-
763
- $this->meta_box = array(
764
- 'id' => $meta_box_id,
765
- 'title' => $meta_box_title,
766
- 'post_type' => $post_type,
767
- );
768
-
769
- $this->fields = array(
770
- 'ect-testimonials',
771
- );
772
-
773
- // Add metaboxes
774
- add_action( 'add_meta_boxes', array( $this, 'add' ) );
775
-
776
- add_action( 'save_post', array( $this, 'save' ) );
777
- }
778
-
779
- /**
780
- * Add Meta Box for multiple post types.
781
- *
782
- *
783
- * @access public
784
- */
785
- public function add( $postType ) {
786
- if ( in_array( $postType, $this->meta_box['post_type'] ) ) {
787
- add_meta_box( $this->meta_box['id'], $this->meta_box['title'], array( $this, 'show' ), null, 'side', 'high' );
788
- }
789
- }
790
-
791
- /**
792
- * Renders metabox
793
- *
794
- *
795
- * @access public
796
- */
797
- public function show() {
798
- global $post;
799
-
800
- // Use nonce for verification
801
- wp_nonce_field( basename( __FILE__ ), 'ect_custom_meta_box_nonce' );
802
-
803
- $position = get_post_meta( $post->ID, 'ect_testimonial_position', true );
804
-
805
- // Begin the form
806
- ?>
807
- <p>
808
- <label for="ect_testimonial_position"><?php esc_html_e( 'Position', 'essential-content-types' ); ?></label>
809
- <input type="text" class="widefat ect-testimonial-position" name="ect_testimonial_position" value="<?php echo esc_attr( $position ); ?>" />
810
- </p>
811
- <?php
812
- }
813
-
814
- /**
815
- * Save custom metabox data
816
- *
817
- * @action save_post
818
- *
819
- * @access public
820
- */
821
- public function save( $post_id ) {
822
- global $post_type;
823
-
824
- $post_type_object = get_post_type_object( $post_type );
825
-
826
- if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) // Check Autosave
827
- || ( ! isset( $_POST['post_ID'] ) || $post_id != $_POST['post_ID'] ) // Check Revision
828
- || ( ! in_array( $post_type, $this->meta_box['post_type'] ) ) // Check if current post type is supported.
829
- || ( ! check_admin_referer( basename( __FILE__ ), 'ect_custom_meta_box_nonce' ) ) // Check nonce - Security
830
- || ( ! current_user_can( $post_type_object->cap->edit_post, $post_id ) ) ) { // Check permission
831
- return $post_id;
832
- }
833
-
834
- if ( ! update_post_meta( $post_id, 'ect_testimonial_position', sanitize_text_field( $_POST['ect_testimonial_position'] ) ) ) {
835
- add_post_meta( $post_id, 'ect_testimonial_position', sanitize_text_field( $_POST['ect_testimonial_position'] ), true );
836
- }
837
- }
838
- }
839
-
840
- $ect_metabox = new Essential_Content_Jetpack_Testimonial_Metabox(
841
- 'ect-options', //metabox id
842
- esc_html__( 'Testmonial Options', 'essential-content-types' ), //metabox title
843
- array( 'jetpack-testimonial' ) //metabox post types
844
- );
845
-
846
-
847
- if ( ! function_exists( 'essential_content_get_testimonial_thumbnail_link' ) ) :
848
- /**
849
- * Display the featured image if it's available
850
- *
851
- * @return html
852
- */
853
- function essential_content_get_testimonial_thumbnail_link( $post_id, $size ) {
854
- if ( has_post_thumbnail( $post_id ) ) {
855
- /**
856
- * Change the Testimonial thumbnail size.
857
- *
858
- * @module custom-content-types
859
- *
860
- * @since 3.4.0
861
- *
862
- * @param string|array $var Either a registered size keyword or size array.
863
- */
864
- //return '<a class="testimonial-featured-image testimonial-thumbnail post-thumbnail" href="' . esc_url( get_permalink( $post_id ) ) . '">' . get_the_post_thumbnail( $post_id, apply_filters( 'testimonial_thumbnail_size', $size ) ) . '</a>';
865
- return '<div class="testimonial-featured-image testimonial-thumbnail post-thumbnail">' . get_the_post_thumbnail( $post_id, apply_filters( 'testimonial_thumbnail_size', $size ) ) . '</div>';
866
- }
867
- }
868
- endif;
869
-
870
-
871
- if ( ! function_exists( 'essential_content_no_testimonial_found' ) ) :
872
- /**
873
- * No items found text
874
- *
875
- * @return html
876
- */
877
- function essential_content_no_testimonial_found() {
878
- echo '<p><em>' . esc_html__( 'Your Testimonial Archive currently has no entries. You can start creating them on your dashboard.', 'essential-content-types-pro' ) . '</em></p>';
879
- }
880
- add_action( 'ect_no_testimonial_found', 'essential_content_no_testimonial_found', 10 );
881
- endif;
882
-
883
-
884
- if ( ! function_exists( 'essential_content_testimonial_section_open' ) ) :
885
- /**
886
- * Open section
887
- *
888
- * @return html
889
- */
890
- function essential_content_testimonial_section_open( $layout = null ) {
891
- echo '<div class="ect-testimonial-content-section ect-section ' . $layout . '">';
892
- echo '<div class="ect-wrapper">';
893
- }
894
- endif;
895
- add_action( 'ect_before_testimonial_loop', 'essential_content_testimonial_section_open', 10, 1 );
896
-
897
-
898
- if ( ! function_exists( 'essential_content_testimonial_loop_start' ) ) :
899
- /**
900
- * open wrapper before loop
901
- *
902
- */
903
- function essential_content_testimonial_loop_start( $layout = null ) {
904
- echo '<div class="section-content-wrapper testimonial-content-wrapper">';
905
- }
906
- endif;
907
- add_action( 'ect_before_testimonial_loop', 'essential_content_testimonial_loop_start', 30 );
908
-
909
-
910
- if ( ! function_exists( 'essential_content_testimonial_slider_open' ) ) :
911
- function essential_content_testimonial_slider_open() {
912
- ?>
913
- <div class="cycle-slideshow"
914
- data-cycle-log="false"
915
- data-cycle-pause-on-hover="true"
916
- data-cycle-swipe="true"
917
- data-cycle-auto-height=container
918
- data-cycle-loader=false
919
- data-cycle-slides=".testimonial_slider_wrap"
920
- data-cycle-pager=".testimonial-slider-pager"
921
- data-cycle-prev=".testimonial-slider-prev"
922
- data-cycle-next=".testimonial-slider-next"
923
- >
924
-
925
- <div class="controller">
926
- <!-- prev/next links -->
927
- <button class="cycle-prev testimonial-slider-prev" aria-label="Previous">
928
- <span class="screen-reader-text"><?php esc_html_e( 'Previous Slide', 'essential-content-types-pro' ); ?></span><?php echo essential_content_get_svg( array( 'icon' => 'angle-down' ) ); ?>
929
- </button>
930
-
931
- <!-- empty element for pager links -->
932
- <div class="cycle-pager testimonial-slider-pager"></div>
933
-
934
- <button class="cycle-next testimonial-slider-next" aria-label="Next">
935
- <span class="screen-reader-text"><?php esc_html_e( 'Next Slide', 'essential-content-types-pro' ); ?></span><?php echo essential_content_get_svg( array( 'icon' => 'angle-down' ) ); ?>
936
- </button>
937
- </div><!-- .controller -->
938
-
939
- <div class="testimonial_slider_wrap">
940
-
941
- <?php
942
- }
943
- endif;
944
- //add_action( 'ect_before_testimonial_loop', 'essential_content_testimonial_slider_open', 40 );
945
-
946
-
947
- if ( ! function_exists( 'essential_content_testimonial_slider_close' ) ) :
948
- function essential_content_testimonial_slider_close() {
949
- echo '</div><!-- .testimonial_slider_wrap -->';
950
- echo '</div><!-- .cycle-slideshow -->';
951
- }
952
- endif;
953
- //add_action( 'ect_after_testimonial_loop', 'essential_content_testimonial_slider_close', 10 );
954
-
955
-
956
- if ( ! function_exists( 'essential_content_testimonial_loop_end' ) ) :
957
- /**
958
- * close wrapper after loop
959
- *
960
- */
961
- function essential_content_testimonial_loop_end() {
962
- echo '</div><!-- .testimonial-content-wrapper -->';
963
- }
964
- endif;
965
- add_action( 'ect_after_testimonial_loop', 'essential_content_testimonial_loop_end', 20 );
966
-
967
-
968
- if ( ! function_exists( 'essential_content_testimonial_section_close' ) ) :
969
- /**
970
- * Close section
971
- *
972
- * @return html
973
- */
974
- function essential_content_testimonial_section_close() {
975
- echo '</div><!-- .ect-wrapper -->';
976
- echo '</div><!-- .ect-section -->';
977
- }
978
- endif;
979
- add_action( 'ect_after_testimonial_loop', 'essential_content_testimonial_section_close', 30 );
980
-
981
-
982
- if ( ! function_exists( 'essential_content_get_svg' ) ) :
983
- /**
984
- * Return SVG markup.
985
- *
986
- * @param array $args {
987
- * Parameters needed to display an SVG.
988
- *
989
- * @type string $icon Required SVG icon filename.
990
- * @type string $title Optional SVG title.
991
- * @type string $desc Optional SVG description.
992
- * }
993
- * @return string SVG markup.
994
- */
995
- function essential_content_get_svg( $args = array() ) {
996
- // Make sure $args are an array.
997
- if ( empty( $args ) ) {
998
- return __( 'Please define default parameters in the form of an array.', 'starter-pro' );
999
- }
1000
-
1001
- // Define an icon.
1002
- if ( false === array_key_exists( 'icon', $args ) ) {
1003
- return __( 'Please define an SVG icon filename.', 'starter-pro' );
1004
- }
1005
-
1006
- // Set defaults.
1007
- $defaults = array(
1008
- 'icon' => '',
1009
- 'title' => '',
1010
- 'desc' => '',
1011
- 'fallback' => false,
1012
- );
1013
-
1014
- // Parse args.
1015
- $args = wp_parse_args( $args, $defaults );
1016
-
1017
- // Set aria hidden.
1018
- $aria_hidden = ' aria-hidden="true"';
1019
-
1020
- // Set ARIA.
1021
- $aria_labelledby = '';
1022
-
1023
- /*
1024
- * Starter doesn't use the SVG title or description attributes; non-decorative icons are described with .screen-reader-text.
1025
- *
1026
- * However, child themes can use the title and description to add information to non-decorative SVG icons to improve accessibility.
1027
- *
1028
- * Example 1 with title: <?php echo starter_get_svg( array( 'icon' => 'arrow-right', 'title' => __( 'This is the title', 'textdomain' ) ) ); ?>
1029
- *
1030
- * Example 2 with title and description: <?php echo starter_get_svg( array( 'icon' => 'arrow-right', 'title' => __( 'This is the title', 'textdomain' ), 'desc' => __( 'This is the description', 'textdomain' ) ) ); ?>
1031
- *
1032
- * See https://www.paciellogroup.com/blog/2013/12/using-aria-enhance-svg-accessibility/.
1033
- */
1034
- if ( $args['title'] ) {
1035
- $aria_hidden = '';
1036
- $unique_id = uniqid();
1037
- $aria_labelledby = ' aria-labelledby="title-' . $unique_id . '"';
1038
-
1039
- if ( $args['desc'] ) {
1040
- $aria_labelledby = ' aria-labelledby="title-' . $unique_id . ' desc-' . $unique_id . '"';
1041
- }
1042
- }
1043
-
1044
- // Begin SVG markup.
1045
- $svg = '<svg class="icon icon-' . esc_attr( $args['icon'] ) . '"' . $aria_hidden . $aria_labelledby . ' role="img">';
1046
-
1047
- // Display the title.
1048
- if ( $args['title'] ) {
1049
- $svg .= '<title id="title-' . $unique_id . '">' . esc_html( $args['title'] ) . '</title>';
1050
-
1051
- // Display the desc only if the title is already set.
1052
- if ( $args['desc'] ) {
1053
- $svg .= '<desc id="desc-' . $unique_id . '">' . esc_html( $args['desc'] ) . '</desc>';
1054
- }
1055
- }
1056
-
1057
- /*
1058
- * Display the icon.
1059
- *
1060
- * The whitespace around `<use>` is intentional - it is a work around to a keyboard navigation bug in Safari 10.
1061
- *
1062
- * See https://core.trac.wordpress.org/ticket/38387.
1063
- */
1064
- $svg .= ' <use href="#icon-' . esc_html( $args['icon'] ) . '" xlink:href="#icon-' . esc_html( $args['icon'] ) . '"></use> ';
1065
-
1066
- // Add some markup to use as a fallback for browsers that do not support SVGs.
1067
- if ( $args['fallback'] ) {
1068
- $svg .= '<span class="svg-fallback icon-' . esc_attr( $args['icon'] ) . '"></span>';
1069
- }
1070
-
1071
- $svg .= '</svg>';
1072
-
1073
- return $svg;
1074
- }
1075
- endif;
1
+ <?php
2
+
3
+ /**
4
+ * Support JetPack Testimonial
5
+ */
6
+ class Essential_Content_Jetpack_Testimonial {
7
+ const CUSTOM_POST_TYPE = 'jetpack-testimonial';
8
+ const OPTION_NAME = 'jetpack_testimonial';
9
+ const OPTION_READING_SETTING = 'jetpack_testimonial_posts_per_page';
10
+
11
+ public $version = ESSENTIAL_CONTENT_TYPES_VERSION;
12
+
13
+ static function init() {
14
+ static $instance = false;
15
+
16
+ if ( ! $instance ) {
17
+ $instance = new Essential_Content_Jetpack_Testimonial;
18
+ }
19
+
20
+ return $instance;
21
+ }
22
+
23
+ /**
24
+ * Conditionally hook into WordPress.
25
+ *
26
+ * Setup user option for enabling CPT.
27
+ * If user has CPT enabled, show in admin.
28
+ */
29
+ function __construct() {
30
+ // Make sure the post types are loaded for imports
31
+ add_action( 'import_start', array( $this, 'register_post_types' ) );
32
+
33
+ // If called via REST API, we need to register later in lifecycle
34
+ add_action( 'restapi_theme_init', array( $this, 'maybe_register_cpt' ) );
35
+
36
+ // Add to REST API post type whitelist
37
+ add_filter( 'rest_api_allowed_post_types', array( $this, 'allow_cpt_rest_api_type' ) );
38
+
39
+ $this->maybe_register_cpt();
40
+ }
41
+
42
+ /**
43
+ * Registers the custom post types and adds action/filter handlers, but
44
+ * only if the site supports it
45
+ */
46
+ function maybe_register_cpt() {
47
+ // Add an option to enable the CPT
48
+ add_action( 'admin_init', array( $this, 'settings_api_init' ) );
49
+
50
+ // Check on theme switch if theme supports CPT and setting is disabled
51
+ add_action( 'after_switch_theme', array( $this, 'activation_post_type_support' ) );
52
+
53
+ $setting = 1;
54
+
55
+ if ( class_exists( 'Jetpack_Options' ) ) {
56
+ $setting = Jetpack_Options::get_option_and_ensure_autoload( self::OPTION_NAME, '0' );
57
+ }
58
+
59
+ // Bail early if Testimonial option is not set and the theme doesn't declare support
60
+ if ( empty( $setting ) && ! $this->site_supports_custom_post_type() ) {
61
+ return;
62
+ }
63
+
64
+ // Enable Omnisearch for CPT.
65
+ if ( class_exists( 'Jetpack_Omnisearch_Posts' ) ) {
66
+ new Jetpack_Omnisearch_Posts( self::CUSTOM_POST_TYPE );
67
+ }
68
+
69
+ // CPT magic
70
+ $this->register_post_types();
71
+ add_action( sprintf( 'add_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 );
72
+ add_action( sprintf( 'update_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 );
73
+ add_action( sprintf( 'publish_%s', self::CUSTOM_POST_TYPE ), array( $this, 'flush_rules_on_first_testimonial' ) );
74
+ add_action( 'after_switch_theme', array( $this, 'flush_rules_on_switch' ) );
75
+
76
+ // Admin Customization
77
+ add_filter( 'enter_title_here', array( $this, 'change_default_title' ) );
78
+ add_filter( sprintf( 'manage_%s_posts_columns', self::CUSTOM_POST_TYPE ), array( $this, 'edit_title_column_label' ) );
79
+ add_filter( 'post_updated_messages', array( $this, 'updated_messages' ) );
80
+ add_action( 'customize_register', array( $this, 'customize_register' ) );
81
+
82
+ // Only add the 'Customize' sub-menu if the theme supports it.
83
+ $num_testimonials = self::count_testimonials();
84
+ if ( ! empty( $num_testimonials ) && current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
85
+ add_action( 'admin_menu', array( $this, 'add_customize_page' ) );
86
+ }
87
+
88
+ // Add to Jetpack XML sitemap
89
+ add_filter( 'jetpack_sitemap_post_types', array( $this, 'add_to_sitemap' ) );
90
+
91
+ // Adjust CPT archive and custom taxonomies to obey CPT reading setting
92
+ add_filter( 'pre_get_posts', array( $this, 'query_reading_setting' ), 20 );
93
+ add_filter( 'infinite_scroll_settings', array( $this, 'infinite_scroll_click_posts_per_page' ) );
94
+
95
+ // Register [jetpack_testimonials] always and
96
+ // register [testimonials] if [testimonials] isn't already set
97
+ add_shortcode( 'jetpack_testimonials', array( $this, 'jetpack_testimonial_shortcode' ) );
98
+
99
+ if ( ! shortcode_exists( 'testimonials' ) ) {
100
+ add_shortcode( 'testimonials', array( $this, 'jetpack_testimonial_shortcode' ) );
101
+ }
102
+
103
+ // If CPT was enabled programatically and no CPT items exist when user switches away, disable
104
+ if ( $setting && $this->site_supports_custom_post_type() ) {
105
+ add_action( 'switch_theme', array( $this, 'deactivation_post_type_support' ) );
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Add a checkbox field in 'Settings' > 'Writing'
111
+ * for enabling CPT functionality.
112
+ *
113
+ * @return null
114
+ */
115
+ function settings_api_init() {
116
+ add_settings_field(
117
+ self::OPTION_NAME,
118
+ '<span class="cpt-options">' . __( 'Testimonials', 'essential-content-types' ) . '</span>',
119
+ array( $this, 'setting_html' ),
120
+ 'writing',
121
+ 'jetpack_cpt_section'
122
+ );
123
+
124
+ register_setting(
125
+ 'writing',
126
+ self::OPTION_NAME,
127
+ 'intval'
128
+ );
129
+
130
+ // Check if CPT is enabled first so that intval doesn't get set to NULL on re-registering
131
+ if ( $this->site_supports_custom_post_type() ) {
132
+ register_setting(
133
+ 'writing',
134
+ self::OPTION_READING_SETTING,
135
+ 'intval'
136
+ );
137
+ }
138
+ }
139
+
140
+ /**
141
+ * HTML code to display a checkbox true/false option
142
+ * for the CPT setting.
143
+ *
144
+ * @return html
145
+ */
146
+ function setting_html() {
147
+ if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) : ?>
148
+ <p><?php printf( __( 'Your theme supports Testimonials', 'essential-content-types' ) ); ?></p>
149
+ <?php else : ?>
150
+ <label for="<?php echo esc_attr( self::OPTION_NAME ); ?>">
151
+ <input name="<?php echo esc_attr( self::OPTION_NAME ); ?>" id="<?php echo esc_attr( self::OPTION_NAME ); ?>" <?php echo checked( get_option( self::OPTION_NAME, '0' ), true, false ); ?> type="checkbox" value="1" />
152
+ <?php esc_html_e( 'Enable Testimonials for this site.', 'essential-content-types' ); ?>
153
+ <a target="_blank" href="http://en.support.wordpress.com/testimonials/"><?php esc_html_e( 'Learn More', 'essential-content-types' ); ?></a>
154
+ </label>
155
+ <?php
156
+ endif;
157
+
158
+ if ( $this->site_supports_custom_post_type() ) :
159
+ printf(
160
+ '<p><label for="%1$s">%2$s</label></p>',
161
+ esc_attr( self::OPTION_READING_SETTING ),
162
+ /* translators: %1$s is replaced with an input field for numbers */
163
+ sprintf(
164
+ __( 'Testimonial pages display at most %1$s testimonials', 'essential-content-types' ),
165
+ sprintf(
166
+ '<input name="%1$s" id="%1$s" type="number" step="1" min="1" value="%2$s" class="small-text" />',
167
+ esc_attr( self::OPTION_READING_SETTING ),
168
+ esc_attr( get_option( self::OPTION_READING_SETTING, '10' ) )
169
+ )
170
+ )
171
+ );
172
+ endif;
173
+ }
174
+
175
+ /**
176
+ * Should this Custom Post Type be made available?
177
+ */
178
+ function site_supports_custom_post_type() {
179
+ // If the current theme requests it.
180
+ if ( current_theme_supports( self::CUSTOM_POST_TYPE ) || get_option( self::OPTION_NAME, '0' ) ) {
181
+ return true;
182
+ }
183
+
184
+ // Otherwise, say no unless something wants to filter us to say yes.
185
+ /** This action is documented in modules/custom-post-types/nova.php */
186
+ return (bool) apply_filters( 'jetpack_enable_cpt', false, self::CUSTOM_POST_TYPE );
187
+ }
188
+
189
+ /**
190
+ * Add to REST API post type whitelist
191
+ */
192
+ function allow_cpt_rest_api_type( $post_types ) {
193
+ $post_types[] = self::CUSTOM_POST_TYPE;
194
+
195
+ return $post_types;
196
+ }
197
+
198
+
199
+ /*
200
+ * Flush permalinks when CPT option is turned on/off
201
+ */
202
+ function flush_rules_on_enable() {
203
+ flush_rewrite_rules();
204
+ }
205
+
206
+ /*
207
+ * Count published testimonials and flush permalinks when first testimonial is published
208
+ */
209
+ function flush_rules_on_first_testimonial() {
210
+ $testimonials = get_transient( 'jetpack-testimonial-count-cache' );
211
+
212
+ if ( false === $testimonials ) {
213
+ flush_rewrite_rules();
214
+ $testimonials = (int) wp_count_posts( self::CUSTOM_POST_TYPE )->publish;
215
+
216
+ if ( ! empty( $testimonials ) ) {
217
+ set_transient( 'jetpack-testimonial-count-cache', $testimonials, HOUR_IN_SECONDS * 12 );
218
+ }
219
+ }
220
+ }
221
+
222
+ /*
223
+ * Flush permalinks when CPT supported theme is activated
224
+ */
225
+ function flush_rules_on_switch() {
226
+ if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
227
+ flush_rewrite_rules();
228
+ }
229
+ }
230
+
231
+ /**
232
+ * On plugin/theme activation, check if current theme supports CPT
233
+ */
234
+ static function activation_post_type_support() {
235
+ if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
236
+ update_option( self::OPTION_NAME, '1' );
237
+ }
238
+ }
239
+
240
+ /**
241
+ * On theme switch, check if CPT item exists and disable if not
242
+ */
243
+ function deactivation_post_type_support() {
244
+ $testimonials = get_posts(
245
+ array(
246
+ 'fields' => 'ids',
247
+ 'posts_per_page' => 1,
248
+ 'post_type' => self::CUSTOM_POST_TYPE,
249
+ 'suppress_filters' => false,
250
+ )
251
+ );
252
+
253
+ if ( empty( $testimonials ) ) {
254
+ update_option( self::OPTION_NAME, '0' );
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Register Post Type
260
+ */
261
+ function register_post_types() {
262
+ if ( post_type_exists( self::CUSTOM_POST_TYPE ) ) {
263
+ return;
264
+ }
265
+
266
+ $args = array(
267
+ 'description' => __( 'Customer Testimonials', 'essential-content-types' ),
268
+ 'labels' => array(
269
+ 'name' => esc_html__( 'Testimonials', 'essential-content-types' ),
270
+ 'singular_name' => esc_html__( 'Testimonial', 'essential-content-types' ),
271
+ 'menu_name' => esc_html__( 'Testimonials', 'essential-content-types' ),
272
+ 'all_items' => esc_html__( 'All Testimonials', 'essential-content-types' ),
273
+ 'add_new' => esc_html__( 'Add New', 'essential-content-types' ),
274
+ 'add_new_item' => esc_html__( 'Add New Testimonial', 'essential-content-types' ),
275
+ 'edit_item' => esc_html__( 'Edit Testimonial', 'essential-content-types' ),
276
+ 'new_item' => esc_html__( 'New Testimonial', 'essential-content-types' ),
277
+ 'view_item' => esc_html__( 'View Testimonial', 'essential-content-types' ),
278
+ 'search_items' => esc_html__( 'Search Testimonials', 'essential-content-types' ),
279
+ 'not_found' => esc_html__( 'No Testimonials found', 'essential-content-types' ),
280
+ 'not_found_in_trash' => esc_html__( 'No Testimonials found in Trash', 'essential-content-types' ),
281
+ 'filter_items_list' => esc_html__( 'Filter Testimonials list', 'essential-content-types' ),
282
+ 'items_list_navigation' => esc_html__( 'Testimonial list navigation', 'essential-content-types' ),
283
+ 'items_list' => esc_html__( 'Testimonials list', 'essential-content-types' ),
284
+ ),
285
+ 'supports' => array(
286
+ 'title',
287
+ 'editor',
288
+ 'excerpt',
289
+ 'thumbnail',
290
+ 'page-attributes',
291
+ 'revisions',
292
+ ),
293
+ 'rewrite' => array(
294
+ 'slug' => 'testimonial',
295
+ 'with_front' => false,
296
+ 'feeds' => false,
297
+ 'pages' => true,
298
+ ),
299
+ 'public' => true,
300
+ 'show_ui' => true,
301
+ 'menu_position' => 20, // below Pages
302
+ 'menu_icon' => 'dashicons-testimonial',
303
+ 'capability_type' => 'page',
304
+ 'map_meta_cap' => true,
305
+ 'has_archive' => true,
306
+ 'query_var' => 'testimonial',
307
+ 'show_in_rest' => true,
308
+ );
309
+
310
+ $description = get_option( 'jetpack_testimonial_content' );
311
+ if ( '' !== $description ) {
312
+ $args['description'] = $description;
313
+ }
314
+
315
+ register_post_type( self::CUSTOM_POST_TYPE, $args );
316
+ }
317
+
318
+ /**
319
+ * Update messages for the Testimonial admin.
320
+ */
321
+ function updated_messages( $messages ) {
322
+ global $post;
323
+
324
+ $messages[ self::CUSTOM_POST_TYPE ] = array(
325
+ 0 => '', // Unused. Messages start at index 1.
326
+ 1 => sprintf( __( 'Testimonial updated. <a href="%s">View testimonial</a>', 'essential-content-types' ), esc_url( get_permalink( $post->ID ) ) ),
327
+ 2 => esc_html__( 'Custom field updated.', 'essential-content-types' ),
328
+ 3 => esc_html__( 'Custom field deleted.', 'essential-content-types' ),
329
+ 4 => esc_html__( 'Testimonial updated.', 'essential-content-types' ),
330
+ /* translators: %s: date and time of the revision */
331
+ 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Testimonial restored to revision from %s', 'essential-content-types' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
332
+ 6 => sprintf( __( 'Testimonial published. <a href="%s">View testimonial</a>', 'essential-content-types' ), esc_url( get_permalink( $post->ID ) ) ),
333
+ 7 => esc_html__( 'Testimonial saved.', 'essential-content-types' ),
334
+ 8 => sprintf( __( 'Testimonial submitted. <a target="_blank" href="%s">Preview testimonial</a>', 'essential-content-types' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
335
+ 9 => sprintf(
336
+ __( 'Testimonial scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview testimonial</a>', 'essential-content-types' ),
337
+ // translators: Publish box date format, see http://php.net/date
338
+ date_i18n( __( 'M j, Y @ G:i', 'essential-content-types' ), strtotime( $post->post_date ) ),
339
+ esc_url( get_permalink( $post->ID ) )
340
+ ),
341
+ 10 => sprintf( __( 'Testimonial draft updated. <a target="_blank" href="%s">Preview testimonial</a>', 'essential-content-types' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
342
+ );
343
+
344
+ return $messages;
345
+ }
346
+
347
+ /**
348
+ * Change ‘Enter Title Here’ text for the Testimonial.
349
+ */
350
+ function change_default_title( $title ) {
351
+ $screen = get_current_screen();
352
+
353
+ if ( self::CUSTOM_POST_TYPE == $screen->post_type ) {
354
+ $title = esc_html__( "Enter the customer's name here", 'essential-content-types' );
355
+ }
356
+
357
+ return $title;
358
+ }
359
+
360
+ /**
361
+ * Change ‘Title’ column label on all Testimonials page.
362
+ */
363
+ function edit_title_column_label( $columns ) {
364
+ $columns['title'] = esc_html__( 'Customer Name', 'essential-content-types' );
365
+
366
+ return $columns;
367
+ }
368
+
369
+ /**
370
+ * Follow CPT reading setting on CPT archive page
371
+ */
372
+ function query_reading_setting( $query ) {
373
+ if ( ! is_admin()
374
+ && $query->is_main_query()
375
+ && $query->is_post_type_archive( self::CUSTOM_POST_TYPE )
376
+ ) {
377
+ $post_per_page = ( null != get_option( 'posts_per_page' ) ) ? get_option( 'posts_per_page' ) : '10';
378
+ $query->set( 'posts_per_page', get_option( self::OPTION_READING_SETTING, $post_per_page ) );
379
+ }
380
+ }
381
+
382
+ /*
383
+ * If Infinite Scroll is set to 'click', use our custom reading setting instead of core's `posts_per_page`.
384
+ */
385
+ function infinite_scroll_click_posts_per_page( $settings ) {
386
+ global $wp_query;
387
+
388
+ if ( ! is_admin() && true === $settings['click_handle'] && $wp_query->is_post_type_archive( self::CUSTOM_POST_TYPE ) ) {
389
+ $settings['posts_per_page'] = get_option( self::OPTION_READING_SETTING, $settings['posts_per_page'] );
390
+ }
391
+
392
+ return $settings;
393
+ }
394
+
395
+ /**
396
+ * Add CPT to Dotcom sitemap
397
+ */
398
+ function add_to_sitemap( $post_types ) {
399
+ $post_types[] = self::CUSTOM_POST_TYPE;
400
+
401
+ return $post_types;
402
+ }
403
+
404
+ function set_testimonial_option() {
405
+ $testimonials = wp_count_posts( self::CUSTOM_POST_TYPE );
406
+ $published_testimonials = $testimonials->publish;
407
+
408
+ update_option( self::OPTION_NAME, $published_testimonials );
409
+ }
410
+
411
+ function count_testimonials() {
412
+ $testimonials = get_transient( 'jetpack-testimonial-count-cache' );
413
+
414
+ if ( false === $testimonials ) {
415
+ $testimonials = (int) wp_count_posts( self::CUSTOM_POST_TYPE )->publish;
416
+
417
+ if ( ! empty( $testimonials ) ) {
418
+ set_transient( 'jetpack-testimonial-count-cache', $testimonials, 60 * 60 * 12 );
419
+ }
420
+ }
421
+
422
+ return $testimonials;
423
+ }
424
+
425
+ /**
426
+ * Adds a submenu link to the Customizer.
427
+ */
428
+ function add_customize_page() {
429
+ add_submenu_page(
430
+ 'edit.php?post_type=' . self::CUSTOM_POST_TYPE,
431
+ esc_html__( 'Customize Testimonials Archive', 'essential-content-types' ),
432
+ esc_html__( 'Customize', 'essential-content-types' ),
433
+ 'edit_theme_options',
434
+ add_query_arg(
435
+ array(
436
+ 'url' => urlencode( home_url( '/testimonial/' ) ),
437
+ 'autofocus[section]' => 'jetpack_testimonials',
438
+ ),
439
+ 'customize.php'
440
+ )
441
+ );
442
+ }
443
+
444
+ /**
445
+ * Adds testimonial section to the Customizer.
446
+ */
447
+ function customize_register( $wp_customize ) {
448
+ essential_content_testimonial_custom_control_classes();
449
+
450
+ $wp_customize->add_panel(
451
+ 'ect_plugin_options',
452
+ array(
453
+ 'title' => esc_html__( 'Essential Content Types Plugin Options', 'essential-content-types' ),
454
+ 'priority' => 1,
455
+ )
456
+ );
457
+
458
+ $wp_customize->add_section(
459
+ 'jetpack_testimonials',
460
+ array(
461
+ 'title' => esc_html__( 'Testimonials', 'essential-content-types' ),
462
+ 'theme_supports' => self::CUSTOM_POST_TYPE,
463
+ 'priority' => 130,
464
+ 'panel' => 'ect_plugin_options',
465
+ )
466
+ );
467
+
468
+ $wp_customize->add_setting(
469
+ 'jetpack_testimonial_title',
470
+ array(
471
+ 'default' => esc_html__( 'Testimonials', 'essential-content-types' ),
472
+ 'type' => 'option',
473
+ 'sanitize_callback' => 'sanitize_text_field',
474
+ 'sanitize_js_callback' => 'sanitize_text_field',
475
+ )
476
+ );
477
+ $wp_customize->add_control(
478
+ 'jetpack_testimonial_title',
479
+ array(
480
+ 'section' => 'jetpack_testimonials',
481
+ 'label' => esc_html__( 'Testimonial Archive Title', 'essential-content-types' ),
482
+ 'type' => 'text',
483
+ )
484
+ );
485
+
486
+ $wp_customize->add_setting(
487
+ 'jetpack_testimonial_content',
488
+ array(
489
+ 'default' => '',
490
+ 'type' => 'option',
491
+ 'sanitize_callback' => 'wp_kses_post',
492
+ 'sanitize_js_callback' => 'wp_kses_post',
493
+ )
494
+ );
495
+ $wp_customize->add_control(
496
+ new Essential_Content_Jetpack_Testimonial_Textarea_Control(
497
+ $wp_customize,
498
+ 'jetpack_testimonial_content',
499
+ array(
500
+ 'section' => 'jetpack_testimonials',
501
+ 'label' => esc_html__( 'Testimonial Archive Content', 'essential-content-types' ),
502
+ 'type' => 'textarea',
503
+ )
504
+ )
505
+ );
506
+
507
+ $wp_customize->add_setting(
508
+ 'jetpack_testimonial_featured_image',
509
+ array(
510
+ 'default' => '',
511
+ 'type' => 'option',
512
+ 'sanitize_callback' => 'attachment_url_to_postid',
513
+ 'sanitize_js_callback' => 'attachment_url_to_postid',
514
+ 'theme_supports' => 'post-thumbnails',
515
+ )
516
+ );
517
+ $wp_customize->add_control(
518
+ new WP_Customize_Image_Control(
519
+ $wp_customize,
520
+ 'jetpack_testimonial_featured_image',
521
+ array(
522
+ 'section' => 'jetpack_testimonials',
523
+ 'label' => esc_html__( 'Testimonial Archive Featured Image', 'essential-content-types' ),
524
+ )
525
+ )
526
+ );
527
+
528
+ // The featured image control doesn't display properly in the Customizer unless we coerce
529
+ // it back into a URL sooner, since that's what WP_Customize_Upload_Control::to_json() expects
530
+ if ( is_admin() ) {
531
+ add_filter( 'theme_mod_jetpack_testimonials', array( $this, 'coerce_testimonial_image_to_url' ) );
532
+ }
533
+ }
534
+
535
+ public function coerce_testimonial_image_to_url( $opt ) {
536
+ if ( ! $opt || ! is_array( $opt ) ) {
537
+ return $opt;
538
+ }
539
+ if ( ! isset( $opt['featured-image'] ) || ! is_scalar( $opt['featured-image'] ) ) {
540
+ return $opt;
541
+ }
542
+ $url = wp_get_attachment_url( $opt['featured-image'] );
543
+ if ( $url ) {
544
+ $opt['featured-image'] = $url;
545
+ }
546
+ return $opt;
547
+ }
548
+
549
+ /**
550
+ * Our [testimonial] shortcode.
551
+ * Prints Testimonial data styled to look good on *any* theme.
552
+ *
553
+ * @return jetpack_testimonial_shortcode_html
554
+ */
555
+ static function jetpack_testimonial_shortcode( $atts ) {
556
+ // Default attributes
557
+ $atts = shortcode_atts(
558
+ array(
559
+ 'display_content' => true,
560
+ 'image' => true,
561
+ 'columns' => 1,
562
+ 'showposts' => -1,
563
+ 'order' => 'asc',
564
+ 'orderby' => 'date',
565
+ ),
566
+ $atts,
567
+ 'testimonial'
568
+ );
569
+
570
+ // A little sanitization
571
+ if ( $atts['display_content'] && 'true' != $atts['display_content'] && 'full' != $atts['display_content'] ) {
572
+ $atts['display_content'] = false;
573
+ }
574
+
575
+ if ( $atts['image'] && 'true' != $atts['image'] ) {
576
+ $atts['image'] = false;
577
+ }
578
+
579
+ // Check if column value is set to valid numbers or else set default value as 1
580
+ if ( 1 == $atts['columns'] || 2 == $atts['columns'] ) {
581
+ $atts['columns'] = absint( $atts['columns'] );
582
+ } else {
583
+ $atts['columns'] = 1;
584
+ }
585
+
586
+ $atts['showposts'] = intval( $atts['showposts'] );
587
+
588
+ if ( $atts['order'] ) {
589
+ $atts['order'] = urldecode( $atts['order'] );
590
+ $atts['order'] = strtoupper( $atts['order'] );
591
+ if ( 'DESC' != $atts['order'] ) {
592
+ $atts['order'] = 'ASC';
593
+ }
594
+ }
595
+
596
+ if ( $atts['orderby'] ) {
597
+ $atts['orderby'] = urldecode( $atts['orderby'] );
598
+ $atts['orderby'] = strtolower( $atts['orderby'] );
599
+ $allowed_keys = array( 'author', 'date', 'title', 'rand' );
600
+
601
+ $parsed = array();
602
+ foreach ( explode( ',', $atts['orderby'] ) as $testimonial_index_number => $orderby ) {
603
+ if ( ! in_array( $orderby, $allowed_keys ) ) {
604
+ continue;
605
+ }
606
+ $parsed[] = $orderby;
607
+ }
608
+
609
+ if ( empty( $parsed ) ) {
610
+ unset( $atts['orderby'] );
611
+ } else {
612
+ $atts['orderby'] = implode( ' ', $parsed );
613
+ }
614
+ }
615
+
616
+ // enqueue shortcode styles when shortcode is used
617
+ wp_enqueue_style( 'jetpack-testimonial-style', plugins_url( 'css/testimonial-shortcode.css', __FILE__ ), array(), '20140326' );
618
+
619
+ return self::jetpack_testimonial_shortcode_html( $atts );
620
+ }
621
+
622
+ /**
623
+ * The Testimonial shortcode loop.
624
+ *
625
+ * @todo add theme color styles
626
+ * @return html
627
+ */
628
+
629
+ static function jetpack_testimonial_shortcode_html( $atts ) {
630
+
631
+ // Default query arguments
632
+ $defaults = array(
633
+ 'order' => $atts['order'],
634
+ 'orderby' => $atts['orderby'],
635
+ 'posts_per_page' => $atts['showposts'],
636
+ );
637
+
638
+ $args = wp_parse_args( $atts, $defaults );
639
+ $args['post_type'] = self::CUSTOM_POST_TYPE; // Force this post type
640
+ $query = new WP_Query( $args );
641
+
642
+ ob_start();
643
+
644
+ // If we have posts, create the html
645
+ // with testimonial markup
646
+ if ( $query->have_posts() ) {
647
+
648
+ /**
649
+ * Hook: ect_before_testimonial_loop.
650
+ *
651
+ * @hooked
652
+ */
653
+ $layout = ect_get_layout();
654
+ do_action( 'ect_before_testimonial_loop', $layout[ $atts['columns'] ] );
655
+ ?>
656
+ <?php
657
+ while ( $query->have_posts() ) {
658
+
659
+ $query->the_post();
660
+ ect_get_template_part( 'content', 'testimonial', $atts );
661
+
662
+ }
663
+ wp_reset_postdata();
664
+ ?>
665
+ <?php
666
+
667
+ /**
668
+ * Hook: ect_after_testimonial_loop.
669
+ *
670
+ * @hooked
671
+ */
672
+ do_action( 'ect_after_testimonial_loop' );
673
+
674
+ } else {
675
+ /**
676
+ * Hook: ect_no_testimonial_found.
677
+ *
678
+ * @hooked ect_no_testimonial_found
679
+ */
680
+ do_action( 'ect_no_testimonial_found' );
681
+ }
682
+
683
+ $html = ob_get_clean();
684
+
685
+ // If there is a [testimonials] within a [testimonials], remove the shortcode
686
+ if ( has_shortcode( $html, 'testimonials' ) ) {
687
+ remove_shortcode( 'testimonials' );
688
+ }
689
+
690
+ // Return the HTML block
691
+ return $html;
692
+ }
693
+ }
694
+ add_action( 'init', array( 'Essential_Content_Jetpack_Testimonial', 'init' ) );
695
+
696
+
697
+ function essential_content_testimonial_custom_control_classes() {
698
+ class Essential_Content_Jetpack_Testimonial_Title_Control extends WP_Customize_Control {
699
+ public static function sanitize_content( $value ) {
700
+ if ( '' != $value ) {
701
+ $value = trim( convert_chars( wptexturize( $value ) ) );
702
+ }
703
+
704
+ return $value;
705
+ }
706
+ }
707
+
708
+ class Essential_Content_Jetpack_Testimonial_Textarea_Control extends WP_Customize_Control {
709
+ public $type = 'textarea';
710
+
711
+ public function render_content() {
712
+ ?>
713
+ <label>
714
+ <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
715
+ <textarea rows="5" style="width:100%;" <?php $this->link(); ?>><?php echo esc_textarea( $this->value() ); ?></textarea>
716
+ </label>
717
+ <?php
718
+ }
719
+
720
+ public static function sanitize_content( $value ) {
721
+ if ( ! empty( $value ) ) {
722
+ /** This filter is already documented in core. wp-includes/post-template.php */
723
+ $value = apply_filters( 'the_content', $value );
724
+ }
725
+
726
+ $value = preg_replace( '@<div id="jp-post-flair"([^>]+)?>(.+)?</div>@is', '', $value ); // Strip WPCOM and Jetpack post flair if included in content
727
+
728
+ return $value;
729
+ }
730
+ }
731
+ }
732
+
733
+ /**
734
+ * Add Testimonial support
735
+ */
736
+ function essential_content_testimonial_support() {
737
+ /*
738
+ * Adding theme support for Jetpack Testimonial CPT.
739
+ */
740
+ add_theme_support( 'jetpack-testimonial' );
741
+ }
742
+ add_action( 'after_setup_theme', 'essential_content_testimonial_support' );
743
+
744
+
745
+ /**
746
+ * Class to Renders and save metabox options
747
+ *
748
+ */
749
+ class Essential_Content_Jetpack_Testimonial_Metabox {
750
+ private $meta_box;
751
+
752
+ private $fields;
753
+
754
+ /**
755
+ * Constructor
756
+ *
757
+ *
758
+ * @access public
759
+ *
760
+ */
761
+ public function __construct( $meta_box_id, $meta_box_title, $post_type ) {
762
+
763
+ $this->meta_box = array(
764
+ 'id' => $meta_box_id,
765
+ 'title' => $meta_box_title,
766
+ 'post_type' => $post_type,
767
+ );
768
+
769
+ $this->fields = array(
770
+ 'ect-testimonials',
771
+ );
772
+
773
+ // Add metaboxes
774
+ add_action( 'add_meta_boxes', array( $this, 'add' ) );
775
+
776
+ add_action( 'save_post', array( $this, 'save' ) );
777
+ }
778
+
779
+ /**
780
+ * Add Meta Box for multiple post types.
781
+ *
782
+ *
783
+ * @access public
784
+ */
785
+ public function add( $postType ) {
786
+ if ( in_array( $postType, $this->meta_box['post_type'] ) ) {
787
+ add_meta_box( $this->meta_box['id'], $this->meta_box['title'], array( $this, 'show' ), null, 'side', 'high' );
788
+ }
789
+ }
790
+
791
+ /**
792
+ * Renders metabox
793
+ *
794
+ *
795
+ * @access public
796
+ */
797
+ public function show() {
798
+ global $post;
799
+
800
+ // Use nonce for verification
801
+ wp_nonce_field( basename( __FILE__ ), 'ect_custom_meta_box_nonce' );
802
+
803
+ $position = get_post_meta( $post->ID, 'ect_testimonial_position', true );
804
+
805
+ // Begin the form
806
+ ?>
807
+ <p>
808
+ <label for="ect_testimonial_position"><?php esc_html_e( 'Position', 'essential-content-types' ); ?></label>
809
+ <input type="text" class="widefat ect-testimonial-position" name="ect_testimonial_position" value="<?php echo esc_attr( $position ); ?>" />
810
+ </p>
811
+ <?php
812
+ }
813
+
814
+ /**
815
+ * Save custom metabox data
816
+ *
817
+ * @action save_post
818
+ *
819
+ * @access public
820
+ */
821
+ public function save( $post_id ) {
822
+ global $post_type;
823
+
824
+ $post_type_object = get_post_type_object( $post_type );
825
+
826
+ if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) // Check Autosave
827
+ || ( ! isset( $_POST['post_ID'] ) || $post_id != $_POST['post_ID'] ) // Check Revision
828
+ || ( ! in_array( $post_type, $this->meta_box['post_type'] ) ) // Check if current post type is supported.
829
+ || ( ! check_admin_referer( basename( __FILE__ ), 'ect_custom_meta_box_nonce' ) ) // Check nonce - Security
830
+ || ( ! current_user_can( $post_type_object->cap->edit_post, $post_id ) ) ) { // Check permission
831
+ return $post_id;
832
+ }
833
+
834
+ if ( ! update_post_meta( $post_id, 'ect_testimonial_position', sanitize_text_field( $_POST['ect_testimonial_position'] ) ) ) {
835
+ add_post_meta( $post_id, 'ect_testimonial_position', sanitize_text_field( $_POST['ect_testimonial_position'] ), true );
836
+ }
837
+ }
838
+ }
839
+
840
+ $ect_metabox = new Essential_Content_Jetpack_Testimonial_Metabox(
841
+ 'ect-options', //metabox id
842
+ esc_html__( 'Testmonial Options', 'essential-content-types' ), //metabox title
843
+ array( 'jetpack-testimonial' ) //metabox post types
844
+ );
845
+
846
+
847
+ if ( ! function_exists( 'essential_content_get_testimonial_thumbnail_link' ) ) :
848
+ /**
849
+ * Display the featured image if it's available
850
+ *
851
+ * @return html
852
+ */
853
+ function essential_content_get_testimonial_thumbnail_link( $post_id, $size ) {
854
+ if ( has_post_thumbnail( $post_id ) ) {
855
+ /**
856
+ * Change the Testimonial thumbnail size.
857
+ *
858
+ * @module custom-content-types
859
+ *
860
+ * @since 3.4.0
861
+ *
862
+ * @param string|array $var Either a registered size keyword or size array.
863
+ */
864
+ //return '<a class="testimonial-featured-image testimonial-thumbnail post-thumbnail" href="' . esc_url( get_permalink( $post_id ) ) . '">' . get_the_post_thumbnail( $post_id, apply_filters( 'testimonial_thumbnail_size', $size ) ) . '</a>';
865
+ return '<div class="testimonial-featured-image testimonial-thumbnail post-thumbnail">' . get_the_post_thumbnail( $post_id, apply_filters( 'testimonial_thumbnail_size', $size ) ) . '</div>';
866
+ }
867
+ }
868
+ endif;
869
+
870
+
871
+ if ( ! function_exists( 'essential_content_no_testimonial_found' ) ) :
872
+ /**
873
+ * No items found text
874
+ *
875
+ * @return html
876
+ */
877
+ function essential_content_no_testimonial_found() {
878
+ echo '<p><em>' . esc_html__( 'Your Testimonial Archive currently has no entries. You can start creating them on your dashboard.', 'essential-content-types-pro' ) . '</em></p>';
879
+ }
880
+ add_action( 'ect_no_testimonial_found', 'essential_content_no_testimonial_found', 10 );
881
+ endif;
882
+
883
+
884
+ if ( ! function_exists( 'essential_content_testimonial_section_open' ) ) :
885
+ /**
886
+ * Open section
887
+ *
888
+ * @return html
889
+ */
890
+ function essential_content_testimonial_section_open( $layout = null ) {
891
+ echo '<div class="ect-testimonial-content-section ect-section ' . $layout . '">';
892
+ echo '<div class="ect-wrapper">';
893
+ }
894
+ endif;
895
+ add_action( 'ect_before_testimonial_loop', 'essential_content_testimonial_section_open', 10, 1 );
896
+
897
+
898
+ if ( ! function_exists( 'essential_content_testimonial_loop_start' ) ) :
899
+ /**
900
+ * open wrapper before loop
901
+ *
902
+ */
903
+ function essential_content_testimonial_loop_start( $layout = null ) {
904
+ echo '<div class="section-content-wrapper testimonial-content-wrapper">';
905
+ }
906
+ endif;
907
+ add_action( 'ect_before_testimonial_loop', 'essential_content_testimonial_loop_start', 30 );
908
+
909
+
910
+ if ( ! function_exists( 'essential_content_testimonial_slider_open' ) ) :
911
+ function essential_content_testimonial_slider_open() {
912
+ ?>
913
+ <div class="cycle-slideshow"
914
+ data-cycle-log="false"
915
+ data-cycle-pause-on-hover="true"
916
+ data-cycle-swipe="true"
917
+ data-cycle-auto-height=container
918
+ data-cycle-loader=false
919
+ data-cycle-slides=".testimonial_slider_wrap"
920
+ data-cycle-pager=".testimonial-slider-pager"
921
+ data-cycle-prev=".testimonial-slider-prev"
922
+ data-cycle-next=".testimonial-slider-next"
923
+ >
924
+
925
+ <div class="controller">
926
+ <!-- prev/next links -->
927
+ <button class="cycle-prev testimonial-slider-prev" aria-label="Previous">
928
+ <span class="screen-reader-text"><?php esc_html_e( 'Previous Slide', 'essential-content-types-pro' ); ?></span><?php echo essential_content_get_svg( array( 'icon' => 'angle-down' ) ); ?>
929
+ </button>
930
+
931
+ <!-- empty element for pager links -->
932
+ <div class="cycle-pager testimonial-slider-pager"></div>
933
+
934
+ <button class="cycle-next testimonial-slider-next" aria-label="Next">
935
+ <span class="screen-reader-text"><?php esc_html_e( 'Next Slide', 'essential-content-types-pro' ); ?></span><?php echo essential_content_get_svg( array( 'icon' => 'angle-down' ) ); ?>
936
+ </button>
937
+ </div><!-- .controller -->
938
+
939
+ <div class="testimonial_slider_wrap">
940
+
941
+ <?php
942
+ }
943
+ endif;
944
+ //add_action( 'ect_before_testimonial_loop', 'essential_content_testimonial_slider_open', 40 );
945
+
946
+
947
+ if ( ! function_exists( 'essential_content_testimonial_slider_close' ) ) :
948
+ function essential_content_testimonial_slider_close() {
949
+ echo '</div><!-- .testimonial_slider_wrap -->';
950
+ echo '</div><!-- .cycle-slideshow -->';
951
+ }
952
+ endif;
953
+ //add_action( 'ect_after_testimonial_loop', 'essential_content_testimonial_slider_close', 10 );
954
+
955
+
956
+ if ( ! function_exists( 'essential_content_testimonial_loop_end' ) ) :
957
+ /**
958
+ * close wrapper after loop
959
+ *
960
+ */
961
+ function essential_content_testimonial_loop_end() {
962
+ echo '</div><!-- .testimonial-content-wrapper -->';
963
+ }
964
+ endif;
965
+ add_action( 'ect_after_testimonial_loop', 'essential_content_testimonial_loop_end', 20 );
966
+
967
+
968
+ if ( ! function_exists( 'essential_content_testimonial_section_close' ) ) :
969
+ /**
970
+ * Close section
971
+ *
972
+ * @return html
973
+ */
974
+ function essential_content_testimonial_section_close() {
975
+ echo '</div><!-- .ect-wrapper -->';
976
+ echo '</div><!-- .ect-section -->';
977
+ }
978
+ endif;
979
+ add_action( 'ect_after_testimonial_loop', 'essential_content_testimonial_section_close', 30 );
980
+
981
+
982
+ if ( ! function_exists( 'essential_content_get_svg' ) ) :
983
+ /**
984
+ * Return SVG markup.
985
+ *
986
+ * @param array $args {
987
+ * Parameters needed to display an SVG.
988
+ *
989
+ * @type string $icon Required SVG icon filename.
990
+ * @type string $title Optional SVG title.
991
+ * @type string $desc Optional SVG description.
992
+ * }
993
+ * @return string SVG markup.
994
+ */
995
+ function essential_content_get_svg( $args = array() ) {
996
+ // Make sure $args are an array.
997
+ if ( empty( $args ) ) {
998
+ return __( 'Please define default parameters in the form of an array.', 'starter-pro' );
999
+ }
1000
+
1001
+ // Define an icon.
1002
+ if ( false === array_key_exists( 'icon', $args ) ) {
1003
+ return __( 'Please define an SVG icon filename.', 'starter-pro' );
1004
+ }
1005
+
1006
+ // Set defaults.
1007
+ $defaults = array(
1008
+ 'icon' => '',
1009
+ 'title' => '',
1010
+ 'desc' => '',
1011
+ 'fallback' => false,
1012
+ );
1013
+
1014
+ // Parse args.
1015
+ $args = wp_parse_args( $args, $defaults );
1016
+
1017
+ // Set aria hidden.
1018
+ $aria_hidden = ' aria-hidden="true"';
1019
+
1020
+ // Set ARIA.
1021
+ $aria_labelledby = '';
1022
+
1023
+ /*
1024
+ * Starter doesn't use the SVG title or description attributes; non-decorative icons are described with .screen-reader-text.
1025
+ *
1026
+ * However, child themes can use the title and description to add information to non-decorative SVG icons to improve accessibility.
1027
+ *
1028
+ * Example 1 with title: <?php echo starter_get_svg( array( 'icon' => 'arrow-right', 'title' => __( 'This is the title', 'textdomain' ) ) ); ?>
1029
+ *
1030
+ * Example 2 with title and description: <?php echo starter_get_svg( array( 'icon' => 'arrow-right', 'title' => __( 'This is the title', 'textdomain' ), 'desc' => __( 'This is the description', 'textdomain' ) ) ); ?>
1031
+ *
1032
+ * See https://www.paciellogroup.com/blog/2013/12/using-aria-enhance-svg-accessibility/.
1033
+ */
1034
+ if ( $args['title'] ) {
1035
+ $aria_hidden = '';
1036
+ $unique_id = uniqid();
1037
+ $aria_labelledby = ' aria-labelledby="title-' . $unique_id . '"';
1038
+
1039
+ if ( $args['desc'] ) {
1040
+ $aria_labelledby = ' aria-labelledby="title-' . $unique_id . ' desc-' . $unique_id . '"';
1041
+ }
1042
+ }
1043
+
1044
+ // Begin SVG markup.
1045
+ $svg = '<svg class="icon icon-' . esc_attr( $args['icon'] ) . '"' . $aria_hidden . $aria_labelledby . ' role="img">';
1046
+
1047
+ // Display the title.
1048
+ if ( $args['title'] ) {
1049
+ $svg .= '<title id="title-' . $unique_id . '">' . esc_html( $args['title'] ) . '</title>';
1050
+
1051
+ // Display the desc only if the title is already set.
1052
+ if ( $args['desc'] ) {
1053
+ $svg .= '<desc id="desc-' . $unique_id . '">' . esc_html( $args['desc'] ) . '</desc>';
1054
+ }
1055
+ }
1056
+
1057
+ /*
1058
+ * Display the icon.
1059
+ *
1060
+ * The whitespace around `<use>` is intentional - it is a work around to a keyboard navigation bug in Safari 10.
1061
+ *
1062
+ * See https://core.trac.wordpress.org/ticket/38387.
1063
+ */
1064
+ $svg .= ' <use href="#icon-' . esc_html( $args['icon'] ) . '" xlink:href="#icon-' . esc_html( $args['icon'] ) . '"></use> ';
1065
+
1066
+ // Add some markup to use as a fallback for browsers that do not support SVGs.
1067
+ if ( $args['fallback'] ) {
1068
+ $svg .= '<span class="svg-fallback icon-' . esc_attr( $args['icon'] ) . '"></span>';
1069
+ }
1070
+
1071
+ $svg .= '</svg>';
1072
+
1073
+ return $svg;
1074
+ }
1075
+ endif;
admin/css/food-menu-shortcode.css CHANGED
@@ -1,160 +1,160 @@
1
- /*--------------------------------------------------------------
2
- # Food Menu
3
- --------------------------------------------------------------*/
4
-
5
- /*--------------------------------------------------------------
6
- # Element
7
- --------------------------------------------------------------*/
8
- html {
9
- -webkit-box-sizing: border-box;
10
- -moz-box-sizing: border-box;
11
- box-sizing: border-box;
12
- }
13
-
14
- *,
15
- *:before,
16
- *:after {
17
- /* Inherit box-sizing to make it easier to change the property for components that leverage other behavior; see http://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/ */
18
- -webkit-box-sizing: inherit;
19
- -moz-box-sizing: inherit;
20
- box-sizing: inherit;
21
- }
22
-
23
- .menu-content-wrapper {
24
- max-width: 1080px;
25
- margin: 0 auto;
26
- }
27
-
28
- .menu-content-wrapper .ui-nav-collapse {
29
- margin: 0;
30
- margin-bottom: 21px;
31
- }
32
-
33
-
34
- .menu-content-wrapper .ui-nav-collapse,
35
- .menu-content-wrapper .ui-tabs-anchor {
36
- display: block;
37
- font-size: 20px;
38
- font-size: 1.25rem;
39
- font-weight: 500;
40
- text-transform: capitalize;
41
- }
42
-
43
- .menu-content-wrapper .tabs-nav,
44
- .menu-content-wrapper .ui-tabs-panel {
45
- display: none;
46
- }
47
-
48
- .menu-content-wrapper .ui-tabs-panel.active-tab {
49
- display: block;
50
- }
51
-
52
- .menu-content-wrapper .hentry {
53
- margin-bottom: 21px;
54
- }
55
-
56
- .menu-content-wrapper .hentry-inner {
57
- border-bottom: 3px solid #eee;
58
- padding-bottom: 21px;
59
- }
60
-
61
- .menu-content-wrapper .entry-container {
62
- display: table;
63
- position: relative;
64
- table-layout: fixed;
65
- width: 100%;
66
- }
67
-
68
- .menu-content-wrapper .entry-description, .entry-price {
69
- display: table-cell;
70
- vertical-align: top;
71
- }
72
-
73
- .menu-content-wrapper .entry-description {
74
- width: 70%;
75
- }
76
-
77
- .entry-price {
78
- padding-top: 4px;
79
- width: 30%;
80
- text-align: right;
81
- }
82
-
83
- .menu-group-header {
84
- display: none;
85
- }
86
-
87
- /*--------------------------------------------------------------
88
- # >= 667px
89
- --------------------------------------------------------------*/
90
-
91
- @media screen and (min-width: 41.6875em) {
92
-
93
- /*menu-content-wrapper*/
94
- .menu-content-wrapper .tabs-nav {
95
- display: block;
96
- margin-bottom: 35px;
97
- }
98
-
99
- .menu-content-wrapper .ui-tabs-nav {
100
- margin: 0;
101
- padding: 0;
102
- text-align: center;
103
- }
104
-
105
- .menu-content-wrapper .ui-tabs-nav li {
106
- display: inline-block;
107
- list-style: none;
108
- }
109
-
110
- .menu-content-wrapper .ui-tabs-anchor {
111
- display: inline-block;
112
- padding: 14px 25px;
113
- }
114
-
115
- .menu-content-wrapper .ui-nav-collapse {
116
- display: none;
117
- }
118
-
119
- .menu-content-wrapper .hentry-inner {
120
- padding-bottom: 28px;
121
- }
122
-
123
- .entry-price {
124
- font-size: 18px;
125
- font-size: 1.125rem;
126
- }
127
- }
128
-
129
- .menu-content-wrapper a {
130
- text-decoration: none;
131
- }
132
-
133
- .menu-content-wrapper .entry-title,
134
- .menu-content-wrapper .entry-header {
135
- margin-bottom: 0;
136
- padding: 0;
137
- }
138
-
139
- .menu-content-wrapper .entry-title {
140
- margin-bottom: 7px;
141
- }
142
-
143
- body.ect-post .content-area .singular-content-wrap .menu-content-wrapper .entry-content {
144
- padding-top: 0;
145
- margin-top: 21px;
146
- }
147
-
148
- .ect-post .menu-content-wrapper .more-button {
149
- text-align: left;
150
- }
151
-
152
- .ect-post .singular-content-wrap .entry-content .entry-header,
153
- .ect-post .entry-content,
154
- .ect-post .ect-menu .menu-content-wrapper .entry-header {
155
- text-align: left;
156
- }
157
-
158
- .ect-post .menu-content-wrapper .ect_food_menu_item .entry-header .entry-title {
159
- font-size: 24px;
160
  }
1
+ /*--------------------------------------------------------------
2
+ # Food Menu
3
+ --------------------------------------------------------------*/
4
+
5
+ /*--------------------------------------------------------------
6
+ # Element
7
+ --------------------------------------------------------------*/
8
+ html {
9
+ -webkit-box-sizing: border-box;
10
+ -moz-box-sizing: border-box;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ *,
15
+ *:before,
16
+ *:after {
17
+ /* Inherit box-sizing to make it easier to change the property for components that leverage other behavior; see http://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/ */
18
+ -webkit-box-sizing: inherit;
19
+ -moz-box-sizing: inherit;
20
+ box-sizing: inherit;
21
+ }
22
+
23
+ .menu-content-wrapper {
24
+ max-width: 1080px;
25
+ margin: 0 auto;
26
+ }
27
+
28
+ .menu-content-wrapper .ui-nav-collapse {
29
+ margin: 0;
30
+ margin-bottom: 21px;
31
+ }
32
+
33
+
34
+ .menu-content-wrapper .ui-nav-collapse,
35
+ .menu-content-wrapper .ui-tabs-anchor {
36
+ display: block;
37
+ font-size: 20px;
38
+ font-size: 1.25rem;
39
+ font-weight: 500;
40
+ text-transform: capitalize;
41
+ }
42
+
43
+ .menu-content-wrapper .tabs-nav,
44
+ .menu-content-wrapper .ui-tabs-panel {
45
+ display: none;
46
+ }
47
+
48
+ .menu-content-wrapper .ui-tabs-panel.active-tab {
49
+ display: block;
50
+ }
51
+
52
+ .menu-content-wrapper .hentry {
53
+ margin-bottom: 21px;
54
+ }
55
+
56
+ .menu-content-wrapper .hentry-inner {
57
+ border-bottom: 3px solid #eee;
58
+ padding-bottom: 21px;
59
+ }
60
+
61
+ .menu-content-wrapper .entry-container {
62
+ display: table;
63
+ position: relative;
64
+ table-layout: fixed;
65
+ width: 100%;
66
+ }
67
+
68
+ .menu-content-wrapper .entry-description, .entry-price {
69
+ display: table-cell;
70
+ vertical-align: top;
71
+ }
72
+
73
+ .menu-content-wrapper .entry-description {
74
+ width: 70%;
75
+ }
76
+
77
+ .entry-price {
78
+ padding-top: 4px;
79
+ width: 30%;
80
+ text-align: right;
81
+ }
82
+
83
+ .menu-group-header {
84
+ display: none;
85
+ }
86
+
87
+ /*--------------------------------------------------------------
88
+ # >= 667px
89
+ --------------------------------------------------------------*/
90
+
91
+ @media screen and (min-width: 41.6875em) {
92
+
93
+ /*menu-content-wrapper*/
94
+ .menu-content-wrapper .tabs-nav {
95
+ display: block;
96
+ margin-bottom: 35px;
97
+ }
98
+
99
+ .menu-content-wrapper .ui-tabs-nav {
100
+ margin: 0;
101
+ padding: 0;
102
+ text-align: center;
103
+ }
104
+
105
+ .menu-content-wrapper .ui-tabs-nav li {
106
+ display: inline-block;
107
+ list-style: none;
108
+ }
109
+
110
+ .menu-content-wrapper .ui-tabs-anchor {
111
+ display: inline-block;
112
+ padding: 14px 25px;
113
+ }
114
+
115
+ .menu-content-wrapper .ui-nav-collapse {
116
+ display: none;
117
+ }
118
+
119
+ .menu-content-wrapper .hentry-inner {
120
+ padding-bottom: 28px;
121
+ }
122
+
123
+ .entry-price {
124
+ font-size: 18px;
125
+ font-size: 1.125rem;
126
+ }
127
+ }
128
+
129
+ .menu-content-wrapper a {
130
+ text-decoration: none;
131
+ }
132
+
133
+ .menu-content-wrapper .entry-title,
134
+ .menu-content-wrapper .entry-header {
135
+ margin-bottom: 0;
136
+ padding: 0;
137
+ }
138
+
139
+ .menu-content-wrapper .entry-title {
140
+ margin-bottom: 7px;
141
+ }
142
+
143
+ body.ect-post .content-area .singular-content-wrap .menu-content-wrapper .entry-content {
144
+ padding-top: 0;
145
+ margin-top: 21px;
146
+ }
147
+
148
+ .ect-post .menu-content-wrapper .more-button {
149
+ text-align: left;
150
+ }
151
+
152
+ .ect-post .singular-content-wrap .entry-content .entry-header,
153
+ .ect-post .entry-content,
154
+ .ect-post .ect-menu .menu-content-wrapper .entry-header {
155
+ text-align: left;
156
+ }
157
+
158
+ .ect-post .menu-content-wrapper .ect_food_menu_item .entry-header .entry-title {
159
+ font-size: 24px;
160
  }
ect-templates/content-menu.php CHANGED
@@ -1,36 +1,36 @@
1
- <?php
2
-
3
- if ( ! defined( 'ABSPATH' ) ) {
4
- exit; // Exit if accessed directly
5
- }
6
-
7
- ?>
8
- <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
9
- <div class="hentry-inner">
10
- <?php
11
- // Check if has thumbnail
12
- if ( has_post_thumbnail( get_the_ID() ) ) :
13
- ?>
14
- <div class="food-menu-thumbnail post-thumbnail">
15
- <a href="<?php echo get_the_permalink(); ?>">
16
- <?php echo get_the_post_thumbnail( get_the_ID(), array( 50, 50 ) ); ?>
17
- </a>
18
- </div>
19
- <?php endif; ?>
20
- <div class="entry-container">
21
- <div class="entry-description">
22
- <header class="entry-header">
23
- <?php the_title( '<h2 class="entry-title"><a href="' . get_the_permalink() . '">', '</a></h2>' ); ?>
24
- </header>
25
-
26
- <div class="entry-content">
27
- <?php the_excerpt(); ?>
28
- </div>
29
- </div>
30
-
31
- <div class="entry-price">
32
- <p class="item-price"><?php echo esc_html( get_post_meta( get_the_ID(), 'ect_food_price', true ) ); ?></p>
33
- </div>
34
- </div>
35
- </div><!-- .hentry-inner -->
36
- </article><!-- .hentry -->
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Exit if accessed directly
5
+ }
6
+
7
+ ?>
8
+ <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
9
+ <div class="hentry-inner">
10
+ <?php
11
+ // Check if has thumbnail
12
+ if ( has_post_thumbnail( get_the_ID() ) ) :
13
+ ?>
14
+ <div class="food-menu-thumbnail post-thumbnail">
15
+ <a href="<?php echo get_the_permalink(); ?>">
16
+ <?php echo get_the_post_thumbnail( get_the_ID(), array( 50, 50 ) ); ?>
17
+ </a>
18
+ </div>
19
+ <?php endif; ?>
20
+ <div class="entry-container">
21
+ <div class="entry-description">
22
+ <header class="entry-header">
23
+ <?php the_title( '<h2 class="entry-title"><a href="' . get_the_permalink() . '">', '</a></h2>' ); ?>
24
+ </header>
25
+
26
+ <div class="entry-content">
27
+ <?php the_excerpt(); ?>
28
+ </div>
29
+ </div>
30
+
31
+ <div class="entry-price">
32
+ <p class="item-price"><?php echo esc_html( get_post_meta( get_the_ID(), 'ect_food_price', true ) ); ?></p>
33
+ </div>
34
+ </div>
35
+ </div><!-- .hentry-inner -->
36
+ </article><!-- .hentry -->
ect-templates/ect-menu.php CHANGED
@@ -1,108 +1,108 @@
1
- <?php
2
- /**
3
- * The template for displaying food_menu items
4
- *
5
- * @package Foodie_World
6
- */
7
- ?>
8
-
9
- <?php
10
- $cat_list = array();
11
-
12
- $menu_categories = essential_content_food_menu_get_menus();
13
-
14
- foreach ( $menu_categories as $category ) {
15
- if ( false != $atts['include_type'] ) {
16
- if ( in_array( $category->slug, $atts['include_type'] ) ) {
17
- $cat_list[] = $category->term_id;
18
- }
19
- } else {
20
- $cat_list[] = $category->term_id;
21
- }
22
- }
23
- ?>
24
- <div id="tabs" class="tabs">
25
- <div class="tabs-nav">
26
- <ul class="ui-tabs-nav menu-tabs-nav">
27
- <?php
28
- $taxonomy = 'ect_food_menu';
29
-
30
- $i = 0;
31
- foreach ( $cat_list as $cat ) :
32
- $term_obj = get_term_by( 'id', absint( $cat ), $taxonomy );
33
- if ( $term_obj ) {
34
- $term_name = $cat_name[] = $term_obj->name;
35
-
36
- $class = 'ui-tabs-tab menu-tabs-tab';
37
-
38
- if ( 0 === $i ) {
39
- $class .= ' ui-state-active';
40
- }
41
-
42
- ?>
43
- <li class="<?php echo $class; ?>"><a href="#tab-<?php echo esc_attr( $i + 1 ); ?>" class="ui-tabs-anchor"><?php echo esc_html( $term_obj->name ); ?></a></li>
44
- <?php
45
- }
46
- $i++;
47
- endforeach;
48
- ?>
49
- </ul>
50
- </div><!-- .tabs-nav -->
51
-
52
- <?php
53
- $i = 0;
54
- foreach ( $cat_list as $cat ) :
55
- if ( isset( $cat_name ) ) {
56
- ?>
57
-
58
- <div class="ui-tabs-panel-wrap">
59
- <h4 class="menu-nav-collapse ui-nav-collapse<?php echo ( 0 === $i ) ? ' ui-state-active' : ''; ?>"><a href="#tab-<?php echo esc_attr( $i + 1 ); ?>" class="ui-tabs-anchor"><?php echo esc_html( $cat_name[ $i ] ); ?></a></h4>
60
- <div id="tab-<?php echo esc_attr( $i + 1 ); ?>" class="menu-tabs-panel ui-tabs-panel<?php echo ( 0 === $i ) ? ' active-tab' : ''; ?>">
61
- <?php
62
-
63
- $args = array();
64
- $args['post_type'] = Essential_Content_Food_Menu::MENU_ITEM_POST_TYPE;
65
-
66
- $tax_query = array(
67
- array(
68
- 'taxonomy' => Essential_Content_Food_Menu::MENU_TAX,
69
- 'terms' => absint( $cat ),
70
- 'field' => 'term_id',
71
- ),
72
- );
73
-
74
- $args['tax_query'] = $tax_query;
75
-
76
- if ( false != $atts['include_tag'] ) {
77
- array_push(
78
- $args['tax_query'],
79
- array(
80
- 'taxonomy' => Essential_Content_Food_Menu::MENU_ITEM_LABEL_TAX,
81
- 'field' => 'slug',
82
- 'terms' => $atts['include_tag'],
83
- )
84
- );
85
- }
86
-
87
- if ( false != $atts['include_type'] && false != $atts['include_tag'] ) {
88
- $args['tax_query']['relation'] = 'AND';
89
- }
90
-
91
- $loop = new WP_Query( $args );
92
- if ( $loop->have_posts() ) :
93
- while ( $loop->have_posts() ) :
94
- $loop->the_post();
95
- ect_get_template_part( 'content', 'menu', $atts );
96
- endwhile;
97
- endif;
98
- wp_reset_postdata();
99
- ?>
100
- </div><!-- #tab-1 -->
101
- </div><!-- .ui-tabs-panel-wrap -->
102
-
103
- <?php
104
- }
105
- $i++;
106
- endforeach;
107
- ?>
108
- </div><!-- .tabs -->
1
+ <?php
2
+ /**
3
+ * The template for displaying food_menu items
4
+ *
5
+ * @package Foodie_World
6
+ */
7
+ ?>
8
+
9
+ <?php
10
+ $cat_list = array();
11
+
12
+ $menu_categories = essential_content_food_menu_get_menus();
13
+
14
+ foreach ( $menu_categories as $category ) {
15
+ if ( false != $atts['include_type'] ) {
16
+ if ( in_array( $category->slug, $atts['include_type'] ) ) {
17
+ $cat_list[] = $category->term_id;
18
+ }
19
+ } else {
20
+ $cat_list[] = $category->term_id;
21
+ }
22
+ }
23
+ ?>
24
+ <div id="tabs" class="tabs">
25
+ <div class="tabs-nav">
26
+ <ul class="ui-tabs-nav menu-tabs-nav">
27
+ <?php
28
+ $taxonomy = 'ect_food_menu';
29
+
30
+ $i = 0;
31
+ foreach ( $cat_list as $cat ) :
32
+ $term_obj = get_term_by( 'id', absint( $cat ), $taxonomy );
33
+ if ( $term_obj ) {
34
+ $term_name = $cat_name[] = $term_obj->name;
35
+
36
+ $class = 'ui-tabs-tab menu-tabs-tab';
37
+
38
+ if ( 0 === $i ) {
39
+ $class .= ' ui-state-active';
40
+ }
41
+
42
+ ?>
43
+ <li class="<?php echo $class; ?>"><a href="#tab-<?php echo esc_attr( $i + 1 ); ?>" class="ui-tabs-anchor"><?php echo esc_html( $term_obj->name ); ?></a></li>
44
+ <?php
45
+ }
46
+ $i++;
47
+ endforeach;
48
+ ?>
49
+ </ul>
50
+ </div><!-- .tabs-nav -->
51
+
52
+ <?php
53
+ $i = 0;
54
+ foreach ( $cat_list as $cat ) :
55
+ if ( isset( $cat_name ) ) {
56
+ ?>
57
+
58
+ <div class="ui-tabs-panel-wrap">
59
+ <h4 class="menu-nav-collapse ui-nav-collapse<?php echo ( 0 === $i ) ? ' ui-state-active' : ''; ?>"><a href="#tab-<?php echo esc_attr( $i + 1 ); ?>" class="ui-tabs-anchor"><?php echo esc_html( $cat_name[ $i ] ); ?></a></h4>
60
+ <div id="tab-<?php echo esc_attr( $i + 1 ); ?>" class="menu-tabs-panel ui-tabs-panel<?php echo ( 0 === $i ) ? ' active-tab' : ''; ?>">
61
+ <?php
62
+
63
+ $args = array();
64
+ $args['post_type'] = Essential_Content_Food_Menu::MENU_ITEM_POST_TYPE;
65
+
66
+ $tax_query = array(
67
+ array(
68
+ 'taxonomy' => Essential_Content_Food_Menu::MENU_TAX,
69
+ 'terms' => absint( $cat ),
70
+ 'field' => 'term_id',
71
+ ),
72
+ );
73
+
74
+ $args['tax_query'] = $tax_query;
75
+
76
+ if ( false != $atts['include_tag'] ) {
77
+ array_push(
78
+ $args['tax_query'],
79
+ array(
80
+ 'taxonomy' => Essential_Content_Food_Menu::MENU_ITEM_LABEL_TAX,
81
+ 'field' => 'slug',
82
+ 'terms' => $atts['include_tag'],
83
+ )
84
+ );
85
+ }
86
+
87
+ if ( false != $atts['include_type'] && false != $atts['include_tag'] ) {
88
+ $args['tax_query']['relation'] = 'AND';
89
+ }
90
+
91
+ $loop = new WP_Query( $args );
92
+ if ( $loop->have_posts() ) :
93
+ while ( $loop->have_posts() ) :
94
+ $loop->the_post();
95
+ ect_get_template_part( 'content', 'menu', $atts );
96
+ endwhile;
97
+ endif;
98
+ wp_reset_postdata();
99
+ ?>
100
+ </div><!-- #tab-1 -->
101
+ </div><!-- .ui-tabs-panel-wrap -->
102
+
103
+ <?php
104
+ }
105
+ $i++;
106
+ endforeach;
107
+ ?>
108
+ </div><!-- .tabs -->
essential-content-types.php CHANGED
@@ -1,273 +1,273 @@
1
- <?php
2
-
3
- /**
4
- * The plugin bootstrap file
5
- *
6
- * This file is read by WordPress to generate the plugin information in the plugin
7
- * admin area. This file also includes all of the dependencies used by the plugin,
8
- * registers the activation and deactivation functions, and defines a function
9
- * that starts the plugin.
10
- *
11
- * @link https://catchplugins.com
12
- * @since 1.0.0
13
- * @package Essential_Content_Types
14
- *
15
- * @wordpress-plugin
16
- * Plugin Name: Essential Content Types
17
- * Plugin URI: https://catchplugins.com/plugins/essential-content-types/
18
- * Description: Essential Content Types allows you to feature the impressive content through different content/post types on your website just the way you want it. These content/post types are missed by the themes in WordPress Theme Directory as the feature falls more towards the plugins’ territory.
19
- * Version: 1.8.5
20
- * Author: Catch Plugins
21
- * Author URI: https://catchplugins.com
22
- * License: GPL-3.0+
23
- * License URI: http://www.gnu.org/licenses/gpl-3.0.txt
24
- * Text Domain: essential-content-types
25
- * Domain Path: /languages
26
- */
27
-
28
- // If this file is called directly, abort.
29
- if ( ! defined( 'WPINC' ) ) {
30
- die;
31
- }
32
-
33
- // Define Version
34
- define( 'ESSENTIAL_CONTENT_TYPES_VERSION', '1.8.5' );
35
-
36
- /**
37
- * The code that runs during plugin activation.
38
- * This action is documented in includes/class-essential-content-types-activator.php
39
- */
40
- // The URL of the directory that contains the plugin
41
- if ( ! defined( 'ESSENTIAL_CONTENT_TYPES_URL' ) ) {
42
- define( 'ESSENTIAL_CONTENT_TYPES_URL', plugin_dir_url( __FILE__ ) );
43
- }
44
-
45
-
46
- // The absolute path of the directory that contains the file
47
- if ( ! defined( 'ESSENTIAL_CONTENT_TYPES_PATH' ) ) {
48
- define( 'ESSENTIAL_CONTENT_TYPES_PATH', plugin_dir_path( __FILE__ ) );
49
- }
50
-
51
-
52
- // Gets the path to a plugin file or directory, relative to the plugins directory, without the leading and trailing slashes.
53
- if ( ! defined( 'ESSENTIAL_CONTENT_TYPES_BASENAME' ) ) {
54
- define( 'ESSENTIAL_CONTENT_TYPES_BASENAME', plugin_basename( __FILE__ ) );
55
- }
56
-
57
- /**
58
- * Make plugin available for translation
59
- * Translations can be filed in the /languages/ directory
60
- */
61
- function activate_essential_content_types() {
62
- $required = 'essential-content-types-pro/essential-content-types-pro.php';
63
- if ( is_plugin_active( $required ) ) {
64
- $message = esc_html__( 'Sorry, Pro plugin is already active. No need to activate Free version. %1$s&laquo; Return to Plugins%2$s.', 'essential-content-types' );
65
- $message = sprintf( $message, '<br><a href="' . esc_url( admin_url( 'plugins.php' ) ) . '">', '</a>' );
66
- wp_die( $message );
67
- }
68
- require_once plugin_dir_path( __FILE__ ) . 'includes/class-essential-content-types-activator.php';
69
- Essential_Content_Types_Activator::activate();
70
- }
71
-
72
- /**
73
- * The code that runs during plugin deactivation.
74
- * This action is documented in includes/class-essential-content-types-deactivator.php
75
- */
76
- function deactivate_essential_content_types() {
77
- require_once plugin_dir_path( __FILE__ ) . 'includes/class-essential-content-types-deactivator.php';
78
- Essential_Content_Types_Deactivator::deactivate();
79
- }
80
-
81
- register_activation_hook( __FILE__, 'activate_essential_content_types' );
82
- register_deactivation_hook( __FILE__, 'deactivate_essential_content_types' );
83
-
84
- /**
85
- * The core plugin class that is used to define internationalization,
86
- * admin-specific hooks, and public-facing site hooks.
87
- */
88
- require plugin_dir_path( __FILE__ ) . 'includes/class-essential-content-types.php';
89
-
90
- /**
91
- * Begins execution of the plugin.
92
- *
93
- * Since everything within the plugin is registered via hooks,
94
- * then kicking off the plugin from this point in the file does
95
- * not affect the page life cycle.
96
- *
97
- * @since 1.0.0
98
- */
99
- function run_essential_content_types() {
100
-
101
- $plugin = new Essential_Content_Types();
102
- $plugin->run();
103
-
104
- }
105
- run_essential_content_types();
106
-
107
-
108
- if ( ! function_exists( 'ect_get_layout' ) ) :
109
- function ect_get_layout() {
110
- $layout = array(
111
- '1' => 'layout-one',
112
- '2' => 'layout-two',
113
- '3' => 'layout-three',
114
- '4' => 'layout-four',
115
- '5' => 'layout-five',
116
- '6' => 'layout-six',
117
- );
118
- return $layout;
119
- }
120
- endif;
121
-
122
-
123
- if ( ! function_exists( 'ect_plugin_path' ) ) :
124
- /**
125
- * Get the plugin path.
126
- *
127
- * @return string
128
- */
129
- function ect_plugin_path() {
130
- return untrailingslashit( plugin_dir_path( __FILE__ ) );
131
- }
132
- endif;
133
-
134
-
135
- if ( ! function_exists( 'ect_template_path' ) ) :
136
- /**
137
- * Get the template path.
138
- *
139
- * @return string
140
- */
141
- function ect_template_path() {
142
- return apply_filters( 'ect_template_path', 'ect-templates/' );
143
- }
144
- endif;
145
-
146
-
147
- if ( ! function_exists( 'ect_get_template_part' ) ) :
148
- /**
149
- * Get template part (for templates like the shop-loop).
150
- *
151
- *
152
- * @access public
153
- * @param mixed $slug Template slug.
154
- * @param string $name Template name (default: '').
155
- * @param array $atts Options to pass into template.
156
- */
157
- function ect_get_template_part( $slug, $name = '', $atts ) {
158
-
159
- $template = '';
160
- set_query_var( 'atts', $atts );
161
-
162
- // Look in yourtheme/slug-name.php and yourtheme/ect-templates/slug-name.php.
163
- if ( $name ) {
164
- $template = locate_template( array( "{$slug}-{$name}.php", ect_template_path() . "{$slug}-{$name}.php" ) );
165
- }
166
-
167
- // Get default slug-name.php.
168
- if ( empty( $template ) && $name && file_exists( ect_plugin_path() . '/' . ect_template_path() . "{$slug}-{$name}.php" ) ) {
169
- $template = ect_plugin_path() . '/' . ect_template_path() . "{$slug}-{$name}.php";
170
- }
171
-
172
- // If template file doesn't exist, look in yourtheme/slug.php and yourtheme/ect-templates/slug.php.
173
- if ( ! $template ) {
174
- $template = locate_template( array( "{$slug}.php", ect_template_path() . "{$slug}.php" ) );
175
- }
176
-
177
- // Allow 3rd party plugins to filter template file from their plugin.
178
- $template = apply_filters( 'ect_get_template_part', $template, $slug, $name );
179
-
180
- if ( $template ) {
181
- load_template( $template, false, 'atts' );
182
- }
183
- }
184
- endif;
185
-
186
- function ect_body_classes( $classes ) {
187
- $classes[] = 'ect-post';
188
- return $classes;
189
- }
190
- add_filter( 'body_class', 'ect_body_classes' );
191
- /* CTP tabs removal options */
192
- require plugin_dir_path( __FILE__ ) . '/includes/ctp-tabs-removal.php';
193
-
194
- $ctp_options = ctp_get_options();
195
- if ( 1 == $ctp_options['theme_plugin_tabs'] ) {
196
- /* Adds Catch Themes tab in Add theme page and Themes by Catch Themes in Customizer's change theme option. */
197
- if ( ! class_exists( 'CatchThemesThemePlugin' ) && ! function_exists( 'add_our_plugins_tab' ) ) {
198
- require plugin_dir_path( __FILE__ ) . '/includes/CatchThemesThemePlugin.php';
199
- }
200
- }
201
-
202
- /* Modify ECT Post type archive title for default values */
203
- function ect_check_archive_title(){
204
- global $wp_query;
205
- $post_type = false;
206
- if ( isset( $wp_query->query['post_type'] ) ) {
207
- $post_type = $wp_query->query['post_type'];
208
- }
209
-
210
- $ect_post_types = array(
211
- 'featured-content',
212
- 'jetpack-portfolio',
213
- 'jetpack-testimonial',
214
- 'ect-service',
215
- );
216
-
217
- if( $post_type !== false || in_array($post_type, $ect_post_types, true)) {
218
- add_filter( 'get_the_archive_title', 'ect_modify_archive_title', 10, 1 );
219
- }
220
- }
221
-
222
- add_action('init', 'ect_check_archive_title');
223
-
224
- /* set default title if options not found */
225
- function ect_modify_archive_title( $title ) {
226
- $title_label = '<span class="some-class">%1$s</span>%2$s';
227
- $type = ect_get_archive_post_type();
228
-
229
- $default_title = array(
230
- 'ect_service_title' => esc_html( 'Services', 'essential-content-types' ),
231
- 'jetpack_testimonial_title' => esc_html( 'Testimonial', 'essential-content-types' ),
232
- 'jetpack_portfolio_title' => esc_html( 'Portfolio', 'essential-content-types' ),
233
- 'featured_content_title' => esc_html( 'Featured Content', 'essential-content-types' ),
234
- );
235
-
236
- $archive_title = get_option( $type, $default_title[ $type ] );
237
-
238
- if ( '' !== $archive_title ) {
239
- return trim(
240
- sprintf(
241
- $title_label,
242
- esc_html_x( 'Archives: ', 'Archive title label.', 'archive-title' ),
243
- $archive_title
244
- )
245
- );
246
- } else {
247
- return trim(
248
- sprintf(
249
- $title_label,
250
- esc_html_x( 'Archives: ', 'Archive title label.', 'archive-title' ),
251
- post_type_archive_title( '', false )
252
- )
253
- );
254
- }
255
-
256
- }
257
-
258
- function ect_get_archive_post_type() {
259
-
260
- global $wp_query;
261
- if ( isset( $wp_query->query['post_type'] ) ) {
262
- $post_type = $wp_query->query['post_type'];
263
- }
264
-
265
- $type = array(
266
- 'featured-content' => 'featured_content_title',
267
- 'jetpack-portfolio' => 'jetpack_portfolio_title',
268
- 'jetpack-testimonial' => 'jetpack_testimonial_title',
269
- 'ect-service' => 'ect_service_title',
270
- );
271
-
272
- return $type[ $post_type ];
273
- }
1
+ <?php
2
+
3
+ /**
4
+ * The plugin bootstrap file
5
+ *
6
+ * This file is read by WordPress to generate the plugin information in the plugin
7
+ * admin area. This file also includes all of the dependencies used by the plugin,
8
+ * registers the activation and deactivation functions, and defines a function
9
+ * that starts the plugin.
10
+ *
11
+ * @link https://catchplugins.com
12
+ * @since 1.0.0
13
+ * @package Essential_Content_Types
14
+ *
15
+ * @wordpress-plugin
16
+ * Plugin Name: Essential Content Types
17
+ * Plugin URI: https://catchplugins.com/plugins/essential-content-types/
18
+ * Description: Essential Content Types allows you to feature the impressive content through different content/post types on your website just the way you want it. These content/post types are missed by the themes in WordPress Theme Directory as the feature falls more towards the plugins’ territory.
19
+ * Version: 1.8.6
20
+ * Author: Catch Plugins
21
+ * Author URI: https://catchplugins.com
22
+ * License: GPL-3.0+
23
+ * License URI: http://www.gnu.org/licenses/gpl-3.0.txt
24
+ * Text Domain: essential-content-types
25
+ * Domain Path: /languages
26
+ */
27
+
28
+ // If this file is called directly, abort.
29
+ if ( ! defined( 'WPINC' ) ) {
30
+ die;
31
+ }
32
+
33
+ // Define Version
34
+ define( 'ESSENTIAL_CONTENT_TYPES_VERSION', '1.8.6' );
35
+
36
+ /**
37
+ * The code that runs during plugin activation.
38
+ * This action is documented in includes/class-essential-content-types-activator.php
39
+ */
40
+ // The URL of the directory that contains the plugin
41
+ if ( ! defined( 'ESSENTIAL_CONTENT_TYPES_URL' ) ) {
42
+ define( 'ESSENTIAL_CONTENT_TYPES_URL', plugin_dir_url( __FILE__ ) );
43
+ }
44
+
45
+
46
+ // The absolute path of the directory that contains the file
47
+ if ( ! defined( 'ESSENTIAL_CONTENT_TYPES_PATH' ) ) {
48
+ define( 'ESSENTIAL_CONTENT_TYPES_PATH', plugin_dir_path( __FILE__ ) );
49
+ }
50
+
51
+
52
+ // Gets the path to a plugin file or directory, relative to the plugins directory, without the leading and trailing slashes.
53
+ if ( ! defined( 'ESSENTIAL_CONTENT_TYPES_BASENAME' ) ) {
54
+ define( 'ESSENTIAL_CONTENT_TYPES_BASENAME', plugin_basename( __FILE__ ) );
55
+ }
56
+
57
+ /**
58
+ * Make plugin available for translation
59
+ * Translations can be filed in the /languages/ directory
60
+ */
61
+ function activate_essential_content_types() {
62
+ $required = 'essential-content-types-pro/essential-content-types-pro.php';
63
+ if ( is_plugin_active( $required ) ) {
64
+ $message = esc_html__( 'Sorry, Pro plugin is already active. No need to activate Free version. %1$s&laquo; Return to Plugins%2$s.', 'essential-content-types' );
65
+ $message = sprintf( $message, '<br><a href="' . esc_url( admin_url( 'plugins.php' ) ) . '">', '</a>' );
66
+ wp_die( $message );
67
+ }
68
+ require_once plugin_dir_path( __FILE__ ) . 'includes/class-essential-content-types-activator.php';
69
+ Essential_Content_Types_Activator::activate();
70
+ }
71
+
72
+ /**
73
+ * The code that runs during plugin deactivation.
74
+ * This action is documented in includes/class-essential-content-types-deactivator.php
75
+ */
76
+ function deactivate_essential_content_types() {
77
+ require_once plugin_dir_path( __FILE__ ) . 'includes/class-essential-content-types-deactivator.php';
78
+ Essential_Content_Types_Deactivator::deactivate();
79
+ }
80
+
81
+ register_activation_hook( __FILE__, 'activate_essential_content_types' );
82
+ register_deactivation_hook( __FILE__, 'deactivate_essential_content_types' );
83
+
84
+ /**
85
+ * The core plugin class that is used to define internationalization,
86
+ * admin-specific hooks, and public-facing site hooks.
87
+ */
88
+ require plugin_dir_path( __FILE__ ) . 'includes/class-essential-content-types.php';
89
+
90
+ /**
91
+ * Begins execution of the plugin.
92
+ *
93
+ * Since everything within the plugin is registered via hooks,
94
+ * then kicking off the plugin from this point in the file does
95
+ * not affect the page life cycle.
96
+ *
97
+ * @since 1.0.0
98
+ */
99
+ function run_essential_content_types() {
100
+
101
+ $plugin = new Essential_Content_Types();
102
+ $plugin->run();
103
+
104
+ }
105
+ run_essential_content_types();
106
+
107
+
108
+ if ( ! function_exists( 'ect_get_layout' ) ) :
109
+ function ect_get_layout() {
110
+ $layout = array(
111
+ '1' => 'layout-one',
112
+ '2' => 'layout-two',
113
+ '3' => 'layout-three',
114
+ '4' => 'layout-four',
115
+ '5' => 'layout-five',
116
+ '6' => 'layout-six',
117
+ );
118
+ return $layout;
119
+ }
120
+ endif;
121
+
122
+
123
+ if ( ! function_exists( 'ect_plugin_path' ) ) :
124
+ /**
125
+ * Get the plugin path.
126
+ *
127
+ * @return string
128
+ */
129
+ function ect_plugin_path() {
130
+ return untrailingslashit( plugin_dir_path( __FILE__ ) );
131
+ }
132
+ endif;
133
+
134
+
135
+ if ( ! function_exists( 'ect_template_path' ) ) :
136
+ /**
137
+ * Get the template path.
138
+ *
139
+ * @return string
140
+ */
141
+ function ect_template_path() {
142
+ return apply_filters( 'ect_template_path', 'ect-templates/' );
143
+ }
144
+ endif;
145
+
146
+
147
+ if ( ! function_exists( 'ect_get_template_part' ) ) :
148
+ /**
149
+ * Get template part (for templates like the shop-loop).
150
+ *
151
+ *
152
+ * @access public
153
+ * @param mixed $slug Template slug.
154
+ * @param string $name Template name (default: '').
155
+ * @param array $atts Options to pass into template.
156
+ */
157
+ function ect_get_template_part( $slug, $name = '', $atts = '' ) {
158
+
159
+ $template = '';
160
+ set_query_var( 'atts', $atts );
161
+
162
+ // Look in yourtheme/slug-name.php and yourtheme/ect-templates/slug-name.php.
163
+ if ( $name ) {
164
+ $template = locate_template( array( "{$slug}-{$name}.php", ect_template_path() . "{$slug}-{$name}.php" ) );
165
+ }
166
+
167
+ // Get default slug-name.php.
168
+ if ( empty( $template ) && $name && file_exists( ect_plugin_path() . '/' . ect_template_path() . "{$slug}-{$name}.php" ) ) {
169
+ $template = ect_plugin_path() . '/' . ect_template_path() . "{$slug}-{$name}.php";
170
+ }
171
+
172
+ // If template file doesn't exist, look in yourtheme/slug.php and yourtheme/ect-templates/slug.php.
173
+ if ( ! $template ) {
174
+ $template = locate_template( array( "{$slug}.php", ect_template_path() . "{$slug}.php" ) );
175
+ }
176
+
177
+ // Allow 3rd party plugins to filter template file from their plugin.
178
+ $template = apply_filters( 'ect_get_template_part', $template, $slug, $name );
179
+
180
+ if ( $template ) {
181
+ load_template( $template, false, 'atts' );
182
+ }
183
+ }
184
+ endif;
185
+
186
+ function ect_body_classes( $classes ) {
187
+ $classes[] = 'ect-post';
188
+ return $classes;
189
+ }
190
+ add_filter( 'body_class', 'ect_body_classes' );
191
+ /* CTP tabs removal options */
192
+ require plugin_dir_path( __FILE__ ) . '/includes/ctp-tabs-removal.php';
193
+
194
+ $ctp_options = ctp_get_options();
195
+ if ( 1 == $ctp_options['theme_plugin_tabs'] ) {
196
+ /* Adds Catch Themes tab in Add theme page and Themes by Catch Themes in Customizer's change theme option. */
197
+ if ( ! class_exists( 'CatchThemesThemePlugin' ) && ! function_exists( 'add_our_plugins_tab' ) ) {
198
+ require plugin_dir_path( __FILE__ ) . '/includes/CatchThemesThemePlugin.php';
199
+ }
200
+ }
201
+
202
+ /* Modify ECT Post type archive title for default values */
203
+ function ect_check_archive_title() {
204
+ global $wp_query;
205
+ $post_type = false;
206
+ if ( isset( $wp_query->query['post_type'] ) ) {
207
+ $post_type = $wp_query->query['post_type'];
208
+ }
209
+
210
+ $ect_post_types = array(
211
+ 'featured-content',
212
+ 'jetpack-portfolio',
213
+ 'jetpack-testimonial',
214
+ 'ect-service',
215
+ );
216
+
217
+ if ( $post_type !== false || in_array( $post_type, $ect_post_types, true ) ) {
218
+ add_filter( 'get_the_archive_title', 'ect_modify_archive_title', 10, 1 );
219
+ }
220
+ }
221
+
222
+ add_action( 'wp_head', 'ect_check_archive_title' );
223
+
224
+ /* set default title if options not found */
225
+ function ect_modify_archive_title( $title ) {
226
+ $title_label = '<span class="some-class">%1$s</span>%2$s';
227
+ $type = ect_get_archive_post_type();
228
+
229
+ $default_title = array(
230
+ 'ect_service_title' => esc_html( 'Services', 'essential-content-types' ),
231
+ 'jetpack_testimonial_title' => esc_html( 'Testimonial', 'essential-content-types' ),
232
+ 'jetpack_portfolio_title' => esc_html( 'Portfolio', 'essential-content-types' ),
233
+ 'featured_content_title' => esc_html( 'Featured Content', 'essential-content-types' ),
234
+ );
235
+
236
+ $archive_title = get_option( $type, $default_title[ $type ] );
237
+
238
+ if ( '' !== $archive_title ) {
239
+ return trim(
240
+ sprintf(
241
+ $title_label,
242
+ esc_html_x( 'Archives: ', 'Archive title label.', 'archive-title' ),
243
+ $archive_title
244
+ )
245
+ );
246
+ } else {
247
+ return trim(
248
+ sprintf(
249
+ $title_label,
250
+ esc_html_x( 'Archives: ', 'Archive title label.', 'archive-title' ),
251
+ post_type_archive_title( '', false )
252
+ )
253
+ );
254
+ }
255
+
256
+ }
257
+
258
+ function ect_get_archive_post_type() {
259
+
260
+ global $wp_query;
261
+ if ( isset( $wp_query->query['post_type'] ) ) {
262
+ $post_type = $wp_query->query['post_type'];
263
+ }
264
+
265
+ $type = array(
266
+ 'featured-content' => 'featured_content_title',
267
+ 'jetpack-portfolio' => 'jetpack_portfolio_title',
268
+ 'jetpack-testimonial' => 'jetpack_testimonial_title',
269
+ 'ect-service' => 'ect_service_title',
270
+ );
271
+
272
+ return $type[ $post_type ];
273
+ }
includes/CatchThemesThemePlugin.php CHANGED
@@ -1,484 +1,484 @@
1
- <?php
2
-
3
- class CatchThemesThemePlugin {
4
- public function __construct() {
5
- remove_action( 'wp_ajax_query-themes', array( $this, 'wp_ajax_query_themes' ), 1 );
6
- add_action( 'wp_ajax_query-themes', array( $this, 'wp_ajax_custom_query_themes' ), 1 );
7
-
8
- add_action( 'admin_enqueue_scripts', array( $this, 'our_themes_script' ) );
9
-
10
- if ( ! is_multisite() ) {
11
- add_action( 'customize_register', array( $this, 'customize_register' ) );
12
- }
13
-
14
- global $wp_customize;
15
- remove_action( 'wp_ajax_customize_load_themes', array( $wp_customize, 'handle_load_themes_request' ) );
16
- add_action( 'wp_ajax_customize_load_themes', array( $this, 'handle_load_themes_request' ) );
17
-
18
- add_filter( 'install_plugins_tabs', array( $this, 'add_our_plugins_tab' ), 1 );
19
- add_filter( 'install_plugins_table_api_args_catchplugins', array( $this, 'catchplugins' ), 1 );
20
- add_action( 'install_plugins_catchplugins', array( $this, 'plugins_table' ) );
21
- }
22
-
23
- /* Adds Catch Themes tab in Add Theme page to show all themes by Catch Themes in wordpress.org
24
- * taken from wp-admin/includes/ajax-action.php wp_ajax_query_themes().
25
- * Ajax handler for getting themes from themes_api().
26
- *
27
- * @since 3.9.0
28
- *
29
- * @global array $themes_allowedtags
30
- * @global array $theme_field_defaults
31
- */
32
- public function wp_ajax_custom_query_themes() {
33
- global $themes_allowedtags, $theme_field_defaults;
34
-
35
- if ( ! current_user_can( 'install_themes' ) ) {
36
- wp_send_json_error();
37
- }
38
-
39
- $args = wp_parse_args(
40
- wp_unslash( $_REQUEST['request'] ),
41
- array(
42
- 'per_page' => 20,
43
- 'fields' => array_merge(
44
- (array) $theme_field_defaults,
45
- array(
46
- 'reviews_url' => true, // Explicitly request the reviews URL to be linked from the Add Themes screen.
47
- )
48
- ),
49
- )
50
- );
51
-
52
- if ( isset( $args['browse'] ) && 'catchthemes' === $args['browse'] && ! isset( $args['user'] ) ) {
53
- $args['author'] = 'catchthemes';
54
- unset( $args['browse'] );
55
- }
56
-
57
- if ( isset( $args['browse'] ) && 'favorites' === $args['browse'] && ! isset( $args['user'] ) ) {
58
- $user = get_user_option( 'wporg_favorites' );
59
- if ( $user ) {
60
- $args['user'] = $user;
61
- }
62
- }
63
-
64
- $old_filter = isset( $args['browse'] ) ? $args['browse'] : 'search';
65
-
66
- /** This filter is documented in wp-admin/includes/class-wp-theme-install-list-table.php */
67
- $args = apply_filters( 'install_themes_table_api_args_' . $old_filter, $args );
68
-
69
- $api = themes_api( 'query_themes', $args );
70
-
71
- if ( is_wp_error( $api ) ) {
72
- wp_send_json_error();
73
- }
74
-
75
- $update_php = network_admin_url( 'update.php?action=install-theme' );
76
- foreach ( $api->themes as &$theme ) {
77
- $theme->install_url = add_query_arg(
78
- array(
79
- 'theme' => $theme->slug,
80
- '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug ),
81
- ),
82
- $update_php
83
- );
84
-
85
- if ( current_user_can( 'switch_themes' ) ) {
86
- if ( is_multisite() ) {
87
- $theme->activate_url = add_query_arg(
88
- array(
89
- 'action' => 'enable',
90
- '_wpnonce' => wp_create_nonce( 'enable-theme_' . $theme->slug ),
91
- 'theme' => $theme->slug,
92
- ),
93
- network_admin_url( 'themes.php' )
94
- );
95
- } else {
96
- $theme->activate_url = add_query_arg(
97
- array(
98
- 'action' => 'activate',
99
- '_wpnonce' => wp_create_nonce( 'switch-theme_' . $theme->slug ),
100
- 'stylesheet' => $theme->slug,
101
- ),
102
- admin_url( 'themes.php' )
103
- );
104
- }
105
- }
106
-
107
- if ( ! is_multisite() && current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) {
108
- $theme->customize_url = add_query_arg(
109
- array(
110
- 'return' => urlencode( network_admin_url( 'theme-install.php', 'relative' ) ),
111
- ),
112
- wp_customize_url( $theme->slug )
113
- );
114
- }
115
-
116
- $theme->name = wp_kses( $theme->name, $themes_allowedtags );
117
- $theme->author = wp_kses( $theme->author['display_name'], $themes_allowedtags );
118
- $theme->version = wp_kses( $theme->version, $themes_allowedtags );
119
- $theme->description = wp_kses( $theme->description, $themes_allowedtags );
120
-
121
- $theme->stars = wp_star_rating(
122
- array(
123
- 'rating' => $theme->rating,
124
- 'type' => 'percent',
125
- 'number' => $theme->num_ratings,
126
- 'echo' => false,
127
- )
128
- );
129
-
130
- $theme->num_ratings = number_format_i18n( $theme->num_ratings );
131
- $theme->preview_url = set_url_scheme( $theme->preview_url );
132
- $theme->compatible_wp = is_wp_version_compatible( $theme->requires );
133
- $theme->compatible_php = is_php_version_compatible( $theme->requires_php );
134
-
135
- }
136
-
137
- wp_send_json_success( $api );
138
- }
139
-
140
- public function our_themes_script( $hook_suffix ) {
141
-
142
- if ( 'theme-install.php' === $hook_suffix ) {
143
- wp_enqueue_script( 'our-themes-script', plugin_dir_url( __FILE__ ) . '../js/our-themes.js', array( 'jquery' ), '2018-05-16' );
144
- }
145
- }
146
-
147
- /* Add Catch Themes Section in Theme in Customizer */
148
- public function customize_register( $wp_customize ) {
149
- $wp_customize->add_section(
150
- new WP_Customize_Themes_Section(
151
- $wp_customize,
152
- 'catchthemes',
153
- array(
154
- 'title' => __( 'Themes by CatchThemes', 'catch-themes-demo-import' ),
155
- 'action' => 'catchthemes',
156
- 'capability' => 'install_themes',
157
- 'panel' => 'themes',
158
- 'priority' => 6,
159
- )
160
- )
161
- );
162
- }
163
-
164
-
165
-
166
-
167
- /**
168
- * Load themes into the theme browsing/installation UI.
169
- * taken from wp-includes/cllass-wp-customize-manager.php
170
- * @since 4.9.0
171
- */
172
- public function handle_load_themes_request() {
173
- check_ajax_referer( 'switch_themes', 'nonce' );
174
- if ( ! current_user_can( 'switch_themes' ) ) {
175
- wp_die( -1 );
176
- }
177
-
178
- if ( empty( $_POST['theme_action'] ) ) {
179
- wp_send_json_error( 'missing_theme_action' );
180
- }
181
- $theme_action = sanitize_key( $_POST['theme_action'] );
182
- $themes = array();
183
- $args = array();
184
-
185
- // Define query filters based on user input.
186
- if ( ! array_key_exists( 'search', $_POST ) ) {
187
- $args['search'] = '';
188
- } else {
189
- $args['search'] = sanitize_text_field( wp_unslash( $_POST['search'] ) );
190
- }
191
-
192
- if ( ! array_key_exists( 'tags', $_POST ) ) {
193
- $args['tag'] = '';
194
- } else {
195
- $args['tag'] = array_map( 'sanitize_text_field', wp_unslash( (array) $_POST['tags'] ) );
196
- }
197
-
198
- if ( ! array_key_exists( 'page', $_POST ) ) {
199
- $args['page'] = 1;
200
- } else {
201
- $args['page'] = absint( $_POST['page'] );
202
- }
203
-
204
- require_once ABSPATH . 'wp-admin/includes/theme.php';
205
-
206
- if ( 'installed' === $theme_action ) {
207
-
208
- // Load all installed themes from wp_prepare_themes_for_js().
209
- $themes = array( 'themes' => wp_prepare_themes_for_js() );
210
- foreach ( $themes['themes'] as &$theme ) {
211
- $theme['type'] = 'installed';
212
- $theme['active'] = ( isset( $_POST['customized_theme'] ) && $_POST['customized_theme'] === $theme['id'] );
213
- }
214
- } elseif ( 'catchthemes' === $theme_action ) {
215
-
216
- // Load WordPress.org themes from the .org API and normalize data to match installed theme objects.
217
- if ( ! current_user_can( 'install_themes' ) ) {
218
- wp_die( -1 );
219
- }
220
-
221
- // Arguments for all queries.
222
- $wporg_args = array(
223
- 'per_page' => 100,
224
- 'fields' => array(
225
- 'reviews_url' => true, // Explicitly request the reviews URL to be linked from the customizer.
226
- ),
227
- );
228
-
229
- $args = array_merge( $wporg_args, $args );
230
-
231
- if ( '' === $args['search'] && '' === $args['tag'] ) {
232
- $args['browse'] = 'new'; // Sort by latest themes by default.
233
- }
234
-
235
- $args['author'] = 'catchthemes';
236
-
237
- // Load themes from the .org API.
238
- $themes = themes_api( 'query_themes', $args );
239
- if ( is_wp_error( $themes ) ) {
240
- wp_send_json_error();
241
- }
242
-
243
- // This list matches the allowed tags in wp-admin/includes/theme-install.php.
244
- $themes_allowedtags = array_fill_keys(
245
- array( 'a', 'abbr', 'acronym', 'code', 'pre', 'em', 'strong', 'div', 'p', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'img' ),
246
- array()
247
- );
248
- $themes_allowedtags['a'] = array_fill_keys( array( 'href', 'title', 'target' ), true );
249
- $themes_allowedtags['acronym']['title'] = true;
250
- $themes_allowedtags['abbr']['title'] = true;
251
- $themes_allowedtags['img'] = array_fill_keys( array( 'src', 'class', 'alt' ), true );
252
-
253
- // Prepare a list of installed themes to check against before the loop.
254
- $installed_themes = array();
255
- $wp_themes = wp_get_themes();
256
- foreach ( $wp_themes as $theme ) {
257
- $installed_themes[] = $theme->get_stylesheet();
258
- }
259
- $update_php = network_admin_url( 'update.php?action=install-theme' );
260
-
261
- // Set up properties for themes available on WordPress.org.
262
- foreach ( $themes->themes as &$theme ) {
263
- $theme->install_url = add_query_arg(
264
- array(
265
- 'theme' => $theme->slug,
266
- '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug ),
267
- ),
268
- $update_php
269
- );
270
-
271
- $theme->name = wp_kses( $theme->name, $themes_allowedtags );
272
- $theme->version = wp_kses( $theme->version, $themes_allowedtags );
273
- $theme->description = wp_kses( $theme->description, $themes_allowedtags );
274
- $theme->stars = wp_star_rating(
275
- array(
276
- 'rating' => $theme->rating,
277
- 'type' => 'percent',
278
- 'number' => $theme->num_ratings,
279
- 'echo' => false,
280
- )
281
- );
282
- $theme->num_ratings = number_format_i18n( $theme->num_ratings );
283
- $theme->preview_url = set_url_scheme( $theme->preview_url );
284
-
285
- // Handle themes that are already installed as installed themes.
286
- if ( in_array( $theme->slug, $installed_themes, true ) ) {
287
- $theme->type = 'installed';
288
- } else {
289
- $theme->type = $theme_action;
290
- }
291
-
292
- // Set active based on customized theme.
293
- $theme->active = ( isset( $_POST['customized_theme'] ) && $_POST['customized_theme'] === $theme->slug );
294
-
295
- // Map available theme properties to installed theme properties.
296
- $theme->id = $theme->slug;
297
- $theme->screenshot = array( $theme->screenshot_url );
298
- $theme->authorAndUri = wp_kses( $theme->author['display_name'], $themes_allowedtags );
299
- $theme->compatibleWP = is_wp_version_compatible( $theme->requires ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName
300
- $theme->compatiblePHP = is_php_version_compatible( $theme->requires_php ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName
301
-
302
- if ( isset( $theme->parent ) ) {
303
- $theme->parent = $theme->parent['slug'];
304
- } else {
305
- $theme->parent = false;
306
- }
307
- unset( $theme->slug );
308
- unset( $theme->screenshot_url );
309
- unset( $theme->author );
310
- } // End foreach().
311
- } elseif ( 'wporg' === $theme_action ) {
312
-
313
- // Load WordPress.org themes from the .org API and normalize data to match installed theme objects.
314
- if ( ! current_user_can( 'install_themes' ) ) {
315
- wp_die( -1 );
316
- }
317
-
318
- // Arguments for all queries.
319
- $wporg_args = array(
320
- 'per_page' => 100,
321
- 'fields' => array(
322
- 'reviews_url' => true, // Explicitly request the reviews URL to be linked from the customizer.
323
- ),
324
- );
325
-
326
- $args = array_merge( $wporg_args, $args );
327
-
328
- if ( '' === $args['search'] && '' === $args['tag'] ) {
329
- $args['browse'] = 'new'; // Sort by latest themes by default.
330
- }
331
-
332
- // Load themes from the .org API.
333
- $themes = themes_api( 'query_themes', $args );
334
- if ( is_wp_error( $themes ) ) {
335
- wp_send_json_error();
336
- }
337
-
338
- // This list matches the allowed tags in wp-admin/includes/theme-install.php.
339
- $themes_allowedtags = array_fill_keys(
340
- array( 'a', 'abbr', 'acronym', 'code', 'pre', 'em', 'strong', 'div', 'p', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'img' ),
341
- array()
342
- );
343
- $themes_allowedtags['a'] = array_fill_keys( array( 'href', 'title', 'target' ), true );
344
- $themes_allowedtags['acronym']['title'] = true;
345
- $themes_allowedtags['abbr']['title'] = true;
346
- $themes_allowedtags['img'] = array_fill_keys( array( 'src', 'class', 'alt' ), true );
347
-
348
- // Prepare a list of installed themes to check against before the loop.
349
- $installed_themes = array();
350
- $wp_themes = wp_get_themes();
351
- foreach ( $wp_themes as $theme ) {
352
- $installed_themes[] = $theme->get_stylesheet();
353
- }
354
- $update_php = network_admin_url( 'update.php?action=install-theme' );
355
-
356
- // Set up properties for themes available on WordPress.org.
357
- foreach ( $themes->themes as &$theme ) {
358
- $theme->install_url = add_query_arg(
359
- array(
360
- 'theme' => $theme->slug,
361
- '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug ),
362
- ),
363
- $update_php
364
- );
365
-
366
- $theme->name = wp_kses( $theme->name, $themes_allowedtags );
367
- $theme->version = wp_kses( $theme->version, $themes_allowedtags );
368
- $theme->description = wp_kses( $theme->description, $themes_allowedtags );
369
- $theme->stars = wp_star_rating(
370
- array(
371
- 'rating' => $theme->rating,
372
- 'type' => 'percent',
373
- 'number' => $theme->num_ratings,
374
- 'echo' => false,
375
- )
376
- );
377
- $theme->num_ratings = number_format_i18n( $theme->num_ratings );
378
- $theme->preview_url = set_url_scheme( $theme->preview_url );
379
-
380
- // Handle themes that are already installed as installed themes.
381
- if ( in_array( $theme->slug, $installed_themes, true ) ) {
382
- $theme->type = 'installed';
383
- } else {
384
- $theme->type = $theme_action;
385
- }
386
-
387
- // Set active based on customized theme.
388
- $theme->active = ( isset( $_POST['customized_theme'] ) && $_POST['customized_theme'] === $theme->slug );
389
-
390
- // Map available theme properties to installed theme properties.
391
- $theme->id = $theme->slug;
392
- $theme->screenshot = array( $theme->screenshot_url );
393
- $theme->authorAndUri = wp_kses( $theme->author['display_name'], $themes_allowedtags );
394
- $theme->compatibleWP = is_wp_version_compatible( $theme->requires ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName
395
- $theme->compatiblePHP = is_php_version_compatible( $theme->requires_php ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName
396
-
397
- if ( isset( $theme->parent ) ) {
398
- $theme->parent = $theme->parent['slug'];
399
- } else {
400
- $theme->parent = false;
401
- }
402
- unset( $theme->slug );
403
- unset( $theme->screenshot_url );
404
- unset( $theme->author );
405
- } // End foreach().
406
- } // End if().
407
-
408
- /**
409
- * Filters the theme data loaded in the customizer.
410
- *
411
- * This allows theme data to be loading from an external source,
412
- * or modification of data loaded from `wp_prepare_themes_for_js()`
413
- * or WordPress.org via `themes_api()`.
414
- *
415
- * @since 4.9.0
416
- *
417
- * @see wp_prepare_themes_for_js()
418
- * @see themes_api()
419
- * @see WP_Customize_Manager::__construct()
420
- *
421
- * @param array $themes Nested array of theme data.
422
- * @param array $args List of arguments, such as page, search term, and tags to query for.
423
- * @param WP_Customize_Manager $manager Instance of Customize manager.
424
- */
425
- $themes = apply_filters( 'customize_load_themes', $themes, $args, $wp_customize );
426
-
427
- wp_send_json_success( $themes );
428
- }
429
-
430
- /* Plugins */
431
- /* Adds Catch Plugins tab in Add Plugin page to show all plugins by Catch Plugins in wordpress.org */
432
- public function add_our_plugins_tab( $tabs ) {
433
- // Add our filter here
434
- $tabs['catchplugins'] = _x( 'Catch Plugins', 'Plugin Installer' );
435
-
436
- return $tabs;
437
- }
438
-
439
- public function catchplugins() {
440
- /* From CORE Start */
441
- global $paged, $tab;
442
- wp_reset_vars( array( 'tab' ) );
443
-
444
- $defined_class = new WP_Plugin_Install_List_Table();
445
- $paged = $defined_class->get_pagenum();
446
-
447
- $per_page = 30;
448
- //$installed_plugins = catch_get_installed_plugins();
449
-
450
- $args = array(
451
- 'page' => $paged,
452
- 'per_page' => $per_page,
453
- 'fields' => array(
454
- 'last_updated' => true,
455
- 'icons' => true,
456
- 'active_installs' => true,
457
- ),
458
- // Send the locale and installed plugin slugs to the API so it can provide context-sensitive results.
459
- 'locale' => get_user_locale(),
460
- //'installed_plugins' => array_keys( $installed_plugins ),
461
- );
462
- /* From CORE End */
463
-
464
- // Add author filter for our plugins
465
- $args['author'] = 'catchplugins';
466
-
467
- return $args;
468
- }
469
-
470
- public function plugins_table() {
471
- global $wp_list_table;
472
- printf(
473
- '<p class="catch-plugins-list">' . __( 'You can use any of our free plugins or premium plugins from <a href="%s" target="_blank">Catch Plugins</a>' ) . '.</p>',
474
- 'https://catchplugins.com/'
475
- );
476
- ?>
477
- <form id="plugin-filter" method="post">
478
- <?php $wp_list_table->display(); ?>
479
- </form>
480
- <?php
481
- }
482
- }
483
-
484
- $catchthemes_theme_plugin = new CatchThemesThemePlugin();
1
+ <?php
2
+
3
+ class CatchThemesThemePlugin {
4
+ public function __construct() {
5
+ remove_action( 'wp_ajax_query-themes', array( $this, 'wp_ajax_query_themes' ), 1 );
6
+ add_action( 'wp_ajax_query-themes', array( $this, 'wp_ajax_custom_query_themes' ), 1 );
7
+
8
+ add_action( 'admin_enqueue_scripts', array( $this, 'our_themes_script' ) );
9
+
10
+ if ( ! is_multisite() ) {
11
+ add_action( 'customize_register', array( $this, 'customize_register' ) );
12
+ }
13
+
14
+ global $wp_customize;
15
+ remove_action( 'wp_ajax_customize_load_themes', array( $wp_customize, 'handle_load_themes_request' ) );
16
+ add_action( 'wp_ajax_customize_load_themes', array( $this, 'handle_load_themes_request' ) );
17
+
18
+ add_filter( 'install_plugins_tabs', array( $this, 'add_our_plugins_tab' ), 1 );
19
+ add_filter( 'install_plugins_table_api_args_catchplugins', array( $this, 'catchplugins' ), 1 );
20
+ add_action( 'install_plugins_catchplugins', array( $this, 'plugins_table' ) );
21
+ }
22
+
23
+ /* Adds Catch Themes tab in Add Theme page to show all themes by Catch Themes in wordpress.org
24
+ * taken from wp-admin/includes/ajax-action.php wp_ajax_query_themes().
25
+ * Ajax handler for getting themes from themes_api().
26
+ *
27
+ * @since 3.9.0
28
+ *
29
+ * @global array $themes_allowedtags
30
+ * @global array $theme_field_defaults
31
+ */
32
+ public function wp_ajax_custom_query_themes() {
33
+ global $themes_allowedtags, $theme_field_defaults;
34
+
35
+ if ( ! current_user_can( 'install_themes' ) ) {
36
+ wp_send_json_error();
37
+ }
38
+
39
+ $args = wp_parse_args(
40
+ wp_unslash( $_REQUEST['request'] ),
41
+ array(
42
+ 'per_page' => 20,
43
+ 'fields' => array_merge(
44
+ (array) $theme_field_defaults,
45
+ array(
46
+ 'reviews_url' => true, // Explicitly request the reviews URL to be linked from the Add Themes screen.
47
+ )
48
+ ),
49
+ )
50
+ );
51
+
52
+ if ( isset( $args['browse'] ) && 'catchthemes' === $args['browse'] && ! isset( $args['user'] ) ) {
53
+ $args['author'] = 'catchthemes';
54
+ unset( $args['browse'] );
55
+ }
56
+
57
+ if ( isset( $args['browse'] ) && 'favorites' === $args['browse'] && ! isset( $args['user'] ) ) {
58
+ $user = get_user_option( 'wporg_favorites' );
59
+ if ( $user ) {
60
+ $args['user'] = $user;
61
+ }
62
+ }
63
+
64
+ $old_filter = isset( $args['browse'] ) ? $args['browse'] : 'search';
65
+
66
+ /** This filter is documented in wp-admin/includes/class-wp-theme-install-list-table.php */
67
+ $args = apply_filters( 'install_themes_table_api_args_' . $old_filter, $args );
68
+
69
+ $api = themes_api( 'query_themes', $args );
70
+
71
+ if ( is_wp_error( $api ) ) {
72
+ wp_send_json_error();
73
+ }
74
+
75
+ $update_php = network_admin_url( 'update.php?action=install-theme' );
76
+ foreach ( $api->themes as &$theme ) {
77
+ $theme->install_url = add_query_arg(
78
+ array(
79
+ 'theme' => $theme->slug,
80
+ '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug ),
81
+ ),
82
+ $update_php
83
+ );
84
+
85
+ if ( current_user_can( 'switch_themes' ) ) {
86
+ if ( is_multisite() ) {
87
+ $theme->activate_url = add_query_arg(
88
+ array(
89
+ 'action' => 'enable',
90
+ '_wpnonce' => wp_create_nonce( 'enable-theme_' . $theme->slug ),
91
+ 'theme' => $theme->slug,
92
+ ),
93
+ network_admin_url( 'themes.php' )
94
+ );
95
+ } else {
96
+ $theme->activate_url = add_query_arg(
97
+ array(
98
+ 'action' => 'activate',
99
+ '_wpnonce' => wp_create_nonce( 'switch-theme_' . $theme->slug ),
100
+ 'stylesheet' => $theme->slug,
101
+ ),
102
+ admin_url( 'themes.php' )
103
+ );
104
+ }
105
+ }
106
+
107
+ if ( ! is_multisite() && current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) {
108
+ $theme->customize_url = add_query_arg(
109
+ array(
110
+ 'return' => urlencode( network_admin_url( 'theme-install.php', 'relative' ) ),
111
+ ),
112
+ wp_customize_url( $theme->slug )
113
+ );
114
+ }
115
+
116
+ $theme->name = wp_kses( $theme->name, $themes_allowedtags );
117
+ $theme->author = wp_kses( $theme->author['display_name'], $themes_allowedtags );
118
+ $theme->version = wp_kses( $theme->version, $themes_allowedtags );
119
+ $theme->description = wp_kses( $theme->description, $themes_allowedtags );
120
+
121
+ $theme->stars = wp_star_rating(
122
+ array(
123
+ 'rating' => $theme->rating,
124
+ 'type' => 'percent',
125
+ 'number' => $theme->num_ratings,
126
+ 'echo' => false,
127
+ )
128
+ );
129
+
130
+ $theme->num_ratings = number_format_i18n( $theme->num_ratings );
131
+ $theme->preview_url = set_url_scheme( $theme->preview_url );
132
+ $theme->compatible_wp = is_wp_version_compatible( $theme->requires );
133
+ $theme->compatible_php = is_php_version_compatible( $theme->requires_php );
134
+
135
+ }
136
+
137
+ wp_send_json_success( $api );
138
+ }
139
+
140
+ public function our_themes_script( $hook_suffix ) {
141
+
142
+ if ( 'theme-install.php' === $hook_suffix ) {
143
+ wp_enqueue_script( 'our-themes-script', plugin_dir_url( __FILE__ ) . '../js/our-themes.js', array( 'jquery' ), '2018-05-16' );
144
+ }
145
+ }
146
+
147
+ /* Add Catch Themes Section in Theme in Customizer */
148
+ public function customize_register( $wp_customize ) {
149
+ $wp_customize->add_section(
150
+ new WP_Customize_Themes_Section(
151
+ $wp_customize,
152
+ 'catchthemes',
153
+ array(
154
+ 'title' => __( 'Themes by CatchThemes', 'catch-themes-demo-import' ),
155
+ 'action' => 'catchthemes',
156
+ 'capability' => 'install_themes',
157
+ 'panel' => 'themes',
158
+ 'priority' => 6,
159
+ )
160
+ )
161
+ );
162
+ }
163
+
164
+
165
+
166
+
167
+ /**
168
+ * Load themes into the theme browsing/installation UI.
169
+ * taken from wp-includes/cllass-wp-customize-manager.php
170
+ * @since 4.9.0
171
+ */
172
+ public function handle_load_themes_request() {
173
+ check_ajax_referer( 'switch_themes', 'nonce' );
174
+ if ( ! current_user_can( 'switch_themes' ) ) {
175
+ wp_die( -1 );
176
+ }
177
+
178
+ if ( empty( $_POST['theme_action'] ) ) {
179
+ wp_send_json_error( 'missing_theme_action' );
180
+ }
181
+ $theme_action = sanitize_key( $_POST['theme_action'] );
182
+ $themes = array();
183
+ $args = array();
184
+
185
+ // Define query filters based on user input.
186
+ if ( ! array_key_exists( 'search', $_POST ) ) {
187
+ $args['search'] = '';
188
+ } else {
189
+ $args['search'] = sanitize_text_field( wp_unslash( $_POST['search'] ) );
190
+ }
191
+
192
+ if ( ! array_key_exists( 'tags', $_POST ) ) {
193
+ $args['tag'] = '';
194
+ } else {
195
+ $args['tag'] = array_map( 'sanitize_text_field', wp_unslash( (array) $_POST['tags'] ) );
196
+ }
197
+
198
+ if ( ! array_key_exists( 'page', $_POST ) ) {
199
+ $args['page'] = 1;
200
+ } else {
201
+ $args['page'] = absint( $_POST['page'] );
202
+ }
203
+
204
+ require_once ABSPATH . 'wp-admin/includes/theme.php';
205
+
206
+ if ( 'installed' === $theme_action ) {
207
+
208
+ // Load all installed themes from wp_prepare_themes_for_js().
209
+ $themes = array( 'themes' => wp_prepare_themes_for_js() );
210
+ foreach ( $themes['themes'] as &$theme ) {
211
+ $theme['type'] = 'installed';
212
+ $theme['active'] = ( isset( $_POST['customized_theme'] ) && $_POST['customized_theme'] === $theme['id'] );
213
+ }
214
+ } elseif ( 'catchthemes' === $theme_action ) {
215
+
216
+ // Load WordPress.org themes from the .org API and normalize data to match installed theme objects.
217
+ if ( ! current_user_can( 'install_themes' ) ) {
218
+ wp_die( -1 );
219
+ }
220
+
221
+ // Arguments for all queries.
222
+ $wporg_args = array(
223
+ 'per_page' => 100,
224
+ 'fields' => array(
225
+ 'reviews_url' => true, // Explicitly request the reviews URL to be linked from the customizer.
226
+ ),
227
+ );
228
+
229
+ $args = array_merge( $wporg_args, $args );
230
+
231
+ if ( '' === $args['search'] && '' === $args['tag'] ) {
232
+ $args['browse'] = 'new'; // Sort by latest themes by default.
233
+ }
234
+
235
+ $args['author'] = 'catchthemes';
236
+
237
+ // Load themes from the .org API.
238
+ $themes = themes_api( 'query_themes', $args );
239
+ if ( is_wp_error( $themes ) ) {
240
+ wp_send_json_error();
241
+ }
242
+
243
+ // This list matches the allowed tags in wp-admin/includes/theme-install.php.
244
+ $themes_allowedtags = array_fill_keys(
245
+ array( 'a', 'abbr', 'acronym', 'code', 'pre', 'em', 'strong', 'div', 'p', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'img' ),
246
+ array()
247
+ );
248
+ $themes_allowedtags['a'] = array_fill_keys( array( 'href', 'title', 'target' ), true );
249
+ $themes_allowedtags['acronym']['title'] = true;
250
+ $themes_allowedtags['abbr']['title'] = true;
251
+ $themes_allowedtags['img'] = array_fill_keys( array( 'src', 'class', 'alt' ), true );
252
+
253
+ // Prepare a list of installed themes to check against before the loop.
254
+ $installed_themes = array();
255
+ $wp_themes = wp_get_themes();
256
+ foreach ( $wp_themes as $theme ) {
257
+ $installed_themes[] = $theme->get_stylesheet();
258
+ }
259
+ $update_php = network_admin_url( 'update.php?action=install-theme' );
260
+
261
+ // Set up properties for themes available on WordPress.org.
262
+ foreach ( $themes->themes as &$theme ) {
263
+ $theme->install_url = add_query_arg(
264
+ array(
265
+ 'theme' => $theme->slug,
266
+ '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug ),
267
+ ),
268
+ $update_php
269
+ );
270
+
271
+ $theme->name = wp_kses( $theme->name, $themes_allowedtags );
272
+ $theme->version = wp_kses( $theme->version, $themes_allowedtags );
273
+ $theme->description = wp_kses( $theme->description, $themes_allowedtags );
274
+ $theme->stars = wp_star_rating(
275
+ array(
276
+ 'rating' => $theme->rating,
277
+ 'type' => 'percent',
278
+ 'number' => $theme->num_ratings,
279
+ 'echo' => false,
280
+ )
281
+ );
282
+ $theme->num_ratings = number_format_i18n( $theme->num_ratings );
283
+ $theme->preview_url = set_url_scheme( $theme->preview_url );
284
+
285
+ // Handle themes that are already installed as installed themes.
286
+ if ( in_array( $theme->slug, $installed_themes, true ) ) {
287
+ $theme->type = 'installed';
288
+ } else {
289
+ $theme->type = $theme_action;
290
+ }
291
+
292
+ // Set active based on customized theme.
293
+ $theme->active = ( isset( $_POST['customized_theme'] ) && $_POST['customized_theme'] === $theme->slug );
294
+
295
+ // Map available theme properties to installed theme properties.
296
+ $theme->id = $theme->slug;
297
+ $theme->screenshot = array( $theme->screenshot_url );
298
+ $theme->authorAndUri = wp_kses( $theme->author['display_name'], $themes_allowedtags );
299
+ $theme->compatibleWP = is_wp_version_compatible( $theme->requires ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName
300
+ $theme->compatiblePHP = is_php_version_compatible( $theme->requires_php ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName
301
+
302
+ if ( isset( $theme->parent ) ) {
303
+ $theme->parent = $theme->parent['slug'];
304
+ } else {
305
+ $theme->parent = false;
306
+ }
307
+ unset( $theme->slug );
308
+ unset( $theme->screenshot_url );
309
+ unset( $theme->author );
310
+ } // End foreach().
311
+ } elseif ( 'wporg' === $theme_action ) {
312
+
313
+ // Load WordPress.org themes from the .org API and normalize data to match installed theme objects.
314
+ if ( ! current_user_can( 'install_themes' ) ) {
315
+ wp_die( -1 );
316
+ }
317
+
318
+ // Arguments for all queries.
319
+ $wporg_args = array(
320
+ 'per_page' => 100,
321
+ 'fields' => array(
322
+ 'reviews_url' => true, // Explicitly request the reviews URL to be linked from the customizer.
323
+ ),
324
+ );
325
+
326
+ $args = array_merge( $wporg_args, $args );
327
+
328
+ if ( '' === $args['search'] && '' === $args['tag'] ) {
329
+ $args['browse'] = 'new'; // Sort by latest themes by default.
330
+ }
331
+
332
+ // Load themes from the .org API.
333
+ $themes = themes_api( 'query_themes', $args );
334
+ if ( is_wp_error( $themes ) ) {
335
+ wp_send_json_error();
336
+ }
337
+
338
+ // This list matches the allowed tags in wp-admin/includes/theme-install.php.
339
+ $themes_allowedtags = array_fill_keys(
340
+ array( 'a', 'abbr', 'acronym', 'code', 'pre', 'em', 'strong', 'div', 'p', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'img' ),
341
+ array()
342
+ );
343
+ $themes_allowedtags['a'] = array_fill_keys( array( 'href', 'title', 'target' ), true );
344
+ $themes_allowedtags['acronym']['title'] = true;
345
+ $themes_allowedtags['abbr']['title'] = true;
346
+ $themes_allowedtags['img'] = array_fill_keys( array( 'src', 'class', 'alt' ), true );
347
+
348
+ // Prepare a list of installed themes to check against before the loop.
349
+ $installed_themes = array();
350
+ $wp_themes = wp_get_themes();
351
+ foreach ( $wp_themes as $theme ) {
352
+ $installed_themes[] = $theme->get_stylesheet();
353
+ }
354
+ $update_php = network_admin_url( 'update.php?action=install-theme' );
355
+
356
+ // Set up properties for themes available on WordPress.org.
357
+ foreach ( $themes->themes as &$theme ) {
358
+ $theme->install_url = add_query_arg(
359
+ array(
360
+ 'theme' => $theme->slug,
361
+ '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug ),
362
+ ),
363
+ $update_php
364
+ );
365
+
366
+ $theme->name = wp_kses( $theme->name, $themes_allowedtags );
367
+ $theme->version = wp_kses( $theme->version, $themes_allowedtags );
368
+ $theme->description = wp_kses( $theme->description, $themes_allowedtags );
369
+ $theme->stars = wp_star_rating(
370
+ array(
371
+ 'rating' => $theme->rating,
372
+ 'type' => 'percent',
373
+ 'number' => $theme->num_ratings,
374
+ 'echo' => false,
375
+ )
376
+ );
377
+ $theme->num_ratings = number_format_i18n( $theme->num_ratings );
378
+ $theme->preview_url = set_url_scheme( $theme->preview_url );
379
+
380
+ // Handle themes that are already installed as installed themes.
381
+ if ( in_array( $theme->slug, $installed_themes, true ) ) {
382
+ $theme->type = 'installed';
383
+ } else {
384
+ $theme->type = $theme_action;
385
+ }
386
+
387
+ // Set active based on customized theme.
388
+ $theme->active = ( isset( $_POST['customized_theme'] ) && $_POST['customized_theme'] === $theme->slug );
389
+
390
+ // Map available theme properties to installed theme properties.
391
+ $theme->id = $theme->slug;
392
+ $theme->screenshot = array( $theme->screenshot_url );
393
+ $theme->authorAndUri = wp_kses( $theme->author['display_name'], $themes_allowedtags );
394
+ $theme->compatibleWP = is_wp_version_compatible( $theme->requires ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName
395
+ $theme->compatiblePHP = is_php_version_compatible( $theme->requires_php ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName
396
+
397
+ if ( isset( $theme->parent ) ) {
398
+ $theme->parent = $theme->parent['slug'];
399
+ } else {
400
+ $theme->parent = false;
401
+ }
402
+ unset( $theme->slug );
403
+ unset( $theme->screenshot_url );
404
+ unset( $theme->author );
405
+ } // End foreach().
406
+ } // End if().
407
+
408
+ /**
409
+ * Filters the theme data loaded in the customizer.
410
+ *
411
+ * This allows theme data to be loading from an external source,
412
+ * or modification of data loaded from `wp_prepare_themes_for_js()`
413
+ * or WordPress.org via `themes_api()`.
414
+ *
415
+ * @since 4.9.0
416
+ *
417
+ * @see wp_prepare_themes_for_js()
418
+ * @see themes_api()
419
+ * @see WP_Customize_Manager::__construct()
420
+ *
421
+ * @param array $themes Nested array of theme data.
422
+ * @param array $args List of arguments, such as page, search term, and tags to query for.
423
+ * @param WP_Customize_Manager $manager Instance of Customize manager.
424
+ */
425
+ $themes = apply_filters( 'customize_load_themes', $themes, $args, $wp_customize );
426
+
427
+ wp_send_json_success( $themes );
428
+ }
429
+
430
+ /* Plugins */
431
+ /* Adds Catch Plugins tab in Add Plugin page to show all plugins by Catch Plugins in wordpress.org */
432
+ public function add_our_plugins_tab( $tabs ) {
433
+ // Add our filter here
434
+ $tabs['catchplugins'] = _x( 'Catch Plugins', 'Plugin Installer' );
435
+
436
+ return $tabs;
437
+ }
438
+
439
+ public function catchplugins() {
440
+ /* From CORE Start */
441
+ global $paged, $tab;
442
+ wp_reset_vars( array( 'tab' ) );
443
+
444
+ $defined_class = new WP_Plugin_Install_List_Table();
445
+ $paged = $defined_class->get_pagenum();
446
+
447
+ $per_page = 30;
448
+ //$installed_plugins = catch_get_installed_plugins();
449
+
450
+ $args = array(
451
+ 'page' => $paged,
452
+ 'per_page' => $per_page,
453
+ 'fields' => array(
454
+ 'last_updated' => true,
455
+ 'icons' => true,
456
+ 'active_installs' => true,
457
+ ),
458
+ // Send the locale and installed plugin slugs to the API so it can provide context-sensitive results.
459
+ 'locale' => get_user_locale(),
460
+ //'installed_plugins' => array_keys( $installed_plugins ),
461
+ );
462
+ /* From CORE End */
463
+
464
+ // Add author filter for our plugins
465
+ $args['author'] = 'catchplugins';
466
+
467
+ return $args;
468
+ }
469
+
470
+ public function plugins_table() {
471
+ global $wp_list_table;
472
+ printf(
473
+ '<p class="catch-plugins-list">' . __( 'You can use any of our free plugins or premium plugins from <a href="%s" target="_blank">Catch Plugins</a>' ) . '.</p>',
474
+ 'https://catchplugins.com/'
475
+ );
476
+ ?>
477
+ <form id="plugin-filter" method="post">
478
+ <?php $wp_list_table->display(); ?>
479
+ </form>
480
+ <?php
481
+ }
482
+ }
483
+
484
+ $catchthemes_theme_plugin = new CatchThemesThemePlugin();
languages/essential-content-types-fr_FR.mo CHANGED
Binary file
languages/essential-content-types-fr_FR.po CHANGED
@@ -1,11 +1,11 @@
1
- # Copyright (C) 2018-2020 Catch Plugins
2
  # This file is distributed under the GNU General Public License v2 or later.
3
  msgid ""
4
  msgstr ""
5
  "Project-Id-Version: Essential Content Types\n"
6
  "Report-Msgid-Bugs-To: https://wordpress.org/tags/_s\n"
7
- "POT-Creation-Date: 2020-12-14 00:28-0500\n"
8
- "PO-Revision-Date: 2020-12-14 00:28-0500\n"
9
  "Last-Translator: Charles Girardin <girardin.charles.57@gmail.com>\n"
10
  "Language-Team: Catch Plugins <info@catchplugins.com>\n"
11
  "Language: fr_FR\n"
@@ -13,7 +13,7 @@ msgstr ""
13
  "Content-Type: text/plain; charset=UTF-8\n"
14
  "Content-Transfer-Encoding: 8bit\n"
15
  "Plural-Forms: nplurals=2; plural=(n > 1);\n"
16
- "X-Generator: Poedit 2.4.2\n"
17
  "X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;esc_attr_e;esc_attr__;_nx;_x;"
18
  "esc_html_e;esc_html__;_ex:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;_n_noop:1,2;"
19
  "__ngettext_noop:1,2;_c,_nc:4c,1,2\n"
1
+ # Copyright (C) 2018-2021 Catch Plugins
2
  # This file is distributed under the GNU General Public License v2 or later.
3
  msgid ""
4
  msgstr ""
5
  "Project-Id-Version: Essential Content Types\n"
6
  "Report-Msgid-Bugs-To: https://wordpress.org/tags/_s\n"
7
+ "POT-Creation-Date: 2021-08-05 10:15-0700\n"
8
+ "PO-Revision-Date: 2021-08-05 10:15-0700\n"
9
  "Last-Translator: Charles Girardin <girardin.charles.57@gmail.com>\n"
10
  "Language-Team: Catch Plugins <info@catchplugins.com>\n"
11
  "Language: fr_FR\n"
13
  "Content-Type: text/plain; charset=UTF-8\n"
14
  "Content-Transfer-Encoding: 8bit\n"
15
  "Plural-Forms: nplurals=2; plural=(n > 1);\n"
16
+ "X-Generator: Poedit 2.4.3\n"
17
  "X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;esc_attr_e;esc_attr__;_nx;_x;"
18
  "esc_html_e;esc_html__;_ex:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;_n_noop:1,2;"
19
  "__ngettext_noop:1,2;_c,_nc:4c,1,2\n"
languages/essential-content-types.pot CHANGED
@@ -1,11 +1,11 @@
1
- # Copyright (C) 2018-2020 Catch Plugins
2
  # This file is distributed under the GNU General Public License v2 or later.
3
  #, fuzzy
4
  msgid ""
5
  msgstr ""
6
  "Project-Id-Version: Essential Content Types\n"
7
  "Report-Msgid-Bugs-To: https://wordpress.org/tags/_s\n"
8
- "POT-Creation-Date: 2020-12-14 00:28-0500\n"
9
  "PO-Revision-Date: 2016-12-12 09:23-0500\n"
10
  "Last-Translator: Sakin Shrestha <info@catchplugins.com>\n"
11
  "Language-Team: Catch Plugins <info@catchplugins.com>\n"
@@ -14,7 +14,7 @@ msgstr ""
14
  "Content-Type: text/plain; charset=UTF-8\n"
15
  "Content-Transfer-Encoding: 8bit\n"
16
  "Plural-Forms: nplurals=2; plural=n != 1;\n"
17
- "X-Generator: Poedit 2.4.2\n"
18
  "X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;esc_attr_e;esc_attr__;_nx;_x;"
19
  "esc_html_e;esc_html__;_ex:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;_n_noop:1,2;"
20
  "__ngettext_noop:1,2;_c,_nc:4c,1,2\n"
1
+ # Copyright (C) 2018-2021 Catch Plugins
2
  # This file is distributed under the GNU General Public License v2 or later.
3
  #, fuzzy
4
  msgid ""
5
  msgstr ""
6
  "Project-Id-Version: Essential Content Types\n"
7
  "Report-Msgid-Bugs-To: https://wordpress.org/tags/_s\n"
8
+ "POT-Creation-Date: 2021-08-05 10:15-0700\n"
9
  "PO-Revision-Date: 2016-12-12 09:23-0500\n"
10
  "Last-Translator: Sakin Shrestha <info@catchplugins.com>\n"
11
  "Language-Team: Catch Plugins <info@catchplugins.com>\n"
14
  "Content-Type: text/plain; charset=UTF-8\n"
15
  "Content-Transfer-Encoding: 8bit\n"
16
  "Plural-Forms: nplurals=2; plural=n != 1;\n"
17
+ "X-Generator: Poedit 2.4.3\n"
18
  "X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;esc_attr_e;esc_attr__;_nx;_x;"
19
  "esc_html_e;esc_html__;_ex:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;_n_noop:1,2;"
20
  "__ngettext_noop:1,2;_c,_nc:4c,1,2\n"