У меня в версии 1.4.11 нет никакого SEO Strict URLs. Строит настройка Дружественные урл, соответственно пихается .html где надо и не надо, шаблон blank не помогает, редирект в htaccess не имеет смысла, либо лезть в код document.parser.class.inc.php прописывать исключение, либо есть решение попроще, которое пока мной не найдено.
/sitemap.xml правит на /sitemap.xml.html Хотя казалось бы шаблон пустой, тип содержимого пробовал application/xml, text/xml — ну зачем добавлять к этому html, где логика
  • avatar Temus
  • 0
Три месяца прошло, а защищенного соединения всё нет.
Сегодня столкнулся с такой же проблемой, единственное, что нашёл гугл — данный топик.
Оставлю тут костыль, который помог мне.
Перед вызовом $modx->makeUrl(), а точнее, сразу после создания ресурса, я вызываю (evo cms 3)
\UrlProcessor::getAliasListing($id); //ID свежесозданной страницы. Полагаю, что в старом API также есть $modx->getAliasListing($id);
После этого — makeUrl даёт корректный результат. Надеюсь, кому-то когда-нибудь может помочь)
  • avatar 1px
  • 0
Можно ещё конвертнуть прямо в шаблоне

@foreach( json_decode($modx->getConfig('client_social'),true) as $one_social )


Где client_social это поле ClientSettings
  • avatar 1px
  • 0
Ещё как представляю…
В вашем случае надо выбрать способ, каким вы будете делать подписи. Поищите сайт и доки по КриптоПро, там всё довольно хорошо изложено.
Скорее всего, у вас будет две сущности. Первая — открытый сертификат. Который можно и нужно выкладывать для проверки подлинности файлов. Вторая — сам файл.
Либо же вместо сертификата будет сгенерированный хэш.
Так и так вам нужно мульти-поле некое, где будут на каждую запись 2 поля: файл+сертификат или файл+хэш.
С этим может справиться multiTV либо multiFields. Первый попроще. Ставятся оба из Extras в админке. Конфигурируйте их по документации под ваши нужды и всё.
Примерно что-то такое будет — это правда для фоточек, но суть та же. Файл и подпись. Либо у вас будет 2 файла. Внешний вид и прочее также по документации сделайте.
  • avatar Droma
  • 0
спасибо, попробуем разобраться с КриптоПро PDF!
  • avatar Droma
  • 0
Спасибо вам большое за комментарий! Хорошо, что проверили доки. Представляете, некая коммерческая фирма рассылает такие коммерческие предложения с услугой по внедрению всей этой системы на сайты школ, якобы с соблюдением всех требований Роспотребнадзора. А по факту документы не проходят проверку.
Скорее всего мы будем загружать реально подписанный ЭЦП документы и теперь вопрос, как сделать, чтобы всплывала картинка с подписью?
«Так что просто напишите любой хэш рядом с доком» — это как сделать? ))
  • avatar Droma
  • 0
Понемногу начинаю въезжать в этот вопрос! А подскажите, Дополнительный параметр TV с текстом, это каким образом можно сделать? Может есть ссылки, где можно посмотреть реализацию подобной штуки? Чтобы при наведении на документ высвечивалось поле с текстом.
  • avatar paic
  • 0
15. Вариант 2, с использованием отдельной дополнительной таблицы в базе данных.

15.1. Устанавливаем, если на сайте еще нет, ClientSettings и multiTV.

15.2. Создаем конфигурацию multiTV, у меня это файл lex.config.json с конфигурацией

{
    "display": "dbtable",
    "table": "lexicon",
    "caption": "Таблица языков",
    "fields": {
        "id": {
            "caption": "id"
        },
        "plh": {
            "caption": "Плейсхолдер"
        },
        "ru": {
            "caption": "Значение ru"
        },
        "en": {
            "caption": "Значение en"
        },
        "name": {
            "caption": "Назначение"
        }
    },
    "columns": [
        {
            "fieldname": "id",
            "width": "10"
        },
        {
            "fieldname": "plh",
            "width": "50"
        },
        {
            "fieldname": "ru",
            "width": "100"
        },
        {
            "fieldname": "en",
            "width": "100"
        },
        {
            "fieldname": "name",
            "width": "100"
        }
    ],
    "form": [
        {
            "caption": "Строка",
            "content": {
                "id": {},
                "plh": {},
                "ru": {},
                "en": {},
                "name": {}
            }
        }
    ],
    "configuration": {
        "radioTabs": 0,
        "sorting": 1
    }
}


15.3. Создаем конфигурацию для ClientSettings, у меня она такая

<?php
return [
	'caption' => 'Лексикон',
	'introtext' => 'Настройка языковых версий',
	
    'settings' => [
	    'lex' => [
            'caption' => 'Замены',
            'type' => 'custom_tv:multitv',
        ]
    ],
];


15.4. Создаем таблицу в базе данных, префикс таблицы надо указать свой, если он отличается от evo_

DROP TABLE IF EXISTS `evo_lexicon`;
CREATE TABLE `evo_lexicon` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `plh` varchar(50) NOT NULL DEFAULT '',
  `ru` varchar(255) NOT NULL DEFAULT '',
  `en` varchar(255) NOT NULL DEFAULT '',
  `name` varchar(255) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  UNIQUE KEY `plh` (`plh`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


15.5. Заполняем лексиконы.

15.6. Создаем плагин miniLang с таким кодом
/**
 * miniLang
 * 
 * Мультиязычность - создание альтернативных страниц и подключение лексиконов
 * 
 * @category    plugin
 * @version     1.0.2
 * @internal    @events OnDocFormSave,OnParseDocument
**/

switch ($modx->Event->name) {
    case 'OnDocFormSave': {
    	if ($mode == "new") {
               include_once(MODX_BASE_PATH.'assets/lib/MODxAPI/modResource.php');
        	$doc = new modResource($modx);
        	$doc->edit($id);
        	$template = $doc->get('template');
			$published = $doc->get('published'); // добавлено
			$pagetitle = $doc->get('pagetitle');
			$image = $doc->get('image');
			
			// Определение родителя альтернативной версии
			$parent = $doc->get('parent');
			if ($parent != 0) {
				$value = $doc->get('parent');
				$tmplvarid = '9';
				$out = $modx->db->getValue('Select contentid from '.$modx->getFullTableName('site_tmplvar_contentvalues').' where value="'.$value.'" and tmplvarid="'.$tmplvarid.'"');
			}else{ 
				$out = '11';
			}
			$newparent = $out;
			
			// Определение шаблона альтернативной версии исключено
			
			// Создание страницы альтернативной версии
        	        if ($published != 0) {  // Проверка через $published вместо $template
				$doc->create(array(
    				'pagetitle' => $pagetitle,
    				'template' => $template, // $template вместо $newtemplate
    				'parent' => $newparent,
				'published' => 0,
				'altRu' => $id,
				'image' => $image
			    ));
				
			// Запись id созданной альтернативной страницы в ТВ основной страницы
			$newid = $doc->save(true, false);
			$table = $modx->getFullTableName('site_tmplvar_contentvalues');
			$fields = array(
                    	'value' => $newid,  
                    	'contentid'  => $id,  
                    	'tmplvarid' => 10
                    );   
    			$modx->db->insert( $fields, $table);  
			}
    	        }
	}
	case 'OnParseDocument': {
		// Определение принадлежности страницы корневой папке языка
		if ($modx->documentObject['parent']=='0') {
  			$lang=0;
		} else {
  			$lang=$modx->documentIdentifier;
  			do {
    			foreach ($modx->documentMap as $mapEntry) {
      				$parentId=array_search($lang, $mapEntry);
      				if ($parentId) break;
    			}
    		if ($parentId) $lang=$parentId;
  			} while ($parentId);
		}
		
		// Псевдоним корневой папки языка
		if ($lang == 11) {$alias = 'en';}else {$alias = 'ru';}
		
		// Запись лексиконов в сессию
		$glossary = array();
                $lang_lexicon = $alias;
		$sql = $modx->db->query("SELECT * FROM " . $modx->getFullTableName('lexicon'));
               while ($row = $modx->db->getRow($sql)) {
          	    $glossary[$row['plh']] = $row[$lang_lexicon]; 
                }
		$_SESSION['miniLang'] = $lang_lexicon;
                $_SESSION['glossary'] = $glossary;
		
		// Считывание языковых плейсхолдеров с префиксом lang и подстановка в них значений соответствующих языку страницы
		if ( preg_match_all("~\[\+lang\.(.*)\+\]~U",
    		    $modx->documentOutput, $matches)) {
		    foreach ($matches[1] as $placeholder) {
    		    $modx->setPlaceholder('lang.'.$placeholder,$_SESSION['glossary'][$placeholder]);
  			}
		}
	}
}


Плагин аналогичный, как в п.14, отличается источником получения данных лексиконов и их обработкой.
  • avatar paic
  • 0
14. Вариант 1, без использования отдельной дополнительной таблицы в базе данных.

14.1. Устанавливаем, если на сайте еще нет, ClientSettings и multiTV.

14.2. Создаем конфигурацию multiTV, у меня это файл lexicon.config.inc.php с конфигурацией

<?php
$settings['display'] = 'horizontal';
$settings['fields'] = array(
    'title' => array(
        'caption' => 'Назначение',
        'type' => 'text',
        'width' => '200'
    ),
    'plh' => array(
        'caption' => 'Плейсхолдер',
        'type' => 'text',
        'width' => '200'
    ),
    'ru' => array(
        'caption' => 'Значение RU',
        'type' => 'text',
        'width' => '200'
    ),
    'en' => array(
        'caption' => 'Значение EN',
        'type' => 'text',
        'width' => '200'
    )
);


14.3. Создаем конфигурацию для ClientSettings, у меня она такая

<?php
return [
	'caption' => 'Лексикон',
	'introtext' => 'Настройка языковых версий',
	
    'settings' => [
	    'lexicon' => [
            'caption' => 'Замены',
            'type' => 'custom_tv:multitv',
        ]
    ],
];


14.4. Заполняем лексиконы.

14.5. Создаем плагин miniLang с таким кодом
/**
 * miniLang
 * 
 * Мультиязычность - создание альтернативных страниц и подключение лексиконов
 * 
 * @category    plugin
 * @version     1.0.1
 * @internal    @events OnDocFormSave,OnParseDocument
**/

switch ($modx->Event->name) {
    case 'OnDocFormSave': {
    	if ($mode == "new") {
        	include_once(MODX_BASE_PATH.'assets/lib/MODxAPI/modResource.php');
        	$doc = new modResource($modx);
        	$doc->edit($id);
        	$template = $doc->get('template');
			$published = $doc->get('published'); // добавлено
			$pagetitle = $doc->get('pagetitle');
			$image = $doc->get('image');
			
			// Определение родителя альтернативной версии
			$parent = $doc->get('parent');
			if ($parent != 0) {
				$value = $doc->get('parent');
				$tmplvarid = '9';
				$out = $modx->db->getValue('Select contentid from '.$modx->getFullTableName('site_tmplvar_contentvalues').' where value="'.$value.'" and tmplvarid="'.$tmplvarid.'"');
			}else{ 
				$out = '11';
			}
			$newparent = $out;
			
			// Определение шаблона альтернативной версии исключено
			
			// Создание страницы альтернативной версии
        	if ($published != 0) {  // Проверка через $published вместо $template
			$doc->create(array(
    			'pagetitle' => $pagetitle,
    			'template' => $template, // $template вместо $newtemplate
    			'parent' => $newparent,
			'published' => 0,
			'altRu' => $id,
			'image' => $image
			));
				
			// Запись id созданной альтернативной страницы в ТВ основной страницы
			$newid = $doc->save(true, false);
			$table = $modx->getFullTableName('site_tmplvar_contentvalues');
		    $fields = array(
                    	'value' => $newid,  
                    	'contentid'  => $id,  
                    	'tmplvarid' => 10
                    );   
    			$modx->db->insert( $fields, $table);  
			}
    	        }
	}
	case 'OnParseDocument': {
		// Определение принадлежности страницы корневой папке языка
		if ($modx->documentObject['parent']=='0') {
  			$lang=0;
		} else {
  			$lang=$modx->documentIdentifier;
  			do {
    			foreach ($modx->documentMap as $mapEntry) {
      				$parentId=array_search($lang, $mapEntry);
      				if ($parentId) break;
    			}
    		if ($parentId) $lang=$parentId;
  			} while ($parentId);
		}
		
		// Псевдоним корневой папки языка
		if ($lang == 11) {$alias = 'en';}else {$alias = 'ru';}
		
		// Запись лексиконов в сессию
		$glossary = array();
                $lang_lexicon = $alias;
		$sql = $modx->getConfig('company_lexicon');
		$items = json_decode($sql, true);
		foreach ($items as $item) {
          	$glossary[$item['plh']] = $item[$lang_lexicon]; 
        }
		$_SESSION['miniLang'] = $lang_lexicon;
                $_SESSION['glossary'] = $glossary;
		
		// Считывание языковых плейсхолдеров с префиксом lang и подстановка в них значений соответствующих языку страницы
		if ( preg_match_all("~\[\+lang\.(.*)\+\]~U",
    		$modx->documentOutput, $matches)) {
			foreach ($matches[1] as $placeholder) {
    			$modx->setPlaceholder('lang.'.$placeholder,$_SESSION['glossary'][$placeholder]);
  			}
		}

	}
}


Пояснения.

1. Предыдущая версия на событие OnDocFormSave упростилась и немного изменилась, ввиду того что теперь для разных языковых версий используется один шаблон, и нет необходимости выполнять сопряжение шаблонов как в случае использования дублирующих шаблонов.

2. Добавился код на событие OnParseDocument, собственно, это и есть управление лексиконами.
Здесь:
0 и 11 — это корневые папки русскоязычных страниц и англоязычных соответственно.
company_lexicon — имя параметра в таблице системных настроек, состоящий из префикса модуля ClientSettings и имени multiTV. Если вы префикс по умолчанию client_ не меняли, то у вас будет client_lexicon.

3. Учитывая, что на 2 языка теперь один комплект шаблонов и чанков, потребуется к каждому шаблону подключать оба ТВ altEn и altRu (или использовать один), так же потребуется пересмотреть организацию кнопок переключения языков, хлебных крошек и других элементов сайта в сторону их унификации (то что раньше легко решалось применением дублирующего шаблона). Здесь трудно дать один какой то рецепт, т.к. все сайты разные.

P.S. Использовались идеи e-kao и evoBabel, спасибо авторам указанных дополнений.
  • avatar paic
  • 0
13. Лексиконы.

Решил все-таки сделать вариант с лексиконами и соответственно, без дублей шаблонов.

В качестве хранилища для лексиконов предлагается два варианта:

13.1. Без использования отдельной таблицы в базе данных.

Лексиконы хранятся в multiTV, а multiTV — модуле ClientSettings. При этом используется уже существующая таблица system_settings

13.2. С использованием отдельной дополнительной таблицы в базе данных.

Для вывода и редактирования так же используется ClientSettings с multiTV в режиме dbtable.

Скриншот, как выглядит и один и второй вариант, здесь они сразу оба, но использовать разумеется надо какой-то один, кому какой больше нравится


Как на мой взгляд — вариант 1 удобнее и для добавления новых строк в лексикон, и для редактирования существующих.

13.3. Использование.

Для вывода лексиконов используются плейсхолдеры вида
[+lang.blog+]

а в шаблонах и чанках теперь будет так
[!DLMenu? 
&parents=`[+lang.menu+]`
&maxDepth=`2`
...
!]

<h3>[+lang.text+]</h3>

и т.д.
  • avatar 1px
  • 2
Может я чего не понимаю, но 2 дока, взятые наугад, в двух сервисах проверку на подлинность не прошли.
Так что просто напишите любой хэш рядом с доком и радуйтесь жизни — сайты-образцы так и поступили. Или же они сделали простую подпись внутри своего компа, и проверить её могут тоже только они.
Ни один сервис у меня не схавал их хэш.
Я так тоже умею подписывать. Даже с файлом. Вот только проверить эту подпись могу только я только на этом компе =)

Берите КриптоПро и подпишите файлы руками. Т.к. для подписи их на сервере нужно сильное шаманство, и это бессмысленно.
Иконка это просто иконка. А сам документ pdf подписан и имеет подпись(уникальный программный ключ). Если документов не много то можно все сделать вручную.
1) Подписываешь документ (или берешь подписанный)
2) проверяешь подпись
3) закидываешь на сайт документ
4) Дополнительный параметр TV с текстом ключом (ключ ведь тоже текст)
5) шаманишь со стилями.
  • avatar paic
  • 0
Так я и не против, просто дополнительно пояснил. Еще могу добавить, что нужно обращать внимание, чтобы админ с пом. подобных дополнений не смог поставил больше одного id.
  • avatar 1px
  • 0
Я не критикую, я предлагаю варианты, раз уж собираем тут примеры решения «а можно и вот так вот».
  • avatar webzic
  • 1
К чему все эти сложности, подписываете документ pdf цифровой подписью и размещаете на сайте, посмотрите на КриптоПро PDF.
  • avatar paic
  • 0
Для связи используется id альтернативных страниц в TV altEn и altRu, а вот как их проставлять, это уже кому как нравится, можно
1. Вручную
2. Автоматически с помощью плагина miniLang п.11
3. Можно и Selector'ом, как у меня в п.12 показан ddTree — я остановился на нем чисто из-за визуализации дерева (где находится), но им можно и подключать, и переподключать, если такое потребуется. И Selector, и ddTree, и другие подобные — физически они в ТВ проставляют id, а все остальное — это внешний вид и удобства.
  • avatar 1px
  • 0
Ну если тема именно в том, чтобы не использовать evoBabel, то для связи было бы проще и логичнее использовать Selector.
Там тебе и дерево нужное, и выпадающий список удобный.
  • avatar paic
  • 0
Ну что ж вы меня все пытаетесь убедить в том, с чем я и так согласен))

Еще раз — обратите внимание на Название темы:
Простая мультиязычность для простого сайта.

Я же никого не заставляю это повторять, если сайт сложный, со многими неизвестными в ТЗ со всякими если и возможно по расширяемости и прочему, если много языков, или если просто не нравится.

Но я считаю, что выбор средств и способов реализации должен осуществляться исходя из поставленных задач. И если задача простая, то и реализация должна быть простой. Зачем самолет с вертикальным взлетом, если достаточно велосипеда.

Да и на практике огромное количество сайтов работают как их сделали на 2-х языках, так и работает годами.

А по объему работы сделать копии шаблонов и отредактировать их никак не больше, чем с применением специальных компонентов. Опять же — если сайт простой.

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

А вот в этом простом варианте мультиязычности для простого сайта вы сразу идете в шаблоны, и без всякого модуля с лексиконами то же самое сразу пишете в шаблоны.

И еще же вам надо поставить, запустить и настроить сам компонент с модулем-плагинами-сниппетами-таблицами.

По моему плагину.
Без плагина сайт тоже будет работать.
Потому что этот плагин — это в дополнение, а не вместо. Сделан на случай какого-либо статейника, блога и подобного активного сайта. Но можно, конечно, и сразу ставить и пользоваться.
А что будет, если нужно добавить третий язык, допустим, французский — третий комплект шаблонов делать?
И в каждом соответственно уже будет по две ссылки на другие языки, то есть их в сумме шесть штук потребуется разметить: ru-> en, ru->fr, fr->en, fr->ru, en->ru, en->fr.

А потом мы друг решим довнести какую-то функциональность в сайт — и нужно будет править уже не два шаблона, а три.

ИМХО, избыточное дублирование кода проблем больше породит, чем кажущаяся сложность работы спец. компонентов. Опять-таки, у вас тоже без плагина не обошлось, и это тоже некий элемент «скрытности физики работы» вносит.

Напротив, наличие доп.таблицы в базе данных достаточно заметно, и содержимое ее будет вполне понятно, если названия столбцов и хранимые данные сделать интуитивно понятными.

Еще важный момент — тот же evoBabel можно доавтоматизировать до такого состояния, что администратор сайта сможет добавить сам дополнительный язык, создав ресурс с соответствующим шаблоном и дозаполнив модуль evobabellexicon соответствующими значениями. Конечно, соображаловка некоторая понадобится, чтобы заполнить поля, в которых внесены идентификаторы всяких родительских/служебныхх ресурсов, но не придется лезть в шаблоны и чанки и что-то там править. В вашем варианте пока без правки шаблонов и чанков добавление дополнительного языка не обойдется, что существенно усложняет задачу «легкого» применения.

Обдумайте :)