view.html.php
309 lines
| 1 | <?php |
| 2 | /** |
| 3 | * @package VikAppointments |
| 4 | * @subpackage core |
| 5 | * @author E4J s.r.l. |
| 6 | * @copyright Copyright (C) 2024 E4J s.r.l. All Rights Reserved. |
| 7 | * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL |
| 8 | * @link https://vikwp.com |
| 9 | */ |
| 10 | |
| 11 | // No direct access |
| 12 | defined('ABSPATH') or die('No script kiddies please!'); |
| 13 | |
| 14 | /** |
| 15 | * VikAppointments Overrides view. |
| 16 | * @wponly |
| 17 | * |
| 18 | * @since 1.2.13 |
| 19 | */ |
| 20 | class VikAppointmentsViewOverrides extends JView |
| 21 | { |
| 22 | /** |
| 23 | * @override |
| 24 | * View display method. |
| 25 | * |
| 26 | * @return void |
| 27 | */ |
| 28 | public function display($tpl = null) |
| 29 | { |
| 30 | $input = JFactory::getApplication()->input; |
| 31 | $user = JFactory::getUser(); |
| 32 | |
| 33 | if (!$user->authorise('core.admin', 'com_vikappointments')) |
| 34 | { |
| 35 | wp_die( |
| 36 | '<h1>' . JText::translate('FATAL_ERROR') . '</h1>' . |
| 37 | '<p>' . JText::translate('RESOURCE_AUTH_ERROR') . '</p>', |
| 38 | 403 |
| 39 | ); |
| 40 | } |
| 41 | |
| 42 | // load filters from request |
| 43 | $filters = []; |
| 44 | $filters['client'] = $input->get('client', 'site', 'string'); |
| 45 | $filters['status'] = $input->get('status', '', 'string'); |
| 46 | $filters['file'] = $input->get('selectedfile', '', 'base64'); |
| 47 | $filters['override'] = $input->get('overridefile', '', 'base64'); |
| 48 | |
| 49 | $filters['hasoverride'] = false; |
| 50 | $filters['published'] = false; |
| 51 | |
| 52 | // get view model |
| 53 | $model = $this->getModel(); |
| 54 | |
| 55 | // scan overrides tree |
| 56 | $this->tree = $model->getTree($filters['client']); |
| 57 | |
| 58 | // check whether a file was specified |
| 59 | if ($filters['file']) |
| 60 | { |
| 61 | // decode file path |
| 62 | $filters['path'] = base64_decode($filters['file']); |
| 63 | $filters['override'] = base64_decode($filters['override']); |
| 64 | } |
| 65 | else if ($filters['override']) |
| 66 | { |
| 67 | // override provided, attempt to fetch the path of the original file |
| 68 | $filters['override'] = base64_decode($filters['override']); |
| 69 | |
| 70 | // search node with matching override file |
| 71 | $node = $model->isSupported($this->tree, $filters['override']); |
| 72 | |
| 73 | if ($node) |
| 74 | { |
| 75 | // node found |
| 76 | $filters['file'] = base64_encode($node['path']); |
| 77 | $filters['path'] = $node['path']; |
| 78 | } |
| 79 | else |
| 80 | { |
| 81 | // node not found |
| 82 | $filters['path'] = false; |
| 83 | $filters['override'] = ''; |
| 84 | } |
| 85 | } |
| 86 | else |
| 87 | { |
| 88 | // no selected file |
| 89 | $filters['path'] = false; |
| 90 | } |
| 91 | |
| 92 | // check if we should filter the tree according to the specified status |
| 93 | if ($filters['status'] !== '') |
| 94 | { |
| 95 | // apply filters |
| 96 | foreach ($this->tree as $i => &$node) |
| 97 | { |
| 98 | // apply filters to the first level nodes |
| 99 | if (!$this->filterOverrides($this->tree[$i], (int) $filters['status'])) |
| 100 | { |
| 101 | // detach node as there are no matching overrides |
| 102 | unset($this->tree[$i]); |
| 103 | } |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | // in case of selected file, make sure it supports overrides |
| 108 | if ($filters['path']) |
| 109 | { |
| 110 | // invoke model check |
| 111 | $supported = $model->isSupported($this->tree, $filters['path']); |
| 112 | |
| 113 | if ($supported) |
| 114 | { |
| 115 | // read default file contents |
| 116 | $filters['defaultcode'] = file_get_contents($filters['path']); |
| 117 | |
| 118 | // override supported, check if already exists |
| 119 | if (JFile::exists($filters['override'])) |
| 120 | { |
| 121 | // existing override, read contents |
| 122 | $filters['code'] = file_get_contents($filters['override']); |
| 123 | |
| 124 | $filters['hasoverride'] = true; |
| 125 | |
| 126 | $filters['published'] = true; |
| 127 | } |
| 128 | else |
| 129 | { |
| 130 | // override is missing, look for an unpublished override |
| 131 | $_path = JPath::clean(dirname($filters['override']) . '/__' . basename($filters['override'])); |
| 132 | |
| 133 | if (JFile::exists($_path)) |
| 134 | { |
| 135 | // existing unpublished override, read contents |
| 136 | $filters['code'] = file_get_contents($_path); |
| 137 | |
| 138 | $filters['hasoverride'] = true; |
| 139 | } |
| 140 | else |
| 141 | { |
| 142 | // no override, use default code |
| 143 | $filters['code'] = $filters['defaultcode']; |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | /** |
| 148 | * Adjust the comments describing the layout location |
| 149 | * to the wordpress structure. |
| 150 | */ |
| 151 | $filters['code'] = preg_replace_callback( |
| 152 | "/\*\s*(\/administrator)?\/?components\/com_vikappointments(.*?)\R/i", |
| 153 | function($match) |
| 154 | { |
| 155 | return '* /wp-content/plugins/vikappointments/' |
| 156 | . ($match[1] ? 'admin' : 'site') |
| 157 | . $match[2] . "\n"; |
| 158 | }, |
| 159 | $filters['code'] |
| 160 | ); |
| 161 | |
| 162 | // detect differences among the override and the original file |
| 163 | $filters['differences'] = $model->calcDiff($filters['code'], $filters['defaultcode']); |
| 164 | } |
| 165 | else |
| 166 | { |
| 167 | // the selected file doesn't support overrides, unset selection |
| 168 | $filters['file'] = ''; |
| 169 | $filters['override'] = ''; |
| 170 | $filters['path'] = false; |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | // register filters |
| 175 | $this->filters = $filters; |
| 176 | |
| 177 | // set up toolbar |
| 178 | $this->addToolbar(); |
| 179 | |
| 180 | // display parent |
| 181 | parent::display($tpl); |
| 182 | } |
| 183 | |
| 184 | /** |
| 185 | * Helper method to setup the toolbar. |
| 186 | * |
| 187 | * @return void |
| 188 | */ |
| 189 | public function addToolbar() |
| 190 | { |
| 191 | JToolbarHelper::title(__('VikAppointments - Page Overrides', 'vikappointments')); |
| 192 | |
| 193 | // back to the configuration |
| 194 | JToolbarHelper::back('JTOOLBAR_BACK', 'index.php?option=com_vikappointments&view=editconfigapp'); |
| 195 | |
| 196 | // check if we have a selected file |
| 197 | if ($this->filters['path']) |
| 198 | { |
| 199 | // add save button |
| 200 | JToolbarHelper::apply('override.save'); |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | /** |
| 205 | * Helper function used to build a navigator node. |
| 206 | * |
| 207 | * @param array $node The tree node. |
| 208 | * |
| 209 | * @return string The HTML to display. |
| 210 | */ |
| 211 | protected function buildNode($node) |
| 212 | { |
| 213 | // in case the selected path contains the current node, mark the node as selected |
| 214 | if ($this->filters['path'] && strpos($this->filters['path'], $node['path'] . '/') === 0) |
| 215 | { |
| 216 | $node['selected'] = true; |
| 217 | } |
| 218 | else |
| 219 | { |
| 220 | $node['selected'] = false; |
| 221 | } |
| 222 | |
| 223 | // set up current node |
| 224 | $this->currentNode = $node; |
| 225 | |
| 226 | if ($node['folder']) |
| 227 | { |
| 228 | // build folder link |
| 229 | $html = $this->loadTemplate('folder'); |
| 230 | } |
| 231 | else |
| 232 | { |
| 233 | // build file link |
| 234 | $html = $this->loadTemplate('file'); |
| 235 | } |
| 236 | |
| 237 | // check whether the node is a leaf |
| 238 | if (!empty($node['files'])) |
| 239 | { |
| 240 | // fetch node children visibility |
| 241 | $style = $node['selected'] ? '' : ' style="display:none;"'; |
| 242 | |
| 243 | // create children list |
| 244 | $html .= '<ul' . $style . '>'; |
| 245 | |
| 246 | // iterate children |
| 247 | foreach ($node['files'] as $file) |
| 248 | { |
| 249 | // create child node |
| 250 | $html .= '<li>'; |
| 251 | // build child HTML |
| 252 | $html .= $this->buildNode($file); |
| 253 | // close child node |
| 254 | $html .= '</li>'; |
| 255 | } |
| 256 | |
| 257 | // close children list |
| 258 | $html .= '</ul>'; |
| 259 | } |
| 260 | |
| 261 | return $html; |
| 262 | } |
| 263 | |
| 264 | /** |
| 265 | * Filter the available pages/widgets/layouts according to the specified status. |
| 266 | * |
| 267 | * @param array &$node The node holding the available files. |
| 268 | * @param int $status The filter status (1: active only, 0: inactive only). |
| 269 | * |
| 270 | * @return void |
| 271 | */ |
| 272 | protected function filterOverrides(array &$node, int $status) |
| 273 | { |
| 274 | // scan all the files of the node |
| 275 | foreach ($node['files'] as $k => &$file) |
| 276 | { |
| 277 | // check whether we have a sub-level node |
| 278 | if (isset($file['files'])) |
| 279 | { |
| 280 | // recursively filter the overrides under this sub-level |
| 281 | if (!$this->filterOverrides($file, $status)) |
| 282 | { |
| 283 | // retach the sub-level in case it has no overrides |
| 284 | unset($node['files'][$k]); |
| 285 | } |
| 286 | } |
| 287 | // or if we have a leaf (file override) |
| 288 | else |
| 289 | { |
| 290 | // make sure the file has an override and it matches the provided status |
| 291 | if (!$file['has'] || $file['published'] != $status) |
| 292 | { |
| 293 | // not compliant, detach file override |
| 294 | unset($node['files'][$k]); |
| 295 | } |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | if (count($node['files']) === 0) |
| 300 | { |
| 301 | // return false in case the node should be detached from the tree |
| 302 | return false; |
| 303 | } |
| 304 | |
| 305 | // the node contains at least a matching override |
| 306 | return true; |
| 307 | } |
| 308 | } |
| 309 |