У меня получилось обновить 1.0.Х -> 1.2.Х -> 1.4.X -> 3.1.3.
Только кеш в браузере надо чистить обязательно.
(Разговор с тех поддержкой:
— Вы кеш почистили
— Я и кешью почистил и фундук наколол :) )
  • avatar Dmi3yy
  • 1
Сначала нужно обновить до 1.4.14 а после уже по вот этой инструкции до 3.1.3
www.youtube.com/watch?v=RjOiq35FgW4
  • avatar Lilbok
  • 0
Добрый день
У меня возникла проблема
Использую ваш плагин ИЗИФОРМ
Проблема заключается в том что, когда я заполняю форму на сайте, нажимаю отправить она приходит ко мне на почту и всё хорошо

Но
1.Я не могу повторно отправить ещё форму, пишет что не надо так часто отправлять… данное окно висит более 30МИн засекал так и не пропало, попытался ограничить отправку и сделал раз в 2 мин `&submitLimit=`2` по истечению 2-х мин форма сама отправляться (то есть то как ранее была заполнена)

Как мне сделать так что бы, я смог отправлять форму раз 2 мин, но разные, что бы повторно появлялись поля для заполнения
  • avatar paic
  • 0
Я ставил этот
modx.evo.im/blog/addons/4656.html
И я тоже ищу программеров на evo))) t:abadello
  • avatar redhat
  • 0
Прописать в .htaccess

RewriteRule ^city/info([0-9]+)-city.html$ city/info-city-all.html?city_id=$1 [QSA,NC]

и на странице city/info-city-all.html получать параметр $_GET['city_id'] и от него «плясать»
  • avatar 3fir
  • 0
Модуль EditDocs спокойно делает импорт из XLS
Создается и создается, что такого.
  • avatar paic
  • 0
Спасибо.
Подскажите еще, из какой папки правильно обновлять через Composer.
Может не из папки assets, потому что дубль создается. А из папки evotwig? Там уже и composer.json есть.

class GlobalController extends BaseController {
    public function __construct(\DocumentParser $modx, array $params = [])
    {
        parent::__construct($modx, $params);
        $this->setTemplateData([
            'service' => 11111
        ]);
    } 
}

А дочерние расширяют GlobalController таким же образом.
  • avatar paic
  • 0
Для setPageData, т.е. для контроллеров дочерних шаблонов понятно, можно расширить напрямую «штатный» базовый контроллер
<?php
namespace Pathologic\EvoTwig;

class ServiceController extends BaseController
{
    public function __construct(\DocumentParser $modx, array $params = [])
    {
	$this->modx = $modx;
        $this->params = $params;
	$this->docid = $this->modx->documentIdentifier;
	$this->setPageData();
    }
    protected function setPageData()
    {
	$this->data['service'] = json_decode($this->modx->runSnippet('DocLister', ['tvPrefix' => '', 'parents' => $this->docid, 'depth' => 0, 'tvList' => 'image', 'api' => 1]), true);
    }
}


А как расширить, чтобы работал setGlobalData?
Но производить какие-то действия с базовым контроллером по штатному месту (ссылка выше) нельзя — при обновлении или затрется, или как случилось в моем случае. Поэтому базовый контроллер я перенес по адресу
Поэтому базовый контроллер нужно расширять, а не переносить и дописывать, ломая автозагрузку.
  • avatar paic
  • 0
13. Контроллеры в EvoTwig

В EvoTwig базовый контроллер BaseController уже есть и находится по адресу assets/plugins/evotwig/vendor/pathologic/evo-twig-lib/src/BaseController.php
Здесь сразу оговорюсь.
Так получилось, что я изначально EvoTwig устанавливал из Extras, а чуть позже — обновлял через composer. В результате на сайте получилось два дубля — один в папке plugins, а другой — в папке vendor. Конфликтов никаких не возникало и ошибок тоже, я даже забыл за этот эпизод. Но Twig помнил. И когда я дошел до контроллеров, это мне вылезло боком — я пытался работать с тем базовым контроллером, что в плагине (ссылка выше), а фактически он не работал, работал тот, что в папке vendor, и при этом не расширялся для дочерних контроллеров.
Может так и надо, может где-то не то сделал, в общем — первый подход.
Поэтому откатился назад и сделал новую установку EvoTwig из Extras.
И все заработало.

13.1. Базовый контроллер

Но производить какие-то действия с базовым контроллером по штатному месту (ссылка выше) нельзя — при обновлении или затрется, или как случилось в моем случае. Поэтому базовый контроллер я перенес по адресу
assets/plugins/evotwig/addons/functions/BaseController.php
Чтобы была возможность с ним работать я добавил в класс BaseController 2 функции
public function setGlobalData()
    {
	// для использования в базовом шаблоне и всех других
    }
    protected function setPageData()
    {
	// для переопределения в контроллерах для дочерних шаблонов
    }

Соответственно в функцию __construct добавил
$this->setGlobalData();
$this->setPageData();
и на будущее (пригодится)
$this->docid = $this->modx->documentIdentifier;
Получилось так:
public function __construct(\DocumentParser $modx, array $params = [])
    {
        $this->modx = $modx;
        $this->params = $params;
	$this->docid = $this->modx->documentIdentifier;
	$this->setGlobalData();
	$this->setPageData();
    }

Пример вывода меню был в п.6.2, с контроллером теперь будет так:
1. В базовом контроллере BaseController
public function setGlobalData()
    {
	$this->data['mymenu'] = json_decode($this->modx->runSnippet('DLMenu', ['parents' => 0, 'maxDepth' => 2, 'api' => 1]), true)[0];
    }

Полностью видоизмененный базовый контроллер в папке functions выглядит так
<?php

namespace Pathologic\EvoTwig;

class BaseController implements ControllerInterface
{
    protected $modx;
    protected $data = [];
    protected $params = [];
    protected $template = '';

    /**
     * BaseController constructor.
     * @param  \DocumentParser  $modx
     * @param  array  $params
     */
    public function __construct(\DocumentParser $modx, array $params = [])
    {
        $this->modx = $modx;
        $this->params = $params;
	$this->docid = $this->modx->documentIdentifier;
	$this->setGlobalData();
	$this->setPageData();
    }

    /**
     * @param  array  $data
     */
    public function setTemplateData(array $data = [], $replace = false)
    {
        $this->data = $replace ? $data : array_merge($this->data, $data);
    }

    /**
     * @return array
     */
    public function getTemplateData(): array
    {
        return $this->data;
    }

    /**
     * @param string $template
     */
    public function setTemplate($template)
    {
        $this->template = $template;
    }

    /**
     * @return string
     */
    public function getTemplate(): string
    {
        $template = $this->template;
        if (empty($template)) {
            $dir = MODX_BASE_PATH . $this->params['templatesPath'];
            $tplExt = $this->params['templatesExtension'];
            $documentObject = $this->modx->documentObject;
            switch (true) {
                case file_exists($dir . 'tpl-' . $documentObject['template'] . '_doc-' . $documentObject['id'] . '.' . $tplExt):
                {
                    $template = 'tpl-' . $documentObject['template'] . '_doc-' . $documentObject['id'];
                    break;
                }
                case file_exists($dir . 'doc-' . $documentObject['id'] . '.' . $tplExt):
                {
                    $template = 'doc-' . $documentObject['id'] . '.' . $tplExt;
                    break;
                }
                case file_exists($dir . 'tpl-' . $documentObject['template'] . '.' . $tplExt):
                {
                    $template = 'tpl-' . $documentObject['template'];
                    break;
                }
            }
        }

        return $template;
    }

    /**
     * @return string
     */
    public function render(): string
    {
        $template = $this->getTemplate();
        $out = '';
        if (!empty($template)) {
            $tpl = $this->modx->twig->load($this->getTemplate() . '.' . $this->params['templatesExtension']);
            $out = $this->modx->twig->render($tpl, $this->getTemplateData());
        }

        return $out;
    }

	 public function setGlobalData()
    {
	$this->data['mymenu'] = json_decode($this->modx->runSnippet('DLMenu', ['parents' => 0, 'maxDepth' => 2, 'api' => 1]), true)[0];
	// сюда же добавляются и другие 
    }

    protected function setPageData()
    {
	// оставлять пустым
    }
}


2. В базовом шаблоне base.tpl теперь можно оставить только цикл for
{% for data in mymenu %}
        <li class="{% if data.children != 0 %}dropdown{% endif %}"><a href="{{ data.url }}" title="{{ data.title }}"><span>{{ data.title }}</span></a>
{% if data.children %}
        <ul>
{% for data in data.children %}
                <li class="dropdown"><a href="{{ data.url }}"><span>{{ data.title }}</span></a></li>
{% endfor %}
        </ul>
{% endif %}
        </li>
{% endfor %}


13.2. Контроллер для дочерних страниц

У меня на сайте есть два подобных шаблона для страниц каталог услуг и категория услуг. И в одном шаблоне, и в другом — DocLister, отличие только в чанках и дизайне.
Я для них сделал один контроллер под названием ServiceController, загружен он в папку assets/plugins/evotwig/addons/functions/ рядом с базовым контроллером и выглядит так
<?php
namespace Pathologic\EvoTwig;

class ServiceController extends BaseController
{

    protected function setPageData()
    {
	$this->data['service'] = json_decode($this->modx->runSnippet('DocLister', ['tvPrefix' => '', 'parents' => $this->docid, 'depth' => 0, 'tvList' => 'image', 'api' => 1]), true);
    }
}


Подключается контроллер к страницам следующим образом
В шаблоне каталога услуг
@FILE:service@ServiceController

В шаблоне категория услуг
@FILE:subservice@ServiceController


Таким образом, один и тот же контроллер может использоваться в разных шаблонах.

В шаблоне Категория услуг subservice.tpl эти самые услуги выводятся так

{% extends 'base.tpl' %}
{% block content %}
    <div class="service-section">
        <div class="row">
{% for data in service %}
	    <div class="col-lg-6">
		<div class="inner-box">
		    <div class="image-box">
			<a href="{{ makeUrl(data.id) }}"><img src="
{{ runSnippet('phpthumb',{
     'input':data.image,
	 'options':'w=500,h=500,zc=1,bg=ffffff'
   })
}}
			" alt="[+pagetitle+]"></a>
		    </div>
		    <div class="info-box">
			<a href="{{ makeUrl(data.id) }}">{{ data.pagetitle }}</a>
			<a href="{{ makeUrl(data.id) }}">Подробнее</a>
		    </div>
		</div>
	    </div>
{% endfor %}
	    </div>
	    <div class="content">
		{{ resource.content | raw}}
	    </div>
	</div>
{% endblock %}


В шаблоне Каталог услуг service.tpl — все аналогично, немного другой html и размеры картинок, приводить не буду.

Собственно, все это очень похоже на Laravel с Blade, только без Laravel и на версии EVO 1.4.х.
  • avatar nt90
  • 0
так и не работает, ошибка осталась, проверял на 3 разных серверах
  • avatar doc555
  • 0
делаю типовые сайты и не только :)
t.me/ezodoc
  • avatar paic
  • 0
  • avatar paic
  • 0
  • avatar paic
  • 0
12. Чанки и символы типа @CODE в EvoTwig

Это исправления и дополнения к предыдущим пунктам статьи касательно Doclister и его производных, поскольку не все так, а где-то и совсем не так, как было написано выше.

Получается так, что если используем в чанке Twig, то надо применять
@T_CODE:
и внутри чанка data c аргументами от твига.

А если используем чанк Evo, по надо применять
@CODE:
и внутри чанка плейсхолдеры от Evo.

12.1. Практически это означает, что в чанке tpl Doclister надо применять @T_CODE:, а в чанках пагинации и другие @CODE:
{{ runSnippet('DocLister',{
'tvPrefix':'',
'display':'2',
'prepare':'imgformat',
'summary':'notags,len:40',
'tvImg':'image',
'phpthumb':'w=500,h=350,zc=1,bg=ffffff',
'depth':'0',
'parents':'5',
'dateFormat':'%d.%m.%Y',
'dateSource':'createdon',
'tvList':'image',
'paginate':'pages',
'id':'cat',
'pageLimit':'1',
'pageAdjacents':'1',
'tpl':'@T_CODE:<div class="col-lg-3">
	<div class="image-box">
             <img src="{{ data.thumb }}" alt="{{ data.pagetitle }}">
	</div>
	<div class="content-area">
	     <p class="date">{{ data.date }}</p>
             <a href="{{ data.url }}">{{ data.pagetitle }}</a>
             <div class="text">{{ data.summary | raw}}...</div>
		<a href="{{ data.url }}"></a>
             </div>
         </div>',
'noneTPL':'@CODE:<div class="alert">В этом разделе еще нет публикаций!</div>',
'TplPrevP':'@CODE: <li><a href="[+link+]" rel="prev"><i class="fa fa-angle-left"></i></a></li>',
'TplPage':'@CODE: <li><a href="[+link+]" class="page">[+num+]</a></li>',
'TplCurrentPage':'@CODE: <li class="active"><a>[+num+]</a></li>',
'TplNextP':'@CODE: <li><a href="[+link+]" rel="next"><i class="fa fa-angle-right"></i></a></li>',
'TplDotsPage':'@CODE:<li><a href="[+link+]" class="page"> ... </a></li>',
'TplWrapPaginate':'@CODE: <ul class="pagination">[+wrap+]</ul>'
   }) | raw
}}


12.2. В чанках sgLister - @CODE
{{ runSnippet('sgLister',{
'thumbSnippet':'phpthumb', 
'thumbOptions':'w=770,h=434,zc=1,bg=ffffff',
'parents':'40',
'sgOrderBy':'sg_index DESC',
'display':'15',
'tpl':'@CODE:<figure class="image"><a href="[+sg_image+]" class="lightbox-image" data-fancybox="object" data-caption="[+e.sg_title+]"><img src="[+thumb.sg_image+]" alt="[+e.sg_title+]"></a></figure>',
   }) | raw
}}


12.3. В чанках DLMenu — @CODE
{{ runSnippet('DLMenu',{
'parents':'0',
'maxDepth':'3',
'hereClass':'current',
'outerTpl':'@CODE:<ul class="navigation clearfix">[+wrap+]</ul>',
'rowTpl':'@CODE:<li><a href="[+url+]" title="[+title+]"><span>[+title+]</span></a></li>',
'rowHereTpl':'@CODE:<li class="current"><a href="[+url+]" title="[+title+]"><span>[+title+]</span></a></li>',
'parentRowTpl':'@CODE:<li class="dropdown"><a href="[+url+]"><span>[+title+]</span></a>[+wrap+]</li>',
'parentRowHereTpl':'@CODE:<li class="current dropdown"><a href="[+url+]"><span>[+title+]</span></a>[+wrap+]</li>',
'parentRowActiveTpl':'@CODE:<li class="dropdown"><a href="[+url+]"><span>[+title+]</span></a>[+wrap+]</li>',
'innerTpl':'@CODE:<ul>[+wrap+]</ul>',
   })|raw
}}


12.4. В чанках DLCrumbs — @CODE
{{ runSnippet('DLCrumbs',{
'showCurrent':'1', 
'ownerTPL':'@CODE:<ul class="page-breadcrumb" itemscope itemtype="http://schema.org/BreadcrumbList">
        [+crumbs.wrap+]
    </ul>',
'tplFirst':'@CODE:<li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
    <meta itemprop="position" content="[+iteration+]" />
    	<a href="[+url+]" title="[(site_name)]" itemprop="item">[(site_name)]</a>
    </li>'
   })|raw
}}


12.5. Даже если применять T_CODE с переменными в DLMenu, sgLister и при этом сниппет срабатывает, все равно этого делать не надо — это лишняя обработка для сниппета и возможны баги, как например, у меня в sgLister не срабатывал встроенный авторесайз.
  • avatar paic
  • 0
11. Шаблон страницы с фотогалереей gallery.tpl

С фотогалереей SimpleGallery и ее сниппетом sgLister все примерно так же, как и с DocLister, хотя данные и хранятся в базе данных в другой таблице.
{% extends 'base.tpl' %}
{% block content %}
<div class="gallery-container">
{% set DL = runSnippet('sgLister', {
    'parents':resource.id,
    'returnDLObject':'1'
}) %}
{% for data in DL.getDocs() %}
    <figure class="image">
	<a href="{{ data.sg_image }}" class="lightbox-image" data-fancybox="object" data-caption="{{ data.sg_title }}">
	    <img src="{{ runSnippet('phpthumb',{'input':data.sg_image, 'options':'w=770,h=434,zc=1,bg=ffffff'}) }}" alt="{{ data.sg_title }}">
	</a>
    </figure>
{% endfor %}
</div>
{# {{ dump(DL.getDocs()) }} #}
{% endblock %}
{% block js %}
{{ parent() }}
<script defer src="assets/templates/js/custom.js"></script>
{% endblock %}

Здесь второй блок под названием js — это пример подключения на страницу дополнительных скриптов.
Если надо, аналогично делается и для стилей в head, но только тогда и в базовом шаблоне надо, чтобы стили или весь head был в одноименном блоке, как блок js.

Пояснения по sgLister.
Для приведенного выше варианта пояснений нет.
Но есть нюансы, если использовать в таком варианте
{{ runSnippet('sgLister',{
'thumbSnippet':'phpthumb', 
'thumbOptions':'w=770,h=434,zc=1,bg=ffffff',
'parents':'40',
'sgOrderBy':'sg_index DESC',
'display':'15',
'tpl':'@T_CODE:<figure class="image"><a href="{{ data.sg_image }}" class="lightbox-image" data-fancybox="object" data-caption="{{ data.sg_title }}"><img src="{{ data['thumb.sg_image'] }}" alt="{{ data.sg_title }}"></a></figure>',
   }) | raw
}}

Twig не понимает переменную, и я не понял почему, если аргумент прописан не через точку, а через индекс, т.е. заключен в квадратные скобки
{{ data['thumb.sg_image'] }}
А другого варианта вроде как и нет, так тоже нельзя
{{ data.thumb.sg_image'] }}
Причем это только для sgLister, у других такого нет и квадратные скобки у других идут на ура.
В итоге, тестируя этот вариант, я отключил параметры встроенного авторесайза и подключил свой препаре, и так сработало
{{ runSnippet('sgLister',{
'prepare':'imgformat',
'tvImg':'sg_image',
'phpthumb':'w=770,h=434,zc=1,bg=ffffff',
'parents':'40',
'sgOrderBy':'sg_index DESC',
'display':'15',
'tpl':'@T_CODE:<figure class="image"><a href="{{ data.sg_image }}" class="lightbox-image" data-fancybox="object" data-caption="{{ data.sg_title }}"><img src="{{ data.thumb }}" alt="{{ data.sg_title }}"></a></figure>',
   }) | raw
}}

Параметр tvImg — это из состава препаре imgformat, для универсальности, в доках его искать не надо.
  • avatar paic
  • 0
10. Шаблон общей страницы новостей blog.tpl

Это и как пример для любой другой страницы, где используется Doclister.
Как уже ранее отмечал, пагинация Doclister в шаблоне Twig не работает, поэтому надо отредактировать и подключить конфигурацию paginate.json в папке custom, которую уже сделали в п.7.

Если у вас на сайте много страниц с пагинациями, сделанных на базе Doclister или sgLister, то и в классическом Evo пагинацию лучше, удобнее и быстрее делать через конфигурацию.

С учетом вышеизложенного, сразу даю пример вызова Doclister с параметром returnDLObject =1 для создания объекта и создания страницы блога с помощью циклов for шаблона Twig
{% extends 'base.tpl' %}
{% block content %}
<div class="section">
    <div class="row">
{% set DL = runSnippet('DocLister', {
    'tvPrefix':'',
    'parents':'5',
    'display':'3',
    'tvList':'image',
    'paginate':'pages',
    'pageLimit':'1',
    'pageAdjacents':'1',
    'config':'paginate',
    'returnDLObject':'1'
}) %}
{% for data in DL.getDocs %}
	<div class="col-lg-3">
	    <div class="image-box">
<img src="
{{ runSnippet('phpthumb',{
    'input':data.image,
    'options':'w=500,h=350,zc=1,bg=ffffff'
   })
}}
" alt="{{ data.pagetitle }}"></a>
	    </div>
	    <div class="content-area">
		<p class="date">{{ data.createdon|date("d.M.Y H:i") }}</p>
                <a href="{{ makeUrl(data.id) }}">{{ data.pagetitle }}</a>
	    </div>
            <div class="text">
{% set content = resource.content|striptags|replace({' ':' '}) %}
{{ data.introtext is empty? content|length > 75 ? content|slice(0, 75) ~ '...': content:  data.introtext}}
	    </div>
	    <a href="{{ makeUrl(data.id) }}">Подробнее</a>
{% else %}
	    <div class="alert">В этом разделе еще нет публикаций!</div>
{% endfor %}
	    </div>
	</div>
	<div class="default-pagination">
	    {{ plh['pages']|raw }}
	</div>
</div>
{% endblock %}

Пояснения.
1. Часть параметров, указанных в вызове DocLister и относящихся к пагинации, тоже можно перенести в конфигурационный файл.
2. Препаре и summary в приведенном варианте не работают. В обычном варианте runSnippet они работают.
3. Поскольку summary не работает, то реализовать функцию обрезания текста можно средствами Twig, как в примере, или с помощью других фильтров, таковые у Twig есть еще.
4. Если вы используете TV параметрами с префиксами, например tv.image, то такой синтаксис
data.tv.image
применять нельзя, будет ошибка. В этом случае (и в других подобных, и не только в DocLister) используйте
data['tv.image']

В примере
{% set content = resource.content|striptags|replace({' ':' '}) %}
{{ data.introtext is empty? content|length > 75? content|slice(0, 75) ~ '...': content: data.introtext}}

так же как и в summary выводится introtext, а если это поле не заполнено, то применяется фильтр и выводится част текста из content, 75 — количество символов, можно изменить на нужное.