# Yupe Web

#  Что такое "Юпи!"? 

# Что такое "Юпи!"? #

"Юпи!" - это современная платформа для разработки веб-приложений.
"Юпи!" подходит для разработки и запуска приложений любого типа. Это могут быть: [сайты-сообщества](https://yupe.ru/community), [интернет-магазины](https://yupe.ru/ecommerce), новостыне порталы и т.д.
Кроме того, "Юпи!" можно использовать для разработки внутренних систем: **CRM-системы**, системы сбора и анализа контактов и лидов, дашборды к внешним сервисам и т.д.

"Юпи!" имеет модульную структуру, благодаря чему, довольно просто расширяется и масштабируется. В стандартной поставке включено [более 40 модулей](/modules/).

В настоящий момент на "Юпи!" работает **более 2500 проектов**, среди которых около **500 интернет-магазинов**.

# Сколько стоит? #

"Юпи!" совершенно бесплатная платформа (Open Source), вы можете использовать ее для своего бизнеса без каких-либо ограничений. Просто [скачайте](https://yupe.ru/download) и начинайте работать!


# Что в документации? #

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

* Начало. [Требования к хостингу](/hosting/), [установка](/install/), [обновление](/update/)
* Пользователю. [Обзор модулей](/modules/), [настройка сайта](/settings/), [как сделать?](/how/)
* Разработчику. [Знакомство с Yii](/intro/), [создание своего модуля](/module-create/)

!!! important "Придем на помощь!"
    Дополнительные модули и услуги вы можете найти в нашем [магазине](https://yupe.ru/store) -  ["Юпи! Store"](https://yupe.ru/store)
    Если вам не хватает какой-то функциональности - [напишите нам](https://yupe.ru/contacts) или [нашим партнерам](https://yupe.ru/partners) и мы обязательно поможем!


#  Знакомство с Yiiframework 1.x

# Знакомство с Yiiframework 1.x

В основе Yupe! лежит фреймворк Yiiframework версии 1.x, поэтому для понимания работы, принципов модификации системы и написания собственных модулей, необходимо в первую очередь поближе познакомиться с этим фреймворком.

Мы рекомендуем ознакомиться со следующими разделами.

Для новичков в Yii:

* [Что такое Yii](http://www.yiiframework.com/doc/guide/1.1/ru/quickstart.what-is-yii)
* [Установка](http://www.yiiframework.com/doc/guide/1.1/ru/quickstart.installation)
* [Создание первого приложения](http://www.yiiframework.com/doc/guide/1.1/ru/quickstart.first-app)
* [Модель-Представление-Контроллер (MVC)](http://www.yiiframework.com/doc/guide/1.1/ru/basics.mvc)
* [Лучшие практики MVC](http://www.yiiframework.com/doc/guide/1.1/ru/basics.best-practices)

!!! note "Обратите внимание!"
    Обязательно прочитайте разделы, описывающие работу с [базой данных](http://www.yiiframework.com/doc/guide/1.1/ru/database.overview) и работу с [формами](http://www.yiiframework.com/doc/guide/1.1/ru/form.overview).

Если вы собираетесь писать свои модули и дополнения для "Юпи!":

* [Модули в Yii](http://www.yiiframework.com/doc/guide/1.1/ru/basics.module)
* [Темы оформления и скины](http://www.yiiframework.com/doc/guide/1.1/ru/topics.theming)
* [Улучшение производительности](http://www.yiiframework.com/doc/guide/1.1/ru/topics.performance)


!!! important "Придем на помощь!"
    Мы предлагаем услугу [обучения разработчиков на Yii и "Юпи!"](https://yupe.ru/service/training), обращайтесь!
    Для новичков в ООП, рекомендуем [курс от Дмитрия Елисеева](http://www.elisdn.ru/oop-week?utm_medium=affiliate&utm_source=yupe) - [Практикум ООП](http://www.elisdn.ru/oop-week?utm_medium=affiliate&utm_source=yupe)!


#  Требования к хостингу

# Требования к хостингу

Для работы проектов на "Юпи!" подойдет самый простой и доступный хостинг.

Убедитесь, что он удовлетворяет следующим параметрам:

* PHP версии **5.4** и выше (рекомендуем 5.6, проверено и на PHP 7)
* MySQL 5.1 и выше с поддержкой InnoDB
* Nginx + PHP-FPM (рекомендовано, хотя все отлично работает и на Apache)
* Возможность использовать Composer-пакеты (желательно)
* Жесткий диск объемом от 1 Гб
* Оперативная память объемом от 512 Мб

Хостинга/сервера с такими параметрами будет вполне достаточно для старта вашего интернет-магазина.
Для небольших магазинов мы рекомендуем хостинг от  [timeweb](http://timeweb.com/ru/services/hosting/?i=28284), для более крупных проектов - виртуальные сервера от [firstvds](https://firstvds.ru/?from=442205).


!!! note "Обратите внимание!"
    Если вы не можете выбрать подходящего поставщика услуг хостинга - обратитесь к <a href='https://yupe.ru/service/hosting' target='_blank'>нашим рекомендациям</a>
    или задайте вопрос на <a href='http://talk.yupe.ru/viewforum.php?f=80' target='_blank'>форуме</a>.


!!! important "Придем на помощь!"
    Наши специалисты могут помочь вам <a href='https://yupe.ru/store/services/install.html' target='_blank'>установить</a> платформу на выбранный хостинг
    или произвести полную <a href='https://yupe.ru/store/services/server.html' target='_blank'>настройку сервера</a> - пожалуйста, обращайтесь!


# Установка Юпи! на Linux

Установка Юпи! на Linux
========================

За пример взят дистрибутив **Ubuntu 12.04**

P.S. Данный пример установки вебсервера "В слепую" большинство операций и настроек система делает за вас автоматически, пример не расчитан на "тонкую настройку" apache2 сервера, а приведен как пример быстрого начала работы с CMS Yupe!

Установка без веб сервера ("С нуля")
====================================

Устанавливаем пакет tasksel. Открываем терминал пишем:

**sudo apt-get install tasksel**


После чего ставим сборку apache2+mysql+php

**sudo tasksel install lamp-server либо sudo apt-get install lamp-server**

В процессе установки указываем пароль и пользователя для базы данных.
Проверяем работу apache2

**http://localhost/**

Должна открыться страница с заголовком "It works!"

Ставим phpmyadmin


**sudo apt-get install phpmyadmin**

Переходим по ссылке:
http://localhost/phpmyadmin.

Для входа используем указанный выше логин и пароль.
Создаем базу анных для Yupe
К примеру с помошью ранее установленного phpmyadmin.
Подключаем mod_rewrite к Apache2

**sudo a2enmod rewrite**


Редактируем конфигурацию хостов:

**sudo nano /etc/apache2/sites-available/default**

А именно, изменяем параметр

 **AllowOverride None на AllowOverride All**

Перезагружаем Apache2

 **sudo service apache2 restart**

Приступаем к установке Юпи!

Из корневого каталога переходим в /var/www/ ,
Создаем папку для Yupe! к примеру "myblog".
Копируем в нее все файлы cms.

При открытиии ссылки **http://localhost/myblog** у Вас должно открываться дерево файлов.

Раздаем права на папку с Yupe!

**chmod -R 755 /var/www/myblog/**

Начинаем установку Yupe!
Переходим по ссылке **http://localhost/myblog/public** , должна открыться начальная страница установки.
Следуем по пунктам установщика и вносим необходимые данные.


Решение проблем с mod_rewrite

Если при переходе по ссылке http://localhost/myblog/public возникает ошибка 500, делаем следующее:
Переходим в файл дирректив который мы уже правили

**sudo nano /etc/apache2/sites-available/default**

меняем правила в Directory /var/www/ на следующие:


Options Indexes FollowSymLinks

AllowOverride None

Allow from all

Под Directory /var/www/ пишем диррективу для нашего сайта:

<Directory /var/www/myblog/>

Options FollowSymLinks

**AllowOverride All**

</Directory>

Переходим по ссылке http://localhost/myblog/public

Автор [https://github.com/hitakiri](https://github.com/hitakiri)

#  Установка и запуск "Юпи!"

# Установка и запуск "Юпи!"

!!! note "Обратите внимание!"
    Перед установкой убедитесь, что ваш хостинг удовлетворяет [системным требованиям](/hosting/) платформы.

Юпи! нетребовательна к ресурсам, поэтому для проектов с невысокой и средней посещаемостью подойдет любой [виртуальный хостинг](https://yupe.ru/service/hosting), на котором будет установлены PHP (не ниже 5.4) и MySQL.
Обо всех остальных расширениях, необходимых для работы сайта, вы будете проинформированы на 3-м шаге установки системы.

Установку платформы можно разделить на следующие этапы:

* Получение дистрибтива
* Подготовка хостинга или сервера
* Установка платформы
* Настройка и запуск


Получение дистрибутива возможно тремя способами:

* Скачивание zip-архива (рекомендуем новичкам)
* Установка с помощью Composer (рекомендуем разработчикам)
* Клонирование репозитория с GitHub (рекомендуем разработчикам)

## Установка на OpenServer (видео)

<iframe width="550" height="315" src="https://www.youtube.com/embed/PA6_wmQqlJw" frameborder="0" allowfullscreen></iframe>

## Установка через Composer

> Для развертывания Юпи! вам понадобится [Composer](https://getcomposer.org/) и [Git](http://git-scm.com/downloads).
> В данной документации подразумевается глобальная установка Composer-а, если в вашем случае это не так, то вместо команды `composer` нужно писать `php composer.phar`

Установка последней стабильной версии производится командой:

```
composer create-project yupe/yupe <директория проекта> --no-dev --prefer-dist
```

Директорию проекта можно не указывать, в таком случае файлы Юпи! будут помещены в папку `yupe` в текущей директории.

Если вам нужно установить другую версию системы, например ту, которая на данный момент находится в разработке, то необходимо немного модифицировать уже известную нам команду:

```
composer create-project yupe/yupe:<номер версии>.x-dev <директория проекта> --no-dev --prefer-dist
```
Будьте осторожны при использовании версии из ветки *dev*, данная версия предназначена исключительно для разработчиков и может содержать нестабильный код.

Номер необходимой версии можно посмотреть на сайте [packagist.org](https://packagist.org/packages/yupe/yupe) или на [github](https://github.com/yupe/yupe/releases)

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

Преимущества установки через composer:

* простота установки - запустите команду и остальное composer сделает за вас
* простота обновления зависимостей - composer самостоятельно может установить более свежие версии используемых пакетов
* простота интеграции дополительных пакетов - лубой composer-пакет может быть использован в вашем "Юпи!"-проекте без проблем

## Установка через Git

> Для развертывания этим способом вам понадобится [Composer](https://getcomposer.org/) и [Git](http://git-scm.com/downloads).
> В данной документации подразумевается глобальная установка Composer-а, если в вашем случае это не так, то вместо команды `composer` нужно писать `php composer.phar`

Клонируем проект:
```
git clone https://github.com/yupe/yupe.git <директория проекта>
```

Если не указывать целевую директорию, то файлы проекта будут размещены в папке `yupe`.
После завершения клонирования, переходим в директорию проекта и устанавливаем зависимости через Composer:
```
composer install
```

Преимущества установки через git:

* простота обновления до любой версии - простой командой вроде git pull вы сможете обновить "Юпи!" до самой последней версии.

Теперь можно переходить к установке Юпи!


## Установка из архива

Последнюю версию Юпи! всегда можно скачать со [страницы загрузки](https://yupe.ru/download).

Преимущества установки из архива:

* вы получаете самую последнюю версию
* все необходимые файлы уже собраны и готовы к установке
* вам не потребуется ничего кроме ftp-доступа к хостингу

!!! note "Обратите внимание!"
    Если при подготовке к установке у вас возникли какие-то проблемы - посетите [специальную ветку нашего форума](http://talk.yupe.ru/viewforum.php?f=10) или [напишите нам](https://yupe.ru/contacts).

Последовательность действий:

* [скачиваем](https://yupe.ru/download) дистрибутив
* загружаем на [хостинг](https://yupe.ru/service/hosting)
* подготавливаем хостинг
* запускаем мастер установки

В загруженном архиве вы обнаружите следующую структуру директорий:

```
.
├── protected
├── public // именно на этот каталог должен смотреть корень вашего сайта
├── read.me
└── tests
├── themes
└── vendor
```

!!! danger "Важно!"
    Обратите внимание, что корень сайта должен быть настроен на каталог public. Именно в public находится index.php, требуемый для работы сайта.



## Мастер установки Юпи!

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

![Установка: шаг 1](img/yupe-install-1.png)

**Шаг 2**: Проверка окружения

![Установка: шаг 2](img/yupe-install-2.png)

**Возможные проблемы**: неправильные права доступа к необходимым директориям.
Поменять права доступа можно командой в терминале `chmod 0777 <путь к директории>`, через ваш любимый FTP-менеджер (например FileZilla) или через файловый менеджер панели управления.

Расположение необходимых директорий в структуре проекта:

Значение | Путь
-------- | ------
Каталог Assets  | public/assets
Каталог Runtime | protected/runtime
Каталог загрузок | public/uploads
Каталог Modules | protected/config/modules
Каталог ModulesBack | protected/config/modulesBack
Файл db.php | protected/config

**Шаг 3**: Проверка системных требований

![Установка: шаг 3](img/yupe-install-3.png)

**Возможные проблемы**: не установлены необходимые расширения.

Виртуальные хостинги позволяют управлять расширениями PHP через панель управления.
Если такой возможности нет, то обратитесь к вашему системному администратору или в службу поддержки хостинга.
_Обратите внимание, что не все расширения обязательны._

**Шаг 4**: Соединение с базой данных

На этом этапе вы указываете данные для соединения с базой данных, которые вам предоставил хостер.

![Установка: шаг 4](img/yupe-install-4.png)

**Шаг 5**: Установка модулей

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

После загрузки страницы отмечены рекомендованные модули, а также доступны три варианта: “Интернет-магазин”, “Только основные”, “Все”.

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

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

![Установка: шаг 5](img/yupe-install-5.png)

**Шаг 6**: Создание учетной записи администратора

![Установка: шаг 6](img/yupe-install-6.png)

**Шаг 7**: Настройки проекта

![Установка: шаг 7](img/yupe-install-7.png)

**Шаг 8**: Окончание установки

Надеемся, что все прошло хорошо и вас можно поздравить с успешной установкой Юпи!

Если что-то пошло не так - посетите [специальную ветку нашего форума](http://talk.yupe.ru/viewforum.php?f=10) или [напишите нам](https://yupe.ru/contacts)

![Установка: шаг 8](img/yupe-install-8.png)

!!! important "Придем на помощь!"
    Наши специалисты могут помочь вам <a href='https://yupe.ru/store/services/install.html' target='_blank'>установить</a> платформу на выбранный хостинг
    или произвести полную <a href='https://yupe.ru/store/services/server.html' target='_blank'>настройку сервера</a> - пожалуйста, обращайтесь!


# Оптимальные настройки APC

Оптимальные настройки APC
=========================

В интернете можно найти множество вариантов настроек для APC, мы же хотим предложить вам те, которые используем сами. Это подборка лучших вариаций настроек из тех, что мы видели. Если у вас есть предложения, которые позволят добиться ещё большей производительности - будем рады, если вы поделитесь ими. И так, всё, что нам требуется, это применить данные настройки:
<pre><code class="bash">
; enable APC
apc.enabled=1    
; The number of shared memory segments
apc.shm_segments=1      
; The size of each shared memory segment
apc.shm_size=64    
; The number of seconds a cache entry is allowed to idle in a slot in case this
; cache entry slot is needed by another entry.
apc.ttl=7200

</code></pre>


**При возникновении проблем - [напишите нам](http://amylabs.ru/contact)!**

# Генерация Atom-ленты

Генерация Atom-ленты
====================

**Автор**: [Комманда разработчиков Юпи!](https://yupe.ru/contacts?from=docs)

**Версия**: 0.1 (dev)

**Авторское право**:  2009-2013 Yupe!

**Лицензия**: [BSD](https://github.com/yupe/yupe/blob/master/LICENSE)


Для того, чтобы встроить в свой сайт feed-ленту, вам потребуется
добавить к нужному контроллеру следующий код:

<pre><code class="php">
public function actions()
{
    return array(
        'rssfeed' => array(
            'class'        => 'yupe\components\actions\YFeedAction',
            'data'         => News::model()->published()->findAll(),
            // Параметр title по умолчанию берётся из настроек приложения
            //'title'        => Yii::t('YupeModule.yupe', 'Site title'),
            // Параметр description по умолчанию берётся из настроек приложения
            //'description'  => Yii::t('YupeModule.yupe', 'Лента новостей сайта'),
            // Параметр link по умолчанию берётся как Yii::app()->getRequest()->getBaseUrl(true)
            //'link' => Yii::app()->getRequest()->getBaseUrl(true),
            'itemFields'   => array(
                // author_object, если не задан - у
                // item-елемента запросится author_nickname
                'author_object'   => 'user',
                // 'author_nickname' => 'nick_name', 
                'author_nickname' => 'nick_name',
                'content'         => 'full_text',
                'datetime'        => 'creation_date',
                'link'            => '/news/news/view',
                'linkParams'      => array('title' => 'alias'),
                'title'           => 'title',
                'updated'         => 'change_date',
            ),
        ),
    );
}

</code></pre>

Как вы могли заметить, это **пример**.  
Давайте немного обсудим поля:

`'rssfeed' => array(` - id-екшена, который будет подключён к нашему контроллеру 

`'class' => 'application.modules.yupe.components.actions.FeedAction',` - класс-екшена, который мы подключаем

`'data' => News::model()->published()->findAll(),` - данные, которые мы будем отображать в ленте

`'title' => Yii::t('YupeModule.yupe', 'Site title'),` - заголовок для нашей ленты

`'description' => Yii::t('YupeModule.yupe', 'Лента новостей сайта'),` - описание ленты

`'link' => Yii::app()->getRequest()->getBaseUrl(true),` - анкор, например на сайт

`'itemFields' => array(` - здесь мы описываем стандартные поля, для создания ленты

`'author_object' => 'user'` - объект автора, если не задан - у item-елемента запросится author_nickname

`'author_nickname' => 'nick_name',` - параметр для получения автора

`'content' => 'full_text',` - контент для элемента ленты

`'datetime' => 'creation_date',` - время создания ленты

`'link' => '/news/news/view',` - линк на отображение

`'linkParams' => array('title' => 'alias'),` - парметры для формирования абсолютной ссылки

`'title' => 'title',` - заголовок для элемента ленты

`'updated' => 'change_date',` - дата последнего изменения элемента ленты

## Об остальном: ##

Вы также можете ознакомиться с [исходным кодом](https://github.com/yupe/yupe/blob/master/protected/modules/yupe/components/actions/FeedAction.php). 

# ConfigManager - компонент для управления конфигурационными файлами модулей

ConfigManager - компонент для управления конфигурационными файлами модулей
==========================================================================

Предисловие:
------------

Так как в Юпи! используется [объединение файлов конфигурации для модулей](https://yupe.ru/docs/yupe/userspace.config.html), при каждом запуске происходил поиск файлов,
их объединение и после чего уже сам запуск приложения. Это трудоёмкий процесс, который требует времени и ресурсов.
По данной причине мы написали специальный компонент, который единожды выполняет слитие файлов, кеширует их и в 
последующем использует полноценный массив настроек.

Запуск приложения первый раз:
-----------------------------

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

Сброс кеша настроек:
--------------------

Сброс кеша настроек производится либо из админ-панели, либо удалением кеш-файла настроек. В случае когда вам необходимо
организовать очистку кеша настроек из своего модуля/компонента - потребуется вызвать следующий метод:

`Yii::app()->configManager->flushDump();`

Описание методов:
-----------------

* **merge** - Инициализируем компонент, настраиваем пути и принемаем необходимыей параметры:
* **getSettings** - Получение настроек из кеш-файла или, запускаем обработчик на создание массива настроек приложения:
* **cachedSettings** - Получаем массив настроек из файла-дампа:
* **dumpSettings** - Сброс дампа настроек в файл
* **prepareSettings** - Готовим настройки приложения
* **mergeSettings** - Сливаем настройки, кешируем и отдаём приложению
* **flushDump** - Сброс кеш-файла настроек


**При возникновении проблем - [напишите нам](http://amylabs.ru/contact)!**

#  Проблемы CWebUser и модуля Gii 

# Проблемы CWebUser и модуля Gii #

Так как модуль `Gii` использует компонент `CWebUser`, идущий в комплекте с Yii и не позволяет в качестве "пользователя" указать другой компонент, могут возникать проблемы связанные с отсутствием методов. Дело в том, что в Юпи! имеется своя реализация компонента `CWebUser`, а именно - `YWebUser`. Данная проблема известна, но решения, без правки кода фреймворка - нет. Потому, если вы столкнулись с данной проблемой - просто закоментируйте (отключите) модуль `Gii`.

**При возникновении проблем - [напишите нам](http://amylabs.ru/contact)!**

#  Работа с eclipse 

# Работа с eclipse #

### Пробелы вместо табуляций
windows -> preferences -> general -> editors -> text editors -> ставим галочку на insert spaces for tabs

windows -> preferences -> PHP -> code style -> formatter -> выставляем tab policy = spaces, indentation size = 4

### Устанавливаем Aptana Studio 3 Plugin
help -> install new software... -> add -> location = http://download.aptana.com/studio3/plugin/install -> ok -> выбираем нужные компоненты и устанавливаем.

### Настройка форматирования в aptana studio
windows -> preferences -> aptana studio -> formatter -> import ... -> [выбираем файл](http://narod.ru/disk/48095872001.1641375368365b1634babfaed26109ac/Yii.xml.html) -> ok

Запуск форматирования CTRL+SHIFT+F

### Установка git (EGit и JGit)
help -> install new software... -> add -> location = http://download.eclipse.org/egit/updates-1.0 -> ok -> выбираем нужные компоненты и устанавливаем.

# Работа с событиями в Юпи!

Работа с событиями в Юпи!
=========================

Начиная с версии 0.7 в состав Юпи! входит компонент, предназначенный для работы с событиями - EventManager.
Реализация EventManager в Юпи! основана на компоненте EventDispatcher от Symfony.
Подробнее про этот компонент можно узнать из [официальной документации](http://symfony.com/doc/current/components/event_dispatcher/introduction.html).

Если модуль и его компоненты содержит события, достуные для "подписки" другими компонентами, такой модуль должен содержать PHP-класс, описывающий все события.
Рассмотрим это на примере модуля "Пользователи" ([user](https://github.com/yupe/yupe/tree/dev/protected/modules/user)).
Внутри модуля имеется каталог events, а в нем класс UserEvents, вот с таким содержанием:

<pre><code class="php">
class UserEvents
{
    const SUCCESS_ACTIVATE_ACCOUNT = 'user.success.activate';

    const FAILURE_ACTIVATE_ACCOUNT = 'user.failure.activate';

    const SUCCESS_EMAIL_CONFIRM = 'user.success.email.confirm';

    const FAILURE_EMAIL_CONFIRM = 'user.failure.email.confirm';

    const SUCCESS_LOGIN = 'user.success.login';

    const FAILURE_LOGIN = 'user.failure.login';

    const BEFORE_LOGIN = 'user.before.login';

    const BEFORE_LOGOUT = 'user.before.logout';

    const AFTER_LOGOUT = 'user.after.logout';

    const BEFORE_PASSWORD_RECOVERY = 'user.before.password.recovery';

    const SUCCESS_PASSWORD_RECOVERY = 'user.success.password.recovery';

    const FAILURE_PASSWORD_RECOVERY = 'user.failure.password.recovery';

    const SUCCESS_ACTIVATE_PASSWORD = 'user.success.activate.password';

    const FAILURE_ACTIVATE_PASSWORD = 'user.failure.activate.password';

    const SUCCESS_REGISTRATION = 'user.success.registration';

    const FAILURE_REGISTRATION = 'user.failure.registration';
}
</code></pre>

Каждая константа описывает одно событие. Название события ОБЯЗАТЕЛЬНО содержит в себе название модуля, к компоненту которого это событие относится.
Остальные слова и фразы внутри названия события разделяются символом "."

**Создание класса события**

**Вызов события**

Для вызова события из кода компонента необходимо обратиться в компоненту EventManager и вызвать его метод "fire".
<pre><code class="php">
Yii::app()->eventManager->fire(UserEvents::SUCCESS_REGISTRATION, new UserRegistrationEvent());
</code></pre>

**Назначение обработчиков**
<pre><code class="php">
<?php

return array(
    'module' => array(
        'class' => 'application.modules.rbac.RbacModule'
    ),
    'import' => array(
        'application.modules.rbac.listeners.AccessControlListener'
    ),
    'component' => array(
        'authManager' => array(
            'class' => 'CDbAuthManager',
            'connectionID' => 'db',
            'assignmentTable' => '{{user_user_auth_assignment}}',
            'itemChildTable' => '{{user_user_auth_item_child}}',
            'itemTable' => '{{user_user_auth_item}}',
        ),
        // override core ModuleManager
        'moduleManager' => array(
            'class' => 'application.modules.rbac.components.ModuleManager'
        ),
        //attach event handlers
        'eventManager' => array(
            'class' => 'yupe\components\EventManager',
            'events'=> array(
                // before backend controllers
                'yupe.before.backend.controller.action' => array(
                    array('AccessControlListener', 'onBeforeBackendControllerAction')
                )
            )
        )
    ),
    'rules' => array(
        '/backend/rbac/<controller:\w+>/<action:\w+>/<id:[\w._-]+>' => 'rbac/<controller>Backend/<action>',
    ),
);
</code></pre>

# [Блог рецептов и советов](https://yupe.ru/blogs/recepty-i-sovety)

[Блог рецептов и советов](https://yupe.ru/blogs/recepty-i-sovety)

Как организовать мультиязычное меню для ""Юпи!"?

[https://yupe.ru/post/kak-organizovat-multiyazychnoe-menyu-dlya-yupe.html](https://yupe.ru/post/kak-organizovat-multiyazychnoe-menyu-dlya-yupe.html)


Как изменить адрес панели управления?

[https://yupe.ru/post/izmenenie-adresa-paneli-upravlenija.html](https://yupe.ru/post/izmenenie-adresa-paneli-upravlenija.html)


Как установить "Юпи!" на OpenServer?

[https://yupe.ru/post/kak-ustanovit-yupi-na-openserver.html](https://yupe.ru/post/kak-ustanovit-yupi-na-openserver.html)


#  Конфигурация Memcahed 

# Конфигурация Memcahed #

## Настройка кеширования через "userspace" и файл конфигурации ##

По умолчанию Юпи! использует кеширование в файлы и стандартный Yii компонент CFileCache.

Для работы с memcached вам потребуется настроить переопределить настройки компонента кэша, для этого достаточно добавить файл
`yupe.php` в каталог `./protected/config/userspace/`


Пример конфиграции:

<pre><code class="php">
return array(
    'cache'              => array(
        'class'          => 'system.caching.CMemCache',
        'useMemcached'   => true,
        'servers'        => array(
            array('host' =>'127.0.0.1', 'port'=>11211),
            ...
        ),
        'behaviors'      => array(
            'clear'      => array(
                'class'  => 'application.modules.yupe.extensions.tagcache.TaggingCacheBehavior',
            ),
        ),
    ),
);
</code></pre>

Подробнее про использование userspace для конфигурирования проекта читайте [здесь](https://yupe.ru/docs/yupe/userspace.config.html).


**При возникновении проблем - [напишите нам](http://amylabs.ru/contact)!**

# Компонент "Мигратор"

Компонент "Мигратор"
====================

Для обновление базы данных в "Юпи" используются миграционные файлы, в которых описаны необходимые действия. Есть возможность "накатывания" и "отката" миграции, соответственно методы `safeUp` и `safeDown`. Миграционные файлы это почти теже самые миграции, что есть в [Yii](http://www.yiiframework.com/doc/guide/1.1/ru/database.migration), есть лишь небольшие правки, которые были внесены для корректной работы поверх поддерживаемых СУБД.

Отличия от Yii-миграций
-----------------------

Начнём пожалуй с того, что вместо `CDbMigration` в "Юпи" используется `yupe\components\DbMigration` который дополняет своим функционалом базовый класс, а именно:

* метод `getOptions` который возвращает опции создания таблицы для различных СУБД
* метод `normTable` для "нормализации" именни таблицы
* метод `dropTableWithForeignKeys` в котором планируется/реализовано удаление таблицы вместе с foreign-ключами

Как писать миграции для "Юпи"
---------------------------

Написание миграций для Юпи почти ничем не отличается от того, как это работает в [Yii](http://www.yiiframework.com/doc/guide/1.1/ru/database.migration), достаточно лишь использовать как базовый класс `yupe\components\DbMigration` и работать с методами, которые рекомендуются в нём, для совместимости со всеми поддерживаемыми СУБД. Имя файла и класса миграции формируется следующим образом - `m{yymmdd_time_name}`, например `130503_091124_BlogPostImage`. В примере используется миграция, котрорая создана 03-05-2013 в 09:11:24 с именем BlogPostImage.
Миграции необходимо располагать в каталоге `./protected/modules/<module>/install/migrations/`, это делается для того, чтобы миграции относились к определённым модулям.

Рассмотрим пример миграции для "Юпи"
------------------------------------

Ниже представлен листинг миграции для "Юпи" на примере модуля "Блог":

<pre><code class="php">
/**
 * Blog install migration
 * Класс миграций для модуля Blog:
 *
 * @category YupeMigration
 * @package  yupe
 * @author   YupeTeam <team@yupe.ru>
 * @license  BSD https://raw.github.com/yupe/yupe/master/LICENSE
 * @link     https://yupe.ru
 **/
class m000000_000000_blog_base extends yupe\components\DbMigration
{
    /**
     * Функция настройки и создания таблицы:
     *
     * @return null
     **/
    public function safeUp()
    {
        // blog
        $this->createTable(
            '{{blog_blog}}',
            array(
                'id' => "pk",
                'category_id' => "integer DEFAULT NULL",
                'name' => "varchar(250) NOT NULL",
                'description' => "text",
                'icon' => "varchar(250) NOT NULL DEFAULT ''",
                'slug' => "varchar(150) NOT NULL",
                'lang' => "char(2) DEFAULT NULL",
                'type' => "integer NOT NULL DEFAULT '1'",
                'status' => "integer NOT NULL DEFAULT '1'",
                'create_user_id' => "integer NOT NULL",
                'update_user_id' => "integer NOT NULL",
                'create_date' => "integer NOT NULL",
                'update_date' => "integer NOT NULL",
            ),
            $this->getOptions()
        );

        // ix
        $this->createIndex("ux_{{blog_blog}}_slug_lang", '{{blog_blog}}', "slug,lang", true);
        $this->createIndex("ix_{{blog_blog}}_create_user", '{{blog_blog}}', "create_user_id", false);
        $this->createIndex("ix_{{blog_blog}}_update_user", '{{blog_blog}}', "update_user_id", false);
        $this->createIndex("ix_{{blog_blog}}_status", '{{blog_blog}}', "status", false);
        $this->createIndex("ix_{{blog_blog}}_type", '{{blog_blog}}', "type", false);
        $this->createIndex("ix_{{blog_blog}}_create_date", '{{blog_blog}}', "create_date", false);
        $this->createIndex("ix_{{blog_blog}}_update_date", '{{blog_blog}}', "update_date", false);
        $this->createIndex("ix_{{blog_blog}}_lang", '{{blog_blog}}', "lang", false);
        $this->createIndex("ix_{{blog_blog}}_slug", '{{blog_blog}}', "slug", false);
        $this->createIndex("ix_{{blog_blog}}_category_id", '{{blog_blog}}', "category_id", false);

        // fk
        $this->addForeignKey("fk_{{blog_blog}}_create_user", '{{blog_blog}}', 'create_user_id', '{{user_user}}', 'id', 'CASCADE', 'NO ACTION');
        $this->addForeignKey("fk_{{blog_blog}}_update_user", '{{blog_blog}}', 'update_user_id', '{{user_user}}', 'id', 'NO ACTION', 'NO ACTION');
        $this->addForeignKey("fk_{{blog_blog}}_category_id", '{{blog_blog}}', 'category_id', '{{category_category}}', 'id', 'SET NULL', 'NO ACTION');


        // post
        $this->createTable(
            '{{blog_post}}',
            array(
                'id' => "pk",
                'blog_id' => "integer NOT NULL",
                'create_user_id' => "integer NOT NULL",
                'update_user_id' => "integer NOT NULL",
                'create_date' => "integer NOT NULL",
                'update_date' => "integer NOT NULL",
                'publish_date' => "integer NOT NULL",
                'slug' => "varchar(150) NOT NULL",
                'lang' => "char(2) DEFAULT NULL",
                'title' => "varchar(250) NOT NULL",
                'quote' => "varchar(250) NOT NULL DEFAULT ''",
                'content' => "text NOT NULL",
                'link' => "varchar(250) NOT NULL DEFAULT ''",
                'status' => "integer NOT NULL DEFAULT '0'",
                'comment_status' => "integer NOT NULL DEFAULT '1'",
                'create_user_ip' => "varchar(20) NOT NULL",
                'access_type' => "integer NOT NULL DEFAULT '1'",
                'keywords' => "varchar(250) NOT NULL DEFAULT ''",
                'description' => "varchar(250) NOT NULL DEFAULT ''",
            ),
            $this->getOptions()
        );

        //ix
        $this->createIndex("ux_{{blog_post}}_lang_slug", '{{blog_post}}', "slug,lang", true);
        $this->createIndex("ix_{{blog_post}}_blog_id", '{{blog_post}}', "blog_id", false);
        $this->createIndex("ix_{{blog_post}}_create_user_id", '{{blog_post}}', "create_user_id", false);
        $this->createIndex("ix_{{blog_post}}_update_user_id", '{{blog_post}}', "update_user_id", false);
        $this->createIndex("ix_{{blog_post}}_status", '{{blog_post}}', "status", false);
        $this->createIndex("ix_{{blog_post}}_access_type", '{{blog_post}}', "access_type", false);
        $this->createIndex("ix_{{blog_post}}_comment_status", '{{blog_post}}', "comment_status", false);
        $this->createIndex("ix_{{blog_post}}_lang", '{{blog_post}}', "lang", false);
        $this->createIndex("ix_{{blog_post}}_slug", '{{blog_post}}', "slug", false);
        $this->createIndex("ix_{{blog_post}}_publish_date", '{{blog_post}}', "publish_date", false);

        //fks 
        $this->addForeignKey("fk_{{blog_post}}_blog", '{{blog_post}}', 'blog_id', '{{blog_blog}}', 'id', 'CASCADE', 'NO ACTION');
        $this->addForeignKey("fk_{{blog_post}}_create_user", '{{blog_post}}', 'create_user_id', '{{user_user}}', 'id', 'CASCADE', 'NO ACTION');
        $this->addForeignKey("fk_{{blog_post}}_update_user", '{{blog_post}}', 'update_user_id', '{{user_user}}', 'id', 'NO ACTION', 'NO ACTION');

        // user to blog
        $this->createTable(
            '{{blog_user_to_blog}}',
            array(
                'id' => "pk",
                'user_id' => "integer NOT NULL",
                'blog_id' => "integer NOT NULL",
                'create_date' => "integer NOT NULL",
                'update_date' => "integer NOT NULL",
                'role' => "integer NOT NULL DEFAULT '1'",
                'status' => "integer NOT NULL DEFAULT '1'",
                'note' => "varchar(250) NOT NULL DEFAULT ''",
            ),
            $this->getOptions()
        );

        //ix
        $this->createIndex(
            "ux_{{blog_user_to_blog}}_blog_user_to_blog_u_b",
            '{{blog_user_to_blog}}',
            "user_id,blog_id",
            true
        );
        $this->createIndex(
            "ix_{{blog_user_to_blog}}_blog_user_to_blog_user_id",
            '{{blog_user_to_blog}}',
            "user_id",
            false
        );
        $this->createIndex(
            "ix_{{blog_user_to_blog}}_blog_user_to_blog_id",
            '{{blog_user_to_blog}}',
            "blog_id",
            false
        );
        $this->createIndex(
            "ix_{{blog_user_to_blog}}_blog_user_to_blog_status",
            '{{blog_user_to_blog}}',
            "status",
            false
        );
        $this->createIndex("ix_{{blog_user_to_blog}}_blog_user_to_blog_role", '{{blog_user_to_blog}}', "role", false);

        //fk
        $this->addForeignKey("fk_{{blog_user_to_blog}}_blog_user_to_blog_user_id", '{{blog_user_to_blog}}', 'user_id', '{{user_user}}', 'id', 'CASCADE', 'NO ACTION');
        $this->addForeignKey("fk_{{blog_user_to_blog}}_blog_user_to_blog_blog_id", '{{blog_user_to_blog}}', 'blog_id', '{{blog_blog}}', 'id', 'CASCADE', 'NO ACTION');

        // tags
        $this->createTable(
            '{{blog_tag}}',
            array(
                'id' => 'pk',
                'name' => 'varchar(255) NOT NULL',
            ),
            $this->getOptions()
        );

        //ix
        $this->createIndex("ux_{{blog_tag}}_tag_name", '{{blog_tag}}', "name", true);

        // post to tag
        $this->createTable(
            '{{blog_post_to_tag}}',
            array(
                'post_id' => 'integer NOT NULL',
                'tag_id' => 'integer NOT NULL',
                'PRIMARY KEY (post_id, tag_id)'
            ),
            $this->getOptions()
        );

        //ix  
        $this->createIndex("ix_{{blog_post_to_tag}}_post_id", '{{blog_post_to_tag}}', "post_id", false);
        $this->createIndex("ix_{{blog_post_to_tag}}_tag_id", '{{blog_post_to_tag}}', "tag_id", false);

        //fk
        $this->addForeignKey("fk_{{blog_post_to_tag}}_post_id", '{{blog_post_to_tag}}', 'post_id', '{{blog_post}}', 'id', 'CASCADE', 'NO ACTION');
        $this->addForeignKey("fk_{{blog_post_to_tag}}_tag_id", '{{blog_post_to_tag}}', 'tag_id', '{{blog_tag}}', 'id', 'CASCADE', 'NO ACTION');
    }

    /**
     * Удаляем таблицы
     *
     * @return null
     **/
    public function safeDown()
    {
        $this->dropTableWithForeignKeys('{{blog_post_to_tag}}');
        $this->dropTableWithForeignKeys('{{blog_tag}}');
        $this->dropTableWithForeignKeys('{{blog_post}}');
        $this->dropTableWithForeignKeys('{{blog_user_to_blog}}');
        $this->dropTableWithForeignKeys('{{blog_blog}}');
    }
}
</code></pre>


Компонент "Мигратор" - предназначен для использования миграций из веб части. То есть это по сути тот же компонент, который используется в Yii, но переписан с учётом специфики работы в фронт-части.

Компонент доступен из namespace'a `yupe\components\Migrator` и для своей работы использует следующие компоненты из глобального namespace'a:

* Yii;
* CDbCacheDependency;
* ErrorException;
* CException;
* CDbConnection;
* TagsCache;
* CHtml;

Методы
------

### Метод init - Инициализируем класс

[листинг](https://github.com/yupe/yupe/blob/master/protected/modules/yupe/components/Migrator.php#L38)

В данном методе перед инициализацией компонента проверяется наличие таблицы истории миграций, если же её нет - вызывается метод `createMigrationHistoryTable()`

### Метод updateToLatest - Обновление модуля до актуальной миграции

[листинг](https://github.com/yupe/yupe/blob/master/protected/modules/yupe/components/Migrator.php#L55)

В данном методе мы получаем список ещё не установленных миграций и постепенно "накатываем" их вызывая метод `migrateUp`. Если же список новых миграций пуст - сообщаем об этом в лог.

<pre>
@param string $module - id-модуля
@return bool true
</pre>

### Метод checkForBadMigration - Проверяем на незавершённые миграции:

[листинг](https://github.com/yupe/yupe/blob/master/protected/modules/yupe/components/Migrator.php#L55)

Метод проверяет список миграций на незавершённые ("плохие") миграции и возвращаем результат, есть таковые миграции у данного модуля или нет.

<pre>
@param string $module - id-модуля
@param string $class  - класс-миграции

@return bool обновились ли до миграции
</pre>

### Метод migrateUp - Обновляем миграцию:

[листинг](https://github.com/yupe/yupe/blob/master/protected/modules/yupe/components/Migrator.php#L210)

Метод "накатывания" миграции до нового состяния с записью результата в журнал миграций.

<pre>
@param string $module - required module
@param string $class  - name of migration class

@return bool is updated to migration
</pre>

### Метод migrateDown - Даунгрейд миграции:

[листинг](https://github.com/yupe/yupe/blob/master/protected/modules/yupe/components/Migrator.php#L282)

Метод "откатывания" миграции до старого состяния с удалением записи о данной миграции из журнала миграций.

<pre>
@param string $module - required module
@param string $class  - name of migration class

@return bool is downgraded to migration
</pre>

### Метод instantiateMigration - инициализация класса миграции:

[листинг](https://github.com/yupe/yupe/blob/master/protected/modules/yupe/components/Migrator.php#L345)

Инициализируем класс миграции для последующей работы с ним.

<pre>
@param string $module - required module
@param string $class  - class of migration

@return mixed version and apply time
</pre>

### Метод getDbConnection - получаем коннектор к БД

[листинг](https://github.com/yupe/yupe/blob/master/protected/modules/yupe/components/Migrator.php#L359)

<pre>@return db connection or make exception</pre>

### Метод getMigrationHistory - получаем историю миграций

[листинг](https://github.com/yupe/yupe/blob/master/protected/modules/yupe/components/Migrator.php#L382)

<pre>
@param string  $module - required module
@param integer $limit  - limit of array

@return mixed version and apply time
</pre>

### Метод createMigrationHistoryTable - создаём таблицу миграций

[листинг](https://github.com/yupe/yupe/blob/master/protected/modules/yupe/components/Migrator.php#L418)

<pre>@return nothing</pre>

### Метод getNewMigrations - проверка на наличие новых миграций для модуля

[листинг](https://github.com/yupe/yupe/blob/master/protected/modules/yupe/components/Migrator.php#L455)

<pre>
@param string $module - required module

@return mixed new migrations
</pre>

### Метод checkForUpdates - получение списка обновлений для выбранных модулей

[листинг](https://github.com/yupe/yupe/blob/master/protected/modules/yupe/components/Migrator.php#L496)

<pre>
@param array $modules - list of modules

@return mixed new migrations
</pre>

### Метод getModulesWithDBInstalled - получаем список модулей с установленными миграциями

[листинг](https://github.com/yupe/yupe/blob/master/protected/modules/yupe/components/Migrator.php#L514)

<pre>
@return mixed db-installed
</pre>




**При возникновении проблем - [напишите нам](http://amylabs.ru/contact)!**


#  МойСклад

# МойСклад

[Модуль](https://yupe.ru/store/modules/crm/moy-sklad.html) создан для интеграции с сервисом "[МойСклад](https://www.moysklad.ru/)", но также позволяет обмениваться данными с программами 1С из-за того, что синхронизация осуществляется посредством стандарта [CommerceML](http://v8.1c.ru/edi/edi_stnd/90/92.htm).

При помощи этого модуля вы можете синхронизировать:

* категории товаров;
* товарные позиции (артикул, описание) с привязкой к категории;
* остатки на складе (количество и стоимость);
* заказы.

<h2>Установка</h2>

!!! important "Внимание!"
    При отсутствии должного технического опыта все перечисленные ниже процедуры лучше [доверить профессионалам](https://yupe.ru/service/support).
    
После [приобретения модуля](https://yupe.ru/store/modules/crm/moy-sklad.html) вам необходимо распаковать архив и поместить его содержимое в папку `protected/modules` сайта, который будет подключаться к сервису "МойСклад".

Затем в панели управления сайтом (Юпи! - Мои модули) устанавливаем его.

![Установка модуля cml](img/module-cml-1.png)

При условии успешной установки в меню раздела `Магазин` должен появиться пункт `CommerceML`

![Модуль CommerceML в меню](img/module-cml-2.png)

<h2>Настройка</h2>

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

![Настройка данных пользователя](img/module-cml-3.png)

_Здесь указываются данные придуманного вами пользователя._

__Обратите внимание__, что нет никакой необходимости указывать данные администратора или любого другого пользователя зарегистрированного на сайте.

Также мы __настоятельно рекомендуем__ воздержаться от использования простого пароля и секретного ключа. Воспользуйтесь [специальными сервисами](https://lastpass.com/generatepassword.php) для их генерации.

__Секретный ключ__ указан для примера и его нужно __обязательно заменить__.

Оставшиеся поля пока не трогаем, сохраняем изменения и переходим к настройкам в сервисе "МойСклад". 

Для этого необходимо перейти в раздел `Синхронизация`, где нужно кликнуть на кнопку `Добавить магазин` и в выпадающем списке выбрать пункт `Обмен по CommerceML`.

![МойСклад синхронизация](img/module-cml-4.png)

!!! important "Владельцам продуктов 1С"
    Достаточно подробную инструкцию по настройке `1С Предприятие` вы можете найти [перейдя по ссылке](http://www.cs-cart.ru/docs/4.1.x/rus_build_pack/1c/instruction/index.html#id3). Следовать инструкции нужно до раздела "Настройки в интернет-магазине".

На открывшейся странице указываем адрес магазина, который формируется следующим образом: `http://ваш-сайт/cml/sync` (например `https://yupe.ru/cml/sync`), и указываем данные пользователя, придуманные ранее. 

После этого вы можете нажать на кнопку `Проверить соединение`, чтобы убедиться в правильности указанных данных. В этом случае появится сообщение "Соединение прошло успешно".

![МойСклад. Настройка](img/module-cml-5.png)

Далее в разделах `Заказы` и `Остатки` необходимо выбрать, который из них вы будете выгружать, и указать временные интервалы, через которые будет осуществляться выгрузка. 

При необходимости у заказов можно поставить отметку "Отправлять статусы заказов в ИМ", т.е. при изменении статуса заказа в МоемСкладе он будет выгружен на сайт.

!!! note "Обратите внимание"
    МойСклад не выгружает в CommerceML формате полные данные заказа. На сайт отправляются только статус заказа, в случае его изменения, и отметка об удалении.

Теперь можно перейти к оставшимся настройкам модуля CommerceML в панели управления интернет-магазином. Мы опишем лишь те, которые могут вызвать хоть какие-то вопросы:

<h5>Что делать с категориями, отсутствующими в файле импорта</h5> 
В каталоге вашего магазина уже могут быть категории, которых не будет в файле выгрузки. На этот случай системе нужно знать, что с ними делать. 

Доступны 3 варианта: Ничего, __Деактивировать__ и Удалить. 

По умолчанию выбран пункт Деактивировать - это значит, что категории будет присвоен статус `Черновик`. Она не будет отображаться на сайте, но вы сможете увидеть ее в панели управления.

<h5>Что делать с товарами, отсутствующими в файле импорта</h5> 
Все то же самое, как и у категорий, только у товара будет установлен статус `Недоступен`, если выбран пункт Деактивировать.

<h5>Статус "Нет в наличии" если количество = 0</h5>
Если в остатках на складе количество товара равно 0, то товару автоматически будет присвоен соответствующий статус. Данная опция будет работать только при включенной `Обновлять количество товара`.

<h5>"Ид" цены продукта, "Ид" скидочной цены продукта</h5>
Системе необходимо определять, которую из цен в файле выгрузки ставить в соответствующее поле товара.

!!! important "Внимание"
    Без указания внешнего кода цены на сайте обновляться не будут!

Чтобы узнать эти коды, вам нужно перейти в раздел `Справочники - Товары и услуги` в МоемСкладе, где кликнуть на кнопке создания нового товара или редактировании уже существующего.

На открывшейся странице, в блоке Цены, нажать на ссылку `Внешние коды цен`.

![МойСклад. Внешние коды цен](img/module-cml-6.png)

![МойСклад. Внешние коды цен](img/module-cml-7.png)

_Количество и названия цен могут отличаться от указанных на скриншоте_

__Не забудьте__ указать выбранные цены в настройках синхронизации сервиса МойСклад, чтобы они попали в файл выгрузки.

На этом все.

!!! note ""
    Сообщения о количестве выгруженных товаров, загруженных заказов или ошибках, будут доступны на вкладке `Отчет` в настроенной вами синхронизации в МоемСкладе. _См. скриншот выше_

# AmoCRM

<h2>Установка</h2>

Скопировать модуль в `protected/modules`.

Установить зависимости через composer.

```bash
$ composer require dotzero/yii-amocrm
```

<h2>Настройка</h2>
* Зайти в настройки AmoCRM и скопировать ключ API
* Перейти в "Сервисы" ---> AmoCrm и указать ключ, поддомен, email пользователя AmoCRM, нажать "Сохранить"
* Выбрать из списка поля email и телефон
* Поставить в крон команду  `php /<путь к сайту>/protected/yiic amocrm-export` 

# Социализация (social)

<h2>Настройка</h2>
В настоящее время отсутствует возможность подключения ключей социальных сетей посредством веб-интерфейса административной панели. Для обеспечения входа через социальные сети необходимо скопировать файл `./protected/config/social.php` в папку `/protected/userspace/social.php` и внести в него изменения.

<h3> Секция Google: </h3>
Необходимо указать 'сlient_id' и 'client_secret' - это идентификаторы OAuth2.0. Для их получения необходимо зайти в аккаунт Google, пройти по адресу `https://console.developers.google.com/`. Далее активировать Google Cloud API и создать на вкладке "Учетные данные" учетную запись OAuth2.0. Также необходимо указать адрес response_URI, по умолчанию это `http(https)://yourdomain.com/social/login/service/google`


#  Создание собственного модуля

# Создание собственного модуля

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

!!! danger "Важно!"
    Если вы не знакомы с фреймворком [Yii](http://www.yiiframework.com/doc/guide/1.1/ru/quickstart.what-is-yii), то для более легкого понимания материала, рекомендую вам ознакомиться с шаблоном проектирования [MVC](http://www.yiiframework.com/doc/guide/1.1/ru/basics.mvc), [модулями](http://www.yiiframework.com/doc/guide/1.1/ru/basics.module) фреймворка, а еще лучше - изучить учебное пособие по [созданию блога](http://www.yiiframework.com/doc/blog/1.1/ru).

## Описание модуля
Поскольку нашей задачей является обучение, а не изобретение чего-то нового и интересного, то мы будем создавать простенький модуль задач "ToDo". Думаю, для наших целей этого будет достаточно.
Дабы не усложнять процесс, сделаем допущение, что в системе может быть зарегистрирован только один пользователь. Это позволит нам не разделять списки задач. В качестве домашнего задания вы можете реализовать эту возможность самостоятельно.

Все этапы разработки будут оформлены в отдельные коммиты, которые вы сможете посмотреть на [GitHub](https://github.com/sabian/yupe-todo).
Возникшие вопросы можно задать на нашем [форуме](https://talk.yupe.ru).

##### Список требований:
- должна быть возможность создавать, редактировать и удалять задачи;
- реализовать поиск по задачам и фильтрацию по статусу;
- inline-редактирование статусов задач;
- изменение приоритета задач перетаскиванием мышью (drag&drop);
- на главной странице панели управления отображать уведомления о количестве невыполненных задач.

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

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

!!! note "Обратите внимание!"
    Для большего понимания материала, мы не будем использовать генератор кода (Gii), а создадим каждый файл и директорию вручную. Конечно, в будущем вы можете использовать его для упрощения работы. Прочитать как это сделать, можно [здесь](http://www.yiiframework.com/doc/guide/1.1/ru/topics.gii).

Дальнейшие действия подразумевают, что у вас установлена последняя стабильная версия Юпи! и открыт любимый редактор кода с добавленными в проект файлами системы.

В `protected/modules` создаем директорию `todo` для нашего модуля. Все дальнейшие действия будут проводиться внутри этой категории. 

Создаем основной файл модуля `TodoModule.php` с одноименным классом, расширяющим компонент `WebModule` модуля `yupe`.

```php
use yupe\components\WebModule;

class TodoModule extends WebModule 
{
    
}
```

И добавляем в него методы:

**getDependencies** - возвращает массив модулей, от которых будет зависеть наш модуль. Допустим, если бы мы хотели добавить комментарии к нашим задачам и возможность добавления изображений, то нужно было бы написать так:
```php
return ['image', 'comment'];
```
Наш модуль вполне самостоятелен, поэтому мы вернем пустой массив.

**getVersion** - возвращает версию вашего модуля для отображения в панели управления.

**getCategory** - сообщает системе в какой вкладке меню, панели управления, отображать меню нашего модуля. Т.е., если мы хотим разместить его в меню "Контент", то так и напишем:
```php
return 'Контент';
```

**getName** - возвращает название модуля.

**getDescription** - краткое описание модуля.

**getAuthor** - имя автора или компании, создавшей модуль.

**getAuthorEmail** - email-адрес.

**getUrl** - ссылка на сайт автора или на демонстрационный сайт.

**getIcon** - иконка модуля. Указывается в формате `fa fa-fw <название>`. Выбрать подходящую иконку и узнать ее название можно на сайте [FontAwesome](https://fortawesome.github.io/Font-Awesome/icons/). В этом модуле используем иконку [thumb-tack](http://fortawesome.github.io/Font-Awesome/icon/thumb-tack/), значит метод должен вернуть строку `fa fa-fw fa-thumb-tack`.

**getAdminPageLink** - здесь указывается ссылка на какую-либо страницу (чаще главную) модуля. Она нужна для отображения кнопки модуля на главной странице панели управления. Строится она по следующему правилу: `/<название модуля>/<название контроллера>/<действие>`. Для модуля она примет такой вид: `/todo/todoBackend/index`.

**getNavigation** - сообщает системе о структуре меню модуля. Он крайне простой, поэтому сделаем только два пункта: просмотр списка задач и создание задачи.
```php
return [
    [
        'icon'  => 'fa fa-fw fa-list-alt',
        'label' => 'Список задач',
        'url'   => ['/todo/todoBackend/index']
    ],
    [
        'icon'  => 'fa fa-fw fa-plus-square',
        'label' => 'Создать задачу',
        'url'   => ['/todo/todoBackend/create']
    ],
];
```
Эти данные будут использованы для генерации главного меню панели управления.

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

Создаем директорию `install`, в которой размещаем файл с точно таким же названием, как и модуль - `todo.php`. В нем будет храниться конфигурация модуля, где, для начала, мы просто сообщим системе о его существовании:
```php
return [
    'module'    => [
        'class' => 'application.modules.todo.TodoModule',
    ],
];
```
Строчка `application.modules.todo.TodoModule` - это путь до главного класса модуля, где `application` указывает на директорию `protected`. 

Вот и все! Если все сделано правильно, то в разделе "Модули", во вкладке "Не установлен!", мы должны увидеть наш модуль.
![Модуль ToDo](img/yupe-module-1.png)

После установки модуля вы должны увидеть пункт "ToDo" в главном меню и кнопку на главной странице панели управления.
![Установленный модуль ToDo](img/yupe-module-2.png)

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

Детально изучить созданные файлы вы можете в [этом коммите на GitHub](https://github.com/sabian/yupe-todo/commit/4ee5d8b316279d2c27c5a536b7fd4033be94db01). А если возникли вопросы, то смело задавайте их на нашем [форуме](https://talk.yupe.ru). Будем рады помочь!

## Миграции
Теперь пришло время задуматься над структурой таблицы модуля и создании миграции. О том, что такое миграции, для чего они нужны и как с ними работать, вы можете прочитать в [официальном руководстве](http://www.yiiframework.com/doc/guide/1.1/ru/database.migration).

В модулях Юпи! файлы миграций хранятся в директории `install/migrations`. Перед тем как продолжать, убедитесь, что вы ее создали.

Далее, в консоли, находясь в корневой папке вашего проекта, выполняем команду `php protected/yiic migrate create todo yupe_todo_table`. Этой командой мы создаем файл миграции `yupe_todo_table` для модуля `todo`. 

Если все прошло хорошо, то на данный момент у вас должна быть следующая структура директорий:

```
.
├── install
│   ├── migrations
│   │   └── m151205_141508_yupe_todo_table.php
│   └── todo.php
├── LICENSE
├── README.md
└── TodoModule.php
```

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

```php
class m151205_141508_yupe_todo_table extends yupe\components\DbMigration
{
    public function safeUp()
    {
        $this->createTable('{{todo}}', [
            'id' => 'pk',
            'description' => 'string NOT NULL',
            'status' => 'TINYINT(1) NOT NULL DEFAULT 1',
            'sort' => 'integer NOT NULL DEFAULT 1',
        ], $this->getOptions());
    }

    public function safeDown()
    {
        $this->dropTable('{{todo}}');
    }
}
```

Обратите внимание, что название таблицы заключено в двойные фигурные скобки `{{todo}}`. Это сделано для того, чтобы система автоматически подставила префикс для таблиц, который задается на этапе установки. Если вы ничего не меняли, то после применения миграции в базе данных должна появиться таблица `yupe_todo`.

Метод `getOptions` добавляет параметры таблицы: `ENGINE=InnoDB DEFAULT CHARSET=utf8`

Осталось только ввести команду `php protected/yiic migrate --module=todo` и подтвердить что хотите применить миграцию.

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

```
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(11)      | NO   | PRI | NULL    | auto_increment |
| description | varchar(255) | NO   |     | NULL    |                |
| status      | tinyint(1)   | NO   |     | 1       |                |
| sort        | int(11)      | NO   |     | 1       |                |
+-------------+--------------+------+-----+---------+----------------+
```

Результат на [GitHub](https://github.com/sabian/yupe-todo/commit/7feff834d603f30d86debccd151738145243455e)

## Модель
Учитывая, что официальное руководство по Yii фреймворку предоставляет исчерпывающую информацию о [моделях](http://www.yiiframework.com/doc/guide/1.1/ru/basics.model), их [создании](http://www.yiiframework.com/doc/guide/1.1/ru/form.model) и [работе с базой данных](http://www.yiiframework.com/doc/guide/1.1/ru/database.overview), лучше сразу перейти к делу.

В Юпи! есть базовый класс `YModel`, от которого должны быть унаследованы все модели. Исходя из этого, определяем класс:
```php
use yupe\models\YModel;

class Todo extends YModel
{
    
}
```

На этом этапе понадобится переопределить только три метода:
- **tableName** - возвращает название таблицы в базе данных. Здесь все просто, это будет строка `{{todo}}`;
- **rules** - правила валидации данных;
- **attributeLabels** - возвращает список меток аттрибутов в формате `'Атрибут' => 'Описание'`.

Все остальное будем дописывать при первой необходимости.

Посмотреть на получившийся код вы можете в соответствующем коммите на GitHub, ссылка на который указана в конце этого раздела.
А пока опишем требования для правил валидации:
- поле `description` должно быть обязательным и иметь длину не более 255 символов;
- поля `status` и `sort` должны быть целочисленными.

```php
['description', 'required'],
['description', 'length', 'max' => 255],
['status, sort', 'numerical', 'integerOnly' => true],
```

Пока это все, что мы можем написать в модели. В дальнейшем придется добавить несколько правил валидации и методов, а пока можно изучить [полученный результат](https://github.com/sabian/yupe-todo/commit/6330d8a7ec06a8dd86976b10514cce34df571ce0).

Если у вас возникли вопросы по предыдущим шагам руководства, то можете смело задавать их на [форуме](https://talk.yupe.ru). Возможно, это поможет нам дополнить руководство и сделать его более понятным.

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

В методе `getNavigation` (файл TodoModule.php) ссылки на пункты меню имеют следующий вид: `['/todo/todoBackend/index']`. Как уже говорилось ранее, они создаются по принципу `/название модуля/название контроллера/действие`. Из этого следует, что контроллер должен называться `TodoBackendController` и располагаться в директории `controllers`. 

К этому моменту структура директорий модуля должна иметь вид:

```
.
├── controllers
│   └── TodoBackendController.php
├── install
│   ├── migrations
│   │   └── m151205_141508_yupe_todo_table.php
│   └── todo.php
├── LICENSE
├── models
│   └── Todo.php
├── README.md
└── TodoModule.php
```

Обратите внимание, что контроллер должен наследовать `BackController`. Также создаем действие `index` в котором задаем отображение одноименного представления.

```php
use yupe\components\controllers\BackController;

class TodoBackendController extends BackController
{
    public function actionIndex()
    {
        $this->render('index');
    }
}
```

Файлы представлений должны находиться в директории `views`, внутри которой они сортируются по директориям, относящимся к конкретному контроллеру. В данном случае это `todoBackend`. Создаем файл и оставляем его пока пустым.

Для тех, кто не уверен, что правильно все понял: размещаем структуру категорий, которая должна быть после создания файла представления.

```
.
├── controllers
│   └── TodoBackendController.php
├── install
│   ├── migrations
│   │   └── m151205_141508_yupe_todo_table.php
│   └── todo.php
├── LICENSE
├── models
│   └── Todo.php
├── README.md
├── TodoModule.php
└── views
    └── todoBackend
        └── index.php
```

Если все сделано правильно, то при клике на пункте меню `Контент - ToDo` в панели управления не должно появляться ошибки, как это было ранее. Вы должны увидеть главное меню панели управления, блок с информацией о модуле и пустую контентную область.

## Отображение списка задач и фильтрация
Отвечать за отображение списка задач будет виджет [CustomGridView](https://github.com/yupe/yupe/blob/master/protected/modules/yupe/widgets/CustomGridView.php), которому нужен источник данных. Для этого будет использоваться [CActiveDataProvider](http://www.yiiframework.com/doc/api/1.1/CActiveDataProvider).

Теперь в модели (файл `models/Todo.php`) необходимо создать метод `search`, который и будет служить источником данных для отображения и фильтрации задач.

```php
public function search()
{
    $criteria = new CDbCriteria;

    $criteria->compare('description', $this->description, true);
    $criteria->compare('status', $this->status);

    return new CActiveDataProvider(get_class($this), [
        'criteria' => $criteria,
        'sort' => [
            'defaultOrder' => 'sort'
        ]
    ]);
}
```

Приведенный выше код не должен вызывать трудностей. Стоит только уделить немного внимания этим двум строкам:

```php
$criteria->compare('description', $this->description, true);
$criteria->compare('status', $this->status);
```

Здесь описываются условия для фильтрации данных, из которых можно понять, что поиск будет осуществляться только по двум полям: `description` и `status`. При этом для поля `description` третьим параметром передается `true`, что сообщает системе о возможности частичного совпадения фразы.

Для правильной работы поиска, в правилах валидации (метод `rules`) необходимо объявить эти поля безопасными для сценария `search`:

```php
['description, sort', 'safe', 'on' => 'search']
```

Теперь нужно изменить контроллер:

```php
public function actionIndex()
{
    // В модели включаем сценарий search
    $model = new Todo('search');

    // Получаем данные из запроса. 
    // Если в фильтре ничего не меняли, то в переменной будет null
    $query = Yii::app()->getRequest()->getQuery('Todo');

    $model->unsetAttributes();

    if ($query) {
        // Присваиваем атрибутам модели значения из запроса
        $model->setAttributes($query);
    }
    
    // Передаем модель в представление
    $this->render('index', ['model' => $model]);
}
```

А в представлении добавить виджет:

```php
$this->widget('yupe\widgets\CustomGridView', [
        'id' => 'todo-grid',
        'type' => 'condensed',
        'dataProvider' => $model->search(),
        'filter' => $model,
        'columns' => [
            'id',
            'description',
            'status',
            [
                'class' => 'yupe\widgets\CustomButtonColumn',
                'template' => '{update} {delete}'
            ],
        ],
    ]
);
```

Теперь в панели управления вы должны увидеть следующую картину:

![Модуль ToDo - Список](img/yupe-module-3.png)

Обращаем ваше внимание на то, что не весь код размещается в этом описании, а только самые важные его части. Более подробно изучить код этой части можно на [GitHub](https://github.com/sabian/yupe-todo/commit/f4d2019ff395c625857935a615bb2d2c1815926d).

## Добавление/изменение/удаление задач
Начнем работу с реализации возможности добавления данных.

Добавляем в `TodoBackendController` действие `actionCreate`:

```php
public function actionCreate()
{
    $model = new Todo();

    if ($data = Yii::app()->getRequest()->getPost('Todo')) {
        // Если форма была отправлена, то присваиваем атрибуты
        $model->setAttributes($data);

        if ($model->save()) {
            // Показываем пользователю сообщение, если данные успешно сохранены
            Yii::app()->user->setFlash(
                yupe\widgets\YFlashMessages::SUCCESS_MESSAGE,
                'Задача успешно добавлена'
            );

            $this->redirect(
                (array)Yii::app()->getRequest()->getPost(
                    'submit-type',
                    ['create']
                )
            );
        }
    }

    $this->render('create', ['model' => $model]);
}
```

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

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

```php
$this->renderPartial('_form', ['model' => $model]);
```

Для удобства навигации по разделам модуля добавим меню в правый сайдбар:
```php
$this->menu = [
    ['icon' => 'fa fa-fw fa-list-alt', 'label' => 'Список задач', 'url' => ['/todo/todoBackend/index']],
    ['icon' => 'fa fa-fw fa-plus-square', 'label' => 'Создать задачу', 'url' => ['/todo/todoBackend/create']],
];
```

Отображение формы будет осуществляться виджетом [ActiveForm](https://github.com/yupe/yupe/blob/master/protected/modules/yupe/widgets/ActiveForm.php). Валидация данных будет проводиться на клиентской стороне:
```php
'enableClientValidation' => true,
```

Работа со статусами будет добавлена позднее, поэтому сейчас форма содержит только поле "Описание" 
```php
<?= $form->textFieldGroup($model, 'description'); ?>
```

После добавления нужных файлов у вас должна быть следующая структура директории `view`

```
└── views
    └── todoBackend
        ├── create.php
        ├── _form.php
        └── index.php
```

Весь добавленный код вы можете увидеть в [этом коммите](https://github.com/sabian/yupe-todo/commit/189ca50a797a782f087f6cc704453d9135787916).

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

Изменение задачи по сути ничем не отличается от ее создания, за исключением того, что нужно получить изменяемые данные. 
Создадим метод `loadModel`, который будет выбирать нужную строку из базы данных по первичному ключу, а в случае её отсутствия сообщать об ошибке.

```php
private function loadModel($id)
{
    $model = Todo::model()->findByPk($id);

    if ($model === null) {
        throw new CHttpException(404, 'Запрошенная страница не найдена.');
    }

    return $model;
}
```

```php
public function actionUpdate($id)
{
    // загружаем данные
    $model = $this->loadModel($id);

    if ($data = Yii::app()->getRequest()->getPost('Todo')) {

        $model->setAttributes($data);

        if ($model->update()) {
            Yii::app()->user->setFlash(
                YFlashMessages::SUCCESS_MESSAGE, 
                'Задача успешно обновлена'
            );

            $submitType = Yii::app()->getRequest()->getPost('submit-type');

            if (isset($submitType)) {
                // Если пользователь нажал "Сохранить и закрыть", 
                // то отправляем его на главную страницу модуля
                $this->redirect([$submitType]);
            } else {
                // Иначе остаемся на странице редактирования задачи
                $this->redirect(['update', 'id' => $model->id]);
            }
        }
    }

    $this->render('update', ['model' => $model]);
}
```

Далее создаем файл представления `update`, который будет отличаться от `create` только названиями заголовков и "хлебными крошками". Попробуйте самостоятельно сделать этот файл. Подсмотреть решение можно [здесь](https://github.com/sabian/yupe-todo/commit/2157ef21c754c40b562aa64bef59a4b96c93587b).

Для завершения этой части работ осталось только реализовать обработку удаления задачи.

Следует отметить, что групповое удаление записей [реализовано](https://github.com/yupe/yupe/blob/master/protected/modules/yupe/components/controllers/BackController.php#L123) в контроллере `BackController`, свойства и методы которого наследует контроллер модуля `TodoBackendController`, так что дополнительной реализации не требуется.

В первую очередь создаем фильтр, который будет запрещать обращение к методу `actionDelete` напрямую и разрешать его только через POST запрос:

```php
public function filters()
{
    return CMap::mergeArray(
        parent::filters(),
        [
            'postOnly + delete',
        ]
    );
}
```

_Обратите внимание на объединение двух массивов: родительского и фильтров текущего контроллера. Про это не стоит забывать, т.к. в родительском классе определяется фильтр, ограничивающий доступ к административной части вашего модуля._

Далее реализуем удаление:
```php
public function actionDelete($id)
{
    if ($this->loadModel($id)->delete()) {
        Yii::app()->user->setFlash(
            YFlashMessages::SUCCESS_MESSAGE, 
            'Задача успешно удалена'
        );

        if (!Yii::app()->getRequest()->getParam('ajax')) {
            $this->redirect(
                (array)Yii::app()->getRequest()->getPost('returnUrl', 'index')
            );
        }
    }
}
```

Здесь все очень просто и не требует дополнительных пояснений.

Работа над основными действиями закончена. Теперь вы можете добавлять, изменять и удалять задачи. Результат, как всегда, можно посмотреть в соответствующем [коммите на GitHub](https://github.com/sabian/yupe-todo/commit/2157ef21c754c40b562aa64bef59a4b96c93587b), а если возникли вопросы, то можно задать их на [форуме Юпи!](https://talk.yupe.ru).

## Статусы
Наверняка вы уже попробовали добавить задачу и увидели: в списке вместо статуса отображается цифра 1, что крайне неинформативно. Также нет возможности редактировать статус. Пора это исправить.

В корневой директории модуля создаем директорию `helpers`, в которой будет находиться файл `TodoStatusHelper`, содержащий названия статусов и методы для работы с ними.

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

```php
class TodoStatusHelper
{
    const STATUS_CANCELLED = 0;
    const STATUS_DEFAULT = 1;
    const STATUS_URGENT = 2;
    const STATUS_DONE = 3;
}
```

Предлагаем ограничиться четырьмя статусами: Отменена, Обычная, Срочная, Выполнена. При необходимости вы можете самостоятельно расширить этот список.

Также понадобятся методы для получения списка статусов и получения названия статуса по его номеру.

```php
public static function getList()
{
    return [
        self::STATUS_CANCELLED => 'Отменена',
        self::STATUS_DEFAULT => 'Обычная',
        self::STATUS_URGENT => 'Срочная',
        self::STATUS_DONE => 'Выполнена',
    ];
}

public static function getLabel($id)
{
    $list = self::getList();

    return $list[$id];
}
```

Чтобы класс хелпера был доступен для дальнейшего использования, необходимо добавить в конфигурационный файл модуля (`install/todo.php`) следующие строки:

```php
'import' => [
    'application.modules.todo.helpers.*',
],
```

После чего нужно обновить конфигурацию модуля. Это можно сделать в панели управления, кликнув на специальный значок, который появится на кнопке модуля:

![Модуль ToDo. Обновление конфигурации](img/yupe-module-4.png)

или выполнив консольную команду: `php protected/yiic yupe updateConfig`

Подготовительные работы закончены, теперь пора отобразить название статусов в списке задач. Для этого в файле `views/todoBackend/index.php` значение `status` массива `columns` заменяем на нижеследующий код:

```php
[
    'name' => 'status',
    'value' => function (Todo $data) {
        return TodoStatusHelper::getLabel($data->status);
    }
],
```

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

![Модуль ToDo. Название статуса](img/yupe-module-5.png)

Для удобства фильтрации задач по статусу сделаем выпадающий список

```php
'filter' => CHtml::activeDropDownList($model, 'status', TodoStatusHelper::getList(), [
    'encode' => false, 
    'empty' => '', 
    'class' => 'form-control',
]),
```

![Модуль ToDo. Выпадающий список для фильтра](img/yupe-module-6.png)

Согласитесь, стало гораздо удобней!

Далее реализуем возможность смены статуса задачи через форму редактирования. Для этого изменим файл `views/todoBackend/_form.php`, где вместо кода

```php
<div class="row">
    <div class="col-sm-12">
        <?= $form->textFieldGroup($model, 'description'); ?>
    </div>
</div>
```

поставим следующий

```php
<div class="row">
    <div class="col-sm-8">
        <?= $form->textFieldGroup($model, 'description'); ?>
    </div>
    <div class="col-sm-4">
        <?= $form->dropDownListGroup($model, 'status', [
            'widgetOptions' => [
                'data' => TodoStatusHelper::getList(),
            ],
        ]); ?>
    </div>
</div>
```

Теперь, зайдя в редактирование или добавление задачи, рядом с текстовым полем вы увидите выпадающий список с названиями статусов.

Наберитесь терпения, остались последние штрихи и со статусами будет покончено. Нам осталось сделать inline-редактирование статуса, т.е. дать возможность пользователю поменять статус задачи прямо из списка, не заходя в форму редактирования.

В Юпи! это сделать не просто, а очень просто! Реализуем в классе `TodoBackendController` метод `actions`

```php
public function actions()
{
    return [
        'inline' => [
            'class' => 'yupe\components\actions\YInLineEditAction',
            'model' => 'Todo',
            'validAttributes' => [
                'status',
            ]
        ],
    ];
}
```

Здесь вы сообщаете системе, что в этом классе будет еще одно действие (`inline`), доступное по адресу `/todo/todoBackend/inline`. Такой подход позволяет избежать ненужного дублирования кода и упростить его изменение. 

Значение ключа `class` указывает на класс, ответственный за обработку данного действия. Т.е. вам не нужно каждый раз создавать метод `actionInline` и копировать в него один и тот же код. Представьте насколько усложнилась бы ваша жизнь, если вам пришлось вносить небольшое изменение в работу этого метода, а их у вас было бы 100. В данном случае вам нужно отредактировать только один файл.

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

Осталось заменить код колонки `status` в файле `views/todoBackend/index.php` на следующий:

```php
[
    'name' => 'status',
    'class' => 'yupe\widgets\EditableStatusColumn',
    'url' => $this->createUrl('/todo/todoBackend/inline'),
    'source' => TodoStatusHelper::getList(),
    'filter' => CHtml::activeDropDownList($model, 'status', TodoStatusHelper::getList(), [
        'encode' => false,
        'empty' => '',
        'class' => 'form-control',
    ]),
],
```

А теперь можно насладиться результатом:

![Модуль ToDo. Inline-редактирование](img/yupe-module-7.gif)

Напоследок предлагаем сделать цветовое кодирование статусов, чтобы их было проще различать. Для этого в класс хелпера добавляем новый метод:

```php
public static function getStylesList()
{
    return [
        self::STATUS_CANCELLED => ['class' => 'label-danger'],
        self::STATUS_DEFAULT => ['class' => 'label-default'],
        self::STATUS_URGENT => ['class' => 'label-primary'],
        self::STATUS_DONE => ['class' => 'label-success'],
    ];
}
```

В нем каждому статусу присваивается свое [название класса](http://getbootstrap.com/components/#labels) метки. И добавляем строку `'options' => TodoStatusHelper::getStylesList(),` в описание колонки `status` в файле `views/todoBackend/index.php` 

![Модуль ToDo. Цветные метки статусов](img/yupe-module-8.png)

Код этой части модуля доступен по [ссылке](https://github.com/sabian/yupe-todo/commit/3f4478ab1e79f704ec59f9316573a0fb8eed1745). Если у вас возникли вопросы, можете смело задавать их на нашем [форуме](https://talk.yupe.ru).

## Сортировка перетаскиванием (drag&drop)
В модуле нет необходимости делать сортировку, т.к. приоритеты задач определяются статусами. Но, поскольку он создается в учебных целях, то предлагаем реализовать эту возможность.

В метод `actions` файла `TodoBackendController` добавляем определение действия `sortable`:

```php
'sortable' => [
    'class' => 'yupe\components\actions\SortAction',
    'model' => 'Todo',
    'attribute' => 'sort'
]
```

Поле `sort`, в котором будет храниться порядок сортировки записей, мы уже создали в разделе Миграции. Обратите внимание, что для корректной работы сортировки значение по умолчанию должно быть 1 (`NOT NULL DEFAULT 1`).

В класс модели `Todo` добавляем метод `beforeSave`, который задает порядок сортировки для новых записей:

```php
protected function beforeSave()
{
    if ($this->isNewRecord) {
        $this->sort = Yii::app()->db->createCommand()
            ->select('MAX(sort) + 1')
            ->from($this->tableName())
            ->queryScalar();
    }

    return parent::beforeSave();
}
```

Следующие строки добавляем к параметрам виджета `CustomGridView` в файле `views/todoBackend/index.php`:

```php
'sortableRows' => true,
'sortableAjaxSave' => true,
'sortableAttribute' => 'sort',
'sortableAction' => '/todo/todoBackend/sortable',
```

Это всё! [Вот так просто](https://github.com/sabian/yupe-todo/commit/ee46a97aaaee031bf8d27ffc76a2fd8214ddfcc1), вы можете реализовать drag&drop сортировку записей в вашем модуле для CMS Юпи!

![Модуль ToDo. D&D сортировка](img/yupe-module-9.gif)

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

Реализацию начнем с создания метода `checkUnfinished` в классе `Todo`:

```php
public function countUnfinished()
{
    return self::model()->count(
        'status != :status', 
        [':status' => TodoStatusHelper::STATUS_DONE]
    );
}
```

Здесь все очень просто: метод возвращает количество записей в базе, у которых статус не равен "Выполнено".

А в класс `TodoModule` добавляем следующий код:

```php
public function checkSelf()
{
    $messages = [];
    $count = Todo::model()->countUnfinished();

    if(!$count) {
        return false;
    }

    $messages[WebModule::CHECK_NOTICE][] = [
        'type' => WebModule::CHECK_NOTICE,
        'message' => CHtml::link('Невыполненных задач - ' . $count, ['/todo/todoBackend/index']),
    ];

    return $messages;
}
```

Метод `checkSelf` будет вызываться всякий раз, когда вы будете заходить в панель управления. Уведомления будут показываться на главной странице в одноименном блоке.

В этом методе мы обращаемся к ранее созданному `countUnfinished`. Если значение переменной `$count` больше ноля, то метод возвращает массив, содержащий сообщение со ссылкой на главную страницу модуля. Впоследствии массивы сообщений всех модулей сливаются в один и выводятся на экран.

![Модуль ToDo. Уведомления.](img/yupe-module-10.png)

[Код раздела на GitHub](https://github.com/sabian/yupe-todo/commit/39d5032b30ddf6229c506a2787983ff25cf501ae)

На этом работу над модулем можно считать завершенной. Изложенного выше материала должно хватить для понимания базовых принципов создания модулей для CMS Юпи! 
Поскольку административная часть модуля отличается от клиентской лишь названием контроллеров, то, в качестве практики, мы предлагаем вам самостоятельно реализовать отдельную страницу для отображения открытых задач на вашем тестовом сайте. При желании вы можете пойти дальше и расширить функциональность модуля или начать писать свой собственный.

В директории `protected/modules` содержится множество готовых модулей, в которых вы можете посмотреть примеры реализаций необходимого вам функционала.

С возникшими вопросами ждем вас [на форуме Юпи! CMS](http://talk.yupe.ru/)


#  Помощь проекту! 

# Помощь проекту! #

Мы помогли запустить свой сайт и интернет-магазин примерно четырем тысячам предпринимателей! Мы очень старались и сделали это совершенно бесплатно! Мы собираемся добавить много новых и классных функций и вы нам можете в этом помочь!

# Морально #

- [Сообщить об ошибке на Github](https://github.com/yupe/yupe/issues/new) или на [нашем форуме](http://talk.yupe.ru/viewforum.php?id=2)
- Мы пишем тесты на [Codeception](http://allframeworks.ru/codeception/) - пишите тесты для своих use-case и присылайте их нам
- Всегда можно поддержать нас морально, [написав письмо](https://yupe.ru/contacts)
- Помогайте пользователям на [форуме поддержки](http://talk.yupe.ru/)
- Делайте проекты на "Юпи!" и становитесь [нашими партнерами](https://yupe.ru/partners)
- [Рассказывайте](http://www.cmsmagazine.ru/catalogue/yupi-cms/works/) о проектах на "Юпи!"

# Материально #

- Создайте свой модуль для "Юпи!" и разместите его в ["Юпи! Market"](https://yupe.ru/store)
- [Закажите](https://yupe.ru/contacts) у нас **разработку проекта** на Yii/Юпи!
- [Переведите нам немного счастья](https://yupe.ru/make-world-better) +)


!!! note "Обратите внимание!"
    Мы верим в будущее Open Source разработки! Но нам необходима [ваша поддержка](https://yupe.ru/make-world-better)!


#  Возможности 

# Возможности #

Из коробки вы получаете уже рабочее приложение, со следующим функционалом:

* [Регистрация](https://yupe.ru/registration), [аутентификация](https://yupe.ru/login), [восстановление пароля](https://yupe.ru/recovery), подтверждение email ([модуль user](https://github.com/yupe/yupe/tree/master/protected/modules/user)).
* Управление пользователями (блокировка, активация, редактирование и т.д.).
* Модуль для <a href='https://yupe.ru/community' target='_blank'>ведения блогов и сообществ</a> ([как индивидуальных, так и коллективных](https://yupe.ru/blogs/yupe-mini-cms-yii)).
* Создание и публикация новостей.
* Создание и управление страницами сайта.
* Создание и управление категориями сайта (разделами).
* Создание и редактирование меню сайта.
* Древовидные комментарии (можно комментировать любую сущность, [пример](https://yupe.ru/post/versiya-099-yupe-internet-magazin-free.html#comments)).
* Модуль простых справочников (хранение и управление справочной информацией).
* Модуль для "Обратной связи" + [раздел FAQ](https://yupe.ru/faq).
* Модуль для работы с блоками контента.
* Удобная админка на Twitter Bootstrap  ([выглядит вот так](https://yupe.ru/albums)).
* [Возможность генерировать CRUD в стиле Twitter Bootstrap](https://github.com/yupe/yupe/tree/master/protected/modules/yupe/extensions/yupe).
* Создание и запуск полноценного [интернет-магазина](https://yupe.ru/ecommerce) ([каталог](https://yupe.ru/ecommerce), [заказы](https://yupe.ru/ecommerce/orders), [купоны](https://yupe.ru/ecommerce/coupons), [оплата](https://yupe.ru/ecommerce/payment), [доставка](https://yupe.ru/ecommerce/delivery))


# Обзор панели управления "Юпи!"
Юпи! имеет модульную структуру, благодаря чему вы можете легко расширить систему под свои нужды.

В стандартной поставке идет множество модулей, решающих большую часть повседневных задач. Если этого недостаточно, то дополнительные модули можно поискать в [магазине](https://yupe.ru/store) или заказать их [разработку у нас](http://amylabs.ru).

## Системные модули
Из стандартных, следует выделить модули **yupe** и **user**, без которых невозможна работа CMS Юпи!

##### yupe
Этот модуль можно назвать краеугольным камнем всей системы. Он отвечает за настройки сайта, работу других модулей, обработку запросов. Компоненты для работы с оповещениями, изображениями, поведения для загрузки файлов и многое многое другое также заключено в нем.

##### Пользователи (user)
Название модуля говорит само за себя и в особом представлении не нуждается. Он позволит решить большую часть ваших задач по управлению пользователями, а в связке с модулем **rbac** представляет из себя мощный инструмент по разделению прав на основе ролей.

## Стандартные модули

##### Блоги (blogs)
Модуль позволяет создавать персональные и коллективные блоги с возможностью настраивать статусы новых участников и записей.

##### Каталог (catalog)
При помощи этого модуля можно организовать простой каталог товаров, сгрупированных по категориям.

##### Категории (category)
Модуль служит для структурирования и разделения контента на тематические категории.

##### Комментарии (comment)
Древовидные комментарии неограниченной вложенности с широким спектром настроек: статусы, уведомления, капча, гостевые комментарии, RSS и т.д.
Применяется для комментирования изображений, постов в блогах, товаров, заказов, новостей. Буквально парой строчек кода вы можете подключить комментирование и к своему модулю.

##### Блоки контента (contentblock)
Идеально подходит для встраивания небольших текстовых блоков (адреса, телефоны, часы работы и т.д.) в дизайн сайта, что позволяет людям без специальных знаний легко управлять информацией на сайте.

##### Справочники (dictionary)
Модуль позволяет создавать различные типы справочников и управлять их содержимым.

##### Обратная связь (feedback)
Модуль для создания раздела обратной связи на вашем сайте. Все сообщения хранятся в базе данных сайта и доступны для просмотра в панели управления, откуда вы можете на них ответить. Также есть возможность из присланных сообщений и ответов организовать раздел часто задаваемых вопросов.

##### Галереи (gallery)
Модуль позволяет группировать ваши изображения в альбомы с различными уровнями доступа: публичные, личные, скрытые и т.д. Также имеется возможность пакетной загрузки изображений.

##### Главная страница (homepage)
Этот модуль поможет быстро создать главную страницу сайта, на которой вы можете разместить последние записи блогов, товары из магазина или созданную по вашему вкусу текстовую страницу с нужным вариантом оформления.

##### Изображения (image)
Модуль позволяет хранить информацию об изображениях в одном месте. Интегрирован с редактором redactorjs.

##### Почта (mail)
Модуль позволяет задавать почтовые события, привязывать к ним шаблоны писем и производить их отправку получателям.

##### Меню (menu)
С этим модулем вы можете создать себе неограниченное количество меню для шапки сайта, футера или сайдбара. Или меню, которое будет отображаться только на какой-нибудь определенной странице. Все эти меню могут содержать неограниченное количество пунктов с неограниченной вложенностью.
А еще прибавьте к этому: управление атрибутами ссылки, возможность указать текст до и после ссылки, а так же условия отображения, удобную сортировку перетаскиванием и возможность редактирования прямо в общем списке.

##### Новости (news)
Модуль для ведения новостной ленты с возможностью разбивки записей по категориям. В комплекте RSS ленты, удобные редакторы, статусы, возможность чтения только авторизованными пользователями и, конечно же, поддержка различных языков.

##### Уведомления (notify)
Работа этого модуля обеспечивает подписку пользователей на уведомления о новых комментариях к своим записям в блогах или ответам на свои комментарии в других блогах.

##### Страницы (page)
Незаменимый инструмент для создания текстовых страниц на сайте. Все это делается в одном из выбранных вами визуальном редакторе с широкими возможностями управления текстами, изображениями, таблицами и видео. А еще вы можете создавать страницы для разных языков, подключать необходимые шаблоны отображения, организовывать страницы по категориям, менять статусы, а также есть все необходимое для SEO-оптимизации.

##### Задания (queue)
Простейшая реализация очереди заданий. Идеально подойдет для фоновой рассылки email-сообщений или обработки изображений.

##### Права доступа(rbac)
Инструмент для распределения ролей между администраторами сайта. Например, вы можете позволить одному пользователю управлять товарами в магазине, другому заказами, а третьему только контентом.

##### Карта сайта (sitemap)
Модуль генерирует xml-карту сайта для лучшей индексации поисковыми системами. С версии 0.9.7 генерируется в виде отдельного статичного файла для увеличения скорости ответа.

##### Социализация (social)
С помощью этого модуля пользователи смогут авторизоваться на вашем сайте через популярные социальные сети (Вконтакте, Одноклассники, Twitter, Facebook, ...)

##### Обновления (update)
Отображает информацию о вышедших обновлениях системы и стандартных модулей в панели управления сайтом.

##### Поиск Zend (zendsearch)
Модуль для индексирования и поиска по контенту на основе Zend Search Lucene

### Интернет-магазин
Начиная с версии 0.9 в составе Юпи! поставляются модули для реализации полноценного интернет-магазина. Из приведенного ниже краткого описания этих модулей вы поймете, что имеющегося функционала достаточно для успешного старта!

##### Магазин (store)
Store - самый главный модуль для интернет-магазина на Юпи!. Модуль содержит в себе функционал, на основе которого строится все содержимое веб-витрины. Прежде всего справочные данные: производители, категории и типы товаров, атрибуты товаров. Модуль позволяет создавать любые типы товаров с произвольными (динамическими) атрибутами. Для каждого товара можно создать его варианты - характеристики товара, влияющие на его конечную стоимость. Количество вариантов товаров, а также количество и вложенность категорий неограничено.

##### Корзина (cart)
Модуль содержит все необходимое для реализации корзины покупателя на сайте: добавление товара в корзину, удаление, изменение количества товаров.
При наличии модуля **coupon** можно указать код скидочного купона и получить соответствующую скидку при оформлении заказа.

##### Купоны (coupon)
Позволяет создавать и управлять скидочными купонами. Скидки к праздникам, подарочные купоны вашим клиентам на дни рождения, промо-акции.

##### Доставка (delivery)
Модуль, отвечающий за управление способами доставки в интернет-магазине.

##### Заказы (order)
Order - модуль для управления самой важной частью интернет-магазина - заказами.
Отправка уведомлений пользователю и администратору сайта, поиск и фильтрация заказов, редактирование, комплектация, статусы заказа и оплаты - весь этот функционал доступен сразу после установки.

##### Платежи (payment)
Модуль для интеграции различных платежных систем и сервисов. Сам по себе этот модуль не содержит реализации платежей ни для одного из сервисов. Модуль является лишь основой и связующим звеном между модулем **order** и реализацией конкретной платежной системы.

##### Робокасса (robokassa)
Вместе с модулем **payment** данный модуль позволяет принимать платежи на сайте через довольно популярный сервис - Робокасса. Оплата принимается через Qiwi, Webmoney, Яндекс.Деньги, пластиковые карты и другие способы оплаты.

##### Яндекс.Маркет (yandexmarket)
Модуль позволяет выгружать каталог товаров в очень распространенный формат - yml. Данная выгрузка позволит присутствовать вашему магазину в самом популярном маркетплейсе в России - Яндекс.Маркете.

##### Payler (payler)
Прием платежей банковскими картами при помощи сервиса payler.com

## Платные модули

##### [AmoCRM (amocrm)](https://yupe.ru/store/modules/crm/amocrm.html)
Интеграция сайта или интернет-магазина с AmoCRM. Обрабатываются: регистрации пользователей, сообщения из формы обратной связи, заказы и заявки из модуля "Обратный звонок" (callback).

##### [RetailCRM (retailcrm)](https://yupe.ru/store/modules/crm/retailcrm.html)
Обмен заказами между интернет-магазином на "Юпи!" и RetailCrm.

##### [Мой склад (cml)](https://yupe.ru/store/modules/crm/moy-sklad.html)
Интеграция вашего магазина на Юпи! с сервисом МойСклад или 1С. Производится синхронизация категорий, товарных позиций, остатков и заказов.

##### [Slack (slack)](https://yupe.ru/store/modules/community/slack.html)
Модуль для интеграции вашего сайта с платформой [Slack](http://slack.com/). Передаются уведомления о сообщениях из формы обратной связи, регистрации новых пользователей и заказах.


#  Управление модулями

# Управление модулями

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

![Раздел "Модули"](img/yupe-settings-1.png)

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

# Настройки

Изменение настроек возможно как из меню "Модули", так и в соответствующем пункте меню нужного вам модуля.

![Настройки модулей](img/yupe-settings-2.png)

Система содержит большое количество модулей с еще большим количеством настроек, поэтому описывать каждый не имеет смысла: все настройки снабжены понятными названиями, либо сопровождаются описаниями. Однако, в качестве примера, стоит рассмотреть некоторые настройки самого главного модуля Юпи!

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

![Настройки сайта](img/yupe-settings-3.png)



# Настройка тестового окружения

Настройка тестового окружения
==========================

Для тестирования Юпи! мы используем популярный фреймворк для тестирования - [Codeception](http://codeception.com/).

Для ознакомления с Codeception мы перевели ешл официальную документацию [http://allframeworks.ru/codeception/](https://yupe.ru/codeception/).

Мы не вносим какие-либо изменения в сам фреймворк, однако добавляем некоторые собственные Помощники (Helpers), а так же придерживаемся определенных соглашений и правил.

Тесты codeception располагаются в каталоге `./tests/` относительно корня yupe.

Так как существует необходимость в настройке тестового окружения для каждого разработчика,
в репозитории мы храним только конфигурационные файлы `*.dist.yml`.

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

Прежде чем начать тестирование, вам необходимо создать конфигурационный файл тестовой базы данных (`protected/config/db-test.php`),
а так же файлы конфигурации Codeception:

- `codeception.yml`
- `tests/acceptance.suite.yml`
- `tests/functional.suite.yml`
- `tests/unit.suite.yml`

**Нет необходимости создавать данные файлы вручную. В этом вам поможет команда подготовки тестового окружения**

Подготовка тестового окружения
------------------------------

Для начала отметим, что в соответсвтии с соглашением **Юпиы!** должен быть установлен на отдельный виртуальных хост и доступен по адресу [http://yupe.local](http://yupe.local).
Конечно, в дельнейшем вы сможете настроить данный адрес так как вам угодно, однако по умолчанию файлы конфигурации будут созданны именно с таким адресом.

Стоит сказать пару слов о настройках базы данных. По умолчанию для тестирования мы используем
базу данных `MySQL` с названием `yupe_test` с логином `root`, без пароля. Если вы захотите сменить тип базы
данных, вы сможете сделать позже, изменив настроки в личных файлах конфигурации.

####Для создания личных файлов конфигурации выполните команду:

`./yiic testenv create`

Данная команда в интерактивном режиме спросит вас о настройках базы данных и создаст необходимые
файлы конфигурации в соответствующих каталогах:

- `codeception.yml` - личный файл конфигурации Codeception, будет объеденен с `codeception.dist.yml`
- `tests/acceptance.suite.yml` - личный файл конфигурации для Acceptance тестов, будет объеденен с `acceptance.suite.dist.yml`
- `tests/functional.suite.yml` - личный файл конфигурации для Functional тестов, будет объеденен с `functional.suite.dist.yml`
- `tests/unit.suite.yml` - личный файл конфигурации для Unit тестов, будет объеденен с `unit.suite.dist.yml`
- `protected/config/db-test.php` - файл конфигурации тестовой базы данных для Yupe
(Используется в index-test.php при Acceptance и Functional тестирвоании, а так же используется при тестировании консольных комманд Yupe и Yii).

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

####Для удаления личных файлов конфигурации выполните команду**:

`./yiic testenv reset`

Данная команда удалит все личные файлы конфигурации.

Тестовое окружение Юпи!
--------------------------

####Файлы Юпи использующиеся при тестировании:

- `protected/config/console-test.php` - Используется при создании приложения для **unit** тестирования консольных команд
- `protected/config/test.php` - Используется при создании приложения для **functional** и **acceptance** тестирования Юпи!
- `public/index-test.php` - Точка входа для приложения при **Acceptance** и **Functional** тестировании

####Структура каталогов и файлов Codeception:

- `codeception.dist.yml` - Главный конфигурационный файл Codeception (Хранится в репозитории)
- `codeception.yml` - Личный главный конфигурационный файл Codeception (создается разработчиком)
- `tests/` - Каталог тестов Codeception
- `tests/acceptance/` - Каталог Acceptance (Приемочных) тестов Yupe
- `tests/functional/` - Каталог Functional (Функциональных) тестов Yupe
- `tests/unit/` - Каталог Unit (Модульных) тестов Yupe
- `tests/acceptance.suite.dist.yml` - файл конфигурации для Acceptance тестов (Хранится в репозитории)
- `tests/acceptance.suite.yml` - личный файл конфигурации для Acceptance тестов (создается разработчиком)
- `tests/functional.suite.dist.yml` - файл конфигурации для Functional тестов (Хранится в репозитории)
- `tests/functional.suite.yml` - личный файл конфигурации для Functional тестов (создается разработчиком)
- `tests/unit.suite.dist.yml` - файл конфигурации для Unit тестов (Хранится в репозитории)
- `tests/unit.suite.yml` - личный файл конфигурации для Unit тестов (создается разработчиком)

В целом структура каталога `tests/` не отличается от стандартной структуры каталога которую создает
Codeception. То же самое касается и конфигурационных файлов.

Единственное о чем следует помнить: В файлах `*.dist.yml` мы используем некоторые сервисные переменные, которые в последствии, при создании личных файлов конфигураци,
заменяются на указаные разработчиком, поэтому данные файлы не будут работать при отсутствии личных файлов конфигурации, сгенерированных командой `./yiic testenv create`.

**Основной особенностью применения Codeception являеся тот факт, что с его помощью можно легко тестировать сайты и веб-приложения, написанные на любой CMS и/или фреймвокре или же вообще без оных. Acceptance- и даже Functional-тесты можно использовать практически на любом проекте. Если у вас есть проект, качество которого, необходимо поддерживать на должном уровне - [напишите нам](http://amylabs.ru/contact).**


#  Обновление Юпи!

# Обновление Юпи!

!!! note "Обратите внимание!"
    На данный момент (версия 1.x) доступно только ручное обновление системы.

!!! danger "Важно!"
    Следует отметить, что безопасное обновление возможно, только если вы не изменяли системные файлы или стандартные модули Юпи!

    **Внимание! Перед обновлением системы не забудьте сделать резервные копии файлов и базы данных**.

## Обновление при развертывании через Composer

Если вы использовали этот способ, то для обновления вам необходимо скопировать [последнюю версию системы](https://github.com/yupe/yupe) на хостинг и выполнить команду
```
composer update
```

После этого можно переходить к пункту "Обновление конфигурации и миграций".

## Обновление при развертывании через Git

Обновляем файлы проекта
```
git pull origin master
```

Обновляем зависимости Composer-a
```
composer update
```

## Обновление при развертывании из архива

Вам нужно просто [скачать](https://yupe.ru/download) полный архив Юпи! со всеми установленными зависимостями и скопировать файлы на хостинг.

## Обновление конфигурации и миграций

Из командной строки обновление конфигураций и миграций выполняется командой
```
php protected/yiic yupe update
```

Обновить конфигурации отдельно можно командой ` php protected/yiic yupe updateConfig `

Для миграций `php protected/yiic yupe update` или `php protected/yiic migrate`

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

![Обновление конфигураций и миграций](img/yupe-update-1.png)

!!! important "Придем на помощь!"
    Мы как никто другой знаем свой продукт, обеспечим [лучшую поддержку и обновления](https://yupe.ru/service/support)!
    [Напишите нам!](https://yupe.ru/contacts)


# файлы конфигурации модулей с настройками пользователей

файлы конфигурации модулей с настройками пользователей
==================================================================

В Юпи! используется объединение файлов конфигурации для модулей. 
Каждый модуль имеет свой файл конфигурации, например, вот [такой](https://github.com/yupe/yupe/blob/master/protected/modules/blog/install/blog.php).

Во время запуска, конфигурационные файлы всех модулей объединяются в один общий файл, который и становится конфигурацией приложения, происходит это вот [здесь](https://github.com/yupe/yupe/blob/master/public/index.php#L44-L45).

Для улучшения производительности полученный файл кэшируется, при условии, что YII_DEBUG === false.

Если при разработке проекта вам потребуется изменить настройки какого-либо модуля - делать это непосредственно в файле конфигурации модуля мы НЕ рекомендуем, так как это может привести к проблемам при обновлении модуля.

Вместо непосредственной правки файл конфигурации модуля - создайте файл в каталоге `./protected/config/userspace`, файл должен иметь имя, совпадающее с именем файла настроек модуля, например если в модуле "Блоги", файл с настройками называется "blog.php", то и в каталог userspace необходимо положить файл "blog.php".

В файлах настроек модулей используются следующие категории:

* **import**    - для дополнения используемых путей импорта классов
* **cache**     - для настройки кеширования
* **preload**   - для настройки компонентов, которые будут изначально загружены приложением
* **component** - для описания компонентов, которые будут доступны в приложении
* **rules**     - для описания настроек маршрутизации, которые необходимы для вашего модуля
* **module**    - для описания вашего модуля, используемых модулем компонентов, настроек модуля

Подробнее остановимся на категории "rules".
Очень часто требуется менять стандартную маршрутизацию модулей: сделать урл-ы более SEO-оптимизированными, повторить структуру урл-ов уже имеющегося сайта и т.д. 

Рассмотим на примере [файла конфигурации](https://github.com/yupe/yupe/blob/master/protected/modules/blog/install/blog.php) для модуля "Блоги":

<pre><code class="php">
return array(
    'module'   => array(
        'class' => 'application.modules.blog.BlogModule',
    ),
    'import'    => array(),
    'component' => array(),
    'rules'     => array(
        '/post/<slug>.html' => 'blog/post/view',
        '/posts/tag/<tag>'  => 'blog/post/tag',
        '/rss/blog/<blog>'  => 'blog/blogRss/feed',
        '/rss/posts/'       => 'blog/blogRss/feed',
        '/blogs/<slug>'     => 'blog/blog/view',
        '/blogs'            => 'blog/blog/index',
        '/posts'            => 'blog/post/index',
        '/posts/archive'    => 'blog/archive/index',
        '/posts/categorys'  => 'blog/post/categorys',        
        '/posts/<slug>/'    => 'blog/post/blog',
        '/posts/category/<alias>'  => 'blog/post/category'
    ),
); 
</code></pre>

На сайте amylabs [портфолио](http://amylabs.ru/works) сделано именно через модуль блогов, каждая работа - это по сути пост из определенного блога.

Как мы видим, вместо "posts" - мы используем "works" (http://amylabs.ru/**works**), аналогично и для конкретного поста/работы.

Чтобы это работало мы создали в каталоге `./protected/config/usespace` файл "blog.php":

<pre><code class="php">
return array(
    'rules' => array(
        '/post/<slug>.html' => false, // запрет урл-ов из модуля для избежания дублирования страниц
        '/posts' => false, // запрет урл-ов из модуля для избежания дублирования страниц
        '/works' => 'works/index',
        '/works/<slug>' => array('blog/post/view', 'urlSuffix' => '.html')
    )
);
</code></pre>

Первые две строчки отменяют действия стандартных для модуля урл-ов (из файла настроек модуля), вторые две строки вводят новые правила уже для работ.

В версии 0.8 мы вернули возможность создания конфигурационного файла для всего проекта. В версиях до 0.7 он назывался userspace.php, с версии 0.8 он называется project.php

Данный файл расположен в каталоге ./protected/config/, при сборке конфига он объединяется с результирующим файлом-конфигом для модулей и main.php

Т.е. если в своем проекте вы хотите гарантированно переопределить какой-то компонент Yii или Юпи! - делайте это именно в этом файле.

**Если проект работает в "боевом режиме" не забудьте сбросить кэш настроек. [Подробнее](https://yupe.ru/docs/yupe/config.manager.html)**

**При возникновении проблем - [напишите нам](http://amylabs.ru/contact)!**


# Модуль поиска на основе Zend Lucene

Модуль поиска на основе Zend Lucene
===================================

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

- Открыть файл /protected/config/modules/zendsearch.php
- В нем есть описание переменной searchModels:

<pre><code class="php">
<?php

return [
    'module' => [
        'class' => 'application.modules.zendsearch.ZendSearchModule',
        'searchModels' => [
            'News' => [
                'path' => 'application.modules.news.models.News',
                'module' => 'news',
                'titleColumn' => 'title',
                'linkColumn' => 'slug',
                'linkPattern' => '/news/news/view?slug={slug}',
                'textColumns' => 'full_text,short_text,keywords,description,slug',
                'criteria' => [
                    'condition' => 'status = :status',
                    'params' => [
                        ':status' => 1
                    ],
                ],
            ],
            'Page' => [
                'path' => 'application.modules.page.models.Page',
                'module' => 'page',
                'titleColumn' => 'title',
                'linkColumn' => 'slug',
                'linkPattern' => '/page/page/view?slug={slug}',
                'textColumns' => 'body,title_short,keywords,description,slug',
                'criteria' => [
                    'condition' => 'status = :status',
                    'params' => [
                        ':status' => 1
                    ],
                ],
            ],
            'Blog' => [
                'path' => 'application.modules.blog.models.Blog',
                'module' => 'blog',
                'titleColumn' => 'name',
                'linkColumn' => 'slug',
                'linkPattern' => '/blog/blog/view?slug={slug}',
                'textColumns' => 'name,description,slug',
                'criteria' => [
                    'condition' => 'status = :status',
                    'params' => [
                        ':status' => 1
                    ],
                ],
            ],
            'Post' => [
                'path' => 'application.modules.blog.models.Post',
                'module' => 'blog',
                'titleColumn' => 'title',
                'linkColumn' => 'slug',
                'linkPattern' => '/blog/post/view?slug={slug}',
                'textColumns' => 'title,quote,content,slug',
                'criteria' => [
                    'condition' => 'status = :status',
                    'params' => [
                        ':status' => 1
                    ],
                ],
            ],
            'Product' => [
                'path' => 'application.modules.store.models.Product',
                'module' => 'store',
                'titleColumn' => 'name',
                'linkColumn' => 'slug',
                'linkPattern' => '/store/product/view?slug={slug}',
                'textColumns' => 'name,sku,slug,description,meta_title,meta_description,meta_keywords',
                'criteria' => [
                    'condition' => 'status = :status',
                    'params' => [
                        ':status' => 1
                    ],
                ],
            ],
        ],
    ],
    'import' => [
        'application.modules.zendsearch.models.*',
    ],
    'component' => [],
    'rules' => [
        '/search' => 'zendsearch/search/search',
    ],
];

</code></pre>


Это описание моделей, которые нужно включить в индекс:

- Page - название модели со следующими параметрами:
- path - алиас пути до модели (для уточнения где лежит эта модель)
- titleColumn - заголовок, который будет выводиться в списке результатов поиска
- linkColumn - поле таблицы, где хранится ссылка на модель
- linkPattern - паттерн ссылки на страницу модели
- textColumns - здесь через запятую нужно перечислить все поля таблицы, которые нужно загнать в индекс Важно! первым нужно указать поле, которое будет выводиться в качестве сниппета для страниц результатов поиска (сниппет формируется путем обрезки текста до 600 знаков, правильнее вывести это значение в свойства модуля, но руки не дошли до этого)
- criteria - Условия фильтрации моделей для индексации

Далее перейти на страницу модуля **http://site.ru/zendsearch/default/index** и обновить индекс поиска.
Индекс поиска по умолчанию хранится в папке **/protected/runtime/search**

После этого можно добавлять виджет формы поиска на страницы сайта:
<pre><code class="php">
    $this->widget('application.modules.zendsearch.widgets.SearchBlockWidget');
</code></pre>

!!! important "Придем на помощь!"
    Если Вам необходима интеграция полнотекстового поиска и/или фасетов (ElasticSearch или Sphinx) - мы можем [это сделать](https://yupe.ru/ecommerce/advanced) для Вас!
    [Напишите нам!](https://yupe.ru/contacts)


# Наша команда

Наша команда
============

- Опейкин Андрей [http://amylabs.ru](http://amylabs.ru/)
- Тимашов Максим  [http://amylabs.ru](http://amylabs.ru/)
- Седов Николай [http://amylabs.ru](http://amylabs.ru)
- Плаксунов Юрий [http://amylabs.ru](http://amylabs.ru)
- Филимонов Олег [http://amylabs.ru](http://amylabs.ru/)

И еще более [70 контрибьюторов на Github](https://github.com/yupe/yupe/graphs/contributors)!

!!! note "Обратите внимание!"
    Внеси свой вклад в Open Source ! **[Ждем только тебя !](https://yupe.ru/contacts)**