Version Description
- added admin dropdowns
- fixed SQL error related to user connections
- fixed 'labels' handling and added 'column_title' subkey
- refactor metabox JavaScript using Backbone.js
- lazy-load connection candidates, for faster page loads
- lazy-load PHP classes using
spl_register_autoload()
Download this release
Release Info
Developer | scribu |
Plugin | Posts 2 Posts |
Version | 1.5 |
Comparing to | |
See all releases |
Code changes from version 1.4.3 to 1.5
- admin/box-factory.php +10 -26
- admin/box.css +7 -10
- admin/box.js +360 -234
- admin/box.php +25 -15
- admin/column-factory.php +6 -39
- admin/column-post.php +34 -0
- admin/column-user.php +46 -0
- admin/column.php +25 -70
- admin/dropdown-factory.php +19 -0
- admin/dropdown-post.php +17 -0
- admin/dropdown-user.php +37 -0
- admin/dropdown.php +61 -0
- admin/factory.php +49 -9
- admin/field-create.php +25 -0
- admin/field-delete.php +23 -0
- admin/field-generic.php +27 -0
- admin/field-order.php +23 -0
- admin/field-title-attachment.php +13 -0
- admin/field-title-post.php +22 -0
- admin/field-title-user.php +11 -0
- admin/field-title.php +26 -0
- admin/field.php +7 -0
- admin/fields.php +0 -159
- admin/mustache.js +610 -0
- admin/templates/tab-list.html +1 -1
- admin/{tools.php → tools-page.php} +0 -2
- autoload.php +35 -0
- command.php +1 -2
- core/api.php +40 -58
- core/{type-factory.php → connection-type-factory.php} +0 -2
- core/{type.php → connection-type.php} +6 -38
- core/{directed-type.php → directed-connection-type.php} +0 -0
- core/indeterminate-connection-type.php +17 -0
- core/{indeterminate-type.php → indeterminate-directed-connection-type.php} +0 -0
- core/item-any.php +19 -0
- core/item-attachment.php +12 -0
- core/item-post.php +17 -0
- core/item-user.php +41 -0
- core/item.php +0 -82
- core/list.php +12 -14
- core/query-post.php +1 -3
- core/query-user.php +1 -3
- core/reciprocal-connection-type.php +18 -0
- core/shortcodes.php +35 -0
- core/side-attachment.php +23 -0
- core/side-post.php +138 -0
- core/side-user.php +122 -0
- core/side.php +0 -277
- core/storage.php +0 -2
- core/url-query.php +0 -24
- core/util.php +75 -0
- core/{extra.php → widget.php} +0 -84
- posts-to-posts.php +24 -21
- readme.txt +10 -3
- scb/AdminPage.php +223 -143
- scb/BoxesPage.php +38 -18
- scb/Cron.php +55 -41
- scb/Forms.php +370 -120
- scb/PostMetabox.php +182 -0
- scb/load.php +2 -2
admin/box-factory.php
CHANGED
@@ -4,22 +4,18 @@ define( 'P2P_BOX_NONCE', 'p2p-box' );
|
|
4 |
|
5 |
class P2P_Box_Factory extends P2P_Factory {
|
6 |
|
|
|
|
|
7 |
function __construct() {
|
8 |
-
|
9 |
|
10 |
-
add_action( 'add_meta_boxes', array( $this, '
|
11 |
add_action( 'save_post', array( $this, 'save_post' ), 10, 2 );
|
12 |
add_action( 'wp_ajax_p2p_box', array( $this, 'wp_ajax_p2p_box' ) );
|
13 |
}
|
14 |
|
15 |
-
function
|
16 |
-
|
17 |
-
$box_args = _p2p_pluck( $args, 'admin_box' );
|
18 |
-
if ( !is_array( $box_args ) )
|
19 |
-
$box_args = array( 'show' => $box_args );
|
20 |
-
} else {
|
21 |
-
$box_args = array();
|
22 |
-
}
|
23 |
|
24 |
foreach ( array( 'can_create_post' ) as $key ) {
|
25 |
if ( isset( $args[ $key ] ) ) {
|
@@ -28,18 +24,15 @@ class P2P_Box_Factory extends P2P_Factory {
|
|
28 |
}
|
29 |
|
30 |
$box_args = wp_parse_args( $box_args, array(
|
31 |
-
'show' => 'any',
|
32 |
'context' => 'side',
|
33 |
'priority' => 'default',
|
34 |
'can_create_post' => true
|
35 |
) );
|
36 |
|
37 |
-
|
38 |
-
|
39 |
-
return $args;
|
40 |
}
|
41 |
|
42 |
-
function
|
43 |
$this->filter( 'post', $post_type );
|
44 |
}
|
45 |
|
@@ -100,7 +93,7 @@ class P2P_Box_Factory extends P2P_Factory {
|
|
100 |
if ( isset( $_POST['p2p_connections'] ) ) {
|
101 |
// Loop through the hidden fields instead of through $_POST['p2p_meta'] because empty checkboxes send no data.
|
102 |
foreach ( $_POST['p2p_connections'] as $p2p_id ) {
|
103 |
-
$data =
|
104 |
|
105 |
$connection = p2p_get_connection( $p2p_id );
|
106 |
|
@@ -112,7 +105,7 @@ class P2P_Box_Factory extends P2P_Factory {
|
|
112 |
|
113 |
$data = scbForms::validate_post_data( $fields, $data );
|
114 |
|
115 |
-
scbForms::update_meta( $fields,
|
116 |
}
|
117 |
}
|
118 |
|
@@ -126,13 +119,6 @@ class P2P_Box_Factory extends P2P_Factory {
|
|
126 |
}
|
127 |
}
|
128 |
|
129 |
-
private function addslashes_deep( $value ) {
|
130 |
-
if ( is_array( $value ) )
|
131 |
-
return array_map( array( __CLASS__, __METHOD__ ), $value );
|
132 |
-
|
133 |
-
return addslashes( $value );
|
134 |
-
}
|
135 |
-
|
136 |
/**
|
137 |
* Controller for all box ajax requests.
|
138 |
*/
|
@@ -162,5 +148,3 @@ class P2P_Box_Factory extends P2P_Factory {
|
|
162 |
}
|
163 |
}
|
164 |
|
165 |
-
new P2P_Box_Factory;
|
166 |
-
|
4 |
|
5 |
class P2P_Box_Factory extends P2P_Factory {
|
6 |
|
7 |
+
protected $key = 'admin_box';
|
8 |
+
|
9 |
function __construct() {
|
10 |
+
parent::__construct();
|
11 |
|
12 |
+
add_action( 'add_meta_boxes', array( $this, 'add_items' ) );
|
13 |
add_action( 'save_post', array( $this, 'save_post' ), 10, 2 );
|
14 |
add_action( 'wp_ajax_p2p_box', array( $this, 'wp_ajax_p2p_box' ) );
|
15 |
}
|
16 |
|
17 |
+
function expand_arg( $args ) {
|
18 |
+
$box_args = parent::expand_arg( $args );
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
|
20 |
foreach ( array( 'can_create_post' ) as $key ) {
|
21 |
if ( isset( $args[ $key ] ) ) {
|
24 |
}
|
25 |
|
26 |
$box_args = wp_parse_args( $box_args, array(
|
|
|
27 |
'context' => 'side',
|
28 |
'priority' => 'default',
|
29 |
'can_create_post' => true
|
30 |
) );
|
31 |
|
32 |
+
return $box_args;
|
|
|
|
|
33 |
}
|
34 |
|
35 |
+
function add_items( $post_type ) {
|
36 |
$this->filter( 'post', $post_type );
|
37 |
}
|
38 |
|
93 |
if ( isset( $_POST['p2p_connections'] ) ) {
|
94 |
// Loop through the hidden fields instead of through $_POST['p2p_meta'] because empty checkboxes send no data.
|
95 |
foreach ( $_POST['p2p_connections'] as $p2p_id ) {
|
96 |
+
$data = scbForms::get_value( array( 'p2p_meta', $p2p_id ), $_POST, array() );
|
97 |
|
98 |
$connection = p2p_get_connection( $p2p_id );
|
99 |
|
105 |
|
106 |
$data = scbForms::validate_post_data( $fields, $data );
|
107 |
|
108 |
+
scbForms::update_meta( $fields, $data, $p2p_id, 'p2p' );
|
109 |
}
|
110 |
}
|
111 |
|
119 |
}
|
120 |
}
|
121 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
122 |
/**
|
123 |
* Controller for all box ajax requests.
|
124 |
*/
|
148 |
}
|
149 |
}
|
150 |
|
|
|
|
admin/box.css
CHANGED
@@ -183,13 +183,12 @@ tr:hover td.p2p-col-order {
|
|
183 |
.p2p-navigation div {
|
184 |
float: left;
|
185 |
line-height: 13px;
|
186 |
-
padding
|
187 |
-
padding-bottom: 3px;
|
188 |
-
margin-right: 5px;
|
189 |
}
|
190 |
|
191 |
.p2p-navigation .p2p-spinner {
|
192 |
-
margin-top:
|
|
|
193 |
}
|
194 |
|
195 |
|
@@ -209,6 +208,10 @@ body.rtl .p2p-tab-search input {
|
|
209 |
padding-right: 0;
|
210 |
}
|
211 |
|
|
|
|
|
|
|
|
|
212 |
body.rtl .p2p-col-create,
|
213 |
body.rtl .p2p-col-delete {
|
214 |
border-left: 1px solid transparent;
|
@@ -219,9 +222,3 @@ body.rtl .p2p-col-create:hover,
|
|
219 |
body.rtl .p2p-col-delete:hover {
|
220 |
border-left-color: #ddd;
|
221 |
}
|
222 |
-
|
223 |
-
body.rtl .p2p-navigation div {
|
224 |
-
margin-right: 0;
|
225 |
-
margin-left: 5px;
|
226 |
-
}
|
227 |
-
|
183 |
.p2p-navigation div {
|
184 |
float: left;
|
185 |
line-height: 13px;
|
186 |
+
padding: 6px 5px 0;
|
|
|
|
|
187 |
}
|
188 |
|
189 |
.p2p-navigation .p2p-spinner {
|
190 |
+
margin-top: 5px;
|
191 |
+
margin-left: 5px;
|
192 |
}
|
193 |
|
194 |
|
208 |
padding-right: 0;
|
209 |
}
|
210 |
|
211 |
+
body.rtl .p2p-navigation .p2p-spinner {
|
212 |
+
margin-right: 5px;
|
213 |
+
}
|
214 |
+
|
215 |
body.rtl .p2p-col-create,
|
216 |
body.rtl .p2p-col-delete {
|
217 |
border-left: 1px solid transparent;
|
222 |
body.rtl .p2p-col-delete:hover {
|
223 |
border-left-color: #ddd;
|
224 |
}
|
|
|
|
|
|
|
|
|
|
|
|
admin/box.js
CHANGED
@@ -1,4 +1,318 @@
|
|
|
|
1 |
(function() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
jQuery(function() {
|
4 |
var clearVal, setVal;
|
@@ -23,259 +337,71 @@
|
|
23 |
};
|
24 |
jQuery('.p2p-search input[placeholder]').each(setVal).focus(clearVal).blur(setVal);
|
25 |
}
|
|
|
26 |
return jQuery('.p2p-box').each(function() {
|
27 |
-
var $
|
28 |
$metabox = jQuery(this);
|
29 |
-
$connections = $metabox.find('.p2p-connections');
|
30 |
$spinner = jQuery('<img>', {
|
31 |
'src': P2PAdmin.spinner,
|
32 |
'class': 'p2p-spinner'
|
33 |
});
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
action: 'p2p_box',
|
41 |
-
nonce: P2PAdmin.nonce
|
42 |
-
p2p_type: $metabox.data('p2p_type'),
|
43 |
-
direction: $metabox.data('direction'),
|
44 |
-
from: jQuery('#post_ID').val(),
|
45 |
-
s: searchTab.params.s,
|
46 |
-
paged: searchTab.params.paged
|
47 |
});
|
48 |
-
|
49 |
try {
|
50 |
response = jQuery.parseJSON(response);
|
51 |
-
if (response.error) {
|
52 |
-
return alert(response.error);
|
53 |
-
} else {
|
54 |
-
return callback(response);
|
55 |
-
}
|
56 |
} catch (e) {
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
return jQuery.ajax({
|
61 |
-
type: type,
|
62 |
-
url: ajaxurl,
|
63 |
-
data: data,
|
64 |
-
success: handler
|
65 |
-
});
|
66 |
-
};
|
67 |
-
PostsTab = (function() {
|
68 |
-
|
69 |
-
function PostsTab(selector) {
|
70 |
-
var _this = this;
|
71 |
-
this.tab = $metabox.find(selector);
|
72 |
-
this.params = {
|
73 |
-
subaction: 'search',
|
74 |
-
s: ''
|
75 |
-
};
|
76 |
-
this.init_pagination_data();
|
77 |
-
this.tab.delegate('.p2p-prev, .p2p-next', 'click', function(ev) {
|
78 |
-
return _this.change_page(ev.target);
|
79 |
-
});
|
80 |
-
}
|
81 |
-
|
82 |
-
PostsTab.prototype.init_pagination_data = function() {
|
83 |
-
this.params.paged = this.tab.find('.p2p-current').data('num') || 1;
|
84 |
-
return this.total_pages = this.tab.find('.p2p-total').data('num') || 1;
|
85 |
-
};
|
86 |
-
|
87 |
-
PostsTab.prototype.change_page = function(button) {
|
88 |
-
var $navButton, new_page;
|
89 |
-
$navButton = jQuery(button);
|
90 |
-
new_page = this.params.paged;
|
91 |
-
if ($navButton.hasClass('inactive')) {
|
92 |
return;
|
93 |
}
|
94 |
-
if (
|
95 |
-
|
96 |
} else {
|
97 |
-
|
98 |
-
}
|
99 |
-
$spinner.appendTo(this.tab.find('.p2p-navigation'));
|
100 |
-
return this.find_posts(new_page);
|
101 |
-
};
|
102 |
-
|
103 |
-
PostsTab.prototype.find_posts = function(new_page) {
|
104 |
-
var _this = this;
|
105 |
-
if ((0 < new_page && new_page <= this.total_pages)) {
|
106 |
-
this.params.paged = new_page;
|
107 |
-
}
|
108 |
-
return ajax_request(this.params, function(response) {
|
109 |
-
return _this.update_rows(response);
|
110 |
-
}, 'GET');
|
111 |
-
};
|
112 |
-
|
113 |
-
PostsTab.prototype.update_rows = function(response) {
|
114 |
-
$spinner.remove();
|
115 |
-
this.tab.find('button, .p2p-results, .p2p-navigation, .p2p-notice').remove();
|
116 |
-
this.tab.append(response.rows);
|
117 |
-
return this.init_pagination_data();
|
118 |
-
};
|
119 |
-
|
120 |
-
return PostsTab;
|
121 |
-
|
122 |
-
})();
|
123 |
-
searchTab = new PostsTab('.p2p-tab-search');
|
124 |
-
row_ajax_request = function($td, data, callback) {
|
125 |
-
$td.find('.p2p-icon').css('background-image', 'url(' + P2PAdmin.spinner + ')');
|
126 |
-
return ajax_request(data, callback);
|
127 |
-
};
|
128 |
-
remove_row = function($td) {
|
129 |
-
var $table;
|
130 |
-
$table = $td.closest('table');
|
131 |
-
$td.closest('tr').remove();
|
132 |
-
if (!$table.find('tbody tr').length) {
|
133 |
-
return $table.hide();
|
134 |
-
}
|
135 |
-
};
|
136 |
-
append_connection = function(response) {
|
137 |
-
$connections.show().find('tbody').append(response.row);
|
138 |
-
if ('one' === $metabox.data('cardinality')) {
|
139 |
-
return $metabox.find('.p2p-create-connections').hide();
|
140 |
-
}
|
141 |
-
};
|
142 |
-
refresh_candidates = function(results) {
|
143 |
-
$metabox.find('.p2p-create-connections').show();
|
144 |
-
return searchTab.update_rows(results);
|
145 |
-
};
|
146 |
-
clear_connections = function(ev) {
|
147 |
-
var $td, data,
|
148 |
-
_this = this;
|
149 |
-
ev.preventDefault();
|
150 |
-
if (!confirm(P2PAdmin.deleteConfirmMessage)) {
|
151 |
-
return;
|
152 |
-
}
|
153 |
-
$td = jQuery(ev.target).closest('td');
|
154 |
-
data = {
|
155 |
-
subaction: 'clear_connections'
|
156 |
-
};
|
157 |
-
row_ajax_request($td, data, function(response) {
|
158 |
-
$connections.hide().find('tbody').html('');
|
159 |
-
return refresh_candidates(response);
|
160 |
-
});
|
161 |
-
return null;
|
162 |
-
};
|
163 |
-
delete_connection = function(ev) {
|
164 |
-
var $td, data,
|
165 |
-
_this = this;
|
166 |
-
ev.preventDefault();
|
167 |
-
$td = jQuery(ev.target).closest('td');
|
168 |
-
data = {
|
169 |
-
subaction: 'disconnect',
|
170 |
-
p2p_id: $td.find('input').val()
|
171 |
-
};
|
172 |
-
row_ajax_request($td, data, function(response) {
|
173 |
-
remove_row($td);
|
174 |
-
return refresh_candidates(response);
|
175 |
-
});
|
176 |
-
return null;
|
177 |
-
};
|
178 |
-
create_connection = function(ev) {
|
179 |
-
var $td, data,
|
180 |
-
_this = this;
|
181 |
-
ev.preventDefault();
|
182 |
-
$td = jQuery(ev.target).closest('td');
|
183 |
-
data = {
|
184 |
-
subaction: 'connect',
|
185 |
-
to: $td.find('div').data('item-id')
|
186 |
-
};
|
187 |
-
row_ajax_request($td, data, function(response) {
|
188 |
-
append_connection(response);
|
189 |
-
if ($metabox.data('duplicate_connections')) {
|
190 |
-
return $td.find('.p2p-icon').css('background-image', '');
|
191 |
-
} else {
|
192 |
-
return remove_row($td);
|
193 |
}
|
194 |
});
|
195 |
-
return null;
|
196 |
};
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
ev.preventDefault();
|
205 |
-
$tab = jQuery(this);
|
206 |
-
$metabox.find('.wp-tab-bar li').removeClass('wp-tab-active');
|
207 |
-
$tab.addClass('wp-tab-active');
|
208 |
-
return $metabox.find('.tabs-panel').hide().end().find($tab.data('ref')).show().find(':text').focus();
|
209 |
-
};
|
210 |
-
$metabox.delegate('th.p2p-col-delete .p2p-icon', 'click', clear_connections).delegate('td.p2p-col-delete .p2p-icon', 'click', delete_connection).delegate('td.p2p-col-create div', 'click', create_connection).delegate('.p2p-toggle-tabs', 'click', toggle_tabs).delegate('.wp-tab-bar li', 'click', switch_to_tab);
|
211 |
-
if ($connections.find('th.p2p-col-order').length) {
|
212 |
-
$connections.find('tbody').sortable({
|
213 |
-
handle: 'td.p2p-col-order',
|
214 |
-
helper: function(e, ui) {
|
215 |
-
ui.children().each(function() {
|
216 |
-
var $this;
|
217 |
-
$this = jQuery(this);
|
218 |
-
return $this.width($this.width());
|
219 |
-
});
|
220 |
-
return ui;
|
221 |
-
}
|
222 |
-
});
|
223 |
-
}
|
224 |
-
$searchInput = $metabox.find('.p2p-tab-search :text');
|
225 |
-
$searchInput.keypress(function(ev) {
|
226 |
-
if (ev.keyCode === 13) {
|
227 |
-
ev.preventDefault();
|
228 |
-
}
|
229 |
-
return null;
|
230 |
-
}).keyup(function(ev) {
|
231 |
-
var delayed;
|
232 |
-
if (delayed !== void 0) {
|
233 |
-
clearTimeout(delayed);
|
234 |
-
}
|
235 |
-
delayed = setTimeout(function() {
|
236 |
-
var searchStr;
|
237 |
-
searchStr = $searchInput.val();
|
238 |
-
if (searchStr === searchTab.params.s) {
|
239 |
-
return;
|
240 |
-
}
|
241 |
-
searchTab.params.s = searchStr;
|
242 |
-
$spinner.insertAfter($searchInput).show();
|
243 |
-
return searchTab.find_posts(1);
|
244 |
-
}, 400);
|
245 |
-
return null;
|
246 |
});
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
$
|
253 |
-
if ($button.hasClass('inactive')) {
|
254 |
-
return;
|
255 |
-
}
|
256 |
-
title = $createInput.val();
|
257 |
-
if (title === '') {
|
258 |
-
$createInput.focus();
|
259 |
-
return;
|
260 |
-
}
|
261 |
-
$button.addClass('inactive');
|
262 |
-
data = {
|
263 |
-
subaction: 'create_post',
|
264 |
-
post_title: title
|
265 |
-
};
|
266 |
-
ajax_request(data, function(response) {
|
267 |
-
append_connection(response);
|
268 |
-
$createInput.val('');
|
269 |
-
return $button.removeClass('inactive');
|
270 |
-
});
|
271 |
-
return null;
|
272 |
});
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
|
|
|
|
|
|
|
|
279 |
});
|
280 |
});
|
281 |
});
|
1 |
+
// Generated by CoffeeScript 1.4.0
|
2 |
(function() {
|
3 |
+
var Candidates, CandidatesView, Connections, ConnectionsView, CreatePostView, ENTER_KEY, MetaboxView, get_mustache_template, remove_row, row_wait;
|
4 |
+
|
5 |
+
ENTER_KEY = 13;
|
6 |
+
|
7 |
+
row_wait = function($td) {
|
8 |
+
return $td.find('.p2p-icon').css('background-image', 'url(' + P2PAdmin.spinner + ')');
|
9 |
+
};
|
10 |
+
|
11 |
+
remove_row = function($td) {
|
12 |
+
var $table;
|
13 |
+
$table = $td.closest('table');
|
14 |
+
$td.closest('tr').remove();
|
15 |
+
if (!$table.find('tbody tr').length) {
|
16 |
+
return $table.hide();
|
17 |
+
}
|
18 |
+
};
|
19 |
+
|
20 |
+
get_mustache_template = function(name) {
|
21 |
+
return jQuery('#p2p-template-' + name).html();
|
22 |
+
};
|
23 |
+
|
24 |
+
Candidates = Backbone.Model.extend({
|
25 |
+
sync: function() {
|
26 |
+
var params,
|
27 |
+
_this = this;
|
28 |
+
params = _.extend({}, this.attributes, {
|
29 |
+
subaction: 'search'
|
30 |
+
});
|
31 |
+
return this.ajax_request(params, function(response) {
|
32 |
+
var _ref;
|
33 |
+
_this.total_pages = ((_ref = response.navigation) != null ? _ref['total-pages-raw'] : void 0) || 1;
|
34 |
+
return _this.trigger('sync', response);
|
35 |
+
});
|
36 |
+
},
|
37 |
+
validate: function(attrs) {
|
38 |
+
var _ref;
|
39 |
+
if ((0 < (_ref = attrs['paged']) && _ref <= this.total_pages)) {
|
40 |
+
return null;
|
41 |
+
}
|
42 |
+
return 'invalid page';
|
43 |
+
}
|
44 |
+
});
|
45 |
+
|
46 |
+
Connections = Backbone.Model.extend({
|
47 |
+
createItemAndConnect: function(title) {
|
48 |
+
var data,
|
49 |
+
_this = this;
|
50 |
+
data = {
|
51 |
+
subaction: 'create_post',
|
52 |
+
post_title: title
|
53 |
+
};
|
54 |
+
return this.ajax_request(data, function(response) {
|
55 |
+
return _this.trigger('create:from_new_item', response);
|
56 |
+
});
|
57 |
+
},
|
58 |
+
create: function($td) {
|
59 |
+
var data,
|
60 |
+
_this = this;
|
61 |
+
data = {
|
62 |
+
subaction: 'connect',
|
63 |
+
to: $td.find('div').data('item-id')
|
64 |
+
};
|
65 |
+
return this.ajax_request(data, function(response) {
|
66 |
+
return _this.trigger('create', response, $td);
|
67 |
+
});
|
68 |
+
},
|
69 |
+
"delete": function($td) {
|
70 |
+
var data,
|
71 |
+
_this = this;
|
72 |
+
data = {
|
73 |
+
subaction: 'disconnect',
|
74 |
+
p2p_id: $td.find('input').val()
|
75 |
+
};
|
76 |
+
return this.ajax_request(data, function(response) {
|
77 |
+
return _this.trigger('delete', response, $td);
|
78 |
+
});
|
79 |
+
},
|
80 |
+
clear: function() {
|
81 |
+
var data,
|
82 |
+
_this = this;
|
83 |
+
data = {
|
84 |
+
subaction: 'clear_connections'
|
85 |
+
};
|
86 |
+
return this.ajax_request(data, function(response) {
|
87 |
+
return _this.trigger('clear', response);
|
88 |
+
});
|
89 |
+
}
|
90 |
+
});
|
91 |
+
|
92 |
+
ConnectionsView = Backbone.View.extend({
|
93 |
+
events: {
|
94 |
+
'click th.p2p-col-delete .p2p-icon': 'clear',
|
95 |
+
'click td.p2p-col-delete .p2p-icon': 'delete'
|
96 |
+
},
|
97 |
+
initialize: function(options) {
|
98 |
+
this.ajax_request = options.ajax_request;
|
99 |
+
this.maybe_make_sortable();
|
100 |
+
this.collection.on('create', this.afterCreate, this);
|
101 |
+
this.collection.on('create:from_new_item', this.afterCreate, this);
|
102 |
+
this.collection.on('delete', this.afterDelete, this);
|
103 |
+
this.collection.on('clear', this.afterClear, this);
|
104 |
+
return options.candidates.on('promote', this.create, this);
|
105 |
+
},
|
106 |
+
maybe_make_sortable: function() {
|
107 |
+
if (this.$('th.p2p-col-order').length) {
|
108 |
+
return this.$('tbody').sortable({
|
109 |
+
handle: 'td.p2p-col-order',
|
110 |
+
helper: function(e, ui) {
|
111 |
+
ui.children().each(function() {
|
112 |
+
var $this;
|
113 |
+
$this = jQuery(this);
|
114 |
+
return $this.width($this.width());
|
115 |
+
});
|
116 |
+
return ui;
|
117 |
+
}
|
118 |
+
});
|
119 |
+
}
|
120 |
+
},
|
121 |
+
clear: function(ev) {
|
122 |
+
var $td;
|
123 |
+
ev.preventDefault();
|
124 |
+
if (!confirm(P2PAdmin.deleteConfirmMessage)) {
|
125 |
+
return;
|
126 |
+
}
|
127 |
+
$td = jQuery(ev.target).closest('td');
|
128 |
+
row_wait($td);
|
129 |
+
return this.collection.clear();
|
130 |
+
},
|
131 |
+
afterClear: function() {
|
132 |
+
return this.$el.hide().find('tbody').html('');
|
133 |
+
},
|
134 |
+
"delete": function(ev) {
|
135 |
+
var $td;
|
136 |
+
ev.preventDefault();
|
137 |
+
$td = jQuery(ev.target).closest('td');
|
138 |
+
row_wait($td);
|
139 |
+
this.collection["delete"]($td);
|
140 |
+
return null;
|
141 |
+
},
|
142 |
+
afterDelete: function(response, $td) {
|
143 |
+
return remove_row($td);
|
144 |
+
},
|
145 |
+
create: function($td) {
|
146 |
+
this.collection.create($td);
|
147 |
+
return null;
|
148 |
+
},
|
149 |
+
afterCreate: function(response) {
|
150 |
+
this.$el.show().find('tbody').append(response.row);
|
151 |
+
return this.collection.trigger('append', response);
|
152 |
+
}
|
153 |
+
});
|
154 |
+
|
155 |
+
CandidatesView = Backbone.View.extend({
|
156 |
+
template: Mustache.compile(get_mustache_template('tab-list')),
|
157 |
+
events: {
|
158 |
+
'keypress :text': 'handleReturn',
|
159 |
+
'keyup :text': 'handleSearch',
|
160 |
+
'click .p2p-prev, .p2p-next': 'changePage',
|
161 |
+
'click td.p2p-col-create div': 'promote'
|
162 |
+
},
|
163 |
+
initialize: function(options) {
|
164 |
+
this.spinner = options.spinner;
|
165 |
+
options.connections.on('create', this.afterConnectionCreated, this);
|
166 |
+
options.connections.on('delete', this.refreshCandidates, this);
|
167 |
+
options.connections.on('clear', this.refreshCandidates, this);
|
168 |
+
this.collection.on('sync', this.refreshCandidates, this);
|
169 |
+
this.collection.on('error', this.afterInvalid, this);
|
170 |
+
return this.collection.on('invalid', this.afterInvalid, this);
|
171 |
+
},
|
172 |
+
afterConnectionCreated: function(response, $td) {
|
173 |
+
if (this.options.duplicate_connections) {
|
174 |
+
return $td.find('.p2p-icon').css('background-image', '');
|
175 |
+
} else {
|
176 |
+
return remove_row($td);
|
177 |
+
}
|
178 |
+
},
|
179 |
+
promote: function(ev) {
|
180 |
+
var $td;
|
181 |
+
$td = jQuery(ev.target).closest('td');
|
182 |
+
row_wait($td);
|
183 |
+
this.collection.trigger('promote', $td);
|
184 |
+
return false;
|
185 |
+
},
|
186 |
+
handleReturn: function(ev) {
|
187 |
+
if (ev.keyCode === ENTER_KEY) {
|
188 |
+
ev.preventDefault();
|
189 |
+
}
|
190 |
+
return null;
|
191 |
+
},
|
192 |
+
handleSearch: function(ev) {
|
193 |
+
var $searchInput, delayed,
|
194 |
+
_this = this;
|
195 |
+
if (delayed !== void 0) {
|
196 |
+
clearTimeout(delayed);
|
197 |
+
}
|
198 |
+
$searchInput = jQuery(ev.target);
|
199 |
+
delayed = setTimeout(function() {
|
200 |
+
var searchStr;
|
201 |
+
searchStr = $searchInput.val();
|
202 |
+
if (searchStr === _this.collection.get('s')) {
|
203 |
+
return;
|
204 |
+
}
|
205 |
+
_this.spinner.insertAfter(_this.searchInput).show();
|
206 |
+
return _this.collection.save({
|
207 |
+
's': searchStr,
|
208 |
+
'paged': 1
|
209 |
+
});
|
210 |
+
}, 400);
|
211 |
+
return null;
|
212 |
+
},
|
213 |
+
changePage: function(ev) {
|
214 |
+
var $navButton, new_page;
|
215 |
+
$navButton = jQuery(ev.currentTarget);
|
216 |
+
new_page = this.collection.get('paged');
|
217 |
+
if ($navButton.hasClass('p2p-prev')) {
|
218 |
+
new_page--;
|
219 |
+
} else {
|
220 |
+
new_page++;
|
221 |
+
}
|
222 |
+
this.spinner.appendTo(this.$('.p2p-navigation'));
|
223 |
+
return this.collection.save('paged', new_page);
|
224 |
+
},
|
225 |
+
refreshCandidates: function(response) {
|
226 |
+
this.spinner.remove();
|
227 |
+
this.$('button, .p2p-results, .p2p-navigation, .p2p-notice').remove();
|
228 |
+
return this.$el.append(this.template(response));
|
229 |
+
},
|
230 |
+
afterInvalid: function() {
|
231 |
+
return this.spinner.remove();
|
232 |
+
}
|
233 |
+
});
|
234 |
+
|
235 |
+
CreatePostView = Backbone.View.extend({
|
236 |
+
events: {
|
237 |
+
'click button': 'createItem',
|
238 |
+
'keypress :text': 'handleReturn'
|
239 |
+
},
|
240 |
+
initialize: function(options) {
|
241 |
+
this.ajax_request = options.ajax_request;
|
242 |
+
this.createButton = this.$('button');
|
243 |
+
this.createInput = this.$(':text');
|
244 |
+
return this.collection.on('create:from_new_item', this.afterItemCreated, this);
|
245 |
+
},
|
246 |
+
handleReturn: function(ev) {
|
247 |
+
if (ev.keyCode === ENTER_KEY) {
|
248 |
+
this.createButton.click();
|
249 |
+
ev.preventDefault();
|
250 |
+
}
|
251 |
+
return null;
|
252 |
+
},
|
253 |
+
createItem: function(ev) {
|
254 |
+
var title;
|
255 |
+
ev.preventDefault();
|
256 |
+
if (this.createButton.hasClass('inactive')) {
|
257 |
+
return false;
|
258 |
+
}
|
259 |
+
title = this.createInput.val();
|
260 |
+
if (title === '') {
|
261 |
+
this.createInput.focus();
|
262 |
+
return;
|
263 |
+
}
|
264 |
+
this.createButton.addClass('inactive');
|
265 |
+
this.collection.createItemAndConnect(title);
|
266 |
+
return null;
|
267 |
+
},
|
268 |
+
afterItemCreated: function() {
|
269 |
+
this.createInput.val('');
|
270 |
+
return this.createButton.removeClass('inactive');
|
271 |
+
}
|
272 |
+
});
|
273 |
+
|
274 |
+
MetaboxView = Backbone.View.extend({
|
275 |
+
events: {
|
276 |
+
'click .p2p-toggle-tabs': 'toggleTabs',
|
277 |
+
'click .wp-tab-bar li': 'setActiveTab'
|
278 |
+
},
|
279 |
+
initialize: function(options) {
|
280 |
+
this.spinner = options.spinner;
|
281 |
+
this.initializedCandidates = false;
|
282 |
+
options.connections.on('append', this.afterConnectionAppended, this);
|
283 |
+
options.connections.on('clear', this.afterConnectionDeleted, this);
|
284 |
+
return options.connections.on('delete', this.afterConnectionDeleted, this);
|
285 |
+
},
|
286 |
+
toggleTabs: function(ev) {
|
287 |
+
var $tabs;
|
288 |
+
ev.preventDefault();
|
289 |
+
$tabs = this.$('.p2p-create-connections-tabs');
|
290 |
+
$tabs.toggle();
|
291 |
+
if (!this.initializedCandidates && $tabs.is(':visible')) {
|
292 |
+
this.options.candidates.sync();
|
293 |
+
this.initializedCandidates = true;
|
294 |
+
}
|
295 |
+
return null;
|
296 |
+
},
|
297 |
+
setActiveTab: function(ev) {
|
298 |
+
var $tab;
|
299 |
+
ev.preventDefault();
|
300 |
+
$tab = jQuery(ev.currentTarget);
|
301 |
+
this.$('.wp-tab-bar li').removeClass('wp-tab-active');
|
302 |
+
$tab.addClass('wp-tab-active');
|
303 |
+
return this.$el.find('.tabs-panel').hide().end().find($tab.data('ref')).show().find(':text').focus();
|
304 |
+
},
|
305 |
+
afterConnectionAppended: function(response) {
|
306 |
+
if ('one' === this.options.cardinality) {
|
307 |
+
return this.$('.p2p-create-connections').hide();
|
308 |
+
}
|
309 |
+
},
|
310 |
+
afterConnectionDeleted: function(response) {
|
311 |
+
if ('one' === this.options.cardinality) {
|
312 |
+
return this.$('.p2p-create-connections').show();
|
313 |
+
}
|
314 |
+
}
|
315 |
+
});
|
316 |
|
317 |
jQuery(function() {
|
318 |
var clearVal, setVal;
|
337 |
};
|
338 |
jQuery('.p2p-search input[placeholder]').each(setVal).focus(clearVal).blur(setVal);
|
339 |
}
|
340 |
+
Mustache.compilePartial('table-row', get_mustache_template('table-row'));
|
341 |
return jQuery('.p2p-box').each(function() {
|
342 |
+
var $metabox, $spinner, ajax_request, candidates, candidatesView, connections, connectionsView, createPostView, ctype, metaboxView;
|
343 |
$metabox = jQuery(this);
|
|
|
344 |
$spinner = jQuery('<img>', {
|
345 |
'src': P2PAdmin.spinner,
|
346 |
'class': 'p2p-spinner'
|
347 |
});
|
348 |
+
candidates = new Candidates({
|
349 |
+
's': '',
|
350 |
+
'paged': 1
|
351 |
+
});
|
352 |
+
candidates.total_pages = $metabox.find('.p2p-total').data('num') || 1;
|
353 |
+
ctype = {
|
354 |
+
p2p_type: $metabox.data('p2p_type'),
|
355 |
+
direction: $metabox.data('direction'),
|
356 |
+
from: jQuery('#post_ID').val()
|
357 |
+
};
|
358 |
+
ajax_request = function(options, callback) {
|
359 |
+
var params;
|
360 |
+
params = _.extend({}, options, candidates.attributes, ctype, {
|
361 |
action: 'p2p_box',
|
362 |
+
nonce: P2PAdmin.nonce
|
|
|
|
|
|
|
|
|
|
|
363 |
});
|
364 |
+
return jQuery.post(ajaxurl, params, function(response) {
|
365 |
try {
|
366 |
response = jQuery.parseJSON(response);
|
|
|
|
|
|
|
|
|
|
|
367 |
} catch (e) {
|
368 |
+
if (typeof console !== "undefined" && console !== null) {
|
369 |
+
console.error('Malformed response', response);
|
370 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
371 |
return;
|
372 |
}
|
373 |
+
if (response.error) {
|
374 |
+
return alert(response.error);
|
375 |
} else {
|
376 |
+
return callback(response);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
377 |
}
|
378 |
});
|
|
|
379 |
};
|
380 |
+
candidates.ajax_request = ajax_request;
|
381 |
+
connections = new Connections;
|
382 |
+
connections.ajax_request = ajax_request;
|
383 |
+
connectionsView = new ConnectionsView({
|
384 |
+
el: $metabox.find('.p2p-connections'),
|
385 |
+
collection: connections,
|
386 |
+
candidates: candidates
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
387 |
});
|
388 |
+
candidatesView = new CandidatesView({
|
389 |
+
el: $metabox.find('.p2p-tab-search'),
|
390 |
+
collection: candidates,
|
391 |
+
connections: connections,
|
392 |
+
spinner: $spinner,
|
393 |
+
duplicate_connections: $metabox.data('duplicate_connections')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
394 |
});
|
395 |
+
createPostView = new CreatePostView({
|
396 |
+
el: $metabox.find('.p2p-tab-create-post'),
|
397 |
+
collection: connections
|
398 |
+
});
|
399 |
+
return metaboxView = new MetaboxView({
|
400 |
+
el: $metabox,
|
401 |
+
spinner: $spinner,
|
402 |
+
cardinality: $metabox.data('cardinality'),
|
403 |
+
candidates: candidates,
|
404 |
+
connections: connections
|
405 |
});
|
406 |
});
|
407 |
});
|
admin/box.php
CHANGED
@@ -1,10 +1,5 @@
|
|
1 |
<?php
|
2 |
|
3 |
-
interface P2P_Field {
|
4 |
-
function get_title();
|
5 |
-
function render( $p2p_id, $item );
|
6 |
-
}
|
7 |
-
|
8 |
class P2P_Box {
|
9 |
private $ctype;
|
10 |
|
@@ -35,9 +30,15 @@ class P2P_Box {
|
|
35 |
if ( self::$enqueued_scripts )
|
36 |
return;
|
37 |
|
38 |
-
wp_enqueue_style( 'p2p-box', plugins_url( 'box.css', __FILE__ ),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
|
40 |
-
wp_enqueue_script( 'p2p-box', plugins_url( 'box.js', __FILE__ ), array( 'jquery' ), P2P_PLUGIN_VERSION, true );
|
41 |
wp_localize_script( 'p2p-box', 'P2PAdmin', array(
|
42 |
'nonce' => wp_create_nonce( P2P_BOX_NONCE ),
|
43 |
'spinner' => admin_url( 'images/wpspin_light.gif' ),
|
@@ -46,6 +47,19 @@ class P2P_Box {
|
|
46 |
|
47 |
self::$enqueued_scripts = true;
|
48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
}
|
50 |
|
51 |
function render( $post ) {
|
@@ -115,7 +129,6 @@ class P2P_Box {
|
|
115 |
// Search tab
|
116 |
$tab_content = P2P_Mustache::render( 'tab-search', array(
|
117 |
'placeholder' => $this->labels->search_items,
|
118 |
-
'candidates' => $this->post_rows( $post->ID )
|
119 |
) );
|
120 |
|
121 |
$data['tabs'][] = array(
|
@@ -165,6 +178,7 @@ class P2P_Box {
|
|
165 |
|
166 |
protected function post_rows( $current_post_id, $page = 1, $search = '' ) {
|
167 |
$extra_qv = array_merge( self::$admin_box_qv, array(
|
|
|
168 |
'p2p:search' => $search,
|
169 |
'p2p:page' => $page,
|
170 |
'p2p:per_page' => 5
|
@@ -191,7 +205,6 @@ class P2P_Box {
|
|
191 |
'current-page' => number_format_i18n( $candidate->current_page ),
|
192 |
'total-pages' => number_format_i18n( $candidate->total_pages ),
|
193 |
|
194 |
-
'current-page-raw' => $candidate->current_page,
|
195 |
'total-pages-raw' => $candidate->total_pages,
|
196 |
|
197 |
'prev-inactive' => ( 1 == $candidate->current_page ) ? 'inactive' : '',
|
@@ -203,7 +216,7 @@ class P2P_Box {
|
|
203 |
);
|
204 |
}
|
205 |
|
206 |
-
return
|
207 |
}
|
208 |
|
209 |
|
@@ -280,11 +293,8 @@ class P2P_Box {
|
|
280 |
}
|
281 |
|
282 |
private function refresh_candidates() {
|
283 |
-
|
284 |
-
|
285 |
-
$results = compact( 'rows' );
|
286 |
-
|
287 |
-
die( json_encode( $results ) );
|
288 |
}
|
289 |
|
290 |
protected function can_create_post() {
|
1 |
<?php
|
2 |
|
|
|
|
|
|
|
|
|
|
|
3 |
class P2P_Box {
|
4 |
private $ctype;
|
5 |
|
30 |
if ( self::$enqueued_scripts )
|
31 |
return;
|
32 |
|
33 |
+
wp_enqueue_style( 'p2p-box', plugins_url( 'box.css', __FILE__ ),
|
34 |
+
array(), P2P_PLUGIN_VERSION );
|
35 |
+
|
36 |
+
wp_register_script( 'mustache', plugins_url( 'mustache.js', __FILE__ ),
|
37 |
+
array(), '0.7.2', true );
|
38 |
+
|
39 |
+
wp_enqueue_script( 'p2p-box', plugins_url( 'box.js', __FILE__ ),
|
40 |
+
array( 'backbone', 'mustache' ), P2P_PLUGIN_VERSION, true );
|
41 |
|
|
|
42 |
wp_localize_script( 'p2p-box', 'P2PAdmin', array(
|
43 |
'nonce' => wp_create_nonce( P2P_BOX_NONCE ),
|
44 |
'spinner' => admin_url( 'images/wpspin_light.gif' ),
|
47 |
|
48 |
self::$enqueued_scripts = true;
|
49 |
|
50 |
+
add_action( 'admin_footer', array( __CLASS__, 'add_templates' ) );
|
51 |
+
}
|
52 |
+
|
53 |
+
static function add_templates() {
|
54 |
+
self::add_template( 'tab-list' );
|
55 |
+
self::add_template( 'table-row' );
|
56 |
+
}
|
57 |
+
|
58 |
+
private static function add_template( $slug ) {
|
59 |
+
echo html( 'script', array(
|
60 |
+
'type' => 'text/html',
|
61 |
+
'id' => "p2p-template-$slug"
|
62 |
+
), file_get_contents( dirname( __FILE__ ) . "/templates/$slug.html" ) );
|
63 |
}
|
64 |
|
65 |
function render( $post ) {
|
129 |
// Search tab
|
130 |
$tab_content = P2P_Mustache::render( 'tab-search', array(
|
131 |
'placeholder' => $this->labels->search_items,
|
|
|
132 |
) );
|
133 |
|
134 |
$data['tabs'][] = array(
|
178 |
|
179 |
protected function post_rows( $current_post_id, $page = 1, $search = '' ) {
|
180 |
$extra_qv = array_merge( self::$admin_box_qv, array(
|
181 |
+
'p2p:context' => 'admin_box_candidates',
|
182 |
'p2p:search' => $search,
|
183 |
'p2p:page' => $page,
|
184 |
'p2p:per_page' => 5
|
205 |
'current-page' => number_format_i18n( $candidate->current_page ),
|
206 |
'total-pages' => number_format_i18n( $candidate->total_pages ),
|
207 |
|
|
|
208 |
'total-pages-raw' => $candidate->total_pages,
|
209 |
|
210 |
'prev-inactive' => ( 1 == $candidate->current_page ) ? 'inactive' : '',
|
216 |
);
|
217 |
}
|
218 |
|
219 |
+
return $data;
|
220 |
}
|
221 |
|
222 |
|
293 |
}
|
294 |
|
295 |
private function refresh_candidates() {
|
296 |
+
die( json_encode( $this->post_rows(
|
297 |
+
$_REQUEST['from'], $_REQUEST['paged'], $_REQUEST['s'] ) ) );
|
|
|
|
|
|
|
298 |
}
|
299 |
|
300 |
protected function can_create_post() {
|
admin/column-factory.php
CHANGED
@@ -2,53 +2,20 @@
|
|
2 |
|
3 |
class P2P_Column_Factory extends P2P_Factory {
|
4 |
|
5 |
-
|
6 |
-
add_action( 'p2p_registered_connection_type', array( $this, 'filter_ctypes' ), 10, 2 );
|
7 |
-
|
8 |
-
add_action( 'admin_print_styles', array( $this, 'add_columns' ) );
|
9 |
-
}
|
10 |
-
|
11 |
-
function filter_ctypes( $ctype, $args ) {
|
12 |
-
if ( isset( $args['admin_column'] ) ) {
|
13 |
-
$column_args = _p2p_pluck( $args, 'admin_column' );
|
14 |
-
if ( !is_array( $column_args ) )
|
15 |
-
$column_args = array( 'show' => $column_args );
|
16 |
-
} else {
|
17 |
-
$column_args = array();
|
18 |
-
}
|
19 |
|
20 |
-
|
21 |
-
|
22 |
-
) );
|
23 |
-
|
24 |
-
$this->register( $ctype->name, $column_args );
|
25 |
-
|
26 |
-
return $args;
|
27 |
-
}
|
28 |
-
|
29 |
-
function add_columns() {
|
30 |
-
$screen = get_current_screen();
|
31 |
-
|
32 |
-
$screen_map = array(
|
33 |
-
'edit' => 'post',
|
34 |
-
'users' => 'user'
|
35 |
-
);
|
36 |
-
|
37 |
-
if ( !isset( $screen_map[ $screen->base ] ) )
|
38 |
-
return;
|
39 |
-
|
40 |
-
$object_type = $screen_map[ $screen->base ];
|
41 |
|
42 |
-
|
|
|
43 |
}
|
44 |
|
45 |
function add_item( $directed, $object_type, $post_type, $title ) {
|
46 |
$class = 'P2P_Column_' . ucfirst( $object_type );
|
47 |
$column = new $class( $directed );
|
48 |
|
49 |
-
$column
|
50 |
}
|
51 |
}
|
52 |
|
53 |
-
new P2P_Column_Factory;
|
54 |
-
|
2 |
|
3 |
class P2P_Column_Factory extends P2P_Factory {
|
4 |
|
5 |
+
protected $key = 'admin_column';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
|
7 |
+
function __construct() {
|
8 |
+
parent::__construct();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
|
10 |
+
add_action( 'load-edit.php', array( $this, 'add_items' ) );
|
11 |
+
add_action( 'load-users.php', array( $this, 'add_items' ) );
|
12 |
}
|
13 |
|
14 |
function add_item( $directed, $object_type, $post_type, $title ) {
|
15 |
$class = 'P2P_Column_' . ucfirst( $object_type );
|
16 |
$column = new $class( $directed );
|
17 |
|
18 |
+
add_action( 'admin_print_styles', array( $column, 'styles' ) );
|
19 |
}
|
20 |
}
|
21 |
|
|
|
|
admin/column-post.php
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Column_Post extends P2P_Column {
|
4 |
+
|
5 |
+
function __construct( $directed ) {
|
6 |
+
parent::__construct( $directed );
|
7 |
+
|
8 |
+
$screen = get_current_screen();
|
9 |
+
|
10 |
+
add_action( "manage_{$screen->post_type}_posts_custom_column", array( $this, 'display_column' ), 10, 2 );
|
11 |
+
}
|
12 |
+
|
13 |
+
protected function get_items() {
|
14 |
+
global $wp_query;
|
15 |
+
|
16 |
+
return $wp_query->posts;
|
17 |
+
}
|
18 |
+
|
19 |
+
function get_admin_link( $item ) {
|
20 |
+
$args = array(
|
21 |
+
'connected_type' => $this->ctype->name,
|
22 |
+
'connected_direction' => $this->ctype->flip_direction()->get_direction(),
|
23 |
+
'connected_items' => $item->get_id(),
|
24 |
+
'post_type' => get_current_screen()->post_type
|
25 |
+
);
|
26 |
+
|
27 |
+
return add_query_arg( $args, admin_url( 'edit.php' ) );
|
28 |
+
}
|
29 |
+
|
30 |
+
function display_column( $column, $item_id ) {
|
31 |
+
echo parent::render_column( $column, $item_id );
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
admin/column-user.php
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Column_User extends P2P_Column {
|
4 |
+
|
5 |
+
function __construct( $directed ) {
|
6 |
+
parent::__construct( $directed );
|
7 |
+
|
8 |
+
add_action( 'pre_user_query', array( __CLASS__, 'user_query' ), 9 );
|
9 |
+
|
10 |
+
add_filter( 'manage_users_custom_column', array( $this, 'display_column' ), 10, 3 );
|
11 |
+
}
|
12 |
+
|
13 |
+
protected function get_items() {
|
14 |
+
global $wp_list_table;
|
15 |
+
|
16 |
+
return $wp_list_table->items;
|
17 |
+
}
|
18 |
+
|
19 |
+
// Add the query vars to the global user query (on the user admin screen)
|
20 |
+
static function user_query( $query ) {
|
21 |
+
if ( isset( $query->_p2p_capture ) )
|
22 |
+
return;
|
23 |
+
|
24 |
+
// Don't overwrite existing P2P query
|
25 |
+
if ( isset( $query->query_vars['connected_type'] ) )
|
26 |
+
return;
|
27 |
+
|
28 |
+
_p2p_append( $query->query_vars, wp_array_slice_assoc( $_GET,
|
29 |
+
P2P_URL_Query::get_custom_qv() ) );
|
30 |
+
}
|
31 |
+
|
32 |
+
function get_admin_link( $item ) {
|
33 |
+
$args = array(
|
34 |
+
'connected_type' => $this->ctype->name,
|
35 |
+
'connected_direction' => $this->ctype->flip_direction()->get_direction(),
|
36 |
+
'connected_items' => $item->get_id(),
|
37 |
+
);
|
38 |
+
|
39 |
+
return add_query_arg( $args, admin_url( 'users.php' ) );
|
40 |
+
}
|
41 |
+
|
42 |
+
function display_column( $content, $column, $item_id ) {
|
43 |
+
return parent::render_column( $column, $item_id );
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
admin/column.php
CHANGED
@@ -6,34 +6,48 @@ abstract class P2P_Column {
|
|
6 |
|
7 |
protected $connected = array();
|
8 |
|
9 |
-
function __construct( $directed
|
10 |
$this->ctype = $directed;
|
|
|
11 |
$this->column_id = sprintf( 'p2p-%s-%s',
|
12 |
$this->ctype->get_direction(),
|
13 |
$this->ctype->name
|
14 |
);
|
15 |
|
16 |
-
$extra_qv = array(
|
17 |
-
'p2p:per_page' => -1,
|
18 |
-
'p2p:context' => 'admin_column'
|
19 |
-
);
|
20 |
-
|
21 |
-
$connected = $this->ctype->get_connected( $items, $extra_qv, 'abstract' );
|
22 |
-
|
23 |
-
$this->connected = p2p_triage_connected( $connected->items );
|
24 |
-
|
25 |
$screen = get_current_screen();
|
26 |
|
27 |
add_filter( "manage_{$screen->id}_columns", array( $this, 'add_column' ) );
|
28 |
}
|
29 |
|
30 |
function add_column( $columns ) {
|
|
|
|
|
|
|
31 |
|
32 |
-
$
|
|
|
|
|
|
|
|
|
33 |
|
34 |
return $columns;
|
35 |
}
|
36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
function styles() {
|
38 |
?>
|
39 |
<style type="text/css">
|
@@ -64,62 +78,3 @@ abstract class P2P_Column {
|
|
64 |
}
|
65 |
}
|
66 |
|
67 |
-
|
68 |
-
class P2P_Column_Post extends P2P_Column {
|
69 |
-
|
70 |
-
function __construct( $directed ) {
|
71 |
-
global $wp_query;
|
72 |
-
|
73 |
-
$this->ctype = $directed;
|
74 |
-
|
75 |
-
$extra_qv = array( 'p2p:context' => 'admin_column' );
|
76 |
-
|
77 |
-
parent::__construct( $directed, $wp_query->posts );
|
78 |
-
|
79 |
-
$screen = get_current_screen();
|
80 |
-
|
81 |
-
add_action( "manage_{$screen->post_type}_posts_custom_column", array( $this, 'display_column' ), 10, 2 );
|
82 |
-
}
|
83 |
-
|
84 |
-
function get_admin_link( $item ) {
|
85 |
-
$args = array(
|
86 |
-
'connected_type' => $this->ctype->name,
|
87 |
-
'connected_direction' => $this->ctype->flip_direction()->get_direction(),
|
88 |
-
'connected_items' => $item->get_id(),
|
89 |
-
'post_type' => get_current_screen()->post_type
|
90 |
-
);
|
91 |
-
|
92 |
-
return add_query_arg( $args, admin_url( 'edit.php' ) );
|
93 |
-
}
|
94 |
-
|
95 |
-
function display_column( $column, $item_id ) {
|
96 |
-
echo parent::render_column( $column, $item_id );
|
97 |
-
}
|
98 |
-
}
|
99 |
-
|
100 |
-
|
101 |
-
class P2P_Column_User extends P2P_Column {
|
102 |
-
|
103 |
-
function __construct( $directed ) {
|
104 |
-
global $wp_list_table;
|
105 |
-
|
106 |
-
parent::__construct( $directed, $wp_list_table->items );
|
107 |
-
|
108 |
-
add_filter( 'manage_users_custom_column', array( $this, 'display_column' ), 10, 3 );
|
109 |
-
}
|
110 |
-
|
111 |
-
function get_admin_link( $item ) {
|
112 |
-
$args = array(
|
113 |
-
'connected_type' => $this->ctype->name,
|
114 |
-
'connected_direction' => $this->ctype->flip_direction()->get_direction(),
|
115 |
-
'connected_items' => $item->get_id(),
|
116 |
-
);
|
117 |
-
|
118 |
-
return add_query_arg( $args, admin_url( 'users.php' ) );
|
119 |
-
}
|
120 |
-
|
121 |
-
function display_column( $content, $column, $item_id ) {
|
122 |
-
return parent::render_column( $column, $item_id );
|
123 |
-
}
|
124 |
-
}
|
125 |
-
|
6 |
|
7 |
protected $connected = array();
|
8 |
|
9 |
+
function __construct( $directed ) {
|
10 |
$this->ctype = $directed;
|
11 |
+
|
12 |
$this->column_id = sprintf( 'p2p-%s-%s',
|
13 |
$this->ctype->get_direction(),
|
14 |
$this->ctype->name
|
15 |
);
|
16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
$screen = get_current_screen();
|
18 |
|
19 |
add_filter( "manage_{$screen->id}_columns", array( $this, 'add_column' ) );
|
20 |
}
|
21 |
|
22 |
function add_column( $columns ) {
|
23 |
+
$this->prepare_items();
|
24 |
+
|
25 |
+
$labels = $this->ctype->get( 'current', 'labels' );
|
26 |
|
27 |
+
$title = isset( $labels->column_title )
|
28 |
+
? $labels->column_title
|
29 |
+
: $labels->title;
|
30 |
+
|
31 |
+
$columns[ $this->column_id ] = $title;
|
32 |
|
33 |
return $columns;
|
34 |
}
|
35 |
|
36 |
+
protected abstract function get_items();
|
37 |
+
|
38 |
+
protected function prepare_items() {
|
39 |
+
$items = $this->get_items();
|
40 |
+
|
41 |
+
$extra_qv = array(
|
42 |
+
'p2p:per_page' => -1,
|
43 |
+
'p2p:context' => 'admin_column'
|
44 |
+
);
|
45 |
+
|
46 |
+
$connected = $this->ctype->get_connected( $items, $extra_qv, 'abstract' );
|
47 |
+
|
48 |
+
$this->connected = p2p_list_cluster( $connected->items, '_p2p_get_other_id' );
|
49 |
+
}
|
50 |
+
|
51 |
function styles() {
|
52 |
?>
|
53 |
<style type="text/css">
|
78 |
}
|
79 |
}
|
80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
admin/dropdown-factory.php
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Dropdown_Factory extends P2P_Factory {
|
4 |
+
|
5 |
+
protected $key = 'admin_dropdown';
|
6 |
+
|
7 |
+
function __construct() {
|
8 |
+
parent::__construct();
|
9 |
+
|
10 |
+
add_action( 'load-edit.php', array( $this, 'add_items' ) );
|
11 |
+
add_action( 'load-users.php', array( $this, 'add_items' ) );
|
12 |
+
}
|
13 |
+
|
14 |
+
function add_item( $directed, $object_type, $post_type, $title ) {
|
15 |
+
$class = 'P2P_Dropdown_' . ucfirst( $object_type );
|
16 |
+
$item = new $class( $directed, $title );
|
17 |
+
}
|
18 |
+
}
|
19 |
+
|
admin/dropdown-post.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Dropdown_Post extends P2P_Dropdown {
|
4 |
+
|
5 |
+
function __construct( $directed, $title ) {
|
6 |
+
parent::__construct( $directed, $title );
|
7 |
+
|
8 |
+
add_filter( 'request', array( __CLASS__, 'massage_query' ) );
|
9 |
+
|
10 |
+
add_action( 'restrict_manage_posts', array( $this, 'show_dropdown' ) );
|
11 |
+
}
|
12 |
+
|
13 |
+
static function massage_query( $request ) {
|
14 |
+
return array_merge( $request, self::get_qv() );
|
15 |
+
}
|
16 |
+
}
|
17 |
+
|
admin/dropdown-user.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Dropdown_User extends P2P_Dropdown_Post {
|
4 |
+
|
5 |
+
function __construct( $directed, $title ) {
|
6 |
+
parent::__construct( $directed, $title );
|
7 |
+
|
8 |
+
add_action( 'pre_user_query', array( __CLASS__, 'massage_query' ), 9 );
|
9 |
+
|
10 |
+
add_action( 'restrict_manage_users', array( $this, 'show_dropdown' ) );
|
11 |
+
}
|
12 |
+
|
13 |
+
static function massage_query( $query ) {
|
14 |
+
if ( isset( $query->_p2p_capture ) )
|
15 |
+
return;
|
16 |
+
|
17 |
+
// Don't overwrite existing P2P query
|
18 |
+
if ( isset( $query->query_vars['connected_type'] ) )
|
19 |
+
return;
|
20 |
+
|
21 |
+
_p2p_append( $query->query_vars, self::get_qv() );
|
22 |
+
}
|
23 |
+
|
24 |
+
protected function render_dropdown() {
|
25 |
+
return html( 'div', array(
|
26 |
+
'style' => 'float: right; margin-left: 16px'
|
27 |
+
),
|
28 |
+
parent::render_dropdown(),
|
29 |
+
html( 'input', array(
|
30 |
+
'type' => 'submit',
|
31 |
+
'class' => 'button',
|
32 |
+
'value' => __( 'Filter', P2P_TEXTDOMAIN )
|
33 |
+
) )
|
34 |
+
);
|
35 |
+
}
|
36 |
+
}
|
37 |
+
|
admin/dropdown.php
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
abstract class P2P_Dropdown {
|
4 |
+
|
5 |
+
protected $ctype;
|
6 |
+
protected $title;
|
7 |
+
|
8 |
+
function __construct( $directed, $title ) {
|
9 |
+
$this->ctype = $directed;
|
10 |
+
$this->title = $title;
|
11 |
+
}
|
12 |
+
|
13 |
+
function show_dropdown() {
|
14 |
+
echo $this->render_dropdown();
|
15 |
+
}
|
16 |
+
|
17 |
+
protected function render_dropdown() {
|
18 |
+
$direction = $this->ctype->flip_direction()->get_direction();
|
19 |
+
|
20 |
+
return scbForms::input( array(
|
21 |
+
'type' => 'select',
|
22 |
+
'name' => array( 'p2p', $this->ctype->name, $direction ),
|
23 |
+
'choices' => self::get_choices( $this->ctype ),
|
24 |
+
'text' => $this->title,
|
25 |
+
), $_GET );
|
26 |
+
}
|
27 |
+
|
28 |
+
protected static function get_qv() {
|
29 |
+
if ( !isset( $_GET['p2p'] ) )
|
30 |
+
return array();
|
31 |
+
|
32 |
+
$args = array();
|
33 |
+
|
34 |
+
$tmp = reset( $_GET['p2p'] );
|
35 |
+
|
36 |
+
$args['connected_type'] = key( $_GET['p2p'] );
|
37 |
+
|
38 |
+
list( $args['connected_direction'], $args['connected_items'] ) = each( $tmp );
|
39 |
+
|
40 |
+
if ( !$args['connected_items'] )
|
41 |
+
return array();
|
42 |
+
|
43 |
+
return $args;
|
44 |
+
}
|
45 |
+
|
46 |
+
protected static function get_choices( $directed ) {
|
47 |
+
$extra_qv = array(
|
48 |
+
'p2p:per_page' => -1,
|
49 |
+
'p2p:context' => 'admin_dropdown'
|
50 |
+
);
|
51 |
+
|
52 |
+
$connected = $directed->get_connected( 'any', $extra_qv, 'abstract' );
|
53 |
+
|
54 |
+
$options = array();
|
55 |
+
foreach ( $connected->items as $item )
|
56 |
+
$options[ $item->get_id() ] = $item->get_title();
|
57 |
+
|
58 |
+
return $options;
|
59 |
+
}
|
60 |
+
}
|
61 |
+
|
admin/factory.php
CHANGED
@@ -2,22 +2,61 @@
|
|
2 |
|
3 |
abstract class P2P_Factory {
|
4 |
|
|
|
|
|
5 |
protected $queue = array();
|
6 |
|
7 |
-
function
|
8 |
-
|
9 |
-
|
10 |
|
11 |
-
|
|
|
|
|
12 |
|
13 |
-
if ( !$
|
14 |
return false;
|
15 |
|
16 |
-
$this->queue[$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
-
|
|
|
|
|
19 |
}
|
20 |
|
|
|
21 |
function filter( $object_type, $post_type ) {
|
22 |
foreach ( $this->queue as $p2p_type => $args ) {
|
23 |
$ctype = p2p_type( $p2p_type );
|
@@ -36,6 +75,9 @@ abstract class P2P_Factory {
|
|
36 |
}
|
37 |
}
|
38 |
|
|
|
|
|
|
|
39 |
protected static function get_title( $directions, $ctype ) {
|
40 |
$title = array(
|
41 |
'from' => $ctype->get_field( 'title', 'from' ),
|
@@ -57,7 +99,5 @@ abstract class P2P_Factory {
|
|
57 |
|
58 |
return $ctype->_directions_for_admin( $direction, $show_ui );
|
59 |
}
|
60 |
-
|
61 |
-
abstract function add_item( $directed, $object_type, $post_type, $title );
|
62 |
}
|
63 |
|
2 |
|
3 |
abstract class P2P_Factory {
|
4 |
|
5 |
+
protected $key;
|
6 |
+
|
7 |
protected $queue = array();
|
8 |
|
9 |
+
function __construct() {
|
10 |
+
add_action( 'p2p_registered_connection_type', array( $this, 'check_ctype' ), 10, 2 );
|
11 |
+
}
|
12 |
|
13 |
+
// Check if a newly registered connection type needs an item to be produced.
|
14 |
+
function check_ctype( $ctype, $args ) {
|
15 |
+
$sub_args = $this->expand_arg( $args );
|
16 |
|
17 |
+
if ( !$sub_args['show'] )
|
18 |
return false;
|
19 |
|
20 |
+
$this->queue[ $ctype->name ] = (object) $sub_args;
|
21 |
+
}
|
22 |
+
|
23 |
+
// Collect sub-args from main connection type args and set defaults
|
24 |
+
protected function expand_arg( $args ) {
|
25 |
+
if ( isset( $args[ $this->key ] ) ) {
|
26 |
+
$sub_args = $args[ $this->key ];
|
27 |
+
|
28 |
+
if ( !is_array( $sub_args ) ) {
|
29 |
+
$sub_args = array( 'show' => $sub_args );
|
30 |
+
}
|
31 |
+
} else {
|
32 |
+
$sub_args = array( 'show' => false );
|
33 |
+
}
|
34 |
+
|
35 |
+
$sub_args = wp_parse_args( $sub_args, array(
|
36 |
+
'show' => 'any',
|
37 |
+
) );
|
38 |
+
|
39 |
+
return $sub_args;
|
40 |
+
}
|
41 |
+
|
42 |
+
// Begin processing item queue for a particular screen.
|
43 |
+
function add_items() {
|
44 |
+
$screen = get_current_screen();
|
45 |
+
|
46 |
+
$screen_map = array(
|
47 |
+
'edit' => 'post',
|
48 |
+
'users' => 'user'
|
49 |
+
);
|
50 |
+
|
51 |
+
if ( !isset( $screen_map[ $screen->base ] ) )
|
52 |
+
return;
|
53 |
|
54 |
+
$object_type = $screen_map[ $screen->base ];
|
55 |
+
|
56 |
+
$this->filter( $object_type, $screen->post_type );
|
57 |
}
|
58 |
|
59 |
+
// Filter item queue based on object type.
|
60 |
function filter( $object_type, $post_type ) {
|
61 |
foreach ( $this->queue as $p2p_type => $args ) {
|
62 |
$ctype = p2p_type( $p2p_type );
|
75 |
}
|
76 |
}
|
77 |
|
78 |
+
// Produce an item and add it to the screen.
|
79 |
+
abstract function add_item( $directed, $object_type, $post_type, $title );
|
80 |
+
|
81 |
protected static function get_title( $directions, $ctype ) {
|
82 |
$title = array(
|
83 |
'from' => $ctype->get_field( 'title', 'from' ),
|
99 |
|
100 |
return $ctype->_directions_for_admin( $direction, $show_ui );
|
101 |
}
|
|
|
|
|
102 |
}
|
103 |
|
admin/field-create.php
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Field_Create implements P2P_Field {
|
4 |
+
|
5 |
+
protected $title_field;
|
6 |
+
|
7 |
+
function __construct( $title_field ) {
|
8 |
+
$this->title_field = $title_field;
|
9 |
+
}
|
10 |
+
|
11 |
+
function get_title() {
|
12 |
+
// Not needed
|
13 |
+
return '';
|
14 |
+
}
|
15 |
+
|
16 |
+
function render( $p2p_id, $item ) {
|
17 |
+
$data = array_merge( $this->title_field->get_data( $item ), array(
|
18 |
+
'title' => $item->get_title(),
|
19 |
+
'item-id' => $item->get_id(),
|
20 |
+
) );
|
21 |
+
|
22 |
+
return P2P_Mustache::render( 'column-create', $data );
|
23 |
+
}
|
24 |
+
}
|
25 |
+
|
admin/field-delete.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Field_Delete implements P2P_Field {
|
4 |
+
|
5 |
+
function get_title() {
|
6 |
+
$data = array(
|
7 |
+
'title' => __( 'Delete all connections', P2P_TEXTDOMAIN )
|
8 |
+
);
|
9 |
+
|
10 |
+
return P2P_Mustache::render( 'column-delete-all', $data );
|
11 |
+
}
|
12 |
+
|
13 |
+
function render( $p2p_id, $_ ) {
|
14 |
+
$data = array(
|
15 |
+
'p2p_id' => $p2p_id,
|
16 |
+
'title' => __( 'Delete connection', P2P_TEXTDOMAIN )
|
17 |
+
);
|
18 |
+
|
19 |
+
return P2P_Mustache::render( 'column-delete', $data );
|
20 |
+
}
|
21 |
+
}
|
22 |
+
|
23 |
+
|
admin/field-generic.php
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Field_Generic implements P2P_Field {
|
4 |
+
|
5 |
+
protected $key;
|
6 |
+
protected $data;
|
7 |
+
|
8 |
+
function __construct( $key, $data ) {
|
9 |
+
$this->key = $key;
|
10 |
+
$this->data = $data;
|
11 |
+
}
|
12 |
+
|
13 |
+
function get_title() {
|
14 |
+
return $this->data['title'];
|
15 |
+
}
|
16 |
+
|
17 |
+
function render( $p2p_id, $_ ) {
|
18 |
+
$args = $this->data;
|
19 |
+
$args['name'] = array( 'p2p_meta', $p2p_id, $this->key );
|
20 |
+
|
21 |
+
if ( 'select' == $args['type'] && !isset( $args['text'] ) )
|
22 |
+
$args['text'] = '';
|
23 |
+
|
24 |
+
return scbForms::input_from_meta( $args, $p2p_id, 'p2p' );
|
25 |
+
}
|
26 |
+
}
|
27 |
+
|
admin/field-order.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Field_Order implements P2P_Field {
|
4 |
+
|
5 |
+
protected $sort_key;
|
6 |
+
|
7 |
+
function __construct( $sort_key ) {
|
8 |
+
$this->sort_key = $sort_key;
|
9 |
+
}
|
10 |
+
|
11 |
+
function get_title() {
|
12 |
+
return '';
|
13 |
+
}
|
14 |
+
|
15 |
+
function render( $p2p_id, $_ ) {
|
16 |
+
return html( 'input', array(
|
17 |
+
'type' => 'hidden',
|
18 |
+
'name' => "p2p_order[$this->sort_key][]",
|
19 |
+
'value' => $p2p_id
|
20 |
+
) );
|
21 |
+
}
|
22 |
+
}
|
23 |
+
|
admin/field-title-attachment.php
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Field_Title_Attachment extends P2P_Field_Title {
|
4 |
+
|
5 |
+
function get_data( $item ) {
|
6 |
+
$data = array(
|
7 |
+
'title-attr' => $item->get_object()->post_title,
|
8 |
+
);
|
9 |
+
|
10 |
+
return $data;
|
11 |
+
}
|
12 |
+
}
|
13 |
+
|
admin/field-title-post.php
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Field_Title_Post extends P2P_Field_Title {
|
4 |
+
|
5 |
+
function get_data( $item ) {
|
6 |
+
$data = array(
|
7 |
+
'title-attr' => $item->get_permalink()
|
8 |
+
);
|
9 |
+
|
10 |
+
$post = $item->get_object();
|
11 |
+
|
12 |
+
if ( 'publish' != $post->post_status ) {
|
13 |
+
$status_obj = get_post_status_object( $post->post_status );
|
14 |
+
if ( $status_obj ) {
|
15 |
+
$data['status']['text'] = $status_obj->label;
|
16 |
+
}
|
17 |
+
}
|
18 |
+
|
19 |
+
return $data;
|
20 |
+
}
|
21 |
+
}
|
22 |
+
|
admin/field-title-user.php
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Field_Title_User extends P2P_Field_Title {
|
4 |
+
|
5 |
+
function get_data( $user ) {
|
6 |
+
return array(
|
7 |
+
'title-attr' => '',
|
8 |
+
);
|
9 |
+
}
|
10 |
+
}
|
11 |
+
|
admin/field-title.php
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
abstract class P2P_Field_Title implements P2P_Field {
|
4 |
+
|
5 |
+
protected $title;
|
6 |
+
|
7 |
+
function __construct( $title = '' ) {
|
8 |
+
$this->title = $title;
|
9 |
+
}
|
10 |
+
|
11 |
+
function get_title() {
|
12 |
+
return $this->title;
|
13 |
+
}
|
14 |
+
|
15 |
+
function render( $p2p_id, $item ) {
|
16 |
+
$data = array_merge( $this->get_data( $item ), array(
|
17 |
+
'title' => $item->get_title(),
|
18 |
+
'url' => $item->get_editlink(),
|
19 |
+
) );
|
20 |
+
|
21 |
+
return P2P_Mustache::render( 'column-title', $data );
|
22 |
+
}
|
23 |
+
|
24 |
+
abstract function get_data( $item );
|
25 |
+
}
|
26 |
+
|
admin/field.php
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
interface P2P_Field {
|
4 |
+
function get_title();
|
5 |
+
function render( $p2p_id, $item );
|
6 |
+
}
|
7 |
+
|
admin/fields.php
DELETED
@@ -1,159 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
class P2P_Field_Delete implements P2P_Field {
|
4 |
-
|
5 |
-
function get_title() {
|
6 |
-
$data = array(
|
7 |
-
'title' => __( 'Delete all connections', P2P_TEXTDOMAIN )
|
8 |
-
);
|
9 |
-
|
10 |
-
return P2P_Mustache::render( 'column-delete-all', $data );
|
11 |
-
}
|
12 |
-
|
13 |
-
function render( $p2p_id, $_ ) {
|
14 |
-
$data = array(
|
15 |
-
'p2p_id' => $p2p_id,
|
16 |
-
'title' => __( 'Delete connection', P2P_TEXTDOMAIN )
|
17 |
-
);
|
18 |
-
|
19 |
-
return P2P_Mustache::render( 'column-delete', $data );
|
20 |
-
}
|
21 |
-
}
|
22 |
-
|
23 |
-
|
24 |
-
class P2P_Field_Order implements P2P_Field {
|
25 |
-
|
26 |
-
protected $sort_key;
|
27 |
-
|
28 |
-
function __construct( $sort_key ) {
|
29 |
-
$this->sort_key = $sort_key;
|
30 |
-
}
|
31 |
-
|
32 |
-
function get_title() {
|
33 |
-
return '';
|
34 |
-
}
|
35 |
-
|
36 |
-
function render( $p2p_id, $_ ) {
|
37 |
-
return html( 'input', array(
|
38 |
-
'type' => 'hidden',
|
39 |
-
'name' => "p2p_order[$this->sort_key][]",
|
40 |
-
'value' => $p2p_id
|
41 |
-
) );
|
42 |
-
}
|
43 |
-
}
|
44 |
-
|
45 |
-
|
46 |
-
class P2P_Field_Generic implements P2P_Field {
|
47 |
-
|
48 |
-
protected $key;
|
49 |
-
protected $data;
|
50 |
-
|
51 |
-
function __construct( $key, $data ) {
|
52 |
-
$this->key = $key;
|
53 |
-
$this->data = $data;
|
54 |
-
}
|
55 |
-
|
56 |
-
function get_title() {
|
57 |
-
return $this->data['title'];
|
58 |
-
}
|
59 |
-
|
60 |
-
function render( $p2p_id, $_ ) {
|
61 |
-
$args = $this->data;
|
62 |
-
$args['name'] = array( 'p2p_meta', $p2p_id, $this->key );
|
63 |
-
|
64 |
-
if ( 'select' == $args['type'] && !isset( $args['text'] ) )
|
65 |
-
$args['text'] = '';
|
66 |
-
|
67 |
-
return scbForms::input_from_meta( $args, $p2p_id, 'p2p' );
|
68 |
-
}
|
69 |
-
}
|
70 |
-
|
71 |
-
|
72 |
-
class P2P_Field_Create implements P2P_Field {
|
73 |
-
|
74 |
-
protected $title_field;
|
75 |
-
|
76 |
-
function __construct( $title_field ) {
|
77 |
-
$this->title_field = $title_field;
|
78 |
-
}
|
79 |
-
|
80 |
-
function get_title() {
|
81 |
-
// Not needed
|
82 |
-
return '';
|
83 |
-
}
|
84 |
-
|
85 |
-
function render( $p2p_id, $item ) {
|
86 |
-
$data = array_merge( $this->title_field->get_data( $item ), array(
|
87 |
-
'title' => $item->get_title(),
|
88 |
-
'item-id' => $item->get_id(),
|
89 |
-
) );
|
90 |
-
|
91 |
-
return P2P_Mustache::render( 'column-create', $data );
|
92 |
-
}
|
93 |
-
}
|
94 |
-
|
95 |
-
|
96 |
-
abstract class P2P_Field_Title implements P2P_Field {
|
97 |
-
|
98 |
-
protected $title;
|
99 |
-
|
100 |
-
function __construct( $title = '' ) {
|
101 |
-
$this->title = $title;
|
102 |
-
}
|
103 |
-
|
104 |
-
function get_title() {
|
105 |
-
return $this->title;
|
106 |
-
}
|
107 |
-
|
108 |
-
function render( $p2p_id, $item ) {
|
109 |
-
$data = array_merge( $this->get_data( $item ), array(
|
110 |
-
'title' => $item->get_title(),
|
111 |
-
'url' => $item->get_editlink(),
|
112 |
-
) );
|
113 |
-
|
114 |
-
return P2P_Mustache::render( 'column-title', $data );
|
115 |
-
}
|
116 |
-
|
117 |
-
abstract function get_data( $item );
|
118 |
-
}
|
119 |
-
|
120 |
-
class P2P_Field_Title_Post extends P2P_Field_Title {
|
121 |
-
|
122 |
-
function get_data( $item ) {
|
123 |
-
$data = array(
|
124 |
-
'title-attr' => $item->get_permalink()
|
125 |
-
);
|
126 |
-
|
127 |
-
$post = $item->get_object();
|
128 |
-
|
129 |
-
if ( 'publish' != $post->post_status ) {
|
130 |
-
$status_obj = get_post_status_object( $post->post_status );
|
131 |
-
if ( $status_obj ) {
|
132 |
-
$data['status']['text'] = $status_obj->label;
|
133 |
-
}
|
134 |
-
}
|
135 |
-
|
136 |
-
return $data;
|
137 |
-
}
|
138 |
-
}
|
139 |
-
|
140 |
-
class P2P_Field_Title_Attachment extends P2P_Field_Title {
|
141 |
-
|
142 |
-
function get_data( $item ) {
|
143 |
-
$data = array(
|
144 |
-
'title-attr' => $item->get_object()->post_title,
|
145 |
-
);
|
146 |
-
|
147 |
-
return $data;
|
148 |
-
}
|
149 |
-
}
|
150 |
-
|
151 |
-
class P2P_Field_Title_User extends P2P_Field_Title {
|
152 |
-
|
153 |
-
function get_data( $user ) {
|
154 |
-
return array(
|
155 |
-
'title-attr' => '',
|
156 |
-
);
|
157 |
-
}
|
158 |
-
}
|
159 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
admin/mustache.js
ADDED
@@ -0,0 +1,610 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* mustache.js - Logic-less {{mustache}} templates with JavaScript
|
3 |
+
* http://github.com/janl/mustache.js
|
4 |
+
*/
|
5 |
+
|
6 |
+
/*global define: false*/
|
7 |
+
|
8 |
+
(function (root, factory) {
|
9 |
+
if (typeof exports === "object" && exports) {
|
10 |
+
module.exports = factory; // CommonJS
|
11 |
+
} else if (typeof define === "function" && define.amd) {
|
12 |
+
define(factory); // AMD
|
13 |
+
} else {
|
14 |
+
root.Mustache = factory; // <script>
|
15 |
+
}
|
16 |
+
}(this, (function () {
|
17 |
+
|
18 |
+
var exports = {};
|
19 |
+
|
20 |
+
exports.name = "mustache.js";
|
21 |
+
exports.version = "0.7.2";
|
22 |
+
exports.tags = ["{{", "}}"];
|
23 |
+
|
24 |
+
exports.Scanner = Scanner;
|
25 |
+
exports.Context = Context;
|
26 |
+
exports.Writer = Writer;
|
27 |
+
|
28 |
+
var whiteRe = /\s*/;
|
29 |
+
var spaceRe = /\s+/;
|
30 |
+
var nonSpaceRe = /\S/;
|
31 |
+
var eqRe = /\s*=/;
|
32 |
+
var curlyRe = /\s*\}/;
|
33 |
+
var tagRe = /#|\^|\/|>|\{|&|=|!/;
|
34 |
+
|
35 |
+
// Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
|
36 |
+
// See https://github.com/janl/mustache.js/issues/189
|
37 |
+
function testRe(re, string) {
|
38 |
+
return RegExp.prototype.test.call(re, string);
|
39 |
+
}
|
40 |
+
|
41 |
+
function isWhitespace(string) {
|
42 |
+
return !testRe(nonSpaceRe, string);
|
43 |
+
}
|
44 |
+
|
45 |
+
var isArray = Array.isArray || function (obj) {
|
46 |
+
return Object.prototype.toString.call(obj) === "[object Array]";
|
47 |
+
};
|
48 |
+
|
49 |
+
function escapeRe(string) {
|
50 |
+
return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
|
51 |
+
}
|
52 |
+
|
53 |
+
var entityMap = {
|
54 |
+
"&": "&",
|
55 |
+
"<": "<",
|
56 |
+
">": ">",
|
57 |
+
'"': '"',
|
58 |
+
"'": ''',
|
59 |
+
"/": '/'
|
60 |
+
};
|
61 |
+
|
62 |
+
function escapeHtml(string) {
|
63 |
+
return String(string).replace(/[&<>"'\/]/g, function (s) {
|
64 |
+
return entityMap[s];
|
65 |
+
});
|
66 |
+
}
|
67 |
+
|
68 |
+
// Export the escaping function so that the user may override it.
|
69 |
+
// See https://github.com/janl/mustache.js/issues/244
|
70 |
+
exports.escape = escapeHtml;
|
71 |
+
|
72 |
+
function Scanner(string) {
|
73 |
+
this.string = string;
|
74 |
+
this.tail = string;
|
75 |
+
this.pos = 0;
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Returns `true` if the tail is empty (end of string).
|
80 |
+
*/
|
81 |
+
Scanner.prototype.eos = function () {
|
82 |
+
return this.tail === "";
|
83 |
+
};
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Tries to match the given regular expression at the current position.
|
87 |
+
* Returns the matched text if it can match, the empty string otherwise.
|
88 |
+
*/
|
89 |
+
Scanner.prototype.scan = function (re) {
|
90 |
+
var match = this.tail.match(re);
|
91 |
+
|
92 |
+
if (match && match.index === 0) {
|
93 |
+
this.tail = this.tail.substring(match[0].length);
|
94 |
+
this.pos += match[0].length;
|
95 |
+
return match[0];
|
96 |
+
}
|
97 |
+
|
98 |
+
return "";
|
99 |
+
};
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Skips all text until the given regular expression can be matched. Returns
|
103 |
+
* the skipped string, which is the entire tail if no match can be made.
|
104 |
+
*/
|
105 |
+
Scanner.prototype.scanUntil = function (re) {
|
106 |
+
var match, pos = this.tail.search(re);
|
107 |
+
|
108 |
+
switch (pos) {
|
109 |
+
case -1:
|
110 |
+
match = this.tail;
|
111 |
+
this.pos += this.tail.length;
|
112 |
+
this.tail = "";
|
113 |
+
break;
|
114 |
+
case 0:
|
115 |
+
match = "";
|
116 |
+
break;
|
117 |
+
default:
|
118 |
+
match = this.tail.substring(0, pos);
|
119 |
+
this.tail = this.tail.substring(pos);
|
120 |
+
this.pos += pos;
|
121 |
+
}
|
122 |
+
|
123 |
+
return match;
|
124 |
+
};
|
125 |
+
|
126 |
+
function Context(view, parent) {
|
127 |
+
this.view = view;
|
128 |
+
this.parent = parent;
|
129 |
+
this.clearCache();
|
130 |
+
}
|
131 |
+
|
132 |
+
Context.make = function (view) {
|
133 |
+
return (view instanceof Context) ? view : new Context(view);
|
134 |
+
};
|
135 |
+
|
136 |
+
Context.prototype.clearCache = function () {
|
137 |
+
this._cache = {};
|
138 |
+
};
|
139 |
+
|
140 |
+
Context.prototype.push = function (view) {
|
141 |
+
return new Context(view, this);
|
142 |
+
};
|
143 |
+
|
144 |
+
Context.prototype.lookup = function (name) {
|
145 |
+
var value = this._cache[name];
|
146 |
+
|
147 |
+
if (!value) {
|
148 |
+
if (name === ".") {
|
149 |
+
value = this.view;
|
150 |
+
} else {
|
151 |
+
var context = this;
|
152 |
+
|
153 |
+
while (context) {
|
154 |
+
if (name.indexOf(".") > 0) {
|
155 |
+
var names = name.split("."), i = 0;
|
156 |
+
|
157 |
+
value = context.view;
|
158 |
+
|
159 |
+
while (value && i < names.length) {
|
160 |
+
value = value[names[i++]];
|
161 |
+
}
|
162 |
+
} else {
|
163 |
+
value = context.view[name];
|
164 |
+
}
|
165 |
+
|
166 |
+
if (value != null) {
|
167 |
+
break;
|
168 |
+
}
|
169 |
+
|
170 |
+
context = context.parent;
|
171 |
+
}
|
172 |
+
}
|
173 |
+
|
174 |
+
this._cache[name] = value;
|
175 |
+
}
|
176 |
+
|
177 |
+
if (typeof value === "function") {
|
178 |
+
value = value.call(this.view);
|
179 |
+
}
|
180 |
+
|
181 |
+
return value;
|
182 |
+
};
|
183 |
+
|
184 |
+
function Writer() {
|
185 |
+
this.clearCache();
|
186 |
+
}
|
187 |
+
|
188 |
+
Writer.prototype.clearCache = function () {
|
189 |
+
this._cache = {};
|
190 |
+
this._partialCache = {};
|
191 |
+
};
|
192 |
+
|
193 |
+
Writer.prototype.compile = function (template, tags) {
|
194 |
+
var fn = this._cache[template];
|
195 |
+
|
196 |
+
if (!fn) {
|
197 |
+
var tokens = exports.parse(template, tags);
|
198 |
+
fn = this._cache[template] = this.compileTokens(tokens, template);
|
199 |
+
}
|
200 |
+
|
201 |
+
return fn;
|
202 |
+
};
|
203 |
+
|
204 |
+
Writer.prototype.compilePartial = function (name, template, tags) {
|
205 |
+
var fn = this.compile(template, tags);
|
206 |
+
this._partialCache[name] = fn;
|
207 |
+
return fn;
|
208 |
+
};
|
209 |
+
|
210 |
+
Writer.prototype.compileTokens = function (tokens, template) {
|
211 |
+
var fn = compileTokens(tokens);
|
212 |
+
var self = this;
|
213 |
+
|
214 |
+
return function (view, partials) {
|
215 |
+
if (partials) {
|
216 |
+
if (typeof partials === "function") {
|
217 |
+
self._loadPartial = partials;
|
218 |
+
} else {
|
219 |
+
for (var name in partials) {
|
220 |
+
self.compilePartial(name, partials[name]);
|
221 |
+
}
|
222 |
+
}
|
223 |
+
}
|
224 |
+
|
225 |
+
return fn(self, Context.make(view), template);
|
226 |
+
};
|
227 |
+
};
|
228 |
+
|
229 |
+
Writer.prototype.render = function (template, view, partials) {
|
230 |
+
return this.compile(template)(view, partials);
|
231 |
+
};
|
232 |
+
|
233 |
+
Writer.prototype._section = function (name, context, text, callback) {
|
234 |
+
var value = context.lookup(name);
|
235 |
+
|
236 |
+
switch (typeof value) {
|
237 |
+
case "object":
|
238 |
+
if (isArray(value)) {
|
239 |
+
var buffer = "";
|
240 |
+
|
241 |
+
for (var i = 0, len = value.length; i < len; ++i) {
|
242 |
+
buffer += callback(this, context.push(value[i]));
|
243 |
+
}
|
244 |
+
|
245 |
+
return buffer;
|
246 |
+
}
|
247 |
+
|
248 |
+
return value ? callback(this, context.push(value)) : "";
|
249 |
+
case "function":
|
250 |
+
var self = this;
|
251 |
+
var scopedRender = function (template) {
|
252 |
+
return self.render(template, context);
|
253 |
+
};
|
254 |
+
|
255 |
+
var result = value.call(context.view, text, scopedRender);
|
256 |
+
return result != null ? result : "";
|
257 |
+
default:
|
258 |
+
if (value) {
|
259 |
+
return callback(this, context);
|
260 |
+
}
|
261 |
+
}
|
262 |
+
|
263 |
+
return "";
|
264 |
+
};
|
265 |
+
|
266 |
+
Writer.prototype._inverted = function (name, context, callback) {
|
267 |
+
var value = context.lookup(name);
|
268 |
+
|
269 |
+
// Use JavaScript's definition of falsy. Include empty arrays.
|
270 |
+
// See https://github.com/janl/mustache.js/issues/186
|
271 |
+
if (!value || (isArray(value) && value.length === 0)) {
|
272 |
+
return callback(this, context);
|
273 |
+
}
|
274 |
+
|
275 |
+
return "";
|
276 |
+
};
|
277 |
+
|
278 |
+
Writer.prototype._partial = function (name, context) {
|
279 |
+
if (!(name in this._partialCache) && this._loadPartial) {
|
280 |
+
this.compilePartial(name, this._loadPartial(name));
|
281 |
+
}
|
282 |
+
|
283 |
+
var fn = this._partialCache[name];
|
284 |
+
|
285 |
+
return fn ? fn(context) : "";
|
286 |
+
};
|
287 |
+
|
288 |
+
Writer.prototype._name = function (name, context) {
|
289 |
+
var value = context.lookup(name);
|
290 |
+
|
291 |
+
if (typeof value === "function") {
|
292 |
+
value = value.call(context.view);
|
293 |
+
}
|
294 |
+
|
295 |
+
return (value == null) ? "" : String(value);
|
296 |
+
};
|
297 |
+
|
298 |
+
Writer.prototype._escaped = function (name, context) {
|
299 |
+
return exports.escape(this._name(name, context));
|
300 |
+
};
|
301 |
+
|
302 |
+
/**
|
303 |
+
* Low-level function that compiles the given `tokens` into a function
|
304 |
+
* that accepts three arguments: a Writer, a Context, and the template.
|
305 |
+
*/
|
306 |
+
function compileTokens(tokens) {
|
307 |
+
var subRenders = {};
|
308 |
+
|
309 |
+
function subRender(i, tokens, template) {
|
310 |
+
if (!subRenders[i]) {
|
311 |
+
var fn = compileTokens(tokens);
|
312 |
+
subRenders[i] = function (writer, context) {
|
313 |
+
return fn(writer, context, template);
|
314 |
+
};
|
315 |
+
}
|
316 |
+
|
317 |
+
return subRenders[i];
|
318 |
+
}
|
319 |
+
|
320 |
+
return function (writer, context, template) {
|
321 |
+
var buffer = "";
|
322 |
+
var token, sectionText;
|
323 |
+
|
324 |
+
for (var i = 0, len = tokens.length; i < len; ++i) {
|
325 |
+
token = tokens[i];
|
326 |
+
|
327 |
+
switch (token[0]) {
|
328 |
+
case "#":
|
329 |
+
sectionText = template.slice(token[3], token[5]);
|
330 |
+
buffer += writer._section(token[1], context, sectionText, subRender(i, token[4], template));
|
331 |
+
break;
|
332 |
+
case "^":
|
333 |
+
buffer += writer._inverted(token[1], context, subRender(i, token[4], template));
|
334 |
+
break;
|
335 |
+
case ">":
|
336 |
+
buffer += writer._partial(token[1], context);
|
337 |
+
break;
|
338 |
+
case "&":
|
339 |
+
buffer += writer._name(token[1], context);
|
340 |
+
break;
|
341 |
+
case "name":
|
342 |
+
buffer += writer._escaped(token[1], context);
|
343 |
+
break;
|
344 |
+
case "text":
|
345 |
+
buffer += token[1];
|
346 |
+
break;
|
347 |
+
}
|
348 |
+
}
|
349 |
+
|
350 |
+
return buffer;
|
351 |
+
};
|
352 |
+
}
|
353 |
+
|
354 |
+
/**
|
355 |
+
* Forms the given array of `tokens` into a nested tree structure where
|
356 |
+
* tokens that represent a section have two additional items: 1) an array of
|
357 |
+
* all tokens that appear in that section and 2) the index in the original
|
358 |
+
* template that represents the end of that section.
|
359 |
+
*/
|
360 |
+
function nestTokens(tokens) {
|
361 |
+
var tree = [];
|
362 |
+
var collector = tree;
|
363 |
+
var sections = [];
|
364 |
+
|
365 |
+
var token;
|
366 |
+
for (var i = 0, len = tokens.length; i < len; ++i) {
|
367 |
+
token = tokens[i];
|
368 |
+
switch (token[0]) {
|
369 |
+
case '#':
|
370 |
+
case '^':
|
371 |
+
sections.push(token);
|
372 |
+
collector.push(token);
|
373 |
+
collector = token[4] = [];
|
374 |
+
break;
|
375 |
+
case '/':
|
376 |
+
var section = sections.pop();
|
377 |
+
section[5] = token[2];
|
378 |
+
collector = sections.length > 0 ? sections[sections.length - 1][4] : tree;
|
379 |
+
break;
|
380 |
+
default:
|
381 |
+
collector.push(token);
|
382 |
+
}
|
383 |
+
}
|
384 |
+
|
385 |
+
return tree;
|
386 |
+
}
|
387 |
+
|
388 |
+
/**
|
389 |
+
* Combines the values of consecutive text tokens in the given `tokens` array
|
390 |
+
* to a single token.
|
391 |
+
*/
|
392 |
+
function squashTokens(tokens) {
|
393 |
+
var squashedTokens = [];
|
394 |
+
|
395 |
+
var token, lastToken;
|
396 |
+
for (var i = 0, len = tokens.length; i < len; ++i) {
|
397 |
+
token = tokens[i];
|
398 |
+
if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {
|
399 |
+
lastToken[1] += token[1];
|
400 |
+
lastToken[3] = token[3];
|
401 |
+
} else {
|
402 |
+
lastToken = token;
|
403 |
+
squashedTokens.push(token);
|
404 |
+
}
|
405 |
+
}
|
406 |
+
|
407 |
+
return squashedTokens;
|
408 |
+
}
|
409 |
+
|
410 |
+
function escapeTags(tags) {
|
411 |
+
return [
|
412 |
+
new RegExp(escapeRe(tags[0]) + "\\s*"),
|
413 |
+
new RegExp("\\s*" + escapeRe(tags[1]))
|
414 |
+
];
|
415 |
+
}
|
416 |
+
|
417 |
+
/**
|
418 |
+
* Breaks up the given `template` string into a tree of token objects. If
|
419 |
+
* `tags` is given here it must be an array with two string values: the
|
420 |
+
* opening and closing tags used in the template (e.g. ["<%", "%>"]). Of
|
421 |
+
* course, the default is to use mustaches (i.e. Mustache.tags).
|
422 |
+
*/
|
423 |
+
exports.parse = function (template, tags) {
|
424 |
+
template = template || '';
|
425 |
+
tags = tags || exports.tags;
|
426 |
+
|
427 |
+
if (typeof tags === 'string') tags = tags.split(spaceRe);
|
428 |
+
if (tags.length !== 2) {
|
429 |
+
throw new Error('Invalid tags: ' + tags.join(', '));
|
430 |
+
}
|
431 |
+
|
432 |
+
var tagRes = escapeTags(tags);
|
433 |
+
var scanner = new Scanner(template);
|
434 |
+
|
435 |
+
var sections = []; // Stack to hold section tokens
|
436 |
+
var tokens = []; // Buffer to hold the tokens
|
437 |
+
var spaces = []; // Indices of whitespace tokens on the current line
|
438 |
+
var hasTag = false; // Is there a {{tag}} on the current line?
|
439 |
+
var nonSpace = false; // Is there a non-space char on the current line?
|
440 |
+
|
441 |
+
// Strips all whitespace tokens array for the current line
|
442 |
+
// if there was a {{#tag}} on it and otherwise only space.
|
443 |
+
function stripSpace() {
|
444 |
+
if (hasTag && !nonSpace) {
|
445 |
+
while (spaces.length) {
|
446 |
+
tokens.splice(spaces.pop(), 1);
|
447 |
+
}
|
448 |
+
} else {
|
449 |
+
spaces = [];
|
450 |
+
}
|
451 |
+
|
452 |
+
hasTag = false;
|
453 |
+
nonSpace = false;
|
454 |
+
}
|
455 |
+
|
456 |
+
var start, type, value, chr;
|
457 |
+
while (!scanner.eos()) {
|
458 |
+
start = scanner.pos;
|
459 |
+
value = scanner.scanUntil(tagRes[0]);
|
460 |
+
|
461 |
+
if (value) {
|
462 |
+
for (var i = 0, len = value.length; i < len; ++i) {
|
463 |
+
chr = value.charAt(i);
|
464 |
+
|
465 |
+
if (isWhitespace(chr)) {
|
466 |
+
spaces.push(tokens.length);
|
467 |
+
} else {
|
468 |
+
nonSpace = true;
|
469 |
+
}
|
470 |
+
|
471 |
+
tokens.push(["text", chr, start, start + 1]);
|
472 |
+
start += 1;
|
473 |
+
|
474 |
+
if (chr === "\n") {
|
475 |
+
stripSpace(); // Check for whitespace on the current line.
|
476 |
+
}
|
477 |
+
}
|
478 |
+
}
|
479 |
+
|
480 |
+
start = scanner.pos;
|
481 |
+
|
482 |
+
// Match the opening tag.
|
483 |
+
if (!scanner.scan(tagRes[0])) {
|
484 |
+
break;
|
485 |
+
}
|
486 |
+
|
487 |
+
hasTag = true;
|
488 |
+
type = scanner.scan(tagRe) || "name";
|
489 |
+
|
490 |
+
// Skip any whitespace between tag and value.
|
491 |
+
scanner.scan(whiteRe);
|
492 |
+
|
493 |
+
// Extract the tag value.
|
494 |
+
if (type === "=") {
|
495 |
+
value = scanner.scanUntil(eqRe);
|
496 |
+
scanner.scan(eqRe);
|
497 |
+
scanner.scanUntil(tagRes[1]);
|
498 |
+
} else if (type === "{") {
|
499 |
+
var closeRe = new RegExp("\\s*" + escapeRe("}" + tags[1]));
|
500 |
+
value = scanner.scanUntil(closeRe);
|
501 |
+
scanner.scan(curlyRe);
|
502 |
+
scanner.scanUntil(tagRes[1]);
|
503 |
+
type = "&";
|
504 |
+
} else {
|
505 |
+
value = scanner.scanUntil(tagRes[1]);
|
506 |
+
}
|
507 |
+
|
508 |
+
// Match the closing tag.
|
509 |
+
if (!scanner.scan(tagRes[1])) {
|
510 |
+
throw new Error('Unclosed tag at ' + scanner.pos);
|
511 |
+
}
|
512 |
+
|
513 |
+
// Check section nesting.
|
514 |
+
if (type === '/') {
|
515 |
+
if (sections.length === 0) {
|
516 |
+
throw new Error('Unopened section "' + value + '" at ' + start);
|
517 |
+
}
|
518 |
+
|
519 |
+
var section = sections.pop();
|
520 |
+
|
521 |
+
if (section[1] !== value) {
|
522 |
+
throw new Error('Unclosed section "' + section[1] + '" at ' + start);
|
523 |
+
}
|
524 |
+
}
|
525 |
+
|
526 |
+
var token = [type, value, start, scanner.pos];
|
527 |
+
tokens.push(token);
|
528 |
+
|
529 |
+
if (type === '#' || type === '^') {
|
530 |
+
sections.push(token);
|
531 |
+
} else if (type === "name" || type === "{" || type === "&") {
|
532 |
+
nonSpace = true;
|
533 |
+
} else if (type === "=") {
|
534 |
+
// Set the tags for the next time around.
|
535 |
+
tags = value.split(spaceRe);
|
536 |
+
|
537 |
+
if (tags.length !== 2) {
|
538 |
+
throw new Error('Invalid tags at ' + start + ': ' + tags.join(', '));
|
539 |
+
}
|
540 |
+
|
541 |
+
tagRes = escapeTags(tags);
|
542 |
+
}
|
543 |
+
}
|
544 |
+
|
545 |
+
// Make sure there are no open sections when we're done.
|
546 |
+
var section = sections.pop();
|
547 |
+
if (section) {
|
548 |
+
throw new Error('Unclosed section "' + section[1] + '" at ' + scanner.pos);
|
549 |
+
}
|
550 |
+
|
551 |
+
return nestTokens(squashTokens(tokens));
|
552 |
+
};
|
553 |
+
|
554 |
+
// The high-level clearCache, compile, compilePartial, and render functions
|
555 |
+
// use this default writer.
|
556 |
+
var _writer = new Writer();
|
557 |
+
|
558 |
+
/**
|
559 |
+
* Clears all cached templates and partials in the default writer.
|
560 |
+
*/
|
561 |
+
exports.clearCache = function () {
|
562 |
+
return _writer.clearCache();
|
563 |
+
};
|
564 |
+
|
565 |
+
/**
|
566 |
+
* Compiles the given `template` to a reusable function using the default
|
567 |
+
* writer.
|
568 |
+
*/
|
569 |
+
exports.compile = function (template, tags) {
|
570 |
+
return _writer.compile(template, tags);
|
571 |
+
};
|
572 |
+
|
573 |
+
/**
|
574 |
+
* Compiles the partial with the given `name` and `template` to a reusable
|
575 |
+
* function using the default writer.
|
576 |
+
*/
|
577 |
+
exports.compilePartial = function (name, template, tags) {
|
578 |
+
return _writer.compilePartial(name, template, tags);
|
579 |
+
};
|
580 |
+
|
581 |
+
/**
|
582 |
+
* Compiles the given array of tokens (the output of a parse) to a reusable
|
583 |
+
* function using the default writer.
|
584 |
+
*/
|
585 |
+
exports.compileTokens = function (tokens, template) {
|
586 |
+
return _writer.compileTokens(tokens, template);
|
587 |
+
};
|
588 |
+
|
589 |
+
/**
|
590 |
+
* Renders the `template` with the given `view` and `partials` using the
|
591 |
+
* default writer.
|
592 |
+
*/
|
593 |
+
exports.render = function (template, view, partials) {
|
594 |
+
return _writer.render(template, view, partials);
|
595 |
+
};
|
596 |
+
|
597 |
+
// This is here for backwards compatibility with 0.4.x.
|
598 |
+
exports.to_html = function (template, view, partials, send) {
|
599 |
+
var result = exports.render(template, view, partials);
|
600 |
+
|
601 |
+
if (typeof send === "function") {
|
602 |
+
send(result);
|
603 |
+
} else {
|
604 |
+
return result;
|
605 |
+
}
|
606 |
+
};
|
607 |
+
|
608 |
+
return exports;
|
609 |
+
|
610 |
+
}())));
|
admin/templates/tab-list.html
CHANGED
@@ -10,7 +10,7 @@
|
|
10 |
<div class="p2p-navigation">
|
11 |
<div class="p2p-prev button {{prev-inactive}}" title="{{prev-label}}">‹</div>
|
12 |
<div>
|
13 |
-
<span class="p2p-current"
|
14 |
{{of-label}}
|
15 |
<span class="p2p-total" data-num="{{total-pages-raw}}">{{total-pages}}</span>
|
16 |
</div>
|
10 |
<div class="p2p-navigation">
|
11 |
<div class="p2p-prev button {{prev-inactive}}" title="{{prev-label}}">‹</div>
|
12 |
<div>
|
13 |
+
<span class="p2p-current">{{current-page}}</span>
|
14 |
{{of-label}}
|
15 |
<span class="p2p-total" data-num="{{total-pages-raw}}">{{total-pages}}</span>
|
16 |
</div>
|
admin/{tools.php → tools-page.php}
RENAMED
@@ -133,5 +133,3 @@ class P2P_Tools_Page extends scbAdminPage {
|
|
133 |
}
|
134 |
}
|
135 |
|
136 |
-
new P2P_Tools_Page;
|
137 |
-
|
133 |
}
|
134 |
}
|
135 |
|
|
|
|
autoload.php
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Autoload {
|
4 |
+
|
5 |
+
protected function __construct( $prefix, $basedir ) {
|
6 |
+
$this->prefix = $prefix;
|
7 |
+
$this->basedir = $basedir;
|
8 |
+
}
|
9 |
+
|
10 |
+
static function register( $prefix, $basedir ) {
|
11 |
+
$loader = new self( $prefix, $basedir );
|
12 |
+
|
13 |
+
spl_autoload_register( array( $loader, 'autoload' ) );
|
14 |
+
}
|
15 |
+
|
16 |
+
function autoload( $class ) {
|
17 |
+
if ( $class[0] === '\\' ) {
|
18 |
+
$class = substr( $class, 1 );
|
19 |
+
}
|
20 |
+
|
21 |
+
if ( strpos( $class, $this->prefix ) !== 0 ) {
|
22 |
+
return;
|
23 |
+
}
|
24 |
+
|
25 |
+
$path = str_replace( $this->prefix, '', $class );
|
26 |
+
$path = str_replace( '_', '-', strtolower( $path ) );
|
27 |
+
|
28 |
+
$file = sprintf( '%s/%s.php', $this->basedir, $path );
|
29 |
+
|
30 |
+
if ( is_file( $file ) ) {
|
31 |
+
require $file;
|
32 |
+
}
|
33 |
+
}
|
34 |
+
}
|
35 |
+
|
command.php
CHANGED
@@ -30,10 +30,9 @@ class P2P_CLI_Command extends WP_CLI_Command {
|
|
30 |
|
31 |
if ( isset( $assoc_args['items'] ) ) {
|
32 |
foreach ( _p2p_extract_post_types( $ctype->side ) as $ptype ) {
|
33 |
-
$command = array( 'wp', 'post', 'generate' );
|
34 |
$assoc_args = array( 'post_type' => $ptype );
|
35 |
|
36 |
-
WP_CLI::launch( WP_CLI
|
37 |
}
|
38 |
}
|
39 |
|
30 |
|
31 |
if ( isset( $assoc_args['items'] ) ) {
|
32 |
foreach ( _p2p_extract_post_types( $ctype->side ) as $ptype ) {
|
|
|
33 |
$assoc_args = array( 'post_type' => $ptype );
|
34 |
|
35 |
+
WP_CLI::launch( 'wp post generate' . \WP_CLI\Utils\assoc_args_to_str( $assoc_args ) );
|
36 |
}
|
37 |
}
|
38 |
|
core/api.php
CHANGED
@@ -1,19 +1,17 @@
|
|
1 |
<?php
|
2 |
|
3 |
/**
|
4 |
-
* Register a connection
|
5 |
*
|
6 |
-
*
|
7 |
-
*
|
8 |
-
* Takes the following parameters, as an associative array:
|
9 |
*
|
10 |
* - 'name' - string A unique identifier for this connection type.
|
11 |
*
|
12 |
-
* - 'from' - string|array The first end of the connection
|
13 |
*
|
14 |
* - 'from_query_vars' - array Additional query vars to pass to WP_Query. Default: none.
|
15 |
*
|
16 |
-
* - 'to' - string|array The second end of the connection
|
17 |
*
|
18 |
* - 'to_query_vars' - array Additional query vars to pass to WP_Query. Default: none.
|
19 |
*
|
@@ -23,7 +21,7 @@
|
|
23 |
*
|
24 |
* - 'duplicate_connections' - bool Whether to allow more than one connection between the same two posts. Default: false.
|
25 |
*
|
26 |
-
* - 'self_connections' - bool Whether to allow a post to connect to itself. Default: false.
|
27 |
*
|
28 |
* - 'sortable' - bool|string Whether to allow connections to be ordered via drag-and-drop. Can be 'from', 'to', 'any' or false. Default: false.
|
29 |
*
|
@@ -35,11 +33,13 @@
|
|
35 |
*
|
36 |
* - 'reciprocal' - bool For indeterminate connections: True means all connections are displayed in a single box. False means 'from' connections are shown in one box and 'to' connections are shown in another box. Default: false.
|
37 |
*
|
38 |
-
* - 'admin_box' - bool|string|array Whether and where to show the admin connections box.
|
39 |
*
|
40 |
* - 'can_create_post' - bool Whether to allow post creation via the connection box. Default: true.
|
41 |
*
|
42 |
-
*
|
|
|
|
|
43 |
*
|
44 |
* @return bool|object False on failure, P2P_Connection_Type instance on success.
|
45 |
*/
|
@@ -77,6 +77,9 @@ function p2p_register_connection_type( $args ) {
|
|
77 |
$args['admin_box']['context'] = _p2p_pluck( $args, 'context' );
|
78 |
}
|
79 |
|
|
|
|
|
|
|
80 |
$ctype = P2P_Connection_Type_Factory::register( $args );
|
81 |
|
82 |
do_action( 'p2p_registered_connection_type', $ctype, $args );
|
@@ -126,35 +129,35 @@ function p2p_connection_exists( $p2p_type, $args = array() ) {
|
|
126 |
* @return array
|
127 |
*/
|
128 |
function p2p_get_connections( $p2p_type, $args = array() ) {
|
129 |
-
|
130 |
'direction' => 'from',
|
131 |
'from' => 'any',
|
132 |
'to' => 'any',
|
133 |
'fields' => 'all',
|
134 |
-
) )
|
135 |
|
136 |
$r = array();
|
137 |
|
138 |
-
foreach ( _p2p_expand_direction( $direction ) as $direction ) {
|
139 |
-
$
|
140 |
|
141 |
if ( 'to' == $direction ) {
|
142 |
-
$
|
143 |
}
|
144 |
|
145 |
-
if ( 'object_id' == $fields )
|
146 |
-
$
|
147 |
else
|
148 |
-
$
|
149 |
|
150 |
$r = array_merge( $r, _p2p_get_connections( $p2p_type, array(
|
151 |
-
'from' => $
|
152 |
-
'to' => $
|
153 |
-
'fields' => $
|
154 |
) ) );
|
155 |
}
|
156 |
|
157 |
-
if ( 'count' == $fields )
|
158 |
return array_sum( $r );
|
159 |
|
160 |
return $r;
|
@@ -164,27 +167,25 @@ function p2p_get_connections( $p2p_type, $args = array() ) {
|
|
164 |
function _p2p_get_connections( $p2p_type, $args = array() ) {
|
165 |
global $wpdb;
|
166 |
|
167 |
-
extract( $args, EXTR_SKIP );
|
168 |
-
|
169 |
$where = $wpdb->prepare( 'WHERE p2p_type = %s', $p2p_type );
|
170 |
|
171 |
foreach ( array( 'from', 'to' ) as $key ) {
|
172 |
-
if ( 'any' ==
|
173 |
continue;
|
174 |
|
175 |
-
if ( empty(
|
176 |
return array();
|
177 |
|
178 |
-
$value = scbUtil::array_to_sql( _p2p_normalize(
|
179 |
|
180 |
$where .= " AND p2p_$key IN ($value)";
|
181 |
}
|
182 |
|
183 |
-
switch ( $fields ) {
|
184 |
case 'p2p_id':
|
185 |
case 'p2p_from':
|
186 |
case 'p2p_to':
|
187 |
-
$sql_field = $fields;
|
188 |
break;
|
189 |
case 'count':
|
190 |
$sql_field = 'COUNT(*)';
|
@@ -225,34 +226,34 @@ function p2p_get_connection( $p2p_id ) {
|
|
225 |
function p2p_create_connection( $p2p_type, $args ) {
|
226 |
global $wpdb;
|
227 |
|
228 |
-
|
229 |
'direction' => 'from',
|
230 |
'from' => false,
|
231 |
'to' => false,
|
232 |
'meta' => array()
|
233 |
-
) )
|
234 |
|
235 |
-
list( $from ) = _p2p_normalize( $from );
|
236 |
-
list( $to ) = _p2p_normalize( $to );
|
237 |
|
238 |
if ( !$from || !$to )
|
239 |
return false;
|
240 |
|
241 |
-
$
|
242 |
|
243 |
-
if ( 'to' == $direction ) {
|
244 |
-
$
|
245 |
}
|
246 |
|
247 |
$wpdb->insert( $wpdb->p2p, array(
|
248 |
'p2p_type' => $p2p_type,
|
249 |
-
'p2p_from' => $
|
250 |
-
'p2p_to' => $
|
251 |
) );
|
252 |
|
253 |
$p2p_id = $wpdb->insert_id;
|
254 |
|
255 |
-
foreach ( $meta as $key => $value )
|
256 |
p2p_add_meta( $p2p_id, $key, $value );
|
257 |
|
258 |
do_action( 'p2p_created_connection', $p2p_id );
|
@@ -350,29 +351,10 @@ function p2p_distribute_connected( $items, $connected, $prop_name ) {
|
|
350 |
$indexed_list[ $item->ID ] = $item;
|
351 |
}
|
352 |
|
353 |
-
$groups =
|
354 |
|
355 |
foreach ( $groups as $outer_item_id => $connected_items ) {
|
356 |
$indexed_list[ $outer_item_id ]->$prop_name = $connected_items;
|
357 |
}
|
358 |
}
|
359 |
|
360 |
-
function p2p_triage_connected( $connected ) {
|
361 |
-
$groups = array();
|
362 |
-
|
363 |
-
foreach ( $connected as $inner_item ) {
|
364 |
-
if ( $inner_item->ID == $inner_item->p2p_from ) {
|
365 |
-
$outer_item_id = $inner_item->p2p_to;
|
366 |
-
} elseif ( $inner_item->ID == $inner_item->p2p_to ) {
|
367 |
-
$outer_item_id = $inner_item->p2p_from;
|
368 |
-
} else {
|
369 |
-
trigger_error( "Corrupted data for item $inner_item->ID", E_USER_WARNING );
|
370 |
-
continue;
|
371 |
-
}
|
372 |
-
|
373 |
-
$groups[ $outer_item_id ][] = $inner_item;
|
374 |
-
}
|
375 |
-
|
376 |
-
return $groups;
|
377 |
-
}
|
378 |
-
|
1 |
<?php
|
2 |
|
3 |
/**
|
4 |
+
* Register a connection type.
|
5 |
*
|
6 |
+
* @param array $args Associative array:
|
|
|
|
|
7 |
*
|
8 |
* - 'name' - string A unique identifier for this connection type.
|
9 |
*
|
10 |
+
* - 'from' - string|array The first end of the connection: post type name or 'user'
|
11 |
*
|
12 |
* - 'from_query_vars' - array Additional query vars to pass to WP_Query. Default: none.
|
13 |
*
|
14 |
+
* - 'to' - string|array The second end of the connection: post type name or 'user'
|
15 |
*
|
16 |
* - 'to_query_vars' - array Additional query vars to pass to WP_Query. Default: none.
|
17 |
*
|
21 |
*
|
22 |
* - 'duplicate_connections' - bool Whether to allow more than one connection between the same two posts. Default: false.
|
23 |
*
|
24 |
+
* - 'self_connections' - bool Whether to allow a post/user to connect to itself. Default: false.
|
25 |
*
|
26 |
* - 'sortable' - bool|string Whether to allow connections to be ordered via drag-and-drop. Can be 'from', 'to', 'any' or false. Default: false.
|
27 |
*
|
33 |
*
|
34 |
* - 'reciprocal' - bool For indeterminate connections: True means all connections are displayed in a single box. False means 'from' connections are shown in one box and 'to' connections are shown in another box. Default: false.
|
35 |
*
|
36 |
+
* - 'admin_box' - bool|string|array Whether and where to show the admin connections box. Default: 'any'
|
37 |
*
|
38 |
* - 'can_create_post' - bool Whether to allow post creation via the connection box. Default: true.
|
39 |
*
|
40 |
+
* - 'admin_column' - bool|string|array Whether to show connection columns on post/user list table. Default: false
|
41 |
+
*
|
42 |
+
* - 'admin_dropdown' - bool|string|array Whether to show connection dropdown on post/user list table. Default: false
|
43 |
*
|
44 |
* @return bool|object False on failure, P2P_Connection_Type instance on success.
|
45 |
*/
|
77 |
$args['admin_box']['context'] = _p2p_pluck( $args, 'context' );
|
78 |
}
|
79 |
|
80 |
+
if ( !isset( $args['admin_box'] ) )
|
81 |
+
$args['admin_box'] = 'any';
|
82 |
+
|
83 |
$ctype = P2P_Connection_Type_Factory::register( $args );
|
84 |
|
85 |
do_action( 'p2p_registered_connection_type', $ctype, $args );
|
129 |
* @return array
|
130 |
*/
|
131 |
function p2p_get_connections( $p2p_type, $args = array() ) {
|
132 |
+
$args = wp_parse_args( $args, array(
|
133 |
'direction' => 'from',
|
134 |
'from' => 'any',
|
135 |
'to' => 'any',
|
136 |
'fields' => 'all',
|
137 |
+
) );
|
138 |
|
139 |
$r = array();
|
140 |
|
141 |
+
foreach ( _p2p_expand_direction( $args['direction'] ) as $direction ) {
|
142 |
+
$dirs = array( $args['from'], $args['to'] );
|
143 |
|
144 |
if ( 'to' == $direction ) {
|
145 |
+
$dirs = array_reverse( $dirs );
|
146 |
}
|
147 |
|
148 |
+
if ( 'object_id' == $args['fields'] )
|
149 |
+
$fields = ( 'to' == $direction ) ? 'p2p_from' : 'p2p_to';
|
150 |
else
|
151 |
+
$fields = $args['fields'];
|
152 |
|
153 |
$r = array_merge( $r, _p2p_get_connections( $p2p_type, array(
|
154 |
+
'from' => $dirs[0],
|
155 |
+
'to' => $dirs[1],
|
156 |
+
'fields' => $fields
|
157 |
) ) );
|
158 |
}
|
159 |
|
160 |
+
if ( 'count' == $args['fields'] )
|
161 |
return array_sum( $r );
|
162 |
|
163 |
return $r;
|
167 |
function _p2p_get_connections( $p2p_type, $args = array() ) {
|
168 |
global $wpdb;
|
169 |
|
|
|
|
|
170 |
$where = $wpdb->prepare( 'WHERE p2p_type = %s', $p2p_type );
|
171 |
|
172 |
foreach ( array( 'from', 'to' ) as $key ) {
|
173 |
+
if ( 'any' == $args[ $key ] )
|
174 |
continue;
|
175 |
|
176 |
+
if ( empty( $args[ $key ] ) )
|
177 |
return array();
|
178 |
|
179 |
+
$value = scbUtil::array_to_sql( _p2p_normalize( $args[ $key ] ) );
|
180 |
|
181 |
$where .= " AND p2p_$key IN ($value)";
|
182 |
}
|
183 |
|
184 |
+
switch ( $args['fields'] ) {
|
185 |
case 'p2p_id':
|
186 |
case 'p2p_from':
|
187 |
case 'p2p_to':
|
188 |
+
$sql_field = $args['fields'];
|
189 |
break;
|
190 |
case 'count':
|
191 |
$sql_field = 'COUNT(*)';
|
226 |
function p2p_create_connection( $p2p_type, $args ) {
|
227 |
global $wpdb;
|
228 |
|
229 |
+
$args = wp_parse_args( $args, array(
|
230 |
'direction' => 'from',
|
231 |
'from' => false,
|
232 |
'to' => false,
|
233 |
'meta' => array()
|
234 |
+
) );
|
235 |
|
236 |
+
list( $from ) = _p2p_normalize( $args['from'] );
|
237 |
+
list( $to ) = _p2p_normalize( $args['to'] );
|
238 |
|
239 |
if ( !$from || !$to )
|
240 |
return false;
|
241 |
|
242 |
+
$dirs = array( $from, $to );
|
243 |
|
244 |
+
if ( 'to' == $args['direction'] ) {
|
245 |
+
$dirs = array_reverse( $dirs );
|
246 |
}
|
247 |
|
248 |
$wpdb->insert( $wpdb->p2p, array(
|
249 |
'p2p_type' => $p2p_type,
|
250 |
+
'p2p_from' => $dirs[0],
|
251 |
+
'p2p_to' => $dirs[1]
|
252 |
) );
|
253 |
|
254 |
$p2p_id = $wpdb->insert_id;
|
255 |
|
256 |
+
foreach ( $args['meta'] as $key => $value )
|
257 |
p2p_add_meta( $p2p_id, $key, $value );
|
258 |
|
259 |
do_action( 'p2p_created_connection', $p2p_id );
|
351 |
$indexed_list[ $item->ID ] = $item;
|
352 |
}
|
353 |
|
354 |
+
$groups = p2p_list_cluster( $connected, '_p2p_get_other_id' );
|
355 |
|
356 |
foreach ( $groups as $outer_item_id => $connected_items ) {
|
357 |
$indexed_list[ $outer_item_id ]->$prop_name = $connected_items;
|
358 |
}
|
359 |
}
|
360 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/{type-factory.php → connection-type-factory.php}
RENAMED
@@ -7,8 +7,6 @@ class P2P_Connection_Type_Factory {
|
|
7 |
public static function register( $args ) {
|
8 |
$defaults = array(
|
9 |
'name' => false,
|
10 |
-
'from_object' => 'post',
|
11 |
-
'to_object' => 'post',
|
12 |
'from' => 'post',
|
13 |
'to' => 'post',
|
14 |
'from_query_vars' => array(),
|
7 |
public static function register( $args ) {
|
8 |
$defaults = array(
|
9 |
'name' => false,
|
|
|
|
|
10 |
'from' => 'post',
|
11 |
'to' => 'post',
|
12 |
'from_query_vars' => array(),
|
core/{type.php → connection-type.php}
RENAMED
@@ -98,11 +98,10 @@ class P2P_Connection_Type {
|
|
98 |
}
|
99 |
|
100 |
private function expand_labels( $additional_labels, $key ) {
|
101 |
-
$additional_labels['create'] = __( 'Create connections', P2P_TEXTDOMAIN );
|
102 |
-
|
103 |
$labels = clone $this->side[ $key ]->get_labels();
|
|
|
104 |
|
105 |
-
foreach ( $additional_labels as $key => $var )
|
106 |
$labels->$key = $var;
|
107 |
|
108 |
return $labels;
|
@@ -380,11 +379,13 @@ class P2P_Connection_Type {
|
|
380 |
}
|
381 |
|
382 |
public function get_desc() {
|
|
|
|
|
383 |
foreach ( array( 'from', 'to' ) as $key ) {
|
384 |
-
|
385 |
}
|
386 |
|
387 |
-
$label =
|
388 |
|
389 |
$title = $this->get_field( 'title', 'from' );
|
390 |
|
@@ -395,36 +396,3 @@ class P2P_Connection_Type {
|
|
395 |
}
|
396 |
}
|
397 |
|
398 |
-
|
399 |
-
class P2P_Indeterminate_Connection_Type extends P2P_Connection_Type {
|
400 |
-
|
401 |
-
protected $directed_class = 'P2P_Indeterminate_Directed_Connection_Type';
|
402 |
-
|
403 |
-
protected $arrow = '↔';
|
404 |
-
|
405 |
-
protected function choose_direction( $direction ) {
|
406 |
-
return 'from';
|
407 |
-
}
|
408 |
-
|
409 |
-
function _directions_for_admin( $direction, $show_ui ) {
|
410 |
-
return parent::_directions_for_admin( 'any', $show_ui );
|
411 |
-
}
|
412 |
-
}
|
413 |
-
|
414 |
-
|
415 |
-
class P2P_Reciprocal_Connection_Type extends P2P_Indeterminate_Connection_Type {
|
416 |
-
|
417 |
-
protected function choose_direction( $direction ) {
|
418 |
-
return 'any';
|
419 |
-
}
|
420 |
-
|
421 |
-
function _directions_for_admin( $direction, $show_ui ) {
|
422 |
-
if ( $show_ui )
|
423 |
-
$directions = array( 'any' );
|
424 |
-
else
|
425 |
-
$directions = array();
|
426 |
-
|
427 |
-
return $directions;
|
428 |
-
}
|
429 |
-
}
|
430 |
-
|
98 |
}
|
99 |
|
100 |
private function expand_labels( $additional_labels, $key ) {
|
|
|
|
|
101 |
$labels = clone $this->side[ $key ]->get_labels();
|
102 |
+
$labels->create = __( 'Create connections', P2P_TEXTDOMAIN );
|
103 |
|
104 |
+
foreach ( $additional_labels[ $key ] as $key => $var )
|
105 |
$labels->$key = $var;
|
106 |
|
107 |
return $labels;
|
379 |
}
|
380 |
|
381 |
public function get_desc() {
|
382 |
+
$desc = array();
|
383 |
+
|
384 |
foreach ( array( 'from', 'to' ) as $key ) {
|
385 |
+
$desc[ $key ] = $this->side[ $key ]->get_desc();
|
386 |
}
|
387 |
|
388 |
+
$label = sprintf( '%s %s %s', $desc['from'], $this->arrow, $desc['to'] );
|
389 |
|
390 |
$title = $this->get_field( 'title', 'from' );
|
391 |
|
396 |
}
|
397 |
}
|
398 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/{directed-type.php → directed-connection-type.php}
RENAMED
File without changes
|
core/indeterminate-connection-type.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Indeterminate_Connection_Type extends P2P_Connection_Type {
|
4 |
+
|
5 |
+
protected $directed_class = 'P2P_Indeterminate_Directed_Connection_Type';
|
6 |
+
|
7 |
+
protected $arrow = '↔';
|
8 |
+
|
9 |
+
protected function choose_direction( $direction ) {
|
10 |
+
return 'from';
|
11 |
+
}
|
12 |
+
|
13 |
+
function _directions_for_admin( $direction, $show_ui ) {
|
14 |
+
return parent::_directions_for_admin( 'any', $show_ui );
|
15 |
+
}
|
16 |
+
}
|
17 |
+
|
core/{indeterminate-type.php → indeterminate-directed-connection-type.php}
RENAMED
File without changes
|
core/item-any.php
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Item_Any extends P2P_Item {
|
4 |
+
|
5 |
+
function __construct() {}
|
6 |
+
|
7 |
+
function get_permalink() {}
|
8 |
+
|
9 |
+
function get_title() {}
|
10 |
+
|
11 |
+
function get_object() {
|
12 |
+
return 'any';
|
13 |
+
}
|
14 |
+
|
15 |
+
function get_id() {
|
16 |
+
return false;
|
17 |
+
}
|
18 |
+
}
|
19 |
+
|
core/item-attachment.php
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Item_Attachment extends P2P_Item_Post {
|
4 |
+
|
5 |
+
function get_title() {
|
6 |
+
if( wp_attachment_is_image( $this->item->ID ) )
|
7 |
+
return wp_get_attachment_image( $this->item->ID, 'thumbnail', false );
|
8 |
+
|
9 |
+
return get_the_title( $this->item );
|
10 |
+
}
|
11 |
+
}
|
12 |
+
|
core/item-post.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Item_Post extends P2P_Item {
|
4 |
+
|
5 |
+
function get_title() {
|
6 |
+
return get_the_title( $this->item );
|
7 |
+
}
|
8 |
+
|
9 |
+
function get_permalink() {
|
10 |
+
return get_permalink( $this->item );
|
11 |
+
}
|
12 |
+
|
13 |
+
function get_editlink() {
|
14 |
+
return get_edit_post_link( $this->item );
|
15 |
+
}
|
16 |
+
}
|
17 |
+
|
core/item-user.php
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Item_User extends P2P_Item {
|
4 |
+
|
5 |
+
function get_title() {
|
6 |
+
return $this->item->display_name;
|
7 |
+
}
|
8 |
+
|
9 |
+
function get_permalink() {
|
10 |
+
return get_author_posts_url( $this->item->ID );
|
11 |
+
}
|
12 |
+
|
13 |
+
function get_editlink() {
|
14 |
+
return get_edit_user_link( $this->item->ID );
|
15 |
+
}
|
16 |
+
}
|
17 |
+
|
18 |
+
|
19 |
+
// WP < 3.5
|
20 |
+
if ( !function_exists( 'get_edit_user_link' ) ) :
|
21 |
+
function get_edit_user_link( $user_id = null ) {
|
22 |
+
if ( ! $user_id )
|
23 |
+
$user_id = get_current_user_id();
|
24 |
+
|
25 |
+
if ( empty( $user_id ) || ! current_user_can( 'edit_user', $user_id ) )
|
26 |
+
return '';
|
27 |
+
|
28 |
+
$user = new WP_User( $user_id );
|
29 |
+
|
30 |
+
if ( ! $user->exists() )
|
31 |
+
return '';
|
32 |
+
|
33 |
+
if ( get_current_user_id() == $user->ID )
|
34 |
+
$link = get_edit_profile_url( $user->ID );
|
35 |
+
else
|
36 |
+
$link = add_query_arg( 'user_id', $user->ID, self_admin_url( 'user-edit.php' ) );
|
37 |
+
|
38 |
+
return apply_filters( 'get_edit_user_link', $link, $user->ID );
|
39 |
+
}
|
40 |
+
endif;
|
41 |
+
|
core/item.php
CHANGED
@@ -32,85 +32,3 @@ abstract class P2P_Item {
|
|
32 |
abstract function get_title();
|
33 |
}
|
34 |
|
35 |
-
|
36 |
-
class P2P_Item_Any extends P2P_Item {
|
37 |
-
|
38 |
-
function __construct() {}
|
39 |
-
|
40 |
-
function get_permalink() {}
|
41 |
-
|
42 |
-
function get_title() {}
|
43 |
-
|
44 |
-
function get_object() {
|
45 |
-
return 'any';
|
46 |
-
}
|
47 |
-
|
48 |
-
function get_id() {
|
49 |
-
return false;
|
50 |
-
}
|
51 |
-
}
|
52 |
-
|
53 |
-
|
54 |
-
class P2P_Item_Post extends P2P_Item {
|
55 |
-
|
56 |
-
function get_title() {
|
57 |
-
return get_the_title( $this->item );
|
58 |
-
}
|
59 |
-
|
60 |
-
function get_permalink() {
|
61 |
-
return get_permalink( $this->item );
|
62 |
-
}
|
63 |
-
|
64 |
-
function get_editlink() {
|
65 |
-
return get_edit_post_link( $this->item );
|
66 |
-
}
|
67 |
-
}
|
68 |
-
|
69 |
-
|
70 |
-
class P2P_Item_Attachment extends P2P_Item_Post {
|
71 |
-
|
72 |
-
function get_title() {
|
73 |
-
return wp_get_attachment_image( $this->item->ID, 'thumbnail', false );
|
74 |
-
}
|
75 |
-
}
|
76 |
-
|
77 |
-
|
78 |
-
class P2P_Item_User extends P2P_Item {
|
79 |
-
|
80 |
-
function get_title() {
|
81 |
-
return $this->item->display_name;
|
82 |
-
}
|
83 |
-
|
84 |
-
function get_permalink() {
|
85 |
-
return get_author_posts_url( $this->item->ID );
|
86 |
-
}
|
87 |
-
|
88 |
-
function get_editlink() {
|
89 |
-
return get_edit_user_link( $this->item->ID );
|
90 |
-
}
|
91 |
-
}
|
92 |
-
|
93 |
-
|
94 |
-
// WP < 3.5
|
95 |
-
if ( !function_exists( 'get_edit_user_link' ) ) :
|
96 |
-
function get_edit_user_link( $user_id = null ) {
|
97 |
-
if ( ! $user_id )
|
98 |
-
$user_id = get_current_user_id();
|
99 |
-
|
100 |
-
if ( empty( $user_id ) || ! current_user_can( 'edit_user', $user_id ) )
|
101 |
-
return '';
|
102 |
-
|
103 |
-
$user = new WP_User( $user_id );
|
104 |
-
|
105 |
-
if ( ! $user->exists() )
|
106 |
-
return '';
|
107 |
-
|
108 |
-
if ( get_current_user_id() == $user->ID )
|
109 |
-
$link = get_edit_profile_url( $user->ID );
|
110 |
-
else
|
111 |
-
$link = add_query_arg( 'user_id', $user->ID, self_admin_url( 'user-edit.php' ) );
|
112 |
-
|
113 |
-
return apply_filters( 'get_edit_user_link', $link, $user->ID );
|
114 |
-
}
|
115 |
-
endif;
|
116 |
-
|
32 |
abstract function get_title();
|
33 |
}
|
34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/list.php
CHANGED
@@ -26,36 +26,34 @@ class P2P_List {
|
|
26 |
'echo' => true
|
27 |
) );
|
28 |
|
29 |
-
|
|
|
|
|
30 |
|
31 |
-
|
32 |
-
|
33 |
-
$before_list = '';
|
34 |
-
|
35 |
-
if ( '</ul>' == $after_list )
|
36 |
-
$after_list = '';
|
37 |
}
|
38 |
|
39 |
-
if ( !$echo )
|
40 |
ob_start();
|
41 |
|
42 |
-
echo $before_list;
|
43 |
|
44 |
-
if ( $separator ) {
|
45 |
$list = array();
|
46 |
foreach ( $this->items as $item ) {
|
47 |
$list[] = $this->render_item( $item );
|
48 |
}
|
49 |
-
echo implode( $separator, $list );
|
50 |
} else {
|
51 |
foreach ( $this->items as $item ) {
|
52 |
-
echo $before_item . $this->render_item( $item ) . $after_item;
|
53 |
}
|
54 |
}
|
55 |
|
56 |
-
echo $after_list;
|
57 |
|
58 |
-
if ( !$echo )
|
59 |
return ob_get_clean();
|
60 |
}
|
61 |
|
26 |
'echo' => true
|
27 |
) );
|
28 |
|
29 |
+
if ( $args['separator'] ) {
|
30 |
+
if ( '<ul>' == $args['before_list'] )
|
31 |
+
$args['before_list'] = '';
|
32 |
|
33 |
+
if ( '</ul>' == $args['after_list'] )
|
34 |
+
$args['after_list'] = '';
|
|
|
|
|
|
|
|
|
35 |
}
|
36 |
|
37 |
+
if ( !$args['echo'] )
|
38 |
ob_start();
|
39 |
|
40 |
+
echo $args['before_list'];
|
41 |
|
42 |
+
if ( $args['separator'] ) {
|
43 |
$list = array();
|
44 |
foreach ( $this->items as $item ) {
|
45 |
$list[] = $this->render_item( $item );
|
46 |
}
|
47 |
+
echo implode( $args['separator'], $list );
|
48 |
} else {
|
49 |
foreach ( $this->items as $item ) {
|
50 |
+
echo $args['before_item'] . $this->render_item( $item ) . $args['after_item'];
|
51 |
}
|
52 |
}
|
53 |
|
54 |
+
echo $args['after_list'];
|
55 |
|
56 |
+
if ( !$args['echo'] )
|
57 |
return ob_get_clean();
|
58 |
}
|
59 |
|
core/query-post.php
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
<?php
|
2 |
|
3 |
-
class
|
4 |
|
5 |
static function init() {
|
6 |
add_action( 'parse_query', array( __CLASS__, 'parse_query' ), 20 );
|
@@ -59,5 +59,3 @@ class P2P_Post_Query {
|
|
59 |
}
|
60 |
}
|
61 |
|
62 |
-
P2P_Post_Query::init();
|
63 |
-
|
1 |
<?php
|
2 |
|
3 |
+
class P2P_Query_Post {
|
4 |
|
5 |
static function init() {
|
6 |
add_action( 'parse_query', array( __CLASS__, 'parse_query' ), 20 );
|
59 |
}
|
60 |
}
|
61 |
|
|
|
|
core/query-user.php
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
<?php
|
2 |
|
3 |
-
class
|
4 |
|
5 |
static function init() {
|
6 |
add_action( 'pre_user_query', array( __CLASS__, 'pre_user_query' ), 20 );
|
@@ -43,5 +43,3 @@ class P2P_User_Query {
|
|
43 |
}
|
44 |
}
|
45 |
|
46 |
-
P2P_User_Query::init();
|
47 |
-
|
1 |
<?php
|
2 |
|
3 |
+
class P2P_Query_User {
|
4 |
|
5 |
static function init() {
|
6 |
add_action( 'pre_user_query', array( __CLASS__, 'pre_user_query' ), 20 );
|
43 |
}
|
44 |
}
|
45 |
|
|
|
|
core/reciprocal-connection-type.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Reciprocal_Connection_Type extends P2P_Indeterminate_Connection_Type {
|
4 |
+
|
5 |
+
protected function choose_direction( $direction ) {
|
6 |
+
return 'any';
|
7 |
+
}
|
8 |
+
|
9 |
+
function _directions_for_admin( $direction, $show_ui ) {
|
10 |
+
if ( $show_ui )
|
11 |
+
$directions = array( 'any' );
|
12 |
+
else
|
13 |
+
$directions = array();
|
14 |
+
|
15 |
+
return $directions;
|
16 |
+
}
|
17 |
+
}
|
18 |
+
|
core/shortcodes.php
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Shortcodes {
|
4 |
+
|
5 |
+
static function init() {
|
6 |
+
add_shortcode( 'p2p_connected', array( __CLASS__, 'connected' ) );
|
7 |
+
add_shortcode( 'p2p_related', array( __CLASS__, 'related' ) );
|
8 |
+
}
|
9 |
+
|
10 |
+
static function connected( $attr ) {
|
11 |
+
return self::get_list( $attr, 'get_connected' );
|
12 |
+
}
|
13 |
+
|
14 |
+
static function related( $attr ) {
|
15 |
+
return self::get_list( $attr, 'get_related' );
|
16 |
+
}
|
17 |
+
|
18 |
+
private static function get_list( $attr, $method ) {
|
19 |
+
global $post;
|
20 |
+
|
21 |
+
$attr = shortcode_atts( array(
|
22 |
+
'type' => '',
|
23 |
+
'mode' => 'ul',
|
24 |
+
), $attr );
|
25 |
+
|
26 |
+
return _p2p_get_list( array(
|
27 |
+
'ctype' => $attr['type'],
|
28 |
+
'method' => $method,
|
29 |
+
'item' => $post,
|
30 |
+
'mode' => $attr['mode'],
|
31 |
+
'context' => 'shortcode'
|
32 |
+
) );
|
33 |
+
}
|
34 |
+
}
|
35 |
+
|
core/side-attachment.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Side_Attachment extends P2P_Side_Post {
|
4 |
+
|
5 |
+
protected $item_type = 'P2P_Item_Attachment';
|
6 |
+
|
7 |
+
function __construct( $query_vars ) {
|
8 |
+
$this->query_vars = $query_vars;
|
9 |
+
|
10 |
+
$this->query_vars['post_type'] = array( 'attachment' );
|
11 |
+
}
|
12 |
+
|
13 |
+
function can_create_item() {
|
14 |
+
return false;
|
15 |
+
}
|
16 |
+
|
17 |
+
function get_base_qv( $q ) {
|
18 |
+
return array_merge( parent::get_base_qv( $q ), array(
|
19 |
+
'post_status' => 'inherit'
|
20 |
+
) );
|
21 |
+
}
|
22 |
+
}
|
23 |
+
|
core/side-post.php
ADDED
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Side_Post extends P2P_Side {
|
4 |
+
|
5 |
+
protected $item_type = 'P2P_Item_Post';
|
6 |
+
|
7 |
+
function __construct( $query_vars ) {
|
8 |
+
$this->query_vars = $query_vars;
|
9 |
+
}
|
10 |
+
|
11 |
+
public function get_object_type() {
|
12 |
+
return 'post';
|
13 |
+
}
|
14 |
+
|
15 |
+
public function first_post_type() {
|
16 |
+
return $this->query_vars['post_type'][0];
|
17 |
+
}
|
18 |
+
|
19 |
+
private function get_ptype() {
|
20 |
+
return get_post_type_object( $this->first_post_type() );
|
21 |
+
}
|
22 |
+
|
23 |
+
function get_base_qv( $q ) {
|
24 |
+
if ( isset( $q['post_type'] ) && 'any' != $q['post_type'] ) {
|
25 |
+
$common = array_intersect( $this->query_vars['post_type'], (array) $q['post_type'] );
|
26 |
+
|
27 |
+
if ( !$common )
|
28 |
+
unset( $q['post_type'] );
|
29 |
+
}
|
30 |
+
|
31 |
+
return array_merge( $this->query_vars, $q, array(
|
32 |
+
'suppress_filters' => false,
|
33 |
+
'ignore_sticky_posts' => true,
|
34 |
+
) );
|
35 |
+
}
|
36 |
+
|
37 |
+
function get_desc() {
|
38 |
+
return implode( ', ', array_map( array( $this, 'post_type_label' ), $this->query_vars['post_type'] ) );
|
39 |
+
}
|
40 |
+
|
41 |
+
private function post_type_label( $post_type ) {
|
42 |
+
$cpt = get_post_type_object( $post_type );
|
43 |
+
return $cpt ? $cpt->label : $post_type;
|
44 |
+
}
|
45 |
+
|
46 |
+
function get_title() {
|
47 |
+
return $this->get_ptype()->labels->name;
|
48 |
+
}
|
49 |
+
|
50 |
+
function get_labels() {
|
51 |
+
return $this->get_ptype()->labels;
|
52 |
+
}
|
53 |
+
|
54 |
+
function can_edit_connections() {
|
55 |
+
return current_user_can( $this->get_ptype()->cap->edit_posts );
|
56 |
+
}
|
57 |
+
|
58 |
+
function can_create_item() {
|
59 |
+
if ( count( $this->query_vars['post_type'] ) > 1 )
|
60 |
+
return false;
|
61 |
+
|
62 |
+
if ( count( $this->query_vars ) > 1 )
|
63 |
+
return false;
|
64 |
+
|
65 |
+
return true;
|
66 |
+
}
|
67 |
+
|
68 |
+
function translate_qv( $qv ) {
|
69 |
+
$map = array(
|
70 |
+
'include' => 'post__in',
|
71 |
+
'exclude' => 'post__not_in',
|
72 |
+
'search' => 's',
|
73 |
+
'page' => 'paged',
|
74 |
+
'per_page' => 'posts_per_page'
|
75 |
+
);
|
76 |
+
|
77 |
+
foreach ( $map as $old => $new )
|
78 |
+
if ( isset( $qv["p2p:$old"] ) )
|
79 |
+
$qv[$new] = _p2p_pluck( $qv, "p2p:$old" );
|
80 |
+
|
81 |
+
return $qv;
|
82 |
+
}
|
83 |
+
|
84 |
+
function do_query( $args ) {
|
85 |
+
return new WP_Query( $args );
|
86 |
+
}
|
87 |
+
|
88 |
+
function capture_query( $args ) {
|
89 |
+
$q = new WP_Query;
|
90 |
+
$q->_p2p_capture = true;
|
91 |
+
|
92 |
+
$q->query( $args );
|
93 |
+
|
94 |
+
return $q->_p2p_sql;
|
95 |
+
}
|
96 |
+
|
97 |
+
function get_list( $wp_query ) {
|
98 |
+
$list = new P2P_List( $wp_query->posts, $this->item_type );
|
99 |
+
|
100 |
+
$list->current_page = max( 1, $wp_query->get('paged') );
|
101 |
+
$list->total_pages = $wp_query->max_num_pages;
|
102 |
+
|
103 |
+
return $list;
|
104 |
+
}
|
105 |
+
|
106 |
+
function is_indeterminate( $side ) {
|
107 |
+
$common = array_intersect(
|
108 |
+
$this->query_vars['post_type'],
|
109 |
+
$side->query_vars['post_type']
|
110 |
+
);
|
111 |
+
|
112 |
+
return !empty( $common );
|
113 |
+
}
|
114 |
+
|
115 |
+
protected function recognize( $arg ) {
|
116 |
+
if ( is_object( $arg ) && !isset( $arg->post_type ) )
|
117 |
+
return false;
|
118 |
+
|
119 |
+
$post = get_post( $arg );
|
120 |
+
|
121 |
+
if ( !is_object( $post ) )
|
122 |
+
return false;
|
123 |
+
|
124 |
+
if ( !$this->recognize_post_type( $post->post_type ) )
|
125 |
+
return false;
|
126 |
+
|
127 |
+
return $post;
|
128 |
+
}
|
129 |
+
|
130 |
+
public function recognize_post_type( $post_type ) {
|
131 |
+
if ( !post_type_exists( $post_type ) )
|
132 |
+
return false;
|
133 |
+
|
134 |
+
return in_array( $post_type, $this->query_vars['post_type'] );
|
135 |
+
}
|
136 |
+
}
|
137 |
+
|
138 |
+
|
core/side-user.php
ADDED
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class P2P_Side_User extends P2P_Side {
|
4 |
+
|
5 |
+
protected $item_type = 'P2P_Item_User';
|
6 |
+
|
7 |
+
function __construct( $query_vars ) {
|
8 |
+
$this->query_vars = $query_vars;
|
9 |
+
}
|
10 |
+
|
11 |
+
function get_object_type() {
|
12 |
+
return 'user';
|
13 |
+
}
|
14 |
+
|
15 |
+
function get_desc() {
|
16 |
+
return __( 'Users', P2P_TEXTDOMAIN );
|
17 |
+
}
|
18 |
+
|
19 |
+
function get_title() {
|
20 |
+
return $this->get_desc();
|
21 |
+
}
|
22 |
+
|
23 |
+
function get_labels() {
|
24 |
+
return (object) array(
|
25 |
+
'singular_name' => __( 'User', P2P_TEXTDOMAIN ),
|
26 |
+
'search_items' => __( 'Search Users', P2P_TEXTDOMAIN ),
|
27 |
+
'not_found' => __( 'No users found.', P2P_TEXTDOMAIN ),
|
28 |
+
);
|
29 |
+
}
|
30 |
+
|
31 |
+
function can_edit_connections() {
|
32 |
+
return current_user_can( 'list_users' );
|
33 |
+
}
|
34 |
+
|
35 |
+
function can_create_item() {
|
36 |
+
return false;
|
37 |
+
}
|
38 |
+
|
39 |
+
function translate_qv( $qv ) {
|
40 |
+
if ( isset( $qv['p2p:include'] ) )
|
41 |
+
$qv['include'] = _p2p_pluck( $qv, 'p2p:include' );
|
42 |
+
|
43 |
+
if ( isset( $qv['p2p:exclude'] ) )
|
44 |
+
$qv['exclude'] = _p2p_pluck( $qv, 'p2p:exclude' );
|
45 |
+
|
46 |
+
if ( isset( $qv['p2p:search'] ) && $qv['p2p:search'] )
|
47 |
+
$qv['search'] = '*' . _p2p_pluck( $qv, 'p2p:search' ) . '*';
|
48 |
+
|
49 |
+
if ( isset( $qv['p2p:page'] ) && $qv['p2p:page'] > 0 ) {
|
50 |
+
if ( isset( $qv['p2p:per_page'] ) && $qv['p2p:per_page'] > 0 ) {
|
51 |
+
$qv['number'] = $qv['p2p:per_page'];
|
52 |
+
$qv['offset'] = $qv['p2p:per_page'] * ( $qv['p2p:page'] - 1 );
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
return $qv;
|
57 |
+
}
|
58 |
+
|
59 |
+
function do_query( $args ) {
|
60 |
+
return new WP_User_Query( $args );
|
61 |
+
}
|
62 |
+
|
63 |
+
function capture_query( $args ) {
|
64 |
+
$args['count_total'] = false;
|
65 |
+
|
66 |
+
$uq = new WP_User_Query;
|
67 |
+
$uq->_p2p_capture = true; // needed by P2P_URL_Query
|
68 |
+
|
69 |
+
// see http://core.trac.wordpress.org/ticket/21119
|
70 |
+
$uq->query_vars = wp_parse_args( $args, array(
|
71 |
+
'blog_id' => $GLOBALS['blog_id'],
|
72 |
+
'role' => '',
|
73 |
+
'meta_key' => '',
|
74 |
+
'meta_value' => '',
|
75 |
+
'meta_compare' => '',
|
76 |
+
'include' => array(),
|
77 |
+
'exclude' => array(),
|
78 |
+
'search' => '',
|
79 |
+
'search_columns' => array(),
|
80 |
+
'orderby' => 'login',
|
81 |
+
'order' => 'ASC',
|
82 |
+
'offset' => '',
|
83 |
+
'number' => '',
|
84 |
+
'count_total' => true,
|
85 |
+
'fields' => 'all',
|
86 |
+
'who' => ''
|
87 |
+
) );
|
88 |
+
|
89 |
+
$uq->prepare_query();
|
90 |
+
|
91 |
+
return "SELECT $uq->query_fields $uq->query_from $uq->query_where $uq->query_orderby $uq->query_limit";
|
92 |
+
}
|
93 |
+
|
94 |
+
function get_list( $query ) {
|
95 |
+
$list = new P2P_List( $query->get_results(), $this->item_type );
|
96 |
+
|
97 |
+
$qv = $query->query_vars;
|
98 |
+
|
99 |
+
if ( isset( $qv['p2p:page'] ) ) {
|
100 |
+
$list->current_page = $qv['p2p:page'];
|
101 |
+
$list->total_pages = ceil( $query->get_total() / $qv['p2p:per_page'] );
|
102 |
+
}
|
103 |
+
|
104 |
+
return $list;
|
105 |
+
}
|
106 |
+
|
107 |
+
function is_indeterminate( $side ) {
|
108 |
+
return true;
|
109 |
+
}
|
110 |
+
|
111 |
+
function get_base_qv( $q ) {
|
112 |
+
return array_merge( $this->query_vars, $q );
|
113 |
+
}
|
114 |
+
|
115 |
+
protected function recognize( $arg ) {
|
116 |
+
if ( is_a( $arg, 'WP_User' ) )
|
117 |
+
return $arg;
|
118 |
+
|
119 |
+
return get_user_by( 'id', $arg );
|
120 |
+
}
|
121 |
+
}
|
122 |
+
|
core/side.php
CHANGED
@@ -52,280 +52,3 @@ abstract class P2P_Side {
|
|
52 |
abstract protected function recognize( $arg );
|
53 |
}
|
54 |
|
55 |
-
|
56 |
-
class P2P_Side_Post extends P2P_Side {
|
57 |
-
|
58 |
-
protected $item_type = 'P2P_Item_Post';
|
59 |
-
|
60 |
-
function __construct( $query_vars ) {
|
61 |
-
$this->query_vars = $query_vars;
|
62 |
-
}
|
63 |
-
|
64 |
-
public function get_object_type() {
|
65 |
-
return 'post';
|
66 |
-
}
|
67 |
-
|
68 |
-
public function first_post_type() {
|
69 |
-
return $this->query_vars['post_type'][0];
|
70 |
-
}
|
71 |
-
|
72 |
-
private function get_ptype() {
|
73 |
-
return get_post_type_object( $this->first_post_type() );
|
74 |
-
}
|
75 |
-
|
76 |
-
function get_base_qv( $q ) {
|
77 |
-
if ( isset( $q['post_type'] ) && 'any' != $q['post_type'] ) {
|
78 |
-
$common = array_intersect( $this->query_vars['post_type'], (array) $q['post_type'] );
|
79 |
-
|
80 |
-
if ( !$common )
|
81 |
-
unset( $q['post_type'] );
|
82 |
-
}
|
83 |
-
|
84 |
-
return array_merge( $this->query_vars, $q, array(
|
85 |
-
'suppress_filters' => false,
|
86 |
-
'ignore_sticky_posts' => true,
|
87 |
-
) );
|
88 |
-
}
|
89 |
-
|
90 |
-
function get_desc() {
|
91 |
-
return implode( ', ', array_map( array( $this, 'post_type_label' ), $this->query_vars['post_type'] ) );
|
92 |
-
}
|
93 |
-
|
94 |
-
private function post_type_label( $post_type ) {
|
95 |
-
$cpt = get_post_type_object( $post_type );
|
96 |
-
return $cpt ? $cpt->label : $post_type;
|
97 |
-
}
|
98 |
-
|
99 |
-
function get_title() {
|
100 |
-
return $this->get_ptype()->labels->name;
|
101 |
-
}
|
102 |
-
|
103 |
-
function get_labels() {
|
104 |
-
return $this->get_ptype()->labels;
|
105 |
-
}
|
106 |
-
|
107 |
-
function can_edit_connections() {
|
108 |
-
return current_user_can( $this->get_ptype()->cap->edit_posts );
|
109 |
-
}
|
110 |
-
|
111 |
-
function can_create_item() {
|
112 |
-
if ( count( $this->query_vars['post_type'] ) > 1 )
|
113 |
-
return false;
|
114 |
-
|
115 |
-
if ( count( $this->query_vars ) > 1 )
|
116 |
-
return false;
|
117 |
-
|
118 |
-
return true;
|
119 |
-
}
|
120 |
-
|
121 |
-
function translate_qv( $qv ) {
|
122 |
-
$map = array(
|
123 |
-
'include' => 'post__in',
|
124 |
-
'exclude' => 'post__not_in',
|
125 |
-
'search' => 's',
|
126 |
-
'page' => 'paged',
|
127 |
-
'per_page' => 'posts_per_page'
|
128 |
-
);
|
129 |
-
|
130 |
-
foreach ( $map as $old => $new )
|
131 |
-
if ( isset( $qv["p2p:$old"] ) )
|
132 |
-
$qv[$new] = _p2p_pluck( $qv, "p2p:$old" );
|
133 |
-
|
134 |
-
return $qv;
|
135 |
-
}
|
136 |
-
|
137 |
-
function do_query( $args ) {
|
138 |
-
return new WP_Query( $args );
|
139 |
-
}
|
140 |
-
|
141 |
-
function capture_query( $args ) {
|
142 |
-
$q = new WP_Query;
|
143 |
-
$q->_p2p_capture = true;
|
144 |
-
|
145 |
-
$q->query( $args );
|
146 |
-
|
147 |
-
return $q->_p2p_sql;
|
148 |
-
}
|
149 |
-
|
150 |
-
function get_list( $wp_query ) {
|
151 |
-
$list = new P2P_List( $wp_query->posts, $this->item_type );
|
152 |
-
|
153 |
-
$list->current_page = max( 1, $wp_query->get('paged') );
|
154 |
-
$list->total_pages = $wp_query->max_num_pages;
|
155 |
-
|
156 |
-
return $list;
|
157 |
-
}
|
158 |
-
|
159 |
-
function is_indeterminate( $side ) {
|
160 |
-
$common = array_intersect(
|
161 |
-
$this->query_vars['post_type'],
|
162 |
-
$side->query_vars['post_type']
|
163 |
-
);
|
164 |
-
|
165 |
-
return !empty( $common );
|
166 |
-
}
|
167 |
-
|
168 |
-
protected function recognize( $arg ) {
|
169 |
-
if ( is_object( $arg ) && !isset( $arg->post_type ) )
|
170 |
-
return false;
|
171 |
-
|
172 |
-
$post = get_post( $arg );
|
173 |
-
|
174 |
-
if ( !is_object( $post ) )
|
175 |
-
return false;
|
176 |
-
|
177 |
-
if ( !$this->recognize_post_type( $post->post_type ) )
|
178 |
-
return false;
|
179 |
-
|
180 |
-
return $post;
|
181 |
-
}
|
182 |
-
|
183 |
-
public function recognize_post_type( $post_type ) {
|
184 |
-
if ( !post_type_exists( $post_type ) )
|
185 |
-
return false;
|
186 |
-
|
187 |
-
return in_array( $post_type, $this->query_vars['post_type'] );
|
188 |
-
}
|
189 |
-
}
|
190 |
-
|
191 |
-
|
192 |
-
class P2P_Side_Attachment extends P2P_Side_Post {
|
193 |
-
|
194 |
-
protected $item_type = 'P2P_Item_Attachment';
|
195 |
-
|
196 |
-
function __construct( $query_vars ) {
|
197 |
-
$this->query_vars = $query_vars;
|
198 |
-
|
199 |
-
$this->query_vars['post_type'] = array( 'attachment' );
|
200 |
-
}
|
201 |
-
|
202 |
-
function can_create_item() {
|
203 |
-
return false;
|
204 |
-
}
|
205 |
-
|
206 |
-
function get_base_qv( $q ) {
|
207 |
-
return array_merge( parent::get_base_qv( $q ), array(
|
208 |
-
'post_status' => 'inherit'
|
209 |
-
) );
|
210 |
-
}
|
211 |
-
}
|
212 |
-
|
213 |
-
|
214 |
-
class P2P_Side_User extends P2P_Side {
|
215 |
-
|
216 |
-
protected $item_type = 'P2P_Item_User';
|
217 |
-
|
218 |
-
function __construct( $query_vars ) {
|
219 |
-
$this->query_vars = $query_vars;
|
220 |
-
}
|
221 |
-
|
222 |
-
function get_object_type() {
|
223 |
-
return 'user';
|
224 |
-
}
|
225 |
-
|
226 |
-
function get_desc() {
|
227 |
-
return __( 'Users', P2P_TEXTDOMAIN );
|
228 |
-
}
|
229 |
-
|
230 |
-
function get_title() {
|
231 |
-
return $this->get_desc();
|
232 |
-
}
|
233 |
-
|
234 |
-
function get_labels() {
|
235 |
-
return (object) array(
|
236 |
-
'singular_name' => __( 'User', P2P_TEXTDOMAIN ),
|
237 |
-
'search_items' => __( 'Search Users', P2P_TEXTDOMAIN ),
|
238 |
-
'not_found' => __( 'No users found.', P2P_TEXTDOMAIN ),
|
239 |
-
);
|
240 |
-
}
|
241 |
-
|
242 |
-
function can_edit_connections() {
|
243 |
-
return current_user_can( 'list_users' );
|
244 |
-
}
|
245 |
-
|
246 |
-
function can_create_item() {
|
247 |
-
return false;
|
248 |
-
}
|
249 |
-
|
250 |
-
function translate_qv( $qv ) {
|
251 |
-
if ( isset( $qv['p2p:include'] ) )
|
252 |
-
$qv['include'] = _p2p_pluck( $qv, 'p2p:include' );
|
253 |
-
|
254 |
-
if ( isset( $qv['p2p:exclude'] ) )
|
255 |
-
$qv['exclude'] = _p2p_pluck( $qv, 'p2p:exclude' );
|
256 |
-
|
257 |
-
if ( isset( $qv['p2p:search'] ) && $qv['p2p:search'] )
|
258 |
-
$qv['search'] = '*' . _p2p_pluck( $qv, 'p2p:search' ) . '*';
|
259 |
-
|
260 |
-
if ( isset( $qv['p2p:page'] ) && $qv['p2p:page'] > 0 ) {
|
261 |
-
$qv['number'] = $qv['p2p:per_page'];
|
262 |
-
$qv['offset'] = $qv['p2p:per_page'] * ( $qv['p2p:page'] - 1 );
|
263 |
-
}
|
264 |
-
|
265 |
-
return $qv;
|
266 |
-
}
|
267 |
-
|
268 |
-
function do_query( $args ) {
|
269 |
-
return new WP_User_Query( $args );
|
270 |
-
}
|
271 |
-
|
272 |
-
function capture_query( $args ) {
|
273 |
-
$args['count_total'] = false;
|
274 |
-
|
275 |
-
$uq = new WP_User_Query;
|
276 |
-
$uq->_p2p_capture = true; // needed by P2P_URL_Query
|
277 |
-
|
278 |
-
// see http://core.trac.wordpress.org/ticket/21119
|
279 |
-
$uq->query_vars = wp_parse_args( $args, array(
|
280 |
-
'blog_id' => $GLOBALS['blog_id'],
|
281 |
-
'role' => '',
|
282 |
-
'meta_key' => '',
|
283 |
-
'meta_value' => '',
|
284 |
-
'meta_compare' => '',
|
285 |
-
'include' => array(),
|
286 |
-
'exclude' => array(),
|
287 |
-
'search' => '',
|
288 |
-
'search_columns' => array(),
|
289 |
-
'orderby' => 'login',
|
290 |
-
'order' => 'ASC',
|
291 |
-
'offset' => '',
|
292 |
-
'number' => '',
|
293 |
-
'count_total' => true,
|
294 |
-
'fields' => 'all',
|
295 |
-
'who' => ''
|
296 |
-
) );
|
297 |
-
|
298 |
-
$uq->prepare_query();
|
299 |
-
|
300 |
-
return "SELECT $uq->query_fields $uq->query_from $uq->query_where $uq->query_orderby $uq->query_limit";
|
301 |
-
}
|
302 |
-
|
303 |
-
function get_list( $query ) {
|
304 |
-
$list = new P2P_List( $query->get_results(), $this->item_type );
|
305 |
-
|
306 |
-
$qv = $query->query_vars;
|
307 |
-
|
308 |
-
if ( isset( $qv['p2p:page'] ) ) {
|
309 |
-
$list->current_page = $qv['p2p:page'];
|
310 |
-
$list->total_pages = ceil( $query->get_total() / $qv['p2p:per_page'] );
|
311 |
-
}
|
312 |
-
|
313 |
-
return $list;
|
314 |
-
}
|
315 |
-
|
316 |
-
function is_indeterminate( $side ) {
|
317 |
-
return true;
|
318 |
-
}
|
319 |
-
|
320 |
-
function get_base_qv( $q ) {
|
321 |
-
return array_merge( $this->query_vars, $q );
|
322 |
-
}
|
323 |
-
|
324 |
-
protected function recognize( $arg ) {
|
325 |
-
if ( is_a( $arg, 'WP_User' ) )
|
326 |
-
return $arg;
|
327 |
-
|
328 |
-
return get_user_by( 'id', $arg );
|
329 |
-
}
|
330 |
-
}
|
331 |
-
|
52 |
abstract protected function recognize( $arg );
|
53 |
}
|
54 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/storage.php
CHANGED
@@ -60,5 +60,3 @@ class P2P_Storage {
|
|
60 |
}
|
61 |
}
|
62 |
|
63 |
-
P2P_Storage::init();
|
64 |
-
|
60 |
}
|
61 |
}
|
62 |
|
|
|
|
core/url-query.php
CHANGED
@@ -8,36 +8,12 @@ class P2P_URL_Query {
|
|
8 |
|
9 |
static function init() {
|
10 |
add_filter( 'query_vars', array( __CLASS__, 'query_vars' ) );
|
11 |
-
|
12 |
-
if ( is_admin() )
|
13 |
-
add_action( 'pre_user_query', array( __CLASS__, 'user_query' ), 9 );
|
14 |
}
|
15 |
|
16 |
// Make the query vars public
|
17 |
static function query_vars( $public_qv ) {
|
18 |
return array_merge( $public_qv, self::get_custom_qv() );
|
19 |
}
|
20 |
-
|
21 |
-
// Add the query vars to the global user query (on the user admin screen)
|
22 |
-
static function user_query( $query ) {
|
23 |
-
if ( !function_exists( 'get_current_screen' ) )
|
24 |
-
return;
|
25 |
-
|
26 |
-
$current_screen = get_current_screen();
|
27 |
-
|
28 |
-
if ( $current_screen && 'users' != $current_screen->id )
|
29 |
-
return;
|
30 |
-
|
31 |
-
if ( isset( $query->_p2p_capture ) )
|
32 |
-
return;
|
33 |
-
|
34 |
-
// Don't overwrite existing P2P query
|
35 |
-
if ( isset( $query->query_vars['connected_type'] ) )
|
36 |
-
return;
|
37 |
-
|
38 |
-
_p2p_append( $query->query_vars, wp_array_slice_assoc( $_GET,
|
39 |
-
self::get_custom_qv() ) );
|
40 |
-
}
|
41 |
}
|
42 |
|
43 |
P2P_URL_Query::init();
|
8 |
|
9 |
static function init() {
|
10 |
add_filter( 'query_vars', array( __CLASS__, 'query_vars' ) );
|
|
|
|
|
|
|
11 |
}
|
12 |
|
13 |
// Make the query vars public
|
14 |
static function query_vars( $public_qv ) {
|
15 |
return array_merge( $public_qv, self::get_custom_qv() );
|
16 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
}
|
18 |
|
19 |
P2P_URL_Query::init();
|
core/util.php
CHANGED
@@ -1,5 +1,20 @@
|
|
1 |
<?php
|
2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
/** @internal */
|
4 |
function _p2p_expand_direction( $direction ) {
|
5 |
if ( !$direction )
|
@@ -107,3 +122,63 @@ function _p2p_first( $args ) {
|
|
107 |
return reset( $args );
|
108 |
}
|
109 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
<?php
|
2 |
|
3 |
+
function p2p_list_cluster( $items, $callback ) {
|
4 |
+
$groups = array();
|
5 |
+
|
6 |
+
foreach ( $items as $item ) {
|
7 |
+
$key = $callback( $item );
|
8 |
+
|
9 |
+
if ( null === $key )
|
10 |
+
continue;
|
11 |
+
|
12 |
+
$groups[ $key ][] = $item;
|
13 |
+
}
|
14 |
+
|
15 |
+
return $groups;
|
16 |
+
}
|
17 |
+
|
18 |
/** @internal */
|
19 |
function _p2p_expand_direction( $direction ) {
|
20 |
if ( !$direction )
|
122 |
return reset( $args );
|
123 |
}
|
124 |
|
125 |
+
/** @internal */
|
126 |
+
function _p2p_get_other_id( $item ) {
|
127 |
+
if ( $item->ID == $item->p2p_from )
|
128 |
+
return $item->p2p_to;
|
129 |
+
|
130 |
+
if ( $item->ID == $item->p2p_to )
|
131 |
+
return $item->p2p_from;
|
132 |
+
|
133 |
+
trigger_error( "Corrupted data for item $inner_item->ID", E_USER_WARNING );
|
134 |
+
}
|
135 |
+
|
136 |
+
/** @internal */
|
137 |
+
function _p2p_get_list( $args ) {
|
138 |
+
$ctype = p2p_type( $args['ctype'] );
|
139 |
+
if ( !$ctype ) {
|
140 |
+
trigger_error( sprintf( "Unregistered connection type '%s'.", $ctype ), E_USER_WARNING );
|
141 |
+
return '';
|
142 |
+
}
|
143 |
+
|
144 |
+
$directed = $ctype->find_direction( $args['item'] );
|
145 |
+
if ( !$directed )
|
146 |
+
return '';
|
147 |
+
|
148 |
+
$context = $args['context'];
|
149 |
+
|
150 |
+
$extra_qv = array(
|
151 |
+
'p2p:per_page' => -1,
|
152 |
+
'p2p:context' => $context
|
153 |
+
);
|
154 |
+
|
155 |
+
$connected = call_user_func( array( $directed, $args['method'] ), $args['item'], $extra_qv, 'abstract' );
|
156 |
+
|
157 |
+
switch ( $args['mode'] ) {
|
158 |
+
case 'inline':
|
159 |
+
$render_args = array(
|
160 |
+
'separator' => ', '
|
161 |
+
);
|
162 |
+
break;
|
163 |
+
|
164 |
+
case 'ol':
|
165 |
+
$render_args = array(
|
166 |
+
'before_list' => '<ol id="' . $ctype->name . '_list">',
|
167 |
+
'after_list' => '</ol>',
|
168 |
+
);
|
169 |
+
break;
|
170 |
+
|
171 |
+
case 'ul':
|
172 |
+
default:
|
173 |
+
$render_args = array(
|
174 |
+
'before_list' => '<ul id="' . $ctype->name . '_list">',
|
175 |
+
'after_list' => '</ul>',
|
176 |
+
);
|
177 |
+
break;
|
178 |
+
}
|
179 |
+
|
180 |
+
$render_args['echo'] = false;
|
181 |
+
|
182 |
+
return apply_filters( "p2p_{$context}_html", $connected->render( $render_args ), $connected, $directed, $args['mode'] );
|
183 |
+
}
|
184 |
+
|
core/{extra.php → widget.php}
RENAMED
@@ -85,87 +85,3 @@ class P2P_Widget extends scbWidget {
|
|
85 |
}
|
86 |
}
|
87 |
|
88 |
-
|
89 |
-
class P2P_Shortcodes {
|
90 |
-
|
91 |
-
static function init() {
|
92 |
-
add_shortcode( 'p2p_connected', array( __CLASS__, 'connected' ) );
|
93 |
-
add_shortcode( 'p2p_related', array( __CLASS__, 'related' ) );
|
94 |
-
}
|
95 |
-
|
96 |
-
static function connected( $attr ) {
|
97 |
-
return self::get_list( $attr, 'get_connected' );
|
98 |
-
}
|
99 |
-
|
100 |
-
static function related( $attr ) {
|
101 |
-
return self::get_list( $attr, 'get_related' );
|
102 |
-
}
|
103 |
-
|
104 |
-
private static function get_list( $attr, $method ) {
|
105 |
-
global $post;
|
106 |
-
|
107 |
-
$attr = shortcode_atts( array(
|
108 |
-
'type' => '',
|
109 |
-
'mode' => 'ul',
|
110 |
-
), $attr );
|
111 |
-
|
112 |
-
return _p2p_get_list( array(
|
113 |
-
'ctype' => $attr['type'],
|
114 |
-
'method' => $method,
|
115 |
-
'item' => $post,
|
116 |
-
'mode' => $attr['mode'],
|
117 |
-
'context' => 'shortcode'
|
118 |
-
) );
|
119 |
-
}
|
120 |
-
}
|
121 |
-
|
122 |
-
|
123 |
-
/** @internal */
|
124 |
-
function _p2p_get_list( $args ) {
|
125 |
-
extract( $args );
|
126 |
-
|
127 |
-
$ctype = p2p_type( $ctype );
|
128 |
-
if ( !$ctype ) {
|
129 |
-
trigger_error( sprintf( "Unregistered connection type '%s'.", $ctype ), E_USER_WARNING );
|
130 |
-
return '';
|
131 |
-
}
|
132 |
-
|
133 |
-
$directed = $ctype->find_direction( $item );
|
134 |
-
if ( !$directed )
|
135 |
-
return '';
|
136 |
-
|
137 |
-
$extra_qv = array(
|
138 |
-
'p2p:per_page' => -1,
|
139 |
-
'p2p:context' => $context
|
140 |
-
);
|
141 |
-
|
142 |
-
$connected = $directed->$method( $item, $extra_qv, 'abstract' );
|
143 |
-
|
144 |
-
switch ( $mode ) {
|
145 |
-
case 'inline':
|
146 |
-
$args = array(
|
147 |
-
'separator' => ', '
|
148 |
-
);
|
149 |
-
break;
|
150 |
-
|
151 |
-
case 'ol':
|
152 |
-
$args = array(
|
153 |
-
'before_list' => '<ol id="' . $ctype->name . '_list">',
|
154 |
-
'after_list' => '</ol>',
|
155 |
-
);
|
156 |
-
break;
|
157 |
-
|
158 |
-
case 'ul':
|
159 |
-
default:
|
160 |
-
$args = array(
|
161 |
-
'before_list' => '<ul id="' . $ctype->name . '_list">',
|
162 |
-
'after_list' => '</ul>',
|
163 |
-
);
|
164 |
-
break;
|
165 |
-
}
|
166 |
-
|
167 |
-
$args['echo'] = false;
|
168 |
-
|
169 |
-
return apply_filters( "p2p_{$context}_html", $connected->render( $args ), $connected, $directed, $mode );
|
170 |
-
}
|
171 |
-
|
85 |
}
|
86 |
}
|
87 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
posts-to-posts.php
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
/*
|
3 |
Plugin Name: Posts 2 Posts
|
4 |
Description: Create many-to-many relationships between all types of posts.
|
5 |
-
Version: 1.
|
6 |
Author: scribu
|
7 |
Author URI: http://scribu.net/
|
8 |
Plugin URI: http://scribu.net/wordpress/posts-to-posts
|
@@ -10,7 +10,7 @@ Text Domain: posts-to-posts
|
|
10 |
Domain Path: /lang
|
11 |
*/
|
12 |
|
13 |
-
define( 'P2P_PLUGIN_VERSION', '1.
|
14 |
|
15 |
define( 'P2P_TEXTDOMAIN', 'posts-to-posts' );
|
16 |
|
@@ -21,37 +21,40 @@ function _p2p_load() {
|
|
21 |
|
22 |
load_plugin_textdomain( P2P_TEXTDOMAIN, '', basename( $base ) . '/lang' );
|
23 |
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
|
|
|
|
|
|
|
|
30 |
|
31 |
P2P_Widget::init();
|
32 |
P2P_Shortcodes::init();
|
33 |
|
34 |
-
if ( is_admin() )
|
35 |
-
|
36 |
-
'mustache', 'factory',
|
37 |
-
'box-factory', 'box', 'fields',
|
38 |
-
'column-factory', 'column',
|
39 |
-
'tools'
|
40 |
-
) );
|
41 |
-
}
|
42 |
|
43 |
register_uninstall_hook( __FILE__, array( 'P2P_Storage', 'uninstall' ) );
|
44 |
}
|
45 |
scb_init( '_p2p_load' );
|
46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
function _p2p_init() {
|
48 |
// Safe hook for calling p2p_register_connection_type()
|
49 |
do_action( 'p2p_init' );
|
50 |
}
|
51 |
add_action( 'wp_loaded', '_p2p_init' );
|
52 |
|
53 |
-
function _p2p_load_files( $dir, $files ) {
|
54 |
-
foreach ( $files as $file )
|
55 |
-
require_once "$dir/$file.php";
|
56 |
-
}
|
57 |
-
|
2 |
/*
|
3 |
Plugin Name: Posts 2 Posts
|
4 |
Description: Create many-to-many relationships between all types of posts.
|
5 |
+
Version: 1.5
|
6 |
Author: scribu
|
7 |
Author URI: http://scribu.net/
|
8 |
Plugin URI: http://scribu.net/wordpress/posts-to-posts
|
10 |
Domain Path: /lang
|
11 |
*/
|
12 |
|
13 |
+
define( 'P2P_PLUGIN_VERSION', '1.5' );
|
14 |
|
15 |
define( 'P2P_TEXTDOMAIN', 'posts-to-posts' );
|
16 |
|
21 |
|
22 |
load_plugin_textdomain( P2P_TEXTDOMAIN, '', basename( $base ) . '/lang' );
|
23 |
|
24 |
+
require $base . '/core/util.php';
|
25 |
+
require $base . '/core/api.php';
|
26 |
+
require $base . '/autoload.php';
|
27 |
+
|
28 |
+
P2P_Autoload::register( 'P2P_', $base . '/core' );
|
29 |
+
|
30 |
+
P2P_Storage::init();
|
31 |
+
|
32 |
+
P2P_Query_Post::init();
|
33 |
+
P2P_Query_User::init();
|
34 |
|
35 |
P2P_Widget::init();
|
36 |
P2P_Shortcodes::init();
|
37 |
|
38 |
+
if ( is_admin() )
|
39 |
+
_p2p_load_admin();
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
|
41 |
register_uninstall_hook( __FILE__, array( 'P2P_Storage', 'uninstall' ) );
|
42 |
}
|
43 |
scb_init( '_p2p_load' );
|
44 |
|
45 |
+
function _p2p_load_admin() {
|
46 |
+
P2P_Autoload::register( 'P2P_', dirname( __FILE__ ) . '/admin' );
|
47 |
+
|
48 |
+
new P2P_Box_Factory;
|
49 |
+
new P2P_Column_Factory;
|
50 |
+
new P2P_Dropdown_Factory;
|
51 |
+
|
52 |
+
new P2P_Tools_Page;
|
53 |
+
}
|
54 |
+
|
55 |
function _p2p_init() {
|
56 |
// Safe hook for calling p2p_register_connection_type()
|
57 |
do_action( 'p2p_init' );
|
58 |
}
|
59 |
add_action( 'wp_loaded', '_p2p_init' );
|
60 |
|
|
|
|
|
|
|
|
|
|
readme.txt
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
=== Posts 2 Posts ===
|
2 |
Contributors: scribu, ciobi
|
3 |
Tags: connections, custom post types, relationships, many-to-many, users
|
4 |
-
Requires at least: 3.
|
5 |
Tested up to: 3.5
|
6 |
-
Stable tag: 1.
|
7 |
License: GPLv2 or later
|
8 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
|
@@ -53,6 +53,14 @@ Make sure your host is running PHP 5. The only foolproof way to do this is to ad
|
|
53 |
|
54 |
== Changelog ==
|
55 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
= 1.4.3 =
|
57 |
* various bug fixes
|
58 |
* added 'inline' mode for shortcodes
|
@@ -248,4 +256,3 @@ Make sure your host is running PHP 5. The only foolproof way to do this is to ad
|
|
248 |
= 0.1 =
|
249 |
* initial release
|
250 |
* [more info](http://scribu.net/wordpress/posts-to-posts/p2p-0-1.html)
|
251 |
-
|
1 |
=== Posts 2 Posts ===
|
2 |
Contributors: scribu, ciobi
|
3 |
Tags: connections, custom post types, relationships, many-to-many, users
|
4 |
+
Requires at least: 3.5
|
5 |
Tested up to: 3.5
|
6 |
+
Stable tag: 1.5
|
7 |
License: GPLv2 or later
|
8 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
|
53 |
|
54 |
== Changelog ==
|
55 |
|
56 |
+
= 1.5 =
|
57 |
+
* added [admin dropdowns](https://github.com/scribu/wp-posts-to-posts/wiki/Admin-dropdown-display)
|
58 |
+
* fixed SQL error related to user connections
|
59 |
+
* fixed 'labels' handling and added 'column_title' subkey
|
60 |
+
* refactor metabox JavaScript using Backbone.js
|
61 |
+
* lazy-load connection candidates, for faster page loads
|
62 |
+
* lazy-load PHP classes using `spl_register_autoload()`
|
63 |
+
|
64 |
= 1.4.3 =
|
65 |
* various bug fixes
|
66 |
* added 'inline' mode for shortcodes
|
256 |
= 0.1 =
|
257 |
* initial release
|
258 |
* [more info](http://scribu.net/wordpress/posts-to-posts/p2p-0-1.html)
|
|
scb/AdminPage.php
CHANGED
@@ -1,13 +1,15 @@
|
|
1 |
<?php
|
2 |
|
3 |
-
|
4 |
-
|
|
|
5 |
abstract class scbAdminPage {
|
6 |
/** Page args
|
7 |
* $page_title string (mandatory)
|
8 |
* $parent (string) (default: options-general.php)
|
9 |
* $capability (string) (default: 'manage_options')
|
10 |
* $menu_title (string) (default: $page_title)
|
|
|
11 |
* $page_slug (string) (default: sanitized $page_title)
|
12 |
* $toplevel (string) If not empty, will create a new top level menu (for expected values see http://codex.wordpress.org/Administration_Menus#Using_add_submenu_page)
|
13 |
* - $icon_url (string) URL to an icon for the top level menu
|
@@ -40,6 +42,13 @@ abstract class scbAdminPage {
|
|
40 |
|
41 |
private static $registered = array();
|
42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
static function register( $class, $file, $options = null ) {
|
44 |
if ( isset( self::$registered[$class] ) )
|
45 |
return false;
|
@@ -51,6 +60,12 @@ abstract class scbAdminPage {
|
|
51 |
return true;
|
52 |
}
|
53 |
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
static function replace( $old_class, $new_class ) {
|
55 |
if ( ! isset( self::$registered[$old_class] ) )
|
56 |
return false;
|
@@ -61,6 +76,11 @@ abstract class scbAdminPage {
|
|
61 |
return true;
|
62 |
}
|
63 |
|
|
|
|
|
|
|
|
|
|
|
64 |
static function remove( $class ) {
|
65 |
if ( ! isset( self::$registered[$class] ) )
|
66 |
return false;
|
@@ -79,7 +99,12 @@ abstract class scbAdminPage {
|
|
79 |
// ____________MAIN METHODS____________
|
80 |
|
81 |
|
82 |
-
|
|
|
|
|
|
|
|
|
|
|
83 |
function __construct( $file = false, $options = null ) {
|
84 |
if ( is_a( $options, 'scbOptions' ) )
|
85 |
$this->options = $options;
|
@@ -105,7 +130,9 @@ abstract class scbAdminPage {
|
|
105 |
}
|
106 |
}
|
107 |
|
108 |
-
|
|
|
|
|
109 |
function setup(){}
|
110 |
|
111 |
/**
|
@@ -115,43 +142,64 @@ abstract class scbAdminPage {
|
|
115 |
*/
|
116 |
function page_loaded() {}
|
117 |
|
118 |
-
|
119 |
-
|
|
|
|
|
120 |
function page_head(){}
|
121 |
|
122 |
-
|
123 |
-
|
|
|
|
|
124 |
function page_help(){}
|
125 |
|
126 |
-
|
|
|
|
|
127 |
function page_header() {
|
128 |
echo "<div class='wrap'>\n";
|
129 |
screen_icon( $this->args['screen_icon'] );
|
130 |
-
echo html(
|
131 |
}
|
132 |
|
133 |
-
|
|
|
|
|
134 |
abstract function page_content();
|
135 |
|
136 |
-
|
|
|
|
|
137 |
function page_footer() {
|
138 |
echo "</div>\n";
|
139 |
}
|
140 |
|
141 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
142 |
function validate( $new_data, $old_data ) {
|
143 |
return $new_data;
|
144 |
}
|
145 |
|
146 |
-
|
|
|
|
|
|
|
|
|
147 |
function form_handler() {
|
148 |
-
if ( empty( $_POST['action'] ) )
|
149 |
return false;
|
150 |
|
151 |
check_admin_referer( $this->nonce );
|
152 |
|
153 |
if ( !isset($this->options) ) {
|
154 |
-
trigger_error('options handler not set', E_USER_WARNING);
|
155 |
return false;
|
156 |
}
|
157 |
|
@@ -164,10 +212,17 @@ abstract class scbAdminPage {
|
|
164 |
$this->options->set( $new_data );
|
165 |
|
166 |
$this->admin_msg();
|
|
|
|
|
167 |
}
|
168 |
|
169 |
-
|
170 |
-
|
|
|
|
|
|
|
|
|
|
|
171 |
if ( empty( $msg ) )
|
172 |
$msg = __( 'Settings <strong>saved</strong>.', $this->textdomain );
|
173 |
|
@@ -178,52 +233,47 @@ abstract class scbAdminPage {
|
|
178 |
// ____________UTILITIES____________
|
179 |
|
180 |
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
if ( ! $ajax )
|
192 |
-
$class .= ' no-ajax';
|
193 |
-
}
|
194 |
-
else {
|
195 |
-
if ( empty( $value ) )
|
196 |
-
$value = __( 'Save Changes', $this->textdomain );
|
197 |
-
}
|
198 |
-
|
199 |
-
$input_args = array(
|
200 |
-
'type' => 'submit',
|
201 |
-
'name' => $action,
|
202 |
-
'value' => $value,
|
203 |
-
'extra' => '',
|
204 |
-
'desc' => false,
|
205 |
-
'wrap' => html( 'p class="submit"', scbForms::TOKEN )
|
206 |
-
);
|
207 |
|
208 |
-
|
209 |
-
|
|
|
|
|
|
|
|
|
210 |
|
211 |
-
return
|
212 |
}
|
213 |
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
227 |
function form_wrap( $content, $submit_button = true ) {
|
228 |
if ( is_array( $submit_button ) ) {
|
229 |
$content .= $this->submit_button( $submit_button );
|
@@ -231,15 +281,24 @@ abstract class scbAdminPage {
|
|
231 |
$content .= $this->submit_button();
|
232 |
} elseif ( false !== strpos( $submit_button, '<input' ) ) {
|
233 |
$content .= $submit_button;
|
|
|
|
|
234 |
} elseif ( false !== $submit_button ) {
|
235 |
$button_args = array_slice( func_get_args(), 1 );
|
236 |
-
$content
|
237 |
}
|
238 |
|
239 |
return scbForms::form_wrap( $content, $this->nonce );
|
240 |
}
|
241 |
|
242 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
243 |
function form_table( $rows, $formdata = false ) {
|
244 |
$output = '';
|
245 |
foreach ( $rows as $row )
|
@@ -250,7 +309,13 @@ abstract class scbAdminPage {
|
|
250 |
return $output;
|
251 |
}
|
252 |
|
253 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
254 |
function form_table_wrap( $content ) {
|
255 |
$output = $this->table_wrap( $content );
|
256 |
$output = $this->form_wrap( $output );
|
@@ -258,7 +323,14 @@ abstract class scbAdminPage {
|
|
258 |
return $output;
|
259 |
}
|
260 |
|
261 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
262 |
function table( $rows, $formdata = false ) {
|
263 |
$output = '';
|
264 |
foreach ( $rows as $row )
|
@@ -269,12 +341,28 @@ abstract class scbAdminPage {
|
|
269 |
return $output;
|
270 |
}
|
271 |
|
272 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
273 |
function table_row( $args, $formdata = false ) {
|
274 |
return $this->row_wrap( $args['title'], $this->input( $args, $formdata ) );
|
275 |
}
|
276 |
|
277 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
278 |
function __call( $method, $args ) {
|
279 |
if ( in_array( $method, array( 'input', 'form' ) ) ) {
|
280 |
if ( empty( $args[1] ) && isset( $this->options ) )
|
@@ -287,12 +375,24 @@ abstract class scbAdminPage {
|
|
287 |
return call_user_func_array( array( 'scbForms', $method ), $args );
|
288 |
}
|
289 |
|
290 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
291 |
function js_wrap( $string ) {
|
292 |
return html( "script type='text/javascript'", $string );
|
293 |
}
|
294 |
|
295 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
296 |
function css_wrap( $string ) {
|
297 |
return html( "style type='text/css'", $string );
|
298 |
}
|
@@ -301,15 +401,40 @@ abstract class scbAdminPage {
|
|
301 |
// ____________INTERNAL METHODS____________
|
302 |
|
303 |
|
304 |
-
|
|
|
|
|
305 |
function page_init() {
|
306 |
-
extract( $this->args );
|
307 |
|
308 |
-
if ( ! $toplevel ) {
|
309 |
-
$this->pagehook = add_submenu_page(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
310 |
} else {
|
311 |
-
$func = 'add_' . $toplevel . '_page';
|
312 |
-
$this->pagehook = $func(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
313 |
}
|
314 |
|
315 |
if ( ! $this->pagehook )
|
@@ -317,11 +442,6 @@ abstract class scbAdminPage {
|
|
317 |
|
318 |
add_action( 'load-' . $this->pagehook, array( $this, 'page_loaded' ) );
|
319 |
|
320 |
-
if ( $ajax_submit ) {
|
321 |
-
$this->ajax_response();
|
322 |
-
add_action( 'admin_footer', array( $this, 'ajax_submit' ), 20 );
|
323 |
-
}
|
324 |
-
|
325 |
add_action( 'admin_print_styles-' . $this->pagehook, array( $this, 'page_head' ) );
|
326 |
}
|
327 |
|
@@ -334,20 +454,22 @@ abstract class scbAdminPage {
|
|
334 |
trigger_error( 'Page title cannot be empty', E_USER_WARNING );
|
335 |
|
336 |
$this->args = wp_parse_args( $this->args, array(
|
337 |
-
'toplevel'
|
338 |
-
'position'
|
339 |
-
'icon_url'
|
340 |
-
'screen_icon'
|
341 |
-
'parent'
|
342 |
-
'capability'
|
343 |
-
'menu_title'
|
344 |
-
'page_slug'
|
345 |
-
'nonce'
|
346 |
-
'action_link'
|
347 |
-
'ajax_submit' => false,
|
348 |
'admin_action_priority' => 10,
|
349 |
) );
|
350 |
|
|
|
|
|
|
|
351 |
if ( empty( $this->args['page_slug'] ) )
|
352 |
$this->args['page_slug'] = sanitize_title_with_dashes( $this->args['menu_title'] );
|
353 |
|
@@ -355,6 +477,12 @@ abstract class scbAdminPage {
|
|
355 |
$this->nonce = $this->args['page_slug'];
|
356 |
}
|
357 |
|
|
|
|
|
|
|
|
|
|
|
|
|
358 |
function _contextual_help( $help, $screen ) {
|
359 |
if ( is_object( $screen ) )
|
360 |
$screen = $screen->id;
|
@@ -367,59 +495,6 @@ abstract class scbAdminPage {
|
|
367 |
return $help;
|
368 |
}
|
369 |
|
370 |
-
function ajax_response() {
|
371 |
-
if ( ! isset( $_POST['_ajax_submit'] ) || $_POST['_ajax_submit'] != $this->pagehook )
|
372 |
-
return;
|
373 |
-
|
374 |
-
$this->form_handler();
|
375 |
-
die;
|
376 |
-
}
|
377 |
-
|
378 |
-
function ajax_submit() {
|
379 |
-
global $page_hook;
|
380 |
-
|
381 |
-
if ( $page_hook != $this->pagehook )
|
382 |
-
return;
|
383 |
-
?>
|
384 |
-
<script type="text/javascript">
|
385 |
-
jQuery( document ).ready( function( $ ){
|
386 |
-
var $spinner = $( new Image() ).attr( 'src', '<?php echo admin_url( "images/wpspin_light.gif" ); ?>' );
|
387 |
-
|
388 |
-
$( ':submit' ).click( function( ev ){
|
389 |
-
var $submit = $( this );
|
390 |
-
var $form = $submit.parents( 'form' );
|
391 |
-
|
392 |
-
if ( $submit.hasClass( 'no-ajax' ) || $form.attr( 'method' ).toLowerCase() != 'post' )
|
393 |
-
return true;
|
394 |
-
|
395 |
-
var $this_spinner = $spinner.clone();
|
396 |
-
|
397 |
-
$submit.before( $this_spinner ).hide();
|
398 |
-
|
399 |
-
var data = $form.serializeArray();
|
400 |
-
data.push( {name: $submit.attr( 'name' ), value: $submit.val()} );
|
401 |
-
data.push( {name: '_ajax_submit', value: '<?php echo $this->pagehook; ?>'} );
|
402 |
-
|
403 |
-
$.post( location.href, data, function( response ){
|
404 |
-
var $prev = $( '.wrap > .updated, .wrap > .error' );
|
405 |
-
var $msg = $( response ).hide().insertAfter( $( '.wrap h2' ) );
|
406 |
-
if ( $prev.length > 0 )
|
407 |
-
$prev.fadeOut( 'slow', function(){ $msg.fadeIn( 'slow' ); } );
|
408 |
-
else
|
409 |
-
$msg.fadeIn( 'slow' );
|
410 |
-
|
411 |
-
$this_spinner.hide();
|
412 |
-
$submit.show();
|
413 |
-
} );
|
414 |
-
|
415 |
-
ev.stopPropagation();
|
416 |
-
ev.preventDefault();
|
417 |
-
} );
|
418 |
-
} );
|
419 |
-
</script>
|
420 |
-
<?php
|
421 |
-
}
|
422 |
-
|
423 |
function _page_content_hook() {
|
424 |
$this->form_handler();
|
425 |
|
@@ -428,6 +503,11 @@ jQuery( document ).ready( function( $ ){
|
|
428 |
$this->page_footer();
|
429 |
}
|
430 |
|
|
|
|
|
|
|
|
|
|
|
431 |
function _action_link( $links ) {
|
432 |
$url = add_query_arg( 'page', $this->args['page_slug'], admin_url( $this->args['parent'] ) );
|
433 |
|
1 |
<?php
|
2 |
|
3 |
+
/**
|
4 |
+
* Administration page base class
|
5 |
+
*/
|
6 |
abstract class scbAdminPage {
|
7 |
/** Page args
|
8 |
* $page_title string (mandatory)
|
9 |
* $parent (string) (default: options-general.php)
|
10 |
* $capability (string) (default: 'manage_options')
|
11 |
* $menu_title (string) (default: $page_title)
|
12 |
+
* $submenu_title (string) (default: $menu_title)
|
13 |
* $page_slug (string) (default: sanitized $page_title)
|
14 |
* $toplevel (string) If not empty, will create a new top level menu (for expected values see http://codex.wordpress.org/Administration_Menus#Using_add_submenu_page)
|
15 |
* - $icon_url (string) URL to an icon for the top level menu
|
42 |
|
43 |
private static $registered = array();
|
44 |
|
45 |
+
/**
|
46 |
+
* @param string $class
|
47 |
+
* @param string $file
|
48 |
+
* @param scbOptions $options
|
49 |
+
*
|
50 |
+
* @return bool
|
51 |
+
*/
|
52 |
static function register( $class, $file, $options = null ) {
|
53 |
if ( isset( self::$registered[$class] ) )
|
54 |
return false;
|
60 |
return true;
|
61 |
}
|
62 |
|
63 |
+
/**
|
64 |
+
* @param string $old_class
|
65 |
+
* @param string $new_class
|
66 |
+
*
|
67 |
+
* @return bool
|
68 |
+
*/
|
69 |
static function replace( $old_class, $new_class ) {
|
70 |
if ( ! isset( self::$registered[$old_class] ) )
|
71 |
return false;
|
76 |
return true;
|
77 |
}
|
78 |
|
79 |
+
/**
|
80 |
+
* @param string $class
|
81 |
+
*
|
82 |
+
* @return bool
|
83 |
+
*/
|
84 |
static function remove( $class ) {
|
85 |
if ( ! isset( self::$registered[$class] ) )
|
86 |
return false;
|
99 |
// ____________MAIN METHODS____________
|
100 |
|
101 |
|
102 |
+
/**
|
103 |
+
* Constructor
|
104 |
+
*
|
105 |
+
* @param string|bool $file
|
106 |
+
* @param scbOptions $options
|
107 |
+
*/
|
108 |
function __construct( $file = false, $options = null ) {
|
109 |
if ( is_a( $options, 'scbOptions' ) )
|
110 |
$this->options = $options;
|
130 |
}
|
131 |
}
|
132 |
|
133 |
+
/**
|
134 |
+
* This is where all the page args can be set
|
135 |
+
*/
|
136 |
function setup(){}
|
137 |
|
138 |
/**
|
142 |
*/
|
143 |
function page_loaded() {}
|
144 |
|
145 |
+
/**
|
146 |
+
* This is where the css and js go
|
147 |
+
* Both wp_enqueue_*() and inline code can be added
|
148 |
+
*/
|
149 |
function page_head(){}
|
150 |
|
151 |
+
/**
|
152 |
+
* This is where the contextual help goes
|
153 |
+
* @return string
|
154 |
+
*/
|
155 |
function page_help(){}
|
156 |
|
157 |
+
/**
|
158 |
+
* A generic page header
|
159 |
+
*/
|
160 |
function page_header() {
|
161 |
echo "<div class='wrap'>\n";
|
162 |
screen_icon( $this->args['screen_icon'] );
|
163 |
+
echo html( 'h2', $this->args['page_title'] );
|
164 |
}
|
165 |
|
166 |
+
/**
|
167 |
+
* This is where the page content goes
|
168 |
+
*/
|
169 |
abstract function page_content();
|
170 |
|
171 |
+
/**
|
172 |
+
* A generic page footer
|
173 |
+
*/
|
174 |
function page_footer() {
|
175 |
echo "</div>\n";
|
176 |
}
|
177 |
|
178 |
+
/**
|
179 |
+
* This is where the form data should be validated
|
180 |
+
*
|
181 |
+
* @param array $new_data
|
182 |
+
* @param array $old_data
|
183 |
+
*
|
184 |
+
* @return array
|
185 |
+
*/
|
186 |
function validate( $new_data, $old_data ) {
|
187 |
return $new_data;
|
188 |
}
|
189 |
|
190 |
+
/**
|
191 |
+
* Manually handle option saving ( use Settings API instead )
|
192 |
+
*
|
193 |
+
* @return bool
|
194 |
+
*/
|
195 |
function form_handler() {
|
196 |
+
if ( empty( $_POST['submit'] ) && empty( $_POST['action'] ) )
|
197 |
return false;
|
198 |
|
199 |
check_admin_referer( $this->nonce );
|
200 |
|
201 |
if ( !isset($this->options) ) {
|
202 |
+
trigger_error( 'options handler not set', E_USER_WARNING );
|
203 |
return false;
|
204 |
}
|
205 |
|
212 |
$this->options->set( $new_data );
|
213 |
|
214 |
$this->admin_msg();
|
215 |
+
|
216 |
+
return true;
|
217 |
}
|
218 |
|
219 |
+
/**
|
220 |
+
* Manually generate a standard admin notice ( use Settings API instead )
|
221 |
+
*
|
222 |
+
* @param string $msg
|
223 |
+
* @param string $class
|
224 |
+
*/
|
225 |
+
function admin_msg( $msg = '', $class = 'updated' ) {
|
226 |
if ( empty( $msg ) )
|
227 |
$msg = __( 'Settings <strong>saved</strong>.', $this->textdomain );
|
228 |
|
233 |
// ____________UTILITIES____________
|
234 |
|
235 |
|
236 |
+
/**
|
237 |
+
* Generates a form submit button
|
238 |
+
*
|
239 |
+
* @param string|array $value button text or array of arguments
|
240 |
+
* @param string $action
|
241 |
+
* @param string $class
|
242 |
+
*
|
243 |
+
* @return string
|
244 |
+
*/
|
245 |
+
function submit_button( $value = '', $action = 'submit', $class = 'button' ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
246 |
|
247 |
+
$args = is_array( $value ) ? $value : compact( 'value', 'action', 'class' );
|
248 |
+
$args = wp_parse_args( $args, array(
|
249 |
+
'value' => null,
|
250 |
+
'action' => $action,
|
251 |
+
'class' => $class,
|
252 |
+
) );
|
253 |
|
254 |
+
return get_submit_button( $args['value'], $args['class'], $args['action'] );
|
255 |
}
|
256 |
|
257 |
+
/**
|
258 |
+
* Mimics scbForms::form_wrap()
|
259 |
+
*
|
260 |
+
* $this->form_wrap( $content ); // generates a form with a default submit button
|
261 |
+
*
|
262 |
+
* $this->form_wrap( $content, false ); // generates a form with no submit button
|
263 |
+
*
|
264 |
+
* // the second argument is sent to submit_button()
|
265 |
+
* $this->form_wrap( $content, array(
|
266 |
+
* 'text' => 'Save changes',
|
267 |
+
* 'name' => 'action',
|
268 |
+
* ) );
|
269 |
+
*
|
270 |
+
* @see scbForms::form_wrap()
|
271 |
+
*
|
272 |
+
* @param string $content
|
273 |
+
* @param boolean|string|array $submit_button
|
274 |
+
*
|
275 |
+
* @return string
|
276 |
+
*/
|
277 |
function form_wrap( $content, $submit_button = true ) {
|
278 |
if ( is_array( $submit_button ) ) {
|
279 |
$content .= $this->submit_button( $submit_button );
|
281 |
$content .= $this->submit_button();
|
282 |
} elseif ( false !== strpos( $submit_button, '<input' ) ) {
|
283 |
$content .= $submit_button;
|
284 |
+
} elseif ( false !== strpos( $submit_button, '<button' ) ) {
|
285 |
+
$content .= $submit_button;
|
286 |
} elseif ( false !== $submit_button ) {
|
287 |
$button_args = array_slice( func_get_args(), 1 );
|
288 |
+
$content .= call_user_func_array( array( $this, 'submit_button' ), $button_args );
|
289 |
}
|
290 |
|
291 |
return scbForms::form_wrap( $content, $this->nonce );
|
292 |
}
|
293 |
|
294 |
+
/**
|
295 |
+
* Generates a table wrapped in a form
|
296 |
+
*
|
297 |
+
* @param array $rows
|
298 |
+
* @param array|boolean $formdata
|
299 |
+
*
|
300 |
+
* @return string
|
301 |
+
*/
|
302 |
function form_table( $rows, $formdata = false ) {
|
303 |
$output = '';
|
304 |
foreach ( $rows as $row )
|
309 |
return $output;
|
310 |
}
|
311 |
|
312 |
+
/**
|
313 |
+
* Wraps the given content in a <form><table>
|
314 |
+
*
|
315 |
+
* @param string $content
|
316 |
+
*
|
317 |
+
* @return string
|
318 |
+
*/
|
319 |
function form_table_wrap( $content ) {
|
320 |
$output = $this->table_wrap( $content );
|
321 |
$output = $this->form_wrap( $output );
|
323 |
return $output;
|
324 |
}
|
325 |
|
326 |
+
/**
|
327 |
+
* Generates a form table
|
328 |
+
*
|
329 |
+
* @param array $rows
|
330 |
+
* @param array|boolean $formdata
|
331 |
+
*
|
332 |
+
* @return string
|
333 |
+
*/
|
334 |
function table( $rows, $formdata = false ) {
|
335 |
$output = '';
|
336 |
foreach ( $rows as $row )
|
341 |
return $output;
|
342 |
}
|
343 |
|
344 |
+
/**
|
345 |
+
* Generates a table row
|
346 |
+
*
|
347 |
+
* @param array $args
|
348 |
+
* @param array|boolean $formdata
|
349 |
+
*
|
350 |
+
* @return string
|
351 |
+
*/
|
352 |
function table_row( $args, $formdata = false ) {
|
353 |
return $this->row_wrap( $args['title'], $this->input( $args, $formdata ) );
|
354 |
}
|
355 |
|
356 |
+
/**
|
357 |
+
* Mimic scbForms inheritance
|
358 |
+
*
|
359 |
+
* @see scbForms
|
360 |
+
*
|
361 |
+
* @param string $method
|
362 |
+
* @param array $args
|
363 |
+
*
|
364 |
+
* @return mixed
|
365 |
+
*/
|
366 |
function __call( $method, $args ) {
|
367 |
if ( in_array( $method, array( 'input', 'form' ) ) ) {
|
368 |
if ( empty( $args[1] ) && isset( $this->options ) )
|
375 |
return call_user_func_array( array( 'scbForms', $method ), $args );
|
376 |
}
|
377 |
|
378 |
+
/**
|
379 |
+
* Wraps a string in a <script> tag
|
380 |
+
*
|
381 |
+
* @param string $string
|
382 |
+
*
|
383 |
+
* @return string
|
384 |
+
*/
|
385 |
function js_wrap( $string ) {
|
386 |
return html( "script type='text/javascript'", $string );
|
387 |
}
|
388 |
|
389 |
+
/**
|
390 |
+
* Wraps a string in a <style> tag
|
391 |
+
*
|
392 |
+
* @param string $string
|
393 |
+
*
|
394 |
+
* @return string
|
395 |
+
*/
|
396 |
function css_wrap( $string ) {
|
397 |
return html( "style type='text/css'", $string );
|
398 |
}
|
401 |
// ____________INTERNAL METHODS____________
|
402 |
|
403 |
|
404 |
+
/**
|
405 |
+
* Registers a page
|
406 |
+
*/
|
407 |
function page_init() {
|
|
|
408 |
|
409 |
+
if ( ! $this->args['toplevel'] ) {
|
410 |
+
$this->pagehook = add_submenu_page(
|
411 |
+
$this->args['parent'],
|
412 |
+
$this->args['page_title'],
|
413 |
+
$this->args['menu_title'],
|
414 |
+
$this->args['capability'],
|
415 |
+
$this->args['page_slug'],
|
416 |
+
array( $this, '_page_content_hook' )
|
417 |
+
);
|
418 |
} else {
|
419 |
+
$func = 'add_' . $this->args['toplevel'] . '_page';
|
420 |
+
$this->pagehook = $func(
|
421 |
+
$this->args['page_title'],
|
422 |
+
$this->args['menu_title'],
|
423 |
+
$this->args['capability'],
|
424 |
+
$this->args['page_slug'],
|
425 |
+
null,
|
426 |
+
$this->args['icon_url'],
|
427 |
+
$this->args['position']
|
428 |
+
);
|
429 |
+
|
430 |
+
add_submenu_page(
|
431 |
+
$this->args['page_slug'],
|
432 |
+
$this->args['page_title'],
|
433 |
+
$this->args['submenu_title'],
|
434 |
+
$this->args['capability'],
|
435 |
+
$this->args['page_slug'],
|
436 |
+
array( $this, '_page_content_hook' )
|
437 |
+
);
|
438 |
}
|
439 |
|
440 |
if ( ! $this->pagehook )
|
442 |
|
443 |
add_action( 'load-' . $this->pagehook, array( $this, 'page_loaded' ) );
|
444 |
|
|
|
|
|
|
|
|
|
|
|
445 |
add_action( 'admin_print_styles-' . $this->pagehook, array( $this, 'page_head' ) );
|
446 |
}
|
447 |
|
454 |
trigger_error( 'Page title cannot be empty', E_USER_WARNING );
|
455 |
|
456 |
$this->args = wp_parse_args( $this->args, array(
|
457 |
+
'toplevel' => '',
|
458 |
+
'position' => null,
|
459 |
+
'icon_url' => '',
|
460 |
+
'screen_icon' => '',
|
461 |
+
'parent' => 'options-general.php',
|
462 |
+
'capability' => 'manage_options',
|
463 |
+
'menu_title' => $this->args['page_title'],
|
464 |
+
'page_slug' => '',
|
465 |
+
'nonce' => '',
|
466 |
+
'action_link' => __( 'Settings', $this->textdomain ),
|
|
|
467 |
'admin_action_priority' => 10,
|
468 |
) );
|
469 |
|
470 |
+
if ( empty( $this->args['submenu_title'] ) )
|
471 |
+
$this->args['submenu_title'] = $this->args['menu_title'];
|
472 |
+
|
473 |
if ( empty( $this->args['page_slug'] ) )
|
474 |
$this->args['page_slug'] = sanitize_title_with_dashes( $this->args['menu_title'] );
|
475 |
|
477 |
$this->nonce = $this->args['page_slug'];
|
478 |
}
|
479 |
|
480 |
+
/**
|
481 |
+
* @param string $help
|
482 |
+
* @param string|object $screen
|
483 |
+
*
|
484 |
+
* @return string
|
485 |
+
*/
|
486 |
function _contextual_help( $help, $screen ) {
|
487 |
if ( is_object( $screen ) )
|
488 |
$screen = $screen->id;
|
495 |
return $help;
|
496 |
}
|
497 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
498 |
function _page_content_hook() {
|
499 |
$this->form_handler();
|
500 |
|
503 |
$this->page_footer();
|
504 |
}
|
505 |
|
506 |
+
/**
|
507 |
+
* @param array $links
|
508 |
+
*
|
509 |
+
* @return array
|
510 |
+
*/
|
511 |
function _action_link( $links ) {
|
512 |
$url = add_query_arg( 'page', $this->args['page_slug'], admin_url( $this->args['parent'] ) );
|
513 |
|
scb/BoxesPage.php
CHANGED
@@ -166,35 +166,55 @@ abstract class scbBoxesPage extends scbAdminPage {
|
|
166 |
) );
|
167 |
|
168 |
$registered = array();
|
|
|
169 |
foreach ( $this->boxes as $box_args ) {
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
|
|
|
|
|
|
|
|
|
|
174 |
|
175 |
-
|
176 |
-
|
177 |
-
if (
|
178 |
-
|
179 |
-
if ( empty( $priority ) )
|
180 |
-
$priority = 'default';
|
181 |
-
if ( empty( $args ) )
|
182 |
-
$args = array();
|
183 |
-
|
184 |
-
if ( isset( $registered[$name] ) ) {
|
185 |
-
if ( empty( $args ) )
|
186 |
trigger_error( "Duplicate box name: $name", E_USER_NOTICE );
|
|
|
187 |
|
188 |
$name = $this->_increment( $name );
|
189 |
} else {
|
190 |
-
$registered[$name] = true;
|
191 |
}
|
192 |
|
193 |
-
add_meta_box(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
194 |
}
|
195 |
}
|
196 |
|
197 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
198 |
function _intermediate_callback( $_, $box ) {
|
199 |
list( $name ) = explode( '-', $box['id'] );
|
200 |
|
166 |
) );
|
167 |
|
168 |
$registered = array();
|
169 |
+
|
170 |
foreach ( $this->boxes as $box_args ) {
|
171 |
+
$box_args = self::numeric_to_assoc( $box_args, array( 'name', 'title', 'context', 'priority', 'args' ) );
|
172 |
+
|
173 |
+
$defaults = array(
|
174 |
+
'title' => ucfirst( $box_args['name'] ),
|
175 |
+
'context' => 'normal',
|
176 |
+
'priority' => 'default',
|
177 |
+
'args' => array()
|
178 |
+
);
|
179 |
+
$box_args = array_merge( $defaults, $box_args );
|
180 |
|
181 |
+
$name = $box_args['name'];
|
182 |
+
|
183 |
+
if ( isset( $registered[ $name ] ) ) {
|
184 |
+
if ( empty( $box_args['args'] ) ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
185 |
trigger_error( "Duplicate box name: $name", E_USER_NOTICE );
|
186 |
+
}
|
187 |
|
188 |
$name = $this->_increment( $name );
|
189 |
} else {
|
190 |
+
$registered[ $name ] = true;
|
191 |
}
|
192 |
|
193 |
+
add_meta_box(
|
194 |
+
$name,
|
195 |
+
$box_args['title'],
|
196 |
+
array( $this, '_intermediate_callback' ),
|
197 |
+
$this->pagehook,
|
198 |
+
$box_args['context'],
|
199 |
+
$box_args['priority'],
|
200 |
+
$box_args['args']
|
201 |
+
);
|
202 |
}
|
203 |
}
|
204 |
|
205 |
+
private static function numeric_to_assoc( $argv, $keys ) {
|
206 |
+
$args = array();
|
207 |
+
|
208 |
+
foreach ( $keys as $i => $key ) {
|
209 |
+
if ( isset( $argv[ $i ] ) )
|
210 |
+
$args[ $key ] = $argv[ $i ];
|
211 |
+
}
|
212 |
+
|
213 |
+
return $args;
|
214 |
+
}
|
215 |
+
|
216 |
+
// Since we don't pass an object to do_meta_boxes(),
|
217 |
+
// pass $box['args'] directly to each method.
|
218 |
function _intermediate_callback( $_, $box ) {
|
219 |
list( $name ) = explode( '-', $box['id'] );
|
220 |
|
scb/Cron.php
CHANGED
@@ -1,7 +1,8 @@
|
|
1 |
<?php
|
2 |
|
3 |
-
|
4 |
-
|
|
|
5 |
class scbCron {
|
6 |
protected $schedule;
|
7 |
protected $interval;
|
@@ -13,41 +14,40 @@ class scbCron {
|
|
13 |
/**
|
14 |
* Create a new cron job
|
15 |
*
|
16 |
-
* @param string Reference to main plugin file
|
17 |
-
* @param array List of args:
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
*/
|
22 |
function __construct( $file = false, $args ) {
|
23 |
-
extract( $args, EXTR_SKIP );
|
24 |
|
25 |
// Set time & schedule
|
26 |
-
if ( isset( $time ) )
|
27 |
-
$this->time = $time;
|
28 |
-
|
29 |
-
if ( isset( $interval ) ) {
|
30 |
-
$this->schedule = $interval . 'secs';
|
31 |
-
$this->interval = $interval;
|
32 |
-
} elseif ( isset( $schedule ) ) {
|
33 |
-
$this->schedule = $schedule;
|
34 |
}
|
35 |
|
36 |
// Set hook
|
37 |
-
if ( isset( $action ) ) {
|
38 |
-
$this->hook = $action;
|
39 |
-
} elseif ( isset( $callback ) ) {
|
40 |
-
$this->hook = self::_callback_to_string( $callback );
|
41 |
-
add_action( $this->hook, $callback );
|
42 |
} elseif ( method_exists( $this, 'callback' ) ) {
|
43 |
$this->hook = self::_callback_to_string( array( $this, 'callback' ) );
|
44 |
-
add_action( $this->hook, $callback );
|
45 |
} else {
|
46 |
trigger_error( '$action OR $callback not set', E_USER_WARNING );
|
47 |
}
|
48 |
|
49 |
-
if ( isset( $callback_args ) )
|
50 |
-
$this->callback_args = (array) $callback_args;
|
51 |
|
52 |
if ( $file && $this->schedule ) {
|
53 |
scbUtil::add_activation_hook( $file, array( $this, 'reset' ) );
|
@@ -57,23 +57,23 @@ class scbCron {
|
|
57 |
add_filter( 'cron_schedules', array( $this, '_add_timing' ) );
|
58 |
}
|
59 |
|
60 |
-
|
|
|
61 |
*
|
62 |
-
* @param array List of args:
|
63 |
-
|
64 |
-
|
65 |
*/
|
66 |
function reschedule( $args ) {
|
67 |
-
extract( $args );
|
68 |
|
69 |
-
if ( $schedule && $this->schedule != $schedule ) {
|
70 |
-
$this->schedule = $schedule;
|
71 |
-
} elseif ( $interval && $this->interval != $interval ) {
|
72 |
-
$this->schedule = $interval . 'secs';
|
73 |
-
$this->interval = $interval;
|
74 |
}
|
75 |
|
76 |
-
$this->time = $time;
|
77 |
|
78 |
$this->reset();
|
79 |
}
|
@@ -107,8 +107,8 @@ class scbCron {
|
|
107 |
|
108 |
/**
|
109 |
* Execute the job with a given delay
|
110 |
-
* @param int
|
111 |
-
* @param array $args
|
112 |
*/
|
113 |
function do_once( $delay = 0, $args = null ) {
|
114 |
if ( is_null( $args ) )
|
@@ -121,13 +121,19 @@ class scbCron {
|
|
121 |
|
122 |
//_____INTERNAL METHODS_____
|
123 |
|
124 |
-
|
|
|
|
|
|
|
|
|
125 |
function _add_timing( $schedules ) {
|
126 |
if ( isset( $schedules[$this->schedule] ) )
|
127 |
return $schedules;
|
128 |
|
129 |
-
$schedules[$this->schedule] = array(
|
130 |
-
'
|
|
|
|
|
131 |
|
132 |
return $schedules;
|
133 |
}
|
@@ -139,6 +145,9 @@ class scbCron {
|
|
139 |
wp_schedule_event( $this->time, $this->schedule, $this->hook, $this->callback_args );
|
140 |
}
|
141 |
|
|
|
|
|
|
|
142 |
protected static function really_clear_scheduled_hook( $name ) {
|
143 |
$crons = _get_cron_array();
|
144 |
|
@@ -147,13 +156,18 @@ class scbCron {
|
|
147 |
if ( $hook == $name )
|
148 |
unset( $crons[$timestamp][$hook] );
|
149 |
|
150 |
-
if ( empty( $
|
151 |
unset( $crons[$timestamp] );
|
152 |
}
|
153 |
|
154 |
_set_cron_array( $crons );
|
155 |
}
|
156 |
|
|
|
|
|
|
|
|
|
|
|
157 |
protected static function _callback_to_string( $callback ) {
|
158 |
if ( ! is_array( $callback ) )
|
159 |
$str = $callback;
|
1 |
<?php
|
2 |
|
3 |
+
/**
|
4 |
+
* wp-cron job container
|
5 |
+
*/
|
6 |
class scbCron {
|
7 |
protected $schedule;
|
8 |
protected $interval;
|
14 |
/**
|
15 |
* Create a new cron job
|
16 |
*
|
17 |
+
* @param string|bool $file Reference to main plugin file
|
18 |
+
* @param array $args List of args:
|
19 |
+
* string $action OR callback $callback
|
20 |
+
* string $schedule OR number $interval
|
21 |
+
* array $callback_args (optional)
|
22 |
*/
|
23 |
function __construct( $file = false, $args ) {
|
|
|
24 |
|
25 |
// Set time & schedule
|
26 |
+
if ( isset( $args['time'] ) )
|
27 |
+
$this->time = $args['time'];
|
28 |
+
|
29 |
+
if ( isset( $args['interval'] ) ) {
|
30 |
+
$this->schedule = $args['interval'] . 'secs';
|
31 |
+
$this->interval = $args['interval'];
|
32 |
+
} elseif ( isset( $args['schedule'] ) ) {
|
33 |
+
$this->schedule = $args['schedule'];
|
34 |
}
|
35 |
|
36 |
// Set hook
|
37 |
+
if ( isset( $args['action'] ) ) {
|
38 |
+
$this->hook = $args['action'];
|
39 |
+
} elseif ( isset( $args['callback'] ) ) {
|
40 |
+
$this->hook = self::_callback_to_string( $args['callback'] );
|
41 |
+
add_action( $this->hook, $args['callback'] );
|
42 |
} elseif ( method_exists( $this, 'callback' ) ) {
|
43 |
$this->hook = self::_callback_to_string( array( $this, 'callback' ) );
|
44 |
+
add_action( $this->hook, $args['callback'] );
|
45 |
} else {
|
46 |
trigger_error( '$action OR $callback not set', E_USER_WARNING );
|
47 |
}
|
48 |
|
49 |
+
if ( isset( $args['callback_args'] ) )
|
50 |
+
$this->callback_args = (array) $args['callback_args'];
|
51 |
|
52 |
if ( $file && $this->schedule ) {
|
53 |
scbUtil::add_activation_hook( $file, array( $this, 'reset' ) );
|
57 |
add_filter( 'cron_schedules', array( $this, '_add_timing' ) );
|
58 |
}
|
59 |
|
60 |
+
/**
|
61 |
+
* Change the interval of the cron job
|
62 |
*
|
63 |
+
* @param array $args List of args:
|
64 |
+
* string $schedule OR number $interval
|
65 |
+
* timestamp $time ( optional )
|
66 |
*/
|
67 |
function reschedule( $args ) {
|
|
|
68 |
|
69 |
+
if ( $args['schedule'] && $this->schedule != $args['schedule'] ) {
|
70 |
+
$this->schedule = $args['schedule'];
|
71 |
+
} elseif ( $args['interval'] && $this->interval != $args['interval'] ) {
|
72 |
+
$this->schedule = $args['interval'] . 'secs';
|
73 |
+
$this->interval = $args['interval'];
|
74 |
}
|
75 |
|
76 |
+
$this->time = $args['time'];
|
77 |
|
78 |
$this->reset();
|
79 |
}
|
107 |
|
108 |
/**
|
109 |
* Execute the job with a given delay
|
110 |
+
* @param int $delay in seconds
|
111 |
+
* @param array $args List of arguments to pass to the callback
|
112 |
*/
|
113 |
function do_once( $delay = 0, $args = null ) {
|
114 |
if ( is_null( $args ) )
|
121 |
|
122 |
//_____INTERNAL METHODS_____
|
123 |
|
124 |
+
/**
|
125 |
+
* @param array $schedules
|
126 |
+
*
|
127 |
+
* @return array
|
128 |
+
*/
|
129 |
function _add_timing( $schedules ) {
|
130 |
if ( isset( $schedules[$this->schedule] ) )
|
131 |
return $schedules;
|
132 |
|
133 |
+
$schedules[$this->schedule] = array(
|
134 |
+
'interval' => $this->interval,
|
135 |
+
'display' => $this->interval . ' seconds',
|
136 |
+
);
|
137 |
|
138 |
return $schedules;
|
139 |
}
|
145 |
wp_schedule_event( $this->time, $this->schedule, $this->hook, $this->callback_args );
|
146 |
}
|
147 |
|
148 |
+
/**
|
149 |
+
* @param string $name
|
150 |
+
*/
|
151 |
protected static function really_clear_scheduled_hook( $name ) {
|
152 |
$crons = _get_cron_array();
|
153 |
|
156 |
if ( $hook == $name )
|
157 |
unset( $crons[$timestamp][$hook] );
|
158 |
|
159 |
+
if ( empty( $crons[$timestamp] ) )
|
160 |
unset( $crons[$timestamp] );
|
161 |
}
|
162 |
|
163 |
_set_cron_array( $crons );
|
164 |
}
|
165 |
|
166 |
+
/**
|
167 |
+
* @param callback $callback
|
168 |
+
*
|
169 |
+
* @return string
|
170 |
+
*/
|
171 |
protected static function _callback_to_string( $callback ) {
|
172 |
if ( ! is_array( $callback ) )
|
173 |
$str = $callback;
|
scb/Forms.php
CHANGED
@@ -1,24 +1,44 @@
|
|
1 |
<?php
|
2 |
|
3 |
-
|
4 |
-
|
|
|
5 |
class scbForms {
|
6 |
|
7 |
const TOKEN = '%input%';
|
8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
static function input_with_value( $args, $value ) {
|
10 |
$field = scbFormField::create( $args );
|
11 |
|
12 |
return $field->render( $value );
|
13 |
}
|
14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
static function input( $args, $formdata = null ) {
|
16 |
$field = scbFormField::create( $args );
|
17 |
|
18 |
return $field->render( scbForms::get_value( $args['name'], $formdata ) );
|
19 |
}
|
20 |
|
21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
static function form_table( $rows, $formdata = null ) {
|
23 |
$output = '';
|
24 |
foreach ( $rows as $row )
|
@@ -29,7 +49,15 @@ class scbForms {
|
|
29 |
return $output;
|
30 |
}
|
31 |
|
32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
static function form( $inputs, $formdata = null, $nonce ) {
|
34 |
$output = '';
|
35 |
foreach ( $inputs as $input )
|
@@ -40,7 +68,14 @@ class scbForms {
|
|
40 |
return $output;
|
41 |
}
|
42 |
|
43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
static function table( $rows, $formdata = null ) {
|
45 |
$output = '';
|
46 |
foreach ( $rows as $row )
|
@@ -51,7 +86,14 @@ class scbForms {
|
|
51 |
return $output;
|
52 |
}
|
53 |
|
54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
static function table_row( $args, $formdata = null ) {
|
56 |
return self::row_wrap( $args['title'], self::input( $args, $formdata ) );
|
57 |
}
|
@@ -59,11 +101,22 @@ class scbForms {
|
|
59 |
|
60 |
// ____________WRAPPERS____________
|
61 |
|
62 |
-
|
|
|
|
|
|
|
|
|
|
|
63 |
static function form_table_wrap( $content, $nonce = 'update_options' ) {
|
64 |
return self::form_wrap( self::table_wrap( $content ), $nonce );
|
65 |
}
|
66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
static function form_wrap( $content, $nonce = 'update_options' ) {
|
68 |
return html( "form method='post' action=''",
|
69 |
$content,
|
@@ -71,14 +124,25 @@ class scbForms {
|
|
71 |
);
|
72 |
}
|
73 |
|
|
|
|
|
|
|
|
|
|
|
74 |
static function table_wrap( $content ) {
|
75 |
return html( "table class='form-table'", $content );
|
76 |
}
|
77 |
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
static function row_wrap( $title, $content ) {
|
79 |
-
return html(
|
80 |
html( "th scope='row'", $title ),
|
81 |
-
html(
|
82 |
);
|
83 |
}
|
84 |
|
@@ -111,9 +175,9 @@ class scbForms {
|
|
111 |
/**
|
112 |
* Traverses the formdata and retrieves the correct value.
|
113 |
*
|
114 |
-
* @param
|
115 |
-
* @param array
|
116 |
-
* @param mixed
|
117 |
*
|
118 |
* @return mixed
|
119 |
*/
|
@@ -131,8 +195,8 @@ class scbForms {
|
|
131 |
/**
|
132 |
* Given a list of fields, validate some data.
|
133 |
*
|
134 |
-
* @param array $fields
|
135 |
-
* @param array $data
|
136 |
* @param array $to_update Existing data to populate. Necessary for nested values
|
137 |
*
|
138 |
* @return array
|
@@ -161,8 +225,8 @@ class scbForms {
|
|
161 |
* For single-choice fields, we can't distinguish either, because of how self::update_meta() works.
|
162 |
* Therefore, the 'default' parameter is always ignored.
|
163 |
*
|
164 |
-
* @param array
|
165 |
-
* @param int
|
166 |
* @param string $meta_type
|
167 |
*
|
168 |
* @return string
|
@@ -178,6 +242,12 @@ class scbForms {
|
|
178 |
return self::input_with_value( $args, $value );
|
179 |
}
|
180 |
|
|
|
|
|
|
|
|
|
|
|
|
|
181 |
static function update_meta( $fields, $data, $object_id, $meta_type = 'post' ) {
|
182 |
foreach ( $fields as $field_args ) {
|
183 |
$key = $field_args['name'];
|
@@ -193,7 +263,7 @@ class scbForms {
|
|
193 |
foreach ( array_diff( $old_values, $new_values ) as $value )
|
194 |
delete_metadata( $meta_type, $object_id, $key, $value );
|
195 |
} else {
|
196 |
-
$value = $data[$key];
|
197 |
|
198 |
if ( '' === $value )
|
199 |
delete_metadata( $meta_type, $object_id, $key );
|
@@ -203,6 +273,11 @@ class scbForms {
|
|
203 |
}
|
204 |
}
|
205 |
|
|
|
|
|
|
|
|
|
|
|
206 |
private static function set_value( &$arr, $name, $value ) {
|
207 |
$name = (array) $name;
|
208 |
|
@@ -226,9 +301,13 @@ class scbForms {
|
|
226 |
* A wrapper for scbForms, containing the formdata
|
227 |
*/
|
228 |
class scbForm {
|
229 |
-
protected $data
|
230 |
protected $prefix = array();
|
231 |
|
|
|
|
|
|
|
|
|
232 |
function __construct( $data, $prefix = false ) {
|
233 |
if ( is_array( $data ) )
|
234 |
$this->data = $data;
|
@@ -237,6 +316,11 @@ class scbForm {
|
|
237 |
$this->prefix = (array) $prefix;
|
238 |
}
|
239 |
|
|
|
|
|
|
|
|
|
|
|
240 |
function traverse_to( $path ) {
|
241 |
$data = scbForms::get_value( $path, $this->data );
|
242 |
|
@@ -245,6 +329,11 @@ class scbForm {
|
|
245 |
return new scbForm( $data, $prefix );
|
246 |
}
|
247 |
|
|
|
|
|
|
|
|
|
|
|
248 |
function input( $args ) {
|
249 |
$value = scbForms::get_value( $args['name'], $this->data );
|
250 |
|
@@ -256,7 +345,9 @@ class scbForm {
|
|
256 |
}
|
257 |
}
|
258 |
|
259 |
-
|
|
|
|
|
260 |
interface scbFormField_I {
|
261 |
|
262 |
/**
|
@@ -278,11 +369,18 @@ interface scbFormField_I {
|
|
278 |
function validate( $value );
|
279 |
}
|
280 |
|
281 |
-
|
|
|
|
|
282 |
abstract class scbFormField implements scbFormField_I {
|
283 |
|
284 |
protected $args;
|
285 |
|
|
|
|
|
|
|
|
|
|
|
286 |
public static function create( $args ) {
|
287 |
if ( is_a( $args, 'scbFormField_I' ) )
|
288 |
return $args;
|
@@ -305,9 +403,9 @@ abstract class scbFormField implements scbFormField_I {
|
|
305 |
$args['extra'] = shortcode_parse_atts( $args['extra'] );
|
306 |
|
307 |
$args = wp_parse_args( $args, array(
|
308 |
-
'desc'
|
309 |
-
'desc_pos'
|
310 |
-
'wrap'
|
311 |
'wrap_each' => scbForms::TOKEN,
|
312 |
) );
|
313 |
|
@@ -332,18 +430,36 @@ abstract class scbFormField implements scbFormField_I {
|
|
332 |
}
|
333 |
}
|
334 |
|
|
|
|
|
|
|
335 |
protected function __construct( $args ) {
|
336 |
$this->args = $args;
|
337 |
}
|
338 |
|
|
|
|
|
|
|
|
|
|
|
339 |
public function __get( $key ) {
|
340 |
return $this->args[ $key ];
|
341 |
}
|
342 |
|
|
|
|
|
|
|
|
|
|
|
343 |
public function __isset( $key ) {
|
344 |
return isset( $this->args[ $key ] );
|
345 |
}
|
346 |
|
|
|
|
|
|
|
|
|
|
|
347 |
public function render( $value = null ) {
|
348 |
if ( null === $value && isset( $this->default ) )
|
349 |
$value = $this->default;
|
@@ -358,58 +474,89 @@ abstract class scbFormField implements scbFormField_I {
|
|
358 |
return str_replace( scbForms::TOKEN, $this->_render( $args ), $this->wrap );
|
359 |
}
|
360 |
|
361 |
-
|
|
|
|
|
|
|
|
|
|
|
362 |
abstract protected function _set_value( &$args, $value );
|
363 |
|
364 |
-
|
|
|
|
|
|
|
|
|
365 |
abstract protected function _render( $args );
|
366 |
|
367 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
368 |
protected static function _checkbox( $args ) {
|
369 |
$args = wp_parse_args( $args, array(
|
370 |
-
'value'
|
371 |
-
'desc'
|
372 |
'checked' => false,
|
373 |
-
'extra'
|
374 |
) );
|
375 |
|
376 |
-
|
377 |
-
$$key = &$val;
|
378 |
-
unset( $val );
|
379 |
|
380 |
-
$
|
381 |
-
|
382 |
-
if ( is_null( $desc ) && !is_bool( $value ) )
|
383 |
-
$desc = str_replace( '[]', '', $value );
|
384 |
|
385 |
return self::_input_gen( $args );
|
386 |
}
|
387 |
|
388 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
389 |
protected static function _input_gen( $args ) {
|
390 |
-
|
391 |
'value' => null,
|
392 |
-
'desc'
|
393 |
-
'extra' => array()
|
394 |
-
) )
|
395 |
|
396 |
-
$extra['name'] = $name;
|
397 |
|
398 |
-
if ( 'textarea' == $type ) {
|
399 |
-
$input = html( 'textarea', $extra, esc_textarea( $value ) );
|
400 |
} else {
|
401 |
-
$extra['value'] = $value;
|
402 |
-
$extra['type']
|
403 |
-
$input = html( 'input', $extra );
|
404 |
}
|
405 |
|
406 |
-
return self::add_label( $input, $desc, $desc_pos );
|
407 |
}
|
408 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
409 |
protected static function add_label( $input, $desc, $desc_pos ) {
|
410 |
return html( 'label', self::add_desc( $input, $desc, $desc_pos ) ) . "\n";
|
411 |
}
|
412 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
413 |
protected static function add_desc( $input, $desc, $desc_pos ) {
|
414 |
if ( empty( $desc ) )
|
415 |
return $input;
|
@@ -420,6 +567,9 @@ abstract class scbFormField implements scbFormField_I {
|
|
420 |
return $input . ' ' . $desc;
|
421 |
}
|
422 |
|
|
|
|
|
|
|
423 |
private static function _expand_choices( &$args ) {
|
424 |
$choices =& $args['choices'];
|
425 |
|
@@ -433,46 +583,70 @@ abstract class scbFormField implements scbFormField_I {
|
|
433 |
}
|
434 |
}
|
435 |
|
|
|
|
|
|
|
|
|
|
|
436 |
private static function is_associative( $array ) {
|
437 |
$keys = array_keys( $array );
|
438 |
return array_keys( $keys ) !== $keys;
|
439 |
}
|
440 |
}
|
441 |
|
442 |
-
|
|
|
|
|
443 |
class scbTextField extends scbFormField {
|
444 |
|
|
|
|
|
|
|
|
|
|
|
445 |
public function validate( $value ) {
|
446 |
$sanitize = isset( $this->sanitize ) ? $this->sanitize : 'wp_filter_kses';
|
447 |
|
448 |
return call_user_func( $sanitize, $value, $this );
|
449 |
}
|
450 |
|
|
|
|
|
|
|
|
|
|
|
451 |
protected function _render( $args ) {
|
452 |
$args = wp_parse_args( $args, array(
|
453 |
-
'value'
|
454 |
'desc_pos' => 'after',
|
455 |
-
'extra'
|
456 |
) );
|
457 |
|
458 |
-
|
459 |
-
|
460 |
-
unset( $val );
|
461 |
-
|
462 |
-
if ( !isset( $extra['id'] ) && !is_array( $name ) && false === strpos( $name, '[' ) )
|
463 |
-
$extra['id'] = $name;
|
464 |
|
465 |
return scbFormField::_input_gen( $args );
|
466 |
}
|
467 |
|
|
|
|
|
|
|
|
|
468 |
protected function _set_value( &$args, $value ) {
|
469 |
$args['value'] = $value;
|
470 |
}
|
471 |
}
|
472 |
|
473 |
-
|
|
|
|
|
474 |
abstract class scbSingleChoiceField extends scbFormField {
|
475 |
|
|
|
|
|
|
|
|
|
|
|
476 |
public function validate( $value ) {
|
477 |
if ( isset( $this->choices[ $value ] ) )
|
478 |
return $value;
|
@@ -480,189 +654,265 @@ abstract class scbSingleChoiceField extends scbFormField {
|
|
480 |
return null;
|
481 |
}
|
482 |
|
|
|
|
|
|
|
|
|
|
|
483 |
protected function _render( $args ) {
|
484 |
$args = wp_parse_args( $args, array(
|
485 |
-
'numeric'
|
486 |
-
'selected' => array( 'foo' ),
|
487 |
) );
|
488 |
|
489 |
return $this->_render_specific( $args );
|
490 |
}
|
491 |
|
|
|
|
|
|
|
|
|
492 |
protected function _set_value( &$args, $value ) {
|
493 |
$args['selected'] = $value;
|
494 |
}
|
495 |
|
|
|
|
|
|
|
|
|
|
|
496 |
abstract protected function _render_specific( $args );
|
497 |
}
|
498 |
|
499 |
-
|
|
|
|
|
500 |
class scbSelectField extends scbSingleChoiceField {
|
501 |
|
|
|
|
|
|
|
|
|
|
|
502 |
protected function _render_specific( $args ) {
|
503 |
-
|
504 |
-
'text'
|
505 |
-
'extra' => array()
|
506 |
-
) )
|
507 |
|
508 |
$options = array();
|
509 |
|
510 |
-
if ( false !== $text ) {
|
511 |
$options[] = array(
|
512 |
-
'value'
|
513 |
-
'selected' => ( $selected == array( 'foo' ) ),
|
514 |
-
'title'
|
515 |
);
|
516 |
}
|
517 |
|
518 |
-
foreach ( $choices as $value => $title ) {
|
519 |
$options[] = array(
|
520 |
-
'value'
|
521 |
-
'selected' => ( $value == $selected ),
|
522 |
-
'title'
|
523 |
);
|
524 |
}
|
525 |
|
526 |
$opts = '';
|
527 |
foreach ( $options as $option ) {
|
528 |
-
|
529 |
-
|
530 |
-
$opts .= html( 'option', compact( 'value', 'selected' ), $title );
|
531 |
}
|
532 |
|
533 |
-
$extra['name'] = $name;
|
534 |
|
535 |
-
$input = html( 'select', $extra, $opts );
|
536 |
|
537 |
-
return scbFormField::add_label( $input, $desc, $desc_pos );
|
538 |
}
|
539 |
}
|
540 |
|
541 |
-
|
|
|
|
|
542 |
class scbRadiosField extends scbSelectField {
|
543 |
|
|
|
|
|
|
|
|
|
|
|
544 |
protected function _render_specific( $args ) {
|
545 |
-
extract( $args );
|
546 |
|
547 |
-
if ( array( 'foo' ) == $selected ) {
|
548 |
// radio buttons should always have one option selected
|
549 |
-
$selected = key( $choices );
|
550 |
}
|
551 |
|
552 |
$opts = '';
|
553 |
-
foreach ( $choices as $value => $title ) {
|
554 |
$single_input = scbFormField::_checkbox( array(
|
555 |
-
'name'
|
556 |
-
'type'
|
557 |
-
'value'
|
558 |
-
'checked'
|
559 |
-
'desc'
|
560 |
-
'desc_pos' => 'after'
|
561 |
) );
|
562 |
|
563 |
-
$opts .= str_replace( scbForms::TOKEN, $single_input, $wrap_each );
|
564 |
}
|
565 |
|
566 |
-
return scbFormField::add_desc( $opts, $desc, $desc_pos );
|
567 |
}
|
568 |
}
|
569 |
|
570 |
-
|
|
|
|
|
571 |
class scbMultipleChoiceField extends scbFormField {
|
572 |
|
|
|
|
|
|
|
|
|
|
|
573 |
public function validate( $value ) {
|
574 |
return array_intersect( array_keys( $this->choices ), (array) $value );
|
575 |
}
|
576 |
|
|
|
|
|
|
|
|
|
|
|
577 |
protected function _render( $args ) {
|
578 |
$args = wp_parse_args( $args, array(
|
579 |
-
'numeric' => false,
|
580 |
'checked' => null,
|
581 |
) );
|
582 |
|
583 |
-
|
584 |
-
|
585 |
-
if ( !is_array( $checked ) )
|
586 |
-
$checked = array();
|
587 |
|
588 |
$opts = '';
|
589 |
-
foreach ( $choices as $value => $title ) {
|
590 |
$single_input = scbFormField::_checkbox( array(
|
591 |
-
'name'
|
592 |
-
'type'
|
593 |
-
'value'
|
594 |
-
'checked'
|
595 |
-
'desc'
|
596 |
-
'desc_pos' => 'after'
|
597 |
) );
|
598 |
|
599 |
-
$opts .= str_replace( scbForms::TOKEN, $single_input, $wrap_each );
|
600 |
}
|
601 |
|
602 |
-
return scbFormField::add_desc( $opts, $desc, $desc_pos );
|
603 |
}
|
604 |
|
|
|
|
|
|
|
|
|
605 |
protected function _set_value( &$args, $value ) {
|
606 |
$args['checked'] = (array) $value;
|
607 |
}
|
608 |
}
|
609 |
|
610 |
-
|
|
|
|
|
611 |
class scbSingleCheckboxField extends scbFormField {
|
612 |
|
|
|
|
|
|
|
|
|
|
|
613 |
public function validate( $value ) {
|
614 |
return (bool) $value;
|
615 |
}
|
616 |
|
|
|
|
|
|
|
|
|
|
|
617 |
protected function _render( $args ) {
|
618 |
$args = wp_parse_args( $args, array(
|
619 |
-
'value'
|
620 |
-
'desc'
|
621 |
'checked' => false,
|
622 |
-
'extra'
|
623 |
) );
|
624 |
|
625 |
-
|
626 |
-
$$key = &$val;
|
627 |
-
unset( $val );
|
628 |
-
|
629 |
-
$extra['checked'] = $checked;
|
630 |
|
631 |
-
if ( is_null( $desc ) && !is_bool( $value ) )
|
632 |
-
$desc = str_replace( '[]', '', $value );
|
633 |
|
634 |
return scbFormField::_input_gen( $args );
|
635 |
}
|
636 |
|
|
|
|
|
|
|
|
|
637 |
protected function _set_value( &$args, $value ) {
|
638 |
$args['checked'] = ( $value || ( isset( $args['value'] ) && $value == $args['value'] ) );
|
639 |
}
|
640 |
}
|
641 |
|
642 |
-
|
|
|
|
|
643 |
class scbCustomField implements scbFormField_I {
|
644 |
|
645 |
protected $args;
|
646 |
|
|
|
|
|
|
|
647 |
function __construct( $args ) {
|
648 |
$this->args = wp_parse_args( $args, array(
|
649 |
-
'render'
|
650 |
'sanitize' => 'wp_filter_kses',
|
651 |
) );
|
652 |
}
|
653 |
|
|
|
|
|
|
|
|
|
|
|
654 |
public function __get( $key ) {
|
655 |
return $this->args[ $key ];
|
656 |
}
|
657 |
|
|
|
|
|
|
|
|
|
|
|
658 |
public function __isset( $key ) {
|
659 |
return isset( $this->args[ $key ] );
|
660 |
}
|
661 |
|
|
|
|
|
|
|
|
|
|
|
662 |
public function render( $value = null ) {
|
663 |
return call_user_func( $this->render, $value, $this );
|
664 |
}
|
665 |
|
|
|
|
|
|
|
|
|
|
|
666 |
public function validate( $value ) {
|
667 |
return call_user_func( $this->sanitize, $value, $this );
|
668 |
}
|
1 |
<?php
|
2 |
|
3 |
+
/**
|
4 |
+
* Data-aware form generator
|
5 |
+
*/
|
6 |
class scbForms {
|
7 |
|
8 |
const TOKEN = '%input%';
|
9 |
|
10 |
+
/**
|
11 |
+
* @param array|scbFormField_I $args
|
12 |
+
* @param mixed $value
|
13 |
+
*
|
14 |
+
* @return string
|
15 |
+
*/
|
16 |
static function input_with_value( $args, $value ) {
|
17 |
$field = scbFormField::create( $args );
|
18 |
|
19 |
return $field->render( $value );
|
20 |
}
|
21 |
|
22 |
+
/**
|
23 |
+
* @param array|scbFormField_I $args
|
24 |
+
* @param array $formdata
|
25 |
+
*
|
26 |
+
* @return string
|
27 |
+
*/
|
28 |
static function input( $args, $formdata = null ) {
|
29 |
$field = scbFormField::create( $args );
|
30 |
|
31 |
return $field->render( scbForms::get_value( $args['name'], $formdata ) );
|
32 |
}
|
33 |
|
34 |
+
/**
|
35 |
+
* Generates a table wrapped in a form
|
36 |
+
*
|
37 |
+
* @param array $rows
|
38 |
+
* @param array $formdata
|
39 |
+
*
|
40 |
+
* @return string
|
41 |
+
*/
|
42 |
static function form_table( $rows, $formdata = null ) {
|
43 |
$output = '';
|
44 |
foreach ( $rows as $row )
|
49 |
return $output;
|
50 |
}
|
51 |
|
52 |
+
/**
|
53 |
+
* Generates a form
|
54 |
+
*
|
55 |
+
* @param array $inputs
|
56 |
+
* @param array $formdata
|
57 |
+
* @param string $nonce
|
58 |
+
*
|
59 |
+
* @return string
|
60 |
+
*/
|
61 |
static function form( $inputs, $formdata = null, $nonce ) {
|
62 |
$output = '';
|
63 |
foreach ( $inputs as $input )
|
68 |
return $output;
|
69 |
}
|
70 |
|
71 |
+
/**
|
72 |
+
* Generates a table
|
73 |
+
*
|
74 |
+
* @param array $rows
|
75 |
+
* @param array $formdata
|
76 |
+
*
|
77 |
+
* @return string
|
78 |
+
*/
|
79 |
static function table( $rows, $formdata = null ) {
|
80 |
$output = '';
|
81 |
foreach ( $rows as $row )
|
86 |
return $output;
|
87 |
}
|
88 |
|
89 |
+
/**
|
90 |
+
* Generates a table row
|
91 |
+
*
|
92 |
+
* @param array $args
|
93 |
+
* @param array $formdata
|
94 |
+
*
|
95 |
+
* @return string
|
96 |
+
*/
|
97 |
static function table_row( $args, $formdata = null ) {
|
98 |
return self::row_wrap( $args['title'], self::input( $args, $formdata ) );
|
99 |
}
|
101 |
|
102 |
// ____________WRAPPERS____________
|
103 |
|
104 |
+
/**
|
105 |
+
* @param string $content
|
106 |
+
* @param string $nonce
|
107 |
+
*
|
108 |
+
* @return string
|
109 |
+
*/
|
110 |
static function form_table_wrap( $content, $nonce = 'update_options' ) {
|
111 |
return self::form_wrap( self::table_wrap( $content ), $nonce );
|
112 |
}
|
113 |
|
114 |
+
/**
|
115 |
+
* @param string $content
|
116 |
+
* @param string $nonce
|
117 |
+
*
|
118 |
+
* @return string
|
119 |
+
*/
|
120 |
static function form_wrap( $content, $nonce = 'update_options' ) {
|
121 |
return html( "form method='post' action=''",
|
122 |
$content,
|
124 |
);
|
125 |
}
|
126 |
|
127 |
+
/**
|
128 |
+
* @param string $content
|
129 |
+
*
|
130 |
+
* @return string
|
131 |
+
*/
|
132 |
static function table_wrap( $content ) {
|
133 |
return html( "table class='form-table'", $content );
|
134 |
}
|
135 |
|
136 |
+
/**
|
137 |
+
* @param string $title
|
138 |
+
* @param string $content
|
139 |
+
*
|
140 |
+
* @return string
|
141 |
+
*/
|
142 |
static function row_wrap( $title, $content ) {
|
143 |
+
return html( 'tr',
|
144 |
html( "th scope='row'", $title ),
|
145 |
+
html( 'td', $content )
|
146 |
);
|
147 |
}
|
148 |
|
175 |
/**
|
176 |
* Traverses the formdata and retrieves the correct value.
|
177 |
*
|
178 |
+
* @param string $name The name of the value
|
179 |
+
* @param array $value The data that will be traversed
|
180 |
+
* @param mixed $fallback The value returned when the key is not found
|
181 |
*
|
182 |
* @return mixed
|
183 |
*/
|
195 |
/**
|
196 |
* Given a list of fields, validate some data.
|
197 |
*
|
198 |
+
* @param array $fields List of args that would be sent to scbForms::input()
|
199 |
+
* @param array $data The data to validate. Defaults to $_POST
|
200 |
* @param array $to_update Existing data to populate. Necessary for nested values
|
201 |
*
|
202 |
* @return array
|
225 |
* For single-choice fields, we can't distinguish either, because of how self::update_meta() works.
|
226 |
* Therefore, the 'default' parameter is always ignored.
|
227 |
*
|
228 |
+
* @param array $args Field arguments.
|
229 |
+
* @param int $object_id The object ID the metadata is attached to
|
230 |
* @param string $meta_type
|
231 |
*
|
232 |
* @return string
|
242 |
return self::input_with_value( $args, $value );
|
243 |
}
|
244 |
|
245 |
+
/**
|
246 |
+
* @param array $fields
|
247 |
+
* @param array $data
|
248 |
+
* @param int $object_id
|
249 |
+
* @param string $meta_type
|
250 |
+
*/
|
251 |
static function update_meta( $fields, $data, $object_id, $meta_type = 'post' ) {
|
252 |
foreach ( $fields as $field_args ) {
|
253 |
$key = $field_args['name'];
|
263 |
foreach ( array_diff( $old_values, $new_values ) as $value )
|
264 |
delete_metadata( $meta_type, $object_id, $key, $value );
|
265 |
} else {
|
266 |
+
$value = isset( $data[$key] ) ? $data[$key] : '';
|
267 |
|
268 |
if ( '' === $value )
|
269 |
delete_metadata( $meta_type, $object_id, $key );
|
273 |
}
|
274 |
}
|
275 |
|
276 |
+
/**
|
277 |
+
* @param array $arr
|
278 |
+
* @param string $name
|
279 |
+
* @param mixed $value
|
280 |
+
*/
|
281 |
private static function set_value( &$arr, $name, $value ) {
|
282 |
$name = (array) $name;
|
283 |
|
301 |
* A wrapper for scbForms, containing the formdata
|
302 |
*/
|
303 |
class scbForm {
|
304 |
+
protected $data = array();
|
305 |
protected $prefix = array();
|
306 |
|
307 |
+
/**
|
308 |
+
* @param array $data
|
309 |
+
* @param string|boolean $prefix
|
310 |
+
*/
|
311 |
function __construct( $data, $prefix = false ) {
|
312 |
if ( is_array( $data ) )
|
313 |
$this->data = $data;
|
316 |
$this->prefix = (array) $prefix;
|
317 |
}
|
318 |
|
319 |
+
/**
|
320 |
+
* @param string $path
|
321 |
+
*
|
322 |
+
* @return scbForm
|
323 |
+
*/
|
324 |
function traverse_to( $path ) {
|
325 |
$data = scbForms::get_value( $path, $this->data );
|
326 |
|
329 |
return new scbForm( $data, $prefix );
|
330 |
}
|
331 |
|
332 |
+
/**
|
333 |
+
* @param array $args
|
334 |
+
*
|
335 |
+
* @return string
|
336 |
+
*/
|
337 |
function input( $args ) {
|
338 |
$value = scbForms::get_value( $args['name'], $this->data );
|
339 |
|
345 |
}
|
346 |
}
|
347 |
|
348 |
+
/**
|
349 |
+
* Interface for form fields.
|
350 |
+
*/
|
351 |
interface scbFormField_I {
|
352 |
|
353 |
/**
|
369 |
function validate( $value );
|
370 |
}
|
371 |
|
372 |
+
/**
|
373 |
+
* Base class for form fields implementations.
|
374 |
+
*/
|
375 |
abstract class scbFormField implements scbFormField_I {
|
376 |
|
377 |
protected $args;
|
378 |
|
379 |
+
/**
|
380 |
+
* @param array|scbFormField_I $args
|
381 |
+
*
|
382 |
+
* @return mixed false on failure or instance of form class
|
383 |
+
*/
|
384 |
public static function create( $args ) {
|
385 |
if ( is_a( $args, 'scbFormField_I' ) )
|
386 |
return $args;
|
403 |
$args['extra'] = shortcode_parse_atts( $args['extra'] );
|
404 |
|
405 |
$args = wp_parse_args( $args, array(
|
406 |
+
'desc' => '',
|
407 |
+
'desc_pos' => 'after',
|
408 |
+
'wrap' => scbForms::TOKEN,
|
409 |
'wrap_each' => scbForms::TOKEN,
|
410 |
) );
|
411 |
|
430 |
}
|
431 |
}
|
432 |
|
433 |
+
/**
|
434 |
+
* @param array $args
|
435 |
+
*/
|
436 |
protected function __construct( $args ) {
|
437 |
$this->args = $args;
|
438 |
}
|
439 |
|
440 |
+
/**
|
441 |
+
* @param string $key
|
442 |
+
*
|
443 |
+
* @return mixed
|
444 |
+
*/
|
445 |
public function __get( $key ) {
|
446 |
return $this->args[ $key ];
|
447 |
}
|
448 |
|
449 |
+
/**
|
450 |
+
* @param string $key
|
451 |
+
*
|
452 |
+
* @return bool
|
453 |
+
*/
|
454 |
public function __isset( $key ) {
|
455 |
return isset( $this->args[ $key ] );
|
456 |
}
|
457 |
|
458 |
+
/**
|
459 |
+
* @param mixed $value
|
460 |
+
*
|
461 |
+
* @return string
|
462 |
+
*/
|
463 |
public function render( $value = null ) {
|
464 |
if ( null === $value && isset( $this->default ) )
|
465 |
$value = $this->default;
|
474 |
return str_replace( scbForms::TOKEN, $this->_render( $args ), $this->wrap );
|
475 |
}
|
476 |
|
477 |
+
/**
|
478 |
+
* Mutate the field arguments so that the value passed is rendered.
|
479 |
+
*
|
480 |
+
* @param array $args
|
481 |
+
* @param mixed $value
|
482 |
+
*/
|
483 |
abstract protected function _set_value( &$args, $value );
|
484 |
|
485 |
+
/**
|
486 |
+
* The actual rendering
|
487 |
+
*
|
488 |
+
* @param array $args
|
489 |
+
*/
|
490 |
abstract protected function _render( $args );
|
491 |
|
492 |
+
/**
|
493 |
+
* Handle args for a single checkbox or radio input
|
494 |
+
*
|
495 |
+
* @param array $args
|
496 |
+
*
|
497 |
+
* @return string
|
498 |
+
*/
|
499 |
protected static function _checkbox( $args ) {
|
500 |
$args = wp_parse_args( $args, array(
|
501 |
+
'value' => true,
|
502 |
+
'desc' => null,
|
503 |
'checked' => false,
|
504 |
+
'extra' => array(),
|
505 |
) );
|
506 |
|
507 |
+
$args['extra']['checked'] = $args['checked'];
|
|
|
|
|
508 |
|
509 |
+
if ( is_null( $args['desc'] ) && ! is_bool( $args['value'] ) )
|
510 |
+
$args['desc'] = str_replace( '[]', '', $args['value'] );
|
|
|
|
|
511 |
|
512 |
return self::_input_gen( $args );
|
513 |
}
|
514 |
|
515 |
+
/**
|
516 |
+
* Generate html with the final args
|
517 |
+
*
|
518 |
+
* @param array $args
|
519 |
+
*
|
520 |
+
* @return string
|
521 |
+
*/
|
522 |
protected static function _input_gen( $args ) {
|
523 |
+
$args = wp_parse_args( $args, array(
|
524 |
'value' => null,
|
525 |
+
'desc' => null,
|
526 |
+
'extra' => array(),
|
527 |
+
) );
|
528 |
|
529 |
+
$args['extra']['name'] = $args['name'];
|
530 |
|
531 |
+
if ( 'textarea' == $args['type'] ) {
|
532 |
+
$input = html( 'textarea', $args['extra'], esc_textarea( $args['value'] ) );
|
533 |
} else {
|
534 |
+
$args['extra']['value'] = $args['value'];
|
535 |
+
$args['extra']['type'] = $args['type'];
|
536 |
+
$input = html( 'input', $args['extra'] );
|
537 |
}
|
538 |
|
539 |
+
return self::add_label( $input, $args['desc'], $args['desc_pos'] );
|
540 |
}
|
541 |
|
542 |
+
/**
|
543 |
+
* @param string $input
|
544 |
+
* @param string $desc
|
545 |
+
* @param string $desc_pos
|
546 |
+
*
|
547 |
+
* @return string
|
548 |
+
*/
|
549 |
protected static function add_label( $input, $desc, $desc_pos ) {
|
550 |
return html( 'label', self::add_desc( $input, $desc, $desc_pos ) ) . "\n";
|
551 |
}
|
552 |
|
553 |
+
/**
|
554 |
+
* @param string $input
|
555 |
+
* @param string $desc
|
556 |
+
* @param string $desc_pos
|
557 |
+
*
|
558 |
+
* @return string
|
559 |
+
*/
|
560 |
protected static function add_desc( $input, $desc, $desc_pos ) {
|
561 |
if ( empty( $desc ) )
|
562 |
return $input;
|
567 |
return $input . ' ' . $desc;
|
568 |
}
|
569 |
|
570 |
+
/**
|
571 |
+
* @param array $args
|
572 |
+
*/
|
573 |
private static function _expand_choices( &$args ) {
|
574 |
$choices =& $args['choices'];
|
575 |
|
583 |
}
|
584 |
}
|
585 |
|
586 |
+
/**
|
587 |
+
* @param array $array
|
588 |
+
*
|
589 |
+
* @return bool
|
590 |
+
*/
|
591 |
private static function is_associative( $array ) {
|
592 |
$keys = array_keys( $array );
|
593 |
return array_keys( $keys ) !== $keys;
|
594 |
}
|
595 |
}
|
596 |
|
597 |
+
/**
|
598 |
+
* Text form field.
|
599 |
+
*/
|
600 |
class scbTextField extends scbFormField {
|
601 |
|
602 |
+
/**
|
603 |
+
* @param string $value
|
604 |
+
*
|
605 |
+
* @return string
|
606 |
+
*/
|
607 |
public function validate( $value ) {
|
608 |
$sanitize = isset( $this->sanitize ) ? $this->sanitize : 'wp_filter_kses';
|
609 |
|
610 |
return call_user_func( $sanitize, $value, $this );
|
611 |
}
|
612 |
|
613 |
+
/**
|
614 |
+
* @param array $args
|
615 |
+
*
|
616 |
+
* @return string
|
617 |
+
*/
|
618 |
protected function _render( $args ) {
|
619 |
$args = wp_parse_args( $args, array(
|
620 |
+
'value' => '',
|
621 |
'desc_pos' => 'after',
|
622 |
+
'extra' => array( 'class' => 'regular-text' ),
|
623 |
) );
|
624 |
|
625 |
+
if ( ! isset( $args['extra']['id'] ) && ! is_array( $args['name'] ) && false === strpos( $args['name'], '[' ) )
|
626 |
+
$args['extra']['id'] = $args['name'];
|
|
|
|
|
|
|
|
|
627 |
|
628 |
return scbFormField::_input_gen( $args );
|
629 |
}
|
630 |
|
631 |
+
/**
|
632 |
+
* @param array $args
|
633 |
+
* @param string $value
|
634 |
+
*/
|
635 |
protected function _set_value( &$args, $value ) {
|
636 |
$args['value'] = $value;
|
637 |
}
|
638 |
}
|
639 |
|
640 |
+
/**
|
641 |
+
* Base class for form fields with single choice.
|
642 |
+
*/
|
643 |
abstract class scbSingleChoiceField extends scbFormField {
|
644 |
|
645 |
+
/**
|
646 |
+
* @param mixed $value
|
647 |
+
*
|
648 |
+
* @return mixed|null
|
649 |
+
*/
|
650 |
public function validate( $value ) {
|
651 |
if ( isset( $this->choices[ $value ] ) )
|
652 |
return $value;
|
654 |
return null;
|
655 |
}
|
656 |
|
657 |
+
/**
|
658 |
+
* @param array $args
|
659 |
+
*
|
660 |
+
* @return string
|
661 |
+
*/
|
662 |
protected function _render( $args ) {
|
663 |
$args = wp_parse_args( $args, array(
|
664 |
+
'numeric' => false, // use numeric array instead of associative
|
665 |
+
'selected' => array( 'foo' ), // hack to make default blank
|
666 |
) );
|
667 |
|
668 |
return $this->_render_specific( $args );
|
669 |
}
|
670 |
|
671 |
+
/**
|
672 |
+
* @param array $args
|
673 |
+
* @param mixed $value
|
674 |
+
*/
|
675 |
protected function _set_value( &$args, $value ) {
|
676 |
$args['selected'] = $value;
|
677 |
}
|
678 |
|
679 |
+
/**
|
680 |
+
* @param array $args
|
681 |
+
*
|
682 |
+
* @return string
|
683 |
+
*/
|
684 |
abstract protected function _render_specific( $args );
|
685 |
}
|
686 |
|
687 |
+
/**
|
688 |
+
* Dropdown field.
|
689 |
+
*/
|
690 |
class scbSelectField extends scbSingleChoiceField {
|
691 |
|
692 |
+
/**
|
693 |
+
* @param array $args
|
694 |
+
*
|
695 |
+
* @return string
|
696 |
+
*/
|
697 |
protected function _render_specific( $args ) {
|
698 |
+
$args = wp_parse_args( $args, array(
|
699 |
+
'text' => false,
|
700 |
+
'extra' => array(),
|
701 |
+
) );
|
702 |
|
703 |
$options = array();
|
704 |
|
705 |
+
if ( false !== $args['text'] ) {
|
706 |
$options[] = array(
|
707 |
+
'value' => '',
|
708 |
+
'selected' => ( $args['selected'] == array( 'foo' ) ),
|
709 |
+
'title' => $args['text'],
|
710 |
);
|
711 |
}
|
712 |
|
713 |
+
foreach ( $args['choices'] as $value => $title ) {
|
714 |
$options[] = array(
|
715 |
+
'value' => $value,
|
716 |
+
'selected' => ( $value == $args['selected'] ),
|
717 |
+
'title' => $title,
|
718 |
);
|
719 |
}
|
720 |
|
721 |
$opts = '';
|
722 |
foreach ( $options as $option ) {
|
723 |
+
$opts .= html( 'option', array( 'value' => $option['value'], 'selected' => $option['selected'] ), $option['title'] );
|
|
|
|
|
724 |
}
|
725 |
|
726 |
+
$args['extra']['name'] = $args['name'];
|
727 |
|
728 |
+
$input = html( 'select', $args['extra'], $opts );
|
729 |
|
730 |
+
return scbFormField::add_label( $input, $args['desc'], $args['desc_pos'] );
|
731 |
}
|
732 |
}
|
733 |
|
734 |
+
/**
|
735 |
+
* Radio field.
|
736 |
+
*/
|
737 |
class scbRadiosField extends scbSelectField {
|
738 |
|
739 |
+
/**
|
740 |
+
* @param array $args
|
741 |
+
*
|
742 |
+
* @return string
|
743 |
+
*/
|
744 |
protected function _render_specific( $args ) {
|
|
|
745 |
|
746 |
+
if ( array( 'foo' ) == $args['selected'] ) {
|
747 |
// radio buttons should always have one option selected
|
748 |
+
$args['selected'] = key( $args['choices'] );
|
749 |
}
|
750 |
|
751 |
$opts = '';
|
752 |
+
foreach ( $args['choices'] as $value => $title ) {
|
753 |
$single_input = scbFormField::_checkbox( array(
|
754 |
+
'name' => $args['name'],
|
755 |
+
'type' => 'radio',
|
756 |
+
'value' => $value,
|
757 |
+
'checked' => ( $value == $args['selected'] ),
|
758 |
+
'desc' => $title,
|
759 |
+
'desc_pos' => 'after',
|
760 |
) );
|
761 |
|
762 |
+
$opts .= str_replace( scbForms::TOKEN, $single_input, $args['wrap_each'] );
|
763 |
}
|
764 |
|
765 |
+
return scbFormField::add_desc( $opts, $args['desc'], $args['desc_pos'] );
|
766 |
}
|
767 |
}
|
768 |
|
769 |
+
/**
|
770 |
+
* Checkbox field with multiple choices.
|
771 |
+
*/
|
772 |
class scbMultipleChoiceField extends scbFormField {
|
773 |
|
774 |
+
/**
|
775 |
+
* @param mixed $value
|
776 |
+
*
|
777 |
+
* @return array
|
778 |
+
*/
|
779 |
public function validate( $value ) {
|
780 |
return array_intersect( array_keys( $this->choices ), (array) $value );
|
781 |
}
|
782 |
|
783 |
+
/**
|
784 |
+
* @param array $args
|
785 |
+
*
|
786 |
+
* @return string
|
787 |
+
*/
|
788 |
protected function _render( $args ) {
|
789 |
$args = wp_parse_args( $args, array(
|
790 |
+
'numeric' => false, // use numeric array instead of associative
|
791 |
'checked' => null,
|
792 |
) );
|
793 |
|
794 |
+
if ( ! is_array( $args['checked'] ) )
|
795 |
+
$args['checked'] = array();
|
|
|
|
|
796 |
|
797 |
$opts = '';
|
798 |
+
foreach ( $args['choices'] as $value => $title ) {
|
799 |
$single_input = scbFormField::_checkbox( array(
|
800 |
+
'name' => $args['name'] . '[]',
|
801 |
+
'type' => 'checkbox',
|
802 |
+
'value' => $value,
|
803 |
+
'checked' => in_array( $value, $args['checked'] ),
|
804 |
+
'desc' => $title,
|
805 |
+
'desc_pos' => 'after',
|
806 |
) );
|
807 |
|
808 |
+
$opts .= str_replace( scbForms::TOKEN, $single_input, $args['wrap_each'] );
|
809 |
}
|
810 |
|
811 |
+
return scbFormField::add_desc( $opts, $args['desc'], $args['desc_pos'] );
|
812 |
}
|
813 |
|
814 |
+
/**
|
815 |
+
* @param array $args
|
816 |
+
* @param mixed $value
|
817 |
+
*/
|
818 |
protected function _set_value( &$args, $value ) {
|
819 |
$args['checked'] = (array) $value;
|
820 |
}
|
821 |
}
|
822 |
|
823 |
+
/**
|
824 |
+
* Checkbox field.
|
825 |
+
*/
|
826 |
class scbSingleCheckboxField extends scbFormField {
|
827 |
|
828 |
+
/**
|
829 |
+
* @param mixed $value
|
830 |
+
*
|
831 |
+
* @return boolean
|
832 |
+
*/
|
833 |
public function validate( $value ) {
|
834 |
return (bool) $value;
|
835 |
}
|
836 |
|
837 |
+
/**
|
838 |
+
* @param array $args
|
839 |
+
*
|
840 |
+
* @return string
|
841 |
+
*/
|
842 |
protected function _render( $args ) {
|
843 |
$args = wp_parse_args( $args, array(
|
844 |
+
'value' => true,
|
845 |
+
'desc' => null,
|
846 |
'checked' => false,
|
847 |
+
'extra' => array(),
|
848 |
) );
|
849 |
|
850 |
+
$args['extra']['checked'] = $args['checked'];
|
|
|
|
|
|
|
|
|
851 |
|
852 |
+
if ( is_null( $args['desc'] ) && ! is_bool( $args['value'] ) )
|
853 |
+
$args['desc'] = str_replace( '[]', '', $args['value'] );
|
854 |
|
855 |
return scbFormField::_input_gen( $args );
|
856 |
}
|
857 |
|
858 |
+
/**
|
859 |
+
* @param array $args
|
860 |
+
* @param mixed $value
|
861 |
+
*/
|
862 |
protected function _set_value( &$args, $value ) {
|
863 |
$args['checked'] = ( $value || ( isset( $args['value'] ) && $value == $args['value'] ) );
|
864 |
}
|
865 |
}
|
866 |
|
867 |
+
/**
|
868 |
+
* Wrapper field for custom callbacks.
|
869 |
+
*/
|
870 |
class scbCustomField implements scbFormField_I {
|
871 |
|
872 |
protected $args;
|
873 |
|
874 |
+
/**
|
875 |
+
* @param array $args
|
876 |
+
*/
|
877 |
function __construct( $args ) {
|
878 |
$this->args = wp_parse_args( $args, array(
|
879 |
+
'render' => 'var_dump',
|
880 |
'sanitize' => 'wp_filter_kses',
|
881 |
) );
|
882 |
}
|
883 |
|
884 |
+
/**
|
885 |
+
* @param string $key
|
886 |
+
*
|
887 |
+
* @return mixed
|
888 |
+
*/
|
889 |
public function __get( $key ) {
|
890 |
return $this->args[ $key ];
|
891 |
}
|
892 |
|
893 |
+
/**
|
894 |
+
* @param string $key
|
895 |
+
*
|
896 |
+
* @return boolean
|
897 |
+
*/
|
898 |
public function __isset( $key ) {
|
899 |
return isset( $this->args[ $key ] );
|
900 |
}
|
901 |
|
902 |
+
/**
|
903 |
+
* @param mixed $value
|
904 |
+
*
|
905 |
+
* @return string
|
906 |
+
*/
|
907 |
public function render( $value = null ) {
|
908 |
return call_user_func( $this->render, $value, $this );
|
909 |
}
|
910 |
|
911 |
+
/**
|
912 |
+
* @param mixed $value
|
913 |
+
*
|
914 |
+
* @return mixed
|
915 |
+
*/
|
916 |
public function validate( $value ) {
|
917 |
return call_user_func( $this->sanitize, $value, $this );
|
918 |
}
|
scb/PostMetabox.php
ADDED
@@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class scbPostMetabox {
|
4 |
+
|
5 |
+
private $id, $title;
|
6 |
+
|
7 |
+
private $post_types;
|
8 |
+
|
9 |
+
private $post_data = array();
|
10 |
+
|
11 |
+
protected $actions = array( 'admin_enqueue_scripts', 'post_updated_messages' );
|
12 |
+
|
13 |
+
public function __construct( $id, $title, $args = array() ) {
|
14 |
+
$this->id = $id;
|
15 |
+
$this->title = $title;
|
16 |
+
|
17 |
+
$args = wp_parse_args( $args, array(
|
18 |
+
'post_type' => 'post',
|
19 |
+
'context' => 'advanced',
|
20 |
+
'priority' => 'default'
|
21 |
+
) );
|
22 |
+
|
23 |
+
if ( is_string( $args['post_type'] ) )
|
24 |
+
$args['post_type'] = array( $args['post_type'] );
|
25 |
+
$this->post_types = $args['post_type'];
|
26 |
+
|
27 |
+
$this->context = $args['context'];
|
28 |
+
$this->priority = $args['priority'];
|
29 |
+
|
30 |
+
add_action( 'load-post.php', array( $this, 'pre_register' ) );
|
31 |
+
add_action( 'load-post-new.php', array( $this, 'pre_register' ) );
|
32 |
+
}
|
33 |
+
|
34 |
+
final public function pre_register() {
|
35 |
+
if ( ! in_array( get_current_screen()->post_type, $this->post_types ) )
|
36 |
+
return;
|
37 |
+
|
38 |
+
if ( ! $this->condition() )
|
39 |
+
return;
|
40 |
+
|
41 |
+
if ( isset( $_GET['post'] ) )
|
42 |
+
$this->post_data = $this->get_meta( intval( $_GET['post'] ) );
|
43 |
+
|
44 |
+
add_action( 'add_meta_boxes', array( $this, 'register' ) );
|
45 |
+
add_action( 'save_post', array( $this, '_save_post' ), 10, 2 );
|
46 |
+
|
47 |
+
foreach ( $this->actions as $action ) {
|
48 |
+
if ( method_exists( $this, $action ) )
|
49 |
+
add_action( $action, array( $this, $action ) );
|
50 |
+
}
|
51 |
+
}
|
52 |
+
|
53 |
+
// Additional checks before registering the metabox
|
54 |
+
protected function condition() {
|
55 |
+
return true;
|
56 |
+
}
|
57 |
+
|
58 |
+
final public function register() {
|
59 |
+
add_meta_box( $this->id, $this->title, array( $this, 'display' ), null, $this->context, $this->priority );
|
60 |
+
}
|
61 |
+
|
62 |
+
public function before_display( $form_data, $post ) {
|
63 |
+
return $form_data;
|
64 |
+
}
|
65 |
+
|
66 |
+
public function display( $post ) {
|
67 |
+
$form_fields = $this->form_fields();
|
68 |
+
if ( ! $form_fields )
|
69 |
+
return;
|
70 |
+
|
71 |
+
$form_data = $this->post_data;
|
72 |
+
$error_fields = array();
|
73 |
+
|
74 |
+
if ( isset( $form_data['_error_data_' . $this->id ] ) ) {
|
75 |
+
$data = unserialize( $form_data['_error_data_' . $this->id ] );
|
76 |
+
|
77 |
+
$error_fields = $data['fields'];
|
78 |
+
$form_data = $data['data'];
|
79 |
+
}
|
80 |
+
|
81 |
+
$form_data = $this->before_display( $form_data, $post );
|
82 |
+
|
83 |
+
$this->before_form( $post );
|
84 |
+
echo $this->table( $form_fields, $form_data, $error_fields );
|
85 |
+
$this->after_form( $post );
|
86 |
+
|
87 |
+
delete_post_meta( $post->ID, '_error_data_' . $this->id );
|
88 |
+
}
|
89 |
+
|
90 |
+
public function table( $rows, $formdata, $errors = array() ) {
|
91 |
+
$output = '';
|
92 |
+
foreach ( $rows as $row ) {
|
93 |
+
$output .= $this->table_row( $row, $formdata, $errors );
|
94 |
+
}
|
95 |
+
|
96 |
+
$output = scbForms::table_wrap( $output );
|
97 |
+
|
98 |
+
return $output;
|
99 |
+
}
|
100 |
+
|
101 |
+
public function table_row( $row, $formdata, $errors = array() ) {
|
102 |
+
$input = scbForms::input( $row, $formdata );
|
103 |
+
|
104 |
+
// If row has an error, highlight it
|
105 |
+
$style = ( in_array( $row['name'], $errors ) ) ? 'style= "background-color: #FFCCCC"' : '';
|
106 |
+
|
107 |
+
return html( 'tr',
|
108 |
+
html( "th $style scope='row'", $row['title'] ),
|
109 |
+
html( "td $style", $input )
|
110 |
+
);
|
111 |
+
}
|
112 |
+
|
113 |
+
// Display some extra HTML before the form
|
114 |
+
public function before_form( $post ) { }
|
115 |
+
|
116 |
+
// Return the list of form fields
|
117 |
+
public function form_fields() {
|
118 |
+
return array();
|
119 |
+
}
|
120 |
+
|
121 |
+
// Display some extra HTML after the form
|
122 |
+
public function after_form( $post ) { }
|
123 |
+
|
124 |
+
// Makes sure that the saving occurs only for the post being edited
|
125 |
+
final public function _save_post( $post_id, $post ) {
|
126 |
+
if ( ! isset( $_POST['action'] ) || $_POST['action'] != 'editpost' )
|
127 |
+
return;
|
128 |
+
|
129 |
+
if ( $post_id != $_POST['post_ID'] )
|
130 |
+
return;
|
131 |
+
|
132 |
+
if ( ! in_array( $post->post_type, $this->post_types ) )
|
133 |
+
return;
|
134 |
+
|
135 |
+
$this->save( $post->ID );
|
136 |
+
}
|
137 |
+
|
138 |
+
protected function save( $post_id ) {
|
139 |
+
$form_fields = $this->form_fields();
|
140 |
+
|
141 |
+
$to_update = scbForms::validate_post_data( $form_fields );
|
142 |
+
|
143 |
+
// Filter data
|
144 |
+
$to_update = $this->before_save( $to_update, $post_id );
|
145 |
+
|
146 |
+
// Validate dataset
|
147 |
+
$is_valid = $this->validate_post_data( $to_update, $post_id );
|
148 |
+
if ( $is_valid instanceof WP_Error && $is_valid->get_error_codes() ) {
|
149 |
+
|
150 |
+
$error_data = array(
|
151 |
+
'fields' => $is_valid->get_error_codes(),
|
152 |
+
'data' => $to_update
|
153 |
+
);
|
154 |
+
update_post_meta( $post_id, '_error_data_' . $this->id, $error_data );
|
155 |
+
|
156 |
+
$location = add_query_arg( 'message', 1, get_edit_post_link( $post_id, 'url' ) );
|
157 |
+
wp_redirect( apply_filters( 'redirect_post_location', $location, $post_id ) );
|
158 |
+
exit;
|
159 |
+
}
|
160 |
+
|
161 |
+
foreach ( $to_update as $key => $value ) {
|
162 |
+
update_post_meta( $post_id, $key, $value );
|
163 |
+
}
|
164 |
+
}
|
165 |
+
|
166 |
+
protected function before_save( $post_data, $post_id ) {
|
167 |
+
return $post_data;
|
168 |
+
}
|
169 |
+
|
170 |
+
protected function validate_post_data( $post_data ) {
|
171 |
+
return false;
|
172 |
+
}
|
173 |
+
|
174 |
+
private function get_meta( $post_id ) {
|
175 |
+
$meta = get_post_custom( $post_id );
|
176 |
+
foreach ( $meta as $key => $values )
|
177 |
+
$meta[$key] = $meta[$key][0];
|
178 |
+
|
179 |
+
return $meta;
|
180 |
+
}
|
181 |
+
}
|
182 |
+
|
scb/load.php
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
<?php
|
2 |
|
3 |
-
$GLOBALS['_scb_data'] = array(
|
4 |
'scbUtil', 'scbOptions', 'scbForms', 'scbTable',
|
5 |
-
'scbWidget', 'scbAdminPage', 'scbBoxesPage',
|
6 |
'scbCron', 'scbHooks',
|
7 |
) );
|
8 |
|
1 |
<?php
|
2 |
|
3 |
+
$GLOBALS['_scb_data'] = array( 58, __FILE__, array(
|
4 |
'scbUtil', 'scbOptions', 'scbForms', 'scbTable',
|
5 |
+
'scbWidget', 'scbAdminPage', 'scbBoxesPage', 'scbPostMetabox',
|
6 |
'scbCron', 'scbHooks',
|
7 |
) );
|
8 |
|