├── .gitignore ├── README.md ├── _build ├── build.php ├── config.inc.php ├── elements │ ├── _plugins.php │ ├── _policies.php │ ├── _policy_templates.php │ ├── _resources.php │ ├── _settings.php │ ├── _templates.php │ ├── _widgets.php │ ├── chunks.php │ ├── menus.php │ └── snippets.php └── resolvers │ ├── _office.php │ ├── _policy.php │ ├── _setup.php │ ├── symlinks.php │ └── tables.php ├── assets └── components │ └── modextra │ ├── connector.php │ ├── css │ ├── index.html │ └── mgr │ │ └── main.css │ ├── index.html │ └── js │ ├── index.html │ ├── mgr │ ├── misc │ │ ├── combo.js │ │ └── utils.js │ ├── modextra.js │ ├── sections │ │ └── home.js │ └── widgets │ │ ├── home.panel.js │ │ ├── items.grid.js │ │ └── items.windows.js │ └── office │ ├── default.js │ ├── home.panel.js │ ├── items.grid.js │ └── items.windows.js ├── core └── components │ └── modextra │ ├── controllers │ ├── home.class.php │ └── office │ │ └── modextra.class.php │ ├── docs │ ├── changelog.txt │ ├── license.txt │ └── readme.txt │ ├── elements │ ├── chunks │ │ ├── item.tpl │ │ └── office.tpl │ ├── plugins │ │ └── modextra.php │ ├── snippets │ │ └── modextra.php │ └── templates │ │ └── base.tpl │ ├── lexicon │ ├── en │ │ ├── default.inc.php │ │ ├── permissions.inc.php │ │ ├── properties.inc.php │ │ └── setting.inc.php │ └── ru │ │ ├── default.inc.php │ │ ├── permissions.inc.php │ │ ├── properties.inc.php │ │ └── setting.inc.php │ ├── model │ ├── modextra.class.php │ ├── modextra │ │ ├── metadata.mysql.php │ │ ├── modextraitem.class.php │ │ └── mysql │ │ │ ├── modextraitem.class.php │ │ │ └── modextraitem.map.inc.php │ └── schema │ │ └── modextra.mysql.schema.xml │ └── processors │ ├── mgr │ └── item │ │ ├── create.class.php │ │ ├── disable.class.php │ │ ├── enable.class.php │ │ ├── get.class.php │ │ ├── getlist.class.php │ │ ├── remove.class.php │ │ └── update.class.php │ └── office │ └── item │ ├── create.class.php │ ├── disable.class.php │ ├── enable.class.php │ ├── get.class.php │ ├── getlist.class.php │ ├── remove.class.php │ └── update.class.php └── rename_it.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | config.core.php 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Quick start 2 | 3 | * Install MODX Revolution 4 | 5 | * Upload this package into the `Extras` directory in the root of site 6 | 7 | * You need to rename it to `anyOtherName` your package, so enter into SSH console and run 8 | ``` 9 | php ~/www/Extras/modExtra/rename_it.php anyOtherName 10 | ``` 11 | *path on your site may differs* 12 | 13 | * Then install it on dev site 14 | ``` 15 | php ~/www/Extras/anyOtherName/_build/build.php 16 | ``` 17 | 18 | ## Settings 19 | 20 | See `_build/config.inc.php` for editable package options. 21 | 22 | All resolvers and elements are in `_build` path. All files that begins not from `.` or `_` will be added automatically. 23 | 24 | If you will add a new type of element, you will need to add the method with that name into `build.php` script as well. 25 | 26 | ## Build and download 27 | 28 | You can build package at any time by opening `http://dev.site.com/Extras/anyOtherName/_build/build.php` 29 | 30 | If you want to download built package - just add `?download=1` to the address. 31 | 32 | ## Example deploy settings 33 | 34 | [](https://file.modx.pro/files/3/a/b/3ab2753b9e8b6c09a4ca0da819db37b6.png) [](https://file.modx.pro/files/c/1/a/c1afbb8988ab358a0b400cdcdb0391d4.png) 35 | -------------------------------------------------------------------------------- /_build/build.php: -------------------------------------------------------------------------------- 1 | modx = new modX(); 31 | $this->modx->initialize('mgr'); 32 | $this->modx->getService('error', 'error.modError'); 33 | 34 | $root = dirname(dirname(__FILE__)) . '/'; 35 | $assets = $root . 'assets/components/' . $config['name_lower'] . '/'; 36 | $core = $root . 'core/components/' . $config['name_lower'] . '/'; 37 | 38 | $this->config = array_merge([ 39 | 'log_level' => modX::LOG_LEVEL_INFO, 40 | 'log_target' => XPDO_CLI_MODE ? 'ECHO' : 'HTML', 41 | 42 | 'root' => $root, 43 | 'build' => $root . '_build/', 44 | 'elements' => $root . '_build/elements/', 45 | 'resolvers' => $root . '_build/resolvers/', 46 | 47 | 'assets' => $assets, 48 | 'core' => $core, 49 | ], $config); 50 | $this->modx->setLogLevel($this->config['log_level']); 51 | $this->modx->setLogTarget($this->config['log_target']); 52 | 53 | $this->initialize(); 54 | } 55 | 56 | 57 | /** 58 | * Initialize package builder 59 | */ 60 | protected function initialize() 61 | { 62 | $this->builder = $this->modx->getService('transport.modPackageBuilder'); 63 | $this->builder->createPackage($this->config['name_lower'], $this->config['version'], $this->config['release']); 64 | $this->builder->registerNamespace($this->config['name_lower'], false, true, '{core_path}components/' . $this->config['name_lower'] . '/'); 65 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Created Transport Package and Namespace.'); 66 | 67 | $this->category = $this->modx->newObject('modCategory'); 68 | $this->category->set('category', $this->config['name']); 69 | $this->category_attributes = [ 70 | xPDOTransport::UNIQUE_KEY => 'category', 71 | xPDOTransport::PRESERVE_KEYS => false, 72 | xPDOTransport::UPDATE_OBJECT => true, 73 | xPDOTransport::RELATED_OBJECTS => true, 74 | xPDOTransport::RELATED_OBJECT_ATTRIBUTES => [], 75 | ]; 76 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Created main Category.'); 77 | } 78 | 79 | 80 | /** 81 | * Update the model 82 | */ 83 | protected function model() 84 | { 85 | $model_file = $this->config['core'] . 'model/schema/' . $this->config['name_lower'] . '.mysql.schema.xml'; 86 | if (!file_exists($model_file) || empty(file_get_contents($model_file))) { 87 | return; 88 | } 89 | /** @var xPDOCacheManager $cache */ 90 | if ($cache = $this->modx->getCacheManager()) { 91 | $cache->deleteTree( 92 | $this->config['core'] . 'model/' . $this->config['name_lower'] . '/mysql', 93 | ['deleteTop' => true, 'skipDirs' => false, 'extensions' => []] 94 | ); 95 | } 96 | 97 | /** @var xPDOManager $manager */ 98 | $manager = $this->modx->getManager(); 99 | /** @var xPDOGenerator $generator */ 100 | $generator = $manager->getGenerator(); 101 | $generator->parseSchema( 102 | $this->config['core'] . 'model/schema/' . $this->config['name_lower'] . '.mysql.schema.xml', 103 | $this->config['core'] . 'model/' 104 | ); 105 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Model updated'); 106 | } 107 | 108 | 109 | /** 110 | * Install nodejs and update assets 111 | */ 112 | protected function assets() 113 | { 114 | $output = []; 115 | if (!file_exists($this->config['build'] . 'node_modules')) { 116 | putenv('PATH=' . trim(shell_exec('echo $PATH')) . ':' . dirname(MODX_BASE_PATH) . '/'); 117 | if (file_exists($this->config['build'] . 'package.json')) { 118 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Trying to install or update nodejs dependencies'); 119 | $output = [ 120 | shell_exec('cd ' . $this->config['build'] . ' && npm config set scripts-prepend-node-path true && npm install'), 121 | ]; 122 | } 123 | if (file_exists($this->config['build'] . 'gulpfile.js')) { 124 | $output = array_merge($output, [ 125 | shell_exec('cd ' . $this->config['build'] . ' && npm link gulp'), 126 | shell_exec('cd ' . $this->config['build'] . ' && gulp copy'), 127 | ]); 128 | } 129 | if ($output) { 130 | $this->modx->log(xPDO::LOG_LEVEL_INFO, implode("\n", array_map('trim', $output))); 131 | } 132 | } 133 | if (file_exists($this->config['build'] . 'gulpfile.js')) { 134 | $output = shell_exec('cd ' . $this->config['build'] . ' && gulp default 2>&1'); 135 | $this->modx->log(xPDO::LOG_LEVEL_INFO, 'Compile scripts and styles ' . trim($output)); 136 | } 137 | } 138 | 139 | 140 | /** 141 | * Add settings 142 | */ 143 | protected function settings() 144 | { 145 | /** @noinspection PhpIncludeInspection */ 146 | $settings = include($this->config['elements'] . 'settings.php'); 147 | if (!is_array($settings)) { 148 | $this->modx->log(modX::LOG_LEVEL_ERROR, 'Could not package in System Settings'); 149 | 150 | return; 151 | } 152 | $attributes = [ 153 | xPDOTransport::UNIQUE_KEY => 'key', 154 | xPDOTransport::PRESERVE_KEYS => true, 155 | xPDOTransport::UPDATE_OBJECT => !empty($this->config['update']['settings']), 156 | xPDOTransport::RELATED_OBJECTS => false, 157 | ]; 158 | foreach ($settings as $name => $data) { 159 | /** @var modSystemSetting $setting */ 160 | $setting = $this->modx->newObject('modSystemSetting'); 161 | $setting->fromArray(array_merge([ 162 | 'key' => $this->config['name_lower'] . '_' . $name, 163 | 'namespace' => $this->config['name_lower'], 164 | ], $data), '', true, true); 165 | $vehicle = $this->builder->createVehicle($setting, $attributes); 166 | $this->builder->putVehicle($vehicle); 167 | } 168 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Packaged in ' . count($settings) . ' System Settings'); 169 | } 170 | 171 | 172 | /** 173 | * Add menus 174 | */ 175 | protected function menus() 176 | { 177 | /** @noinspection PhpIncludeInspection */ 178 | $menus = include($this->config['elements'] . 'menus.php'); 179 | if (!is_array($menus)) { 180 | $this->modx->log(modX::LOG_LEVEL_ERROR, 'Could not package in Menus'); 181 | 182 | return; 183 | } 184 | $attributes = [ 185 | xPDOTransport::PRESERVE_KEYS => true, 186 | xPDOTransport::UPDATE_OBJECT => !empty($this->config['update']['menus']), 187 | xPDOTransport::UNIQUE_KEY => 'text', 188 | xPDOTransport::RELATED_OBJECTS => true, 189 | ]; 190 | if (is_array($menus)) { 191 | foreach ($menus as $name => $data) { 192 | /** @var modMenu $menu */ 193 | $menu = $this->modx->newObject('modMenu'); 194 | $menu->fromArray(array_merge([ 195 | 'text' => $name, 196 | 'parent' => 'components', 197 | 'namespace' => $this->config['name_lower'], 198 | 'icon' => '', 199 | 'menuindex' => 0, 200 | 'params' => '', 201 | 'handler' => '', 202 | ], $data), '', true, true); 203 | $vehicle = $this->builder->createVehicle($menu, $attributes); 204 | $this->builder->putVehicle($vehicle); 205 | } 206 | } 207 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Packaged in ' . count($menus) . ' Menus'); 208 | } 209 | 210 | 211 | /** 212 | * Add Dashboard Widgets 213 | */ 214 | protected function widgets() 215 | { 216 | /** @noinspection PhpIncludeInspection */ 217 | $widgets = include($this->config['elements'] . 'widgets.php'); 218 | if (!is_array($widgets)) { 219 | $this->modx->log(modX::LOG_LEVEL_ERROR, 'Could not package in Dashboard Widgets'); 220 | 221 | return; 222 | } 223 | $attributes = [ 224 | xPDOTransport::PRESERVE_KEYS => true, 225 | xPDOTransport::UPDATE_OBJECT => !empty($this->config['update']['widgets']), 226 | xPDOTransport::UNIQUE_KEY => 'name', 227 | ]; 228 | foreach ($widgets as $name => $data) { 229 | /** @var modDashboardWidget $widget */ 230 | $widget = $this->modx->newObject('modDashboardWidget'); 231 | $widget->fromArray(array_merge([ 232 | 'name' => $name, 233 | 'namespace' => 'core', 234 | 'lexicon' => 'core:dashboards', 235 | ], $data), '', true, true); 236 | $vehicle = $this->builder->createVehicle($widget, $attributes); 237 | $this->builder->putVehicle($vehicle); 238 | } 239 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Packaged in ' . count($widgets) . ' Dashboard Widgets'); 240 | } 241 | 242 | 243 | /** 244 | * Add resources 245 | */ 246 | protected function resources() 247 | { 248 | /** @noinspection PhpIncludeInspection */ 249 | $resources = include($this->config['elements'] . 'resources.php'); 250 | if (!is_array($resources)) { 251 | $this->modx->log(modX::LOG_LEVEL_ERROR, 'Could not package in Resources'); 252 | 253 | return; 254 | } 255 | $attributes = [ 256 | xPDOTransport::UNIQUE_KEY => 'id', 257 | xPDOTransport::PRESERVE_KEYS => true, 258 | xPDOTransport::UPDATE_OBJECT => !empty($this->config['update']['resources']), 259 | xPDOTransport::RELATED_OBJECTS => false, 260 | ]; 261 | $objects = []; 262 | foreach ($resources as $context => $items) { 263 | $menuindex = 0; 264 | foreach ($items as $alias => $item) { 265 | if (!isset($item['id'])) { 266 | $item['id'] = $this->_idx++; 267 | } 268 | $item['alias'] = $alias; 269 | $item['context_key'] = $context; 270 | $item['menuindex'] = $menuindex++; 271 | $objects = array_merge( 272 | $objects, 273 | $this->_addResource($item, $alias) 274 | ); 275 | } 276 | } 277 | 278 | /** @var modResource $resource */ 279 | foreach ($objects as $resource) { 280 | $vehicle = $this->builder->createVehicle($resource, $attributes); 281 | $this->builder->putVehicle($vehicle); 282 | } 283 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Packaged in ' . count($objects) . ' Resources'); 284 | } 285 | 286 | 287 | /** 288 | * Add plugins 289 | */ 290 | protected function plugins() 291 | { 292 | /** @noinspection PhpIncludeInspection */ 293 | $plugins = include($this->config['elements'] . 'plugins.php'); 294 | if (!is_array($plugins)) { 295 | $this->modx->log(modX::LOG_LEVEL_ERROR, 'Could not package in Plugins'); 296 | 297 | return; 298 | } 299 | $this->category_attributes[xPDOTransport::RELATED_OBJECT_ATTRIBUTES]['Plugins'] = [ 300 | xPDOTransport::UNIQUE_KEY => 'name', 301 | xPDOTransport::PRESERVE_KEYS => false, 302 | xPDOTransport::UPDATE_OBJECT => !empty($this->config['update']['plugins']), 303 | xPDOTransport::RELATED_OBJECTS => true, 304 | xPDOTransport::RELATED_OBJECT_ATTRIBUTES => [ 305 | 'PluginEvents' => [ 306 | xPDOTransport::PRESERVE_KEYS => true, 307 | xPDOTransport::UPDATE_OBJECT => true, 308 | xPDOTransport::UNIQUE_KEY => ['pluginid', 'event'], 309 | ], 310 | ], 311 | ]; 312 | $objects = []; 313 | foreach ($plugins as $name => $data) { 314 | /** @var modPlugin $plugin */ 315 | $plugin = $this->modx->newObject('modPlugin'); 316 | $plugin->fromArray(array_merge([ 317 | 'name' => $name, 318 | 'category' => 0, 319 | 'description' => @$data['description'], 320 | 'plugincode' => $this::_getContent($this->config['core'] . 'elements/plugins/' . $data['file'] . '.php'), 321 | 'static' => !empty($this->config['static']['plugins']), 322 | 'source' => 1, 323 | 'static_file' => 'core/components/' . $this->config['name_lower'] . '/elements/plugins/' . $data['file'] . '.php', 324 | ], $data), '', true, true); 325 | 326 | $events = []; 327 | if (!empty($data['events'])) { 328 | foreach ($data['events'] as $event_name => $event_data) { 329 | /** @var modPluginEvent $event */ 330 | $event = $this->modx->newObject('modPluginEvent'); 331 | $event->fromArray(array_merge([ 332 | 'event' => $event_name, 333 | 'priority' => 0, 334 | 'propertyset' => 0, 335 | ], $event_data), '', true, true); 336 | $events[] = $event; 337 | } 338 | } 339 | if (!empty($events)) { 340 | $plugin->addMany($events); 341 | } 342 | $objects[] = $plugin; 343 | } 344 | $this->category->addMany($objects); 345 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Packaged in ' . count($objects) . ' Plugins'); 346 | } 347 | 348 | 349 | /** 350 | * Add snippets 351 | */ 352 | protected function snippets() 353 | { 354 | /** @noinspection PhpIncludeInspection */ 355 | $snippets = include($this->config['elements'] . 'snippets.php'); 356 | if (!is_array($snippets)) { 357 | $this->modx->log(modX::LOG_LEVEL_ERROR, 'Could not package in Snippets'); 358 | 359 | return; 360 | } 361 | $this->category_attributes[xPDOTransport::RELATED_OBJECT_ATTRIBUTES]['Snippets'] = [ 362 | xPDOTransport::PRESERVE_KEYS => false, 363 | xPDOTransport::UPDATE_OBJECT => !empty($this->config['update']['snippets']), 364 | xPDOTransport::UNIQUE_KEY => 'name', 365 | ]; 366 | $objects = []; 367 | foreach ($snippets as $name => $data) { 368 | /** @var modSnippet[] $objects */ 369 | $objects[$name] = $this->modx->newObject('modSnippet'); 370 | $objects[$name]->fromArray(array_merge([ 371 | 'id' => 0, 372 | 'name' => $name, 373 | 'description' => @$data['description'], 374 | 'snippet' => $this::_getContent($this->config['core'] . 'elements/snippets/' . $data['file'] . '.php'), 375 | 'static' => !empty($this->config['static']['snippets']), 376 | 'source' => 1, 377 | 'static_file' => 'core/components/' . $this->config['name_lower'] . '/elements/snippets/' . $data['file'] . '.php', 378 | ], $data), '', true, true); 379 | $properties = []; 380 | foreach (@$data['properties'] as $k => $v) { 381 | $properties[] = array_merge([ 382 | 'name' => $k, 383 | 'desc' => $this->config['name_lower'] . '_prop_' . $k, 384 | 'lexicon' => $this->config['name_lower'] . ':properties', 385 | ], $v); 386 | } 387 | $objects[$name]->setProperties($properties); 388 | } 389 | $this->category->addMany($objects); 390 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Packaged in ' . count($objects) . ' Snippets'); 391 | } 392 | 393 | 394 | /** 395 | * Add chunks 396 | */ 397 | protected function chunks() 398 | { 399 | /** @noinspection PhpIncludeInspection */ 400 | $chunks = include($this->config['elements'] . 'chunks.php'); 401 | if (!is_array($chunks)) { 402 | $this->modx->log(modX::LOG_LEVEL_ERROR, 'Could not package in Chunks'); 403 | 404 | return; 405 | } 406 | $this->category_attributes[xPDOTransport::RELATED_OBJECT_ATTRIBUTES]['Chunks'] = [ 407 | xPDOTransport::PRESERVE_KEYS => false, 408 | xPDOTransport::UPDATE_OBJECT => !empty($this->config['update']['chunks']), 409 | xPDOTransport::UNIQUE_KEY => 'name', 410 | ]; 411 | $objects = []; 412 | foreach ($chunks as $name => $data) { 413 | /** @var modChunk[] $objects */ 414 | $objects[$name] = $this->modx->newObject('modChunk'); 415 | $objects[$name]->fromArray(array_merge([ 416 | 'id' => 0, 417 | 'name' => $name, 418 | 'description' => @$data['description'], 419 | 'snippet' => $this::_getContent($this->config['core'] . 'elements/chunks/' . $data['file'] . '.tpl'), 420 | 'static' => !empty($this->config['static']['chunks']), 421 | 'source' => 1, 422 | 'static_file' => 'core/components/' . $this->config['name_lower'] . '/elements/chunks/' . $data['file'] . '.tpl', 423 | ], $data), '', true, true); 424 | $objects[$name]->setProperties(@$data['properties']); 425 | } 426 | $this->category->addMany($objects); 427 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Packaged in ' . count($objects) . ' Chunks'); 428 | } 429 | 430 | 431 | /** 432 | * Add templates 433 | */ 434 | protected function templates() 435 | { 436 | /** @noinspection PhpIncludeInspection */ 437 | $templates = include($this->config['elements'] . 'templates.php'); 438 | if (!is_array($templates)) { 439 | $this->modx->log(modX::LOG_LEVEL_ERROR, 'Could not package in Templates'); 440 | 441 | return; 442 | } 443 | $this->category_attributes[xPDOTransport::RELATED_OBJECT_ATTRIBUTES]['Templates'] = [ 444 | xPDOTransport::UNIQUE_KEY => 'templatename', 445 | xPDOTransport::PRESERVE_KEYS => false, 446 | xPDOTransport::UPDATE_OBJECT => !empty($this->config['update']['templates']), 447 | xPDOTransport::RELATED_OBJECTS => false, 448 | ]; 449 | $objects = []; 450 | foreach ($templates as $name => $data) { 451 | /** @var modTemplate[] $objects */ 452 | $objects[$name] = $this->modx->newObject('modTemplate'); 453 | $objects[$name]->fromArray(array_merge([ 454 | 'templatename' => $name, 455 | 'description' => $data['description'], 456 | 'content' => $this::_getContent($this->config['core'] . 'elements/templates/' . $data['file'] . '.tpl'), 457 | 'static' => !empty($this->config['static']['templates']), 458 | 'source' => 1, 459 | 'static_file' => 'core/components/' . $this->config['name_lower'] . '/elements/templates/' . $data['file'] . '.tpl', 460 | ], $data), '', true, true); 461 | } 462 | $this->category->addMany($objects); 463 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Packaged in ' . count($objects) . ' Templates'); 464 | } 465 | 466 | 467 | /** 468 | * Add access policy 469 | */ 470 | protected function policies() 471 | { 472 | /** @noinspection PhpIncludeInspection */ 473 | $policies = include($this->config['elements'] . 'policies.php'); 474 | if (!is_array($policies)) { 475 | $this->modx->log(modX::LOG_LEVEL_ERROR, 'Could not package in Access Policies'); 476 | return; 477 | } 478 | $attributes = [ 479 | xPDOTransport::PRESERVE_KEYS => false, 480 | xPDOTransport::UNIQUE_KEY => array('name'), 481 | xPDOTransport::UPDATE_OBJECT => !empty($this->config['update']['policies']), 482 | ]; 483 | foreach ($policies as $name => $data) { 484 | if (isset($data['data'])) { 485 | $data['data'] = json_encode($data['data']); 486 | } 487 | /** @var $policy modAccessPolicy */ 488 | $policy = $this->modx->newObject('modAccessPolicy'); 489 | $policy->fromArray(array_merge(array( 490 | 'name' => $name, 491 | 'lexicon' => $this->config['name_lower'] . ':permissions', 492 | ), $data) 493 | , '', true, true); 494 | $vehicle = $this->builder->createVehicle($policy, $attributes); 495 | $this->builder->putVehicle($vehicle); 496 | } 497 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Packaged in ' . count($policies) . ' Access Policies'); 498 | } 499 | 500 | 501 | /** 502 | * Add policy templates 503 | */ 504 | protected function policy_templates() 505 | { 506 | /** @noinspection PhpIncludeInspection */ 507 | $policy_templates = include($this->config['elements'] . 'policy_templates.php'); 508 | if (!is_array($policy_templates)) { 509 | $this->modx->log(modX::LOG_LEVEL_ERROR, 'Could not package in Policy Templates'); 510 | return; 511 | } 512 | $attributes = [ 513 | xPDOTransport::PRESERVE_KEYS => false, 514 | xPDOTransport::UNIQUE_KEY => array('name'), 515 | xPDOTransport::UPDATE_OBJECT => !empty($this->config['update']['policy_templates']), 516 | xPDOTransport::RELATED_OBJECTS => true, 517 | xPDOTransport::RELATED_OBJECT_ATTRIBUTES => array( 518 | 'Permissions' => array( 519 | xPDOTransport::PRESERVE_KEYS => false, 520 | xPDOTransport::UPDATE_OBJECT => !empty($this->config['update']['permission']), 521 | xPDOTransport::UNIQUE_KEY => array('template', 'name'), 522 | ), 523 | ), 524 | ]; 525 | foreach ($policy_templates as $name => $data) { 526 | $permissions = array(); 527 | if (isset($data['permissions']) && is_array($data['permissions'])) { 528 | foreach ($data['permissions'] as $name2 => $data2) { 529 | /** @var $permission modAccessPermission */ 530 | $permission = $this->modx->newObject('modAccessPermission'); 531 | $permission->fromArray(array_merge(array( 532 | 'name' => $name2, 533 | 'description' => $name2, 534 | 'value' => true, 535 | ), $data2) 536 | , '', true, true); 537 | $permissions[] = $permission; 538 | } 539 | } 540 | /** @var $permission modAccessPolicyTemplate */ 541 | $permission = $this->modx->newObject('modAccessPolicyTemplate'); 542 | $permission->fromArray(array_merge(array( 543 | 'name' => $name, 544 | 'lexicon' => $this->config['name_lower'] . ':permissions', 545 | ), $data) 546 | , '', true, true); 547 | if (!empty($permissions)) { 548 | $permission->addMany($permissions); 549 | } 550 | $vehicle = $this->builder->createVehicle($permission, $attributes); 551 | $this->builder->putVehicle($vehicle); 552 | } 553 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Packaged in ' . count($policy_templates) . ' Access Policy Templates'); 554 | } 555 | 556 | 557 | /** 558 | * @param $filename 559 | * 560 | * @return string 561 | */ 562 | static public function _getContent($filename) 563 | { 564 | if (file_exists($filename)) { 565 | $file = trim(file_get_contents($filename)); 566 | 567 | return preg_match('#\<\?php(.*)#is', $file, $data) 568 | ? rtrim(rtrim(trim(@$data[1]), '?>')) 569 | : $file; 570 | } 571 | 572 | return ''; 573 | } 574 | 575 | 576 | /** 577 | * @param array $data 578 | * @param string $uri 579 | * @param int $parent 580 | * 581 | * @return array 582 | */ 583 | protected function _addResource(array $data, $uri, $parent = 0) 584 | { 585 | $file = $data['context_key'] . '/' . $uri; 586 | /** @var modResource $resource */ 587 | $resource = $this->modx->newObject('modResource'); 588 | $resource->fromArray(array_merge([ 589 | 'parent' => $parent, 590 | 'published' => true, 591 | 'deleted' => false, 592 | 'hidemenu' => false, 593 | 'createdon' => time(), 594 | 'template' => 1, 595 | 'isfolder' => !empty($data['isfolder']) || !empty($data['resources']), 596 | 'uri' => $uri, 597 | 'uri_override' => false, 598 | 'richtext' => false, 599 | 'searchable' => true, 600 | 'content' => $this::_getContent($this->config['core'] . 'elements/resources/' . $file . '.tpl'), 601 | ], $data), '', true, true); 602 | 603 | if (!empty($data['groups'])) { 604 | foreach ($data['groups'] as $group) { 605 | $resource->joinGroup($group); 606 | } 607 | } 608 | $resources[] = $resource; 609 | 610 | if (!empty($data['resources'])) { 611 | $menuindex = 0; 612 | foreach ($data['resources'] as $alias => $item) { 613 | if (!isset($item['id'])) { 614 | $item['id'] = $this->_idx++; 615 | } 616 | $item['alias'] = $alias; 617 | $item['context_key'] = $data['context_key']; 618 | $item['menuindex'] = $menuindex++; 619 | $resources = array_merge( 620 | $resources, 621 | $this->_addResource($item, $uri . '/' . $alias, $data['id']) 622 | ); 623 | } 624 | } 625 | 626 | return $resources; 627 | } 628 | 629 | 630 | /** 631 | * Install package 632 | */ 633 | protected function install() 634 | { 635 | $signature = $this->builder->getSignature(); 636 | $sig = explode('-', $signature); 637 | $versionSignature = explode('.', $sig[1]); 638 | 639 | /** @var modTransportPackage $package */ 640 | if (!$package = $this->modx->getObject('transport.modTransportPackage', ['signature' => $signature])) { 641 | $package = $this->modx->newObject('transport.modTransportPackage'); 642 | $package->set('signature', $signature); 643 | $package->fromArray([ 644 | 'created' => date('Y-m-d h:i:s'), 645 | 'updated' => null, 646 | 'state' => 1, 647 | 'workspace' => 1, 648 | 'provider' => 0, 649 | 'source' => $signature . '.transport.zip', 650 | 'package_name' => $this->config['name'], 651 | 'version_major' => $versionSignature[0], 652 | 'version_minor' => !empty($versionSignature[1]) ? $versionSignature[1] : 0, 653 | 'version_patch' => !empty($versionSignature[2]) ? $versionSignature[2] : 0, 654 | ]); 655 | if (!empty($sig[2])) { 656 | $r = preg_split('#([0-9]+)#', $sig[2], -1, PREG_SPLIT_DELIM_CAPTURE); 657 | if (is_array($r) && !empty($r)) { 658 | $package->set('release', $r[0]); 659 | $package->set('release_index', (isset($r[1]) ? $r[1] : '0')); 660 | } else { 661 | $package->set('release', $sig[2]); 662 | } 663 | } 664 | $package->save(); 665 | } 666 | if ($package->install()) { 667 | $this->modx->runProcessor('system/clearcache'); 668 | } 669 | } 670 | 671 | 672 | /** 673 | * @return modPackageBuilder 674 | */ 675 | public function process() 676 | { 677 | $this->model(); 678 | $this->assets(); 679 | 680 | // Add elements 681 | $elements = scandir($this->config['elements']); 682 | foreach ($elements as $element) { 683 | if (in_array($element[0], ['_', '.'])) { 684 | continue; 685 | } 686 | $name = preg_replace('#\.php$#', '', $element); 687 | if (method_exists($this, $name)) { 688 | $this->{$name}(); 689 | } 690 | } 691 | 692 | // Create main vehicle 693 | /** @var modTransportVehicle $vehicle */ 694 | $vehicle = $this->builder->createVehicle($this->category, $this->category_attributes); 695 | 696 | // Files resolvers 697 | $vehicle->resolve('file', [ 698 | 'source' => $this->config['core'], 699 | 'target' => "return MODX_CORE_PATH . 'components/';", 700 | ]); 701 | $vehicle->resolve('file', [ 702 | 'source' => $this->config['assets'], 703 | 'target' => "return MODX_ASSETS_PATH . 'components/';", 704 | ]); 705 | 706 | // Add resolvers into vehicle 707 | $resolvers = scandir($this->config['resolvers']); 708 | // Remove Office files 709 | if (!in_array('office', $resolvers)) { 710 | if ($cache = $this->modx->getCacheManager()) { 711 | $dirs = [ 712 | $this->config['assets'] . 'js/office', 713 | $this->config['core'] . 'controllers/office', 714 | $this->config['core'] . 'processors/office', 715 | ]; 716 | foreach ($dirs as $dir) { 717 | $cache->deleteTree($dir, ['deleteTop' => true, 'skipDirs' => false, 'extensions' => []]); 718 | } 719 | } 720 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Deleted Office files'); 721 | } 722 | foreach ($resolvers as $resolver) { 723 | if (in_array($resolver[0], ['_', '.'])) { 724 | continue; 725 | } 726 | if ($vehicle->resolve('php', ['source' => $this->config['resolvers'] . $resolver])) { 727 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Added resolver ' . preg_replace('#\.php$#', '', $resolver)); 728 | } 729 | } 730 | $this->builder->putVehicle($vehicle); 731 | 732 | $this->builder->setPackageAttributes([ 733 | 'changelog' => file_get_contents($this->config['core'] . 'docs/changelog.txt'), 734 | 'license' => file_get_contents($this->config['core'] . 'docs/license.txt'), 735 | 'readme' => file_get_contents($this->config['core'] . 'docs/readme.txt'), 736 | ]); 737 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Added package attributes and setup options.'); 738 | 739 | $this->modx->log(modX::LOG_LEVEL_INFO, 'Packing up transport package zip...'); 740 | $this->builder->pack(); 741 | 742 | if (!empty($this->config['install'])) { 743 | $this->install(); 744 | } 745 | 746 | return $this->builder; 747 | } 748 | 749 | } 750 | 751 | /** @var array $config */ 752 | if (!file_exists(dirname(__FILE__) . '/config.inc.php')) { 753 | exit('Could not load MODX config. Please specify correct MODX_CORE_PATH constant in config file!'); 754 | } 755 | $config = require(dirname(__FILE__) . '/config.inc.php'); 756 | $install = new modExtraPackage(MODX_CORE_PATH, $config); 757 | $builder = $install->process(); 758 | 759 | if (!empty($config['download'])) { 760 | $name = $builder->getSignature() . '.transport.zip'; 761 | if ($content = file_get_contents(MODX_CORE_PATH . '/packages/' . $name)) { 762 | header('Content-Description: File Transfer'); 763 | header('Content-Type: application/octet-stream'); 764 | header('Content-Disposition: attachment; filename=' . $name); 765 | header('Content-Transfer-Encoding: binary'); 766 | header('Expires: 0'); 767 | header('Cache-Control: must-revalidate'); 768 | header('Pragma: public'); 769 | header('Content-Length: ' . strlen($content)); 770 | exit($content); 771 | } 772 | } 773 | -------------------------------------------------------------------------------- /_build/config.inc.php: -------------------------------------------------------------------------------- 1 | 1)) { 6 | $path = dirname($path); 7 | } 8 | define('MODX_CORE_PATH', $path . '/core/'); 9 | } 10 | 11 | return [ 12 | 'name' => 'modExtra', 13 | 'name_lower' => 'modextra', 14 | 'version' => '2.0.0', 15 | 'release' => 'pl', 16 | // Install package to site right after build 17 | 'install' => true, 18 | // Which elements should be updated on package upgrade 19 | 'update' => [ 20 | 'chunks' => false, 21 | 'menus' => true, 22 | 'permission' => true, 23 | 'plugins' => true, 24 | 'policies' => true, 25 | 'policy_templates' => true, 26 | 'resources' => false, 27 | 'settings' => false, 28 | 'snippets' => true, 29 | 'templates' => false, 30 | 'widgets' => false, 31 | ], 32 | // Which elements should be static by default 33 | 'static' => [ 34 | 'plugins' => false, 35 | 'snippets' => false, 36 | 'chunks' => false, 37 | ], 38 | // Log settings 39 | 'log_level' => !empty($_REQUEST['download']) ? 0 : 3, 40 | 'log_target' => php_sapi_name() == 'cli' ? 'ECHO' : 'HTML', 41 | // Download transport.zip after build 42 | 'download' => !empty($_REQUEST['download']), 43 | ]; -------------------------------------------------------------------------------- /_build/elements/_plugins.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'file' => 'modextra', 6 | 'description' => '', 7 | 'events' => [ 8 | 'OnManagerPageInit' => [], 9 | ], 10 | ], 11 | ]; -------------------------------------------------------------------------------- /_build/elements/_policies.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'description' => 'modExtra policy description.', 6 | 'data' => [ 7 | 'modextra_save' => true, 8 | ] 9 | ], 10 | ]; -------------------------------------------------------------------------------- /_build/elements/_policy_templates.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'description' => 'modExtra policy template description.', 6 | 'template_group' => 1, 7 | 'permissions' => [ 8 | 'modextra_save' => [], 9 | ] 10 | ], 11 | ]; -------------------------------------------------------------------------------- /_build/elements/_resources.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'index' => [ 6 | 'pagetitle' => 'Home', 7 | 'template' => 1, 8 | 'hidemenu' => false, 9 | ], 10 | 'service' => [ 11 | 'pagetitle' => 'Service', 12 | 'template' => 0, 13 | 'hidemenu' => true, 14 | 'published' => false, 15 | 'resources' => [ 16 | '404' => [ 17 | 'pagetitle' => '404', 18 | 'template' => 1, 19 | 'hidemenu' => true, 20 | 'uri' => '404', 21 | 'uri_override' => true, 22 | ], 23 | 'sitemap.xml' => [ 24 | 'pagetitle' => 'Sitemap', 25 | 'template' => 0, 26 | 'hidemenu' => true, 27 | 'uri' => 'sitemap.xml', 28 | 'uri_override' => true, 29 | ], 30 | ], 31 | ], 32 | ], 33 | ]; -------------------------------------------------------------------------------- /_build/elements/_settings.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'xtype' => 'combo-boolean', 6 | 'value' => true, 7 | 'area' => 'modextra_main', 8 | ], 9 | ]; -------------------------------------------------------------------------------- /_build/elements/_templates.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'file' => 'base', 6 | 'description' => 'Base template', 7 | ], 8 | ]; -------------------------------------------------------------------------------- /_build/elements/_widgets.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'description' => '', 6 | 'type' => 'file', 7 | 'content' => '', 8 | 'namespace' => 'modextra', 9 | 'lexicon' => 'modextra:dashboards', 10 | 'size' => 'half', 11 | ], 12 | ]; -------------------------------------------------------------------------------- /_build/elements/chunks.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'file' => 'item', 6 | 'description' => '', 7 | ], 8 | 'tpl.modExtra.office' => [ 9 | 'file' => 'office', 10 | 'description' => '', 11 | ], 12 | ]; -------------------------------------------------------------------------------- /_build/elements/menus.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'description' => 'modextra_menu_desc', 6 | 'action' => 'home', 7 | //'icon' => '', 8 | ], 9 | ]; -------------------------------------------------------------------------------- /_build/elements/snippets.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'file' => 'modextra', 6 | 'description' => 'modExtra snippet to list items', 7 | 'properties' => [ 8 | 'tpl' => [ 9 | 'type' => 'textfield', 10 | 'value' => 'tpl.modExtra.item', 11 | ], 12 | 'sortby' => [ 13 | 'type' => 'textfield', 14 | 'value' => 'name', 15 | ], 16 | 'sortdir' => [ 17 | 'type' => 'list', 18 | 'options' => [ 19 | ['text' => 'ASC', 'value' => 'ASC'], 20 | ['text' => 'DESC', 'value' => 'DESC'], 21 | ], 22 | 'value' => 'ASC', 23 | ], 24 | 'limit' => [ 25 | 'type' => 'numberfield', 26 | 'value' => 10, 27 | ], 28 | 'outputSeparator' => [ 29 | 'type' => 'textfield', 30 | 'value' => "\n", 31 | ], 32 | 'toPlaceholder' => [ 33 | 'type' => 'combo-boolean', 34 | 'value' => false, 35 | ], 36 | ], 37 | ], 38 | ]; -------------------------------------------------------------------------------- /_build/resolvers/_office.php: -------------------------------------------------------------------------------- 1 | xpdo) { 6 | $modx =& $transport->xpdo; 7 | /** @var Office $office */ 8 | if ($Office = $modx->getService('Office', 'Office', MODX_CORE_PATH . 'components/office/model/office/')) { 9 | if (!($Office instanceof Office)) { 10 | $modx->log(xPDO::LOG_LEVEL_ERROR, '[modExtra] Could not register paths for Office component!'); 11 | 12 | return true; 13 | } elseif (!method_exists($Office, 'addExtension')) { 14 | $modx->log(xPDO::LOG_LEVEL_ERROR, 15 | '[modExtra] You need to update Office for support of 3rd party packages!'); 16 | 17 | return true; 18 | } 19 | 20 | /** @var array $options */ 21 | switch ($options[xPDOTransport::PACKAGE_ACTION]) { 22 | case xPDOTransport::ACTION_INSTALL: 23 | case xPDOTransport::ACTION_UPGRADE: 24 | $Office->addExtension('modExtra', '[[++core_path]]components/modextra/controllers/office/'); 25 | $modx->log(xPDO::LOG_LEVEL_INFO, '[modExtra] Successfully registered modExtra as Office extension!'); 26 | break; 27 | 28 | case xPDOTransport::ACTION_UNINSTALL: 29 | $Office->removeExtension('modExtra'); 30 | $modx->log(xPDO::LOG_LEVEL_INFO, '[modExtra] Successfully unregistered modExtra as Office extension.'); 31 | break; 32 | } 33 | } 34 | } 35 | 36 | return true; -------------------------------------------------------------------------------- /_build/resolvers/_policy.php: -------------------------------------------------------------------------------- 1 | xpdo) { 6 | $modx =& $transport->xpdo; 7 | switch ($options[xPDOTransport::PACKAGE_ACTION]) { 8 | case xPDOTransport::ACTION_INSTALL: 9 | case xPDOTransport::ACTION_UPGRADE: 10 | // Assign policy to template 11 | if ($policy = $modx->getObject('modAccessPolicy', array('name' => 'modExtraUserPolicy'))) { 12 | if ($template = $modx->getObject('modAccessPolicyTemplate', 13 | array('name' => 'modExtraUserPolicyTemplate')) 14 | ) { 15 | $policy->set('template', $template->get('id')); 16 | $policy->save(); 17 | } else { 18 | $modx->log(xPDO::LOG_LEVEL_ERROR, 19 | '[modExtra] Could not find modExtraUserPolicyTemplate Access Policy Template!'); 20 | } 21 | } else { 22 | $modx->log(xPDO::LOG_LEVEL_ERROR, '[modExtra] Could not find modExtraUserPolicyTemplate Access Policy!'); 23 | } 24 | break; 25 | } 26 | } 27 | return true; -------------------------------------------------------------------------------- /_build/resolvers/_setup.php: -------------------------------------------------------------------------------- 1 | xpdo || !($transport instanceof xPDOTransport)) { 6 | return false; 7 | } 8 | 9 | $modx =& $transport->xpdo; 10 | $packages = [ 11 | 'Ace' => [ 12 | 'version' => '1.6.5-pl', 13 | 'service_url' => 'modstore.pro', 14 | ], 15 | 'pdoTools' => [ 16 | 'version' => '2.10.0-pl', 17 | 'service_url' => 'modstore.pro', 18 | ], 19 | ]; 20 | 21 | $downloadPackage = function ($src, $dst) { 22 | if (ini_get('allow_url_fopen')) { 23 | $file = @file_get_contents($src); 24 | } else { 25 | if (function_exists('curl_init')) { 26 | $ch = curl_init(); 27 | curl_setopt($ch, CURLOPT_URL, $src); 28 | curl_setopt($ch, CURLOPT_HEADER, 0); 29 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 30 | curl_setopt($ch, CURLOPT_TIMEOUT, 180); 31 | $safeMode = @ini_get('safe_mode'); 32 | $openBasedir = @ini_get('open_basedir'); 33 | if (empty($safeMode) && empty($openBasedir)) { 34 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); 35 | } 36 | 37 | $file = curl_exec($ch); 38 | curl_close($ch); 39 | } else { 40 | return false; 41 | } 42 | } 43 | file_put_contents($dst, $file); 44 | 45 | return file_exists($dst); 46 | }; 47 | 48 | $installPackage = function ($packageName, $options = []) use ($modx, $downloadPackage) { 49 | /** @var modTransportProvider $provider */ 50 | if (!empty($options['service_url'])) { 51 | $provider = $modx->getObject('transport.modTransportProvider', [ 52 | 'service_url:LIKE' => '%' . $options['service_url'] . '%', 53 | ]); 54 | } 55 | if (empty($provider)) { 56 | $provider = $modx->getObject('transport.modTransportProvider', 1); 57 | } 58 | $modx->getVersionData(); 59 | $productVersion = $modx->version['code_name'] . '-' . $modx->version['full_version']; 60 | 61 | $response = $provider->request('package', 'GET', [ 62 | 'supports' => $productVersion, 63 | 'query' => $packageName, 64 | ]); 65 | 66 | if (!empty($response)) { 67 | $foundPackages = simplexml_load_string($response->response); 68 | foreach ($foundPackages as $foundPackage) { 69 | /** @var modTransportPackage $foundPackage */ 70 | /** @noinspection PhpUndefinedFieldInspection */ 71 | if ($foundPackage->name == $packageName) { 72 | $sig = explode('-', $foundPackage->signature); 73 | $versionSignature = explode('.', $sig[1]); 74 | /** @noinspection PhpUndefinedFieldInspection */ 75 | $url = $foundPackage->location; 76 | 77 | if (!$downloadPackage($url, $modx->getOption('core_path') . 'packages/' . $foundPackage->signature . '.transport.zip')) { 78 | return [ 79 | 'success' => 0, 80 | 'message' => "Could not download package {$packageName}.", 81 | ]; 82 | } 83 | 84 | // Add in the package as an object so it can be upgraded 85 | /** @var modTransportPackage $package */ 86 | $package = $modx->newObject('transport.modTransportPackage'); 87 | $package->set('signature', $foundPackage->signature); 88 | /** @noinspection PhpUndefinedFieldInspection */ 89 | $package->fromArray([ 90 | 'created' => date('Y-m-d h:i:s'), 91 | 'updated' => null, 92 | 'state' => 1, 93 | 'workspace' => 1, 94 | 'provider' => $provider->get('id'), 95 | 'source' => $foundPackage->signature . '.transport.zip', 96 | 'package_name' => $packageName, 97 | 'version_major' => $versionSignature[0], 98 | 'version_minor' => !empty($versionSignature[1]) ? $versionSignature[1] : 0, 99 | 'version_patch' => !empty($versionSignature[2]) ? $versionSignature[2] : 0, 100 | ]); 101 | 102 | if (!empty($sig[2])) { 103 | $r = preg_split('/([0-9]+)/', $sig[2], -1, PREG_SPLIT_DELIM_CAPTURE); 104 | if (is_array($r) && !empty($r)) { 105 | $package->set('release', $r[0]); 106 | $package->set('release_index', (isset($r[1]) ? $r[1] : '0')); 107 | } else { 108 | $package->set('release', $sig[2]); 109 | } 110 | } 111 | 112 | if ($package->save() && $package->install()) { 113 | return [ 114 | 'success' => 1, 115 | 'message' => "{$packageName} was successfully installed", 116 | ]; 117 | } else { 118 | return [ 119 | 'success' => 0, 120 | 'message' => "Could not save package {$packageName}", 121 | ]; 122 | } 123 | break; 124 | } 125 | } 126 | } else { 127 | return [ 128 | 'success' => 0, 129 | 'message' => "Could not find {$packageName} in MODX repository", 130 | ]; 131 | } 132 | 133 | return true; 134 | }; 135 | 136 | $success = false; 137 | switch ($options[xPDOTransport::PACKAGE_ACTION]) { 138 | case xPDOTransport::ACTION_INSTALL: 139 | case xPDOTransport::ACTION_UPGRADE: 140 | foreach ($packages as $name => $data) { 141 | if (!is_array($data)) { 142 | $data = ['version' => $data]; 143 | } 144 | $installed = $modx->getIterator('transport.modTransportPackage', ['package_name' => $name]); 145 | /** @var modTransportPackage $package */ 146 | foreach ($installed as $package) { 147 | if ($package->compareVersion($data['version'], '<=')) { 148 | continue(2); 149 | } 150 | } 151 | $modx->log(modX::LOG_LEVEL_INFO, "Trying to install {$name}. Please wait..."); 152 | $response = $installPackage($name, $data); 153 | $level = $response['success'] 154 | ? modX::LOG_LEVEL_INFO 155 | : modX::LOG_LEVEL_ERROR; 156 | $modx->log($level, $response['message']); 157 | } 158 | $success = true; 159 | break; 160 | 161 | case xPDOTransport::ACTION_UNINSTALL: 162 | $success = true; 163 | break; 164 | } 165 | 166 | return $success; -------------------------------------------------------------------------------- /_build/resolvers/symlinks.php: -------------------------------------------------------------------------------- 1 | xpdo) { 6 | $modx =& $transport->xpdo; 7 | 8 | $dev = MODX_BASE_PATH . 'Extras/modExtra/'; 9 | /** @var xPDOCacheManager $cache */ 10 | $cache = $modx->getCacheManager(); 11 | if (file_exists($dev) && $cache) { 12 | if (!is_link($dev . 'assets/components/modextra')) { 13 | $cache->deleteTree( 14 | $dev . 'assets/components/modextra/', 15 | ['deleteTop' => true, 'skipDirs' => false, 'extensions' => []] 16 | ); 17 | symlink(MODX_ASSETS_PATH . 'components/modextra/', $dev . 'assets/components/modextra'); 18 | } 19 | if (!is_link($dev . 'core/components/modextra')) { 20 | $cache->deleteTree( 21 | $dev . 'core/components/modextra/', 22 | ['deleteTop' => true, 'skipDirs' => false, 'extensions' => []] 23 | ); 24 | symlink(MODX_CORE_PATH . 'components/modextra/', $dev . 'core/components/modextra'); 25 | } 26 | } 27 | } 28 | 29 | return true; -------------------------------------------------------------------------------- /_build/resolvers/tables.php: -------------------------------------------------------------------------------- 1 | xpdo) { 6 | $modx =& $transport->xpdo; 7 | 8 | switch ($options[xPDOTransport::PACKAGE_ACTION]) { 9 | case xPDOTransport::ACTION_INSTALL: 10 | case xPDOTransport::ACTION_UPGRADE: 11 | $modx->addPackage('modextra', MODX_CORE_PATH . 'components/modextra/model/'); 12 | $manager = $modx->getManager(); 13 | $objects = []; 14 | $schemaFile = MODX_CORE_PATH . 'components/modextra/model/schema/modextra.mysql.schema.xml'; 15 | if (is_file($schemaFile)) { 16 | $schema = new SimpleXMLElement($schemaFile, 0, true); 17 | if (isset($schema->object)) { 18 | foreach ($schema->object as $obj) { 19 | $objects[] = (string)$obj['class']; 20 | } 21 | } 22 | unset($schema); 23 | } 24 | foreach ($objects as $class) { 25 | $table = $modx->getTableName($class); 26 | $sql = "SHOW TABLES LIKE '" . trim($table, '`') . "'"; 27 | $stmt = $modx->prepare($sql); 28 | $newTable = true; 29 | if ($stmt->execute() && $stmt->fetchAll()) { 30 | $newTable = false; 31 | } 32 | // If the table is just created 33 | if ($newTable) { 34 | $manager->createObjectContainer($class); 35 | } else { 36 | // If the table exists 37 | // 1. Operate with tables 38 | $tableFields = []; 39 | $c = $modx->prepare("SHOW COLUMNS IN {$modx->getTableName($class)}"); 40 | $c->execute(); 41 | while ($cl = $c->fetch(PDO::FETCH_ASSOC)) { 42 | $tableFields[$cl['Field']] = $cl['Field']; 43 | } 44 | foreach ($modx->getFields($class) as $field => $v) { 45 | if (in_array($field, $tableFields)) { 46 | unset($tableFields[$field]); 47 | $manager->alterField($class, $field); 48 | } else { 49 | $manager->addField($class, $field); 50 | } 51 | } 52 | foreach ($tableFields as $field) { 53 | $manager->removeField($class, $field); 54 | } 55 | // 2. Operate with indexes 56 | $indexes = []; 57 | $c = $modx->prepare("SHOW INDEX FROM {$modx->getTableName($class)}"); 58 | $c->execute(); 59 | while ($row = $c->fetch(PDO::FETCH_ASSOC)) { 60 | $name = $row['Key_name']; 61 | if (!isset($indexes[$name])) { 62 | $indexes[$name] = [$row['Column_name']]; 63 | } else { 64 | $indexes[$name][] = $row['Column_name']; 65 | } 66 | } 67 | foreach ($indexes as $name => $values) { 68 | sort($values); 69 | $indexes[$name] = implode(':', $values); 70 | } 71 | $map = $modx->getIndexMeta($class); 72 | // Remove old indexes 73 | foreach ($indexes as $key => $index) { 74 | if (!isset($map[$key])) { 75 | if ($manager->removeIndex($class, $key)) { 76 | $modx->log(modX::LOG_LEVEL_INFO, "Removed index \"{$key}\" of the table \"{$class}\""); 77 | } 78 | } 79 | } 80 | // Add or alter existing 81 | foreach ($map as $key => $index) { 82 | ksort($index['columns']); 83 | $index = implode(':', array_keys($index['columns'])); 84 | if (!isset($indexes[$key])) { 85 | if ($manager->addIndex($class, $key)) { 86 | $modx->log(modX::LOG_LEVEL_INFO, "Added index \"{$key}\" in the table \"{$class}\""); 87 | } 88 | } else { 89 | if ($index != $indexes[$key]) { 90 | if ($manager->removeIndex($class, $key) && $manager->addIndex($class, $key)) { 91 | $modx->log(modX::LOG_LEVEL_INFO, 92 | "Updated index \"{$key}\" of the table \"{$class}\"" 93 | ); 94 | } 95 | } 96 | } 97 | } 98 | } 99 | } 100 | break; 101 | 102 | case xPDOTransport::ACTION_UNINSTALL: 103 | break; 104 | } 105 | } 106 | 107 | return true; -------------------------------------------------------------------------------- /assets/components/modextra/connector.php: -------------------------------------------------------------------------------- 1 | getService('modExtra', 'modExtra', MODX_CORE_PATH . 'components/modextra/model/'); 14 | $modx->lexicon->load('modextra:default'); 15 | 16 | // handle request 17 | $corePath = $modx->getOption('modextra_core_path', null, $modx->getOption('core_path') . 'components/modextra/'); 18 | $path = $modx->getOption('processorsPath', $modExtra->config, $corePath . 'processors/'); 19 | $modx->getRequest(); 20 | 21 | /** @var modConnectorRequest $request */ 22 | $request = $modx->request; 23 | $request->handleRequest([ 24 | 'processors_path' => $path, 25 | 'location' => '', 26 | ]); -------------------------------------------------------------------------------- /assets/components/modextra/css/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modx-pro/modExtra/8295c2a6e987a0427c40ade90acb594be28b5696/assets/components/modextra/css/index.html -------------------------------------------------------------------------------- /assets/components/modextra/css/mgr/main.css: -------------------------------------------------------------------------------- 1 | /* Grid */ 2 | .modextra-modextra-row-disabled { font-style: italic; opacity: .6; color: #555; } 3 | .x-grid3-col-actions { padding: 3px 0 3px 5px; } 4 | 5 | /* Actions */ 6 | .action-red { color: darkred !important; } 7 | .action-green { color: darkgreen !important; } 8 | .action-blue { color: cadetblue !important; } 9 | .action-yellow { color: goldenrod !important; } 10 | .action-gray { color: dimgray !important; } 11 | ul.modextra-row-actions { margin: 0; padding: 0; list-style: none; } 12 | ul.modextra-row-actions li { float: left; } 13 | ul.modextra-row-actions .modextra-btn { padding: 5px; margin-right: 2px; min-width: 26px; font-size: inherit; } 14 | 15 | /* Action menu */ 16 | a.x-menu-item .x-menu-item-text, a.x-menu-item .x-menu-item-text .icon { cursor: pointer; } 17 | a.x-menu-item .x-menu-item-text .icon { line-height: 16px; top: auto; } 18 | .x-menu-list .icon { min-width: 1em; text-align: center; } 19 | .x-menu-list-item:hover .icon { color: inherit !important; } 20 | ul.modextra-row-actions .actions-menu { width: 40px; } 21 | ul.modextra-row-actions .actions-menu:after { content: " \f107"; } 22 | 23 | /* Search field */ 24 | .x-field-search-clear, 25 | .x-field-search-go { border-left: 1px solid #e4e4e4 !important; } 26 | .x-field-search-clear:hover, 27 | .x-field-search-go:hover { border-left-color: transparent !important; } 28 | .x-field-search-clear:before { content: '\f00d' !important; } 29 | .x-field-search-go { right: 31px !important; border-radius: 0 !important; } 30 | .x-field-search-go:before { content: '\f002' !important; } 31 | 32 | /* Buttons */ 33 | .modextra-btn { display: inline-block; margin-bottom: 0; font-weight: normal; text-align: center; vertical-align: middle; -ms-touch-action: manipulation; touch-action: manipulation; cursor: pointer; background-image: none; border: 1px solid transparent; white-space: nowrap; padding: 6px 12px; font-size: 14px; line-height: 1.42857143; border-radius: 4px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none } 34 | .modextra-btn:focus, .modextra-btn:active:focus, .modextra-btn.active:focus, .modextra-btn.focus, .modextra-btn:active.focus, .modextra-btn.active.focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px } 35 | .modextra-btn:hover, .modextra-btn:focus, .modextra-btn.focus { color: #333; text-decoration: none } 36 | .modextra-btn:active, .modextra-btn.active { outline: 0; background-image: none; -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125) } 37 | .modextra-btn.disabled, .modextra-btn[disabled], fieldset[disabled] .modextra-btn { cursor: not-allowed; opacity: .65; filter: alpha(opacity=65); -webkit-box-shadow: none; box-shadow: none } 38 | a.modextra-btn.disabled, fieldset[disabled] a.modextra-btn { pointer-events: none } 39 | .modextra-btn-default { color: #333; background-color: #fff; border-color: #ccc } 40 | .modextra-btn-default:focus, .modextra-btn-default.focus { color: #333; background-color: #e6e6e6; border-color: #8c8c8c } 41 | .modextra-btn-default:hover { color: #333; background-color: #e6e6e6; border-color: #adadad } 42 | .modextra-btn-default:active, .modextra-btn-default.active, .open > .dropdown-toggle.modextra-btn-default { color: #333; background-color: #e6e6e6; border-color: #adadad } 43 | .modextra-btn-default:active:hover, .modextra-btn-default.active:hover, .open > .dropdown-toggle.modextra-btn-default:hover, .modextra-btn-default:active:focus, .modextra-btn-default.active:focus, .open > .dropdown-toggle.modextra-btn-default:focus, .modextra-btn-default:active.focus, .modextra-btn-default.active.focus, .open > .dropdown-toggle.modextra-btn-default.focus { color: #333; background-color: #d4d4d4; border-color: #8c8c8c } 44 | .modextra-btn-default:active, .modextra-btn-default.active, .open > .dropdown-toggle.modextra-btn-default { background-image: none } 45 | .modextra-btn-default.disabled:hover, .modextra-btn-default[disabled]:hover, fieldset[disabled] .modextra-btn-default:hover, .modextra-btn-default.disabled:focus, .modextra-btn-default[disabled]:focus, fieldset[disabled] .modextra-btn-default:focus, .modextra-btn-default.disabled.focus, .modextra-btn-default[disabled].focus, fieldset[disabled] .modextra-btn-default.focus { background-color: #fff; border-color: #ccc } 46 | .modextra-btn-default .badge { color: #fff; background-color: #333 } 47 | .modextra-btn-primary { color: #fff; background-color: #337ab7; border-color: #2e6da4 } 48 | .modextra-btn-primary:focus, .modextra-btn-primary.focus { color: #fff; background-color: #286090; border-color: #122b40 } 49 | .modextra-btn-primary:hover { color: #fff; background-color: #286090; border-color: #204d74 } 50 | .modextra-btn-primary:active, .modextra-btn-primary.active, .open > .dropdown-toggle.modextra-btn-primary { color: #fff; background-color: #286090; border-color: #204d74 } 51 | .modextra-btn-primary:active:hover, .modextra-btn-primary.active:hover, .open > .dropdown-toggle.modextra-btn-primary:hover, .modextra-btn-primary:active:focus, .modextra-btn-primary.active:focus, .open > .dropdown-toggle.modextra-btn-primary:focus, .modextra-btn-primary:active.focus, .modextra-btn-primary.active.focus, .open > .dropdown-toggle.modextra-btn-primary.focus { color: #fff; background-color: #204d74; border-color: #122b40 } 52 | .modextra-btn-primary:active, .modextra-btn-primary.active, .open > .dropdown-toggle.modextra-btn-primary { background-image: none } 53 | .modextra-btn-primary.disabled:hover, .modextra-btn-primary[disabled]:hover, fieldset[disabled] .modextra-btn-primary:hover, .modextra-btn-primary.disabled:focus, .modextra-btn-primary[disabled]:focus, fieldset[disabled] .modextra-btn-primary:focus, .modextra-btn-primary.disabled.focus, .modextra-btn-primary[disabled].focus, fieldset[disabled] .modextra-btn-primary.focus { background-color: #337ab7; border-color: #2e6da4 } 54 | .modextra-btn-primary .badge { color: #337ab7; background-color: #fff } 55 | .modextra-btn-success { color: #fff; background-color: #5cb85c; border-color: #4cae4c } 56 | .modextra-btn-success:focus, .modextra-btn-success.focus { color: #fff; background-color: #449d44; border-color: #255625 } 57 | .modextra-btn-success:hover { color: #fff; background-color: #449d44; border-color: #398439 } 58 | .modextra-btn-success:active, .modextra-btn-success.active, .open > .dropdown-toggle.modextra-btn-success { color: #fff; background-color: #449d44; border-color: #398439 } 59 | .modextra-btn-success:active:hover, .modextra-btn-success.active:hover, .open > .dropdown-toggle.modextra-btn-success:hover, .modextra-btn-success:active:focus, .modextra-btn-success.active:focus, .open > .dropdown-toggle.modextra-btn-success:focus, .modextra-btn-success:active.focus, .modextra-btn-success.active.focus, .open > .dropdown-toggle.modextra-btn-success.focus { color: #fff; background-color: #398439; border-color: #255625 } 60 | .modextra-btn-success:active, .modextra-btn-success.active, .open > .dropdown-toggle.modextra-btn-success { background-image: none } 61 | .modextra-btn-success.disabled:hover, .modextra-btn-success[disabled]:hover, fieldset[disabled] .modextra-btn-success:hover, .modextra-btn-success.disabled:focus, .modextra-btn-success[disabled]:focus, fieldset[disabled] .modextra-btn-success:focus, .modextra-btn-success.disabled.focus, .modextra-btn-success[disabled].focus, fieldset[disabled] .modextra-btn-success.focus { background-color: #5cb85c; border-color: #4cae4c } 62 | .modextra-btn-success .badge { color: #5cb85c; background-color: #fff } 63 | .modextra-btn-info { color: #fff; background-color: #5bc0de; border-color: #46b8da } 64 | .modextra-btn-info:focus, .modextra-btn-info.focus { color: #fff; background-color: #31b0d5; border-color: #1b6d85 } 65 | .modextra-btn-info:hover { color: #fff; background-color: #31b0d5; border-color: #269abc } 66 | .modextra-btn-info:active, .modextra-btn-info.active, .open > .dropdown-toggle.modextra-btn-info { color: #fff; background-color: #31b0d5; border-color: #269abc } 67 | .modextra-btn-info:active:hover, .modextra-btn-info.active:hover, .open > .dropdown-toggle.modextra-btn-info:hover, .modextra-btn-info:active:focus, .modextra-btn-info.active:focus, .open > .dropdown-toggle.modextra-btn-info:focus, .modextra-btn-info:active.focus, .modextra-btn-info.active.focus, .open > .dropdown-toggle.modextra-btn-info.focus { color: #fff; background-color: #269abc; border-color: #1b6d85 } 68 | .modextra-btn-info:active, .modextra-btn-info.active, .open > .dropdown-toggle.modextra-btn-info { background-image: none } 69 | .modextra-btn-info.disabled:hover, .modextra-btn-info[disabled]:hover, fieldset[disabled] .modextra-btn-info:hover, .modextra-btn-info.disabled:focus, .modextra-btn-info[disabled]:focus, fieldset[disabled] .modextra-btn-info:focus, .modextra-btn-info.disabled.focus, .modextra-btn-info[disabled].focus, fieldset[disabled] .modextra-btn-info.focus { background-color: #5bc0de; border-color: #46b8da } 70 | .modextra-btn-info .badge { color: #5bc0de; background-color: #fff } 71 | .modextra-btn-warning { color: #fff; background-color: #f0ad4e; border-color: #eea236 } 72 | .modextra-btn-warning:focus, .modextra-btn-warning.focus { color: #fff; background-color: #ec971f; border-color: #985f0d } 73 | .modextra-btn-warning:hover { color: #fff; background-color: #ec971f; border-color: #d58512 } 74 | .modextra-btn-warning:active, .modextra-btn-warning.active, .open > .dropdown-toggle.modextra-btn-warning { color: #fff; background-color: #ec971f; border-color: #d58512 } 75 | .modextra-btn-warning:active:hover, .modextra-btn-warning.active:hover, .open > .dropdown-toggle.modextra-btn-warning:hover, .modextra-btn-warning:active:focus, .modextra-btn-warning.active:focus, .open > .dropdown-toggle.modextra-btn-warning:focus, .modextra-btn-warning:active.focus, .modextra-btn-warning.active.focus, .open > .dropdown-toggle.modextra-btn-warning.focus { color: #fff; background-color: #d58512; border-color: #985f0d } 76 | .modextra-btn-warning:active, .modextra-btn-warning.active, .open > .dropdown-toggle.modextra-btn-warning { background-image: none } 77 | .modextra-btn-warning.disabled:hover, .modextra-btn-warning[disabled]:hover, fieldset[disabled] .modextra-btn-warning:hover, .modextra-btn-warning.disabled:focus, .modextra-btn-warning[disabled]:focus, fieldset[disabled] .modextra-btn-warning:focus, .modextra-btn-warning.disabled.focus, .modextra-btn-warning[disabled].focus, fieldset[disabled] .modextra-btn-warning.focus { background-color: #f0ad4e; border-color: #eea236 } 78 | .modextra-btn-warning .badge { color: #f0ad4e; background-color: #fff } 79 | .modextra-btn-danger { color: #fff; background-color: #d9534f; border-color: #d43f3a } 80 | .modextra-btn-danger:focus, .modextra-btn-danger.focus { color: #fff; background-color: #c9302c; border-color: #761c19 } 81 | .modextra-btn-danger:hover { color: #fff; background-color: #c9302c; border-color: #ac2925 } 82 | .modextra-btn-danger:active, .modextra-btn-danger.active, .open > .dropdown-toggle.modextra-btn-danger { color: #fff; background-color: #c9302c; border-color: #ac2925 } 83 | .modextra-btn-danger:active:hover, .modextra-btn-danger.active:hover, .open > .dropdown-toggle.modextra-btn-danger:hover, .modextra-btn-danger:active:focus, .modextra-btn-danger.active:focus, .open > .dropdown-toggle.modextra-btn-danger:focus, .modextra-btn-danger:active.focus, .modextra-btn-danger.active.focus, .open > .dropdown-toggle.modextra-btn-danger.focus { color: #fff; background-color: #ac2925; border-color: #761c19 } 84 | .modextra-btn-danger:active, .modextra-btn-danger.active, .open > .dropdown-toggle.modextra-btn-danger { background-image: none } 85 | .modextra-btn-danger.disabled:hover, .modextra-btn-danger[disabled]:hover, fieldset[disabled] .modextra-btn-danger:hover, .modextra-btn-danger.disabled:focus, .modextra-btn-danger[disabled]:focus, fieldset[disabled] .modextra-btn-danger:focus, .modextra-btn-danger.disabled.focus, .modextra-btn-danger[disabled].focus, fieldset[disabled] .modextra-btn-danger.focus { background-color: #d9534f; border-color: #d43f3a } 86 | .modextra-btn-danger .badge { color: #d9534f; background-color: #fff } 87 | .modextra-btn-link { color: #337ab7; font-weight: normal; border-radius: 0 } 88 | .modextra-btn-link, .modextra-btn-link:active, .modextra-btn-link.active, .modextra-btn-link[disabled], fieldset[disabled] .modextra-btn-link { background-color: transparent; -webkit-box-shadow: none; box-shadow: none } 89 | .modextra-btn-link, .modextra-btn-link:hover, .modextra-btn-link:focus, .modextra-btn-link:active { border-color: transparent } 90 | .modextra-btn-link:hover, .modextra-btn-link:focus { color: #23527c; text-decoration: underline; background-color: transparent } 91 | .modextra-btn-link[disabled]:hover, fieldset[disabled] .modextra-btn-link:hover, .modextra-btn-link[disabled]:focus, fieldset[disabled] .modextra-btn-link:focus { color: #777; text-decoration: none } 92 | .modextra-btn-lg { padding: 10px 16px; font-size: 18px; line-height: 1.3333333; border-radius: 6px } 93 | .modextra-btn-sm { padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px } 94 | .modextra-btn-xs { padding: 1px 5px; font-size: 12px; line-height: 1.5; border-radius: 3px } 95 | .modextra-btn-block { display: block; width: 100% } 96 | .modextra-btn-block + .modextra-btn-block { margin-top: 5px } -------------------------------------------------------------------------------- /assets/components/modextra/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modx-pro/modExtra/8295c2a6e987a0427c40ade90acb594be28b5696/assets/components/modextra/index.html -------------------------------------------------------------------------------- /assets/components/modextra/js/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modx-pro/modExtra/8295c2a6e987a0427c40ade90acb594be28b5696/assets/components/modextra/js/index.html -------------------------------------------------------------------------------- /assets/components/modextra/js/mgr/misc/combo.js: -------------------------------------------------------------------------------- 1 | modExtra.combo.Search = function (config) { 2 | config = config || {}; 3 | Ext.applyIf(config, { 4 | xtype: 'twintrigger', 5 | ctCls: 'x-field-search', 6 | allowBlank: true, 7 | msgTarget: 'under', 8 | emptyText: _('search'), 9 | name: 'query', 10 | triggerAction: 'all', 11 | clearBtnCls: 'x-field-search-clear', 12 | searchBtnCls: 'x-field-search-go', 13 | onTrigger1Click: this._triggerSearch, 14 | onTrigger2Click: this._triggerClear, 15 | }); 16 | modExtra.combo.Search.superclass.constructor.call(this, config); 17 | this.on('render', function () { 18 | this.getEl().addKeyListener(Ext.EventObject.ENTER, function () { 19 | this._triggerSearch(); 20 | }, this); 21 | }); 22 | this.addEvents('clear', 'search'); 23 | }; 24 | Ext.extend(modExtra.combo.Search, Ext.form.TwinTriggerField, { 25 | 26 | initComponent: function () { 27 | Ext.form.TwinTriggerField.superclass.initComponent.call(this); 28 | this.triggerConfig = { 29 | tag: 'span', 30 | cls: 'x-field-search-btns', 31 | cn: [ 32 | {tag: 'div', cls: 'x-form-trigger ' + this.searchBtnCls}, 33 | {tag: 'div', cls: 'x-form-trigger ' + this.clearBtnCls} 34 | ] 35 | }; 36 | }, 37 | 38 | _triggerSearch: function () { 39 | this.fireEvent('search', this); 40 | }, 41 | 42 | _triggerClear: function () { 43 | this.fireEvent('clear', this); 44 | }, 45 | 46 | }); 47 | Ext.reg('modextra-combo-search', modExtra.combo.Search); 48 | Ext.reg('modextra-field-search', modExtra.combo.Search); -------------------------------------------------------------------------------- /assets/components/modextra/js/mgr/misc/utils.js: -------------------------------------------------------------------------------- 1 | modExtra.utils.renderBoolean = function (value) { 2 | return value 3 | ? String.format('{0}', _('yes')) 4 | : String.format('{0}', _('no')); 5 | }; 6 | 7 | modExtra.utils.getMenu = function (actions, grid, selected) { 8 | var menu = []; 9 | var cls, icon, title, action; 10 | 11 | var has_delete = false; 12 | for (var i in actions) { 13 | if (!actions.hasOwnProperty(i)) { 14 | continue; 15 | } 16 | 17 | var a = actions[i]; 18 | if (!a['menu']) { 19 | if (a == '-') { 20 | menu.push('-'); 21 | } 22 | continue; 23 | } 24 | else if (menu.length > 0 && !has_delete && (/^remove/i.test(a['action']) || /^delete/i.test(a['action']))) { 25 | menu.push('-'); 26 | has_delete = true; 27 | } 28 | 29 | if (selected.length > 1) { 30 | if (!a['multiple']) { 31 | continue; 32 | } 33 | else if (typeof(a['multiple']) == 'string') { 34 | a['title'] = a['multiple']; 35 | } 36 | } 37 | 38 | icon = a['icon'] ? a['icon'] : ''; 39 | if (typeof(a['cls']) == 'object') { 40 | if (typeof(a['cls']['menu']) != 'undefined') { 41 | icon += ' ' + a['cls']['menu']; 42 | } 43 | } 44 | else { 45 | cls = a['cls'] ? a['cls'] : ''; 46 | } 47 | title = a['title'] ? a['title'] : a['title']; 48 | action = a['action'] ? grid[a['action']] : ''; 49 | 50 | menu.push({ 51 | handler: action, 52 | text: String.format( 53 | '{2}', 54 | cls, icon, title 55 | ), 56 | scope: grid 57 | }); 58 | } 59 | 60 | return menu; 61 | }; 62 | 63 | modExtra.utils.renderActions = function (value, props, row) { 64 | var res = []; 65 | var cls, icon, title, action, item; 66 | for (var i in row.data.actions) { 67 | if (!row.data.actions.hasOwnProperty(i)) { 68 | continue; 69 | } 70 | var a = row.data.actions[i]; 71 | if (!a['button']) { 72 | continue; 73 | } 74 | 75 | icon = a['icon'] ? a['icon'] : ''; 76 | if (typeof(a['cls']) == 'object') { 77 | if (typeof(a['cls']['button']) != 'undefined') { 78 | icon += ' ' + a['cls']['button']; 79 | } 80 | } 81 | else { 82 | cls = a['cls'] ? a['cls'] : ''; 83 | } 84 | action = a['action'] ? a['action'] : ''; 85 | title = a['title'] ? a['title'] : ''; 86 | 87 | item = String.format( 88 | '
', 89 | cls, icon, action, title 90 | ); 91 | 92 | res.push(item); 93 | } 94 | 95 | return String.format( 96 | '2 | [[+name]] - 3 | [[+description]] 4 |
-------------------------------------------------------------------------------- /core/components/modextra/elements/chunks/office.tpl: -------------------------------------------------------------------------------- 1 |