XTRO

Участники
  • Публикации

    1039
  • Зарегистрирован

  • Посещение

  • Days Won

    59

Изменения репутации

  1. Like
    XTRO изменил репутацию abushyk в [Песочница] Пользовательские сущности и Связанные элементы   
    1. Предистория
     
    Допустим мы имеем базовый сайт на сайтбилле. У нас есть стандартная модель квартиры-недвижимости со всякими Цена, Площадь и прочими свойствами по вкусу. И вот, в один прекрасный момент, мы решили заняться торговлей квартирами в новостройках. А новостройки эти у нас зачастую представлены не отдельными зданиями, а целыми комплексами зданий, которые в свою очередь разбиты на секции.
     
    2. Анализ.
     
    Сначала разберемся со структурой. Самый простой вариант - мы решили еализовывать каждую квартиру в виде отдельного объекта. Например, если в ЖК "Элитный" у нас есть корпус А, а в нем секция I и в этой секции, грубо говоря, 15 квартир одной планировки, но некоторые на разных этажах, мы будем считать, что каждая квартира, даже одинаковой планировки - является одним объектом. Иными словами, мы не делаем группировок по типам, а ведем каждую квартиру отдельно. Это дает нам некоторую гибкость, так как квартиры на крайних этажах могут иметь меньшую цену в отличии от одноплановых с ними, на остальных этажах. Так же, из плюсов то, что мы можем задать какие-то особенности поквартирно, даже не смотря на то, что они одноплановые. Особенно это касается, когда квартиры продаются не в сыром виде, а меблированные или с финишной отделкой. Либо учесть иные особенности каждой квартиры.
     
    Из минусов такого подхода то, что если секция у нас 9-ти этажная с 4 квартирами на этаж, то на секцию получается 36 объектов. И дальше в геометрической прогрессии в зависимости от количества секций в корпусе и корпусов в ЖК.
     
    Соответственно, логично предположить, что кроме обычных для недвижимости параметров (цена, площадь, этаж,и т.д.) нам придется снабдить модель недвижимости дополнительными параметрами, устанавливающими ее привязку к конкретному положению в разрезе ЖК - конкретный ЖК, корпус ЖК, секция корпуса ЖК. И так же очевидно, что при заполнении формы эти параметры должны бы быть связаны цепочкой, как то при выборе отдельного ЖК, мы должны получить под выбор список корпусов только этого ЖК, что бы не рыскать в длиннющем списке всех корпусов. Аналогино и с секциями.
     
    3. Инструментарий.
     
    Для реализации нам понадобятся свежие приложения system, admin, table, customentity. Привлекать готовые приложения типа "Жилые комплексы" мы не будем.
     
    Из настроек нам будет необходимо включить параметр Настройки - Дополнительно - Off system Ajax. Данная опция выключает автоматические связки между зависимыми элементами и предоставляет нам полный контроль над определением своих зависимостей.
     
    Из почитать - http://wiki.sitebill.ru/index.php?title=%D0%A1%D0%B2%D1%8F%D0%B7%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5_%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%D1%8B
     
    4. Подготовка дополнительных сущностей.
     
    Если мы должны иметь возможность выбрать корпус, ЖК и секцию, значит у нас где-то должны быть списки этих значений. В Сайтбилле, для организации списков значений существует два типа полей - select_box и selectbox_by_query.
     
    Первый нам не подходит, так как не поддерживает организацию наследия. Т.е. штатными средствами этого элемента мы можем задать набор его вариантов значений, но не можем указать какие-то связующие ключи с другим элементом.
     
    Второй, поскольку хранится в БД в виде отдельной таблицы, вполне на это способен. Но для его использования необходима соответствующая таблица в БД. В нашем случае таблиц будет три - таблица ЖК, таблица корпусов и секций корпусов. Что бы избавить себя от рутинной работы по наполнению этих данных через phpMyAdmin создадим соответствующие таблицы в Редакторе форм.
     
    4.1. Жилые комплексы.
     
    Для ЖК - это таблица czhilkom с полями czhilcom_id (primary_key) и name (safe_string).

    Создав таблицу и наполнив ее элементами, нажимаем кнопку Создать таблицу.

    Теперь таблица ЖК существует физически в БД.
    Надо наполнить ее какими-то жилыми комплексами.
    Для того, что бы получить доступ к работе с этой сущность через стандартную админку, воспользуемся приложением Пользовательские сущности. Для начала проверим, что приложение установлено. Для этого в админке переходим по адресу 
    /admin/index.php?action=customentity&do=install
     
    При переходе по нему, в случае отсутствия необходимых таблиц для работы приложения, они будут созданы.
    После этого возвращаемся в Редактор форм и в заголовке таблицы czhilkom ищем кнопку со завездочкой

    Эта кнопка отвечает за создание мини-обработчика для сущности, у которой нет штатного обработчика (в виде встроенного модуля или стандартного\стороннего приложения). NB. Попытка создать этой кнопкой обработчик для встроенных сущностей, как Город, Район или для тех у которых есть приложения - Баннеры, Новости - ни к чему не приведет.
     
    После нажатия кнопки Создания обработчика мы увидим следующее окно

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

    Если перейти по предложенной ссылке, вы получите минималистический инструмент для управления вашими ЖК где вы можете добавить ЖК в список, изменить существующий или удалить ненужный. Приложение не следит за целостностью, т.е. если вы удаляете ЖК, то об удалении соответствующих зависимых корпусов и секций вам так же придется позаботиться самому.
     
    Не надейтесь на возможность реализации таким способом каких-то "творческих вывихов" - это исключительно инструмент для обеспечения удобства. Для полноценной работы с такими сущностями, как разделение их по пользователям с возможностью редактирования последними, организация страницы, например конкретного ЖК, шаблонизация необходимо создавать полноценное приложение.
     
    Для наших целей добавим два ЖК - Элитный и Морской с помощью кнопки Добавить запись
    Процесс, как вы можете заметить, вполне привычный и не должен вызвать трудностей
     
    4.1. Корпуса.
     
    Создание таблицы корпусов абсолютно ничем не отличается от создания таблицы ЖК кроме того, что у корпусов есть зависимость от ЖК. Например ЖК Элитный имеет два корпуса - Корпус А и Корпус Б. Тогда модель корпуса (ckorps) будет состоять из полей

    Поле czhilcom_id является обычным полем селектбокса подбирающим данныеиз внешней таблицы, в данном случае из таблицы czhilcom
    Простыня под спойлером
     
    Дальше все по алгоритму - создали таблицу в Редакторе форм, наполнили полями, создали физическую таблицу, зарегистрировали обработчик. Из Пользовательских переходим в Корпуса  добавляем

    Названия сущностей рекомендую давать расширенные - с включением родительского описания. Так как обработчик весьма прост, то особых способов отличить Корпус А от ЖК Элитный и Корпус А от ЖК Морской у вас не будет.
     
    В результате мы получаем нечто похожее на 

  2. Like
    XTRO изменил репутацию abushyk в [Песочница] Пользовательские сущности и Связанные элементы   
    Продолжаю отдельными постами, так как исчерпал лимит на картинки в одном сообщении ))
     
    4.1. Секции.
     
    Не буду давать расширенного описания, скажу только, что все идентично как для корпусов.
    Таблица csection и поля csection_id, name, ckorps_id (по таблице ckorps)
    В принципе для секций можно было бы установить двойную зависимость - указывать принадлежность секции к корпусу и к ЖК. Для некоторых случаев это оправдано (особенено если делается полноценное приложение), но в нашем случае, когда необходимо лишь поразграничивать принадлежности и сам корпус и ЖК будет указан в свойствах недвижимости, такая связка будет избыточной.
     
    В итоге
      5. Внедрение в недвижимость   Сущности у нас готовы, можно приступать к привязке их на объявление.   Нам необходимо добавить три свойства в нашу таблицу data - ЖК, Корпус и Секция. Все они будут добавляться полями типа select_by_query, что бы мы могли сформировать их списки в элементах выбора из соответствующих таблиц. Носить имена будут эти элементы czhilcom_id, ckorps_id и csection_id   Если теперь мы перейдем в форму добавления объявления мы увидим, что наши новые три поля уютно прописались в форме в виде привычных списков выбора.   Но если их поразворачивать, то вы увидите, что они вмещают все варианты из своих таблиц и не реагируют на состояние "родительского" элемента. Например выбор ЖК никак не отражается на содержимом списка корпусов.   Приступаем к наладке связей.   6. Связывание   Основой для связывания служит принцип связанных элементов формы - http://wiki.sitebill.ru/index.php?title=%D0%A1%D0%B2%D1%8F%D0%B7%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5_%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%D1%8B   Нам необходимо в рамках одной формы указать элементам на какие другие элементы они влияют в форме и от каких зависят.   Например - ЖК. От состояния этого элемента зависит возможный список выбора в элементе Корпуса. Эта зависимость описывается как Это значит, что элемент, в котором мы выбираем название ЖК связан с элементом с системным именем ckorps_id (элемент выбора Корпуса ЖК), а ключем, который внутри Корпуса соответствует жилому корпусу является значение из поля czhilcom_id модели Корпуса. Если взлянете выше, то это значение в модели Корпуса у нас является идентификатором ЖК,  к которому привязан Корпус. Больше ЖК у нас ни на что не влияет, потому и других параметров нет.   Далее Корпус. Корпус, аналогично ЖК влияет на "следующий" элемент - Секцию. Но, кроме этого, он еще должен знать от какого элемента зависит сам - это необходимо для формирования адекватного списка значений элемента Корпус, но не тогда, когда сделан выбор конкретного ЖК, а при загрузке формы. Например, если вы открыли на редактирование объявление в котором ЖК был указан как Элитный, тогда в списке Корпусов вполне ожидаемо окажется уже готовый список корпусо ЖК Элитный.   linked - описывает зависмость когда элемент влияет на что-то. depended - когда что-то влияет на элемент   И, наконец, Секция. Самый простой элемент. Он ни на что не влияет, но на него влияет Корпус. Что и видно из параметров.   Нет ничего страшного, если вы ничего не поняли про связи с первого раза. Это нормально, Я гарантирую это.   Если теперь вы попробуете загрузить форму добавления объявления, вы видите, что у вас доступен на выбор только элемент ЖК, а остальные будут подгружены только после выбора соответствующего родительского.   Для того, что бы увидеть этот эффект в Настройках необходимо включить параметр Настройки - Дополнительно - Off system Ajax   7. Эпилог   Ай, у меня не работают элементы выбора географии. Что делать?...   Тут все ожидаемо. Изначально принцип связанных элементов предназначался как-раз для географических элементов, что бы вывести из кода движка жесткие зависимости Страна-Регион-Город-Район\Улица и иметь более широкую возможность настройки своих связей. А так же, иметь возможность введения промежуточных элементов (Страна-Регион-Субрегион-Город), которые разрывали бы существующие связи, заложенные в код Сайтбилля. Именно поэтому опция Off system Ajax отрубает всю систему заложенных связей.   Возможно это слишком кардинально и стоило бы предусмотреть ступенчатую систему, когда подключение пользовательских связанных элементов регулировалось бы одной настройкой, а отключение привычной связки от Страны к Улице другой. На данный момент четкого мнения у меня пока нет.   Для себя я решил эту проблему навеской связей на географические элементы в виде, аналогичном системным правилам.   Т.е. country_id linked    region_id,country_id   region_id linked    city_id,region_id depended    country_id   city_id linked    street_id,city_id;district_id,city_id depended    region_id   district_id linked    mkrn_id,district_id depended    city_id   street_id depended    city_id   mkrn_id depended    district_id   8. Offtop   С другой стороны, даже этот способ немного избыточен. Если Город является дочерним к Региону, а Регион к Стране, то хранение всех трех значений для объявления - это "лишние" данные, хотя при организации поиска они весьма кстати.   Суть в том, что географические данные вполне возможно хранить в виде, схожем со структурой
      и получать к ним доступ более "человечным" путем Но в этом случае остается так же много вопросов связанных с совместимостью с многими приложениями, принципом организации смой геоструктуры (ведь если заструктурить географию от страны до улиц - это может стать неподъемным грузом, а если закончить городом, то не совсем понятно, как вести связь дальше к улицам, которые должны таки быть привязаны к городам или чему-то наследному от них).   В общем идея у нас полно, была бы возможность все реализовать)
  3. Like
    XTRO изменил репутацию abushyk в Удаление таблицы   
    Аналогия приблизительно такая - колонки в re_data - некие абстрактные хранилища информации. А поля-колонки учтенные в Редакторе форм (и которые хранятся в re_columns) - это некие фильтры, которые определяют как отображать, сохранять, проверять данные проходящие через них. В большинстве случаем один и другие колонки имеют прямое соответствие по системному имени - какое системное имя в re_columns, такое поле в re_data ему соответствует как источник. Но для некоторых полей такой связи нет. Например uploadify_images, который хранит данные в других таблицах, geodata - который имеет соответствие в виде двух поле в re_data или tlocation, который покрывает аж пять полей и таблицы данных.
    В целом, колонки из Редактора форм - это как настраиваемое сито для данных из re_data.
  4. Like
    XTRO получил репутацию от Дмитрий Кондин в Безопасность сайта   
    ну, а нам, тем более )))
     
    я б не расслаблялся
    не помню, но что-то понадобилось в PMA, залез и вижу картину (см.аттач)
    Это так, к вопросу о личке и xss
       


  5. Like
    XTRO получил репутацию от Дмитрий Кондин в Предложения для будущих версий.   
    вычистить лишний код
  6. Like
    XTRO получил репутацию от Дмитрий Кондин в Добавить поле Документы на объект   
    в типе записи выбрать uploadify_file
  7. Like
    XTRO получил репутацию от Дмитрий Кондин в Рекламные баннеры sitebill   
    почитать ветки форума, воспользоваться поиском, ознакомиться с wiki
    например http://www.etown.ru/s/topic/352-%D0%B1%D0%B0%D0%BD%D0%B5%D1%80-%D0%BD%D0%B0-%D1%81%D0%B0%D0%B9%D1%82%D0%B5/
    ознакомиться с видео http://www.youtube.com/user/DMn1c/videos
    уже не раз всё обсуждалось.
  8. Like
    XTRO изменил репутацию abushyk в Хлебные крошки   
    1. Базовый файл /apps/system/lib/frontend/view/kvartira_view.php
    2. Делаем /template/frontend/имя_шаблона/main/view/kvartira_view.php с содержимым
    class Local_Kvartira_View extends Kvartira_View {} 3. В /template/frontend/realia/main/main.php
    находим двоестрочия
    require_once(SITEBILL_DOCUMENT_ROOT.'/apps/system/lib/frontend/view/kvartira_view.php'); $kvartira_view = new Kvartira_View(); и заменяем их на require_once(SITEBILL_DOCUMENT_ROOT.'/apps/system/lib/frontend/view/kvartira_view.php'); require_once(SITEBILL_DOCUMENT_ROOT.'/template/frontend/'.$this->getConfigValue('theme').'/main/view/local_kvartira_view.php'); $kvartira_view = new Local_Kvartira_View();  
    4. Теперь мы имеем свой локальный обработчик просмотра карточки.
    5. Из базового файла (п.1) тянем в свой файл (п.2) функцию 
    protected function getBreadcrumbs($params){    require_once(SITEBILL_DOCUMENT_ROOT.'/apps/system/lib/admin/structure/structure_manager.php');    $Structure_Manager = new Structure_Manager();    $category_structure = $Structure_Manager->loadCategoryStructure();    return $this->get_category_breadcrumbs( $params, $category_structure, SITEBILL_MAIN_URL.'/' );} 6. И вот тут уже изголяемся. Можем придумать свой алгоритм ХК, можем использовать то, что есть.
    Например
    protected function getBreadcrumbs($params){     require_once(SITEBILL_DOCUMENT_ROOT.'/apps/system/lib/admin/structure/structure_manager.php');     $Structure_Manager = new Structure_Manager();     $category_structure = $Structure_Manager->loadCategoryStructure();     $bcstr=$this->get_category_breadcrumbs( $params, $category_structure, SITEBILL_MAIN_URL.'/' );$bcarray=explode(' / ', $bcstr);return implode('*', $bcarray);} В результате ХК склеенные звездочкой. Не самый оптимальный путь, но довольно быстрый.
     
    В целом, думаю, направление понятное.
  9. Like
    XTRO изменил репутацию abushyk в Помещать изображения в подпапку   
    Проблема не в том, что бы разложить по папкам, а в том, что потом с ними делать и как эти картинки оттуда вынимать для шаблонов списков, просмотров, выводов в колонках, всяких экспортах в эксели, пдфы и хмл. Это ведь тоже надо прописать, что бы код знал КАК сформировать путь к файлу с картинкой. И что бы знал как удалить все картинки, привязанные к данному объявлению.
    А просто по папкам кинуть - это да, делается быстро.
  10. Like
    XTRO получил репутацию от abushyk в Безопасность сайта   
    ещё как вариант (jquery) и капча вообще не нужна.
    Поскольку бот не умеет думать:
    -если submit завязан на GET, то при загрузке страницы дефолтную кнопку Отправить (type="submit") прячем жабой (append), добавляем жабой новый input (bind), но уже type="button", но него вешаем событие Save (ajax), в котором ещё добавляем флаг (немного избыточно, но не помешает) для проверки потом этого флага, но уже в  POST.
    В скрипте на входе смотрим: Если не POST или нет флага - досвидос.
  11. Like
    XTRO изменил репутацию OXYGEN в Редирект со страницы без слеша в конце адреса на со слешем   
    Рабочая конструкция. XTRO теперь вход в админку работает
    Options -IndexesAddDefaultCharset UTF-8<IfModule mod_rewrite.c>RewriteEngine OnRewriteBase /RewriteCond %{REQUEST_FILENAME} -s [OR]RewriteCond %{REQUEST_FILENAME} -l [OR]RewriteCond %{REQUEST_FILENAME} -dRewriteRule ^.*$ - [NC,L]RewriteRule ^(.*[^\/])$ /$1\/ [R=301,L]RewriteRule . index.php [L]ErrorDocument 404 /404.html</IfModule> 
  12. Like
    XTRO получил репутацию от Дмитрий Кондин в Где увидеть все переменные сайта?   
    а я юзаю
    Smarty Debug Console просто через {debug}
    или для конкретники
    <pre>{$news_list_column|debug_print_var}</pre> или
    {$themeurl|var_dump}
  13. Like
    XTRO получил репутацию от Дмитрий Кондин в Банер на сайте   
    а я, по своей тупости, (ещё до просмотра этого видео) полез приспосабливать для другого , т.к понадобилось сделать рандомный вывод графических custom_банеров в определенном разделе,
    подход использовал аналогичный: хтмл_код - обычная ссылка с картинки, а в имени - осмысленное системное имя для каждого банера (типа right_top_banner1, right_top_banner2, right_top_banner3 ), т.е. отличаются только цифрой, потом в нужном месте темплейта:
    {if $smarty.server.REQUEST_URI|regex_replace:"/\?(.*)/":"" eq '/kommercheskaya'} <div class="custom_banners">                {assign var=tmp_var value = $right_top_banner{rand(1,3)} }                {if $tmp_var}                    {$tmp_var}                {/if} </div> {/if} + добавить учёт кликов и получим миниротатор
  14. Like
    XTRO получил репутацию от vihr в Memory Usage Information   
    в начало index.php
    $time_start = microtime(true);require('memdbg.php');$m = new MemoryUsageInformation(true); сам класс:
    <?php/* спасибо автору скрипта, к сожадению мне не известному*/class MemoryUsageInformation { private $real_usage; private $statistics = array(); public function __construct($real_usage = false) { $this->real_usage = $real_usage; } public function getCurrentMemoryUsage($with_style = true) { $mem = memory_get_usage($this->real_usage); return ($with_style) ? $this->byteFormat($mem) : $mem; } public function getPeakMemoryUsage($with_style = true) { $mem = memory_get_peak_usage($this->real_usage); return ($with_style) ? $this->byteFormat($mem) : $mem; } public function setMemoryUsage($info = '') { $this->statistics[] = array('time' => time(), 'info' => $info, 'memory_usage' => $this->getCurrentMemoryUsage()); } public function printMemoryUsageInformation() { foreach ($this->statistics as $satistic) { $str = "Time: " . $satistic['time'] . " | Memory Usage: " . $satistic['memory_usage'] . " | Info: " . $satistic['info']; $str .= "\n"; } $str.= "\n\n<br />"; $str.= "Peak of memory usage: " . $this->getPeakMemoryUsage(); $str.= "\n\n<br />"; return $str; } public function setStart($info = 'Initial Memory Usage') { $this->setMemoryUsage($info); } public function setEnd($info = 'Memory Usage at the End') { $this->setMemoryUsage($info); } private function byteFormat($bytes, $unit = "", $decimals = 2) { $units = array('B' => 0, 'KB' => 1, 'MB' => 2, 'GB' => 3, 'TB' => 4, 'PB' => 5, 'EB' => 6, 'ZB' => 7, 'YB' => 8); $value = 0; if ($bytes > 0) { if (!array_key_exists($unit, $units)) { $pow = floor(log($bytes)/log(1024)); $unit = array_search($pow, $units); } $value = ($bytes/pow(1024,floor($units[$unit]))); } if (!is_numeric($decimals) || $decimals < 0) { $decimals = 2; } return sprintf('%.' . $decimals . 'f '.$unit, $value); }} для независимости добавил в main.php перед render шаблона , но по правильному надо в выход:
    // ************************************************// global $time_start, $m; $time_end = microtime(true); $precision = 2; $tr= 'Build time : '. intval(($time_end - $time_start)*pow(10,$precision))/pow(10,$precision).' sec'; $smarty->assign('timerender',$tr); unset($tr); $usemem= memory_get_peak_usage(); $usemem = 'Memory: '. round($usemem/1024/1024,2) . ' Mb'; $smarty->assign('usemem',$usemem); unset($usemem); $smarty->assign('pr_m',$m->printMemoryUsageInformation());// ************************************************// ну и в самом main.tpl перед </body>:
    <div class="dbg">{$timerender} - {$usemem} - {$pr_m}</div>
    можно использовать, как вариант, для замера цикла:
    $m = new MemoryUsageInformation(true);$m->setStart();$a = array();$m->setMemoryUsage("до цикла");for($i = 0; $i < 100000; $i++) { $a[$i] = uniqid();}$m->setMemoryUsage("после цикла");unset($a);$m->setMemoryUsage("после unset()");$m->setEnd();$m->printMemoryUsageInformation(); Осталось притулить sql вывод и получится debug панелька. Хотя лучше всё организовать на уровне хелпера.

  15. Like
    XTRO получил репутацию от Дмитрий Кондин в количество тем в категориях   
    я сделал по другому (всё по аналогии, не совсем кошерно, т.к. дублирование в декораторе, но на скорую руку работает) :
    В темплейте как обычно:
    {$resp_menu} создал \apps\system\lib\frontend\menu\resp_menu.php:
    <?php/** * Resp menu class */class Resp_Menu extends Structure_Manager{    /**     * Constructor     */    function __construct()    {        $this->SiteBill();    }    /**     * Get resp menu     * @param     * @return     */    function get_menu()    {        require_once(SITEBILL_DOCUMENT_ROOT.'/apps/system/lib/frontend/menu/menu_decorator.php');        //require_once(SITEBILL_DOCUMENT_ROOT.'/apps/system/lib/admin/structure/structure_implements.php');        $SM=new Structure_Manager();        $structure=$SM->loadCategoryStructure();        $data_structure=$SM->load_data_structure(0);        foreach($structure['catalog'] as $cat_point){            $ch=0;            $SM->getChildsItemsCount($cat_point['id'], $structure['childs'], $data_structure['data'][0], $ch);            $data_structure['data'][0][$cat_point['id']]+=$ch;        }        foreach($structure['catalog'] as $id=>$cat_point){            if(!in_array($cat_point['id'], $structure['childs'][0])){                $structure['catalog'][$id]['name']=$structure['catalog'][$id]['name'].' ('.$data_structure['data'][0][$id].')';            }        }        $rs = Menu_Decorator::getMenu('resp_menu', $structure);        return $rs;    }}?> в apps\system\lib\frontend\menu\menu_decorator.php добавил выбор:
    case 'resp_menu' : {                $function_name='getMenu_respmenu';                break;            } и в конец класса:
    private static function getMenu_respmenu($category_structure){        $rs = '<ul class="menu">';        foreach ( $category_structure['childs'][0] as $item_id => $categoryID ) {            if($category_structure['catalog'][$categoryID]['url']!=''){                if ( preg_match('/^http/', $category_structure['catalog'][$categoryID]['url']) ) {                    $rs .= '<li><a href="'.$category_structure['catalog'][$categoryID]['url'].'">'.$category_structure['catalog'][$categoryID]['name'].'</a>';                } else {                    $rs .= '<li><a href="'.SITEBILL_MAIN_URL.'/'.$category_structure['catalog'][$categoryID]['url'].'">'.$category_structure['catalog'][$categoryID]['name'].'</a>';                }            }else{                $rs .= '<li><a href="'.SITEBILL_MAIN_URL.'/topic'.$categoryID.'.html">'.$category_structure['catalog'][$categoryID]['name'].'</a>';            }            $rs .= self::getChildNodes_respmenu($categoryID, $category_structure, $current_category_id);            $rs .= '</li>';        }        $rs .= '</ul>';        return $rs;    }    private static function getChildNodes_respmenu($categoryID, $category_structure, $current_category_id) {        if ( !is_array($category_structure['childs'][$categoryID]) ) {            return '';        }        $rs = '<ul style="z-index: 50">';//TODO move in css //        foreach ( $category_structure['childs'][$categoryID] as $child_id ) {            if($category_structure['catalog'][$child_id]['url']!=''){                $rs .= '<li><a href="'.SITEBILL_MAIN_URL.'/'.$category_structure['catalog'][$child_id]['url'].'">'.$category_structure['catalog'][$child_id]['name'].'</a>';            }else{                $rs .= '<li><a href="'.SITEBILL_MAIN_URL.'/topic'.$child_id.'.html"><span class="no-image">'.$category_structure['catalog'][$child_id]['name'].'</a>';            }            if ( count($category_structure['childs'][$child_id]) > 0 ) {                $rs .= self::getChildNodes_respmenu($child_id, $category_structure, $current_category_id);            }            $rs .= '</li>';        }        $rs .= '</ul>';        return $rs;    } в main.php в main() добавил
            } elseif ( $this->getConfigValue('menu_type') == 'respmenu' ) {                require_once(SITEBILL_DOCUMENT_ROOT.'/apps/system/lib/frontend/menu/respmenu.php');                $resp_menu = new Resp_Menu();                $this->template->assert('resp_menu', $resp_menu->get_menu()); Ну, и в \apps\config\admin\config_mask.php, чтобы в админке можно было выбрать тип, заменил на:
    $data_model['menu_type']['select_data'] = array('purecss'=>'purecss','slidemenu'=>'slidemenu','megamenu'=>'megamenu', 'respmenu'=>'respmenu'); css по вкусу. Я юзал KickStart. Правда, этот вариант отработает до очередного обновления .

    Имхо, просто добавить getChildsItemsCount() в виде опции (в конфиге рядом с выбором типа меню в следующих релизах )

  16. Like
    XTRO получил репутацию от Дмитрий Кондин в Memory Usage Information   
    в начало index.php
    $time_start = microtime(true);require('memdbg.php');$m = new MemoryUsageInformation(true); сам класс:
    <?php/* спасибо автору скрипта, к сожадению мне не известному*/class MemoryUsageInformation { private $real_usage; private $statistics = array(); public function __construct($real_usage = false) { $this->real_usage = $real_usage; } public function getCurrentMemoryUsage($with_style = true) { $mem = memory_get_usage($this->real_usage); return ($with_style) ? $this->byteFormat($mem) : $mem; } public function getPeakMemoryUsage($with_style = true) { $mem = memory_get_peak_usage($this->real_usage); return ($with_style) ? $this->byteFormat($mem) : $mem; } public function setMemoryUsage($info = '') { $this->statistics[] = array('time' => time(), 'info' => $info, 'memory_usage' => $this->getCurrentMemoryUsage()); } public function printMemoryUsageInformation() { foreach ($this->statistics as $satistic) { $str = "Time: " . $satistic['time'] . " | Memory Usage: " . $satistic['memory_usage'] . " | Info: " . $satistic['info']; $str .= "\n"; } $str.= "\n\n<br />"; $str.= "Peak of memory usage: " . $this->getPeakMemoryUsage(); $str.= "\n\n<br />"; return $str; } public function setStart($info = 'Initial Memory Usage') { $this->setMemoryUsage($info); } public function setEnd($info = 'Memory Usage at the End') { $this->setMemoryUsage($info); } private function byteFormat($bytes, $unit = "", $decimals = 2) { $units = array('B' => 0, 'KB' => 1, 'MB' => 2, 'GB' => 3, 'TB' => 4, 'PB' => 5, 'EB' => 6, 'ZB' => 7, 'YB' => 8); $value = 0; if ($bytes > 0) { if (!array_key_exists($unit, $units)) { $pow = floor(log($bytes)/log(1024)); $unit = array_search($pow, $units); } $value = ($bytes/pow(1024,floor($units[$unit]))); } if (!is_numeric($decimals) || $decimals < 0) { $decimals = 2; } return sprintf('%.' . $decimals . 'f '.$unit, $value); }} для независимости добавил в main.php перед render шаблона , но по правильному надо в выход:
    // ************************************************// global $time_start, $m; $time_end = microtime(true); $precision = 2; $tr= 'Build time : '. intval(($time_end - $time_start)*pow(10,$precision))/pow(10,$precision).' sec'; $smarty->assign('timerender',$tr); unset($tr); $usemem= memory_get_peak_usage(); $usemem = 'Memory: '. round($usemem/1024/1024,2) . ' Mb'; $smarty->assign('usemem',$usemem); unset($usemem); $smarty->assign('pr_m',$m->printMemoryUsageInformation());// ************************************************// ну и в самом main.tpl перед </body>:
    <div class="dbg">{$timerender} - {$usemem} - {$pr_m}</div>
    можно использовать, как вариант, для замера цикла:
    $m = new MemoryUsageInformation(true);$m->setStart();$a = array();$m->setMemoryUsage("до цикла");for($i = 0; $i < 100000; $i++) { $a[$i] = uniqid();}$m->setMemoryUsage("после цикла");unset($a);$m->setMemoryUsage("после unset()");$m->setEnd();$m->printMemoryUsageInformation(); Осталось притулить sql вывод и получится debug панелька. Хотя лучше всё организовать на уровне хелпера.

  17. Like
    XTRO получил репутацию от bureau в Сравнение HTML5 фреймворков   
    "A Collection of best front End frameworks"
    (Сравнительная таблица со ссылками на разрабов и гитхаб.)
     
    Статья на хабре с описанием 30 решений.
  18. Like
    XTRO изменил репутацию Дмитрий Кондин в Начинаем защищать авторское право   
    http://www.youtube.com/watch?v=2L9_Tmxg5jY
  19. Like
    XTRO изменил репутацию Realtor в Предложения для будущих версий.   
    В ЛК разделить таблицу объявлений на Все Активные Архив (неактивные) Корзина 
    с возможностью оптовой Активации, Деактивации, Удаления и Восстановления
  20. Like
    XTRO изменил репутацию abushyk в добавить новые поля в поиск (template_search и др.)   
    Итак, мы имеем набор полей:
     
    is_wifi Наличие интернета - поле типа checkbox. На форме присутствует в сиде чекбокса.
    floor_type Тип покрытия пола - select_box с вариантами {0~~не указано}{1~~плитка}{2~~дерево}{3~~ламинат} - отображается в виде выпадающего списка
    sea_distance Расстояние до моря. Тип safe_string, но отмеченный как is_ranged=1, что бы в форме поиска выводилось в виде двух полей - макс. и мин. значения.
     
    Мы добавили эти поля в модель, каким-то образом разместили их на формах поиска. Теперь главная задача - заставить движек обработать их.
    Для этого существует файл шаблонного поиска, который размещается в /template/frontend/имя_шаблона/main/ и носит имя template_search.php и не иначе.
    При наличии этого файла движек автоматически обратится к нему и запросит данные для осуществления выборки.
    В минимальной комплектации этот файл состоит из класса и двух функций:
    http://pastebin.com/TmBSS9q8
     
    Задача функции getParams забрать данные из запроса и подумать, стоит ли их передавать дальше.
    А функции run, к которой обращается движек за данными, решить каким образом следует сравнить\обработать полученные параметры для формирования нужной выборки данных.
     
    Итак, поехали.
    1
    Начнем с самого простого - чекбокса is_wifi. Чекбоксы отличаются тем, что в запросе они либо приходят, либо нет.
    Из запроса берем его функцией $this->getRequestValue('is_wifi'), которая возвращает значение NULL, если такого параметра не существует.
    if(NULL!==$this->getRequestValue('is_wifi')){
    $params['is_wifi'] = 1;
    }
     
    Проверили, не пусто ли, если нет, значит чекбокс отметили и мы записываем его в $params  в виде утвердительной единицы. Единицы потому, что в принципе больше нам инфы не нужно, достаточно знать, что параметр запрошен.
     
    Дальше floor_type. Этот тип передается в запрос в виде ключа своих значений. Т.е. выбрав "дерево" в запрос у нас приедет "2". Значит мы знаем, что будет целая цифра.
    if(0!==(int)$this->getRequestValue('floor_type')){
    $params['floor_type'] = (int)$this->getRequestValue('floor_type');
    }
     
    Мы гарантированно делаем из значения параметра целое число с помощью (int) и сравниваем его с 0 - нашим значением никакого значения. Если оно не равно нулю, значит пользователь запросил конкретный тип покрытия и мы сохраняем его значение в $params['floor_type']. Но сохраняем уже конкретным начением, таккак, в отличии от чекбокса, тут нам важно само значение, а не его наличие.
     
    sea_distance. При использовании пользовательских форм, которые енерирует движек на основе ваших выборок это поле представится в виде двух полей с именами созданными по принципу sea_distance_min и sea_distance_max. Соотв. и дву переменные прийдут в запросе. Каждую ловим отдельно.
    Для простоты допустим, что мы готовы обработать целые расстояния до моря: 1, 5, 100.
    if(0!==(int)$this->getRequestValue('sea_distance_min')){
    $params['sea_distance_min'] = (int)$this->getRequestValue('sea_distance_min');
    }
    if(0!==(int)$this->getRequestValue('sea_distance_max')){
    $params['sea_distance_max'] = (int)$this->getRequestValue('sea_distance_max');
    }
    Принцип прост. Мы приводим значение к целому. Если пользователь вписал в поле не число, а "аврцуоац" строку, она приведется к нулю. И сравниваем все это с нулем. Искать по нулевому значению смысла нет, поэтому мы сохраняем только те значения, которые от него отличны. Разницы между мин и макс значением в момент их забора из запроса мы не делаем. Она не важна сейчас, но будет важна в следующей функции.
     
    2
    Переходим к функции run()
     
    Методика ее работы такая
    1. взять параметр
    2. создать кусочек запроса.
     
    Для чекбокса
    if(isset($params['is_wifi']) && isset($data_model_array['is_wifi'])){ $where_array[]=DB_PREFIX.'_data.is_wifi=1'; } Расшифровка. Проверяем, есть ли в параметрах запроса переменная is_wifi  и есть ли в нашей модели поле с таким именем (так как условие может быть, а поле мы давно погасили за ненадобностью). Если все эти условия выполнены, мы указываем, что хотим дополнить условия нашего запроса сравнением, которое выберет записи, где is_wifi равно1, т.е. при сохранении записи был отмечен чекбокс.   Для floor_type if(isset($params['floor_type']) && isset($data_model_array['floor_type'])){ $where_array[]=DB_PREFIX.'_data.floor_type='.$params['floor_type']; } Все аналогично предыдущему за исключением того, что тут мы просим сравнить поле floor_type записи, которое хранит ключ указанного типа покрытия, с переданным в запросе.
     
    Для ранжированного sea_distance
    if(isset($params['sea_distance_min']) && isset($data_model_array['sea_distance'])){ $where_array[]=DB_PREFIX.'_data.sea_distance*1>='.$params['sea_distance_min']; } if(isset($params['sea_distance_max']) && isset($data_model_array['sea_distance'])){ $where_array[]=DB_PREFIX.'_data.sea_distance*1<='.$params['sea_distance_max']; } И тут почти без изменений. Главное отличие - мы устанавливаем условия в зависимости от того _max или _min параметр мы хотим сравнить. Обратите внимание на DB_PREFIX.'_data.sea_distance*1. В неоптимизированных БД сайтбилля поля под safe_string имеют строковой тип. Поэтому, что бы не было строкового сравнения, где строковое "2" больше строкового "100", мы принудительно делаем значение поля числом перед сравнением. И тогда уже будет натуральное сравнение, где 2<100.
     
    и вот примерно вот так http://pastebin.com/8jX7WEEH все єто будет выглядеть в конце.
  21. Like
    XTRO изменил репутацию Дмитрий Кондин в Обновление 2.8.2   
    Сначала обновить system до 2.8.2
    Затем приложения admin, table, columns, config, comment (у кого есть).
    В обновлениях:
    1. Заменили редактор структуры, теперь удобнее удалять и редактировать.
    2. Добавили новый тип загрузчика картинок (в редакторе форм для таблицы data - удалить старое поле image и добавить новое поле image с типом uploads и отмеченной галочкой "Хранить значение поля в таблице")
    3. В конфиге добавились опции: 
    Не публиковать объявления из ЛК без премодерации,  Использовать встроенный счетчик просмторов (когда он выключен, то работает система гораздо быстрее),  Mailer: Отключить передачу дополнительных флагов в заголовках письма - на некоторых хостингах с этим флагом почта не идет,  Сохранять копию изображений без водяного знака (для этого нужно создать каталог /img/data/nowatermark/ Вскоре сделаю видео об этих новых функциях и их использовании.
     
    NB. Поле типа uploads по сути не является заменителем для поля типа uploadify_image. Мы предприняли некоторые действия для поддержания новым полем старых данных, но, в первую очередь, следует рассматривать поле типа uploads как новый тип данных со схожим с uploadify_image функционалом. (abushyk)
  22. Like
    XTRO изменил репутацию Дмитрий Кондин в Не прикрепляются фотки к обьявлению   
    В новой версии будет новый тип загрузчика, он построен по другой технологии и там будет мультизагрузка и нет привязки к флеш, думаю это уменьшить количество глюков.
  23. Like
    XTRO изменил репутацию abushyk в Пропали объявления   
    В ближайшее время будет обновление где мы избавились от этого досадного глюка - неотображения объявления с неуказанным типом недвижимости.
  24. Like
    XTRO изменил репутацию Дмитрий Кондин в maskedinput.js и мобильные броузеры   
    На мобильных маску ввода еще не тестировали, попробуем найти адаптивные библиотеки.
  25. Like
    XTRO изменил репутацию Дмитрий Кондин в Предложения для будущих версий.   
    Думаю надо сделать отдельную ветку в форуме.
    Там будет каждая новая фича отдельным топиком. Если пользователю интересна эта фича, то он там пишет +1, в результате тема которая интересна большему количеству появится в новой версии.