Permalink
Browse files

migration step1

  • Loading branch information...
1 parent f392682 commit 0ff0c9c7b33b28224a3fffabe853bdcd2d02d2a1 @yandod committed Apr 3, 2012
Showing with 19,735 additions and 0 deletions.
  1. +150 −0 app/Console/Command/AdminShell.php
  2. +31 −0 app/Console/Command/AppShell.php
  3. +160 −0 app/Console/Command/CcPluginShell.php
  4. 0 {cake/tests/test_app/views/scaffolds → app/Console/Command/Task}/empty
  5. 0 {cake/tests/test_app/views/pages → app/Console/Templates}/empty
  6. +33 −0 app/Console/cake
  7. +32 −0 app/Console/cake.bat
  8. +33 −0 app/Console/cake.php
  9. +357 −0 app/Controller/AccountController.php
  10. +180 −0 app/Controller/AdminController.php
  11. +491 −0 app/Controller/AppController.php
  12. +162 −0 app/Controller/AttachmentsController.php
  13. +176 −0 app/Controller/Component/FetcherComponent.php
  14. +382 −0 app/Controller/Component/MailerComponent.php
  15. +157 −0 app/Controller/Component/MenuManagerComponent.php
  16. +152 −0 app/Controller/Component/QueriesComponent.php
  17. +130 −0 app/Controller/Component/SortComponent.php
  18. +51 −0 app/Controller/Component/UsersComponent.php
  19. +107 −0 app/Controller/CustomFieldsController.php
  20. +101 −0 app/Controller/EnumerationsController.php
  21. +62 −0 app/Controller/IssueCategoriesController.php
  22. +98 −0 app/Controller/IssueRelationsController.php
  23. +177 −0 app/Controller/IssueStatusesController.php
  24. +1,133 −0 app/Controller/IssuesController.php
  25. +68 −0 app/Controller/JournalsController.php
  26. +80 −0 app/Controller/MembersController.php
  27. +212 −0 app/Controller/MyController.php
  28. +332 −0 app/Controller/NewsController.php
  29. +1,000 −0 app/Controller/ProjectsController.php
  30. +149 −0 app/Controller/QueriesController.php
  31. +450 −0 app/Controller/ReportsController.php
  32. +286 −0 app/Controller/RolesController.php
  33. +204 −0 app/Controller/SearchController.php
  34. +101 −0 app/Controller/SettingsController.php
  35. +252 −0 app/Controller/TimelogController.php
  36. +87 −0 app/Controller/TrackersController.php
  37. +237 −0 app/Controller/UsersController.php
  38. +97 −0 app/Controller/VersionsController.php
  39. +90 −0 app/Controller/WatchersController.php
  40. 0 app/{controllers/welcome_controller.php → Controller/WelcomeController.php}
  41. +449 −0 app/Controller/WikiController.php
  42. +38 −0 app/Controller/WikisController.php
  43. +114 −0 app/Controller/WorkflowsController.php
  44. +120 −0 app/MimeType.php
  45. +274 −0 app/Model/AppModel.php
  46. 0 app/{models/attachment.php → Model/Attachment.php}
  47. 0 app/{models/auth_source.php → Model/AuthSource.php}
  48. 0 app/{models/auth_source_ldap.php → Model/AuthSourceLdap.php}
  49. 0 app/{models/behaviors/activity_provider.php → Model/Behavior/ActivityProviderBehavior.php}
  50. 0 app/{models/behaviors/attachable.php → Model/Behavior/AttachableBehavior.php}
  51. +17 −0 app/Model/Behavior/CandyBehavior.php
  52. 0 app/{models/behaviors/customizable.php → Model/Behavior/CustomizableBehavior.php}
  53. 0 app/{models/behaviors/event.php → Model/Behavior/EventBehavior.php}
  54. 0 app/{models/behaviors/list.php → Model/Behavior/ListBehavior.php}
  55. 0 app/{models/behaviors/ploymorphic.php → Model/Behavior/PolymorphicBehavior.php}
  56. 0 app/{models/behaviors/revision.php → Model/Behavior/RevisionBehavior.php}
  57. 0 app/{models/behaviors/searchable.php → Model/Behavior/SearchableBehavior.php}
  58. 0 app/{models/behaviors/timestamp.php → Model/Behavior/TimestampBehavior.php}
  59. 0 app/{models/behaviors/watchable.php → Model/Behavior/WatchableBehavior.php}
  60. 0 app/{models/board.php → Model/Board.php}
  61. 0 app/{models/change.php → Model/Change.php}
  62. 0 app/{models/changeset.php → Model/Changeset.php}
  63. 0 app/{models/comment.php → Model/Comment.php}
  64. 0 app/{models/custom_field.php → Model/CustomField.php}
  65. 0 app/{models/custom_fields_project.php → Model/CustomFieldsProject.php}
  66. 0 app/{models/custom_fields_tracker.php → Model/CustomFieldsTracker.php}
  67. 0 app/{models/custom_value.php → Model/CustomValue.php}
  68. 0 {cake/tests/test_app/views/helpers → app/Model/Datasource}/empty
  69. +63 −0 app/Model/Document.php
  70. 0 app/{models/enabled_module.php → Model/EnabledModule.php}
  71. 0 app/{models/enumeration.php → Model/Enumeration.php}
  72. 0 app/{models/event.php → Model/Event.php}
  73. 0 app/{models/issue.php → Model/Issue.php}
  74. 0 app/{models/issue_category.php → Model/IssueCategory.php}
  75. 0 app/{models/issue_custom_field.php → Model/IssueCustomField.php}
  76. 0 app/{models/issue_relation.php → Model/IssueRelation.php}
  77. 0 app/{models/issue_status.php → Model/IssueStatus.php}
  78. 0 app/{models/journal.php → Model/Journal.php}
  79. 0 app/{models/journal_detail.php → Model/JournalDetail.php}
  80. 0 app/{models/member.php → Model/Member.php}
  81. 0 app/{models/message.php → Model/Message.php}
  82. 0 app/{models/message_observer.php → Model/MessageObserver.php}
  83. 0 app/{models/news.php → Model/News.php}
  84. 0 app/{models/permission.php → Model/Permission.php}
  85. +758 −0 app/Model/Project.php
  86. 0 app/{models/project_custom_field.php → Model/ProjectCustomField.php}
  87. +847 −0 app/Model/Query.php
  88. 0 app/{models/report.php → Model/Report.php}
  89. 0 app/{models/repository.php → Model/Repository.php}
  90. 0 app/{models/role.php → Model/Role.php}
  91. +206 −0 app/Model/Setting.php
  92. +430 −0 app/Model/TimeEntry.php
  93. 0 app/{models/time_entry_custom_field.php → Model/TimeEntryCustomfield.php}
  94. 0 app/{models/token.php → Model/Token.php}
  95. 0 app/{models/tracker.php → Model/Tracker.php}
  96. 0 app/{models/user.php → Model/User.php}
  97. 0 app/{models/user_custom_field.php → Model/UserCustomField.php}
  98. 0 app/{models/user_preference.php → Model/UserPreference.php}
  99. 0 app/{models/version.php → Model/Version.php}
  100. 0 app/{models/watcher.php → Model/Watcher.php}
  101. 0 app/{models/wiki.php → Model/Wiki.php}
  102. 0 app/{models/wiki_content.php → Model/WikiContent.php}
  103. 0 app/{models/wiki_content_version.php → Model/WikiContentVersion.php}
  104. +340 −0 app/Model/WikiPage.php
  105. 0 app/{models/wiki_redirect.php → Model/WikiRedirect.php}
  106. 0 app/{models/workflow.php → Model/Workflow.php}
  107. +86 −0 app/Test/Case/behaviors/ActivityProviderTest.php
  108. 0 {cake/tests/test_app/views/errors → app/Test/Case/behaviors}/empty
  109. +119 −0 app/Test/Case/candycane/PluginContainerTest.php
  110. +186 −0 app/Test/Case/components/FetcherComponentTest.php
  111. 0 {cake/tests/test_app/views/elements → app/Test/Case/components}/empty
  112. 0 {cake/tests/test_app/vendors/shells/templates → app/Test/Case/controllers}/empty
  113. +330 −0 app/Test/Case/helpers/AppFormTest.php
  114. 0 {cake/tests/test_app/vendors/shells/tasks → app/Test/Case/helpers}/empty
  115. +93 −0 app/Test/Case/models/IssueStatusTest.php
  116. +414 −0 app/Test/Case/models/IssueTest.php
  117. +31 −0 app/Test/Case/models/ProjectTest.php
  118. +51 −0 app/Test/Case/models/RoleTest.php
  119. +311 −0 app/Test/Case/models/TimeEntryTest.php
  120. +70 −0 app/Test/Case/models/WatcherTest.php
  121. 0 {cake/tests/test_app/plugins/test_plugin_two/vendors/shells/templates → app/Test/Case/models}/empty
  122. +16 −0 app/Test/Fixture/attachment_fixture.php
  123. +19 −0 app/Test/Fixture/board_fixture.php
  124. +11 −0 app/Test/Fixture/changeset_fixture.php
  125. +12 −0 app/Test/Fixture/changesets_issue_fixture.php
  126. +9 −0 app/Test/Fixture/comment_fixture.php
  127. +31 −0 app/Test/Fixture/custom_field_fixture.php
  128. +7 −0 app/Test/Fixture/custom_fields_project_fixture.php
  129. +16 −0 app/Test/Fixture/custom_value_fixture.php
  130. +8 −0 app/Test/Fixture/document_fixture.php
  131. 0 {cake/tests/test_app/plugins/test_plugin_two/vendors/shells/tasks → app/Test/Fixture}/empty
  132. +26 −0 app/Test/Fixture/enabled_module_fixture.php
  133. +26 −0 app/Test/Fixture/enumeration_fixture.php
  134. +12 −0 app/Test/Fixture/issue_category_fixture.php
  135. +136 −0 app/Test/Fixture/issue_fixture.php
  136. +7 −0 app/Test/Fixture/issue_relation_fixture.php
  137. +20 −0 app/Test/Fixture/issue_status_fixture.php
  138. +17 −0 app/Test/Fixture/journal_detail_fixture.php
  139. +33 −0 app/Test/Fixture/journal_fixture.php
  140. +20 −0 app/Test/Fixture/member_fixture.php
  141. +49 −0 app/Test/Fixture/message_fixture.php
  142. +12 −0 app/Test/Fixture/news_fixture.php
  143. +35 −0 app/Test/Fixture/project_fixture.php
  144. +25 −0 app/Test/Fixture/projects_tracker_fixture.php
  145. +159 −0 app/Test/Fixture/role_fixture.php
  146. +11 −0 app/Test/Fixture/time_entry_fixture.php
  147. +9 −0 app/Test/Fixture/token_fixture.php
  148. +17 −0 app/Test/Fixture/tracker_fixture.php
  149. +48 −0 app/Test/Fixture/user_fixture.php
  150. +33 −0 app/Test/Fixture/user_preference_fixture.php
  151. +25 −0 app/Test/Fixture/version_fixture.php
  152. +9 −0 app/Test/Fixture/watcher_fixture.php
  153. +74 −0 app/Test/Fixture/wiki_content_fixture.php
  154. +7 −0 app/Test/Fixture/wiki_content_version_fixture.php
  155. +9 −0 app/Test/Fixture/wiki_fixture.php
  156. +13 −0 app/Test/Fixture/wiki_page_fixture.php
  157. +7 −0 app/Test/Fixture/wiki_redirect_fixture.php
  158. +276 −0 app/Test/Fixture/workflow_fixture.php
  159. 0 {cake/tests/test_app/plugins/test_plugin/vendors/shells/templates → app/Test/groups}/empty
  160. 0 app/{views/auth_sources → View/AuthSources}/_form.ctp
  161. 0 app/{views/auth_sources → View/AuthSources}/edit.ctp
  162. 0 app/{views/auth_sources → View/AuthSources}/list.ctp
  163. 0 app/{views/auth_sources → View/AuthSources}/new.ctp
  164. +76 −0 app/View/CandyView.php
  165. +6 −0 app/View/CustomFields/edit.ctp
  166. +60 −0 app/View/CustomFields/list.ctp
  167. +7 −0 app/View/CustomFields/new.ctp
  168. 0 app/{views/helpers/app_ajax.php → View/Helper/AppAjaxHelper.php}
  169. 0 app/{app_helper.php → View/Helper/AppHelper.php}
  170. +1,552 −0 app/View/Helper/CandyHelper.php
  171. +235 −0 app/View/Helper/CustomFieldHelper.php
  172. +235 −0 app/View/Helper/IssuesHelper.php
  173. +56 −0 app/View/Helper/JournalsHelper.php
  174. 0 app/{views/helpers/project.php → View/Helper/ProjectHelper.php}
  175. +128 −0 app/View/Helper/QueriesHelper.php
  176. 0 app/{views/helpers/query_column.php → View/Helper/QueryColumnHelper.php}
  177. 0 app/{views/helpers/reports.php → View/Helper/ReportsHelper.php}
  178. +81 −0 app/View/Helper/SearchHelper.php
  179. +197 −0 app/View/Helper/SortHelper.php
  180. 0 app/{views/helpers/tcpdf.php → View/Helper/TcpdfHelper.php}
  181. +209 −0 app/View/Helper/TimelogHelper.php
  182. +190 −0 app/View/Helper/UnifiedDiffHelper.php
  183. +85 −0 app/View/Helper/UsersHelper.php
  184. 0 app/{views/helpers/version.php → View/Helper/VersionHelper.php}
  185. +66 −0 app/View/Helper/WatchersHelper.php
  186. 0 app/{views/helpers/wiki.php → View/Helper/WikiHelper.php}
  187. 0 app/{views/issue_categories → View/IssueCategories}/_form.ctp
  188. +19 −0 app/View/IssueCategories/destroy.ctp
  189. +17 −0 app/View/IssueCategories/edit.ctp
  190. 0 app/{views/issue_relations → View/IssueRelations}/_form.ctp
  191. 0 app/{views/issue_relations → View/IssueRelations}/add.ctp
  192. 0 app/{views/issue_relations → View/IssueRelations}/destroy.ctp
  193. 0 app/{views/issue_statuses → View/IssueStatuses}/_form.ctp
  194. +19 −0 app/View/IssueStatuses/edit.ctp
  195. +42 −0 app/View/IssueStatuses/list.ctp
  196. +18 −0 app/View/IssueStatuses/new.ctp
  197. +45 −0 app/View/account/login.ctp
  198. +12 −0 app/View/account/lost_password.ctp
  199. +23 −0 app/View/account/password_recovery.ctp
  200. +58 −0 app/View/account/register.ctp
  201. +75 −0 app/View/account/show.ctp
  202. +59 −0 app/View/admin/index.ctp
  203. +12 −0 app/View/admin/info.ctp
  204. +69 −0 app/View/admin/plugins.ctp
  205. +115 −0 app/View/admin/projects.ctp
  206. +14 −0 app/View/attachments/diff.ctp
  207. +14 −0 app/View/attachments/file.ctp
  208. 0 app/{views → View}/common/403.ctp
  209. 0 app/{views → View}/common/404.ctp
  210. 0 app/{views → View}/common/_calendar.ctp
  211. 0 app/{views → View}/common/_diff.ctp
  212. 0 app/{views → View}/common/_file.ctp
  213. +3 −0 app/View/common/_preview.ctp
  214. +29 −0 app/View/common/feed.atom.ctp
  215. 0 app/{views → View}/common/feed.atom.rxml
  216. +11 −0 app/View/elements/account_menu.ctp
  217. +8 −0 app/View/elements/attachments/form.ctp
  218. +19 −0 app/View/elements/attachments/links.ctp
  219. +157 −0 app/View/elements/custom_fields/form.ctp
  220. +90 −0 app/View/elements/diff.ctp
  221. 0 {cake/tests/test_app/plugins/test_plugin/vendors/shells/tasks → app/View/elements}/empty
  222. +13 −0 app/View/elements/enumerations/_form.ctp
  223. +41 −0 app/View/elements/error_explanation.ctp
  224. +16 −0 app/View/elements/file.ctp
  225. +71 −0 app/View/elements/issues/edit.ctp
  226. +143 −0 app/View/elements/issues/form.ctp
  227. +19 −0 app/View/elements/issues/history.ctp
  228. +47 −0 app/View/elements/issues/list.ctp
  229. +31 −0 app/View/elements/issues/list_simple.ctp
  230. +62 −0 app/View/elements/issues/relations.ctp
  231. 0 app/{views → View}/elements/issues/show_details_bottom.ctp
  232. +39 −0 app/View/elements/issues/sidebar.ctp
  233. +23 −0 app/View/elements/journals/notes_form.ctp
  234. +14 −0 app/View/elements/mailer/issue_text_plain.ctp
  235. 0 app/{views → View}/elements/my/blocks/calendar.ctp
  236. 0 app/{views → View}/elements/my/blocks/documents.ctp
  237. +32 −0 app/View/elements/my/blocks/issuesassignedtome.ctp
  238. +35 −0 app/View/elements/my/blocks/issuesreportedbyme.ctp
  239. 0 app/{views → View}/elements/my/blocks/issueswatched.ctp
  240. 0 app/{views → View}/elements/my/blocks/news.ctp
  241. 0 app/{views → View}/elements/my/blocks/timelog.ctp
  242. +10 −0 app/View/elements/my/sidebar.ctp
  243. +20 −0 app/View/elements/news.ctp
  244. +9 −0 app/View/elements/news/_form.ctp
  245. +19 −0 app/View/elements/project_selector.ctp
  246. +4 −0 app/View/elements/projects/edit.ctp
  247. +104 −0 app/View/elements/projects/form.ctp
  248. +1 −0 app/View/elements/projects/rss.ctp
  249. 0 app/{views → View}/elements/projects/settings/boards.ctp
  250. +23 −0 app/View/elements/projects/settings/issue_categories.ctp
  251. +85 −0 app/View/elements/projects/settings/members.ctp
  252. +33 −0 app/View/elements/projects/settings/modules.ctp
  253. 0 app/{views → View}/elements/projects/settings/repository.ctp
  254. +31 −0 app/View/elements/projects/settings/versions.ctp
  255. +39 −0 app/View/elements/projects/settings/wiki.ctp
  256. +16 −0 app/View/elements/projects/sidebar/activity.ctp
  257. +14 −0 app/View/elements/projects/sidebar/changelog.ctp
  258. +19 −0 app/View/elements/projects/sidebar/roadmap.ctp
  259. +20 −0 app/View/elements/projects/sidebar/show.ctp
  260. 0 app/{views → View}/elements/queries/columns.ctp
Sorry, we could not display the entire diff because too many files (2,199) changed.
View
150 app/Console/Command/AdminShell.php
@@ -0,0 +1,150 @@
+<?php
+/**
+ *
+ * @url http://c-brains.jp/blog/wsg/09/07/22-170044.php
+ * @url http://d.hatena.ne.jp/yuhei_kagaya/20080730/1217421386
+ * @url http://blog.takeda-soft.jp/blog/show/187
+ */
+
+App::import('Model', 'ConnectionManager');
+
+class AdminShell extends Shell {
+
+ //var $tasks = array('Menu1','Menu2','Menu3');
+
+ //overrideでcakeメッセージ除去
+ function startup(){}
+
+ function main() {
+ //メインメニュー作成
+ $mainMenu = array(
+ '1' => array('name' => 'Menu2','alt' => '初期設定を行う'),
+ '2' => array('name' => 'Menu1','alt' => '指定ユーザをadminに昇格'),
+ //'3' => array('name' => 'Menu3','alt' => 'メニュー3'),
+ 'q' => array('name' => null,'alt' => '終了'),
+ );
+ $mainMenuKeys = array_keys($mainMenu);
+
+ //メインメニュー表示
+ $value = "";
+ $this->out("---------------------------------------------");
+ foreach($mainMenu as $k => $v) {
+ $this->out("[{$k}] {$v['alt']}");
+ }
+ $this->out("---------------------------------------------");
+ while ($value <> "q") {
+ $value = $this->in("実行するメニューの番号を選択してください", $mainMenuKeys, "q" );
+
+ if ($value <> 'q') {
+ //$this->$mainMenu[$value]['name']->execute();
+ }
+
+ switch ($value) {
+ case '1':
+ $this->install();
+ break;
+ case '2':
+ $this->upgrade();
+ break;
+ }
+ }
+ }
+
+ function install()
+ {
+ $db_config = new DATABASE_CONFIG;
+
+ $db_config = $db_config->default;
+ unset($db_config['persistent']);
+
+ $keys = array_flip(array_keys($db_config));
+ while (true) {
+ $this->out("---------------------------------------------");
+ $this->out("DATABASE_CONFIG:");
+ foreach($db_config as $k => $v) {
+ $this->out("[{$keys[$k]}] [{$k}] {$v}");
+ }
+ $this->out("---------------------------------------------");
+ $value = $this->in("変更したい項目の番号を選択してください。これでよければyを押してください。", array_values($keys), "q");
+
+ if ($value == 'q') {
+ break;
+ }
+
+ if ($value == 'y') {
+
+ // 接続確認
+ $db = ConnectionManager::getDataSource('default');
+ $db->config = array_merge($db->_baseConfig, $db_config);
+ if ($db->connect() === false) {
+ $this->err("接続できません。データベースの設定が間違っている可能性があります。");
+ $db->disconnect();
+ continue;
+ } else {
+ $db->disconnect();
+ }
+
+ // @TODO database.php.installの内容をもとにデータをいれる
+ $this->rewriteConfig($db_config);
+
+ // @TODO dump.sqlを実行
+ $dump_file = APP . 'config' . DS . 'sql' . DS . 'dump.sql';
+ $db->query(file_get_contents($dump_file));
+
+ $this->out("Install finished!");
+ exit;
+ break;
+ }
+
+ if (in_array($value, array_values($keys))) {
+ $buf = array_keys($db_config);
+ $key_name = $buf[$value];
+ $db_config_value = $this->in("{$key_name}を入力してください。");
+ $db_config[$key_name] = $db_config_value;
+ }
+ }
+ }
+
+ private function rewriteConfig($db_config)
+ {
+ $base_file = APP . 'config' . DS . 'database.php.install';
+ $target_file = APP . 'config' . DS . 'database.php';
+
+ if (!file_exists($base_file)) {
+ $this->err("database.php.install not found.");
+ exit;
+ }
+
+ $text = file_get_contents($base_file);
+ $text = str_replace('{default_host}', $db_config['host'], $text);
+ $text = str_replace('{default_login}', $db_config['login'], $text);
+ $text = str_replace('{default_password}', $db_config['password'], $text);
+ $text = str_replace('{default_database}', $db_config['database'], $text);
+ $text = str_replace('{default_prefix}', $db_config['prefix'], $text);
+
+ file_put_contents($target_file, $text);
+ }
+
+ /**
+ * upgrade
+ * 指定ユーザをadminに昇格
+ */
+ function upgrade()
+ {
+ $db = ConnectionManager::getDataSource('default');
+ $value = $this->in("昇格したいユーザ名を入力してください。");
+ $result = $db->query('SELECT * FROM users WHERE login = ?;', array($value), false);
+ if (count($result) == 0) {
+ $this->err('入力されたユーザは存在しません。');
+ return;
+ }
+
+ if ($result[0]['users']['admin'] === '1') {
+ $this->err('入力されたユーザはすでにadminです。');
+ return;
+ }
+
+ $db->query('UPDATE users SET admin = 1 WHERE login = ?', array($value));
+ $this->out("ユーザの昇格に成功しました。");
+ }
+}
View
31 app/Console/Command/AppShell.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * AppShell file
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link http://cakephp.org CakePHP(tm) Project
+ * @since CakePHP(tm) v 2.0
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+App::uses('Shell', 'Console');
+
+/**
+ * Application Shell
+ *
+ * Add your application-wide methods in the class below, your shells
+ * will inherit them.
+ *
+ * @package app.Console.Command
+ */
+class AppShell extends Shell {
+
+}
View
160 app/Console/Command/CcPluginShell.php
@@ -0,0 +1,160 @@
+<?php
+/**
+ * Task class for creating a plugin
+ *
+ * @package cake
+ * @subpackage cake.cake.console.libs.tasks
+ */
+class CcPluginShell extends Shell {
+/**
+ * initialize
+ *
+ * @return void
+ */
+ function initialize() {
+ $this->path = APP . 'plugins' . DS;
+ }
+/**
+ * Execution method always used for tasks
+ *
+ * @return void
+ */
+ function main() {
+ if (empty($this->params['skel'])) {
+ $this->params['skel'] = '';
+ if (is_dir(CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'skel') === true) {
+ $this->params['skel'] = CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'skel';
+ }
+ }
+
+ $plugin = null;
+
+ if (isset($this->args[0])) {
+ $plugin = Inflector::camelize($this->args[0]);
+ $pluginPath = Inflector::underscore($plugin) . DS;
+ $this->bake($plugin);
+ } else {
+ $this->__interactive($plugin);
+ }
+ }
+
+/**
+ * Interactive interface
+ *
+ * @access private
+ * @return void
+ */
+ function __interactive($plugin = null) {
+ while ($plugin === null || substr($plugin,0,2) !== 'Cc') {
+ $plugin = $this->in(__('Enter the name of the plugin in CamelCase format start with "Cc"'));
+ }
+
+ $this->bake($plugin);
+ }
+
+/**
+ * Bake the plugin, create directories and files
+ *
+ * @params $plugin name of the plugin in CamelCased format
+ * @access public
+ * @return bool
+ */
+ function bake($plugin) {
+
+ $pluginPath = Inflector::underscore($plugin);
+
+ if ( substr($pluginPath,0,3) !== 'cc_' ) {
+ $this->err('Plugin name must start with "Cc"');
+ return;
+ }
+
+ $this->hr();
+ $this->out("Plugin Name: $plugin");
+ $this->out("Plugin Directory: {$this->path}{$pluginPath}");
+ $this->hr();
+
+
+ $looksGood = $this->in('Look okay?', array('y', 'n', 'q'), 'y');
+
+ if (strtolower($looksGood) == 'y' || strtolower($looksGood) == 'yes') {
+ $verbose = $this->in(__('Do you want verbose output?'), array('y', 'n'), 'n');
+
+ $Folder = new Folder($this->path . $pluginPath);
+ $directories = array('models' . DS . 'behaviors', 'controllers' . DS . 'components', 'views' . DS . 'helpers');
+
+ foreach ($directories as $directory) {
+ $Folder->create($this->path . $pluginPath . DS . $directory);
+ }
+
+ if (strtolower($verbose) == 'y' || strtolower($verbose) == 'yes') {
+ foreach ($Folder->messages() as $message) {
+ $this->out($message);
+ }
+ }
+
+ $errors = $Folder->errors();
+ if (!empty($errors)) {
+ return false;
+ }
+
+ $controllerFileName = $pluginPath . '_app_controller.php';
+
+ $out = "<?php\n\n";
+ $out .= "class {$plugin}AppController extends AppController {\n\n";
+ $out .= "}\n\n";
+ $out .= "?>";
+ $this->createFile($this->path . $pluginPath. DS . $controllerFileName, $out);
+
+ $modelFileName = $pluginPath . '_app_model.php';
+
+ $out = "<?php\n\n";
+ $out .= "class {$plugin}AppModel extends AppModel {\n\n";
+ $out .= "}\n\n";
+ $out .= "?>";
+ $this->createFile($this->path . $pluginPath . DS . $modelFileName, $out);
+
+ $out = "<?php\n";
+ $out .= "\$pluginContainer = ClassRegistry::getObject('PluginContainer');\n";
+ $out .= "\$pluginContainer->installed('{$pluginPath}','0.1');\n";
+ $out .= "\n\n";
+ $this->createFile($this->path . $pluginPath . DS . 'init.php', $out);
+
+ $out = "<?php\n";
+ $out .= "\tclass HomeController extends {$plugin}AppController {\n";
+ $out .= "\tpublic \$uses = array('Issue');\n";
+ $out .= "\tpublic function index() {\n";
+ $out .= "\t\t\$this->set('count',\$this->Issue->find('count'));\n";
+ $out .= "\t}\n";
+ $out .= "}\n";
+ $out .= "\n";
+ $this->createFile($this->path . $pluginPath . DS . 'controllers/home_controller.php', $out);
+
+ $out = "";
+ $out .= "Hello CandyCane {$plugin} Plugin.<br/>";
+ $out .= "You have <?php echo \$count ?> of issues.";
+ $out .= "\n\n";
+ $this->createFile($this->path . $pluginPath . DS . 'views/home/index.ctp', $out);
+
+ $this->hr();
+ $this->out(sprintf(__("Created: %s in %s"), $plugin, $this->path . $pluginPath));
+ $this->hr();
+ }
+
+ return true;
+ }
+/**
+ * Help
+ *
+ * @return void
+ * @access public
+ */
+ function help() {
+ $this->hr();
+ $this->out("Usage: cake cc_plugin <name>");
+ $this->hr();
+ $this->out('Commands:');
+ $this->out("\n\tcc_plugin <name>\n\t\tbakes plugin directory structure and init.php.");
+ $this->out("");
+ $this->_stop();
+ }
+}
View
0 cake/tests/test_app/views/scaffolds/empty → app/Console/Command/Task/empty 100755 → 100644
File renamed without changes.
View
0 cake/tests/test_app/views/pages/empty → app/Console/Templates/empty
File renamed without changes.
View
33 app/Console/cake
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+################################################################################
+#
+# Bake is a shell script for running CakePHP bake script
+# PHP 5
+#
+# CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+# Copyright 2005-2012, Cake Software Foundation, Inc.
+#
+# Licensed under The MIT License
+# Redistributions of files must retain the above copyright notice.
+#
+# @copyright Copyright 2005-2012, Cake Software Foundation, Inc.
+# @link http://cakephp.org CakePHP(tm) Project
+# @package app.Console
+# @since CakePHP(tm) v 2.0
+# @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+#
+################################################################################
+LIB=$(cd -P -- "$(dirname -- "$0")" && pwd -P) && LIB=$LIB/$(basename -- "$0")
+
+while [ -h "$LIB" ]; do
+ DIR=$(dirname -- "$LIB")
+ SYM=$(readlink "$LIB")
+ LIB=$(cd "$DIR" && cd $(dirname -- "$SYM") && pwd)/$(basename -- "$SYM")
+done
+
+LIB=$(dirname -- "$LIB")/
+APP=`pwd`
+
+exec php -q "$LIB"cake.php -working "$APP" "$@"
+
+exit;
View
32 app/Console/cake.bat
@@ -0,0 +1,32 @@
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::
+:: Bake is a shell script for running CakePHP bake script
+:: PHP 5
+::
+:: CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+:: Copyright 2005-2012, Cake Software Foundation, Inc.
+::
+:: Licensed under The MIT License
+:: Redistributions of files must retain the above copyright notice.
+::
+:: @copyright Copyright 2005-2012, Cake Software Foundation, Inc.
+:: @link http://cakephp.org CakePHP(tm) Project
+:: @package app.Console
+:: @since CakePHP(tm) v 2.0
+:: @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+:: In order for this script to work as intended, the cake\console\ folder must be in your PATH
+
+@echo.
+@echo off
+
+SET app=%0
+SET lib=%~dp0
+
+php -q "%lib%cake.php" -working "%CD% " %*
+
+echo.
+
+exit /B %ERRORLEVEL%
View
33 app/Console/cake.php
@@ -0,0 +1,33 @@
+#!/usr/bin/php -q
+<?php
+/**
+ * Command-line code generation utility to automate programmer chores.
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link http://cakephp.org CakePHP(tm) Project
+ * @package app.Console
+ * @since CakePHP(tm) v 2.0
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+$ds = DIRECTORY_SEPARATOR;
+$dispatcher = 'Cake' . $ds . 'Console' . $ds . 'ShellDispatcher.php';
+
+if (function_exists('ini_set')) {
+ $root = dirname(dirname(dirname(__FILE__)));
+ ini_set('include_path', $root . $ds. 'lib' . PATH_SEPARATOR . ini_get('include_path'));
+}
+
+if (!include($dispatcher)) {
+ trigger_error('Could not locate CakePHP core files.', E_USER_ERROR);
+}
+unset($paths, $path, $dispatcher, $root, $ds);
+
+return ShellDispatcher::run($argv);
View
357 app/Controller/AccountController.php
@@ -0,0 +1,357 @@
+<?php
+/**
+ * account_controller.php
+ *
+ */
+
+/**
+ * AccountController
+ *
+ * @todo implement yet
+ */
+class AccountController extends AppController {
+
+ public $uses = array('User', 'Project', 'Setting', 'Event', 'Issue', 'Token');
+
+ public $helpers = array('Text');
+
+ public $components = array('Fetcher', 'RequestHandler', 'Mailer');
+
+ // helper :custom_fields
+ // include CustomFieldsHelper
+
+ // prevents login action to be filtered by check_if_login_required application scope filter
+
+/**
+ * beforeFilter
+ *
+ * skip_before_filter :check_if_login_required, :only => [:login, :lost_password, :register, :activate]
+ */
+ public function beforeFilter() {
+ $skip_action = array('login', 'lost_password', 'register', 'activate');
+ if (!in_array($this->request->action, $skip_action)) {
+ parent::beforeFilter();
+ } else {
+ $this->setSettings(); // todo: kimoi
+ $this->set_localization();
+ $this->set('currentuser',aa('logged',false));
+ }
+ }
+
+/**
+ * activate
+ *
+ * Token based account activation
+ */
+ public function activate() {
+
+ if (
+ !isset($this->request->params['url']['token']) ||
+ $this->Setting->self_registration !== '1'
+ ) {
+ $this->redirect('/');
+ }
+ $token = $this->Token->find('first',array(
+ 'conditions' => array(
+ 'Token.action' => 'register',
+ 'Token.value' => $this->request->params['url']['token']
+ )
+ ));
+
+ if (
+ $token == false ||
+ // todo: expired
+ $token['User']['status'] !== '2'
+ ) {
+ $this->redirect('/');
+ }
+
+ $data = array(
+ 'id' => $token['User']['id'],
+ 'status' => 1 //active
+ );
+
+ if ($this->User->save($data)) {
+ $this->Token->destroy($data['id'], 'register');
+ $this->Session->setFlash(
+ __('Your account has been activated. You can now log in.'),
+ 'default',
+ array('class' => 'flash flash_notice')
+ );
+ }
+ $this->redirect('/account/login');
+ }
+
+/**
+ * register
+ *
+ * User self-registration
+ *
+ */
+ public function register() {
+ if (!$this->Setting->self_registration || $this->Session->read('auth_source_registration')) {
+ $this->redirect('/');
+ return;
+ }
+
+ if (!$this->request->data) {
+ $this->Session->write('auth_source_registration', null);
+ $this->request->data['User']['language'] = $this->Setting->default_language;
+ return;
+ }
+
+ $this->request->data['User']['admin'] = 0;
+ $this->request->data['User']['status'] = 2; // registred
+
+ if ($this->Session->read('auth_source_registration')) {
+ $this->request->data['User']['status'] = 1; // active
+
+ $auth_source_registration = $this->Session->read('auth_source_registration');
+ $this->request->data['User']['login'] = $auth_source_registration['login'];
+
+ $this->request->data['User']['auth_source_id'] = $auth_source_registration['auth_source_id'];
+ if ($this->User->save($this->request->data)) {
+ $this->Session->write('auth_source_registration', null);
+ $this->logged_user($this->User->findByLogin($this->request->data['User']['login']));
+
+ # flash[:notice] = l(:notice_account_activated)
+ $this->redirect('/my/account');
+ }
+ } else {
+ switch ($this->Setting->self_registration) {
+ case '1':
+ // Email activation
+ if ($this->User->save($this->request->data)) {
+ $user = $this->User->findByLogin($this->request->data['User']['login']);
+ $data = array(
+ 'user_id' => $user['User']['id'],
+ 'action' => 'register',
+ 'value' => $this->Token->_generate_token_value(),
+ );
+ if ($this->Token->save($data)) {
+ $this->Mailer->deliver_register($data,$user);
+ $this->Session->setFlash(__('Account was successfully created. To activate your account, click on the link that was emailed to you.'), 'default', array('class' => 'flash flash_notice'));
+ $this->redirect('/account/login');
+ }
+ }
+ break;
+ case '3':
+ // Automatic activation
+ $this->request->data['User']['status'] = 1;
+ if ($this->User->save($this->request->data)) {
+ $this->logged_user($this->User->findByLogin($this->request->data['User']['login']));
+ $this->Session->setFlash(
+ __('Your account has been activated. You can now log in.'),
+ 'default',
+ array('class' => 'flash flash_notice')
+ );
+ $this->redirect('/my/account');
+ }
+ break;
+ default:
+ // Manual activation by the administrator
+ if ($this->User->save($this->request->data)) {
+ // Sends an email to the administrators
+ $this->Mailer->deliver_account_activation_request(
+ $this->User->findByLogin($this->request->data['User']['login']),
+ $this->User
+ );
+ $this->Session->setFlash(
+ __('Your account was created and is now pending administrator approval.'),
+ 'default',
+ array('class' => 'flash flash_notice')
+ );
+ $this->redirect('/account/login');
+ }
+ }
+ }
+ }
+
+/**
+ * login
+ *
+ * Login request and validation
+ *
+ * @todo implement yet
+ */
+ public function login() {
+ $this->set('setting', $this->Setting);
+
+ if (!$this->request->data) {
+ $this->set('back_url',$this->referer());
+ return;
+ }
+ $this->set('back_url',$this->request->params['form']['back_url']);
+ // validate
+ $this->User->set($this->request->data);
+ if (!$this->User->validates()) {
+ return;
+ }
+
+ // Authenticate user
+ $user = $this->User->tryToLogin($this->request->data['User']['username'], $this->request->data['User']['password']);
+
+ if ($user === false) {
+ // Invalid credentials
+ $this->Session->setFlash(__('Invalid user or password'), 'default', array('class' => 'flash flash_error'));
+ return;
+ } else if ((bool)$this->User->data) {
+ // (bool)$this->User->data == new_record
+ // Onthefly creation failed, display the registration form to fill/fix attributes
+ #@user = user
+ #session[:auth_source_registration] = {:login => user.login, :auth_source_id => user.auth_source_id }
+ $this->render('register');
+ } else {
+ // Valid user
+ $this->logged_user($user);
+
+ ## generate a key and set cookie if autologin
+ #if params[:autologin] && Setting.autologin?
+ # token = Token.create(:user => user, :action => 'autologin')
+ # cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
+ #end
+ #redirect_back_or_default :controller => 'my', :action => 'page'
+ if (!$this->request->params['form']['back_url'][0] == '/' ||
+ Router::url($this->request->params['form']['back_url']) == Router::url($this->request->action)
+ ) {
+ $this->request->params['form']['back_url'] = '/';
+ }
+ $this->redirect($this->request->params['form']['back_url']);
+ }
+ }
+
+/**
+ * logout
+ *
+ * Log out current user and redirect to welcome page
+ *
+ * @todo implement yet
+ */
+ public function logout() {
+ $this->Session->destroy();
+ #cookies.delete :autologin
+ #Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin']) if User.current.logged?
+ #self.logged_user = nil
+ $this->redirect('/');
+ }
+
+/**
+ * show
+ *
+ * Show user's account
+ *
+ * @todo implement yet
+ * @todo custom values
+ */
+ public function show($id) {
+ $id = (int)$id;
+
+ $user = $this->User->find('first',
+ array(
+ 'conditions' => array('User.id' => $id),
+ 'recursive' => 2)
+ );
+
+ if (!is_array($user)) {
+ $this->cakeError('error', array('message' => "user id {$id} not found."));
+ }
+
+ $this->set('user', $user);
+ #@custom_values = @user.custom_values
+ $this->Fetcher->fetch($this->current_user,array('author' => $user['User']['id']));
+ $events = $this->Fetcher->events(null, null, array('limit' => 10));
+ $events_by_day_data = $this->Event->group_by($events, 'event_date');
+ $this->set('events_by_day_data',$events_by_day_data);
+ $this->set('issue_count',$this->Issue->find('count',aa('conditions',aa('author_id',$user['User']['id']))));
+ # events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
+ # @events_by_day = events.group_by(&:event_date)
+ # rescue ActiveRecord::RecordNotFound
+ # render_404
+ }
+
+/**
+ * lost_password
+ *
+ * Enable user to choose a new password
+ */
+ public function lost_password() {
+ if (!$this->Setting->lost_password) {
+ $this->redirect('/');
+ }
+
+ if (isset($this->request->params['named']['token'])) {
+ $token = $this->Token->find('first',
+ array('conditions' => array('Token.action' => 'recovery', 'Token.value' => $this->request->params['named']['token']))
+ );
+
+ if ($this->Token->isExpired($token)) {
+ // @TODO add error message
+ $this->redirect('/');
+ }
+ if ($this->RequestHandler->isPost()) {
+ if ($this->request->data['User']['new_password'] != $this->request->data['User']['new_password_confirmation']) {
+ // @TODO add error message
+ $this->render('password_recovery');
+ return;
+ }
+
+ $user = $this->User->findById($token['Token']['user_id']);
+ $user['User']['password'] = $this->request->data['User']['new_password'];
+ $user['User']['password_confirmation'] = $this->request->data['User']['new_password_confirmation'];
+
+ if ($this->User->save($user)) {
+ $this->Token->destroy($user['User']['id'], 'recovery');
+ $this->Session->setFlash(__('Password was successfully updated.'), 'default', array('class' => 'flash flash_notice'));
+ $this->redirect('/login');
+ return;
+ }
+ }
+
+ $this->set('token', $token);
+ $this->render('password_recovery');
+ return;
+ }
+
+ if ($this->RequestHandler->isPost()) {
+ $user = $this->User->findByMail($this->request->data['User']['mail']);
+
+ # user not found in db
+ if ($user === false) {
+ // @TODO add error message
+ return;
+ }
+
+ # create a new token for password recovery
+ $data = array(
+ 'user_id' => $user['User']['id'],
+ 'action' => 'recovery',
+ 'value' => $this->Token->_generate_token_value(), // @TODO more smart
+ );
+
+ if ($token = $this->Token->save($data)) {
+ $this->Mailer->deliver_lost_password($token, $user);
+ $this->Session->setFlash(__('An email with instructions to choose a new password has been sent to you.'), 'default', array('class' => 'flash flash_notice'));
+ $this->redirect('/login');
+ }
+ }
+ }
+
+/**
+ * logged_user
+ * @access private
+ */
+ public function logged_user($user) {
+ if (isset($user['User'])) {
+ $user = $user['User'];
+ }
+
+ if (isset($user) && is_array($user)) {
+ $this->currentuser = $this->User->findById($user['id']);
+ $this->Session->write('user_id', $user['id']);
+ } else {
+ $this->currentuser = null;
+ $this->Session->write('user_id', null);
+ }
+ }
+
+}
View
180 app/Controller/AdminController.php
@@ -0,0 +1,180 @@
+<?php
+/**
+ * Admin Controller
+ *
+ * @package candycane
+ * @subpackage candycane.controllers
+ */
+# helper :sort
+# include SortHelper
+#
+# def index
+# @no_configuration_data = Redmine::DefaultData::Loader::no_data?
+# end
+#
+
+# def plugins
+# @plugins = Redmine::Plugin.all
+# end
+#
+# # Loads the default configuration
+# # (roles, trackers, statuses, workflow, enumerations)
+# def default_configuration
+# if request.post?
+# begin
+# Redmine::DefaultData::Loader::load(params[:lang])
+# flash[:notice] = l(:notice_default_data_loaded)
+# rescue Exception => e
+# flash[:error] = l(:error_can_t_load_default_data, e.message)
+# end
+# end
+# redirect_to :action => 'index'
+# end
+#
+#
+# def info
+# @db_adapter_name = ActiveRecord::Base.connection.adapter_name
+# @flags = {
+# :default_admin_changed => User.find(:first, :conditions => ["login=? and hashed_password=?", 'admin', User.hash_password('admin')]).nil?,
+# :file_repository_writable => File.writable?(Attachment.storage_path),
+# :plugin_assets_writable => File.writable?(Engines.public_directory),
+# :rmagick_available => Object.const_defined?(:Magick)
+# }
+# end
+#end
+class AdminController extends AppController {
+
+ var $name = 'Admin';
+ var $uses = array('Project');
+ var $helpers = array('Candy');
+ var $components = array('Sort','Mailer');
+
+/**
+ * beforeFilter
+ *
+ * # before_filter :require_admin
+ */
+ public function beforeFilter() {
+ parent::beforeFilter();
+ $this->require_admin();
+ }
+
+ function index()
+ {
+ }
+
+ /**
+ * projects
+ *
+ */
+ function projects()
+ {
+ $this->Sort->sort_init('name', 'asc');
+ $this->Sort->sort_update(
+ array('name', 'is_public', 'created_on')
+ );
+
+ if (isset($this->request->params['url']['status'])) {
+ $status = (int)$this->request->params['url']['status'];
+ } else {
+ $status = 1;
+ }
+
+ $this->set('status', $status);
+
+ $status_options = array(
+ '' => __('all'),
+ 1 => __('active'),
+ );
+
+ $this->set('status_options', $status_options);
+
+ if ($status == '1') {
+ $condition = array('Project.status' => $status);
+ } else {
+ $condition = array();
+ }
+
+ $name = null;
+ if(!empty($this->request->params['url']['name'])) {
+ $name = $this->request->params['url']['name'];
+ $q_name = "%{$name}%";
+ $condition['LOWER(Project.identifier) LIKE ? OR LOWER(Project.name) LIKE ?'] = array($q_name, $q_name);
+ }
+
+ $this->set('name', $name);
+
+# @project_count = Project.count(:conditions => c.conditions)
+# @project_pages = Paginator.new self, @project_count,
+# per_page_option,
+# params['page']
+# @projects = Project.find :all, :order => sort_clause,
+# :conditions => c.conditions,
+# :limit => @project_pages.items_per_page,
+# :offset => @project_pages.current.offset
+#
+
+ // @todo fix limit count
+ $projects = $this->Project->find('all',
+ array(
+ 'recursive' => 0,
+ 'conditions' => $condition,
+ 'limit' => 10,
+ )
+ );
+
+ $this->set('projects', $projects);
+# render :action => "projects", :layout => false if request.xhr?
+
+ }
+
+ public function plugins() {
+ $pluginContainer = ClassRegistry::getObject('PluginContainer');
+ $pluginContainer->fetchEntry();
+ $this->set('plugins',$pluginContainer->getEntries());
+ }
+
+ public function installPlugin($id){
+ $pluginContainer = ClassRegistry::getObject('PluginContainer');
+ $pluginContainer->fetchEntry();
+ if ($pluginContainer->install($id)) {
+ $this->Session->setFlash(sprintf(__('Installed plugin: %s'),$id), 'default', array('class'=>'flash flash_notice'));
+ }
+ $this->redirect('plugins');
+ }
+
+ public function uninstallPlugin($id){
+ $pluginContainer = ClassRegistry::getObject('PluginContainer');
+ $pluginContainer->fetchEntry();
+ if ($pluginContainer->uninstall($id)) {
+ $this->Session->setFlash(sprintf(__('Uninstalled plugin: %s'),$id), 'default', array('class'=>'flash flash_notice'));
+ }
+ $this->redirect('plugins');
+ }
+
+ public function default_configration() {
+ }
+
+ public function test_email() {
+ if ($this->Mailer->deliver_test($this->current_user)) {
+ $this->Session->setFlash(sprintf(__('An email was sent to %s'),$this->current_user['mail']), 'default', array('class'=>'flash flash_notice'));
+ } else {
+ $this->Session->setFlash(sprintf(__('An error occurred while sending mail (%s)'),$this->current_user['mail']), 'default', array('class'=>'flash flash_error'));
+ }
+ $this->redirect(array(
+ 'controller' => 'settings',
+ 'action' => 'edit',
+ 'tab' => 'notifications'
+ ));
+ }
+
+/**
+ * info
+ *
+ */
+ public function info() {
+ $db =& ConnectionManager::getDataSource($this->Project->useDbConfig);
+ $this->set('db_driver', $db->config['driver']);
+ }
+
+}
View
491 app/Controller/AppController.php
@@ -0,0 +1,491 @@
+<?php
+#require 'uri'
+#require 'cgi'
+
+App::import('Core', 'l10n');
+App::import('View', 'CandyView');
+
+/**
+ * Application Controller
+ *
+ * @package candycane
+ */
+class AppController extends Controller {
+
+ public $layout = 'base';
+
+ public $helpers = array('Html', 'Form', 'Javascript', 'Candy');
+
+ public $components = array('Cookie', 'MenuManager','DebugKit.Toolbar');
+
+ public $uses = array('User', 'Setting', 'Project');
+
+ public $current_user; // alternate User.current
+
+ public $per_page;
+
+ public $view = 'Candy';
+
+ public $theme = '';
+
+ public $pure_params = array();
+
+ public $authorize = false;
+
+/**
+ * beforeFilter
+ *
+ * @todo set_localzation
+ */
+ function beforeFilter() {
+ $this->_setUrlParam();
+ $this->user_setup();
+ $this->setSettings();
+ $this->set_localization();
+ $this->check_if_login_required();
+ $this->_findProject();
+ $this->_authorize();
+ }
+
+/**
+ * Set URL Parameters
+ *
+ * @return void
+ */
+ function _setUrlParam() {
+ $url_param = $this->request->params;
+ foreach (array(
+ 'data',
+ 'url',
+ 'form',
+ 'isAjax',
+ 'plugin',
+ 'models',
+ 'pass',
+ 'named',
+ ) as $key) {
+ unset($url_param[$key]);
+ }
+ $this->request->params['url_param'] = $url_param;
+ }
+
+# filter_parameter_logging :password
+#
+# include Redmine::MenuManager::MenuController
+# helper Redmine::MenuManager::MenuHelper
+#
+# REDMINE_SUPPORTED_SCM.each do |scm|
+# require_dependency "repository/#{scm.underscore}"
+# end
+#
+# def current_role
+# @current_role ||= User.current.role_for_project(@project)
+# end
+#
+
+/**
+ * User Setup
+ *
+ * @return void
+ * @todo Setting.check_cache
+ */
+ public function user_setup() {
+ # # Check the settings cache for each request
+ # Setting.check_cache
+
+ // Find the current user
+ $this->current_user = $this->find_current_user();
+ $this->set('currentuser', $this->current_user);
+ }
+
+/**
+ * Find the current logged in user
+ *
+ * Returns the current user or nil if no user is logged in
+ *
+ * @return void
+ * @todo Setting.autologin
+ * @todo auto_login
+ * @todo rss key authentication
+ */
+ protected function _find_current_user() {
+ if ($this->Session->read('user_id')) {
+ // existing session
+ return $this->User->find_by_id_logged($this->Session->read('user_id'));
+ # (User.active.find(session[:user_id]) rescue nil)
+ } else if ($this->Cookie->read('autologin')) {
+ # elsif cookies[:autologin] && Setting.autologin?
+ # # auto-login feature
+ # User.find_by_autologin_key(cookies[:autologin])
+ } elseif (!empty($this->request->params['url']['key'])) {
+ // from rss reader
+ $user = $this->User->find_by_rss_key($this->request->params['url']['key']);
+ if (!empty($user)) {
+ $user = $this->User->find_by_id_logged($user['id']);
+ }
+ if (empty($user)) {
+ throw new NotFoundException();
+ }
+ return $user;
+ } else {
+ $user = $this->User->anonymous();
+ $user['User']['logged'] = false;
+ $user['User']['name'] = $user['User']['login'];
+ $user['User']['memberships'] = array();
+ return $user['User'];
+ }
+ return null;
+ }
+
+/**
+ * Find current user
+ *
+ * @return mixed Current User
+ */
+ public function find_current_user() {
+ return $this->_find_current_user();
+ }
+
+/**
+ * check_if_login_required
+ *
+ * check if login is globally required to access the application
+ *
+ * @todo implement Setting.login_required
+ * @todo logged?
+ */
+/**
+ * Check if login is required
+ *
+ * check if login is globally required to access the application
+ *
+ * @return mixed
+ */
+ public function check_if_login_required() {
+ // no check needed if user is already logged in
+ if ($this->current_user['logged']) {
+ return true;
+ }
+
+ if ($this->Setting->login_required) {
+ $this->require_login();
+ }
+ }
+
+/**
+ * Set Localization
+ *
+ * @return void
+ */
+ public function set_localization() {
+ # User.current.language = nil unless User.current.logged?
+ # lang = begin
+ # if !User.current.language.blank? && GLoc.valid_language?(User.current.language)
+ # User.current.language
+ # elsif request.env['HTTP_ACCEPT_LANGUAGE']
+ # accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first.downcase
+ # if !accept_lang.blank? && (GLoc.valid_language?(accept_lang) || GLoc.valid_language?(accept_lang = accept_lang.split('-').first))
+ # User.current.language = accept_lang
+ # end
+ # end
+ # rescue
+ # nil
+ # end || Setting.default_language
+ # set_language_if_valid(lang)
+ $lang = null;
+ if (!empty($this->current_user['language'])) {
+ $lang = $this->current_user['language'];
+ } elseif (!empty($this->Setting->default_language)) {
+ $lang = $this->Setting->default_language;
+ }
+ $this->L10n = new L10n();
+ $this->L10n->get($lang);
+ Configure::write('Config.language', $lang);
+ }
+
+/**
+ * Require Login
+ *
+ * @return boolean
+ * @todo set back_url
+ */
+ public function require_login() {
+ if (!$this->current_user || !$this->current_user['logged']) {
+ $this->redirect('/account/login');
+ # redirect_to :controller => "account", :action => "login", :back_url => url_for(params)
+ return false;
+ }
+
+ return true;
+ }
+
+/**
+ * Require Admin
+ *
+ * @return boolean
+ */
+ public function require_admin() {
+ if (!$this->require_login()) {
+ return false;
+ }
+
+ if ($this->current_user['admin'] != 1) {
+ $this->redirect('/', 403);
+ return false;
+ }
+
+ return true;
+ }
+
+/**
+ * Deny Access
+ *
+ * @return mixed
+ */
+ public function deny_access() {
+ return $this->current_user['logged'] ? $this->cakeError('error_403') : $this->require_login();
+ }
+
+/**
+ * Authorize the user for the requested action
+ *
+ * @param string $ctrl Controller
+ * @param string $action Action
+ * @return boolean Allowed access
+ */
+ protected function _authorize($ctrl = false, $action = false) {
+ if (!empty($this->request->params['requested'])) {
+ return true;
+ }
+ if ($this->authorize === false) {
+ return true;
+ }
+ if ($ctrl === false) {
+ $ctrl = $this->request->params['controller'];
+ }
+ if ($action === false) {
+ $action = $this->request->params['action'];
+ }
+ if ($action == 'add') {
+ $action = 'new';
+ }
+ $authorize = array_merge(array('only' => array(), 'except' => array()), $this->authorize);
+ extract($authorize);
+
+ if ((!empty($only) && !in_array($action, $only)) || (!empty($except) && in_array($action, $except)) ) {
+ return true;
+ }
+ $allowed = $this->User->is_allowed_to($this->current_user, array('controller' => $ctrl, 'action' => $action), $this->_project);
+ return $allowed ? true : $this->deny_access();
+ }
+
+# # make sure that the user is a member of the project (or admin) if project is private
+# # used as a before_filter for actions that do not require any particular permission on the project
+# def check_project_privacy
+# if @project && @project.active?
+# if @project.is_public? || User.current.member_of?(@project) || User.current.admin?
+# true
+# else
+# User.current.logged? ? render_403 : require_login
+# end
+# else
+# @project = nil
+# render_404
+# false
+# end
+# end
+#
+
+/**
+ * redirect_back_or_default
+ *
+ * @param string $default_url
+ * @return void
+ */
+ public function redirect_back_or_default($default_url) {
+ if (!empty($this->request->data['back_url'])) {
+ $back_url = urldecode($this->request->data['back_url']);
+ $uri = parse_url($back_url);
+ # do not redirect user to another host or to the login or register page
+ # TODO relative
+ if (($uri['host'] == env('HTTP_HOST')) && !preg_match('/(login|account\/register)$/', $uri['path'])) {
+ $this->redirect($back_url);
+ }
+ }
+ $this->redirect($default_url);
+ }
+
+/**
+ * Render Feed
+ *
+ * @param string $event_model
+ * @param array $items Items
+ * @param array $options Options
+ * @return void
+ */
+ public function render_feed($event_model, $items, $options=array()) {
+ if (!($options['sort'] === false)) {
+ usort($items, array($event_model, 'cmp_event_datetime'));
+ }
+ unset($options['sort']);
+ $items = array_reverse($items);
+ $items = array_slice($items, 0, $this->Setting->feeds_limit);
+ $atom_title = !empty($options['title']) ? $options['title'] : $this->Setting->app_title;
+ $this->set(compact('atom_title', 'items'));
+ $this->set('EventModel', $event_model);
+ $this->helpers = array('Candy', 'Xml', 'Time');
+ $this->layout = 'rss/atom';
+ $this->render("/common/feed.atom");
+ }
+
+# def render_403
+# @project = nil
+# render :template => "common/403", :layout => !request.xhr?, :status => 403
+# return false
+# end
+#
+# def render_404
+# render :template => "common/404", :layout => !request.xhr?, :status => 404
+# return false
+# end
+#
+# def render_error(msg)
+# flash.now[:error] = msg
+# render :nothing => true, :layout => !request.xhr?, :status => 500
+# end
+#
+# def render_feed(items, options={})
+# @items = items || []
+# @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
+# @items = @items.slice(0, Setting.feeds_limit.to_i)
+# @title = options[:title] || Setting.app_title
+# render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
+# end
+#
+# def self.accept_key_auth(*actions)
+# actions = actions.flatten.map(&:to_s)
+# write_inheritable_attribute('accept_key_auth_actions', actions)
+# end
+#
+# def accept_key_auth_actions
+# self.class.read_inheritable_attribute('accept_key_auth_actions') || []
+# end
+#
+# # TODO: move to model
+# def attach_files(obj, attachments)
+# attached = []
+# unsaved = []
+# if attachments && attachments.is_a?(Hash)
+# attachments.each_value do |attachment|
+# file = attachment['file']
+# next unless file && file.size > 0
+# a = Attachment.create(:container => obj,
+# :file => file,
+# :description => attachment['description'].to_s.strip,
+# :author => User.current)
+# a.new_record? ? (unsaved << a) : (attached << a)
+# end
+# if unsaved.any?
+# flash[:warning] = l(:warning_attachments_not_saved, unsaved.size)
+# end
+# end
+# attached
+# end
+
+/**
+ * The presence of the parameter is checked in the following order, and the value matched first is returned.
+ *
+ * - $this->request->params[$name]
+ * - $this->request->params['named'][$name]
+ * - $this->request->params['url'][$name]
+ * - $this->request->data[$this->{$this->modelClass}->name][$name]
+ *
+ * @param string $name
+ * @return null if $name is not found.
+ */
+ protected function _get_param($name) {
+ if (array_key_exists($name, $this->request->params)) {
+ $value = $this->request->params[$name];
+ } elseif (array_key_exists('named', $this->request->params) && array_key_exists($name, $this->request->params['named'])) {
+ $value = $this->request->params['named'][$name];
+ } elseif (array_key_exists('url', $this->request->params) && array_key_exists($name, $this->request->params['url'])) {
+ $value = $this->request->params['url'][$name];
+ } elseif (is_array($this->request->data) && array_key_exists($this->{$this->modelClass}->name, $this->request->data) && array_key_exists($name, $this->request->data[$this->{$this->modelClass}->name])) {
+ $value = $this->request->data[$this->{$this->modelClass}->name][$name];
+ } elseif (array_key_exists('form', $this->request->params) && array_key_exists($name, $this->request->params['form'])) {
+ $value = $this->request->params['form'][$name];
+ } else {
+ $value = null;
+ }
+ return $value;
+ }
+
+/**
+ * Returns the number of objects that should be displayed
+ * on the paginated list
+ *
+ * @return int Number of objects to be displayed
+ */
+ protected function _per_page_option() {
+ if (isset($this->request->params['url']['per_page']) && in_array($this->request->params['url']['per_page'], $this->Setting->per_page_options)) {
+ $this->per_page = (int)$this->request->params['url']['per_page'];
+ $this->Session->write('per_page', $this->per_page);
+ } else if (strlen($this->Session->read('per_page'))) {
+ $this->per_page = $this->Session->read('per_page');
+ } else {
+ $this->per_page = $this->Setting->per_page_options[0];
+ }
+ return $this->per_page;
+ }
+
+/**
+ * Set Settings
+ *
+ * @return void
+ */
+ public function setSettings() {
+ $this->theme = strtolower($this->Setting->ui_theme);
+ $this->set('Settings', $this->Setting);
+ }
+
+/**
+ * Find Projects
+ *
+ * @return void
+ */
+ protected function _findProject() {
+ $project_id = $this->_get_param('project_id');
+ if (!empty($project_id)) {
+ if ($this->_project = $this->Project->findMainProject($project_id)) {
+ $this->set(array('main_project' => $this->_project));
+ $this->set('main_project', $this->_project);
+ } else {
+ throw new NotFoundException();
+ }
+ if (!$this->_isVisible($this->_project['Project']['id'])) {
+ throw new NotFoundException();
+ }
+ }
+ }
+
+/**
+ * Is Visible
+ *
+ * @param string $project_id Project ID
+ * @return boolean Visible
+ */
+ protected function _isVisible($project_id) {
+ $cond = $this->Project->get_visible_by_condition($this->current_user);
+ $cond['Project.id'] = $project_id;
+ if ( in_array($this->request->action,array('unarchive','destroy')) && $this->name == 'Projects') {
+ $cond['Project.status'] = Project::STATUS_ARCHIVED;
+ }
+
+ $visible = $this->Project->find('first', array('conditions' => $cond));
+ if ($visible == false) {
+ return false;
+ }
+ return true;
+ }
+
+}
View
162 app/Controller/AttachmentsController.php
@@ -0,0 +1,162 @@
+<?php
+## Redmine - project management software
+## Copyright (C) 2006-2008 Jean-Philippe Lang
+##
+## This program is free software; you can redistribute it and/or
+## modify it under the terms of the GNU General Public License
+## as published by the Free Software Foundation; either version 2
+## of the License, or (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+/**
+ * Attachments Controller
+ *
+ * @package candycane
+ * @subpackage candycane.controllers
+ */
+class AttachmentsController extends AppController {
+
+/**
+ * Controller name
+ *
+ * @var string
+ */
+ public $name = 'Attachments';
+
+/**
+ * Helpers
+ *
+ * @var array
+ */
+ public $helpers = array(
+ 'UnifiedDiff',
+ 'Number',
+ );
+
+/**
+ * BeforeFilter
+ *
+ * @return void
+ */
+ public function beforeFilter() {
+ parent::beforeFilter();
+ $this->_find_project();
+
+ switch ($this->request->action) {
+ case 'destroy':
+ $this->_delete_authorize();
+ break;
+ default:
+ $this->_read_authorize();
+ break;
+ }
+ }
+
+/**
+ * Show action
+ *
+ * @return void
+ */
+ function show() {
+ if ($this->Attachment->is_diff()) {
+ $diff = @file($this->Attachment->diskfile());
+ $this->set('diff', $diff);
+ $this->set('diff_type', $this->_get_param('diff_type'));
+
+ $this->render('diff');
+ } elseif ($this->Attachment->is_text()) {
+ $content = @file($this->Attachment->diskfile());
+ $this->set('content', $content);
+ $this->render('file');
+ } else {
+ $this->download();
+ }
+ }
+
+/**
+ * Download action
+ *
+ * @return void
+ */
+ function download() {
+ $data = $this->Attachment->data[$this->Attachment->alias];
+ if ($data['container_type'] == 'Version' || $data['container_type'] == 'Project') {
+ $this->Attachment->increment_download();
+ }
+ $path = pathinfo($this->Attachment->diskfile());
+ $this->view = 'Media';
+ $params = array(
+ 'id' => $data['disk_filename'],
+ 'name' => basename($data['filename'], '.'.$path['extension']),
+ 'download' => !$this->Attachment->is_image(),
+ 'path' => $path['dirname'] . DS,
+ 'extension' => $path['extension'],
+ );
+ $this->set($params);
+ }
+
+/**
+ * Destroy action
+ *
+ * @return void
+ */
+ function destroy() {
+ // Make sure association callbacks are called
+ $this->Attachment->del();
+ if ($this->referer(false)) {
+ $this->redirect($this->referer());
+ } else {
+ $this->redirect(array('controller' => 'projects', 'action' => 'show', 'id' => $this->request->params['project_id']));
+ }
+ }
+
+/**
+ * Find a project
+ *
+ * @return void
+ * @access private
+ */
+ function _find_project() {
+ $attachment = $this->Attachment->read(null, $this->request->params['id']);
+ $this->set('attachment', $attachment[$this->Attachment->alias]);
+ $this->set('author', $attachment[$this->Attachment->Author->alias]);
+
+ // Show 404 if the filename in the url is wrong
+ if (!empty($this->request->params['filename']) && $this->request->params['filename'] != $attachment[$this->Attachment->alias]['filename']) {
+ throw new NotFoundException();
+ }
+ $project = $this->Attachment->project();
+ if (!empty($project['Project']['identifier'])) {
+ $this->request->params['project_id'] = $project['Project']['identifier'];
+ parent::_findProject();
+ } else {
+ throw new NotFoundException();
+ }
+ }
+
+/**
+ * Check for read authorization
+ *
+ * @return boolean True if user can read
+ */
+ function _read_authorize() {
+ $this->Attachment->is_visible($this->current_user, $this->_project) ? true : $this->deny_access();
+ }
+
+/**
+ * Check for delete authorization