PluginProbe
CSS & JavaScript Toolbox / trunk
CSS & JavaScript Toolbox vtrunk
trunk 0.3 0.8 10 10.1 11 11.2 11.3 11.4 11.5 11.6 11.7 11.8 11.9 11.9.1 12 12.0 12.0.1 12.0.3 12.0.4 12.0.5 12.0.6 12.0.7 6.0 6.0.11 All 60 releases
css-javascript-toolbox / controllers / blocks-ajax.php

blocks-ajax.php in CSS & JavaScript Toolbox trunk, at controllers/blocks-ajax.php

299 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @version $ Id; blocks-ajax.php 21-03-2012 03:22:10 Ahmed Said $
4 */
5
6 // Disallow direct access.
7 defined('ABSPATH') or die("Access denied");
8
9 // import dependencies.
10 cssJSToolbox::import('framework:mvc:controller-ajax.inc.php');
11
12 /**
13 * Serve blocks page Ajax requests.
14 *
15 * The Actions resident here is global for only the blocks page, its not
16 * for a specific/single block. You can find single block
17 * actions in block-ajax.php file.
18 *
19 * @deprecated DONT ADD MORE ACTIONS HERE!
20 * @author Ahmed Said
21 * @version 6
22 */
23 class CJTBlocksAjaxController extends CJTAjaxController {
24
25 /**
26 * put your comment there...
27 *
28 * @var mixed
29 */
30 protected $controllerInfo = array('model' => 'blocks');
31
32 /**
33 * Initialize controller object.
34 *
35 * @see CJTController for more details
36 * @return void
37 */
38 public function __construct() {
39 parent::__construct();
40 // Register action.
41 $this->registryAction('create_block');
42 $this->registryAction('get_view');
43 $this->registryAction('save_blocks');
44 $this->registryAction('saveOrder');
45 $this->registryAction('loadBlock');
46 }
47
48 /**
49 * Create new block.
50 *
51 * Once this method is called a new block is saved into
52 * the database.
53 *
54 * Call this method using GET method with the following parameters.
55 * - array ids Ids for all the available blocks.
56 * - [name] string Block name.
57 * - [state] string Block state.
58 * - [location] string Block hook location.
59 *
60 * Response body is array with the following elements.
61 * - integer id New block id.
62 * - string view Block HTML code.
63 *
64 * @return void
65 */
66 public function createBlockAction($blockId = null, $blockType = null, $pinPoint = null, $viewName = null) {
67 $response = array();
68 // If viewName not provided read it from request vars.
69 if (!$viewName) {
70 $viewName = filter_input(INPUT_GET, 'viewName', FILTER_UNSAFE_RAW);
71 $viewName = is_string($viewName) ? sanitize_text_field($viewName) : null;
72 }
73 // $viewName is interpolated into a filesystem path by CJTController::getView(),
74 // which require_once's "views/blocks/{$viewName}/view.php". Restrict it to the
75 // known block views so traversal sequences can never reach that include.
76 $allowedBlockViews = array('cjt-block', 'block', 'metabox', 'create-metabox');
77 if ($viewName && !in_array($viewName, $allowedBlockViews, true)) {
78 $this->httpCode = '403 Forbidden';
79 return;
80 }
81 // Prepare parameters.
82 $defaultBlockName = 'block_' . hexdec(substr(md5(time()), 0, 6));
83 $wordpressMYSQLTime = current_time('mysql');
84 // Block data to insert.
85 $blockData = array(
86 'id' => $blockId,
87 'name' => $defaultBlockName,
88 'state' => null,
89 'location' => null,
90 'owner' => get_current_user_id(),
91 'created' => $wordpressMYSQLTime,
92 'lastModified' => $wordpressMYSQLTime,
93 'type' => $blockType,
94 'pinPoint' => $pinPoint,
95 );
96 // Read parameters from the request.
97 foreach ($blockData as $name => $default) {
98 // Use default if not supplied.
99 if (array_key_exists($name, $_GET)) {
100 $blockData[$name] = $_GET[$name];
101 }
102 }
103 // Harden the block name server-side (CVE-2025-13533 defence in depth).
104 $blockData['name'] = self::sanitizeBlockName($blockData['name']);
105 // Import block model.
106 require_once CJTOOLBOX_MODELS_PATH . '/block.php';
107 $block = new CJTBlockModel($blockData);
108 // Add block.
109 $blocksModel =& $this->model;
110 $blockId = $blocksModel->add($block->getValues(), true);
111 $blocksModel->save();
112 // Read newly added block from database.
113 $newBlockData = $blocksModel->getBlock($blockId, array('returnCodeFile' => true));
114
115 if ($newBlockData === null) {
116 throw new Exception('Could not add new block!!!');
117 }
118 else {
119 $block->setValues($newBlockData);
120 if ($viewName ){
121 // Get block view.
122 $blockView = CJTController::getView("blocks/{$viewName}");
123 // Push vars into the view.
124 $blockView->setBlock($block);
125 $response['view'] = $blockView->getTemplate('new');
126 }
127 $response['id'] = $blockId;
128 // Set response object.
129 $this->response = $response;
130 }
131 }
132
133 /**
134 * Get view content through ajax request.
135 *
136 * The method is useful for requesting Popup forms through ajax (e.g ThickBox).
137 * You can request any view specified in the $allowedViews array.
138 *
139 * Call this method using GET method with the following parameters.
140 * - viewName string Name of the view.
141 *
142 * Response body is the view content string.
143 *
144 * @return void
145 */
146 public function getViewAction() {
147 // Some views required objects to be pushed into it before displaying
148 // the controller element is a callback that a Dummy Controller from which
149 // this variables should be pushed.
150 $allowedViews = array(
151 'blocks/new' => array(),
152 );
153 // Prepare parameters.
154 $viewName = filter_input(INPUT_GET, 'viewName', FILTER_UNSAFE_RAW);
155 $viewName = is_string($viewName) ? sanitize_text_field($viewName) : '';
156 if (array_key_exists($viewName, $allowedViews) === FALSE) {
157 $this->httpCode = '403 Forbidden';
158 }
159 else {
160 // Import view file.
161 $viewInfo = $allowedViews[$viewName];
162 // Get view object.
163 $view = CJTController::getView($viewName);
164 // Push view variables.
165 foreach ((isset($viewInfo['vars']) ? $viewInfo['vars'] : array()) as $var) {
166 $view->$var = $_GET["view.{$var}"];
167 }
168 // Some views required custom pushing, this is can
169 // be done by the registered controller.
170 if (isset($viewInfo['controller'])) {
171 $viewController = $viewInfo['controller'];
172 $this->$viewController($view);
173 }
174 // Set Content type.
175 $this->httpContentType = "text/html";
176 // Get view content.
177 $this->response = $view->getTemplate('default');
178 }
179 }
180
181 /**
182 * put your comment there...
183 *
184 */
185 public function loadBlockAction() {
186 // Block Id.
187 $blockId = (int) $_GET['blockId'];
188 // Get block content.
189 $view = CJTView::getInstance('blocks/cjt-block');
190 $view->setBlock(CJTModel::create('blocks')->getBlock($blockId, array('returnCodeFile' => true)));
191 // Return View content.
192 $view->getTemplate('default');
193 $this->response = $view->structuredContent;
194 }
195
196 /**
197 * put your comment there...
198 *
199 */
200 public function saveBlocksAction()
201 {
202 $response = array();
203
204 // Blocks are sent ins single array list.
205 $blocksToSave = filter_input( INPUT_POST, 'blocks', FILTER_UNSAFE_RAW, FILTER_REQUIRE_ARRAY );
206 $calculatePinPoint = ( bool ) filter_input( INPUT_POST, 'calculatePinPoint', FILTER_SANITIZE_NUMBER_INT );
207 $createRevision = ( bool ) filter_input( INPUT_POST, 'createRevision', FILTER_SANITIZE_NUMBER_INT );
208
209 // For any reason that cause Client/JavaScript to send empty blocks,
210 // make sure we're save.
211 if ( is_array( $blocksToSave ) && ! empty( $blocksToSave ) )
212 {
213
214 foreach ( $blocksToSave as $id => $postedblockPartialData )
215 {
216 // Push block id into block data.
217 $blockData = ( object ) $postedblockPartialData;
218 $blockData->id = $id;
219
220 // Harden the block name server-side (CVE-2025-13533 defence in depth).
221 if ( isset( $blockData->name ) ) {
222 $blockData->name = self::sanitizeBlockName( $blockData->name );
223 }
224
225 // Recalculate pinPoint field value.
226 ! $calculatePinPoint or ( CJTBlockModel::arrangePins( $blockData ) && CJTBlockModel::calculateBlockPinPoint( $blockData ) );
227
228 // Create block revision.
229 ! $createRevision or $this->model->addRevision( $id, $blockData->activeFileId );
230
231 // Set lastModified field to current time.
232 $blockData->lastModified = current_time( 'mysql' );
233
234 // Update database.
235 $this->model->update( $blockData, $calculatePinPoint );
236 $this->model->save();
237
238 // Send the changes properties back to client.
239 $updatedBlockData = $this->model->getBlock($id, null, array('*'), ARRAY_A);
240
241 foreach ( $updatedBlockData as $property => $value )
242 {
243 $response[ $id ][ $property ][ 'value' ] = $value;
244 }
245
246 }
247
248 }
249
250 // Delete other blocks.
251 empty( $_POST[ 'deletedBlocks' ] ) or $this->model->delete( $_POST[ 'deletedBlocks' ] );
252
253 // Save changes.
254 $this->model->save();
255
256 // Return
257 // Set response.
258 $this->response = $response;
259
260 }
261
262 /**
263 * put your comment there...
264 *
265 */
266 public function saveOrderAction() {
267 // Read order.
268 $order = array('normal' => $_GET['order']);
269 // Centralized orders to be shared between all users!
270 $this->model->setOrder($order);
271 $this->response = array('order' => $order, 'state' => 'saved');
272 }
273
274 /**
275 * Sanitize a code-block name coming from the request.
276 *
277 * Defence-in-depth for the stored-XSS issue (CVE-2025-13533): the client-side UI
278 * already restricts block names to A-Z, 0-9, space, '-' and '_', but that check is
279 * trivially bypassed (e.g. via an HTTP proxy). We enforce the exact same contract
280 * server-side so characters that could break out of an HTML attribute/element -
281 * such as < > " ' - can never be persisted. Output is still escaped at every sink
282 * as the primary defence; this simply keeps the stored data clean.
283 *
284 * @param mixed $name Raw name value from the request.
285 * @return string Sanitized name (never empty).
286 */
287 public static function sanitizeBlockName($name) {
288 // Keep only the documented allowed characters.
289 $name = preg_replace('/[^A-Za-z0-9 _-]/', '', (string) $name);
290 // Collapse to a trimmed value and guard against an empty result.
291 $name = trim($name);
292 if ($name === '') {
293 $name = 'block_' . hexdec(substr(md5((string) time()), 0, 6));
294 }
295 // Respect the 50 char column/UI limit.
296 return substr($name, 0, 50);
297 }
298
299 } // End class.