├── .coveralls.yml
├── .gitignore
├── .hound.yml
├── .rspec
├── .travis.yml
├── CHANGELOG.md
├── Gemfile
├── Gemfile.lock
├── LICENSE.txt
├── Procfile
├── README.md
├── Rakefile
├── VERSION
├── app
├── assets
│ ├── images
│ │ ├── .keep
│ │ └── loading.gif
│ ├── javascripts
│ │ ├── application.coffee
│ │ ├── bootstrap.js.coffee
│ │ ├── drag_manager.coffee
│ │ ├── kanbans_edit.js.coffee
│ │ ├── kanbans_show.js.coffee
│ │ ├── top.js.coffee
│ │ └── util.js.coffee
│ └── stylesheets
│ │ ├── application.css
│ │ ├── bootstrap_and_overrides.css.less
│ │ ├── kanbans_edit.css.scss
│ │ ├── kanbans_show.css.scss
│ │ └── top.css.scss
├── controllers
│ ├── application_controller.rb
│ ├── concerns
│ │ └── .keep
│ ├── issues_controller.rb
│ ├── kanbans_controller.rb
│ ├── sessions_controller.rb
│ └── top_controller.rb
├── helpers
│ ├── application_helper.rb
│ ├── issues_helper.rb
│ ├── kanbans_helper.rb
│ ├── people_helper.rb
│ └── top_helper.rb
├── mailers
│ └── .keep
├── models
│ ├── .keep
│ ├── concerns
│ │ └── .keep
│ ├── kanban.rb
│ ├── label.rb
│ ├── user.rb
│ └── user_kanban.rb
└── views
│ ├── kanbans
│ ├── edit.html.slim
│ ├── show.html.slim
│ └── show.json.jbuilder
│ ├── layouts
│ └── application.html.slim
│ ├── shared
│ └── _issue_panel.html.slim
│ └── top
│ ├── _logged_in.html.slim
│ ├── _not_logged_in.html.slim
│ └── index.html.slim
├── bin
├── bundle
├── rails
├── rake
└── unicorn
├── config.ru
├── config
├── application.rb
├── boot.rb
├── database.yml.mysql
├── database.yml.postgresql
├── environment.rb
├── environments
│ ├── development.rb
│ ├── production.rb
│ └── test.rb
├── gitlab.yml.sample
├── initializers
│ ├── backtrace_silencers.rb
│ ├── cookies_serializer.rb
│ ├── exception_full_backtrace.rb
│ ├── filter_parameter_logging.rb
│ ├── friendly_id.rb
│ ├── gitlab.rb
│ ├── inflections.rb
│ ├── mime_types.rb
│ ├── pusher.rb
│ ├── secret_token.rb
│ ├── session_store.rb
│ ├── url_for_with_unescape_id.rb
│ └── wrap_parameters.rb
├── locales
│ ├── en.bootstrap.yml
│ ├── en.yml
│ ├── ja.bootstrap.yml
│ └── ja.yml
├── pusher.yml.sample
├── routes.rb
└── unicorn.rb
├── db
├── migrate
│ ├── 20140110175259_create_users.rb
│ ├── 20140112144617_create_friendly_id_slugs.rb
│ ├── 20140112145704_create_kanbans.rb
│ └── 20140112164427_create_labels.rb
└── seeds.rb
├── lib
├── assets
│ └── .keep
├── support
│ ├── init.d
│ │ └── unicorn_gitpeach
│ ├── logrotate.d
│ │ └── gitpeach
│ └── nginx
│ │ └── gitpeach
└── tasks
│ ├── .keep
│ └── auto_annotate_models.rake
├── log
└── .keep
├── public
├── 404.html
├── 422.html
├── 500.html
├── favicon.ico
├── fonts
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.svg
│ ├── glyphicons-halflings-regular.ttf
│ └── glyphicons-halflings-regular.woff
├── javascripts
│ ├── jquery.js
│ ├── jquery.min.js
│ └── jquery_ujs.js
└── robots.txt
├── script
├── build_for_jenkins.sh
└── plot-rspec-slowest-examples.rb
├── shots
├── gitpeach.gif
├── issue.png
└── pusher.png
├── spec
├── controllers
│ ├── issues_controller_spec.rb
│ ├── kanbans_controller_spec.rb
│ ├── sessions_controller_spec.rb
│ └── top_controller_spec.rb
├── factories
│ ├── kanbans.rb
│ ├── labels.rb
│ ├── sequences.rb
│ └── users.rb
├── helpers
│ └── application_helper_spec.rb
├── models
│ └── kanban_spec.rb
├── spec_helper.rb
└── support
│ ├── shared_contexts
│ └── using_gitlab_mock.rb
│ └── shared_examples
│ └── a_user.rb
├── tmp
└── .keep
└── vendor
└── assets
├── javascripts
└── .keep
└── stylesheets
├── .keep
└── 1pxdeep
├── 1pxdeep.less
└── scheme.less
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | repo_token: A4Ghae9PeorXsNQuenOQfqdGXtgbYgTnm
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #----------------------------------------------------------------------------
2 | # Ignore these files when commiting to a git repository.
3 | #
4 | # See http://help.github.com/ignore-files/ for more about ignoring files.
5 | #
6 | # The original version of this file is found here:
7 | # https://github.com/RailsApps/rails-composer/blob/master/files/gitignore.txt
8 | #
9 | # Corrections? Improvements? Create a GitHub issue:
10 | # http://github.com/RailsApps/rails-composer/issues
11 | #----------------------------------------------------------------------------
12 |
13 | # bundler state
14 | /.bundle
15 | /vendor/bundle/
16 | /vendor/ruby/
17 |
18 | # minimal Rails specific artifacts
19 | db/*.sqlite3
20 | /db/*.sqlite3-journal
21 | /log/*
22 | /tmp/*
23 |
24 | # configuration file introduced in Rails 4.1
25 | /config/secrets.yml
26 |
27 | # various artifacts
28 | **.war
29 | *.rbc
30 | *.sassc
31 | .redcar/
32 | .sass-cache
33 | /config/config.yml
34 | /config/database.yml
35 | /coverage.data
36 | /coverage/
37 | /db/*.javadb/
38 | /db/*.sqlite3
39 | /doc/api/
40 | /doc/app/
41 | /doc/features.html
42 | /doc/specs.html
43 | /public/cache
44 | /public/stylesheets/compiled
45 | /public/system/*
46 | /spec/tmp/*
47 | /cache
48 | /capybara*
49 | /capybara-*.html
50 | /gems
51 | /specifications
52 | rerun.txt
53 | pickle-email-*.html
54 | .zeus.sock
55 |
56 | # If you find yourself ignoring temporary files generated by your text editor
57 | # or operating system, you probably want to add a global ignore instead:
58 | # git config --global core.excludesfile ~/.gitignore_global
59 | #
60 | # Here are some files you may want to ignore globally:
61 |
62 | # scm revert files
63 | **.orig
64 |
65 | # Mac finder artifacts
66 | .DS_Store
67 |
68 | # Netbeans project directory
69 | /nbproject/
70 |
71 | # RubyMine project files
72 | .idea
73 |
74 | # Textmate project files
75 | /*.tmproj
76 |
77 | # vim artifacts
78 | **.swp
79 | /reports/
80 | /coverage/
81 | /db/schema.rb
82 |
83 | config/gitlab.yml
84 | pusher.yml
85 |
--------------------------------------------------------------------------------
/.hound.yml:
--------------------------------------------------------------------------------
1 | Style/LineLength:
2 | Description: 'Limit lines to 130 characters.'
3 | Max: 130
4 | Style/SpaceInsideParens:
5 | Enabled: false
6 | Style/SpaceBeforeBlockBraces:
7 | Enabled: false
8 | StringLiterals:
9 | Enabled: false
10 | Style/TrailingComma:
11 | Enabled: false
12 | Style/BlockComments:
13 | Enabled: false
14 | Style/NilComparison:
15 | Enabled: false
16 | Style/Documentation:
17 | Enabled: false
18 | Style/RegexpLiteral:
19 | Enabled: false
20 | Style/SignalException:
21 | Enabled: false
22 | Style/CaseEquality:
23 | Enabled: false
24 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 | --require spec_helper
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | rvm:
3 | - 2.0
4 | - 2.1
5 | - 2.2
6 | env:
7 | - DB=mysql
8 | - DB=postgresql
9 | cache: bundler
10 | before_script:
11 | - "cp config/database.yml.$DB config/database.yml"
12 | - "cp config/gitlab.yml.sample config/gitlab.yml"
13 | - "cp config/pusher.yml.sample config/pusher.yml"
14 | - "RAILS_ENV=test bundle exec rake db:create db:migrate"
15 | script: bundle exec rspec
16 | branches:
17 | only:
18 | - master
19 | notifications:
20 | email: false
21 | sudo: false
22 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## master
2 | [full changelog](http://github.com/sue445/gitpeach/compare/0.0.7...master)
3 |
4 | ## 0.0.7
5 | [full changelog](http://github.com/sue445/gitpeach/compare/0.0.6...0.0.7)
6 |
7 | * upgrade to rails 4.1.4
8 |
9 | ## 0.0.6
10 | [full changelog](http://github.com/sue445/gitpeach/compare/0.0.5...0.0.6)
11 |
12 | * Support [Heroku](http://www.heroku.com/)
13 |
14 | ## 0.0.5
15 | [full changelog](http://github.com/sue445/gitpeach/compare/0.0.4...0.0.5)
16 |
17 | * upgrade to rails 4.1.1
18 | * Refactor: js -> coffeescript
19 |
20 | ## 0.0.4
21 | [full changelog](http://github.com/sue445/gitpeach/compare/0.0.3...0.0.4)
22 |
23 | * upgrade to rails 4.0.4
24 |
25 | ## 0.0.3
26 | [full changelog](http://github.com/sue445/gitpeach/compare/0.0.2...0.0.3)
27 |
28 | * upgrade to rails 4.0.3
29 | * bugfix: Can not remove label
30 | * https://github.com/sue445/gitpeach/issues/3
31 |
32 | ## 0.0.2
33 | [full changelog](http://github.com/sue445/gitpeach/compare/0.0.1...0.0.2)
34 |
35 | * tweak UI
36 | * show issue timestamp
37 |
38 | ## 0.0.1
39 | * first release
40 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # uncomment out when deploy to Heroku
4 | # ruby "2.1.2"
5 |
6 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
7 | gem 'rails', '4.1.6'
8 |
9 | # Support DBs
10 | gem 'mysql2', "~> 0.3.15", group: :mysql
11 | gem 'pg', group: :postgres
12 |
13 | gem "avatar", "~> 0.2.0"
14 | gem 'coffee-rails', '~> 4.1.0'
15 | gem "friendly_id", "~> 5.0.3"
16 | gem "gitlab", "~> 3.2.0"
17 | gem 'jbuilder', '~> 2.2.3'
18 | gem 'jquery-rails', "~> 3.1.2"
19 | gem "jquery-ui-rails", "~> 5.0.2"
20 | gem "less-rails", "2.5.0"
21 | gem "libv8", "~> 3.16.14.7"
22 | gem 'pusher', "~> 0.14.2"
23 | gem 'sass-rails', '~> 4.0.3'
24 | gem "slim-rails", "~> 2.1.5"
25 | gem "therubyracer", "~> 0.12.1", platform: :ruby
26 | gem "twitter-bootstrap-rails", github: "seyhunak/twitter-bootstrap-rails", branch: "bootstrap3", ref: "128f37"
27 | gem 'uglifier', '~> 2.5.3'
28 |
29 | group :development do
30 | gem "annotate", "~> 2.6.5", require: false
31 | gem "better_errors", '~> 2.0.0'
32 | gem "binding_of_caller"
33 | gem "net-http-spy"
34 | gem "pry" , "~> 0.10.1" , group: :test
35 | gem "pry-remote", "~> 0.1.8" , group: :test
36 | gem "pry-nav" , "~> 0.2.4" , group: :test
37 | gem "pry-rails" , "~> 0.3.2" , group: :test
38 | gem "view_source_map", "0.1.0"
39 | end
40 |
41 | group :test do
42 | gem 'coveralls', '~> 0.7.1', require: false
43 | gem "database_rewinder", "~> 0.4.1"
44 | gem "factory_girl_rails", "~> 4.5.0", group: :development
45 | gem "rspec-collection_matchers", "~> 1.0.0"
46 | gem "rspec-its", "1.0.1"
47 | gem "rspec-parameterized"
48 | gem "rspec-rails", "~> 3.1.0", group: :development
49 | gem "webmock", "~> 1.20.0"
50 | end
51 |
52 | group :production do
53 | gem "rails_12factor", "0.0.3"
54 | gem "unicorn", "~> 4.8.3"
55 | end
56 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: git://github.com/seyhunak/twitter-bootstrap-rails.git
3 | revision: 128f373e151528c9516ea8df32831fc60ac1c35d
4 | ref: 128f37
5 | branch: bootstrap3
6 | specs:
7 | twitter-bootstrap-rails (3.1.1)
8 | actionpack (>= 3.1)
9 | execjs
10 | rails (>= 3.1)
11 | railties (>= 3.1)
12 |
13 | GEM
14 | remote: https://rubygems.org/
15 | specs:
16 | abstract_type (0.0.7)
17 | actionmailer (4.1.6)
18 | actionpack (= 4.1.6)
19 | actionview (= 4.1.6)
20 | mail (~> 2.5, >= 2.5.4)
21 | actionpack (4.1.6)
22 | actionview (= 4.1.6)
23 | activesupport (= 4.1.6)
24 | rack (~> 1.5.2)
25 | rack-test (~> 0.6.2)
26 | actionview (4.1.6)
27 | activesupport (= 4.1.6)
28 | builder (~> 3.1)
29 | erubis (~> 2.7.0)
30 | activemodel (4.1.6)
31 | activesupport (= 4.1.6)
32 | builder (~> 3.1)
33 | activerecord (4.1.6)
34 | activemodel (= 4.1.6)
35 | activesupport (= 4.1.6)
36 | arel (~> 5.0.0)
37 | activesupport (4.1.6)
38 | i18n (~> 0.6, >= 0.6.9)
39 | json (~> 1.7, >= 1.7.7)
40 | minitest (~> 5.1)
41 | thread_safe (~> 0.1)
42 | tzinfo (~> 1.1)
43 | adamantium (0.2.0)
44 | ice_nine (~> 0.11.0)
45 | memoizable (~> 0.4.0)
46 | addressable (2.3.6)
47 | annotate (2.6.5)
48 | activerecord (>= 2.3.0)
49 | rake (>= 0.8.7)
50 | arel (5.0.1.20140414130214)
51 | ast (2.0.0)
52 | avatar (0.2.0)
53 | better_errors (2.0.0)
54 | coderay (>= 1.0.0)
55 | erubis (>= 2.6.6)
56 | rack (>= 0.9.0)
57 | binding_of_caller (0.7.2)
58 | debug_inspector (>= 0.0.1)
59 | builder (3.2.2)
60 | coderay (1.1.0)
61 | coffee-rails (4.1.0)
62 | coffee-script (>= 2.2.0)
63 | railties (>= 4.0.0, < 5.0)
64 | coffee-script (2.3.0)
65 | coffee-script-source
66 | execjs
67 | coffee-script-source (1.8.0)
68 | commonjs (0.2.7)
69 | concord (0.1.5)
70 | adamantium (~> 0.2.0)
71 | equalizer (~> 0.0.9)
72 | coveralls (0.7.1)
73 | multi_json (~> 1.3)
74 | rest-client
75 | simplecov (>= 0.7)
76 | term-ansicolor
77 | thor
78 | crack (0.4.2)
79 | safe_yaml (~> 1.0.0)
80 | database_rewinder (0.4.1)
81 | debug_inspector (0.0.2)
82 | diff-lcs (1.2.5)
83 | docile (1.1.5)
84 | equalizer (0.0.9)
85 | erubis (2.7.0)
86 | execjs (2.2.2)
87 | factory_girl (4.5.0)
88 | activesupport (>= 3.0.0)
89 | factory_girl_rails (4.5.0)
90 | factory_girl (~> 4.5.0)
91 | railties (>= 3.0.0)
92 | friendly_id (5.0.4)
93 | activerecord (>= 4.0.0)
94 | gitlab (3.2.0)
95 | httparty
96 | terminal-table
97 | hike (1.2.3)
98 | httparty (0.13.1)
99 | json (~> 1.8)
100 | multi_xml (>= 0.5.2)
101 | httpclient (2.5.1)
102 | i18n (0.6.11)
103 | ice_nine (0.11.1)
104 | jbuilder (2.2.3)
105 | activesupport (>= 3.0.0, < 5)
106 | multi_json (~> 1.2)
107 | jquery-rails (3.1.2)
108 | railties (>= 3.0, < 5.0)
109 | thor (>= 0.14, < 2.0)
110 | jquery-ui-rails (5.0.2)
111 | railties (>= 3.2.16)
112 | json (1.8.1)
113 | kgio (2.9.2)
114 | less (2.5.1)
115 | commonjs (~> 0.2.7)
116 | less-rails (2.5.0)
117 | actionpack (>= 3.1)
118 | less (~> 2.5.0)
119 | libv8 (3.16.14.7)
120 | mail (2.6.1)
121 | mime-types (>= 1.16, < 3)
122 | memoizable (0.4.2)
123 | thread_safe (~> 0.3, >= 0.3.1)
124 | method_source (0.8.2)
125 | mime-types (2.4.3)
126 | minitest (5.4.2)
127 | multi_json (1.10.1)
128 | multi_xml (0.5.5)
129 | mysql2 (0.3.16)
130 | net-http-spy (0.2.1)
131 | netrc (0.8.0)
132 | parser (2.2.0)
133 | ast (>= 1.1, < 3.0)
134 | slop (~> 3.4, >= 3.4.5)
135 | pg (0.17.1)
136 | proc_to_ast (0.0.7)
137 | coderay
138 | parser
139 | unparser
140 | procto (0.0.2)
141 | pry (0.10.1)
142 | coderay (~> 1.1.0)
143 | method_source (~> 0.8.1)
144 | slop (~> 3.4)
145 | pry-nav (0.2.4)
146 | pry (>= 0.9.10, < 0.11.0)
147 | pry-rails (0.3.2)
148 | pry (>= 0.9.10)
149 | pry-remote (0.1.8)
150 | pry (~> 0.9)
151 | slop (~> 3.0)
152 | pusher (0.14.2)
153 | httpclient (~> 2.4)
154 | multi_json (~> 1.0)
155 | signature (~> 0.1.6)
156 | rack (1.5.2)
157 | rack-test (0.6.2)
158 | rack (>= 1.0)
159 | rails (4.1.6)
160 | actionmailer (= 4.1.6)
161 | actionpack (= 4.1.6)
162 | actionview (= 4.1.6)
163 | activemodel (= 4.1.6)
164 | activerecord (= 4.1.6)
165 | activesupport (= 4.1.6)
166 | bundler (>= 1.3.0, < 2.0)
167 | railties (= 4.1.6)
168 | sprockets-rails (~> 2.0)
169 | rails_12factor (0.0.3)
170 | rails_serve_static_assets
171 | rails_stdout_logging
172 | rails_serve_static_assets (0.0.2)
173 | rails_stdout_logging (0.0.3)
174 | railties (4.1.6)
175 | actionpack (= 4.1.6)
176 | activesupport (= 4.1.6)
177 | rake (>= 0.8.7)
178 | thor (>= 0.18.1, < 2.0)
179 | raindrops (0.13.0)
180 | rake (10.3.2)
181 | ref (1.0.5)
182 | rest-client (1.7.2)
183 | mime-types (>= 1.16, < 3.0)
184 | netrc (~> 0.7)
185 | rspec (3.1.0)
186 | rspec-core (~> 3.1.0)
187 | rspec-expectations (~> 3.1.0)
188 | rspec-mocks (~> 3.1.0)
189 | rspec-collection_matchers (1.0.0)
190 | rspec-expectations (>= 2.99.0.beta1)
191 | rspec-core (3.1.7)
192 | rspec-support (~> 3.1.0)
193 | rspec-expectations (3.1.2)
194 | diff-lcs (>= 1.2.0, < 2.0)
195 | rspec-support (~> 3.1.0)
196 | rspec-its (1.0.1)
197 | rspec-core (>= 2.99.0.beta1)
198 | rspec-expectations (>= 2.99.0.beta1)
199 | rspec-mocks (3.1.3)
200 | rspec-support (~> 3.1.0)
201 | rspec-parameterized (0.1.2)
202 | binding_of_caller
203 | parser
204 | proc_to_ast
205 | rspec (>= 2.13, < 4)
206 | unparser
207 | rspec-rails (3.1.0)
208 | actionpack (>= 3.0)
209 | activesupport (>= 3.0)
210 | railties (>= 3.0)
211 | rspec-core (~> 3.1.0)
212 | rspec-expectations (~> 3.1.0)
213 | rspec-mocks (~> 3.1.0)
214 | rspec-support (~> 3.1.0)
215 | rspec-support (3.1.2)
216 | safe_yaml (1.0.4)
217 | sass (3.2.19)
218 | sass-rails (4.0.3)
219 | railties (>= 4.0.0, < 5.0)
220 | sass (~> 3.2.0)
221 | sprockets (~> 2.8, <= 2.11.0)
222 | sprockets-rails (~> 2.0)
223 | signature (0.1.7)
224 | simplecov (0.9.1)
225 | docile (~> 1.1.0)
226 | multi_json (~> 1.0)
227 | simplecov-html (~> 0.8.0)
228 | simplecov-html (0.8.0)
229 | slim (2.1.0)
230 | temple (~> 0.6.9)
231 | tilt (>= 1.3.3, < 2.1)
232 | slim-rails (2.1.5)
233 | actionpack (>= 3.0, < 4.2)
234 | activesupport (>= 3.0, < 4.2)
235 | railties (>= 3.0, < 4.2)
236 | slim (~> 2.0)
237 | slop (3.6.0)
238 | sprockets (2.11.0)
239 | hike (~> 1.2)
240 | multi_json (~> 1.0)
241 | rack (~> 1.0)
242 | tilt (~> 1.1, != 1.3.0)
243 | sprockets-rails (2.2.0)
244 | actionpack (>= 3.0)
245 | activesupport (>= 3.0)
246 | sprockets (>= 2.8, < 4.0)
247 | temple (0.6.9)
248 | term-ansicolor (1.3.0)
249 | tins (~> 1.0)
250 | terminal-table (1.4.5)
251 | therubyracer (0.12.1)
252 | libv8 (~> 3.16.14.0)
253 | ref
254 | thor (0.19.1)
255 | thread_safe (0.3.4)
256 | tilt (1.4.1)
257 | tins (1.3.3)
258 | tzinfo (1.2.2)
259 | thread_safe (~> 0.1)
260 | uglifier (2.5.3)
261 | execjs (>= 0.3.0)
262 | json (>= 1.8.0)
263 | unicorn (4.8.3)
264 | kgio (~> 2.6)
265 | rack
266 | raindrops (~> 0.7)
267 | unparser (0.1.16)
268 | abstract_type (~> 0.0.7)
269 | adamantium (~> 0.2.0)
270 | concord (~> 0.1.5)
271 | equalizer (~> 0.0.9)
272 | parser (~> 2.2.0.pre.7)
273 | procto (~> 0.0.2)
274 | view_source_map (0.1.0)
275 | rails (>= 3.2)
276 | webmock (1.20.0)
277 | addressable (>= 2.3.6)
278 | crack (>= 0.3.2)
279 |
280 | PLATFORMS
281 | ruby
282 |
283 | DEPENDENCIES
284 | annotate (~> 2.6.5)
285 | avatar (~> 0.2.0)
286 | better_errors (~> 2.0.0)
287 | binding_of_caller
288 | coffee-rails (~> 4.1.0)
289 | coveralls (~> 0.7.1)
290 | database_rewinder (~> 0.4.1)
291 | factory_girl_rails (~> 4.5.0)
292 | friendly_id (~> 5.0.3)
293 | gitlab (~> 3.2.0)
294 | jbuilder (~> 2.2.3)
295 | jquery-rails (~> 3.1.2)
296 | jquery-ui-rails (~> 5.0.2)
297 | less-rails (= 2.5.0)
298 | libv8 (~> 3.16.14.7)
299 | mysql2 (~> 0.3.15)
300 | net-http-spy
301 | pg
302 | pry (~> 0.10.1)
303 | pry-nav (~> 0.2.4)
304 | pry-rails (~> 0.3.2)
305 | pry-remote (~> 0.1.8)
306 | pusher (~> 0.14.2)
307 | rails (= 4.1.6)
308 | rails_12factor (= 0.0.3)
309 | rspec-collection_matchers (~> 1.0.0)
310 | rspec-its (= 1.0.1)
311 | rspec-parameterized
312 | rspec-rails (~> 3.1.0)
313 | sass-rails (~> 4.0.3)
314 | slim-rails (~> 2.1.5)
315 | therubyracer (~> 0.12.1)
316 | twitter-bootstrap-rails!
317 | uglifier (~> 2.5.3)
318 | unicorn (~> 4.8.3)
319 | view_source_map (= 0.1.0)
320 | webmock (~> 1.20.0)
321 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 sue445
2 |
3 | MIT License
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | "Software"), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Gitpeach
2 |
3 | [](https://travis-ci.org/sue445/gitpeach)
4 | [](https://coveralls.io/r/sue445/gitpeach)
5 | [](https://gemnasium.com/sue445/gitpeach)
6 | [](https://codeclimate.com/github/sue445/gitpeach)
7 | [](http://inch-ci.org/github/sue445/gitpeach)
8 |
9 | [waffle.io](https://waffle.io/) clone for [Gitlab](http://gitlab.org/)
10 |
11 | 
12 |
13 | ## Requirements
14 | * Gitlab API 5.3.0+ and 6.0.x and 6.2.0+
15 | * **only 6.1.0** is not supported
16 | * ruby 2.0.0+
17 | * MySQL or PostgreSQL
18 | * Pusher
19 |
20 | ## Setup
21 | ### Signup to [Pusher](https://app.pusher.com/)
22 | Create new app
23 |
24 | 
25 |
26 | ### Setup command
27 | ```sh
28 | bundle install --path vendor/bundle
29 |
30 | # Mysql
31 | cp config/database.yml{.mysql,}
32 |
33 | # PostgreSQL
34 | cp config/database.yml{.postgresql,}
35 |
36 | cp config/gitlab.yml{.sample,}
37 | cp config/pusher.yml{.sample,}
38 | vi config/database.yml
39 | vi config/gitlab.yml
40 | vi config/pusher.yml
41 | bundle exec rake db:create
42 | bundle exec rake db:migrate RAILS_ENV=development
43 |
44 | bundle exec rails s
45 | open http://localhost:3000/
46 | ```
47 |
48 | ## Test
49 | ```sh
50 | bundle exec rake db:migrate RAILS_ENV=test
51 | bundle exec rspec
52 | ```
53 |
54 | ## Sample scripts
55 | see [lib/support](lib/support)
56 |
57 | ## Production (example)
58 | ### Setup
59 | ```sh
60 | cd /path/to
61 | git clone git@github.com:sue445/gitpeach.git
62 | cd gitpeach
63 |
64 | cp config/database.yml{.mysql,}
65 | bundle install --path vendor/bundle --without development test postgres
66 | # or
67 | cp config/database.yml{.postgresql,}
68 | bundle install --path vendor/bundle --without development test mysql
69 |
70 | cp config/gitlab.yml{.sample,}
71 | cp config/pusher.yml{.sample,}
72 | vi config/database.yml
73 | vi config/gitlab.yml
74 | vi config/pusher.yml
75 |
76 | RAILS_ENV=production bundle exec db:create rake db:migrate
77 |
78 | sudo cp /path/to/gitpeach/lib/support/nginx/gitpeach /etc/nginx/sites-enabled/gitpeach
79 | sudo vi /etc/nginx/sites-enabled/gitpeach
80 |
81 | sudo cp /path/to/gitpeach/lib/support/init.d/unicorn_gitpeach /etc/init.d/unicorn_gitpeach
82 | sudo /etc/init.d/unicorn_gitpeach start
83 |
84 | sudo /etc/init.d/nginx reload
85 |
86 | open http://peach.your-site.com/
87 | ```
88 |
89 | ### Deploy
90 | ```sh
91 | cd /path/to/gitpeach
92 | git pull --ff
93 | bundle install --path vendor/bundle --without development test postgres
94 | RAILS_ENV=production bundle exec rake assets:clean assets:precompile
95 | RAILS_ENV=production bundle exec rake db:migrate
96 | sudo /etc/init.d/unicorn_gitpeach restart
97 | ```
98 |
99 | ## Production (Heroku)
100 | ```sh
101 | heroku create
102 | git checkout -b deploy
103 |
104 | vi Gemfile
105 | # uncomment out this
106 | # ruby "2.1.2"
107 |
108 | bundle install --without mysql
109 | git add -f config/gitlab.yml
110 | git add -f config/pusher.yml
111 | git commit -am "Add setting for heroku"
112 |
113 | # push deploy branch as master branch
114 | git push heroku deploy:master
115 |
116 | heroku run rake db:migrate
117 | heroku open
118 | ```
119 |
120 | ## FAQ
121 | ### Q. Difference with waffle.io
122 | 1. realtime updates
123 | * using websocket
124 | 2. show milestone and timestamp
125 | * 
126 |
127 | ### Q. Why Peach?
128 | A. Gitlab -> Git love -> Momozono Love -> Cure Peach
129 |
130 | Detail: [Fresh Pretty Cure! - Wikipedia](http://en.wikipedia.org/wiki/Fresh_Pretty_Cure!)
131 |
132 | ### Q. I want to change color
133 | 1. open [bootstrap_and_overrides.css.less](app/assets/stylesheets/bootstrap_and_overrides.css.less)
134 | 2. edit `@seed-color`
135 | * ref. http://rriepe.github.io/1pxdeep/
136 |
137 |
138 | [](https://bitdeli.com/free "Bitdeli Badge")
139 |
140 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require File.expand_path('../config/application', __FILE__)
5 |
6 | Gitpeach::Application.load_tasks
7 |
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | 0.0.7
2 |
--------------------------------------------------------------------------------
/app/assets/images/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/app/assets/images/.keep
--------------------------------------------------------------------------------
/app/assets/images/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/app/assets/images/loading.gif
--------------------------------------------------------------------------------
/app/assets/javascripts/application.coffee:
--------------------------------------------------------------------------------
1 | # This is a manifest file that'll be compiled into application.js, which will include all the files
2 | # listed below.
3 | #
4 | # Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 | # or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6 | #
7 | # It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | # compiled file.
9 | #
10 | # Read Sprockets README (https:#github.com/sstephenson/sprockets#sprockets-directives) for details
11 | # about supported directives.
12 | #
13 | #= require jquery
14 | #= require jquery_ujs
15 | #= require twitter/bootstrap
16 | #
17 | # UI Core
18 | #= require jquery-ui/core
19 | #= require jquery-ui/widget
20 | #= require jquery-ui/mouse
21 | #= require jquery-ui/position
22 | #
23 | # Interactions
24 | #= require jquery-ui/draggable
25 | #= require jquery-ui/droppable
26 | #= require jquery-ui/sortable
27 | #
28 | #= stub kanbans_show
29 | #= stub kanbans_edit
30 | #= stub top
31 | #
32 | #= require_tree .
33 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bootstrap.js.coffee:
--------------------------------------------------------------------------------
1 | jQuery ->
2 | $("a[rel~=popover], .has-popover").popover()
3 | $("a[rel~=tooltip], .has-tooltip").tooltip()
4 |
--------------------------------------------------------------------------------
/app/assets/javascripts/drag_manager.coffee:
--------------------------------------------------------------------------------
1 | # via. http://jsdo.it/tgaisho/draggable1
2 |
3 | class DragManager
4 | # instance variables
5 | target_id = null
6 | original_ui = null
7 |
8 | # same to kanban.css.scss
9 | issue_panel_default_z_index = 50
10 |
11 | # public methods
12 | drag_start: (event, ui) =>
13 | target_id = event.target.id
14 | original_ui = ui
15 |
16 | target = $("#" + target_id)
17 | $(".ui-draggable").css("z-index", issue_panel_default_z_index)
18 | target.css("z-index", 100);
19 |
20 | drag_end: (event, ui) =>
21 | target = $("#" + target_id)
22 |
23 | draggables = $.grep($(".ui-draggable"), (v, i) ->
24 | v = $(v)
25 | # reject own
26 | return (v.attr("id") && v.attr("id") != target_id)
27 | )
28 |
29 | is_conflict = is_conflict_position.call(
30 | @,
31 | target.get(0).getBoundingClientRect(),
32 | {width: target.width(), height: target.height()},
33 | draggables
34 | )
35 |
36 | if(is_conflict)
37 | rollback()
38 | false
39 | else
40 | true
41 |
42 | rollback: =>
43 | target = $("#" + target_id)
44 |
45 | target.css(
46 | left: original_ui.originalPosition.left,
47 | top: original_ui.originalPosition.top
48 | )
49 |
50 | # private methods
51 | is_conflict_position = (position, size, targets) =>
52 | corner_positions = get_corner_positions(position.left, position.top, size.width, size.height)
53 | conflict = false
54 |
55 | $(targets).each (i, v) ->
56 | target_position = $(v).get(0).getBoundingClientRect()
57 | target_corner_positions = get_corner_positions.call(@, target_position.left, target_position.top, $(v).width(), $(v).height())
58 |
59 | $(corner_positions).each ->
60 | (j, o) ->
61 | if o.left >= target_corner_positions[0].left - 15 &&
62 | o.left <= target_corner_positions[1].left + 15 &&
63 | o.top >= target_corner_positions[0].top - 15 &&
64 | o.top <= target_corner_positions[2].top + 15
65 | conflict = true
66 |
67 | conflict
68 |
69 | get_corner_positions = (left, top, width, height) =>
70 | obj = [];
71 | obj.push(left: left , top: top) # left top
72 | obj.push(left: left + width, top: top) # right top
73 | obj.push(left: left , top: top + height) # left down
74 | obj.push(left: left + width, top: top + height) # right down
75 | obj
76 |
77 | window.drag_manager = new DragManager()
78 |
--------------------------------------------------------------------------------
/app/assets/javascripts/kanbans_edit.js.coffee:
--------------------------------------------------------------------------------
1 | refresh_table = ->
2 | $("li.edit_label").each ->
3 | is_backlog = $(this).find("input[name='labels[][is_backlog_issue]']").is(':checked')
4 | is_close = $(this).find("input[name='labels[][is_close_issue]']").is(':checked')
5 |
6 | if is_backlog || is_close
7 | # disabled
8 | $(this).find("input[name='labels[][gitlab_label]']").attr('disabled','disabled')
9 | else
10 | # enabled
11 | $(this).find("input[name='labels[][gitlab_label]']").removeAttr('disabled')
12 |
13 | $(document).ready ->
14 | $(".help-tooltip").tooltip()
15 | refresh_table()
16 |
17 | $("li.edit_label input:radio").change ->
18 | refresh_table()
19 |
20 | $("li.edit_label button.delete_label").click ->
21 | $(this).parents("li.edit_label").remove()
22 |
23 | # clear old radio button
24 | $("input[name='labels[][is_backlog_issue]']").click ->
25 | that = this
26 | $("input[name='labels[][is_backlog_issue]']").each ->
27 | $(this).removeAttr("checked") unless that == this
28 |
29 | $("input[name='labels[][is_close_issue]']").click ->
30 | that = this
31 | $("input[name='labels[][is_close_issue]']").each ->
32 | $(this).removeAttr("checked") unless that == this
33 |
34 | $("#add_label").click ->
35 | $("ul#hidden_list li").clone().prependTo($("#update_kanban ul"))
36 |
37 | $("#update_kanban ul").sortable
38 | connectWith: "#update_kanban ul"
39 | cursor: "move"
40 | containment: "#update_kanban ul"
41 | axis: "y"
42 | handle: "i.drag-column"
43 |
--------------------------------------------------------------------------------
/app/assets/javascripts/kanbans_show.js.coffee:
--------------------------------------------------------------------------------
1 | issue_id = null
2 |
3 | init_issue_panel = (selector) ->
4 | $(selector).draggable(
5 | scope: "label-column-scope"
6 | revert: "invalid"
7 | stack: "issue-panel"
8 | cursor: "move"
9 | snap: true
10 | handle: ".issue-panel__header"
11 | start: (event, ui) ->
12 | issue_id = event.target.id.replace("issue_", "")
13 | drag_manager.drag_start(event, ui)
14 | null
15 | stop: (event, ui) ->
16 | drag_manager.drag_end(event, ui)
17 | null
18 | )
19 | $("#{selector} .issue-panel__updated_at").tooltip()
20 |
21 | init_label_column = (selector) ->
22 | $(selector).droppable(
23 | scope: "label-column-scope"
24 | tolerance: "fit"
25 | drop: (event, ui) ->
26 | to_label_id = event.target.id.replace("label_", "")
27 | kanban_name = $("#kanban_name").val()
28 | $.ajax(
29 | url: "/#{kanban_name}/issues/#{issue_id}"
30 | data:
31 | to_label_id: to_label_id
32 | method: "PUT"
33 | error: ->
34 | drag_manager.rollback()
35 | )
36 | null
37 | )
38 |
39 | $(document).ready ->
40 | width_rate = 100 / $("#label_groups_count").val()
41 | $(".label-area").css("width", "#{width_rate}%")
42 |
43 | init_issue_panel(".issue-panel")
44 | init_label_column(".label-column")
45 |
46 | pusher = new window.Pusher($("#pusher_key").val())
47 | channel = pusher.subscribe($("#channel").val())
48 | channel.bind(
49 | "issue_update_event",
50 | (data) ->
51 | for label_id, issue_ids of data.label_group_ids
52 | do (label_id, issue_ids) ->
53 | $("#count_#{label_id}").text(issue_ids.length)
54 | for issue_id in issue_ids
55 | do (issue_id) ->
56 | $.ajax(
57 | url: "/#{$("#kanban_name").val()}/issues/#{issue_id}"
58 | dataType: "html"
59 | success: (html, data_type) ->
60 | $("#issue_#{issue_id}").remove()
61 | $("#label_#{label_id}").append($(html))
62 | init_issue_panel("#issue_#{issue_id}")
63 | )
64 | )
65 |
66 | $("#create_issue_form").submit ->
67 | if $("#new_issue_title").val()
68 | $("#loading").show()
69 | $("#create_issue_button").attr("disabled", "disabled")
70 | $.ajax(
71 | url: "/#{$("#kanban_name").val()}/issues"
72 | method: "POST"
73 | dataType: "json"
74 | data:
75 | title: $("#new_issue_title").val()
76 | success: (data, data_type) ->
77 | $("#loading").hide()
78 | $("#create_issue_button").removeAttr("disabled")
79 | $("#new_issue_title").val("")
80 | )
81 | false
82 |
--------------------------------------------------------------------------------
/app/assets/javascripts/top.js.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/app/assets/javascripts/util.js.coffee:
--------------------------------------------------------------------------------
1 | window.util =
2 | alert: (message, alert_class="alert-success", is_auto_close=false) ->
3 | $("#alert-area .alert").alert('close')
4 |
5 | close_button =
6 | $("").
7 | addClass("close").
8 | attr(
9 | "data-dismiss": "alert"
10 | href: "#"
11 | "aria-hidden": true
12 | ).
13 | html("×")
14 |
15 | $("
").
16 | addClass("alert").
17 | addClass(alert_class).
18 | text(message).
19 | append(close_button).
20 | appendTo($("#alert-area"))
21 |
22 | if is_auto_close
23 | window.setTimeout(
24 | ->
25 | $("#alert-area .alert").alert('close')
26 | 2000
27 | )
28 |
29 | $(document).ready ->
30 | $(document).ajaxError (event, jqxhr, settings, exception) ->
31 | if jqxhr.responseJSON
32 | json = jqxhr.responseJSON
33 | util.alert("#{json.exception} #{json.message}", "alert-danger", false)
34 | else
35 | util.alert(jqxhr.responseText, "alert-danger", false)
36 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the top of the
9 | * compiled file, but it's generally better to create a new file per style scope.
10 | *
11 | *= require_self
12 | *
13 | * UI Core
14 | *= require jquery-ui/core
15 | *= require jquery-ui/theme
16 | *
17 | *= require_tree .
18 | */
19 |
20 | input:disabled{
21 | opacity: 1;
22 | background: #d3d3d3;
23 | }
24 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/bootstrap_and_overrides.css.less:
--------------------------------------------------------------------------------
1 | @import "1pxdeep/scheme.less";
2 | @import "twitter/bootstrap/bootstrap";
3 | @import "1pxdeep/1pxdeep.less";
4 |
5 | // Set the correct sprite paths
6 | @iconSpritePath: image-url("twitter/bootstrap/glyphicons-halflings.png");
7 | @iconWhiteSpritePath: image-url("twitter/bootstrap/glyphicons-halflings-white.png");
8 |
9 | // Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines)
10 | @fontAwesomeEotPath: asset-url("fontawesome-webfont.eot");
11 | @fontAwesomeEotPath_iefix: asset-url("fontawesome-webfont.eot?#iefix");
12 | @fontAwesomeWoffPath: asset-url("fontawesome-webfont.woff");
13 | @fontAwesomeTtfPath: asset-url("fontawesome-webfont.ttf");
14 | @fontAwesomeSvgPath: asset-url("fontawesome-webfont.svg#fontawesomeregular");
15 |
16 | // Font Awesome
17 | @import "fontawesome/font-awesome";
18 |
19 | // Glyphicons
20 | //@import "twitter/bootstrap/sprites.less";
21 |
22 | // Your custom LESS stylesheets goes here
23 | //
24 | // Since bootstrap was imported above you have access to its mixins which
25 | // you may use and inherit here
26 | //
27 | // If you'd like to override bootstrap's own variables, you can do so here as well
28 | // See http://twitter.github.com/bootstrap/customize.html#variables for their names and documentation
29 | //
30 | // Example:
31 | // @linkColor: #ff0000;
32 |
33 | @input-bg-disabled: @gray-darker;
34 | @seed-color:#f5c9da;
35 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/kanbans_edit.css.scss:
--------------------------------------------------------------------------------
1 | li.edit_label i.drag-column {
2 | cursor: move;
3 | }
4 |
5 | li.edit_label input {
6 | color: #333;
7 | }
8 |
9 | ul#hidden_list {
10 | display: none;
11 | }
12 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/kanbans_show.css.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the kanbans controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 | .label-area {
5 | float: left;
6 | }
7 |
8 | .label-column {
9 | min-height: 800px;
10 | z-index: 10;
11 | }
12 |
13 | .issue-panel {
14 | z-index: 50;
15 | }
16 |
17 | .issue-panel a{
18 | color: #7e5b68;
19 | }
20 |
21 | .issue-panel .issue-panel__header{
22 | position: relative;
23 | cursor: move;
24 | }
25 |
26 | .issue-panel .issue-panel__header .issue-panel__assignee{
27 | position: absolute;
28 | top: 0px;
29 | right: 0px;
30 | }
31 |
32 | .issue-panel .issue-panel__footer{
33 | position: relative;
34 | }
35 |
36 | .issue-panel .issue-panel__footer .issue-panel__note{
37 | position: absolute;
38 | top: 10px;
39 | right: 10px;
40 | }
41 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/top.css.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the top controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | # Prevent CSRF attacks by raising an exception.
3 | # For APIs, you may want to use :null_session instead.
4 | protect_from_forgery with: :exception
5 |
6 | helper_method :current_user, :logged_in?, :not_logged_in?
7 |
8 | private
9 |
10 | # if not logged in, redirect to top
11 | def authenticate_user
12 | redirect_to root_path(back_to: request.original_fullpath) if not_logged_in?
13 | end
14 |
15 | def current_user
16 | @current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
17 | end
18 |
19 | def logged_in?
20 | current_user != nil
21 | end
22 |
23 | def not_logged_in?
24 | !logged_in?
25 | end
26 |
27 | end
28 |
--------------------------------------------------------------------------------
/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/app/controllers/issues_controller.rb:
--------------------------------------------------------------------------------
1 | class IssuesController < ApplicationController
2 | before_action :set_kanban
3 | before_action :setup_notification, only: [:create, :update]
4 |
5 | unless Rails.env.test?
6 | before_action :authenticate_user
7 | before_action :set_user_kanban
8 | before_action :set_issue
9 |
10 | after_action :notify_event, only: [:create, :update]
11 |
12 | rescue_from StandardError do |exception|
13 | Rails.logger.error exception.full_backtrace
14 | response = {
15 | status: :error,
16 | exception: exception.class.to_s,
17 | message: exception.message
18 | }
19 | render json: response, status: 500
20 | end
21 | end
22 |
23 | # POST /:kanban_id/issues
24 | def create
25 | created_issue = create_gitlab_issue(params[:title])
26 | render json: created_issue, status: 200
27 | end
28 |
29 | # GET /:kanban_id/issues/:id
30 | def show
31 | render partial: "shared/issue_panel", locals: {issue: @issue}
32 | end
33 |
34 | # PATCH/PUT /:kanban_id/issues/:id
35 | def update
36 | raise ArgumentError, "require to_label_id" unless params[:to_label_id]
37 |
38 | from_label_id = gitlab_current_issue_label_id
39 |
40 | if from_label_id.to_i == params[:to_label_id].to_i
41 | @is_notify_event = false
42 | else
43 | @labels = @kanban.update_gitlab_issue_labels(gitlab_issue_labels, from_label_id, params[:to_label_id])
44 | @state = @kanban.gitlab_issue_state(from_label_id, params[:to_label_id])
45 |
46 | update_gitlab_issue(@labels, @state)
47 | end
48 |
49 | render json: updated_issue, status: 200
50 | end
51 |
52 | private
53 | def set_kanban
54 | @kanban = Kanban.friendly.find(params[:kanban_id])
55 | end
56 |
57 | def set_user_kanban
58 | @user_kanban = UserKanban.new(current_user, @kanban) if current_user
59 | end
60 |
61 | def set_issue
62 | @issue = @user_kanban.issue(params[:id])
63 | end
64 |
65 | def gitlab_issue_labels
66 | @issue.labels
67 | end
68 |
69 | def gitlab_current_issue_label_id
70 | @kanban.issue_label_id(@issue)
71 | end
72 |
73 | def update_gitlab_issue(labels, state_event)
74 | @user_kanban.update_issue(@issue.id, labels, state_event)
75 | end
76 |
77 | def updated_issue
78 | @user_kanban.issue(params[:id])
79 | end
80 |
81 | def create_gitlab_issue(title)
82 | @user_kanban.create_issue(title)
83 | end
84 |
85 | def setup_notification
86 | @is_notify_event = true
87 | end
88 |
89 | def notify_event
90 | return unless @is_notify_event
91 |
92 | label_groups = @kanban.label_groups(@user_kanban.issues)
93 | label_group_ids = label_groups.inject({}){|res, label_issues|
94 | label_id = label_issues[:label].id
95 | issues = label_issues[:issues]
96 | res[label_id] = issues.map(&:id)
97 | res
98 | }
99 |
100 | Pusher.trigger("kanban_#{@kanban.id}", :issue_update_event, {label_group_ids: label_group_ids}, {socket_id: params[:socket_id]})
101 | end
102 | end
103 |
--------------------------------------------------------------------------------
/app/controllers/kanbans_controller.rb:
--------------------------------------------------------------------------------
1 | class KanbansController < ApplicationController
2 | before_action :set_kanban, only: [:show, :destroy, :edit, :update, :sync]
3 |
4 | unless Rails.env.test?
5 | before_action :authenticate_user
6 | before_action :set_user_kanban, only: [:show, :sync]
7 | end
8 |
9 | # GET /:id
10 | # GET /:id.json
11 | def show
12 | @label_groups = @kanban.label_groups(project_issues)
13 | end
14 |
15 | # GET /:id/edit
16 | def edit
17 | @labels = @kanban.labels.map{|label| label.attributes.with_indifferent_access }
18 | end
19 |
20 | # POST /:id
21 | # POST /:id.json
22 | def create
23 | @kanban = Kanban.new(kanban_params)
24 |
25 | respond_to do |format|
26 | if @kanban.save
27 | format.html { redirect_to @kanban, notice: 'Kanban was successfully created.' }
28 | format.json { render action: 'show', status: :created, location: @kanban }
29 | else
30 | format.html { render action: 'new' }
31 | format.json { render json: @kanban.errors, status: :unprocessable_entity }
32 | end
33 | end
34 | end
35 |
36 | # PATCH/PUT /:id
37 | # PATCH/PUT /:id.json
38 | def update
39 | is_all_success = true
40 |
41 | Label.transaction do
42 | param_label_ids = params[:labels].inject([]){|array, label_params| array << label_params[:id]; array }.compact.map(&:to_i)
43 | @kanban.labels.each do |label|
44 | label.destroy unless param_label_ids.include?(label.id)
45 | end
46 |
47 | labels_params[:labels].each_with_index do |label_params, index|
48 | # for checked -> unchecked
49 | label_params[:is_backlog_issue] = false unless label_params.has_key?(:is_backlog_issue)
50 | label_params[:is_close_issue] = false unless label_params.has_key?(:is_close_issue)
51 |
52 | label_params[:disp_order] = index
53 | if label_params[:id].blank?
54 | label = @kanban.labels.build(label_params)
55 | is_all_success &= label.save
56 | else
57 | label = @kanban.labels.find(label_params[:id])
58 | is_all_success &= label.update(label_params.reject{|k,v| k == :id })
59 | end
60 |
61 | label.errors.each do |attribute, error|
62 | @kanban.errors[attribute] = error
63 | end
64 | end
65 | end
66 |
67 | if is_all_success
68 | redirect_to edit_kanban_path(@kanban), notice: 'Kanban was successfully updated.'
69 | else
70 | @labels = params[:labels]
71 | render action: 'edit'
72 | end
73 | end
74 |
75 | # DELETE /:id
76 | # DELETE /:id.json
77 | def destroy
78 | @kanban.destroy
79 | respond_to do |format|
80 | format.html { redirect_to root_url }
81 | format.json { head :no_content }
82 | end
83 | end
84 |
85 | # GET /:id/sync
86 | def sync
87 | @kanban.gitlab_project_id = @user_kanban.project.id
88 | @kanban.name = @user_kanban.project.path_with_namespace
89 | @kanban.slug = nil
90 |
91 | if @kanban.save
92 | redirect_to edit_kanban_path(@kanban), notice: 'Kanban was synchronized with Gitlab.'
93 | else
94 | redirect_to edit_kanban_path(@kanban), error: 'Failed synchronized with Gitlab.'
95 | end
96 | end
97 |
98 | private
99 | # Use callbacks to share common setup or constraints between actions.
100 | def set_kanban
101 | @kanban = Kanban.friendly.find(params[:id])
102 | end
103 |
104 | def set_user_kanban
105 | @user_kanban = UserKanban.new(current_user, @kanban) if current_user
106 | end
107 |
108 | # Never trust parameters from the scary internet, only allow the white list through.
109 | def kanban_params
110 | params.require(:kanban).permit(:gitlab_project_id, :name)
111 | end
112 |
113 | def labels_params
114 | params.permit(labels: [:name, :gitlab_label, :is_backlog_issue, :is_close_issue, :disp_order, :id])
115 | end
116 |
117 | def project_issues
118 | @user_kanban.issues
119 | end
120 | end
121 |
--------------------------------------------------------------------------------
/app/controllers/sessions_controller.rb:
--------------------------------------------------------------------------------
1 | class SessionsController < ApplicationController
2 | def create
3 | api_response = Gitlab.session(params[:login], params[:password])
4 | user = User.find_or_create_by(gitlab_user_id: api_response.id)
5 |
6 | user.username = api_response.username
7 | user.private_token = api_response.private_token
8 | user.email = api_response.email
9 | user.save!
10 |
11 | session[:user_id] = user.id
12 |
13 | back_to_path = params[:back_to] ? params[:back_to] : root_path
14 | redirect_to back_to_path, notice: "Signed in!"
15 |
16 | rescue Gitlab::Error::Unauthorized => e
17 | redirect_to root_path(back_to: params[:back_to]), alert: "Unauthorized"
18 | end
19 |
20 | def destroy
21 | session[:user_id] = nil
22 | redirect_to root_path, notice: "Signed Out!"
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/app/controllers/top_controller.rb:
--------------------------------------------------------------------------------
1 | class TopController < ApplicationController
2 | def index
3 | if current_user
4 | projects = current_user.projects
5 | @user_kanban_projects = projects.select{|project| Kanban.exists?(gitlab_project_id: project.id) }
6 | @no_kanban_projects = projects.reject{|project| Kanban.exists?(gitlab_project_id: project.id) }
7 |
8 | user_project_ids = @user_kanban_projects.map(&:id)
9 | @other_kanbans = Kanban.where.not(gitlab_project_id: user_project_ids)
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/issues_helper.rb:
--------------------------------------------------------------------------------
1 | module IssuesHelper
2 | # via.
3 | # * https://github.com/gitlabhq/gitlabhq/blob/master/lib/gitlab/issues_labels.rb
4 | # * https://github.com/gitlabhq/gitlabhq/blob/master/app/helpers/labels_helper.rb
5 | def label_css_class(name)
6 | warning_labels = %w(documentation support)
7 | neutral_labels = %w(discussion suggestion)
8 | positive_labels = %w(feature enhancement)
9 | important_labels = %w(bug critical confirmed)
10 |
11 | case name
12 | when *warning_labels
13 | 'label-warning'
14 | when *neutral_labels
15 | 'label-primary'
16 | when *positive_labels
17 | 'label-success'
18 | when *important_labels
19 | 'label-danger'
20 | else
21 | 'label-info'
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/app/helpers/kanbans_helper.rb:
--------------------------------------------------------------------------------
1 | module KanbansHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/people_helper.rb:
--------------------------------------------------------------------------------
1 | require 'avatar/view/action_view_support'
2 |
3 | module PeopleHelper
4 | include Avatar::View::ActionViewSupport
5 | end
6 |
--------------------------------------------------------------------------------
/app/helpers/top_helper.rb:
--------------------------------------------------------------------------------
1 | module TopHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/mailers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/app/mailers/.keep
--------------------------------------------------------------------------------
/app/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/app/models/.keep
--------------------------------------------------------------------------------
/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/app/models/concerns/.keep
--------------------------------------------------------------------------------
/app/models/kanban.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: kanbans
4 | #
5 | # id :integer not null, primary key
6 | # gitlab_project_id :integer
7 | # name :string(255)
8 | # slug :string(255)
9 | # created_at :datetime
10 | # updated_at :datetime
11 | #
12 | # Indexes
13 | #
14 | # index_kanbans_on_name (name) UNIQUE
15 | # index_kanbans_on_slug (slug) UNIQUE
16 | #
17 |
18 | class Kanban < ActiveRecord::Base
19 | extend FriendlyId
20 | friendly_id :name, use: :slugged
21 |
22 | has_many :labels, -> { order(:disp_order) }, dependent: :destroy
23 |
24 | after_create :create_default_labels
25 |
26 | def normalize_friendly_id(text)
27 | text
28 | end
29 |
30 | # @return [Hash] key: label_id, value: issues
31 | def issues_group_by_label(issues)
32 | issues ||= []
33 | issues.group_by{|issue| issue_label_id(issue) }
34 | end
35 |
36 | def label_groups(issues)
37 | issues_group_by_label = self.issues_group_by_label(issues)
38 | done_label = self.labels.done.first
39 |
40 | label_groups = []
41 | self.labels.each do |label|
42 | issues = issues_group_by_label[label.id] || []
43 | if label == done_label
44 | # reject old closed tasks
45 | issues = issues.reject{|issue| issue.updated_at < 1.week.ago }
46 | end
47 |
48 | label_groups << {
49 | label: label,
50 | issues: issues
51 | }
52 | end
53 |
54 | label_groups
55 | end
56 |
57 | # @return [Integer] label_id
58 | def issue_label_id(issue)
59 | if issue.state == "closed"
60 | self.labels.done.first.id
61 | else
62 | not_backlog_label = self.labels.other.where(gitlab_label: issue.labels).first
63 | not_backlog_label ? not_backlog_label.id : self.labels.backlog.first.id
64 | end
65 | end
66 |
67 | # @param gitlab_labels [Array]
68 | # @param from_label_id [Integer]
69 | # @param to_label_id [Integer]
70 | # @return [Array]
71 | def update_gitlab_issue_labels(gitlab_labels, from_label_id, to_label_id)
72 | from_label = self.labels.find(from_label_id)
73 | to_label = self.labels.find(to_label_id)
74 |
75 | updated_labels = []
76 | updated_labels << to_label.gitlab_label if from_label.is_backlog_issue? || from_label.is_close_issue?
77 |
78 | gitlab_labels.each do |gitlab_label|
79 | if gitlab_label == from_label.gitlab_label
80 | updated_labels << to_label.gitlab_label
81 | else
82 | updated_labels << gitlab_label
83 | end
84 | end
85 |
86 | updated_labels.compact
87 | end
88 |
89 | def gitlab_issue_state(from_label_id, to_label_id)
90 | from_label = self.labels.find(from_label_id)
91 | to_label = self.labels.find(to_label_id)
92 |
93 | if from_label.opened? && to_label.closed?
94 | "close"
95 | elsif from_label.closed? && to_label.opened?
96 | "reopen"
97 | else
98 | # send "open" to "opened" issue (or send "close" to "closed" issue), 404 error at gitlab v6.4.3
99 | nil
100 | end
101 | end
102 |
103 | private
104 | def create_default_labels
105 | Label::DEFAULTS.each_with_index do |params, index|
106 | labels.create(params.merge(disp_order: index))
107 | end
108 | end
109 | end
110 |
--------------------------------------------------------------------------------
/app/models/label.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: labels
4 | #
5 | # id :integer not null, primary key
6 | # kanban_id :integer
7 | # name :string(255)
8 | # gitlab_label :string(255)
9 | # disp_order :integer
10 | # is_backlog_issue :boolean
11 | # is_close_issue :boolean
12 | # created_at :datetime
13 | # updated_at :datetime
14 | #
15 | # Indexes
16 | #
17 | # index_labels_on_kanban_id_and_disp_order (kanban_id,disp_order)
18 | #
19 |
20 | class Label < ActiveRecord::Base
21 | DEFAULTS = [
22 | {name: "Backlog" , gitlab_label: nil , is_backlog_issue: true , is_close_issue: false},
23 | {name: "Ready" , gitlab_label: "ready" , is_backlog_issue: false, is_close_issue: false},
24 | {name: "In Progress", gitlab_label: "in progress", is_backlog_issue: false, is_close_issue: false},
25 | {name: "Done" , gitlab_label: nil , is_backlog_issue: false, is_close_issue: true},
26 | ]
27 |
28 | scope :backlog, -> { where(is_backlog_issue: true , is_close_issue: false) }
29 | scope :done , -> { where(is_backlog_issue: false, is_close_issue: true) }
30 | scope :other , -> { where(is_backlog_issue: false, is_close_issue: false) }
31 |
32 | validates_presence_of :name
33 | validates_presence_of :gitlab_label, if: -> label{ !label.is_backlog_issue? && !label.is_close_issue? }
34 |
35 | before_save :normalize_gitlab_label
36 |
37 | def closed?
38 | self.is_close_issue?
39 | end
40 |
41 | def opened?
42 | !self.closed?
43 | end
44 |
45 | private
46 | def normalize_gitlab_label
47 | self.gitlab_label = nil if self.is_backlog_issue? || self.is_close_issue?
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/app/models/user.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: users
4 | #
5 | # id :integer not null, primary key
6 | # gitlab_user_id :integer
7 | # username :string(255)
8 | # email :string(255)
9 | # private_token :string(255)
10 | # created_at :datetime
11 | # updated_at :datetime
12 | #
13 | # Indexes
14 | #
15 | # index_users_on_gitlab_user_id (gitlab_user_id) UNIQUE
16 | #
17 |
18 | class User < ActiveRecord::Base
19 | def projects
20 | gitlab.projects(page: 1, per_page: 100)
21 | end
22 |
23 | def gitlab
24 | Gitlab.client(private_token: self.private_token)
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/app/models/user_kanban.rb:
--------------------------------------------------------------------------------
1 | class UserKanban
2 | attr_reader :project
3 |
4 | def initialize(user, kanban)
5 | @user = user
6 | @kanban = kanban
7 | @project = user.gitlab.project(@kanban.gitlab_project_id)
8 | end
9 |
10 | def issues
11 | # sort by newest
12 | @user.gitlab.issues(@kanban.gitlab_project_id, page: 1, per_page: 100).sort{ |a,b| b.updated_at <=> a.updated_at }
13 | end
14 |
15 | def issue(issue_id)
16 | @user.gitlab.issue(@kanban.gitlab_project_id, issue_id)
17 | end
18 |
19 | def issue_url(issue)
20 | issue_id = issue.iid || issue.id
21 | "#{@project.web_url}/issues/#{issue_id}"
22 | end
23 |
24 | def update_issue(issue_id, labels, state_event)
25 | options = {
26 | labels: labels.empty? ? %q{''} : labels.join(",")
27 | }
28 | options[:state_event] = state_event if state_event
29 | @user.gitlab.edit_issue(@project.id, issue_id, options)
30 | end
31 |
32 | def create_issue(title)
33 | @user.gitlab.create_issue(@project.id, title)
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/app/views/kanbans/edit.html.slim:
--------------------------------------------------------------------------------
1 | h1
2 | | Editing kanban
3 |
4 | = link_to sync_kanban_path, class: "btn btn-default btn-xs help-tooltip", "data-toggle"=>"tooltip", title: "Sync with Gitlab (if changed repository name, click this)" do
5 | span.glyphicon.glyphicon-refresh
6 |
7 | = button_tag :add_label, type: "button", class: "btn btn-default btn-xs", id: :add_label do
8 | span.glyphicon.glyphicon-plus
9 |
10 | - if @kanban.errors.any?
11 | #error_explanation.alert.alert-warning
12 | h4 = "#{pluralize(@kanban.errors.count, "error")} prohibited this kanban from being saved:"
13 | ul
14 | - @kanban.errors.full_messages.each do |message|
15 | li = message
16 |
17 | = form_tag kanban_path, id: "update_kanban", method: :put do
18 | ul.list-group
19 | - @labels.each do |label|
20 | li.edit_label.list-group-item
21 | i.glyphicon.glyphicon-align-justify.drag-column
22 | = hidden_field_tag "labels[][id]", label[:id]
23 | = text_field_tag "labels[][name]", label[:name], id: nil, placeholder: "name"
24 | = text_field_tag "labels[][gitlab_label]", label[:gitlab_label], id: nil, placeholder: "gitlab label"
25 |
26 | label.btn.btn-success
27 | = radio_button_tag "labels[][is_backlog_issue]", true, label[:is_backlog_issue], id: nil
28 | | Backlog Issue
29 | = link_to "#", "data-toggle"=>"tooltip", title: "Check to display all open issues in this column", class: "help-tooltip" do
30 | span.glyphicon.glyphicon-question-sign
31 |
32 | label.btn.btn-warning
33 | = radio_button_tag "labels[][is_close_issue]", true, label[:is_close_issue], id: nil
34 | | Close Issue
35 | = link_to "#", "data-toggle"=>"tooltip", title: "Check to close an issue on Gitlab when a card is dragged into this column", class: "help-tooltip" do
36 | span.glyphicon.glyphicon-question-sign
37 |
38 | = button_tag :delete_label, type: "button", class: "btn btn-danger delete_label", id: nil do
39 | span.glyphicon.glyphicon-trash
40 |
41 | div
42 | = button_tag :submit_button, class: "btn btn-primary" do
43 | span.glyphicon.glyphicon-floppy-save
44 | | Save
45 |
46 | = link_to kanban_path, class: "btn btn-default" do
47 | | Back
48 |
49 |
50 | = javascript_include_tag "kanbans_edit"
51 |
52 |
53 | / if append line, copied DOM
54 | ul#hidden_list
55 | li.edit_label.list-group-item
56 | i.glyphicon.glyphicon-align-justify.drag-column
57 | = hidden_field_tag "labels[][id]", nil
58 | = text_field_tag "labels[][name]", nil, id: nil, placeholder: "name"
59 | = text_field_tag "labels[][gitlab_label]", nil, id: nil, placeholder: "gitlab label"
60 |
61 | label.btn.btn-success
62 | = radio_button_tag "labels[][is_backlog_issue]", true, nil, id: nil
63 | | Backlog Issue
64 | = link_to "#", "data-toggle"=>"tooltip", title: "Check to display all open issues in this column", class: "help-tooltip" do
65 | span.glyphicon.glyphicon-question-sign
66 |
67 | label.btn.btn-warning
68 | = radio_button_tag "labels[][is_close_issue]", true, nil, id: nil
69 | | Close Issue
70 | = link_to "#", "data-toggle"=>"tooltip", title: "Check to close an issue on Gitlab when a card is dragged into this column", class: "help-tooltip" do
71 | span.glyphicon.glyphicon-question-sign
72 |
73 | = button_tag :delete_label, type: "button", class: "btn btn-danger delete_label", id: nil do
74 | span.glyphicon.glyphicon-trash
75 |
--------------------------------------------------------------------------------
/app/views/kanbans/show.html.slim:
--------------------------------------------------------------------------------
1 | - content_for :title, @kanban.name
2 |
3 |
4 | h1
5 | = link_to @kanban.name, @user_kanban.project.web_url
6 | = link_to edit_kanban_path, class: "btn btn-default btn-xs" do
7 | span.glyphicon.glyphicon-wrench
8 |
9 | = hidden_field_tag :label_groups_count, @label_groups.count
10 |
11 | form.form-inline#create_issue_form role="form"
12 | .form-group
13 | = text_field_tag :new_issue_title, nil, class: "form-control", placeholder: "issue title", size: 50
14 | button.btn.btn-primary#create_issue_button type="submit"
15 | span.glyphicon.glyphicon-plus
16 | | New Issue
17 |
18 | = image_tag "loading.gif", id: "loading", style: "display: none"
19 |
20 | .row
21 | - @label_groups.each do |label_group|
22 | - label = label_group[:label]
23 | - issues = label_group[:issues]
24 | .label-area
25 | h3
26 | - if label.is_backlog_issue?
27 | span.glyphicon.glyphicon-tasks
28 | - if label.is_close_issue?
29 | span.glyphicon.glyphicon-ok
30 |
31 | = label.name
32 | span.badge id="count_#{label.id}"
33 | = issues.count
34 |
35 | div.well.label-column id="label_#{label.id}"
36 | = render partial: "shared/issue_panel", collection: issues, as: :issue
37 |
38 | = hidden_field_tag :kanban_name, @kanban.name
39 | = hidden_field_tag :pusher_key , Pusher.key
40 | = hidden_field_tag :channel , "kanban_#{@kanban.id}"
41 |
42 | = javascript_include_tag "http://js.pusher.com/2.1/pusher.min.js"
43 |
44 | - unless Rails.env.production?
45 | coffee:
46 | window.Pusher.log = (message) ->
47 | window.console.log(message) if window.console && window.console.log
48 |
49 | = javascript_include_tag "kanbans_show"
50 |
--------------------------------------------------------------------------------
/app/views/kanbans/show.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.kanban @kanban, :id, :gitlab_project_id, :name, :slug, :created_at, :updated_at
2 | json.label_groups @label_groups, :label, :issues
3 |
--------------------------------------------------------------------------------
/app/views/layouts/application.html.slim:
--------------------------------------------------------------------------------
1 | doctype html
2 | html lang="en"
3 | head
4 | meta charset="utf-8"
5 | meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1"
6 | meta name="viewport" content="width=device-width, initial-scale=1.0"
7 | title= content_for?(:title) ? yield(:title) + " | Gitpeach" : "Gitpeach"
8 | = csrf_meta_tags
9 |
10 | /! Le HTML5 shim, for IE6-8 support of HTML elements
11 | /[if lt IE 9]
12 | = javascript_include_tag "//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.6.1/html5shiv.js"
13 | = stylesheet_link_tag "application", :media => "all"
14 | = favicon_link_tag 'apple-touch-icon-144x144-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '144x144'
15 | = favicon_link_tag 'apple-touch-icon-114x114-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '114x114'
16 | = favicon_link_tag 'apple-touch-icon-72x72-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '72x72'
17 | = favicon_link_tag 'apple-touch-icon-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png'
18 | = favicon_link_tag 'favicon.ico', :rel => 'shortcut icon'
19 | = javascript_include_tag "application"
20 |
21 |
22 |
23 | body
24 | - if logged_in?
25 | .navbar.navbar-static-top
26 | .container
27 | button.navbar-toggle type="button" data-toggle="collapse" data-target=".navbar-responsive-collapse"
28 | span.icon-bar
29 | span.icon-bar
30 | span.icon-bar
31 | a.navbar-brand href="/"
32 | | Gitpeach
33 | .nav-collapse.collapse.in.navbar-responsive-collapse
34 | ul.nav.navbar-nav.pull-right
35 | li
36 | = link_to logout_path, alt: "logout", title: "logout" do
37 | span.glyphicon.glyphicon-log-out
38 | li
39 | = link_to "#" do
40 | = avatar_tag(current_user, {size: 24}, class: "img-rounded")
41 |
42 | .container
43 | #alert-area
44 | .row
45 | .col-lg-12
46 | = bootstrap_flash
47 | = yield
48 |
49 | footer
50 | p © sue445 2014-
51 |
--------------------------------------------------------------------------------
/app/views/shared/_issue_panel.html.slim:
--------------------------------------------------------------------------------
1 | .panel.panel-primary.issue-panel id="issue_#{issue.id}"
2 | .panel-heading.issue-panel__header
3 | h3.panel-title
4 | = issue.iid || issue.iid
5 | - if issue.assignee
6 | = avatar_tag(issue.assignee, {size: 36}, class: "img-circle issue-panel__assignee")
7 | .panel-body
8 | .issue-panel__title
9 | = link_to @user_kanban.issue_url(issue) do
10 | = issue.title
11 | small.issue-panel__updated_at data-toggle="tooltip" title=Time.zone.parse(issue.updated_at)
12 | span.glyphicon.glyphicon-time
13 | = " #{time_ago_in_words(issue.updated_at, include_seconds: true)} ago"
14 | .panel-footer.issue-panel__footer
15 | .issue-panel__labels
16 | - issue.labels.each do |label|
17 | span.label class=label_css_class(label)
18 | = label
19 | - if issue.milestone
20 | .issue-panel__milestone
21 | p.text-success
22 | span.glyphicon.glyphicon-calendar
23 | = issue.milestone.title
24 | .issue-panel__note
25 | = link_to "#{@user_kanban.issue_url(issue)}#notes" do
26 | span.glyphicon.glyphicon-comment
27 |
--------------------------------------------------------------------------------
/app/views/top/_logged_in.html.slim:
--------------------------------------------------------------------------------
1 | - unless @user_kanban_projects.empty?
2 | h1 Your Kanbans
3 |
4 | table.table
5 | tbody
6 | - @user_kanban_projects.each do |project|
7 | tr
8 | td
9 | = link_to kanban_path(project.path_with_namespace) do
10 | = project.path_with_namespace
11 | td
12 | = link_to kanban_path(project.path_with_namespace), class: "btn btn-default" do
13 | span.glyphicon.glyphicon-tasks
14 | | Show
15 | = link_to edit_kanban_path(project.path_with_namespace), class: "btn btn-default" do
16 | span.glyphicon.glyphicon-wrench
17 | | Edit
18 | = link_to project.path_with_namespace, data: {:confirm => 'Are you sure?'}, class: "btn btn-danger", method: :delete do
19 | span.glyphicon.glyphicon-trash
20 | | Destroy
21 |
22 | - unless @other_kanbans.empty?
23 | h1 Other Kanbans
24 |
25 | table.table
26 | tbody
27 | - @other_kanbans.each do |kanban|
28 | tr
29 | td
30 | = link_to kanban_path(kanban) do
31 | = kanban.name
32 | td
33 | = link_to kanban_path(kanban), class: "btn btn-default" do
34 | span.glyphicon.glyphicon-tasks
35 | | Show
36 |
37 |
38 | - unless @no_kanban_projects.empty?
39 | h1 Your Projects
40 |
41 | table.table
42 | tbody
43 | - @no_kanban_projects.each do |project|
44 | tr
45 | td
46 | = link_to project.path_with_namespace, project.web_url
47 | td
48 | = form_tag kanbans_path
49 | = hidden_field_tag "kanban[gitlab_project_id]", project.id
50 | = hidden_field_tag "kanban[name]", project.path_with_namespace
51 | = button_tag type: "submit", class: "btn btn-primary" do
52 | span.glyphicon.glyphicon-file
53 | | Create Kanban
54 |
--------------------------------------------------------------------------------
/app/views/top/_not_logged_in.html.slim:
--------------------------------------------------------------------------------
1 | = link_to "https://github.com/sue445/gitpeach" do
2 | = image_tag "https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png", alt: "Fork me on GitHub", style: "position: absolute; top: 0; right: 0; border: 0;"
3 |
4 | h1 Gitpeach
5 |
6 | - gitlab_url = Gitlab.endpoint.gsub(%r{/api/.+}, "/")
7 |
8 | .alert.alert-info
9 | | Input username/password of
10 | |
11 | = link_to(gitlab_url, gitlab_url)
12 |
13 | = form_tag login_path, class: "form-horizontal" do
14 | = hidden_field_tag :back_to, params[:back_to]
15 | .form-group
16 | label for="login" class="col-sm-2 control-label"
17 | | Login
18 | .col-sm-8
19 | = text_field_tag :login, nil, class: "form-control"
20 | .form-group
21 | label for="password" class="col-sm-2 control-label"
22 | | Password
23 | .col-sm-8
24 | = password_field_tag :password, nil, class: "form-control"
25 | .form-group
26 | .col-sm-offset-2.col-sm-10
27 | = button_tag type: "submit", class: "btn btn-default" do
28 | | Login with Gitlab
29 |
30 | coffee:
31 | $(document).ready ->
32 | $("#login").focus()
--------------------------------------------------------------------------------
/app/views/top/index.html.slim:
--------------------------------------------------------------------------------
1 | - if logged_in?
2 | = render partial: "logged_in"
3 | - else
4 | = render partial: "not_logged_in"
5 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path('../../config/application', __FILE__)
3 | require_relative '../config/boot'
4 | require 'rails/commands'
5 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/bin/unicorn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require File.dirname(__FILE__) + '/../config/boot'
3 |
4 | require "fileutils"
5 | require "optparse"
6 |
7 | RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
8 | options = {
9 | "-c" => File.join(RAILS_ROOT, 'config', 'unicorn.rb'),
10 | "-E" => "development",
11 | "-p" => "8080"
12 | }
13 |
14 | ARGV.clone.options do |opts|
15 | opts.on("-d", "--daemon", "Make server run as a Daemon") { options["-D"] = nil }
16 | opts.on("-e", "--env=environment", "Rails environment(default: development)") {|v| options["-E"] = v }
17 | opts.on("-c", "--config=file", "use custom unicorn configuration file(default: config/unicorn.rb)") {|v| options["-c"] = v }
18 | opts.on("-u", "--debugger", "Enable Debugger") {|v| options["-d"] = nil }
19 | opts.on("-p", "--port=number", "Port number") {|v| options["-p"] = v}
20 |
21 | opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
22 | opts.parse!
23 | end
24 |
25 |
26 | class UnicornManager
27 | class << self
28 | def start(options = {})
29 | exec("unicorn_rails #{options.to_a.join(' ')}")
30 | end
31 |
32 | # reloading config but deployed application code cannot be loaded when preload_app == true.
33 | def hup(options = {})
34 | if send_signal("HUP", master_pid)
35 | puts "unicorn master successfully SIGHUPed."
36 | else
37 | puts "cannot send SIGHUP signal to unicorn server."
38 | end
39 | end
40 |
41 | # graceful_restarting with USR2+QUIT
42 | def graceful(options = {})
43 | if send_signal("USR2", master_pid)
44 | puts "send USR2 to unicorn master successfully.."
45 | else
46 | puts "cannot send USR2 signal to unicorn server."
47 | end
48 | end
49 |
50 | def stop(options = {})
51 | if send_signal("QUIT", master_pid)
52 | puts "unicorn master successfully SIGQUITed"
53 | else
54 | puts "cannot send SIGQUIT signal to unicorn server."
55 | end
56 | end
57 |
58 | def kill(options = {})
59 | if send_signal("INT", master_pid)
60 | puts "unicorn master successfully SIGINTed"
61 | else
62 | puts "cannot send SIGINT signal to unicorn server."
63 | end
64 | end
65 |
66 | def rotatelog(options = {})
67 | if send_signal("USR1", master_pid)
68 | puts "unicorn master successfully SIGUSR1ed"
69 | else
70 | puts "cannot send SIGUSR1 signal to unicorn server."
71 | end
72 | end
73 |
74 | def incr(options = {})
75 | if send_signal("TTIN", master_pid)
76 | puts "unicorn master successfully SIGTTINed"
77 | else
78 | puts "cannot send SIGTTIN signal to unicorn server."
79 | end
80 | end
81 |
82 | def decr(options = {})
83 | if send_signal("TTOU", master_pid)
84 | puts "unicorn master successfully SIGTTOUed"
85 | else
86 | puts "cannot send SIGTTOU signal to unicorn server."
87 | end
88 | end
89 |
90 | def command(command, options)
91 | self.send(command, options)
92 | end
93 |
94 | def master_pid
95 | File.read(File.join(RAILS_ROOT, 'tmp', 'pids', 'unicorn.pid')).strip
96 | end
97 |
98 | def old_master_pid
99 | File.read(File.join(RAILS_ROOT, 'tmp', 'pids', 'unicorn.pid.oldbin')).strip
100 | end
101 |
102 | def send_signal(signal, pid)
103 | exec("kill", "-#{signal}", pid)
104 | end
105 | end
106 | end
107 |
108 | command = if ["start", "stop", "hup", "graceful", "kill", "rotatelog", "incr", "decr"].include?(ARGV.first)
109 | ARGV.first
110 | else
111 | "start"
112 | end
113 |
114 | if options["-E"] != "development"
115 | options.delete("-p")
116 | end
117 | UnicornManager.command(command, options)
118 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require ::File.expand_path('../config/environment', __FILE__)
4 | run Rails.application
5 |
--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../boot', __FILE__)
2 |
3 | require 'rails/all'
4 |
5 | # Require the gems listed in Gemfile, including any gems
6 | # you've limited to :test, :development, or :production.
7 | Bundler.require(*Rails.groups)
8 |
9 | module Gitpeach
10 | class Application < Rails::Application
11 | # Settings in config/environments/* take precedence over those specified here.
12 | # Application configuration should go into files in config/initializers
13 | # -- all .rb files in that directory are automatically loaded.
14 |
15 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
16 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
17 | # config.time_zone = 'Central Time (US & Canada)'
18 |
19 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
20 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
21 | # config.i18n.default_locale = :de
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | # Set up gems listed in the Gemfile.
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 |
4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
5 |
--------------------------------------------------------------------------------
/config/database.yml.mysql:
--------------------------------------------------------------------------------
1 | # MySQL. Versions 4.1 and 5.0 are recommended.
2 | #
3 | # Install the MYSQL driver
4 | # gem install mysql2
5 | #
6 | # Ensure the MySQL gem is defined in your Gemfile
7 | # gem 'mysql2'
8 | #
9 | # And be sure to use new-style password hashing:
10 | # http://dev.mysql.com/doc/refman/5.0/en/old-client.html
11 | development:
12 | adapter: mysql2
13 | encoding: utf8
14 | database: gitpeach_development
15 | pool: 5
16 | username: root
17 | # password: root
18 | # socket: /tmp/mysql.sock
19 |
20 | # Warning: The database defined as "test" will be erased and
21 | # re-generated from your development database when you run "rake".
22 | # Do not set this db to the same as development or production.
23 | test:
24 | adapter: mysql2
25 | encoding: utf8
26 | database: gitpeach_test
27 | pool: 5
28 | username: root
29 | # password: root
30 | # socket: /tmp/mysql.sock
31 |
32 | production:
33 | adapter: mysql2
34 | encoding: utf8
35 | database: gitpeach_production
36 | pool: 5
37 | username: momozono
38 | password: love
39 | socket: /tmp/mysql.sock
40 |
--------------------------------------------------------------------------------
/config/database.yml.postgresql:
--------------------------------------------------------------------------------
1 | # PostgreSQL. Versions 8.2 and up are supported.
2 | #
3 | # Install the pg driver:
4 | # gem install pg
5 | # On OS X with Homebrew:
6 | # gem install pg -- --with-pg-config=/usr/local/bin/pg_config
7 | # On OS X with MacPorts:
8 | # gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config
9 | # On Windows:
10 | # gem install pg
11 | # Choose the win32 build.
12 | # Install PostgreSQL and put its /bin directory on your path.
13 | #
14 | # Configure Using Gemfile
15 | # gem 'pg'
16 | #
17 | development:
18 | adapter: postgresql
19 | encoding: unicode
20 | database: gitpeach_development
21 | pool: 5
22 | username: root
23 | # password: root
24 |
25 | # Connect on a TCP socket. Omitted by default since the client uses a
26 | # domain socket that doesn't need configuration. Windows does not have
27 | # domain sockets, so uncomment these lines.
28 | #host: localhost
29 |
30 | # The TCP port the server listens on. Defaults to 5432.
31 | # If your server runs on a different port number, change accordingly.
32 | #port: 5432
33 |
34 | # Schema search path. The server defaults to $user,public
35 | #schema_search_path: myapp,sharedapp,public
36 |
37 | # Minimum log levels, in increasing order:
38 | # debug5, debug4, debug3, debug2, debug1,
39 | # log, notice, warning, error, fatal, and panic
40 | # Defaults to warning.
41 | #min_messages: notice
42 |
43 | # Warning: The database defined as "test" will be erased and
44 | # re-generated from your development database when you run "rake".
45 | # Do not set this db to the same as development or production.
46 | test:
47 | adapter: postgresql
48 | encoding: unicode
49 | database: gitpeach_test
50 | pool: 5
51 | # username: root
52 | # password: root
53 |
54 | production:
55 | adapter: postgresql
56 | encoding: unicode
57 | database: gitpeach_production
58 | pool: 5
59 | username: momozono
60 | password: love
61 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require File.expand_path('../application', __FILE__)
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports and disable caching.
13 | config.consider_all_requests_local = true
14 | config.action_controller.perform_caching = false
15 |
16 | # Don't care if the mailer can't send.
17 | config.action_mailer.raise_delivery_errors = false
18 |
19 | # Print deprecation notices to the Rails logger.
20 | config.active_support.deprecation = :log
21 |
22 | # Raise an error on page load if there are pending migrations.
23 | config.active_record.migration_error = :page_load
24 |
25 | # Debug mode disables concatenation and preprocessing of assets.
26 | # This option may cause significant delays in view rendering with a large
27 | # number of complex assets.
28 | config.assets.debug = true
29 |
30 | # Adds additional error checking when serving assets at runtime.
31 | # Checks for improperly declared sprockets dependencies.
32 | # Raises helpful error messages.
33 | config.assets.raise_runtime_errors = true
34 |
35 | # Raises error for missing translations
36 | # config.action_view.raise_on_missing_translations = true
37 |
38 | config.assets.precompile += %w( *.js )
39 | end
40 |
41 | # indent slim view
42 | Slim::Engine.set_default_options pretty: true
43 |
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both threaded web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application
18 | # Add `rack-cache` to your Gemfile before enabling this.
19 | # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid.
20 | # config.action_dispatch.rack_cache = true
21 |
22 | # Disable Rails's static asset server (Apache or nginx will already do this).
23 | config.serve_static_assets = false
24 |
25 | # Compress JavaScripts and CSS.
26 | config.assets.js_compressor = :uglifier
27 | # config.assets.css_compressor = :sass
28 |
29 | # Do not fallback to assets pipeline if a precompiled asset is missed.
30 | config.assets.compile = false
31 |
32 | # Generate digests for assets URLs.
33 | config.assets.digest = true
34 |
35 | # Version of your assets, change this if you want to expire all your assets.
36 | config.assets.version = '1.0'
37 |
38 | # Specifies the header that your server uses for sending files.
39 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
40 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
41 |
42 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
43 | # config.force_ssl = true
44 |
45 | # Set to :debug to see everything in the log.
46 | config.log_level = :info
47 |
48 | # Prepend all log lines with the following tags.
49 | # config.log_tags = [ :subdomain, :uuid ]
50 |
51 | # Use a different logger for distributed setups.
52 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
53 |
54 | # Use a different cache store in production.
55 | # config.cache_store = :mem_cache_store
56 |
57 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
58 | # config.action_controller.asset_host = "http://assets.example.com"
59 |
60 | # Precompile additional assets.
61 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
62 | # config.assets.precompile += %w( search.js )
63 | config.assets.precompile += %w( *.js )
64 |
65 | # Ignore bad email addresses and do not raise email delivery errors.
66 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
67 | # config.action_mailer.raise_delivery_errors = false
68 |
69 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
70 | # the I18n.default_locale when a translation cannot be found).
71 | config.i18n.fallbacks = true
72 |
73 | # Send deprecation notices to registered listeners.
74 | config.active_support.deprecation = :notify
75 |
76 | # Disable automatic flushing of the log to improve performance.
77 | # config.autoflush_log = false
78 |
79 | # Use default logging formatter so that PID and timestamp are not suppressed.
80 | config.log_formatter = ::Logger::Formatter.new
81 |
82 | # Do not dump schema after migrations.
83 | config.active_record.dump_schema_after_migration = false
84 | end
85 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Do not eager load code on boot. This avoids loading your whole application
11 | # just for the purpose of running a single test. If you are using a tool that
12 | # preloads Rails for running tests, you may have to set it to true.
13 | config.eager_load = false
14 |
15 | # Configure static asset server for tests with Cache-Control for performance.
16 | config.serve_static_assets = true
17 | config.static_cache_control = 'public, max-age=3600'
18 |
19 | # Show full error reports and disable caching.
20 | config.consider_all_requests_local = true
21 | config.action_controller.perform_caching = false
22 |
23 | # Raise exceptions instead of rendering exception templates.
24 | config.action_dispatch.show_exceptions = false
25 |
26 | # Disable request forgery protection in test environment.
27 | config.action_controller.allow_forgery_protection = false
28 |
29 | # Tell Action Mailer not to deliver emails to the real world.
30 | # The :test delivery method accumulates sent emails in the
31 | # ActionMailer::Base.deliveries array.
32 | config.action_mailer.delivery_method = :test
33 |
34 | # Print deprecation notices to the stderr.
35 | config.active_support.deprecation = :stderr
36 |
37 | # Raises error for missing translations
38 | # config.action_view.raise_on_missing_translations = true
39 | end
40 |
--------------------------------------------------------------------------------
/config/gitlab.yml.sample:
--------------------------------------------------------------------------------
1 | development: &development
2 | endpoint: https://example.net/api/v3
3 |
4 | test:
5 | <<: *development
6 |
7 | production:
8 | <<: *development
9 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Rails.application.config.action_dispatch.cookies_serializer = :hybrid
4 |
--------------------------------------------------------------------------------
/config/initializers/exception_full_backtrace.rb:
--------------------------------------------------------------------------------
1 | class Exception
2 | def full_backtrace
3 | ([self.to_s] + self.backtrace).join("\n")
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password]
5 |
--------------------------------------------------------------------------------
/config/initializers/friendly_id.rb:
--------------------------------------------------------------------------------
1 | # FriendlyId Global Configuration
2 | #
3 | # Use this to set up shared configuration options for your entire application.
4 | # Any of the configuration options shown here can also be applied to single
5 | # models by passing arguments to the `friendly_id` class method or defining
6 | # methods in your model.
7 | #
8 | # To learn more, check out the guide:
9 | #
10 | # http://norman.github.io/friendly_id/file.Guide.html
11 |
12 | FriendlyId.defaults do |config|
13 | # ## Reserved Words
14 | #
15 | # Some words could conflict with Rails's routes when used as slugs, or are
16 | # undesirable to allow as slugs. Edit this list as needed for your app.
17 | config.use :reserved
18 |
19 | config.reserved_words = %w(new edit index session login logout users admin
20 | stylesheets assets javascripts images)
21 |
22 | # ## Friendly Finders
23 | #
24 | # Uncomment this to use friendly finders in all models. By default, if
25 | # you wish to find a record by its friendly id, you must do:
26 | #
27 | # MyModel.friendly.find('foo')
28 | #
29 | # If you uncomment this, you can do:
30 | #
31 | # MyModel.find('foo')
32 | #
33 | # This is significantly more convenient but may not be appropriate for
34 | # all applications, so you must explicity opt-in to this behavior. You can
35 | # always also configure it on a per-model basis if you prefer.
36 | #
37 | # Something else to consider is that using the :finders addon boosts
38 | # performance because it will avoid Rails-internal code that makes runtime
39 | # calls to `Module.extend`.
40 | #
41 | # config.use :finders
42 | #
43 | # ## Slugs
44 | #
45 | # Most applications will use the :slugged module everywhere. If you wish
46 | # to do so, uncomment the following line.
47 | #
48 | # config.use :slugged
49 | #
50 | # By default, FriendlyId's :slugged addon expects the slug column to be named
51 | # 'slug', but you can change it if you wish.
52 | #
53 | # config.slug_column = 'slug'
54 | #
55 | # When FriendlyId can not generate a unique ID from your base method, it appends
56 | # a UUID, separated by a single dash. You can configure the character used as the
57 | # separator. If you're upgrading from FriendlyId 4, you may wish to replace this
58 | # with two dashes.
59 | #
60 | # config.sequence_separator = '-'
61 | #
62 | # ## Tips and Tricks
63 | #
64 | # ### Controlling when slugs are generated
65 | #
66 | # As of FriendlyId 5.0, new slugs are generated only when the slug field is
67 | # nil, but you if you're using a column as your base method can change this
68 | # behavior by overriding the `should_generate_new_friendly_id` method that
69 | # FriendlyId adds to your model. The change below makes FriendlyId 5.0 behave
70 | # more like 4.0.
71 | #
72 | # config.use Module.new {
73 | # def should_generate_new_friendly_id?
74 | # slug.blank? || _changed?
75 | # end
76 | # }
77 | #
78 | # FriendlyId uses Rails's `parameterize` method to generate slugs, but for
79 | # languages that don't use the Roman alphabet, that's not usually suffient. Here
80 | # we use the Babosa library to transliterate Russian Cyrillic slugs to ASCII. If
81 | # you use this, don't forget to add "babosa" to your Gemfile.
82 | #
83 | # config.use Module.new {
84 | # def normalize_friendly_id(text)
85 | # text.to_slug.normalize! :transliterations => [:russian, :latin]
86 | # end
87 | # }
88 | end
89 |
--------------------------------------------------------------------------------
/config/initializers/gitlab.rb:
--------------------------------------------------------------------------------
1 | Gitlab.configure do |config|
2 | gitlab_config = YAML.load_file(File.join(Rails.root, "config", "gitlab.yml")).with_indifferent_access
3 | config.endpoint = gitlab_config[Rails.env][:endpoint] # API endpoint URL (required)
4 | config.private_token = "dummy"
5 | #config.user_agent = 'Custom User Agent' # user agent, default to 'Gitlab Ruby Gem [version]' (optional)
6 | end
7 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/config/initializers/pusher.rb:
--------------------------------------------------------------------------------
1 | require 'pusher'
2 |
3 | config_file = File.join(Rails.root, "config", "pusher.yml")
4 | if File.exists?(config_file)
5 | pusher_config = YAML.load_file(config_file).with_indifferent_access
6 | Pusher.app_id = pusher_config[Rails.env][:app_id]
7 | Pusher.key = pusher_config[Rails.env][:key]
8 | Pusher.secret = pusher_config[Rails.env][:secret]
9 | else
10 | Pusher.app_id = ENV["PUSHER_APP_ID"]
11 | Pusher.key = ENV["PUSHER_KEY"]
12 | Pusher.secret = ENV["PUSHER_SECRET"]
13 | end
14 |
15 | unless Rails.env.test?
16 | raise "pusher app_id is required" if Pusher.app_id.blank?
17 | raise "pusher key is required" if Pusher.key.blank?
18 | raise "pusher secret is required" if Pusher.secret.blank?
19 | end
20 |
21 | Pusher.logger = Rails.logger
22 |
--------------------------------------------------------------------------------
/config/initializers/secret_token.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Your secret key is used for verifying the integrity of signed cookies.
4 | # If you change this key, all old signed cookies will become invalid!
5 |
6 | # Make sure the secret is at least 30 characters and all random,
7 | # no regular words or you'll be exposed to dictionary attacks.
8 | # You can use `rake secret` to generate a secure secret key.
9 |
10 | # Make sure your secret_key_base is kept private
11 | # if you're sharing your code publicly.
12 | Gitpeach::Application.config.secret_key_base = 'ef0a70620d35aab2bf68968cf9577e7d05e8d7bf337f867487659a0a2d8d99e85ff5a23492ef25707d202890057f4bb5e0a5c418a02fd03fe1d46a436e6e97cc'
13 |
--------------------------------------------------------------------------------
/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Rails.application.config.session_store :cookie_store, key: '_gitpeach_session'
4 |
--------------------------------------------------------------------------------
/config/initializers/url_for_with_unescape_id.rb:
--------------------------------------------------------------------------------
1 | # unescape url encoded id for rails 4.1.2+
2 | #
3 | # via.
4 | # * https://github.com/rails/rails/blob/v4.1.2/actionpack/CHANGELOG.md
5 | # * https://github.com/rails/rails/commit/8a067640e6fe222022dc77bb63d5da37ef75a189
6 |
7 | def url_for_with_unescape_id(options = nil)
8 | path = url_for_without_unescape_id(options)
9 |
10 | # unescape: %2F -> /
11 | path.gsub(%r{([a-zA-Z.0-9_\-]+)%2F([a-zA-Z.0-9_\-]+)}){ $1 + "/" + $2 }
12 | end
13 |
14 | module ActionView
15 | module RoutingUrlFor
16 | alias_method_chain :url_for, :unescape_id
17 | end
18 | end
19 |
20 | class ActionController::Base
21 | alias_method_chain :url_for, :unescape_id
22 | end
23 |
--------------------------------------------------------------------------------
/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/config/locales/en.bootstrap.yml:
--------------------------------------------------------------------------------
1 | # Sample localization file for English. Add more files in this directory for other locales.
2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3 |
4 | en:
5 | helpers:
6 | actions: "Actions"
7 | links:
8 | back: "Back"
9 | cancel: "Cancel"
10 | confirm: "Are you sure?"
11 | destroy: "Delete"
12 | new: "New"
13 | edit: "Edit"
14 | titles:
15 | edit: "Edit %{model}"
16 | save: "Save %{model}"
17 | new: "New %{model}"
18 | delete: "Delete %{model}"
19 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # To learn more, please read the Rails Internationalization guide
20 | # available at http://guides.rubyonrails.org/i18n.html.
21 |
22 | en:
23 | hello: "Hello world"
24 |
--------------------------------------------------------------------------------
/config/locales/ja.bootstrap.yml:
--------------------------------------------------------------------------------
1 | # https://gist.github.com/sue445/5261654
2 |
3 | ja:
4 | helpers:
5 | actions: "Actions"
6 | links:
7 | back: "戻る"
8 | cancel: "キャンセル"
9 | confirm: "本当にいいですか?"
10 | destroy: "削除"
11 | new: "新規"
12 | titles:
13 | edit: "編集"
14 | save: "保存"
15 | new: "新規"
16 | delete: "削除"
17 |
--------------------------------------------------------------------------------
/config/locales/ja.yml:
--------------------------------------------------------------------------------
1 | ja:
2 | date:
3 | abbr_day_names:
4 | - 日
5 | - 月
6 | - 火
7 | - 水
8 | - 木
9 | - 金
10 | - 土
11 | abbr_month_names:
12 | -
13 | - 1月
14 | - 2月
15 | - 3月
16 | - 4月
17 | - 5月
18 | - 6月
19 | - 7月
20 | - 8月
21 | - 9月
22 | - 10月
23 | - 11月
24 | - 12月
25 | day_names:
26 | - 日曜日
27 | - 月曜日
28 | - 火曜日
29 | - 水曜日
30 | - 木曜日
31 | - 金曜日
32 | - 土曜日
33 | formats:
34 | default: ! '%Y/%m/%d'
35 | long: ! '%Y年%m月%d日(%a)'
36 | short: ! '%m/%d'
37 | month_names:
38 | -
39 | - 1月
40 | - 2月
41 | - 3月
42 | - 4月
43 | - 5月
44 | - 6月
45 | - 7月
46 | - 8月
47 | - 9月
48 | - 10月
49 | - 11月
50 | - 12月
51 | order:
52 | - :year
53 | - :month
54 | - :day
55 | datetime:
56 | distance_in_words:
57 | about_x_hours:
58 | one: 約1時間
59 | other: 約%{count}時間
60 | about_x_months:
61 | one: 約1ヶ月
62 | other: 約%{count}ヶ月
63 | about_x_years:
64 | one: 約1年
65 | other: 約%{count}年
66 | almost_x_years:
67 | one: 1年弱
68 | other: ! '%{count}年弱'
69 | half_a_minute: 30秒前後
70 | less_than_x_minutes:
71 | one: 1分以内
72 | other: ! '%{count}分未満'
73 | less_than_x_seconds:
74 | one: 1秒以内
75 | other: ! '%{count}秒未満'
76 | over_x_years:
77 | one: 1年以上
78 | other: ! '%{count}年以上'
79 | x_days:
80 | one: 1日
81 | other: ! '%{count}日'
82 | x_minutes:
83 | one: 1分
84 | other: ! '%{count}分'
85 | x_months:
86 | one: 1ヶ月
87 | other: ! '%{count}ヶ月'
88 | x_seconds:
89 | one: 1秒
90 | other: ! '%{count}秒'
91 | prompts:
92 | day: 日
93 | hour: 時
94 | minute: 分
95 | month: 月
96 | second: 秒
97 | year: 年
98 | errors: &errors
99 | format: ! '%{attribute}%{message}'
100 | messages:
101 | accepted: を受諾してください。
102 | blank: を入力してください。
103 | present: は入力しないでください。
104 | confirmation: と%{attribute}の入力が一致しません。
105 | empty: を入力してください。
106 | equal_to: は%{count}にしてください。
107 | even: は偶数にしてください。
108 | exclusion: は予約されています。
109 | greater_than: は%{count}より大きい値にしてください。
110 | greater_than_or_equal_to: は%{count}以上の値にしてください。
111 | inclusion: は一覧にありません。
112 | invalid: は不正な値です。
113 | less_than: は%{count}より小さい値にしてください。
114 | less_than_or_equal_to: は%{count}以下の値にしてください。
115 | not_a_number: は数値で入力してください。
116 | not_an_integer: は整数で入力してください。
117 | odd: は奇数にしてください。
118 | record_invalid: バリデーションに失敗しました。 %{errors}
119 | restrict_dependent_destroy: ! '%{record}が存在しているので削除できません。'
120 | taken: はすでに存在します。
121 | too_long: は%{count}文字以内で入力してください。
122 | too_short: は%{count}文字以上で入力してください。
123 | wrong_length: は%{count}文字で入力してください。
124 | other_than: "は%{count}以外の値にしてください。"
125 | template:
126 | body: 次の項目を確認してください。
127 | header:
128 | one: ! '%{model}にエラーが発生しました。'
129 | other: ! '%{model}に%{count}個のエラーが発生しました。'
130 | helpers:
131 | select:
132 | prompt: 選択してください。
133 | submit:
134 | create: 登録する
135 | submit: 保存する
136 | update: 更新する
137 | number:
138 | currency:
139 | format:
140 | delimiter: ! ','
141 | format: ! '%n%u'
142 | precision: 0
143 | separator: .
144 | significant: false
145 | strip_insignificant_zeros: false
146 | unit: 円
147 | format:
148 | delimiter: ! ','
149 | precision: 3
150 | separator: .
151 | significant: false
152 | strip_insignificant_zeros: false
153 | human:
154 | decimal_units:
155 | format: ! '%n %u'
156 | units:
157 | billion: 十億
158 | million: 百万
159 | quadrillion: 千兆
160 | thousand: 千
161 | trillion: 兆
162 | unit: ''
163 | format:
164 | delimiter: ''
165 | precision: 3
166 | significant: true
167 | strip_insignificant_zeros: true
168 | storage_units:
169 | format: ! '%n%u'
170 | units:
171 | byte: バイト
172 | gb: ギガバイト
173 | kb: キロバイト
174 | mb: メガバイト
175 | tb: テラバイト
176 | percentage:
177 | format:
178 | delimiter: ''
179 | format: "%n%"
180 | precision:
181 | format:
182 | delimiter: ''
183 | support:
184 | array:
185 | last_word_connector: と
186 | two_words_connector: と
187 | words_connector: と
188 | time:
189 | am: 午前
190 | formats:
191 | default: ! '%Y/%m/%d %H:%M:%S'
192 | long: ! '%Y年%m月%d日(%a) %H時%M分%S秒 %z'
193 | short: ! '%y/%m/%d %H:%M'
194 | pm: 午後
195 | # remove these aliases after 'activemodel' and 'activerecord' namespaces are removed from Rails repository
196 | activemodel:
197 | errors:
198 | <<: *errors
199 | activerecord:
200 | errors:
201 | <<: *errors
202 |
--------------------------------------------------------------------------------
/config/pusher.yml.sample:
--------------------------------------------------------------------------------
1 | development: &development
2 | app_id: your-pusher-app-id
3 | key: your-pusher-key
4 | secret: your-pusher-secret
5 |
6 | test:
7 | <<: *development
8 |
9 | production:
10 | <<: *development
11 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | Gitpeach::Application.routes.draw do
2 | post "login" => "sessions#create" , as: :login
3 | get "logout" => "sessions#destroy", as: :logout
4 |
5 | # The priority is based upon order of creation: first created -> highest priority.
6 | # See how all your routes lay out with "rake routes".
7 |
8 | # You can have the root of your site routed with "root"
9 | root 'top#index'
10 |
11 | resources :kanbans, constraints: { id: /[a-zA-Z.0-9_\-]+\/[a-zA-Z.0-9_\-]+/ }, only: [:show, :create, :destroy, :edit, :update], path: "/" do
12 | member do
13 | get :sync
14 | end
15 |
16 | resources :issues, constraints: {id: /\d+/}, only: [:show, :update, :create] do
17 | end
18 | end
19 |
20 | # Example of regular route:
21 | # get 'products/:id' => 'catalog#view'
22 |
23 | # Example of named route that can be invoked with purchase_url(id: product.id)
24 | # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase
25 |
26 | # Example resource route (maps HTTP verbs to controller actions automatically):
27 | # resources :products
28 |
29 | # Example resource route with options:
30 | # resources :products do
31 | # member do
32 | # get 'short'
33 | # post 'toggle'
34 | # end
35 | #
36 | # collection do
37 | # get 'sold'
38 | # end
39 | # end
40 |
41 | # Example resource route with sub-resources:
42 | # resources :products do
43 | # resources :comments, :sales
44 | # resource :seller
45 | # end
46 |
47 | # Example resource route with more complex sub-resources:
48 | # resources :products do
49 | # resources :comments
50 | # resources :sales do
51 | # get 'recent', on: :collection
52 | # end
53 | # end
54 |
55 | # Example resource route with concerns:
56 | # concern :toggleable do
57 | # post 'toggle'
58 | # end
59 | # resources :posts, concerns: :toggleable
60 | # resources :photos, concerns: :toggleable
61 |
62 | # Example resource route within a namespace:
63 | # namespace :admin do
64 | # # Directs /admin/products/* to Admin::ProductsController
65 | # # (app/controllers/admin/products_controller.rb)
66 | # resources :products
67 | # end
68 | end
69 |
--------------------------------------------------------------------------------
/config/unicorn.rb:
--------------------------------------------------------------------------------
1 | # Sample configuration file for Unicorn (not Rack)
2 | #
3 | # See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
4 | # documentation.
5 |
6 | APP_DIR = File.expand_path("../../", __FILE__)
7 | UNICORN_USER = "www-data"
8 | UNICORN_GROUP = "www-data"
9 |
10 | # Use at least one worker per core if you're on a dedicated server,
11 | # more will usually help for _short_ waits on databases/caches.
12 | worker_processes 4
13 |
14 | # Help ensure your application will always spawn in the symlinked
15 | # "current" directory that Capistrano sets up.
16 | # working_directory "<%= unicorn_working_directory %>" # available in 0.94.0+
17 |
18 | # listen on both a Unix domain socket and a TCP port,
19 | # we use a shorter backlog for quicker failover when busy
20 | listen "#{APP_DIR}/tmp/unicorn.sock", :backlog => 128
21 | listen 8080, :tcp_nopush => true
22 |
23 | # nuke workers after 30 seconds instead of 60 seconds (the default)
24 | timeout 30
25 |
26 | # feel free to point this anywhere accessible on the filesystem
27 | #pid '/tmp/pids/unicorn.pid'
28 |
29 | # some applications/frameworks log to stderr or stdout, so prevent
30 | # them from going to /dev/null when daemonized here:
31 | stderr_path "#{APP_DIR}/log/unicorn.stderr.log"
32 | stdout_path "#{APP_DIR}/log/unicorn.stdout.log"
33 |
34 | # combine REE with "preload_app true" for memory savings
35 | # http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
36 | preload_app true
37 | GC.respond_to?(:copy_on_write_friendly=) and GC.copy_on_write_friendly = true
38 |
39 | before_fork do |server, worker|
40 | # the following is highly recomended for Rails + "preload_app true"
41 | # as there's no need for the master process to hold a connection
42 | defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
43 | defined?(Resque) and Resque.redis and Resque.redis.client.disconnect
44 |
45 | # The following is only recommended for memory/DB-constrained
46 | # installations. It is not needed if your system can house
47 | # twice as many worker_processes as you have configured.
48 |
49 | # This allows a new master process to incrementally
50 | # phase out the old master process with SIGTTOU to avoid a
51 | # thundering herd (especially in the "preload_app false" case)
52 | # when doing a transparent upgrade. The last worker spawned
53 | # will then kill off the old master process with a SIGQUIT.
54 | old_pid = "#{server.config[:pid]}.oldbin"
55 | if File.exists?(old_pid) && server.pid != old_pid
56 | begin
57 | Process.kill("QUIT", File.read(old_pid).to_i)
58 | rescue Errno::ENOENT, Errno::ESRCH
59 | # someone else did our job for us
60 | end
61 | end
62 |
63 | # # *optionally* throttle the master from forking too quickly by sleeping
64 | # sleep 1
65 | end
66 |
67 | after_fork do |server, worker|
68 | # per-process listener ports for debugging/admin/migrations
69 | # addr = "127.0.0.1:#{9293 + worker.nr}"
70 | # server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
71 |
72 | # the following is *required* for Rails + "preload_app true",
73 | defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
74 | # TODO uncomment out
75 | #defined?(Resque) and Resque.redis and Resque.redis.client.connect
76 |
77 | # if preload_app is true, then you may also want to check and # restart any other shared sockets/descriptors such as Memcached,
78 | # and Redis. TokyoCabinet file handles are safe to reuse
79 | # between any number of forked children (assuming your kernel correctly implements pread()/pwrite() system calls)
80 | worker.user(UNICORN_USER, UNICORN_GROUP) if Process.euid == 0
81 | end
82 |
--------------------------------------------------------------------------------
/db/migrate/20140110175259_create_users.rb:
--------------------------------------------------------------------------------
1 | class CreateUsers < ActiveRecord::Migration
2 | def change
3 | create_table :users do |t|
4 | t.integer :gitlab_user_id
5 | t.string :username
6 | t.string :email
7 | t.string :private_token
8 |
9 | t.timestamps
10 | end
11 |
12 | add_index :users, :gitlab_user_id, unique: true
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/db/migrate/20140112144617_create_friendly_id_slugs.rb:
--------------------------------------------------------------------------------
1 | class CreateFriendlyIdSlugs < ActiveRecord::Migration
2 | def change
3 | create_table :friendly_id_slugs do |t|
4 | t.string :slug, :null => false
5 | t.integer :sluggable_id, :null => false
6 | t.string :sluggable_type, :limit => 50
7 | t.string :scope
8 | t.datetime :created_at
9 | end
10 | add_index :friendly_id_slugs, :sluggable_id
11 | add_index :friendly_id_slugs, [:slug, :sluggable_type]
12 | add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], :unique => true
13 | add_index :friendly_id_slugs, :sluggable_type
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/db/migrate/20140112145704_create_kanbans.rb:
--------------------------------------------------------------------------------
1 | class CreateKanbans < ActiveRecord::Migration
2 | def change
3 | create_table :kanbans do |t|
4 | t.integer :gitlab_project_id
5 | t.string :name
6 | t.string :slug
7 |
8 | t.timestamps
9 | end
10 |
11 | add_index :kanbans, :slug, unique: true
12 | add_index :kanbans, :name, unique: true
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/db/migrate/20140112164427_create_labels.rb:
--------------------------------------------------------------------------------
1 | class CreateLabels < ActiveRecord::Migration
2 | def change
3 | create_table :labels do |t|
4 | t.integer :kanban_id
5 | t.string :name
6 | t.string :gitlab_label
7 | t.integer :disp_order
8 | t.boolean :is_backlog_issue
9 | t.boolean :is_close_issue
10 |
11 | t.timestamps
12 | end
13 |
14 | add_index :labels, [:kanban_id, :disp_order]
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3 | #
4 | # Examples:
5 | #
6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
7 | # Mayor.create(name: 'Emanuel', city: cities.first)
8 |
--------------------------------------------------------------------------------
/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/lib/assets/.keep
--------------------------------------------------------------------------------
/lib/support/init.d/unicorn_gitpeach:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # refs: http://gist.github.com/237953
3 | set -u
4 | set -e
5 |
6 | APP_ROOT=/var/www/gitpeach
7 | CONF=$APP_ROOT/config/unicorn.conf
8 | USAGE="Usage: $0 <(production=default|staging)> "
9 | ENVIRONMENT=production
10 |
11 | cd $APP_ROOT || exit 1
12 |
13 | if [ $# -eq 0 ]; then
14 | echo >&2 $USAGE
15 | exit 0
16 | else
17 | echo "$1 ....... "
18 | fi
19 |
20 | ENV=$ENVIRONMENT
21 | if [ $# -gt 1 ] ; then
22 | case $2 in
23 | production)
24 | ENV=production
25 | ;;
26 | staging)
27 | ENV=staging
28 | ;;
29 | stress)
30 | ENV=stress
31 | ;;
32 | development)
33 | ENV=development
34 | ;;
35 | *)
36 | ENV=$ENVIRONMENT
37 | ;;
38 | esac
39 | fi
40 |
41 | GEMFILE=$APP_ROOT/Gemfile
42 | SCRIPT=$APP_ROOT/bin/unicorn
43 |
44 | CMD_START="$SCRIPT start -d -e $ENV"
45 | CMD_STOP="$SCRIPT stop"
46 | CMD_KILL="$SCRIPT kill"
47 | CMD_RESTART="$SCRIPT graceful"
48 | CMD_ROTATE="$SCRIPT rotatelog"
49 |
50 | SUCCESS="$1 success."
51 |
52 | # run command
53 | case $1 in
54 | start)
55 | BUNDLE_GEMFILE=$GEMFILE $CMD_START
56 | echo $SUCCESS
57 | ;;
58 | stop)
59 | $CMD_STOP
60 | echo $SUCCESS
61 | ;;
62 | force-stop)
63 | $CMD_KILL
64 | echo $SUCCESS
65 | ;;
66 | restart|reload)
67 | $CMD_RESTART
68 | echo $SUCCESS
69 | ;;
70 | rotate)
71 | $CMD_ROTATE
72 | echo $SUCCESS
73 | ;;
74 | *)
75 | echo >&2 $USAGE
76 | exit 1
77 | ;;
78 | esac
79 |
80 |
--------------------------------------------------------------------------------
/lib/support/logrotate.d/gitpeach:
--------------------------------------------------------------------------------
1 | /var/www/gitpeach/log/*.log {
2 | daily
3 | dateext
4 | missingok
5 | rotate 14
6 | compress
7 | notifempty
8 | copytruncate
9 | olddir /var/www/gitpeach/log/old
10 | }
11 |
--------------------------------------------------------------------------------
/lib/support/nginx/gitpeach:
--------------------------------------------------------------------------------
1 | # statements for each of your virtual hosts
2 | upstream gitpeach {
3 | server 127.0.0.1:8080;
4 | }
5 |
6 | server {
7 | listen 80 default;
8 |
9 | #listen 80 ;
10 | #listen 443 default_server ssl;
11 | #listen 443 default ssl;
12 | # TODO edit here
13 | server_name peach.example.com;
14 |
15 | #ssl on;
16 | #ssl_certificate /etc/nginx/conf.d/cert.pem;
17 | #ssl_certificate_key /etc/nginx/conf.d/cert.key;
18 |
19 | access_log /var/log/nginx/gitpeach_access.log combined;
20 | error_log /var/log/nginx/gitpeach_error.log warn;
21 |
22 | location / {
23 | root /var/www/gitpeach/public;
24 |
25 | if (-f $request_filename) {
26 | access_log off;
27 | rewrite_log off;
28 | expires 1h;
29 | break;
30 | }
31 |
32 | proxy_set_header X-Forwarded-Proto $scheme;
33 | # proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
34 | proxy_set_header X-Real-IP $remote_addr;
35 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
36 | proxy_set_header Host $host;
37 | proxy_redirect off;
38 | #proxy_read_timeout 8;
39 | #proxy_connect_timeout 2;
40 | send_timeout 300;
41 | proxy_read_timeout 300;
42 | proxy_connect_timeout 300;
43 | proxy_buffer_size 16k;
44 | proxy_buffers 32 16k;
45 | proxy_busy_buffers_size 64k;
46 |
47 | if (!-f $request_filename) {
48 | proxy_pass http://gitpeach;
49 | break;
50 | }
51 | }
52 |
53 | location ~ /\.git {
54 | deny all;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/lib/tasks/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/lib/tasks/.keep
--------------------------------------------------------------------------------
/lib/tasks/auto_annotate_models.rake:
--------------------------------------------------------------------------------
1 | # via. https://github.com/ctran/annotate_models/wiki
2 |
3 | if(Rails.env.development?)
4 | task :set_annotation_options do
5 | Annotate.set_defaults(
6 | exclude_tests: true,
7 | exclude_fixtures: true,
8 | exclude_factories: true,
9 | show_indexes: true,
10 | )
11 | end
12 |
13 | # Comes with the current master when running `rails g annotate:install`
14 | # But somehow won't annotate my models correctly (only one)
15 | # Thus commented out
16 | # Annotate.load_tasks
17 |
18 | # Annotate models
19 | task :annotate do
20 | puts 'Annotating models...'
21 | system 'bundle exec annotate'
22 | end
23 |
24 | # Run annotate task after db:migrate
25 | # and db:rollback tasks
26 | Rake::Task['db:migrate'].enhance do
27 | Rake::Task['annotate'].invoke
28 | end
29 |
30 | Rake::Task['db:rollback'].enhance do
31 | Rake::Task['annotate'].invoke
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/log/.keep
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
48 |
49 |
50 |
51 |
52 |
53 |
The page you were looking for doesn't exist.
54 |
You may have mistyped the address or the page may have moved.
55 |
56 | If you are the application owner check the logs for more information.
57 |
58 |
59 |
--------------------------------------------------------------------------------
/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
48 |
49 |
50 |
51 |
52 |
53 |
The change you wanted was rejected.
54 |
Maybe you tried to change something you didn't have access to.
55 |
56 | If you are the application owner check the logs for more information.
57 |
58 |
59 |
--------------------------------------------------------------------------------
/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
48 |
49 |
50 |
51 |
52 |
53 |
We're sorry, but something went wrong.
54 |
55 | If you are the application owner check the logs for more information.
56 |
57 |
58 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/public/favicon.ico
--------------------------------------------------------------------------------
/public/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/public/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/public/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/public/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/public/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/public/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/public/javascripts/jquery_ujs.js:
--------------------------------------------------------------------------------
1 | (function($, undefined) {
2 |
3 | /**
4 | * Unobtrusive scripting adapter for jQuery
5 | * https://github.com/rails/jquery-ujs
6 | *
7 | * Requires jQuery 1.7.0 or later.
8 | *
9 | * Released under the MIT license
10 | *
11 | */
12 |
13 | // Cut down on the number of issues from people inadvertently including jquery_ujs twice
14 | // by detecting and raising an error when it happens.
15 | if ( $.rails !== undefined ) {
16 | $.error('jquery-ujs has already been loaded!');
17 | }
18 |
19 | // Shorthand to make it a little easier to call public rails functions from within rails.js
20 | var rails;
21 | var $document = $(document);
22 |
23 | $.rails = rails = {
24 | // Link elements bound by jquery-ujs
25 | linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with]',
26 |
27 | // Button elements boud jquery-ujs
28 | buttonClickSelector: 'button[data-remote]',
29 |
30 | // Select elements bound by jquery-ujs
31 | inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]',
32 |
33 | // Form elements bound by jquery-ujs
34 | formSubmitSelector: 'form',
35 |
36 | // Form input elements bound by jquery-ujs
37 | formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])',
38 |
39 | // Form input elements disabled during form submission
40 | disableSelector: 'input[data-disable-with], button[data-disable-with], textarea[data-disable-with]',
41 |
42 | // Form input elements re-enabled after form submission
43 | enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled',
44 |
45 | // Form required input elements
46 | requiredInputSelector: 'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])',
47 |
48 | // Form file input elements
49 | fileInputSelector: 'input[type=file]',
50 |
51 | // Link onClick disable selector with possible reenable after remote submission
52 | linkDisableSelector: 'a[data-disable-with]',
53 |
54 | // Make sure that every Ajax request sends the CSRF token
55 | CSRFProtection: function(xhr) {
56 | var token = $('meta[name="csrf-token"]').attr('content');
57 | if (token) xhr.setRequestHeader('X-CSRF-Token', token);
58 | },
59 |
60 | // Triggers an event on an element and returns false if the event result is false
61 | fire: function(obj, name, data) {
62 | var event = $.Event(name);
63 | obj.trigger(event, data);
64 | return event.result !== false;
65 | },
66 |
67 | // Default confirm dialog, may be overridden with custom confirm dialog in $.rails.confirm
68 | confirm: function(message) {
69 | return confirm(message);
70 | },
71 |
72 | // Default ajax function, may be overridden with custom function in $.rails.ajax
73 | ajax: function(options) {
74 | return $.ajax(options);
75 | },
76 |
77 | // Default way to get an element's href. May be overridden at $.rails.href.
78 | href: function(element) {
79 | return element.attr('href');
80 | },
81 |
82 | // Submits "remote" forms and links with ajax
83 | handleRemote: function(element) {
84 | var method, url, data, elCrossDomain, crossDomain, withCredentials, dataType, options;
85 |
86 | if (rails.fire(element, 'ajax:before')) {
87 | elCrossDomain = element.data('cross-domain');
88 | crossDomain = elCrossDomain === undefined ? null : elCrossDomain;
89 | withCredentials = element.data('with-credentials') || null;
90 | dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType);
91 |
92 | if (element.is('form')) {
93 | method = element.attr('method');
94 | url = element.attr('action');
95 | data = element.serializeArray();
96 | // memoized value from clicked submit button
97 | var button = element.data('ujs:submit-button');
98 | if (button) {
99 | data.push(button);
100 | element.data('ujs:submit-button', null);
101 | }
102 | } else if (element.is(rails.inputChangeSelector)) {
103 | method = element.data('method');
104 | url = element.data('url');
105 | data = element.serialize();
106 | if (element.data('params')) data = data + "&" + element.data('params');
107 | } else if (element.is(rails.buttonClickSelector)) {
108 | method = element.data('method') || 'get';
109 | url = element.data('url');
110 | data = element.serialize();
111 | if (element.data('params')) data = data + "&" + element.data('params');
112 | } else {
113 | method = element.data('method');
114 | url = rails.href(element);
115 | data = element.data('params') || null;
116 | }
117 |
118 | options = {
119 | type: method || 'GET', data: data, dataType: dataType,
120 | // stopping the "ajax:beforeSend" event will cancel the ajax request
121 | beforeSend: function(xhr, settings) {
122 | if (settings.dataType === undefined) {
123 | xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script);
124 | }
125 | return rails.fire(element, 'ajax:beforeSend', [xhr, settings]);
126 | },
127 | success: function(data, status, xhr) {
128 | element.trigger('ajax:success', [data, status, xhr]);
129 | },
130 | complete: function(xhr, status) {
131 | element.trigger('ajax:complete', [xhr, status]);
132 | },
133 | error: function(xhr, status, error) {
134 | element.trigger('ajax:error', [xhr, status, error]);
135 | },
136 | crossDomain: crossDomain
137 | };
138 |
139 | // There is no withCredentials for IE6-8 when
140 | // "Enable native XMLHTTP support" is disabled
141 | if (withCredentials) {
142 | options.xhrFields = {
143 | withCredentials: withCredentials
144 | };
145 | }
146 |
147 | // Only pass url to `ajax` options if not blank
148 | if (url) { options.url = url; }
149 |
150 | var jqxhr = rails.ajax(options);
151 | element.trigger('ajax:send', jqxhr);
152 | return jqxhr;
153 | } else {
154 | return false;
155 | }
156 | },
157 |
158 | // Handles "data-method" on links such as:
159 | // Delete
160 | handleMethod: function(link) {
161 | var href = rails.href(link),
162 | method = link.data('method'),
163 | target = link.attr('target'),
164 | csrf_token = $('meta[name=csrf-token]').attr('content'),
165 | csrf_param = $('meta[name=csrf-param]').attr('content'),
166 | form = $(''),
167 | metadata_input = '';
168 |
169 | if (csrf_param !== undefined && csrf_token !== undefined) {
170 | metadata_input += '';
171 | }
172 |
173 | if (target) { form.attr('target', target); }
174 |
175 | form.hide().append(metadata_input).appendTo('body');
176 | form.submit();
177 | },
178 |
179 | /* Disables form elements:
180 | - Caches element value in 'ujs:enable-with' data store
181 | - Replaces element text with value of 'data-disable-with' attribute
182 | - Sets disabled property to true
183 | */
184 | disableFormElements: function(form) {
185 | form.find(rails.disableSelector).each(function() {
186 | var element = $(this), method = element.is('button') ? 'html' : 'val';
187 | element.data('ujs:enable-with', element[method]());
188 | element[method](element.data('disable-with'));
189 | element.prop('disabled', true);
190 | });
191 | },
192 |
193 | /* Re-enables disabled form elements:
194 | - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
195 | - Sets disabled property to false
196 | */
197 | enableFormElements: function(form) {
198 | form.find(rails.enableSelector).each(function() {
199 | var element = $(this), method = element.is('button') ? 'html' : 'val';
200 | if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with'));
201 | element.prop('disabled', false);
202 | });
203 | },
204 |
205 | /* For 'data-confirm' attribute:
206 | - Fires `confirm` event
207 | - Shows the confirmation dialog
208 | - Fires the `confirm:complete` event
209 |
210 | Returns `true` if no function stops the chain and user chose yes; `false` otherwise.
211 | Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog.
212 | Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function
213 | return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog.
214 | */
215 | allowAction: function(element) {
216 | var message = element.data('confirm'),
217 | answer = false, callback;
218 | if (!message) { return true; }
219 |
220 | if (rails.fire(element, 'confirm')) {
221 | answer = rails.confirm(message);
222 | callback = rails.fire(element, 'confirm:complete', [answer]);
223 | }
224 | return answer && callback;
225 | },
226 |
227 | // Helper function which checks for blank inputs in a form that match the specified CSS selector
228 | blankInputs: function(form, specifiedSelector, nonBlank) {
229 | var inputs = $(), input, valueToCheck,
230 | selector = specifiedSelector || 'input,textarea',
231 | allInputs = form.find(selector);
232 |
233 | allInputs.each(function() {
234 | input = $(this);
235 | valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : input.val();
236 | // If nonBlank and valueToCheck are both truthy, or nonBlank and valueToCheck are both falsey
237 | if (!valueToCheck === !nonBlank) {
238 |
239 | // Don't count unchecked required radio if other radio with same name is checked
240 | if (input.is('input[type=radio]') && allInputs.filter('input[type=radio]:checked[name="' + input.attr('name') + '"]').length) {
241 | return true; // Skip to next input
242 | }
243 |
244 | inputs = inputs.add(input);
245 | }
246 | });
247 | return inputs.length ? inputs : false;
248 | },
249 |
250 | // Helper function which checks for non-blank inputs in a form that match the specified CSS selector
251 | nonBlankInputs: function(form, specifiedSelector) {
252 | return rails.blankInputs(form, specifiedSelector, true); // true specifies nonBlank
253 | },
254 |
255 | // Helper function, needed to provide consistent behavior in IE
256 | stopEverything: function(e) {
257 | $(e.target).trigger('ujs:everythingStopped');
258 | e.stopImmediatePropagation();
259 | return false;
260 | },
261 |
262 | // replace element's html with the 'data-disable-with' after storing original html
263 | // and prevent clicking on it
264 | disableElement: function(element) {
265 | element.data('ujs:enable-with', element.html()); // store enabled state
266 | element.html(element.data('disable-with')); // set to disabled state
267 | element.bind('click.railsDisable', function(e) { // prevent further clicking
268 | return rails.stopEverything(e);
269 | });
270 | },
271 |
272 | // restore element to its original state which was disabled by 'disableElement' above
273 | enableElement: function(element) {
274 | if (element.data('ujs:enable-with') !== undefined) {
275 | element.html(element.data('ujs:enable-with')); // set to old enabled state
276 | element.removeData('ujs:enable-with'); // clean up cache
277 | }
278 | element.unbind('click.railsDisable'); // enable element
279 | }
280 |
281 | };
282 |
283 | if (rails.fire($document, 'rails:attachBindings')) {
284 |
285 | $.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }});
286 |
287 | $document.delegate(rails.linkDisableSelector, 'ajax:complete', function() {
288 | rails.enableElement($(this));
289 | });
290 |
291 | $document.delegate(rails.linkClickSelector, 'click.rails', function(e) {
292 | var link = $(this), method = link.data('method'), data = link.data('params');
293 | if (!rails.allowAction(link)) return rails.stopEverything(e);
294 |
295 | if (link.is(rails.linkDisableSelector)) rails.disableElement(link);
296 |
297 | if (link.data('remote') !== undefined) {
298 | if ( (e.metaKey || e.ctrlKey) && (!method || method === 'GET') && !data ) { return true; }
299 |
300 | var handleRemote = rails.handleRemote(link);
301 | // response from rails.handleRemote() will either be false or a deferred object promise.
302 | if (handleRemote === false) {
303 | rails.enableElement(link);
304 | } else {
305 | handleRemote.error( function() { rails.enableElement(link); } );
306 | }
307 | return false;
308 |
309 | } else if (link.data('method')) {
310 | rails.handleMethod(link);
311 | return false;
312 | }
313 | });
314 |
315 | $document.delegate(rails.buttonClickSelector, 'click.rails', function(e) {
316 | var button = $(this);
317 | if (!rails.allowAction(button)) return rails.stopEverything(e);
318 |
319 | rails.handleRemote(button);
320 | return false;
321 | });
322 |
323 | $document.delegate(rails.inputChangeSelector, 'change.rails', function(e) {
324 | var link = $(this);
325 | if (!rails.allowAction(link)) return rails.stopEverything(e);
326 |
327 | rails.handleRemote(link);
328 | return false;
329 | });
330 |
331 | $document.delegate(rails.formSubmitSelector, 'submit.rails', function(e) {
332 | var form = $(this),
333 | remote = form.data('remote') !== undefined,
334 | blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector),
335 | nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector);
336 |
337 | if (!rails.allowAction(form)) return rails.stopEverything(e);
338 |
339 | // skip other logic when required values are missing or file upload is present
340 | if (blankRequiredInputs && form.attr("novalidate") == undefined && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) {
341 | return rails.stopEverything(e);
342 | }
343 |
344 | if (remote) {
345 | if (nonBlankFileInputs) {
346 | // slight timeout so that the submit button gets properly serialized
347 | // (make it easy for event handler to serialize form without disabled values)
348 | setTimeout(function(){ rails.disableFormElements(form); }, 13);
349 | var aborted = rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]);
350 |
351 | // re-enable form elements if event bindings return false (canceling normal form submission)
352 | if (!aborted) { setTimeout(function(){ rails.enableFormElements(form); }, 13); }
353 |
354 | return aborted;
355 | }
356 |
357 | rails.handleRemote(form);
358 | return false;
359 |
360 | } else {
361 | // slight timeout so that the submit button gets properly serialized
362 | setTimeout(function(){ rails.disableFormElements(form); }, 13);
363 | }
364 | });
365 |
366 | $document.delegate(rails.formInputClickSelector, 'click.rails', function(event) {
367 | var button = $(this);
368 |
369 | if (!rails.allowAction(button)) return rails.stopEverything(event);
370 |
371 | // register the pressed submit button
372 | var name = button.attr('name'),
373 | data = name ? {name:name, value:button.val()} : null;
374 |
375 | button.closest('form').data('ujs:submit-button', data);
376 | });
377 |
378 | $document.delegate(rails.formSubmitSelector, 'ajax:beforeSend.rails', function(event) {
379 | if (this == event.target) rails.disableFormElements($(this));
380 | });
381 |
382 | $document.delegate(rails.formSubmitSelector, 'ajax:complete.rails', function(event) {
383 | if (this == event.target) rails.enableFormElements($(this));
384 | });
385 |
386 | $(function(){
387 | // making sure that all forms have actual up-to-date token(cached forms contain old one)
388 | var csrf_token = $('meta[name=csrf-token]').attr('content');
389 | var csrf_param = $('meta[name=csrf-param]').attr('content');
390 | $('form input[name="' + csrf_param + '"]').val(csrf_token);
391 | });
392 | }
393 |
394 | })( jQuery );
395 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
2 | #
3 | # To ban all spiders from the entire site uncomment the next two lines:
4 | # User-agent: *
5 | # Disallow: /
6 |
--------------------------------------------------------------------------------
/script/build_for_jenkins.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | readonly JENKINS_RAILS_ENV="test"
4 | export LANG=ja_JP.UTF-8
5 |
6 | # Jenkins build script
7 |
8 | run()
9 | {
10 | command=$1
11 | echo "$command"
12 |
13 | eval $command
14 |
15 | # if error code returned, exit this script with error code
16 | RET=$?
17 | if [ $RET -ne 0 ]; then
18 | exit $RET
19 | fi
20 | }
21 |
22 | run "which ruby"
23 | run "ruby --version"
24 | run "cp config/database.yml.jenkins config/database.yml"
25 | run "gem install bundler --no-ri --no-rdoc"
26 |
27 |
28 | #########################
29 | echo "bundle install --path vendor/bundle"
30 |
31 | bundle install --path vendor/bundle
32 |
33 | RET=$?
34 | if [ $RET -ne 0 ]; then
35 | # if failed 'bundle install', run 'bundle update'
36 | run "bundle update"
37 | fi
38 |
39 | run "RAILS_ENV=${JENKINS_RAILS_ENV} bundle exec rake db:create"
40 | run "RAILS_ENV=${JENKINS_RAILS_ENV} bundle exec rake db:migrate"
41 |
42 | run "rm -rf reports"
43 | run "mkdir -m 777 reports/"
44 |
45 | run "RAILS_ENV=${JENKINS_RAILS_ENV} bundle exec rspec --profile > ./reports/rspec-console.log"
46 | run "ruby ./script/plot-rspec-slowest-examples.rb ./reports/rspec-console.log > ./reports/rspec-plot.csv"
47 |
48 | # * if you use mysql_partitioning, use don't use `rake spec`. because primary key is dropped
49 | # * `rake spec` can not use --profile
50 | #run "RAILS_ENV=${JENKINS_RAILS_ENV} bundle exec rake ci:setup:rspec spec"
51 |
52 |
53 | exit 0
54 |
--------------------------------------------------------------------------------
/script/plot-rspec-slowest-examples.rb:
--------------------------------------------------------------------------------
1 | # extract example seconds from rspec profile log
2 | #
3 | # required:
4 | # rspec >= 2.10.0
5 | # Jenkins Plot Plugin
6 | #
7 | # usage:
8 | # rspec --profile 5 > rspec-console.log # rspec >= 2.13.0
9 | # rspec --profile > rspec-console.log # 2.10.0 <= rspec < 2.13.0
10 | # ruby plot-rspec-slowest-examples.rb rspec-console.log > plot.csv
11 | #
12 | # and more:
13 | # https://gist.github.com/sue445/5140150
14 |
15 | if ARGV.empty?
16 | puts "usage: ruby plot-rspec-slowest-examples.rb "
17 | exit
18 | end
19 |
20 | LOG_FILE = ARGV[0]
21 |
22 | total = {}
23 | example_seconds = []
24 | open(LOG_FILE) do |f|
25 | f.each_line do |line|
26 | case line
27 | when %r{Top (.+) slowest examples \(([0-9.]+) seconds, ([0-9.]+)% of total time\)}
28 | total[:ratio] = $3
29 | when %r{Finished in ([0-9.]+) seconds}
30 | total[:second] = $1
31 | when %r{([0-9.]+) seconds (.+)}
32 | example_seconds << $1
33 | end
34 | end
35 | end
36 |
37 | # header
38 | print "total,"
39 | #print "ratio %,"
40 | example_seconds.count.times do |n|
41 | print "top #{n+1},"
42 | end
43 | print "\n"
44 |
45 | # data row
46 | print "#{total[:second]},"
47 | #print "#{total[:ratio]},"
48 | example_seconds.each do |sec|
49 | print "#{sec},"
50 | end
51 | print "\n"
52 |
53 |
--------------------------------------------------------------------------------
/shots/gitpeach.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/shots/gitpeach.gif
--------------------------------------------------------------------------------
/shots/issue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/shots/issue.png
--------------------------------------------------------------------------------
/shots/pusher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/shots/pusher.png
--------------------------------------------------------------------------------
/spec/controllers/issues_controller_spec.rb:
--------------------------------------------------------------------------------
1 | describe IssuesController, type: :controller do
2 | let(:kanban) { FactoryGirl.create(:kanban) }
3 |
4 | describe "POST create" do
5 | subject{ post :create, params }
6 |
7 | let(:params){
8 | {
9 | kanban_id: kanban.name,
10 | title: "some title",
11 | }
12 | }
13 |
14 | before do
15 | allow(controller).to receive(:create_gitlab_issue){}
16 | end
17 |
18 | it "should be success" do
19 | subject
20 | expect(response).to be_success
21 | end
22 |
23 | it "should be call create_gitlab_issue with valid params" do
24 | expect(controller).to receive(:create_gitlab_issue).with(params[:title])
25 | subject
26 | end
27 | end
28 |
29 | describe "GET show" do
30 | subject{ get :show, params }
31 |
32 | let(:params){
33 | {
34 | id: issue_id,
35 | kanban_id: kanban.name,
36 | }
37 | }
38 |
39 | let(:issue_id){ 1 }
40 |
41 | it "should be success" do
42 | subject
43 | expect(response).to be_success
44 | end
45 | end
46 |
47 | describe "PUT update" do
48 | subject{ put :update, params }
49 |
50 | let(:params){
51 | {
52 | id: issue_id,
53 | kanban_id: kanban.name,
54 | to_label_id: to_label.id,
55 | }
56 | }
57 |
58 | let(:issue_id){ 1 }
59 |
60 | let(:from_label){ kanban.labels.backlog.first! }
61 | let(:to_label) { kanban.labels.done.first! }
62 |
63 | before do
64 | expect(controller).to receive(:gitlab_current_issue_label_id){ from_label.id }
65 | expect(controller).to receive(:gitlab_issue_labels){ %w(bug high) }
66 | expect(controller).to receive(:update_gitlab_issue)
67 | expect(controller).to receive(:updated_issue) { {} }
68 | end
69 |
70 | it "should be success" do
71 | subject
72 | expect(response).to be_success
73 | end
74 |
75 | it "should update labels" do
76 | subject
77 | expect(assigns(:labels)).to eq %w(bug high)
78 | end
79 |
80 | it "should update state" do
81 | subject
82 | expect(assigns(:state)).to eq "close"
83 | end
84 | end
85 | end
86 |
--------------------------------------------------------------------------------
/spec/controllers/kanbans_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # This spec was generated by rspec-rails when you ran the scaffold generator.
2 | # It demonstrates how one might use RSpec to specify the controller code that
3 | # was generated by Rails when you ran the scaffold generator.
4 | #
5 | # It assumes that the implementation code is generated by the rails scaffold
6 | # generator. If you are using any extension libraries to generate different
7 | # controller code, this generated spec may or may not pass.
8 | #
9 | # It only uses APIs available in rails and/or rspec-rails. There are a number
10 | # of tools you can use to make these specs even more expressive, but we're
11 | # sticking to rails and rspec-rails APIs to keep things simple and stable.
12 | #
13 | # Compared to earlier versions of this generator, there is very limited use of
14 | # stubs and message expectations in this spec. Stubs are only used when there
15 | # is no simpler way to get a handle on the object needed for the example.
16 | # Message expectations are only used when there is no simpler way to specify
17 | # that an instance is receiving a specific message.
18 |
19 | describe KanbansController, type: :controller do
20 |
21 | # This should return the minimal set of attributes required to create a valid
22 | # Kanban. As you add validations to Kanban, be sure to
23 | # adjust the attributes here as well.
24 | let(:valid_attributes) { { "gitlab_project_id" => "1", "name" => "namespace/path" } }
25 |
26 | # This should return the minimal set of values that should be in the session
27 | # in order to pass any filters (e.g. authentication) defined in
28 | # KanbansController. Be sure to keep this updated too.
29 | let(:valid_session) { {} }
30 |
31 | describe "GET show" do
32 | before do
33 | allow(controller).to receive(:project_issues).and_return([])
34 | end
35 |
36 | it "assigns the requested kanban as @kanban" do
37 | kanban = Kanban.create! valid_attributes
38 | get :show, {:id => kanban.to_param}, valid_session
39 | expect(assigns(:kanban)).to eq(kanban)
40 | end
41 | end
42 |
43 | describe "GET edit" do
44 | it "assigns the requested kanban as @kanban" do
45 | kanban = Kanban.create! valid_attributes
46 | get :edit, {:id => kanban.to_param}, valid_session
47 | expect(assigns(:kanban)).to eq(kanban)
48 | end
49 | end
50 |
51 | describe "POST create" do
52 | it "creates a new Kanban" do
53 | expect {
54 | post :create, {:kanban => valid_attributes}, valid_session
55 | }.to change(Kanban, :count).by(1)
56 | end
57 |
58 | it "assigns a newly created kanban as @kanban" do
59 | post :create, {:kanban => valid_attributes}, valid_session
60 | expect(assigns(:kanban)).to be_a(Kanban)
61 | expect(assigns(:kanban)).to be_persisted
62 | end
63 |
64 | it "redirects to the created kanban" do
65 | post :create, {:kanban => valid_attributes}, valid_session
66 | expect(response).to redirect_to(Kanban.last)
67 | end
68 | end
69 |
70 | describe "PUT update" do
71 | subject{ put :update, params }
72 |
73 | let(:params) do
74 | {
75 | id: kanban.name,
76 | labels: labels,
77 | }
78 | end
79 |
80 | let(:labels) do
81 | [
82 | {name: "Backlog" , gitlab_label: nil , is_backlog_issue: true , is_close_issue: false, id: backlog.id},
83 | {name: "Ready" , gitlab_label: "ready" , is_backlog_issue: false, is_close_issue: false, id: ready.id},
84 | {name: "In Progress", gitlab_label: "in progress", is_backlog_issue: false, is_close_issue: false, id: in_progress.id},
85 | {name: "Done" , gitlab_label: nil , is_backlog_issue: false, is_close_issue: true , id: done.id},
86 | ]
87 | end
88 |
89 | let!(:kanban) { FactoryGirl.create(:kanban) }
90 | let(:backlog) { kanban.labels.backlog.first }
91 | let(:ready) { kanban.labels.other.where(name: "Ready").first }
92 | let(:in_progress){ kanban.labels.other.where(name: "In Progress").first }
93 | let(:done) { kanban.labels.done.first }
94 |
95 | it "should be redirect" do
96 | subject
97 | expect(response).to be_redirect
98 | end
99 |
100 | context "When update label" do
101 | before do
102 | labels[0][:name] = "TODO"
103 | labels[1][:gitlab_label] = "ready5"
104 | end
105 |
106 | it{ expect{ subject }.to change{ Label.find(backlog.id).name }.from("Backlog").to("TODO") }
107 | it{ expect{ subject }.to change{ Label.find(ready.id).gitlab_label }.from("ready").to("ready5") }
108 | end
109 |
110 | context "When add new label" do
111 | before do
112 | labels.insert(1, new_label)
113 | end
114 |
115 | let(:new_label) do
116 | {name: "Pending", gitlab_label: "pending", is_backlog_issue: false, is_close_issue: false}
117 | end
118 |
119 | it{ expect{ subject }.to change(Label, :count).by(1) }
120 | it{ expect{ subject }.to change{ Label.find(done.id).disp_order }.from(3).to(4) }
121 | end
122 |
123 | context "When delete label" do
124 | before do
125 | labels.delete_at(2)
126 | end
127 |
128 | it{ expect{ subject }.to change(Label, :count).by(-1) }
129 | it{ expect{ subject }.to change{ Label.where(id: in_progress.id).exists? }.from(true).to(false) }
130 | end
131 |
132 | context "When swap labels" do
133 | before do
134 | labels[0], labels[1] = labels[1], labels[0]
135 | end
136 |
137 | it{ expect{ subject }.to change{ Label.find(backlog.id).disp_order }.from(0).to(1) }
138 | it{ expect{ subject }.to change{ Label.find(ready.id).disp_order }.from(1).to(0) }
139 | end
140 | end
141 |
142 | describe "DELETE destroy" do
143 | it "destroys the requested kanban" do
144 | kanban = Kanban.create! valid_attributes
145 | expect {
146 | delete :destroy, {:id => kanban.to_param}, valid_session
147 | }.to change(Kanban, :count).by(-1)
148 | end
149 |
150 | it "redirects to the top" do
151 | kanban = Kanban.create! valid_attributes
152 | delete :destroy, {:id => kanban.to_param}, valid_session
153 | expect(response).to redirect_to(root_url)
154 | end
155 | end
156 |
157 | end
158 |
--------------------------------------------------------------------------------
/spec/controllers/sessions_controller_spec.rb:
--------------------------------------------------------------------------------
1 | describe SessionsController, type: :controller do
2 | describe "GET 'create'" do
3 | include_context :using_gitlab_mock
4 |
5 | subject{ post 'create', valid_params }
6 |
7 | before do
8 | json = < "email=#{login}&password=#{password}",
13 | :headers => {'Accept'=>'application/json'}).
14 | to_return(:status => 200, :body => json, :headers => {})
15 | end
16 |
17 | let(:login) { "momozono_love" }
18 | let(:password){ "cure_peach" }
19 | let(:valid_params) {
20 | {
21 | login: login,
22 | password: password,
23 | }
24 | }
25 |
26 | describe "action behavior" do
27 | before do
28 | subject
29 | end
30 |
31 | it "returns http redirect" do
32 | expect(response).to be_redirect
33 | end
34 |
35 | it{ expect(response).to redirect_to root_path }
36 |
37 | it{ expect(session[:user_id]).not_to be_nil }
38 | end
39 |
40 | context "When not exists user" do
41 | before do
42 | subject
43 | end
44 |
45 | it{ expect(User.count).to eq 1 }
46 |
47 | let(:user){ User.first }
48 | it_behaves_like :a_user
49 | end
50 |
51 | context "When exists user" do
52 | before do
53 | @user = FactoryGirl.create(:momozono_love)
54 | subject
55 | end
56 |
57 | it{ expect(User.count).to eq 1 }
58 |
59 | let(:user){ @user }
60 | it_behaves_like :a_user
61 | end
62 | end
63 |
64 | describe "GET 'destroy'" do
65 | subject{ get 'destroy' }
66 |
67 | before do
68 | subject
69 | end
70 |
71 | it "returns http success" do
72 | expect(response).to be_redirect
73 | end
74 |
75 | it{ expect(response).to redirect_to root_path }
76 |
77 | it{ expect(session[:user_id]).to be_nil }
78 | end
79 |
80 | end
81 |
--------------------------------------------------------------------------------
/spec/controllers/top_controller_spec.rb:
--------------------------------------------------------------------------------
1 | describe TopController, type: :controller do
2 |
3 | describe "GET 'index'" do
4 | it "returns http success" do
5 | get 'index'
6 | expect(response).to be_success
7 | end
8 | end
9 |
10 | end
11 |
--------------------------------------------------------------------------------
/spec/factories/kanbans.rb:
--------------------------------------------------------------------------------
1 | # Read about factories at https://github.com/thoughtbot/factory_girl
2 |
3 | FactoryGirl.define do
4 | factory :kanban do
5 | gitlab_project_id 1
6 | name { generate :random_path }
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/spec/factories/labels.rb:
--------------------------------------------------------------------------------
1 | # Read about factories at https://github.com/thoughtbot/factory_girl
2 |
3 | FactoryGirl.define do
4 | factory :label do
5 | kanban_id 1
6 | name "MyString"
7 | gitlab_label "MyString"
8 | is_backlog_issue false
9 | is_close_issue false
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/spec/factories/sequences.rb:
--------------------------------------------------------------------------------
1 | FactoryGirl.define do
2 | def rand_str
3 | # via. http://d.hatena.ne.jp/JunMitani/20080214
4 | a = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
5 | Array.new(10){a[rand(a.size)]}.join
6 | end
7 |
8 | sequence(:random_int) { rand(1 .. 100000) }
9 | sequence(:random_flag){ rand(10) < 5 }
10 | sequence(:random_str) { rand_str }
11 | sequence(:random_url) { "http://"+ rand_str }
12 | sequence(:random_mail){ "#{rand_str}@#{rand_str}.com" }
13 |
14 | sequence(:random_path) { rand_str + "/" + rand_str }
15 |
16 | sequence(:random_time){
17 | diff_day = 150 - rand(365)
18 | Time.now + diff_day
19 | }
20 | end
21 |
22 |
--------------------------------------------------------------------------------
/spec/factories/users.rb:
--------------------------------------------------------------------------------
1 | # Read about factories at https://github.com/thoughtbot/factory_girl
2 |
3 | FactoryGirl.define do
4 | factory :user do
5 | gitlab_user_id { generate :random_int }
6 | username { generate :random_str }
7 | email { generate :random_mail }
8 | private_token { generate :random_str }
9 |
10 | factory :momozono_love do
11 | gitlab_user_id 1
12 | username "momozono_love"
13 | email "cure_peach@fresh.precure.jp"
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/helpers/application_helper_spec.rb:
--------------------------------------------------------------------------------
1 | describe ApplicationHelper, type: :helper do
2 |
3 | # via.
4 | # * https://github.com/rails/rails/blob/v4.1.2/actionpack/CHANGELOG.md
5 | # * https://github.com/rails/rails/commit/8a067640e6fe222022dc77bb63d5da37ef75a189
6 | describe "Upgrade to Rails 4.1.4" do
7 | let(:slug){ "namespace/repository" }
8 |
9 | describe "url helper" do
10 | it "should not be escaped" do
11 | expect(kanban_path(slug)).to eq "/#{slug}"
12 | end
13 | end
14 |
15 | describe "#url_for" do
16 | it "should not be escaped" do
17 | expect(url_for(controller: :kanbans, action: :edit, id: slug)).to eq "/#{slug}/edit"
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/models/kanban_spec.rb:
--------------------------------------------------------------------------------
1 | describe Kanban do
2 | describe "#create_default_labels" do
3 | subject(:create_default_labels){ kanban.save! }
4 |
5 | let(:kanban){ FactoryGirl.build(:kanban) }
6 |
7 | it{ expect{ subject }.to change(kanban.labels, :count).by(4) }
8 |
9 | describe "1st label" do
10 | subject do
11 | create_default_labels
12 | kanban.labels[0]
13 | end
14 |
15 | its(:name) { should == "Backlog" }
16 | its(:gitlab_label) { should be nil }
17 | its(:disp_order) { should == 0 }
18 | its(:is_backlog_issue){ should be true }
19 | its(:is_close_issue) { should be false }
20 | end
21 |
22 | describe "2nd label" do
23 | subject do
24 | create_default_labels
25 | kanban.labels[1]
26 | end
27 |
28 | its(:name) { should == "Ready" }
29 | its(:gitlab_label) { should == "ready" }
30 | its(:disp_order) { should == 1 }
31 | its(:is_backlog_issue){ should be false }
32 | its(:is_close_issue) { should be false }
33 | end
34 |
35 | describe "3rd label" do
36 | subject do
37 | create_default_labels
38 | kanban.labels[2]
39 | end
40 |
41 | its(:name) { should == "In Progress" }
42 | its(:gitlab_label) { should == "in progress" }
43 | its(:disp_order) { should == 2 }
44 | its(:is_backlog_issue){ should be false }
45 | its(:is_close_issue) { should be false }
46 | end
47 |
48 | describe "4th label" do
49 | subject do
50 | create_default_labels
51 | kanban.labels[3]
52 | end
53 |
54 | its(:name) { should == "Done" }
55 | its(:gitlab_label) { should be nil }
56 | its(:disp_order) { should == 3 }
57 | its(:is_backlog_issue){ should be false }
58 | its(:is_close_issue) { should be true }
59 | end
60 | end
61 |
62 | describe "#issues_group_by_label" do
63 | before do
64 | @grouped_issue = kanban.issues_group_by_label(issues)
65 | end
66 |
67 | let(:kanban){ FactoryGirl.create(:kanban) }
68 | let(:issues){
69 | [
70 | # backlog
71 | Gitlab::ObjectifiedHash.new(id: 1 , state: "opened" , labels: nil),
72 | Gitlab::ObjectifiedHash.new(id: 2 , state: "reopened", labels: ["Bug", "Hotfix"]),
73 |
74 | # ready
75 | Gitlab::ObjectifiedHash.new(id: 3 , state: "opened" , labels: ["ready"]),
76 |
77 | # in progress
78 | Gitlab::ObjectifiedHash.new(id: 4 , state: "opened" , labels: ["in progress"]),
79 | Gitlab::ObjectifiedHash.new(id: 5 , state: "reopened", labels: ["in progress"]),
80 | Gitlab::ObjectifiedHash.new(id: 6 , state: "opened" , labels: ["Bug", "in progress"]),
81 |
82 | # done
83 | Gitlab::ObjectifiedHash.new(id: 7 , state: "closed" , labels: nil),
84 | Gitlab::ObjectifiedHash.new(id: 8 , state: "closed" , labels: ["ready"]),
85 | Gitlab::ObjectifiedHash.new(id: 9 , state: "closed" , labels: ["in progress"]),
86 | Gitlab::ObjectifiedHash.new(id: 10, state: "closed" , labels: ["Bug", "Hotfix"]),
87 | ]
88 | }
89 | let(:backlog_id) { kanban.labels.backlog.first.id }
90 | let(:ready_id) { kanban.labels.other.find_by(name: "Ready").id }
91 | let(:in_progress_id) { kanban.labels.other.find_by(name: "In Progress").id }
92 | let(:done_id) { kanban.labels.done.first.id }
93 |
94 | it{ expect(@grouped_issue.count).to eq kanban.labels.count }
95 |
96 | it "should return backlog issues" do
97 | expect(@grouped_issue[backlog_id]).to have(2).items
98 | end
99 |
100 | it "should return ready issues" do
101 | expect(@grouped_issue[ready_id]).to have(1).items
102 | end
103 |
104 | it "should return in progress issues" do
105 | expect(@grouped_issue[in_progress_id]).to have(3).items
106 | end
107 |
108 | it "should return done issues" do
109 | expect(@grouped_issue[done_id]).to have(4).items
110 | end
111 | end
112 |
113 | describe "#update_gitlab_issue_labels" do
114 | subject{ kanban.update_gitlab_issue_labels(gitlab_labels, @from_label.id, @to_label.id) }
115 |
116 | let(:kanban){ FactoryGirl.create(:kanban) }
117 |
118 | where(:gitlab_labels, :from_label_name, :to_label_name, :expected) do
119 | [
120 | # backlog -> backlog
121 | [ ["bug", "high"] , "Backlog", "Backlog" , ["bug", "high"] ],
122 | # backlog -> other
123 | [ ["bug", "high"] , "Backlog", "In Progress", ["bug", "in progress", "high"] ],
124 | # backlog -> done
125 | [ ["bug", "high"] , "Backlog", "Done" , ["bug", "high"] ],
126 | # other -> backlog
127 | [ ["bug", "ready", "high"] , "Ready", "Backlog" , ["bug", "high"] ],
128 | # other -> other
129 | [ ["bug", "ready", "high"] , "Ready", "Ready" , ["bug", "ready", "high"] ],
130 | # other -> other2
131 | [ ["bug", "ready", "high"] , "Ready", "In Progress" , ["bug", "in progress", "high"] ],
132 | # other -> done
133 | [ ["bug", "ready", "high"] , "Ready", "Done" , ["bug", "high"] ],
134 | # done -> backlog
135 | [ ["bug", "high"] , "Done", "Backlog" , ["bug", "high"] ],
136 | # done -> other
137 | [ ["bug", "high"] , "Done", "Ready" , ["bug", "ready", "high"] ],
138 | # done -> done
139 | [ ["bug", "high"] , "Done", "Done" , ["bug", "high"] ],
140 | ]
141 | end
142 |
143 | with_them do
144 | before do
145 | @from_label = kanban.labels.find_by!(name: from_label_name)
146 | @to_label = kanban.labels.find_by!(name: to_label_name)
147 | end
148 |
149 | it{ should match_array expected }
150 | end
151 |
152 | end
153 |
154 | describe "#gitlab_issue_state" do
155 | subject{ kanban.gitlab_issue_state(@from_label.id, @to_label.id) }
156 |
157 | let(:kanban){ FactoryGirl.create(:kanban) }
158 |
159 | where(:from_label_name, :to_label_name, :expected) do
160 | [
161 | # opened -> opened
162 | ["Ready", "In Progress", nil],
163 | # opened -> closed
164 | ["Ready", "Done" , "close"],
165 | # closed -> reopened
166 | ["Done" , "Ready" , "reopen"],
167 | # closed -> closed
168 | ["Done" , "Done" , nil],
169 | ]
170 | end
171 |
172 | with_them do
173 | before do
174 | @from_label = kanban.labels.find_by!(name: from_label_name)
175 | @to_label = kanban.labels.find_by!(name: to_label_name)
176 | end
177 |
178 | it{ should == expected }
179 | end
180 |
181 | end
182 | end
183 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'rspec-parameterized'
3 | require 'webmock/rspec'
4 |
5 | require 'coveralls'
6 | Coveralls.wear!
7 |
8 | # This file is copied to spec/ when you run 'rails generate rspec:install'
9 | ENV["RAILS_ENV"] ||= 'test'
10 | require File.expand_path("../../config/environment", __FILE__)
11 | require 'rspec/rails'
12 | require 'rspec'
13 |
14 | # Requires supporting ruby files with custom matchers and macros, etc, in
15 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
16 | # run as spec files by default. This means that files in spec/support that end
17 | # in _spec.rb will both be required and run as specs, causing the specs to be
18 | # run twice. It is recommended that you do not name files matching this glob to
19 | # end with _spec.rb. You can configure this pattern with with the --pattern
20 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
21 | Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
22 |
23 | # Checks for pending migrations before tests are run.
24 | # If you are not using ActiveRecord, you can remove this line.
25 | ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
26 |
27 | RSpec.configure do |config|
28 | # ## Mock Framework
29 | #
30 | # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
31 | #
32 | # config.mock_with :mocha
33 | # config.mock_with :flexmock
34 | # config.mock_with :rr
35 |
36 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
37 | config.fixture_path = "#{::Rails.root}/spec/fixtures"
38 |
39 | # If you're not using ActiveRecord, or you'd prefer not to run each of your
40 | # examples within a transaction, remove the following line or assign false
41 | # instead of true.
42 | config.use_transactional_fixtures = true
43 |
44 | # If true, the base class of anonymous controllers will be inferred
45 | # automatically. This will be the default behavior in future versions of
46 | # rspec-rails.
47 | config.infer_base_class_for_anonymous_controllers = false
48 |
49 | # Run specs in random order to surface order dependencies. If you find an
50 | # order dependency and want to debug it, you can fix the order by providing
51 | # the seed, which is printed after each run.
52 | # --seed 1234
53 | config.order = "random"
54 |
55 | # DatabaseRewinder
56 | config.before :suite do
57 | DatabaseRewinder.clean_all
58 | # or
59 | # DatabaseRewinder.clean_with :any_arg_that_would_be_actually_ignored_anyway
60 | end
61 |
62 | config.after :each do
63 | DatabaseRewinder.clean
64 | end
65 | end
66 |
67 |
--------------------------------------------------------------------------------
/spec/support/shared_contexts/using_gitlab_mock.rb:
--------------------------------------------------------------------------------
1 | shared_context :using_gitlab_mock do
2 | before do
3 | Gitlab.configure do |config|
4 | config.endpoint = gitlab_endpoint
5 | config.private_token = gitlab_private_token
6 | end
7 | end
8 |
9 | let(:gitlab_endpoint) { "https://example.net/api/v3" }
10 | let(:gitlab_private_token){ "gitlab_private_token" }
11 | end
12 |
--------------------------------------------------------------------------------
/spec/support/shared_examples/a_user.rb:
--------------------------------------------------------------------------------
1 | shared_examples_for :a_user do
2 | it{ expect(user.gitlab_user_id).not_to be_nil }
3 | it{ expect(user.username).not_to be_blank }
4 | it{ expect(user.private_token).not_to be_blank }
5 | it{ expect(user.email).not_to be_blank }
6 | end
7 |
--------------------------------------------------------------------------------
/tmp/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/tmp/.keep
--------------------------------------------------------------------------------
/vendor/assets/javascripts/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/vendor/assets/javascripts/.keep
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sue445/gitpeach/08096149c8d768c98159c9061cb324a4b2663be1/vendor/assets/stylesheets/.keep
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/1pxdeep/1pxdeep.less:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////
2 | /////////////////////////////////////////////////
3 | ////
4 | //// 1pxdeep v1.0
5 | ////
6 | //// Copyright 2013 Rex Riepe
7 | //// Licensed under the Apache License v2.0
8 | //// http://www.apache.org/licenses/LICENSE-2.0
9 | ////
10 | /////////////////////////////////////////////////
11 | /////////////////////////////////////////////////
12 |
13 | //// Bootstrap variable overrides
14 |
15 | @font-size-base: 16px;
16 |
17 | @border-radius-base: 6px;
18 | @border-radius-large: 8px;
19 | @border-radius-small: 4px;
20 |
21 | @list-group-bg: transparent;
22 |
23 | @table-border-color: transparent;
24 |
25 | @modal-backdrop-bg: @color1a;
26 |
27 | @input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 4);
28 |
29 | @btn-default-bg: @color1;
30 | @btn-default-border: @btn-default-bg;
31 | @btn-font-weight: normal;
32 |
33 | @brand-primary: hsl(hue(#428bca),@sat,@l-factor);
34 | @brand-info: hsl(hue(#5bc0de),@sat,@l-factor);
35 | @brand-success: hsl(hue(#5cb85c),(saturation(#5cb85c) + @sat)/2,(luma(#5cb85c) + @luma - 10)/2);
36 | @brand-warning: hsl(hue(#f0ad4e),(saturation(#f0ad4e) + @sat + 16)/2,(luma(#f0ad4e) + @luma - 10)/2);
37 | @brand-danger: hsl(hue(#d9534f),(saturation(#d9534f) + @sat)/2,(luma(#d9534f) + @luma + 10)/2);
38 |
39 | @navbar-link-color: white;
40 | @navbar-link-hover-color: white;
41 | @navbar-link-hover-bg: transparent;
42 | @navbar-link-active-color: white;
43 | @navbar-link-active-bg: @1pxdeep-bg;
44 | @navbar-link-disabled-color: #ccc;
45 | @navbar-link-disabled-bg: transparent;
46 |
47 | //// SchemeLESS variable overrides
48 |
49 | @luma-upper-break:72;
50 |
51 | @contrast:@luma/50;
52 |
53 | //// Theme variables
54 |
55 | @1pxdeep-default-button-color: @color1b;
56 | @1pxdeep-bg: @color1;
57 | @body-bg: @1pxdeep-bg;
58 |
59 |
60 | //// Mix-ins
61 |
62 | .button-text (@color) when (luma(@color1) >= @luma-upper-break) {
63 | color:average(darken(@color,30%),#222);
64 | }
65 |
66 | .button-text (@color) when (luma(@color1) < @luma-upper-break) {
67 | color:#ffffff;
68 | }
69 |
70 | .contrast (@color) when (luma(@color) >= @luma-upper-break) {
71 | color:mix(darken(@color,18%),#111,50%);
72 | }
73 |
74 | .contrast (@color) when (luma(@color) < @luma-upper-break) {
75 | color:hsl(hue(@color),@sat,97%);
76 | }
77 |
78 | .contrast-dim (@color) when (luma(@color) >= @luma-upper-break) {
79 | color:mix(darken(@color,5%),#111,65%);
80 | }
81 |
82 | .contrast-dim (@color) when (luma(@color) < @luma-upper-break) {
83 | color:mix(@color,#ffffff,25%);
84 | }
85 |
86 | .contrast-link (@color) when (luma(@color) >= @luma-upper-break) {
87 | color:average(darken(@color,80%),#222);
88 | }
89 |
90 | .contrast-link (@color) when (luma(@color) < @luma-upper-break) {
91 | color:mix(#ffffff,@color1,75%);
92 | }
93 |
94 | .btn-borders(@color) {
95 | border:1px solid transparent;
96 | border-top-color:mix(softlight(white,@color),@color,30%);
97 | }
98 |
99 | .btn-borders(@color) when (luma(@seed-color) > @luma-upper-break) {
100 | border:1px solid transparent;
101 | border-bottom-color:mix(#333,@color,30%);
102 | }
103 |
104 | .btn-borders(@color) when (luma(@seed-color) < 10%) {
105 | border:1px solid transparent;
106 | border-top-color:mix(softlight(white,@color),@color,30%);
107 | }
108 |
109 | .btn-pseudo-states(@color, @background, @border) { // overwrites bootstrap mix-in
110 | .contrast(@background);
111 | .btn-borders(@background);
112 |
113 | &:hover,
114 | &:focus,
115 | &:active,
116 | &.active {
117 | background-color: darken(@background, 2%);
118 | .btn-borders(darken(@background, 2%));
119 | border-top:1px solid darken(@background, 2%);
120 | .1pxdeep-shadow(1.5);
121 | }
122 |
123 | &.disabled,
124 | &[disabled],
125 | fieldset[disabled] & {
126 | &,
127 | &:hover,
128 | &:focus,
129 | &:active,
130 | &.active {
131 | background-color: @background;
132 | border-color:transparent;
133 | }
134 | }
135 | }
136 |
137 | //shadows
138 |
139 | @1pxdeep-shadow-intensity:.1;
140 |
141 | @1pxdeep-shadow-color:mix(darken(@color1,33%),#222,50%);
142 |
143 | @1pxdeep-shadow-r:red(@1pxdeep-shadow-color);
144 | @1pxdeep-shadow-g:green(@1pxdeep-shadow-color);
145 | @1pxdeep-shadow-b:blue(@1pxdeep-shadow-color);
146 |
147 | .1pxdeep-shadow(@intensity:1) {
148 | .box-shadow(0px 1px 2px rgba(@1pxdeep-shadow-r,@1pxdeep-shadow-g,@1pxdeep-shadow-b,@intensity*@1pxdeep-shadow-intensity));
149 | //.box-shadow(none);
150 | }
151 |
152 | .1pxdeep-shadow(@intensity:1) when (luma(@color1) > @luma-upper-break) {
153 | @1pxdeep-shadow-intensity:.05;
154 | .box-shadow(0px 1px 2px rgba(@1pxdeep-shadow-r,@1pxdeep-shadow-g,@1pxdeep-shadow-b,@intensity*@1pxdeep-shadow-intensity));
155 | //.box-shadow(none);
156 | }
157 |
158 | .1pxdeep-shadow-inset(@intensity:1) {
159 | .box-shadow(inset 0px 0px 3px rgba(@1pxdeep-shadow-r,@1pxdeep-shadow-g,@1pxdeep-shadow-b,@intensity*@1pxdeep-shadow-intensity));
160 | //.box-shadow(none);
161 | }
162 |
163 | .1pxdeep-shadow-inset(@intensity:1) when (luma(@color1) > @luma-upper-break) {
164 | @1pxdeep-shadow-intensity:.05;
165 | .box-shadow(inset 0px 0px 3px rgba(@1pxdeep-shadow-r,@1pxdeep-shadow-g,@1pxdeep-shadow-b,@intensity*@1pxdeep-shadow-intensity));
166 | //.box-shadow(none);
167 | }
168 |
169 |
170 | .btn {
171 | .1pxdeep-shadow();
172 |
173 | &:active,
174 | &.active {
175 | .box-shadow(none);
176 | }
177 |
178 | &.disabled,
179 | &[disabled],
180 | fieldset[disabled] & {
181 | .opacity(.65);
182 | .box-shadow(none);
183 | }
184 |
185 | .btn-color(@color) {
186 | background:@color;
187 | .contrast(@color);
188 | .btn-borders(@color);
189 |
190 | &:hover,
191 | &:focus {
192 | background:lighten(@color,2%);
193 | .btn-borders(lighten(@color,2%));
194 | .contrast(@color);
195 | }
196 |
197 | &:active,
198 | &.active {
199 | .contrast-dim(@color);
200 | background:darken(@color,2%);
201 | border:1px solid darken(@color,2%);
202 | }
203 | }
204 |
205 | .btn-color(@color) when (luma(@color) > @luma-upper-break) {
206 | background:@color;
207 | .contrast(@color);
208 | .btn-borders(@color);
209 |
210 | &:hover,
211 | &:focus {
212 | background:lighten(@color,2%);
213 | .contrast(@color);
214 | }
215 |
216 | &:active,
217 | &.active {
218 | background:darken(@color,2%);
219 | border:1px solid darken(@color,2%);
220 | }
221 | }
222 |
223 | .btn-color(@1pxdeep-default-button-color);
224 | &.color1 {.btn-color(@color1);}
225 | &.color1a {.btn-color(@color1a);}
226 | &.color1b {.btn-color(@color1b);}
227 | &.color1c {.btn-color(@color1c);}
228 | &.color2 {.btn-color(@color2);}
229 | &.color2a {.btn-color(@color2a);}
230 | &.color2b {.btn-color(@color2b);}
231 | &.color2c {.btn-color(@color2c);}
232 | &.color3 {.btn-color(@color3);}
233 | &.color3a {.btn-color(@color3a);}
234 | &.color3b {.btn-color(@color3b);}
235 | &.color3c {.btn-color(@color3c);}
236 | &.color4 {.btn-color(@color4);}
237 | &.color4a {.btn-color(@color4a);}
238 | &.color4b {.btn-color(@color4b);}
239 | &.color4c {.btn-color(@color4c);}
240 |
241 | &.btn-primary {.btn-color(@brand-primary);}
242 | &.btn-info {.btn-color(@brand-info);}
243 | &.btn-success {.btn-color(@brand-success);}
244 | &.btn-warning {.btn-color(@brand-warning);}
245 | &.btn-danger {.btn-color(@brand-danger);}
246 | &.btn-inverse {.btn-color(desaturate(@color1a,100%));}
247 | }
248 |
249 | .schemify(@color) {
250 | @mix-weight:60%;
251 | @new-color:mix(hsl(hue(@color),@sat,@luma),@color,@mix-weight);
252 | background:@new-color;
253 | .contrast(@new-color);
254 | }
255 |
256 |
257 | //elements
258 |
259 | body,html {
260 | background:@1pxdeep-bg;
261 | .contrast(@1pxdeep-bg);
262 |
263 | -webkit-font-smoothing: antialiased;
264 | -moz-font-smoothing: antialiased;
265 | font-smoothing: antialiased;
266 | }
267 |
268 | a {
269 | .contrast-link(@1pxdeep-bg);
270 |
271 | &:hover {
272 | .contrast-link(@1pxdeep-bg);
273 | }
274 | }
275 |
276 | blockquote {
277 |
278 | padding:6px 8px;
279 |
280 | cite {
281 | font-style:italic;
282 | }
283 |
284 | .blockquote-color(@color,@alt-color) {
285 | background:@color;
286 | border-left:4px solid @alt-color;
287 | .contrast(@color);
288 |
289 | &.pull-right {
290 | border-left:none;
291 | border-right:4px solid @alt-color;
292 | }
293 |
294 | small {
295 | .contrast(@color);
296 | text-shadow:none;
297 | }
298 | }
299 |
300 | .blockquote-color(@color1c,@color1a);
301 | &.color1 { .blockquote-color(@color1c,@color1a)}
302 | &.color2 { .blockquote-color(@color2c,@color2a)}
303 | &.color3 { .blockquote-color(@color3c,@color3a)}
304 | &.color4 { .blockquote-color(@color4c,@color4a)}
305 | }
306 |
307 | /*input[type="checkbox"] { //experimental flat look for checkboxes
308 | margin-right:13px;
309 | margin-top:5px;
310 | background:white;
311 |
312 | &:before {
313 | display:block;
314 | content:"";
315 | width:22px;
316 | height:22px;
317 | //border:2px solid @1pxdeep-bg;
318 | border-radius:@border-radius-base;
319 | background:white;
320 | position:relative;
321 | top:-4px;
322 | left:-1px;
323 | .box-shadow(inset 1px 1px 0px @1pxdeep-bg);
324 | }
325 |
326 | &:checked {
327 | &:after {
328 | display:block;
329 | content:"";
330 | width:8px;
331 | height:13px;
332 | border-radius:0;
333 | background:white;
334 | position:relative;
335 | top:-23px;
336 | left:7px;
337 | .box-shadow(inset -3px -3px 0px @1pxdeep-bg);
338 | border:none;
339 | border-radius:0;
340 | .rotate(45deg);
341 | }
342 | }
343 | }*/
344 |
345 | .form-control {
346 | .box-shadow(none);
347 | }
348 |
349 | // small fix in headlines
350 |
351 | h1, h2, h3, h4, h5, h6,
352 | .h1, .h2, .h3, .h4, .h5, .h6 {
353 | small {
354 | color:inherit;
355 | }
356 | }
357 |
358 | //dropdown
359 |
360 | .dropdown-menu {
361 | .box-shadow(none);
362 | border:none;
363 |
364 | li {
365 | border:none;
366 | }
367 |
368 | .dropdown-menu-color(@color) {
369 | li {
370 | &:hover {
371 | a {
372 | background:@color;
373 | .contrast(@color);
374 | }
375 |
376 | }
377 | }
378 | }
379 |
380 | .dropdown-menu-color(@color1b);
381 |
382 | &.color1 {.dropdown-menu-color(@color1b);}
383 | &.color2 {.dropdown-menu-color(@color2b);}
384 | &.color3 {.dropdown-menu-color(@color3b);}
385 | &.color4 {.dropdown-menu-color(@color4b);}
386 |
387 | }
388 |
389 | .btn-group.open .dropdown-toggle {
390 | .box-shadow(none);
391 | }
392 |
393 | .dropup .caret, .navbar-fixed-bottom .dropdown .caret {
394 | border-top:none;
395 | border-bottom-color:white;
396 | }
397 |
398 | .btn-default .caret {
399 | border-top-color:white;
400 | }
401 |
402 | //well
403 |
404 | .well {
405 | border:none;
406 |
407 | .well-color(@color) {
408 | .1pxdeep-shadow-inset(2.5);
409 | background:@color;
410 | .contrast(@color);
411 |
412 | a {
413 | .contrast-link(@color);
414 | }
415 | }
416 |
417 | .well-color(@modal-backdrop-bg);
418 | }
419 |
420 | //navbar
421 |
422 | .navbar {
423 |
424 | border:none;
425 |
426 | .navbar-color(@color) {
427 | background:@color;
428 | .contrast(@color);
429 | }
430 |
431 | .navbar-color(@color1a);
432 |
433 | &.color1 { .navbar-color(@color1a);}
434 | &.color2 { .navbar-color(@color2a);}
435 | &.color3 { .navbar-color(@color3a);}
436 | &.color4 { .navbar-color(@color4a);}
437 | }
438 |
439 | .navbar-brand {
440 | color:inherit;
441 | }
442 |
443 | .navbar-nav {
444 |
445 | .active {
446 | overflow-y:hidden;
447 | }
448 |
449 | .navbar-nav-color(@color) {
450 | .active > a {
451 | .1pxdeep-shadow(5);
452 | .contrast(@color);
453 | background-color: @color;
454 |
455 | &:hover {
456 | .contrast(@color);
457 | background-color: @color;
458 | }
459 | }
460 |
461 | li > a {
462 | .contrast-dim(darken(@color,10%));
463 | line-height: 25px;
464 | z-index:100;
465 |
466 | &:hover,
467 | &:focus {
468 | @ncolor:lighten(@color,4%);
469 | .contrast(@ncolor);
470 | background-color: @ncolor;
471 | }
472 | }
473 | }
474 |
475 | .navbar-nav-color(@1pxdeep-bg);
476 |
477 | &.color1 { .navbar-nav-color(@color1);}
478 | &.color2 { .navbar-nav-color(@color2);}
479 | &.color3 { .navbar-nav-color(@color3);}
480 | &.color4 { .navbar-nav-color(@color4);}
481 | }
482 |
483 | .navbar-fixed-top, .navbar-fixed-bottom {
484 | .1pxdeep-shadow-inset(2);
485 |
486 | .navbar-inner {
487 | //.box-shadow(none);
488 | padding:0;
489 | //margin-bottom:12px;
490 | }
491 | }
492 |
493 | .navbar-toggle {
494 | @color:@color1;
495 | background-color: @color;
496 | .btn-borders(@color);
497 |
498 | &:hover,
499 | &:focus {
500 | background-color: lighten(@color,2%);
501 | }
502 |
503 | &:active,
504 | .active {
505 | background-color:darken(@color,2%);
506 | border-top-color:darken(@color,2%);
507 | }
508 |
509 | .icon-bar-color(@color) when (luma(@color) > @luma-upper-break) {
510 | background-color:#333;
511 | }
512 |
513 | .icon-bar-color(@color) when (luma(@color) =< @luma-upper-break) {
514 | background-color:white;
515 | }
516 |
517 | .icon-bar {
518 | .icon-bar-color(@color);
519 | }
520 | }
521 |
522 | .navbar-collapse {
523 | border-top:none;
524 | }
525 |
526 | //pills
527 |
528 | .nav-pills {
529 | text-shadow:none;
530 | color:black;
531 |
532 | .pills-color(@color,@active-color) {
533 | > li > a {
534 | &:hover {
535 | background:lighten(@color,10%);
536 | .contrast(lighten(@color,10%));
537 | }
538 | }
539 |
540 | > li.active > a {
541 | background:@active-color;
542 | .contrast(@active-color);
543 |
544 | &:hover {
545 | background:lighten(@active-color,2%);
546 | .contrast(lighten(@active-color,2%));
547 | }
548 | }
549 | }
550 |
551 | .pills-color(@color1,@color1c);
552 |
553 | &.color1 { .pills-color(@color1,@color1c);}
554 | &.color2 { .pills-color(@color2,@color2c);}
555 | &.color3 { .pills-color(@color3,@color2c);}
556 | &.color4 { .pills-color(@color4,@color2c);}
557 | }
558 |
559 | //alert
560 |
561 | .alert {
562 | border:none;
563 | text-shadow:none;
564 |
565 | .close {
566 | .opacity(100);
567 | text-shadow:none;
568 | margin-top:-1px;
569 | }
570 |
571 | .alert-color(@color) {
572 | background:@color;
573 | .contrast(@color);
574 |
575 | .close {
576 | .contrast(@color);
577 | }
578 | }
579 |
580 | .alert-color-schemify(@color) {
581 | .alert-color(@color);
582 | .schemify(@color);
583 |
584 | .close {
585 | .contrast(mix(hsl(hue(@color),@sat,@luma),@color,60%););
586 | }
587 | }
588 |
589 | .alert-color-schemify(#fbfcd6);
590 |
591 | &.color1 { .alert-color(@color1);}
592 | &.color2 { .alert-color(@color2);}
593 | &.color3 { .alert-color(@color3);}
594 | &.color4 { .alert-color(@color4);}
595 |
596 | &.alert-error,&.alert-danger {.alert-color-schemify(#ee5f5b);}
597 | &.alert-info {.alert-color-schemify(#08c);}
598 | &.alert-success {.alert-color-schemify(#62c462);}
599 | }
600 |
601 | //progress bars
602 |
603 | .progress {
604 | background:darken(@1pxdeep-bg,5%);
605 | .1pxdeep-shadow-inset(2);
606 |
607 | .progress-color(@color) {
608 | background:darken(@color,5%);
609 | }
610 |
611 | &.color1 { .progress-color(@color1);}
612 | &.color2 { .progress-color(@color2);}
613 | &.color3 { .progress-color(@color3);}
614 | &.color4 { .progress-color(@color4);}
615 | }
616 |
617 | .progress-bar {
618 | .box-shadow(none);
619 |
620 | .progress-bar-color(@color) {
621 | background:@color;
622 | .contrast(@color);
623 | }
624 |
625 | .progress-bar-color(@color1c);
626 | &.color1 { .progress-bar-color(@color1c);}
627 | &.color2 { .progress-bar-color(@color2c);}
628 | &.color3 { .progress-bar-color(@color3c);}
629 | &.color4 { .progress-bar-color(@color4c);}
630 | &.progress-bar-success { .progress-bar-color(@brand-success);}
631 | &.progress-bar-warning { .progress-bar-color(@brand-warning);}
632 | &.progress-bar-danger { .progress-bar-color(@brand-danger);}
633 | &.progress-bar-info { .progress-bar-color(@brand-info);}
634 | }
635 |
636 | //accordion
637 |
638 | .accordion {
639 | .accordion-heading {
640 | border-bottom: 0;
641 | border-radius:6px;
642 | a {
643 | &:hover {
644 | text-decoration:none;
645 | }
646 | }
647 | }
648 |
649 | .accordion-group {
650 | border:none;
651 | }
652 |
653 | .accordion-inner {
654 | margin:12px;
655 | padding:15px;
656 | border-radius:6px;
657 | }
658 |
659 | .accordion-inner-color(@color) {
660 | .accordion-inner {
661 | background:@color;
662 | .contrast(@color);
663 | //text-shadow:none;
664 | }
665 | }
666 |
667 | .accordion-heading-color(@color) {
668 | .accordion-heading {
669 | background:@color;
670 | .contrast(@color);
671 | .btn-borders(@color);
672 |
673 | a {
674 | .contrast(@color);
675 | }
676 | }
677 | }
678 |
679 | .accordion-heading-color(@color1b);
680 | .accordion-inner-color(@color1c);
681 |
682 | &.color1 { .accordion-heading-color(@color1b);
683 | .accordion-inner-color(@color1c);}
684 | &.color2 { .accordion-heading-color(@color2b);
685 | .accordion-inner-color(@color2c);}
686 | &.color3 { .accordion-heading-color(@color3b);
687 | .accordion-inner-color(@color3c);}
688 | &.color4 { .accordion-heading-color(@color4b);
689 | .accordion-inner-color(@color4c);}
690 | }
691 |
692 | //jumbotron
693 |
694 | .jumbotron {
695 | font-size: 24px;
696 | line-height:32px;
697 | font-weight: normal;
698 |
699 | .jumbotron-color(@color) {
700 | background:@color;
701 | .contrast(@color);
702 | }
703 |
704 | .jumbotron-color(@color1b);
705 |
706 | &.color1 {.jumbotron-color(@color1b);}
707 | &.color2 {.jumbotron-color(@color2b);}
708 | &.color3 {.jumbotron-color(@color3b);}
709 | &.color4 {.jumbotron-color(@color4b);}
710 |
711 | h1 {
712 | margin-bottom: 12px;
713 | font-size: 60px;
714 | line-height: 1;
715 | color: @jumbotron-heading-color;
716 | letter-spacing: -1px;
717 | }
718 | }
719 |
720 | //breadcrumbs
721 |
722 | .breadcrumb {
723 |
724 | > .active {
725 | font-weight:bold;
726 | }
727 |
728 | .breadcrumb-color(@color) {
729 | background:@color;
730 | li {
731 | .contrast(@color);
732 |
733 | > a {
734 | .contrast(@color);
735 | }
736 | }
737 |
738 | > .active {
739 | .contrast(@color)
740 | }
741 |
742 | }
743 |
744 | .breadcrumb-color(@color1b);
745 |
746 | &.color1 { .breadcrumb-color(@color1b);}
747 | &.color2 { .breadcrumb-color(@color2b);}
748 | &.color3 { .breadcrumb-color(@color3b);}
749 | &.color4 { .breadcrumb-color(@color4b);}
750 |
751 |
752 | }
753 |
754 | //pager
755 |
756 | .pager {
757 | > li {
758 | > a {
759 | border:none;
760 | }
761 | }
762 |
763 | .pager-color(@color) {
764 | > li {
765 | > a {
766 | background:@color;
767 | .contrast(@color);
768 | }
769 |
770 | > a:hover,
771 | > a:focus {
772 | background-color: lighten(@color,2%);
773 | }
774 | }
775 |
776 | > .disabled {
777 | > a {
778 | background:@color;
779 | .contrast-dim(@color);
780 | .opacity(.5);
781 | }
782 |
783 | > a:hover,
784 | > a:focus {
785 | background-color:@color;
786 | .contrast-dim(@color);
787 | }
788 |
789 | }
790 | }
791 |
792 | .pager-color(@color1b);
793 |
794 | &.color1 { .pager-color(@color1b);}
795 | &.color2 { .pager-color(@color2b);}
796 | &.color3 { .pager-color(@color3b);}
797 | &.color4 { .pager-color(@color4b);}
798 |
799 | &.disabled { .pager-color(#ddd);}
800 | }
801 |
802 | //pagination
803 |
804 | .pagination {
805 | background:none;
806 |
807 | .page-color(@color,@active-color) {
808 |
809 | > li > a {
810 | background:@color;
811 | .contrast(@color);
812 | .btn-borders(@color);
813 | }
814 |
815 | > li > a:hover,
816 | > li > a:focus,
817 | > li > a:active,
818 | > .active > a,
819 | > .active > span {
820 | .contrast(lighten(@color,2%));
821 | background-color:lighten(@color,2%);
822 | }
823 | > .active > a,
824 | > .active > span {
825 | .contrast(@active-color);
826 | background-color:lighten(@active-color,2%);
827 | }
828 | }
829 |
830 | .page-color(@color1b,@color1c);
831 |
832 | &.color1 {.page-color(@color1b,@color1c);}
833 | &.color2 {.page-color(@color2b,@color2c);}
834 | &.color3 {.page-color(@color3b,@color3c);}
835 | &.color4 {.page-color(@color4b,@color4c);}
836 |
837 | > .disabled {
838 | > span,
839 | > a,
840 | > a:hover,
841 | > a:focus {
842 | color:inherit;
843 | background-color:inherit;
844 | .opacity(.5);
845 | }
846 | }
847 | }
848 |
849 | //thumbnail
850 |
851 | .thumbnail,
852 | .img-thumbnail {
853 | border: 1px solid transparent;
854 |
855 | .thumbnail-color(@color) {
856 | background-color:@color;
857 |
858 | .caption {
859 | .contrast(@color);
860 | }
861 | }
862 |
863 | .thumbnail-color(@color1b);
864 |
865 | &.color1 {.thumbnail-color(@color1b);}
866 | &.color2 {.thumbnail-color(@color2b);}
867 | &.color3 {.thumbnail-color(@color3b);}
868 | &.color4 {.thumbnail-color(@color4b);}
869 | }
870 |
871 | //form
872 |
873 | .form-horizontal {
874 | font-size:18px;
875 | }
876 |
877 | legend {
878 | .contrast(@1pxdeep-bg);
879 | }
880 |
881 | .help-block {
882 | .contrast(@1pxdeep-bg);
883 | }
884 |
885 | .form-color(@color) {
886 | legend {
887 | .contrast(@color);
888 | }
889 |
890 | .help-block {
891 | .contrast(@color);
892 | }
893 | }
894 |
895 | .color1 {
896 | .form-color(@color1);
897 | }
898 |
899 | //input group
900 |
901 | .input-group {
902 | position: relative; // For dropdowns
903 | display: table;
904 | border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table
905 |
906 | // Undo padding and float of grid classes
907 | &.col {
908 | float: none;
909 | padding-left: 0;
910 | padding-right: 0;
911 | }
912 |
913 | .form-control {
914 | width: 100%;
915 | margin-bottom: 0;
916 | }
917 | }
918 |
919 | .input-group-addon {
920 | border: none;
921 | border-radius: @border-radius-base;
922 |
923 | .input-group-addon-color(@color) {
924 | background:@color;
925 | .contrast(@color);
926 | }
927 |
928 | .input-group-addon-color(@color1b);
929 |
930 | &.color1 {.input-group-addon-color(@color1b)}
931 |
932 | }
933 |
934 | //code
935 |
936 | code, pre {
937 | font-size:14px;
938 | text-shadow:none;
939 |
940 | .code-color(@color) {
941 | background:@color;
942 | .contrast(@color);
943 | border-color:@color;
944 | }
945 |
946 | .code-color(@color1c);
947 |
948 | &.color1 { .code-color(@color1b)}
949 | &.color2 { .code-color(@color2b)}
950 | &.color3 { .code-color(@color3b)}
951 | &.color4 { .code-color(@color4b)}
952 | }
953 |
954 | //labels and badges
955 |
956 | .label, .badge {
957 | .label-color(@color) {
958 | background:@color;
959 | .contrast(@color);
960 | text-shadow:none;
961 | }
962 |
963 | .label-color(@color1a);
964 |
965 | &.color1 { .label-color(@color1);}
966 | &.color2 { .label-color(@color2);}
967 | &.color3 { .label-color(@color3);}
968 | &.color4 { .label-color(@color4);}
969 |
970 | &.label-success { .label-color(@brand-success);}
971 | &.label-info { .label-color(@brand-info);}
972 | &.label-warning { .label-color(@brand-warning);}
973 | &.label-danger { .label-color(@brand-danger);}
974 | }
975 |
976 | //nav tabs
977 |
978 | .nav-tabs {
979 | > li {
980 | > a {
981 | border: none;
982 |
983 | &:hover {
984 | border:none;
985 | }
986 | }
987 |
988 | &.active > a {
989 | &,
990 | &:hover,
991 | &:focus {
992 | border:none;
993 | }
994 | }
995 | }
996 |
997 | .nav-tabs-color(@color) {
998 | border-bottom:2px solid @color;
999 |
1000 | > li {
1001 |
1002 | > a {
1003 | &:hover {
1004 | background:lighten(@color,2%);
1005 | }
1006 | }
1007 |
1008 | &.active > a {
1009 | &,
1010 | &:hover,
1011 | &:focus {
1012 | .contrast(@color);
1013 | background-color:@color;
1014 | }
1015 | }
1016 | }
1017 | }
1018 |
1019 | .nav-tabs-color(@color1c);
1020 |
1021 | &.color1 { .nav-tabs-color(@color1a);}
1022 | &.color2 { .nav-tabs-color(@color2a);}
1023 | &.color3 { .nav-tabs-color(@color3a);}
1024 | &.color4 { .nav-tabs-color(@color4a);}
1025 |
1026 |
1027 | }
1028 |
1029 | //table
1030 |
1031 | table {
1032 | max-width: 100%;
1033 | text-shadow:none;
1034 | border-collapse: collapse;
1035 | border-spacing: 0;
1036 | border-color:transparent;
1037 | border-radius:@border-radius-base;
1038 |
1039 | thead,
1040 | tbody,
1041 | tfoot {
1042 | > tr {
1043 | > th,
1044 | > td {
1045 | border-top:none;
1046 | }
1047 | }
1048 | }
1049 |
1050 | tbody + tbody {
1051 | border-top: none;
1052 | }
1053 |
1054 | .table-color(@color) {
1055 | background-color:@color;
1056 | .contrast(@color);
1057 |
1058 | &.table {
1059 | th,
1060 | td {
1061 | border-top-color: @color;
1062 | }
1063 | tbody + tbody {
1064 | border-top-color:@color;
1065 | }
1066 | }
1067 |
1068 | &.table-bordered {
1069 | border:none;
1070 | }
1071 |
1072 | &.table-striped {
1073 | .table-striped-color(darken(@color,5%));
1074 | }
1075 |
1076 | .table-striped-color(@color) {
1077 | tbody {
1078 | > tr:nth-child(odd) > td,
1079 | > tr:nth-child(odd) > th {
1080 | background-color:darken(@color,1%);
1081 | }
1082 | }
1083 | }
1084 |
1085 | .table-striped-color (@color) when (luma(@color) > @luma-upper-break) {
1086 | tbody {
1087 | > tr:nth-child(odd) > td,
1088 | > tr:nth-child(odd) > th {
1089 | background-color:darken(@color,1%);
1090 | }
1091 | }
1092 | }
1093 |
1094 | .table-striped-color (@color) when (luma(@color) < @luma-lower-break) {
1095 | tbody {
1096 | > tr:nth-child(odd) > td,
1097 | > tr:nth-child(odd) > th {
1098 | background-color:lighten(@color,1%);
1099 | }
1100 | }
1101 | }
1102 |
1103 | &.table-hover {
1104 | tbody {
1105 | tr:hover > td,
1106 | tr:hover > th {
1107 | background-color: lighten(@color,5%);
1108 | }
1109 | }
1110 | }
1111 | }
1112 |
1113 | .table-color(@color1b);
1114 |
1115 | &.color1 { .table-color(@color1b);}
1116 | &.color2 { .table-color(@color2b);}
1117 | &.color3 { .table-color(@color3b);}
1118 | &.color4 { .table-color(@color4b);}
1119 | }
1120 |
1121 | //hr
1122 |
1123 | hr {
1124 | .hr-color(@color) {
1125 | background:@color;
1126 | color:@color;
1127 | border-color:@color;
1128 | }
1129 |
1130 | .hr-color(color1c);
1131 | &.color1 {.hr-color(@color1c);}
1132 | &.color2 {.hr-color(@color2c);}
1133 | &.color3 {.hr-color(@color3c);}
1134 | &.color4 {.hr-color(@color4c);}
1135 | }
1136 |
1137 | //page header
1138 |
1139 | .page-header {
1140 | border-width:2px;
1141 |
1142 | .page-header-color(@color) {
1143 | border-color:@color;
1144 | .contrast(@1pxdeep-bg);
1145 |
1146 | small {
1147 | .contrast(@1pxdeep-bg);
1148 | }
1149 | }
1150 |
1151 | .page-header-color(@color1c);
1152 | &.color1 {.page-header-color(@color1c);}
1153 | &.color2 {.page-header-color(@color2c);}
1154 | &.color3 {.page-header-color(@color3c);}
1155 | &.color4 {.page-header-color(@color4c);}
1156 | }
1157 |
1158 | //modal
1159 |
1160 | .modal-dialog {
1161 | padding-right:0;
1162 | padding-left:0;
1163 | width:100%;
1164 | }
1165 |
1166 | .modal-content {
1167 | .box-shadow(none);
1168 | border:none;
1169 | background:none;
1170 | width:100%;
1171 | }
1172 |
1173 | .modal-header {
1174 | border:none;
1175 | width:60%;
1176 | margin-left:20%;
1177 | padding-left:0;
1178 | padding-right:0;
1179 |
1180 | > .close {
1181 | .contrast(@modal-backdrop-bg);
1182 | margin-top:0;
1183 | .opacity(1);
1184 | padding:14px; //more touch-friendly
1185 | position:relative;
1186 | top:-12px;
1187 | right:-14px;
1188 | }
1189 | }
1190 |
1191 | .modal-title {
1192 | .contrast(@modal-backdrop-bg);
1193 | //text-shadow:none;
1194 | margin-left:0;
1195 | }
1196 |
1197 | .modal-body {
1198 | border-radius:0;
1199 | width:100%;
1200 | padding-left:20%;
1201 | padding-right:20%;
1202 | margin:0;
1203 |
1204 | .modal-body-color(@color) {
1205 | background:@color;
1206 | .contrast(@color);
1207 | }
1208 |
1209 | .modal-body-color(@color1);
1210 |
1211 | &.color1 { .modal-body-color(@color1);}
1212 | &.color1a { .modal-body-color(@color1a);}
1213 | &.color1b { .modal-body-color(@color1b);}
1214 | &.color1c { .modal-body-color(@color1c);}
1215 | &.color2 { .modal-body-color(@color2);}
1216 | &.color2a { .modal-body-color(@color2a);}
1217 | &.color2b { .modal-body-color(@color2b);}
1218 | &.color2c { .modal-body-color(@color2c);}
1219 | &.color3 { .modal-body-color(@color3);}
1220 | &.color3a { .modal-body-color(@color3a);}
1221 | &.color3b { .modal-body-color(@color3b);}
1222 | &.color3c { .modal-body-color(@color3c);}
1223 | &.color4 { .modal-body-color(@color4);}
1224 | &.color4a { .modal-body-color(@color4a);}
1225 | &.color4b { .modal-body-color(@color4b);}
1226 | &.color4c { .modal-body-color(@color4c);}
1227 | }
1228 |
1229 | .modal-footer {
1230 | background:transparent;
1231 | margin-top:0;
1232 | border:none;
1233 | width:60%;
1234 | margin-left:20%;
1235 | padding-left:0;
1236 | padding-right:0;
1237 | }
1238 |
1239 | .modal-backdrop {
1240 | background-color: @modal-backdrop-bg;
1241 | &.fade { .opacity(0); }
1242 | &.in { .opacity(1); }
1243 |
1244 | &:before {
1245 | display:block;
1246 | content:"";
1247 | background:@modal-backdrop-bg;
1248 | width:100%;
1249 | height:50%;
1250 | position:absolute;
1251 | top:0;
1252 | left:0;
1253 | }
1254 | }
1255 |
1256 | .close {
1257 | text-shadow:none;
1258 | }
1259 |
1260 | @media screen and (max-width: @screen-tablet) {
1261 |
1262 | .modal-header, .modal-footer {
1263 | margin-left:4%;
1264 | margin-right:4%;
1265 | width:92%;
1266 | }
1267 |
1268 | .modal-body {
1269 | padding-left:4%;
1270 | padding-right:4%;
1271 | }
1272 |
1273 | }
1274 |
1275 | //panel
1276 | .panel {
1277 | border:none;
1278 | .box-shadow(none);
1279 |
1280 | .panel-color(@color) {
1281 | background:@color;
1282 | .contrast(@color);
1283 | }
1284 |
1285 | .panel-color(@color1c);
1286 |
1287 | &.color1 { .panel-color(@color1);}
1288 | &.color1a { .panel-color(@color1a);}
1289 | &.color1b { .panel-color(@color1b);}
1290 | &.color1c { .panel-color(@color1c);}
1291 | &.color2 { .panel-color(@color2);}
1292 | &.color2a { .panel-color(@color2a);}
1293 | &.color2b { .panel-color(@color2b);}
1294 | &.color2c { .panel-color(@color2c);}
1295 | &.color3 { .panel-color(@color3);}
1296 | &.color3a { .panel-color(@color3a);}
1297 | &.color3b { .panel-color(@color3b);}
1298 | &.color3c { .panel-color(@color3c);}
1299 | &.color4 { .panel-color(@color4);}
1300 | &.color4a { .panel-color(@color4a);}
1301 | &.color4b { .panel-color(@color4b);}
1302 | &.color4c { .panel-color(@color4c);}
1303 | }
1304 |
1305 | .panel-heading, .panel-footer {
1306 | border:none;
1307 |
1308 | }
1309 |
1310 | .panel-body {
1311 | padding-bottom:0;
1312 | }
1313 |
1314 | .panel-footer {
1315 | padding-bottom:15px;
1316 | }
1317 |
1318 | .panel-heading, .panel-body, .panel-footer {
1319 |
1320 | .panel-heading-color(@color) {
1321 | background:@color;
1322 | .contrast(@color);
1323 | }
1324 |
1325 | .panel-heading-color(@color1c);
1326 |
1327 | &.color1 { .panel-heading-color(@color1);}
1328 | &.color1a { .panel-heading-color(@color1a);}
1329 | &.color1b { .panel-heading-color(@color1b);}
1330 | &.color1c { .panel-heading-color(@color1c);}
1331 | &.color2 { .panel-heading-color(@color2);}
1332 | &.color2a { .panel-heading-color(@color2a);}
1333 | &.color2b { .panel-heading-color(@color2b);}
1334 | &.color2c { .panel-heading-color(@color2c);}
1335 | &.color3 { .panel-heading-color(@color3);}
1336 | &.color3a { .panel-heading-color(@color3a);}
1337 | &.color3b { .panel-heading-color(@color3b);}
1338 | &.color3c { .panel-heading-color(@color3c);}
1339 | &.color4 { .panel-heading-color(@color4);}
1340 | &.color4a { .panel-heading-color(@color4a);}
1341 | &.color4b { .panel-heading-color(@color4b);}
1342 | &.color4c { .panel-heading-color(@color4c);}
1343 | }
1344 |
1345 | //carousel
1346 |
1347 | .carousel-control {
1348 | &.left {
1349 | background:transparent;
1350 | }
1351 |
1352 | &.right {
1353 | left: auto;
1354 | right: 0;
1355 | background:transparent;
1356 | }
1357 |
1358 | .icon-nav-defaults() {
1359 | @size:44px;
1360 | font-size:@size;
1361 | line-height:@size;
1362 | text-shadow:none;
1363 | font-weight:bold;
1364 | }
1365 |
1366 | .icon-prev {
1367 | &:before {
1368 | content: '\00ab';// DOUBLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039)
1369 | .icon-nav-defaults();
1370 | }
1371 | }
1372 |
1373 | .icon-next {
1374 | &:before {
1375 | content: '\00bb';// DOUBLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A)
1376 | .icon-nav-defaults();
1377 | }
1378 | }
1379 | }
1380 |
1381 | .carousel-indicators {
1382 | @size:24px;
1383 | @border-radius:2px;
1384 |
1385 | li {
1386 | width: (@size - @border-radius);
1387 | height: (@size - @border-radius);
1388 | border-radius: @size;
1389 | margin:@border-radius;
1390 | }
1391 |
1392 | .active {
1393 | width: (@size + @border-radius);
1394 | height: (@size + @border-radius);
1395 | }
1396 |
1397 | .carousel-indicators-color(@color) {
1398 | li {
1399 | border: @border-radius solid @color;
1400 | }
1401 |
1402 | .active {
1403 | background-color:@color;
1404 | }
1405 | }
1406 |
1407 | .carousel-indicators-color(@color1c);
1408 |
1409 | &.color1{ .carousel-indicators-color(@color1c);}
1410 | &.color2{ .carousel-indicators-color(@color2c);}
1411 | &.color3{ .carousel-indicators-color(@color3c);}
1412 | &.color4{ .carousel-indicators-color(@color4c);}
1413 | }
1414 |
1415 | //list groups
1416 |
1417 | .list-group {
1418 | background:transparent;
1419 | }
1420 |
1421 | .list-group-item {
1422 | .list-group-item-color(@color,@active-color) {
1423 |
1424 | background:@color;
1425 | .contrast(@color);
1426 | border-color:darken(@color,4%);
1427 |
1428 | a& {
1429 | .contrast(@color);
1430 |
1431 | &:hover,
1432 | &:focus {
1433 | background-color: lighten(@color,2%);
1434 | }
1435 |
1436 | > .list-group-item-heading {
1437 | .contrast(@color);
1438 | }
1439 | }
1440 |
1441 | &.active,
1442 | &.active:hover,
1443 | &.active:focus {
1444 | .contrast(@active-color);
1445 | background-color: @active-color;
1446 | .btn-borders(@active-color);
1447 |
1448 | .list-group-item-text {
1449 | color: lighten(@active-color, 40%);
1450 | }
1451 | }
1452 | }
1453 |
1454 | .list-group-item-color(@color1b,@color1c);
1455 |
1456 | &.color1 { .list-group-item-color(@color1b,@color1c);}
1457 | &.color2 { .list-group-item-color(@color2b,@color2c);}
1458 | &.color3 { .list-group-item-color(@color3b,@color3c);}
1459 | &.color4 { .list-group-item-color(@color4b,@color4c);}
1460 | }
1461 |
1462 | // sectionals
1463 | .sectional {
1464 | position:relative;
1465 | padding-top:90px;
1466 | padding-bottom:90px;
1467 |
1468 | .sectional-title {
1469 | position:absolute;
1470 | top:12px;
1471 | width:100%;
1472 | text-align:center;
1473 | text-transform:uppercase;
1474 | font-size:14px;
1475 | .opacity(.65);
1476 | }
1477 |
1478 | .sectional-color(@color) {
1479 | background:@color;
1480 | .contrast(@color);
1481 | }
1482 |
1483 | .sectional-color(@1pxdeep-bg);
1484 |
1485 | &.monochrome {.sectional-color(greyscale(@color1));}
1486 | &.highlight {.sectional-color(lighten(@1pxdeep-bg,6%));}
1487 |
1488 | &.color1 {.sectional-color(@color1);}
1489 | &.color1a {.sectional-color(@color1a);}
1490 | &.color1b {.sectional-color(@color1b);}
1491 | &.color1c {.sectional-color(@color1c);}
1492 | &.color2 {.sectional-color(@color2);}
1493 | &.color2a {.sectional-color(@color2a);}
1494 | &.color2b {.sectional-color(@color2b);}
1495 | &.color2c {.sectional-color(@color2c);}
1496 | &.color3 {.sectional-color(@color3);}
1497 | &.color3a {.sectional-color(@color3a);}
1498 | &.color3b {.sectional-color(@color3b);}
1499 | &.color3c {.sectional-color(@color3c);}
1500 | &.color4 {.sectional-color(@color4);}
1501 | &.color4a {.sectional-color(@color4a);}
1502 | &.color4b {.sectional-color(@color4b);}
1503 | &.color4c {.sectional-color(@color4c);}
1504 | }
1505 |
1506 | //well headline fix
1507 |
1508 | .well {
1509 | h1,h2,h3,h4,h5,h6 {
1510 | &:first-child {
1511 | margin-top:0;
1512 | }
1513 | }
1514 | }
1515 |
1516 |
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/1pxdeep/scheme.less:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////
2 | /////////////////////////////////////////////////
3 | //// SchemeLESS v1.0
4 | ////
5 | //// Copyright 2013 Rex Riepe
6 | //// Licensed under the Apache License v2.0
7 | //// http://www.apache.org/licenses/LICENSE-2.0
8 | ////
9 | /////////////////////////////////////////////////
10 | /////////////////////////////////////////////////
11 |
12 | ///////////////////////////
13 | ////Customizable values
14 | ///////////////////////////
15 |
16 | // This color is used to generate the scheme
17 | @seed-color:#578562;
18 |
19 | //// Color wheel positions
20 | ///////////////////////////
21 |
22 | // Uncomment the color wheel you want to use
23 |
24 | // Accented analogue (default)
25 | @wheel_pos1:45; @wheel_pos2:315; @wheel_pos3:180;
26 |
27 | // Tetrad
28 | //@wheel_pos1:30; @wheel_pos2:180; @wheel_pos3:210;
29 |
30 | // Triad
31 | //@wheel_pos1:120; @wheel_pos2:240; @wheel_pos3:0;
32 |
33 | // Compliment
34 | //@wheel_pos1:180; @wheel_pos2:0; @wheel_pos3:180;
35 |
36 | // Monochrome
37 | //@wheel_pos1:8; @wheel_pos2:352; @wheel_pos3:0;
38 |
39 | //// Luma breaks
40 | ///////////////////////////
41 |
42 | // Change these for different contrast cutoff points
43 |
44 | @luma-upper-break:80%;
45 | @luma-lower-break:16%;
46 |
47 | //// Relative changes to subcolors (lightness, saturation)
48 | ///////////////////////////
49 |
50 | // Customize these for different relative a, b and c colors
51 |
52 | @contrast:1;
53 |
54 | @color-a-sat:8%*@contrast;
55 | @color-a-lit:-15%*@contrast;
56 |
57 | @color-b-sat:7%*@contrast;
58 | @color-b-lit:-5%*@contrast;
59 |
60 | @color-c-sat:-3%*@contrast;
61 | @color-c-lit:8%*@contrast;
62 |
63 | ///////////////////////////
64 | ////Scheme building
65 | ///////////////////////////
66 |
67 | //// Beginning color values
68 | ///////////////////////////
69 |
70 | @sat:saturation(@seed-color);
71 | @luma:luma(@seed-color);
72 | @lit:lightness(@seed-color);
73 | @tone:desaturate(@seed-color,100%);
74 |
75 | //// Color creation
76 | ///////////////////////////
77 |
78 | //This makes the scheme's colors using the wheel positions
79 |
80 | @l-factor:@luma; // what we'll use for the L in HSL
81 |
82 | @color1:@seed-color;
83 | @color1theme:hsl(hue(spin(@seed-color,0)),@sat,@l-factor); // a color 1 alternate, to keep a, b and c colors consistent
84 |
85 | @color1a:lighten(saturate(@color1theme,@color-a-sat), @color-a-lit);
86 | @color1b:lighten(saturate(@color1theme,@color-b-sat), @color-b-lit);
87 | @color1c:lighten(saturate(@color1theme,@color-c-sat), @color-c-lit);
88 |
89 | @color2:hsl(hue(spin(@seed-color,@wheel_pos1)),@sat,@l-factor);
90 |
91 | @color2a:lighten(saturate(@color2,@color-a-sat), @color-a-lit);
92 | @color2b:lighten(saturate(@color2,@color-b-sat), @color-b-lit);
93 | @color2c:lighten(saturate(@color2,@color-c-sat), @color-c-lit);
94 |
95 | @color3:hsl(hue(spin(@seed-color,@wheel_pos2)),@sat,@l-factor);
96 |
97 | @color3a:lighten(saturate(@color3,@color-a-sat), @color-a-lit);
98 | @color3b:lighten(saturate(@color3,@color-b-sat), @color-b-lit);
99 | @color3c:lighten(saturate(@color3,@color-c-sat), @color-c-lit);
100 |
101 | @color4:hsl(hue(spin(@seed-color,@wheel_pos3)),@sat,@l-factor);
102 |
103 | @color4a:lighten(saturate(@color4,@color-a-sat), @color-a-lit);
104 | @color4b:lighten(saturate(@color4,@color-b-sat), @color-b-lit);
105 | @color4c:lighten(saturate(@color4,@color-c-sat), @color-c-lit);
106 |
107 | ///////////////////////////
108 | //// Mix-ins
109 | ///////////////////////////
110 |
111 | //// Contrast
112 | ///////////////////////////
113 |
114 | // contrasts text against a given background color
115 |
116 | .contrast (@color) when (luma(@color) >= @luma-upper-break) {
117 | //darker text for lighter backgrounds
118 | color:average(darken(@color,30%),#222);
119 | }
120 |
121 | .contrast (@color) when (luma(@color) < @luma-upper-break) {
122 | //white text for everything else
123 | color:#ffffff;
124 | }
125 |
126 | //// Schemify
127 | ///////////////////////////
128 |
129 | // brings outside colors more in line with the current scheme
130 |
131 | .schemify(@color) { //schemifies a background color
132 | @mix-weight:60%;
133 | @new-color:mix(hsl(hue(@color),@sat,@luma),@color,@mix-weight);
134 | background:@new-color;
135 | .contrast(@new-color);
136 | }
137 |
138 | .schemify-text(@color) {
139 | @mix-weight:60%;
140 | @new-color:mix(hsl(hue(@color),@sat,@luma),@color,@mix-weight);
141 | color:@new-color;
142 | .contrast(@color);
143 | }
144 |
145 |
--------------------------------------------------------------------------------