Виталий Серый

Виталий Серый

Был в сети 16 ноября 2021, 22:01
Заказы не принимаю
Василий Наумкин
Василий Наумкин
Василий Наумкин
Василий Наумкин
Проверил на скорую руку, вроде работает:
1. Берем
файл для Smarty.
2. Загружаем его на сервер как /assets/components/ace/ace/mode-smarty.js
3. В файле /assets/components/ace/modx.texteditor.js на 7й строке меняем mode c text на smarty.
4. Хорошенько чистим кэш браузера

Результат:
Василий Наумкин
Василий Наумкин
как можно запустить скрипт от рута из под PHP, либо дать пользователю все необходимые права на выполнение скрипта
Сервер настроен по инструкции Василия.
Основной смысл моей инструкции именно в том, чтобы не давать сайтам дополнительных прав. Каждый сайт запускается из под своего пользователя и он имеет доступ только к своим файлам.

А теперь представь, что если бы существовал способ делать так, как ты хочешь? Выходит, что любой сайт мог бы выполнять команды от root и в чем тогда защита? Злоумышленник ломает сайт и получает доступ ко всему серверу, ко всем сайтам и файлам.

Так что, ответ прост — это невозможно.

Процесс, который создаёт сайты должен работать не как обычный сайт. Это или отдельный демон на Python, как у нас на хостинге, или отдельный процесс на PHP, но запущенный от root и защищенный по IP от случайного коннекта. Или просто консольные скрипты, как в моей инструкции.

Когда я делал
тестовые сайты, там использовались именно эти скрипты, которые запускались по расписанию раз в n минут.
То есть, юзер заходил на обычный сайт, заказывал себе тест, запрос сохранялся в БД, а сервер уже раз в 10 минут от root запускал скрипт, который проверял заказы в БД, создавал тестовые сайты и отправлял на email уведомления об этом.

Ты можешь сделать так же.
Василий Наумкин
Василий Наумкин
1. Обнови mSearch2 и pdoTools до последних версий
2. Вызови mFilter2
[[!mFilter2?
	&parents=`0`
	&limit=`5`
	&element=`pdoResources`
	&ajaxMode=`button`
	&ajaxElemWrapper=`#mse2_mfilter`
	&ajaxElemRows=`#mse2_results`
	&ajaxElemPagination=`#mse2_pagination`
	&ajaxElemLink=`#mse2_pagination a`
	&ajaxElemMore=`#mse2_mfilter .btn-more`
]]
3. Допиши javascript для скрытия кнопки на странице, когда больше нечего показывать:
$(document).on('mse2_load', function(e, response) {
	var data = response.data;
	var total = data.total;
	var limit = mse2Config['limit'] || mse2Config['start_limit'];
	var page = pdoHash.get()['page'] || 1;
	
	var more = $('#mse2_mfilter .btn-more');
	if (page * limit >= total) {
		more.hide();
	}
	else if (more.is(':hidden')) {
		more.show();
	}
	//setMore();
});
4. По желанию можно добавить функцию setMore, которая будет писать сколько загрузится результатов на кнопке:
function setMore() {
	var btn = $('#mse2_mfilter .btn-more');
	var total = $(mSearch2.options['total']).text();
	var page = pdoHash.get()['page'] || 1;
	var limit = mse2Config['limit'] || mse2Config['start_limit'];
	
	var remains = total - (page * limit);
	if (remains > limit) {
		remains = limit;
	}
	
	var results = 'результатов';
	number = Math.abs(remains);
	number %= 100;
	if (number >= 5 && number <= 20) {
		results = 'результатов';
	}
	number %= 10;
	if (number == 1) {
		results = 'результат';
	}
	if (number >= 2 && number <= 4) {
		results = 'результата';
	}
	
	btn.text('Еще ' + remains + ' ' + results);
}

Вызов этой функции нужно раскомментировать в конце первой функции и добавить при загрузке страницы:
$(document).ready(function() {
	window.setTimeout(function() {
		setMore();
	}, 500);
});

Вот так у меня на тесте всё работает, даже пагинация кнопками. Вот
вся тестовая страница.
Константин
Константин
На всякий случай выложу пример который я использую, может кто поправит, а если не требуется, то может кому пригодится.

$q = $modx->newQuery( 'TicketFile' );
$q->limit( 1 );
$q->where(array(
   'parent' => $input
));

$q->prepare();
$q->stmt->execute();
$res = $q->stmt->fetchAll( PDO::FETCH_ASSOC );

foreach ( $res as $thumb ) {
    return $thumb['TicketFile_thumb'];
}

Вызывается в чанке таким макаром:

[[+id:getThumb]]
Алексей Ерохин
Алексей Ерохин
Я так делал.
Перед собственно определением плагина в js-файле добавлял это:
var storesex = new Ext.data.ArrayStore({
    id: 'minishop2-product-sex'
    ,fields: [{name: 'name', type: 'string']}
    ,data: [['Мужской'],['Женский'],['Унисекс']]
});
miniShop2.combo.ProductSex = function(config) {
    config = config || {};
    Ext.applyIf(config,{
        store: storesex
        ,emptyText: _('ms2_combo_select')
        ,displayField: 'name'
        ,valueField: 'name'
        ,hiddenName: 'sex'
        ,mode: 'local'
        ,triggerAction: 'all'
        ,editable: false
        ,selectOnFocus: false
        ,preventRender: true
        ,forceSelection: true
        ,enableKeyEvents: true
    });
    miniShop2.combo.ProductSex.superclass.constructor.call(this,config);
};
Ext.extend(miniShop2.combo.ProductSex,MODx.combo.ComboBox);
Ext.reg('minishop2-combo-product-sex',miniShop2.combo.ProductSex);
Теперь в плагине можно использовать xtype только что зарегистрированного комбобокса

sex: {xtype: 'minishop2-combo-product-sex',fieldLabel: 'Пол'}
Наумов Алексей
Наумов Алексей
<?php
if (!$modx->user->hasSessionContext('web') ) {
  die('Not logged!');
}

$prefix = trim($modx->getOption('placeholderPrefix', $scriptProperties, 'user.'));

$profile = $modx->user->getOne('Profile');

if(isset($_REQUEST['update']) && $_REQUEST['update'] == '1'){
    
    // имя
    $fullname = (isset($_REQUEST['fullname']) && !empty($_REQUEST['fullname'])) ? $_REQUEST['fullname'] : $profile->get('fullname');
    $fullname = trim(strip_tags($fullname));
    if(empty($fullname)){
        $fullname = 'Без имени';
    }
    $profile->set('fullname', $fullname);
    
    //фото
    $clearphoto = (isset($_REQUEST['clearphoto']) && $_REQUEST['clearphoto'] == '1');
    if($clearphoto){
        $profile->set('photo', '');
    }else{
        $photoConfig = array('w' => 48,'h' => 48,'zc' => 1,'bg' => '#fff','f' => 'png');
        // если выбран файл
        if(isset($_FILES['photo']) && isset($_FILES['photo']['tmp_name']) && !empty($_FILES['photo']['tmp_name'])){
            $file_info = getimagesize($_FILES['photo']['tmp_name']);
            if(!empty($file_info)){
                // пути
                $photoFileName = substr(uniqid(), 8).'.png';
                $newPath = 'uploads/users/'.$modx->user->get('id').'/';
                
                // проверяем каталог
                if(!file_exists(MODX_BASE_PATH.$newPath)){
                    if(!mkdir(MODX_BASE_PATH.$newPath, 0777, true)){
                        echo "Can`t create directory ".$newPath;
                    }
                }
                // перемещаем файл
                if(move_uploaded_file($_FILES['photo']['tmp_name'], MODX_BASE_PATH.$newPath.$photoFileName)){
                    // подключаем phpthumb
                    require_once MODX_CORE_PATH.'model/phpthumb/phpthumb.class.php';
                    // инициализируем
                    $phpThumb = new phpThumb();
                    $phpThumb->setSourceFilename(MODX_BASE_PATH.$newPath.$photoFileName);
                    foreach ($photoConfig as $k => $v) {
                        $phpThumb->setParameter($k, $v);
                    }
                    
                    // генерируем файл
                    if ($phpThumb->GenerateThumbnail()) {
                        
                        if ($phpThumb->RenderToFile(MODX_BASE_PATH.$newPath.$photoFileName)) {
                            // устанавливаем права на файл, это опционально, зависит от сервера
                            chmod(MODX_BASE_PATH.$newPath.$photoFileName, 0666);
                            // сохраняем изменения
                            $profile->set('photo', $newPath.$photoFileName);
                        }
                    }
                }
            }
        }
    }
    
    // вызываем событие OnBeforeUserFormSave
    $modx->invokeEvent('OnBeforeUserFormSave',array(
  	'mode' => modSystemEvent::MODE_UPD
		,'id' => $modx->user->get('id')
		,'user' => &$modx->user
		,'profile' => &$profile
	));

    // сохраняем все изменения
    $profile->save();
    $modx->user->save();

    // вызываем событие OnBeforeUserFormSave
    $modx->invokeEvent('OnUserFormSave',array(
		'mode' => modSystemEvent::MODE_UPD
		,'id' => $modx->user->get('id')
		,'user' => &$modx->user
		,'profile' => &$profile
	));
    // устанавливаем плейсхолдер, чтобы показать сообщение
    $modx->setPlaceholder($prefix.'success', '1');
}

// устанавливаем плейсхолдеры для отображения информации
$modx->setPlaceholder($prefix.'fullname', $profile->get('fullname'));
$modx->setPlaceholder($prefix.'photo', $profile->get('photo'));

return '';
Форму надеюсь с полями сами сделаете.

Поле clearphoto — это checkbox, типа «Удалить аватар».