-
Notifications
You must be signed in to change notification settings - Fork 0
/
extension.driver.php
296 lines (265 loc) · 9.8 KB
/
extension.driver.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
<?php
require_once(EXTENSIONS . '/restengine/lib/class.restengine.php');
require_once(EXTENSIONS . '/restengine/lib/class.restresource.php');
Class extension_RestEngine extends Extension {
private static $pageTypeString = 'RestEngine';
private static $debugQSParam = '?XDEBUG_SESSION_START=16407';
private $restAPITrigger = false;
public function fetchNavigation() {
return array(
array(
'location' => __('System'),
'name' => __('RestEngine Settings'),
'link' => '/settings/' . self::$debugQSParam
)
);
}
public function getSubscribedDelegates() {
return array(
/*Backend*/
array(
'page' => '/blueprints/events/new/',
'delegate' => 'AppendEventFilter',
'callback' => 'appendEventFilter'
),
array(
'page' => '/blueprints/events/edit/',
'delegate' => 'AppendEventFilter',
'callback' => 'appendEventFilter'
),
/*Frontend*/
array(
'page' => '/frontend/',
'delegate' => 'FrontendPageResolved',
'callback' => 'checkPageType'
),
array(
'page' => '/frontend/',
'delegate' => 'FrontendParamsResolve',
'callback' => 'addHTTPParams'
),
array(
'page' => '/frontend/',
'delegate' => 'FrontendProcessEvents',
'callback' => 'processCRUDEvents'
),
array(
'page' => '/frontend/',
'delegate' => 'EventPreSaveFilter',
'callback' => 'eventPreSaveFilter'
),
array(
'page' => '/frontend/',
'delegate' => 'FrontendOutputPostGenerate',
'callback' => 'processTags'
)
);
}
/**
* TODO: Think about creating child object by POSTing to the parent.
* How that might integrate with the subsection manager?
* TODO: Restructure settings to have multiple sections => field relationships per page
* So you could have hierarchical data nested inside each other
* Projects page
* projects/category/single-project
* POSTing to projects would create a new category, POSTing to projects/category would create a new project
* How would the PUT logic be handled?
*/
//We want to map a field in a particular section to a parameter specified in the page.
/**
* So we need to:
* 1. Get the section number from the event.
* 2. Use the section number to return a list of fields
* 3. Map a field to a parameter name for that page.
*/
public function install() {
return Symphony::Database()->import("
DROP TABLE IF EXISTS `tbl_restengine_fieldmaps`;
CREATE TABLE `tbl_restengine_fieldmaps` (
`id` int(11) unsigned NOT NULL auto_increment,
`page_id` int(11) unsigned NOT NULL,
`section_id` int(11) unsigned NOT NULL,
`field_id` int(11) unsigned NOT NULL,
`uid_parameter` varchar(255) NOT NULL,
`format_parameter` varchar(255),
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
");
}
public function uninstall() {
return Symphony::Database()->query("
DROP TABLE IF EXISTS
`tbl_restengine_fieldmaps`
");
}
public function update($previousVersion) {
}
public function appendEventFilter(array $context) {
$context['options'][] = array(
'rest-fields-mismatch-fail',
is_array($context['selected']) ? in_array('rest-fields-mismatch-fail', $context['selected']) : false,
__('RestEngine: Fail on data mismatch between section fields and submitted data.')
);
}
public function checkPageType($context) {
if (is_array($context) &&
is_array($context['page_data']) &&
array_search(self::$pageTypeString, $context['page_data']['type']) !== false
) {
$this->restAPITrigger = true;
}
if ($this->restAPITrigger === true) {
RestEngine::init();
$page = $context['page_data'];
switch (RestEngine::getHTTPMethod()) {
case 'post':
//TODO: we are replacing the events set not appending in. need to change that to not overwrite other events
$page['events'] = 'restengine_post';
break;
case 'put':
$page['events'] = 'restengine_post';
break;
case 'delete':
//$page['events'] = 'restengine_delete';
break;
}
$context['page_data'] = $page;
RestEngine::setPageData($context['page_data']);
}
}
public function addHTTPParams($context) {
if ($this->restAPITrigger === true &&
is_array($context) &&
is_array($context['params'])
) {
$context['params']['http-method'] = XMLElement::stripInvalidXMLCharacters(RestEngine::getHttpMethod());
$context['params']['http-accept'] = XMLElement::stripInvalidXMLCharacters(RestEngine::getHTTPAccept());
$context['params']['put-content'] = XMLElement::stripInvalidXMLCharacters(RestEngine::getHTTPBodyContent());
}
}
public function processCRUDEvents() {
if ($this->restAPITrigger === true) {
switch (RestEngine::getHTTPMethod()) {
case 'post':
break;
case 'put':
break;
case 'delete':
break;
}
}
}
public function eventPreSaveFilter($context) {
if ($context['fields'] === null && (RestEngine::getHTTPMethod() === 'put' || 'post')) {
$parsedData = RestEngine::parseBodyContent();
if ($context['entry_id'] === null && RestEngine::getHTTPMethod() === 'put') {
$pageID = RestEngine::getPageID();
$urlParams = $this::getPageURLParams();
//use the page ID to look up the format and uid param settings
$settings = RestResourceManager::getByPageID($pageID);
if(array_key_exists($settings['uid_parameter'], $urlParams)){
$entryIDValue = $urlParams[$settings['uid_parameter']];
if($settings['field_id'] == 0) {
// 0 stands for using the Entry ID number directly so just
// check to see if that entry number exists in the correct section
$entrySection = EntryManager::fetchEntrySectionID($entryIDValue);
if($entrySection == $settings['section_id']) {
//good to go
$entryID = $entryIDValue;
}
} else {
$fieldType = FieldManager::fetchFieldTypeFromID($settings['field_id']);
//TODO: Eventually add in a more robust field type management to distinguish between value and handle based fields if necessary, or do it by array searching?
if($fieldType != 'memberemail') {
$query = "SELECT `entry_id` FROM `tbl_entries_data_" . $settings['field_id'] . "` WHERE `handle` = '" . $entryIDValue . "' LIMIT 1";
} else {
$query = "SELECT `entry_id` FROM `tbl_entries_data_" . $settings['field_id'] . "` WHERE `value` = '" . $entryIDValue . "' LIMIT 1";
}
$entryID = Symphony::Database()->fetchVar('entry_id', 0, $query);
}
if(is_null($entryID)) {
//no matching entry
$context['messages'][] = array('restengine:invalid-id', FALSE, __('The specified resource "%1$s" does not exist.', array($entryIDValue)));
} else {
//good to go
$context['entry_id'] = $entryID;
}
} else {
$context['messages'][] = array('restengine:settings-error', FALSE, __('Invalid Rest Resource unique ID URL parameter: %1$s.', array($settings['uid_parameter'])));
//probably some kind of error needs returning here
}
}
if (is_array($parsedData)
&& !empty($parsedData['data'])
&& $parsedData['errors'] === null
) {
//Create the post data cookie element.
General::array_to_xml($context['post_values'], $parsedData, true);
//TODO: check for field mapping
//TODO: Do we need to error when message body contains properties that we don't have fields for in the assigned section?
$context['fields'] = $parsedData['data'];
}
}
}
public function processTags($output) {
//Find the first special <restengine-status> tag and get its number value
//Unsupported status code numbers will return 500 Internal Server Error.
preg_match('#<restengine-status>(\d\d\d)</restengine-status>[\s?]#s', $output['output'], $statusTag);
//Search for the first <restengine-headers> tag and get the tag contents as $headerTag variable for subsequent parsing
preg_match('#<restengine-headers>.*?</restengine-headers>#s', $output['output'], $headerTags);
//Delete the status code tag from frontend output if one was matched by the regex above
//Set full HTTP Status code header with number from statusTag regex above
if (count($statusTag) > 0) {
$output['output'] = preg_replace('#' . preg_quote($statusTag[0], '#') . '#s', NULL, $output['output'], 1);
header('HTTP/1.1 ' . $statusTag[1] . ' ' . RestEngine::getHTTPStatusMessage($statusTag[1]));
}
//If a set of special header tags are found by the regex above delete the tag from the output
//and parse it into XML and create headers from the contents
//or throw an error page if the string isn't valid XML.
if (count($headerTags) > 0) {
$output['output'] = preg_replace('#' . preg_quote($headerTags[0], '#') . '#s', NULL, $output['output'], 1);
if (General::validateXML($headerTags[0], $errors, false)) {
$headerTagsXML = new SimpleXMLElement($headerTags[0]);
foreach ($headerTagsXML->header as $headerText) {
switch ((string)$headerText['action']) {
case "remove":
header_remove($headerText);
break;
case "replace":
header($headerText, true);
break;
default:
header($headerText, false);
break;
}
}
} else {
//If logged in as a Symphony admin/author show a more detailed error message
if (Symphony::Engine()->isLoggedIn()) {
$errorMessage = __('The following XML is not valid.') . '<p><code>' . General::sanitize($headerTags[0]) . '</code></p>' . __('Error %1$s', $errors);
} else {
//Otherwise show a generic message.
$errorMessage = __('Invalid template HTTP header XML string.');
}
throw new SymphonyErrorPage(
$errorMessage,
__('RestEngine Internal Server Error')
);
}
}
}
/*
* Utility Functions
* */
public static function getPageURLParams() {
$params = Frontend::instance()->Page()->Env();
$url = $params['url'];
return $url;
}
public static function baseURL() {
return SYMPHONY_URL . '/extension/restengine/';
}
public static function pageType() {
return self::$pageTypeString;
}
}