Commits

Bogdan Savluk committed 77ac975

move app from webroot

Comments (0)

Files changed (70)

 syntax: glob
-app/assets
+www/assets
 .git
-app/protected/runtime
-app/uploads
+app/runtime
+www/uploads
 .idea
-app/gallery
-app/protected/config/main.php
-app/protected/config/console.php
+www/gallery
+app/config/main.php
+app/config/console.php
 *.mwb.bak
-app/images
+www/images
-app/protected/extensions/chosen=https://bitbucket.org/z_bodya/yii-chosen
-app/protected/extensions/coordinatepicker=https://bitbucket.org/z_bodya/yii-coordinatepicker
-app/protected/extensions/tinymce=https://bitbucket.org/z_bodya/yii-tinymce
-app/protected/extensions/elFinder=https://bitbucket.org/z_bodya/yii-elfinder
-app/protected/extensions/fileimagearbehavior=https://bitbucket.org/z_bodya/fileimagearbehavior
-app/protected/extensions/galleryManager=https://bitbucket.org/z_bodya/gallerymanager
-app/protected/extensions/image=https://bitbucket.org/z_bodya/yii-image
-app/protected/extensions/swiftMailer=https://bitbucket.org/z_bodya/yii-swiftmailer
-app/protected/extensions/multiselect=https://bitbucket.org/z_bodya/yii-multiselect
-app/protected/extensions/datetimepicker=https://bitbucket.org/z_bodya/yii-datetimepicker
-app/protected/extensions/admin=https://bitbucket.org/z_bodya/yii-admin
-app/protected/extensions/settings=https://bitbucket.org/z_bodya/yii-settings
-app/protected/extensions/imperavi-redactor-widget=[git]https://github.com/zxbodya/imperavi-redactor-widget.git
+app/extensions/chosen=https://bitbucket.org/z_bodya/yii-chosen
+app/extensions/coordinatepicker=https://bitbucket.org/z_bodya/yii-coordinatepicker
+app/extensions/tinymce=https://bitbucket.org/z_bodya/yii-tinymce
+app/extensions/elFinder=https://bitbucket.org/z_bodya/yii-elfinder
+app/extensions/fileimagearbehavior=https://bitbucket.org/z_bodya/fileimagearbehavior
+app/extensions/galleryManager=https://bitbucket.org/z_bodya/gallerymanager
+app/extensions/image=https://bitbucket.org/z_bodya/yii-image
+app/extensions/swiftMailer=https://bitbucket.org/z_bodya/yii-swiftmailer
+app/extensions/multiselect=https://bitbucket.org/z_bodya/yii-multiselect
+app/extensions/datetimepicker=https://bitbucket.org/z_bodya/yii-datetimepicker
+app/extensions/admin=https://bitbucket.org/z_bodya/yii-admin
+app/extensions/settings=https://bitbucket.org/z_bodya/yii-settings
+app/extensions/imperavi-redactor-widget=[git]https://github.com/zxbodya/imperavi-redactor-widget.git
-3417f834f613b7d99a0c5b0ad150959ba8ddb175 app/protected/extensions/admin
-3e666f5d36f57b8501fece078d57a1183d9d5412 app/protected/extensions/chosen
-10f95710bd0c9fabee232b0b5dd6d0b9fa0c1928 app/protected/extensions/coordinatepicker
-59b564d09b749090a666832178b77dee82ca63e5 app/protected/extensions/datetimepicker
-f0142cc5cb14eb3dac7386fef8687347a340b6ad app/protected/extensions/elFinder
-d6378edd2934085356c4094dc1d3e2e0cb88ce0d app/protected/extensions/fileimagearbehavior
-c6c3d579315ac7c44d03c71c39bbc6b84d5055b9 app/protected/extensions/galleryManager
-2c669abd97f92c898b10672b8ebf0f2f7e73ff27 app/protected/extensions/image
-96480e918e0e8bca874975ddc30809207b4b328f app/protected/extensions/imperavi-redactor-widget
-b41015cc5fcc809c427290746ee5afb10200306b app/protected/extensions/multiselect
-8dc517ab01762a9f33feb6e6cdef319f02c0ff05 app/protected/extensions/settings
-29a901c09ec8ec796efe967b2eb127790a78e3ea app/protected/extensions/swiftMailer
-4a204c302973fade3c2afdf56aec9729c5b655d5 app/protected/extensions/tinymce
+3417f834f613b7d99a0c5b0ad150959ba8ddb175 app/extensions/admin
+3e666f5d36f57b8501fece078d57a1183d9d5412 app/extensions/chosen
+10f95710bd0c9fabee232b0b5dd6d0b9fa0c1928 app/extensions/coordinatepicker
+59b564d09b749090a666832178b77dee82ca63e5 app/extensions/datetimepicker
+f0142cc5cb14eb3dac7386fef8687347a340b6ad app/extensions/elFinder
+d6378edd2934085356c4094dc1d3e2e0cb88ce0d app/extensions/fileimagearbehavior
+c6c3d579315ac7c44d03c71c39bbc6b84d5055b9 app/extensions/galleryManager
+2c669abd97f92c898b10672b8ebf0f2f7e73ff27 app/extensions/image
+96480e918e0e8bca874975ddc30809207b4b328f app/extensions/imperavi-redactor-widget
+b41015cc5fcc809c427290746ee5afb10200306b app/extensions/multiselect
+8dc517ab01762a9f33feb6e6cdef319f02c0ff05 app/extensions/settings
+29a901c09ec8ec796efe967b2eb127790a78e3ea app/extensions/swiftMailer
+4a204c302973fade3c2afdf56aec9729c5b655d5 app/extensions/tinymce
+deny from all

app/components/Controller.php

+<?php
+/**
+ * Controller is the customized base controller class.
+ * All controller classes for this application should extend from this base class.
+ */
+class Controller extends CController
+{
+    /**
+     * @var string the default layout for the controller view. Defaults to '//layouts/column1',
+     * meaning using a single column layout. See 'protected/views/layouts/column1.php'.
+     */
+    public $layout = '//layouts/column1';
+    /**
+     * @var array context menu items. This property will be assigned to {@link CMenu::items}.
+     */
+    public $menu = array();
+    /**
+     * @var array the breadcrumbs of the current page. The value of this property will
+     * be assigned to {@link CBreadcrumbs::links}. Please refer to {@link CBreadcrumbs::links}
+     * for more details on how to specify this property.
+     */
+    public $breadcrumbs = array();
+
+
+    public $keywords = null;
+    public $description = null;
+    public $pageName;
+    private $_pageTitle;
+
+    public function setSEOParams($title = null, $keywords = null, $description = null)
+    {
+        if ($title) $this->pageTitle = $title;
+        if ($keywords) $this->keywords = $keywords;
+        if ($description) $this->description = $description;
+    }
+
+    /**
+     * @return string the page title. Defaults to the controller name and the action name.
+     */
+    public function getPageTitle()
+    {
+        if ($this->_pageTitle !== null)
+            return $this->_pageTitle;
+        else {
+            if (!empty($this->pageName))
+                $this->_pageTitle = $this->pageName . " | " . Yii::app()->name;
+            else
+                $this->_pageTitle = Yii::app()->name;
+            return $this->_pageTitle;
+        }
+    }
+
+    /**
+     * @param string $value the page title.
+     */
+    public function setPageTitle($value)
+    {
+        $this->_pageTitle = $value;
+    }
+}

app/components/FormattedDateFieldBehavior.php

+<?php
+
+/**
+ * Behavior to add custom formatted date time field to model.
+ *
+ * @author Bogdan Savluk <savluk.bogdan@gmail.com>
+ *
+ * Example configuration:
+ *
+ *   'dateRuBehavior' => array(
+ *        'class' => 'FormattedDateFieldBehavior',
+ *        'fieldName' => 'dateRu',
+ *        'fieldFormat' => 'dd.MM.yyyy hh:mm:ss',
+ *        'modelFieldName' => 'date',
+ *        'modelFieldFormat' => 'yyyy-MM-dd hh:mm:ss',
+ *   )
+ */
+class FormattedDateFieldBehavior extends CActiveRecordBehavior
+{
+
+    public $fieldName;
+    public $fieldFormat;
+
+    public $modelFieldName;
+    public $modelFieldFormat;
+
+    public function hasProperty($name)
+    {
+        if ($name == $this->fieldName) return true;
+        return parent::hasProperty($name);
+    }
+
+    public function canGetProperty($name)
+    {
+        if ($name == $this->fieldName) return true;
+        return parent::canGetProperty($name);
+    }
+
+    public function canSetProperty($name)
+    {
+        if ($name == $this->fieldName) return true;
+        return parent::canSetProperty($name);
+    }
+
+    public function __get($name)
+    {
+        if ($name == $this->fieldName) {
+            return $this->getField();
+        }
+        return parent::__get($name);
+    }
+
+    public function __set($name, $value)
+    {
+        if ($name == $this->fieldName) {
+            $this->setField($value);
+        } else {
+            parent::__set($name, $value);
+        }
+    }
+
+    private function getField()
+    {
+        $date = $this->getOwner()->{$this->modelFieldName};
+        if ($date !== null)
+            $timestamp = CDateTimeParser::parse($date, $this->modelFieldFormat);
+        else
+            $timestamp = time();
+        return Yii::app()->dateFormatter->format($this->fieldFormat, $timestamp);
+    }
+
+    private function setField($value)
+    {
+        $timestamp = CDateTimeParser::parse($value, $this->fieldFormat);
+        $this->getOwner()->{$this->modelFieldName} = Yii::app()->dateFormatter->format($this->modelFieldFormat, $timestamp);
+    }
+}

app/components/HtmlUtils.php

+<?php
+class HtmlUtils
+{
+    /**
+     * Truncates text.
+     *
+     * Code grabbed from:
+     * http://www.gsdesign.ro/blog/cut-html-string-without-breaking-the-tags/
+     *
+     * Cuts a string to the length of $length and replaces the last characters
+     * with the ending if the text is longer than length.
+     *
+     * @param string  $text String to truncate.
+     * @param integer $length Length of returned string, including ellipsis.
+     * @param string  $ending Ending to be appended to the trimmed string.
+     * @param boolean $exact If false, $text will not be cut mid-word
+     * @param boolean $considerHtml If true, HTML tags would be handled correctly
+     * @return string Trimmed string.
+     */
+    static function truncate($text, $length = 100, $ending = '...', $exact = true, $considerHtml = false)
+    {
+        if ($considerHtml) {
+            // if the plain text is shorter than the maximum length, return the whole text
+            if (strlen(preg_replace('/<.*?>/', '', $text)) <= $length) {
+                return $text;
+            }
+
+            // splits all html-tags to scanable lines
+            preg_match_all('/(<.+?>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER);
+
+            $total_length = strlen($ending);
+            $open_tags = array();
+            $truncate = '';
+
+            foreach ($lines as $line_matchings) {
+                // if there is any html-tag in this line, handle it and add it (uncounted) to the output
+                if (!empty($line_matchings[1])) {
+                    // if it's an "empty element" with or without xhtml-conform closing slash (f.e. <br/>)
+                    if (preg_match('/^<(\s*.+?\/\s*|\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\s.+?)?)>$/is', $line_matchings[1])) {
+                        // do nothing
+                        // if tag is a closing tag (f.e. </b>)
+                    } else if (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $line_matchings[1], $tag_matchings)) {
+                        // delete tag from $open_tags list
+                        $pos = array_search($tag_matchings[1], $open_tags);
+                        if ($pos !== false) {
+                            unset($open_tags[$pos]);
+                        }
+                        // if tag is an opening tag (f.e. <b>)
+                    } else if (preg_match('/^<\s*([^\s>!]+).*?>$/s', $line_matchings[1], $tag_matchings)) {
+                        // add tag to the beginning of $open_tags list
+                        array_unshift($open_tags, strtolower($tag_matchings[1]));
+                    }
+                    // add html-tag to $truncate'd text
+                    $truncate .= $line_matchings[1];
+                }
+
+                // calculate the length of the plain text part of the line; handle entities as one character
+                $content_length = strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', ' ', $line_matchings[2]));
+                if ($total_length + $content_length > $length) {
+                    // the number of characters which are left
+                    $left = $length - $total_length;
+                    $entities_length = 0;
+                    // search for html entities
+                    if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', $line_matchings[2], $entities, PREG_OFFSET_CAPTURE)) {
+                        // calculate the real length of all entities in the legal range
+                        foreach ($entities[0] as $entity) {
+                            if ($entity[1] + 1 - $entities_length <= $left) {
+                                $left--;
+                                $entities_length += strlen($entity[0]);
+                            } else {
+                                // no more characters left
+                                break;
+                            }
+                        }
+                    }
+                    $truncate .= substr($line_matchings[2], 0, $left + $entities_length);
+                    // maximum lenght is reached, so get off the loop
+                    break;
+                } else {
+                    $truncate .= $line_matchings[2];
+                    $total_length += $content_length;
+                }
+
+                // if the maximum length is reached, get off the loop
+                if ($total_length >= $length) {
+                    break;
+                }
+            }
+        } else {
+            if (strlen($text) <= $length) {
+                return $text;
+            } else {
+                $truncate = substr($text, 0, $length - strlen($ending));
+            }
+        }
+
+        // if the words shouldn't be cut in the middle...
+        if (!$exact) {
+            // ...search the last occurance of a space...
+            $spacepos = strrpos($truncate, ' ');
+            if (isset($spacepos)) {
+                // ...and cut the text in this position
+                $truncate = substr($truncate, 0, $spacepos);
+            }
+        }
+
+        // add the defined ending to the text
+        $truncate .= $ending;
+
+        if ($considerHtml) {
+            // close all unclosed html-tags
+            foreach ($open_tags as $tag) {
+                $truncate .= '</' . $tag . '>';
+            }
+        }
+
+        return $truncate;
+
+    }
+
+    /**
+     * strtr replacement for utf8 strings
+     * @static
+     * @param $str
+     * @param $from
+     * @param $to
+     * @return string
+     */
+    static function strtr_utf8($str, $from, $to)
+    {
+        $keys = array();
+        $values = array();
+        preg_match_all('/./u', $from, $keys);
+        preg_match_all('/./u', $to, $values);
+        $mapping = array_combine($keys[0], $values[0]);
+        return strtr($str, $mapping);
+    }
+}

app/components/RelatedIdsFieldBehavior.php

+<?php
+
+/**
+ * Adds field to manage many-to-many relation
+ *
+ * @author Bogdan Savluk <savluk.bogdan@gmail.com>
+ *
+ * Example configuration, for models:
+ *
+ *      Picture(id),
+ *      PictureCategory(id),
+ *      PictureHasCategory(picture_id, picture_category_id)
+ *
+ *      'pictureCategoryIdsBehavior' => array(
+ *          'class' => 'RelatedIdsFieldBehavior',
+ *          'modelName' => 'PictureHasCategory',
+ *          'relatedId' => 'picture_category_id',
+ *          'ownerId' => 'picture_id',
+ *          'relatedModelId' => 'id',
+ *          'modelId' => 'id',
+ *          'fieldName' => 'pictureCategoryIds',
+ *          'relationName' => 'pictureCategories',
+ *      ),
+ */
+class RelatedIdsFieldBehavior extends CActiveRecordBehavior
+{
+    /**
+     * Name of model for many-to-many relation table
+     * @var string
+     */
+    public $modelName;
+
+    /**
+     * Name of filed for related model id in relation table
+     * @var string
+     */
+    public $relatedId;
+
+    /**
+     * Name of filed for owner model id in relation table
+     * @var string
+     */
+    public $ownerId;
+
+    /**
+     * Name of id filed in related model
+     * @var string
+     */
+    public $relatedModelId;
+
+    /**
+     * Name of id filed in host model
+     * @var string
+     */
+    public $modelId;
+
+    /**
+     * Name of filed to add into model
+     * @var string
+     */
+    public $fieldName;
+
+    /**
+     * Name of many-to-many relation in owner
+     * @var string
+     */
+    public $relationName;
+
+    private $_relatedIds;
+
+    public function hasProperty($name)
+    {
+        if ($name == $this->fieldName) return true;
+        return parent::hasProperty($name);
+    }
+
+    public function canGetProperty($name)
+    {
+        if ($name == $this->fieldName) return true;
+        return parent::canGetProperty($name);
+    }
+
+    public function canSetProperty($name)
+    {
+        if ($name == $this->fieldName) return true;
+        return parent::canSetProperty($name);
+    }
+
+    public function __get($name)
+    {
+        if ($name == $this->fieldName) {
+            return $this->getRelatedIds();
+        }
+        return parent::__get($name);
+    }
+
+    public function __set($name, $value)
+    {
+        if ($name == $this->fieldName) {
+            $this->setRelatedIds($value);
+        } else {
+            parent::__set($name, $value);
+        }
+    }
+
+
+    private function getRelatedIds()
+    {
+        if (!isset($this->_relatedIds)) {
+            $res = array();
+            foreach ($this->owner->{$this->relationName} as $related) {
+                $res[] = $related->{$this->relatedModelId};
+            }
+            $this->_relatedIds = $res;
+        }
+        return $this->_relatedIds;
+    }
+
+    public function setRelatedIds($value)
+    {
+        if (is_array($value))
+            $this->_relatedIds = $value;
+    }
+
+    public function afterSave($event)
+    {
+        parent::afterSave($event);
+        $relatedModels = $this->owner->{$this->relationName};
+
+        $relatedIds = array_flip($this->getRelatedIds());
+        foreach ($relatedModels as $related) {
+            if (!isset($relatedIds[$related->{$this->relatedModelId}])) {
+                $this->getModel()->deleteByPk(array(
+                    $this->ownerId => $this->owner->{$this->modelId},
+                    $this->relatedId => $related->{$this->relatedModelId}
+                ));
+            }
+        }
+        $idsToAdd = $relatedIds;
+        foreach ($relatedModels as $related) {
+            if (isset($idsToAdd[$related->{$this->relatedModelId}]))
+                unset($idsToAdd[$related->{$this->relatedModelId}]);
+        }
+        foreach ($idsToAdd as $id => $v) {
+            $rel = $this->newRelation();
+            $rel->{$this->ownerId} = $this->owner->{$this->modelId};
+            $rel->{$this->relatedId} = $id;
+            $rel->save();
+        }
+    }
+
+    public function beforeDelete($event)
+    {
+        $this->getModel()->deleteAll($this->ownerId . ' = ' . $this->owner->{$this->modelId});
+        parent::beforeDelete($event);
+    }
+
+    /**
+     * @return CActiveRecord
+     */
+    private function getModel()
+    {
+        return CActiveRecord::model($this->modelName);
+    }
+
+    /**
+     * @return CActiveRecord
+     */
+    private function newRelation()
+    {
+        return new $this->modelName;
+    }
+}

app/components/SiteMapWriter.php

+<?php
+/**
+ * @author Bogdan Savluk <savluk.bogdan@gmail.com>
+ * For more info about params see:
+ * @link http://www.sitemaps.org/protocol.html
+ */
+class SiteMapWriter
+{
+    const FREQ_ALWAYS = 0;
+    const FREQ_HOURLY = 1;
+    const FREQ_DAILY = 2;
+    const FREQ_WEEKLY = 3;
+    const FREQ_MONTHLY = 4;
+    const FREQ_YEARLY = 5;
+    const FREQ_NEVER = 6;
+    private static $freq = array(
+        'always',
+        'hourly',
+        'daily',
+        'weekly',
+        'monthly',
+        'yearly',
+        'newer');
+    private $_pull = array();
+
+    public function addPage($location, $lastmod = null, $changefreq = null, $priority = null)
+    {
+        $this->_pull[] = array(
+            $location, $lastmod, $changefreq, $priority
+        );
+    }
+
+    public function generateXml($processOutput = false)
+    {
+        if ($processOutput) {
+            ob_start();
+        }
+        echo '<?xml version="1.0" encoding="UTF-8"?>',
+
+        '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
+
+        foreach ($this->_pull as &$item) {
+            echo '<url>', '<loc>', htmlentities($item[0]), '</loc>';
+            if (isset($item[1])) {
+                echo '<lastmod>', $item[1], '</lastmod>';
+            }
+            if (isset($item[1])) {
+                echo '<changefreq>', self::$freq[$item[2]], '</changefreq>';
+
+            }
+            if (isset($item[1])) {
+                echo '<priority>', $item[3], '</priority>';
+            }
+            echo '</url>';
+        }
+        echo '</urlset>';
+        $res = null;
+        if ($processOutput) {
+            $res = ob_get_clean();
+        }
+        return $res;
+    }
+}

app/components/Transliterator.php

+<?php
+
+class Transliterator
+{
+    /**
+     * Transliterate string
+     * @static
+     * @param $string
+     * @return string
+     */
+    public static function encode($string)
+    {
+        $converter = array(
+            'а' => 'a', 'б' => 'b', 'в' => 'v',
+            'г' => 'g', 'д' => 'd', 'е' => 'e',
+            'ё' => 'e', 'ж' => 'zh', 'з' => 'z',
+            'и' => 'i', 'й' => 'y', 'к' => 'k',
+            'л' => 'l', 'м' => 'm', 'н' => 'n',
+            'о' => 'o', 'п' => 'p', 'р' => 'r',
+            'с' => 's', 'т' => 't', 'у' => 'u',
+            'ф' => 'f', 'х' => 'h', 'ц' => 'c',
+            'ч' => 'ch', 'ш' => 'sh', 'щ' => 'sch',
+            'ь' => "'", 'ы' => 'y', 'ъ' => "'",
+            'э' => 'e', 'ю' => 'yu', 'я' => 'ya',
+
+            'А' => 'A', 'Б' => 'B', 'В' => 'V',
+            'Г' => 'G', 'Д' => 'D', 'Е' => 'E',
+            'Ё' => 'E', 'Ж' => 'Zh', 'З' => 'Z',
+            'И' => 'I', 'Й' => 'Y', 'К' => 'K',
+            'Л' => 'L', 'М' => 'M', 'Н' => 'N',
+            'О' => 'O', 'П' => 'P', 'Р' => 'R',
+            'С' => 'S', 'Т' => 'T', 'У' => 'U',
+            'Ф' => 'F', 'Х' => 'H', 'Ц' => 'C',
+            'Ч' => 'Ch', 'Ш' => 'Sh', 'Щ' => 'Sch',
+            'Ь' => "'", 'Ы' => 'Y', 'Ъ' => "'",
+            'Э' => 'E', 'Ю' => 'Yu', 'Я' => 'Ya',
+        );
+        return strtr($string, $converter);
+    }
+}

app/components/UserIdentity.php

+<?php
+
+/**
+ * UserIdentity represents the data needed to identity a user.
+ * It contains the authentication method that checks if the provided
+ * data can identity the user.
+ */
+class UserIdentity extends CUserIdentity
+{
+	/**
+	 * Authenticates a user.
+	 * The example implementation makes sure if the username and password
+	 * are both 'demo'.
+	 * In practical applications, this should be changed to authenticate
+	 * against some persistent user identity storage (e.g. database).
+	 * @return boolean whether authentication succeeds.
+	 */
+	public function authenticate()
+	{
+		$users=array(
+			// username => password
+			'demo'=>'demo',
+			'admin'=>'admin',
+		);
+		if(!isset($users[$this->username]))
+			$this->errorCode=self::ERROR_USERNAME_INVALID;
+		else if($users[$this->username]!==$this->password)
+			$this->errorCode=self::ERROR_PASSWORD_INVALID;
+		else
+			$this->errorCode=self::ERROR_NONE;
+		return !$this->errorCode;
+	}
+}

app/config/console.php

+<?php
+
+return CMap::mergeArray(
+    require(dirname(__FILE__) . '/main.php'),
+    array()
+);

app/config/console.php.dist

+<?php
+
+return CMap::mergeArray(
+	require(dirname(__FILE__).'/main.php'),
+	array(
+	)
+);

app/config/main.php.dist

+<?php
+
+// uncomment the following to define a path alias
+// Yii::setPathOfAlias('local','path/to/local-folder');
+
+// This is the main Web application configuration. Any writable
+// CWebApplication properties can be configured here.
+return array(
+    'basePath' => dirname(__FILE__) . DIRECTORY_SEPARATOR . '..',
+    'name' => 'CMS Test',
+    'language' => 'ru',
+
+    // preloading 'log' component
+    'preload' => array('log'),
+
+    // autoloading model and component classes
+    'import' => array(
+        'application.models.*',
+        'application.components.*',
+        'ext.fileimagearbehavior.*',
+        'ext.galleryManager.*',
+        'ext.galleryManager.models.*',
+        'ext.chosen.*',
+        'ext.swiftMailer.*',
+    ),
+
+    'modules' => array(
+        // uncomment the following to enable the Gii tool
+        'gii' => array(
+            'class' => 'system.gii.GiiModule',
+            'password' => 'password',
+            // If removed, Gii defaults to localhost only. Edit carefully to taste.
+            'ipFilters' => array('127.0.0.1', '::1'),
+            'generatorPaths' => array(
+                'ext.admin.gii', // a path alias
+            ),
+        ),
+        'admin' => array(
+            'class' => 'ext.admin.AdminModule',
+            'defaultController' => 'playground',
+            'modules' => array(
+                'manager' => array(
+                    'class' => 'application.modules.manager.ManagerModule',
+                ),
+                'playground',
+            ),
+        ),
+        // uncomment while using gii
+        // 'manager',
+    ),
+
+    // application components
+    'components' => array(
+        'cache' => array(
+            'class' => 'system.caching.CFileCache',
+        ),
+        'settings' => array(
+            'class' => 'ext.settings.Settings',
+            'cacheComponentId' => 'cache',
+            'cacheId' => 'website_settings',
+            'cacheTime' => 0,
+            'tableName' => '{{settings}}',
+            'dbComponentId' => 'db',
+            'createTable' => true,
+            'dbEngine' => 'InnoDB',
+        ),
+        'swiftMailer' => array(
+            'class' => 'ext.swiftMailer.YiiSwiftMailer'
+        ),
+        'request' => array(
+            'enableCsrfValidation' => true,
+        ),
+        'assetManager' => array(
+            'linkAssets' => true,
+        ),
+        'image' => array(
+            'class' => 'application.extensions.image.CImageComponent',
+            // GD or ImageMagick
+            'driver' => 'GD',
+            // ImageMagick setup path
+            'params' => array('directory' => '/opt/local/bin'),
+        ),
+
+        'user' => array(
+            // enable cookie-based authentication
+            'allowAutoLogin' => true,
+        ),
+        // uncomment the following to enable URLs in path-format
+        'urlManager' => array(
+            'urlFormat' => 'path',
+            'showScriptName' => false,
+            'rules' => array(
+                'sitemap.xml'=>'site/siteMap',
+                '<controller:\w+>/<id:\d+>' => '<controller>/view',
+                '<controller:\w+>/<action:\w+>/<id:\d+>' => '<controller>/<action>',
+                '<controller:\w+>/<action:\w+>' => '<controller>/<action>',
+            ),
+        ),
+
+        'db' => array(
+            'connectionString' => 'mysql:host=localhost;dbname=testcms',
+            'emulatePrepare' => true,
+            'username' => 'root',
+            'password' => '',
+            'charset' => 'utf8',
+            'tablePrefix' => 'tbl_'
+        ),
+
+        'errorHandler' => array(
+            // use 'site/error' action to display errors
+            'errorAction' => 'site/error',
+        ),
+
+        'log' => array(
+            'class' => 'CLogRouter',
+            'routes' => array(
+                array(
+                    'class' => 'CFileLogRoute',
+                    'levels' => 'error, warning',
+                ),
+                // uncomment the following to show log messages on web pages
+                /*
+                    array(
+                        'class'=>'CWebLogRoute',
+                    ),
+                    */
+            ),
+        ),
+    ),
+
+    // application-level parameters that can be accessed
+    // using Yii::app()->params['paramName']
+    'params' => array(
+        // this is used in contact page
+        'adminEmail' => 'webmaster@example.com',
+    ),
+);

app/config/web.php

+<?php
+
+return CMap::mergeArray(
+    require(dirname(__FILE__) . '/main.php'),
+    array(
+    )
+);

app/controllers/SiteController.php

+<?php
+
+class SiteController extends Controller
+{
+    /**
+     * Declares class-based actions.
+     */
+    public function actions()
+    {
+        return array(
+            // captcha action renders the CAPTCHA image displayed on the contact page
+            'captcha' => array(
+                'class' => 'CCaptchaAction',
+                'backColor' => 0xFFFFFF,
+                'backend'=>'gd',
+            ),
+            // page action renders "static" pages stored under 'protected/views/site/pages'
+            // They can be accessed via: index.php?r=site/page&view=FileName
+            'page' => array(
+                'class' => 'CViewAction',
+            ),
+        );
+    }
+
+    /**
+     * This is the default 'index' action that is invoked
+     * when an action is not explicitly requested by users.
+     */
+    public function actionIndex()
+    {
+        // renders the view file 'protected/views/site/index.php'
+        // using the default layout 'protected/views/layouts/main.php'
+        $this->render('index');
+    }
+
+    /**
+     * This is the action to handle external exceptions.
+     */
+    public function actionError()
+    {
+        if ($error = Yii::app()->errorHandler->error) {
+            if (Yii::app()->request->isAjaxRequest)
+                echo $error['message'];
+            else
+                $this->render('error', $error);
+        }
+    }
+
+    /**
+     * Displays the contact page
+     */
+    public function actionContact()
+    {
+        $model = new ContactForm;
+        if (isset($_POST['ContactForm'])) {
+            $model->attributes = $_POST['ContactForm'];
+            if ($model->validate()) {
+                $headers = "From: {$model->email}\r\nReply-To: {$model->email}";
+                mail(Yii::app()->params['adminEmail'], $model->subject, $model->body, $headers);
+                Yii::app()->user->setFlash('contact', 'Thank you for contacting us. We will respond to you as soon as possible.');
+                $this->refresh();
+            }
+        }
+        $this->render('contact', array('model' => $model));
+    }
+
+    public function actionSiteMap()
+    {
+        $writer = new SiteMapWriter();
+        $staticPages = array(
+            'site/index',
+            'site/contact',
+        );
+
+        // add all static pages
+        foreach ($staticPages as $r) {
+            $writer->addPage(Yii::app()->createAbsoluteUrl($r));
+        }
+
+        header('Content-Type:application/xml');
+        $writer->generateXml();
+    }
+}

app/migrations/m130123_200914_base.php

+<?php
+
+class m130123_200914_base extends CDbMigration
+{
+    public function up()
+    {
+        //return true;
+        $this->execute(<<<SQL
+
+CREATE TABLE IF NOT EXISTS `{{admin_auth_assignment}}` (
+  `itemname` varchar(64) NOT NULL,
+  `userid` varchar(64) NOT NULL,
+  `bizrule` text,
+  `data` text,
+  PRIMARY KEY (`itemname`,`userid`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `{{admin_auth_item}}` (
+  `name` varchar(64) NOT NULL,
+  `type` int(11) NOT NULL,
+  `description` text,
+  `bizrule` text,
+  `data` text,
+  PRIMARY KEY (`name`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `{{admin_auth_item_child}}` (
+  `parent` varchar(64) NOT NULL,
+  `child` varchar(64) NOT NULL,
+  PRIMARY KEY (`parent`,`child`),
+  KEY `child` (`child`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
+CREATE TABLE IF NOT EXISTS `{{admin_user}}` (
+  `username` varchar(64) NOT NULL,
+  `password` varchar(40) NOT NULL,
+  `salt` varchar(10) NOT NULL,
+  `name` varchar(64) NOT NULL,
+  PRIMARY KEY (`username`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+INSERT INTO `{{admin_auth_item}}` (`name`, `type`, `description`, `bizrule`, `data`) VALUES
+('admin', 2, 'Администратор', NULL, 'N;'),
+('authenticated', 2, 'Пользователь', NULL, 'N;'),
+('developer', 2, 'Разработчик', NULL, 'N;');
+
+INSERT INTO `{{admin_auth_item_child}}` (`parent`, `child`) VALUES
+('developer', 'admin');
+
+INSERT INTO `{{admin_user}}` (`username`, `password`, `salt`, `name`) VALUES
+('admin', 'cf373489fa2425711ba9526f204251bf1b30da3d', 'uqLt5vcZ2V', 'Администратор'),
+('developer', '20239caa272a13eb425afb4b5f4d6faaab198765', 'mbnjdHReOf', 'Разработчик');
+
+INSERT INTO `{{admin_auth_assignment}}` (`itemname`, `userid`, `bizrule`, `data`) VALUES
+('admin', 'admin', NULL, 'N;'),
+('developer', 'developer', NULL, 'N;');
+
+ALTER TABLE `{{admin_auth_assignment}}`
+  ADD CONSTRAINT `{{admin_auth_assignment}}_ibfk_1` FOREIGN KEY (`itemname`) REFERENCES `{{admin_auth_item}}` (`name`) ON DELETE CASCADE ON UPDATE CASCADE;
+
+ALTER TABLE `{{admin_auth_item_child}}`
+  ADD CONSTRAINT `{{admin_auth_item_child}}_ibfk_1` FOREIGN KEY (`parent`) REFERENCES `{{admin_auth_item}}` (`name`) ON DELETE CASCADE ON UPDATE CASCADE,
+  ADD CONSTRAINT `{{admin_auth_item_child}}_ibfk_2` FOREIGN KEY (`child`) REFERENCES `{{admin_auth_item}}` (`name`) ON DELETE CASCADE ON UPDATE CASCADE;
+
+CREATE TABLE IF NOT EXISTS `{{static_page}}` (
+  `section` varchar(64) NOT NULL,
+  `key` varchar(64) NOT NULL,
+  `name` varchar(256) DEFAULT NULL,
+  `data` longtext,
+  `title` varchar(512) DEFAULT NULL,
+  `description` text,
+  `keywords` text,
+  PRIMARY KEY (`section`,`key`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+SQL
+        );
+    }
+
+    public function down()
+    {
+        $this->execute(<<<SQL
+DROP TABLE IF EXISTS `{{admin_auth_assignment}}`;
+DROP TABLE IF EXISTS `{{admin_auth_item_child}}`;
+DROP TABLE IF EXISTS `{{admin_auth_item}}`;
+DROP TABLE IF EXISTS `{{admin_auth_item}}`;
+DROP TABLE IF EXISTS `{{admin_user}}`;
+DROP TABLE IF EXISTS `{{static_page}}`;
+SQL
+        );
+        return true;
+    }
+
+    /*
+    // Use safeUp/safeDown to do migration with transaction
+    public function safeUp()
+    {
+    }
+
+    public function safeDown()
+    {
+    }
+    */
+}

app/migrations/m130123_200915_gallery.php

+<?php
+
+class m130123_200915_gallery extends CDbMigration
+{
+    public function up()
+    {
+        //return true;
+        $this->execute(<<<SQL
+CREATE TABLE IF NOT EXISTS `{{gallery}}` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `versions_data` text NOT NULL,
+  `name` tinyint(1) NOT NULL DEFAULT '1',
+  `description` tinyint(1) NOT NULL DEFAULT '1',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB  DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `{{gallery_photo}}` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `gallery_id` int(11) NOT NULL,
+  `rank` int(11) NOT NULL DEFAULT '0',
+  `name` varchar(512) NOT NULL,
+  `description` text NOT NULL,
+  `file_name` varchar(128) NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `fk_gallery_photo_gallery1_idx` (`gallery_id`)
+) ENGINE=InnoDB  DEFAULT CHARSET=utf8;
+
+ALTER TABLE `{{gallery_photo}}`
+  ADD CONSTRAINT `fk_{{gallery_photo}}_{{gallery}}1` FOREIGN KEY (`gallery_id`) REFERENCES `{{gallery}}` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION;
+SQL
+        );
+    }
+
+    public function down()
+    {
+        $this->execute(<<<SQL
+DROP TABLE IF EXISTS `{{gallery_photo}}`;
+DROP TABLE IF EXISTS `{{gallery}}`;
+SQL
+        );
+        return true;
+    }
+
+    /*
+    // Use safeUp/safeDown to do migration with transaction
+    public function safeUp()
+    {
+    }
+
+    public function safeDown()
+    {
+    }
+    */
+}

app/models/ContactForm.php

+<?php
+
+/**
+ * ContactForm class.
+ * ContactForm is the data structure for keeping
+ * contact form data. It is used by the 'contact' action of 'SiteController'.
+ */
+class ContactForm extends CFormModel
+{
+    public $name;
+    public $email;
+    public $subject;
+    public $body;
+    public $verifyCode;
+
+    /**
+     * Declares the validation rules.
+     */
+    public function rules()
+    {
+        return array(
+            // name, email, subject and body are required
+            array('name, email, subject, body', 'required'),
+            // email has to be a valid email address
+            array('email', 'email'),
+            // verifyCode needs to be entered correctly
+            array('verifyCode', 'captcha', 'allowEmpty' => !CCaptcha::checkRequirements()),
+        );
+    }
+
+    /**
+     * Declares customized attribute labels.
+     * If not declared here, an attribute would have a label that is
+     * the same as its name with the first letter in upper case.
+     */
+    public function attributeLabels()
+    {
+        return array(
+            'verifyCode' => 'Verification Code',
+        );
+    }
+}

app/models/LoginForm.php

+<?php
+
+/**
+ * LoginForm class.
+ * LoginForm is the data structure for keeping
+ * user login form data. It is used by the 'login' action of 'SiteController'.
+ */
+class LoginForm extends CFormModel
+{
+    public $username;
+    public $password;
+    public $rememberMe;
+
+    private $_identity;
+
+    /**
+     * Declares the validation rules.
+     * The rules state that username and password are required,
+     * and password needs to be authenticated.
+     */
+    public function rules()
+    {
+        return array(
+            // username and password are required
+            array('username, password', 'required'),
+            // rememberMe needs to be a boolean
+            array('rememberMe', 'boolean'),
+            // password needs to be authenticated
+            array('password', 'authenticate'),
+        );
+    }
+
+    /**
+     * Declares attribute labels.
+     */
+    public function attributeLabels()
+    {
+        return array(
+            'rememberMe' => 'Remember me next time',
+        );
+    }
+
+    /**
+     * Authenticates the password.
+     * This is the 'authenticate' validator as declared in rules().
+     */
+    public function authenticate($attribute, $params)
+    {
+        if (!$this->hasErrors()) {
+            $this->_identity = new UserIdentity($this->username, $this->password);
+            if (!$this->_identity->authenticate())
+                $this->addError('password', 'Incorrect username or password.');
+        }
+    }
+
+    /**
+     * Logs in the user using the given username and password in the model.
+     * @return boolean whether login is successful
+     */
+    public function login()
+    {
+        if ($this->_identity === null) {
+            $this->_identity = new UserIdentity($this->username, $this->password);
+            $this->_identity->authenticate();
+        }
+        if ($this->_identity->errorCode === UserIdentity::ERROR_NONE) {
+            $duration = $this->rememberMe ? 3600 * 24 * 30 : 0; // 30 days
+            Yii::app()->user->login($this->_identity, $duration);
+            return true;
+        } else
+            return false;
+    }
+}

app/models/StaticPage.php

+<?php
+
+/**
+ * This is the model class for table "static_page".
+ *
+ * The followings are the available columns in table 'static_page':
+ * @property string $section
+ * @property string $key
+ * //property string $link
+ * @property string $name
+ * @property string $data
+ * @property string $title
+ * @property string $description
+ * @property string $keywords
+// * @property string $last_change
+// * @property double $priority
+// * @property string $change_freq
+ */
+class StaticPage extends CActiveRecord
+{
+    /**
+     * Returns the static model of the specified AR class.
+     * @param string $className active record class name.
+     * @return StaticPage the static model class
+     */
+    public static function model($className = __CLASS__)
+    {
+        return parent::model($className);
+    }
+
+    /**
+     * @return string the associated database table name
+     */
+    public function tableName()
+    {
+        return '{{static_page}}';
+    }
+
+    /**
+     * @return array validation rules for model attributes.
+     */
+    public function rules()
+    {
+        // NOTE: you should only define rules for those attributes that
+        // will receive user inputs.
+        return array(
+            array('section, key' /*link*/, 'required'),
+            //array('priority', 'numerical'),
+            array('section, key' /*link*/, 'length', 'max' => 64),
+            array('name', 'length', 'max' => 256),
+            array('title', 'length', 'max' => 512),
+            array('data, description, keywords' /* last_change, change_freq */, 'safe'),
+            // The following rule is used by search().
+            // Please remove those attributes that should not be searched.
+            array('section, key, name, data, title, description, keywords' /*last_change, priority, change_freq, link*/, 'safe', 'on' => 'search'),
+        );
+    }
+
+    /**
+     * @return array relational rules.
+     */
+    public function relations()
+    {
+        // NOTE: you may need to adjust the relation name and the related
+        // class name for the relations automatically generated below.
+        return array();
+    }
+
+    /**
+     * @return array customized attribute labels (name=>label)
+     */
+    public function attributeLabels()
+    {
+        return array(
+            'section' => 'Раздел сайта куда относится страница',
+            'key' => 'Уникальный ключ страницы в разделе',
+            // 'link' => 'Плевдоным страницы',
+            'name' => 'Название',
+            'data' => 'Data',
+            'title' => 'Заголовок',
+            'description' => 'Описание',
+            'keywords' => 'Ключевые слова',
+            // 'last_change' => 'Дата последнего редактирования',
+            // 'priority' => 'Относительный приоритет страницы',
+            // 'change_freq' => 'Частота редактырования',
+        );
+    }
+
+    /**
+     * Retrieves a list of models based on the current search/filter conditions.
+     * @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.
+     */
+    public function search()
+    {
+        // Warning: Please modify the following code to remove attributes that
+        // should not be searched.
+
+        $criteria = new CDbCriteria;
+
+        $criteria->compare('section', $this->section, true);
+        $criteria->compare('key', $this->key, true);
+        // $criteria->compare('link', $this->link, true);
+        $criteria->compare('name', $this->name, true);
+        $criteria->compare('data', $this->data, true);
+
+        $criteria->compare('title', $this->title, true);
+        $criteria->compare('description', $this->description, true);
+        $criteria->compare('keywords', $this->keywords, true);
+        //  $criteria->compare('last_change', $this->last_change, true);
+        //  $criteria->compare('priority', $this->priority);
+        //  $criteria->compare('change_freq', $this->change_freq, true);
+
+        return new CActiveDataProvider($this, array(
+            'criteria' => $criteria,
+        ));
+    }
+
+    private $_data = null;
+
+    /**
+     * @return array
+     */
+    public function getDataAttributes()
+    {
+        if (!isset($this->_data)) {
+            $this->_data = unserialize($this->data);
+        }
+        if (!is_array($this->_data)) $this->_data = array();
+        return $this->_data;
+    }
+
+    /**
+     * @param array $data
+     */
+    public function setDataAttributes($data)
+    {
+        $this->_data = $data;
+        $this->data = serialize($data);
+    }
+}

app/modules/manager/ManagerModule.php

+<?php
+
+Yii::import('admin.AdminExtModule');
+class ManagerModule extends AdminExtModule
+{
+    public function init()
+    {
+        // this method is called when the module is being created
+        // you may place code here to customize the module or the application
+
+        // import the module-level models and components
+        $this->setImport(array(//			'manager.models.*',
+//			'manager.components.*',
+        ));
+        $this->setLayoutPath(Yii::app()->getModule('admin')->getLayoutPath());
+    }
+    public static function menuConfig()
+    {
+        return array(
+            'site' => array(
+                'label' => 'Site',
+                'items' => array(
+                    array(
+                        'label' => 'Site Management',
+                        'url' => array('default/index'),
+                    ),
+                )
+            )
+        );
+    }
+}

app/modules/manager/controllers/DefaultController.php

+<?php
+
+class DefaultController extends AdminController
+{
+
+    public function actionIndex()
+    {
+        $this->render('index');
+    }
+}

app/modules/manager/views/default/index.php

+<div class="container-fluid">
+    <div class="row-fluid">
+        <div class="span12">
+            <h1><?php echo $this->uniqueId . '/' . $this->action->id; ?></h1>
+
+            <p>
+                This is the view content for action "<?php echo $this->action->id; ?>".
+                The action belongs to the controller "<?php echo get_class($this); ?>"
+                in the "<?php echo $this->module->id; ?>" module.
+            </p>
+
+            <p>
+                You may customize this page by editing <tt><?php echo __FILE__; ?></tt>
+            </p>
+        </div>
+    </div>
+</div>

app/runtime/.empty

Empty file added.

app/views/layouts/column1.php

+<?php $this->beginContent('//layouts/main'); ?>
+<div id="content">
+    <?php echo $content; ?>
+</div><!-- content -->
+<?php $this->endContent(); ?>

app/views/layouts/column2.php

+<?php $this->beginContent('//layouts/main'); ?>
+<div class="span-19">
+    <div id="content">
+        <?php echo $content; ?>
+    </div>
+    <!-- content -->
+</div>
+<div class="span-5 last">
+    <div id="sidebar">
+        <?php
+        $this->beginWidget('zii.widgets.CPortlet', array(
+            'title' => 'Operations',
+        ));
+        $this->widget('zii.widgets.CMenu', array(
+            'items' => $this->menu,
+            'htmlOptions' => array('class' => 'operations'),
+        ));
+        $this->endWidget();
+        ?>
+    </div>
+    <!-- sidebar -->
+</div>
+<?php $this->endContent(); ?>

app/views/layouts/main.php

+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+    <meta name="language" content="en"/>
+
+    <!-- blueprint CSS framework -->
+    <link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->request->baseUrl; ?>/css/screen.css"
+          media="screen, projection"/>
+    <link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->request->baseUrl; ?>/css/print.css"
+          media="print"/>
+    <!--[if lt IE 8]>
+    <link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->request->baseUrl; ?>/css/ie.css"
+          media="screen, projection"/>
+    <![endif]-->
+
+    <link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->request->baseUrl; ?>/css/main.css"/>
+    <link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->request->baseUrl; ?>/css/form.css"/>
+
+    <title><?php echo CHtml::encode($this->pageTitle); ?></title>
+
+    <?php
+
+    if (!empty($this->keywords))
+        echo '<meta name="keywords" content="', CHtml::encode($this->keywords), '"/>', "\n";
+
+    if (!empty($this->description))
+        echo '<meta name="description" content="', CHtml::encode($this->description), '"/>', "\n";
+    ?>
+</head>
+
+<body>
+
+<div class="container" id="page">
+
+    <div id="header">
+        <div id="logo"><?php echo CHtml::encode(Yii::app()->name); ?></div>
+    </div>
+    <!-- header -->
+
+    <div id="mainmenu">
+        <?php $this->widget('zii.widgets.CMenu', array(
+        'items' => array(
+            array('label' => 'Home', 'url' => array('/site/index')),
+            array('label' => 'About', 'url' => array('/site/page', 'view' => 'about')),
+            array('label' => 'Contact', 'url' => array('/site/contact')),
+            array('label' => 'Login', 'url' => array('/site/login'), 'visible' => Yii::app()->user->isGuest),
+            array('label' => 'Logout (' . Yii::app()->user->name . ')', 'url' => array('/site/logout'), 'visible' => !Yii::app()->user->isGuest)
+        ),
+    )); ?>
+    </div>
+    <!-- mainmenu -->
+    <?php if (isset($this->breadcrumbs)): ?>
+        <?php $this->widget('zii.widgets.CBreadcrumbs', array(
+        'links' => $this->breadcrumbs,
+    )); ?><!-- breadcrumbs -->
+    <?php endif?>
+
+    <?php echo $content; ?>
+
+    <div class="clear"></div>
+
+    <div id="footer">
+        Copyright &copy; <?php echo date('Y'); ?> by My Company.<br/>
+        All Rights Reserved.<br/>
+        <?php echo Yii::powered(); ?>
+    </div>
+    <!-- footer -->
+
+</div>
+<!-- page -->
+
+</body>
+</html>

app/views/site/contact.php

+<?php
+$this->pageTitle = Yii::app()->name . ' - Contact Us';
+$this->breadcrumbs = array(
+    'Contact',
+);
+?>
+
+<h1>Contact Us</h1>
+
+<?php if (Yii::app()->user->hasFlash('contact')): ?>
+
+<div class="flash-success">
+    <?php echo Yii::app()->user->getFlash('contact'); ?>
+</div>
+
+<?php else: ?>
+
+<p>
+    If you have business inquiries or other questions, please fill out the following form to contact us. Thank you.
+</p>
+
+<div class="form">
+
+    <?php $form = $this->beginWidget('CActiveForm', array(
+    'id' => 'contact-form',
+    'enableClientValidation' => true,
+    'clientOptions' => array(
+        'validateOnSubmit' => true,
+    ),
+)); ?>
+
+    <p class="note">Fields with <span class="required">*</span> are required.</p>
+
+    <?php echo $form->errorSummary($model); ?>
+
+    <div class="row">
+        <?php echo $form->labelEx($model, 'name'); ?>
+        <?php echo $form->textField($model, 'name'); ?>
+        <?php echo $form->error($model, 'name'); ?>
+    </div>
+
+    <div class="row">
+        <?php echo $form->labelEx($model, 'email'); ?>
+        <?php echo $form->textField($model, 'email'); ?>
+        <?php echo $form->error($model, 'email'); ?>
+    </div>
+
+    <div class="row">
+        <?php echo $form->labelEx($model, 'subject'); ?>
+        <?php echo $form->textField($model, 'subject', array('size' => 60, 'maxlength' => 128)); ?>
+        <?php echo $form->error($model, 'subject'); ?>
+    </div>
+
+    <div class="row">
+        <?php echo $form->labelEx($model, 'body'); ?>
+        <?php echo $form->textArea($model, 'body', array('rows' => 6, 'cols' => 50)); ?>
+        <?php echo $form->error($model, 'body'); ?>
+    </div>
+
+    <?php if (CCaptcha::checkRequirements()): ?>
+    <div class="row">
+        <?php echo $form->labelEx($model, 'verifyCode'); ?>
+        <div>
+            <?php $this->widget('CCaptcha'); ?>
+            <?php echo $form->textField($model, 'verifyCode'); ?>
+        </div>
+        <div class="hint">Please enter the letters as they are shown in the image above.
+            <br/>Letters are not case-sensitive.
+        </div>
+        <?php echo $form->error($model, 'verifyCode'); ?>
+    </div>
+    <?php endif; ?>
+
+    <div class="row buttons">
+        <?php echo CHtml::submitButton('Submit'); ?>
+    </div>
+
+    <?php $this->endWidget(); ?>
+
+</div><!-- form -->
+
+<?php endif; ?>

app/views/site/error.php

+<?php
+$this->pageTitle = Yii::app()->name . ' - Error';
+$this->breadcrumbs = array(
+    'Error',
+);
+?>
+
+<h2>Error <?php echo $code; ?></h2>
+
+<div class="error">
+    <?php echo CHtml::encode($message); ?>
+</div>

app/views/site/index.php

+<?php $this->pageTitle = Yii::app()->name; ?>
+
+<h1>Welcome to <i><?php echo CHtml::encode(Yii::app()->name); ?></i></h1>
+
+<p>Congratulations! You have successfully created your Yii application.</p>
+
+<p>You may change the content of this page by modifying the following two files:</p>
+<ul>
+    <li>View file: <tt><?php echo __FILE__; ?></tt></li>
+    <li>Layout file: <tt><?php echo $this->getLayoutFile('main'); ?></tt></li>
+</ul>
+
+<p>For more details on how to further develop this application, please read
+    the <a href="http://www.yiiframework.com/doc/">documentation</a>.
+    Feel free to ask in the <a href="http://www.yiiframework.com/forum/">forum</a>,
+    should you have any questions.</p>
+
+<?php
+$images = array();
+foreach ($gallery = Gallery::model()->findByPk(2)->galleryPhotos as $photo) {
+    /** @var $photo GalleryPhoto */
+    $images[] = array(
+        'small' => $photo->getUrl('small'),
+        'large' => $photo->getUrl('medium'),
+        'big' => $photo->getUrl(''),
+    );
+}
+
+?>

app/views/site/login.php

+<?php
+$this->pageTitle = Yii::app()->name . ' - Login';
+$this->breadcrumbs = array(
+    'Login',
+);
+?>
+
+<h1>Login</h1>
+
+<p>Please fill out the following form with your login credentials:</p>
+
+<div class="form">
+    <?php $form = $this->beginWidget('CActiveForm', array(
+    'id' => 'login-form',
+    'enableClientValidation' => true,
+    'clientOptions' => array(
+        'validateOnSubmit' => true,
+    ),
+)); ?>
+
+    <p class="note">Fields with <span class="required">*</span> are required.</p>
+
+    <div class="row">
+        <?php echo $form->labelEx($model, 'username'); ?>
+        <?php echo $form->textField($model, 'username'); ?>
+        <?php echo $form->error($model, 'username'); ?>
+    </div>
+
+    <div class="row">
+        <?php echo $form->labelEx($model, 'password'); ?>
+        <?php echo $form->passwordField($model, 'password'); ?>
+        <?php echo $form->error($model, 'password'); ?>
+        <p class="hint">
+            Hint: You may login with <tt>demo/demo</tt> or <tt>admin/admin</tt>.
+        </p>
+    </div>
+
+    <div class="row rememberMe">
+        <?php echo $form->checkBox($model, 'rememberMe'); ?>
+        <?php echo $form->label($model, 'rememberMe'); ?>
+        <?php echo $form->error($model, 'rememberMe'); ?>
+    </div>
+
+    <div class="row buttons">
+        <?php echo CHtml::submitButton('Login'); ?>
+    </div>
+
+    <?php $this->endWidget(); ?>
+</div><!-- form -->

app/views/site/pages/about.php

+<?php
+$this->pageTitle = Yii::app()->name . ' - About';
+$this->breadcrumbs = array(
+    'About',
+);
+?>
+<h1>About</h1>
+
+<p>This is a "static" page. You may change the content of this page
+    by updating the file <tt><?php echo __FILE__; ?></tt>.</p>
+#!/usr/bin/env php
+<?php
+
+require_once(dirname(__FILE__).'/yiic.php');
+@echo off
+
+rem -------------------------------------------------------------
+rem  Yii command line script for Windows.
+rem  This is the bootstrap script for running yiic on Windows.
+rem -------------------------------------------------------------
+
+@setlocal
+
+set BIN_PATH=%~dp0
+
+if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
+
+"%PHP_COMMAND%" "%BIN_PATH%yiic.php" %*
+
+@endlocal
+<?php
+
+// change the following paths if necessary
+$yiic=dirname(__FILE__).'/../../../yii-1.1.13/framework/yiic.php';
+$config=dirname(__FILE__).'/config/console.php';
+
+require_once($yiic);
 
 // change the following paths if necessary
 $yii=dirname(__FILE__).'/../../yii-1.1.13/framework/yii.php';
-$config=dirname(__FILE__).'/protected/config/web.php';
+$config=dirname(__FILE__).'/../app/config/web.php';
 
 // remove the following lines when in production mode
 defined('YII_DEBUG') or define('YII_DEBUG',true);

www/protected/.htaccess

-deny from all

www/protected/components/Controller.php

-<?php
-/**
- * Controller is the customized base controller class.
- * All controller classes for this application should extend from this base class.
- */
-class Controller extends CController
-{
-    /**
-     * @var string the default layout for the controller view. Defaults to '//layouts/column1',
-     * meaning using a single column layout. See 'protected/views/layouts/column1.php'.
-     */
-    public $layout = '//layouts/column1';
-    /**
-     * @var array context menu items. This property will be assigned to {@link CMenu::items}.
-     */
-    public $menu = array();
-    /**
-     * @var array the breadcrumbs of the current page. The value of this property will
-     * be assigned to {@link CBreadcrumbs::links}. Please refer to {@link CBreadcrumbs::links}
-     * for more details on how to specify this property.
-     */
-    public $breadcrumbs = array();
-
-
-    public $keywords = null;
-    public $description = null;
-    public $pageName;
-    private $_pageTitle;
-
-    public function setSEOParams($title = null, $keywords = null, $description = null)
-    {
-        if ($title) $this->pageTitle = $title;
-        if ($keywords) $this->keywords = $keywords;
-        if ($description) $this->description = $description;
-    }
-
-    /**
-     * @return string the page title. Defaults to the controller name and the action name.
-     */
-    public function getPageTitle()
-    {
-        if ($this->_pageTitle !== null)
-            return $this->_pageTitle;
-        else {
-            if (!empty($this->pageName))
-                $this->_pageTitle = $this->pageName . " | " . Yii::app()->name;
-            else
-                $this->_pageTitle = Yii::app()->name;
-            return $this->_pageTitle;
-        }
-    }
-
-    /**
-     * @param string $value the page title.
-     */
-    public function setPageTitle($value)
-    {
-        $this->_pageTitle = $value;
-    }
-}

www/protected/components/FormattedDateFieldBehavior.php

-<?php
-
-/**
- * Behavior to add custom formatted date time field to model.
- *
- * @author Bogdan Savluk <savluk.bogdan@gmail.com>
- *
- * Example configuration:
- *
- *   'dateRuBehavior' => array(
- *        'class' => 'FormattedDateFieldBehavior',
- *        'fieldName' => 'dateRu',
- *        'fieldFormat' => 'dd.MM.yyyy hh:mm:ss',
- *        'modelFieldName' => 'date',
- *        'modelFieldFormat' => 'yyyy-MM-dd hh:mm:ss',
- *   )
- */
-class FormattedDateFieldBehavior extends CActiveRecordBehavior
-{
-
-    public $fieldName;
-    public $fieldFormat;
-
-    public $modelFieldName;
-    public $modelFieldFormat;
-
-    public function hasProperty($name)
-    {
-        if ($name == $this->fieldName) return true;
-        return parent::hasProperty($name);
-    }
-
-    public function canGetProperty($name)
-    {
-        if ($name == $this->fieldName) return true;
-        return parent::canGetProperty($name);
-    }
-
-    public function canSetProperty($name)
-    {
-        if ($name == $this->fieldName) return true;
-        return parent::canSetProperty($name);
-    }
-
-    public function __get($name)
-    {
-        if ($name == $this->fieldName) {
-            return $this->getField();
-        }
-        return parent::__get($name);
-    }
-
-    public function __set($name, $value)
-    {
-        if ($name == $this->fieldName) {
-            $this->setField($value);
-        } else {
-            parent::__set($name, $value);
-        }
-    }
-
-    private function getField()
-    {
-        $date = $this->getOwner()->{$this->modelFieldName};
-        if ($date !== null)
-            $timestamp = CDateTimeParser::parse($date, $this->modelFieldFormat);
-        else
-            $timestamp = time();
-        return Yii::app()->dateFormatter->format($this->fieldFormat, $timestamp);
-    }
-
-    private function setField($value)
-    {
-        $timestamp = CDateTimeParser::parse($value, $this->fieldFormat);
-        $this->getOwner()->{$this->modelFieldName} = Yii::app()->dateFormatter->format($this->modelFieldFormat, $timestamp);
-    }
-}

www/protected/components/HtmlUtils.php

-<?php
-class HtmlUtils
-{
-    /**
-     * Truncates text.
-     *