├── LICENSE
├── README.md
├── css
├── images
│ ├── drag.png
│ ├── gray-grad.png
│ └── wpspin_light.gif
├── jquery-ui
│ └── smoothness
│ │ ├── images
│ │ ├── ui-bg_flat_0_aaaaaa_40x100.png
│ │ ├── ui-bg_flat_75_ffffff_40x100.png
│ │ ├── ui-bg_glass_55_fbf9ee_1x400.png
│ │ ├── ui-bg_glass_65_ffffff_1x400.png
│ │ ├── ui-bg_glass_75_dadada_1x400.png
│ │ ├── ui-bg_glass_75_e6e6e6_1x400.png
│ │ ├── ui-bg_glass_95_fef1ec_1x400.png
│ │ ├── ui-bg_highlight-soft_75_cccccc_1x100.png
│ │ ├── ui-icons_222222_256x240.png
│ │ ├── ui-icons_2e83ff_256x240.png
│ │ ├── ui-icons_454545_256x240.png
│ │ ├── ui-icons_888888_256x240.png
│ │ └── ui-icons_cd0a0a_256x240.png
│ │ └── jquery-ui-zoninator.css
└── zoninator.css
├── functions.php
├── js
├── jquery.ui.touch-punch.min.js
└── zoninator.js
├── language
├── zoninator-he_IL.mo
└── zoninator-he_IL.po
├── rector.php
├── src
├── class-zoninator-api-controller.php
├── class-zoninator-api-filter-search.php
├── class-zoninator-api-schema-converter.php
├── class-zoninator-api.php
├── class-zoninator-zoneposts-widget.php
├── class-zoninator.php
└── zoninator_rest
│ ├── class-zoninator-rest-bootstrap.php
│ ├── class-zoninator-rest-classloader.php
│ ├── class-zoninator-rest-controller.php
│ ├── class-zoninator-rest-environment.php
│ ├── class-zoninator-rest-exception.php
│ ├── class-zoninator-rest-expect.php
│ ├── class-zoninator-rest-model.php
│ ├── class-zoninator-rest-type.php
│ ├── controller
│ ├── bundle
│ │ └── class-zoninator-rest-controller-bundle-builder.php
│ ├── class-zoninator-rest-controller-action.php
│ ├── class-zoninator-rest-controller-bundle.php
│ ├── class-zoninator-rest-controller-crud.php
│ ├── class-zoninator-rest-controller-extension.php
│ ├── class-zoninator-rest-controller-model.php
│ ├── class-zoninator-rest-controller-route.php
│ └── class-zoninator-rest-controller-settings.php
│ ├── data
│ ├── class-zoninator-rest-data-mapper.php
│ ├── class-zoninator-rest-data-serializer.php
│ └── store
│ │ ├── class-zoninator-rest-data-store-abstract.php
│ │ ├── class-zoninator-rest-data-store-builder.php
│ │ ├── class-zoninator-rest-data-store-customposttype.php
│ │ ├── class-zoninator-rest-data-store-nil.php
│ │ └── class-zoninator-rest-data-store-option.php
│ ├── field
│ ├── class-zoninator-rest-field-declaration.php
│ └── declaration
│ │ └── class-zoninator-rest-field-declaration-builder.php
│ ├── interfaces
│ ├── class-zoninator-rest-interfaces-builder.php
│ ├── class-zoninator-rest-interfaces-classloader.php
│ ├── class-zoninator-rest-interfaces-controller.php
│ ├── class-zoninator-rest-interfaces-model.php
│ ├── class-zoninator-rest-interfaces-registrable.php
│ ├── class-zoninator-rest-interfaces-type.php
│ ├── controller
│ │ └── class-zoninator-rest-interfaces-controller-bundle.php
│ ├── data
│ │ └── class-zoninator-rest-interfaces-data-store.php
│ ├── model
│ │ ├── class-zoninator-rest-interfaces-model-collection.php
│ │ └── class-zoninator-rest-interfaces-model-declaration.php
│ └── permissions
│ │ └── class-zoninator-rest-interfaces-permissions-provider.php
│ ├── model
│ ├── class-zoninator-rest-model-collection.php
│ ├── class-zoninator-rest-model-declaration.php
│ ├── class-zoninator-rest-model-definition.php
│ ├── class-zoninator-rest-model-settings.php
│ ├── class-zoninator-rest-model-validationdata.php
│ ├── declaration
│ │ └── class-zoninator-rest-model-declaration-settings.php
│ └── definition
│ │ └── class-zoninator-rest-model-definition-builder.php
│ ├── permissions
│ └── class-zoninator-rest-permissions-any.php
│ └── type
│ ├── class-zoninator-rest-type-array.php
│ ├── class-zoninator-rest-type-boolean.php
│ ├── class-zoninator-rest-type-integer.php
│ ├── class-zoninator-rest-type-nullable.php
│ ├── class-zoninator-rest-type-number.php
│ ├── class-zoninator-rest-type-registry.php
│ ├── class-zoninator-rest-type-string.php
│ └── class-zoninator-rest-type-typedarray.php
└── zoninator.php
/README.md:
--------------------------------------------------------------------------------
1 | # Zone Manager (Zoninator)
2 |
3 | Stable tag: 0.10.1
4 | Requires at least: 5.9
5 | Tested up to: 6.6
6 | Requires PHP: 7.4
7 | License: GPLv2 or later
8 | License URI: https://www.gnu.org/licenses/gpl-2.0.html
9 | Tags: zones, post order, post list, posts, order, zonination, content curation, curation, content management
10 | Contributors: batmoo, automattic, wpcomvip, pkevan, matthumphreys, potatomaster, jblz, nickdaugherty, betzster, garyj
11 |
12 | Content curation made easy! Create "zones" then add and order your content!
13 |
14 | ## Description
15 |
16 | This plugin is designed to help you curate your content. It lets you assign and order stories within zones that you create, edit, and delete, and display those groupings of related stories on your site.
17 |
18 | This plugin was originally built by [Mohammad Jangda](http://digitalize.ca) in conjunction with [William Davis](http://wpdavis.com/) and the [Bangor Daily News](http://www.bangordailynews.com/).
19 |
20 | ### Features
21 |
22 | * Add, edit, and delete zones.
23 | * Add and remove posts (or any custom post type) to or from zones.
24 | * Order posts in any given zone.
25 | * Limit capabilities on who can add, edit, and delete zones vs add content to zones.
26 | * Locking mechanism, so only one user can edit a zone at a time (to avoid conflicts).
27 | * Idle control, so people can't keep the zone locked.
28 |
29 | ## Installation
30 |
31 | 1. Unzip contents and upload to the `/wp-content/plugins/` directory
32 | 2. Activate the plugin through the 'Plugins' menu in WordPress
33 | 3. Go to Dashboard > Zones to create and manage your zones, and easily search for and add existing posts.
34 | 4. Use the plugin's handy API functions to add zones to your theme that retrieve and display your content. Or, for those who are a bit code-averse, go to Appearance-Widgets and add Zone Posts widgets to display your zone posts in your sidebar or footer. The widget will pull the posts from the chosen zone.
35 |
36 | ### Usage examples
37 |
38 | You can work with a zone's posts either as an array or a WP_Query object.
39 |
40 | **WP_Query**
41 |
42 | ~~~php
43 | $zone_query = z_get_zone_query( 'homepage' );
44 | if ( $zone_query->have_posts() ) :
45 | while ( $zone_query->have_posts() ) : $zone_query->the_post();
46 | echo '
' . get_the_title() . '';
47 | endwhile;
48 | endif;
49 | wp_reset_query();
50 | ~~~
51 |
52 | **Posts Array**
53 |
54 | ~~~php
55 | $zone_posts = z_get_posts_in_zone( 'homepage' );
56 | foreach ( $zone_posts as $zone_post ) :
57 | echo '' . get_the_title( $zone_post->ID ) . '';
58 | endforeach;
59 | ~~~
60 |
61 | ## Function Reference
62 |
63 | Get an array of all zones:
64 |
65 | ~~~php
66 | z_get_zones()
67 | ~~~
68 |
69 | Get a single zone, accepts either ID or slug:
70 |
71 | ~~~php
72 | z_get_zone( $zone )
73 | ~~~
74 |
75 | Get an array of ordered posts in a given zone, accepts either ID or slug:
76 |
77 | ~~~php
78 | z_get_posts_in_zone( $zone )
79 | ~~~
80 |
81 | Get a WP_Query object for a given zone, accepts either ID or slug:
82 |
83 | ~~~php
84 | z_get_zone_query( $zone );
85 | ~~~
86 |
87 | More functions listed in `functions.php`.
88 |
89 | ## Frequently Asked Questions
90 |
91 | ### How do I disable the locking feature?
92 |
93 | You can use a filter:
94 |
95 | ~~~php
96 | add_filter( 'zoninator_zone_max_lock_period', 'z_disable_zoninator_locks' );
97 | ~~~
98 |
99 | ### How do I change the locking feature settings?
100 |
101 | Filter the following and change according to your needs:
102 |
103 | * Number of seconds a lock is valid for, default `30`: `zoninator_zone_lock_period`
104 | * Max idle time in seconds: `zoninator_zone_max_lock_period`
105 |
106 | ## Changelog
107 |
108 | Please visit the [changelog](https://github.com/automattic/zoninator/blob/trunk/CHANGELOG.md).
109 |
110 | ## Screenshots
111 |
112 | 1. Create and manage your zones and content through a fairly intuitive and familiar interface.
113 | 
114 | 2. Zone editing
115 | 
116 | 3. Use the Zone Posts widget in the widgets area.
117 | 
118 | 4. Output of the zone posts widgets.
119 | 
120 |
--------------------------------------------------------------------------------
/css/images/drag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/zoninator/7352d0572052bcae506781c3b41d89970ef044d3/css/images/drag.png
--------------------------------------------------------------------------------
/css/images/gray-grad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/zoninator/7352d0572052bcae506781c3b41d89970ef044d3/css/images/gray-grad.png
--------------------------------------------------------------------------------
/css/images/wpspin_light.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/zoninator/7352d0572052bcae506781c3b41d89970ef044d3/css/images/wpspin_light.gif
--------------------------------------------------------------------------------
/css/jquery-ui/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/zoninator/7352d0572052bcae506781c3b41d89970ef044d3/css/jquery-ui/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png
--------------------------------------------------------------------------------
/css/jquery-ui/smoothness/images/ui-bg_flat_75_ffffff_40x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/zoninator/7352d0572052bcae506781c3b41d89970ef044d3/css/jquery-ui/smoothness/images/ui-bg_flat_75_ffffff_40x100.png
--------------------------------------------------------------------------------
/css/jquery-ui/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/zoninator/7352d0572052bcae506781c3b41d89970ef044d3/css/jquery-ui/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png
--------------------------------------------------------------------------------
/css/jquery-ui/smoothness/images/ui-bg_glass_65_ffffff_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/zoninator/7352d0572052bcae506781c3b41d89970ef044d3/css/jquery-ui/smoothness/images/ui-bg_glass_65_ffffff_1x400.png
--------------------------------------------------------------------------------
/css/jquery-ui/smoothness/images/ui-bg_glass_75_dadada_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/zoninator/7352d0572052bcae506781c3b41d89970ef044d3/css/jquery-ui/smoothness/images/ui-bg_glass_75_dadada_1x400.png
--------------------------------------------------------------------------------
/css/jquery-ui/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/zoninator/7352d0572052bcae506781c3b41d89970ef044d3/css/jquery-ui/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png
--------------------------------------------------------------------------------
/css/jquery-ui/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/zoninator/7352d0572052bcae506781c3b41d89970ef044d3/css/jquery-ui/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png
--------------------------------------------------------------------------------
/css/jquery-ui/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/zoninator/7352d0572052bcae506781c3b41d89970ef044d3/css/jquery-ui/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png
--------------------------------------------------------------------------------
/css/jquery-ui/smoothness/images/ui-icons_222222_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/zoninator/7352d0572052bcae506781c3b41d89970ef044d3/css/jquery-ui/smoothness/images/ui-icons_222222_256x240.png
--------------------------------------------------------------------------------
/css/jquery-ui/smoothness/images/ui-icons_2e83ff_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/zoninator/7352d0572052bcae506781c3b41d89970ef044d3/css/jquery-ui/smoothness/images/ui-icons_2e83ff_256x240.png
--------------------------------------------------------------------------------
/css/jquery-ui/smoothness/images/ui-icons_454545_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/zoninator/7352d0572052bcae506781c3b41d89970ef044d3/css/jquery-ui/smoothness/images/ui-icons_454545_256x240.png
--------------------------------------------------------------------------------
/css/jquery-ui/smoothness/images/ui-icons_888888_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/zoninator/7352d0572052bcae506781c3b41d89970ef044d3/css/jquery-ui/smoothness/images/ui-icons_888888_256x240.png
--------------------------------------------------------------------------------
/css/jquery-ui/smoothness/images/ui-icons_cd0a0a_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/zoninator/7352d0572052bcae506781c3b41d89970ef044d3/css/jquery-ui/smoothness/images/ui-icons_cd0a0a_256x240.png
--------------------------------------------------------------------------------
/css/zoninator.css:
--------------------------------------------------------------------------------
1 | .wrap.zoninator-page {
2 | width: 95%;
3 | }
4 |
5 | #zoninator-wrap {
6 | overflow: hidden;
7 | clear: both;
8 | position: relative;
9 | margin-top: 15px;
10 | }
11 |
12 | #zoninator-wrap .delete {
13 | color: #BC0B0B;
14 | }
15 |
16 | /** Tabs **/
17 | .zone-tabs-container {
18 | width: 30%;
19 | float: left;
20 | }
21 |
22 | .zone-tabs-wrapper .zone-tab {
23 | display: block;
24 | background: #fff;
25 | float: none;
26 | font-size: 12px;
27 | line-height: 16px;
28 | padding: 4px 14px 6px;
29 | margin: 0px;
30 | font-weight: normal;
31 | }
32 |
33 | .zone-tabs-wrapper .zone-tab:hover,
34 | .zone-tabs-wrapper .zone-tab:active,
35 | .zone-tabs-wrapper .zone-tab-active {
36 | background: #0074a2;
37 | color: #fff;
38 | margin-bottom: -1px;
39 | }
40 |
41 | .nav-tab-active {
42 | float: none;
43 | }
44 |
45 | /** Edit **/
46 | #zone-edit-wrapper {
47 | padding: 10px;
48 | overflow: hidden;
49 | border: 1px solid #ccc;
50 | -moz-border-radius: 4px;
51 | -wekbit-border-radius: 4px;
52 | -ms-border-radius: 4px;
53 | border-radius: 4px;
54 | background: url(images/gray-grad.png) repeat-x scroll left top #dfdfdf;
55 |
56 | width: 67%;
57 | float: right;
58 | }
59 |
60 | #zone-edit-wrapper .form-wrap {
61 | margin: 0;
62 | }
63 |
64 | #zone-edit-wrapper .form-field {
65 | padding: 0;
66 | }
67 |
68 | #zone-edit-wrapper .form-field.error {
69 | padding: 0 10px 5px;
70 | }
71 |
72 | #zone-edit-wrapper .form-field input,
73 | #zone-edit-wrapper .form-field textarea {
74 | width: 98%;
75 | }
76 |
77 | #zone-edit-wrapper .submit-field {
78 | width: 95%;
79 | }
80 |
81 | #zone-edit-wrapper .submit-field .submitdelete {
82 | float: right;
83 | }
84 |
85 | #zone-info-readonly label {
86 | font-weight: bold;
87 | }
88 |
89 | #zone-info-readonly span {
90 | }
91 |
92 | /** Zone Posts **/
93 | .zone-info-col {
94 | border-bottom: 1px dotted #333;
95 | padding-bottom: 15px;
96 | margin-bottom: 15px;
97 | }
98 |
99 | .zone-posts-col {
100 | background: #fff;
101 | border: 1px solid #ccc;
102 | -moz-border-radius: 4px;
103 | -wekbit-border-radius: 4px;
104 | -ms-border-radius: 4px;
105 | border-radius: 4px;
106 |
107 | padding: 10px;
108 | }
109 |
110 | .zone-posts-col h3 {
111 | margin: 3px 0;
112 | }
113 |
114 | .zone-posts-wrapper.loading {
115 | background: url(images/wpspin_light.gif) no-repeat right top;
116 | }
117 |
118 | .zone-posts-wrapper.readonly {
119 | }
120 |
121 | .zone-posts-wrapper.readonly .zone-search-wrapper,
122 | .zone-posts-wrapper.readonly .row-actions {
123 | display: none !important;
124 | }
125 |
126 | .zone-posts-wrapper.readonly .zone-post {
127 | cursor: default !important;
128 | }
129 |
130 | .zone-posts-wrapper.readonly .zone-post:hover .zone-post-position {
131 | background: none;
132 | text-indent: 0;
133 | }
134 |
135 | .zone-posts-list {
136 | margin: 10px 0;
137 | }
138 |
139 | .zone-posts-list .ui-state-highlight {
140 | margin-bottom: 5px;
141 | -moz-border-radius: 4px;
142 | -wekbit-border-radius: 4px;
143 | -ms-border-radius: 4px;
144 | border-radius: 4px;
145 | }
146 |
147 | .zone-posts-list.ui-sortable .zone-post {
148 | cursor: move;
149 | }
150 |
151 | .zone-post {
152 | padding: 5px 0 5px 10px;
153 | margin-bottom: 5px;
154 | font-size: 12px;
155 | line-height: 15px;
156 | font-weight: bold;
157 | color: #444;
158 | overflow: hidden;
159 |
160 | border: 1px dotted #eee;
161 | -moz-border-radius: 4px;
162 | -wekbit-border-radius: 4px;
163 | -ms-border-radius: 4px;
164 | border-radius: 4px;
165 |
166 | background: #f5f5f5;
167 | }
168 |
169 | .zone-post table {
170 | width: 100%;
171 | }
172 |
173 | .zone-post-col {
174 | }
175 |
176 | .zone-post:hover .zone-post-position {
177 | background: url(images/drag.png) no-repeat left center;
178 | text-indent: -9999px;
179 | }
180 |
181 | .zone-post-position {
182 | font-size: 24px;
183 | line-height: 32px;
184 | font-weight: normal;
185 | text-align: center;
186 | width: 32px;
187 | }
188 |
189 | /*
190 | .zone-post-handle {
191 | display: block;
192 | background: url(images/drag.png) no-repeat center center;
193 | width: 32px;
194 | height: 32px;
195 |
196 | float: right;
197 | visibility: hidden;
198 | }
199 | */
200 | .zone-post-status {
201 | color: #999;
202 | }
203 |
204 | .zone-post .row-actions {
205 | font-size: 11px;
206 | font-weight: normal;
207 | }
208 |
209 | .zone-post:hover .row-actions,
210 | .zone-post.loading .row-actions,
211 | .zone-post:hover .zone-post-handle,
212 | .zone-post.loading .zone-post-handle {
213 | visibility: visible;
214 | }
215 |
216 | .zone-post.loading .zone-post-position {
217 | background: url(images/wpspin_light.gif) no-repeat center center;
218 | text-indent: -9999px;
219 | }
220 |
221 | .zone-search-wrapper {
222 | padding: 7px 0;
223 | border-bottom: 1px dotted #999;
224 | }
225 |
226 | .zone-posts-save-input {
227 | padding: 7px 0;
228 | overflow: hidden;
229 | }
230 |
231 | #zone-posts-save {
232 | float: left;
233 | width: 125px;
234 | }
235 |
236 | .zone-posts-save-info {
237 | float: left;
238 | margin: 0 0 0 10px;
239 | padding: 0 10px;
240 | line-height: 28px;
241 | color: #666;
242 | }
243 |
244 | .notice-error { color: #a94442; background-color: #f2dede; }
245 |
246 | .notice-success { color: #3c763d; background-color: #dff0d8; }
247 |
248 | .notice-info { color: #0f1d79; background-color: #d9edf7; }
249 |
250 | #zone-post-search {
251 | width: 99%;
252 | }
253 |
254 | #zone-post-latest {
255 | width: 98%;
256 | }
257 |
258 | /** Autocomplete **/
259 | .ui-autocomplete-input.loading {
260 | background: url(images/wpspin_light.gif) no-repeat right center;
261 | }
262 |
263 | .ui-autocomplete {
264 | min-width: 330px;
265 | max-width: 600px;
266 | font-size: 13px;
267 | line-height: 17px;
268 | }
269 |
270 | .ui-autocomplete .ui-corner-all {
271 | padding-top: 5px;
272 | padding-bottom: 5px;
273 | border-bottom: 1px dotted #eee;
274 | cursor: pointer;
275 | overflow: hidden;
276 | }
277 |
278 | .ui-autocomplete .title {
279 | display: block;
280 | width: 80%;
281 | float: left;
282 | }
283 |
284 | .ui-autocomplete .date {
285 | text-transform: uppercase;
286 | color: #666;
287 | font-size: 11px;
288 | clear: both;
289 | float: left;
290 | }
291 |
292 | .ui-autocomplete .type {
293 | text-transform: uppercase;
294 | color: #666;
295 | font-size: 11px;
296 | width: 19%;
297 | margin-left: 1%;
298 | float: right;
299 | text-align: right;
300 | }
301 |
302 | .ui-autocomplete .status {
303 | font-size: 10px;
304 | width: 19%;
305 | margin-left: 1%;
306 | float: right;
307 | text-align: right;
308 | }
309 |
310 | .zone-advanced-search-filters-wrapper {
311 | display: none;
312 | }
313 |
314 | .zone-advanced-search-filters-heading {
315 | float: right;
316 | font-weight: bold;
317 | }
318 |
319 | .zone-toggle-advanced-search {
320 | cursor: pointer;
321 | }
322 |
--------------------------------------------------------------------------------
/functions.php:
--------------------------------------------------------------------------------
1 | get_zones();
16 | }
17 |
18 | /**
19 | * @param $zone int|string ID or Slug of the zone
20 | * @return array Zone object
21 | */
22 | function z_get_zone( $zone ) {
23 | return z_get_zoninator()->get_zone( $zone );
24 | }
25 |
26 | /**
27 | * @param $zone int|string ID or Slug of the zone
28 | * @param $args array override default zoninator args
29 | * @return array List of orders post objects
30 | */
31 | function z_get_posts_in_zone( $zone, $args = array() ) {
32 | return z_get_zoninator()->get_zone_posts( $zone, $args );
33 | }
34 |
35 | /**
36 | * @param $zone int|string ID or Slug of the zone
37 | * @return WP_Query List of orders post objects
38 | */
39 | function z_get_zone_query( $zone, $args = array() ) {
40 | return z_get_zoninator()->get_zone_query( $zone, $args );
41 | }
42 |
43 | /**
44 | * @param $zone int|string ID or Slug of the zone
45 | * @param $post_id int ID of the post (or, null if in The Loop)
46 | * @return array|false Returns next post relative to post_id for the given zone
47 | */
48 | function z_get_next_post_in_zone( $zone, $post_id = 0 ) {
49 | $post_id = z_get_loop_post_id_or_default( $post_id );
50 | return z_get_zoninator()->get_next_post_in_zone( $zone, $post_id );
51 | }
52 |
53 | /**
54 | * @param $zone int|string ID or Slug of the zone
55 | * @param $post_id int ID of the post (or, null if in The Loop)
56 | * @return array|false Returns previous post relative to post_id for the given zone
57 | */
58 | function z_get_prev_post_in_zone( $zone, $post_id = 0 ) {
59 | $post_id = z_get_loop_post_id_or_default( $post_id );
60 | return z_get_zoninator()->get_prev_post_in_zone( $zone, $post_id );
61 | }
62 |
63 | /**
64 | * @param $post_id int ID of the post (or, null if in The Loop)
65 | * @return array List of of zones that the given post is in
66 | */
67 | function z_get_post_zones( $post_id = 0 ) {
68 | $post_id = z_get_loop_post_id_or_default( $post_id );
69 | return z_get_zoninator()->get_zones_for_post( $post_id );
70 | }
71 |
72 | function z_get_loop_post_id_or_default( $post_id = 0 ) {
73 | if ( ! $post_id ) {
74 | global $post;
75 | if ( $post && isset( $post->ID ) ) {
76 | $post_id = $post->ID;
77 | }
78 | }
79 |
80 | return $post_id;
81 | }
82 |
83 | /**
84 | * Handy function to disable the locking mechanism
85 | */
86 | function z_disable_zoninator_locks() {
87 | return -1;
88 | }
89 |
90 | // (Should probably publicly expose set_zone_posts as well, e.g. if we wanted to add a metabox on the Edit Post page)
91 |
--------------------------------------------------------------------------------
/js/jquery.ui.touch-punch.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery UI Touch Punch 0.2.2
3 | *
4 | * Copyright 2011, Dave Furfero
5 | * Dual licensed under the MIT or GPL Version 2 licenses.
6 | *
7 | * Depends:
8 | * jquery.ui.widget.js
9 | * jquery.ui.mouse.js
10 | */
11 | (function(b){b.support.touch="ontouchend" in document;if(!b.support.touch){return;}var c=b.ui.mouse.prototype,e=c._mouseInit,a;function d(g,h){if(g.originalEvent.touches.length>1){return;}g.preventDefault();var i=g.originalEvent.changedTouches[0],f=document.createEvent("MouseEvents");f.initMouseEvent(h,true,true,window,1,i.screenX,i.screenY,i.clientX,i.clientY,false,false,false,false,0,null);g.target.dispatchEvent(f);}c._touchStart=function(g){var f=this;if(a||!f._mouseCapture(g.originalEvent.changedTouches[0])){return;}a=true;f._touchMoved=false;d(g,"mouseover");d(g,"mousemove");d(g,"mousedown");};c._touchMove=function(f){if(!a){return;}this._touchMoved=true;d(f,"mousemove");};c._touchEnd=function(f){if(!a){return;}d(f,"mouseup");d(f,"mouseout");if(!this._touchMoved){d(f,"click");}a=false;};c._mouseInit=function(){var f=this;f.element.bind("touchstart",b.proxy(f,"_touchStart")).bind("touchmove",b.proxy(f,"_touchMove")).bind("touchend",b.proxy(f,"_touchEnd"));e.call(f);};})(jQuery);
12 |
--------------------------------------------------------------------------------
/language/zoninator-he_IL.mo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Automattic/zoninator/7352d0572052bcae506781c3b41d89970ef044d3/language/zoninator-he_IL.mo
--------------------------------------------------------------------------------
/language/zoninator-he_IL.po:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "Project-Id-Version: Zoninator 1.0.0\n"
4 | "Report-Msgid-Bugs-To: http://pojo.me/\n"
5 | "POT-Creation-Date: 2014-06-09 17:04+0200\n"
6 | "PO-Revision-Date: 2014-06-09 17:13+0200\n"
7 | "Last-Translator: Ariel K \n"
8 | "Language-Team: Pojo.me Team \n"
9 | "Language: he_IL\n"
10 | "MIME-Version: 1.0\n"
11 | "Content-Type: text/plain; charset=UTF-8\n"
12 | "Content-Transfer-Encoding: 8bit\n"
13 | "X-Poedit-KeywordsList: __;_e;__ngettext:1,2;_n:1,2;__ngettext_noop:1,2;"
14 | "_n_noop:1,2;_c,_nc:4c,1,2;_x:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;_ex:1,2c;"
15 | "esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c\n"
16 | "X-Poedit-Basepath: .\n"
17 | "X-Poedit-SourceCharset: UTF-8\n"
18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
19 | "X-Generator: Poedit 1.5.5\n"
20 | "X-Poedit-SearchPath-0: ..\n"
21 |
22 | #: ../widget.zone-posts.php:11
23 | msgid "Use this widget to display a list of posts from any zone."
24 | msgstr "יש להשתמש בוידג'ט הזה כדי להציג רשימת פוסטים מאזור כלשהו."
25 |
26 | #: ../widget.zone-posts.php:21
27 | msgid "Zone Posts"
28 | msgstr "אזור פוסטים"
29 |
30 | #: ../widget.zone-posts.php:99
31 | msgid "You need to create at least one zone before you use this widget!"
32 | msgstr "צריך ליצור אזור אחד לפחות לפני שניתן להשתמש בוידג'ט זה!"
33 |
34 | #: ../widget.zone-posts.php:108
35 | msgid "Zone:"
36 | msgstr "אזור:"
37 |
38 | #: ../widget.zone-posts.php:111
39 | msgid "-- Select a zone --"
40 | msgstr "--בחירת אזור--"
41 |
42 | #: ../widget.zone-posts.php:125
43 | msgid "Show zone description in widget"
44 | msgstr "להציג תיאור אזור בוידג'ט"
45 |
46 | #: ../zoninator.php:81
47 | msgid "The zone was successfully created."
48 | msgstr "האזור נוצר בהצלחה."
49 |
50 | #: ../zoninator.php:82
51 | msgid "The zone was successfully updated."
52 | msgstr "אזור עודכן בהצלחה."
53 |
54 | #: ../zoninator.php:83
55 | msgid "The zone was successfully deleted."
56 | msgstr "אזור נמחק בהצלחה"
57 |
58 | #: ../zoninator.php:84
59 | msgid "Sorry, something went wrong! Please try again?"
60 | msgstr "מצטערים, משהו פה שגוי! יש לנסות שוב."
61 |
62 | #: ../zoninator.php:85
63 | #, php-format
64 | msgid ""
65 | "Sorry, this zone is in use by %s and is currently locked. Please try again "
66 | "later."
67 | msgstr ""
68 | "מצטערים, אזור זה נמצא בשימוש על ידי %s ולכן הוא נעול ברגע זה. יש לנסות שוב "
69 | "מאוחר יותר."
70 |
71 | #: ../zoninator.php:86
72 | msgid ""
73 | "Sorry, you have reached the maximum idle limit and will now be redirected to "
74 | "the Dashboard."
75 | msgstr "מצטערים, המערכת לא זיהתה פעילות לאחרונה, הינך מופנה אל לוח הבקרה."
76 |
77 | #: ../zoninator.php:102 ../zoninator.php:153 ../zoninator.php:258
78 | msgid "Zones"
79 | msgstr "אזורים"
80 |
81 | #: ../zoninator.php:153
82 | msgid "Zoninator"
83 | msgstr "ניהול אזורים"
84 |
85 | #: ../zoninator.php:165
86 | msgid "another user"
87 | msgstr "משתמש אחר"
88 |
89 | #: ../zoninator.php:271
90 | msgid "Edit Zone"
91 | msgstr "עריכת אזור"
92 |
93 | #: ../zoninator.php:284 ../zoninator.php:286
94 | msgid "Add New"
95 | msgstr "הוספת חדש"
96 |
97 | #: ../zoninator.php:368 ../zoninator.php:411
98 | msgid "Name"
99 | msgstr "שם"
100 |
101 | #: ../zoninator.php:374 ../zoninator.php:417
102 | msgid "Slug"
103 | msgstr "מזהה"
104 |
105 | #: ../zoninator.php:381 ../zoninator.php:423
106 | msgid "Description"
107 | msgstr "תיאור"
108 |
109 | #: ../zoninator.php:395
110 | msgid "Save"
111 | msgstr "שמירה"
112 |
113 | #: ../zoninator.php:398
114 | msgid "Delete"
115 | msgstr "מחיקה"
116 |
117 | #: ../zoninator.php:442
118 | msgid "Zone Content"
119 | msgstr "תוכן אזור"
120 |
121 | #: ../zoninator.php:457
122 | msgid ""
123 | "To create a zone, enter a name (and any other info) to to left and click "
124 | "\"Save\". You can then choose content items to add to the zone."
125 | msgstr ""
126 | "כדי ליצור אזור, יש להזין שם (ומידע נוסף אם צריך) וללחוץ על \"שמירה\". לאחר "
127 | "מכן ניתן לבחור פריטי תוכן ולהוסיף לאזור."
128 |
129 | #: ../zoninator.php:493
130 | msgid "Click and drag to change the position of this item."
131 | msgstr "יש ללחוץ ולגרור כדי לשנות את המיקום של פריט זה."
132 |
133 | #: ../zoninator.php:500 ../zoninator.php:502
134 | msgid "Opens in new window"
135 | msgstr "פתיחה בלשונית חדשה"
136 |
137 | #: ../zoninator.php:500
138 | msgid "Edit"
139 | msgstr "עריכה"
140 |
141 | #: ../zoninator.php:501
142 | msgid "Remove this item from the zone"
143 | msgstr "למחוק פריט זה מהאזור"
144 |
145 | #: ../zoninator.php:501
146 | msgid "Remove"
147 | msgstr "הסרה"
148 |
149 | #: ../zoninator.php:502
150 | msgid "View"
151 | msgstr "תצוגה"
152 |
153 | #: ../zoninator.php:518
154 | msgid "Hide"
155 | msgstr "להסתיר"
156 |
157 | #: ../zoninator.php:518
158 | msgid "Show Filters"
159 | msgstr "להציג סינון"
160 |
161 | #: ../zoninator.php:529
162 | msgid "Filter:"
163 | msgstr "סינון:"
164 |
165 | #: ../zoninator.php:532
166 | msgid "Show all Categories"
167 | msgstr "הצגת כל הקטגוריות"
168 |
169 | #: ../zoninator.php:609
170 | msgid "No results found"
171 | msgstr "לא נמצאו תוצאות"
172 |
173 | #: ../zoninator.php:611
174 | #, php-format
175 | msgid "Choose post from %s"
176 | msgstr "לבחור פוסט מ %s"
177 |
178 | #: ../zoninator.php:613 ../zoninator.php:645
179 | msgid "Choose a post"
180 | msgstr "בחירת פוסט"
181 |
182 | #: ../zoninator.php:643
183 | msgid "Add Recent Content"
184 | msgstr "להוסיף תוכן אחרון"
185 |
186 | #: ../zoninator.php:660
187 | msgid "Search for content"
188 | msgstr "חיפוש בתוכן"
189 |
190 | #: ../zoninator.php:662
191 | msgid ""
192 | "Enter a term or phrase in the text box above to search for and add content "
193 | "to this zone."
194 | msgstr "יש להזין מונח או ביטוי בתיבת הטקסט כדי לחפש ולהוסיף תוכן לאזור זה."
195 |
196 | #: ../zoninator.php:835
197 | msgid "(no title)"
198 | msgstr "(ללא כותרת)"
199 |
200 | #: ../zoninator.php:881
201 | msgid "Slug is a required field."
202 | msgstr "מזהה פוסט הינו שדה חובה"
203 |
204 | #: ../zoninator.php:926 ../zoninator.php:949
205 | msgid "Sorry, that zone doesn't exist."
206 | msgstr "מצטערים, אזור זה לא קיים."
207 |
208 | #: ../zoninator.php:943
209 | msgid "Sorry, we couldn't delete the zone."
210 | msgstr "מצטערים, לא ניתן למחוק אזור זה."
211 |
212 | #: ../zoninator.php:1306
213 | msgid "Sorry, you're not supposed to do that..."
214 | msgstr "מצטערים, איך לך הרשאות לעשות זאת..."
215 |
216 | #: ../zoninator.php:1340
217 | msgid "Invalid zone supplied"
218 | msgstr "הוזן אזור לא תקין"
219 |
220 | #: ../zoninator.php:1346
221 | msgid "No zone posts found"
222 | msgstr "לא נמצא אזור פוסטים"
223 |
--------------------------------------------------------------------------------
/rector.php:
--------------------------------------------------------------------------------
1 | withPaths(
22 | array(
23 | __DIR__ . '/src',
24 | __DIR__ . '/tests',
25 | __DIR__ . '/functions.php',
26 | __DIR__ . '/zoninator.php',
27 | )
28 | )
29 | ->withSkip(
30 | array(
31 | LongArrayToShortArrayRector::class,
32 | // Need a better understanding of intent in this file before switching out the use of empty().
33 | DisallowedEmptyRuleFixerRector::class => array(
34 | __DIR__ . '/src/zoninator_rest/type/class-zoninator-rest-type-registry.php',
35 | ),
36 | // Child classes can legitimately relax the visibility of a method, so while this
37 | // might not be desirable, changing them from public to the original parent-defined
38 | // protected would count as a breaking change.
39 | MakeInheritedMethodVisibilitySameAsParentRector::class,
40 | )
41 | )
42 | ->withPhpSets( php74: true )
43 | // Changes from later PHP Sets that are backwards compatible:
44 | ->withRules(
45 | array(
46 | // 8.0
47 | ConsistentImplodeRector::class,
48 | OptionalParametersAfterRequiredRector::class,
49 | RemoveParentCallWithoutParentRector::class,
50 | // Backfilled from PHP 8.0 into WP 5.9, so these can be used.
51 | StrContainsRector::class,
52 | StrEndsWithRector::class,
53 | StrStartsWithRector::class,
54 |
55 | // 8.1
56 | NullToStrictStringFuncCallArgRector::class,
57 |
58 | // 8.2
59 | VariableInStringInterpolationFixerRector::class,
60 |
61 | // 8.3
62 | AddOverrideAttributeToOverriddenMethodsRector::class,
63 |
64 | // 8.4
65 | ExplicitNullableParamTypeRector::class,
66 | )
67 | )
68 | ->withPreparedSets( deadCode: true, codeQuality: true, instanceOf: true, codingStyle: true )
69 | ->withTypeCoverageLevel( 1 );
70 |
--------------------------------------------------------------------------------
/src/class-zoninator-api-controller.php:
--------------------------------------------------------------------------------
1 | [\d]+)';
11 |
12 | public const ZONE_ITEM_POSTS_URL_REGEX = '/zones/(?P[\d]+)/posts';
13 |
14 | public const ZONE_ITEM_POSTS_POST_REGEX = '/zones/(?P[\d]+)/posts/(?P\d+)';
15 |
16 | public const INVALID_ZONE_ID = 'invalid-zone-id';
17 |
18 | public const INVALID_POST_ID = 'invalid-post-id';
19 |
20 | public const ZONE_ID_POST_ID_REQUIRED = 'zone-id-post-id-required';
21 |
22 | public const ZONE_ID_POST_IDS_REQUIRED = 'zone-id-post-ids-required';
23 |
24 | public const ZONE_ID_REQUIRED = 'zone-id-required';
25 |
26 | public const ZONE_FEED_ERROR = 'zone-feed-error';
27 |
28 | public const TERM_REQUIRED = 'term-required';
29 |
30 | public const PERMISSION_DENIED = 'permission-denied';
31 |
32 | public const ZONE_NOT_FOUND = 'zone-not-found';
33 |
34 | public const POST_NOT_FOUND = 'post-not-found';
35 |
36 | public const INVALID_ZONE_SETTINGS = 'invalid-zone-settings';
37 |
38 | /**
39 | * Instance
40 | *
41 | * @var Zoninator
42 | */
43 | private $instance;
44 |
45 | /**
46 | * Key Value Translation array
47 | *
48 | * @var array
49 | */
50 | private $translations;
51 |
52 | /**
53 | * Zoninator_Api_Controller constructor.
54 | *
55 | * @param string $base Base.
56 | * @param Zoninator $instance Instance.
57 | */
58 | public function __construct( $instance ) {
59 | $this->instance = $instance;
60 | $this->base = '/';
61 | }
62 |
63 | /**
64 | * Set up this controller
65 | */
66 | public function setup() {
67 | $this->translations = array(
68 | self::ZONE_NOT_FOUND => __( 'Zone not found', 'zoninator' ),
69 | self::INVALID_POST_ID => __( 'Invalid post id', 'zoninator' ),
70 | self::INVALID_ZONE_ID => __( 'Zone not found', 'zoninator' ),
71 | self::ZONE_ID_POST_ID_REQUIRED => __( 'post id and zone id required', 'zoninator' ),
72 | );
73 |
74 | $this->add_route( 'zones' )
75 | ->add_action(
76 | $this->action( 'index', 'get_zones' )
77 | ->permissions( 'get_zones_permissions_check' )
78 | )
79 | ->add_action(
80 | $this->action( 'create', 'create_zone' )
81 | ->permissions( 'add_zone_permissions_check' )
82 | ->args( '_params_for_create_zone' )
83 | );
84 |
85 | $this->add_route( 'zones/(?P[\d]+)' )
86 | ->add_action(
87 | $this->action( 'update', 'update_zone' )
88 | ->permissions( 'update_zone_permissions_check' )
89 | ->args( '_params_for_update_zone' )
90 | )
91 | ->add_action(
92 | $this->action( 'delete', 'delete_zone' )
93 | ->permissions( 'update_zone_permissions_check' )
94 | );
95 |
96 | $this->add_route( 'zones/(?P[\d]+)/posts' )
97 | ->add_action(
98 | $this->action( 'index', 'get_zone_posts' )
99 | ->permissions( 'get_zone_posts_permissions_check' )
100 | ->args( '_get_zone_id_param' )
101 | )
102 | ->add_action(
103 | $this->action( 'update', 'update_zone_posts' )
104 | ->permissions( 'update_zone_permissions_check' )
105 | ->args( '_get_zone_post_rest_route_params' )
106 | );
107 |
108 | $this->add_route( 'zones/(?P[\d]+)/lock' )
109 | ->add_action(
110 | $this->action( 'update', 'zone_update_lock' )
111 | ->permissions( 'update_zone_permissions_check' )
112 | ->args( '_get_zone_id_param' )
113 | );
114 | }
115 |
116 | /**
117 | * Get the list of all zones
118 | *
119 | * @param WP_REST_Request $request Full data about the request.
120 | * @return WP_Error|WP_REST_Response
121 | */
122 | public function get_zones( $request ) {
123 | $results = $this->instance->get_zones();
124 |
125 | if ( is_wp_error( $results ) ) {
126 | return $this->bad_request(
127 | array(
128 | 'message' => $results->get_error_message(),
129 | )
130 | );
131 | }
132 |
133 | $zones = array_map( array( $this, '_filter_zone_properties' ), $results );
134 |
135 | return $this->ok( $zones );
136 | }
137 |
138 | /**
139 | * Create a Zone
140 | *
141 | * @param WP_REST_Request $request Full data about the request.
142 | * @return WP_Error|WP_REST_Response
143 | */
144 | public function create_zone( $request ) {
145 | $name = $this->get_param( $request, 'name', '' );
146 | $slug = $this->get_param( $request, 'slug', $name );
147 | $description = $this->get_param( $request, 'description', '' );
148 |
149 | $result = $this->instance->insert_zone(
150 | $slug,
151 | $name,
152 | array(
153 | 'description' => $description,
154 | )
155 | );
156 |
157 | if ( is_wp_error( $result ) ) {
158 | return $this->bad_request(
159 | array(
160 | 'message' => $result->get_error_message(),
161 | )
162 | );
163 | }
164 |
165 | $zone = $this->instance->get_zone( $result['term_id'] );
166 |
167 | return $this->created( $this->_filter_zone_properties( $zone ) );
168 | }
169 |
170 | /**
171 | * Update zone details
172 | *
173 | * @param WP_REST_Request $request Full data about the request.
174 | * @return WP_Error|WP_REST_Response
175 | */
176 | public function update_zone( $request ) {
177 | $zone_id = $this->get_param( $request, 'zone_id', 0, 'absint' );
178 | $name = $this->get_param( $request, 'name', '' );
179 | $slug = $this->get_param( $request, 'slug', '' );
180 | $description = $this->get_param( $request, 'description', '', 'strip_tags' );
181 |
182 | $zone = $this->instance->get_zone( $zone_id );
183 | $update_params = array();
184 |
185 | if ( ! $zone ) {
186 | return $this->not_found( $this->translations[ self::INVALID_ZONE_ID ] );
187 | }
188 |
189 | if ( $name ) {
190 | $update_params['name'] = $name;
191 | }
192 |
193 | if ( $slug ) {
194 | $update_params['slug'] = $slug;
195 | }
196 |
197 | if ( $description ) {
198 | $update_params['details'] = array( 'description' => $description );
199 | }
200 |
201 | $result = $this->instance->update_zone( $zone, $update_params );
202 |
203 | if ( is_wp_error( $result ) ) {
204 | return $this->bad_request(
205 | array(
206 | 'message' => $result->get_error_message(),
207 | )
208 | );
209 | }
210 |
211 | return $this->ok( array( 'success' => true ) );
212 | }
213 |
214 | /**
215 | * Delete a zone
216 | *
217 | * @param WP_REST_Request $request Full data about the request.
218 | * @return WP_Error|WP_REST_Response
219 | */
220 | public function delete_zone( $request ) {
221 | $zone_id = $this->get_param( $request, 'zone_id', 0, 'absint' );
222 |
223 | $zone = $this->instance->get_zone( $zone_id );
224 |
225 | if ( ! $zone ) {
226 | return $this->not_found( $this->translations[ self::INVALID_ZONE_ID ] );
227 | }
228 |
229 | $result = $this->instance->delete_zone( $zone );
230 |
231 | if ( is_wp_error( $result ) ) {
232 | return $this->bad_request(
233 | array(
234 | 'message' => $result->get_error_message(),
235 | )
236 | );
237 | }
238 |
239 | return $this->ok( array( 'success' => true ) );
240 | }
241 |
242 | /**
243 | * Get zone posts
244 | *
245 | * @param WP_REST_Request $request Full data about the request.
246 | * @return WP_Error|WP_REST_Response
247 | */
248 | public function get_zone_posts( $request ) {
249 | $zone_id = $this->get_param( $request, 'zone_id', 0, 'absint' );
250 |
251 | if ( empty( $zone_id ) || false === $this->instance->get_zone( $zone_id ) ) {
252 | return $this->not_found( $this->translations[ self::INVALID_ZONE_ID ] );
253 | }
254 |
255 | $results = $this->instance->get_zone_feed( $zone_id );
256 |
257 | if ( is_wp_error( $results ) ) {
258 | return $this->_bad_request( self::ZONE_FEED_ERROR, $results->get_error_message() );
259 | }
260 |
261 | return new WP_REST_Response( $results, 200 );
262 | }
263 |
264 | /**
265 | * Sets the posts for a zone
266 | *
267 | * @param WP_REST_Request $request Full data about the request.]
268 | * @return WP_Error|WP_REST_Response
269 | */
270 | public function update_zone_posts( $request ) {
271 | $zone_id = $this->get_param( $request, 'zone_id', 0, 'absint' );
272 | $post_ids = $this->get_param( $request, 'post_ids', array() );
273 |
274 | if ( ! $this->instance->get_zone( $zone_id ) ) {
275 | return $this->not_found( $this->translations[ self::INVALID_ZONE_ID ] );
276 | }
277 |
278 | $posts = array_map( 'get_post', $post_ids );
279 |
280 | if ( count( $posts ) !== count( array_filter( $posts ) ) ) {
281 | return $this->bad_request(
282 | array(
283 | 'message' => $this->translations[ self::INVALID_POST_ID ],
284 | )
285 | );
286 | }
287 |
288 | $result = $this->instance->add_zone_posts( $zone_id, $posts );
289 |
290 | if ( is_wp_error( $result ) ) {
291 | return $this->respond( $result, 500 );
292 | }
293 |
294 | return $this->ok( array( 'success' => true ) );
295 | }
296 |
297 | /**
298 | * Update the zone's lock
299 | *
300 | * @param WP_REST_Request $request Full data about the request.
301 | * @return WP_Error|WP_REST_Response
302 | */
303 | public function zone_update_lock( $request ) {
304 | $zone_id = $this->get_param( $request, 'zone_id', 0, 'absint' );
305 | if ( ! $zone_id ) {
306 | return $this->_bad_request( self::ZONE_ID_REQUIRED, __( 'zone id required', 'zoninator' ) );
307 | }
308 |
309 | $zone = $this->instance->get_zone( $zone_id );
310 | if ( ! $zone ) {
311 | return $this->not_found( $this->translations[ self::INVALID_ZONE_ID ] );
312 | }
313 |
314 | $zone_locked = $this->instance->is_zone_locked( $zone );
315 | if ( $zone_locked ) {
316 | $locking_user = get_userdata( $zone_locked );
317 | return new WP_REST_Response(
318 | array(
319 | 'zone_id' => $this->instance->get_zone_id( $zone ),
320 | 'blocked' => true,
321 | ),
322 | 400
323 | );
324 | }
325 |
326 | $this->instance->lock_zone( $zone_id );
327 | return new WP_REST_Response(
328 | array(
329 | 'zone_id' => $this->instance->get_zone_id( $zone ),
330 | 'timeout' => $this->instance->zone_lock_period,
331 | 'max_lock_period' => $this->instance->zone_max_lock_period,
332 | ),
333 | 200
334 | );
335 | }
336 |
337 | /**
338 | * Check if a given request has access to the zones index.
339 | *
340 | * @param WP_REST_Request $request Full data about the request.
341 | * @return WP_Error|bool
342 | */
343 | public function get_zones_permissions_check( $request ) {
344 | return true;
345 | }
346 |
347 | /**
348 | * Check if a given request has access to add new zones.
349 | *
350 | * @param WP_REST_Request $request Full data about the request.
351 | * @return WP_Error|bool
352 | */
353 | public function add_zone_permissions_check( $request ) {
354 | return $this->_permissions_check( 'insert' );
355 | }
356 |
357 | /**
358 | * Check if a given request has access to get zone posts.
359 | *
360 | * @param WP_REST_Request $request Full data about the request.
361 | * @return WP_Error|bool
362 | */
363 | public function get_zone_posts_permissions_check( $request ) {
364 | return true;
365 | }
366 |
367 | /**
368 | * Check if a given request has access to update a zone.
369 | *
370 | * @param WP_REST_Request $request Full data about the request.
371 | * @return WP_Error|bool
372 | */
373 | public function update_zone_permissions_check( $request ) {
374 | $zone_id = $this->get_param( $request, 'zone_id', 0, 'absint' );
375 | return $this->_permissions_check( 'update', $zone_id );
376 | }
377 |
378 | public function is_numeric( $item ) {
379 | // see https://github.com/WP-API/WP-API/issues/1520 on why we do not use is_numeric directly.
380 | return is_numeric( $item );
381 | }
382 |
383 | public function is_numeric_array( $items ) {
384 | return count( $items ) === count( array_filter( $items, 'is_numeric' ) );
385 | }
386 |
387 | public function sanitize_string( $item ) {
388 | return htmlentities( stripslashes( $item ) );
389 | }
390 |
391 | /**
392 | * @param WP_REST_Request $rest_request
393 | * @param string $key Parameter name.
394 | * @param string $default_value
395 | * @param string $sanitize_callback
396 | *
397 | * @return array|mixed|null|string
398 | */
399 | private function get_param( $rest_request, $key, $default_value = '', $sanitize_callback = '' ) {
400 | $value = $rest_request->get_param( $key );
401 | $value = empty( $value ) ? $default_value : $value;
402 |
403 | if ( is_callable( $sanitize_callback ) ) {
404 | $value = ( is_array( $value ) ) ? array_map( $sanitize_callback, $value ) : call_user_func( $sanitize_callback, $value );
405 | }
406 |
407 | return $value;
408 | }
409 |
410 | public function _params_for_create_zone() { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
411 | return array(
412 | 'name' => array(
413 | 'type' => 'string',
414 | 'sanitize_callback' => array( $this, 'sanitize_string' ),
415 | 'default' => '',
416 | 'required' => false,
417 | ),
418 | 'slug' => array(
419 | 'type' => 'string',
420 | 'sanitize_callback' => array( $this, 'sanitize_string' ),
421 | 'default' => '',
422 | 'required' => false,
423 | ),
424 | 'description' => array(
425 | 'type' => 'string',
426 | 'sanitize_callback' => array( $this, 'sanitize_string' ),
427 | 'default' => '',
428 | 'required' => false,
429 | ),
430 | );
431 | }
432 |
433 | public function _params_for_update_zone() { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
434 | return array(
435 | 'name' => array(
436 | 'type' => 'string',
437 | 'sanitize_callback' => array( $this, 'sanitize_string' ),
438 | 'required' => false,
439 | ),
440 | 'slug' => array(
441 | 'type' => 'string',
442 | 'sanitize_callback' => array( $this, 'sanitize_string' ),
443 | 'required' => false,
444 | ),
445 | 'description' => array(
446 | 'type' => 'string',
447 | 'sanitize_callback' => array( $this, 'sanitize_string' ),
448 | 'required' => false,
449 | ),
450 | );
451 | }
452 |
453 | public function _get_zone_id_param() { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
454 | return array(
455 | 'zone_id' => array(
456 | 'type' => 'integer',
457 | 'validate_callback' => array( $this, 'is_numeric' ),
458 | 'sanitize_callback' => 'absint',
459 | 'required' => true,
460 | ),
461 | );
462 | }
463 |
464 | public function _get_zone_post_rest_route_params() { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
465 | $zone_params = $this->_get_zone_id_param();
466 | return array_merge(
467 | array(
468 | 'post_ids' => array(
469 | 'type' => 'array',
470 | 'validate_callback' => array( $this, 'is_numeric_array' ),
471 | 'required' => true,
472 | 'items' => array( 'type' => 'integer' ),
473 | ),
474 | ),
475 | $zone_params
476 | );
477 | }
478 |
479 | public function _filter_zone_properties( $zone ) { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
480 | $data = $zone->to_array();
481 |
482 | return array(
483 | 'term_id' => $data['term_id'],
484 | 'slug' => $data['slug'],
485 | 'name' => $data['name'],
486 | 'description' => $data['description'],
487 | );
488 | }
489 |
490 | private function _bad_request( $code, $message ) { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
491 | return new WP_Error( $code, $message, array( 'status' => 400 ) );
492 | }
493 |
494 | /**
495 | * @param $zone_id
496 | * @return bool|WP_Error
497 | */
498 | private function _permissions_check( $action, $zone_id = null ) { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
499 | if ( ! $this->instance->check( $action, $zone_id ) ) {
500 | return new WP_Error( self::PERMISSION_DENIED, __( "Sorry, you're not supposed to do that...", 'zoninator' ) );
501 | }
502 |
503 | return true;
504 | }
505 | }
506 |
--------------------------------------------------------------------------------
/src/class-zoninator-api-filter-search.php:
--------------------------------------------------------------------------------
1 | get_environment();
25 | return array(
26 | $env->field( 'term', __( 'search term', 'zoninator' ) )
27 | ->with_type( $env->type( 'string' ) )
28 | ->with_before_set( array( $this, 'strip_slashes' ) )
29 | ->with_required( true )
30 | ->with_default( '' ),
31 | $env->field( 'cat', __( 'filter by category', 'zoninator' ) )
32 | ->with_type( $env->type( 'uint' ) )
33 | ->with_validations( array( $this, 'is_numeric' ) )
34 | ->with_default( 0 ),
35 | $env->field( 'date', __( 'only get posts after this date (format YYYY-mm-dd)', 'zoninator' ) )
36 | ->with_type( $env->type( 'string' ) )
37 | ->with_before_set( array( $this, 'date_before_set' ) )
38 | ->with_default( '' ),
39 | $env->field( 'limit', __( 'limit results', 'zoninator' ) )
40 | ->with_type( $env->type( 'uint' ) )
41 | ->with_before_set( array( $this, 'strip_slashes' ) )
42 | ->with_default( Zoninator()->posts_per_page ),
43 | $env->field( 'exclude', __( 'post_ids to exclude', 'zoninator' ) )
44 | ->with_type( $env->type( 'array:uint' ) ),
45 | );
46 | }
47 |
48 | /**
49 | * Is Numeric
50 | *
51 | * @param mixed $item The item.
52 | * @return bool
53 | */
54 | public function is_numeric( $item ) {
55 | // see https://github.com/WP-API/WP-API/issues/1520 on why we do not use is_numeric directly.
56 | return is_numeric( $item );
57 | }
58 |
59 | /**
60 | * Strip slashes
61 | *
62 | * @param mixed $item Item.
63 | * @return string
64 | */
65 | public function strip_slashes( $item ) {
66 | // see https://github.com/WP-API/WP-API/issues/1520 on why we do not use stripslashes directly.
67 | return stripslashes( $item );
68 | }
69 |
70 | public function strip_tags( $item ) {
71 | return wp_strip_all_tags( $item );
72 | }
73 |
74 | public function date_before_set( $model, $item ) {
75 | return $this->strip_tags( $this->strip_slashes( $item ) );
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/class-zoninator-api-schema-converter.php:
--------------------------------------------------------------------------------
1 | get_fields();
12 | $properties = array();
13 | $required = array();
14 | foreach ( $fields as $field_declaration ) {
15 | /**
16 | * Our declaration
17 | *
18 | * @var Zoninator_REST_Field_Declaration $field_declaration
19 | */
20 | $properties[ $field_declaration->get_data_transfer_name() ] = $field_declaration->as_item_schema_property();
21 | if ( $field_declaration->is_required() ) {
22 | $required[] = $field_declaration->get_data_transfer_name();
23 | }
24 | }
25 |
26 | $schema = array(
27 | '$schema' => 'http://json-schema.org/schema#',
28 | 'title' => $model_definition->get_name(),
29 | 'type' => 'object',
30 | // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- applying a core filter.
31 | 'properties' => (array) apply_filters( 'rest_api_schema_properties', $properties, $model_definition ),
32 | );
33 |
34 | if ( array() !== $required ) {
35 | $schema['required'] = $required;
36 | }
37 |
38 | return $schema;
39 | }
40 |
41 | /**
42 | * As Schema
43 | *
44 | * @param Zoninator_REST_Model $model_definition Def.
45 | * @return array
46 | */
47 | public function as_args( $model_definition ) {
48 | $fields = $model_definition->get_fields();
49 | $result = array();
50 | foreach ( $fields as $field_declaration ) {
51 | $type_schema = $field_declaration->get_type()->schema();
52 | /**
53 | * Our declaration
54 | *
55 | * @var Zoninator_REST_Field_Declaration $field_declaration
56 | */
57 | $arg = array(
58 | 'description' => $field_declaration->get_description(),
59 | 'type' => $type_schema['type'],
60 | 'required' => $field_declaration->is_required(),
61 | );
62 |
63 | if ( ! $field_declaration->is_required() ) {
64 | $arg['default'] = $field_declaration->get_default_value();
65 | }
66 |
67 | $result[ $field_declaration->get_data_transfer_name() ] = $arg;
68 | }
69 |
70 | return $result;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/class-zoninator-api.php:
--------------------------------------------------------------------------------
1 | instance = $instance;
25 | add_action( 'rest_api_init', array( $this, 'rest_api' ) );
26 | }
27 |
28 | /**
29 | * Rest Api.
30 | */
31 | public function rest_api() {
32 | include_once dirname( ZONINATOR_FILE ) . '/src/zoninator_rest/class-zoninator-rest-bootstrap.php';
33 | $this->bootstrap = Zoninator_REST_Bootstrap::create()->load();
34 | include_once __DIR__ . '/class-zoninator-api-schema-converter.php';
35 | include_once __DIR__ . '/class-zoninator-api-filter-search.php';
36 | include_once __DIR__ . '/class-zoninator-api-controller.php';
37 | $env = $this->bootstrap->environment();
38 |
39 | $env->define_model( 'Zoninator_Api_Filter_Search' );
40 |
41 | $env->rest_api( 'zoninator/v1' )
42 | ->add_endpoint( new Zoninator_Api_Controller( $this->instance ) );
43 | $env->start();
44 | return $this;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/class-zoninator-zoneposts-widget.php:
--------------------------------------------------------------------------------
1 | 'widget-zone-posts',
12 | 'description' => __( 'Use this widget to display a list of posts from any zone.', 'zoninator' ),
13 | );
14 |
15 | $this->alt_option_name = 'widget_zone_posts';
16 | add_action( 'save_post', array( &$this, 'flush_widget_cache' ) );
17 | add_action( 'deleted_post', array( &$this, 'flush_widget_cache' ) );
18 | add_action( 'switch_theme', array( &$this, 'flush_widget_cache' ) );
19 |
20 | parent::__construct(
21 | false,
22 | __( 'Zone Posts', 'zoninator' ),
23 | $widget_ops
24 | );
25 | }
26 |
27 | public function widget( $args, $instance ) {
28 | $cache_key = 'widget-zone-posts';
29 | $cache = wp_cache_get( $cache_key, 'widget' );
30 |
31 | if ( ! is_array( $cache ) ) {
32 | $cache = array();
33 | }
34 |
35 | if ( isset( $cache[ $args['widget_id'] ] ) ) {
36 | echo wp_kses_post( $cache[ $args['widget_id'] ] );
37 | return;
38 | }
39 |
40 | ob_start();
41 |
42 | $zone_id = $instance['zone_id'] ?: 0;
43 | $show_description = $instance['show_description'] ? 1 : 0;
44 | if ( ! $zone_id ) {
45 | return;
46 | }
47 |
48 | $zone = z_get_zone( $zone_id );
49 | if ( ! $zone ) {
50 | return;
51 | }
52 |
53 | $posts = z_get_posts_in_zone( $zone_id );
54 | if ( empty( $posts ) ) {
55 | return;
56 | }
57 |
58 | ?>
59 |
60 |
61 | name ) . wp_kses_post( $args['after_title'] ); ?>
62 |
63 | description ) && $show_description ) : ?>
64 | description ); ?>
65 |
68 |
69 |
80 |
81 |
82 | 0,
100 | 'show_description' => 0,
101 | )
102 | );
103 | $instance['zone_id'] = absint( $new_instance['zone_id'] );
104 | $instance['show_description'] = $new_instance['show_description'] ? 1 : 0;
105 | $this->flush_widget_cache();
106 | $alloptions = wp_cache_get( 'alloptions', 'options' );
107 | if ( isset( $alloptions['widget-zone-posts'] ) ) {
108 | delete_option( 'widget-zone-posts' );
109 | }
110 |
111 | return $instance;
112 | }
113 |
114 | public function flush_widget_cache() {
115 | $cache_key = 'widget-zone-posts';
116 |
117 | // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- breaking change to rename this filter with the correct prefix.
118 | $block_save_cache_seconds = absint( apply_filters( 'zone_posts_widget_block_save_cache_seconds', 5 ) );
119 | if ( $block_save_cache_seconds > 0 ) {
120 | // This key will block updating the cache for n seconds so the following cache delete can propagate
121 | // phpcs:ignore -- WordPressVIPMinimum.Performance.LowExpiryCacheTime.CacheTimeUndetermined -- not changing behavior now.
122 | wp_cache_set( $cache_key . '-save_blocked', 1, 'widget', $block_save_cache_seconds );
123 | }
124 |
125 | wp_cache_delete( $cache_key, 'widget' );
126 | }
127 |
128 | public function form( $instance ) {
129 | // select - zone
130 | // checkbox - show description
131 | $zones = z_get_zones();
132 | if ( empty( $zones ) ) {
133 | esc_html_e( 'You need to create at least one zone before you use this widget!', 'zoninator' );
134 | return;
135 | }
136 |
137 | $zone_id = isset( $instance['zone_id'] ) ? absint( $instance['zone_id'] ) : 0;
138 | $show_description = isset( $instance['show_description'] ) ? (bool) $instance['show_description'] : true;
139 | ?>
140 |
141 |
142 |
143 |
156 |
157 |
158 |
159 |
163 |
164 | class_loader = $class_loader;
44 | }
45 |
46 | /**
47 | * Check compatibility of PHP Version.
48 | *
49 | * @return bool
50 | */
51 | public static function is_compatible() {
52 | return version_compare( phpversion(), self::MINIMUM_PHP_VERSION, '>=' );
53 | }
54 |
55 | /**
56 | * Get Base Dir
57 | *
58 | * @return string
59 | */
60 | public static function get_base_dir() {
61 | return untrailingslashit( __DIR__ );
62 | }
63 |
64 | /**
65 | * Create a Bootstrap, unless we are using a really early php version (< 5.3.0)
66 | *
67 | * @param Zoninator_REST_Interfaces_Classloader|null $class_loader The class loader to use.
68 | * @return Zoninator_REST_Bootstrap|null
69 | */
70 | public static function create( $class_loader = null ) {
71 | if ( empty( $class_loader ) ) {
72 | include_once __DIR__ . '/interfaces/class-zoninator-rest-interfaces-classloader.php';
73 | include_once __DIR__ . '/class-zoninator-rest-classloader.php';
74 | $prefix = str_replace( '_Bootstrap', '', self::class );
75 | $base_dir = self::get_base_dir();
76 | $class_loader = new Zoninator_REST_Classloader( $prefix, $base_dir );
77 | }
78 |
79 | return new self( $class_loader );
80 | }
81 |
82 | /**
83 | * Run the app
84 | *
85 | * @return bool
86 | */
87 | public function run() {
88 | if ( ! self::is_compatible() ) {
89 | return false;
90 | }
91 |
92 | $this->load()
93 | ->environment()->start();
94 | return true;
95 | }
96 |
97 | /**
98 | * Optional: Instead of calling load() you can
99 | * register as an auto-loader
100 | *
101 | * @return Zoninator_REST_Bootstrap $this
102 | */
103 | public function register_autoload() {
104 | if ( function_exists( 'spl_autoload_register' ) ) {
105 | spl_autoload_register( array( $this->class_loader(), 'load_class' ), true );
106 | }
107 |
108 | return $this;
109 | }
110 |
111 | /**
112 | * Loads all classes
113 | *
114 | * @return Zoninator_REST_Bootstrap $this
115 | * @throws Exception In case a class/file is not found.
116 | */
117 | public function load() {
118 | $this->class_loader()
119 | ->load_class( 'Interfaces_Data_Store' )
120 | ->load_class( 'Interfaces_Registrable' )
121 | ->load_class( 'Interfaces_Type' )
122 | ->load_class( 'Interfaces_Model' )
123 | ->load_class( 'Interfaces_Builder' )
124 | ->load_class( 'Interfaces_Model_Collection' )
125 | ->load_class( 'Interfaces_Controller' )
126 | ->load_class( 'Interfaces_Controller_Bundle' )
127 | ->load_class( 'Interfaces_Permissions_Provider' )
128 | ->load_class( 'Exception' )
129 | ->load_class( 'Expect' )
130 | ->load_class( 'Environment' )
131 | ->load_class( 'Type' )
132 | ->load_class( 'Type_String' )
133 | ->load_class( 'Type_Integer' )
134 | ->load_class( 'Type_Number' )
135 | ->load_class( 'Type_Boolean' )
136 | ->load_class( 'Type_Array' )
137 | ->load_class( 'Type_TypedArray' )
138 | ->load_class( 'Type_Nullable' )
139 | ->load_class( 'Type_Registry' )
140 | ->load_class( 'Data_Store_Nil' )
141 | ->load_class( 'Data_Store_Abstract' )
142 | ->load_class( 'Data_Store_CustomPostType' )
143 | ->load_class( 'Data_Store_Option' )
144 | ->load_class( 'Permissions_Any' )
145 | ->load_class( 'Field_Declaration' )
146 | ->load_class( 'Field_Declaration_Builder' )
147 | ->load_class( 'Model' )
148 | ->load_class( 'Model_Settings' )
149 | ->load_class( 'Model_Collection' )
150 | ->load_class( 'Controller' )
151 | ->load_class( 'Controller_Action' )
152 | ->load_class( 'Controller_Model' )
153 | ->load_class( 'Controller_Settings' )
154 | ->load_class( 'Controller_Route' )
155 | ->load_class( 'Controller_CRUD' )
156 | ->load_class( 'Controller_Bundle' )
157 | ->load_class( 'Controller_Extension' )
158 | ->load_class( 'Controller_Bundle_Builder' );
159 |
160 | return $this;
161 | }
162 |
163 | /**
164 | * Load Unit Testing Base Classes
165 | *
166 | * @return Zoninator_REST_Bootstrap $this
167 | */
168 | public function load_testing_classes() {
169 | $this->class_loader()
170 | ->load_class( 'Testing_TestCase' )
171 | ->load_class( 'Testing_Model_TestCase' )
172 | ->load_class( 'Testing_Controller_TestCase' );
173 | return $this;
174 | }
175 |
176 | /**
177 | * Get the class loader
178 | *
179 | * @return Zoninator_REST_Classloader
180 | */
181 | public function class_loader() {
182 | return $this->class_loader;
183 | }
184 |
185 | /**
186 | * Lazy-load the environment
187 | *
188 | * @return Zoninator_REST_Environment
189 | */
190 | public function environment() {
191 | if ( null === $this->environment ) {
192 | $this->environment = new Zoninator_REST_Environment( $this );
193 | }
194 |
195 | return $this->environment;
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/src/zoninator_rest/class-zoninator-rest-classloader.php:
--------------------------------------------------------------------------------
1 | prefix = $prefix;
49 | $this->base_dir = $base_dir;
50 | if ( ! is_dir( $this->base_dir ) ) {
51 | throw new Exception( esc_html( 'base_dir does not exist: ' . $this->base_dir ) );
52 | }
53 | }
54 |
55 | /**
56 | * Loads a class
57 | *
58 | * @param string $class_name The class to load.
59 | *
60 | * @return Zoninator_REST_Interfaces_Classloader
61 | * @throws Exception Throws in case include_class_file fails.
62 | */
63 | public function load_class( $class_name ) {
64 | $path = $this->get_path_to_class_file( $class_name );
65 | return $this->include_class_file( $path );
66 | }
67 |
68 | /**
69 | * Get path_to_class_file
70 | *
71 | * @param string $class_name The class.
72 | *
73 | * @return string The full path to the file.
74 | */
75 | public function get_path_to_class_file( $class_name ) {
76 | return path_join( $this->base_dir, $this->class_name_to_relative_path( $class_name ) );
77 | }
78 |
79 | /**
80 | * Class_name_to_relative_path
81 | *
82 | * @param string $class_name The class name.
83 | * @param null|string $prefix The prefix.
84 | *
85 | * @return string
86 | */
87 | public function class_name_to_relative_path( $class_name, $prefix = null ) {
88 | $lowercase = strtolower( $this->prefixed_class_name( $class_name, $prefix ) );
89 | $file_name = 'class-' . str_replace( '_', '-', $lowercase ) . '.php';
90 | $parts = explode( '_', strtolower( $this->strip_prefix( $class_name, $prefix ) ) );
91 | array_pop( $parts );
92 | $parts[] = $file_name;
93 | return implode( DIRECTORY_SEPARATOR, $parts );
94 | }
95 |
96 | /**
97 | * Prefixed_class_name
98 | *
99 | * @param string $class_name The class name.
100 | * @param null|string $prefix The prefix.
101 | *
102 | * @return string
103 | */
104 | public function prefixed_class_name( $class_name, $prefix = null ) {
105 | if ( empty( $prefix ) ) {
106 | $prefix = $this->prefix;
107 | }
108 |
109 | return $prefix . '_' . $this->strip_prefix( $class_name, $prefix );
110 | }
111 |
112 | /**
113 | * Strip_prefix
114 | *
115 | * @param string $class_name The class name.
116 | * @param null|string $prefix The prefix.
117 | *
118 | * @return string
119 | */
120 | private function strip_prefix( $class_name, $prefix = null ) {
121 | if ( empty( $prefix ) ) {
122 | $prefix = $this->prefix;
123 | }
124 |
125 | return str_replace( $prefix, '', $class_name );
126 | }
127 |
128 | /**
129 | * Include_class_file
130 | *
131 | * @param string $path_to_the_class The file path.
132 | *
133 | * @return string
134 | * @throws Exception Throws when the file does not exist.
135 | */
136 | private function include_class_file( $path_to_the_class ) {
137 | if ( isset( $this->loaded_classes[ $path_to_the_class ] ) ) {
138 | return $this;
139 | }
140 |
141 | if ( ! file_exists( $path_to_the_class ) ) {
142 | throw new Exception( esc_html( $path_to_the_class . ' not found' ) );
143 | }
144 |
145 | $included = include_once $path_to_the_class;
146 | $this->loaded_classes[ $path_to_the_class ] = $included;
147 |
148 | return $this;
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/src/zoninator_rest/class-zoninator-rest-controller.php:
--------------------------------------------------------------------------------
1 | controller_bundle = $controller_bundle;
67 | return $this;
68 | }
69 |
70 | /**
71 | * Set the Environment for this Controller.
72 | *
73 | * @param Zoninator_REST_Environment|null $environment The Environment.
74 | * @return Zoninator_REST_Controller
75 | */
76 | public function set_environment( $environment ) {
77 | $this->environment = $environment;
78 | return $this;
79 | }
80 |
81 | /**
82 | * Register This Controller
83 | *
84 | * @param Zoninator_REST_Controller_Bundle $bundle The bundle to register with.
85 | * @param Zoninator_REST_Environment $environment The Environment to use.
86 | * @throws Zoninator_REST_Exception Throws.
87 | *
88 | * @return bool|WP_Error true if valid otherwise error.
89 | */
90 | public function register( $bundle, $environment ) {
91 | $this->set_controller_bundle( $bundle );
92 | $this->set_environment( $environment );
93 | $this->setup();
94 | Zoninator_REST_Expect::that( ! empty( $this->base ), 'Need to put a string with a backslash in $base' );
95 | $prefix = $this->controller_bundle->get_prefix();
96 | foreach ( $this->routes as $route ) {
97 | /**
98 | * The route we want to register.
99 | *
100 | * @var Zoninator_REST_Controller_Route $route
101 | */
102 | $params = $route->as_array();
103 | $result = register_rest_route( $prefix, $this->base . $params['pattern'], $params['actions'] );
104 | if ( false === $result ) {
105 | // For now we throw on error, maybe we just need to warn though.
106 | throw new Zoninator_REST_Exception( 'Registration failed' );
107 | }
108 | }
109 |
110 | return true;
111 | }
112 |
113 | /**
114 | * Create Action
115 | *
116 | * @param string $action_name Action Name.
117 | * @param null|string|array|callable $callback Callback.
118 | * @return Zoninator_REST_Controller_Action
119 | */
120 | public function action( $action_name, $callback = null ) {
121 | $route_action = new Zoninator_REST_Controller_Action( $this, $action_name );
122 | if ( null !== $callback ) {
123 | $route_action->callback( $callback );
124 | }
125 |
126 | return $route_action;
127 | }
128 |
129 | /**
130 | * Do any additional Configuration. Runs inside register before any register_rest_route
131 | *
132 | * This is a good place for overriding classes to define routes and handlers
133 | */
134 | protected function setup() {
135 | }
136 |
137 | /**
138 | * Succeed
139 | *
140 | * @param array $data The dto.
141 | *
142 | * @return WP_REST_Response
143 | */
144 | public function ok( $data ) {
145 | return $this->respond( $data, self::HTTP_OK );
146 | }
147 |
148 | /**
149 | * Created
150 | *
151 | * @param array $data The dto.
152 | *
153 | * @return WP_REST_Response
154 | */
155 | public function created( $data ) {
156 | return $this->respond( $data, self::HTTP_CREATED );
157 | }
158 |
159 | /**
160 | * Bad request
161 | *
162 | * @param array|WP_Error $data The dto.
163 | *
164 | * @return WP_REST_Response
165 | */
166 | public function bad_request( $data ) {
167 | return $this->respond( $data, self::HTTP_BAD_REQUEST );
168 | }
169 |
170 | /**
171 | * Not Found (404)
172 | *
173 | * @param string $message The message.
174 | *
175 | * @return WP_REST_Response
176 | */
177 | public function not_found( $message ) {
178 | return $this->respond(
179 | array(
180 | 'message' => $message,
181 | ),
182 | self::HTTP_NOT_FOUND
183 | );
184 | }
185 |
186 | /**
187 | * Respond
188 | *
189 | * @param array|WP_REST_Response|WP_Error|mixed $data The thing.
190 | * @param int $status The Status.
191 | *
192 | * @return mixed|WP_REST_Response
193 | */
194 | public function respond( $data, $status ) {
195 | if ( is_a( $data, 'WP_REST_Response' ) ) {
196 | return $data;
197 | }
198 |
199 | return new WP_REST_Response( $data, $status );
200 | }
201 |
202 | /**
203 | * Permissions for get_items
204 | *
205 | * @param WP_REST_Request $request Request.
206 | * @return bool
207 | */
208 | public function index_permissions_check( $request ) {
209 | return $this->permissions_check( $request, 'index' );
210 | }
211 |
212 | /**
213 | * Permissions for get_item
214 | *
215 | * @param WP_REST_Request $request The request.
216 | * @return bool
217 | */
218 | public function show_permissions_check( $request ) {
219 | return $this->permissions_check( $request, 'show' );
220 | }
221 |
222 | /**
223 | * Permissions for create_item
224 | *
225 | * @param WP_REST_Request $request Request.
226 | * @return bool
227 | */
228 | public function create_permissions_check( $request ) {
229 | return $this->permissions_check( $request, 'create' );
230 | }
231 |
232 | /**
233 | * Permissions for update_item
234 | *
235 | * @param WP_REST_Request $request Request.
236 | * @return bool
237 | */
238 | public function update_permissions_check( $request ) {
239 | return $this->permissions_check( $request, 'update' );
240 | }
241 |
242 | /**
243 | * Permissions for delete
244 | *
245 | * @param WP_REST_Request $request Request.
246 | * @return bool
247 | */
248 | public function delete_permissions_check( $request ) {
249 | return $this->permissions_check( $request, 'delete' );
250 | }
251 |
252 | /**
253 | * Generic Permissions Check.
254 | *
255 | * @param WP_REST_Request $request Request.
256 | * @param string $action One of (index, show, create, update, delete, any).
257 | * @return bool
258 | */
259 | public function permissions_check( $request, $action = 'any' ) {
260 | return true;
261 | }
262 |
263 | /**
264 | * Add a route
265 | *
266 | * @param string $pattern The route pattern (e.g. '/').
267 | * @return Zoninator_REST_Controller_Route
268 | */
269 | public function add_route( $pattern = '' ) {
270 | $route = new Zoninator_REST_Controller_Route( $this, $pattern );
271 | $this->routes[ $pattern ] = $route;
272 | return $this->routes[ $pattern ];
273 | }
274 |
275 | /**
276 | * Get Environment
277 | *
278 | * @return Zoninator_REST_Environment
279 | */
280 | protected function environment() {
281 | return $this->environment;
282 | }
283 |
284 | /**
285 | * Get base url
286 | *
287 | * @return string
288 | */
289 | public function get_base() {
290 | return rest_url( $this->controller_bundle->get_prefix() . $this->base );
291 | }
292 | }
293 |
--------------------------------------------------------------------------------
/src/zoninator_rest/class-zoninator-rest-environment.php:
--------------------------------------------------------------------------------
1 | bootstrap = $bootstrap;
81 | $this->type_registry = new Zoninator_REST_Type_Registry();
82 | $this->type_registry->initialize( $this );
83 | // initialize our array vars.
84 | $this->array_var( self::MODELS )
85 | ->array_var( self::REGISTRABLE )
86 | ->array_var( self::BUNDLES );
87 | }
88 |
89 | /**
90 | * Push a Builder to the Environment.
91 | *
92 | * All builders are evaluated lazily when needed
93 | *
94 | * @param string $where The queue to push the builder to.
95 | * @param Zoninator_REST_Interfaces_Builder $builder The builder to push.
96 | *
97 | * @return Zoninator_REST_Environment $this
98 | * @throws Zoninator_REST_Exception In case the $builder is not a Mixtape_Interfaces_Builder.
99 | */
100 | public function push_builder( $where, $builder ) {
101 | Zoninator_REST_Expect::that( is_string( $where ), '$where should be a string' );
102 | Zoninator_REST_Expect::is_a( $builder, 'Zoninator_REST_Interfaces_Builder' );
103 | return $this->array_var( $where, $builder );
104 | }
105 |
106 | /**
107 | * Retrieve a previously defined Zoninator_REST_Model
108 | *
109 | * @param string $class_name the class name.
110 | *
111 | * @return Zoninator_REST_Model the definition.
112 | * @throws Zoninator_REST_Exception Throws in case the model is not registered.
113 | */
114 | public function model( $class_name ) {
115 | if ( ! class_exists( $class_name ) ) {
116 | throw new Zoninator_REST_Exception( esc_html( $class_name . ' does not exist' ) );
117 | }
118 |
119 | Zoninator_REST_Expect::that( isset( $this->model_definitions[ $class_name ] ), $class_name . ' definition does not exist' );
120 | return $this->model_definitions[ $class_name ];
121 | }
122 |
123 | /**
124 | * Time to build pending models and bundles
125 | *
126 | * @param string $type One of (models, bundles).
127 | * @return Zoninator_REST_Environment
128 | */
129 | private function load_pending_builders( $type ) {
130 | $things = $this->get( $type );
131 | if ( ! empty( $things ) && is_array( $things ) ) {
132 | foreach ( $things as $pending ) {
133 | /**
134 | * Our pending builder.
135 | *
136 | * @var Zoninator_REST_Interfaces_Builder $pending Our builder.
137 | */
138 | if ( self::BUNDLES === $type ) {
139 | $this->add_rest_bundle( $pending->build() );
140 | }
141 | }
142 | }
143 |
144 | return $this;
145 | }
146 |
147 | /**
148 | * Start things up
149 | *
150 | * This should be called once our Environment is set up to our liking.
151 | * Evaluates all Builders, creating missing REST Api and Model Definitions.
152 | *
153 | *
154 | * It is recommended we hook this into 'rest_api_init'.
155 | *
156 | * @return Zoninator_REST_Environment $this
157 | */
158 | public function start() {
159 | if ( ! $this->bootstrap->is_compatible() ) {
160 | // Do not even start on an incompatible system.
161 | return $this;
162 | }
163 |
164 | if ( false === $this->has_started ) {
165 | // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
166 | do_action( 'mt_environment_before_start', $this, get_class( $this ) );
167 | $this->load_pending_builders( self::MODELS );
168 | $this->load_pending_builders( self::BUNDLES );
169 | $registrables = $this->get( self::REGISTRABLE ) ?: array();
170 | foreach ( $registrables as $registrable ) {
171 | /**
172 | * A Registrable
173 | *
174 | * @var Zoninator_REST_Interfaces_Registrable $registrable
175 | */
176 | $registrable->register( $this );
177 | }
178 |
179 | /**
180 | * Use this hook to add/remove rest api bundles
181 | *
182 | * @param array $rest_apis The existing rest apis.
183 | * @param Zoninator_REST_Environment $this The Environment.
184 | */
185 | $rest_apis = (array) apply_filters( 'mt_environment_get_rest_apis', $this->rest_apis, $this ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
186 |
187 | foreach ( $rest_apis as $bundle ) {
188 | /**
189 | * Register this bundle
190 | */
191 | $bundle->register( $this );
192 | }
193 |
194 | $this->has_started = true;
195 | do_action( 'mt_environment_after_start', $this ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
196 | }
197 |
198 | return $this;
199 | }
200 |
201 | /**
202 | * Add Registrable
203 | *
204 | * @param Zoninator_REST_Interfaces_Registrable $registrable_thing Registrable.
205 | * @return Zoninator_REST_Environment
206 | * @throws Zoninator_REST_Exception When not a Zoninator_REST_Interfaces_Registrable.
207 | */
208 | public function add_registrable( $registrable_thing ) {
209 | Zoninator_REST_Expect::is_a( $registrable_thing, 'Zoninator_REST_Interfaces_Registrable' );
210 | $this->array_var( self::REGISTRABLE, $registrable_thing );
211 | return $this->define_var( get_class( $registrable_thing ), $registrable_thing );
212 | }
213 |
214 | /**
215 | * Has Variable
216 | *
217 | * @param string $name Is this variable Set.
218 | * @return bool
219 | */
220 | public function has_variable( $name ) {
221 | return isset( $this->variables[ $name ] );
222 | }
223 |
224 | /**
225 | * Append to an array
226 | *
227 | * @param string $name The VarArray Name.
228 | * @param mixed $thing The thing.
229 | * @return Zoninator_REST_Environment
230 | */
231 | public function array_var( $name, $thing = null ) {
232 | return $this->define_var( $name, $thing, true );
233 | }
234 |
235 | /**
236 | * Get A Variable
237 | *
238 | * @param string $name The Variable Name.
239 | * @return mixed|null The variable or null
240 | *
241 | * @throws Zoninator_REST_Exception Name should be a string.
242 | */
243 | public function get( $name ) {
244 | Zoninator_REST_Expect::that( is_string( $name ), '$name should be a string' );
245 | $value = $this->has_variable( $name ) ? $this->variables[ $name ] : null;
246 | /**
247 | * Filter the variable value
248 | *
249 | * @param mixed $value The value.
250 | * @param Zoninator_REST_Environment $this The Environment.
251 | * @param string $name The var name.
252 | *
253 | * @return mixed
254 | */
255 | return apply_filters( 'mt_variable_get', $value, $this, $name ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
256 | }
257 |
258 | /**
259 | * Def.
260 | *
261 | * @param string $name The Variable To Add.
262 | * @param mixed $thing The thing that is associated with the var.
263 | * @param bool $append If true, this var is a list.
264 | *
265 | * @return $this
266 | *
267 | * @throws Zoninator_REST_Exception When name is not a string.
268 | */
269 | public function define_var( $name, $thing = null, $append = false ) {
270 | Zoninator_REST_Expect::that( is_string( $name ), '$name should be a string' );
271 | if ( $append && ! $this->has_variable( $name ) ) {
272 | $this->variables[ $name ] = array();
273 | }
274 |
275 | if ( null !== $thing ) {
276 | if ( $append ) {
277 | $this->variables[ $name ][] = $thing;
278 | } else {
279 | $this->variables[ $name ] = $thing;
280 | }
281 | }
282 |
283 | return $this;
284 | }
285 |
286 | /**
287 | * Auto start on rest_api_init. For more control, use ::start();
288 | */
289 | public function auto_start() {
290 | add_action( 'rest_api_init', array( $this, 'start' ) );
291 | }
292 |
293 | /**
294 | * Get this Environment's bootstrap instance
295 | *
296 | * @return Zoninator_REST_Bootstrap our bootstrap.
297 | */
298 | public function get_bootstrap() {
299 | return $this->bootstrap;
300 | }
301 |
302 | /**
303 | * Create a new Field Declaration Builder
304 | *
305 | * @param null|string $name Optional, the field name.
306 | * @param null|string $description Optional, the description.
307 | * @param null|string $field_kind The field kind (default 'field').
308 | *
309 | * @return Zoninator_REST_Field_Declaration_Builder
310 | */
311 | public function field( $name = null, $description = null, $field_kind = null ) {
312 | $builder = new Zoninator_REST_Field_Declaration_Builder();
313 |
314 | if ( ! empty( $name ) ) {
315 | $builder->with_name( $name );
316 | }
317 |
318 | if ( ! empty( $description ) ) {
319 | $builder->with_description( $description );
320 | }
321 |
322 | if ( empty( $field_kind ) ) {
323 | $field_kind = Zoninator_REST_Field_Declaration::FIELD;
324 | }
325 |
326 | $builder->with_kind( $field_kind );
327 |
328 | return $builder;
329 | }
330 |
331 | /**
332 | * Get our registered types
333 | *
334 | * @return Zoninator_REST_Type_Registry
335 | */
336 | public function get_type_registry() {
337 | return $this->type_registry;
338 | }
339 |
340 | /**
341 | * Get a known type definition
342 | *
343 | * @param string $type_name The type name.
344 | * @return Zoninator_REST_Interfaces_Type
345 | *
346 | * @throws Zoninator_REST_Exception When provided with an unknown/invalid type.
347 | */
348 | public function type( $type_name ) {
349 | return $this->get_type_registry()->definition( $type_name );
350 | }
351 |
352 | /**
353 | * Define a new REST API Bundle.
354 | *
355 | * @param null|string|Zoninator_REST_Interfaces_Controller_Bundle $maybe_bundle_or_prefix The bundle name.
356 | * @return Zoninator_REST_Controller_Bundle_Builder
357 | */
358 | public function rest_api( $maybe_bundle_or_prefix = null ) {
359 | if ( is_a( $maybe_bundle_or_prefix, 'Zoninator_REST_Interfaces_Controller_Bundle' ) ) {
360 | $builder = new Zoninator_REST_Controller_Bundle_Builder( $maybe_bundle_or_prefix );
361 | } else {
362 | $builder = new Zoninator_REST_Controller_Bundle_Builder();
363 | if ( is_string( $maybe_bundle_or_prefix ) ) {
364 | $builder->with_prefix( $maybe_bundle_or_prefix );
365 | }
366 |
367 | $builder->with_environment( $this );
368 | }
369 |
370 | $this->push_builder( self::BUNDLES, $builder );
371 | return $builder;
372 | }
373 |
374 | /**
375 | * Define a new Model
376 | *
377 | * @param string $declaration A Model class string.
378 | *
379 | * @return Zoninator_REST_Model
380 | */
381 | public function define_model( $declaration ) {
382 | Zoninator_REST_Expect::that( class_exists( $declaration ), '$declaration string should be an existing class' );
383 | Zoninator_REST_Expect::that( in_array( 'Zoninator_REST_Interfaces_Model', class_implements( $declaration ), true ), '$declaration does not implement Zoninator_REST_Interfaces_Model' );
384 |
385 | /**
386 | * Create an empty Model to act as our factory (I know this is weird, see php5.2)
387 | *
388 | * @var Zoninator_REST_Model $factory
389 | */
390 | $factory = new $declaration();
391 | $factory->with_environment( $this );
392 | $factory->with_data_store( new Zoninator_REST_Data_Store_Nil() );
393 | $factory->with_permissions_provider( new Zoninator_REST_Permissions_Any() );
394 | $this->model_definitions[ $declaration ] = $factory;
395 | return $factory;
396 | }
397 |
398 | /**
399 | * Add a Bundle to our bundles (muse be Mixtape_Interfaces_Rest_Api_Controller_Bundle)
400 | *
401 | * @param Zoninator_REST_Interfaces_Controller_Bundle $bundle the bundle.
402 | *
403 | * @return Zoninator_REST_Environment $this
404 | * @throws Zoninator_REST_Exception In case it's not a Zoninator_REST_Interfaces_Controller_Bundle.
405 | */
406 | private function add_rest_bundle( $bundle ) {
407 | Zoninator_REST_Expect::is_a( $bundle, 'Zoninator_REST_Interfaces_Controller_Bundle' );
408 | $key = $bundle->get_prefix();
409 | $this->rest_apis[ $key ] = $bundle;
410 | return $this;
411 | }
412 | }
413 |
--------------------------------------------------------------------------------
/src/zoninator_rest/class-zoninator-rest-exception.php:
--------------------------------------------------------------------------------
1 | identifier = $identifier;
30 | }
31 |
32 | /**
33 | * The name
34 | *
35 | * @return string
36 | */
37 | public function name() {
38 | return $this->identifier;
39 | }
40 |
41 | /**
42 | * The default value
43 | */
44 | public function default_value() {
45 | return null;
46 | }
47 |
48 | /**
49 | * Cast value to be Type
50 | *
51 | * @param mixed $value The value that needs casting.
52 | *
53 | * @return mixed
54 | */
55 | public function cast( $value ) {
56 | return $value;
57 | }
58 |
59 | /**
60 | * Sanitize this value
61 | *
62 | * @param mixed $value The value to sanitize.
63 | *
64 | * @return mixed
65 | */
66 | public function sanitize( $value ) {
67 | return $value;
68 | }
69 |
70 | /**
71 | * Get this type's JSON Schema.
72 | *
73 | * @return array
74 | */
75 | public function schema() {
76 | return array(
77 | 'type' => $this->name(),
78 | );
79 | }
80 |
81 | /**
82 | * Get our "Any" type
83 | *
84 | * @return Zoninator_REST_Type
85 | */
86 | public static function any() {
87 | return new self( 'any' );
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/zoninator_rest/controller/bundle/class-zoninator-rest-controller-bundle-builder.php:
--------------------------------------------------------------------------------
1 | bundle = $bundle;
45 | }
46 |
47 | /**
48 | * Build it
49 | *
50 | * @return Zoninator_REST_Interfaces_Controller_Bundle
51 | */
52 | public function build() {
53 | if ( is_a( $this->bundle, 'Zoninator_REST_Interfaces_Controller_Bundle' ) ) {
54 | return $this->bundle;
55 | }
56 |
57 | return new Zoninator_REST_Controller_Bundle( $this->bundle_prefix, $this->endpoint_builders );
58 | }
59 |
60 | /**
61 | * Prefix.
62 | *
63 | * @param string $bundle_prefix Prefix.
64 | * @return Zoninator_REST_Controller_Bundle_Builder $this
65 | */
66 | public function with_prefix( $bundle_prefix ) {
67 | $this->bundle_prefix = $bundle_prefix;
68 | return $this;
69 | }
70 |
71 | /**
72 | * Env.
73 | *
74 | * @param Zoninator_REST_Environment $env Env.
75 | * @return Zoninator_REST_Controller_Bundle_Builder $this
76 | */
77 | public function with_environment( $env ) {
78 | return $this;
79 | }
80 |
81 | /**
82 | * Endpoint.
83 | *
84 | * Adds a new Zoninator_REST_Controller_Builder to our builders and returns it for further setup.
85 | *
86 | * @param null|Zoninator_REST_Interfaces_Controller $controller_object The (optional) controller object.
87 | * @return Zoninator_REST_Controller_Bundle_Builder $this
88 | */
89 | public function add_endpoint( $controller_object = null ) {
90 | Zoninator_REST_Expect::is_a( $controller_object, 'Zoninator_REST_Interfaces_Controller' );
91 | $this->endpoint_builders[] = $controller_object;
92 | return $this;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/zoninator_rest/controller/class-zoninator-rest-controller-action.php:
--------------------------------------------------------------------------------
1 | WP_REST_Server::READABLE,
25 | 'show' => WP_REST_Server::READABLE,
26 | 'create' => WP_REST_Server::CREATABLE,
27 | 'update' => WP_REST_Server::EDITABLE,
28 | 'delete' => WP_REST_Server::DELETABLE,
29 | 'any' => WP_REST_Server::ALLMETHODS,
30 | );
31 |
32 | /**
33 | * The action name
34 | *
35 | * @var string
36 | */
37 | private $action_name;
38 |
39 | /**
40 | * The Handler
41 | *
42 | * @var null|array|string
43 | */
44 | private $handler;
45 |
46 | /**
47 | * The Permissions Callback
48 | *
49 | * @var null|array|string
50 | */
51 | private $permission_callback;
52 |
53 | /**
54 | * The Args
55 | *
56 | * @var null|array|string
57 | */
58 | private $args;
59 |
60 | /**
61 | * Zoninator_REST_Controller_Action constructor.
62 | *
63 | * @param Zoninator_REST_Controller $controller Controller.
64 | * @param string $action_name The action Name.
65 | */
66 | public function __construct( $controller, $action_name ) {
67 | $is_known_action = in_array( $action_name, array_keys( $this->actions_to_http_methods ), true );
68 | Zoninator_REST_Expect::that( $is_known_action, 'Unknown method: ' . $action_name );
69 |
70 | $this->controller = $controller;
71 | $this->action_name = $action_name;
72 | }
73 |
74 | /**
75 | * Get Name
76 | *
77 | * @return string
78 | */
79 | public function name() {
80 | return $this->action_name;
81 | }
82 |
83 | /**
84 | * Set Permissions
85 | *
86 | * @param mixed $a_callable A Callable.
87 | *
88 | * @return Zoninator_REST_Controller_Action
89 | */
90 | public function permissions( $a_callable ) {
91 | $this->permission_callback = $a_callable;
92 | return $this;
93 | }
94 |
95 | /**
96 | * Set Handler
97 | *
98 | * @param mixed $a_callable A Callable.
99 | *
100 | * @return Zoninator_REST_Controller_Action
101 | */
102 | public function callback( $a_callable ) {
103 | $this->handler = $a_callable;
104 | return $this;
105 | }
106 |
107 | /**
108 | * Set Handler
109 | *
110 | * @param mixed $a_callable A Callable.
111 | *
112 | * @return Zoninator_REST_Controller_Action
113 | */
114 | public function args( $a_callable ) {
115 | $this->args = $a_callable;
116 | return $this;
117 | }
118 |
119 | /**
120 | * Used in register rest route
121 | *
122 | * @return array
123 | */
124 | public function as_array() {
125 | $callable_func = $this->expect_callable( $this->handler );
126 | if ( null !== $this->permission_callback ) {
127 | $permission_callback = $this->expect_callable( $this->permission_callback );
128 | } else {
129 | $permission_callback = $this->expect_callable( array( $this->controller, $this->action_name . '_permissions_check' ) );
130 | }
131 |
132 | if ( null !== $this->args ) {
133 | $args = call_user_func( $this->expect_callable( $this->args ), $this->actions_to_http_methods[ $this->action_name ] );
134 | } else {
135 | $args = $this->controller->get_endpoint_args_for_item_schema( $this->actions_to_http_methods[ $this->action_name ] );
136 | }
137 |
138 | return array(
139 | 'methods' => $this->actions_to_http_methods[ $this->action_name ],
140 | 'callback' => $callable_func,
141 | 'permission_callback' => $permission_callback,
142 | 'args' => $args,
143 | );
144 | }
145 |
146 | /**
147 | * Expect a callable
148 | *
149 | * @param mixed $callable_func A Callable.
150 | * @return array
151 | * @throws Zoninator_REST_Exception If not a callable.
152 | */
153 | private function expect_callable( $callable_func ) {
154 | if ( ! is_callable( $callable_func ) ) {
155 | // Check if controller has a public method called $callable_func.
156 | if ( is_string( $callable_func ) && method_exists( $this->controller, $callable_func ) ) {
157 | return array( $this->controller, $callable_func );
158 | }
159 |
160 | Zoninator_REST_Expect::that( is_callable( $callable_func ), 'Callable Expected: $callable_func' );
161 | }
162 |
163 | return $callable_func;
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/src/zoninator_rest/controller/class-zoninator-rest-controller-bundle.php:
--------------------------------------------------------------------------------
1 | prefix = $bundle_prefix;
46 | $this->endpoints = $endpoints;
47 | }
48 |
49 | /**
50 | * Register this bundle with the environment.
51 | *
52 | * @param Zoninator_REST_Environment $environment The Environment.
53 | * @return Zoninator_REST_Controller_Bundle $this
54 | * @throws Zoninator_REST_Exception When no prefix is defined.
55 | */
56 | public function register( $environment ) {
57 | Zoninator_REST_Expect::that( null !== $this->prefix, 'prefix should be defined' );
58 | $this->environment = $environment;
59 | /**
60 | * Add/remove endpoints. Useful for extensions
61 | *
62 | * @param array $endpoints An array of Zoninator_REST_Interfaces_Controller
63 | * @param $bundle Zoninator_REST_Controller_Bundle The bundle instance.
64 | *
65 | * @return array
66 | */
67 | $endpoints = (array) apply_filters(
68 | 'mt_rest_api_controller_bundle_get_endpoints', // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
69 | $this->endpoints,
70 | $this
71 | );
72 |
73 | foreach ( $endpoints as $endpoint ) {
74 | /**
75 | * Controller
76 | */
77 | $endpoint->register( $this, $this->environment );
78 | }
79 |
80 | return $this;
81 | }
82 |
83 | /**
84 | * Get Prefix.
85 | *
86 | * @return string
87 | */
88 | public function get_prefix() {
89 | return $this->prefix;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/zoninator_rest/controller/class-zoninator-rest-controller-crud.php:
--------------------------------------------------------------------------------
1 | add_route( '/' )
22 | ->add_action( $this->action( 'index', array( $this, 'get_items' ) ) )
23 | ->add_action( $this->action( 'create', array( $this, 'create_item' ) ) );
24 |
25 | $this->add_route( '/(?P\d+)' )
26 | ->add_action( $this->action( 'show', array( $this, 'get_item' ) ) )
27 | ->add_action( $this->action( 'update', array( $this, 'update_item' ) ) )
28 | ->add_action( $this->action( 'delete', array( $this, 'delete_item' ) ) );
29 | }
30 |
31 | /**
32 | * Get Items.
33 | *
34 | * @param WP_REST_Request $request Request.
35 | * @return WP_REST_Response
36 | */
37 | public function get_items( $request ) {
38 | $item_id = isset( $request['id'] ) ? absint( $request['id'] ) : null;
39 |
40 | if ( null === $item_id ) {
41 | $models = $this->get_model_data_store()->get_entities();
42 | $data = $this->prepare_dto( $models );
43 | return $this->ok( $data );
44 | }
45 |
46 | $model = $this->model_prototype->get_data_store()->get_entity( $item_id );
47 | if ( empty( $model ) ) {
48 | return $this->not_found( __( 'Model not found', 'zoninator' ) );
49 | }
50 |
51 | return $this->ok( $this->prepare_dto( $model ) );
52 | }
53 |
54 | /**
55 | * Get Item
56 | *
57 | * @param WP_REST_Request $request Request.
58 | * @return WP_REST_Response
59 | */
60 | public function get_item( $request ) {
61 | return $this->get_items( $request );
62 | }
63 |
64 | /**
65 | * Create Item
66 | *
67 | * @param WP_REST_Request $request Request.
68 | * @return WP_REST_Response
69 | */
70 | public function create_item( $request ) {
71 | $is_update = false;
72 | return $this->create_or_update( $request, $is_update );
73 | }
74 |
75 | /**
76 | * Update Item
77 | *
78 | * @param WP_REST_Request $request Request.
79 | * @return WP_REST_Response
80 | */
81 | public function update_item( $request ) {
82 | $is_update = true;
83 | return $this->create_or_update( $request, $is_update );
84 | }
85 |
86 | /**
87 | * Create Or Update Item
88 | *
89 | * @param WP_REST_Request $request Request.
90 | * @param bool $is_update Is Update.
91 | *
92 | * @return WP_REST_Response
93 | */
94 | protected function create_or_update( $request, $is_update = false ) {
95 | $model_to_update = null;
96 | if ( $is_update ) {
97 | $id = isset( $request['id'] ) ? absint( $request['id'] ) : null;
98 | if ( ! empty( $id ) ) {
99 | $model_to_update = $this->model_prototype->get_data_store()->get_entity( $id );
100 | if ( empty( $model_to_update ) ) {
101 | return $this->not_found( 'Model does not exist' );
102 | }
103 | }
104 | }
105 |
106 | if ( $is_update && $model_to_update ) {
107 | $model = $model_to_update->update_from_array( $request->get_params(), $is_update );
108 | } else {
109 | $model = $this->get_model_prototype()->new_from_array( $request->get_params() );
110 | }
111 |
112 | if ( is_wp_error( $model ) ) {
113 | $wp_err = $model;
114 | return $this->bad_request( $wp_err );
115 | }
116 |
117 | $validation = $model->validate();
118 | if ( is_wp_error( $validation ) ) {
119 | return $this->bad_request( $validation );
120 | }
121 |
122 | $id_or_error = $this->model_data_store->upsert( $model );
123 |
124 | if ( is_wp_error( $id_or_error ) ) {
125 | return $this->bad_request( $id_or_error );
126 | }
127 |
128 | $dto = $this->prepare_dto(
129 | array(
130 | 'id' => absint( $id_or_error ),
131 | )
132 | );
133 |
134 | return $is_update ? $this->ok( $dto ) : $this->created( $dto );
135 | }
136 |
137 | /**
138 | * Delete an Item
139 | *
140 | * @param WP_REST_Request $request Request.
141 | * @return WP_REST_Response
142 | */
143 | public function delete_item( $request ) {
144 | $id = isset( $request['id'] ) ? absint( $request['id'] ) : null;
145 | if ( empty( $id ) ) {
146 | return $this->bad_request( 'No Model ID provided' );
147 | }
148 |
149 | $model = $this->model_prototype->get_data_store()->get_entity( $id );
150 | if ( null === $model ) {
151 | return $this->not_found( 'Model does not exist' );
152 | }
153 |
154 | $result = $this->model_data_store->delete( $model );
155 | return $this->ok( $result );
156 | }
157 |
158 | /**
159 | * Model To Dto
160 | *
161 | * @param Zoninator_REST_Interfaces_Model $model The Model.
162 | * @return array
163 | */
164 | protected function model_to_dto( $model ) {
165 | $result = parent::model_to_dto( $model );
166 | $result['_links'] = $this->add_links( $model );
167 | return $result;
168 | }
169 |
170 | /**
171 | * Add Links
172 | *
173 | * @param Zoninator_REST_Interfaces_Model $model Model.
174 | * @return array
175 | */
176 | protected function add_links( $model ) {
177 | $base_url = rest_url() . $this->controller_bundle->get_prefix() . $this->base . '/';
178 |
179 | $result = array(
180 | 'collection' => array(
181 | array(
182 | 'href' => esc_url( $base_url ),
183 | ),
184 | ),
185 | );
186 | if ( $model->get_id() ) {
187 | $result['self'] = array(
188 | array(
189 | 'href' => esc_url( $base_url . $model->get_id() ),
190 | ),
191 | );
192 | }
193 |
194 | if ( $model->has( 'author' ) ) {
195 | $result['author'] = array(
196 | array(
197 | 'href' => esc_url( rest_url() . 'wp/v2/users/' . $model->get( 'author' ) ),
198 | ),
199 | );
200 | }
201 |
202 | return $result;
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/src/zoninator_rest/controller/class-zoninator-rest-controller-extension.php:
--------------------------------------------------------------------------------
1 | model_definition_name = $model_definition_name;
49 | $this->object_to_extend = $object_to_extend;
50 | }
51 |
52 | /**
53 | * Register This Controller
54 | *
55 | * @param Zoninator_REST_Environment $environment The Environment to use.
56 | * @throws Zoninator_REST_Exception Throws.
57 | *
58 | * @return bool|WP_Error true if valid otherwise error.
59 | */
60 | public function register( $environment ) {
61 | $this->environment = $environment;
62 | $this->model_definition = $this->environment->model( $this->model_definition_name );
63 | if ( ! $this->model_definition ) {
64 | return new WP_Error( 'model-not-found' );
65 | }
66 |
67 | $fields = $this->model_definition->get_fields();
68 | foreach ( $fields as $field ) {
69 | $this->register_field( $field );
70 | }
71 |
72 | return true;
73 | }
74 |
75 | /**
76 | * Register Field
77 | *
78 | * @param Zoninator_REST_Field_Declaration $field Field.
79 | */
80 | private function register_field( $field ) {
81 | register_rest_field(
82 | $this->object_to_extend,
83 | $field->get_data_transfer_name(),
84 | array(
85 | 'get_callback' => $field->get_reader(),
86 | 'update_callback' => $field->get_updater(),
87 | 'schema' => $field->as_item_schema_property(),
88 | )
89 | );
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/zoninator_rest/controller/class-zoninator-rest-controller-model.php:
--------------------------------------------------------------------------------
1 | base = $base;
47 | $this->model_class_name = $model_class_name;
48 | }
49 |
50 | /**
51 | * Get our model factory
52 | *
53 | * @return Zoninator_REST_Model
54 | */
55 | protected function get_model_prototype() {
56 | return $this->model_prototype;
57 | }
58 |
59 | /**
60 | * Register this controller, initialize model-related object fields.
61 | *
62 | * @param Zoninator_REST_Controller_Bundle $bundle The bundle to use.
63 | * @param Zoninator_REST_Environment $environment The Environment.
64 | *
65 | * @throws Zoninator_REST_Exception If an invalid model is provided.
66 | *
67 | * @return bool|WP_Error true if valid otherwise error.
68 | */
69 | public function register( $bundle, $environment ) {
70 | $this->model_prototype = $environment->model( $this->model_class_name );
71 | $this->model_data_store = $this->model_prototype->get_data_store();
72 | return parent::register( $bundle, $environment );
73 | }
74 |
75 | /**
76 | * Retrieves the item's schema, conforming to JSON Schema.
77 | *
78 | * In our case, it gets fields/types from our definition's declared fields.
79 | *
80 | * @access public
81 | *
82 | * @return array Item schema data.
83 | */
84 | public function get_item_schema() {
85 | $model_definition = $this->get_model_prototype();
86 | $fields = $model_definition->get_fields();
87 | $properties = array();
88 | $required = array();
89 | foreach ( $fields as $field_declaration ) {
90 | /**
91 | * Our declaration
92 | *
93 | * @var Zoninator_REST_Field_Declaration $field_declaration
94 | */
95 | $properties[ $field_declaration->get_data_transfer_name() ] = $field_declaration->as_item_schema_property();
96 | if ( $field_declaration->is_required() ) {
97 | $required[] = $field_declaration->get_data_transfer_name();
98 | }
99 | }
100 |
101 | $schema = array(
102 | '$schema' => 'http://json-schema.org/schema#',
103 | 'title' => $model_definition->get_name(),
104 | 'type' => 'object',
105 | // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- breaking change to fix prefix.
106 | 'properties' => (array) apply_filters( 'mixtape_rest_api_schema_properties', $properties, $this->get_model_prototype() ),
107 | );
108 |
109 | if ( array() !== $required ) {
110 | $schema['required'] = $required;
111 | }
112 |
113 | return $this->add_additional_fields_schema( $schema );
114 | }
115 |
116 | /**
117 | * Get Model DataStore
118 | *
119 | * @return Zoninator_REST_Interfaces_Data_Store
120 | */
121 | protected function get_model_data_store() {
122 | return $this->model_data_store;
123 | }
124 |
125 | /**
126 | * Generic Permissions Check.
127 | *
128 | * @param WP_REST_Request $request Request.
129 | * @param string $action One of (index, show, create, update, delete).
130 | * @return bool
131 | */
132 | public function permissions_check( $request, $action = 'any' ) {
133 | return $this->get_model_prototype()->permissions_check( $request, $action );
134 | }
135 |
136 | /**
137 | * Prepare Entity to be a DTO
138 | *
139 | * @param array|Zoninator_REST_Model_Collection|Zoninator_REST_Interfaces_Model $entity The Entity.
140 | * @return array
141 | */
142 | protected function prepare_dto( $entity ) {
143 | if ( is_a( $entity, 'Zoninator_REST_Model_Collection' ) ) {
144 | $results = array();
145 | foreach ( $entity->get_items() as $model ) {
146 | $results[] = $this->model_to_dto( $model );
147 | }
148 |
149 | return $results;
150 | }
151 |
152 | if ( is_a( $entity, 'Zoninator_REST_Interfaces_Model' ) ) {
153 | return $this->model_to_dto( $entity );
154 | }
155 |
156 | return $entity;
157 | }
158 |
159 | /**
160 | * Map a model to a Data Transfer Object (plain array)
161 | *
162 | * @param Zoninator_REST_Interfaces_Model $model The Model.
163 | * @return array
164 | */
165 | protected function model_to_dto( $model ) {
166 | return $model->to_dto();
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/src/zoninator_rest/controller/class-zoninator-rest-controller-route.php:
--------------------------------------------------------------------------------
1 | pattern = $pattern;
39 | }
40 |
41 | /**
42 | * Add/Get an action
43 | *
44 | * @param Zoninator_REST_Controller_Action $action Action.
45 | *
46 | * @return Zoninator_REST_Controller_Route
47 | */
48 | public function add_action( $action ) {
49 | $this->actions[ $action->name() ] = $action;
50 | return $this;
51 | }
52 |
53 | /**
54 | * Gets Route info to use in Register rest route.
55 | *
56 | * @throws Zoninator_REST_Exception If invalid callable.
57 | * @return array
58 | */
59 | public function as_array() {
60 | $result = array();
61 | $result['pattern'] = $this->pattern;
62 | $result['actions'] = array();
63 | foreach ( $this->actions as $route_action ) {
64 | /**
65 | * The route action.
66 | *
67 | * @var Zoninator_REST_Controller_Action $route_action
68 | */
69 | $result['actions'][] = $route_action->as_array();
70 | }
71 |
72 | return $result;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/zoninator_rest/controller/class-zoninator-rest-controller-settings.php:
--------------------------------------------------------------------------------
1 | add_route()
22 | ->add_action( $this->action( 'index', array( $this, 'get_items' ) ) )
23 | ->add_action( $this->action( 'update', array( $this, 'create_item' ) ) );
24 | }
25 |
26 | /**
27 | * Get Settings
28 | *
29 | * @param WP_REST_Request $request The request.
30 | * @return WP_REST_Response
31 | */
32 | public function get_items( $request ) {
33 | $model = $this->model_prototype->get_data_store()->get_entity( null );
34 | if ( empty( $model ) ) {
35 | return $this->not_found( __( 'Settings not found', 'zoninator' ) );
36 | }
37 |
38 | return $this->ok( $this->prepare_dto( $model ) );
39 | }
40 |
41 | /**
42 | * Create or Update settings.
43 | *
44 | * @param WP_REST_Request $request Request.
45 | * @return WP_REST_Response
46 | */
47 | public function create_item( $request ) {
48 | return $this->create_or_update( $request );
49 | }
50 |
51 | /**
52 | * Create or Update a Model
53 | *
54 | * @param WP_REST_Request $request Request.
55 | * @return WP_REST_Response
56 | */
57 | protected function create_or_update( $request ) {
58 | $is_update = $request->get_method() !== 'POST';
59 | $model_to_update = $this->model_prototype->get_data_store()->get_entity( null );
60 | if ( empty( $model_to_update ) ) {
61 | return $this->not_found( 'Model does not exist' );
62 | }
63 |
64 | $model = $model_to_update->update_from_array( $request->get_params(), true );
65 |
66 | if ( is_wp_error( $model ) ) {
67 | return $this->bad_request( $model );
68 | }
69 |
70 | $validation = $model->validate();
71 | if ( is_wp_error( $validation ) ) {
72 | return $this->bad_request( $validation );
73 | }
74 |
75 | $id_or_error = $this->model_data_store->upsert( $model );
76 |
77 | if ( is_wp_error( $id_or_error ) ) {
78 | return $this->bad_request( $id_or_error );
79 | }
80 |
81 | $model = $this->model_prototype->get_data_store()->get_entity( null );
82 | $dto = $this->prepare_dto( $model );
83 |
84 | return $is_update ? $this->ok( $dto ) : $this->created( $dto );
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/zoninator_rest/data/class-zoninator-rest-data-mapper.php:
--------------------------------------------------------------------------------
1 | definition = $definition;
38 | $this->serializer = $serializer;
39 | }
40 |
41 | /**
42 | * Transform raw data to model data
43 | *
44 | * @param array $data Data.
45 | * @param array $field_declarations Declarations.
46 | * @return array
47 | */
48 | public function raw_data_to_model_data( $data, $field_declarations ) {
49 | $raw_data = array();
50 | $post_array_keys = array_keys( $data );
51 | foreach ( $field_declarations as $declaration ) {
52 | /**
53 | * Declaration
54 | *
55 | * @var Zoninator_REST_Field_Declaration $declaration
56 | */
57 | $key = $declaration->get_name();
58 | $mapping = $declaration->get_map_from();
59 | $value = null;
60 | if ( in_array( $key, $post_array_keys, true ) ) {
61 | // simplest case: we got a $key for this, so just map it.
62 | $value = $this->serializer->deserialize( $declaration, $data[ $key ] );
63 | } elseif ( in_array( $mapping, $post_array_keys, true ) ) {
64 | $value = $this->serializer->deserialize( $declaration, $data[ $mapping ] );
65 | } else {
66 | $value = $declaration->get_default_value();
67 | }
68 |
69 | $raw_data[ $key ] = $declaration->cast_value( $value );
70 | }
71 |
72 | return $raw_data;
73 | }
74 |
75 | /**
76 | * Transform Model to raw data array
77 | *
78 | * @param Zoninator_REST_Interfaces_Model $model Model.
79 | * @param null|string $field_type Type.
80 | * @return array
81 | */
82 | public function model_to_data( $model, $field_type = null ) {
83 | $field_values_to_insert = array();
84 | foreach ( $this->definition->get_field_declarations( $field_type ) as $field_declaration ) {
85 | /**
86 | * Declaration
87 | *
88 | * @var Zoninator_REST_Field_Declaration $field_declaration
89 | */
90 | $what_to_map_to = $field_declaration->get_map_from();
91 | $value = $model->get( $field_declaration->get_name() );
92 | $field_values_to_insert[ $what_to_map_to ] = $this->serializer->serialize( $field_declaration, $value );
93 | }
94 |
95 | return $field_values_to_insert;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/zoninator_rest/data/class-zoninator-rest-data-serializer.php:
--------------------------------------------------------------------------------
1 | model_declaration = $model_definition->get_model_declaration();
30 | }
31 |
32 | /**
33 | * Deserialize
34 | *
35 | * @param Zoninator_REST_Field_Declaration $field_declaration Declaration.
36 | * @param mixed $value Value.
37 | * @return mixed the deserialized value
38 | */
39 | public function deserialize( $field_declaration, $value ) {
40 | $deserializer = $field_declaration->get_deserializer();
41 | return $deserializer ? $this->model_declaration->call( $deserializer, array( $value ) ) : $value;
42 | }
43 |
44 | /**
45 | * Serialize
46 | *
47 | * @param Zoninator_REST_Field_Declaration $field_declaration Declaration.
48 | * @param mixed $value Value.
49 | * @return mixed
50 | * @throws Zoninator_REST_Exception If call fails.
51 | */
52 | public function serialize( $field_declaration, $value ) {
53 | $serializer = $field_declaration->get_serializer();
54 | if ( isset( $serializer ) && ! empty( $serializer ) ) {
55 | return $this->model_declaration->call( $serializer, array( $value ) );
56 | }
57 |
58 | return $value;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/zoninator_rest/data/store/class-zoninator-rest-data-store-abstract.php:
--------------------------------------------------------------------------------
1 | args = $args;
38 | Zoninator_REST_Expect::is_a( $model_prototype, 'Zoninator_REST_Interfaces_Model' );
39 | $this->set_model_factory( $model_prototype );
40 | }
41 |
42 | /**
43 | * Set Definition
44 | *
45 | * @param Zoninator_REST_Model $factory Def.
46 | *
47 | * @return Zoninator_REST_Interfaces_Data_Store $this
48 | */
49 | private function set_model_factory( $factory ) {
50 | $this->model_prototype = $factory;
51 | $this->configure();
52 | return $this;
53 | }
54 |
55 | /**
56 | * Configure
57 | */
58 | protected function configure() {
59 | }
60 |
61 | /**
62 | * Get Definition
63 | *
64 | * @return Zoninator_REST_Model
65 | */
66 | public function get_model_prototype() {
67 | return $this->model_prototype;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/zoninator_rest/data/store/class-zoninator-rest-data-store-builder.php:
--------------------------------------------------------------------------------
1 | store_class = $data_store_class;
51 | return $this;
52 | }
53 |
54 | /**
55 | * Set Args
56 | *
57 | * @param array $args Args.
58 | * @return Zoninator_REST_Data_Store_Builder $this
59 | */
60 | public function with_args( $args ) {
61 | $this->args = $args;
62 | return $this;
63 | }
64 |
65 | /**
66 | * Set Model Definition
67 | *
68 | * @param string|Zoninator_REST_Model_Definition $model_definition Def.
69 | * @return Zoninator_REST_Data_Store_Builder $this
70 | */
71 | public function with_model_definition( $model_definition ) {
72 | $this->model_definition = $model_definition;
73 | return $this;
74 | }
75 |
76 | /**
77 | * Build
78 | *
79 | * @return Zoninator_REST_Interfaces_Data_Store
80 | */
81 | public function build() {
82 | $store_class = $this->store_class;
83 | return new $store_class( $this->model_definition, $this->args );
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/zoninator_rest/data/store/class-zoninator-rest-data-store-customposttype.php:
--------------------------------------------------------------------------------
1 | post_type = $args['post_type'] ?? 'post';
31 | parent::__construct( $model_prototype, $args );
32 | }
33 |
34 | /**
35 | * Get Entities
36 | *
37 | * @param null|mixed $filter Filter.
38 | *
39 | * @return Zoninator_REST_Model_Collection
40 | */
41 | public function get_entities( $filter = null ) {
42 | $query = new WP_Query(
43 | array(
44 | 'post_type' => $this->post_type,
45 | 'post_status' => 'any',
46 | )
47 | );
48 | $posts = $query->get_posts();
49 | $collection = array();
50 | foreach ( $posts as $post ) {
51 | $collection[] = $this->create_from_post( $post );
52 | }
53 |
54 | return new Zoninator_REST_Model_Collection( $collection );
55 | }
56 |
57 | /**
58 | * Get Entity
59 | *
60 | * @param int $id The id of the entity.
61 | * @return Zoninator_REST_Model|null
62 | */
63 | public function get_entity( $id ) {
64 | $post = get_post( absint( $id ) );
65 | if ( empty( $post ) || $post->post_type !== $this->post_type ) {
66 | return null;
67 | }
68 |
69 | return $this->create_from_post( $post );
70 | }
71 |
72 | /**
73 | * Create from Post.
74 | *
75 | * @param WP_Post $post Post.
76 | * @return Zoninator_REST_Model
77 | * @throws Zoninator_REST_Exception If something goes wrong.
78 | */
79 | private function create_from_post( $post ) {
80 | $raw_post_data = $post->to_array();
81 | $raw_meta_data = get_post_meta( $post->ID ); // assumes we are only ever adding one postmeta per key.
82 |
83 | $flattened_meta = array();
84 | foreach ( $raw_meta_data as $key => $value_arr ) {
85 | $flattened_meta[ $key ] = $value_arr[0];
86 | }
87 |
88 | $merged_data = array_merge( $raw_post_data, $flattened_meta );
89 |
90 | return $this->get_model_prototype()->create(
91 | $merged_data,
92 | array(
93 | 'deserialize' => true,
94 | )
95 | );
96 | }
97 |
98 | /**
99 | * Delete
100 | *
101 | * @param Zoninator_REST_Interfaces_Model $model Model.
102 | * @param array $args Args.
103 | * @return mixed
104 | */
105 | public function delete( $model, $args = array() ) {
106 | $id = $model->get_id();
107 |
108 | $args = wp_parse_args(
109 | $args,
110 | array(
111 | 'force_delete' => false,
112 | )
113 | );
114 |
115 | // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
116 | do_action( 'mixtape_data_store_delete_model_before', $model, $id );
117 |
118 | if ( $args['force_delete'] ) {
119 | $result = wp_delete_post( $model->get_id() );
120 | $model->set( 'id', 0 );
121 | // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
122 | do_action( 'mixtape_data_store_delete_model', $model, $id );
123 | } else {
124 | $result = wp_trash_post( $model->get_id() );
125 | $model->set( 'status', 'trash' );
126 | // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
127 | do_action( 'mixtape_data_store_trash_model', $model, $id );
128 | }
129 |
130 | if ( false === $result ) {
131 | // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
132 | do_action( 'mixtape_data_store_delete_model_fail', $model, $id );
133 | return new WP_Error( 'delete-failed', 'delete-failed' );
134 | }
135 |
136 | return $result;
137 | }
138 |
139 | /**
140 | * Upsert
141 | *
142 | * @param Zoninator_REST_Interfaces_Model $model Model.
143 | *
144 | * @return mixed|WP_Error
145 | */
146 | public function upsert( $model ) {
147 | $id = $model->get_id();
148 | $updating = ! empty( $id );
149 | $fields = $model->serialize( Zoninator_REST_Field_Declaration::FIELD );
150 | $meta_fields = $model->serialize( Zoninator_REST_Field_Declaration::META );
151 | if ( ! isset( $fields['post_type'] ) ) {
152 | $fields['post_type'] = $this->post_type;
153 | }
154 |
155 | if ( isset( $fields['ID'] ) && empty( $fields['ID'] ) ) {
156 | // ID of 0 is not acceptable on CPTs, so remove it.
157 | unset( $fields['ID'] );
158 | }
159 |
160 | // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
161 | do_action( 'mixtape_data_store_model_upsert_before', $model );
162 |
163 | $id_or_error = wp_insert_post( $fields, true );
164 | if ( is_wp_error( $id_or_error ) ) {
165 | // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
166 | do_action( 'mixtape_data_store_model_upsert_error', $model );
167 | return $id_or_error;
168 | }
169 |
170 | $model->set( 'id', absint( $id_or_error ) );
171 | foreach ( $meta_fields as $meta_key => $meta_value ) {
172 | if ( $updating ) {
173 | $id_or_bool = update_post_meta( $id_or_error, $meta_key, $meta_value );
174 | } else {
175 | $id_or_bool = add_post_meta( $id_or_error, $meta_key, $meta_value );
176 | }
177 |
178 | if ( false === $id_or_bool ) {
179 | // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
180 | do_action( 'mixtape_data_store_model_upsert_error', $model );
181 | // Something was wrong with this update/create. TODO: Should we stop mid create/update?
182 | return new WP_Error(
183 | 'mixtape-error-creating-meta',
184 | 'There was an error updating/creating an entity field',
185 | array(
186 | 'field_key' => $meta_key,
187 | 'field_value' => $meta_value,
188 | )
189 | );
190 | }
191 | }
192 |
193 | // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
194 | do_action( 'mixtape_data_store_model_upsert_after', $model );
195 |
196 | return absint( $id_or_error );
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/src/zoninator_rest/data/store/class-zoninator-rest-data-store-nil.php:
--------------------------------------------------------------------------------
1 | does_not_exist_guard = new stdClass();
33 | }
34 |
35 | /**
36 | * Get Entities
37 | *
38 | * @param null|mixed $filter Filter.
39 | * @return Zoninator_REST_Interfaces_Model
40 | */
41 | public function get_entities( $filter = null ) {
42 | // there is only one option bag and one option bag global per data store.
43 | return $this->get_entity( null );
44 | }
45 |
46 | /**
47 | * Get Entity
48 | *
49 | * @param int $id The id of the entity.
50 | * @return Zoninator_REST_Interfaces_Model
51 | */
52 | public function get_entity( $id ) {
53 | $field_declarations = $this->get_model_prototype()->get_fields();
54 | $raw_data = array();
55 | foreach ( $field_declarations as $field_declaration ) {
56 | /**
57 | * Field Declaration
58 | *
59 | * @var Zoninator_REST_Field_Declaration $field_declaration
60 | */
61 | $option = get_option( $field_declaration->get_map_from(), $this->does_not_exist_guard );
62 | if ( $this->does_not_exist_guard !== $option ) {
63 | $raw_data[ $field_declaration->get_map_from() ] = $option;
64 | }
65 | }
66 |
67 | return $this->get_model_prototype()->create(
68 | $raw_data,
69 | array(
70 | 'deserialize' => true,
71 | )
72 | );
73 | }
74 |
75 | /**
76 | * Delete
77 | *
78 | * @param Zoninator_REST_Interfaces_Model $model Model.
79 | * @param array $args Args.
80 | * @return mixed
81 | */
82 | public function delete( $model, $args = array() ) {
83 | $options_to_delete = array_keys( $model->serialize() );
84 | foreach ( $options_to_delete as $option_to_delete ) {
85 | if ( false !== get_option( $option_to_delete, false ) ) {
86 | $result = delete_option( $option_to_delete );
87 | if ( false === $result ) {
88 | return new WP_Error( 'delete-option-failed' );
89 | }
90 | }
91 | }
92 |
93 | return true;
94 | }
95 |
96 | /**
97 | * Update/Insert
98 | *
99 | * @param Zoninator_REST_Interfaces_Model $model Model.
100 | * @return mixed
101 | */
102 | public function upsert( $model ) {
103 | $fields_for_insert = $model->serialize();
104 | foreach ( $fields_for_insert as $option_name => $option_value ) {
105 | $previous_value = get_option( $option_name, $this->does_not_exist_guard );
106 | if ( $this->does_not_exist_guard !== $previous_value ) {
107 | update_option( $option_name, $option_value );
108 | } else {
109 | add_option( $option_name, $option_value );
110 | }
111 | }
112 |
113 | return true;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/zoninator_rest/field/class-zoninator-rest-field-declaration.php:
--------------------------------------------------------------------------------
1 | field_kinds, true ) ) {
190 | throw new Zoninator_REST_Exception( esc_html( 'every field should have a kind (one of ' . implode( ',', $this->field_kinds ) . ')' ) );
191 | }
192 |
193 | $this->name = $args['name'];
194 | $this->description = $this->value_or_default( $args, 'description', '' );
195 |
196 | $this->kind = $args['kind'];
197 | $this->type = $this->value_or_default( $args, 'type', Zoninator_REST_Type::any() );
198 | $this->choices = $this->value_or_default( $args, 'choices', null );
199 | $this->default_value = $this->value_or_default( $args, 'default_value' );
200 |
201 | $this->map_from = $this->value_or_default( $args, 'map_from' );
202 | $this->data_transfer_name = $this->value_or_default( $args, 'data_transfer_name', $this->get_name() );
203 |
204 | $this->primary = $this->value_or_default( $args, 'primary', false );
205 | $this->required = $this->value_or_default( $args, 'required', false );
206 | $this->supported_outputs = $this->value_or_default( $args, 'supported_outputs', array( 'json' ) );
207 |
208 | $this->sanitizer = $this->value_or_default( $args, 'sanitizer' );
209 | $this->validations = $this->value_or_default( $args, 'validations', array() );
210 |
211 | $this->serializer = $this->value_or_default( $args, 'serializer' );
212 | $this->deserializer = $this->value_or_default( $args, 'deserializer' );
213 |
214 | $this->before_get = $this->value_or_default( $args, 'before_get' );
215 | $this->before_set = $this->value_or_default( $args, 'before_set' );
216 |
217 | $this->reader = $this->value_or_default( $args, 'reader' );
218 | $this->updater = $this->value_or_default( $args, 'updater' );
219 | }
220 |
221 | /**
222 | * Get possible choices if set
223 | *
224 | * @return null|array
225 | */
226 | public function get_choices() {
227 | return $this->choices;
228 | }
229 |
230 | /**
231 | * Get Sanitizer
232 | *
233 | * @return callable|null
234 | */
235 | public function get_sanitizer() {
236 | return $this->sanitizer;
237 | }
238 |
239 | /**
240 | * Value or Default
241 | *
242 | * @param array $args Args.
243 | * @param string $name Name.
244 | * @param mixed $default_value Default.
245 | */
246 | private function value_or_default( $args, $name, $default_value = null ) {
247 | return $args[ $name ] ?? $default_value;
248 | }
249 |
250 | /**
251 | * Is Kind
252 | *
253 | * @param string $kind The kind.
254 | * @return bool
255 | */
256 | public function is_kind( $kind ) {
257 | if ( ! in_array( $kind, $this->field_kinds, true ) ) {
258 | return false;
259 | }
260 |
261 | return $this->kind === $kind;
262 | }
263 |
264 | /**
265 | * Get default value
266 | *
267 | * @return mixed
268 | */
269 | public function get_default_value() {
270 | if ( null !== $this->default_value && ! empty( $this->default_value ) ) {
271 | return ( is_array( $this->default_value ) && is_callable( $this->default_value ) ) ? call_user_func( $this->default_value ) : $this->default_value;
272 | }
273 |
274 | return $this->type->default_value();
275 | }
276 |
277 | /**
278 | * Cast a value
279 | *
280 | * @param mixed $value Val.
281 | * @return mixed
282 | */
283 | public function cast_value( $value ) {
284 | return $this->type->cast( $value );
285 | }
286 |
287 | /**
288 | * Supports this type of output.
289 | *
290 | * @param string $type Type.
291 | * @return bool
292 | */
293 | public function supports_output_type( $type ) {
294 | return in_array( $type, $this->supported_outputs, true );
295 | }
296 |
297 | /**
298 | * As Item Schema Property
299 | *
300 | * @return array
301 | */
302 | public function as_item_schema_property() {
303 | $schema = $this->type->schema();
304 | $schema['context'] = array( 'view', 'edit' );
305 | $schema['description'] = $this->get_description();
306 |
307 | if ( $this->get_choices() ) {
308 | $schema['enum'] = (array) $this->get_choices();
309 | }
310 |
311 | return $schema;
312 | }
313 |
314 | /**
315 | * Get Map From
316 | */
317 | public function get_map_from() {
318 | if ( null !== $this->map_from && ! empty( $this->map_from ) ) {
319 | return $this->map_from;
320 | }
321 |
322 | return $this->get_name();
323 | }
324 |
325 | /**
326 | * Get Kind
327 | *
328 | * @return mixed
329 | */
330 | public function get_kind() {
331 | return $this->kind;
332 | }
333 |
334 | /**
335 | * Get Name
336 | *
337 | * @return mixed
338 | */
339 | public function get_name() {
340 | return $this->name;
341 | }
342 |
343 | /**
344 | * Is Primary
345 | *
346 | * @return bool
347 | */
348 | public function is_primary() {
349 | return (bool) $this->primary;
350 | }
351 |
352 | /**
353 | * Is Required
354 | *
355 | * @return bool
356 | */
357 | public function is_required() {
358 | return (bool) $this->required;
359 | }
360 |
361 | /**
362 | * Get Description
363 | *
364 | * @return string
365 | */
366 | public function get_description() {
367 | if ( null !== $this->description && ! empty( $this->description ) ) {
368 | return $this->description;
369 | }
370 |
371 | return ucfirst( str_replace( '_', ' ', $this->get_name() ) );
372 | }
373 |
374 | /**
375 | * Get Dto name
376 | *
377 | * @return string
378 | */
379 | public function get_data_transfer_name() {
380 | return $this->data_transfer_name ?? $this->get_name();
381 | }
382 |
383 | /**
384 | * Get Validations
385 | *
386 | * @return array
387 | */
388 | public function get_validations() {
389 | return $this->validations;
390 | }
391 |
392 | /**
393 | * Get Before get
394 | *
395 | * @return callable|null
396 | */
397 | public function before_get() {
398 | return $this->before_get;
399 | }
400 |
401 | /**
402 | * Get Serializer
403 | *
404 | * @return callable|null
405 | */
406 | public function get_serializer() {
407 | return $this->serializer;
408 | }
409 |
410 | /**
411 | * Get Deserializer
412 | *
413 | * @return callable|null
414 | */
415 | public function get_deserializer() {
416 | return $this->deserializer;
417 | }
418 |
419 | /**
420 | * Get Type
421 | *
422 | * @return Zoninator_REST_Interfaces_Type
423 | */
424 | public function get_type() {
425 | return $this->type;
426 | }
427 |
428 | /**
429 | * Before Set
430 | *
431 | * @return callable|null
432 | */
433 | public function before_set() {
434 | return $this->before_set;
435 | }
436 |
437 | /**
438 | * Get Reader
439 | *
440 | * @return callable|null
441 | */
442 | public function get_reader() {
443 | return $this->reader;
444 | }
445 |
446 | /**
447 | * Get Updater
448 | *
449 | * @return callable|null
450 | */
451 | public function get_updater() {
452 | return $this->updater;
453 | }
454 | }
455 |
--------------------------------------------------------------------------------
/src/zoninator_rest/field/declaration/class-zoninator-rest-field-declaration-builder.php:
--------------------------------------------------------------------------------
1 | args = array(
25 | 'name' => '',
26 | 'kind' => Zoninator_REST_Field_Declaration::FIELD,
27 | 'type' => Zoninator_REST_Type::any(),
28 | 'required' => false,
29 | 'map_from' => null,
30 |
31 | 'sanitizer' => null,
32 |
33 | 'serializer' => null,
34 | 'deserializer' => null,
35 |
36 | 'default_value' => null,
37 | 'data_transfer_name' => null,
38 | 'supported_outputs' => array( 'json' ),
39 | 'description' => null,
40 | 'validations' => array(),
41 | 'choices' => null,
42 | 'contexts' => array( 'view', 'edit' ),
43 | 'before_set' => null,
44 | 'before_get' => null,
45 | 'reader' => null,
46 | 'updater' => null,
47 | );
48 | }
49 |
50 | /**
51 | * Build it
52 | *
53 | * @return Zoninator_REST_Field_Declaration
54 | */
55 | public function build() {
56 | return new Zoninator_REST_Field_Declaration( $this->args );
57 | }
58 |
59 | /**
60 | * Default Value.
61 | *
62 | * @param mixed $default_value Default.
63 | * @return Zoninator_REST_Field_Declaration_Builder
64 | */
65 | public function with_default( $default_value ) {
66 | return $this->with( 'default_value', $default_value );
67 | }
68 |
69 | /**
70 | * With Name
71 | *
72 | * @param string $name Name.
73 | * @return Zoninator_REST_Field_Declaration_Builder
74 | */
75 | public function with_name( $name ) {
76 | return $this->with( 'name', $name );
77 | }
78 |
79 | /**
80 | * With Kind
81 | *
82 | * @param string $kind Kind.
83 | * @return Zoninator_REST_Field_Declaration_Builder
84 | */
85 | public function with_kind( $kind ) {
86 | return $this->with( 'kind', $kind );
87 | }
88 |
89 | /**
90 | * With Map From
91 | *
92 | * @param string $mapped_from Mapped From.
93 | * @return Zoninator_REST_Field_Declaration_Builder
94 | */
95 | public function with_map_from( $mapped_from ) {
96 | return $this->with( 'map_from', $mapped_from );
97 | }
98 |
99 | /**
100 | * With Sanitizer
101 | *
102 | * @param callable $sanitizer Sanitizer.
103 | * @return Zoninator_REST_Field_Declaration_Builder
104 | */
105 | public function with_sanitizer( $sanitizer ) {
106 | $this->expect_is_callable( $sanitizer, __METHOD__ );
107 | return $this->with( 'sanitizer', $sanitizer );
108 | }
109 |
110 | /**
111 | * With Serializer
112 | *
113 | * @param callable $serializer Serializer.
114 | * @return Zoninator_REST_Field_Declaration_Builder
115 | */
116 | public function with_serializer( $serializer ) {
117 | return $this->with( 'serializer', $serializer );
118 | }
119 |
120 | /**
121 | * With Deserializer
122 | *
123 | * @param callable $deserializer Deserializer.
124 | * @return Zoninator_REST_Field_Declaration_Builder
125 | */
126 | public function with_deserializer( $deserializer ) {
127 | return $this->with( 'deserializer', $deserializer );
128 | }
129 |
130 | /**
131 | * With Required
132 | *
133 | * @param bool $required Req.
134 | * @return Zoninator_REST_Field_Declaration_Builder
135 | */
136 | public function with_required( $required = true ) {
137 | return $this->with( 'required', $required );
138 | }
139 |
140 | /**
141 | * With Supported Outputs
142 | *
143 | * @param array $supported_outputs Outputs.
144 | * @return Zoninator_REST_Field_Declaration_Builder
145 | */
146 | public function with_supported_outputs( $supported_outputs = array() ) {
147 | return $this->with( 'supported_outputs', (array) $supported_outputs );
148 | }
149 |
150 | /**
151 | * Set the type definition of this field declaration
152 | *
153 | * @param Zoninator_REST_Interfaces_Type $value_type Type.
154 | * @return Zoninator_REST_Field_Declaration_Builder $this
155 | *
156 | * @throws Zoninator_REST_Exception When not a type.
157 | */
158 | public function with_type( $value_type ) {
159 | if ( ! is_a( $value_type, 'Zoninator_REST_Interfaces_Type' ) ) {
160 | throw new Zoninator_REST_Exception( esc_html( get_class( $value_type ) . ' is not a Mixtape_Interfaces_Type' ) );
161 | }
162 |
163 | return $this->with( 'type', $value_type );
164 | }
165 |
166 | /**
167 | * With Dto Name
168 | *
169 | * @param string $dto_name Dto Name.
170 | * @return Zoninator_REST_Field_Declaration_Builder
171 | */
172 | public function with_dto_name( $dto_name ) {
173 | return $this->with( 'data_transfer_name', $dto_name );
174 | }
175 |
176 | /**
177 | * With Description
178 | *
179 | * @param string $description Description.
180 | * @return Zoninator_REST_Field_Declaration_Builder
181 | */
182 | public function with_description( $description ) {
183 | return $this->with( 'description', $description );
184 | }
185 |
186 | /**
187 | * With Validations
188 | *
189 | * @param array|mixed $validations Validations.
190 | * @return Zoninator_REST_Field_Declaration_Builder
191 | */
192 | public function with_validations( $validations ) {
193 | if ( is_callable( $validations ) || ! is_array( $validations ) ) {
194 | $validations = array( $validations );
195 | }
196 |
197 | return $this->with( 'validations', $validations );
198 | }
199 |
200 | /**
201 | * Before Set
202 | *
203 | * @param callable $before_set Before set.
204 | * @return Zoninator_REST_Field_Declaration_Builder
205 | */
206 | public function with_before_set( $before_set ) {
207 | return $this->with( 'before_set', $before_set );
208 | }
209 |
210 | /**
211 | * Before Get
212 | *
213 | * @param callable $before_get Before get.
214 | * @return Zoninator_REST_Field_Declaration_Builder
215 | */
216 | public function with_before_get( $before_get ) {
217 | return $this->with( 'before_get', $before_get );
218 | }
219 |
220 | /**
221 | * Choices.
222 | *
223 | * @param array|mixed $choices Choices.
224 | *
225 | * @return $this|Zoninator_REST_Field_Declaration_Builder
226 | */
227 | public function with_choices( $choices ) {
228 | if ( empty( $choices ) ) {
229 | return $this;
230 | }
231 |
232 | return $this->with( 'choices', is_array( $choices ) ? $choices : array( $choices ) );
233 | }
234 |
235 | /**
236 | * Set
237 | *
238 | * @param string $name Name.
239 | * @param mixed $value Value.
240 | * @return Zoninator_REST_Field_Declaration_Builder $this
241 | */
242 | private function with( $name, $value ) {
243 | $this->args[ $name ] = $value;
244 | return $this;
245 | }
246 |
247 | /**
248 | * Derived Field
249 | *
250 | * @param callable $func The func.
251 | *
252 | * @return Zoninator_REST_Field_Declaration_Builder
253 | */
254 | public function derived( $func = null ) {
255 | if ( $func ) {
256 | $this->with_map_from( $func );
257 | }
258 |
259 | return $this->with_kind( Zoninator_REST_Field_Declaration::DERIVED );
260 | }
261 |
262 | /**
263 | * Set Updater
264 | *
265 | * @param callable $func Func.
266 | * @return Zoninator_REST_Field_Declaration_Builder $this
267 | * @throws Zoninator_REST_Exception When no callable.
268 | */
269 | public function with_updater( $func ) {
270 | return $this->with( 'updater', $func );
271 | }
272 |
273 | /**
274 | * Set reader
275 | *
276 | * @param callable $func Func.
277 | * @return Zoninator_REST_Field_Declaration_Builder $this
278 | * @throws Zoninator_REST_Exception When no callable.
279 | */
280 | public function with_reader( $func ) {
281 | return $this->with( 'reader', $func );
282 | }
283 |
284 | /**
285 | * Callable test
286 | *
287 | * @param callable|mixed $thing Thing to test.
288 | * @param string $func The caller.
289 | *
290 | * @throws Zoninator_REST_Exception If not callable.
291 | */
292 | private function expect_is_callable( $thing, $func ) {
293 | Zoninator_REST_Expect::that( is_callable( $thing ), $func . ' Expected a callable' );
294 | }
295 | }
296 |
--------------------------------------------------------------------------------
/src/zoninator_rest/interfaces/class-zoninator-rest-interfaces-builder.php:
--------------------------------------------------------------------------------
1 | models = $models;
32 | }
33 |
34 | /**
35 | * Get the contents of this collection.
36 | *
37 | * @return Iterator
38 | */
39 | public function get_items() {
40 | return new ArrayIterator( $this->models );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/zoninator_rest/model/class-zoninator-rest-model-declaration.php:
--------------------------------------------------------------------------------
1 | model_definition = $def;
32 | return $this;
33 | }
34 |
35 | /**
36 | * Get definition.
37 | *
38 | * @return Zoninator_REST_Model_Definition
39 | */
40 | public function definition() {
41 | return $this->model_definition;
42 | }
43 |
44 | /**
45 | * Declare fields
46 | *
47 | * @param Zoninator_REST_Environment $env The Environment.
48 | *
49 | * @return void
50 | * @throws Zoninator_REST_Exception Override this.
51 | */
52 | public function declare_fields( $env ) {
53 | throw new Zoninator_REST_Exception( 'Override me: ' . __FUNCTION__ );
54 | }
55 |
56 | /**
57 | * Get the id
58 | *
59 | * @param Zoninator_REST_Interfaces_Model $model The model.
60 | *
61 | * @return mixed|null
62 | */
63 | public function get_id( $model ) {
64 | return $model->get( 'id' );
65 | }
66 |
67 | /**
68 | * Set the id
69 | *
70 | * @param Zoninator_REST_Interfaces_Model $model The model.
71 | * @param mixed $new_id The new id.
72 | *
73 | * @return mixed|null
74 | */
75 | public function set_id( $model, $new_id ) {
76 | return $model->set( 'id', $new_id );
77 | }
78 |
79 | /**
80 | * Call a method.
81 | *
82 | * @param string $method The method.
83 | * @param array $args The args.
84 | *
85 | * @return mixed
86 | * @throws Zoninator_REST_Exception Throw if method nonexistent.
87 | */
88 | public function call( $method, $args = array() ) {
89 | if ( is_callable( $method ) ) {
90 | return $this->perform_call( $method, $args );
91 | }
92 |
93 | Zoninator_REST_Expect::that( method_exists( $this, $method ), $method . ' does not exist' );
94 | return $this->perform_call( array( $this, $method ), $args );
95 | }
96 |
97 | /**
98 | * Get name
99 | *
100 | * @return string
101 | */
102 | public function get_name() {
103 | return strtolower( get_class( $this ) );
104 | }
105 |
106 | /**
107 | * Perform call
108 | *
109 | * @param mixed $a_callable A Callable.
110 | * @param array $args The args.
111 | *
112 | * @return mixed
113 | */
114 | private function perform_call( $a_callable, $args ) {
115 | return call_user_func_array( $a_callable, $args );
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/zoninator_rest/model/class-zoninator-rest-model-definition.php:
--------------------------------------------------------------------------------
1 | environment = $environment;
86 | $this->model_declaration = $model_declaration;
87 | $this->model_class = get_class( $model_declaration );
88 | $this->permissions_provider = $permissions_provider;
89 | $this->name = strtolower( $this->model_class );
90 |
91 | $this->set_data_store( $data_store );
92 | }
93 |
94 | /**
95 | * Get Model Class
96 | *
97 | * @return string
98 | */
99 | public function get_model_class() {
100 | return $this->model_class;
101 | }
102 |
103 | /**
104 | * Get Data Store
105 | *
106 | * @return Zoninator_REST_Interfaces_Data_Store
107 | */
108 | public function get_data_store() {
109 | return $this->data_store;
110 | }
111 |
112 | /**
113 | * Set the Data Store
114 | *
115 | * @param Zoninator_REST_Interfaces_Data_Store|Zoninator_REST_Data_Store_Builder $data_store A builder or a Data store.
116 | * @return $this
117 | * @throws Zoninator_REST_Exception Throws when Data Store Invalid.
118 | */
119 | public function set_data_store( $data_store ) {
120 | if ( is_a( $data_store, 'Zoninator_REST_Data_Store_Builder' ) ) {
121 | $this->data_store = $data_store
122 | ->with_model_definition( $this )
123 | ->build();
124 | } else {
125 | $this->data_store = $data_store;
126 | }
127 |
128 | // at this point we should have a data store.
129 | Zoninator_REST_Expect::is_a( $this->data_store, 'Zoninator_REST_Interfaces_Data_Store' );
130 |
131 | return $this;
132 | }
133 |
134 | /**
135 | * Environment
136 | *
137 | * @return Zoninator_REST_Environment
138 | */
139 | public function environment() {
140 | return $this->environment;
141 | }
142 |
143 | /**
144 | * Get this Definition's Field Declarations
145 | *
146 | * @param null|string $filter_by_type The type to filter with.
147 | *
148 | * @return array|null
149 | */
150 | public function get_field_declarations( $filter_by_type = null ) {
151 | $model_declaration = $this->get_model_declaration()->set_definition( $this );
152 |
153 | Zoninator_REST_Expect::is_a( $model_declaration, 'Zoninator_REST_Interfaces_Model_Declaration' );
154 |
155 | if ( null === $this->field_declarations ) {
156 | $fields = $model_declaration->declare_fields( $this->environment() );
157 |
158 | $this->field_declarations = $this->initialize_field_map( $fields );
159 | }
160 |
161 | if ( null === $filter_by_type ) {
162 | return $this->field_declarations;
163 | }
164 |
165 | $filtered = array();
166 |
167 | foreach ( $this->field_declarations as $field_declaration ) {
168 | /**
169 | * The field declaration.
170 | *
171 | * @var Zoninator_REST_Field_Declaration $field_declaration
172 | */
173 | if ( $field_declaration->get_kind() === $filter_by_type ) {
174 | $filtered[] = $field_declaration;
175 | }
176 | }
177 |
178 | return $filtered;
179 | }
180 |
181 | /**
182 | * Create a new Model Instance
183 | *
184 | * @param array $data The data.
185 | *
186 | * @return Zoninator_REST_Model
187 | * @throws Zoninator_REST_Exception Throws if data not an array.
188 | */
189 | public function create_instance( $data ) {
190 | if ( is_array( $data ) ) {
191 | return new Zoninator_REST_Model( $this, $data );
192 | }
193 |
194 | throw new Zoninator_REST_Exception( 'does not understand entity' );
195 | }
196 |
197 | /**
198 | * * Merge values from array with current values.
199 | * Note: Values change in place.
200 | *
201 | * @param Zoninator_REST_Interfaces_Model $model The model.
202 | * @param array $data The data (key-value assumed).
203 | * @param bool $updating Is this an update?.
204 | *
205 | * @return Zoninator_REST_Interfaces_Model|WP_Error
206 | * @throws Zoninator_REST_Exception Throws.
207 | */
208 | public function update_model_from_array( $model, $data, $updating = false ) {
209 | $mapped_data = $this->map_data( $data, $updating );
210 | foreach ( $mapped_data as $name => $value ) {
211 | $model->set( $name, $value );
212 | }
213 |
214 | return $model->sanitize();
215 | }
216 |
217 | /**
218 | * Get Model Declaration
219 | *
220 | * @return Zoninator_REST_Interfaces_Model_Declaration
221 | */
222 | public function get_model_declaration() {
223 | return $this->model_declaration;
224 | }
225 |
226 | /**
227 | * Creates a new Model From a Request
228 | *
229 | * @param array $data The request.
230 | * @return Zoninator_REST_Model|WP_Error
231 | */
232 | public function new_from_array( $data ) {
233 | $field_data = $this->map_data( $data, false );
234 | return $this->create_instance( $field_data )->sanitize();
235 | }
236 |
237 | /**
238 | * Get field DTO Mappings
239 | *
240 | * @return array
241 | */
242 | public function get_dto_field_mappings() {
243 | $mappings = array();
244 | foreach ( $this->get_field_declarations() as $field_declaration ) {
245 | /**
246 | * Declaration
247 | *
248 | * @var Zoninator_REST_Field_Declaration $field_declaration
249 | */
250 | if ( ! $field_declaration->supports_output_type( 'json' ) ) {
251 | continue;
252 | }
253 |
254 | $mappings[ $field_declaration->get_data_transfer_name() ] = $field_declaration->get_name();
255 | }
256 |
257 | return $mappings;
258 | }
259 |
260 | /**
261 | * Prepare the Model for Data Transfer
262 | *
263 | * @param Zoninator_REST_Interfaces_Model $model The model.
264 | *
265 | * @return array
266 | */
267 | public function model_to_dto( $model ) {
268 | $result = array();
269 | foreach ( $this->get_dto_field_mappings() as $mapping_name => $field_name ) {
270 | $value = $model->get( $field_name );
271 | $result[ $mapping_name ] = $value;
272 | }
273 |
274 | return $result;
275 | }
276 |
277 | /**
278 | * Get Name
279 | *
280 | * @return string
281 | */
282 | public function get_name() {
283 | return $this->name;
284 | }
285 |
286 | /**
287 | * Check permissions
288 | *
289 | * @param WP_REST_Request $request The request.
290 | * @param string $action The action.
291 | * @return bool
292 | */
293 | public function permissions_check( $request, $action ) {
294 | return $this->permissions_provider->permissions_check( $request, $action );
295 | }
296 |
297 | /**
298 | * Map data names
299 | *
300 | * @param array $data The data to map.
301 | * @param bool $updating Are we Updating.
302 | * @return array
303 | */
304 | private function map_data( $data, $updating = false ) {
305 | $request_data = array();
306 | $fields = $this->get_field_declarations();
307 | foreach ( $fields as $field ) {
308 | /**
309 | * Field
310 | *
311 | * @var Zoninator_REST_Field_Declaration $field Field.
312 | */
313 | if ( $field->is_kind( Zoninator_REST_Field_Declaration::DERIVED ) ) {
314 | continue;
315 | }
316 |
317 | $dto_name = $field->get_data_transfer_name();
318 | $field_name = $field->get_name();
319 | if ( isset( $data[ $dto_name ] ) && ! ( $updating && $field->is_primary() ) ) {
320 | $value = $data[ $dto_name ];
321 | $request_data[ $field_name ] = $value;
322 | }
323 | }
324 |
325 | return $request_data;
326 | }
327 |
328 | /**
329 | * Initialize_field_map
330 | *
331 | * @param array $declared_field_builders Array.
332 | *
333 | * @return array
334 | */
335 | private function initialize_field_map( $declared_field_builders ) {
336 | $fields = array();
337 | foreach ( $declared_field_builders as $field_builder ) {
338 | /**
339 | * Builder
340 | *
341 | * @var Zoninator_REST_Field_Declaration $field Field Builder.
342 | */
343 | $field = $field_builder->build();
344 | $fields[ $field->get_name() ] = $field;
345 | }
346 |
347 | return $fields;
348 | }
349 | }
350 |
--------------------------------------------------------------------------------
/src/zoninator_rest/model/class-zoninator-rest-model-settings.php:
--------------------------------------------------------------------------------
1 | get_environment();
59 | $settings_per_group = $this->get_settings();
60 | $fields = array();
61 |
62 | foreach ( $settings_per_group as $group_data ) {
63 | $group_fields = $group_data[1];
64 |
65 | foreach ( $group_fields as $field_data ) {
66 | $field_builder = $this->field_declaration_builder_from_data( $env, $field_data );
67 | $fields[] = $field_builder;
68 | }
69 | }
70 |
71 | return $fields;
72 | }
73 |
74 | /**
75 | * Convert bool to bit
76 | *
77 | * @param mixed $value Val.
78 | * @return string
79 | */
80 | public function bool_to_bit( $value ) {
81 | return ( ! empty( $value ) && 'false' !== $value ) ? '1' : '';
82 | }
83 |
84 | /**
85 | * Convert bit to bool
86 | *
87 | * @param mixed $value Val.
88 | * @return bool
89 | */
90 | public function bit_to_bool( $value ) {
91 | return ! empty( $value ) && '0' !== $value;
92 | }
93 |
94 | /**
95 | * Get ID
96 | *
97 | * @return string
98 | */
99 | public function get_id() {
100 | return strtolower( get_class( $this ) );
101 | }
102 |
103 | /**
104 | * Set ID
105 | *
106 | * @param mixed $new_id New ID.
107 | * @return Zoninator_REST_Interfaces_Model $this
108 | */
109 | public function set_id( $new_id ) {
110 | return $this;
111 | }
112 |
113 | /**
114 | * Build declarations from array
115 | *
116 | * @param Zoninator_REST_Environment $env Environment.
117 | * @param array $field_data Data.
118 | * @return Zoninator_REST_Field_Declaration_Builder
119 | */
120 | private function field_declaration_builder_from_data( $env, $field_data ) {
121 | $field_name = $field_data['name'];
122 | $field_builder = $env->field( $field_name );
123 | $default_value = $field_data['std'] ?? $this->default_for_attribute( $field_data, 'std' );
124 | $label = $field_data['label'] ?? $field_name;
125 | $description = $field_data['desc'] ?? $label;
126 | $setting_type = $field_data['type'] ?? null;
127 | $choices = isset( $field_data['options'] ) ? array_keys( $field_data['options'] ) : null;
128 | $field_type = 'string';
129 |
130 | if ( 'checkbox' === $setting_type ) {
131 | $field_type = 'boolean';
132 | if ( $default_value ) {
133 | // convert our default value as well.
134 | $default_value = $this->bit_to_bool( $default_value );
135 | }
136 |
137 | $field_builder
138 | ->with_serializer( array( $this, 'bool_to_bit' ) )
139 | ->with_deserializer( array( $this, 'bit_to_bool' ) );
140 | } elseif ( 'select' === $setting_type ) {
141 | $field_type = 'string';
142 | } elseif ( is_numeric( $default_value ) ) {
143 | // try to guess numeric fields, although this is not perfect.
144 | $field_type = is_float( $default_value ) ? 'float' : 'integer';
145 | }
146 |
147 | if ( $default_value ) {
148 | $field_builder->with_default( $default_value );
149 | }
150 |
151 | $field_builder
152 | ->with_description( $description )
153 | ->with_dto_name( $field_name )
154 | ->with_type( $env->type( $field_type ) );
155 | if ( $choices ) {
156 | $field_builder->with_choices( $choices );
157 | }
158 |
159 | $this->on_field_setup( $field_name, $field_builder, $field_data, $env );
160 |
161 | return $field_builder;
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/src/zoninator_rest/model/class-zoninator-rest-model-validationdata.php:
--------------------------------------------------------------------------------
1 | value = $value;
46 | $this->model = $model;
47 | $this->field = $field;
48 | }
49 |
50 | /**
51 | * Get Value
52 | *
53 | * @return mixed $this->value the value that needs validation
54 | */
55 | public function get_value() {
56 | return $this->value;
57 | }
58 |
59 | /**
60 | * Get Model
61 | *
62 | * @return Zoninator_REST_Interfaces_Model
63 | */
64 | public function get_model() {
65 | return $this->model;
66 | }
67 |
68 | /**
69 | * Get Field
70 | *
71 | * @return Zoninator_REST_Field_Declaration
72 | */
73 | public function get_field() {
74 | return $this->field;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/zoninator_rest/model/declaration/class-zoninator-rest-model-declaration-settings.php:
--------------------------------------------------------------------------------
1 | get_settings();
58 | $fields = array();
59 |
60 | foreach ( $settings_per_group as $group_data ) {
61 | $group_fields = $group_data[1];
62 |
63 | foreach ( $group_fields as $field_data ) {
64 | $field_builder = $this->field_declaration_builder_from_data( $env, $field_data );
65 | $fields[] = $field_builder;
66 | }
67 | }
68 |
69 | return $fields;
70 | }
71 |
72 | /**
73 | * Convert bool to bit
74 | *
75 | * @param mixed $value Val.
76 | * @return string
77 | */
78 | public function bool_to_bit( $value ) {
79 | return ( ! empty( $value ) && 'false' !== $value ) ? '1' : '';
80 | }
81 |
82 | /**
83 | * Convert bit to bool
84 | *
85 | * @param mixed $value Val.
86 | * @return bool
87 | */
88 | public function bit_to_bool( $value ) {
89 | return ! empty( $value ) && '0' !== $value;
90 | }
91 |
92 | /**
93 | * Get ID
94 | *
95 | * @param Zoninator_REST_Interfaces_Model $model Model.
96 | * @return string
97 | */
98 | public function get_id( $model ) {
99 | return strtolower( get_class( $this ) );
100 | }
101 |
102 | /**
103 | * Set ID
104 | *
105 | * @param Zoninator_REST_Interfaces_Model $model Model.
106 | * @param mixed $new_id New ID.
107 | * @return Zoninator_REST_Interfaces_Model $this
108 | */
109 | public function set_id( $model, $new_id ) {
110 | return $this;
111 | }
112 |
113 | /**
114 | * Build declarations from array
115 | *
116 | * @param Zoninator_REST_Environment $env Environment.
117 | * @param array $field_data Data.
118 | * @return Zoninator_REST_Field_Declaration_Builder
119 | */
120 | private function field_declaration_builder_from_data( $env, $field_data ) {
121 | $field_name = $field_data['name'];
122 | $field_builder = $env->field( $field_name );
123 | $default_value = $field_data['std'] ?? $this->default_for_attribute( $field_data, 'std' );
124 | $label = $field_data['label'] ?? $field_name;
125 | $description = $field_data['desc'] ?? $label;
126 | $setting_type = $field_data['type'] ?? null;
127 | $choices = isset( $field_data['options'] ) ? array_keys( $field_data['options'] ) : null;
128 | $field_type = 'string';
129 |
130 | if ( 'checkbox' === $setting_type ) {
131 | $field_type = 'boolean';
132 | if ( $default_value ) {
133 | // convert our default value as well.
134 | $default_value = $this->bit_to_bool( $default_value );
135 | }
136 |
137 | $field_builder
138 | ->with_serializer( array( $this, 'bool_to_bit' ) )
139 | ->with_deserializer( array( $this, 'bit_to_bool' ) );
140 | } elseif ( 'select' === $setting_type ) {
141 | $field_type = 'string';
142 | } elseif ( is_numeric( $default_value ) ) {
143 | // try to guess numeric fields, although this is not perfect.
144 | $field_type = is_float( $default_value ) ? 'float' : 'integer';
145 | }
146 |
147 | if ( $default_value ) {
148 | $field_builder->with_default( $default_value );
149 | }
150 |
151 | $field_builder
152 | ->with_description( $description )
153 | ->with_dto_name( $field_name )
154 | ->with_type( $env->type( $field_type ) );
155 | if ( $choices ) {
156 | $field_builder->with_choices( $choices );
157 | }
158 |
159 | $this->on_field_setup( $field_name, $field_builder, $field_data, $env );
160 | return $field_builder;
161 | }
162 |
163 | /**
164 | * Permissions Check
165 | *
166 | * @param WP_REST_Request $request Request.
167 | * @param string $action Action.
168 | * @return bool
169 | */
170 | public function permissions_check( $request, $action ) {
171 | return true;
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/src/zoninator_rest/model/definition/class-zoninator-rest-model-definition-builder.php:
--------------------------------------------------------------------------------
1 | with_data_store( new Zoninator_REST_Data_Store_Nil() )
49 | ->with_permissions_provider( new Zoninator_REST_Permissions_Any() );
50 | }
51 |
52 | /**
53 | * With Declaration
54 | *
55 | * @param Zoninator_REST_Interfaces_Model_Declaration|Zoninator_REST_Interfaces_Permissions_Provider $declaration D.
56 | * @return Zoninator_REST_Model_Definition_Builder
57 | */
58 | public function with_declaration( $declaration ) {
59 | if ( is_string( $declaration ) && class_exists( $declaration ) ) {
60 | $declaration = new $declaration();
61 | }
62 |
63 | Zoninator_REST_Expect::is_a( $declaration, 'Zoninator_REST_Interfaces_Model_Declaration' );
64 | $this->declaration = $declaration;
65 | if ( is_a( $declaration, 'Zoninator_REST_Interfaces_Permissions_Provider' ) ) {
66 | $this->with_permissions_provider( $declaration );
67 | }
68 |
69 | return $this;
70 | }
71 |
72 | /**
73 | * With Data Store
74 | *
75 | * @param null|Zoninator_REST_Interfaces_Builder $data_store Data Store.
76 | *
77 | * @return Zoninator_REST_Model_Definition_Builder $this
78 | */
79 | public function with_data_store( $data_store = null ) {
80 | $this->data_store = $data_store;
81 | return $this;
82 | }
83 |
84 | /**
85 | * With Permissions Provider
86 | *
87 | * @param Zoninator_REST_Interfaces_Permissions_Provider $permissions_provider Provider.
88 | */
89 | public function with_permissions_provider( $permissions_provider ) {
90 | $this->permissions_provider = $permissions_provider;
91 | }
92 |
93 | /**
94 | * With Environment
95 | *
96 | * @param Zoninator_REST_Environment $environment Environment.
97 | *
98 | * @return Zoninator_REST_Model_Definition_Builder $this
99 | */
100 | public function with_environment( $environment ) {
101 | $this->environment = $environment;
102 | return $this;
103 | }
104 |
105 | /**
106 | * Build
107 | *
108 | * @return Zoninator_REST_Model_Definition
109 | */
110 | public function build() {
111 | return new Zoninator_REST_Model_Definition( $this->environment, $this->declaration, $this->data_store, $this->permissions_provider );
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/zoninator_rest/permissions/class-zoninator-rest-permissions-any.php:
--------------------------------------------------------------------------------
1 | unsigned = $unsigned;
31 | parent::__construct( 'integer' );
32 | }
33 |
34 | /**
35 | * Default
36 | *
37 | * @return int
38 | */
39 | public function default_value() {
40 | return 0;
41 | }
42 |
43 | /**
44 | * Cast
45 | *
46 | * @param mixed $value Val.
47 | * @return int
48 | */
49 | public function cast( $value ) {
50 | if ( ! is_numeric( $value ) ) {
51 | return $this->default_value();
52 | }
53 |
54 | return $this->unsigned ? absint( $value ) : intval( $value, 10 );
55 | }
56 |
57 | /**
58 | * Sanitize
59 | *
60 | * @param mixed $value Val.
61 | * @return int
62 | */
63 | public function sanitize( $value ) {
64 | return $this->cast( $value );
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/zoninator_rest/type/class-zoninator-rest-type-nullable.php:
--------------------------------------------------------------------------------
1 | name() );
30 | $this->item_type_definition = $item_type_definition;
31 | }
32 |
33 | /**
34 | * Default value as always null.
35 | */
36 | public function default_value() {
37 | return null;
38 | }
39 |
40 | /**
41 | * Cast
42 | *
43 | * @param mixed $value Value.
44 | * @return mixed|null
45 | */
46 | public function cast( $value ) {
47 | if ( null === $value ) {
48 | return null;
49 | }
50 |
51 | return $this->item_type_definition->cast( $value );
52 | }
53 |
54 | /**
55 | * Sanitize.
56 | *
57 | * @param mixed $value Value.
58 | * @return mixed|null
59 | */
60 | public function sanitize( $value ) {
61 | if ( null === $value ) {
62 | return null;
63 | }
64 |
65 | return $this->item_type_definition->sanitize( $value );
66 | }
67 |
68 | /**
69 | * Schema
70 | */
71 | public function schema() {
72 | $schema = parent::schema();
73 | $schema['type'] = array_unique( array_merge( $schema['type'], array( 'null' ) ) );
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/zoninator_rest/type/class-zoninator-rest-type-number.php:
--------------------------------------------------------------------------------
1 | default_value();
42 | }
43 |
44 | return floatval( $value );
45 | }
46 |
47 | /**
48 | * Sanitize
49 | *
50 | * @param mixed $value The value to sanitize.
51 | * @return float
52 | */
53 | public function sanitize( $value ) {
54 | return $this->cast( $value );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/zoninator_rest/type/class-zoninator-rest-type-registry.php:
--------------------------------------------------------------------------------
1 | types[ $identifier ] = $instance;
46 | return $this;
47 | }
48 |
49 | /**
50 | * Get a type definition
51 | *
52 | * @param string $type The type name.
53 | * @return Zoninator_REST_Interfaces_Type
54 | *
55 | * @throws Zoninator_REST_Exception In case of type name not confirming to syntax.
56 | */
57 | public function definition( $type ) {
58 | $types = $this->get_types();
59 |
60 | if ( ! isset( $types[ $type ] ) ) {
61 | // maybe lazy-register missing compound type.
62 | $parts = explode( ':', $type );
63 | if ( count( $parts ) > 1 ) {
64 | $container_type = $parts[0];
65 | if ( ! in_array( $container_type, $this->container_types, true ) ) {
66 | throw new Zoninator_REST_Exception( esc_html( $container_type . ' is not a known container type' ) );
67 | }
68 |
69 | $item_type = $parts[1];
70 | if ( empty( $item_type ) ) {
71 | throw new Zoninator_REST_Exception( esc_html( $type . ': invalid syntax' ) );
72 | }
73 |
74 | $item_type_definition = $this->definition( $item_type );
75 |
76 | if ( 'array' === $container_type ) {
77 | $this->define( $type, new Zoninator_REST_Type_TypedArray( $item_type_definition ) );
78 | $types = $this->get_types();
79 | }
80 |
81 | if ( 'nullable' === $container_type ) {
82 | $this->define( $type, new Zoninator_REST_Type_Nullable( $item_type_definition ) );
83 | $types = $this->get_types();
84 | }
85 | }
86 | }
87 |
88 | if ( ! isset( $types[ $type ] ) ) {
89 | throw new Zoninator_REST_Exception();
90 | }
91 |
92 | return $types[ $type ];
93 | }
94 |
95 | /**
96 | * Get Types
97 | *
98 | * @return array
99 | */
100 | private function get_types() {
101 | // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
102 | return (array) apply_filters( 'mixtape_type_registry_get_types', $this->types, $this );
103 | }
104 |
105 | /**
106 | * Initialize the type registry
107 | *
108 | * @param Zoninator_REST_Environment $environment The Environment.
109 | */
110 | public function initialize( $environment ) {
111 | if ( null !== $this->types ) {
112 | return;
113 | }
114 |
115 | $this->types = apply_filters(
116 | 'mixtape_type_registry_register_types', // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
117 | array(
118 | 'any' => new Zoninator_REST_Type( 'any' ),
119 | 'string' => new Zoninator_REST_Type_String(),
120 | 'integer' => new Zoninator_REST_Type_Integer(),
121 | 'int' => new Zoninator_REST_Type_Integer(),
122 | 'uint' => new Zoninator_REST_Type_Integer( true ),
123 | 'number' => new Zoninator_REST_Type_Number(),
124 | 'float' => new Zoninator_REST_Type_Number(),
125 | 'boolean' => new Zoninator_REST_Type_Boolean(),
126 | 'array' => new Zoninator_REST_Type_Array(),
127 | ),
128 | $this,
129 | $environment
130 | );
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/zoninator_rest/type/class-zoninator-rest-type-string.php:
--------------------------------------------------------------------------------
1 | cast( $v );
53 | }
54 |
55 | return '(' . implode( ',', $cast_ones ) . ')';
56 | }
57 |
58 | return (string) $value;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/zoninator_rest/type/class-zoninator-rest-type-typedarray.php:
--------------------------------------------------------------------------------
1 | name() );
33 | $this->item_type_definition = $item_type_definition;
34 | }
35 |
36 | /**
37 | * Get the default value
38 | *
39 | * @return array
40 | */
41 | public function default_value() {
42 | return array();
43 | }
44 |
45 | /**
46 | * Cast the value to be a typed array
47 | *
48 | * @param mixed $value an array of mixed.
49 | * @return array
50 | */
51 | public function cast( $value ) {
52 | $new_value = array();
53 |
54 | foreach ( $value as $v ) {
55 | $new_value[] = $this->item_type_definition->cast( $v );
56 | }
57 |
58 | return $new_value;
59 | }
60 |
61 | /**
62 | * Get this type's JSON Schema
63 | *
64 | * @return array
65 | */
66 | public function schema() {
67 | $schema = parent::schema();
68 | $schema['type'] = 'array';
69 | $schema['items'] = $this->item_type_definition->schema();
70 | return $schema;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/zoninator.php:
--------------------------------------------------------------------------------
1 |
16 |
17 | This program is free software; you can redistribute it and/or modify
18 | it under the terms of the GNU General Public License as published by
19 | the Free Software Foundation; either version 2 of the License, or
20 | (at your option) any later version.
21 |
22 | This program is distributed in the hope that it will be useful,
23 | but WITHOUT ANY WARRANTY; without even the implied warranty of
24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 | GNU General Public License for more details.
26 |
27 | You should have received a copy of the GNU General Public License
28 | along with this program; if not, write to the Free Software
29 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 |
31 | */
32 |
33 | define( 'ZONINATOR_VERSION', '0.10.1' );
34 | define( 'ZONINATOR_FILE', __FILE__ );
35 |
36 | require_once __DIR__ . '/functions.php';
37 | require_once __DIR__ . '/src/class-zoninator-zoneposts-widget.php';
38 | require_once __DIR__ . '/src/class-zoninator.php';
39 |
40 | function Zoninator() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid, Universal.Files.SeparateFunctionsFromOO.Mixed -- Windows is case-sensitive, so changing this is a breaking change.
41 | global $zoninator;
42 | if ( ! isset( $zoninator ) || null === $zoninator ) {
43 | $zoninator = new Zoninator();
44 | }
45 |
46 | return $zoninator;
47 | }
48 |
49 | Zoninator();
50 |
--------------------------------------------------------------------------------