Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'master' of github.com:yiisoft/yii into 1980-better

Conflicts:
	CHANGELOG
  • Loading branch information...
commit 64a6aa85f8ef1dce3644d532e1e693fed2e9e1a2 2 parents de416ba + 70190e3
@resurtm resurtm authored
Showing with 3,248 additions and 408 deletions.
  1. +1 −4 .travis.yml
  2. +37 −0 CHANGELOG
  3. +1 −1  LICENSE
  4. +16 −0 UPGRADE
  5. +1 −1  build/build
  6. +1 −1  build/build.bat
  7. +1 −1  build/build.xml
  8. +1 −1  build/commands/ApiCommand.php
  9. +1 −1  build/commands/AutoloadCommand.php
  10. +2 −2 build/commands/CldrCommand.php
  11. +2 −2 build/commands/LiteCommand.php
  12. +1 −1  build/commands/Utf8Command.php
  13. +1 −1  build/commands/api/ApiModel.php
  14. +1 −1  build/commands/api/layouts/main.php
  15. +1 −1  build/commands/blog/title.tex
  16. +1 −1  build/commands/guide/title.tex
  17. +1 −1  build/commands/lite/css/form.css
  18. +5 −6 build/generate_accessors_phpdoc.php
  19. +1 −1  build/tasks/YiiInitTask.php
  20. +1 −1  build/tasks/YiiPearTask.php
  21. +57 −0 composer.json
  22. +1 −1  demos/blog/css/form.css
  23. +1 −1  demos/hangman/protected/controllers/GameController.php
  24. +1 −1  docs/blog/ja/prototype.scaffold.txt
  25. +1 −1  docs/blog/prototype.scaffold.txt
  26. +1 −1  docs/blog/ru/prototype.scaffold.txt
  27. +104 −0 docs/blog/uk/comment.admin.txt
  28. +195 −0 docs/blog/uk/comment.create.txt
  29. +80 −0 docs/blog/uk/comment.model.txt
  30. +139 −0 docs/blog/uk/post.admin.txt
  31. +120 −0 docs/blog/uk/post.create.txt
  32. +138 −0 docs/blog/uk/post.display.txt
  33. +254 −0 docs/blog/uk/post.model.txt
  34. +157 −0 docs/blog/uk/prototype.auth.txt
  35. +101 −0 docs/blog/uk/prototype.database.txt
  36. +152 −0 docs/blog/uk/prototype.scaffold.txt
  37. +25 −0 docs/blog/uk/prototype.summary.txt
  38. +53 −0 docs/blog/uk/start.design.txt
  39. +21 −0 docs/blog/uk/start.overview.txt
  40. +26 −0 docs/blog/uk/start.requirements.txt
  41. +104 −0 docs/blog/uk/start.testdrive.txt
  42. +33 −0 docs/blog/uk/toc.txt
  43. +1 −1  docs/guide/form.model.txt
  44. +1 −1  docs/guide/pl/index.txt
  45. +265 −0 docs/guide/pt_br/extension.create.txt
  46. +98 −0 docs/guide/pt_br/extension.integration.txt
  47. +3 −3 docs/guide/pt_br/index.txt
  48. +112 −0 docs/guide/pt_br/test.fixture.txt
  49. +104 −0 docs/guide/pt_br/test.functional.txt
  50. +157 −0 docs/guide/pt_br/test.overview.txt
  51. +120 −0 docs/guide/pt_br/test.unit.txt
  52. +1 −1  docs/guide/pt_br/toc.txt
  53. +67 −0 docs/guide/pt_br/upgrade.txt
  54. +6 −0 docs/guide/ru/changes.txt
  55. +1 −1  docs/guide/ru/database.arr.txt
  56. +1 −1  docs/guide/ru/form.builder.txt
  57. +2 −2 docs/guide/ru/topics.theming.txt
  58. +3 −3 docs/guide/uk/basics.component.txt
  59. +6 −3 docs/guide/uk/basics.view.txt
  60. +6 −1 docs/guide/uk/changes.txt
  61. +3 −3 docs/guide/uk/database.ar.txt
  62. +4 −2 docs/guide/uk/database.arr.txt
  63. +2 −2 docs/guide/uk/form.builder.txt
  64. +2 −4 docs/guide/uk/index.txt
  65. +3 −3 docs/guide/uk/topics.logging.txt
  66. +3 −3 docs/guide/uk/topics.theming.txt
  67. +1 −1  docs/guide/zh_cn/index.txt
  68. +1 −1  docs/guide/zh_tw/index.txt
  69. +1 −0  docs/viewer/controllers/BlogController.php
  70. +1 −1  framework/YiiBase.php
  71. +2 −1  framework/base/CApplication.php
  72. +1 −1  framework/base/CApplicationComponent.php
  73. +1 −1  framework/base/CBehavior.php
  74. +4 −1 framework/base/CComponent.php
  75. +1 −1  framework/base/CErrorEvent.php
  76. +1 −1  framework/base/CErrorHandler.php
  77. +1 −1  framework/base/CException.php
  78. +1 −1  framework/base/CExceptionEvent.php
  79. +1 −1  framework/base/CHttpException.php
  80. +2 −1  framework/base/CModel.php
  81. +1 −1  framework/base/CModelBehavior.php
  82. +1 −1  framework/base/CModelEvent.php
  83. +1 −1  framework/base/CModule.php
  84. +2 −1  framework/base/CSecurityManager.php
  85. +1 −1  framework/base/CStatePersister.php
  86. +1 −1  framework/base/interfaces.php
  87. +1 −1  framework/caching/CApcCache.php
  88. +1 −1  framework/caching/CCache.php
  89. +1 −1  framework/caching/CDbCache.php
  90. +1 −1  framework/caching/CDummyCache.php
  91. +1 −1  framework/caching/CEAcceleratorCache.php
  92. +1 −1  framework/caching/CFileCache.php
  93. +1 −1  framework/caching/CMemCache.php
  94. +1 −1  framework/caching/CWinCache.php
  95. +1 −1  framework/caching/CXCache.php
  96. +1 −1  framework/caching/CZendDataCache.php
  97. +1 −1  framework/caching/dependencies/CCacheDependency.php
  98. +2 −2 framework/caching/dependencies/CChainedCacheDependency.php
  99. +2 −1  framework/caching/dependencies/CDbCacheDependency.php
  100. +3 −1 framework/caching/dependencies/CDirectoryCacheDependency.php
  101. +1 −1  framework/caching/dependencies/CExpressionDependency.php
  102. +2 −1  framework/caching/dependencies/CFileCacheDependency.php
  103. +2 −1  framework/caching/dependencies/CGlobalStateCacheDependency.php
  104. +3 −3 framework/cli/commands/MessageCommand.php
  105. +1 −1  framework/cli/commands/MigrateCommand.php
  106. +1 −1  framework/cli/commands/ShellCommand.php
  107. +1 −1  framework/cli/commands/WebAppCommand.php
  108. +2 −2 framework/cli/commands/shell/ControllerCommand.php
  109. +2 −2 framework/cli/commands/shell/CrudCommand.php
  110. +2 −2 framework/cli/commands/shell/FormCommand.php
  111. +2 −2 framework/cli/commands/shell/HelpCommand.php
  112. +12 −11 framework/cli/commands/shell/ModelCommand.php
  113. +2 −2 framework/cli/commands/shell/ModuleCommand.php
  114. +1 −1  framework/cli/views/shell/crud/admin.php
  115. +1 −1  framework/cli/views/webapp/css/form.css
  116. +3 −3 framework/cli/views/webapp/hg-hgignore
  117. +6 −6 framework/collections/CAttributeCollection.php
  118. +1 −1  framework/collections/CConfiguration.php
  119. +1 −1  framework/collections/CList.php
  120. +2 −2 framework/collections/CListIterator.php
  121. +4 −4 framework/collections/CMap.php
  122. +2 −2 framework/collections/CMapIterator.php
  123. +2 −2 framework/collections/CQueue.php
  124. +2 −2 framework/collections/CQueueIterator.php
  125. +1 −1  framework/collections/CStack.php
  126. +2 −2 framework/collections/CStackIterator.php
  127. +1 −1  framework/collections/CTypedList.php
  128. +1 −1  framework/collections/CTypedMap.php
  129. +1 −1  framework/console/CConsoleApplication.php
  130. +8 −1 framework/console/CConsoleCommand.php
  131. +3 −3 framework/console/CConsoleCommandBehavior.php
  132. +1 −1  framework/console/CConsoleCommandEvent.php
  133. +1 −1  framework/console/CConsoleCommandRunner.php
  134. +1 −1  framework/console/CHelpCommand.php
  135. +11 −7 framework/db/CDbCommand.php
  136. +5 −3 framework/db/CDbConnection.php
  137. +2 −2 framework/db/CDbDataReader.php
  138. +1 −1  framework/db/CDbException.php
  139. +3 −2 framework/db/CDbMigration.php
  140. +1 −1  framework/db/CDbTransaction.php
  141. +36 −18 framework/db/ar/CActiveFinder.php
  142. +14 −11 framework/db/ar/CActiveRecord.php
  143. +1 −1  framework/db/ar/CActiveRecordBehavior.php
  144. +1 −1  framework/db/schema/CDbColumnSchema.php
  145. +7 −2 framework/db/schema/CDbCommandBuilder.php
  146. +1 −1  framework/db/schema/CDbCriteria.php
  147. +1 −1  framework/db/schema/CDbExpression.php
  148. +2 −1  framework/db/schema/CDbSchema.php
  149. +1 −1  framework/db/schema/CDbTableSchema.php
  150. +1 −1  framework/db/schema/mssql/CMssqlColumnSchema.php
  151. +2 −1  framework/db/schema/mssql/CMssqlCommandBuilder.php
  152. +1 −1  framework/db/schema/mssql/CMssqlPdoAdapter.php
  153. +5 −4 framework/db/schema/mssql/CMssqlSchema.php
  154. +1 −1  framework/db/schema/mssql/CMssqlSqlsrvPdoAdapter.php
  155. +1 −1  framework/db/schema/mssql/CMssqlTableSchema.php
  156. +1 −1  framework/db/schema/mysql/CMysqlColumnSchema.php
  157. +8 −2 framework/db/schema/mysql/CMysqlCommandBuilder.php
  158. +4 −2 framework/db/schema/mysql/CMysqlSchema.php
  159. +1 −1  framework/db/schema/mysql/CMysqlTableSchema.php
  160. +1 −1  framework/db/schema/oci/COciColumnSchema.php
  161. +1 −1  framework/db/schema/oci/COciCommandBuilder.php
  162. +1 −1  framework/db/schema/oci/COciSchema.php
  163. +1 −1  framework/db/schema/oci/COciTableSchema.php
  164. +1 −1  framework/db/schema/pgsql/CPgsqlColumnSchema.php
  165. +16 −16 framework/db/schema/pgsql/CPgsqlSchema.php
  166. +1 −1  framework/db/schema/pgsql/CPgsqlTableSchema.php
  167. +1 −1  framework/db/schema/sqlite/CSqliteColumnSchema.php
  168. +1 −1  framework/db/schema/sqlite/CSqliteCommandBuilder.php
  169. +1 −1  framework/db/schema/sqlite/CSqliteSchema.php
  170. +1 −1  framework/gii/CCodeFile.php
  171. +1 −1  framework/gii/CCodeForm.php
  172. +1 −1  framework/gii/CCodeGenerator.php
  173. +4 −2 framework/gii/CCodeModel.php
  174. +2 −1  framework/gii/GiiModule.php
  175. +1 −1  framework/i18n/CChoiceFormat.php
  176. +22 −6 framework/i18n/CDateFormatter.php
  177. +2 −1  framework/i18n/CDbMessageSource.php
  178. +2 −2 framework/i18n/CGettextMessageSource.php
  179. +5 −3 framework/i18n/CLocale.php
  180. +1 −1  framework/i18n/CMessageSource.php
  181. +16 −7 framework/i18n/CNumberFormatter.php
  182. +1 −1  framework/i18n/CPhpMessageSource.php
  183. +1 −1  framework/i18n/data/aa.php
  184. +1 −1  framework/i18n/data/aa_dj.php
  185. +1 −1  framework/i18n/data/aa_er.php
  186. +1 −1  framework/i18n/data/aa_et.php
  187. +1 −1  framework/i18n/data/af.php
  188. +1 −1  framework/i18n/data/af_na.php
  189. +1 −1  framework/i18n/data/af_za.php
  190. +1 −1  framework/i18n/data/agq.php
  191. +1 −1  framework/i18n/data/agq_cm.php
  192. +1 −1  framework/i18n/data/ak.php
  193. +1 −1  framework/i18n/data/ak_gh.php
  194. +1 −1  framework/i18n/data/am.php
  195. +1 −1  framework/i18n/data/am_et.php
  196. +1 −1  framework/i18n/data/ar.php
  197. +1 −1  framework/i18n/data/ar_001.php
  198. +1 −1  framework/i18n/data/ar_ae.php
  199. +1 −1  framework/i18n/data/ar_bh.php
  200. +1 −1  framework/i18n/data/ar_dz.php
  201. +1 −1  framework/i18n/data/ar_eg.php
  202. +1 −1  framework/i18n/data/ar_iq.php
  203. +1 −1  framework/i18n/data/ar_jo.php
  204. +1 −1  framework/i18n/data/ar_kw.php
  205. +1 −1  framework/i18n/data/ar_lb.php
  206. +1 −1  framework/i18n/data/ar_ly.php
  207. +1 −1  framework/i18n/data/ar_ma.php
  208. +1 −1  framework/i18n/data/ar_om.php
  209. +1 −1  framework/i18n/data/ar_qa.php
  210. +1 −1  framework/i18n/data/ar_sa.php
  211. +1 −1  framework/i18n/data/ar_sd.php
  212. +1 −1  framework/i18n/data/ar_sy.php
  213. +1 −1  framework/i18n/data/ar_tn.php
  214. +1 −1  framework/i18n/data/ar_ye.php
  215. +1 −1  framework/i18n/data/as.php
  216. +1 −1  framework/i18n/data/as_in.php
  217. +1 −1  framework/i18n/data/asa.php
  218. +1 −1  framework/i18n/data/asa_tz.php
  219. +1 −1  framework/i18n/data/az.php
  220. +1 −1  framework/i18n/data/az_arab.php
  221. +1 −1  framework/i18n/data/az_arab_ir.php
  222. +1 −1  framework/i18n/data/az_az.php
  223. +1 −1  framework/i18n/data/az_cyrl.php
  224. +1 −1  framework/i18n/data/az_cyrl_az.php
  225. +1 −1  framework/i18n/data/az_ir.php
  226. +1 −1  framework/i18n/data/az_latn.php
  227. +1 −1  framework/i18n/data/az_latn_az.php
  228. +1 −1  framework/i18n/data/bas.php
  229. +1 −1  framework/i18n/data/bas_cm.php
  230. +1 −1  framework/i18n/data/be.php
  231. +1 −1  framework/i18n/data/be_by.php
  232. +1 −1  framework/i18n/data/bem.php
  233. +1 −1  framework/i18n/data/bem_zm.php
  234. +1 −1  framework/i18n/data/bez.php
  235. +1 −1  framework/i18n/data/bez_tz.php
  236. +1 −1  framework/i18n/data/bg.php
  237. +1 −1  framework/i18n/data/bg_bg.php
  238. +1 −1  framework/i18n/data/bm.php
  239. +1 −1  framework/i18n/data/bm_ml.php
  240. +1 −1  framework/i18n/data/bn.php
  241. +1 −1  framework/i18n/data/bn_bd.php
  242. +1 −1  framework/i18n/data/bn_in.php
  243. +1 −1  framework/i18n/data/bo.php
  244. +1 −1  framework/i18n/data/bo_cn.php
  245. +1 −1  framework/i18n/data/bo_in.php
  246. +1 −1  framework/i18n/data/br.php
  247. +1 −1  framework/i18n/data/br_fr.php
  248. +1 −1  framework/i18n/data/brx.php
  249. +1 −1  framework/i18n/data/brx_in.php
  250. +1 −1  framework/i18n/data/bs.php
  251. +1 −1  framework/i18n/data/bs_ba.php
  252. +1 −1  framework/i18n/data/byn.php
  253. +1 −1  framework/i18n/data/byn_er.php
  254. +1 −1  framework/i18n/data/ca.php
  255. +1 −1  framework/i18n/data/ca_es.php
  256. +1 −1  framework/i18n/data/cch.php
  257. +1 −1  framework/i18n/data/cch_ng.php
  258. +1 −1  framework/i18n/data/cgg.php
  259. +1 −1  framework/i18n/data/cgg_ug.php
  260. +1 −1  framework/i18n/data/chr.php
  261. +1 −1  framework/i18n/data/chr_us.php
  262. +1 −1  framework/i18n/data/cs.php
  263. +1 −1  framework/i18n/data/cs_cz.php
  264. +1 −1  framework/i18n/data/cy.php
  265. +1 −1  framework/i18n/data/cy_gb.php
  266. +1 −1  framework/i18n/data/da.php
  267. +1 −1  framework/i18n/data/da_dk.php
  268. +1 −1  framework/i18n/data/dav.php
  269. +1 −1  framework/i18n/data/dav_ke.php
  270. +1 −1  framework/i18n/data/de.php
  271. +1 −1  framework/i18n/data/de_at.php
  272. +1 −1  framework/i18n/data/de_be.php
  273. +1 −1  framework/i18n/data/de_ch.php
  274. +1 −1  framework/i18n/data/de_de.php
  275. +1 −1  framework/i18n/data/de_li.php
  276. +1 −1  framework/i18n/data/de_lu.php
  277. +1 −1  framework/i18n/data/dje.php
  278. +1 −1  framework/i18n/data/dje_ne.php
  279. +1 −1  framework/i18n/data/dua.php
  280. +1 −1  framework/i18n/data/dua_cm.php
  281. +1 −1  framework/i18n/data/dv.php
  282. +1 −1  framework/i18n/data/dv_mv.php
  283. +1 −1  framework/i18n/data/dyo.php
  284. +1 −1  framework/i18n/data/dyo_sn.php
  285. +1 −1  framework/i18n/data/dz.php
  286. +1 −1  framework/i18n/data/dz_bt.php
  287. +1 −1  framework/i18n/data/ebu.php
  288. +1 −1  framework/i18n/data/ebu_ke.php
  289. +1 −1  framework/i18n/data/ee.php
  290. +1 −1  framework/i18n/data/ee_gh.php
  291. +1 −1  framework/i18n/data/ee_tg.php
  292. +1 −1  framework/i18n/data/el.php
  293. +1 −1  framework/i18n/data/el_cy.php
  294. +1 −1  framework/i18n/data/el_gr.php
  295. +1 −1  framework/i18n/data/el_polyton.php
  296. +1 −1  framework/i18n/data/en.php
  297. +1 −1  framework/i18n/data/en_as.php
  298. +1 −1  framework/i18n/data/en_au.php
  299. +1 −1  framework/i18n/data/en_bb.php
  300. +1 −1  framework/i18n/data/en_be.php
Sorry, we could not display the entire diff because too many files (1,078) changed.
View
5 .travis.yml
@@ -6,13 +6,10 @@ php:
- 5.4
- 5.5
-services:
- - memcached
-
before_script:
- ./tests/travis/mysql-setup.sh
- ./tests/travis/postgresql-setup.sh
- ./tests/travis/memcache-setup.sh
- cd tests
-script: phpunit --colors --coverage-text --exclude-group mssql,oci framework
+script: phpunit --colors --no-globals-backup --exclude-group mssql,oci framework
View
37 CHANGELOG
@@ -3,17 +3,54 @@
Version 1.1.14 work in progress
-------------------------------
+- Bug #135: Fixed wrong CActiveRecord rows count with having (klimov-paul)
- Bug #150: Fixed CWidget was not switching between view paths when using themes (antoncpu)
- Bug #1464: Fixed transparent background for ImageMagick in CCaptchaAction (manuel-84, cebe)
+- Bug #1693: Fixed log file will rotate twice when high performance (monque)
+- Bug #1724: Allow CClientScript registering scripts and script files with the HTML options (klimov-paul)
- Bug #1763: CSqlDataProvider was appending another ORDER BY string to an existing ORDER BY statement when using fieldname with dot (szako)
+- Bug #1895: Fixed erroneous language attributes in french views (located at `framework\views\fr`) (Ragazzo)
- Bug #1915: CDataProviderIterator: fixed init in case of disabled pagination (antoncpu)
- Bug #1941: yiiactiveform.js form reset now uses CHtml::errorCss instead of a hardcoded value (mdomba)
- Bug #1942: CActiveForm client/ajax validation will now remove error class from server side validation (mdomba)
+- Bug #1945: Reference to undefined variable $column in CDbMigration::dropPrimaryKey (paystey)
+- Bug #1955: Some validators used to cause warnings or errors in case non-scalar array typed values being checked (resurtm)
+- Bug #1984: CDbMigration: fix of undeclared variable usage in debug information in dropPrimaryKey (papulovskiy)
+- Bug #1996: Using yiic help for commands with parameters with array as default value resulted in PHP error with latest PHP versions (dInGd0nG, samdark)
+- Bug #1997: Cache key in CGettextMessageSource::loadMessages wasn't specific enough (odevyatkov)
+- Bug #2023: CHttpRequest::stripSlashes() now modifies array keys as well (etienneq)
+- Bug #2030: Fixed problem with MySQL 4.x: Undefined Index: Comment in CMysqlSchema (cebe)
+- Bug #2048: AR now uses alias from CActiveRecord::getTableAlias instead of always using default "t" (s-larionov)
+- Bug #2049: CStatElement relation with join option throw exception when key-field present on joined table (Yiivgeny)
+- Bug #2078: Fixed problem with "undefined" parameter in query string when using CListView or CGridView with enableHistory (Parpaing)
+- Bug #2086: Fixed .hgignore rule for assets folder (GeXu3, Koduc)
+- Bug #2087: CLocale: getLocaleDisplayName() was only returning the language display name, not the full locale display name (brandonkelly)
+- Bug #2112: Fixed broken yiic shell CRUD command (mbischof)
+- Bug #2121: CMssqlSchema::resetSequence() incorrectly resets sequence (resurtm, joewoodhouse)
+- Bug #2123: Fixed error in plural rules handling if locale has no plural rules defined (cebe, stepanselyuk)
+- Bug #2146: CEmailValidator fix when fsockopen() can output uncatched error 'Connection refused (61)' (armab)
+- Bug #2159: Fixed SQL syntax for delete command with join in MySQL (serebrov)
+- Bug #2184: CDbHttpSession now supports MS SQL Server BLOB data type (cheuschober, resurtm)
+- Bug #2216: CDbCommandBuilder::createInCondition() has been updated, allowing to pass array of values with mixed keys for the single type column (klimov-paul)
+- Enh: Better CFileLogRoute performance (Qiang, samdark)
+- Enh: Refactored CHttpRequest::getDelete and CHttpRequest::getPut not to use _restParams directly (samdark)
- Enh #1743: Added CActiveForm::searchField() and CHtml::activeSearchField() to create HTML input field of type SEARCH (njasm)
- Enh #1847: Added COutputCache::varyByLanguage to generate separate cache for different languages (Obramko)
+- Enh #1948: Tidy up and improve html5 input support in CHtml and CActiveForm (phpnode)
+- Enh #1977: CFormatter::normalizeDateValue() now is protected instead of private to enable child classes to override it (etienneq)
+- Enh #2038: CFormatter::formatNtext() method can replace newlines with `<p></p>` not just with `<br />` as it was before (resurtm)
+- Enh #2135: MessageCommand can now handles Yii::t() messages with files in subfolders (firsyura)
+- Enh #2205: CActiveForm::error() now depends on CHtml::$errorContainerTag (malyshev)
+- Enh #2217: Support of the empty option for CHtml::radioButtonList() has been introduced (resurtm)
+- Chg: Upgraded HTMLPurifier to v4.5.0 (samdark)
- Chg #645: CDbConnection now throws CDbException when failed to open DB connection instead of failing with a warning (kidol, eirikhm, samdark, cebe)
- Chg #1891: Changed order of methods in models generated by Gii and yiic, added better description of search method (hijarian, samdark)
+- Chg #2069: Upgraded jQuery BBQ Plugin to 1.4pre to fix jQuery compatibility problems (samdark)
+- Chg #2183: Vendors: phlymail's Net_IDNA was replaced by PEAR Net_IDNA2 (resurtm, DaSourcerer)
+- Chg #2187: Vendors: punycode.js updated from 1.1.1 (June 27, 2012) to 1.2.0 (October 10, 2012) (resurtm)
+- New #575: Yii registering at Packagist, added composer info file (schmunk42)
- New #1785: Added CPasswordHelper (tom--)
+- New #2178: Added Catalan Translation (ArnauAregall)
Version 1.1.13 December 30, 2012
--------------------------------
View
2  LICENSE
@@ -1,7 +1,7 @@
The Yii framework is free software. It is released under the terms of
the following BSD License.
-Copyright © 2008-2011 by Yii Software LLC (http://www.yiisoft.com)
+Copyright (c) 2008-2013 by Yii Software LLC (http://www.yiisoft.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without
View
16 UPGRADE
@@ -20,6 +20,22 @@ General upgrade instructions
Upgrading from v1.1.13
----------------------
+- CActiveRecord::count() now respects group by and having. If your code relied
+ on ignoring it your application may break and should be updated.
+
+- Vendors: phlymail's Net_IDNA was replaced by PEAR Net_IDNA2. The latter library is better maintained than the former.
+ In case your code relies on bundled phlymail's Net_IDNA you should change it a bit. Old way of encoding IDNs:
+
+ require_once(Yii::getPathOfAlias('system.vendors.idna_convert').DIRECTORY_SEPARATOR.'idna_convert.class.php');
+ $idnaConvert=new idna_convert();
+ $result=$idnaConvert->encode($value);
+
+ New:
+
+ require_once(Yii::getPathOfAlias('system.vendors.Net_IDNA2.Net').DIRECTORY_SEPARATOR.'IDNA2.php');
+ $idna=new Net_IDNA2();
+ $result=$idna->encode($value);
+
Upgrading from v1.1.12
----------------------
- Both jQuery and jQueryUI were updated. Check [jQuery UI upgrade guide](http://jqueryui.com/upgrade-guide/1.9/)
View
2  build/build
@@ -8,7 +8,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
- * @copyright Copyright &copy; 2008 Yii Software LLC
+ * @copyright 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
View
2  build/build.bat
@@ -7,7 +7,7 @@ rem This is the bootstrap script for running build on Windows.
rem
rem @author Qiang Xue <qiang.xue@gmail.com>
rem @link http://www.yiiframework.com/
-rem @copyright Copyright &copy; 2008 Yii Software LLC
+rem @copyright 2008 Yii Software LLC
rem @license http://www.yiiframework.com/license/
rem @version $Id$
rem -------------------------------------------------------------
View
2  build/build.xml
@@ -5,7 +5,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
- * @copyright Copyright &copy; 2008-2009 Yii Software LLC
+ * @copyright 2008-2009 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
-->
View
2  build/commands/ApiCommand.php
@@ -4,7 +4,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
- * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
Yii::import('application.commands.api.ApiModel');
View
2  build/commands/AutoloadCommand.php
@@ -4,7 +4,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
- * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
View
4 build/commands/CldrCommand.php
@@ -4,7 +4,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
- * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
@@ -207,7 +207,7 @@ protected function process($path)
* Copyright © 1991-2007 Unicode, Inc. All rights reserved.
* Distributed under the Terms of Use in http://www.unicode.org/copyright.html.
*
- * Copyright © 2008-2011 Yii Software LLC (http://www.yiiframework.com/license/)
+ * @copyright 2008-2013 Yii Software LLC (http://www.yiiframework.com/license/)
*/
return $data;
EOD;
View
4 build/commands/LiteCommand.php
@@ -4,7 +4,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
- * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
@@ -58,7 +58,7 @@ public function run($args)
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
- * @copyright Copyright &copy; 2008-2012 Yii Software LLC
+ * @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
* @version \$Id: \$
* @since 1.0
View
2  build/commands/Utf8Command.php
@@ -4,7 +4,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
- * @copyright Copyright &copy; 2008-2012 Yii Software LLC
+ * @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
View
2  build/commands/api/ApiModel.php
@@ -4,7 +4,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
- * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
View
2  build/commands/api/layouts/main.php
@@ -21,7 +21,7 @@
</div><!-- end of content -->
<div id="apiFooter">
-Copyright &copy; 2008-2011 by <a href="http://www.yiisoft.com">Yii Software LLC</a><br/>
+&copy; 2008-2013 by <a href="http://www.yiisoft.com">Yii Software LLC</a><br/>
All Rights Reserved.<br/>
</div><!-- end of footer -->
View
2  build/commands/blog/title.tex
@@ -15,7 +15,7 @@
%\begin{center} \large \today \end{center}
\begin{center}
- Copyright 2008-2012. All Rights Reserved.
+ Copyright 2008-2013. All Rights Reserved.
\end{center}
\vfill
View
2  build/commands/guide/title.tex
@@ -15,7 +15,7 @@
%\begin{center} \large \today \end{center}
\begin{center}
- Copyright 2008-2012. All Rights Reserved.
+ Copyright 2008-2013. All Rights Reserved.
\end{center}
\vfill
View
2  build/commands/lite/css/form.css
@@ -5,7 +5,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
- * @copyright Copyright &copy; 2008-2010 Yii Software LLC
+ * @copyright 2008-2010 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
View
11 build/generate_accessors_phpdoc.php
@@ -6,12 +6,11 @@
$nClassesTotal = 0;
file_put_contents(
- dirname(__FILE__) . '\phpdoc.txt',
- getPhpDocForDir('D:\Web\libs-dev\yii\framework') . getPhpDocStats());
- //getPhpDocForDir('D:\Web\libs\yii\framework') . getPhpDocStats());
-//echo getPhpDocForDir('D:\Web\libs\yii\framework');
-//echo getPhpDocForDir('D:\Web\libs\yii\framework\caching');
-//echo getPhpDocForFile('D:\Web\libs\yii\framework\base\CModel.php');
+ dirname(__FILE__) . '/phpdoc.txt',
+ getPhpDocForDir(dirname(dirname(__FILE__)) . '/framework') . getPhpDocStats()
+// getPhpDocForDir(dirname(dirname(__FILE__)) . '/framework/caching') . getPhpDocStats()
+// getPhpDocForDir(dirname(dirname(__FILE__)) . '/framework/base/CModel.php') . getPhpDocStats()
+);
function getPhpDocStats()
{
View
2  build/tasks/YiiInitTask.php
@@ -4,7 +4,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
- * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
View
2  build/tasks/YiiPearTask.php
@@ -4,7 +4,7 @@
*
* @author Wei Zhuo <weizho@gmail.com>
* @link http://www.yiiframework.com/
- * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
View
57 composer.json
@@ -0,0 +1,57 @@
+{
+ "name": "yiisoft/yii",
+ "description": "Yii Web Programming Framework",
+ "keywords": ["yii", "framework"],
+ "homepage": "http://www.yiiframework.com/",
+ "type": "library",
+ "license": "BSD-3-Clause",
+ "authors": [
+ {
+ "name": "Qiang Xue",
+ "email": "qiang.xue@gmail.com",
+ "homepage": "http://www.yiiframework.com/",
+ "role": "Founder and project lead"
+ },
+ {
+ "name": "Alexander Makarov",
+ "email": "sam@rmcreative.ru",
+ "homepage": "http://rmcreative.ru/",
+ "role": "Core framework development"
+ },
+ {
+ "name": "Maurizio Domba",
+ "homepage": "http://mdomba.info/",
+ "role": "Core framework development"
+ },
+ {
+ "name": "Carsten Brandt",
+ "email": "mail@cebe.cc",
+ "homepage": "http://cebe.cc/",
+ "role": "Core framework development"
+ },
+ {
+ "name": "Wei Zhuo",
+ "email": "weizhuo@gmail.com",
+ "role": "Project site maintenance and development"
+ },
+ {
+ "name": "Sebastián Thierer",
+ "role": "Component development"
+ },
+ {
+ "name": "Jeffrey Winesett",
+ "role": "Documentation and marketing"
+ }
+
+ ],
+ "support": {
+ "issues": "https://github.com/yiisoft/yii/issues?state=open",
+ "forum": "http://www.yiiframework.com/forum/",
+ "wiki": "http://www.yiiframework.com/wiki/",
+ "irc": "irc://irc.freenode.net/yii",
+ "source": "https://github.com/yiisoft/yii"
+ },
+ "require": {
+ "php": ">=5.1.0"
+ }
+}
View
2  demos/blog/css/form.css
@@ -30,7 +30,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
- * @copyright Copyright &copy; 2008-2010 Yii Software LLC
+ * @copyright 2008-2010 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
View
2  demos/hangman/protected/controllers/GameController.php
@@ -4,7 +4,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
- * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
View
2  docs/blog/ja/prototype.scaffold.txt
@@ -60,7 +60,7 @@ http://www.example.com/blog/index.php?r=gii
残りのデータベーステーブル (`tbl_post`, `tbl_comment`, `tbl_tag`, `tbl_lookup`) について、同じ手順を繰り返します。
-> Tip|ヒント: `Table Name`フィールドにアスタリスク '*' を入力することも可能です。これにより一発で、**全ての** データベーステーブルのモデルクラスが生成されます。
+> Tip|ヒント: `Table Name`フィールドにアスタリスク `*` を入力することも可能です。これにより一発で、**全ての** データベーステーブルのモデルクラスが生成されます。
この段階において、以下のファイルが新しく生成されています。
View
2  docs/blog/prototype.scaffold.txt
@@ -54,7 +54,7 @@ On the `Model Generator` page, enter `tbl_user` (the user table name) in the `Ta
Repeat the same procedure for the rest of the database tables, including `tbl_post`, `tbl_comment`, `tbl_tag` and `tbl_lookup`.
-> Tip: We can also enter an asterisk character '*' in the `Table Name` field. This will generate a model class for *every* database table in a single shot.
+> Tip: We can also enter an asterisk character `*` in the `Table Name` field. This will generate a model class for *every* database table in a single shot.
At this stage, we will have the following newly created files:
View
2  docs/blog/ru/prototype.scaffold.txt
@@ -73,7 +73,7 @@ http://www.example.com/blog/index.php?r=gii
Повторим те же действия для всех остальных таблиц БД, включая
`tbl_post`, `tbl_comment`, `tbl_tag` и `tbl_lookup`.
-> Tip|Подсказка: Также мы можем ввести '\*' в поле `Table Name`. Так мы сгенерируем
+> Tip|Подсказка: Также мы можем ввести `*` в поле `Table Name`. Так мы сгенерируем
модели для *каждой* таблицы БД за один раз.
На данном этапе у нас будут созданы следующие файлы:
View
104 docs/blog/uk/comment.admin.txt
@@ -0,0 +1,104 @@
+Управління коментарями
+======================
+
+Управління коментарями включає оновлення, видалення та схвалення коментарів.
+Ці операції реалізовані як дії класу `CommentController`.
+
+Оновлення та видалення коментарів
+---------------------------------
+
+Код, що згенеровано утилітою `Gii` для оновлення та видалення коментарів,
+залишається, в основному, незмінним.
+
+Схвалення коментарів
+--------------------
+
+Нещодавно створені коментарі перебувають у статусі очікування схвалення і повинні бути схвалені, щоб стати видимими для гостьового користувача. Схвалення коментаря - це, головним чином, зміна стовпця стану коментаря.
+
+Ми створюємо метод `actionApprove()` у класі `CommentController` наступним чином:
+
+~~~
+[php]
+public function actionApprove()
+{
+ if(Yii::app()->request->isPostRequest)
+ {
+ $comment=$this->loadModel();
+ $comment->approve();
+ $this->redirect(array('index'));
+ }
+ else
+ throw new CHttpException(400,'Невірний запит...');
+}
+~~~
+
+Вище, коли дія `approve` викликається через запит POST,
+ми викликаємо метод `approve()`, визначений у моделі `Comment`, для зміни статусу.
+Потім ми переадресовуємо браузер користувача до сторінки, що відображає запис,
+до якої відноситься цей коментар.
+
+Звісно, потрібно створити і сам метод `approve` у моделі `Comment`:
+
+~~~
+[php]
+public function approve()
+{
+ $this->status=Comment::STATUS_APPROVED;
+ $this->update(array('status'));
+}
+~~~
+
+Тут ми просто виставляємо властивість `status` коментаря у `approved` згідно
+значенню відповідної константи класу `Comment`:
+
+~~~
+[php]
+class Comment extends CActiveRecord
+{
+ ...
+
+ const STATUS_PENDING=1;
+ const STATUS_APPROVED=2;
+
+ ..
+}
+~~~
+
+Потім ми викликаємо метод `update()` для того, щоб зберегти виставлену властивість у БД.
+
+Ми також змінюємо метод `actionIndex()` контролера `CommentController`,
+щоб показати всі коментарі і вивести першими ті, що очікують схвалення.
+
+~~~
+[php]
+public function actionIndex()
+{
+ $dataProvider=new CActiveDataProvider('Comment', array(
+ 'criteria'=>array(
+ 'with'=>'post',
+ 'order'=>'t.status, t.create_time DESC',
+ ),
+ ));
+
+ $this->render('index',array(
+ 'dataProvider'=>$dataProvider,
+ ));
+}
+~~~
+
+Варто відзначити, що у коді вище необхідно розвʼязати конфлікт імен стовпців,
+вказавши що нам необхідні значення із основної таблиці `tbl_comment`.
+Сам конфлікт виникає так як і у `tbl_post` і у `tbl_comment` є стовпці
+`status` і `create_time`.
+
+Зробити це можна шляхом використання псевдонімів таблиць.
+Як описано у [керівництві](/doc/guide/uk/database.arr#disambiguating-column-names),
+псевдонім головної таблиці реляційного запиту завжди дорівнює `t`.
+Тому, ми додаємо `t` до полів `status` та `create_time`.
+
+Як і відображення записів, відображення `index` контролера `CommentController`
+використовує [CListView] для виведення списку коментарів, який, у свою чергу,
+використовує `/wwwroot/blog/protected/views/comment/_view.php` для виведення кожного
+окремого коментаря. Ми не будемо детально розглядати цей механізм.
+Зацікавлені читачі можуть вивчити відповідний файл демонстраційного
+додатку: `/wwwroot/yii/demos/blog/protected/views/comment/_view.php`.
View
195 docs/blog/uk/comment.create.txt
@@ -0,0 +1,195 @@
+Створення та відображення коментарів
+====================================
+
+У даному розділі ми реалізуємо функції відображення і створення коментарів.
+
+Для більшої інтерактивності будемо проводити валідацію на стороні клієнта.
+За допомогою Yii зробити це досить легко. Відзначимо, що для цього буде потрібно
+Yii версії 1.1.1 чи новіше.
+
+Відображення коментарів
+-----------------------
+
+Замість використання окремих сторінок для відображення та створення
+коментарів, ми використовуємо сторінку запису (що генерується дією `view`
+контролера `PostController`). Під текстом запису ми відображаємо список
+коментарів, що належать їй та форму створення коментаря.
+
+Щоб відобразити коментарі на сторінці запису, ми змінюємо відображення
+`/wwwroot/blog/protected/views/post/view.php` наступним чином:
+
+~~~
+[php]
+…основна частина відображення post…
+
+<div id="comments">
+ <?php if($model->commentCount>=1): ?>
+ <h3>
+ <?php echo $model->commentCount . 'comment(s)'; ?>
+ </h3>
+
+ <?php $this->renderPartial('_comments',array(
+ 'post'=>$model,
+ 'comments'=>$model->comments,
+ )); ?>
+ <?php endif; ?>
+</div>
+~~~
+
+Вище ми викликаємо `renderPartial()` для виведення відображення `_comments`,
+що показує список коментарів до поточного запису.
+Зауважимо, що у відображенні, для отримання коментарів до запису,
+ми використовуємо вираз `$model->comments`.
+Це можливо, так як ми оголосили звʼязок `comments` у класі `Post`.
+Виконання цього виразу викликає додатковий JOIN-запит до БД,
+щоб повернути потрібні коментарі.
+Ця можливість відома як [ліниве завантаження](/doc/guide/uk/database.arr).
+
+Відображення `_comments` не дуже цікаво.
+У ньому проводиться обхід всіх коментарів і їх виведення.
+Зацікавлені читачі можуть подивитися файл
+`/wwwroot/yii/demos/blog/protected/views/post/_comments.php`.
+
+Створення коментарів
+--------------------
+
+Щоб обробити створення коментаря, ми спочатку змінюємо метод `actionView()`
+контролера `PostController` наступним чином:
+
+~~~
+[php]
+public function actionView()
+{
+ $post=$this->loadModel();
+ $comment=$this->newComment($post);
+
+ $this->render('view',array(
+ 'model'=>$post,
+ 'comment'=>$comment,
+ ));
+}
+
+protected function newComment($post)
+{
+ $comment=new Comment;
+ if(isset($_POST['Comment']))
+ {
+ $comment->attributes=$_POST['Comment'];
+ if($post->addComment($comment))
+ {
+ if($comment->status==Comment::STATUS_PENDING)
+ Yii::app()->user->setFlash('commentSubmitted','Дякуємо за ваш коментар.
+ Ваш коментар зʼявиться одразу після ухвалення.');
+ $this->refresh();
+ }
+ }
+ return $comment;
+}
+~~~
+
+Далі ми додаємо метод `addComment()` у модель `Post`:
+
+~~~
+[php]
+public function addComment($comment)
+{
+ if(Yii::app()->params['commentNeedApproval'])
+ $comment->status=Comment::STATUS_PENDING;
+ else
+ $comment->status=Comment::STATUS_APPROVED;
+ $comment->post_id=$this->id;
+ return $comment->save();
+}
+~~~
+
+Вище ми викликаємо метод `newComment()` перед відображенням представлення `view`.
+У методі `newComment()` ми створюємо екземпляр класу `Comment` і перевіряємо,
+чи відправлена ​​форма коментаря. Якщо відправлена ​​- намагаємося додати коментар
+до запису, викликаючи `$post->addComment($comment)`.
+Якщо вийшло - оновлюємо сторінку запису, на якій буде показаний тільки що
+створений коментар у тому випадку, якщо він не вимагає схвалення.
+У іншому випадку - показуємо моментальне повідомлення про те,
+що коментар буде показаний як тільки він буде схвалений.
+Моментальне повідомлення зазвичай виводиться для підтвердження якоїсь дії.
+Якщо користувач оновлює сторінку, таке повідомлення зникає.
+
+Продовжуємо змінювати `/wwwroot/blog/protected/views/post/view.php`:
+
+~~~
+[php]
+…
+<div id="comments">
+ …
+ <h3>Залишити коментар</h3>
+
+ <?php if(Yii::app()->user->hasFlash('commentSubmitted')): ?>
+ <div class="flash-success">
+ <?php echo Yii::app()->user->getFlash('commentSubmitted'); ?>
+ </div>
+ <?php else: ?>
+ <?php $this->renderPartial('/comment/_form',array(
+ 'model'=>$comment,
+ )); ?>
+ <?php endif; ?>
+
+</div><!-- comments -->
+~~~
+
+У наведеному вище коді ми показуємо моментальне повідомлення, якщо воно є.
+У зворотному випадку - показуємо форму введення коментаря із файлу
+`/wwwroot/blog/protected/views/comment/_form.php`.
+
+AJAX валідація
+--------------
+
+Для того, щоб поліпшити зручність форми, можна використовувати AJAX валідацію полів форми.
+У цьому випадку користувач отримує інформацію про помилки по мірі заповнення форми.
+Для використання даної можливості у формі коментарів необхідно зробити декілька змін у відображенні `/wwwroot/blog/protected/views/comment/_form.php` та методі `newComment()`.
+
+У файлі `_form.php` нам необхідно встановити властивість [CActiveForm::enableAjaxValidation]
+для віджета [CActiveForm] у `true`:
+
+~~~
+[php]
+<div class="form">
+
+<?php $form=$this->beginWidget('CActiveForm', array(
+ 'id'=>'comment-form',
+ 'enableAjaxValidation'=>true,
+)); ?>
+…
+<?php $this->endWidget(); ?>
+
+</div><!-- form -->
+~~~
+
+У метод `newComment()` ми додаємо код, який відповідає на запити AJAX валідації.
+Код перевіряє, чи є параметр `POST` з імʼям `ajax`.
+Якщо є - віддає результат валідації, використовуючи [CActiveForm::validate].
+
+~~~
+[php]
+protected function newComment($post)
+{
+ $comment=new Comment;
+
+ if(isset($_POST['ajax']) && $_POST['ajax']==='comment-form')
+ {
+ echo CActiveForm::validate($comment);
+ Yii::app()->end();
+ }
+
+ if(isset($_POST['Comment']))
+ {
+ $comment->attributes=$_POST['Comment'];
+ if($post->addComment($comment))
+ {
+ if($comment->status==Comment::STATUS_PENDING)
+ Yii::app()->user->setFlash('commentSubmitted','Дякуємо за ваш коментар.
+ Ваш коментар зʼявиться одразу після ухвалення.');
+ $this->refresh();
+ }
+ }
+ return $comment;
+}
+~~~
View
80 docs/blog/uk/comment.model.txt
@@ -0,0 +1,80 @@
+Доопрацювання моделі Comment
+============================
+
+У моделі `Comment` нам необхідно виправити методи `rules()` та `attributeLabels()`.
+Метод `attributeLabels()` повертає масив заголовків для зазначених полів.
+Метод `relations()` виправляти не будемо, так як код, що згенерував `Gii`, нам
+підходить.
+
+Зміна методу `rules()`
+----------------------
+
+Почнемо із уточнення правил валідації, згенерованих за допомогою `Gii`.
+Для коментарів будемо використовувати наступні правила:
+
+~~~
+[php]
+public function rules()
+{
+ return array(
+ array('content, author, email', 'required'),
+ array('author, email, url', 'length', 'max'=>128),
+ array('email','email'),
+ array('url','url'),
+ );
+}
+~~~
+
+Тут ми вказуємо, що атрибути `author`, `email` та `content` обовʼязкові.
+Довжина `author`, `email` та `url` не може перевищувати 128 символів.
+Атрибут `email` повинен містити коректну email-адресу.
+`url` повинен містити коректний URL.
+
+Зміна методу `attributeLabels()`
+--------------------------------
+
+Змінимо метод `attributeLabels()`. Задамо свої підписи атрибутам.
+Цей метод повертає масив пар імʼя атрибута-підпис.
+
+~~~
+[php]
+public function attributeLabels()
+{
+ return array(
+ 'id' => 'Номер',
+ 'content' => 'Коментар',
+ 'status' => 'Статус',
+ 'create_time' => 'Час створення',
+ 'author' => 'Імʼя автора',
+ 'email' => 'Email',
+ 'url' => 'Website',
+ 'post_id' => 'Запис',
+ );
+}
+~~~
+
+> Tip|Підказка: Якщо підпис атрибуту не заданий у `attributeLabels()`,
+для його генерації використовується спеціальний алгоритм.
+Наприклад, для атрибутів `create_time` та `createTime` підпис буде виглядати як
+`Create Time`.
+
+Зміна процесу збереження
+------------------------
+
+Для того, щоб записувати час створення коментаря, перевизначимо
+метод `beforeSave()` класу `Comment` так само, як це зроблено для моделі `Post`:
+
+~~~
+[php]
+protected function beforeSave()
+{
+ if(parent::beforeSave())
+ {
+ if($this->isNewRecord)
+ $this->create_time=time();
+ return true;
+ }
+ else
+ return false;
+}
+~~~
View
139 docs/blog/uk/post.admin.txt
@@ -0,0 +1,139 @@
+Управління записами
+===================
+
+Під управлінням записами мається на увазі відображення їх списку у адміністративному
+розділі з можливістю переглядати записи з будь-яким статусом, редагувати та
+видаляти їх. Цей функціонал реалізується у діях `admin` та `delete`
+відповідно. Код, згенерований за допомогою `Gii`, майже не потребує
+змін. Нижче ми пояснимо, як реалізовані ці дії.
+
+Відображення записів у вигляді таблиці
+--------------------------------------
+
+Дія `admin` виводить записи із всіма статусами у вигляді таблиці,
+розбитої на кілька сторінок, яка підтримує сортування по декільком колонкам.
+Далі наведено метод `actionAdmin()` контролера `PostController`:
+
+~~~
+[php]
+public function actionAdmin()
+{
+ $model=new Post('search');
+ if(isset($_GET['Post']))
+ $model->attributes=$_GET['Post'];
+ $this->render('admin',array(
+ 'model'=>$model,
+ ));
+}
+~~~
+
+Даний код повністю згенеровано `Gii`.
+Спочатку створюється модель `Post` із сценарієм `search`
+[scenario](/doc/guide/uk/form.model), який ми будемо використовувати для
+збору критеріїв пошуку, вказаних користувачем.
+Далі ми присвоюємо дані, введені користувачем, моделі.
+І, нарешті, ми виводимо відображення `admin`, використовуючи модель.
+
+Нижче приведений код відображення `admin`:
+
+~~~
+[php]
+<?php
+$this->breadcrumbs=array(
+ 'Manage Posts',
+);
+?>
+<h1>Manage Posts</h1>
+
+<?php $this->widget('zii.widgets.grid.CGridView', array(
+ 'dataProvider'=>$model->search(),
+ 'filter'=>$model,
+ 'columns'=>array(
+ array(
+ 'name'=>'title',
+ 'type'=>'raw',
+ 'value'=>'CHtml::link(CHtml::encode($data->title), $data->url)'
+ ),
+ array(
+ 'name'=>'status',
+ 'value'=>'Lookup::item("PostStatus",$data->status)',
+ 'filter'=>Lookup::items('PostStatus'),
+ ),
+ array(
+ 'name'=>'create_time',
+ 'type'=>'datetime',
+ 'filter'=>false,
+ ),
+ array(
+ 'class'=>'CButtonColumn',
+ ),
+ ),
+)); ?>
+~~~
+
+Для виведення записів ми використовуємо компонент [CGridView], який
+розбиває дані на сторінки і дозволяє сортувати їх по стовпцях.
+Наша зміна стосується, головним чином, відображення кожного стовпця.
+Наприклад, для стовпця `title` ми вказуємо, що він повинен містити
+посилання на перегляд запису. Вираз `$data->url` повертає значення
+властивості `url`, яке ми визначаємо у класі `Post`.
+
+> Tip|Підказка: При виведенні тексту ми використовуємо [CHtml::encode()] для
+ кодування сутностей HTML. Це дозволяє уникнути атак через [міжсайтовий
+ скриптинг](/doc/guide/uk/topics.security).
+
+
+Видалення записів
+-----------------
+
+У таблиці данних `admin` у кожному рядку є кнопка «Видалити», яка
+видаляє відповідний запис. Дія `delete` виглядає наступним чином:
+
+~~~
+[php]
+public function actionDelete()
+{
+ if(Yii::app()->request->isPostRequest)
+ {
+ // we only allow deletion via POST request
+ $this->loadModel()->delete();
+
+ if(!isset($_GET['ajax']))
+ $this->redirect(array('index'));
+ }
+ else
+ throw new CHttpException(400,'Invalid request. Please do not repeat this request again.');
+}
+~~~
+
+Наведений вище код повністю згенеровано `Gii`.
+Зупинимося докладніше на перевірці `$_GET['ajax']`.
+У віджеті [CGridView] сортування, розбивка на сторінки
+та видалення за замовчуванням реалізовані з використанням AJAX.
+Тобто при виконанні перерахованих дій сторінка перезавантажуватися не буде.
+Віджет може працювати і без AJAX (якщо властивість `ajaxUpdate` виставлено у
+`false` або відключений JavaScript). У дії `delete` при AJAX запиті перенаправлення
+проводитися не повинно.
+
+Видалення запису повинно викликати видалення всіх коментарів до неї.
+До того ж, ми повинні оновити теги у таблиці `tbl_tag`.
+Обидва завдання можуть бути виконані у методі `afterDelete` моделі `Post`:
+
+~~~
+[php]
+protected function afterDelete()
+{
+ parent::afterDelete();
+ Comment::model()->deleteAll('post_id='.$this->id);
+ Tag::model()->updateFrequency($this->tags, '');
+}
+~~~
+
+Код, наведений вище не складний: спочатку видаляються всі коментарі, чий
+`post_id` дорівнює ID запису, що видаляється. Далі оновлюється таблиця `tbl_tag`.
+
+> Tip|Підказка: Ми видаляємо коментарі запису, що видаляється, у коді, так як SQLite
+не підтримує обмеження по зовнішньому ключу. У СУБД, які дані обмеження
+підтримують (наприклад, MySQL або PostgreSQL), можна використовувати каскадне
+видалення коментарів у разі видалення запису. У цьому випадку немає необхідності
+видаляти коментарі у коді.
View
120 docs/blog/uk/post.create.txt
@@ -0,0 +1,120 @@
+Створення та редагування записів
+================================
+
+Після того, як ми закінчили із моделлю `Post`, займемося контролером `PostController`
+і його відображеннями. У даному розділі ми налаштуємо правила доступу операцій CRUD.
+Потім змінимо код, відповідальний за `створення` (`create`) та `оновлення` (`update`).
+
+Налаштування правил доступу
+---------------------------
+
+Перше, що ми запланували - налаштування
+[прав доступу](/doc/guide/uk/topics.auth#access-control-filter).
+Код, згенерований за допомогою `gii`, нам не підійде.
+
+Необхідно змінити метод `accessRules()` у файлі
+`/wwwroot/blog/protected/controllers/PostController.php` наступним чином:
+
+~~~
+[php]
+public function accessRules()
+{
+ return array(
+ array('allow', // дозволяє всім користувачам виконувати дії 'list' та 'show'
+ 'actions'=>array('index', 'view'),
+ 'users'=>array('*'),
+ ),
+ array('allow', // дозволяє аутентифікованим користувачам виконувати будь-які дії
+ 'users'=>array('@'),
+ ),
+ array('deny', // забороняє всім користувачам
+ 'users'=>array('*'),
+ ),
+ );
+}
+~~~
+
+Описані вище правила дозволяють всім користувачам виконувати дії
+`index` та `view`. Аутентифікованим - будь-які дії, включаючи `admin`.
+Всім іншим користувачам заборонено все. Варто відзначити, що правила
+застосовуються у порядку їх опису. Перше правило, що спрацювало, визначає,
+давати доступ або не давати. Наприклад, якщо поточний користувач є
+власником системи та намагається зайти на сторінку створення запису,
+буде застосовано друге правило і доступ буде дозволений.
+
+Правки у діях `create` та `update`
+----------------------------------
+
+Операції `create` та `update` досить схожі.
+У обох випадках необхідно вивести HTML форму для збору даних, що вводяться користувачем.
+Також потрібна валідація та збереження даних у БД.
+Головна відмінність у тому, що при `update` форма буде заповнюватися даними запису,
+що редагується. З цієї причини `gii` генерує вкладене відображення
+`/wwwroot/blog/protected/views/post/_form.php`, яке включається
+як у відображення 'create', так і відображення 'update' для виводу HTML форми.
+
+Для початку змінимо файл `_form.php` таким чином, щоб форма збирала тільки
+потрібні нам дані: `title`, `content`, `tags` та `status`.
+Для перших трьох атрибутів ми використовуємо текстові поля.
+Для `status` - випадаючий список з усіма можливими станами запису:
+
+~~~
+[php]
+<?php echo $form->dropDownList($model,'status',Lookup::items('PostStatus')); ?>
+~~~
+
+У наведеному коді для отримання списку статусів використовується виклик `Lookup::items('PostStatus')`.
+
+Далі змінимо клас `Post` таким чином, щоб він автоматично виставляв
+деякі атрибути (такі, як `create_time` та `author_id`) безпосередньо перед
+збереженням запису у БД. Перекриємо метод `beforeSave()`:
+
+~~~
+[php]
+protected function beforeSave()
+{
+ if(parent::beforeSave())
+ {
+ if($this->isNewRecord)
+ {
+ $this->create_time=$this->update_time=time();
+ $this->author_id=Yii::app()->user->id;
+ }
+ else
+ $this->update_time=time();
+ return true;
+ }
+ else
+ return false;
+}
+~~~
+
+При збереженні запису ми хочемо також оновити інформацію про частоту використання
+тегів у таблиці `tbl_tag`. Ми можемо реалізувати це у методі `afterSave()`,
+який автоматично викликається після успішного збереження запису у БД.
+
+~~~
+[php]
+protected function afterSave()
+{
+ parent::afterSave();
+ Tag::model()->updateFrequency($this->_oldTags, $this->tags);
+}
+
+private $_oldTags;
+
+protected function afterFind()
+{
+ parent::afterFind();
+ $this->_oldTags=$this->tags;
+}
+~~~
+
+Так як необхідно визначити, чи змінював користувач теги при редагуванні запису,
+нам знадобляться старі теги. Для цього ми реалізуємо метод `afterFind()`,
+який записує старі теги у властивість `_oldTags`. Метод `afterFind()` викликається
+автоматично при заповненні моделі AR даними, отриманими із БД.
+
+Тут ми не будемо детально розглядати метод `Tag::updateFrequency()`.
+Зацікавлені читачі можуть ознайомитися із ним у файлі
+`/wwwroot/yii/demos/blog/protected/models/Tag.php`.
View
138 docs/blog/uk/post.display.txt
@@ -0,0 +1,138 @@
+Відображення записів
+====================
+
+У нашому додатку запис може відображатися як окремо, так і серед інших
+записів. Перше реалізується дією `view`, друге - `index`. У даному розділі
+ми змінимо обидві дії для досягнення первинних вимог.
+
+Зміна дії `view`
+----------------
+
+Дія `view` реалізована у методі `actionView()` контролера `PostController`.
+HTML, що віддається користувачеві, генерується із відображення `view`, який знаходиться
+у файлі `/wwwroot/blog/protected/views/post/view.php`.
+
+Нижче приведений код дії `view` контролера `PostController`:
+
+~~~
+[php]
+public function actionView()
+{
+ $post=$this->loadModel();
+ $this->render('view',array(
+ 'model'=>$post,
+ ));
+}
+
+private $_model;
+
+public function loadModel()
+{
+ if($this->_model===null)
+ {
+ if(isset($_GET['id']))
+ {
+ if(Yii::app()->user->isGuest)
+ $condition='status='.Post::STATUS_PUBLISHED
+ .' OR status='.Post::STATUS_ARCHIVED;
+ else
+ $condition='';
+ $this->_model=Post::model()->findByPk($_GET['id'], $condition);
+ }
+ if($this->_model===null)
+ throw new CHttpException(404,'Запитувана сторінка не існує.');
+ }
+ return $this->_model;
+}
+~~~
+
+Наші зміни в основному торкнулися методу `loadModel()`.
+У ньому ми отримуємо запис із таблиці `Post`, використовуючи параметр `id` із GET.
+Якщо запис не знайдено, не опублікована або знаходиться у архіві,
+і при цьому користувач є гостем - відображаємо помилку 404.
+Інакше повертаємо обʼєкт запису методу `actionView()`, який передає обʼєкт відображенню.
+
+> Tip|Підказка: Yii перехоплює виключення HTTP (екземпляри класу [CHttpException])
+і відображає їх, використовуючи або зумовлені, або власні шаблони.
+Каркас, згенерований `yiic` вже містить свій шаблон для помилок у файлі
+`/wwwroot/blog/protected/views/site/error.php`.
+При необхідності ми можемо змінити цей файл.
+
+Зміни у відображенні `view` в основному торкаються форматування та стилю
+відображення запису, тому на ньому ми зупинятися не будемо.
+Зацікавлені читачі можуть звернутися до файла
+`/wwwroot/blog/protected/views/post/view.php`.
+
+Зміна дії `index`
+-----------------
+
+Як і в дії `view`, ми будемо змінювати дію `index` у двох місцях:
+метод `actionIndex()` контролера `PostController` і відображення
+`/wwwroot/blog/protected/views/post/index.php`.
+Потрібно додати підтримку відображення записів з певним тегом.
+
+Нижче наведено змінений метод `actionIndex()` контролера `PostController`:
+
+~~~
+[php]
+public function actionIndex()
+{
+ $criteria=new CDbCriteria(array(
+ 'condition'=>'status='.Post::STATUS_PUBLISHED,
+ 'order'=>'update_time DESC',
+ 'with'=>'commentCount',
+ ));
+ if(isset($_GET['tag']))
+ $criteria->addSearchCondition('tags',$_GET['tag']);
+
+ $dataProvider=new CActiveDataProvider('Post', array(
+ 'pagination'=>array(
+ 'pageSize'=>5,
+ ),
+ 'criteria'=>$criteria,
+ ));
+
+ $this->render('index',array(
+ 'dataProvider'=>$dataProvider,
+ ));
+}
+~~~
+
+Спочатку ми створюємо критерій запиту для отримання списку записів.
+Критерій включає обмеження на одержання тільки опублікованих записів і сортування
+по часу їх оновлення у зворотному порядку. Так як при відображенні запису
+в списку ми також хочемо показувати кількість коментарів, у критерії вказується
+необхідність одержання звʼязку `commentCount`, описаного у `Post::relations()`.
+
+У тому випадку, коли користувач хоче отримати записи з певним тегом,
+ми додаємо у критерій умову пошуку тега.
+
+Використовуючи критерій ми створюємо провайдер даних, потрібний для трьох цілей.
+По-перше, він займається розбивкою даних на сторінки.
+Ми задаємо кількість результатів на сторінку рівним 5.
+По-друге, дані сортуються відповідно до запиту користувача.
+І, нарешті, провайдер віддає розбиті на сторінки
+відсортовані дані віджетам або відображенню.
+
+Після того, як ми закінчили із `actionIndex()`, ми змінюємо відображення `index`
+як показано нижче. Будемо відображати заголовок `h1` у тому випадку, коли користувач
+буде запитувати записи із певним тегом.
+
+~~~
+[php]
+<?php if(!empty($_GET['tag'])): ?>
+<h1>Записи із тегом <i><?php echo CHtml::encode($_GET['tag']); ?></i></h1>
+<?php endif; ?>
+
+<?php $this->widget('zii.widgets.CListView', array(
+ 'dataProvider'=>$dataProvider,
+ 'itemView'=>'_view',
+ 'template'=>"{items}\n{pager}",
+)); ?>
+~~~
+
+Варто відзначити, що для побудови списку записів ми використовуємо [CListView].
+Цей віджет використовує відображення для побудови кожного окремого запису.
+Ми вказуємо відображення `_view`, тобто файл
+`/wwwroot/blog/protected/views/post/_view.php`, у якому ми можемо
+звертатися до запису через змінну `$data`.
View
254 docs/blog/uk/post.model.txt
@@ -0,0 +1,254 @@
+Доопрацювання моделі Post
+=========================
+
+Модель `Post`, згенерована за допомогою `Gii`, потребує наступних змін:
+
+ - метод `rules()`: задає правила валідації атрибутів моделі;
+ - метод `relations()`: задає звʼязки з іншими обʼєктами.
+
+> Info|Інформація: [Модель](/doc/guide/uk/basics.model) складається із набору атрибутів,
+кожен з яких асоціюється з відповідним полем у таблиці БД. Атрибути можуть бути описані
+явно як змінні класу, або використовуватися без будь-якого опису.
+
+Зміна методу `rules()`
+----------------------
+
+У першу чергу необхідно визначити правила валідації, які дозволять переконатися в тому,
+що дані, введені користувачем, коректні до їх збереження в БД.
+Наприклад, атрибут `status` моделі `Post` повинен бути цілим числом, рівним 1, 2 або 3.
+Консоль `Gii` генерує правила валідації для кожної моделі.
+При цьому використовується структура БД,
+тому деякі правила можуть виявитися неточними.
+
+Грунтуючись на аналізі вимог, змінимо метод `rules ()` наступним чином:
+
+~~~
+[php]
+public function rules()
+{
+ return array(
+ array('title, content, status', 'required'),
+ array('title', 'length', 'max'=>128),
+ array('status', 'in', 'range'=>array(1,2,3)),
+ array('tags', 'match', 'pattern'=>'/^[\w\s,]+$/',
+ 'message'=>'У тегах можна використовувати лише літери.'),
+ array('tags', 'normalizeTags'),
+
+ array('title, status', 'safe', 'on'=>'search'),
+ );
+}
+~~~
+
+У коді вище ми визначили, що атрибути `title`, `content` і `status`
+є обовʼязковими для заповнення. Довжина `title` не повинна перевищувати 128 символів.
+Значення `status` може бути 1 (чернетка), 2 (опубліковано) або 3 (в архіві).
+В `tags` можуть міститися тільки букви, коми та пробіли.
+Теги, що вводяться користувачем, додатково нормалізуються за допомогою `normalizeTags`.
+Це робиться для того, щоб теги були унікальними і правильно розділялися комами.
+Останнє правило використовується пошуком і буде описано пізніше.
+
+Валідатори, такі як `required`, `length`, `in` та `match` є стандартними валідаторами Yii.
+Валідатор `normalizeTags` використовує визначений метод у класі `Post`.
+За додатковою інформацією про те, як описувати правила валідації ви можете звернутися до [повного керівництва](/doc/guide/uk/form.model#declaring-validation-rules).
+
+~~~
+[php]
+public function normalizeTags($attribute,$params)
+{
+ $this->tags=Tag::array2string(array_unique(Tag::string2array($this->tags)));
+}
+~~~
+
+де `array2string` та `string2array` - нові методи,
+які ми повинні визначити у класі моделі `Tag`:
+
+~~~
+[php]
+public static function string2array($tags)
+{
+ return preg_split('/\s*,\s*/',trim($tags),-1,PREG_SPLIT_NO_EMPTY);
+}
+
+public static function array2string($tags)
+{
+ return implode(', ',$tags);
+}
+~~~
+
+Правила, описані у методі `rules()`, викликаються по черзі при виклику методів моделі
+[validate()|CModel::validate] або [save()|CActiveRecord::save].
+
+> Note|Примітка: Важливо памʼятати, що атрибути, описувані у `rules()` повинні
+вводитися користувачем. Інші атрибути моделі `Post`, такі як `id` або `create_time`,
+що заповнюються у коді або безпосередньо у БД, не повинні бути присутніми в `rules()`.
+Детальніше це описано у розділі [Безпечне присвоювання значень атрибутів](/doc/guide/uk/form.model#securing-attribute-assignments).
+
+Після того, як ми зробили описані зміни, ми можемо зайти на сторінку створення запису
+і перевірити, що нові правила валідації працюють.
+
+Зміна методу `relations()`
+--------------------------
+
+Далі вкажемо у методі `relations()` звʼязані із записом обʼєкти. Після цього ми
+зможемо використовувати [реляційну ActiveRecord (RAR)](/doc/guide/uk/database.arr)
+для отримання звʼязаних із записом даних, таких як інформацію про автора та коментарі. Складні SQL запити з JOIN в цьому випадку не потрібні.
+
+Визначимо метод `relations()`:
+
+~~~
+[php]
+public function relations()
+{
+ return array(
+ 'author' => array(self::BELONGS_TO, 'User', 'author_id'),
+ 'comments' => array(self::HAS_MANY, 'Comment', 'post_id',
+ 'condition'=>'comments.status='.Comment::STATUS_APPROVED,
+ 'order'=>'comments.create_time DESC'),
+ 'commentCount' => array(self::STAT, 'Comment', 'post_id',
+ 'condition'=>'status='.Comment::STATUS_APPROVED),
+ );
+}
+~~~
+
+Також, у класі моделі `Comment` ми описуємо дві константи, які використовуються у наведеному вище методі:
+
+~~~
+[php]
+class Comment extends CActiveRecord
+{
+ const STATUS_PENDING=1;
+ const STATUS_APPROVED=2;
+ ......
+}
+~~~
+
+Звʼязки, описані у методі `relations ()`, означають наступне:
+
+ * Запис належить автору (`User`), звʼязок із яким встановлюється на основі поля
+запису `author_id`;
+ * Запис може містити багато коментарів (`Comment`), звʼязок із якими встановлюється на основі поля коментаря `post_id`. Коментарі сортуються за часом їх створення;
+ * Звʼязок `commentCount` є особливим, оскільки повертає результат агрегації, тобто число коментарів запису.
+
+Задавши описані вище звʼязки, ми можемо отримати інформацію про автора
+та коментарі до запису наступним чином:
+
+~~~
+[php]
+$author=$post->author;
+echo $author->username;
+
+$comments=$post->comments;
+foreach($comments as $comment)
+ echo $comment->content;
+~~~
+
+Більш докладно використання та визначення звʼязків описано у
+[повному керівництві](/doc/guide/uk/database.arr).
+
+Додаємо властивість `url`
+-------------------------
+
+Кожному запису відповідає унікальний URL.
+Замість повсюдного виклику [CWebApplication::createUrl] для формування цього URL,
+ми можемо додати властивість `url` моделі `Post` і повторно використовувати
+код для генерації URL. Пізніше ми опишемо, як отримати гарні URL.
+Використання властивості моделі дозволить реалізувати це максимально зручно.
+
+Для того, щоб додати властивість `url`, ми додаємо геттер у клас `Post`:
+
+~~~
+[php]
+class Post extends CActiveRecord
+{
+ public function getUrl()
+ {
+ return Yii::app()->createUrl('post/view', array(
+ 'id'=>$this->id,
+ 'title'=>$this->title,
+ ));
+ }
+}
+~~~
+
+На додаток до ID запису, в URL через GET-параметр ми виводимо заголовок.
+Робиться це головним чином для оптимізації під пошукові алгоритми (SEO).
+Детальніше це буде описано в розділі «[людинозрозумілі URL](/doc/blog/final.url)».
+
+Так як [CComponent] є предком класу `Post`, геттер `getUrl()` дозволяє нам
+писати код на зразок `$post->url`. При зверненні до `$post->url` буде викликаний
+геттер і ми отримаємо результат його виконання.
+Більш докладно це описано у [повному керівництві](/doc/guide/uk/basics.component).
+
+Текстове представлення для статусу
+----------------------------------
+
+Так як статус запису зберігається у БД у вигляді числа, нам необхідно отримати його
+текстове представлення для відображення користувачам. Для великих систем така
+вимога є досить типовою.
+
+Для зберігання звʼязків між цілими числами і їх текстовим поданням, необхідним
+іншим обʼєктам даних, ми використовуємо таблицю `tbl_lookup`. Для більш зручного
+отримання текстових даних змінимо модель `Lookup` наступним чином:
+
+~~~
+[php]
+class Lookup extends CActiveRecord
+{
+ …
+
+ private static $_items=array();
+
+ public static function items($type)
+ {
+ if(!isset(self::$_items[$type]))
+ self::loadItems($type);
+ return self::$_items[$type];
+ }
+
+ public static function item($type,$code)
+ {
+ if(!isset(self::$_items[$type]))
+ self::loadItems($type);
+ return isset(self::$_items[$type][$code]) ? self::$_items[$type][$code] : false;
+ }
+
+ private static function loadItems($type)
+ {
+ self::$_items[$type]=array();
+ $models=self::model()->findAll(array(
+ 'condition'=>'type=:type',
+ 'params'=>array(':type'=>$type),
+ 'order'=>'position',
+ ));
+ foreach($models as $model)
+ self::$_items[$type][$model->code]=$model->name;
+ }
+}
+~~~
+
+Ми додали два статичних методи: `Lookup::items()` та `Lookup::item()`.
+Перший повертає список рядків для заданого типу даних, другий - конкретний
+рядок для заданого типу даних і значення.
+
+У базі даних блогу є два типи даних: `PostStatus` та `CommentStatus`.
+Перший містить можливі статуси запису, другий - статуси коментаря.
+
+Для того, щоб зробити код більш читабельним ми описуємо константи, відповідні
+цілочисловим значенням статусу. Ці константи необхідно використовувати у коді
+замість відповідних їм цілих значень.
+
+~~~
+[php]
+class Post extends CActiveRecord
+{
+ const STATUS_DRAFT=1;
+ const STATUS_PUBLISHED=2;
+ const STATUS_ARCHIVED=3;
+ ......
+}
+~~~
+
+Отже, для отримання списку всіх можливих статусів запису (масиву рядків
+із ключами, рівними відповідним їм значенням), ми можемо скористатися кодом
+`Lookup::items('PostStatus')`. А для отримання конкретного рядка - кодом
+`Lookup::item('PostStatus', Post::STATUS_PUBLISHED)`.
View
157 docs/blog/uk/prototype.auth.txt
@@ -0,0 +1,157 @@
+Аутентифікація користувача
+==========================
+
+Наш додаток повинен розрізняти власника системи та гостей. Тому
+ми повинні реалізувати функцію
+[користувальницької аутентифікації](/doc/guide/uk/topics.auth).
+
+Ви, можливо, помітили, що каркас додатка вже реалізує аутентифікацію,
+перевіряючи, чи є імʼя користувача та пароль значеннями `demo` та `admin`.
+У цьому розділі ми змінимо відповідний код так, щоб аутентифікація
+відбувалася за допомогою таблиці `User` БД.
+
+Аутентифікація користувача виконується у класі, який реалізує інтерфейс
+[IUserIdentity]. Для цієї мети каркас додатка використовує клас `UserIdentity`.
+Клас знаходиться у файлі `/wwwroot/blog/protected/components/UserIdentity.php`.
+
+> Tip|Підказка: У відповідності з угодою, назва файла класу має бути
+> такою ж, як імʼя класу плюс розширення `.php`. Можна звернутися до класу, використовуючи
+> [псевдонім шляху](/doc/guide/uk/basics.namespace). Наприклад, ми
+> можемо звернутися до класу `UserIdentity` використовуючи псевдонім
+> `application.components.UserIdentity`. Багато методів API у Yii можуть розпізнати
+> псевдоніми шляху (наприклад, [Yii::createComponent()|YiiBase::createComponent]).
+> Це дозволяє уникнути використання абсолютних шляхів у коді, які створюють
+> неприємності при розгортанні додатка.
+
+Модифікуємо клас `UserIdentity` наступним чином:
+
+~~~
+[php]
+<?php
+class UserIdentity extends CUserIdentity
+{
+ private $_id;
+
+ public function authenticate()
+ {
+ $username=strtolower($this->username);
+ $user=User::model()->find('LOWER(username)=?',array($username));
+ if($user===null)
+ $this->errorCode=self::ERROR_USERNAME_INVALID;
+ else if(!$user->validatePassword($this->password))
+ $this->errorCode=self::ERROR_PASSWORD_INVALID;
+ else
+ {
+ $this->_id=$user->id;
+ $this->username=$user->username;
+ $this->errorCode=self::ERROR_NONE;
+ }
+ return $this->errorCode==self::ERROR_NONE;
+ }
+
+ public function getId()
+ {
+ return $this->_id;
+ }
+}
+~~~
+
+У методі `authenticate()` ми використовуємо клас `User` для пошуку рядка у таблиці `tbl_user`,
+в якій значення поля `username` таке ж, як отримане імʼя користувача без урахування регістра.
+Памʼятайте, що клас `User` був створений, використовуючи інструмент `gii` у попередньому розділі.
+Оскільки клас `User` успадковується від класу [CActiveRecord], ми можемо використовувати
+[можливості ActiveRecord](/doc/guide/uk/database.ar) для того, щоб звертатися до таблиці `tbl_user` в ОО манері.
+
+Для того, щоб перевірити чи ввів користувач правильний пароль, ми викликаємо метод `validatePassword` класу `User`.
+Нам необхідно змінити файл `/wwwroot/blog/protected/models/User.php` як показано нижче.
+Відзначимо, що замість зберігання пароля у БД у явному вигляді,
+ми використовуємо хеш від пароля та ключа (солі), що генерується випадковим чином.
+При перевірці введеного користувачем пароля, замість порівняння паролів, ми повинні порівнювати хеші.
+Ми використовуємо стандартну PHP-функцію `crypt()` для хешування і перевірки пароля.
+Ознайомтеся з вікі-статтею [Use crypt() for password storage](http://www.yiiframework.com/wiki/425),
+яка описує всі подробиці роботи з даною функцією.
+
+~~~
+[php]
+class User extends CActiveRecord
+{
+ ......
+ public function validatePassword($password)
+ {
+ return crypt($password,$this->password)===$this->password;
+ }
+
+ public function hashPassword($password)
+ {
+ return crypt($password, $this->generateSalt());
+ }
+}
+~~~
+
+У класі `UserIdentity` ми також перевизначаємо метод `getId()`,
+який повертає значення `id` користувача, знайденого у таблиці `tbl_user`.
+Батьківська реалізація повернула б імʼя користувача замість `id`.
+І `username` і `id` будуть збережені в сесії і доступні через `Yii::app()->user`
+у будь-якому місці нашого коду.
+
+> Tip|Підказка: У класі `UserIdentity` ми використовуємо [CUserIdentity]
+> без явного підключення відповідного файлу. Це можливо тому
+> [CUserIdentity] — один з класів ядра фреймворку Yii. Yii буде
+> автоматично підключати файл класу для будь-якого класу ядра, коли до нього
+> звернуться вперше. Те ж можливо з класом `User` тому що він розташований у директорії
+> `/wwwroot/blog/protected/models`, яка була додана до параметру
+> `include_path` PHP в конфігурації додатка наступним чином:
+>
+> ~~~
+> [php]
+> return array(
+> …
+> 'import'=>array(
+> 'application.models.*',
+> 'application.components.*',
+> ),
+> …
+> );
+> ~~~
+>
+> Конфігурація вище говорить, що будь-який клас, файл якого розташований
+> в директорії `/wwwroot/blog/protected/models` або
+> `/wwwroot/blog/protected/components`, буде автоматично підключений, коли до
+> класу звернуться вперше.
+
+Клас `UserIdentity` використовується класом `LoginForm` для аутентифікації користувача,
+заснованої на введених імені та паролі, отриманих на сторінці входу в систему.
+Наступний фрагмент коду показує як використовується клас `UserIdentity`:
+
+~~~
+[php]
+$identity=new UserIdentity($username,$password);
+$identity->authenticate();
+switch($identity->errorCode)
+{
+ case UserIdentity::ERROR_NONE:
+ Yii::app()->user->login($identity);
+ break;
+ …
+}
+~~~
+
+
+> Info|Інформація: Люди часто плутаються в ідентифікації (identity) та компоненті
+> додатка `user`. Перша представляє спосіб виконання аутентифікації, у той
+> час як останній використовується, щоб надати інформацію, повʼязану з
+> поточним користувачем. У додатка може бути тільки один компонент `user`,
+> але один або кілька класів ідентифікації, в залежності від того, яку
+> аутентифікацію підтримує додаток. При аутентифікації,
+> екземпляр ідентифікації може передати деяку інформацію компоненту `user`.
+> Ця інформація буде глобально доступна через компонент `user`.
+
+Щоб перевірити змінений клас `UserIdentity`, ми можемо відкрити адресу
+`http://www.example.com/blog/index.php` і спробувати увійти з імʼям
+користувача та паролем, які ми зберігаємо у таблиці `tbl_user`.
+Якщо ми використовуємо базу даних, надану
+[демонстраційною версією блогу](http://www.yiiframework.com/demos/blog/),
+ми можемо увійти з імʼям користувача `demo` і паролем `demo`.
+Відзначимо, що ця система блогу не забезпечує функцію управління користувачами.
+Тому користувач не може змінити свій обліковий запис або створити новий через веб-інтерфейс.
+Функцію управління користувачами можна розглядати як майбутнє розширення до додатка блогу.
View
101 docs/blog/uk/prototype.database.txt
@@ -0,0 +1,101 @@
+Налаштування бази даних
+=======================
+
+Ми створили каркас додатка і закінчили зі структурою БД.
+У даному розділі ми створимо саму базу і налаштуємо підключення до неї.
+
+Створення бази даних
+--------------------
+
+Для створення бази даних ми використовуємо SQLite.
+Оскільки підтримка баз даних в Yii заснована на [PDO](http://www.php.net/manual/en/book.pdo.php),
+ми можемо легко перемикатися між різними СУБД (наприклад, MySQL, PostgreSQL)
+без необхідності змінювати наш код.
+
+Створюємо файл бази даних `blog.db` в директорії `/wwwroot/blog/protected/data`.
+І каталог і файл бази даних повинні мати можливість перезапису процесом
+Web-сервера, як цього вимагає SQLite.
+Ми можемо просто скопіювати файл бази даних з демонстраційною версією блогу із встановленої копії Yii.
+Він розташований у директорії `/wwwroot/yii/demos/blog/protected/data/blog.db`.
+Щоб створити базу даних вручну, можна виконати SQL-вирази із файлу
+`/wwwroot/yii/demos/blog/protected/data/schema.sqlite.sql`.
+
+> Tip|Підказка: Щоб виконати SQL-вирази, необхідно використовувати інструмент
+> командного рядка `sqlite3`, який може бути знайдений на
+> [офіційному Web-сайті SQLite](http://www.sqlite.org/download.html).
+
+
+Встановлення підключення до бази даних
+--------------------------------------
+
+Щоб використовувати базу даних блогу у нашому додатку, необхідно задати
+[конфігурацію додатка](/doc/guide/uk/basics.application#application-configuration),
+що знаходиться у PHP-файлі `/wwwroot/blog/protected/config/main.php`.
+Код даного файлу повертає асоціативний масив, що містить пари імʼя-значення,
+кожна з яких використовується для ініціалізації доступних для запису властивостей
+[екземпляра додатка](/doc/guide/uk/basics.application).
+
+Налаштуємо компонент додатка `db` наступним чином:
+
+~~~
+[php]
+return array(
+ …
+ 'components'=>array(
+ …
+ 'db'=>array(
+ 'connectionString'=>'sqlite:/wwwroot/blog/protected/data/blog.db',
+ 'tablePrefix'=>'tbl_',
+ ),
+ ),
+ …
+);
+~~~
+
+Конфігурація вище говорить, що
+[компонент додатка](/doc/guide/uk/basics.application#application-component)
+`db` є екземпляром класу [CDbConnection] із властивістю
+`connectionString`, початкове значення якого
+`sqlite:/wwwroot/blog/protected/data/blog.db` та властивістю `tablePrefix`
+зі значенням `tbl_`.
+
+Тепер ми можемо використовувати підключення до бази даних `Yii::app()->db`
+у будь-якому місці нашого коду. Зауважимо, що вираз
+`Yii::app()` повертає екземпляр додатка, який ми створюємо у вхідному сценарії.
+За більш детальним описом методів та властивостей підключення до БД,
+можна звернутися до [опису його класу|CDbConnection].
+Однак, у більшості випадків, підключення до БД використовується не безпосередньо, а через
+[ActiveRecord](/doc/guide/uk/database.ar).
+
+Зупинимося докладніше на властивості `tablePrefix`, яке ми використовували під час налаштування.
+Дана властивість задає префікс `tbl_` для таблиць БД.
+Тобто, якщо імʼя таблиці у SQL-виразі укладено у подвійні фігурні дужки
+(наприклад, `{{post}}`), то компонент `db`, перед тим, як відправити запит СУБД,
+перетворює його у імʼя таблиці з префіксом (наприклад, `tbl_post`).
+Дана можливість особливо корисна, якщо у майбутньому ми плануємо змінити префікс таблиць без зміни коду.
+Наприклад, якщо ми розробляємо CMS, ми можемо використовувати дану можливість,
+щоб при установці користувач міг вибрати потрібний йому префікс.
+
+> Tip|Підказка: Якщо ви бажаєте використовувати для зберігання даних замість SQLite
+> СУБД MySQL, ви можете створити БД `blog`, використовуючи SQL із файла
+> `/wwwroot/yii/demos/blog/protected/data/schema.mysql.sql`.
+> Після цього змініть налаштування додатка наступним чином:
+>
+> ~~~
+> [php]
+> return array(
+> …
+> 'components'=>array(
+> …
+> 'db'=>array(
+> 'connectionString' => 'mysql:host=localhost;dbname=blog',
+> 'emulatePrepare' => true,
+> 'username' => 'root',
+> 'password' => '',
+> 'charset' => 'utf8',
+> 'tablePrefix' => 'tbl_',
+> ),
+> ),
+> …
+> );
+> ~~~
View
152 docs/blog/uk/prototype.scaffold.txt
@@ -0,0 +1,152 @@
+Генерація каркаса
+=================
+
+Create, read, update та delete (CRUD) — чотири основних операції,
+за допомогою яких можна управляти обʼєктами даних.
+Так як реалізація CRUD є типовим завданням для будь-якого веб-додатка,
+для автоматизації можна скористатися спеціальними інструментами для генерації коду
+*Gii* (також відомим як *скаффолдінг*).
+
+> Note|Примітка: Gii доступний починаючи з версії 1.1.2. До цього ми б використовували
+[yiic shell](/doc/guide/uk/quickstart.first-app-yiic).
+
+Далі ми опишемо як використовувати цей інструмент для реалізації операцій CRUD записів та коментарів нашого блогу.
+
+Встановлення Gii
+----------------
+
+Для початку необхідно встановити Gii. Відкриваємо файл
+`/wwwroot/blog/protected/config/main.php` та додаємо наступне:
+
+~~~
+[php]
+return array(
+ ......
+ 'import'=>array(
+ 'application.models.*',
+ 'application.components.*',
+ ),
+
+ 'modules'=>array(
+ 'gii'=>array(
+ 'class'=>'system.gii.GiiModule',
+ 'password'=>'ваш пароль',
+ ),
+ ),
+);
+~~~
+
+Код вище включає модуль з імʼям `gii`, який дозволяє нам використовувати
+Gii за наступним URL:
+
+~~~
+http://www.example.com/blog/index.php?r=gii
+~~~
+
+Буде запитаний пароль, який ми вказали у `/wwwroot/blog/protected/config/main.php`.
+Після цього буде показана сторінка з усіма доступними інструментами генерації коду.
+
+> Note|Примітка: Код, наведений вище не повинен потрапити на сервер.
+Інструменти для генерації коду повинні використовуватися тільки при розробці.
+
+Створення моделей
+-----------------
+
+Для початку нам необхідно створити [класи моделей](/doc/guide/uk/basics.model)
+для кожної із таблиць у БД. Ці класи дозволять нам працювати з БД у стилі ООП,
+що буде показано далі у цьому посібнику.
+
+Запускаємо `Model Generator`.
+На сторінці вводимо `tbl_user` (імʼя таблиці, що зберігає користувачів) у поле `Table Name`,
+`tbl_` у поле `Table Prefix` та натискаємо кнопку `Preview`.
+Буде показана таблиця, посилання у якій дозволять переглянути код, який ми збираємося згенерувати.
+Якщо все у порядку, можна натискати `Generate`. При цьому код буде збережений у файл.
+
+> Info|Інформація: Так як генератору коду необхідно зберегти код у файли,
+процес повинен мати права на створення та зміну відповідних файлів.
+Найпростіше надати процесу права на запис у всю директорію `/wwwroot/blog`.
+Варто відзначити, що це потрібно зробити лише на машині розробника
+при використанні `Gii`.
+
+Повторимо ті самі дії для всіх інших таблиць БД, включаючи
+`tbl_post`, `tbl_comment`, `tbl_tag` та `tbl_lookup`.
+
+> Tip|Підказка: Також ми можемо ввести `*` у поле `Table Name`.
+Так ми згенеруємо моделі для *кожної* таблиці БД за один раз.
+
+На даному етапі у нас будуть створені наступні файли:
+
+ * `models/User.php` містить клас `User`, який успадковується від
+ [CActiveRecord] та може використовуватися для звернення до таблиці `tbl_user`;
+ * `models/Post.php` містить клас `Post`, який успадковується від
+ [CActiveRecord] та може використовуватися для звернення до таблиці `tbl_post`;
+ * `models/Tag.php` містить клас `Tag`, який успадковується від
+ [CActiveRecord] та може використовуватися для звернення до таблиці `tbl_tag`;
+ * `models/Comment.php` містить клас `Comment`, який успадковується від
+ [CActiveRecord] та може використовуватися для звернення до таблиці `tbl_comment`;
+ * `models/Lookup.php` містить клас `Lookup`, який успадковується від
+ [CActiveRecord] та може використовуватися для звернення до таблиці `tbl_lookup`.
+
+Реалізація операцій CRUD
+------------------------
+
+Після того, як були створені класи моделі, ми можемо використовувати `Crud Generator`
+для генерації коду операцій CRUD для них. Зробимо це для моделей `Post` та `Comment`.
+
+На сторінці `Crud Generator` введемо `Post` (імʼя моделі запису блогу, яку ми створили раніше)
+у поле `Model Class` та натиснемо `Preview`, а потім `Generate`.
+
+Повторимо ці ж дії для моделі `Comment`.
+
+Розглянемо згенеровані файли у `/wwwroot/blog/protected`.
+Для зручності згрупуємо їх у [файли контролерів](/doc/guide/uk/basics.controller) та
+[файли представлень](/doc/guide/uk/basics.view):
+
+ - файли контролерів:
+ * `Controllers/PostController.php` містить клас `PostController`, який є контролером,
+ відповідальним за всі операції CRUD для записів;
+ * `Controllers/CommentController.php` містить клас `CommentController`,
+ який є контролером, відповідальним за всі операції CRUD для коментарів;
+
+ - файли представлень:
+ * `views/post/create.php` — файл представлення, який відображає HTML-форму для створення запису;
+ * `views/post/update.php` — файл представлення, який відображає HTML-форму для оновлення запису;
+ * `views/post/view.php` — файл представлення, який відображає детальну інформацію запису;
+ * `views/post/index.php` — файл представлення, який відображає список записів;
+ * `views/post/admin.php`— файл представлення, який відображає записи у таблиці з адміністративними командами;
+ * `views/post/_form.php` — частковий файл представлення, що використовується у
+ `views/post/create.php` та `views/post/update.php`. Він відображає HTML-форму для збору інформації про запис;
+ * `views/post/_view.php` — частковий файл представлення, що використовується у
+ `views/post/index.php`. Він відображає короткий вигляд окремого запису.
+ * `views/post/_search.php`— файл представлення, що використовується у `views/post/admin.php`.
+ Використовується для форми пошуку.
+ * Подібний набір файлів представлень також згенерований для коментарів.
+
+Тестування
+----------
+
+Ми можемо перевірити роботу згенерованого коду, використовуючи наступні URL:
+
+~~~
+http://www.example.com/blog/index.php?r=post
+http://www.example.com/blog/index.php?r=comment
+~~~
+
+Варто відзначити, що можливості по управлінню записами та коментарями повністю незалежні.
+Також, при створенні нового запису або коментаря необхідно вводити такі дані,
+як `author_id` та `create_time`, що в реальному додатку повинно робитися автоматично.
+Не турбуйтеся, ми виправимо ці проблеми далі.
+На даний момент прототип вже містить більшість можливостей, необхідних нашому блогу.
+
+Щоб краще зрозуміти, як використовуються файли вище, розглянемо,
+що відбувається при відображенні списку записів:
+
+ 0. Користувач запитує URL `http://www.example.com/blog/index.php?r=post`;