Как создать свою сборку для MODX

Как и обещал, выкладываю инструкцию — как на основе сборки siteExtra сделать свою сборку. Скачать исходники можно на гитхабе https://github.com/ilyautkin/siteExtra. Полученный архив загружаем на сайт и распаковываем.



И так, сначала давайте придумаем название новой сборки — чтобы всегда сразу знать, что это наш продукт. Представим, что мы пользуемся не бутстрапом, а фреймворком Simpliste. Так и назовём нашу сборку, Simpliste.

Открываем файл /siteExtra-master/_build/build.transport.php и указываем наше название, вместо site. После чего открываем наш файл в браузере.

После этих манипуляций в папке /core/packages/ будет создан транспортный пакет с нашей сборкой. Его можно уже скачать и использовать в работе.

Но нам не нужна копия сборки с новым именем. Поэтому, разберёмся со структурой. Если вы знакомы с modExtra, то ничего нового для себя вы не откроете, так как за основу я брал именно этот компонент.

А если вы еще не разбирались с созданием компонентов, сейчас всё подробно объясню.

В файле build.transport.php вы могли заметить целый список резолверов.
$resolvers = array(
    'providers',
    'addons',
    'rename_htaccess',
    'remove_changelog',
    'cache_options',
    'template',
    'tvs',
    'resources',
    'settings',
    'fix_translit',
    'fix_fastuploadtv',
    'manager_customisation'
);

Это список файлов, которые будут запущены во время установки пакета. В основном, вся логика находится в них. Вы можете написать собственные резолверы и включить их в этот список. Можете убрать из списка любой резолвер — и он не будет выполнен. Сами резолверы хранятся в папке /_build/resolvers/.

Что делает каждый из резолверов?
  • providers — подключает репозиторий modstore.pro. Вы можете прописать здесь подключение любых других репозиториев
  • addons — устанавливает дополнения. В файле для каждого провайдера прописан свой список дополнений (почти в самом конце файла — строки со 129 по 151).
  • rename_htaccess — переименовывает файл ht.access. Если вы пользуетесь связкой nginx и php-fpm, для вас этот резолвер бесполезен. Если же на сервере работает Apache, то ht.access надо переименовать, чтобы заработали дружественные URL.
  • remove_changelog — удаляет файл changelog.txt, чтобы ошибка об угрозе исчезла.
  • cache_options — прописывает в конфиге пример настройки для MemCached. Я стараюсь подключать к сайтам MemCached и, чтобы он нормально заработал, нужно внести изменение в файл config.core.php. Со сборкой мне не нужно запоминать, что и куда прописывать — нужно только раскомментировать эти строки:
  • template — указывает установленный шаблон в качестве шаблона по умолчанию
  • tvs — создаёт ТВ-параметры. Если вам какие-то из ТВ-шек мешают, или, наоборот, захочется добавить ТВ, редактируйте этот файл. В нём есть пример создания ТВ типа FastUploadTV и MIGX.
  • resources — создаёт ресурсы. Тут, думаю, всё понятно.
  • settings — устанавливает значение для системных настроек. Код писался на скорую руку, но я планирую в дальнейшем приводить код в порядок и делать редактирование резолверов проще.
  • fix_translit — создаёт таблицу транслитерации. Этот резолвер можно взять в пример, если надо создать какой-нибудь файл, например, файл импорта из CSV или какой-то нужный скрипт.
  • fix_fastuploadtv — указывает правильный адрес папки /connectors/ для FastUploadTV
  • manager_customisation — создаёт правила кастомизации ресурсов. Здесь вы можете вынести определённые ТВ-параметры на первую вкладку ресурса, например.

Резолверы выполняются в порядке их следования. Поэтому, например, установка дополнений указана первым делом, а создание ресурсов происходит после того, как шаблон был указан в качестве дефолтного.

В нашей сборке ничего из этого менять необязательно. Мы переходим к следующей части.


Помимо резолверов в сборке есть 2 папки — /assets/components/simpliste/ и /core/components/simpliste/ (они были переименованы, когда мы в первый раз запустили файл build.transport.php). Всё, что вы загрузите в эти папки, будет распаковано при установке. Поэтому, файлы фреймворка Simpliste мы загрузим в /assets/components/simpliste/framework/ (на иллюстрации слева)

В папке /core/components/simpliste/elements/chunks/ хранятся все чанки, которые будут установлены. Если вы захотите создать новый чанк, не забудьте прописать его еще и в файле /_build/data/transport.chunks.php

Теперь поправим блоки на сайте — чтобы вёрстка соответствовала используемому фреймворку.

Вот, какие изменения я внёс в чанки и в шаблон:

Кроме чанков и шаблонов в сборке присутствуют плагины и сниппет. Так что работа далеко не ограничивается только вёрсткой.

Вот конечный вариант — Сборка Simpliste-1.2.0-beta.transport.zip

Предлагаю в комментариях выкладывать свои варианты сборки, чтобы все могли ими пользоваться.
Илья Уткин
08 августа 2016, 18:42
modx.pro
59
7 602
+20

Комментарии: 29

Андрей Сухомозгий
08 августа 2016, 23:10
0
Очень здорово!
Илья, а подскажи вот такой момент. У тебя один шаблон всего идет в сборке и написано, что ресолвер «указывает установленный шаблон в качестве шаблона по умолчанию». А если я несколько шаблонов создам и захочу какой-то из них сделать по умолчанию? Я смотрю код и не понимаю как ресолвер понимает какой ему шаблон взять и сделать по умолчанию…
Петр
09 августа 2016, 08:13
+1
Слушай, а как запаковать уже скачанный пакет, и потом его установить, т.е чтобы из репозитория не качать?
    Илья Уткин
    09 августа 2016, 09:50
    0
    Нужно написать свой резолвер (наверное, на основе resolve.addons.php). Готового решения у меня нет.
    Павел
    09 августа 2016, 16:25
    0
    На тестовом сайте modhost долго компоненты ставятся и это больше минуты примерно занимает… Приходится несколько раз пробовать устанавливать свой пакет, пока все компоненты не будут установлены…
      Илья Уткин
      09 августа 2016, 16:35
      +3
      У меня такая ерунда была, если выбран Белорусский сервер. Попробуйте Hetzner выбрать при создании сайта
        Владимир
        10 августа 2016, 07:29
        0
        Проверил, быстро все встало, точно как установка любого другого пакета. Сервер h5.modhost.pro (Hetzner, Deutschland)
        Павел
        20 октября 2016, 02:18
        0
        Кто-нибудь в курсе, как это лечится?\
        При установки пакета такая ошибка выскакивает
        xPDOVehicle does not support resolvers of type
          Алексей
          02 декабря 2016, 12:25
          0
          Доброго времени суток! А есть возможность сделать выбор устанавливаемых tv/шаблонов/чанков/ресурсов/дополнений при установке данного дополнения? В частности изменяю эту сборку под свои нужды, и было бы очень удобно выбирать что установить чекбоксами, как в дополнении modmysettings
            Илья Уткин
            02 декабря 2016, 13:45
            0
            Такой функционал есть в планах, но когда я приступлю к его разработке — я не знаю, пока времени нет
            Alexandr
            14 декабря 2016, 11:26
            0
            Подскажите
            'CKEditor' => '1.3.0-pl',
            		            'Collections' => '3.4.2-pl',
            		            'Console' => '2.1.0-beta',
            		            'FastUploadTV' => '1.0.0-beta2',
            		            'MIGX' => '2.9.6-pl',
            		            'translit' => '1.0.0-beta',
            		            'VersionX' => '2.1.3-pl'
            Версии дополнений указывать обязательно? Что делать если вышла новая версия создавать новый пакет?
              Илья Уткин
              14 декабря 2016, 16:58
              0
              Это минимальные версии. Качаться всегда будет последняя версия.
              Aleksandr Huz
              04 января 2017, 19:04
              0
              Добрый день, Илья!

              Хочу чтобы чанки помещались в указанные каталоги.
              В файле transport.chunks.php
              пишу такой код:
              $ifCategory = $this->modx->getObject( 'modCategory', array('category' => $v['category']) );
              $getCategory = $ifCategory->get('category');
              if( $getCategory === '' ){
                  $category = $this->modx->newObject('modCategory');
                  $category->set('category', $v['category']);
                  $category->save();
                  $id_category = $category->get('id'); 
              } else {
                  $id_category = $ifCategory->get('id');
              }
              на что сборщик выдает ошибку
              Fatal error: Call to a member function get() on a non-object
              Подскажи, пожалуйста, как решить эту проблему.
                Илья Уткин
                09 января 2017, 10:04
                0
                Если категории не существует, то вторая строчка выдаёт такую ошибку. Проверять на существование категории можно прям в первой строке:
                if(!$ifCategory = $this->modx->getObject('modCategory', array('category' => $v['category']))){
                    $category = $this->modx->newObject('modCategory');
                    $category->set('category', $v['category']);
                    $category->save();
                    $id_category = $category->get('id'); 
                } else {
                    $id_category = $ifCategory->get('id');
                }
                Анатолий
                20 марта 2018, 11:31
                0
                Друзья, а как включить в сборку собственный транспортный пакет который не находится в репозитории?
                  Илья Уткин
                  20 марта 2018, 16:07
                  2
                  0
                  Создать свой приватный репозиторий и разместить пакет в нём.

                    Анатолий
                    20 марта 2018, 17:09
                    0
                    А без репозитория? Локально. Задать как-то путь к транспортнику
                      Илья Уткин
                      21 марта 2018, 12:23
                      0
                      Я такого способа не знаю.
                        Анатолий
                        21 марта 2018, 14:37
                        +1
                          Анатолий
                          22 марта 2018, 21:05
                          0
                          То есть к $addons добавить еще один массив с локальными пакетами:
                          array(
                          		'type'			=> 'transport',
                          		'path'			=> '/packages/', // путь до локальных пакетов
                          		'packages'		=> array(
                          			'lightboxgal'	=> array(
                          				'version'		=> '1.1.2-pl',
                          				'install'		=> true,
                          			),
                          			'youtubepreview'	=> array(
                          				// 'path'			=> '/packages/', // можно указать путь здесь, переопределив тот, который выше
                          				'version'		=> '1.0.0-pl',
                          				'install'		=> true,
                          			),
                          		),
                          	),
                          А дальше в резольвере обработать этот массив типа как тут:

                          foreach( $providers as $prov )
                          {
                          	$transport = (!empty($prov['type']) && $prov['type'] == 'transport') ? true : false;
                          
                          	if(!$transport && !$provider = $modx->getObject('transport.modTransportProvider', array('service_url' => $prov['service_url'])))
                          	{
                          		$provider = $modx->newObject('transport.modTransportProvider');
                          		$provider->fromArray($prov);
                          		$provider->save();
                          	}
                          
                          	if(!$transport) {
                          		$provider->getClient();
                          	}
                          
                          	foreach( $prov['packages'] as $packageName => $data )
                          	{
                          		$install = $transport
                          			? $data['install']
                          			: $data;
                          
                          		$params = array();
                          
                          		if(!$transport)
                          		{
                          			$response = $provider->request('package', 'GET',
                          				array(
                          					'query' => $packageName
                          			));
                          			if(!empty($response))
                          			{
                          				$foundPackages = simplexml_load_string($response->response);
                          				if($foundPackages['total'] > 0)
                          				{
                          					foreach($foundPackages as $foundPackage)
                          					{
                          						if(strtolower((string)$foundPackage->name) == strtolower($packageName))
                          						{
                          							$params = array(
                          								'package_name'	=> (string)$foundPackage->name,
                          								'signature'		=> (string)$foundPackage->signature,
                          								'location'		=> $foundPackage->location,
                          							);
                          						}
                          					}
                          				}
                          				else {
                          					_print('Not found: "'. $packageName .'"');
                          				}
                          			}
                          		}
                          		else if($transport)
                          		{
                          			$path = !empty($data['path'])
                          				? $data['path']
                          				: $prov['path'];
                          
                          			$signature = $packageName .'-'. $data['version'];
                          			$location = $path . $signature .'.transport.zip';
                          
                          			if(file_exists($location))
                          			{
                          				$params = array(
                          					'package_name'	=> $packageName,
                          					'signature'		=> $signature,
                          					'location'		=> $location,
                          				);
                          			}
                          		}
                          
                          		if(!empty($params))
                          		{
                          			packageSaveInstall(array_merge(
                          				$params,
                          				array(
                          					'provider'	=> is_object($provider) ? $provider->id : '0',
                          					'install'	=> $install ? $install : 0,
                          				)
                          			));
                          		}
                          	}
                          }
                    mngatoff
                    20 марта 2018, 17:13
                    1
                    +3
                    я пошел по другому пути и вполне норм вроде…
                    у меня на локалке развернут сайт build...ru, и он заряжен всем базовым из обоих репозиториев, и всеми настройками — системными, визуальными, структурными, htaccess, метатеги, роботс, 404, в общем, все, что так или иначе приходится делать на всех сайтах. Там же, на локалке, я поддерживаю сам модекс и все дополнения в последней версии. И когда новый сайт — тупо заливаю дамп на хостинг, меняю доступы к репозиторию modstore и парольку, и можно работать без предварительной нудятины.
                      SEQUEL.ONE
                      23 марта 2018, 21:54
                      0
                      Подскажите, а как в ресольвере создать новую системную настройку?

                          SEQUEL.ONE
                          01 августа 2018, 23:41
                          0
                          Спасибо. Илья, а не подскажешь каким образом можно создать папку templates в папке assets и выгрузить в неё содержимое этой папки? И второй вопрос, можно как-то сделать выбор устанавливаемых компонентов по чекбоксам? Если не нужно сразу всё устанавливать. К примеру сделать 3 кнопки Интернет-магазин, Каталог и Сайт, а при клике по одной из кнопок чекбоксы менялись скажем если выбрать Сайт, то чекбокс с miniShop2 снимался бы и наоборот, если нажать на Интернет-магазин то основные компоненты подгружать?
                            Jameson
                            25 февраля 2019, 09:05
                            0
                            Тоже интересно, каким образом можно выгружать кастомные папки и все что в них есть при сборке/установке пакета. Илья не подскажешь?
                              Илья Уткин
                              25 февраля 2019, 09:12
                              +1
                              Дописываете свои папки в метод addResolvers:

                              /* now pack in resolvers */
                              $vehicle->resolve('file', array(
                                  'source' => $this->config['PACKAGE_ROOT'] . 'assets/components/' . strtolower($this->config['PACKAGE_NAME']),
                                  'target' => "return MODX_ASSETS_PATH . 'components/';",
                              ));
                              $vehicle->resolve('file', array(
                                  'source' => $this->config['PACKAGE_ROOT'] . 'core/components/' . strtolower($this->config['PACKAGE_NAME']),
                                  'target' => "return MODX_CORE_PATH . 'components/';",
                              ));
                              
                              /* add assets/template dir */
                              $vehicle->resolve('file', array(
                                  'source' => $this->config['PACKAGE_ROOT'] . 'assets/template/',
                                  'target' => "return MODX_ASSETS_PATH;",
                              ));
                                Jameson
                                25 февраля 2019, 09:30
                                0
                                Илья, большое спасибо!
                        Jameson
                        04 апреля 2019, 08:39
                        0
                        Илья еще один вопрос, направьте в нужное русло. Указал у сниппетов и плагинов опцию static, источник указал родной, filesystem и при попытке установить в логе пишет такие ошибки:

                        Could not load class: modFileMediaSource from mysql.modfilemediasource.
                        Could not load vehicle!
                        Could not install related objects with foreign owned keys for vehicle object of class modPlugin; criteria: Array ( [name] => snippetName)
                        При этом все устанавливается и работает.
                          Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                          29