Илья Уткин

Илья Уткин

С нами с 15 декабря 2012; Место в рейтинге пользователей: #9
Максим Кузнецов
27 августа 2015, 13:57
2
+3
<script src="http://api-maps.yandex.ru/2.0-stable/?load=package.standard&lang=ru-RU" type="text/javascript"></script>

<script type="text/javascript">
	var userCity = ymaps.geolocation.city; //получаем город пользователя
	//далее находим соответствие в массиве iata и возврвщаем результат
</script>
Антон ХайЭксель
16 июля 2015, 02:10
3
+1
Просуммирую метод Володи, думаю может понадобится кому-то еще

1. естественно меняем класс обработчик фильтров. Идем в настройки системы и в настройках mSearch2 меняем параметр mse2_filters_handler_class на CustomFilter

2. теперь нам нужно создать сам класс. для этого создаем файл core/components/msearch2/custom/filters/custom.class.php с содержимым

<?php
class CustomFilter extends mse2FiltersHandler {

    public function getMsOptionMyValues(array $keys, array $ids) {
		$filters = array();
		$q = $this->modx->newQuery('msProductOption');
		$q->where(array('product_id:IN' => $ids, 'key:IN' => $keys));
		$q->select('`product_id`,`key`,`value`');
		$tstart = microtime(true);
		if ($q->prepare() && $q->stmt->execute()) {
			$this->modx->queryTime += microtime(true) - $tstart;
			$this->modx->executedQueries++;
			while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
				$value = trim($row['value']);
				if ($value == '') {continue;}
				$key = $row['key'];
				// Get ready for the special options in "key==value" format
				if (strpos($value, '==')) {
					list($key, $value) = explode('==', $value);
					$key = preg_replace('/\s+/', '_', $key);
				}
				// --
				if (isset($filters[$key][$value])) {
					$filters[$key][$value][] = $row['product_id'];
				}
				else {
					$filters[$key][$value] = array($row['product_id']);
				}
			}
		}
		else {
			$this->modx->log(modX::LOG_LEVEL_ERROR, "[mSearch2] Error on get filter params.\nQuery: ".$q->toSql()."\nResponse: ".print_r($q->stmt->errorInfo(),1));
		}
		return $filters;
	}

	public function buildDefaultMyFilter(array $values, $name = '') {
		if (count($values) < 2 && empty($this->config['showEmptyFilters'])) {
			return array();
		}
		$results = array();
		foreach ($values as $value => $ids) {
			$results[$value] = array(
				'title' => $value
				,'value' => $value
				,'type' => 'default'
				,'resources' => $ids
			);
		}
		ksort($results);
		return $results;
	}
	

    public function filterDefaultMy(array $requested, array $values, array $ids) {
		$matched = array();
		$tmp = array_flip($ids);
		foreach ($requested as $value) {
			if (isset($values[$value])) {
				$resources = $values[$value];
				foreach ($resources as $id) {
					if (isset($tmp[$id])) {
						$matched[] = $id;
					}
				}
			}
		}
		
	    $match = $matched;
		$matched = array();
		$count = count($requested);
		$count_values = array_count_values($match);
		foreach ($count_values as $id => $value) {
		    if ($value >= $count) {
		        $matched[] = $id;
		    }
		    else {
		        $matched[] = 0;
		    }
		}
		return $matched;
	}
	
}
осталось добавить в чанк вызова мфильтра строчку

&filters=`
		msoption|tags:DefaultMy'


вуаля — фильтр работает как нужно ;)
Максим Кузнецов
22 мая 2015, 12:46
1
+1
Тоже самое. Временно пофиксил таким решением:

Создал плагин adminStyles на событие OnManagerPageInit
<?php
	if ($modx->event->name != 'OnManagerPageInit') return;
	$modx->regClientCSS('/design/admin.css');
И в вышеописанном ксс-файле добавил строчку
.ace_editor {width: 100% !important;}
Максим Кузнецов
10 января 2015, 09:34
4
+2
С вашего позволения, приведу сюда пример реализации большинства пунктов (не отрицаю востребованнось решения данных задач сразу из коробки, т.к. работы для реализации этих пунктов нужно сделать прилично):

Страницы пользователей с нормальными урл

1. Создаем страницу «профиль пользователя» (не путаем с личным кабинетом), выставляем ему псевдоним, допустим users, к которой будут обращаться в виде site.ru/users?profile=имя

2. Ставим дополнение pdoTools

3. Создаем сниппет user.Profile и добавляем его в шаблон вывода
<?php
	$count = $modx->getCount('modUser', array('username' => $_GET[profile]));
	if($count <= 0){
		echo'	<h2>
				<a>Ошибка</a>
			</h2>
			<div>
				Пользователя не существует.
			</div>';
	}
	else {
		$params = array();
		$params['users'] = $_GET[profile];
		$params['showBlocked'] = '1';
		$params['tpl'] = 'user.Profile';
		$params['prepareSnippet'] = 'user.Profile.Prepare';
		
		$result = $modx->runSnippet('pdoUsers', $params);
		
		if (!empty($result)) {
			return $result;
		}
		else {
			return '<h2>
					<a>Ошибка</a>
				</h2>
				<div>
					Что-то сломалось.. Сейчас починим.
				</div>';
		}

	}
— насколько я помню, сразу вызвать pdoUsers с параметром конкретного пользователя нежелательно, т.к. если пользователя не существует, он выдает по-умолчанию весь список пользователей. Возможно, сейчас что-то поменялось или это можно обойти — не проверял.

Для данного сниппета также можно дописывать условия, если пользователь не активирован и тд и тп. При помощи параметров tpl и prepareSnippet кастомизируем до нужного уровня.

4. Дописываем в .htaccess
RewriteRule ^users/([^/]+)$ /users?profile=$1 [L]
— чтобы ссылка приняла вид site.ru/users/Имя_пользователя

Возможность добавлять поля в профиль пользователя

При регистрации: дополнение login
Для редактирования пользователем (личный кабинет) — дополнение office

Возможность указывать шаблон для оформления страницы пользователя

1. Добавляем дополнительное поле в личный кабинет пользователя (шаблон отображения)
2. В шаблоне отображения профиля пользователя дописываем классы, завязанные на полученном значении (class=«userInfo-[[+tpl.style]]»)
2.А. Если необходимо менять структуру шаблона в зависимости от выбранного пользователя значением, то в сниппете в первой части дописываем до

$params = array();
получение extended-поля по id пользователя с вытекающими условиями if, внутри которых будет разный параметр $params['tpl']

Добавить «из коробки» дату регистрации и дату последней активности

Дату регистрации — сниппет логин и 1 доп. поле.
С датой последней активности сложнее, т.к. в таблицах Modx'a, насколько я помню, есть только поле последней авторизации. Возможно, нужно завязывать на сессии +временной промежуток.

Возможность сделать станицу пользователя общедоступной для просмотра

Аналогично пункту 2.А. в разделе «шаблона отображения»
Алексей Карташов
19 ноября 2014, 16:52
2
+1
Т.к. не мне одному интересна данная тема и кто-то даже добавил этот тикет себе в избранное, то расскажу-ка я об ещё одной возникшей проблеме дабы сэкономить потомкам часы гугления и нервы.

Вот такой вот нехитрый код:
$id = 1;

$o = $modx->getObject('myObjectOne', $id);
$o->fromArray(array(
  'active' => 0
));
// или так:
// $o->set('active', 0);

var_dump($o->save());
будет приводить к вот такой вот ошибке (в modx-логе):
Attempt to save lazy object: Array( <массив объекта> )

Дело в том, что такой вот унаследованный объект полученный методом $modx->getObject будет lazy (а вот через newObject всё хорошо). Не буду расписывать что это такое и почему. Факт в том, что сохраняться ничего не будет.
Чтобы всё работало как ожидается, надо в унаследованных классах переопределять метод set:
function set ($k, $v= null, $vType= '') {
  if ($this->isLazy()) $this->_loadFieldData($this->_lazy);
  return parent::set($k, $v, $vType);
}
Либо перед изменением данных объекта делать
$o->toArray();
// а потом что-то изменяем
$o->set('active', 0);
Тогда тоже будет хорошо, но это не удобно.

Лучше переопределить метод set и не заморачиваться. В этом случае $o->fromArray() тоже будет работать адекватно.
Сергей Шлоков
03 ноября 2014, 19:41
2
0
Вот еще в дополнение — изменение префикса таблиц. Может пригодится.
<?php
define('MODX_API_MODE', true);
require 'index.php';

// Включаем обработку ошибок
$modx->getService('error','error.modError');
$modx->setLogLevel(modX::LOG_LEVEL_INFO);
// 
$modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML');
$db = 'your_db_name';
$query = "SHOW TABLES FROM {$db}";
$old_prefix='modx';
$new_prefix='mdx';
$result = $modx->query($query);
$tables = $result->fetchAll(PDO::FETCH_ASSOC);

foreach ($tables as $table) {
	foreach ($table as $tablename) {
		$new_tablename = str_replace($old_prefix,$new_prefix, $tablename);
		$q = "rename table {$tablename} to {$new_tablename}";
		$res = $modx->query($q);
		if ($res) print 'Таблица '.$tablename.' переименование в '.$new_tablename.'\n\r'; else print 'Ошибка переименования'.'\n\r';
	}
}
?>
П.С. Файл должен быть в корне сайта. И не забудь изменить префикс в настройках core/config/config.inc.php
П.П.С. Только надо быть осторожным на рабочем сайте. Нужно проверить работу всех дополнений. Login, например, может глючить.
Алексей Ерохин
17 октября 2014, 10:31
1
0
Пишите процессор, и вызываете его в store для комбобокса.
var storeusers = new Ext.data.JsonStore({
    autoLoad: true,
    url: '/servicesection/componentsajax.html?action=contragents',
    fields: [{name:'id', type: 'int'}, 'fullname'],
    storeId: 'storeusers'
});
У меня примерно так было давно, на вызываемой странице сниппет.
Cyrax_02
30 сентября 2014, 14:43
1
+1
Поскольку reflection позволяет извне получить доступ к защищённым/закрытым полям объекта, то через reflection сабжевая задача решается гораздо проще:

abstract class xPDOQueryMemento {
    static $reflectionProperties;
    
    static protected function getReflectionProperties() {
        global $modx;
        
        if(!isset(xPDOQueryMemento::$reflectionProperties)) {
            $reflection = new ReflectionClass('xPDOQuery_'.$modx->config['dbtype']);
            
            $properties = array();
            foreach($reflection->getProperties() as $property) {
                $properties[$property->getName()] = $property;
            }
            xPDOQueryMemento::$reflectionProperties = $properties;
        }
        return xPDOQueryMemento::$reflectionProperties;
    }

    // memento pattern save
    static public function save($query) {
        global $modx;
        if(is_null($query)) { return ''; }
        
        $data = array();
        $properties = xPDOQueryMemento::getReflectionProperties();  // $reflection = new ReflectionObject($query);
        
        foreach($properties as $property) {
            $property->setAccessible(true);
            
            $value = $property->getValue($query);
            if(!is_object($value) || is_null($value)) {
                $data[$property->getName()] = $value;
            }
            if (!$property->isPublic()) { $property->setAccessible(false); }
        }
        return serialize($data);
    }
    
    // memento pattern restore
    static public function restore($data) {
        global $modx;
        if(($data == '') || is_null($data)) { return null; }
        
        $query = $modx->newQuery('modResource');
        $properties = xPDOQueryMemento::getReflectionProperties();  // $reflection = new ReflectionObject($query);
        
        foreach(unserialize($data) as $name => $value) {
            $property = $properties[$name];
            
            $property->setAccessible(true);
            $property->setValue($query, $value);
            if (!$property->isPublic()) { $property->setAccessible(false); }
        }
        return $query;
    }
}

Далее в коде делаем так (кэшируем состояние объекта xPDOQuery):
$key = md5('Ключ кэша с состоянием объекта xPDOQuery');
$query = xPDOQueryMemento::restore($modx->cacheManager->get($key));
if(is_null($query)) {
    $query = $modx->newQuery('modResource');
    ... // Сложные операции с query (подключение TV, условия и пр.)
    cacheManager::cacheSet($key, xPDOQueryMemento::save($query));
}
... // Часто меняющиеся операции с query. Например, указание LIMIT/OFFSET при пагинации

Тест (1 TV и 2 простых условия):
$time0 = microtime(true);
$key = md5('key125');
$query = xPDOQueryMemento::restore($modx->cacheManager->get($key));
if(is_null($query)) {
    $query = $modx->newQuery('modResource');
    $query->leftJoin('modTemplateVarResource', 'tv', array('tv.contentid = modResource.id', 'tv.tmplvarid = 1239');
    $query->where(array('modResource.id:=' => 1000, 'tv.value:>' => 25), xPDOQuery::SQL_OR);
    
    cacheManager::cacheSet($key, xPDOQueryMemento::save($query));
}
print_r(number_format(microtime(true) - $time0, 4).' сек');

При первом выполнении (запрос строится с нуля): 0.005-0.007 сек
При последующих выполнениях (объект xPDOQuery восстанавливается из файлового кэша): 0.0007 сек (из них 0.0005 сек — чтение кэш-файла с состоянием xPDOQuery)
Разница: в 7-10 раз. И это только на простом запросе (1 tv и 2 условия).

Мой запрос с 30 tv-ками и кучей сложных условий готовится (без выполнения) 0.1 сек.
С кэшированием запроса вплоть до пагинации (до добавления limit/offset) — 0.0007-0.0010 сек
Разница: в 70-100 раз.

P.S. Кэширование запроса xPDOQuery совместно с его выполнением в режиме pdo может придать ему реактивные свойства. И переписывать запрос на чистый pdo не потребуется.
Evgeny Epifanov
23 сентября 2014, 22:05
8
+1
Чанк tpl.msEmail.new.manager
[[+address.receiver]] — Покупатель
[[+address.phone]] — Телефон
[[+user.email]] — Почта
[[+address.index]] — Индекс
[[+address.region]] — Область
[[+address.city]] — Город
[[+address.street]] — Улица
[[+address.building]] — Дом
[[+address.room]] — Квартира
[[+delivery.name]] — Способ доставки
[[+payment.name]] — Тип оплаты
[[+address.comment]] — Комментрарий

Это то, что я знаю. А вообще где-то это уже обсуждалось.