diff --git a/.gitignore b/.gitignore index 69fc58944..2bc78b8dd 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,8 @@ crash.log config/newrelic.yml config/deploy/*.rb config/deploy.rb +.swo +.swn +.ruby-gemset +.ruby-version +.localeapp \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..ea7b73b88 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,25 @@ +language: ruby +rvm: + - 1.9.3 +bundler_args: --without development +env: + - SPEC_GROUP=controllers + - SPEC_GROUP=platform_controllers + - SPEC_GROUP=api + - SPEC_GROUP=models + - SPEC_GROUP=others +before_install: + - sudo apt-get update + - sudo apt-get --no-install-recommends install bash curl git patch bzip2 build-essential openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev libgdbm-dev ncurses-dev automake libtool bison subversion pkg-config libffi-dev libicu-dev rpm + - gem update --system + - gem --version + - git config --global user.email "abf@travis.com" + - git config --global user.name "ABF" +before_script: + - cp config/database.yml.sample config/database.yml + - cp config/application.yml.travis config/application.yml + - bundle exec rake db:create db:structure:load db:migrate +script: "./travis.sh" +branches: + only: + - /.*/ diff --git a/Gemfile b/Gemfile index c0dadcac7..eac94e8df 100644 --- a/Gemfile +++ b/Gemfile @@ -1,66 +1,86 @@ source 'https://rubygems.org' -gem 'rails', '3.2.13' #, :git => 'git://github.com/rails/rails.git' -gem 'redhillonrails_core', :git => 'git://github.com/warpc/redhillonrails_core.git', :branch => 'rails31' # '~> 2.0.0.pre' # deprecated +gem 'rails', '3.2.16' #, :git => 'git://github.com/rails/rails.git' +gem 'redhillonrails_core', :git => 'git://github.com/rosa-abf/redhillonrails_core.git', :branch => 'rails31' # '~> 2.0.0.pre' # deprecated gem 'pg', '~> 0.14.0' -gem 'devise', '~> 2.1.2' -gem 'omniauth', '~> 1.1.0' -gem 'omniauth-openid', '~> 1.0.1' +gem 'devise', '~> 2.2.3' +gem 'omniauth' +gem 'omniauth-facebook' +gem 'omniauth-google-oauth2' +gem 'omniauth-github' +# gem 'omniauth-openid', '~> 1.0.1' gem 'cancan', '1.6.7' # 1.6.8 fail specs with strange error gem 'ancestry', '~> 1.3.0' gem 'paperclip', '~> 3.3.1' -gem 'resque', '~> 1.21.0' +gem 'resque', '~> 1.24' gem 'resque-status', '~> 0.3.3' -gem 'resque_mailer', '~> 2.1.0' +gem 'resque_mailer', '~> 2.2' gem 'perform_later', '~> 1.3.0' # should be after resque_mailer gem 'russian', '~> 0.6.0' gem 'highline', '~> 1.6.11' gem 'state_machine' +gem 'redis-rails' -gem 'grack', :git => 'git://github.com/rdblue/grack.git', :require => 'git_http' -gem 'grit', :git => 'git://github.com/avokhmin/grit.git', :branch => '117-pack-format-error' -# gem "grit", :git => 'git://github.com/warpc/grit.git' #, :path => '~/Sites/code/grit' +gem 'grack', :git => 'git://github.com/rosa-abf/grack.git', :require => 'git_http' +gem "gitlab-grit", "~> 2.6.3" gem 'charlock_holmes', '~> 0.6.9' #, :git => 'git://github.com/brianmario/charlock_holmes.git', :branch => 'bundle-icu' -gem 'github-linguist', '~> 2.2.1', :require => 'linguist' +gem 'github-linguist', '~> 2.3.4', :require => 'linguist' gem 'diff-display', '~> 0.0.1' # Wiki gem "gollum", '~> 2.1.3' -gem "redcarpet", "~> 2.1.1" +gem "redcarpet", '~> 2.2.2' gem 'creole' gem 'rdiscount' # gem 'org-ruby' gem 'RedCloth' gem 'wikicloth' -gem 'unicorn', '~> 4.3.1', :platforms => [:mri, :rbx] +# gem 'unicorn', '~> 4.3.1', :platforms => [:mri, :rbx] gem 'trinidad', '~> 1.0.2', :platforms => :jruby -gem 'newrelic_rpm', '~> 3.5.8.72', :platforms => [:mri, :rbx] + +gem 'newrelic_rpm' gem 'whenever', '~> 0.7.3', :require => false -gem 'jbuilder', '~> 0.8.2' +gem 'jbuilder', '~> 1.4.2' gem 'rails3-jquery-autocomplete', '~> 1.0.7' gem 'will_paginate', '~> 3.0.3' gem 'meta-tags', '~> 1.2.5', :require => 'meta_tags' gem "haml-rails", '~> 0.3.4' gem 'jquery-rails', '~> 2.0.2' gem 'ruby-haml-js', '~> 0.0.3' -gem 'rails-backbone', '~> 0.7.2' +gem 'friendly_id' gem 'rack-throttle' gem 'rest-client', '~> 1.6.6' +gem 'ohm' +gem 'ohm-expire' + +gem 'ffi' gem 'attr_encrypted', '1.2.1' +gem "gemoji", "~> 1.2.1", require: 'emoji/railtie' + +# AngularJS related stuff +gem 'underscore-rails' +gem 'angularjs-rails' +gem 'ng-rails-csrf' +gem 'momentjs-rails' +gem 'angular-i18n', '0.1.2' +gem 'js-routes' +gem 'soundmanager-rails' + +gem 'time_diff' group :assets do gem 'sass-rails', '~> 3.2.5' gem 'coffee-rails', '~> 3.2.2' gem 'compass-rails', '~> 1.0.3' gem 'uglifier', '~> 1.2.4' - gem 'therubyracer', '~> 0.10.2', :platforms => [:mri, :rbx] + #gem 'therubyracer', '~> 0.10.2', :platforms => [:mri, :rbx] gem 'therubyrhino', '~> 1.73.1', :platforms => :jruby gem 'turbo-sprockets-rails3' end @@ -68,6 +88,9 @@ end group :production do gem "airbrake", '~> 3.1.2' gem 'bluepill', '~> 0.0.60', :require => false + # gem 'le' + gem 'logglier' + gem 'puma' end group :development do @@ -80,6 +103,11 @@ group :development do gem 'rvm-capistrano', :require => false gem 'cape', :require => false gem 'capistrano_colors', :require => false + # Better Errors & RailsPanel + gem 'better_errors' + gem 'binding_of_caller' + gem 'meta_request' + gem 'localeapp' end group :test do @@ -88,4 +116,5 @@ group :test do gem 'rr', '~> 1.0.4' gem 'shoulda' gem 'mock_redis', '0.6.2' + gem 'rake' end diff --git a/Gemfile.lock b/Gemfile.lock index 9298aeba7..56e77de54 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,21 +1,11 @@ GIT - remote: git://github.com/avokhmin/grit.git - revision: 1495f405d2b34e76dcc976db557df350b175e4b2 - branch: 117-pack-format-error - specs: - grit (2.5.0) - diff-lcs (~> 1.1) - mime-types (~> 1.15) - posix-spawn (~> 0.3.6) - -GIT - remote: git://github.com/rdblue/grack.git + remote: git://github.com/rosa-abf/grack.git revision: 020be3fef3fb308b9d214252522aa5945bf6584a specs: grack (0.2.0) GIT - remote: git://github.com/warpc/redhillonrails_core.git + remote: git://github.com/rosa-abf/redhillonrails_core.git revision: c0945a4c6ad4bae4ca2750b105efcff162408b15 branch: rails31 specs: @@ -26,12 +16,12 @@ GEM remote: https://rubygems.org/ specs: RedCloth (4.2.9) - actionmailer (3.2.13) - actionpack (= 3.2.13) - mail (~> 2.5.3) - actionpack (3.2.13) - activemodel (= 3.2.13) - activesupport (= 3.2.13) + actionmailer (3.2.16) + actionpack (= 3.2.16) + mail (~> 2.5.4) + actionpack (3.2.16) + activemodel (= 3.2.16) + activesupport (= 3.2.16) builder (~> 3.0.0) erubis (~> 2.7.0) journey (~> 1.0.4) @@ -39,59 +29,66 @@ GEM rack-cache (~> 1.2) rack-test (~> 0.6.1) sprockets (~> 2.2.1) - activemodel (3.2.13) - activesupport (= 3.2.13) + activemodel (3.2.16) + activesupport (= 3.2.16) builder (~> 3.0.0) - activerecord (3.2.13) - activemodel (= 3.2.13) - activesupport (= 3.2.13) + activerecord (3.2.16) + activemodel (= 3.2.16) + activesupport (= 3.2.16) arel (~> 3.0.2) tzinfo (~> 0.3.29) - activeresource (3.2.13) - activemodel (= 3.2.13) - activesupport (= 3.2.13) - activesupport (3.2.13) - i18n (= 0.6.1) + activeresource (3.2.16) + activemodel (= 3.2.16) + activesupport (= 3.2.16) + activesupport (3.2.16) + i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) - airbrake (3.1.9) + airbrake (3.1.12) activesupport builder json ancestry (1.3.0) activerecord (>= 2.3.14) - arel (3.0.2) + angular-i18n (0.1.2) + angularjs-rails (1.0.7) + arel (3.0.3) attr_encrypted (1.2.1) encryptor (>= 1.1.1) - bcrypt-ruby (3.0.1) + bcrypt-ruby (3.1.1) + better_errors (0.9.0) + coderay (>= 1.0.0) + erubis (>= 2.6.6) + binding_of_caller (0.7.2) + debug_inspector (>= 0.0.1) blankslate (3.1.2) - bluepill (0.0.62) - activesupport (>= 3.0.0) + bluepill (0.0.66) + activesupport (>= 3.0.0, < 4.0.0) daemons (~> 1.1.4) i18n (>= 0.5.0) - state_machine (~> 1.1.0) - bourne (1.4.0) - mocha (~> 0.13.2) + state_machine (~> 1.1) builder (3.0.4) + callsite (0.0.11) cancan (1.6.7) cape (1.7.0) - capistrano (2.14.2) + capistrano (2.15.5) highline net-scp (>= 1.0.0) net-sftp (>= 2.0.0) net-ssh (>= 2.0.14) net-ssh-gateway (>= 1.1.0) capistrano_colors (0.5.5) - charlock_holmes (0.6.9.3) + charlock_holmes (0.6.9.4) chronic (0.6.7) chunky_png (1.2.8) cocaine (0.4.2) + coderay (1.0.9) coffee-rails (3.2.2) coffee-script (>= 2.2.0) railties (~> 3.2.0) coffee-script (2.2.0) coffee-script-source execjs - coffee-script-source (1.6.2) + coffee-script-source (1.6.3) compass (0.12.2) chunky_png (~> 1.2) fssm (>= 0.2.7) @@ -100,14 +97,14 @@ GEM compass (>= 0.12.2, < 0.14) creole (0.5.0) daemons (1.1.9) - devise (2.1.3) + debug_inspector (0.0.2) + devise (2.2.4) bcrypt-ruby (~> 3.0) orm_adapter (~> 0.1) railties (~> 3.1) warden (~> 1.2.1) diff-display (0.0.1) diff-lcs (1.1.3) - ejs (1.0.0) encryptor (1.1.3) erubis (2.7.0) escape_utils (0.2.4) @@ -120,15 +117,26 @@ GEM factory_girl_rails (4.0.0) factory_girl (~> 4.0.0) railties (>= 3.0.0) + faraday (0.8.7) + multipart-post (~> 1.1) ffi (1.0.11) + friendly_id (4.0.10.1) + activerecord (>= 3.0, < 4.0) fssm (0.2.10) - github-linguist (2.2.1) + gemoji (1.2.1) + github-linguist (2.3.4) charlock_holmes (~> 0.6.6) escape_utils (~> 0.2.3) - mime-types (~> 1.18) + mime-types (~> 1.19) pygments.rb (>= 0.2.13) github-markdown (0.5.3) github-markup (0.7.5) + gitlab-grit (2.6.3) + charlock_holmes (~> 0.6.9) + diff-lcs (~> 1.1) + mime-types (~> 1.15) + posix-spawn (~> 0.3.6) + gli (2.8.1) gollum (2.1.10) github-markdown github-markup (>= 0.7.0, < 1.0.0) @@ -141,33 +149,48 @@ GEM sinatra (~> 1.0) stringex (~> 1.4.0) useragent (~> 0.4.9) + grit (2.5.0) + diff-lcs (~> 1.1) + mime-types (~> 1.15) + posix-spawn (~> 0.3.6) haml (3.1.8) haml-rails (0.3.5) actionpack (>= 3.1, < 4.1) activesupport (>= 3.1, < 4.1) haml (~> 3.1) railties (>= 3.1, < 4.1) - hashie (1.2.0) - highline (1.6.16) - hike (1.2.1) + hashie (2.0.5) + highline (1.6.19) + hike (1.2.3) hirb (0.7.1) - i18n (0.6.1) - jbuilder (0.8.3) + httpauth (0.2.0) + i18n (0.6.9) + jbuilder (1.4.2) activesupport (>= 3.0.0) + multi_json (>= 1.2.0) journey (1.0.4) jquery-rails (2.0.3) railties (>= 3.1.0, < 5.0) thor (~> 0.14) - json (1.7.7) - kgio (2.8.0) - libv8 (3.3.10.4) + js-routes (0.9.3) + rails (>= 3.2) + json (1.8.1) + jwt (0.1.8) + multi_json (>= 1.5) + localeapp (0.6.14) + gli + i18n + json + rack + rest-client + ya2yaml + logglier (0.2.11) macaddr (1.6.1) systemu (~> 2.5.0) - mail (2.5.3) - i18n (>= 0.4.0) + mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) - mailcatcher (0.5.11) + mailcatcher (0.5.12) activesupport (~> 3.0) eventmachine (~> 1.0.0) haml (>= 3.1, < 5) @@ -178,28 +201,58 @@ GEM thin (~> 1.5.0) meta-tags (1.2.6) actionpack - metaclass (0.0.1) - mime-types (1.22) - mocha (0.13.3) - metaclass (~> 0.0.1) + meta_request (0.2.7) + callsite + rack-contrib + railties + mime-types (1.25.1) + mini_portile (0.5.1) mock_redis (0.6.2) - multi_json (1.7.2) + momentjs-rails (2.0.0.2) + railties (>= 3.1) + mono_logger (1.1.0) + multi_json (1.8.2) + multipart-post (1.2.0) mustache (0.99.4) - net-scp (1.1.0) + nest (1.1.2) + redis + net-scp (1.1.2) net-ssh (>= 2.6.5) - net-sftp (2.1.1) + net-sftp (2.1.2) net-ssh (>= 2.6.5) - net-ssh (2.6.6) + net-ssh (2.6.8) net-ssh-gateway (1.2.0) net-ssh (>= 2.6.5) - newrelic_rpm (3.5.8.72) - nokogiri (1.5.9) - omniauth (1.1.3) - hashie (~> 1.2) + newrelic_rpm (3.7.0.177) + ng-rails-csrf (0.1.0) + nokogiri (1.6.0) + mini_portile (~> 0.5.0) + oauth2 (0.8.1) + faraday (~> 0.8) + httpauth (~> 0.1) + jwt (~> 0.1.4) + multi_json (~> 1.0) + rack (~> 1.2) + ohm (1.3.2) + nest (~> 1.0) + redis + scrivener (~> 0.0.3) + ohm-expire (0.1.3.2) + ohm (>= 0.1.5) + omniauth (1.1.4) + hashie (>= 1.2, < 3) rack - omniauth-openid (1.0.1) + omniauth-facebook (1.4.1) + omniauth-oauth2 (~> 1.1.0) + omniauth-github (1.1.1) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.1) + omniauth-google-oauth2 (0.2.0) + omniauth (~> 1.0) + omniauth-oauth2 + omniauth-oauth2 (1.1.1) + oauth2 (~> 0.8.0) omniauth (~> 1.0) - rack-openid (~> 1.3.1) orm_adapter (0.4.0) paperclip (3.3.1) activemodel (>= 3.0.0) @@ -214,14 +267,15 @@ GEM pg (0.14.1) polyglot (0.3.3) posix-spawn (0.3.6) + puma (2.7.1) + rack (>= 1.1, < 2.0) pygments.rb (0.2.13) rubypython (~> 0.5.3) rack (1.4.5) rack-cache (1.2) rack (>= 0.4) - rack-openid (1.3.1) - rack (>= 1.1.0) - ruby-openid (>= 2.1.8) + rack-contrib (1.1.0) + rack (>= 0.9.1) rack-protection (1.5.0) rack rack-ssl (1.3.3) @@ -230,52 +284,64 @@ GEM rack (>= 1.0) rack-throttle (0.3.0) rack (>= 1.0.0) - rails (3.2.13) - actionmailer (= 3.2.13) - actionpack (= 3.2.13) - activerecord (= 3.2.13) - activeresource (= 3.2.13) - activesupport (= 3.2.13) + rails (3.2.16) + actionmailer (= 3.2.16) + actionpack (= 3.2.16) + activerecord (= 3.2.16) + activeresource (= 3.2.16) + activesupport (= 3.2.16) bundler (~> 1.0) - railties (= 3.2.13) - rails-backbone (0.7.2) - coffee-script (~> 2.2.0) - ejs (~> 1.0.0) - railties (>= 3.1.0) + railties (= 3.2.16) rails3-generators (1.0.0) railties (>= 3.0.0) rails3-jquery-autocomplete (1.0.11) rails (~> 3.0) - railties (3.2.13) - actionpack (= 3.2.13) - activesupport (= 3.2.13) + railties (3.2.16) + actionpack (= 3.2.16) + activesupport (= 3.2.16) rack-ssl (~> 1.3.2) rake (>= 0.8.7) rdoc (~> 3.4) thor (>= 0.14.6, < 2.0) - raindrops (0.10.0) - rake (10.0.4) - rdiscount (2.0.7.1) + rake (10.1.0) + rdiscount (2.1.6) rdoc (3.12.2) json (~> 1.4) - redcarpet (2.1.1) - redis (3.0.2) - redis-namespace (1.2.1) + redcarpet (2.2.2) + redis (3.0.4) + redis-actionpack (3.2.3) + actionpack (~> 3.2.3) + redis-rack (~> 1.4.0) + redis-store (~> 1.1.0) + redis-activesupport (3.2.3) + activesupport (~> 3.2.3) + redis-store (~> 1.1.0) + redis-namespace (1.3.0) redis (~> 3.0.0) + redis-rack (1.4.2) + rack (~> 1.4.1) + redis-store (~> 1.1.0) + redis-rails (3.2.3) + redis-actionpack (~> 3.2.3) + redis-activesupport (~> 3.2.3) + redis-store (~> 1.1.0) + redis-store (1.1.3) + redis (>= 2.2.0) redisk (0.2.2) redis (>= 0.1.1) redis-namespace (>= 0.1.0) - resque (1.21.0) + resque (1.24.1) + mono_logger (~> 1.0) multi_json (~> 1.0) - redis-namespace (~> 1.0) + redis-namespace (~> 1.2) sinatra (>= 0.9.2) vegas (~> 0.1.2) resque-status (0.3.3) redisk (>= 0.2.1) resque (~> 1.19) uuid (~> 2.3) - resque_mailer (2.1.0) - actionmailer (~> 3.0) + resque_mailer (2.2.4) + actionmailer (>= 3.0) rest-client (1.6.7) mime-types (>= 1.16) rr (1.0.5) @@ -292,77 +358,75 @@ GEM activesupport (>= 3.0) railties (>= 3.0) rspec (~> 2.11.0) - ruby-haml-js (0.0.3) + ruby-haml-js (0.0.4) execjs sprockets (>= 2.0.0) - ruby-openid (2.2.3) rubypython (0.5.3) blankslate (>= 2.1.2.3) ffi (~> 1.0.7) russian (0.6.0) i18n (>= 0.5.0) - rvm-capistrano (1.2.7) + rvm-capistrano (1.4.1) capistrano (>= 2.0.0) - sanitize (2.0.3) - nokogiri (>= 1.4.4, < 1.6) - sass (3.2.7) + sanitize (2.0.6) + nokogiri (>= 1.4.4) + sass (3.2.9) sass-rails (3.2.6) railties (~> 3.2.0) sass (>= 3.1.10) tilt (~> 1.3) + scrivener (0.0.3) shotgun (0.9) rack (>= 1.0) - shoulda (3.4.0) + shoulda (3.5.0) shoulda-context (~> 1.0, >= 1.0.1) - shoulda-matchers (~> 1.0, >= 1.4.1) - shoulda-context (1.1.0) - shoulda-matchers (1.5.6) + shoulda-matchers (>= 1.4.1, < 3.0) + shoulda-context (1.1.4) + shoulda-matchers (2.2.0) activesupport (>= 3.0.0) - bourne (~> 1.3) - sinatra (1.3.6) + sinatra (1.4.3) rack (~> 1.4) - rack-protection (~> 1.3) - tilt (~> 1.3, >= 1.3.3) + rack-protection (~> 1.4) + tilt (~> 1.3, >= 1.3.4) skinny (0.2.3) eventmachine (~> 1.0.0) thin (~> 1.5.0) + soundmanager-rails (0.1.5) sprockets (2.2.2) hike (~> 1.2) multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) sqlite3 (1.3.7) - state_machine (1.1.2) + state_machine (1.2.0) stringex (1.4.0) systemu (2.5.2) - therubyracer (0.10.2) - libv8 (~> 3.3.10) thin (1.5.1) daemons (>= 1.0.9) eventmachine (>= 0.12.6) rack (>= 1.0.0) thor (0.18.1) - tilt (1.3.6) - treetop (1.4.12) + tilt (1.4.1) + time_diff (0.3.0) + activesupport + i18n + treetop (1.4.15) polyglot polyglot (>= 0.3.1) turbo-sprockets-rails3 (0.3.6) railties (> 3.2.8, < 4.0.0) sprockets (>= 2.0.0) - tzinfo (0.3.37) + tzinfo (0.3.38) uglifier (1.2.7) execjs (>= 0.3.0) multi_json (~> 1.3) - unicorn (4.3.1) - kgio (~> 2.6) - rack - raindrops (~> 0.7) + underscore-rails (1.5.1) useragent (0.4.16) uuid (2.3.7) macaddr (~> 1.0) vegas (0.1.11) rack (>= 1.0.0) - warden (1.2.1) + warden (1.2.3) rack (>= 1.0) whenever (0.7.3) activesupport (>= 2.3.4) @@ -371,6 +435,7 @@ GEM builder expression_parser will_paginate (3.0.4) + ya2yaml (0.31) PLATFORMS ruby @@ -379,7 +444,11 @@ DEPENDENCIES RedCloth airbrake (~> 3.1.2) ancestry (~> 1.3.0) + angular-i18n (= 0.1.2) + angularjs-rails attr_encrypted (= 1.2.1) + better_errors + binding_of_caller bluepill (~> 0.0.60) cancan (= 1.6.7) cape @@ -389,38 +458,53 @@ DEPENDENCIES coffee-rails (~> 3.2.2) compass-rails (~> 1.0.3) creole - devise (~> 2.1.2) + devise (~> 2.2.3) diff-display (~> 0.0.1) factory_girl_rails (~> 4.0.0) - github-linguist (~> 2.2.1) + ffi + friendly_id + gemoji (~> 1.2.1) + github-linguist (~> 2.3.4) + gitlab-grit (~> 2.6.3) gollum (~> 2.1.3) grack! - grit! haml-rails (~> 0.3.4) highline (~> 1.6.11) hirb - jbuilder (~> 0.8.2) + jbuilder (~> 1.4.2) jquery-rails (~> 2.0.2) + js-routes + localeapp + logglier mailcatcher meta-tags (~> 1.2.5) + meta_request mock_redis (= 0.6.2) - newrelic_rpm (~> 3.5.8.72) - omniauth (~> 1.1.0) - omniauth-openid (~> 1.0.1) + momentjs-rails + newrelic_rpm + ng-rails-csrf + ohm + ohm-expire + omniauth + omniauth-facebook + omniauth-github + omniauth-google-oauth2 paperclip (~> 3.3.1) perform_later (~> 1.3.0) pg (~> 0.14.0) + puma rack-throttle - rails (= 3.2.13) - rails-backbone (~> 0.7.2) + rails (= 3.2.16) rails3-generators rails3-jquery-autocomplete (~> 1.0.7) + rake rdiscount - redcarpet (~> 2.1.1) + redcarpet (~> 2.2.2) redhillonrails_core! - resque (~> 1.21.0) + redis-rails + resque (~> 1.24) resque-status (~> 0.3.3) - resque_mailer (~> 2.1.0) + resque_mailer (~> 2.2) rest-client (~> 1.6.6) rr (~> 1.0.4) rspec-rails (~> 2.11.0) @@ -430,13 +514,14 @@ DEPENDENCIES sass-rails (~> 3.2.5) shotgun shoulda + soundmanager-rails state_machine - therubyracer (~> 0.10.2) therubyrhino (~> 1.73.1) + time_diff trinidad (~> 1.0.2) turbo-sprockets-rails3 uglifier (~> 1.2.4) - unicorn (~> 4.3.1) + underscore-rails whenever (~> 0.7.3) wikicloth will_paginate (~> 3.0.3) diff --git a/LICENSE.txt b/LICENSE.txt index 908fc5f26..dcbd52435 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -2,8 +2,6 @@ ROSA ABF is Copyright 2010-2013 ROSA CJSC. ROSA CJSC ("ROSA") distributes the ABF source code under the GNU General Public License (GPL), version 2.0 or later. -The image and icon files in ABF are copyright ROSA CJSC ("ROSA"), but unlike the source code they are not licensed under the GPL version 2.0 or later. ROSA grants you the right to use them for testing and development purposes only, but not to use them in production (commercially or non-commercially). - Third-party copyright in this distribution is noted where applicable. All rights not expressly granted are reserved. \ No newline at end of file diff --git a/README b/README deleted file mode 100644 index fe7013d52..000000000 --- a/README +++ /dev/null @@ -1,256 +0,0 @@ -== Welcome to Rails - -Rails is a web-application framework that includes everything needed to create -database-backed web applications according to the Model-View-Control pattern. - -This pattern splits the view (also called the presentation) into "dumb" -templates that are primarily responsible for inserting pre-built data in between -HTML tags. The model contains the "smart" domain objects (such as Account, -Product, Person, Post) that holds all the business logic and knows how to -persist themselves to a database. The controller handles the incoming requests -(such as Save New Account, Update Product, Show Post) by manipulating the model -and directing data to the view. - -In Rails, the model is handled by what's called an object-relational mapping -layer entitled Active Record. This layer allows you to present the data from -database rows as objects and embellish these data objects with business logic -methods. You can read more about Active Record in -link:files/vendor/rails/activerecord/README.html. - -The controller and view are handled by the Action Pack, which handles both -layers by its two parts: Action View and Action Controller. These two layers -are bundled in a single package due to their heavy interdependence. This is -unlike the relationship between the Active Record and Action Pack that is much -more separate. Each of these packages can be used independently outside of -Rails. You can read more about Action Pack in -link:files/vendor/rails/actionpack/README.html. - - -== Getting Started - -1. At the command prompt, create a new Rails application: - rails new myapp (where myapp is the application name) - -2. Change directory to myapp and start the web server: - cd myapp; rails server (run with --help for options) - -3. Go to http://localhost:3000/ and you'll see: - "Welcome aboard: You're riding Ruby on Rails!" - -4. Follow the guidelines to start developing your application. You can find -the following resources handy: - -* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html -* Ruby on Rails Tutorial Book: http://www.railstutorial.org/ - - -== Debugging Rails - -Sometimes your application goes wrong. Fortunately there are a lot of tools that -will help you debug it and get it back on the rails. - -First area to check is the application log files. Have "tail -f" commands -running on the server.log and development.log. Rails will automatically display -debugging and runtime information to these files. Debugging info will also be -shown in the browser on requests from 127.0.0.1. - -You can also log your own messages directly into the log file from your code -using the Ruby logger class from inside your controllers. Example: - - class WeblogController < ActionController::Base - def destroy - @weblog = Weblog.find(params[:id]) - @weblog.destroy - logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!") - end - end - -The result will be a message in your log file along the lines of: - - Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1! - -More information on how to use the logger is at http://www.ruby-doc.org/core/ - -Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are -several books available online as well: - -* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe) -* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide) - -These two books will bring you up to speed on the Ruby language and also on -programming in general. - - -== Debugger - -Debugger support is available through the debugger command when you start your -Mongrel or WEBrick server with --debugger. This means that you can break out of -execution at any point in the code, investigate and change the model, and then, -resume execution! You need to install ruby-debug to run the server in debugging -mode. With gems, use sudo gem install ruby-debug. Example: - - class WeblogController < ActionController::Base - def index - @posts = Post.find(:all) - debugger - end - end - -So the controller will accept the action, run the first line, then present you -with a IRB prompt in the server window. Here you can do things like: - - >> @posts.inspect - => "[#nil, "body"=>nil, "id"=>"1"}>, - #"Rails", "body"=>"Only ten..", "id"=>"2"}>]" - >> @posts.first.title = "hello from a debugger" - => "hello from a debugger" - -...and even better, you can examine how your runtime objects actually work: - - >> f = @posts.first - => #nil, "body"=>nil, "id"=>"1"}> - >> f. - Display all 152 possibilities? (y or n) - -Finally, when you're ready to resume execution, you can enter "cont". - - -== Console - -The console is a Ruby shell, which allows you to interact with your -application's domain model. Here you'll have all parts of the application -configured, just like it is when the application is running. You can inspect -domain models, change values, and save to the database. Starting the script -without arguments will launch it in the development environment. - -To start the console, run rails console from the application -directory. - -Options: - -* Passing the -s, --sandbox argument will rollback any modifications - made to the database. -* Passing an environment name as an argument will load the corresponding - environment. Example: rails console production. - -To reload your controllers and models after launching the console run -reload! - -More information about irb can be found at: -link:http://www.rubycentral.com/pickaxe/irb.html - - -== dbconsole - -You can go to the command line of your database directly through rails -dbconsole. You would be connected to the database with the credentials -defined in database.yml. Starting the script without arguments will connect you -to the development database. Passing an argument will connect you to a different -database, like rails dbconsole production. Currently works for MySQL, -PostgreSQL and SQLite 3. - -== Description of Contents - -The default directory structure of a generated Ruby on Rails application: - - |-- app - | |-- controllers - | |-- helpers - | |-- mailers - | |-- models - | `-- views - | `-- layouts - |-- config - | |-- environments - | |-- initializers - | `-- locales - |-- db - |-- doc - |-- lib - | `-- tasks - |-- log - |-- public - | |-- images - | |-- javascripts - | `-- stylesheets - |-- script - |-- test - | |-- fixtures - | |-- functional - | |-- integration - | |-- performance - | `-- unit - |-- tmp - | |-- cache - | |-- pids - | |-- sessions - | `-- sockets - `-- vendor - `-- plugins - -app - Holds all the code that's specific to this particular application. - -app/controllers - Holds controllers that should be named like weblogs_controller.rb for - automated URL mapping. All controllers should descend from - ApplicationController which itself descends from ActionController::Base. - -app/models - Holds models that should be named like post.rb. Models descend from - ActiveRecord::Base by default. - -app/views - Holds the template files for the view that should be named like - weblogs/index.html.erb for the WeblogsController#index action. All views use - eRuby syntax by default. - -app/views/layouts - Holds the template files for layouts to be used with views. This models the - common header/footer method of wrapping views. In your views, define a layout - using the layout :default and create a file named default.html.erb. - Inside default.html.erb, call <% yield %> to render the view using this - layout. - -app/helpers - Holds view helpers that should be named like weblogs_helper.rb. These are - generated for you automatically when using generators for controllers. - Helpers can be used to wrap functionality for your views into methods. - -config - Configuration files for the Rails environment, the routing map, the database, - and other dependencies. - -db - Contains the database schema in schema.rb. db/migrate contains all the - sequence of Migrations for your schema. - -doc - This directory is where your application documentation will be stored when - generated using rake doc:app - -lib - Application specific libraries. Basically, any kind of custom code that - doesn't belong under controllers, models, or helpers. This directory is in - the load path. - -public - The directory available for the web server. Contains subdirectories for - images, stylesheets, and javascripts. Also contains the dispatchers and the - default HTML files. This should be set as the DOCUMENT_ROOT of your web - server. - -script - Helper scripts for automation and generation. - -test - Unit and functional tests along with fixtures. When using the rails generate - command, template test files will be generated for you and placed in this - directory. - -vendor - External libraries that the application depends on. Also includes the plugins - subdirectory. If the app has frozen rails, those gems also go here, under - vendor/rails/. This directory is in the load path. diff --git a/README.md b/README.md new file mode 100644 index 000000000..ebaec7479 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +RosaLab ABF +=========== + +A distributed environment to build distributions, supporting all steps from managing source code to creating ISO images. If you have any problems or requests please contact +[support](https://abf.rosalinux.ru/contact). + +**Note: This Documentation is in a beta state. Breaking changes may occur.** + +[![Build Status](https://travis-ci.org/rosa-abf/rosa-build.png?branch=master)](https://travis-ci.org/rosa-abf/rosa-build) + +* [API](http://abf-doc.rosalinux.ru/abf/api/) +* [Integration with FileStore (.abf.yml)](http://abf-doc.rosalinux.ru/abf/file_store_integration/) +* [ISO build environment](http://abf-doc.rosalinux.ru/abf/iso_build/) +* [Package build environment](http://abf-doc.rosalinux.ru/abf/scripts/) +* [Deployment](http://abf-doc.rosalinux.ru/abf/deployment/) + +## Contributing to rosa-build + +A ruby translation project managed on [Locale](http://www.localeapp.com/) that's open to all! + +- Edit the translations directly on the [rosa-build](http://www.localeapp.com/projects/public?search=rosa-build) project on Locale. +- **That's it!** +- The maintainer will then pull translations from the Locale project and push to Github. + +Happy translating! diff --git a/app/assets/images/bg_blue.png b/app/assets/images/bg_blue.png new file mode 100644 index 000000000..ecfede360 Binary files /dev/null and b/app/assets/images/bg_blue.png differ diff --git a/app/assets/images/emoji/+1.png b/app/assets/images/emoji/+1.png new file mode 100644 index 000000000..3a43ecae2 Binary files /dev/null and b/app/assets/images/emoji/+1.png differ diff --git a/app/assets/images/emoji/-1.png b/app/assets/images/emoji/-1.png new file mode 100644 index 000000000..e44c04219 Binary files /dev/null and b/app/assets/images/emoji/-1.png differ diff --git a/app/assets/images/emoji/100.png b/app/assets/images/emoji/100.png new file mode 100644 index 000000000..bce9ab14f Binary files /dev/null and b/app/assets/images/emoji/100.png differ diff --git a/app/assets/images/emoji/1234.png b/app/assets/images/emoji/1234.png new file mode 100644 index 000000000..c47c2e1f9 Binary files /dev/null and b/app/assets/images/emoji/1234.png differ diff --git a/app/assets/images/emoji/8ball.png b/app/assets/images/emoji/8ball.png new file mode 100644 index 000000000..c2c710d45 Binary files /dev/null and b/app/assets/images/emoji/8ball.png differ diff --git a/app/assets/images/emoji/a.png b/app/assets/images/emoji/a.png new file mode 100644 index 000000000..4908a44fc Binary files /dev/null and b/app/assets/images/emoji/a.png differ diff --git a/app/assets/images/emoji/ab.png b/app/assets/images/emoji/ab.png new file mode 100644 index 000000000..2a5222047 Binary files /dev/null and b/app/assets/images/emoji/ab.png differ diff --git a/app/assets/images/emoji/abc.png b/app/assets/images/emoji/abc.png new file mode 100644 index 000000000..505d40a15 Binary files /dev/null and b/app/assets/images/emoji/abc.png differ diff --git a/app/assets/images/emoji/abcd.png b/app/assets/images/emoji/abcd.png new file mode 100644 index 000000000..5218470b6 Binary files /dev/null and b/app/assets/images/emoji/abcd.png differ diff --git a/app/assets/images/emoji/accept.png b/app/assets/images/emoji/accept.png new file mode 100644 index 000000000..2d2009031 Binary files /dev/null and b/app/assets/images/emoji/accept.png differ diff --git a/app/assets/images/emoji/aerial_tramway.png b/app/assets/images/emoji/aerial_tramway.png new file mode 100644 index 000000000..38f6dfe23 Binary files /dev/null and b/app/assets/images/emoji/aerial_tramway.png differ diff --git a/app/assets/images/emoji/airplane.png b/app/assets/images/emoji/airplane.png new file mode 100644 index 000000000..8407cb675 Binary files /dev/null and b/app/assets/images/emoji/airplane.png differ diff --git a/app/assets/images/emoji/alarm_clock.png b/app/assets/images/emoji/alarm_clock.png new file mode 100644 index 000000000..86ca8c8ed Binary files /dev/null and b/app/assets/images/emoji/alarm_clock.png differ diff --git a/app/assets/images/emoji/alien.png b/app/assets/images/emoji/alien.png new file mode 100644 index 000000000..e3fd76a78 Binary files /dev/null and b/app/assets/images/emoji/alien.png differ diff --git a/app/assets/images/emoji/ambulance.png b/app/assets/images/emoji/ambulance.png new file mode 100644 index 000000000..b740f45db Binary files /dev/null and b/app/assets/images/emoji/ambulance.png differ diff --git a/app/assets/images/emoji/anchor.png b/app/assets/images/emoji/anchor.png new file mode 100644 index 000000000..0c5192e64 Binary files /dev/null and b/app/assets/images/emoji/anchor.png differ diff --git a/app/assets/images/emoji/angel.png b/app/assets/images/emoji/angel.png new file mode 100644 index 000000000..da52c310c Binary files /dev/null and b/app/assets/images/emoji/angel.png differ diff --git a/app/assets/images/emoji/anger.png b/app/assets/images/emoji/anger.png new file mode 100644 index 000000000..6fb4dca18 Binary files /dev/null and b/app/assets/images/emoji/anger.png differ diff --git a/app/assets/images/emoji/angry.png b/app/assets/images/emoji/angry.png new file mode 100644 index 000000000..34174f5e5 Binary files /dev/null and b/app/assets/images/emoji/angry.png differ diff --git a/app/assets/images/emoji/anguished.png b/app/assets/images/emoji/anguished.png new file mode 100644 index 000000000..c2edad796 Binary files /dev/null and b/app/assets/images/emoji/anguished.png differ diff --git a/app/assets/images/emoji/ant.png b/app/assets/images/emoji/ant.png new file mode 100644 index 000000000..b92d1cc14 Binary files /dev/null and b/app/assets/images/emoji/ant.png differ diff --git a/app/assets/images/emoji/apple.png b/app/assets/images/emoji/apple.png new file mode 100644 index 000000000..08aa17b95 Binary files /dev/null and b/app/assets/images/emoji/apple.png differ diff --git a/app/assets/images/emoji/aquarius.png b/app/assets/images/emoji/aquarius.png new file mode 100644 index 000000000..cbff66edc Binary files /dev/null and b/app/assets/images/emoji/aquarius.png differ diff --git a/app/assets/images/emoji/aries.png b/app/assets/images/emoji/aries.png new file mode 100644 index 000000000..d676fd392 Binary files /dev/null and b/app/assets/images/emoji/aries.png differ diff --git a/app/assets/images/emoji/arrow_backward.png b/app/assets/images/emoji/arrow_backward.png new file mode 100644 index 000000000..2be422ba3 Binary files /dev/null and b/app/assets/images/emoji/arrow_backward.png differ diff --git a/app/assets/images/emoji/arrow_double_down.png b/app/assets/images/emoji/arrow_double_down.png new file mode 100644 index 000000000..2ecbebcda Binary files /dev/null and b/app/assets/images/emoji/arrow_double_down.png differ diff --git a/app/assets/images/emoji/arrow_double_up.png b/app/assets/images/emoji/arrow_double_up.png new file mode 100644 index 000000000..d42979d4b Binary files /dev/null and b/app/assets/images/emoji/arrow_double_up.png differ diff --git a/app/assets/images/emoji/arrow_down.png b/app/assets/images/emoji/arrow_down.png new file mode 100644 index 000000000..3956eb399 Binary files /dev/null and b/app/assets/images/emoji/arrow_down.png differ diff --git a/app/assets/images/emoji/arrow_down_small.png b/app/assets/images/emoji/arrow_down_small.png new file mode 100644 index 000000000..f7f2d5101 Binary files /dev/null and b/app/assets/images/emoji/arrow_down_small.png differ diff --git a/app/assets/images/emoji/arrow_forward.png b/app/assets/images/emoji/arrow_forward.png new file mode 100644 index 000000000..fbfe711b6 Binary files /dev/null and b/app/assets/images/emoji/arrow_forward.png differ diff --git a/app/assets/images/emoji/arrow_heading_down.png b/app/assets/images/emoji/arrow_heading_down.png new file mode 100644 index 000000000..56dd3b9d3 Binary files /dev/null and b/app/assets/images/emoji/arrow_heading_down.png differ diff --git a/app/assets/images/emoji/arrow_heading_up.png b/app/assets/images/emoji/arrow_heading_up.png new file mode 100644 index 000000000..c8f670a1e Binary files /dev/null and b/app/assets/images/emoji/arrow_heading_up.png differ diff --git a/app/assets/images/emoji/arrow_left.png b/app/assets/images/emoji/arrow_left.png new file mode 100644 index 000000000..9d7d1b568 Binary files /dev/null and b/app/assets/images/emoji/arrow_left.png differ diff --git a/app/assets/images/emoji/arrow_lower_left.png b/app/assets/images/emoji/arrow_lower_left.png new file mode 100644 index 000000000..a4438cb6e Binary files /dev/null and b/app/assets/images/emoji/arrow_lower_left.png differ diff --git a/app/assets/images/emoji/arrow_lower_right.png b/app/assets/images/emoji/arrow_lower_right.png new file mode 100644 index 000000000..2a15cc7cc Binary files /dev/null and b/app/assets/images/emoji/arrow_lower_right.png differ diff --git a/app/assets/images/emoji/arrow_right.png b/app/assets/images/emoji/arrow_right.png new file mode 100644 index 000000000..e5cca853d Binary files /dev/null and b/app/assets/images/emoji/arrow_right.png differ diff --git a/app/assets/images/emoji/arrow_right_hook.png b/app/assets/images/emoji/arrow_right_hook.png new file mode 100644 index 000000000..8b4ea6e17 Binary files /dev/null and b/app/assets/images/emoji/arrow_right_hook.png differ diff --git a/app/assets/images/emoji/arrow_up.png b/app/assets/images/emoji/arrow_up.png new file mode 100644 index 000000000..565ce2952 Binary files /dev/null and b/app/assets/images/emoji/arrow_up.png differ diff --git a/app/assets/images/emoji/arrow_up_down.png b/app/assets/images/emoji/arrow_up_down.png new file mode 100644 index 000000000..b718c2145 Binary files /dev/null and b/app/assets/images/emoji/arrow_up_down.png differ diff --git a/app/assets/images/emoji/arrow_up_small.png b/app/assets/images/emoji/arrow_up_small.png new file mode 100644 index 000000000..121733197 Binary files /dev/null and b/app/assets/images/emoji/arrow_up_small.png differ diff --git a/app/assets/images/emoji/arrow_upper_left.png b/app/assets/images/emoji/arrow_upper_left.png new file mode 100644 index 000000000..12aebd9a7 Binary files /dev/null and b/app/assets/images/emoji/arrow_upper_left.png differ diff --git a/app/assets/images/emoji/arrow_upper_right.png b/app/assets/images/emoji/arrow_upper_right.png new file mode 100644 index 000000000..0daf4e940 Binary files /dev/null and b/app/assets/images/emoji/arrow_upper_right.png differ diff --git a/app/assets/images/emoji/arrows_clockwise.png b/app/assets/images/emoji/arrows_clockwise.png new file mode 100644 index 000000000..5f84d7e72 Binary files /dev/null and b/app/assets/images/emoji/arrows_clockwise.png differ diff --git a/app/assets/images/emoji/arrows_counterclockwise.png b/app/assets/images/emoji/arrows_counterclockwise.png new file mode 100644 index 000000000..1933ae18b Binary files /dev/null and b/app/assets/images/emoji/arrows_counterclockwise.png differ diff --git a/app/assets/images/emoji/art.png b/app/assets/images/emoji/art.png new file mode 100644 index 000000000..d45212b03 Binary files /dev/null and b/app/assets/images/emoji/art.png differ diff --git a/app/assets/images/emoji/articulated_lorry.png b/app/assets/images/emoji/articulated_lorry.png new file mode 100644 index 000000000..81ec1f917 Binary files /dev/null and b/app/assets/images/emoji/articulated_lorry.png differ diff --git a/app/assets/images/emoji/astonished.png b/app/assets/images/emoji/astonished.png new file mode 100644 index 000000000..858a83484 Binary files /dev/null and b/app/assets/images/emoji/astonished.png differ diff --git a/app/assets/images/emoji/athletic_shoe.png b/app/assets/images/emoji/athletic_shoe.png new file mode 100644 index 000000000..45b82e61c Binary files /dev/null and b/app/assets/images/emoji/athletic_shoe.png differ diff --git a/app/assets/images/emoji/atm.png b/app/assets/images/emoji/atm.png new file mode 100644 index 000000000..c2846e792 Binary files /dev/null and b/app/assets/images/emoji/atm.png differ diff --git a/app/assets/images/emoji/b.png b/app/assets/images/emoji/b.png new file mode 100644 index 000000000..8742b3d2e Binary files /dev/null and b/app/assets/images/emoji/b.png differ diff --git a/app/assets/images/emoji/baby.png b/app/assets/images/emoji/baby.png new file mode 100644 index 000000000..3b29da40b Binary files /dev/null and b/app/assets/images/emoji/baby.png differ diff --git a/app/assets/images/emoji/baby_bottle.png b/app/assets/images/emoji/baby_bottle.png new file mode 100644 index 000000000..1b2cfe5e3 Binary files /dev/null and b/app/assets/images/emoji/baby_bottle.png differ diff --git a/app/assets/images/emoji/baby_chick.png b/app/assets/images/emoji/baby_chick.png new file mode 100644 index 000000000..9be8d2930 Binary files /dev/null and b/app/assets/images/emoji/baby_chick.png differ diff --git a/app/assets/images/emoji/baby_symbol.png b/app/assets/images/emoji/baby_symbol.png new file mode 100644 index 000000000..2e58725cf Binary files /dev/null and b/app/assets/images/emoji/baby_symbol.png differ diff --git a/app/assets/images/emoji/back.png b/app/assets/images/emoji/back.png new file mode 100644 index 000000000..0cde62876 Binary files /dev/null and b/app/assets/images/emoji/back.png differ diff --git a/app/assets/images/emoji/baggage_claim.png b/app/assets/images/emoji/baggage_claim.png new file mode 100644 index 000000000..59ae044a4 Binary files /dev/null and b/app/assets/images/emoji/baggage_claim.png differ diff --git a/app/assets/images/emoji/balloon.png b/app/assets/images/emoji/balloon.png new file mode 100644 index 000000000..a4d3207b8 Binary files /dev/null and b/app/assets/images/emoji/balloon.png differ diff --git a/app/assets/images/emoji/ballot_box_with_check.png b/app/assets/images/emoji/ballot_box_with_check.png new file mode 100644 index 000000000..f07a466c7 Binary files /dev/null and b/app/assets/images/emoji/ballot_box_with_check.png differ diff --git a/app/assets/images/emoji/bamboo.png b/app/assets/images/emoji/bamboo.png new file mode 100644 index 000000000..fc858d0fc Binary files /dev/null and b/app/assets/images/emoji/bamboo.png differ diff --git a/app/assets/images/emoji/banana.png b/app/assets/images/emoji/banana.png new file mode 100644 index 000000000..a0563afb9 Binary files /dev/null and b/app/assets/images/emoji/banana.png differ diff --git a/app/assets/images/emoji/bangbang.png b/app/assets/images/emoji/bangbang.png new file mode 100644 index 000000000..7270f0afe Binary files /dev/null and b/app/assets/images/emoji/bangbang.png differ diff --git a/app/assets/images/emoji/bank.png b/app/assets/images/emoji/bank.png new file mode 100644 index 000000000..1faa8777e Binary files /dev/null and b/app/assets/images/emoji/bank.png differ diff --git a/app/assets/images/emoji/bar_chart.png b/app/assets/images/emoji/bar_chart.png new file mode 100644 index 000000000..7871cc603 Binary files /dev/null and b/app/assets/images/emoji/bar_chart.png differ diff --git a/app/assets/images/emoji/barber.png b/app/assets/images/emoji/barber.png new file mode 100644 index 000000000..a10cb2322 Binary files /dev/null and b/app/assets/images/emoji/barber.png differ diff --git a/app/assets/images/emoji/baseball.png b/app/assets/images/emoji/baseball.png new file mode 100644 index 000000000..da004e2ea Binary files /dev/null and b/app/assets/images/emoji/baseball.png differ diff --git a/app/assets/images/emoji/basketball.png b/app/assets/images/emoji/basketball.png new file mode 100644 index 000000000..ef694bec4 Binary files /dev/null and b/app/assets/images/emoji/basketball.png differ diff --git a/app/assets/images/emoji/bath.png b/app/assets/images/emoji/bath.png new file mode 100644 index 000000000..8f75d1d24 Binary files /dev/null and b/app/assets/images/emoji/bath.png differ diff --git a/app/assets/images/emoji/bathtub.png b/app/assets/images/emoji/bathtub.png new file mode 100644 index 000000000..1c3f844ab Binary files /dev/null and b/app/assets/images/emoji/bathtub.png differ diff --git a/app/assets/images/emoji/battery.png b/app/assets/images/emoji/battery.png new file mode 100644 index 000000000..aa7eedce4 Binary files /dev/null and b/app/assets/images/emoji/battery.png differ diff --git a/app/assets/images/emoji/bear.png b/app/assets/images/emoji/bear.png new file mode 100644 index 000000000..f5afe920e Binary files /dev/null and b/app/assets/images/emoji/bear.png differ diff --git a/app/assets/images/emoji/bee.png b/app/assets/images/emoji/bee.png new file mode 100644 index 000000000..f53733953 Binary files /dev/null and b/app/assets/images/emoji/bee.png differ diff --git a/app/assets/images/emoji/beer.png b/app/assets/images/emoji/beer.png new file mode 100644 index 000000000..cd78bed74 Binary files /dev/null and b/app/assets/images/emoji/beer.png differ diff --git a/app/assets/images/emoji/beers.png b/app/assets/images/emoji/beers.png new file mode 100644 index 000000000..cc5e4ab5a Binary files /dev/null and b/app/assets/images/emoji/beers.png differ diff --git a/app/assets/images/emoji/beetle.png b/app/assets/images/emoji/beetle.png new file mode 100644 index 000000000..222577ca7 Binary files /dev/null and b/app/assets/images/emoji/beetle.png differ diff --git a/app/assets/images/emoji/beginner.png b/app/assets/images/emoji/beginner.png new file mode 100644 index 000000000..1f022d175 Binary files /dev/null and b/app/assets/images/emoji/beginner.png differ diff --git a/app/assets/images/emoji/bell.png b/app/assets/images/emoji/bell.png new file mode 100644 index 000000000..69acceb28 Binary files /dev/null and b/app/assets/images/emoji/bell.png differ diff --git a/app/assets/images/emoji/bento.png b/app/assets/images/emoji/bento.png new file mode 100644 index 000000000..c6d99e89b Binary files /dev/null and b/app/assets/images/emoji/bento.png differ diff --git a/app/assets/images/emoji/bicyclist.png b/app/assets/images/emoji/bicyclist.png new file mode 100644 index 000000000..4e3e0549c Binary files /dev/null and b/app/assets/images/emoji/bicyclist.png differ diff --git a/app/assets/images/emoji/bike.png b/app/assets/images/emoji/bike.png new file mode 100644 index 000000000..657386027 Binary files /dev/null and b/app/assets/images/emoji/bike.png differ diff --git a/app/assets/images/emoji/bikini.png b/app/assets/images/emoji/bikini.png new file mode 100644 index 000000000..4ff63b40f Binary files /dev/null and b/app/assets/images/emoji/bikini.png differ diff --git a/app/assets/images/emoji/bird.png b/app/assets/images/emoji/bird.png new file mode 100644 index 000000000..e6be8c027 Binary files /dev/null and b/app/assets/images/emoji/bird.png differ diff --git a/app/assets/images/emoji/birthday.png b/app/assets/images/emoji/birthday.png new file mode 100644 index 000000000..36e8edcbe Binary files /dev/null and b/app/assets/images/emoji/birthday.png differ diff --git a/app/assets/images/emoji/black_circle.png b/app/assets/images/emoji/black_circle.png new file mode 100644 index 000000000..e46f9df61 Binary files /dev/null and b/app/assets/images/emoji/black_circle.png differ diff --git a/app/assets/images/emoji/black_joker.png b/app/assets/images/emoji/black_joker.png new file mode 100644 index 000000000..4c78f3614 Binary files /dev/null and b/app/assets/images/emoji/black_joker.png differ diff --git a/app/assets/images/emoji/black_large_square.png b/app/assets/images/emoji/black_large_square.png new file mode 100644 index 000000000..71da10de8 Binary files /dev/null and b/app/assets/images/emoji/black_large_square.png differ diff --git a/app/assets/images/emoji/black_medium_small_square.png b/app/assets/images/emoji/black_medium_small_square.png new file mode 100644 index 000000000..25bfe9c45 Binary files /dev/null and b/app/assets/images/emoji/black_medium_small_square.png differ diff --git a/app/assets/images/emoji/black_medium_square.png b/app/assets/images/emoji/black_medium_square.png new file mode 100644 index 000000000..204cce12c Binary files /dev/null and b/app/assets/images/emoji/black_medium_square.png differ diff --git a/app/assets/images/emoji/black_nib.png b/app/assets/images/emoji/black_nib.png new file mode 100644 index 000000000..29f6994c1 Binary files /dev/null and b/app/assets/images/emoji/black_nib.png differ diff --git a/app/assets/images/emoji/black_small_square.png b/app/assets/images/emoji/black_small_square.png new file mode 100644 index 000000000..a247751ec Binary files /dev/null and b/app/assets/images/emoji/black_small_square.png differ diff --git a/app/assets/images/emoji/black_square_button.png b/app/assets/images/emoji/black_square_button.png new file mode 100644 index 000000000..7332e397c Binary files /dev/null and b/app/assets/images/emoji/black_square_button.png differ diff --git a/app/assets/images/emoji/blossom.png b/app/assets/images/emoji/blossom.png new file mode 100644 index 000000000..55a97353b Binary files /dev/null and b/app/assets/images/emoji/blossom.png differ diff --git a/app/assets/images/emoji/blowfish.png b/app/assets/images/emoji/blowfish.png new file mode 100644 index 000000000..a1d47cb7e Binary files /dev/null and b/app/assets/images/emoji/blowfish.png differ diff --git a/app/assets/images/emoji/blue_book.png b/app/assets/images/emoji/blue_book.png new file mode 100644 index 000000000..e2b9e8c79 Binary files /dev/null and b/app/assets/images/emoji/blue_book.png differ diff --git a/app/assets/images/emoji/blue_car.png b/app/assets/images/emoji/blue_car.png new file mode 100644 index 000000000..978291e08 Binary files /dev/null and b/app/assets/images/emoji/blue_car.png differ diff --git a/app/assets/images/emoji/blue_heart.png b/app/assets/images/emoji/blue_heart.png new file mode 100644 index 000000000..baa29b31b Binary files /dev/null and b/app/assets/images/emoji/blue_heart.png differ diff --git a/app/assets/images/emoji/blush.png b/app/assets/images/emoji/blush.png new file mode 100644 index 000000000..1e9021cb6 Binary files /dev/null and b/app/assets/images/emoji/blush.png differ diff --git a/app/assets/images/emoji/boar.png b/app/assets/images/emoji/boar.png new file mode 100644 index 000000000..8196ad4a1 Binary files /dev/null and b/app/assets/images/emoji/boar.png differ diff --git a/app/assets/images/emoji/boat.png b/app/assets/images/emoji/boat.png new file mode 100644 index 000000000..ff656dc62 Binary files /dev/null and b/app/assets/images/emoji/boat.png differ diff --git a/app/assets/images/emoji/bomb.png b/app/assets/images/emoji/bomb.png new file mode 100644 index 000000000..3289787dc Binary files /dev/null and b/app/assets/images/emoji/bomb.png differ diff --git a/app/assets/images/emoji/book.png b/app/assets/images/emoji/book.png new file mode 100644 index 000000000..8b698415c Binary files /dev/null and b/app/assets/images/emoji/book.png differ diff --git a/app/assets/images/emoji/bookmark.png b/app/assets/images/emoji/bookmark.png new file mode 100644 index 000000000..dbee45c60 Binary files /dev/null and b/app/assets/images/emoji/bookmark.png differ diff --git a/app/assets/images/emoji/bookmark_tabs.png b/app/assets/images/emoji/bookmark_tabs.png new file mode 100644 index 000000000..0c4e3bf17 Binary files /dev/null and b/app/assets/images/emoji/bookmark_tabs.png differ diff --git a/app/assets/images/emoji/books.png b/app/assets/images/emoji/books.png new file mode 100644 index 000000000..dca06a1ad Binary files /dev/null and b/app/assets/images/emoji/books.png differ diff --git a/app/assets/images/emoji/boom.png b/app/assets/images/emoji/boom.png new file mode 100644 index 000000000..bddeb8f49 Binary files /dev/null and b/app/assets/images/emoji/boom.png differ diff --git a/app/assets/images/emoji/boot.png b/app/assets/images/emoji/boot.png new file mode 100644 index 000000000..58d0fdbcd Binary files /dev/null and b/app/assets/images/emoji/boot.png differ diff --git a/app/assets/images/emoji/bouquet.png b/app/assets/images/emoji/bouquet.png new file mode 100644 index 000000000..ce637832e Binary files /dev/null and b/app/assets/images/emoji/bouquet.png differ diff --git a/app/assets/images/emoji/bow.png b/app/assets/images/emoji/bow.png new file mode 100644 index 000000000..024cb6104 Binary files /dev/null and b/app/assets/images/emoji/bow.png differ diff --git a/app/assets/images/emoji/bowling.png b/app/assets/images/emoji/bowling.png new file mode 100644 index 000000000..13d8ece2e Binary files /dev/null and b/app/assets/images/emoji/bowling.png differ diff --git a/app/assets/images/emoji/bowtie.png b/app/assets/images/emoji/bowtie.png new file mode 100644 index 000000000..28ff0c787 Binary files /dev/null and b/app/assets/images/emoji/bowtie.png differ diff --git a/app/assets/images/emoji/boy.png b/app/assets/images/emoji/boy.png new file mode 100644 index 000000000..f79f1f298 Binary files /dev/null and b/app/assets/images/emoji/boy.png differ diff --git a/app/assets/images/emoji/bread.png b/app/assets/images/emoji/bread.png new file mode 100644 index 000000000..7e7c63753 Binary files /dev/null and b/app/assets/images/emoji/bread.png differ diff --git a/app/assets/images/emoji/bride_with_veil.png b/app/assets/images/emoji/bride_with_veil.png new file mode 100644 index 000000000..dd0b0cfda Binary files /dev/null and b/app/assets/images/emoji/bride_with_veil.png differ diff --git a/app/assets/images/emoji/bridge_at_night.png b/app/assets/images/emoji/bridge_at_night.png new file mode 100644 index 000000000..495b06c3d Binary files /dev/null and b/app/assets/images/emoji/bridge_at_night.png differ diff --git a/app/assets/images/emoji/briefcase.png b/app/assets/images/emoji/briefcase.png new file mode 100644 index 000000000..46e82b001 Binary files /dev/null and b/app/assets/images/emoji/briefcase.png differ diff --git a/app/assets/images/emoji/broken_heart.png b/app/assets/images/emoji/broken_heart.png new file mode 100644 index 000000000..a1bc850ec Binary files /dev/null and b/app/assets/images/emoji/broken_heart.png differ diff --git a/app/assets/images/emoji/bug.png b/app/assets/images/emoji/bug.png new file mode 100644 index 000000000..c2eaf7a70 Binary files /dev/null and b/app/assets/images/emoji/bug.png differ diff --git a/app/assets/images/emoji/bulb.png b/app/assets/images/emoji/bulb.png new file mode 100644 index 000000000..23afca1c7 Binary files /dev/null and b/app/assets/images/emoji/bulb.png differ diff --git a/app/assets/images/emoji/bullettrain_front.png b/app/assets/images/emoji/bullettrain_front.png new file mode 100644 index 000000000..16651acff Binary files /dev/null and b/app/assets/images/emoji/bullettrain_front.png differ diff --git a/app/assets/images/emoji/bullettrain_side.png b/app/assets/images/emoji/bullettrain_side.png new file mode 100644 index 000000000..8eca36845 Binary files /dev/null and b/app/assets/images/emoji/bullettrain_side.png differ diff --git a/app/assets/images/emoji/bus.png b/app/assets/images/emoji/bus.png new file mode 100644 index 000000000..823aa39e4 Binary files /dev/null and b/app/assets/images/emoji/bus.png differ diff --git a/app/assets/images/emoji/busstop.png b/app/assets/images/emoji/busstop.png new file mode 100644 index 000000000..99af2322a Binary files /dev/null and b/app/assets/images/emoji/busstop.png differ diff --git a/app/assets/images/emoji/bust_in_silhouette.png b/app/assets/images/emoji/bust_in_silhouette.png new file mode 100644 index 000000000..d13139869 Binary files /dev/null and b/app/assets/images/emoji/bust_in_silhouette.png differ diff --git a/app/assets/images/emoji/busts_in_silhouette.png b/app/assets/images/emoji/busts_in_silhouette.png new file mode 100644 index 000000000..1f3aabcff Binary files /dev/null and b/app/assets/images/emoji/busts_in_silhouette.png differ diff --git a/app/assets/images/emoji/cactus.png b/app/assets/images/emoji/cactus.png new file mode 100644 index 000000000..5a2c3cc72 Binary files /dev/null and b/app/assets/images/emoji/cactus.png differ diff --git a/app/assets/images/emoji/cake.png b/app/assets/images/emoji/cake.png new file mode 100644 index 000000000..efeb9b4b2 Binary files /dev/null and b/app/assets/images/emoji/cake.png differ diff --git a/app/assets/images/emoji/calendar.png b/app/assets/images/emoji/calendar.png new file mode 100644 index 000000000..900b868bb Binary files /dev/null and b/app/assets/images/emoji/calendar.png differ diff --git a/app/assets/images/emoji/calling.png b/app/assets/images/emoji/calling.png new file mode 100644 index 000000000..837897f26 Binary files /dev/null and b/app/assets/images/emoji/calling.png differ diff --git a/app/assets/images/emoji/camel.png b/app/assets/images/emoji/camel.png new file mode 100644 index 000000000..496c186ae Binary files /dev/null and b/app/assets/images/emoji/camel.png differ diff --git a/app/assets/images/emoji/camera.png b/app/assets/images/emoji/camera.png new file mode 100644 index 000000000..397d03b39 Binary files /dev/null and b/app/assets/images/emoji/camera.png differ diff --git a/app/assets/images/emoji/cancer.png b/app/assets/images/emoji/cancer.png new file mode 100644 index 000000000..ea43a4a2a Binary files /dev/null and b/app/assets/images/emoji/cancer.png differ diff --git a/app/assets/images/emoji/candy.png b/app/assets/images/emoji/candy.png new file mode 100644 index 000000000..33722f236 Binary files /dev/null and b/app/assets/images/emoji/candy.png differ diff --git a/app/assets/images/emoji/capital_abcd.png b/app/assets/images/emoji/capital_abcd.png new file mode 100644 index 000000000..ffc0cba4b Binary files /dev/null and b/app/assets/images/emoji/capital_abcd.png differ diff --git a/app/assets/images/emoji/capricorn.png b/app/assets/images/emoji/capricorn.png new file mode 100644 index 000000000..f2044e789 Binary files /dev/null and b/app/assets/images/emoji/capricorn.png differ diff --git a/app/assets/images/emoji/car.png b/app/assets/images/emoji/car.png new file mode 100644 index 000000000..d70a2f062 Binary files /dev/null and b/app/assets/images/emoji/car.png differ diff --git a/app/assets/images/emoji/card_index.png b/app/assets/images/emoji/card_index.png new file mode 100644 index 000000000..374e94e9e Binary files /dev/null and b/app/assets/images/emoji/card_index.png differ diff --git a/app/assets/images/emoji/carousel_horse.png b/app/assets/images/emoji/carousel_horse.png new file mode 100644 index 000000000..765d2c0a8 Binary files /dev/null and b/app/assets/images/emoji/carousel_horse.png differ diff --git a/app/assets/images/emoji/cat.png b/app/assets/images/emoji/cat.png new file mode 100644 index 000000000..09b9ef79a Binary files /dev/null and b/app/assets/images/emoji/cat.png differ diff --git a/app/assets/images/emoji/cat2.png b/app/assets/images/emoji/cat2.png new file mode 100644 index 000000000..977c992c5 Binary files /dev/null and b/app/assets/images/emoji/cat2.png differ diff --git a/app/assets/images/emoji/cd.png b/app/assets/images/emoji/cd.png new file mode 100644 index 000000000..baff835c4 Binary files /dev/null and b/app/assets/images/emoji/cd.png differ diff --git a/app/assets/images/emoji/chart.png b/app/assets/images/emoji/chart.png new file mode 100644 index 000000000..ac2c4bb09 Binary files /dev/null and b/app/assets/images/emoji/chart.png differ diff --git a/app/assets/images/emoji/chart_with_downwards_trend.png b/app/assets/images/emoji/chart_with_downwards_trend.png new file mode 100644 index 000000000..65b82f044 Binary files /dev/null and b/app/assets/images/emoji/chart_with_downwards_trend.png differ diff --git a/app/assets/images/emoji/chart_with_upwards_trend.png b/app/assets/images/emoji/chart_with_upwards_trend.png new file mode 100644 index 000000000..de3e9ba7b Binary files /dev/null and b/app/assets/images/emoji/chart_with_upwards_trend.png differ diff --git a/app/assets/images/emoji/checkered_flag.png b/app/assets/images/emoji/checkered_flag.png new file mode 100644 index 000000000..ead4a68dd Binary files /dev/null and b/app/assets/images/emoji/checkered_flag.png differ diff --git a/app/assets/images/emoji/cherries.png b/app/assets/images/emoji/cherries.png new file mode 100644 index 000000000..8d3e044f2 Binary files /dev/null and b/app/assets/images/emoji/cherries.png differ diff --git a/app/assets/images/emoji/cherry_blossom.png b/app/assets/images/emoji/cherry_blossom.png new file mode 100644 index 000000000..e03155499 Binary files /dev/null and b/app/assets/images/emoji/cherry_blossom.png differ diff --git a/app/assets/images/emoji/chestnut.png b/app/assets/images/emoji/chestnut.png new file mode 100644 index 000000000..066fb6bf6 Binary files /dev/null and b/app/assets/images/emoji/chestnut.png differ diff --git a/app/assets/images/emoji/chicken.png b/app/assets/images/emoji/chicken.png new file mode 100644 index 000000000..6d25c0ef4 Binary files /dev/null and b/app/assets/images/emoji/chicken.png differ diff --git a/app/assets/images/emoji/children_crossing.png b/app/assets/images/emoji/children_crossing.png new file mode 100644 index 000000000..b0302ae62 Binary files /dev/null and b/app/assets/images/emoji/children_crossing.png differ diff --git a/app/assets/images/emoji/chocolate_bar.png b/app/assets/images/emoji/chocolate_bar.png new file mode 100644 index 000000000..c7ec19d07 Binary files /dev/null and b/app/assets/images/emoji/chocolate_bar.png differ diff --git a/app/assets/images/emoji/christmas_tree.png b/app/assets/images/emoji/christmas_tree.png new file mode 100644 index 000000000..d813b9593 Binary files /dev/null and b/app/assets/images/emoji/christmas_tree.png differ diff --git a/app/assets/images/emoji/church.png b/app/assets/images/emoji/church.png new file mode 100644 index 000000000..4c07c6b9e Binary files /dev/null and b/app/assets/images/emoji/church.png differ diff --git a/app/assets/images/emoji/cinema.png b/app/assets/images/emoji/cinema.png new file mode 100644 index 000000000..a990ccf99 Binary files /dev/null and b/app/assets/images/emoji/cinema.png differ diff --git a/app/assets/images/emoji/circus_tent.png b/app/assets/images/emoji/circus_tent.png new file mode 100644 index 000000000..4af8719aa Binary files /dev/null and b/app/assets/images/emoji/circus_tent.png differ diff --git a/app/assets/images/emoji/city_sunrise.png b/app/assets/images/emoji/city_sunrise.png new file mode 100644 index 000000000..91ca2a40b Binary files /dev/null and b/app/assets/images/emoji/city_sunrise.png differ diff --git a/app/assets/images/emoji/city_sunset.png b/app/assets/images/emoji/city_sunset.png new file mode 100644 index 000000000..7cb178a2c Binary files /dev/null and b/app/assets/images/emoji/city_sunset.png differ diff --git a/app/assets/images/emoji/cl.png b/app/assets/images/emoji/cl.png new file mode 100644 index 000000000..15ac67525 Binary files /dev/null and b/app/assets/images/emoji/cl.png differ diff --git a/app/assets/images/emoji/clap.png b/app/assets/images/emoji/clap.png new file mode 100644 index 000000000..d01c982a7 Binary files /dev/null and b/app/assets/images/emoji/clap.png differ diff --git a/app/assets/images/emoji/clapper.png b/app/assets/images/emoji/clapper.png new file mode 100644 index 000000000..4e1dc111d Binary files /dev/null and b/app/assets/images/emoji/clapper.png differ diff --git a/app/assets/images/emoji/clipboard.png b/app/assets/images/emoji/clipboard.png new file mode 100644 index 000000000..e2c74e6df Binary files /dev/null and b/app/assets/images/emoji/clipboard.png differ diff --git a/app/assets/images/emoji/clock1.png b/app/assets/images/emoji/clock1.png new file mode 100644 index 000000000..ca34e8975 Binary files /dev/null and b/app/assets/images/emoji/clock1.png differ diff --git a/app/assets/images/emoji/clock10.png b/app/assets/images/emoji/clock10.png new file mode 100644 index 000000000..f710bef5c Binary files /dev/null and b/app/assets/images/emoji/clock10.png differ diff --git a/app/assets/images/emoji/clock1030.png b/app/assets/images/emoji/clock1030.png new file mode 100644 index 000000000..84a3bc8fb Binary files /dev/null and b/app/assets/images/emoji/clock1030.png differ diff --git a/app/assets/images/emoji/clock11.png b/app/assets/images/emoji/clock11.png new file mode 100644 index 000000000..fbc165b99 Binary files /dev/null and b/app/assets/images/emoji/clock11.png differ diff --git a/app/assets/images/emoji/clock1130.png b/app/assets/images/emoji/clock1130.png new file mode 100644 index 000000000..415999ec8 Binary files /dev/null and b/app/assets/images/emoji/clock1130.png differ diff --git a/app/assets/images/emoji/clock12.png b/app/assets/images/emoji/clock12.png new file mode 100644 index 000000000..c1ca82f39 Binary files /dev/null and b/app/assets/images/emoji/clock12.png differ diff --git a/app/assets/images/emoji/clock1230.png b/app/assets/images/emoji/clock1230.png new file mode 100644 index 000000000..a6527154d Binary files /dev/null and b/app/assets/images/emoji/clock1230.png differ diff --git a/app/assets/images/emoji/clock130.png b/app/assets/images/emoji/clock130.png new file mode 100644 index 000000000..df9392019 Binary files /dev/null and b/app/assets/images/emoji/clock130.png differ diff --git a/app/assets/images/emoji/clock2.png b/app/assets/images/emoji/clock2.png new file mode 100644 index 000000000..1a12524ee Binary files /dev/null and b/app/assets/images/emoji/clock2.png differ diff --git a/app/assets/images/emoji/clock230.png b/app/assets/images/emoji/clock230.png new file mode 100644 index 000000000..f12c6912a Binary files /dev/null and b/app/assets/images/emoji/clock230.png differ diff --git a/app/assets/images/emoji/clock3.png b/app/assets/images/emoji/clock3.png new file mode 100644 index 000000000..cd99bb155 Binary files /dev/null and b/app/assets/images/emoji/clock3.png differ diff --git a/app/assets/images/emoji/clock330.png b/app/assets/images/emoji/clock330.png new file mode 100644 index 000000000..1dc9628ea Binary files /dev/null and b/app/assets/images/emoji/clock330.png differ diff --git a/app/assets/images/emoji/clock4.png b/app/assets/images/emoji/clock4.png new file mode 100644 index 000000000..7274e8b07 Binary files /dev/null and b/app/assets/images/emoji/clock4.png differ diff --git a/app/assets/images/emoji/clock430.png b/app/assets/images/emoji/clock430.png new file mode 100644 index 000000000..7726aaea1 Binary files /dev/null and b/app/assets/images/emoji/clock430.png differ diff --git a/app/assets/images/emoji/clock5.png b/app/assets/images/emoji/clock5.png new file mode 100644 index 000000000..3ed5a81af Binary files /dev/null and b/app/assets/images/emoji/clock5.png differ diff --git a/app/assets/images/emoji/clock530.png b/app/assets/images/emoji/clock530.png new file mode 100644 index 000000000..e08d4ad2b Binary files /dev/null and b/app/assets/images/emoji/clock530.png differ diff --git a/app/assets/images/emoji/clock6.png b/app/assets/images/emoji/clock6.png new file mode 100644 index 000000000..ac38cb926 Binary files /dev/null and b/app/assets/images/emoji/clock6.png differ diff --git a/app/assets/images/emoji/clock630.png b/app/assets/images/emoji/clock630.png new file mode 100644 index 000000000..46f0681f1 Binary files /dev/null and b/app/assets/images/emoji/clock630.png differ diff --git a/app/assets/images/emoji/clock7.png b/app/assets/images/emoji/clock7.png new file mode 100644 index 000000000..6a138dfde Binary files /dev/null and b/app/assets/images/emoji/clock7.png differ diff --git a/app/assets/images/emoji/clock730.png b/app/assets/images/emoji/clock730.png new file mode 100644 index 000000000..18aab22fd Binary files /dev/null and b/app/assets/images/emoji/clock730.png differ diff --git a/app/assets/images/emoji/clock8.png b/app/assets/images/emoji/clock8.png new file mode 100644 index 000000000..6690cd74e Binary files /dev/null and b/app/assets/images/emoji/clock8.png differ diff --git a/app/assets/images/emoji/clock830.png b/app/assets/images/emoji/clock830.png new file mode 100644 index 000000000..ec3e382dd Binary files /dev/null and b/app/assets/images/emoji/clock830.png differ diff --git a/app/assets/images/emoji/clock9.png b/app/assets/images/emoji/clock9.png new file mode 100644 index 000000000..c4ad74609 Binary files /dev/null and b/app/assets/images/emoji/clock9.png differ diff --git a/app/assets/images/emoji/clock930.png b/app/assets/images/emoji/clock930.png new file mode 100644 index 000000000..fd3522142 Binary files /dev/null and b/app/assets/images/emoji/clock930.png differ diff --git a/app/assets/images/emoji/closed_book.png b/app/assets/images/emoji/closed_book.png new file mode 100644 index 000000000..484029c5e Binary files /dev/null and b/app/assets/images/emoji/closed_book.png differ diff --git a/app/assets/images/emoji/closed_lock_with_key.png b/app/assets/images/emoji/closed_lock_with_key.png new file mode 100644 index 000000000..e6fdf6cb2 Binary files /dev/null and b/app/assets/images/emoji/closed_lock_with_key.png differ diff --git a/app/assets/images/emoji/closed_umbrella.png b/app/assets/images/emoji/closed_umbrella.png new file mode 100644 index 000000000..072c5c217 Binary files /dev/null and b/app/assets/images/emoji/closed_umbrella.png differ diff --git a/app/assets/images/emoji/cloud.png b/app/assets/images/emoji/cloud.png new file mode 100644 index 000000000..b31c08c0b Binary files /dev/null and b/app/assets/images/emoji/cloud.png differ diff --git a/app/assets/images/emoji/clubs.png b/app/assets/images/emoji/clubs.png new file mode 100644 index 000000000..bfab53656 Binary files /dev/null and b/app/assets/images/emoji/clubs.png differ diff --git a/app/assets/images/emoji/cn.png b/app/assets/images/emoji/cn.png new file mode 100644 index 000000000..b30dcc53d Binary files /dev/null and b/app/assets/images/emoji/cn.png differ diff --git a/app/assets/images/emoji/cocktail.png b/app/assets/images/emoji/cocktail.png new file mode 100644 index 000000000..28b45ea51 Binary files /dev/null and b/app/assets/images/emoji/cocktail.png differ diff --git a/app/assets/images/emoji/coffee.png b/app/assets/images/emoji/coffee.png new file mode 100644 index 000000000..57e1adcb0 Binary files /dev/null and b/app/assets/images/emoji/coffee.png differ diff --git a/app/assets/images/emoji/cold_sweat.png b/app/assets/images/emoji/cold_sweat.png new file mode 100644 index 000000000..b9e39bc60 Binary files /dev/null and b/app/assets/images/emoji/cold_sweat.png differ diff --git a/app/assets/images/emoji/collision.png b/app/assets/images/emoji/collision.png new file mode 100644 index 000000000..bddeb8f49 Binary files /dev/null and b/app/assets/images/emoji/collision.png differ diff --git a/app/assets/images/emoji/computer.png b/app/assets/images/emoji/computer.png new file mode 100644 index 000000000..d4d268762 Binary files /dev/null and b/app/assets/images/emoji/computer.png differ diff --git a/app/assets/images/emoji/confetti_ball.png b/app/assets/images/emoji/confetti_ball.png new file mode 100644 index 000000000..bd293e3d8 Binary files /dev/null and b/app/assets/images/emoji/confetti_ball.png differ diff --git a/app/assets/images/emoji/confounded.png b/app/assets/images/emoji/confounded.png new file mode 100644 index 000000000..a5877a0a7 Binary files /dev/null and b/app/assets/images/emoji/confounded.png differ diff --git a/app/assets/images/emoji/confused.png b/app/assets/images/emoji/confused.png new file mode 100644 index 000000000..18ff760ac Binary files /dev/null and b/app/assets/images/emoji/confused.png differ diff --git a/app/assets/images/emoji/congratulations.png b/app/assets/images/emoji/congratulations.png new file mode 100644 index 000000000..dcbb1d229 Binary files /dev/null and b/app/assets/images/emoji/congratulations.png differ diff --git a/app/assets/images/emoji/construction.png b/app/assets/images/emoji/construction.png new file mode 100644 index 000000000..523e9f10b Binary files /dev/null and b/app/assets/images/emoji/construction.png differ diff --git a/app/assets/images/emoji/construction_worker.png b/app/assets/images/emoji/construction_worker.png new file mode 100644 index 000000000..4d6486047 Binary files /dev/null and b/app/assets/images/emoji/construction_worker.png differ diff --git a/app/assets/images/emoji/convenience_store.png b/app/assets/images/emoji/convenience_store.png new file mode 100644 index 000000000..671696c2d Binary files /dev/null and b/app/assets/images/emoji/convenience_store.png differ diff --git a/app/assets/images/emoji/cookie.png b/app/assets/images/emoji/cookie.png new file mode 100644 index 000000000..653edb258 Binary files /dev/null and b/app/assets/images/emoji/cookie.png differ diff --git a/app/assets/images/emoji/cool.png b/app/assets/images/emoji/cool.png new file mode 100644 index 000000000..937dcd792 Binary files /dev/null and b/app/assets/images/emoji/cool.png differ diff --git a/app/assets/images/emoji/cop.png b/app/assets/images/emoji/cop.png new file mode 100644 index 000000000..43a5a84f8 Binary files /dev/null and b/app/assets/images/emoji/cop.png differ diff --git a/app/assets/images/emoji/copyright.png b/app/assets/images/emoji/copyright.png new file mode 100644 index 000000000..d59f580a9 Binary files /dev/null and b/app/assets/images/emoji/copyright.png differ diff --git a/app/assets/images/emoji/corn.png b/app/assets/images/emoji/corn.png new file mode 100644 index 000000000..fe5d8b128 Binary files /dev/null and b/app/assets/images/emoji/corn.png differ diff --git a/app/assets/images/emoji/couple.png b/app/assets/images/emoji/couple.png new file mode 100644 index 000000000..9e51f40e1 Binary files /dev/null and b/app/assets/images/emoji/couple.png differ diff --git a/app/assets/images/emoji/couple_with_heart.png b/app/assets/images/emoji/couple_with_heart.png new file mode 100644 index 000000000..c503f40a9 Binary files /dev/null and b/app/assets/images/emoji/couple_with_heart.png differ diff --git a/app/assets/images/emoji/couplekiss.png b/app/assets/images/emoji/couplekiss.png new file mode 100644 index 000000000..d02790822 Binary files /dev/null and b/app/assets/images/emoji/couplekiss.png differ diff --git a/app/assets/images/emoji/cow.png b/app/assets/images/emoji/cow.png new file mode 100644 index 000000000..12e1ab6c0 Binary files /dev/null and b/app/assets/images/emoji/cow.png differ diff --git a/app/assets/images/emoji/cow2.png b/app/assets/images/emoji/cow2.png new file mode 100644 index 000000000..594c92155 Binary files /dev/null and b/app/assets/images/emoji/cow2.png differ diff --git a/app/assets/images/emoji/credit_card.png b/app/assets/images/emoji/credit_card.png new file mode 100644 index 000000000..be1c1dd30 Binary files /dev/null and b/app/assets/images/emoji/credit_card.png differ diff --git a/app/assets/images/emoji/crescent_moon.png b/app/assets/images/emoji/crescent_moon.png new file mode 100644 index 000000000..afdb450d1 Binary files /dev/null and b/app/assets/images/emoji/crescent_moon.png differ diff --git a/app/assets/images/emoji/crocodile.png b/app/assets/images/emoji/crocodile.png new file mode 100644 index 000000000..7435d5ab3 Binary files /dev/null and b/app/assets/images/emoji/crocodile.png differ diff --git a/app/assets/images/emoji/crossed_flags.png b/app/assets/images/emoji/crossed_flags.png new file mode 100644 index 000000000..2ffbb2627 Binary files /dev/null and b/app/assets/images/emoji/crossed_flags.png differ diff --git a/app/assets/images/emoji/crown.png b/app/assets/images/emoji/crown.png new file mode 100644 index 000000000..39da1d528 Binary files /dev/null and b/app/assets/images/emoji/crown.png differ diff --git a/app/assets/images/emoji/cry.png b/app/assets/images/emoji/cry.png new file mode 100644 index 000000000..6d0d9afd2 Binary files /dev/null and b/app/assets/images/emoji/cry.png differ diff --git a/app/assets/images/emoji/crying_cat_face.png b/app/assets/images/emoji/crying_cat_face.png new file mode 100644 index 000000000..42d4c27ca Binary files /dev/null and b/app/assets/images/emoji/crying_cat_face.png differ diff --git a/app/assets/images/emoji/crystal_ball.png b/app/assets/images/emoji/crystal_ball.png new file mode 100644 index 000000000..6d2c6c42d Binary files /dev/null and b/app/assets/images/emoji/crystal_ball.png differ diff --git a/app/assets/images/emoji/cupid.png b/app/assets/images/emoji/cupid.png new file mode 100644 index 000000000..498728476 Binary files /dev/null and b/app/assets/images/emoji/cupid.png differ diff --git a/app/assets/images/emoji/curly_loop.png b/app/assets/images/emoji/curly_loop.png new file mode 100644 index 000000000..8f051aca4 Binary files /dev/null and b/app/assets/images/emoji/curly_loop.png differ diff --git a/app/assets/images/emoji/currency_exchange.png b/app/assets/images/emoji/currency_exchange.png new file mode 100644 index 000000000..d5ee21fc6 Binary files /dev/null and b/app/assets/images/emoji/currency_exchange.png differ diff --git a/app/assets/images/emoji/curry.png b/app/assets/images/emoji/curry.png new file mode 100644 index 000000000..7983c706a Binary files /dev/null and b/app/assets/images/emoji/curry.png differ diff --git a/app/assets/images/emoji/custard.png b/app/assets/images/emoji/custard.png new file mode 100644 index 000000000..9f843b4c1 Binary files /dev/null and b/app/assets/images/emoji/custard.png differ diff --git a/app/assets/images/emoji/customs.png b/app/assets/images/emoji/customs.png new file mode 100644 index 000000000..92691e311 Binary files /dev/null and b/app/assets/images/emoji/customs.png differ diff --git a/app/assets/images/emoji/cyclone.png b/app/assets/images/emoji/cyclone.png new file mode 100644 index 000000000..6c49f64b2 Binary files /dev/null and b/app/assets/images/emoji/cyclone.png differ diff --git a/app/assets/images/emoji/dancer.png b/app/assets/images/emoji/dancer.png new file mode 100644 index 000000000..6885a0bc3 Binary files /dev/null and b/app/assets/images/emoji/dancer.png differ diff --git a/app/assets/images/emoji/dancers.png b/app/assets/images/emoji/dancers.png new file mode 100644 index 000000000..2dfb451a7 Binary files /dev/null and b/app/assets/images/emoji/dancers.png differ diff --git a/app/assets/images/emoji/dango.png b/app/assets/images/emoji/dango.png new file mode 100644 index 000000000..2d042aebe Binary files /dev/null and b/app/assets/images/emoji/dango.png differ diff --git a/app/assets/images/emoji/dart.png b/app/assets/images/emoji/dart.png new file mode 100644 index 000000000..0438fe54f Binary files /dev/null and b/app/assets/images/emoji/dart.png differ diff --git a/app/assets/images/emoji/dash.png b/app/assets/images/emoji/dash.png new file mode 100644 index 000000000..dc2c0a8f4 Binary files /dev/null and b/app/assets/images/emoji/dash.png differ diff --git a/app/assets/images/emoji/date.png b/app/assets/images/emoji/date.png new file mode 100644 index 000000000..6ad2efa5f Binary files /dev/null and b/app/assets/images/emoji/date.png differ diff --git a/app/assets/images/emoji/de.png b/app/assets/images/emoji/de.png new file mode 100644 index 000000000..16a28548c Binary files /dev/null and b/app/assets/images/emoji/de.png differ diff --git a/app/assets/images/emoji/deciduous_tree.png b/app/assets/images/emoji/deciduous_tree.png new file mode 100644 index 000000000..9bb16bdfe Binary files /dev/null and b/app/assets/images/emoji/deciduous_tree.png differ diff --git a/app/assets/images/emoji/department_store.png b/app/assets/images/emoji/department_store.png new file mode 100644 index 000000000..68d959c50 Binary files /dev/null and b/app/assets/images/emoji/department_store.png differ diff --git a/app/assets/images/emoji/diamond_shape_with_a_dot_inside.png b/app/assets/images/emoji/diamond_shape_with_a_dot_inside.png new file mode 100644 index 000000000..dfd1098b3 Binary files /dev/null and b/app/assets/images/emoji/diamond_shape_with_a_dot_inside.png differ diff --git a/app/assets/images/emoji/diamonds.png b/app/assets/images/emoji/diamonds.png new file mode 100644 index 000000000..fe0827758 Binary files /dev/null and b/app/assets/images/emoji/diamonds.png differ diff --git a/app/assets/images/emoji/disappointed.png b/app/assets/images/emoji/disappointed.png new file mode 100644 index 000000000..825520087 Binary files /dev/null and b/app/assets/images/emoji/disappointed.png differ diff --git a/app/assets/images/emoji/disappointed_relieved.png b/app/assets/images/emoji/disappointed_relieved.png new file mode 100644 index 000000000..fa5f9e7f9 Binary files /dev/null and b/app/assets/images/emoji/disappointed_relieved.png differ diff --git a/app/assets/images/emoji/dizzy.png b/app/assets/images/emoji/dizzy.png new file mode 100644 index 000000000..55213d2dd Binary files /dev/null and b/app/assets/images/emoji/dizzy.png differ diff --git a/app/assets/images/emoji/dizzy_face.png b/app/assets/images/emoji/dizzy_face.png new file mode 100644 index 000000000..8001d6ff8 Binary files /dev/null and b/app/assets/images/emoji/dizzy_face.png differ diff --git a/app/assets/images/emoji/do_not_litter.png b/app/assets/images/emoji/do_not_litter.png new file mode 100644 index 000000000..38c7ae7af Binary files /dev/null and b/app/assets/images/emoji/do_not_litter.png differ diff --git a/app/assets/images/emoji/dog.png b/app/assets/images/emoji/dog.png new file mode 100644 index 000000000..389a02bf2 Binary files /dev/null and b/app/assets/images/emoji/dog.png differ diff --git a/app/assets/images/emoji/dog2.png b/app/assets/images/emoji/dog2.png new file mode 100644 index 000000000..c7f6a24ac Binary files /dev/null and b/app/assets/images/emoji/dog2.png differ diff --git a/app/assets/images/emoji/dollar.png b/app/assets/images/emoji/dollar.png new file mode 100644 index 000000000..63de88495 Binary files /dev/null and b/app/assets/images/emoji/dollar.png differ diff --git a/app/assets/images/emoji/dolls.png b/app/assets/images/emoji/dolls.png new file mode 100644 index 000000000..47ce33900 Binary files /dev/null and b/app/assets/images/emoji/dolls.png differ diff --git a/app/assets/images/emoji/dolphin.png b/app/assets/images/emoji/dolphin.png new file mode 100644 index 000000000..9326077a9 Binary files /dev/null and b/app/assets/images/emoji/dolphin.png differ diff --git a/app/assets/images/emoji/door.png b/app/assets/images/emoji/door.png new file mode 100644 index 000000000..83c819ae4 Binary files /dev/null and b/app/assets/images/emoji/door.png differ diff --git a/app/assets/images/emoji/doughnut.png b/app/assets/images/emoji/doughnut.png new file mode 100644 index 000000000..ccf869129 Binary files /dev/null and b/app/assets/images/emoji/doughnut.png differ diff --git a/app/assets/images/emoji/dragon.png b/app/assets/images/emoji/dragon.png new file mode 100644 index 000000000..e399d60e1 Binary files /dev/null and b/app/assets/images/emoji/dragon.png differ diff --git a/app/assets/images/emoji/dragon_face.png b/app/assets/images/emoji/dragon_face.png new file mode 100644 index 000000000..e5e556bd1 Binary files /dev/null and b/app/assets/images/emoji/dragon_face.png differ diff --git a/app/assets/images/emoji/dress.png b/app/assets/images/emoji/dress.png new file mode 100644 index 000000000..6434e2e2f Binary files /dev/null and b/app/assets/images/emoji/dress.png differ diff --git a/app/assets/images/emoji/dromedary_camel.png b/app/assets/images/emoji/dromedary_camel.png new file mode 100644 index 000000000..c8c7b9ffa Binary files /dev/null and b/app/assets/images/emoji/dromedary_camel.png differ diff --git a/app/assets/images/emoji/droplet.png b/app/assets/images/emoji/droplet.png new file mode 100644 index 000000000..9eff46339 Binary files /dev/null and b/app/assets/images/emoji/droplet.png differ diff --git a/app/assets/images/emoji/dvd.png b/app/assets/images/emoji/dvd.png new file mode 100644 index 000000000..363c83d01 Binary files /dev/null and b/app/assets/images/emoji/dvd.png differ diff --git a/app/assets/images/emoji/e-mail.png b/app/assets/images/emoji/e-mail.png new file mode 100644 index 000000000..176a8e1e8 Binary files /dev/null and b/app/assets/images/emoji/e-mail.png differ diff --git a/app/assets/images/emoji/ear.png b/app/assets/images/emoji/ear.png new file mode 100644 index 000000000..2bbbf10c9 Binary files /dev/null and b/app/assets/images/emoji/ear.png differ diff --git a/app/assets/images/emoji/ear_of_rice.png b/app/assets/images/emoji/ear_of_rice.png new file mode 100644 index 000000000..a9bba5c2c Binary files /dev/null and b/app/assets/images/emoji/ear_of_rice.png differ diff --git a/app/assets/images/emoji/earth_africa.png b/app/assets/images/emoji/earth_africa.png new file mode 100644 index 000000000..44ce5ecb6 Binary files /dev/null and b/app/assets/images/emoji/earth_africa.png differ diff --git a/app/assets/images/emoji/earth_americas.png b/app/assets/images/emoji/earth_americas.png new file mode 100644 index 000000000..97d717671 Binary files /dev/null and b/app/assets/images/emoji/earth_americas.png differ diff --git a/app/assets/images/emoji/earth_asia.png b/app/assets/images/emoji/earth_asia.png new file mode 100644 index 000000000..95ec357ca Binary files /dev/null and b/app/assets/images/emoji/earth_asia.png differ diff --git a/app/assets/images/emoji/egg.png b/app/assets/images/emoji/egg.png new file mode 100644 index 000000000..c3de6ae4e Binary files /dev/null and b/app/assets/images/emoji/egg.png differ diff --git a/app/assets/images/emoji/eggplant.png b/app/assets/images/emoji/eggplant.png new file mode 100644 index 000000000..566d6a844 Binary files /dev/null and b/app/assets/images/emoji/eggplant.png differ diff --git a/app/assets/images/emoji/eight.png b/app/assets/images/emoji/eight.png new file mode 100644 index 000000000..7bdb42232 Binary files /dev/null and b/app/assets/images/emoji/eight.png differ diff --git a/app/assets/images/emoji/eight_pointed_black_star.png b/app/assets/images/emoji/eight_pointed_black_star.png new file mode 100644 index 000000000..73dc6a0c9 Binary files /dev/null and b/app/assets/images/emoji/eight_pointed_black_star.png differ diff --git a/app/assets/images/emoji/eight_spoked_asterisk.png b/app/assets/images/emoji/eight_spoked_asterisk.png new file mode 100644 index 000000000..946a20333 Binary files /dev/null and b/app/assets/images/emoji/eight_spoked_asterisk.png differ diff --git a/app/assets/images/emoji/electric_plug.png b/app/assets/images/emoji/electric_plug.png new file mode 100644 index 000000000..7a3d6cee6 Binary files /dev/null and b/app/assets/images/emoji/electric_plug.png differ diff --git a/app/assets/images/emoji/elephant.png b/app/assets/images/emoji/elephant.png new file mode 100644 index 000000000..5ca04570e Binary files /dev/null and b/app/assets/images/emoji/elephant.png differ diff --git a/app/assets/images/emoji/email.png b/app/assets/images/emoji/email.png new file mode 100644 index 000000000..3631861bb Binary files /dev/null and b/app/assets/images/emoji/email.png differ diff --git a/app/assets/images/emoji/end.png b/app/assets/images/emoji/end.png new file mode 100644 index 000000000..edb0bda24 Binary files /dev/null and b/app/assets/images/emoji/end.png differ diff --git a/app/assets/images/emoji/envelope.png b/app/assets/images/emoji/envelope.png new file mode 100644 index 000000000..3631861bb Binary files /dev/null and b/app/assets/images/emoji/envelope.png differ diff --git a/app/assets/images/emoji/envelope_with_arrow.png b/app/assets/images/emoji/envelope_with_arrow.png new file mode 100644 index 000000000..0e01fd5f0 Binary files /dev/null and b/app/assets/images/emoji/envelope_with_arrow.png differ diff --git a/app/assets/images/emoji/es.png b/app/assets/images/emoji/es.png new file mode 100644 index 000000000..71b30bff3 Binary files /dev/null and b/app/assets/images/emoji/es.png differ diff --git a/app/assets/images/emoji/euro.png b/app/assets/images/emoji/euro.png new file mode 100644 index 000000000..1c5904b71 Binary files /dev/null and b/app/assets/images/emoji/euro.png differ diff --git a/app/assets/images/emoji/european_castle.png b/app/assets/images/emoji/european_castle.png new file mode 100644 index 000000000..8229b8a8a Binary files /dev/null and b/app/assets/images/emoji/european_castle.png differ diff --git a/app/assets/images/emoji/european_post_office.png b/app/assets/images/emoji/european_post_office.png new file mode 100644 index 000000000..0f65b1453 Binary files /dev/null and b/app/assets/images/emoji/european_post_office.png differ diff --git a/app/assets/images/emoji/evergreen_tree.png b/app/assets/images/emoji/evergreen_tree.png new file mode 100644 index 000000000..ae8ad1037 Binary files /dev/null and b/app/assets/images/emoji/evergreen_tree.png differ diff --git a/app/assets/images/emoji/exclamation.png b/app/assets/images/emoji/exclamation.png new file mode 100644 index 000000000..4c560f5e3 Binary files /dev/null and b/app/assets/images/emoji/exclamation.png differ diff --git a/app/assets/images/emoji/expressionless.png b/app/assets/images/emoji/expressionless.png new file mode 100644 index 000000000..1798f24de Binary files /dev/null and b/app/assets/images/emoji/expressionless.png differ diff --git a/app/assets/images/emoji/eyeglasses.png b/app/assets/images/emoji/eyeglasses.png new file mode 100644 index 000000000..a3cf75a27 Binary files /dev/null and b/app/assets/images/emoji/eyeglasses.png differ diff --git a/app/assets/images/emoji/eyes.png b/app/assets/images/emoji/eyes.png new file mode 100644 index 000000000..dc2216f63 Binary files /dev/null and b/app/assets/images/emoji/eyes.png differ diff --git a/app/assets/images/emoji/facepunch.png b/app/assets/images/emoji/facepunch.png new file mode 100644 index 000000000..2d41fd37e Binary files /dev/null and b/app/assets/images/emoji/facepunch.png differ diff --git a/app/assets/images/emoji/factory.png b/app/assets/images/emoji/factory.png new file mode 100644 index 000000000..640463479 Binary files /dev/null and b/app/assets/images/emoji/factory.png differ diff --git a/app/assets/images/emoji/fallen_leaf.png b/app/assets/images/emoji/fallen_leaf.png new file mode 100644 index 000000000..d49f9c175 Binary files /dev/null and b/app/assets/images/emoji/fallen_leaf.png differ diff --git a/app/assets/images/emoji/family.png b/app/assets/images/emoji/family.png new file mode 100644 index 000000000..b4b365f3a Binary files /dev/null and b/app/assets/images/emoji/family.png differ diff --git a/app/assets/images/emoji/fast_forward.png b/app/assets/images/emoji/fast_forward.png new file mode 100644 index 000000000..b94a11726 Binary files /dev/null and b/app/assets/images/emoji/fast_forward.png differ diff --git a/app/assets/images/emoji/fax.png b/app/assets/images/emoji/fax.png new file mode 100644 index 000000000..62be2c958 Binary files /dev/null and b/app/assets/images/emoji/fax.png differ diff --git a/app/assets/images/emoji/fearful.png b/app/assets/images/emoji/fearful.png new file mode 100644 index 000000000..513fce47b Binary files /dev/null and b/app/assets/images/emoji/fearful.png differ diff --git a/app/assets/images/emoji/feelsgood.png b/app/assets/images/emoji/feelsgood.png new file mode 100644 index 000000000..bad80a6b1 Binary files /dev/null and b/app/assets/images/emoji/feelsgood.png differ diff --git a/app/assets/images/emoji/feet.png b/app/assets/images/emoji/feet.png new file mode 100644 index 000000000..89b9fec9e Binary files /dev/null and b/app/assets/images/emoji/feet.png differ diff --git a/app/assets/images/emoji/ferris_wheel.png b/app/assets/images/emoji/ferris_wheel.png new file mode 100644 index 000000000..54a1dcfa1 Binary files /dev/null and b/app/assets/images/emoji/ferris_wheel.png differ diff --git a/app/assets/images/emoji/file_folder.png b/app/assets/images/emoji/file_folder.png new file mode 100644 index 000000000..4d8bebf8a Binary files /dev/null and b/app/assets/images/emoji/file_folder.png differ diff --git a/app/assets/images/emoji/finnadie.png b/app/assets/images/emoji/finnadie.png new file mode 100644 index 000000000..05ba8ac5e Binary files /dev/null and b/app/assets/images/emoji/finnadie.png differ diff --git a/app/assets/images/emoji/fire.png b/app/assets/images/emoji/fire.png new file mode 100644 index 000000000..f2a3149bb Binary files /dev/null and b/app/assets/images/emoji/fire.png differ diff --git a/app/assets/images/emoji/fire_engine.png b/app/assets/images/emoji/fire_engine.png new file mode 100644 index 000000000..9e6c59c99 Binary files /dev/null and b/app/assets/images/emoji/fire_engine.png differ diff --git a/app/assets/images/emoji/fireworks.png b/app/assets/images/emoji/fireworks.png new file mode 100644 index 000000000..b4eccd577 Binary files /dev/null and b/app/assets/images/emoji/fireworks.png differ diff --git a/app/assets/images/emoji/first_quarter_moon.png b/app/assets/images/emoji/first_quarter_moon.png new file mode 100644 index 000000000..f38c23693 Binary files /dev/null and b/app/assets/images/emoji/first_quarter_moon.png differ diff --git a/app/assets/images/emoji/first_quarter_moon_with_face.png b/app/assets/images/emoji/first_quarter_moon_with_face.png new file mode 100644 index 000000000..85ae2ce72 Binary files /dev/null and b/app/assets/images/emoji/first_quarter_moon_with_face.png differ diff --git a/app/assets/images/emoji/fish.png b/app/assets/images/emoji/fish.png new file mode 100644 index 000000000..dc2a3f52d Binary files /dev/null and b/app/assets/images/emoji/fish.png differ diff --git a/app/assets/images/emoji/fish_cake.png b/app/assets/images/emoji/fish_cake.png new file mode 100644 index 000000000..a8f22614d Binary files /dev/null and b/app/assets/images/emoji/fish_cake.png differ diff --git a/app/assets/images/emoji/fishing_pole_and_fish.png b/app/assets/images/emoji/fishing_pole_and_fish.png new file mode 100644 index 000000000..d84609c3b Binary files /dev/null and b/app/assets/images/emoji/fishing_pole_and_fish.png differ diff --git a/app/assets/images/emoji/fist.png b/app/assets/images/emoji/fist.png new file mode 100644 index 000000000..ecc8874c2 Binary files /dev/null and b/app/assets/images/emoji/fist.png differ diff --git a/app/assets/images/emoji/five.png b/app/assets/images/emoji/five.png new file mode 100644 index 000000000..794321aa2 Binary files /dev/null and b/app/assets/images/emoji/five.png differ diff --git a/app/assets/images/emoji/flags.png b/app/assets/images/emoji/flags.png new file mode 100644 index 000000000..540164e84 Binary files /dev/null and b/app/assets/images/emoji/flags.png differ diff --git a/app/assets/images/emoji/flashlight.png b/app/assets/images/emoji/flashlight.png new file mode 100644 index 000000000..215940aa8 Binary files /dev/null and b/app/assets/images/emoji/flashlight.png differ diff --git a/app/assets/images/emoji/floppy_disk.png b/app/assets/images/emoji/floppy_disk.png new file mode 100644 index 000000000..4ad56315a Binary files /dev/null and b/app/assets/images/emoji/floppy_disk.png differ diff --git a/app/assets/images/emoji/flower_playing_cards.png b/app/assets/images/emoji/flower_playing_cards.png new file mode 100644 index 000000000..cc46a6a1f Binary files /dev/null and b/app/assets/images/emoji/flower_playing_cards.png differ diff --git a/app/assets/images/emoji/flushed.png b/app/assets/images/emoji/flushed.png new file mode 100644 index 000000000..9b49410c0 Binary files /dev/null and b/app/assets/images/emoji/flushed.png differ diff --git a/app/assets/images/emoji/foggy.png b/app/assets/images/emoji/foggy.png new file mode 100644 index 000000000..3c7b8b04b Binary files /dev/null and b/app/assets/images/emoji/foggy.png differ diff --git a/app/assets/images/emoji/football.png b/app/assets/images/emoji/football.png new file mode 100644 index 000000000..0e4e168fa Binary files /dev/null and b/app/assets/images/emoji/football.png differ diff --git a/app/assets/images/emoji/footprints.png b/app/assets/images/emoji/footprints.png new file mode 100644 index 000000000..d7a25614f Binary files /dev/null and b/app/assets/images/emoji/footprints.png differ diff --git a/app/assets/images/emoji/fork_and_knife.png b/app/assets/images/emoji/fork_and_knife.png new file mode 100644 index 000000000..8ba4bc653 Binary files /dev/null and b/app/assets/images/emoji/fork_and_knife.png differ diff --git a/app/assets/images/emoji/fountain.png b/app/assets/images/emoji/fountain.png new file mode 100644 index 000000000..da126e648 Binary files /dev/null and b/app/assets/images/emoji/fountain.png differ diff --git a/app/assets/images/emoji/four.png b/app/assets/images/emoji/four.png new file mode 100644 index 000000000..14782ba23 Binary files /dev/null and b/app/assets/images/emoji/four.png differ diff --git a/app/assets/images/emoji/four_leaf_clover.png b/app/assets/images/emoji/four_leaf_clover.png new file mode 100644 index 000000000..f2014bea4 Binary files /dev/null and b/app/assets/images/emoji/four_leaf_clover.png differ diff --git a/app/assets/images/emoji/fr.png b/app/assets/images/emoji/fr.png new file mode 100644 index 000000000..6311c9115 Binary files /dev/null and b/app/assets/images/emoji/fr.png differ diff --git a/app/assets/images/emoji/free.png b/app/assets/images/emoji/free.png new file mode 100644 index 000000000..c886cf249 Binary files /dev/null and b/app/assets/images/emoji/free.png differ diff --git a/app/assets/images/emoji/fried_shrimp.png b/app/assets/images/emoji/fried_shrimp.png new file mode 100644 index 000000000..c8c284bf1 Binary files /dev/null and b/app/assets/images/emoji/fried_shrimp.png differ diff --git a/app/assets/images/emoji/fries.png b/app/assets/images/emoji/fries.png new file mode 100644 index 000000000..cfef66966 Binary files /dev/null and b/app/assets/images/emoji/fries.png differ diff --git a/app/assets/images/emoji/frog.png b/app/assets/images/emoji/frog.png new file mode 100644 index 000000000..cfe11b18f Binary files /dev/null and b/app/assets/images/emoji/frog.png differ diff --git a/app/assets/images/emoji/frowning.png b/app/assets/images/emoji/frowning.png new file mode 100644 index 000000000..7f8b6c77b Binary files /dev/null and b/app/assets/images/emoji/frowning.png differ diff --git a/app/assets/images/emoji/fuelpump.png b/app/assets/images/emoji/fuelpump.png new file mode 100644 index 000000000..54c29aeb1 Binary files /dev/null and b/app/assets/images/emoji/fuelpump.png differ diff --git a/app/assets/images/emoji/full_moon.png b/app/assets/images/emoji/full_moon.png new file mode 100644 index 000000000..8ff657a25 Binary files /dev/null and b/app/assets/images/emoji/full_moon.png differ diff --git a/app/assets/images/emoji/full_moon_with_face.png b/app/assets/images/emoji/full_moon_with_face.png new file mode 100644 index 000000000..94395a408 Binary files /dev/null and b/app/assets/images/emoji/full_moon_with_face.png differ diff --git a/app/assets/images/emoji/game_die.png b/app/assets/images/emoji/game_die.png new file mode 100644 index 000000000..4136e78ec Binary files /dev/null and b/app/assets/images/emoji/game_die.png differ diff --git a/app/assets/images/emoji/gb.png b/app/assets/images/emoji/gb.png new file mode 100644 index 000000000..2a62c7a08 Binary files /dev/null and b/app/assets/images/emoji/gb.png differ diff --git a/app/assets/images/emoji/gem.png b/app/assets/images/emoji/gem.png new file mode 100644 index 000000000..8a5d8dad5 Binary files /dev/null and b/app/assets/images/emoji/gem.png differ diff --git a/app/assets/images/emoji/gemini.png b/app/assets/images/emoji/gemini.png new file mode 100644 index 000000000..d926f6e88 Binary files /dev/null and b/app/assets/images/emoji/gemini.png differ diff --git a/app/assets/images/emoji/ghost.png b/app/assets/images/emoji/ghost.png new file mode 100644 index 000000000..671dd0c9e Binary files /dev/null and b/app/assets/images/emoji/ghost.png differ diff --git a/app/assets/images/emoji/gift.png b/app/assets/images/emoji/gift.png new file mode 100644 index 000000000..552cfdc2b Binary files /dev/null and b/app/assets/images/emoji/gift.png differ diff --git a/app/assets/images/emoji/gift_heart.png b/app/assets/images/emoji/gift_heart.png new file mode 100644 index 000000000..f31c26a3f Binary files /dev/null and b/app/assets/images/emoji/gift_heart.png differ diff --git a/app/assets/images/emoji/girl.png b/app/assets/images/emoji/girl.png new file mode 100644 index 000000000..ea4126941 Binary files /dev/null and b/app/assets/images/emoji/girl.png differ diff --git a/app/assets/images/emoji/globe_with_meridians.png b/app/assets/images/emoji/globe_with_meridians.png new file mode 100644 index 000000000..b19864667 Binary files /dev/null and b/app/assets/images/emoji/globe_with_meridians.png differ diff --git a/app/assets/images/emoji/goat.png b/app/assets/images/emoji/goat.png new file mode 100644 index 000000000..4be9cf304 Binary files /dev/null and b/app/assets/images/emoji/goat.png differ diff --git a/app/assets/images/emoji/goberserk.png b/app/assets/images/emoji/goberserk.png new file mode 100644 index 000000000..59a742aaa Binary files /dev/null and b/app/assets/images/emoji/goberserk.png differ diff --git a/app/assets/images/emoji/godmode.png b/app/assets/images/emoji/godmode.png new file mode 100644 index 000000000..7e75ab208 Binary files /dev/null and b/app/assets/images/emoji/godmode.png differ diff --git a/app/assets/images/emoji/golf.png b/app/assets/images/emoji/golf.png new file mode 100644 index 000000000..cba2116a7 Binary files /dev/null and b/app/assets/images/emoji/golf.png differ diff --git a/app/assets/images/emoji/grapes.png b/app/assets/images/emoji/grapes.png new file mode 100644 index 000000000..0f9f007a1 Binary files /dev/null and b/app/assets/images/emoji/grapes.png differ diff --git a/app/assets/images/emoji/green_apple.png b/app/assets/images/emoji/green_apple.png new file mode 100644 index 000000000..337205cd1 Binary files /dev/null and b/app/assets/images/emoji/green_apple.png differ diff --git a/app/assets/images/emoji/green_book.png b/app/assets/images/emoji/green_book.png new file mode 100644 index 000000000..e86651e5c Binary files /dev/null and b/app/assets/images/emoji/green_book.png differ diff --git a/app/assets/images/emoji/green_heart.png b/app/assets/images/emoji/green_heart.png new file mode 100644 index 000000000..7289cb814 Binary files /dev/null and b/app/assets/images/emoji/green_heart.png differ diff --git a/app/assets/images/emoji/grey_exclamation.png b/app/assets/images/emoji/grey_exclamation.png new file mode 100644 index 000000000..a50d265e9 Binary files /dev/null and b/app/assets/images/emoji/grey_exclamation.png differ diff --git a/app/assets/images/emoji/grey_question.png b/app/assets/images/emoji/grey_question.png new file mode 100644 index 000000000..57db41ead Binary files /dev/null and b/app/assets/images/emoji/grey_question.png differ diff --git a/app/assets/images/emoji/grimacing.png b/app/assets/images/emoji/grimacing.png new file mode 100644 index 000000000..f78e9407d Binary files /dev/null and b/app/assets/images/emoji/grimacing.png differ diff --git a/app/assets/images/emoji/grin.png b/app/assets/images/emoji/grin.png new file mode 100644 index 000000000..591cfcef8 Binary files /dev/null and b/app/assets/images/emoji/grin.png differ diff --git a/app/assets/images/emoji/grinning.png b/app/assets/images/emoji/grinning.png new file mode 100644 index 000000000..0ef00d79d Binary files /dev/null and b/app/assets/images/emoji/grinning.png differ diff --git a/app/assets/images/emoji/guardsman.png b/app/assets/images/emoji/guardsman.png new file mode 100644 index 000000000..b67b335d6 Binary files /dev/null and b/app/assets/images/emoji/guardsman.png differ diff --git a/app/assets/images/emoji/guitar.png b/app/assets/images/emoji/guitar.png new file mode 100644 index 000000000..2b7fa43c9 Binary files /dev/null and b/app/assets/images/emoji/guitar.png differ diff --git a/app/assets/images/emoji/gun.png b/app/assets/images/emoji/gun.png new file mode 100644 index 000000000..c49dc52c6 Binary files /dev/null and b/app/assets/images/emoji/gun.png differ diff --git a/app/assets/images/emoji/haircut.png b/app/assets/images/emoji/haircut.png new file mode 100644 index 000000000..902d273f6 Binary files /dev/null and b/app/assets/images/emoji/haircut.png differ diff --git a/app/assets/images/emoji/hamburger.png b/app/assets/images/emoji/hamburger.png new file mode 100644 index 000000000..9f1a3fdff Binary files /dev/null and b/app/assets/images/emoji/hamburger.png differ diff --git a/app/assets/images/emoji/hammer.png b/app/assets/images/emoji/hammer.png new file mode 100644 index 000000000..6b75bc37b Binary files /dev/null and b/app/assets/images/emoji/hammer.png differ diff --git a/app/assets/images/emoji/hamster.png b/app/assets/images/emoji/hamster.png new file mode 100644 index 000000000..ada9c3108 Binary files /dev/null and b/app/assets/images/emoji/hamster.png differ diff --git a/app/assets/images/emoji/hand.png b/app/assets/images/emoji/hand.png new file mode 100644 index 000000000..5e45c25a5 Binary files /dev/null and b/app/assets/images/emoji/hand.png differ diff --git a/app/assets/images/emoji/handbag.png b/app/assets/images/emoji/handbag.png new file mode 100644 index 000000000..d7adf04dd Binary files /dev/null and b/app/assets/images/emoji/handbag.png differ diff --git a/app/assets/images/emoji/hankey.png b/app/assets/images/emoji/hankey.png new file mode 100644 index 000000000..73a4dc840 Binary files /dev/null and b/app/assets/images/emoji/hankey.png differ diff --git a/app/assets/images/emoji/hash.png b/app/assets/images/emoji/hash.png new file mode 100644 index 000000000..6765d7d3c Binary files /dev/null and b/app/assets/images/emoji/hash.png differ diff --git a/app/assets/images/emoji/hatched_chick.png b/app/assets/images/emoji/hatched_chick.png new file mode 100644 index 000000000..39c25bc7c Binary files /dev/null and b/app/assets/images/emoji/hatched_chick.png differ diff --git a/app/assets/images/emoji/hatching_chick.png b/app/assets/images/emoji/hatching_chick.png new file mode 100644 index 000000000..005a55519 Binary files /dev/null and b/app/assets/images/emoji/hatching_chick.png differ diff --git a/app/assets/images/emoji/headphones.png b/app/assets/images/emoji/headphones.png new file mode 100644 index 000000000..ad83000e6 Binary files /dev/null and b/app/assets/images/emoji/headphones.png differ diff --git a/app/assets/images/emoji/hear_no_evil.png b/app/assets/images/emoji/hear_no_evil.png new file mode 100644 index 000000000..f97a1f9a0 Binary files /dev/null and b/app/assets/images/emoji/hear_no_evil.png differ diff --git a/app/assets/images/emoji/heart.png b/app/assets/images/emoji/heart.png new file mode 100644 index 000000000..7d7790ce4 Binary files /dev/null and b/app/assets/images/emoji/heart.png differ diff --git a/app/assets/images/emoji/heart_decoration.png b/app/assets/images/emoji/heart_decoration.png new file mode 100644 index 000000000..b40a48675 Binary files /dev/null and b/app/assets/images/emoji/heart_decoration.png differ diff --git a/app/assets/images/emoji/heart_eyes.png b/app/assets/images/emoji/heart_eyes.png new file mode 100644 index 000000000..0e5794270 Binary files /dev/null and b/app/assets/images/emoji/heart_eyes.png differ diff --git a/app/assets/images/emoji/heart_eyes_cat.png b/app/assets/images/emoji/heart_eyes_cat.png new file mode 100644 index 000000000..eeba240e5 Binary files /dev/null and b/app/assets/images/emoji/heart_eyes_cat.png differ diff --git a/app/assets/images/emoji/heartbeat.png b/app/assets/images/emoji/heartbeat.png new file mode 100644 index 000000000..b6628f6fa Binary files /dev/null and b/app/assets/images/emoji/heartbeat.png differ diff --git a/app/assets/images/emoji/heartpulse.png b/app/assets/images/emoji/heartpulse.png new file mode 100644 index 000000000..a7491cbea Binary files /dev/null and b/app/assets/images/emoji/heartpulse.png differ diff --git a/app/assets/images/emoji/hearts.png b/app/assets/images/emoji/hearts.png new file mode 100644 index 000000000..e89471538 Binary files /dev/null and b/app/assets/images/emoji/hearts.png differ diff --git a/app/assets/images/emoji/heavy_check_mark.png b/app/assets/images/emoji/heavy_check_mark.png new file mode 100644 index 000000000..336d2626d Binary files /dev/null and b/app/assets/images/emoji/heavy_check_mark.png differ diff --git a/app/assets/images/emoji/heavy_division_sign.png b/app/assets/images/emoji/heavy_division_sign.png new file mode 100644 index 000000000..ac757a238 Binary files /dev/null and b/app/assets/images/emoji/heavy_division_sign.png differ diff --git a/app/assets/images/emoji/heavy_dollar_sign.png b/app/assets/images/emoji/heavy_dollar_sign.png new file mode 100644 index 000000000..361e26aef Binary files /dev/null and b/app/assets/images/emoji/heavy_dollar_sign.png differ diff --git a/app/assets/images/emoji/heavy_exclamation_mark.png b/app/assets/images/emoji/heavy_exclamation_mark.png new file mode 100644 index 000000000..4c560f5e3 Binary files /dev/null and b/app/assets/images/emoji/heavy_exclamation_mark.png differ diff --git a/app/assets/images/emoji/heavy_minus_sign.png b/app/assets/images/emoji/heavy_minus_sign.png new file mode 100644 index 000000000..b8d3d82f2 Binary files /dev/null and b/app/assets/images/emoji/heavy_minus_sign.png differ diff --git a/app/assets/images/emoji/heavy_multiplication_x.png b/app/assets/images/emoji/heavy_multiplication_x.png new file mode 100644 index 000000000..13d666078 Binary files /dev/null and b/app/assets/images/emoji/heavy_multiplication_x.png differ diff --git a/app/assets/images/emoji/heavy_plus_sign.png b/app/assets/images/emoji/heavy_plus_sign.png new file mode 100644 index 000000000..61595387b Binary files /dev/null and b/app/assets/images/emoji/heavy_plus_sign.png differ diff --git a/app/assets/images/emoji/helicopter.png b/app/assets/images/emoji/helicopter.png new file mode 100644 index 000000000..8e82a0d58 Binary files /dev/null and b/app/assets/images/emoji/helicopter.png differ diff --git a/app/assets/images/emoji/herb.png b/app/assets/images/emoji/herb.png new file mode 100644 index 000000000..de1ff1b73 Binary files /dev/null and b/app/assets/images/emoji/herb.png differ diff --git a/app/assets/images/emoji/hibiscus.png b/app/assets/images/emoji/hibiscus.png new file mode 100644 index 000000000..32a3774c0 Binary files /dev/null and b/app/assets/images/emoji/hibiscus.png differ diff --git a/app/assets/images/emoji/high_brightness.png b/app/assets/images/emoji/high_brightness.png new file mode 100644 index 000000000..ba9de7d40 Binary files /dev/null and b/app/assets/images/emoji/high_brightness.png differ diff --git a/app/assets/images/emoji/high_heel.png b/app/assets/images/emoji/high_heel.png new file mode 100644 index 000000000..525b6a0dd Binary files /dev/null and b/app/assets/images/emoji/high_heel.png differ diff --git a/app/assets/images/emoji/hocho.png b/app/assets/images/emoji/hocho.png new file mode 100644 index 000000000..18eade0ac Binary files /dev/null and b/app/assets/images/emoji/hocho.png differ diff --git a/app/assets/images/emoji/honey_pot.png b/app/assets/images/emoji/honey_pot.png new file mode 100644 index 000000000..73278898a Binary files /dev/null and b/app/assets/images/emoji/honey_pot.png differ diff --git a/app/assets/images/emoji/honeybee.png b/app/assets/images/emoji/honeybee.png new file mode 100644 index 000000000..f53733953 Binary files /dev/null and b/app/assets/images/emoji/honeybee.png differ diff --git a/app/assets/images/emoji/horse.png b/app/assets/images/emoji/horse.png new file mode 100644 index 000000000..78d580ad3 Binary files /dev/null and b/app/assets/images/emoji/horse.png differ diff --git a/app/assets/images/emoji/horse_racing.png b/app/assets/images/emoji/horse_racing.png new file mode 100644 index 000000000..e3bbaec1d Binary files /dev/null and b/app/assets/images/emoji/horse_racing.png differ diff --git a/app/assets/images/emoji/hospital.png b/app/assets/images/emoji/hospital.png new file mode 100644 index 000000000..c05c49377 Binary files /dev/null and b/app/assets/images/emoji/hospital.png differ diff --git a/app/assets/images/emoji/hotel.png b/app/assets/images/emoji/hotel.png new file mode 100644 index 000000000..d29f276a1 Binary files /dev/null and b/app/assets/images/emoji/hotel.png differ diff --git a/app/assets/images/emoji/hotsprings.png b/app/assets/images/emoji/hotsprings.png new file mode 100644 index 000000000..a0bc9d75f Binary files /dev/null and b/app/assets/images/emoji/hotsprings.png differ diff --git a/app/assets/images/emoji/hourglass.png b/app/assets/images/emoji/hourglass.png new file mode 100644 index 000000000..405aab41b Binary files /dev/null and b/app/assets/images/emoji/hourglass.png differ diff --git a/app/assets/images/emoji/hourglass_flowing_sand.png b/app/assets/images/emoji/hourglass_flowing_sand.png new file mode 100644 index 000000000..52c9eb704 Binary files /dev/null and b/app/assets/images/emoji/hourglass_flowing_sand.png differ diff --git a/app/assets/images/emoji/house.png b/app/assets/images/emoji/house.png new file mode 100644 index 000000000..95b9ee094 Binary files /dev/null and b/app/assets/images/emoji/house.png differ diff --git a/app/assets/images/emoji/house_with_garden.png b/app/assets/images/emoji/house_with_garden.png new file mode 100644 index 000000000..eccbfe943 Binary files /dev/null and b/app/assets/images/emoji/house_with_garden.png differ diff --git a/app/assets/images/emoji/hurtrealbad.png b/app/assets/images/emoji/hurtrealbad.png new file mode 100644 index 000000000..146ef1a6a Binary files /dev/null and b/app/assets/images/emoji/hurtrealbad.png differ diff --git a/app/assets/images/emoji/hushed.png b/app/assets/images/emoji/hushed.png new file mode 100644 index 000000000..afa3f6686 Binary files /dev/null and b/app/assets/images/emoji/hushed.png differ diff --git a/app/assets/images/emoji/ice_cream.png b/app/assets/images/emoji/ice_cream.png new file mode 100644 index 000000000..190be0165 Binary files /dev/null and b/app/assets/images/emoji/ice_cream.png differ diff --git a/app/assets/images/emoji/icecream.png b/app/assets/images/emoji/icecream.png new file mode 100644 index 000000000..871ce0976 Binary files /dev/null and b/app/assets/images/emoji/icecream.png differ diff --git a/app/assets/images/emoji/id.png b/app/assets/images/emoji/id.png new file mode 100644 index 000000000..47437a76d Binary files /dev/null and b/app/assets/images/emoji/id.png differ diff --git a/app/assets/images/emoji/ideograph_advantage.png b/app/assets/images/emoji/ideograph_advantage.png new file mode 100644 index 000000000..e79af7844 Binary files /dev/null and b/app/assets/images/emoji/ideograph_advantage.png differ diff --git a/app/assets/images/emoji/imp.png b/app/assets/images/emoji/imp.png new file mode 100644 index 000000000..48e570105 Binary files /dev/null and b/app/assets/images/emoji/imp.png differ diff --git a/app/assets/images/emoji/inbox_tray.png b/app/assets/images/emoji/inbox_tray.png new file mode 100644 index 000000000..e2df0f897 Binary files /dev/null and b/app/assets/images/emoji/inbox_tray.png differ diff --git a/app/assets/images/emoji/incoming_envelope.png b/app/assets/images/emoji/incoming_envelope.png new file mode 100644 index 000000000..afc827125 Binary files /dev/null and b/app/assets/images/emoji/incoming_envelope.png differ diff --git a/app/assets/images/emoji/information_desk_person.png b/app/assets/images/emoji/information_desk_person.png new file mode 100644 index 000000000..52c0a50a3 Binary files /dev/null and b/app/assets/images/emoji/information_desk_person.png differ diff --git a/app/assets/images/emoji/information_source.png b/app/assets/images/emoji/information_source.png new file mode 100644 index 000000000..9cb8b09b2 Binary files /dev/null and b/app/assets/images/emoji/information_source.png differ diff --git a/app/assets/images/emoji/innocent.png b/app/assets/images/emoji/innocent.png new file mode 100644 index 000000000..503b614f8 Binary files /dev/null and b/app/assets/images/emoji/innocent.png differ diff --git a/app/assets/images/emoji/interrobang.png b/app/assets/images/emoji/interrobang.png new file mode 100644 index 000000000..64304b9f5 Binary files /dev/null and b/app/assets/images/emoji/interrobang.png differ diff --git a/app/assets/images/emoji/iphone.png b/app/assets/images/emoji/iphone.png new file mode 100644 index 000000000..df007103b Binary files /dev/null and b/app/assets/images/emoji/iphone.png differ diff --git a/app/assets/images/emoji/it.png b/app/assets/images/emoji/it.png new file mode 100644 index 000000000..70bc9f324 Binary files /dev/null and b/app/assets/images/emoji/it.png differ diff --git a/app/assets/images/emoji/izakaya_lantern.png b/app/assets/images/emoji/izakaya_lantern.png new file mode 100644 index 000000000..18730ad55 Binary files /dev/null and b/app/assets/images/emoji/izakaya_lantern.png differ diff --git a/app/assets/images/emoji/jack_o_lantern.png b/app/assets/images/emoji/jack_o_lantern.png new file mode 100644 index 000000000..1f7667ea4 Binary files /dev/null and b/app/assets/images/emoji/jack_o_lantern.png differ diff --git a/app/assets/images/emoji/japan.png b/app/assets/images/emoji/japan.png new file mode 100644 index 000000000..459328035 Binary files /dev/null and b/app/assets/images/emoji/japan.png differ diff --git a/app/assets/images/emoji/japanese_castle.png b/app/assets/images/emoji/japanese_castle.png new file mode 100644 index 000000000..f225ab217 Binary files /dev/null and b/app/assets/images/emoji/japanese_castle.png differ diff --git a/app/assets/images/emoji/japanese_goblin.png b/app/assets/images/emoji/japanese_goblin.png new file mode 100644 index 000000000..bd21b1875 Binary files /dev/null and b/app/assets/images/emoji/japanese_goblin.png differ diff --git a/app/assets/images/emoji/japanese_ogre.png b/app/assets/images/emoji/japanese_ogre.png new file mode 100644 index 000000000..e9f5471c9 Binary files /dev/null and b/app/assets/images/emoji/japanese_ogre.png differ diff --git a/app/assets/images/emoji/jeans.png b/app/assets/images/emoji/jeans.png new file mode 100644 index 000000000..d721cea54 Binary files /dev/null and b/app/assets/images/emoji/jeans.png differ diff --git a/app/assets/images/emoji/joy.png b/app/assets/images/emoji/joy.png new file mode 100644 index 000000000..47df693d4 Binary files /dev/null and b/app/assets/images/emoji/joy.png differ diff --git a/app/assets/images/emoji/joy_cat.png b/app/assets/images/emoji/joy_cat.png new file mode 100644 index 000000000..6c60cb0ef Binary files /dev/null and b/app/assets/images/emoji/joy_cat.png differ diff --git a/app/assets/images/emoji/jp.png b/app/assets/images/emoji/jp.png new file mode 100644 index 000000000..b786efbbd Binary files /dev/null and b/app/assets/images/emoji/jp.png differ diff --git a/app/assets/images/emoji/key.png b/app/assets/images/emoji/key.png new file mode 100644 index 000000000..34673213f Binary files /dev/null and b/app/assets/images/emoji/key.png differ diff --git a/app/assets/images/emoji/keycap_ten.png b/app/assets/images/emoji/keycap_ten.png new file mode 100644 index 000000000..71dac1c1c Binary files /dev/null and b/app/assets/images/emoji/keycap_ten.png differ diff --git a/app/assets/images/emoji/kimono.png b/app/assets/images/emoji/kimono.png new file mode 100644 index 000000000..34ffe137d Binary files /dev/null and b/app/assets/images/emoji/kimono.png differ diff --git a/app/assets/images/emoji/kiss.png b/app/assets/images/emoji/kiss.png new file mode 100644 index 000000000..4ae2c2b5d Binary files /dev/null and b/app/assets/images/emoji/kiss.png differ diff --git a/app/assets/images/emoji/kissing.png b/app/assets/images/emoji/kissing.png new file mode 100644 index 000000000..eb049c80b Binary files /dev/null and b/app/assets/images/emoji/kissing.png differ diff --git a/app/assets/images/emoji/kissing_cat.png b/app/assets/images/emoji/kissing_cat.png new file mode 100644 index 000000000..adc62fbe3 Binary files /dev/null and b/app/assets/images/emoji/kissing_cat.png differ diff --git a/app/assets/images/emoji/kissing_closed_eyes.png b/app/assets/images/emoji/kissing_closed_eyes.png new file mode 100644 index 000000000..449de1970 Binary files /dev/null and b/app/assets/images/emoji/kissing_closed_eyes.png differ diff --git a/app/assets/images/emoji/kissing_heart.png b/app/assets/images/emoji/kissing_heart.png new file mode 100644 index 000000000..af9a80b7f Binary files /dev/null and b/app/assets/images/emoji/kissing_heart.png differ diff --git a/app/assets/images/emoji/kissing_smiling_eyes.png b/app/assets/images/emoji/kissing_smiling_eyes.png new file mode 100644 index 000000000..d85706e70 Binary files /dev/null and b/app/assets/images/emoji/kissing_smiling_eyes.png differ diff --git a/app/assets/images/emoji/koala.png b/app/assets/images/emoji/koala.png new file mode 100644 index 000000000..e17bd3cf5 Binary files /dev/null and b/app/assets/images/emoji/koala.png differ diff --git a/app/assets/images/emoji/koko.png b/app/assets/images/emoji/koko.png new file mode 100644 index 000000000..3bef28c9f Binary files /dev/null and b/app/assets/images/emoji/koko.png differ diff --git a/app/assets/images/emoji/kr.png b/app/assets/images/emoji/kr.png new file mode 100644 index 000000000..b4c0c1b67 Binary files /dev/null and b/app/assets/images/emoji/kr.png differ diff --git a/app/assets/images/emoji/lantern.png b/app/assets/images/emoji/lantern.png new file mode 100644 index 000000000..18730ad55 Binary files /dev/null and b/app/assets/images/emoji/lantern.png differ diff --git a/app/assets/images/emoji/large_blue_circle.png b/app/assets/images/emoji/large_blue_circle.png new file mode 100644 index 000000000..a5b4ad4aa Binary files /dev/null and b/app/assets/images/emoji/large_blue_circle.png differ diff --git a/app/assets/images/emoji/large_blue_diamond.png b/app/assets/images/emoji/large_blue_diamond.png new file mode 100644 index 000000000..f4598ec0f Binary files /dev/null and b/app/assets/images/emoji/large_blue_diamond.png differ diff --git a/app/assets/images/emoji/large_orange_diamond.png b/app/assets/images/emoji/large_orange_diamond.png new file mode 100644 index 000000000..46d52e5cb Binary files /dev/null and b/app/assets/images/emoji/large_orange_diamond.png differ diff --git a/app/assets/images/emoji/last_quarter_moon.png b/app/assets/images/emoji/last_quarter_moon.png new file mode 100644 index 000000000..355e3c3f7 Binary files /dev/null and b/app/assets/images/emoji/last_quarter_moon.png differ diff --git a/app/assets/images/emoji/last_quarter_moon_with_face.png b/app/assets/images/emoji/last_quarter_moon_with_face.png new file mode 100644 index 000000000..9ece82dfe Binary files /dev/null and b/app/assets/images/emoji/last_quarter_moon_with_face.png differ diff --git a/app/assets/images/emoji/laughing.png b/app/assets/images/emoji/laughing.png new file mode 100644 index 000000000..11c91eb22 Binary files /dev/null and b/app/assets/images/emoji/laughing.png differ diff --git a/app/assets/images/emoji/leaves.png b/app/assets/images/emoji/leaves.png new file mode 100644 index 000000000..801e578e6 Binary files /dev/null and b/app/assets/images/emoji/leaves.png differ diff --git a/app/assets/images/emoji/ledger.png b/app/assets/images/emoji/ledger.png new file mode 100644 index 000000000..e4f72acea Binary files /dev/null and b/app/assets/images/emoji/ledger.png differ diff --git a/app/assets/images/emoji/left_luggage.png b/app/assets/images/emoji/left_luggage.png new file mode 100644 index 000000000..1c08b464d Binary files /dev/null and b/app/assets/images/emoji/left_luggage.png differ diff --git a/app/assets/images/emoji/left_right_arrow.png b/app/assets/images/emoji/left_right_arrow.png new file mode 100644 index 000000000..b9fd11c51 Binary files /dev/null and b/app/assets/images/emoji/left_right_arrow.png differ diff --git a/app/assets/images/emoji/leftwards_arrow_with_hook.png b/app/assets/images/emoji/leftwards_arrow_with_hook.png new file mode 100644 index 000000000..bc45dfefd Binary files /dev/null and b/app/assets/images/emoji/leftwards_arrow_with_hook.png differ diff --git a/app/assets/images/emoji/lemon.png b/app/assets/images/emoji/lemon.png new file mode 100644 index 000000000..9814dc959 Binary files /dev/null and b/app/assets/images/emoji/lemon.png differ diff --git a/app/assets/images/emoji/leo.png b/app/assets/images/emoji/leo.png new file mode 100644 index 000000000..e025933b2 Binary files /dev/null and b/app/assets/images/emoji/leo.png differ diff --git a/app/assets/images/emoji/leopard.png b/app/assets/images/emoji/leopard.png new file mode 100644 index 000000000..8abfc4a27 Binary files /dev/null and b/app/assets/images/emoji/leopard.png differ diff --git a/app/assets/images/emoji/libra.png b/app/assets/images/emoji/libra.png new file mode 100644 index 000000000..c9062dd2e Binary files /dev/null and b/app/assets/images/emoji/libra.png differ diff --git a/app/assets/images/emoji/light_rail.png b/app/assets/images/emoji/light_rail.png new file mode 100644 index 000000000..bcfe801ee Binary files /dev/null and b/app/assets/images/emoji/light_rail.png differ diff --git a/app/assets/images/emoji/link.png b/app/assets/images/emoji/link.png new file mode 100644 index 000000000..ffb8f62ce Binary files /dev/null and b/app/assets/images/emoji/link.png differ diff --git a/app/assets/images/emoji/lips.png b/app/assets/images/emoji/lips.png new file mode 100644 index 000000000..826ed1102 Binary files /dev/null and b/app/assets/images/emoji/lips.png differ diff --git a/app/assets/images/emoji/lipstick.png b/app/assets/images/emoji/lipstick.png new file mode 100644 index 000000000..82f990c56 Binary files /dev/null and b/app/assets/images/emoji/lipstick.png differ diff --git a/app/assets/images/emoji/lock.png b/app/assets/images/emoji/lock.png new file mode 100644 index 000000000..4892b0235 Binary files /dev/null and b/app/assets/images/emoji/lock.png differ diff --git a/app/assets/images/emoji/lock_with_ink_pen.png b/app/assets/images/emoji/lock_with_ink_pen.png new file mode 100644 index 000000000..375e67e82 Binary files /dev/null and b/app/assets/images/emoji/lock_with_ink_pen.png differ diff --git a/app/assets/images/emoji/lollipop.png b/app/assets/images/emoji/lollipop.png new file mode 100644 index 000000000..ba55e7093 Binary files /dev/null and b/app/assets/images/emoji/lollipop.png differ diff --git a/app/assets/images/emoji/loop.png b/app/assets/images/emoji/loop.png new file mode 100644 index 000000000..ef34df3a4 Binary files /dev/null and b/app/assets/images/emoji/loop.png differ diff --git a/app/assets/images/emoji/loudspeaker.png b/app/assets/images/emoji/loudspeaker.png new file mode 100644 index 000000000..752385e52 Binary files /dev/null and b/app/assets/images/emoji/loudspeaker.png differ diff --git a/app/assets/images/emoji/love_hotel.png b/app/assets/images/emoji/love_hotel.png new file mode 100644 index 000000000..44d7db828 Binary files /dev/null and b/app/assets/images/emoji/love_hotel.png differ diff --git a/app/assets/images/emoji/love_letter.png b/app/assets/images/emoji/love_letter.png new file mode 100644 index 000000000..e29981f44 Binary files /dev/null and b/app/assets/images/emoji/love_letter.png differ diff --git a/app/assets/images/emoji/low_brightness.png b/app/assets/images/emoji/low_brightness.png new file mode 100644 index 000000000..ea15bde4f Binary files /dev/null and b/app/assets/images/emoji/low_brightness.png differ diff --git a/app/assets/images/emoji/m.png b/app/assets/images/emoji/m.png new file mode 100644 index 000000000..7424665e2 Binary files /dev/null and b/app/assets/images/emoji/m.png differ diff --git a/app/assets/images/emoji/mag.png b/app/assets/images/emoji/mag.png new file mode 100644 index 000000000..aa5b1d7c4 Binary files /dev/null and b/app/assets/images/emoji/mag.png differ diff --git a/app/assets/images/emoji/mag_right.png b/app/assets/images/emoji/mag_right.png new file mode 100644 index 000000000..6e6cf11e6 Binary files /dev/null and b/app/assets/images/emoji/mag_right.png differ diff --git a/app/assets/images/emoji/mahjong.png b/app/assets/images/emoji/mahjong.png new file mode 100644 index 000000000..f51ce65fd Binary files /dev/null and b/app/assets/images/emoji/mahjong.png differ diff --git a/app/assets/images/emoji/mailbox.png b/app/assets/images/emoji/mailbox.png new file mode 100644 index 000000000..8351e7076 Binary files /dev/null and b/app/assets/images/emoji/mailbox.png differ diff --git a/app/assets/images/emoji/mailbox_closed.png b/app/assets/images/emoji/mailbox_closed.png new file mode 100644 index 000000000..a5982b69b Binary files /dev/null and b/app/assets/images/emoji/mailbox_closed.png differ diff --git a/app/assets/images/emoji/mailbox_with_mail.png b/app/assets/images/emoji/mailbox_with_mail.png new file mode 100644 index 000000000..dae345943 Binary files /dev/null and b/app/assets/images/emoji/mailbox_with_mail.png differ diff --git a/app/assets/images/emoji/mailbox_with_no_mail.png b/app/assets/images/emoji/mailbox_with_no_mail.png new file mode 100644 index 000000000..59f15c5d7 Binary files /dev/null and b/app/assets/images/emoji/mailbox_with_no_mail.png differ diff --git a/app/assets/images/emoji/man.png b/app/assets/images/emoji/man.png new file mode 100644 index 000000000..d9bfa26a6 Binary files /dev/null and b/app/assets/images/emoji/man.png differ diff --git a/app/assets/images/emoji/man_with_gua_pi_mao.png b/app/assets/images/emoji/man_with_gua_pi_mao.png new file mode 100644 index 000000000..7aad74b55 Binary files /dev/null and b/app/assets/images/emoji/man_with_gua_pi_mao.png differ diff --git a/app/assets/images/emoji/man_with_turban.png b/app/assets/images/emoji/man_with_turban.png new file mode 100644 index 000000000..036604caf Binary files /dev/null and b/app/assets/images/emoji/man_with_turban.png differ diff --git a/app/assets/images/emoji/mans_shoe.png b/app/assets/images/emoji/mans_shoe.png new file mode 100644 index 000000000..ecba9ba7d Binary files /dev/null and b/app/assets/images/emoji/mans_shoe.png differ diff --git a/app/assets/images/emoji/maple_leaf.png b/app/assets/images/emoji/maple_leaf.png new file mode 100644 index 000000000..4e9b47207 Binary files /dev/null and b/app/assets/images/emoji/maple_leaf.png differ diff --git a/app/assets/images/emoji/mask.png b/app/assets/images/emoji/mask.png new file mode 100644 index 000000000..05887e99c Binary files /dev/null and b/app/assets/images/emoji/mask.png differ diff --git a/app/assets/images/emoji/massage.png b/app/assets/images/emoji/massage.png new file mode 100644 index 000000000..dd30d1597 Binary files /dev/null and b/app/assets/images/emoji/massage.png differ diff --git a/app/assets/images/emoji/meat_on_bone.png b/app/assets/images/emoji/meat_on_bone.png new file mode 100644 index 000000000..d6b311b6b Binary files /dev/null and b/app/assets/images/emoji/meat_on_bone.png differ diff --git a/app/assets/images/emoji/mega.png b/app/assets/images/emoji/mega.png new file mode 100644 index 000000000..5d9319e72 Binary files /dev/null and b/app/assets/images/emoji/mega.png differ diff --git a/app/assets/images/emoji/melon.png b/app/assets/images/emoji/melon.png new file mode 100644 index 000000000..11c13cbbd Binary files /dev/null and b/app/assets/images/emoji/melon.png differ diff --git a/app/assets/images/emoji/memo.png b/app/assets/images/emoji/memo.png new file mode 100644 index 000000000..fc97ddbc9 Binary files /dev/null and b/app/assets/images/emoji/memo.png differ diff --git a/app/assets/images/emoji/mens.png b/app/assets/images/emoji/mens.png new file mode 100644 index 000000000..abccfc9f2 Binary files /dev/null and b/app/assets/images/emoji/mens.png differ diff --git a/app/assets/images/emoji/metal.png b/app/assets/images/emoji/metal.png new file mode 100644 index 000000000..94f1fda22 Binary files /dev/null and b/app/assets/images/emoji/metal.png differ diff --git a/app/assets/images/emoji/metro.png b/app/assets/images/emoji/metro.png new file mode 100644 index 000000000..7f34f6be3 Binary files /dev/null and b/app/assets/images/emoji/metro.png differ diff --git a/app/assets/images/emoji/microphone.png b/app/assets/images/emoji/microphone.png new file mode 100644 index 000000000..ce19a2bb6 Binary files /dev/null and b/app/assets/images/emoji/microphone.png differ diff --git a/app/assets/images/emoji/microscope.png b/app/assets/images/emoji/microscope.png new file mode 100644 index 000000000..f11d54c01 Binary files /dev/null and b/app/assets/images/emoji/microscope.png differ diff --git a/app/assets/images/emoji/milky_way.png b/app/assets/images/emoji/milky_way.png new file mode 100644 index 000000000..901090a12 Binary files /dev/null and b/app/assets/images/emoji/milky_way.png differ diff --git a/app/assets/images/emoji/minibus.png b/app/assets/images/emoji/minibus.png new file mode 100644 index 000000000..c52cef234 Binary files /dev/null and b/app/assets/images/emoji/minibus.png differ diff --git a/app/assets/images/emoji/minidisc.png b/app/assets/images/emoji/minidisc.png new file mode 100644 index 000000000..e19cc5d01 Binary files /dev/null and b/app/assets/images/emoji/minidisc.png differ diff --git a/app/assets/images/emoji/mobile_phone_off.png b/app/assets/images/emoji/mobile_phone_off.png new file mode 100644 index 000000000..fa16c763c Binary files /dev/null and b/app/assets/images/emoji/mobile_phone_off.png differ diff --git a/app/assets/images/emoji/money_with_wings.png b/app/assets/images/emoji/money_with_wings.png new file mode 100644 index 000000000..135e3981e Binary files /dev/null and b/app/assets/images/emoji/money_with_wings.png differ diff --git a/app/assets/images/emoji/moneybag.png b/app/assets/images/emoji/moneybag.png new file mode 100644 index 000000000..5546c04ba Binary files /dev/null and b/app/assets/images/emoji/moneybag.png differ diff --git a/app/assets/images/emoji/monkey.png b/app/assets/images/emoji/monkey.png new file mode 100644 index 000000000..640703597 Binary files /dev/null and b/app/assets/images/emoji/monkey.png differ diff --git a/app/assets/images/emoji/monkey_face.png b/app/assets/images/emoji/monkey_face.png new file mode 100644 index 000000000..6964cf4d5 Binary files /dev/null and b/app/assets/images/emoji/monkey_face.png differ diff --git a/app/assets/images/emoji/monorail.png b/app/assets/images/emoji/monorail.png new file mode 100644 index 000000000..913d30024 Binary files /dev/null and b/app/assets/images/emoji/monorail.png differ diff --git a/app/assets/images/emoji/moon.png b/app/assets/images/emoji/moon.png new file mode 100644 index 000000000..dd8c48458 Binary files /dev/null and b/app/assets/images/emoji/moon.png differ diff --git a/app/assets/images/emoji/mortar_board.png b/app/assets/images/emoji/mortar_board.png new file mode 100644 index 000000000..2e811b097 Binary files /dev/null and b/app/assets/images/emoji/mortar_board.png differ diff --git a/app/assets/images/emoji/mount_fuji.png b/app/assets/images/emoji/mount_fuji.png new file mode 100644 index 000000000..4c313e583 Binary files /dev/null and b/app/assets/images/emoji/mount_fuji.png differ diff --git a/app/assets/images/emoji/mountain_bicyclist.png b/app/assets/images/emoji/mountain_bicyclist.png new file mode 100644 index 000000000..b69889756 Binary files /dev/null and b/app/assets/images/emoji/mountain_bicyclist.png differ diff --git a/app/assets/images/emoji/mountain_cableway.png b/app/assets/images/emoji/mountain_cableway.png new file mode 100644 index 000000000..5688bb239 Binary files /dev/null and b/app/assets/images/emoji/mountain_cableway.png differ diff --git a/app/assets/images/emoji/mountain_railway.png b/app/assets/images/emoji/mountain_railway.png new file mode 100644 index 000000000..1f3d1aab5 Binary files /dev/null and b/app/assets/images/emoji/mountain_railway.png differ diff --git a/app/assets/images/emoji/mouse.png b/app/assets/images/emoji/mouse.png new file mode 100644 index 000000000..8ff162e2d Binary files /dev/null and b/app/assets/images/emoji/mouse.png differ diff --git a/app/assets/images/emoji/mouse2.png b/app/assets/images/emoji/mouse2.png new file mode 100644 index 000000000..2d777e5e1 Binary files /dev/null and b/app/assets/images/emoji/mouse2.png differ diff --git a/app/assets/images/emoji/movie_camera.png b/app/assets/images/emoji/movie_camera.png new file mode 100644 index 000000000..9c1438409 Binary files /dev/null and b/app/assets/images/emoji/movie_camera.png differ diff --git a/app/assets/images/emoji/moyai.png b/app/assets/images/emoji/moyai.png new file mode 100644 index 000000000..61a1a9c21 Binary files /dev/null and b/app/assets/images/emoji/moyai.png differ diff --git a/app/assets/images/emoji/muscle.png b/app/assets/images/emoji/muscle.png new file mode 100644 index 000000000..19f92efb6 Binary files /dev/null and b/app/assets/images/emoji/muscle.png differ diff --git a/app/assets/images/emoji/mushroom.png b/app/assets/images/emoji/mushroom.png new file mode 100644 index 000000000..5eeed8e79 Binary files /dev/null and b/app/assets/images/emoji/mushroom.png differ diff --git a/app/assets/images/emoji/musical_keyboard.png b/app/assets/images/emoji/musical_keyboard.png new file mode 100644 index 000000000..93647a4a3 Binary files /dev/null and b/app/assets/images/emoji/musical_keyboard.png differ diff --git a/app/assets/images/emoji/musical_note.png b/app/assets/images/emoji/musical_note.png new file mode 100644 index 000000000..68b261bcb Binary files /dev/null and b/app/assets/images/emoji/musical_note.png differ diff --git a/app/assets/images/emoji/musical_score.png b/app/assets/images/emoji/musical_score.png new file mode 100644 index 000000000..0c927d32f Binary files /dev/null and b/app/assets/images/emoji/musical_score.png differ diff --git a/app/assets/images/emoji/mute.png b/app/assets/images/emoji/mute.png new file mode 100644 index 000000000..4cf67c367 Binary files /dev/null and b/app/assets/images/emoji/mute.png differ diff --git a/app/assets/images/emoji/nail_care.png b/app/assets/images/emoji/nail_care.png new file mode 100644 index 000000000..6a66e63d2 Binary files /dev/null and b/app/assets/images/emoji/nail_care.png differ diff --git a/app/assets/images/emoji/name_badge.png b/app/assets/images/emoji/name_badge.png new file mode 100644 index 000000000..2b712dcd5 Binary files /dev/null and b/app/assets/images/emoji/name_badge.png differ diff --git a/app/assets/images/emoji/neckbeard.png b/app/assets/images/emoji/neckbeard.png new file mode 100644 index 000000000..15108fc97 Binary files /dev/null and b/app/assets/images/emoji/neckbeard.png differ diff --git a/app/assets/images/emoji/necktie.png b/app/assets/images/emoji/necktie.png new file mode 100644 index 000000000..80461c66f Binary files /dev/null and b/app/assets/images/emoji/necktie.png differ diff --git a/app/assets/images/emoji/negative_squared_cross_mark.png b/app/assets/images/emoji/negative_squared_cross_mark.png new file mode 100644 index 000000000..b47a0cece Binary files /dev/null and b/app/assets/images/emoji/negative_squared_cross_mark.png differ diff --git a/app/assets/images/emoji/neutral_face.png b/app/assets/images/emoji/neutral_face.png new file mode 100644 index 000000000..682a1ba06 Binary files /dev/null and b/app/assets/images/emoji/neutral_face.png differ diff --git a/app/assets/images/emoji/new.png b/app/assets/images/emoji/new.png new file mode 100644 index 000000000..28d1570e0 Binary files /dev/null and b/app/assets/images/emoji/new.png differ diff --git a/app/assets/images/emoji/new_moon.png b/app/assets/images/emoji/new_moon.png new file mode 100644 index 000000000..540239b1f Binary files /dev/null and b/app/assets/images/emoji/new_moon.png differ diff --git a/app/assets/images/emoji/new_moon_with_face.png b/app/assets/images/emoji/new_moon_with_face.png new file mode 100644 index 000000000..b9aff7a06 Binary files /dev/null and b/app/assets/images/emoji/new_moon_with_face.png differ diff --git a/app/assets/images/emoji/newspaper.png b/app/assets/images/emoji/newspaper.png new file mode 100644 index 000000000..d171394e6 Binary files /dev/null and b/app/assets/images/emoji/newspaper.png differ diff --git a/app/assets/images/emoji/ng.png b/app/assets/images/emoji/ng.png new file mode 100644 index 000000000..2ca180ae3 Binary files /dev/null and b/app/assets/images/emoji/ng.png differ diff --git a/app/assets/images/emoji/nine.png b/app/assets/images/emoji/nine.png new file mode 100644 index 000000000..8006cc909 Binary files /dev/null and b/app/assets/images/emoji/nine.png differ diff --git a/app/assets/images/emoji/no_bell.png b/app/assets/images/emoji/no_bell.png new file mode 100644 index 000000000..613b81cd2 Binary files /dev/null and b/app/assets/images/emoji/no_bell.png differ diff --git a/app/assets/images/emoji/no_bicycles.png b/app/assets/images/emoji/no_bicycles.png new file mode 100644 index 000000000..4b2621664 Binary files /dev/null and b/app/assets/images/emoji/no_bicycles.png differ diff --git a/app/assets/images/emoji/no_entry.png b/app/assets/images/emoji/no_entry.png new file mode 100644 index 000000000..cf2086a8e Binary files /dev/null and b/app/assets/images/emoji/no_entry.png differ diff --git a/app/assets/images/emoji/no_entry_sign.png b/app/assets/images/emoji/no_entry_sign.png new file mode 100644 index 000000000..a8444d18d Binary files /dev/null and b/app/assets/images/emoji/no_entry_sign.png differ diff --git a/app/assets/images/emoji/no_good.png b/app/assets/images/emoji/no_good.png new file mode 100644 index 000000000..d459a35bc Binary files /dev/null and b/app/assets/images/emoji/no_good.png differ diff --git a/app/assets/images/emoji/no_mobile_phones.png b/app/assets/images/emoji/no_mobile_phones.png new file mode 100644 index 000000000..41df57cf8 Binary files /dev/null and b/app/assets/images/emoji/no_mobile_phones.png differ diff --git a/app/assets/images/emoji/no_mouth.png b/app/assets/images/emoji/no_mouth.png new file mode 100644 index 000000000..d9ec7ca7d Binary files /dev/null and b/app/assets/images/emoji/no_mouth.png differ diff --git a/app/assets/images/emoji/no_pedestrians.png b/app/assets/images/emoji/no_pedestrians.png new file mode 100644 index 000000000..c35f530b2 Binary files /dev/null and b/app/assets/images/emoji/no_pedestrians.png differ diff --git a/app/assets/images/emoji/no_smoking.png b/app/assets/images/emoji/no_smoking.png new file mode 100644 index 000000000..eb11d7911 Binary files /dev/null and b/app/assets/images/emoji/no_smoking.png differ diff --git a/app/assets/images/emoji/non-potable_water.png b/app/assets/images/emoji/non-potable_water.png new file mode 100644 index 000000000..1b29d35b9 Binary files /dev/null and b/app/assets/images/emoji/non-potable_water.png differ diff --git a/app/assets/images/emoji/nose.png b/app/assets/images/emoji/nose.png new file mode 100644 index 000000000..ad17c16c2 Binary files /dev/null and b/app/assets/images/emoji/nose.png differ diff --git a/app/assets/images/emoji/notebook.png b/app/assets/images/emoji/notebook.png new file mode 100644 index 000000000..07ea6087e Binary files /dev/null and b/app/assets/images/emoji/notebook.png differ diff --git a/app/assets/images/emoji/notebook_with_decorative_cover.png b/app/assets/images/emoji/notebook_with_decorative_cover.png new file mode 100644 index 000000000..4f3b14c85 Binary files /dev/null and b/app/assets/images/emoji/notebook_with_decorative_cover.png differ diff --git a/app/assets/images/emoji/notes.png b/app/assets/images/emoji/notes.png new file mode 100644 index 000000000..a13147fae Binary files /dev/null and b/app/assets/images/emoji/notes.png differ diff --git a/app/assets/images/emoji/nut_and_bolt.png b/app/assets/images/emoji/nut_and_bolt.png new file mode 100644 index 000000000..bddfa72a7 Binary files /dev/null and b/app/assets/images/emoji/nut_and_bolt.png differ diff --git a/app/assets/images/emoji/o.png b/app/assets/images/emoji/o.png new file mode 100644 index 000000000..0ededebe3 Binary files /dev/null and b/app/assets/images/emoji/o.png differ diff --git a/app/assets/images/emoji/o2.png b/app/assets/images/emoji/o2.png new file mode 100644 index 000000000..d85f9fb98 Binary files /dev/null and b/app/assets/images/emoji/o2.png differ diff --git a/app/assets/images/emoji/ocean.png b/app/assets/images/emoji/ocean.png new file mode 100644 index 000000000..f8d520cd4 Binary files /dev/null and b/app/assets/images/emoji/ocean.png differ diff --git a/app/assets/images/emoji/octocat.png b/app/assets/images/emoji/octocat.png new file mode 100644 index 000000000..9d74d902a Binary files /dev/null and b/app/assets/images/emoji/octocat.png differ diff --git a/app/assets/images/emoji/octopus.png b/app/assets/images/emoji/octopus.png new file mode 100644 index 000000000..52ce64b46 Binary files /dev/null and b/app/assets/images/emoji/octopus.png differ diff --git a/app/assets/images/emoji/oden.png b/app/assets/images/emoji/oden.png new file mode 100644 index 000000000..73add1c73 Binary files /dev/null and b/app/assets/images/emoji/oden.png differ diff --git a/app/assets/images/emoji/office.png b/app/assets/images/emoji/office.png new file mode 100644 index 000000000..3f20b5642 Binary files /dev/null and b/app/assets/images/emoji/office.png differ diff --git a/app/assets/images/emoji/ok.png b/app/assets/images/emoji/ok.png new file mode 100644 index 000000000..6433d1a90 Binary files /dev/null and b/app/assets/images/emoji/ok.png differ diff --git a/app/assets/images/emoji/ok_hand.png b/app/assets/images/emoji/ok_hand.png new file mode 100644 index 000000000..3177439dc Binary files /dev/null and b/app/assets/images/emoji/ok_hand.png differ diff --git a/app/assets/images/emoji/ok_woman.png b/app/assets/images/emoji/ok_woman.png new file mode 100644 index 000000000..e8b98194e Binary files /dev/null and b/app/assets/images/emoji/ok_woman.png differ diff --git a/app/assets/images/emoji/older_man.png b/app/assets/images/emoji/older_man.png new file mode 100644 index 000000000..149f0cfb8 Binary files /dev/null and b/app/assets/images/emoji/older_man.png differ diff --git a/app/assets/images/emoji/older_woman.png b/app/assets/images/emoji/older_woman.png new file mode 100644 index 000000000..f839565f4 Binary files /dev/null and b/app/assets/images/emoji/older_woman.png differ diff --git a/app/assets/images/emoji/on.png b/app/assets/images/emoji/on.png new file mode 100644 index 000000000..3595387fb Binary files /dev/null and b/app/assets/images/emoji/on.png differ diff --git a/app/assets/images/emoji/oncoming_automobile.png b/app/assets/images/emoji/oncoming_automobile.png new file mode 100644 index 000000000..cb46de22c Binary files /dev/null and b/app/assets/images/emoji/oncoming_automobile.png differ diff --git a/app/assets/images/emoji/oncoming_bus.png b/app/assets/images/emoji/oncoming_bus.png new file mode 100644 index 000000000..3695f7623 Binary files /dev/null and b/app/assets/images/emoji/oncoming_bus.png differ diff --git a/app/assets/images/emoji/oncoming_police_car.png b/app/assets/images/emoji/oncoming_police_car.png new file mode 100644 index 000000000..af20e7eff Binary files /dev/null and b/app/assets/images/emoji/oncoming_police_car.png differ diff --git a/app/assets/images/emoji/oncoming_taxi.png b/app/assets/images/emoji/oncoming_taxi.png new file mode 100644 index 000000000..f78cf3103 Binary files /dev/null and b/app/assets/images/emoji/oncoming_taxi.png differ diff --git a/app/assets/images/emoji/one.png b/app/assets/images/emoji/one.png new file mode 100644 index 000000000..2d1f9f8c4 Binary files /dev/null and b/app/assets/images/emoji/one.png differ diff --git a/app/assets/images/emoji/open_book.png b/app/assets/images/emoji/open_book.png new file mode 100644 index 000000000..8b698415c Binary files /dev/null and b/app/assets/images/emoji/open_book.png differ diff --git a/app/assets/images/emoji/open_file_folder.png b/app/assets/images/emoji/open_file_folder.png new file mode 100644 index 000000000..2bbbbf5e7 Binary files /dev/null and b/app/assets/images/emoji/open_file_folder.png differ diff --git a/app/assets/images/emoji/open_hands.png b/app/assets/images/emoji/open_hands.png new file mode 100644 index 000000000..2cc25bd41 Binary files /dev/null and b/app/assets/images/emoji/open_hands.png differ diff --git a/app/assets/images/emoji/open_mouth.png b/app/assets/images/emoji/open_mouth.png new file mode 100644 index 000000000..e5283582c Binary files /dev/null and b/app/assets/images/emoji/open_mouth.png differ diff --git a/app/assets/images/emoji/ophiuchus.png b/app/assets/images/emoji/ophiuchus.png new file mode 100644 index 000000000..4eef715bc Binary files /dev/null and b/app/assets/images/emoji/ophiuchus.png differ diff --git a/app/assets/images/emoji/orange_book.png b/app/assets/images/emoji/orange_book.png new file mode 100644 index 000000000..49650d59e Binary files /dev/null and b/app/assets/images/emoji/orange_book.png differ diff --git a/app/assets/images/emoji/outbox_tray.png b/app/assets/images/emoji/outbox_tray.png new file mode 100644 index 000000000..7ad15e649 Binary files /dev/null and b/app/assets/images/emoji/outbox_tray.png differ diff --git a/app/assets/images/emoji/ox.png b/app/assets/images/emoji/ox.png new file mode 100644 index 000000000..f76698024 Binary files /dev/null and b/app/assets/images/emoji/ox.png differ diff --git a/app/assets/images/emoji/package.png b/app/assets/images/emoji/package.png new file mode 100644 index 000000000..26602af9d Binary files /dev/null and b/app/assets/images/emoji/package.png differ diff --git a/app/assets/images/emoji/page_facing_up.png b/app/assets/images/emoji/page_facing_up.png new file mode 100644 index 000000000..64cd2e1b2 Binary files /dev/null and b/app/assets/images/emoji/page_facing_up.png differ diff --git a/app/assets/images/emoji/page_with_curl.png b/app/assets/images/emoji/page_with_curl.png new file mode 100644 index 000000000..bf8f979d3 Binary files /dev/null and b/app/assets/images/emoji/page_with_curl.png differ diff --git a/app/assets/images/emoji/pager.png b/app/assets/images/emoji/pager.png new file mode 100644 index 000000000..e3e1fc44e Binary files /dev/null and b/app/assets/images/emoji/pager.png differ diff --git a/app/assets/images/emoji/palm_tree.png b/app/assets/images/emoji/palm_tree.png new file mode 100644 index 000000000..d534785ef Binary files /dev/null and b/app/assets/images/emoji/palm_tree.png differ diff --git a/app/assets/images/emoji/panda_face.png b/app/assets/images/emoji/panda_face.png new file mode 100644 index 000000000..a794fb17f Binary files /dev/null and b/app/assets/images/emoji/panda_face.png differ diff --git a/app/assets/images/emoji/paperclip.png b/app/assets/images/emoji/paperclip.png new file mode 100644 index 000000000..774412dc1 Binary files /dev/null and b/app/assets/images/emoji/paperclip.png differ diff --git a/app/assets/images/emoji/parking.png b/app/assets/images/emoji/parking.png new file mode 100644 index 000000000..c24af81cc Binary files /dev/null and b/app/assets/images/emoji/parking.png differ diff --git a/app/assets/images/emoji/part_alternation_mark.png b/app/assets/images/emoji/part_alternation_mark.png new file mode 100644 index 000000000..45dc9b851 Binary files /dev/null and b/app/assets/images/emoji/part_alternation_mark.png differ diff --git a/app/assets/images/emoji/partly_sunny.png b/app/assets/images/emoji/partly_sunny.png new file mode 100644 index 000000000..020dd5ff6 Binary files /dev/null and b/app/assets/images/emoji/partly_sunny.png differ diff --git a/app/assets/images/emoji/passport_control.png b/app/assets/images/emoji/passport_control.png new file mode 100644 index 000000000..675b76d37 Binary files /dev/null and b/app/assets/images/emoji/passport_control.png differ diff --git a/app/assets/images/emoji/paw_prints.png b/app/assets/images/emoji/paw_prints.png new file mode 100644 index 000000000..89b9fec9e Binary files /dev/null and b/app/assets/images/emoji/paw_prints.png differ diff --git a/app/assets/images/emoji/peach.png b/app/assets/images/emoji/peach.png new file mode 100644 index 000000000..ee2139ecb Binary files /dev/null and b/app/assets/images/emoji/peach.png differ diff --git a/app/assets/images/emoji/pear.png b/app/assets/images/emoji/pear.png new file mode 100644 index 000000000..f24aca8c0 Binary files /dev/null and b/app/assets/images/emoji/pear.png differ diff --git a/app/assets/images/emoji/pencil.png b/app/assets/images/emoji/pencil.png new file mode 100644 index 000000000..fc97ddbc9 Binary files /dev/null and b/app/assets/images/emoji/pencil.png differ diff --git a/app/assets/images/emoji/pencil2.png b/app/assets/images/emoji/pencil2.png new file mode 100644 index 000000000..e624373b4 Binary files /dev/null and b/app/assets/images/emoji/pencil2.png differ diff --git a/app/assets/images/emoji/penguin.png b/app/assets/images/emoji/penguin.png new file mode 100644 index 000000000..d8edbcb8f Binary files /dev/null and b/app/assets/images/emoji/penguin.png differ diff --git a/app/assets/images/emoji/pensive.png b/app/assets/images/emoji/pensive.png new file mode 100644 index 000000000..2f3bad945 Binary files /dev/null and b/app/assets/images/emoji/pensive.png differ diff --git a/app/assets/images/emoji/performing_arts.png b/app/assets/images/emoji/performing_arts.png new file mode 100644 index 000000000..899fbe5a7 Binary files /dev/null and b/app/assets/images/emoji/performing_arts.png differ diff --git a/app/assets/images/emoji/persevere.png b/app/assets/images/emoji/persevere.png new file mode 100644 index 000000000..c7e433e8e Binary files /dev/null and b/app/assets/images/emoji/persevere.png differ diff --git a/app/assets/images/emoji/person_frowning.png b/app/assets/images/emoji/person_frowning.png new file mode 100644 index 000000000..6f34d5e15 Binary files /dev/null and b/app/assets/images/emoji/person_frowning.png differ diff --git a/app/assets/images/emoji/person_with_blond_hair.png b/app/assets/images/emoji/person_with_blond_hair.png new file mode 100644 index 000000000..c144301cb Binary files /dev/null and b/app/assets/images/emoji/person_with_blond_hair.png differ diff --git a/app/assets/images/emoji/person_with_pouting_face.png b/app/assets/images/emoji/person_with_pouting_face.png new file mode 100644 index 000000000..c4a95c3b2 Binary files /dev/null and b/app/assets/images/emoji/person_with_pouting_face.png differ diff --git a/app/assets/images/emoji/phone.png b/app/assets/images/emoji/phone.png new file mode 100644 index 000000000..87d2559b5 Binary files /dev/null and b/app/assets/images/emoji/phone.png differ diff --git a/app/assets/images/emoji/pig.png b/app/assets/images/emoji/pig.png new file mode 100644 index 000000000..f7f273c73 Binary files /dev/null and b/app/assets/images/emoji/pig.png differ diff --git a/app/assets/images/emoji/pig2.png b/app/assets/images/emoji/pig2.png new file mode 100644 index 000000000..fec3374d7 Binary files /dev/null and b/app/assets/images/emoji/pig2.png differ diff --git a/app/assets/images/emoji/pig_nose.png b/app/assets/images/emoji/pig_nose.png new file mode 100644 index 000000000..38d612446 Binary files /dev/null and b/app/assets/images/emoji/pig_nose.png differ diff --git a/app/assets/images/emoji/pill.png b/app/assets/images/emoji/pill.png new file mode 100644 index 000000000..cd84a78ff Binary files /dev/null and b/app/assets/images/emoji/pill.png differ diff --git a/app/assets/images/emoji/pineapple.png b/app/assets/images/emoji/pineapple.png new file mode 100644 index 000000000..d6f8e2876 Binary files /dev/null and b/app/assets/images/emoji/pineapple.png differ diff --git a/app/assets/images/emoji/pisces.png b/app/assets/images/emoji/pisces.png new file mode 100644 index 000000000..5a2da0a05 Binary files /dev/null and b/app/assets/images/emoji/pisces.png differ diff --git a/app/assets/images/emoji/pizza.png b/app/assets/images/emoji/pizza.png new file mode 100644 index 000000000..460367d02 Binary files /dev/null and b/app/assets/images/emoji/pizza.png differ diff --git a/app/assets/images/emoji/point_down.png b/app/assets/images/emoji/point_down.png new file mode 100644 index 000000000..658c6d918 Binary files /dev/null and b/app/assets/images/emoji/point_down.png differ diff --git a/app/assets/images/emoji/point_left.png b/app/assets/images/emoji/point_left.png new file mode 100644 index 000000000..fee9cac4d Binary files /dev/null and b/app/assets/images/emoji/point_left.png differ diff --git a/app/assets/images/emoji/point_right.png b/app/assets/images/emoji/point_right.png new file mode 100644 index 000000000..b04e2849d Binary files /dev/null and b/app/assets/images/emoji/point_right.png differ diff --git a/app/assets/images/emoji/point_up.png b/app/assets/images/emoji/point_up.png new file mode 100644 index 000000000..01896e214 Binary files /dev/null and b/app/assets/images/emoji/point_up.png differ diff --git a/app/assets/images/emoji/point_up_2.png b/app/assets/images/emoji/point_up_2.png new file mode 100644 index 000000000..196d109a8 Binary files /dev/null and b/app/assets/images/emoji/point_up_2.png differ diff --git a/app/assets/images/emoji/police_car.png b/app/assets/images/emoji/police_car.png new file mode 100644 index 000000000..b8f17275e Binary files /dev/null and b/app/assets/images/emoji/police_car.png differ diff --git a/app/assets/images/emoji/poodle.png b/app/assets/images/emoji/poodle.png new file mode 100644 index 000000000..adac80bd9 Binary files /dev/null and b/app/assets/images/emoji/poodle.png differ diff --git a/app/assets/images/emoji/poop.png b/app/assets/images/emoji/poop.png new file mode 100644 index 000000000..73a4dc840 Binary files /dev/null and b/app/assets/images/emoji/poop.png differ diff --git a/app/assets/images/emoji/post_office.png b/app/assets/images/emoji/post_office.png new file mode 100644 index 000000000..43b59e30e Binary files /dev/null and b/app/assets/images/emoji/post_office.png differ diff --git a/app/assets/images/emoji/postal_horn.png b/app/assets/images/emoji/postal_horn.png new file mode 100644 index 000000000..e9b713bbe Binary files /dev/null and b/app/assets/images/emoji/postal_horn.png differ diff --git a/app/assets/images/emoji/postbox.png b/app/assets/images/emoji/postbox.png new file mode 100644 index 000000000..ce04b7008 Binary files /dev/null and b/app/assets/images/emoji/postbox.png differ diff --git a/app/assets/images/emoji/potable_water.png b/app/assets/images/emoji/potable_water.png new file mode 100644 index 000000000..e9fd56079 Binary files /dev/null and b/app/assets/images/emoji/potable_water.png differ diff --git a/app/assets/images/emoji/pouch.png b/app/assets/images/emoji/pouch.png new file mode 100644 index 000000000..0bc5879fc Binary files /dev/null and b/app/assets/images/emoji/pouch.png differ diff --git a/app/assets/images/emoji/poultry_leg.png b/app/assets/images/emoji/poultry_leg.png new file mode 100644 index 000000000..43ad85965 Binary files /dev/null and b/app/assets/images/emoji/poultry_leg.png differ diff --git a/app/assets/images/emoji/pound.png b/app/assets/images/emoji/pound.png new file mode 100644 index 000000000..f8be91d7a Binary files /dev/null and b/app/assets/images/emoji/pound.png differ diff --git a/app/assets/images/emoji/pouting_cat.png b/app/assets/images/emoji/pouting_cat.png new file mode 100644 index 000000000..4325fd48d Binary files /dev/null and b/app/assets/images/emoji/pouting_cat.png differ diff --git a/app/assets/images/emoji/pray.png b/app/assets/images/emoji/pray.png new file mode 100644 index 000000000..f86c992d5 Binary files /dev/null and b/app/assets/images/emoji/pray.png differ diff --git a/app/assets/images/emoji/princess.png b/app/assets/images/emoji/princess.png new file mode 100644 index 000000000..1ebb2ce9b Binary files /dev/null and b/app/assets/images/emoji/princess.png differ diff --git a/app/assets/images/emoji/punch.png b/app/assets/images/emoji/punch.png new file mode 100644 index 000000000..2d41fd37e Binary files /dev/null and b/app/assets/images/emoji/punch.png differ diff --git a/app/assets/images/emoji/purple_heart.png b/app/assets/images/emoji/purple_heart.png new file mode 100644 index 000000000..d5f875043 Binary files /dev/null and b/app/assets/images/emoji/purple_heart.png differ diff --git a/app/assets/images/emoji/purse.png b/app/assets/images/emoji/purse.png new file mode 100644 index 000000000..8f06a2b93 Binary files /dev/null and b/app/assets/images/emoji/purse.png differ diff --git a/app/assets/images/emoji/pushpin.png b/app/assets/images/emoji/pushpin.png new file mode 100644 index 000000000..540c4ecb8 Binary files /dev/null and b/app/assets/images/emoji/pushpin.png differ diff --git a/app/assets/images/emoji/put_litter_in_its_place.png b/app/assets/images/emoji/put_litter_in_its_place.png new file mode 100644 index 000000000..c2e350c2d Binary files /dev/null and b/app/assets/images/emoji/put_litter_in_its_place.png differ diff --git a/app/assets/images/emoji/question.png b/app/assets/images/emoji/question.png new file mode 100644 index 000000000..63fd7f837 Binary files /dev/null and b/app/assets/images/emoji/question.png differ diff --git a/app/assets/images/emoji/rabbit.png b/app/assets/images/emoji/rabbit.png new file mode 100644 index 000000000..5cb3ef6f0 Binary files /dev/null and b/app/assets/images/emoji/rabbit.png differ diff --git a/app/assets/images/emoji/rabbit2.png b/app/assets/images/emoji/rabbit2.png new file mode 100644 index 000000000..5bc993e79 Binary files /dev/null and b/app/assets/images/emoji/rabbit2.png differ diff --git a/app/assets/images/emoji/racehorse.png b/app/assets/images/emoji/racehorse.png new file mode 100644 index 000000000..4d09c64de Binary files /dev/null and b/app/assets/images/emoji/racehorse.png differ diff --git a/app/assets/images/emoji/radio.png b/app/assets/images/emoji/radio.png new file mode 100644 index 000000000..ea589efe3 Binary files /dev/null and b/app/assets/images/emoji/radio.png differ diff --git a/app/assets/images/emoji/radio_button.png b/app/assets/images/emoji/radio_button.png new file mode 100644 index 000000000..63755eec2 Binary files /dev/null and b/app/assets/images/emoji/radio_button.png differ diff --git a/app/assets/images/emoji/rage.png b/app/assets/images/emoji/rage.png new file mode 100644 index 000000000..c65ddff55 Binary files /dev/null and b/app/assets/images/emoji/rage.png differ diff --git a/app/assets/images/emoji/rage1.png b/app/assets/images/emoji/rage1.png new file mode 100644 index 000000000..dd2c84f92 Binary files /dev/null and b/app/assets/images/emoji/rage1.png differ diff --git a/app/assets/images/emoji/rage2.png b/app/assets/images/emoji/rage2.png new file mode 100644 index 000000000..f792e063b Binary files /dev/null and b/app/assets/images/emoji/rage2.png differ diff --git a/app/assets/images/emoji/rage3.png b/app/assets/images/emoji/rage3.png new file mode 100644 index 000000000..58764cbcb Binary files /dev/null and b/app/assets/images/emoji/rage3.png differ diff --git a/app/assets/images/emoji/rage4.png b/app/assets/images/emoji/rage4.png new file mode 100644 index 000000000..c726c94a2 Binary files /dev/null and b/app/assets/images/emoji/rage4.png differ diff --git a/app/assets/images/emoji/railway_car.png b/app/assets/images/emoji/railway_car.png new file mode 100644 index 000000000..22361158f Binary files /dev/null and b/app/assets/images/emoji/railway_car.png differ diff --git a/app/assets/images/emoji/rainbow.png b/app/assets/images/emoji/rainbow.png new file mode 100644 index 000000000..6b1faa037 Binary files /dev/null and b/app/assets/images/emoji/rainbow.png differ diff --git a/app/assets/images/emoji/raised_hand.png b/app/assets/images/emoji/raised_hand.png new file mode 100644 index 000000000..5e45c25a5 Binary files /dev/null and b/app/assets/images/emoji/raised_hand.png differ diff --git a/app/assets/images/emoji/raised_hands.png b/app/assets/images/emoji/raised_hands.png new file mode 100644 index 000000000..e03142bdc Binary files /dev/null and b/app/assets/images/emoji/raised_hands.png differ diff --git a/app/assets/images/emoji/raising_hand.png b/app/assets/images/emoji/raising_hand.png new file mode 100644 index 000000000..e1741a40e Binary files /dev/null and b/app/assets/images/emoji/raising_hand.png differ diff --git a/app/assets/images/emoji/ram.png b/app/assets/images/emoji/ram.png new file mode 100644 index 000000000..5ea7bfbc0 Binary files /dev/null and b/app/assets/images/emoji/ram.png differ diff --git a/app/assets/images/emoji/ramen.png b/app/assets/images/emoji/ramen.png new file mode 100644 index 000000000..78dc7d537 Binary files /dev/null and b/app/assets/images/emoji/ramen.png differ diff --git a/app/assets/images/emoji/rat.png b/app/assets/images/emoji/rat.png new file mode 100644 index 000000000..1c463dfde Binary files /dev/null and b/app/assets/images/emoji/rat.png differ diff --git a/app/assets/images/emoji/recycle.png b/app/assets/images/emoji/recycle.png new file mode 100644 index 000000000..99104c0e9 Binary files /dev/null and b/app/assets/images/emoji/recycle.png differ diff --git a/app/assets/images/emoji/red_car.png b/app/assets/images/emoji/red_car.png new file mode 100644 index 000000000..d70a2f062 Binary files /dev/null and b/app/assets/images/emoji/red_car.png differ diff --git a/app/assets/images/emoji/red_circle.png b/app/assets/images/emoji/red_circle.png new file mode 100644 index 000000000..b391289b2 Binary files /dev/null and b/app/assets/images/emoji/red_circle.png differ diff --git a/app/assets/images/emoji/registered.png b/app/assets/images/emoji/registered.png new file mode 100644 index 000000000..e5394109a Binary files /dev/null and b/app/assets/images/emoji/registered.png differ diff --git a/app/assets/images/emoji/relaxed.png b/app/assets/images/emoji/relaxed.png new file mode 100644 index 000000000..bbab82d3b Binary files /dev/null and b/app/assets/images/emoji/relaxed.png differ diff --git a/app/assets/images/emoji/relieved.png b/app/assets/images/emoji/relieved.png new file mode 100644 index 000000000..820cf315a Binary files /dev/null and b/app/assets/images/emoji/relieved.png differ diff --git a/app/assets/images/emoji/repeat.png b/app/assets/images/emoji/repeat.png new file mode 100644 index 000000000..80113b692 Binary files /dev/null and b/app/assets/images/emoji/repeat.png differ diff --git a/app/assets/images/emoji/repeat_one.png b/app/assets/images/emoji/repeat_one.png new file mode 100644 index 000000000..3c47bcc1f Binary files /dev/null and b/app/assets/images/emoji/repeat_one.png differ diff --git a/app/assets/images/emoji/restroom.png b/app/assets/images/emoji/restroom.png new file mode 100644 index 000000000..312ca3dc2 Binary files /dev/null and b/app/assets/images/emoji/restroom.png differ diff --git a/app/assets/images/emoji/revolving_hearts.png b/app/assets/images/emoji/revolving_hearts.png new file mode 100644 index 000000000..ea3317c47 Binary files /dev/null and b/app/assets/images/emoji/revolving_hearts.png differ diff --git a/app/assets/images/emoji/rewind.png b/app/assets/images/emoji/rewind.png new file mode 100644 index 000000000..13ba866ad Binary files /dev/null and b/app/assets/images/emoji/rewind.png differ diff --git a/app/assets/images/emoji/ribbon.png b/app/assets/images/emoji/ribbon.png new file mode 100644 index 000000000..63ee5ba5a Binary files /dev/null and b/app/assets/images/emoji/ribbon.png differ diff --git a/app/assets/images/emoji/rice.png b/app/assets/images/emoji/rice.png new file mode 100644 index 000000000..f4773edec Binary files /dev/null and b/app/assets/images/emoji/rice.png differ diff --git a/app/assets/images/emoji/rice_ball.png b/app/assets/images/emoji/rice_ball.png new file mode 100644 index 000000000..04f8a8806 Binary files /dev/null and b/app/assets/images/emoji/rice_ball.png differ diff --git a/app/assets/images/emoji/rice_cracker.png b/app/assets/images/emoji/rice_cracker.png new file mode 100644 index 000000000..954c901e9 Binary files /dev/null and b/app/assets/images/emoji/rice_cracker.png differ diff --git a/app/assets/images/emoji/rice_scene.png b/app/assets/images/emoji/rice_scene.png new file mode 100644 index 000000000..14361988d Binary files /dev/null and b/app/assets/images/emoji/rice_scene.png differ diff --git a/app/assets/images/emoji/ring.png b/app/assets/images/emoji/ring.png new file mode 100644 index 000000000..8a57fd68b Binary files /dev/null and b/app/assets/images/emoji/ring.png differ diff --git a/app/assets/images/emoji/rocket.png b/app/assets/images/emoji/rocket.png new file mode 100644 index 000000000..783078d37 Binary files /dev/null and b/app/assets/images/emoji/rocket.png differ diff --git a/app/assets/images/emoji/roller_coaster.png b/app/assets/images/emoji/roller_coaster.png new file mode 100644 index 000000000..9180b9861 Binary files /dev/null and b/app/assets/images/emoji/roller_coaster.png differ diff --git a/app/assets/images/emoji/rooster.png b/app/assets/images/emoji/rooster.png new file mode 100644 index 000000000..fab23ad36 Binary files /dev/null and b/app/assets/images/emoji/rooster.png differ diff --git a/app/assets/images/emoji/rose.png b/app/assets/images/emoji/rose.png new file mode 100644 index 000000000..3479fbcbb Binary files /dev/null and b/app/assets/images/emoji/rose.png differ diff --git a/app/assets/images/emoji/rotating_light.png b/app/assets/images/emoji/rotating_light.png new file mode 100644 index 000000000..6cf4a775e Binary files /dev/null and b/app/assets/images/emoji/rotating_light.png differ diff --git a/app/assets/images/emoji/round_pushpin.png b/app/assets/images/emoji/round_pushpin.png new file mode 100644 index 000000000..e498e92cf Binary files /dev/null and b/app/assets/images/emoji/round_pushpin.png differ diff --git a/app/assets/images/emoji/rowboat.png b/app/assets/images/emoji/rowboat.png new file mode 100644 index 000000000..fe8ae3ecd Binary files /dev/null and b/app/assets/images/emoji/rowboat.png differ diff --git a/app/assets/images/emoji/ru.png b/app/assets/images/emoji/ru.png new file mode 100644 index 000000000..55fcf3549 Binary files /dev/null and b/app/assets/images/emoji/ru.png differ diff --git a/app/assets/images/emoji/rugby_football.png b/app/assets/images/emoji/rugby_football.png new file mode 100644 index 000000000..f8db67d70 Binary files /dev/null and b/app/assets/images/emoji/rugby_football.png differ diff --git a/app/assets/images/emoji/runner.png b/app/assets/images/emoji/runner.png new file mode 100644 index 000000000..1ecfd9059 Binary files /dev/null and b/app/assets/images/emoji/runner.png differ diff --git a/app/assets/images/emoji/running.png b/app/assets/images/emoji/running.png new file mode 100644 index 000000000..1ecfd9059 Binary files /dev/null and b/app/assets/images/emoji/running.png differ diff --git a/app/assets/images/emoji/running_shirt_with_sash.png b/app/assets/images/emoji/running_shirt_with_sash.png new file mode 100644 index 000000000..0d68bba09 Binary files /dev/null and b/app/assets/images/emoji/running_shirt_with_sash.png differ diff --git a/app/assets/images/emoji/sa.png b/app/assets/images/emoji/sa.png new file mode 100644 index 000000000..387f098b9 Binary files /dev/null and b/app/assets/images/emoji/sa.png differ diff --git a/app/assets/images/emoji/sagittarius.png b/app/assets/images/emoji/sagittarius.png new file mode 100644 index 000000000..8b5435baa Binary files /dev/null and b/app/assets/images/emoji/sagittarius.png differ diff --git a/app/assets/images/emoji/sailboat.png b/app/assets/images/emoji/sailboat.png new file mode 100644 index 000000000..ff656dc62 Binary files /dev/null and b/app/assets/images/emoji/sailboat.png differ diff --git a/app/assets/images/emoji/sake.png b/app/assets/images/emoji/sake.png new file mode 100644 index 000000000..1f69907e5 Binary files /dev/null and b/app/assets/images/emoji/sake.png differ diff --git a/app/assets/images/emoji/sandal.png b/app/assets/images/emoji/sandal.png new file mode 100644 index 000000000..aa62cca5d Binary files /dev/null and b/app/assets/images/emoji/sandal.png differ diff --git a/app/assets/images/emoji/santa.png b/app/assets/images/emoji/santa.png new file mode 100644 index 000000000..a2240c07e Binary files /dev/null and b/app/assets/images/emoji/santa.png differ diff --git a/app/assets/images/emoji/satellite.png b/app/assets/images/emoji/satellite.png new file mode 100644 index 000000000..3481cc2ef Binary files /dev/null and b/app/assets/images/emoji/satellite.png differ diff --git a/app/assets/images/emoji/satisfied.png b/app/assets/images/emoji/satisfied.png new file mode 100644 index 000000000..11c91eb22 Binary files /dev/null and b/app/assets/images/emoji/satisfied.png differ diff --git a/app/assets/images/emoji/saxophone.png b/app/assets/images/emoji/saxophone.png new file mode 100644 index 000000000..011559a76 Binary files /dev/null and b/app/assets/images/emoji/saxophone.png differ diff --git a/app/assets/images/emoji/school.png b/app/assets/images/emoji/school.png new file mode 100644 index 000000000..afd922bf1 Binary files /dev/null and b/app/assets/images/emoji/school.png differ diff --git a/app/assets/images/emoji/school_satchel.png b/app/assets/images/emoji/school_satchel.png new file mode 100644 index 000000000..edfb19aec Binary files /dev/null and b/app/assets/images/emoji/school_satchel.png differ diff --git a/app/assets/images/emoji/scissors.png b/app/assets/images/emoji/scissors.png new file mode 100644 index 000000000..020e05224 Binary files /dev/null and b/app/assets/images/emoji/scissors.png differ diff --git a/app/assets/images/emoji/scorpius.png b/app/assets/images/emoji/scorpius.png new file mode 100644 index 000000000..67fcea165 Binary files /dev/null and b/app/assets/images/emoji/scorpius.png differ diff --git a/app/assets/images/emoji/scream.png b/app/assets/images/emoji/scream.png new file mode 100644 index 000000000..76bfc6b8a Binary files /dev/null and b/app/assets/images/emoji/scream.png differ diff --git a/app/assets/images/emoji/scream_cat.png b/app/assets/images/emoji/scream_cat.png new file mode 100644 index 000000000..d94cd34ff Binary files /dev/null and b/app/assets/images/emoji/scream_cat.png differ diff --git a/app/assets/images/emoji/scroll.png b/app/assets/images/emoji/scroll.png new file mode 100644 index 000000000..c5a10e6b8 Binary files /dev/null and b/app/assets/images/emoji/scroll.png differ diff --git a/app/assets/images/emoji/seat.png b/app/assets/images/emoji/seat.png new file mode 100644 index 000000000..d1cb864b4 Binary files /dev/null and b/app/assets/images/emoji/seat.png differ diff --git a/app/assets/images/emoji/secret.png b/app/assets/images/emoji/secret.png new file mode 100644 index 000000000..82e383a60 Binary files /dev/null and b/app/assets/images/emoji/secret.png differ diff --git a/app/assets/images/emoji/see_no_evil.png b/app/assets/images/emoji/see_no_evil.png new file mode 100644 index 000000000..0890a6222 Binary files /dev/null and b/app/assets/images/emoji/see_no_evil.png differ diff --git a/app/assets/images/emoji/seedling.png b/app/assets/images/emoji/seedling.png new file mode 100644 index 000000000..f0eb5a6b9 Binary files /dev/null and b/app/assets/images/emoji/seedling.png differ diff --git a/app/assets/images/emoji/seven.png b/app/assets/images/emoji/seven.png new file mode 100644 index 000000000..354e89ae7 Binary files /dev/null and b/app/assets/images/emoji/seven.png differ diff --git a/app/assets/images/emoji/shaved_ice.png b/app/assets/images/emoji/shaved_ice.png new file mode 100644 index 000000000..0d0b382c2 Binary files /dev/null and b/app/assets/images/emoji/shaved_ice.png differ diff --git a/app/assets/images/emoji/sheep.png b/app/assets/images/emoji/sheep.png new file mode 100644 index 000000000..c7277d289 Binary files /dev/null and b/app/assets/images/emoji/sheep.png differ diff --git a/app/assets/images/emoji/shell.png b/app/assets/images/emoji/shell.png new file mode 100644 index 000000000..3145b5649 Binary files /dev/null and b/app/assets/images/emoji/shell.png differ diff --git a/app/assets/images/emoji/ship.png b/app/assets/images/emoji/ship.png new file mode 100644 index 000000000..5d2d8b602 Binary files /dev/null and b/app/assets/images/emoji/ship.png differ diff --git a/app/assets/images/emoji/shipit.png b/app/assets/images/emoji/shipit.png new file mode 100644 index 000000000..a58a47f62 Binary files /dev/null and b/app/assets/images/emoji/shipit.png differ diff --git a/app/assets/images/emoji/shirt.png b/app/assets/images/emoji/shirt.png new file mode 100644 index 000000000..297a6d63e Binary files /dev/null and b/app/assets/images/emoji/shirt.png differ diff --git a/app/assets/images/emoji/shit.png b/app/assets/images/emoji/shit.png new file mode 100644 index 000000000..73a4dc840 Binary files /dev/null and b/app/assets/images/emoji/shit.png differ diff --git a/app/assets/images/emoji/shoe.png b/app/assets/images/emoji/shoe.png new file mode 100644 index 000000000..ecba9ba7d Binary files /dev/null and b/app/assets/images/emoji/shoe.png differ diff --git a/app/assets/images/emoji/shower.png b/app/assets/images/emoji/shower.png new file mode 100644 index 000000000..94f82aac0 Binary files /dev/null and b/app/assets/images/emoji/shower.png differ diff --git a/app/assets/images/emoji/signal_strength.png b/app/assets/images/emoji/signal_strength.png new file mode 100644 index 000000000..a4bd23ebf Binary files /dev/null and b/app/assets/images/emoji/signal_strength.png differ diff --git a/app/assets/images/emoji/six.png b/app/assets/images/emoji/six.png new file mode 100644 index 000000000..568805565 Binary files /dev/null and b/app/assets/images/emoji/six.png differ diff --git a/app/assets/images/emoji/six_pointed_star.png b/app/assets/images/emoji/six_pointed_star.png new file mode 100644 index 000000000..010f8f5f9 Binary files /dev/null and b/app/assets/images/emoji/six_pointed_star.png differ diff --git a/app/assets/images/emoji/ski.png b/app/assets/images/emoji/ski.png new file mode 100644 index 000000000..c97de3ed9 Binary files /dev/null and b/app/assets/images/emoji/ski.png differ diff --git a/app/assets/images/emoji/skull.png b/app/assets/images/emoji/skull.png new file mode 100644 index 000000000..bd4ee3829 Binary files /dev/null and b/app/assets/images/emoji/skull.png differ diff --git a/app/assets/images/emoji/sleeping.png b/app/assets/images/emoji/sleeping.png new file mode 100644 index 000000000..a2f3bf757 Binary files /dev/null and b/app/assets/images/emoji/sleeping.png differ diff --git a/app/assets/images/emoji/sleepy.png b/app/assets/images/emoji/sleepy.png new file mode 100644 index 000000000..df4f55efd Binary files /dev/null and b/app/assets/images/emoji/sleepy.png differ diff --git a/app/assets/images/emoji/slot_machine.png b/app/assets/images/emoji/slot_machine.png new file mode 100644 index 000000000..26f114830 Binary files /dev/null and b/app/assets/images/emoji/slot_machine.png differ diff --git a/app/assets/images/emoji/small_blue_diamond.png b/app/assets/images/emoji/small_blue_diamond.png new file mode 100644 index 000000000..5a7b5d555 Binary files /dev/null and b/app/assets/images/emoji/small_blue_diamond.png differ diff --git a/app/assets/images/emoji/small_orange_diamond.png b/app/assets/images/emoji/small_orange_diamond.png new file mode 100644 index 000000000..04941d37b Binary files /dev/null and b/app/assets/images/emoji/small_orange_diamond.png differ diff --git a/app/assets/images/emoji/small_red_triangle.png b/app/assets/images/emoji/small_red_triangle.png new file mode 100644 index 000000000..8c4428da8 Binary files /dev/null and b/app/assets/images/emoji/small_red_triangle.png differ diff --git a/app/assets/images/emoji/small_red_triangle_down.png b/app/assets/images/emoji/small_red_triangle_down.png new file mode 100644 index 000000000..94832f060 Binary files /dev/null and b/app/assets/images/emoji/small_red_triangle_down.png differ diff --git a/app/assets/images/emoji/smile.png b/app/assets/images/emoji/smile.png new file mode 100644 index 000000000..81a839689 Binary files /dev/null and b/app/assets/images/emoji/smile.png differ diff --git a/app/assets/images/emoji/smile_cat.png b/app/assets/images/emoji/smile_cat.png new file mode 100644 index 000000000..ad333ba3b Binary files /dev/null and b/app/assets/images/emoji/smile_cat.png differ diff --git a/app/assets/images/emoji/smiley.png b/app/assets/images/emoji/smiley.png new file mode 100644 index 000000000..77b581d68 Binary files /dev/null and b/app/assets/images/emoji/smiley.png differ diff --git a/app/assets/images/emoji/smiley_cat.png b/app/assets/images/emoji/smiley_cat.png new file mode 100644 index 000000000..dbf1b0276 Binary files /dev/null and b/app/assets/images/emoji/smiley_cat.png differ diff --git a/app/assets/images/emoji/smiling_imp.png b/app/assets/images/emoji/smiling_imp.png new file mode 100644 index 000000000..d90404930 Binary files /dev/null and b/app/assets/images/emoji/smiling_imp.png differ diff --git a/app/assets/images/emoji/smirk.png b/app/assets/images/emoji/smirk.png new file mode 100644 index 000000000..bc6e5082c Binary files /dev/null and b/app/assets/images/emoji/smirk.png differ diff --git a/app/assets/images/emoji/smirk_cat.png b/app/assets/images/emoji/smirk_cat.png new file mode 100644 index 000000000..351565e24 Binary files /dev/null and b/app/assets/images/emoji/smirk_cat.png differ diff --git a/app/assets/images/emoji/smoking.png b/app/assets/images/emoji/smoking.png new file mode 100644 index 000000000..4aad6cbd7 Binary files /dev/null and b/app/assets/images/emoji/smoking.png differ diff --git a/app/assets/images/emoji/snail.png b/app/assets/images/emoji/snail.png new file mode 100644 index 000000000..e75e69a84 Binary files /dev/null and b/app/assets/images/emoji/snail.png differ diff --git a/app/assets/images/emoji/snake.png b/app/assets/images/emoji/snake.png new file mode 100644 index 000000000..ef58933e2 Binary files /dev/null and b/app/assets/images/emoji/snake.png differ diff --git a/app/assets/images/emoji/snowboarder.png b/app/assets/images/emoji/snowboarder.png new file mode 100644 index 000000000..aeda5c8d8 Binary files /dev/null and b/app/assets/images/emoji/snowboarder.png differ diff --git a/app/assets/images/emoji/snowflake.png b/app/assets/images/emoji/snowflake.png new file mode 100644 index 000000000..54b68ff4f Binary files /dev/null and b/app/assets/images/emoji/snowflake.png differ diff --git a/app/assets/images/emoji/snowman.png b/app/assets/images/emoji/snowman.png new file mode 100644 index 000000000..a97902e53 Binary files /dev/null and b/app/assets/images/emoji/snowman.png differ diff --git a/app/assets/images/emoji/sob.png b/app/assets/images/emoji/sob.png new file mode 100644 index 000000000..7d433183a Binary files /dev/null and b/app/assets/images/emoji/sob.png differ diff --git a/app/assets/images/emoji/soccer.png b/app/assets/images/emoji/soccer.png new file mode 100644 index 000000000..1e118b5b1 Binary files /dev/null and b/app/assets/images/emoji/soccer.png differ diff --git a/app/assets/images/emoji/soon.png b/app/assets/images/emoji/soon.png new file mode 100644 index 000000000..9386615a3 Binary files /dev/null and b/app/assets/images/emoji/soon.png differ diff --git a/app/assets/images/emoji/sos.png b/app/assets/images/emoji/sos.png new file mode 100644 index 000000000..e3e16ef73 Binary files /dev/null and b/app/assets/images/emoji/sos.png differ diff --git a/app/assets/images/emoji/sound.png b/app/assets/images/emoji/sound.png new file mode 100644 index 000000000..6aa4dbff4 Binary files /dev/null and b/app/assets/images/emoji/sound.png differ diff --git a/app/assets/images/emoji/space_invader.png b/app/assets/images/emoji/space_invader.png new file mode 100644 index 000000000..384049167 Binary files /dev/null and b/app/assets/images/emoji/space_invader.png differ diff --git a/app/assets/images/emoji/spades.png b/app/assets/images/emoji/spades.png new file mode 100644 index 000000000..133a1aba8 Binary files /dev/null and b/app/assets/images/emoji/spades.png differ diff --git a/app/assets/images/emoji/spaghetti.png b/app/assets/images/emoji/spaghetti.png new file mode 100644 index 000000000..08de243f5 Binary files /dev/null and b/app/assets/images/emoji/spaghetti.png differ diff --git a/app/assets/images/emoji/sparkle.png b/app/assets/images/emoji/sparkle.png new file mode 100644 index 000000000..23a68ceb2 Binary files /dev/null and b/app/assets/images/emoji/sparkle.png differ diff --git a/app/assets/images/emoji/sparkler.png b/app/assets/images/emoji/sparkler.png new file mode 100644 index 000000000..4aabd7e0e Binary files /dev/null and b/app/assets/images/emoji/sparkler.png differ diff --git a/app/assets/images/emoji/sparkles.png b/app/assets/images/emoji/sparkles.png new file mode 100644 index 000000000..92138828d Binary files /dev/null and b/app/assets/images/emoji/sparkles.png differ diff --git a/app/assets/images/emoji/sparkling_heart.png b/app/assets/images/emoji/sparkling_heart.png new file mode 100644 index 000000000..0826bbc06 Binary files /dev/null and b/app/assets/images/emoji/sparkling_heart.png differ diff --git a/app/assets/images/emoji/speak_no_evil.png b/app/assets/images/emoji/speak_no_evil.png new file mode 100644 index 000000000..87944c4de Binary files /dev/null and b/app/assets/images/emoji/speak_no_evil.png differ diff --git a/app/assets/images/emoji/speaker.png b/app/assets/images/emoji/speaker.png new file mode 100644 index 000000000..c884bd4f6 Binary files /dev/null and b/app/assets/images/emoji/speaker.png differ diff --git a/app/assets/images/emoji/speech_balloon.png b/app/assets/images/emoji/speech_balloon.png new file mode 100644 index 000000000..2896c2788 Binary files /dev/null and b/app/assets/images/emoji/speech_balloon.png differ diff --git a/app/assets/images/emoji/speedboat.png b/app/assets/images/emoji/speedboat.png new file mode 100644 index 000000000..da6689b3b Binary files /dev/null and b/app/assets/images/emoji/speedboat.png differ diff --git a/app/assets/images/emoji/squirrel.png b/app/assets/images/emoji/squirrel.png new file mode 100644 index 000000000..a58a47f62 Binary files /dev/null and b/app/assets/images/emoji/squirrel.png differ diff --git a/app/assets/images/emoji/star.png b/app/assets/images/emoji/star.png new file mode 100644 index 000000000..1bfddc862 Binary files /dev/null and b/app/assets/images/emoji/star.png differ diff --git a/app/assets/images/emoji/star2.png b/app/assets/images/emoji/star2.png new file mode 100644 index 000000000..8b40ff4c8 Binary files /dev/null and b/app/assets/images/emoji/star2.png differ diff --git a/app/assets/images/emoji/stars.png b/app/assets/images/emoji/stars.png new file mode 100644 index 000000000..097a84241 Binary files /dev/null and b/app/assets/images/emoji/stars.png differ diff --git a/app/assets/images/emoji/station.png b/app/assets/images/emoji/station.png new file mode 100644 index 000000000..e77daa8a7 Binary files /dev/null and b/app/assets/images/emoji/station.png differ diff --git a/app/assets/images/emoji/statue_of_liberty.png b/app/assets/images/emoji/statue_of_liberty.png new file mode 100644 index 000000000..9ad902806 Binary files /dev/null and b/app/assets/images/emoji/statue_of_liberty.png differ diff --git a/app/assets/images/emoji/steam_locomotive.png b/app/assets/images/emoji/steam_locomotive.png new file mode 100644 index 000000000..549507766 Binary files /dev/null and b/app/assets/images/emoji/steam_locomotive.png differ diff --git a/app/assets/images/emoji/stew.png b/app/assets/images/emoji/stew.png new file mode 100644 index 000000000..6e80b4a9c Binary files /dev/null and b/app/assets/images/emoji/stew.png differ diff --git a/app/assets/images/emoji/straight_ruler.png b/app/assets/images/emoji/straight_ruler.png new file mode 100644 index 000000000..af8cb4bcf Binary files /dev/null and b/app/assets/images/emoji/straight_ruler.png differ diff --git a/app/assets/images/emoji/strawberry.png b/app/assets/images/emoji/strawberry.png new file mode 100644 index 000000000..13eb827ab Binary files /dev/null and b/app/assets/images/emoji/strawberry.png differ diff --git a/app/assets/images/emoji/stuck_out_tongue.png b/app/assets/images/emoji/stuck_out_tongue.png new file mode 100644 index 000000000..53c41433f Binary files /dev/null and b/app/assets/images/emoji/stuck_out_tongue.png differ diff --git a/app/assets/images/emoji/stuck_out_tongue_closed_eyes.png b/app/assets/images/emoji/stuck_out_tongue_closed_eyes.png new file mode 100644 index 000000000..333716ee1 Binary files /dev/null and b/app/assets/images/emoji/stuck_out_tongue_closed_eyes.png differ diff --git a/app/assets/images/emoji/stuck_out_tongue_winking_eye.png b/app/assets/images/emoji/stuck_out_tongue_winking_eye.png new file mode 100644 index 000000000..6ae9d497d Binary files /dev/null and b/app/assets/images/emoji/stuck_out_tongue_winking_eye.png differ diff --git a/app/assets/images/emoji/sun_with_face.png b/app/assets/images/emoji/sun_with_face.png new file mode 100644 index 000000000..ee276636f Binary files /dev/null and b/app/assets/images/emoji/sun_with_face.png differ diff --git a/app/assets/images/emoji/sunflower.png b/app/assets/images/emoji/sunflower.png new file mode 100644 index 000000000..d9bad194a Binary files /dev/null and b/app/assets/images/emoji/sunflower.png differ diff --git a/app/assets/images/emoji/sunglasses.png b/app/assets/images/emoji/sunglasses.png new file mode 100644 index 000000000..1c468a1c9 Binary files /dev/null and b/app/assets/images/emoji/sunglasses.png differ diff --git a/app/assets/images/emoji/sunny.png b/app/assets/images/emoji/sunny.png new file mode 100644 index 000000000..d23c095e0 Binary files /dev/null and b/app/assets/images/emoji/sunny.png differ diff --git a/app/assets/images/emoji/sunrise.png b/app/assets/images/emoji/sunrise.png new file mode 100644 index 000000000..ec58dcc94 Binary files /dev/null and b/app/assets/images/emoji/sunrise.png differ diff --git a/app/assets/images/emoji/sunrise_over_mountains.png b/app/assets/images/emoji/sunrise_over_mountains.png new file mode 100644 index 000000000..ebc3db146 Binary files /dev/null and b/app/assets/images/emoji/sunrise_over_mountains.png differ diff --git a/app/assets/images/emoji/surfer.png b/app/assets/images/emoji/surfer.png new file mode 100644 index 000000000..b067e8cb3 Binary files /dev/null and b/app/assets/images/emoji/surfer.png differ diff --git a/app/assets/images/emoji/sushi.png b/app/assets/images/emoji/sushi.png new file mode 100644 index 000000000..0d179bd97 Binary files /dev/null and b/app/assets/images/emoji/sushi.png differ diff --git a/app/assets/images/emoji/suspect.png b/app/assets/images/emoji/suspect.png new file mode 100644 index 000000000..58e8921c0 Binary files /dev/null and b/app/assets/images/emoji/suspect.png differ diff --git a/app/assets/images/emoji/suspension_railway.png b/app/assets/images/emoji/suspension_railway.png new file mode 100644 index 000000000..aaa45f61f Binary files /dev/null and b/app/assets/images/emoji/suspension_railway.png differ diff --git a/app/assets/images/emoji/sweat.png b/app/assets/images/emoji/sweat.png new file mode 100644 index 000000000..e894b7699 Binary files /dev/null and b/app/assets/images/emoji/sweat.png differ diff --git a/app/assets/images/emoji/sweat_drops.png b/app/assets/images/emoji/sweat_drops.png new file mode 100644 index 000000000..a83b3e960 Binary files /dev/null and b/app/assets/images/emoji/sweat_drops.png differ diff --git a/app/assets/images/emoji/sweat_smile.png b/app/assets/images/emoji/sweat_smile.png new file mode 100644 index 000000000..3903f717f Binary files /dev/null and b/app/assets/images/emoji/sweat_smile.png differ diff --git a/app/assets/images/emoji/sweet_potato.png b/app/assets/images/emoji/sweet_potato.png new file mode 100644 index 000000000..32117fa9c Binary files /dev/null and b/app/assets/images/emoji/sweet_potato.png differ diff --git a/app/assets/images/emoji/swimmer.png b/app/assets/images/emoji/swimmer.png new file mode 100644 index 000000000..d3878a065 Binary files /dev/null and b/app/assets/images/emoji/swimmer.png differ diff --git a/app/assets/images/emoji/symbols.png b/app/assets/images/emoji/symbols.png new file mode 100644 index 000000000..16bc1da92 Binary files /dev/null and b/app/assets/images/emoji/symbols.png differ diff --git a/app/assets/images/emoji/syringe.png b/app/assets/images/emoji/syringe.png new file mode 100644 index 000000000..e7e7ab6e3 Binary files /dev/null and b/app/assets/images/emoji/syringe.png differ diff --git a/app/assets/images/emoji/tada.png b/app/assets/images/emoji/tada.png new file mode 100644 index 000000000..7411b5266 Binary files /dev/null and b/app/assets/images/emoji/tada.png differ diff --git a/app/assets/images/emoji/tanabata_tree.png b/app/assets/images/emoji/tanabata_tree.png new file mode 100644 index 000000000..473346410 Binary files /dev/null and b/app/assets/images/emoji/tanabata_tree.png differ diff --git a/app/assets/images/emoji/tangerine.png b/app/assets/images/emoji/tangerine.png new file mode 100644 index 000000000..fc9d4f82a Binary files /dev/null and b/app/assets/images/emoji/tangerine.png differ diff --git a/app/assets/images/emoji/taurus.png b/app/assets/images/emoji/taurus.png new file mode 100644 index 000000000..6af582f69 Binary files /dev/null and b/app/assets/images/emoji/taurus.png differ diff --git a/app/assets/images/emoji/taxi.png b/app/assets/images/emoji/taxi.png new file mode 100644 index 000000000..60a50d365 Binary files /dev/null and b/app/assets/images/emoji/taxi.png differ diff --git a/app/assets/images/emoji/tea.png b/app/assets/images/emoji/tea.png new file mode 100644 index 000000000..3ece0b708 Binary files /dev/null and b/app/assets/images/emoji/tea.png differ diff --git a/app/assets/images/emoji/telephone.png b/app/assets/images/emoji/telephone.png new file mode 100644 index 000000000..87d2559b5 Binary files /dev/null and b/app/assets/images/emoji/telephone.png differ diff --git a/app/assets/images/emoji/telephone_receiver.png b/app/assets/images/emoji/telephone_receiver.png new file mode 100644 index 000000000..36e21e012 Binary files /dev/null and b/app/assets/images/emoji/telephone_receiver.png differ diff --git a/app/assets/images/emoji/telescope.png b/app/assets/images/emoji/telescope.png new file mode 100644 index 000000000..51fd8a07f Binary files /dev/null and b/app/assets/images/emoji/telescope.png differ diff --git a/app/assets/images/emoji/tennis.png b/app/assets/images/emoji/tennis.png new file mode 100644 index 000000000..278d904ee Binary files /dev/null and b/app/assets/images/emoji/tennis.png differ diff --git a/app/assets/images/emoji/tent.png b/app/assets/images/emoji/tent.png new file mode 100644 index 000000000..5c0d20e48 Binary files /dev/null and b/app/assets/images/emoji/tent.png differ diff --git a/app/assets/images/emoji/thought_balloon.png b/app/assets/images/emoji/thought_balloon.png new file mode 100644 index 000000000..701bdf0f6 Binary files /dev/null and b/app/assets/images/emoji/thought_balloon.png differ diff --git a/app/assets/images/emoji/three.png b/app/assets/images/emoji/three.png new file mode 100644 index 000000000..55644c990 Binary files /dev/null and b/app/assets/images/emoji/three.png differ diff --git a/app/assets/images/emoji/thumbsdown.png b/app/assets/images/emoji/thumbsdown.png new file mode 100644 index 000000000..e44c04219 Binary files /dev/null and b/app/assets/images/emoji/thumbsdown.png differ diff --git a/app/assets/images/emoji/thumbsup.png b/app/assets/images/emoji/thumbsup.png new file mode 100644 index 000000000..3a43ecae2 Binary files /dev/null and b/app/assets/images/emoji/thumbsup.png differ diff --git a/app/assets/images/emoji/ticket.png b/app/assets/images/emoji/ticket.png new file mode 100644 index 000000000..cdacf1a70 Binary files /dev/null and b/app/assets/images/emoji/ticket.png differ diff --git a/app/assets/images/emoji/tiger.png b/app/assets/images/emoji/tiger.png new file mode 100644 index 000000000..d6cc84a3b Binary files /dev/null and b/app/assets/images/emoji/tiger.png differ diff --git a/app/assets/images/emoji/tiger2.png b/app/assets/images/emoji/tiger2.png new file mode 100644 index 000000000..b0c7d8dc3 Binary files /dev/null and b/app/assets/images/emoji/tiger2.png differ diff --git a/app/assets/images/emoji/tired_face.png b/app/assets/images/emoji/tired_face.png new file mode 100644 index 000000000..3a8eefe56 Binary files /dev/null and b/app/assets/images/emoji/tired_face.png differ diff --git a/app/assets/images/emoji/tm.png b/app/assets/images/emoji/tm.png new file mode 100644 index 000000000..9ba71b75b Binary files /dev/null and b/app/assets/images/emoji/tm.png differ diff --git a/app/assets/images/emoji/toilet.png b/app/assets/images/emoji/toilet.png new file mode 100644 index 000000000..e5cc4119a Binary files /dev/null and b/app/assets/images/emoji/toilet.png differ diff --git a/app/assets/images/emoji/tokyo_tower.png b/app/assets/images/emoji/tokyo_tower.png new file mode 100644 index 000000000..e1cbd7a3c Binary files /dev/null and b/app/assets/images/emoji/tokyo_tower.png differ diff --git a/app/assets/images/emoji/tomato.png b/app/assets/images/emoji/tomato.png new file mode 100644 index 000000000..a129700bb Binary files /dev/null and b/app/assets/images/emoji/tomato.png differ diff --git a/app/assets/images/emoji/tongue.png b/app/assets/images/emoji/tongue.png new file mode 100644 index 000000000..b0bab1207 Binary files /dev/null and b/app/assets/images/emoji/tongue.png differ diff --git a/app/assets/images/emoji/top.png b/app/assets/images/emoji/top.png new file mode 100644 index 000000000..5aa4dd442 Binary files /dev/null and b/app/assets/images/emoji/top.png differ diff --git a/app/assets/images/emoji/tophat.png b/app/assets/images/emoji/tophat.png new file mode 100644 index 000000000..7d27134d6 Binary files /dev/null and b/app/assets/images/emoji/tophat.png differ diff --git a/app/assets/images/emoji/tractor.png b/app/assets/images/emoji/tractor.png new file mode 100644 index 000000000..058fd3eda Binary files /dev/null and b/app/assets/images/emoji/tractor.png differ diff --git a/app/assets/images/emoji/traffic_light.png b/app/assets/images/emoji/traffic_light.png new file mode 100644 index 000000000..42eaf7091 Binary files /dev/null and b/app/assets/images/emoji/traffic_light.png differ diff --git a/app/assets/images/emoji/train.png b/app/assets/images/emoji/train.png new file mode 100644 index 000000000..22361158f Binary files /dev/null and b/app/assets/images/emoji/train.png differ diff --git a/app/assets/images/emoji/train2.png b/app/assets/images/emoji/train2.png new file mode 100644 index 000000000..9c0d3ab64 Binary files /dev/null and b/app/assets/images/emoji/train2.png differ diff --git a/app/assets/images/emoji/tram.png b/app/assets/images/emoji/tram.png new file mode 100644 index 000000000..5eb29fb71 Binary files /dev/null and b/app/assets/images/emoji/tram.png differ diff --git a/app/assets/images/emoji/triangular_flag_on_post.png b/app/assets/images/emoji/triangular_flag_on_post.png new file mode 100644 index 000000000..f9a3f32d7 Binary files /dev/null and b/app/assets/images/emoji/triangular_flag_on_post.png differ diff --git a/app/assets/images/emoji/triangular_ruler.png b/app/assets/images/emoji/triangular_ruler.png new file mode 100644 index 000000000..383677cb7 Binary files /dev/null and b/app/assets/images/emoji/triangular_ruler.png differ diff --git a/app/assets/images/emoji/trident.png b/app/assets/images/emoji/trident.png new file mode 100644 index 000000000..d79a7b4cc Binary files /dev/null and b/app/assets/images/emoji/trident.png differ diff --git a/app/assets/images/emoji/triumph.png b/app/assets/images/emoji/triumph.png new file mode 100644 index 000000000..92f93bd10 Binary files /dev/null and b/app/assets/images/emoji/triumph.png differ diff --git a/app/assets/images/emoji/trolleybus.png b/app/assets/images/emoji/trolleybus.png new file mode 100644 index 000000000..b9740a53f Binary files /dev/null and b/app/assets/images/emoji/trolleybus.png differ diff --git a/app/assets/images/emoji/trollface.png b/app/assets/images/emoji/trollface.png new file mode 100644 index 000000000..cce7c7585 Binary files /dev/null and b/app/assets/images/emoji/trollface.png differ diff --git a/app/assets/images/emoji/trophy.png b/app/assets/images/emoji/trophy.png new file mode 100644 index 000000000..95d3b63f5 Binary files /dev/null and b/app/assets/images/emoji/trophy.png differ diff --git a/app/assets/images/emoji/tropical_drink.png b/app/assets/images/emoji/tropical_drink.png new file mode 100644 index 000000000..55ca9eeda Binary files /dev/null and b/app/assets/images/emoji/tropical_drink.png differ diff --git a/app/assets/images/emoji/tropical_fish.png b/app/assets/images/emoji/tropical_fish.png new file mode 100644 index 000000000..a6d734987 Binary files /dev/null and b/app/assets/images/emoji/tropical_fish.png differ diff --git a/app/assets/images/emoji/truck.png b/app/assets/images/emoji/truck.png new file mode 100644 index 000000000..3f25ba1f9 Binary files /dev/null and b/app/assets/images/emoji/truck.png differ diff --git a/app/assets/images/emoji/trumpet.png b/app/assets/images/emoji/trumpet.png new file mode 100644 index 000000000..8d4703fc2 Binary files /dev/null and b/app/assets/images/emoji/trumpet.png differ diff --git a/app/assets/images/emoji/tshirt.png b/app/assets/images/emoji/tshirt.png new file mode 100644 index 000000000..297a6d63e Binary files /dev/null and b/app/assets/images/emoji/tshirt.png differ diff --git a/app/assets/images/emoji/tulip.png b/app/assets/images/emoji/tulip.png new file mode 100644 index 000000000..b3ee1102a Binary files /dev/null and b/app/assets/images/emoji/tulip.png differ diff --git a/app/assets/images/emoji/turtle.png b/app/assets/images/emoji/turtle.png new file mode 100644 index 000000000..04d1d9684 Binary files /dev/null and b/app/assets/images/emoji/turtle.png differ diff --git a/app/assets/images/emoji/tv.png b/app/assets/images/emoji/tv.png new file mode 100644 index 000000000..803dc3d41 Binary files /dev/null and b/app/assets/images/emoji/tv.png differ diff --git a/app/assets/images/emoji/twisted_rightwards_arrows.png b/app/assets/images/emoji/twisted_rightwards_arrows.png new file mode 100644 index 000000000..25cde18b2 Binary files /dev/null and b/app/assets/images/emoji/twisted_rightwards_arrows.png differ diff --git a/app/assets/images/emoji/two.png b/app/assets/images/emoji/two.png new file mode 100644 index 000000000..c191f8a32 Binary files /dev/null and b/app/assets/images/emoji/two.png differ diff --git a/app/assets/images/emoji/two_hearts.png b/app/assets/images/emoji/two_hearts.png new file mode 100644 index 000000000..b189e9aea Binary files /dev/null and b/app/assets/images/emoji/two_hearts.png differ diff --git a/app/assets/images/emoji/two_men_holding_hands.png b/app/assets/images/emoji/two_men_holding_hands.png new file mode 100644 index 000000000..d1099f21f Binary files /dev/null and b/app/assets/images/emoji/two_men_holding_hands.png differ diff --git a/app/assets/images/emoji/two_women_holding_hands.png b/app/assets/images/emoji/two_women_holding_hands.png new file mode 100644 index 000000000..619646c4e Binary files /dev/null and b/app/assets/images/emoji/two_women_holding_hands.png differ diff --git a/app/assets/images/emoji/u5272.png b/app/assets/images/emoji/u5272.png new file mode 100644 index 000000000..2148253fc Binary files /dev/null and b/app/assets/images/emoji/u5272.png differ diff --git a/app/assets/images/emoji/u5408.png b/app/assets/images/emoji/u5408.png new file mode 100644 index 000000000..03ab0d874 Binary files /dev/null and b/app/assets/images/emoji/u5408.png differ diff --git a/app/assets/images/emoji/u55b6.png b/app/assets/images/emoji/u55b6.png new file mode 100644 index 000000000..ba946d3f3 Binary files /dev/null and b/app/assets/images/emoji/u55b6.png differ diff --git a/app/assets/images/emoji/u6307.png b/app/assets/images/emoji/u6307.png new file mode 100644 index 000000000..6557f5672 Binary files /dev/null and b/app/assets/images/emoji/u6307.png differ diff --git a/app/assets/images/emoji/u6708.png b/app/assets/images/emoji/u6708.png new file mode 100644 index 000000000..e4dfe5aa7 Binary files /dev/null and b/app/assets/images/emoji/u6708.png differ diff --git a/app/assets/images/emoji/u6709.png b/app/assets/images/emoji/u6709.png new file mode 100644 index 000000000..cd8fb3f62 Binary files /dev/null and b/app/assets/images/emoji/u6709.png differ diff --git a/app/assets/images/emoji/u6e80.png b/app/assets/images/emoji/u6e80.png new file mode 100644 index 000000000..5df1cb878 Binary files /dev/null and b/app/assets/images/emoji/u6e80.png differ diff --git a/app/assets/images/emoji/u7121.png b/app/assets/images/emoji/u7121.png new file mode 100644 index 000000000..25f694ed3 Binary files /dev/null and b/app/assets/images/emoji/u7121.png differ diff --git a/app/assets/images/emoji/u7533.png b/app/assets/images/emoji/u7533.png new file mode 100644 index 000000000..fc4a9901b Binary files /dev/null and b/app/assets/images/emoji/u7533.png differ diff --git a/app/assets/images/emoji/u7981.png b/app/assets/images/emoji/u7981.png new file mode 100644 index 000000000..f550a573d Binary files /dev/null and b/app/assets/images/emoji/u7981.png differ diff --git a/app/assets/images/emoji/u7a7a.png b/app/assets/images/emoji/u7a7a.png new file mode 100644 index 000000000..c05f5cff7 Binary files /dev/null and b/app/assets/images/emoji/u7a7a.png differ diff --git a/app/assets/images/emoji/uk.png b/app/assets/images/emoji/uk.png new file mode 100644 index 000000000..2a62c7a08 Binary files /dev/null and b/app/assets/images/emoji/uk.png differ diff --git a/app/assets/images/emoji/umbrella.png b/app/assets/images/emoji/umbrella.png new file mode 100644 index 000000000..1db722fa6 Binary files /dev/null and b/app/assets/images/emoji/umbrella.png differ diff --git a/app/assets/images/emoji/unamused.png b/app/assets/images/emoji/unamused.png new file mode 100644 index 000000000..3722e6f57 Binary files /dev/null and b/app/assets/images/emoji/unamused.png differ diff --git a/app/assets/images/emoji/underage.png b/app/assets/images/emoji/underage.png new file mode 100644 index 000000000..a789b3c62 Binary files /dev/null and b/app/assets/images/emoji/underage.png differ diff --git a/app/assets/images/emoji/unicode/0023.png b/app/assets/images/emoji/unicode/0023.png new file mode 100644 index 000000000..6765d7d3c Binary files /dev/null and b/app/assets/images/emoji/unicode/0023.png differ diff --git a/app/assets/images/emoji/unicode/0030.png b/app/assets/images/emoji/unicode/0030.png new file mode 100644 index 000000000..15e7446c8 Binary files /dev/null and b/app/assets/images/emoji/unicode/0030.png differ diff --git a/app/assets/images/emoji/unicode/0031.png b/app/assets/images/emoji/unicode/0031.png new file mode 100644 index 000000000..2d1f9f8c4 Binary files /dev/null and b/app/assets/images/emoji/unicode/0031.png differ diff --git a/app/assets/images/emoji/unicode/0032.png b/app/assets/images/emoji/unicode/0032.png new file mode 100644 index 000000000..c191f8a32 Binary files /dev/null and b/app/assets/images/emoji/unicode/0032.png differ diff --git a/app/assets/images/emoji/unicode/0033.png b/app/assets/images/emoji/unicode/0033.png new file mode 100644 index 000000000..55644c990 Binary files /dev/null and b/app/assets/images/emoji/unicode/0033.png differ diff --git a/app/assets/images/emoji/unicode/0034.png b/app/assets/images/emoji/unicode/0034.png new file mode 100644 index 000000000..14782ba23 Binary files /dev/null and b/app/assets/images/emoji/unicode/0034.png differ diff --git a/app/assets/images/emoji/unicode/0035.png b/app/assets/images/emoji/unicode/0035.png new file mode 100644 index 000000000..794321aa2 Binary files /dev/null and b/app/assets/images/emoji/unicode/0035.png differ diff --git a/app/assets/images/emoji/unicode/0036.png b/app/assets/images/emoji/unicode/0036.png new file mode 100644 index 000000000..568805565 Binary files /dev/null and b/app/assets/images/emoji/unicode/0036.png differ diff --git a/app/assets/images/emoji/unicode/0037.png b/app/assets/images/emoji/unicode/0037.png new file mode 100644 index 000000000..354e89ae7 Binary files /dev/null and b/app/assets/images/emoji/unicode/0037.png differ diff --git a/app/assets/images/emoji/unicode/0038.png b/app/assets/images/emoji/unicode/0038.png new file mode 100644 index 000000000..7bdb42232 Binary files /dev/null and b/app/assets/images/emoji/unicode/0038.png differ diff --git a/app/assets/images/emoji/unicode/0039.png b/app/assets/images/emoji/unicode/0039.png new file mode 100644 index 000000000..8006cc909 Binary files /dev/null and b/app/assets/images/emoji/unicode/0039.png differ diff --git a/app/assets/images/emoji/unicode/00a9.png b/app/assets/images/emoji/unicode/00a9.png new file mode 100644 index 000000000..d59f580a9 Binary files /dev/null and b/app/assets/images/emoji/unicode/00a9.png differ diff --git a/app/assets/images/emoji/unicode/00ae.png b/app/assets/images/emoji/unicode/00ae.png new file mode 100644 index 000000000..e5394109a Binary files /dev/null and b/app/assets/images/emoji/unicode/00ae.png differ diff --git a/app/assets/images/emoji/unicode/1f004.png b/app/assets/images/emoji/unicode/1f004.png new file mode 100644 index 000000000..f51ce65fd Binary files /dev/null and b/app/assets/images/emoji/unicode/1f004.png differ diff --git a/app/assets/images/emoji/unicode/1f0cf.png b/app/assets/images/emoji/unicode/1f0cf.png new file mode 100644 index 000000000..4c78f3614 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f0cf.png differ diff --git a/app/assets/images/emoji/unicode/1f170.png b/app/assets/images/emoji/unicode/1f170.png new file mode 100644 index 000000000..4908a44fc Binary files /dev/null and b/app/assets/images/emoji/unicode/1f170.png differ diff --git a/app/assets/images/emoji/unicode/1f171.png b/app/assets/images/emoji/unicode/1f171.png new file mode 100644 index 000000000..8742b3d2e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f171.png differ diff --git a/app/assets/images/emoji/unicode/1f17e.png b/app/assets/images/emoji/unicode/1f17e.png new file mode 100644 index 000000000..d85f9fb98 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f17e.png differ diff --git a/app/assets/images/emoji/unicode/1f17f.png b/app/assets/images/emoji/unicode/1f17f.png new file mode 100644 index 000000000..c24af81cc Binary files /dev/null and b/app/assets/images/emoji/unicode/1f17f.png differ diff --git a/app/assets/images/emoji/unicode/1f18e.png b/app/assets/images/emoji/unicode/1f18e.png new file mode 100644 index 000000000..2a5222047 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f18e.png differ diff --git a/app/assets/images/emoji/unicode/1f191.png b/app/assets/images/emoji/unicode/1f191.png new file mode 100644 index 000000000..15ac67525 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f191.png differ diff --git a/app/assets/images/emoji/unicode/1f192.png b/app/assets/images/emoji/unicode/1f192.png new file mode 100644 index 000000000..937dcd792 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f192.png differ diff --git a/app/assets/images/emoji/unicode/1f193.png b/app/assets/images/emoji/unicode/1f193.png new file mode 100644 index 000000000..c886cf249 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f193.png differ diff --git a/app/assets/images/emoji/unicode/1f194.png b/app/assets/images/emoji/unicode/1f194.png new file mode 100644 index 000000000..47437a76d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f194.png differ diff --git a/app/assets/images/emoji/unicode/1f195.png b/app/assets/images/emoji/unicode/1f195.png new file mode 100644 index 000000000..28d1570e0 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f195.png differ diff --git a/app/assets/images/emoji/unicode/1f196.png b/app/assets/images/emoji/unicode/1f196.png new file mode 100644 index 000000000..2ca180ae3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f196.png differ diff --git a/app/assets/images/emoji/unicode/1f197.png b/app/assets/images/emoji/unicode/1f197.png new file mode 100644 index 000000000..6433d1a90 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f197.png differ diff --git a/app/assets/images/emoji/unicode/1f198.png b/app/assets/images/emoji/unicode/1f198.png new file mode 100644 index 000000000..e3e16ef73 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f198.png differ diff --git a/app/assets/images/emoji/unicode/1f199.png b/app/assets/images/emoji/unicode/1f199.png new file mode 100644 index 000000000..829219a86 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f199.png differ diff --git a/app/assets/images/emoji/unicode/1f19a.png b/app/assets/images/emoji/unicode/1f19a.png new file mode 100644 index 000000000..863638850 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f19a.png differ diff --git a/app/assets/images/emoji/unicode/1f1e8.png b/app/assets/images/emoji/unicode/1f1e8.png new file mode 100644 index 000000000..b30dcc53d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f1e8.png differ diff --git a/app/assets/images/emoji/unicode/1f1e9.png b/app/assets/images/emoji/unicode/1f1e9.png new file mode 100644 index 000000000..16a28548c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f1e9.png differ diff --git a/app/assets/images/emoji/unicode/1f1ea.png b/app/assets/images/emoji/unicode/1f1ea.png new file mode 100644 index 000000000..71b30bff3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f1ea.png differ diff --git a/app/assets/images/emoji/unicode/1f1eb.png b/app/assets/images/emoji/unicode/1f1eb.png new file mode 100644 index 000000000..6311c9115 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f1eb.png differ diff --git a/app/assets/images/emoji/unicode/1f1ec.png b/app/assets/images/emoji/unicode/1f1ec.png new file mode 100644 index 000000000..2a62c7a08 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f1ec.png differ diff --git a/app/assets/images/emoji/unicode/1f1ee.png b/app/assets/images/emoji/unicode/1f1ee.png new file mode 100644 index 000000000..70bc9f324 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f1ee.png differ diff --git a/app/assets/images/emoji/unicode/1f1ef.png b/app/assets/images/emoji/unicode/1f1ef.png new file mode 100644 index 000000000..b786efbbd Binary files /dev/null and b/app/assets/images/emoji/unicode/1f1ef.png differ diff --git a/app/assets/images/emoji/unicode/1f1f0.png b/app/assets/images/emoji/unicode/1f1f0.png new file mode 100644 index 000000000..b4c0c1b67 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f1f0.png differ diff --git a/app/assets/images/emoji/unicode/1f1f7.png b/app/assets/images/emoji/unicode/1f1f7.png new file mode 100644 index 000000000..55fcf3549 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f1f7.png differ diff --git a/app/assets/images/emoji/unicode/1f1fa.png b/app/assets/images/emoji/unicode/1f1fa.png new file mode 100644 index 000000000..38137669a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f1fa.png differ diff --git a/app/assets/images/emoji/unicode/1f201.png b/app/assets/images/emoji/unicode/1f201.png new file mode 100644 index 000000000..3bef28c9f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f201.png differ diff --git a/app/assets/images/emoji/unicode/1f202.png b/app/assets/images/emoji/unicode/1f202.png new file mode 100644 index 000000000..387f098b9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f202.png differ diff --git a/app/assets/images/emoji/unicode/1f21a.png b/app/assets/images/emoji/unicode/1f21a.png new file mode 100644 index 000000000..25f694ed3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f21a.png differ diff --git a/app/assets/images/emoji/unicode/1f22f.png b/app/assets/images/emoji/unicode/1f22f.png new file mode 100644 index 000000000..6557f5672 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f22f.png differ diff --git a/app/assets/images/emoji/unicode/1f232.png b/app/assets/images/emoji/unicode/1f232.png new file mode 100644 index 000000000..f550a573d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f232.png differ diff --git a/app/assets/images/emoji/unicode/1f233.png b/app/assets/images/emoji/unicode/1f233.png new file mode 100644 index 000000000..c05f5cff7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f233.png differ diff --git a/app/assets/images/emoji/unicode/1f234.png b/app/assets/images/emoji/unicode/1f234.png new file mode 100644 index 000000000..03ab0d874 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f234.png differ diff --git a/app/assets/images/emoji/unicode/1f235.png b/app/assets/images/emoji/unicode/1f235.png new file mode 100644 index 000000000..5df1cb878 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f235.png differ diff --git a/app/assets/images/emoji/unicode/1f236.png b/app/assets/images/emoji/unicode/1f236.png new file mode 100644 index 000000000..cd8fb3f62 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f236.png differ diff --git a/app/assets/images/emoji/unicode/1f237.png b/app/assets/images/emoji/unicode/1f237.png new file mode 100644 index 000000000..e4dfe5aa7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f237.png differ diff --git a/app/assets/images/emoji/unicode/1f238.png b/app/assets/images/emoji/unicode/1f238.png new file mode 100644 index 000000000..fc4a9901b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f238.png differ diff --git a/app/assets/images/emoji/unicode/1f239.png b/app/assets/images/emoji/unicode/1f239.png new file mode 100644 index 000000000..2148253fc Binary files /dev/null and b/app/assets/images/emoji/unicode/1f239.png differ diff --git a/app/assets/images/emoji/unicode/1f23a.png b/app/assets/images/emoji/unicode/1f23a.png new file mode 100644 index 000000000..ba946d3f3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f23a.png differ diff --git a/app/assets/images/emoji/unicode/1f250.png b/app/assets/images/emoji/unicode/1f250.png new file mode 100644 index 000000000..e79af7844 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f250.png differ diff --git a/app/assets/images/emoji/unicode/1f251.png b/app/assets/images/emoji/unicode/1f251.png new file mode 100644 index 000000000..2d2009031 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f251.png differ diff --git a/app/assets/images/emoji/unicode/1f300.png b/app/assets/images/emoji/unicode/1f300.png new file mode 100644 index 000000000..6c49f64b2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f300.png differ diff --git a/app/assets/images/emoji/unicode/1f301.png b/app/assets/images/emoji/unicode/1f301.png new file mode 100644 index 000000000..3c7b8b04b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f301.png differ diff --git a/app/assets/images/emoji/unicode/1f302.png b/app/assets/images/emoji/unicode/1f302.png new file mode 100644 index 000000000..072c5c217 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f302.png differ diff --git a/app/assets/images/emoji/unicode/1f303.png b/app/assets/images/emoji/unicode/1f303.png new file mode 100644 index 000000000..097a84241 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f303.png differ diff --git a/app/assets/images/emoji/unicode/1f304.png b/app/assets/images/emoji/unicode/1f304.png new file mode 100644 index 000000000..ebc3db146 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f304.png differ diff --git a/app/assets/images/emoji/unicode/1f305.png b/app/assets/images/emoji/unicode/1f305.png new file mode 100644 index 000000000..ec58dcc94 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f305.png differ diff --git a/app/assets/images/emoji/unicode/1f306.png b/app/assets/images/emoji/unicode/1f306.png new file mode 100644 index 000000000..7cb178a2c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f306.png differ diff --git a/app/assets/images/emoji/unicode/1f307.png b/app/assets/images/emoji/unicode/1f307.png new file mode 100644 index 000000000..91ca2a40b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f307.png differ diff --git a/app/assets/images/emoji/unicode/1f308.png b/app/assets/images/emoji/unicode/1f308.png new file mode 100644 index 000000000..6b1faa037 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f308.png differ diff --git a/app/assets/images/emoji/unicode/1f309.png b/app/assets/images/emoji/unicode/1f309.png new file mode 100644 index 000000000..495b06c3d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f309.png differ diff --git a/app/assets/images/emoji/unicode/1f30a.png b/app/assets/images/emoji/unicode/1f30a.png new file mode 100644 index 000000000..f8d520cd4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f30a.png differ diff --git a/app/assets/images/emoji/unicode/1f30b.png b/app/assets/images/emoji/unicode/1f30b.png new file mode 100644 index 000000000..9b434539b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f30b.png differ diff --git a/app/assets/images/emoji/unicode/1f30c.png b/app/assets/images/emoji/unicode/1f30c.png new file mode 100644 index 000000000..901090a12 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f30c.png differ diff --git a/app/assets/images/emoji/unicode/1f30d.png b/app/assets/images/emoji/unicode/1f30d.png new file mode 100644 index 000000000..44ce5ecb6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f30d.png differ diff --git a/app/assets/images/emoji/unicode/1f30e.png b/app/assets/images/emoji/unicode/1f30e.png new file mode 100644 index 000000000..97d717671 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f30e.png differ diff --git a/app/assets/images/emoji/unicode/1f30f.png b/app/assets/images/emoji/unicode/1f30f.png new file mode 100644 index 000000000..95ec357ca Binary files /dev/null and b/app/assets/images/emoji/unicode/1f30f.png differ diff --git a/app/assets/images/emoji/unicode/1f310.png b/app/assets/images/emoji/unicode/1f310.png new file mode 100644 index 000000000..b19864667 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f310.png differ diff --git a/app/assets/images/emoji/unicode/1f311.png b/app/assets/images/emoji/unicode/1f311.png new file mode 100644 index 000000000..540239b1f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f311.png differ diff --git a/app/assets/images/emoji/unicode/1f312.png b/app/assets/images/emoji/unicode/1f312.png new file mode 100644 index 000000000..c8f13dd31 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f312.png differ diff --git a/app/assets/images/emoji/unicode/1f313.png b/app/assets/images/emoji/unicode/1f313.png new file mode 100644 index 000000000..f38c23693 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f313.png differ diff --git a/app/assets/images/emoji/unicode/1f314.png b/app/assets/images/emoji/unicode/1f314.png new file mode 100644 index 000000000..dd8c48458 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f314.png differ diff --git a/app/assets/images/emoji/unicode/1f315.png b/app/assets/images/emoji/unicode/1f315.png new file mode 100644 index 000000000..8ff657a25 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f315.png differ diff --git a/app/assets/images/emoji/unicode/1f316.png b/app/assets/images/emoji/unicode/1f316.png new file mode 100644 index 000000000..8e324ec5f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f316.png differ diff --git a/app/assets/images/emoji/unicode/1f317.png b/app/assets/images/emoji/unicode/1f317.png new file mode 100644 index 000000000..355e3c3f7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f317.png differ diff --git a/app/assets/images/emoji/unicode/1f318.png b/app/assets/images/emoji/unicode/1f318.png new file mode 100644 index 000000000..30387780f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f318.png differ diff --git a/app/assets/images/emoji/unicode/1f319.png b/app/assets/images/emoji/unicode/1f319.png new file mode 100644 index 000000000..afdb450d1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f319.png differ diff --git a/app/assets/images/emoji/unicode/1f31a.png b/app/assets/images/emoji/unicode/1f31a.png new file mode 100644 index 000000000..b9aff7a06 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f31a.png differ diff --git a/app/assets/images/emoji/unicode/1f31b.png b/app/assets/images/emoji/unicode/1f31b.png new file mode 100644 index 000000000..85ae2ce72 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f31b.png differ diff --git a/app/assets/images/emoji/unicode/1f31c.png b/app/assets/images/emoji/unicode/1f31c.png new file mode 100644 index 000000000..9ece82dfe Binary files /dev/null and b/app/assets/images/emoji/unicode/1f31c.png differ diff --git a/app/assets/images/emoji/unicode/1f31d.png b/app/assets/images/emoji/unicode/1f31d.png new file mode 100644 index 000000000..94395a408 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f31d.png differ diff --git a/app/assets/images/emoji/unicode/1f31e.png b/app/assets/images/emoji/unicode/1f31e.png new file mode 100644 index 000000000..ee276636f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f31e.png differ diff --git a/app/assets/images/emoji/unicode/1f31f.png b/app/assets/images/emoji/unicode/1f31f.png new file mode 100644 index 000000000..8b40ff4c8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f31f.png differ diff --git a/app/assets/images/emoji/unicode/1f330.png b/app/assets/images/emoji/unicode/1f330.png new file mode 100644 index 000000000..066fb6bf6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f330.png differ diff --git a/app/assets/images/emoji/unicode/1f331.png b/app/assets/images/emoji/unicode/1f331.png new file mode 100644 index 000000000..f0eb5a6b9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f331.png differ diff --git a/app/assets/images/emoji/unicode/1f332.png b/app/assets/images/emoji/unicode/1f332.png new file mode 100644 index 000000000..ae8ad1037 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f332.png differ diff --git a/app/assets/images/emoji/unicode/1f333.png b/app/assets/images/emoji/unicode/1f333.png new file mode 100644 index 000000000..9bb16bdfe Binary files /dev/null and b/app/assets/images/emoji/unicode/1f333.png differ diff --git a/app/assets/images/emoji/unicode/1f334.png b/app/assets/images/emoji/unicode/1f334.png new file mode 100644 index 000000000..d534785ef Binary files /dev/null and b/app/assets/images/emoji/unicode/1f334.png differ diff --git a/app/assets/images/emoji/unicode/1f335.png b/app/assets/images/emoji/unicode/1f335.png new file mode 100644 index 000000000..5a2c3cc72 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f335.png differ diff --git a/app/assets/images/emoji/unicode/1f337.png b/app/assets/images/emoji/unicode/1f337.png new file mode 100644 index 000000000..b3ee1102a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f337.png differ diff --git a/app/assets/images/emoji/unicode/1f338.png b/app/assets/images/emoji/unicode/1f338.png new file mode 100644 index 000000000..e03155499 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f338.png differ diff --git a/app/assets/images/emoji/unicode/1f339.png b/app/assets/images/emoji/unicode/1f339.png new file mode 100644 index 000000000..3479fbcbb Binary files /dev/null and b/app/assets/images/emoji/unicode/1f339.png differ diff --git a/app/assets/images/emoji/unicode/1f33a.png b/app/assets/images/emoji/unicode/1f33a.png new file mode 100644 index 000000000..32a3774c0 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f33a.png differ diff --git a/app/assets/images/emoji/unicode/1f33b.png b/app/assets/images/emoji/unicode/1f33b.png new file mode 100644 index 000000000..d9bad194a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f33b.png differ diff --git a/app/assets/images/emoji/unicode/1f33c.png b/app/assets/images/emoji/unicode/1f33c.png new file mode 100644 index 000000000..55a97353b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f33c.png differ diff --git a/app/assets/images/emoji/unicode/1f33d.png b/app/assets/images/emoji/unicode/1f33d.png new file mode 100644 index 000000000..fe5d8b128 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f33d.png differ diff --git a/app/assets/images/emoji/unicode/1f33e.png b/app/assets/images/emoji/unicode/1f33e.png new file mode 100644 index 000000000..a9bba5c2c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f33e.png differ diff --git a/app/assets/images/emoji/unicode/1f33f.png b/app/assets/images/emoji/unicode/1f33f.png new file mode 100644 index 000000000..de1ff1b73 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f33f.png differ diff --git a/app/assets/images/emoji/unicode/1f340.png b/app/assets/images/emoji/unicode/1f340.png new file mode 100644 index 000000000..f2014bea4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f340.png differ diff --git a/app/assets/images/emoji/unicode/1f341.png b/app/assets/images/emoji/unicode/1f341.png new file mode 100644 index 000000000..4e9b47207 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f341.png differ diff --git a/app/assets/images/emoji/unicode/1f342.png b/app/assets/images/emoji/unicode/1f342.png new file mode 100644 index 000000000..d49f9c175 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f342.png differ diff --git a/app/assets/images/emoji/unicode/1f343.png b/app/assets/images/emoji/unicode/1f343.png new file mode 100644 index 000000000..801e578e6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f343.png differ diff --git a/app/assets/images/emoji/unicode/1f344.png b/app/assets/images/emoji/unicode/1f344.png new file mode 100644 index 000000000..5eeed8e79 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f344.png differ diff --git a/app/assets/images/emoji/unicode/1f345.png b/app/assets/images/emoji/unicode/1f345.png new file mode 100644 index 000000000..a129700bb Binary files /dev/null and b/app/assets/images/emoji/unicode/1f345.png differ diff --git a/app/assets/images/emoji/unicode/1f346.png b/app/assets/images/emoji/unicode/1f346.png new file mode 100644 index 000000000..566d6a844 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f346.png differ diff --git a/app/assets/images/emoji/unicode/1f347.png b/app/assets/images/emoji/unicode/1f347.png new file mode 100644 index 000000000..0f9f007a1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f347.png differ diff --git a/app/assets/images/emoji/unicode/1f348.png b/app/assets/images/emoji/unicode/1f348.png new file mode 100644 index 000000000..11c13cbbd Binary files /dev/null and b/app/assets/images/emoji/unicode/1f348.png differ diff --git a/app/assets/images/emoji/unicode/1f349.png b/app/assets/images/emoji/unicode/1f349.png new file mode 100644 index 000000000..fc212be78 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f349.png differ diff --git a/app/assets/images/emoji/unicode/1f34a.png b/app/assets/images/emoji/unicode/1f34a.png new file mode 100644 index 000000000..fc9d4f82a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f34a.png differ diff --git a/app/assets/images/emoji/unicode/1f34b.png b/app/assets/images/emoji/unicode/1f34b.png new file mode 100644 index 000000000..9814dc959 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f34b.png differ diff --git a/app/assets/images/emoji/unicode/1f34c.png b/app/assets/images/emoji/unicode/1f34c.png new file mode 100644 index 000000000..a0563afb9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f34c.png differ diff --git a/app/assets/images/emoji/unicode/1f34d.png b/app/assets/images/emoji/unicode/1f34d.png new file mode 100644 index 000000000..d6f8e2876 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f34d.png differ diff --git a/app/assets/images/emoji/unicode/1f34e.png b/app/assets/images/emoji/unicode/1f34e.png new file mode 100644 index 000000000..08aa17b95 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f34e.png differ diff --git a/app/assets/images/emoji/unicode/1f34f.png b/app/assets/images/emoji/unicode/1f34f.png new file mode 100644 index 000000000..337205cd1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f34f.png differ diff --git a/app/assets/images/emoji/unicode/1f350.png b/app/assets/images/emoji/unicode/1f350.png new file mode 100644 index 000000000..f24aca8c0 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f350.png differ diff --git a/app/assets/images/emoji/unicode/1f351.png b/app/assets/images/emoji/unicode/1f351.png new file mode 100644 index 000000000..ee2139ecb Binary files /dev/null and b/app/assets/images/emoji/unicode/1f351.png differ diff --git a/app/assets/images/emoji/unicode/1f352.png b/app/assets/images/emoji/unicode/1f352.png new file mode 100644 index 000000000..8d3e044f2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f352.png differ diff --git a/app/assets/images/emoji/unicode/1f353.png b/app/assets/images/emoji/unicode/1f353.png new file mode 100644 index 000000000..13eb827ab Binary files /dev/null and b/app/assets/images/emoji/unicode/1f353.png differ diff --git a/app/assets/images/emoji/unicode/1f354.png b/app/assets/images/emoji/unicode/1f354.png new file mode 100644 index 000000000..9f1a3fdff Binary files /dev/null and b/app/assets/images/emoji/unicode/1f354.png differ diff --git a/app/assets/images/emoji/unicode/1f355.png b/app/assets/images/emoji/unicode/1f355.png new file mode 100644 index 000000000..460367d02 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f355.png differ diff --git a/app/assets/images/emoji/unicode/1f356.png b/app/assets/images/emoji/unicode/1f356.png new file mode 100644 index 000000000..d6b311b6b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f356.png differ diff --git a/app/assets/images/emoji/unicode/1f357.png b/app/assets/images/emoji/unicode/1f357.png new file mode 100644 index 000000000..43ad85965 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f357.png differ diff --git a/app/assets/images/emoji/unicode/1f358.png b/app/assets/images/emoji/unicode/1f358.png new file mode 100644 index 000000000..954c901e9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f358.png differ diff --git a/app/assets/images/emoji/unicode/1f359.png b/app/assets/images/emoji/unicode/1f359.png new file mode 100644 index 000000000..04f8a8806 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f359.png differ diff --git a/app/assets/images/emoji/unicode/1f35a.png b/app/assets/images/emoji/unicode/1f35a.png new file mode 100644 index 000000000..f4773edec Binary files /dev/null and b/app/assets/images/emoji/unicode/1f35a.png differ diff --git a/app/assets/images/emoji/unicode/1f35b.png b/app/assets/images/emoji/unicode/1f35b.png new file mode 100644 index 000000000..7983c706a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f35b.png differ diff --git a/app/assets/images/emoji/unicode/1f35c.png b/app/assets/images/emoji/unicode/1f35c.png new file mode 100644 index 000000000..78dc7d537 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f35c.png differ diff --git a/app/assets/images/emoji/unicode/1f35d.png b/app/assets/images/emoji/unicode/1f35d.png new file mode 100644 index 000000000..08de243f5 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f35d.png differ diff --git a/app/assets/images/emoji/unicode/1f35e.png b/app/assets/images/emoji/unicode/1f35e.png new file mode 100644 index 000000000..7e7c63753 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f35e.png differ diff --git a/app/assets/images/emoji/unicode/1f35f.png b/app/assets/images/emoji/unicode/1f35f.png new file mode 100644 index 000000000..cfef66966 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f35f.png differ diff --git a/app/assets/images/emoji/unicode/1f360.png b/app/assets/images/emoji/unicode/1f360.png new file mode 100644 index 000000000..32117fa9c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f360.png differ diff --git a/app/assets/images/emoji/unicode/1f361.png b/app/assets/images/emoji/unicode/1f361.png new file mode 100644 index 000000000..2d042aebe Binary files /dev/null and b/app/assets/images/emoji/unicode/1f361.png differ diff --git a/app/assets/images/emoji/unicode/1f362.png b/app/assets/images/emoji/unicode/1f362.png new file mode 100644 index 000000000..73add1c73 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f362.png differ diff --git a/app/assets/images/emoji/unicode/1f363.png b/app/assets/images/emoji/unicode/1f363.png new file mode 100644 index 000000000..0d179bd97 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f363.png differ diff --git a/app/assets/images/emoji/unicode/1f364.png b/app/assets/images/emoji/unicode/1f364.png new file mode 100644 index 000000000..c8c284bf1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f364.png differ diff --git a/app/assets/images/emoji/unicode/1f365.png b/app/assets/images/emoji/unicode/1f365.png new file mode 100644 index 000000000..a8f22614d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f365.png differ diff --git a/app/assets/images/emoji/unicode/1f366.png b/app/assets/images/emoji/unicode/1f366.png new file mode 100644 index 000000000..871ce0976 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f366.png differ diff --git a/app/assets/images/emoji/unicode/1f367.png b/app/assets/images/emoji/unicode/1f367.png new file mode 100644 index 000000000..0d0b382c2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f367.png differ diff --git a/app/assets/images/emoji/unicode/1f368.png b/app/assets/images/emoji/unicode/1f368.png new file mode 100644 index 000000000..190be0165 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f368.png differ diff --git a/app/assets/images/emoji/unicode/1f369.png b/app/assets/images/emoji/unicode/1f369.png new file mode 100644 index 000000000..ccf869129 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f369.png differ diff --git a/app/assets/images/emoji/unicode/1f36a.png b/app/assets/images/emoji/unicode/1f36a.png new file mode 100644 index 000000000..653edb258 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f36a.png differ diff --git a/app/assets/images/emoji/unicode/1f36b.png b/app/assets/images/emoji/unicode/1f36b.png new file mode 100644 index 000000000..c7ec19d07 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f36b.png differ diff --git a/app/assets/images/emoji/unicode/1f36c.png b/app/assets/images/emoji/unicode/1f36c.png new file mode 100644 index 000000000..33722f236 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f36c.png differ diff --git a/app/assets/images/emoji/unicode/1f36d.png b/app/assets/images/emoji/unicode/1f36d.png new file mode 100644 index 000000000..ba55e7093 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f36d.png differ diff --git a/app/assets/images/emoji/unicode/1f36e.png b/app/assets/images/emoji/unicode/1f36e.png new file mode 100644 index 000000000..9f843b4c1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f36e.png differ diff --git a/app/assets/images/emoji/unicode/1f36f.png b/app/assets/images/emoji/unicode/1f36f.png new file mode 100644 index 000000000..73278898a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f36f.png differ diff --git a/app/assets/images/emoji/unicode/1f370.png b/app/assets/images/emoji/unicode/1f370.png new file mode 100644 index 000000000..efeb9b4b2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f370.png differ diff --git a/app/assets/images/emoji/unicode/1f371.png b/app/assets/images/emoji/unicode/1f371.png new file mode 100644 index 000000000..c6d99e89b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f371.png differ diff --git a/app/assets/images/emoji/unicode/1f372.png b/app/assets/images/emoji/unicode/1f372.png new file mode 100644 index 000000000..6e80b4a9c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f372.png differ diff --git a/app/assets/images/emoji/unicode/1f373.png b/app/assets/images/emoji/unicode/1f373.png new file mode 100644 index 000000000..c3de6ae4e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f373.png differ diff --git a/app/assets/images/emoji/unicode/1f374.png b/app/assets/images/emoji/unicode/1f374.png new file mode 100644 index 000000000..8ba4bc653 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f374.png differ diff --git a/app/assets/images/emoji/unicode/1f375.png b/app/assets/images/emoji/unicode/1f375.png new file mode 100644 index 000000000..3ece0b708 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f375.png differ diff --git a/app/assets/images/emoji/unicode/1f376.png b/app/assets/images/emoji/unicode/1f376.png new file mode 100644 index 000000000..1f69907e5 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f376.png differ diff --git a/app/assets/images/emoji/unicode/1f377.png b/app/assets/images/emoji/unicode/1f377.png new file mode 100644 index 000000000..82b0f0005 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f377.png differ diff --git a/app/assets/images/emoji/unicode/1f378.png b/app/assets/images/emoji/unicode/1f378.png new file mode 100644 index 000000000..28b45ea51 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f378.png differ diff --git a/app/assets/images/emoji/unicode/1f379.png b/app/assets/images/emoji/unicode/1f379.png new file mode 100644 index 000000000..55ca9eeda Binary files /dev/null and b/app/assets/images/emoji/unicode/1f379.png differ diff --git a/app/assets/images/emoji/unicode/1f37a.png b/app/assets/images/emoji/unicode/1f37a.png new file mode 100644 index 000000000..cd78bed74 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f37a.png differ diff --git a/app/assets/images/emoji/unicode/1f37b.png b/app/assets/images/emoji/unicode/1f37b.png new file mode 100644 index 000000000..cc5e4ab5a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f37b.png differ diff --git a/app/assets/images/emoji/unicode/1f37c.png b/app/assets/images/emoji/unicode/1f37c.png new file mode 100644 index 000000000..1b2cfe5e3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f37c.png differ diff --git a/app/assets/images/emoji/unicode/1f380.png b/app/assets/images/emoji/unicode/1f380.png new file mode 100644 index 000000000..63ee5ba5a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f380.png differ diff --git a/app/assets/images/emoji/unicode/1f381.png b/app/assets/images/emoji/unicode/1f381.png new file mode 100644 index 000000000..552cfdc2b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f381.png differ diff --git a/app/assets/images/emoji/unicode/1f382.png b/app/assets/images/emoji/unicode/1f382.png new file mode 100644 index 000000000..36e8edcbe Binary files /dev/null and b/app/assets/images/emoji/unicode/1f382.png differ diff --git a/app/assets/images/emoji/unicode/1f383.png b/app/assets/images/emoji/unicode/1f383.png new file mode 100644 index 000000000..1f7667ea4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f383.png differ diff --git a/app/assets/images/emoji/unicode/1f384.png b/app/assets/images/emoji/unicode/1f384.png new file mode 100644 index 000000000..d813b9593 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f384.png differ diff --git a/app/assets/images/emoji/unicode/1f385.png b/app/assets/images/emoji/unicode/1f385.png new file mode 100644 index 000000000..a2240c07e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f385.png differ diff --git a/app/assets/images/emoji/unicode/1f386.png b/app/assets/images/emoji/unicode/1f386.png new file mode 100644 index 000000000..b4eccd577 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f386.png differ diff --git a/app/assets/images/emoji/unicode/1f387.png b/app/assets/images/emoji/unicode/1f387.png new file mode 100644 index 000000000..4aabd7e0e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f387.png differ diff --git a/app/assets/images/emoji/unicode/1f388.png b/app/assets/images/emoji/unicode/1f388.png new file mode 100644 index 000000000..a4d3207b8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f388.png differ diff --git a/app/assets/images/emoji/unicode/1f389.png b/app/assets/images/emoji/unicode/1f389.png new file mode 100644 index 000000000..7411b5266 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f389.png differ diff --git a/app/assets/images/emoji/unicode/1f38a.png b/app/assets/images/emoji/unicode/1f38a.png new file mode 100644 index 000000000..bd293e3d8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f38a.png differ diff --git a/app/assets/images/emoji/unicode/1f38b.png b/app/assets/images/emoji/unicode/1f38b.png new file mode 100644 index 000000000..473346410 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f38b.png differ diff --git a/app/assets/images/emoji/unicode/1f38c.png b/app/assets/images/emoji/unicode/1f38c.png new file mode 100644 index 000000000..2ffbb2627 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f38c.png differ diff --git a/app/assets/images/emoji/unicode/1f38d.png b/app/assets/images/emoji/unicode/1f38d.png new file mode 100644 index 000000000..fc858d0fc Binary files /dev/null and b/app/assets/images/emoji/unicode/1f38d.png differ diff --git a/app/assets/images/emoji/unicode/1f38e.png b/app/assets/images/emoji/unicode/1f38e.png new file mode 100644 index 000000000..47ce33900 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f38e.png differ diff --git a/app/assets/images/emoji/unicode/1f38f.png b/app/assets/images/emoji/unicode/1f38f.png new file mode 100644 index 000000000..540164e84 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f38f.png differ diff --git a/app/assets/images/emoji/unicode/1f390.png b/app/assets/images/emoji/unicode/1f390.png new file mode 100644 index 000000000..efacf5dd4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f390.png differ diff --git a/app/assets/images/emoji/unicode/1f391.png b/app/assets/images/emoji/unicode/1f391.png new file mode 100644 index 000000000..14361988d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f391.png differ diff --git a/app/assets/images/emoji/unicode/1f392.png b/app/assets/images/emoji/unicode/1f392.png new file mode 100644 index 000000000..edfb19aec Binary files /dev/null and b/app/assets/images/emoji/unicode/1f392.png differ diff --git a/app/assets/images/emoji/unicode/1f393.png b/app/assets/images/emoji/unicode/1f393.png new file mode 100644 index 000000000..2e811b097 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f393.png differ diff --git a/app/assets/images/emoji/unicode/1f3a0.png b/app/assets/images/emoji/unicode/1f3a0.png new file mode 100644 index 000000000..765d2c0a8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3a0.png differ diff --git a/app/assets/images/emoji/unicode/1f3a1.png b/app/assets/images/emoji/unicode/1f3a1.png new file mode 100644 index 000000000..54a1dcfa1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3a1.png differ diff --git a/app/assets/images/emoji/unicode/1f3a2.png b/app/assets/images/emoji/unicode/1f3a2.png new file mode 100644 index 000000000..9180b9861 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3a2.png differ diff --git a/app/assets/images/emoji/unicode/1f3a3.png b/app/assets/images/emoji/unicode/1f3a3.png new file mode 100644 index 000000000..d84609c3b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3a3.png differ diff --git a/app/assets/images/emoji/unicode/1f3a4.png b/app/assets/images/emoji/unicode/1f3a4.png new file mode 100644 index 000000000..ce19a2bb6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3a4.png differ diff --git a/app/assets/images/emoji/unicode/1f3a5.png b/app/assets/images/emoji/unicode/1f3a5.png new file mode 100644 index 000000000..9c1438409 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3a5.png differ diff --git a/app/assets/images/emoji/unicode/1f3a6.png b/app/assets/images/emoji/unicode/1f3a6.png new file mode 100644 index 000000000..a990ccf99 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3a6.png differ diff --git a/app/assets/images/emoji/unicode/1f3a7.png b/app/assets/images/emoji/unicode/1f3a7.png new file mode 100644 index 000000000..ad83000e6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3a7.png differ diff --git a/app/assets/images/emoji/unicode/1f3a8.png b/app/assets/images/emoji/unicode/1f3a8.png new file mode 100644 index 000000000..d45212b03 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3a8.png differ diff --git a/app/assets/images/emoji/unicode/1f3a9.png b/app/assets/images/emoji/unicode/1f3a9.png new file mode 100644 index 000000000..7d27134d6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3a9.png differ diff --git a/app/assets/images/emoji/unicode/1f3aa.png b/app/assets/images/emoji/unicode/1f3aa.png new file mode 100644 index 000000000..4af8719aa Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3aa.png differ diff --git a/app/assets/images/emoji/unicode/1f3ab.png b/app/assets/images/emoji/unicode/1f3ab.png new file mode 100644 index 000000000..cdacf1a70 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3ab.png differ diff --git a/app/assets/images/emoji/unicode/1f3ac.png b/app/assets/images/emoji/unicode/1f3ac.png new file mode 100644 index 000000000..4e1dc111d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3ac.png differ diff --git a/app/assets/images/emoji/unicode/1f3ad.png b/app/assets/images/emoji/unicode/1f3ad.png new file mode 100644 index 000000000..899fbe5a7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3ad.png differ diff --git a/app/assets/images/emoji/unicode/1f3ae.png b/app/assets/images/emoji/unicode/1f3ae.png new file mode 100644 index 000000000..59d45baea Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3ae.png differ diff --git a/app/assets/images/emoji/unicode/1f3af.png b/app/assets/images/emoji/unicode/1f3af.png new file mode 100644 index 000000000..0438fe54f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3af.png differ diff --git a/app/assets/images/emoji/unicode/1f3b0.png b/app/assets/images/emoji/unicode/1f3b0.png new file mode 100644 index 000000000..26f114830 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3b0.png differ diff --git a/app/assets/images/emoji/unicode/1f3b1.png b/app/assets/images/emoji/unicode/1f3b1.png new file mode 100644 index 000000000..c2c710d45 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3b1.png differ diff --git a/app/assets/images/emoji/unicode/1f3b2.png b/app/assets/images/emoji/unicode/1f3b2.png new file mode 100644 index 000000000..4136e78ec Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3b2.png differ diff --git a/app/assets/images/emoji/unicode/1f3b3.png b/app/assets/images/emoji/unicode/1f3b3.png new file mode 100644 index 000000000..13d8ece2e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3b3.png differ diff --git a/app/assets/images/emoji/unicode/1f3b4.png b/app/assets/images/emoji/unicode/1f3b4.png new file mode 100644 index 000000000..cc46a6a1f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3b4.png differ diff --git a/app/assets/images/emoji/unicode/1f3b5.png b/app/assets/images/emoji/unicode/1f3b5.png new file mode 100644 index 000000000..68b261bcb Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3b5.png differ diff --git a/app/assets/images/emoji/unicode/1f3b6.png b/app/assets/images/emoji/unicode/1f3b6.png new file mode 100644 index 000000000..a13147fae Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3b6.png differ diff --git a/app/assets/images/emoji/unicode/1f3b7.png b/app/assets/images/emoji/unicode/1f3b7.png new file mode 100644 index 000000000..011559a76 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3b7.png differ diff --git a/app/assets/images/emoji/unicode/1f3b8.png b/app/assets/images/emoji/unicode/1f3b8.png new file mode 100644 index 000000000..2b7fa43c9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3b8.png differ diff --git a/app/assets/images/emoji/unicode/1f3b9.png b/app/assets/images/emoji/unicode/1f3b9.png new file mode 100644 index 000000000..93647a4a3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3b9.png differ diff --git a/app/assets/images/emoji/unicode/1f3ba.png b/app/assets/images/emoji/unicode/1f3ba.png new file mode 100644 index 000000000..8d4703fc2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3ba.png differ diff --git a/app/assets/images/emoji/unicode/1f3bb.png b/app/assets/images/emoji/unicode/1f3bb.png new file mode 100644 index 000000000..0dba5ba2b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3bb.png differ diff --git a/app/assets/images/emoji/unicode/1f3bc.png b/app/assets/images/emoji/unicode/1f3bc.png new file mode 100644 index 000000000..0c927d32f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3bc.png differ diff --git a/app/assets/images/emoji/unicode/1f3bd.png b/app/assets/images/emoji/unicode/1f3bd.png new file mode 100644 index 000000000..0d68bba09 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3bd.png differ diff --git a/app/assets/images/emoji/unicode/1f3be.png b/app/assets/images/emoji/unicode/1f3be.png new file mode 100644 index 000000000..278d904ee Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3be.png differ diff --git a/app/assets/images/emoji/unicode/1f3bf.png b/app/assets/images/emoji/unicode/1f3bf.png new file mode 100644 index 000000000..c97de3ed9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3bf.png differ diff --git a/app/assets/images/emoji/unicode/1f3c0.png b/app/assets/images/emoji/unicode/1f3c0.png new file mode 100644 index 000000000..ef694bec4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3c0.png differ diff --git a/app/assets/images/emoji/unicode/1f3c1.png b/app/assets/images/emoji/unicode/1f3c1.png new file mode 100644 index 000000000..ead4a68dd Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3c1.png differ diff --git a/app/assets/images/emoji/unicode/1f3c2.png b/app/assets/images/emoji/unicode/1f3c2.png new file mode 100644 index 000000000..aeda5c8d8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3c2.png differ diff --git a/app/assets/images/emoji/unicode/1f3c3.png b/app/assets/images/emoji/unicode/1f3c3.png new file mode 100644 index 000000000..1ecfd9059 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3c3.png differ diff --git a/app/assets/images/emoji/unicode/1f3c4.png b/app/assets/images/emoji/unicode/1f3c4.png new file mode 100644 index 000000000..b067e8cb3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3c4.png differ diff --git a/app/assets/images/emoji/unicode/1f3c6.png b/app/assets/images/emoji/unicode/1f3c6.png new file mode 100644 index 000000000..95d3b63f5 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3c6.png differ diff --git a/app/assets/images/emoji/unicode/1f3c7.png b/app/assets/images/emoji/unicode/1f3c7.png new file mode 100644 index 000000000..e3bbaec1d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3c7.png differ diff --git a/app/assets/images/emoji/unicode/1f3c8.png b/app/assets/images/emoji/unicode/1f3c8.png new file mode 100644 index 000000000..0e4e168fa Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3c8.png differ diff --git a/app/assets/images/emoji/unicode/1f3c9.png b/app/assets/images/emoji/unicode/1f3c9.png new file mode 100644 index 000000000..f8db67d70 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3c9.png differ diff --git a/app/assets/images/emoji/unicode/1f3ca.png b/app/assets/images/emoji/unicode/1f3ca.png new file mode 100644 index 000000000..d3878a065 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3ca.png differ diff --git a/app/assets/images/emoji/unicode/1f3e0.png b/app/assets/images/emoji/unicode/1f3e0.png new file mode 100644 index 000000000..95b9ee094 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3e0.png differ diff --git a/app/assets/images/emoji/unicode/1f3e1.png b/app/assets/images/emoji/unicode/1f3e1.png new file mode 100644 index 000000000..eccbfe943 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3e1.png differ diff --git a/app/assets/images/emoji/unicode/1f3e2.png b/app/assets/images/emoji/unicode/1f3e2.png new file mode 100644 index 000000000..3f20b5642 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3e2.png differ diff --git a/app/assets/images/emoji/unicode/1f3e3.png b/app/assets/images/emoji/unicode/1f3e3.png new file mode 100644 index 000000000..43b59e30e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3e3.png differ diff --git a/app/assets/images/emoji/unicode/1f3e4.png b/app/assets/images/emoji/unicode/1f3e4.png new file mode 100644 index 000000000..0f65b1453 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3e4.png differ diff --git a/app/assets/images/emoji/unicode/1f3e5.png b/app/assets/images/emoji/unicode/1f3e5.png new file mode 100644 index 000000000..c05c49377 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3e5.png differ diff --git a/app/assets/images/emoji/unicode/1f3e6.png b/app/assets/images/emoji/unicode/1f3e6.png new file mode 100644 index 000000000..1faa8777e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3e6.png differ diff --git a/app/assets/images/emoji/unicode/1f3e7.png b/app/assets/images/emoji/unicode/1f3e7.png new file mode 100644 index 000000000..c2846e792 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3e7.png differ diff --git a/app/assets/images/emoji/unicode/1f3e8.png b/app/assets/images/emoji/unicode/1f3e8.png new file mode 100644 index 000000000..d29f276a1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3e8.png differ diff --git a/app/assets/images/emoji/unicode/1f3e9.png b/app/assets/images/emoji/unicode/1f3e9.png new file mode 100644 index 000000000..44d7db828 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3e9.png differ diff --git a/app/assets/images/emoji/unicode/1f3ea.png b/app/assets/images/emoji/unicode/1f3ea.png new file mode 100644 index 000000000..671696c2d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3ea.png differ diff --git a/app/assets/images/emoji/unicode/1f3eb.png b/app/assets/images/emoji/unicode/1f3eb.png new file mode 100644 index 000000000..afd922bf1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3eb.png differ diff --git a/app/assets/images/emoji/unicode/1f3ec.png b/app/assets/images/emoji/unicode/1f3ec.png new file mode 100644 index 000000000..68d959c50 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3ec.png differ diff --git a/app/assets/images/emoji/unicode/1f3ed.png b/app/assets/images/emoji/unicode/1f3ed.png new file mode 100644 index 000000000..640463479 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3ed.png differ diff --git a/app/assets/images/emoji/unicode/1f3ee.png b/app/assets/images/emoji/unicode/1f3ee.png new file mode 100644 index 000000000..18730ad55 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3ee.png differ diff --git a/app/assets/images/emoji/unicode/1f3ef.png b/app/assets/images/emoji/unicode/1f3ef.png new file mode 100644 index 000000000..f225ab217 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3ef.png differ diff --git a/app/assets/images/emoji/unicode/1f3f0.png b/app/assets/images/emoji/unicode/1f3f0.png new file mode 100644 index 000000000..8229b8a8a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f3f0.png differ diff --git a/app/assets/images/emoji/unicode/1f400.png b/app/assets/images/emoji/unicode/1f400.png new file mode 100644 index 000000000..1c463dfde Binary files /dev/null and b/app/assets/images/emoji/unicode/1f400.png differ diff --git a/app/assets/images/emoji/unicode/1f401.png b/app/assets/images/emoji/unicode/1f401.png new file mode 100644 index 000000000..2d777e5e1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f401.png differ diff --git a/app/assets/images/emoji/unicode/1f402.png b/app/assets/images/emoji/unicode/1f402.png new file mode 100644 index 000000000..f76698024 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f402.png differ diff --git a/app/assets/images/emoji/unicode/1f403.png b/app/assets/images/emoji/unicode/1f403.png new file mode 100644 index 000000000..3bcde3edd Binary files /dev/null and b/app/assets/images/emoji/unicode/1f403.png differ diff --git a/app/assets/images/emoji/unicode/1f404.png b/app/assets/images/emoji/unicode/1f404.png new file mode 100644 index 000000000..594c92155 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f404.png differ diff --git a/app/assets/images/emoji/unicode/1f405.png b/app/assets/images/emoji/unicode/1f405.png new file mode 100644 index 000000000..b0c7d8dc3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f405.png differ diff --git a/app/assets/images/emoji/unicode/1f406.png b/app/assets/images/emoji/unicode/1f406.png new file mode 100644 index 000000000..8abfc4a27 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f406.png differ diff --git a/app/assets/images/emoji/unicode/1f407.png b/app/assets/images/emoji/unicode/1f407.png new file mode 100644 index 000000000..5bc993e79 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f407.png differ diff --git a/app/assets/images/emoji/unicode/1f408.png b/app/assets/images/emoji/unicode/1f408.png new file mode 100644 index 000000000..977c992c5 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f408.png differ diff --git a/app/assets/images/emoji/unicode/1f409.png b/app/assets/images/emoji/unicode/1f409.png new file mode 100644 index 000000000..e399d60e1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f409.png differ diff --git a/app/assets/images/emoji/unicode/1f40a.png b/app/assets/images/emoji/unicode/1f40a.png new file mode 100644 index 000000000..7435d5ab3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f40a.png differ diff --git a/app/assets/images/emoji/unicode/1f40b.png b/app/assets/images/emoji/unicode/1f40b.png new file mode 100644 index 000000000..4af657b2f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f40b.png differ diff --git a/app/assets/images/emoji/unicode/1f40c.png b/app/assets/images/emoji/unicode/1f40c.png new file mode 100644 index 000000000..e75e69a84 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f40c.png differ diff --git a/app/assets/images/emoji/unicode/1f40d.png b/app/assets/images/emoji/unicode/1f40d.png new file mode 100644 index 000000000..ef58933e2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f40d.png differ diff --git a/app/assets/images/emoji/unicode/1f40e.png b/app/assets/images/emoji/unicode/1f40e.png new file mode 100644 index 000000000..4d09c64de Binary files /dev/null and b/app/assets/images/emoji/unicode/1f40e.png differ diff --git a/app/assets/images/emoji/unicode/1f40f.png b/app/assets/images/emoji/unicode/1f40f.png new file mode 100644 index 000000000..5ea7bfbc0 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f40f.png differ diff --git a/app/assets/images/emoji/unicode/1f410.png b/app/assets/images/emoji/unicode/1f410.png new file mode 100644 index 000000000..4be9cf304 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f410.png differ diff --git a/app/assets/images/emoji/unicode/1f411.png b/app/assets/images/emoji/unicode/1f411.png new file mode 100644 index 000000000..c7277d289 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f411.png differ diff --git a/app/assets/images/emoji/unicode/1f412.png b/app/assets/images/emoji/unicode/1f412.png new file mode 100644 index 000000000..640703597 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f412.png differ diff --git a/app/assets/images/emoji/unicode/1f413.png b/app/assets/images/emoji/unicode/1f413.png new file mode 100644 index 000000000..fab23ad36 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f413.png differ diff --git a/app/assets/images/emoji/unicode/1f414.png b/app/assets/images/emoji/unicode/1f414.png new file mode 100644 index 000000000..6d25c0ef4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f414.png differ diff --git a/app/assets/images/emoji/unicode/1f415.png b/app/assets/images/emoji/unicode/1f415.png new file mode 100644 index 000000000..c7f6a24ac Binary files /dev/null and b/app/assets/images/emoji/unicode/1f415.png differ diff --git a/app/assets/images/emoji/unicode/1f416.png b/app/assets/images/emoji/unicode/1f416.png new file mode 100644 index 000000000..fec3374d7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f416.png differ diff --git a/app/assets/images/emoji/unicode/1f417.png b/app/assets/images/emoji/unicode/1f417.png new file mode 100644 index 000000000..8196ad4a1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f417.png differ diff --git a/app/assets/images/emoji/unicode/1f418.png b/app/assets/images/emoji/unicode/1f418.png new file mode 100644 index 000000000..5ca04570e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f418.png differ diff --git a/app/assets/images/emoji/unicode/1f419.png b/app/assets/images/emoji/unicode/1f419.png new file mode 100644 index 000000000..52ce64b46 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f419.png differ diff --git a/app/assets/images/emoji/unicode/1f41a.png b/app/assets/images/emoji/unicode/1f41a.png new file mode 100644 index 000000000..3145b5649 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f41a.png differ diff --git a/app/assets/images/emoji/unicode/1f41b.png b/app/assets/images/emoji/unicode/1f41b.png new file mode 100644 index 000000000..c2eaf7a70 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f41b.png differ diff --git a/app/assets/images/emoji/unicode/1f41c.png b/app/assets/images/emoji/unicode/1f41c.png new file mode 100644 index 000000000..b92d1cc14 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f41c.png differ diff --git a/app/assets/images/emoji/unicode/1f41d.png b/app/assets/images/emoji/unicode/1f41d.png new file mode 100644 index 000000000..f53733953 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f41d.png differ diff --git a/app/assets/images/emoji/unicode/1f41e.png b/app/assets/images/emoji/unicode/1f41e.png new file mode 100644 index 000000000..222577ca7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f41e.png differ diff --git a/app/assets/images/emoji/unicode/1f41f.png b/app/assets/images/emoji/unicode/1f41f.png new file mode 100644 index 000000000..dc2a3f52d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f41f.png differ diff --git a/app/assets/images/emoji/unicode/1f420.png b/app/assets/images/emoji/unicode/1f420.png new file mode 100644 index 000000000..a6d734987 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f420.png differ diff --git a/app/assets/images/emoji/unicode/1f421.png b/app/assets/images/emoji/unicode/1f421.png new file mode 100644 index 000000000..a1d47cb7e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f421.png differ diff --git a/app/assets/images/emoji/unicode/1f422.png b/app/assets/images/emoji/unicode/1f422.png new file mode 100644 index 000000000..04d1d9684 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f422.png differ diff --git a/app/assets/images/emoji/unicode/1f423.png b/app/assets/images/emoji/unicode/1f423.png new file mode 100644 index 000000000..005a55519 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f423.png differ diff --git a/app/assets/images/emoji/unicode/1f424.png b/app/assets/images/emoji/unicode/1f424.png new file mode 100644 index 000000000..9be8d2930 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f424.png differ diff --git a/app/assets/images/emoji/unicode/1f425.png b/app/assets/images/emoji/unicode/1f425.png new file mode 100644 index 000000000..39c25bc7c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f425.png differ diff --git a/app/assets/images/emoji/unicode/1f426.png b/app/assets/images/emoji/unicode/1f426.png new file mode 100644 index 000000000..e6be8c027 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f426.png differ diff --git a/app/assets/images/emoji/unicode/1f427.png b/app/assets/images/emoji/unicode/1f427.png new file mode 100644 index 000000000..d8edbcb8f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f427.png differ diff --git a/app/assets/images/emoji/unicode/1f428.png b/app/assets/images/emoji/unicode/1f428.png new file mode 100644 index 000000000..e17bd3cf5 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f428.png differ diff --git a/app/assets/images/emoji/unicode/1f429.png b/app/assets/images/emoji/unicode/1f429.png new file mode 100644 index 000000000..adac80bd9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f429.png differ diff --git a/app/assets/images/emoji/unicode/1f42a.png b/app/assets/images/emoji/unicode/1f42a.png new file mode 100644 index 000000000..c8c7b9ffa Binary files /dev/null and b/app/assets/images/emoji/unicode/1f42a.png differ diff --git a/app/assets/images/emoji/unicode/1f42b.png b/app/assets/images/emoji/unicode/1f42b.png new file mode 100644 index 000000000..496c186ae Binary files /dev/null and b/app/assets/images/emoji/unicode/1f42b.png differ diff --git a/app/assets/images/emoji/unicode/1f42c.png b/app/assets/images/emoji/unicode/1f42c.png new file mode 100644 index 000000000..9326077a9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f42c.png differ diff --git a/app/assets/images/emoji/unicode/1f42d.png b/app/assets/images/emoji/unicode/1f42d.png new file mode 100644 index 000000000..8ff162e2d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f42d.png differ diff --git a/app/assets/images/emoji/unicode/1f42e.png b/app/assets/images/emoji/unicode/1f42e.png new file mode 100644 index 000000000..12e1ab6c0 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f42e.png differ diff --git a/app/assets/images/emoji/unicode/1f42f.png b/app/assets/images/emoji/unicode/1f42f.png new file mode 100644 index 000000000..d6cc84a3b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f42f.png differ diff --git a/app/assets/images/emoji/unicode/1f430.png b/app/assets/images/emoji/unicode/1f430.png new file mode 100644 index 000000000..5cb3ef6f0 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f430.png differ diff --git a/app/assets/images/emoji/unicode/1f431.png b/app/assets/images/emoji/unicode/1f431.png new file mode 100644 index 000000000..09b9ef79a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f431.png differ diff --git a/app/assets/images/emoji/unicode/1f432.png b/app/assets/images/emoji/unicode/1f432.png new file mode 100644 index 000000000..e5e556bd1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f432.png differ diff --git a/app/assets/images/emoji/unicode/1f433.png b/app/assets/images/emoji/unicode/1f433.png new file mode 100644 index 000000000..5bb113e42 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f433.png differ diff --git a/app/assets/images/emoji/unicode/1f434.png b/app/assets/images/emoji/unicode/1f434.png new file mode 100644 index 000000000..78d580ad3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f434.png differ diff --git a/app/assets/images/emoji/unicode/1f435.png b/app/assets/images/emoji/unicode/1f435.png new file mode 100644 index 000000000..6964cf4d5 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f435.png differ diff --git a/app/assets/images/emoji/unicode/1f436.png b/app/assets/images/emoji/unicode/1f436.png new file mode 100644 index 000000000..389a02bf2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f436.png differ diff --git a/app/assets/images/emoji/unicode/1f437.png b/app/assets/images/emoji/unicode/1f437.png new file mode 100644 index 000000000..f7f273c73 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f437.png differ diff --git a/app/assets/images/emoji/unicode/1f438.png b/app/assets/images/emoji/unicode/1f438.png new file mode 100644 index 000000000..cfe11b18f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f438.png differ diff --git a/app/assets/images/emoji/unicode/1f439.png b/app/assets/images/emoji/unicode/1f439.png new file mode 100644 index 000000000..ada9c3108 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f439.png differ diff --git a/app/assets/images/emoji/unicode/1f43a.png b/app/assets/images/emoji/unicode/1f43a.png new file mode 100644 index 000000000..c60c96895 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f43a.png differ diff --git a/app/assets/images/emoji/unicode/1f43b.png b/app/assets/images/emoji/unicode/1f43b.png new file mode 100644 index 000000000..f5afe920e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f43b.png differ diff --git a/app/assets/images/emoji/unicode/1f43c.png b/app/assets/images/emoji/unicode/1f43c.png new file mode 100644 index 000000000..a794fb17f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f43c.png differ diff --git a/app/assets/images/emoji/unicode/1f43d.png b/app/assets/images/emoji/unicode/1f43d.png new file mode 100644 index 000000000..38d612446 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f43d.png differ diff --git a/app/assets/images/emoji/unicode/1f43e.png b/app/assets/images/emoji/unicode/1f43e.png new file mode 100644 index 000000000..89b9fec9e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f43e.png differ diff --git a/app/assets/images/emoji/unicode/1f440.png b/app/assets/images/emoji/unicode/1f440.png new file mode 100644 index 000000000..dc2216f63 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f440.png differ diff --git a/app/assets/images/emoji/unicode/1f442.png b/app/assets/images/emoji/unicode/1f442.png new file mode 100644 index 000000000..2bbbf10c9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f442.png differ diff --git a/app/assets/images/emoji/unicode/1f443.png b/app/assets/images/emoji/unicode/1f443.png new file mode 100644 index 000000000..ad17c16c2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f443.png differ diff --git a/app/assets/images/emoji/unicode/1f444.png b/app/assets/images/emoji/unicode/1f444.png new file mode 100644 index 000000000..826ed1102 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f444.png differ diff --git a/app/assets/images/emoji/unicode/1f445.png b/app/assets/images/emoji/unicode/1f445.png new file mode 100644 index 000000000..b0bab1207 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f445.png differ diff --git a/app/assets/images/emoji/unicode/1f446.png b/app/assets/images/emoji/unicode/1f446.png new file mode 100644 index 000000000..196d109a8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f446.png differ diff --git a/app/assets/images/emoji/unicode/1f447.png b/app/assets/images/emoji/unicode/1f447.png new file mode 100644 index 000000000..658c6d918 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f447.png differ diff --git a/app/assets/images/emoji/unicode/1f448.png b/app/assets/images/emoji/unicode/1f448.png new file mode 100644 index 000000000..fee9cac4d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f448.png differ diff --git a/app/assets/images/emoji/unicode/1f449.png b/app/assets/images/emoji/unicode/1f449.png new file mode 100644 index 000000000..b04e2849d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f449.png differ diff --git a/app/assets/images/emoji/unicode/1f44a.png b/app/assets/images/emoji/unicode/1f44a.png new file mode 100644 index 000000000..2d41fd37e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f44a.png differ diff --git a/app/assets/images/emoji/unicode/1f44b.png b/app/assets/images/emoji/unicode/1f44b.png new file mode 100644 index 000000000..e78402eb0 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f44b.png differ diff --git a/app/assets/images/emoji/unicode/1f44c.png b/app/assets/images/emoji/unicode/1f44c.png new file mode 100644 index 000000000..3177439dc Binary files /dev/null and b/app/assets/images/emoji/unicode/1f44c.png differ diff --git a/app/assets/images/emoji/unicode/1f44d.png b/app/assets/images/emoji/unicode/1f44d.png new file mode 100644 index 000000000..3a43ecae2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f44d.png differ diff --git a/app/assets/images/emoji/unicode/1f44e.png b/app/assets/images/emoji/unicode/1f44e.png new file mode 100644 index 000000000..e44c04219 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f44e.png differ diff --git a/app/assets/images/emoji/unicode/1f44f.png b/app/assets/images/emoji/unicode/1f44f.png new file mode 100644 index 000000000..d01c982a7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f44f.png differ diff --git a/app/assets/images/emoji/unicode/1f450.png b/app/assets/images/emoji/unicode/1f450.png new file mode 100644 index 000000000..2cc25bd41 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f450.png differ diff --git a/app/assets/images/emoji/unicode/1f451.png b/app/assets/images/emoji/unicode/1f451.png new file mode 100644 index 000000000..39da1d528 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f451.png differ diff --git a/app/assets/images/emoji/unicode/1f452.png b/app/assets/images/emoji/unicode/1f452.png new file mode 100644 index 000000000..4cb2e6a69 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f452.png differ diff --git a/app/assets/images/emoji/unicode/1f453.png b/app/assets/images/emoji/unicode/1f453.png new file mode 100644 index 000000000..a3cf75a27 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f453.png differ diff --git a/app/assets/images/emoji/unicode/1f454.png b/app/assets/images/emoji/unicode/1f454.png new file mode 100644 index 000000000..80461c66f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f454.png differ diff --git a/app/assets/images/emoji/unicode/1f455.png b/app/assets/images/emoji/unicode/1f455.png new file mode 100644 index 000000000..297a6d63e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f455.png differ diff --git a/app/assets/images/emoji/unicode/1f456.png b/app/assets/images/emoji/unicode/1f456.png new file mode 100644 index 000000000..d721cea54 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f456.png differ diff --git a/app/assets/images/emoji/unicode/1f457.png b/app/assets/images/emoji/unicode/1f457.png new file mode 100644 index 000000000..6434e2e2f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f457.png differ diff --git a/app/assets/images/emoji/unicode/1f458.png b/app/assets/images/emoji/unicode/1f458.png new file mode 100644 index 000000000..34ffe137d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f458.png differ diff --git a/app/assets/images/emoji/unicode/1f459.png b/app/assets/images/emoji/unicode/1f459.png new file mode 100644 index 000000000..4ff63b40f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f459.png differ diff --git a/app/assets/images/emoji/unicode/1f45a.png b/app/assets/images/emoji/unicode/1f45a.png new file mode 100644 index 000000000..aa297c7b6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f45a.png differ diff --git a/app/assets/images/emoji/unicode/1f45b.png b/app/assets/images/emoji/unicode/1f45b.png new file mode 100644 index 000000000..8f06a2b93 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f45b.png differ diff --git a/app/assets/images/emoji/unicode/1f45c.png b/app/assets/images/emoji/unicode/1f45c.png new file mode 100644 index 000000000..d7adf04dd Binary files /dev/null and b/app/assets/images/emoji/unicode/1f45c.png differ diff --git a/app/assets/images/emoji/unicode/1f45d.png b/app/assets/images/emoji/unicode/1f45d.png new file mode 100644 index 000000000..0bc5879fc Binary files /dev/null and b/app/assets/images/emoji/unicode/1f45d.png differ diff --git a/app/assets/images/emoji/unicode/1f45e.png b/app/assets/images/emoji/unicode/1f45e.png new file mode 100644 index 000000000..ecba9ba7d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f45e.png differ diff --git a/app/assets/images/emoji/unicode/1f45f.png b/app/assets/images/emoji/unicode/1f45f.png new file mode 100644 index 000000000..45b82e61c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f45f.png differ diff --git a/app/assets/images/emoji/unicode/1f460.png b/app/assets/images/emoji/unicode/1f460.png new file mode 100644 index 000000000..525b6a0dd Binary files /dev/null and b/app/assets/images/emoji/unicode/1f460.png differ diff --git a/app/assets/images/emoji/unicode/1f461.png b/app/assets/images/emoji/unicode/1f461.png new file mode 100644 index 000000000..aa62cca5d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f461.png differ diff --git a/app/assets/images/emoji/unicode/1f462.png b/app/assets/images/emoji/unicode/1f462.png new file mode 100644 index 000000000..58d0fdbcd Binary files /dev/null and b/app/assets/images/emoji/unicode/1f462.png differ diff --git a/app/assets/images/emoji/unicode/1f463.png b/app/assets/images/emoji/unicode/1f463.png new file mode 100644 index 000000000..d7a25614f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f463.png differ diff --git a/app/assets/images/emoji/unicode/1f464.png b/app/assets/images/emoji/unicode/1f464.png new file mode 100644 index 000000000..d13139869 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f464.png differ diff --git a/app/assets/images/emoji/unicode/1f465.png b/app/assets/images/emoji/unicode/1f465.png new file mode 100644 index 000000000..1f3aabcff Binary files /dev/null and b/app/assets/images/emoji/unicode/1f465.png differ diff --git a/app/assets/images/emoji/unicode/1f466.png b/app/assets/images/emoji/unicode/1f466.png new file mode 100644 index 000000000..f79f1f298 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f466.png differ diff --git a/app/assets/images/emoji/unicode/1f467.png b/app/assets/images/emoji/unicode/1f467.png new file mode 100644 index 000000000..ea4126941 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f467.png differ diff --git a/app/assets/images/emoji/unicode/1f468.png b/app/assets/images/emoji/unicode/1f468.png new file mode 100644 index 000000000..d9bfa26a6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f468.png differ diff --git a/app/assets/images/emoji/unicode/1f469.png b/app/assets/images/emoji/unicode/1f469.png new file mode 100644 index 000000000..6bf0d2b12 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f469.png differ diff --git a/app/assets/images/emoji/unicode/1f46a.png b/app/assets/images/emoji/unicode/1f46a.png new file mode 100644 index 000000000..b4b365f3a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f46a.png differ diff --git a/app/assets/images/emoji/unicode/1f46b.png b/app/assets/images/emoji/unicode/1f46b.png new file mode 100644 index 000000000..9e51f40e1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f46b.png differ diff --git a/app/assets/images/emoji/unicode/1f46c.png b/app/assets/images/emoji/unicode/1f46c.png new file mode 100644 index 000000000..d1099f21f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f46c.png differ diff --git a/app/assets/images/emoji/unicode/1f46d.png b/app/assets/images/emoji/unicode/1f46d.png new file mode 100644 index 000000000..619646c4e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f46d.png differ diff --git a/app/assets/images/emoji/unicode/1f46e.png b/app/assets/images/emoji/unicode/1f46e.png new file mode 100644 index 000000000..43a5a84f8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f46e.png differ diff --git a/app/assets/images/emoji/unicode/1f46f.png b/app/assets/images/emoji/unicode/1f46f.png new file mode 100644 index 000000000..2dfb451a7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f46f.png differ diff --git a/app/assets/images/emoji/unicode/1f470.png b/app/assets/images/emoji/unicode/1f470.png new file mode 100644 index 000000000..dd0b0cfda Binary files /dev/null and b/app/assets/images/emoji/unicode/1f470.png differ diff --git a/app/assets/images/emoji/unicode/1f471.png b/app/assets/images/emoji/unicode/1f471.png new file mode 100644 index 000000000..c144301cb Binary files /dev/null and b/app/assets/images/emoji/unicode/1f471.png differ diff --git a/app/assets/images/emoji/unicode/1f472.png b/app/assets/images/emoji/unicode/1f472.png new file mode 100644 index 000000000..7aad74b55 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f472.png differ diff --git a/app/assets/images/emoji/unicode/1f473.png b/app/assets/images/emoji/unicode/1f473.png new file mode 100644 index 000000000..036604caf Binary files /dev/null and b/app/assets/images/emoji/unicode/1f473.png differ diff --git a/app/assets/images/emoji/unicode/1f474.png b/app/assets/images/emoji/unicode/1f474.png new file mode 100644 index 000000000..149f0cfb8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f474.png differ diff --git a/app/assets/images/emoji/unicode/1f475.png b/app/assets/images/emoji/unicode/1f475.png new file mode 100644 index 000000000..f839565f4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f475.png differ diff --git a/app/assets/images/emoji/unicode/1f476.png b/app/assets/images/emoji/unicode/1f476.png new file mode 100644 index 000000000..3b29da40b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f476.png differ diff --git a/app/assets/images/emoji/unicode/1f477.png b/app/assets/images/emoji/unicode/1f477.png new file mode 100644 index 000000000..4d6486047 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f477.png differ diff --git a/app/assets/images/emoji/unicode/1f478.png b/app/assets/images/emoji/unicode/1f478.png new file mode 100644 index 000000000..1ebb2ce9b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f478.png differ diff --git a/app/assets/images/emoji/unicode/1f479.png b/app/assets/images/emoji/unicode/1f479.png new file mode 100644 index 000000000..e9f5471c9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f479.png differ diff --git a/app/assets/images/emoji/unicode/1f47a.png b/app/assets/images/emoji/unicode/1f47a.png new file mode 100644 index 000000000..bd21b1875 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f47a.png differ diff --git a/app/assets/images/emoji/unicode/1f47b.png b/app/assets/images/emoji/unicode/1f47b.png new file mode 100644 index 000000000..671dd0c9e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f47b.png differ diff --git a/app/assets/images/emoji/unicode/1f47c.png b/app/assets/images/emoji/unicode/1f47c.png new file mode 100644 index 000000000..da52c310c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f47c.png differ diff --git a/app/assets/images/emoji/unicode/1f47d.png b/app/assets/images/emoji/unicode/1f47d.png new file mode 100644 index 000000000..e3fd76a78 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f47d.png differ diff --git a/app/assets/images/emoji/unicode/1f47e.png b/app/assets/images/emoji/unicode/1f47e.png new file mode 100644 index 000000000..384049167 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f47e.png differ diff --git a/app/assets/images/emoji/unicode/1f47f.png b/app/assets/images/emoji/unicode/1f47f.png new file mode 100644 index 000000000..48e570105 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f47f.png differ diff --git a/app/assets/images/emoji/unicode/1f480.png b/app/assets/images/emoji/unicode/1f480.png new file mode 100644 index 000000000..bd4ee3829 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f480.png differ diff --git a/app/assets/images/emoji/unicode/1f481.png b/app/assets/images/emoji/unicode/1f481.png new file mode 100644 index 000000000..52c0a50a3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f481.png differ diff --git a/app/assets/images/emoji/unicode/1f482.png b/app/assets/images/emoji/unicode/1f482.png new file mode 100644 index 000000000..b67b335d6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f482.png differ diff --git a/app/assets/images/emoji/unicode/1f483.png b/app/assets/images/emoji/unicode/1f483.png new file mode 100644 index 000000000..6885a0bc3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f483.png differ diff --git a/app/assets/images/emoji/unicode/1f484.png b/app/assets/images/emoji/unicode/1f484.png new file mode 100644 index 000000000..82f990c56 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f484.png differ diff --git a/app/assets/images/emoji/unicode/1f485.png b/app/assets/images/emoji/unicode/1f485.png new file mode 100644 index 000000000..6a66e63d2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f485.png differ diff --git a/app/assets/images/emoji/unicode/1f486.png b/app/assets/images/emoji/unicode/1f486.png new file mode 100644 index 000000000..dd30d1597 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f486.png differ diff --git a/app/assets/images/emoji/unicode/1f487.png b/app/assets/images/emoji/unicode/1f487.png new file mode 100644 index 000000000..902d273f6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f487.png differ diff --git a/app/assets/images/emoji/unicode/1f488.png b/app/assets/images/emoji/unicode/1f488.png new file mode 100644 index 000000000..a10cb2322 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f488.png differ diff --git a/app/assets/images/emoji/unicode/1f489.png b/app/assets/images/emoji/unicode/1f489.png new file mode 100644 index 000000000..e7e7ab6e3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f489.png differ diff --git a/app/assets/images/emoji/unicode/1f48a.png b/app/assets/images/emoji/unicode/1f48a.png new file mode 100644 index 000000000..cd84a78ff Binary files /dev/null and b/app/assets/images/emoji/unicode/1f48a.png differ diff --git a/app/assets/images/emoji/unicode/1f48b.png b/app/assets/images/emoji/unicode/1f48b.png new file mode 100644 index 000000000..4ae2c2b5d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f48b.png differ diff --git a/app/assets/images/emoji/unicode/1f48c.png b/app/assets/images/emoji/unicode/1f48c.png new file mode 100644 index 000000000..e29981f44 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f48c.png differ diff --git a/app/assets/images/emoji/unicode/1f48d.png b/app/assets/images/emoji/unicode/1f48d.png new file mode 100644 index 000000000..8a57fd68b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f48d.png differ diff --git a/app/assets/images/emoji/unicode/1f48e.png b/app/assets/images/emoji/unicode/1f48e.png new file mode 100644 index 000000000..8a5d8dad5 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f48e.png differ diff --git a/app/assets/images/emoji/unicode/1f48f.png b/app/assets/images/emoji/unicode/1f48f.png new file mode 100644 index 000000000..d02790822 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f48f.png differ diff --git a/app/assets/images/emoji/unicode/1f490.png b/app/assets/images/emoji/unicode/1f490.png new file mode 100644 index 000000000..ce637832e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f490.png differ diff --git a/app/assets/images/emoji/unicode/1f491.png b/app/assets/images/emoji/unicode/1f491.png new file mode 100644 index 000000000..c503f40a9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f491.png differ diff --git a/app/assets/images/emoji/unicode/1f492.png b/app/assets/images/emoji/unicode/1f492.png new file mode 100644 index 000000000..ead19d52c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f492.png differ diff --git a/app/assets/images/emoji/unicode/1f493.png b/app/assets/images/emoji/unicode/1f493.png new file mode 100644 index 000000000..b6628f6fa Binary files /dev/null and b/app/assets/images/emoji/unicode/1f493.png differ diff --git a/app/assets/images/emoji/unicode/1f494.png b/app/assets/images/emoji/unicode/1f494.png new file mode 100644 index 000000000..a1bc850ec Binary files /dev/null and b/app/assets/images/emoji/unicode/1f494.png differ diff --git a/app/assets/images/emoji/unicode/1f495.png b/app/assets/images/emoji/unicode/1f495.png new file mode 100644 index 000000000..b189e9aea Binary files /dev/null and b/app/assets/images/emoji/unicode/1f495.png differ diff --git a/app/assets/images/emoji/unicode/1f496.png b/app/assets/images/emoji/unicode/1f496.png new file mode 100644 index 000000000..0826bbc06 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f496.png differ diff --git a/app/assets/images/emoji/unicode/1f497.png b/app/assets/images/emoji/unicode/1f497.png new file mode 100644 index 000000000..a7491cbea Binary files /dev/null and b/app/assets/images/emoji/unicode/1f497.png differ diff --git a/app/assets/images/emoji/unicode/1f498.png b/app/assets/images/emoji/unicode/1f498.png new file mode 100644 index 000000000..498728476 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f498.png differ diff --git a/app/assets/images/emoji/unicode/1f499.png b/app/assets/images/emoji/unicode/1f499.png new file mode 100644 index 000000000..baa29b31b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f499.png differ diff --git a/app/assets/images/emoji/unicode/1f49a.png b/app/assets/images/emoji/unicode/1f49a.png new file mode 100644 index 000000000..7289cb814 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f49a.png differ diff --git a/app/assets/images/emoji/unicode/1f49b.png b/app/assets/images/emoji/unicode/1f49b.png new file mode 100644 index 000000000..fa41ce78a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f49b.png differ diff --git a/app/assets/images/emoji/unicode/1f49c.png b/app/assets/images/emoji/unicode/1f49c.png new file mode 100644 index 000000000..d5f875043 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f49c.png differ diff --git a/app/assets/images/emoji/unicode/1f49d.png b/app/assets/images/emoji/unicode/1f49d.png new file mode 100644 index 000000000..f31c26a3f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f49d.png differ diff --git a/app/assets/images/emoji/unicode/1f49e.png b/app/assets/images/emoji/unicode/1f49e.png new file mode 100644 index 000000000..ea3317c47 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f49e.png differ diff --git a/app/assets/images/emoji/unicode/1f49f.png b/app/assets/images/emoji/unicode/1f49f.png new file mode 100644 index 000000000..b40a48675 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f49f.png differ diff --git a/app/assets/images/emoji/unicode/1f4a0.png b/app/assets/images/emoji/unicode/1f4a0.png new file mode 100644 index 000000000..dfd1098b3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4a0.png differ diff --git a/app/assets/images/emoji/unicode/1f4a1.png b/app/assets/images/emoji/unicode/1f4a1.png new file mode 100644 index 000000000..23afca1c7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4a1.png differ diff --git a/app/assets/images/emoji/unicode/1f4a2.png b/app/assets/images/emoji/unicode/1f4a2.png new file mode 100644 index 000000000..6fb4dca18 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4a2.png differ diff --git a/app/assets/images/emoji/unicode/1f4a3.png b/app/assets/images/emoji/unicode/1f4a3.png new file mode 100644 index 000000000..3289787dc Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4a3.png differ diff --git a/app/assets/images/emoji/unicode/1f4a4.png b/app/assets/images/emoji/unicode/1f4a4.png new file mode 100644 index 000000000..30be04655 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4a4.png differ diff --git a/app/assets/images/emoji/unicode/1f4a5.png b/app/assets/images/emoji/unicode/1f4a5.png new file mode 100644 index 000000000..bddeb8f49 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4a5.png differ diff --git a/app/assets/images/emoji/unicode/1f4a6.png b/app/assets/images/emoji/unicode/1f4a6.png new file mode 100644 index 000000000..a83b3e960 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4a6.png differ diff --git a/app/assets/images/emoji/unicode/1f4a7.png b/app/assets/images/emoji/unicode/1f4a7.png new file mode 100644 index 000000000..9eff46339 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4a7.png differ diff --git a/app/assets/images/emoji/unicode/1f4a8.png b/app/assets/images/emoji/unicode/1f4a8.png new file mode 100644 index 000000000..dc2c0a8f4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4a8.png differ diff --git a/app/assets/images/emoji/unicode/1f4a9.png b/app/assets/images/emoji/unicode/1f4a9.png new file mode 100644 index 000000000..73a4dc840 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4a9.png differ diff --git a/app/assets/images/emoji/unicode/1f4aa.png b/app/assets/images/emoji/unicode/1f4aa.png new file mode 100644 index 000000000..19f92efb6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4aa.png differ diff --git a/app/assets/images/emoji/unicode/1f4ab.png b/app/assets/images/emoji/unicode/1f4ab.png new file mode 100644 index 000000000..55213d2dd Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4ab.png differ diff --git a/app/assets/images/emoji/unicode/1f4ac.png b/app/assets/images/emoji/unicode/1f4ac.png new file mode 100644 index 000000000..2896c2788 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4ac.png differ diff --git a/app/assets/images/emoji/unicode/1f4ad.png b/app/assets/images/emoji/unicode/1f4ad.png new file mode 100644 index 000000000..701bdf0f6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4ad.png differ diff --git a/app/assets/images/emoji/unicode/1f4ae.png b/app/assets/images/emoji/unicode/1f4ae.png new file mode 100644 index 000000000..c0929d0dd Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4ae.png differ diff --git a/app/assets/images/emoji/unicode/1f4af.png b/app/assets/images/emoji/unicode/1f4af.png new file mode 100644 index 000000000..bce9ab14f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4af.png differ diff --git a/app/assets/images/emoji/unicode/1f4b0.png b/app/assets/images/emoji/unicode/1f4b0.png new file mode 100644 index 000000000..5546c04ba Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4b0.png differ diff --git a/app/assets/images/emoji/unicode/1f4b1.png b/app/assets/images/emoji/unicode/1f4b1.png new file mode 100644 index 000000000..d5ee21fc6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4b1.png differ diff --git a/app/assets/images/emoji/unicode/1f4b2.png b/app/assets/images/emoji/unicode/1f4b2.png new file mode 100644 index 000000000..361e26aef Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4b2.png differ diff --git a/app/assets/images/emoji/unicode/1f4b3.png b/app/assets/images/emoji/unicode/1f4b3.png new file mode 100644 index 000000000..be1c1dd30 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4b3.png differ diff --git a/app/assets/images/emoji/unicode/1f4b4.png b/app/assets/images/emoji/unicode/1f4b4.png new file mode 100644 index 000000000..139bc936e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4b4.png differ diff --git a/app/assets/images/emoji/unicode/1f4b5.png b/app/assets/images/emoji/unicode/1f4b5.png new file mode 100644 index 000000000..63de88495 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4b5.png differ diff --git a/app/assets/images/emoji/unicode/1f4b6.png b/app/assets/images/emoji/unicode/1f4b6.png new file mode 100644 index 000000000..1c5904b71 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4b6.png differ diff --git a/app/assets/images/emoji/unicode/1f4b7.png b/app/assets/images/emoji/unicode/1f4b7.png new file mode 100644 index 000000000..f8be91d7a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4b7.png differ diff --git a/app/assets/images/emoji/unicode/1f4b8.png b/app/assets/images/emoji/unicode/1f4b8.png new file mode 100644 index 000000000..135e3981e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4b8.png differ diff --git a/app/assets/images/emoji/unicode/1f4b9.png b/app/assets/images/emoji/unicode/1f4b9.png new file mode 100644 index 000000000..ac2c4bb09 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4b9.png differ diff --git a/app/assets/images/emoji/unicode/1f4ba.png b/app/assets/images/emoji/unicode/1f4ba.png new file mode 100644 index 000000000..d1cb864b4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4ba.png differ diff --git a/app/assets/images/emoji/unicode/1f4bb.png b/app/assets/images/emoji/unicode/1f4bb.png new file mode 100644 index 000000000..d4d268762 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4bb.png differ diff --git a/app/assets/images/emoji/unicode/1f4bc.png b/app/assets/images/emoji/unicode/1f4bc.png new file mode 100644 index 000000000..46e82b001 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4bc.png differ diff --git a/app/assets/images/emoji/unicode/1f4bd.png b/app/assets/images/emoji/unicode/1f4bd.png new file mode 100644 index 000000000..e19cc5d01 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4bd.png differ diff --git a/app/assets/images/emoji/unicode/1f4be.png b/app/assets/images/emoji/unicode/1f4be.png new file mode 100644 index 000000000..4ad56315a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4be.png differ diff --git a/app/assets/images/emoji/unicode/1f4bf.png b/app/assets/images/emoji/unicode/1f4bf.png new file mode 100644 index 000000000..baff835c4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4bf.png differ diff --git a/app/assets/images/emoji/unicode/1f4c0.png b/app/assets/images/emoji/unicode/1f4c0.png new file mode 100644 index 000000000..363c83d01 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4c0.png differ diff --git a/app/assets/images/emoji/unicode/1f4c1.png b/app/assets/images/emoji/unicode/1f4c1.png new file mode 100644 index 000000000..4d8bebf8a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4c1.png differ diff --git a/app/assets/images/emoji/unicode/1f4c2.png b/app/assets/images/emoji/unicode/1f4c2.png new file mode 100644 index 000000000..2bbbbf5e7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4c2.png differ diff --git a/app/assets/images/emoji/unicode/1f4c3.png b/app/assets/images/emoji/unicode/1f4c3.png new file mode 100644 index 000000000..bf8f979d3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4c3.png differ diff --git a/app/assets/images/emoji/unicode/1f4c4.png b/app/assets/images/emoji/unicode/1f4c4.png new file mode 100644 index 000000000..64cd2e1b2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4c4.png differ diff --git a/app/assets/images/emoji/unicode/1f4c5.png b/app/assets/images/emoji/unicode/1f4c5.png new file mode 100644 index 000000000..6ad2efa5f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4c5.png differ diff --git a/app/assets/images/emoji/unicode/1f4c6.png b/app/assets/images/emoji/unicode/1f4c6.png new file mode 100644 index 000000000..900b868bb Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4c6.png differ diff --git a/app/assets/images/emoji/unicode/1f4c7.png b/app/assets/images/emoji/unicode/1f4c7.png new file mode 100644 index 000000000..374e94e9e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4c7.png differ diff --git a/app/assets/images/emoji/unicode/1f4c8.png b/app/assets/images/emoji/unicode/1f4c8.png new file mode 100644 index 000000000..de3e9ba7b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4c8.png differ diff --git a/app/assets/images/emoji/unicode/1f4c9.png b/app/assets/images/emoji/unicode/1f4c9.png new file mode 100644 index 000000000..65b82f044 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4c9.png differ diff --git a/app/assets/images/emoji/unicode/1f4ca.png b/app/assets/images/emoji/unicode/1f4ca.png new file mode 100644 index 000000000..7871cc603 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4ca.png differ diff --git a/app/assets/images/emoji/unicode/1f4cb.png b/app/assets/images/emoji/unicode/1f4cb.png new file mode 100644 index 000000000..e2c74e6df Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4cb.png differ diff --git a/app/assets/images/emoji/unicode/1f4cc.png b/app/assets/images/emoji/unicode/1f4cc.png new file mode 100644 index 000000000..540c4ecb8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4cc.png differ diff --git a/app/assets/images/emoji/unicode/1f4cd.png b/app/assets/images/emoji/unicode/1f4cd.png new file mode 100644 index 000000000..e498e92cf Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4cd.png differ diff --git a/app/assets/images/emoji/unicode/1f4ce.png b/app/assets/images/emoji/unicode/1f4ce.png new file mode 100644 index 000000000..774412dc1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4ce.png differ diff --git a/app/assets/images/emoji/unicode/1f4cf.png b/app/assets/images/emoji/unicode/1f4cf.png new file mode 100644 index 000000000..af8cb4bcf Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4cf.png differ diff --git a/app/assets/images/emoji/unicode/1f4d0.png b/app/assets/images/emoji/unicode/1f4d0.png new file mode 100644 index 000000000..383677cb7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4d0.png differ diff --git a/app/assets/images/emoji/unicode/1f4d1.png b/app/assets/images/emoji/unicode/1f4d1.png new file mode 100644 index 000000000..0c4e3bf17 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4d1.png differ diff --git a/app/assets/images/emoji/unicode/1f4d2.png b/app/assets/images/emoji/unicode/1f4d2.png new file mode 100644 index 000000000..e4f72acea Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4d2.png differ diff --git a/app/assets/images/emoji/unicode/1f4d3.png b/app/assets/images/emoji/unicode/1f4d3.png new file mode 100644 index 000000000..07ea6087e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4d3.png differ diff --git a/app/assets/images/emoji/unicode/1f4d4.png b/app/assets/images/emoji/unicode/1f4d4.png new file mode 100644 index 000000000..4f3b14c85 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4d4.png differ diff --git a/app/assets/images/emoji/unicode/1f4d5.png b/app/assets/images/emoji/unicode/1f4d5.png new file mode 100644 index 000000000..484029c5e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4d5.png differ diff --git a/app/assets/images/emoji/unicode/1f4d6.png b/app/assets/images/emoji/unicode/1f4d6.png new file mode 100644 index 000000000..8b698415c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4d6.png differ diff --git a/app/assets/images/emoji/unicode/1f4d7.png b/app/assets/images/emoji/unicode/1f4d7.png new file mode 100644 index 000000000..e86651e5c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4d7.png differ diff --git a/app/assets/images/emoji/unicode/1f4d8.png b/app/assets/images/emoji/unicode/1f4d8.png new file mode 100644 index 000000000..e2b9e8c79 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4d8.png differ diff --git a/app/assets/images/emoji/unicode/1f4d9.png b/app/assets/images/emoji/unicode/1f4d9.png new file mode 100644 index 000000000..49650d59e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4d9.png differ diff --git a/app/assets/images/emoji/unicode/1f4da.png b/app/assets/images/emoji/unicode/1f4da.png new file mode 100644 index 000000000..dca06a1ad Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4da.png differ diff --git a/app/assets/images/emoji/unicode/1f4db.png b/app/assets/images/emoji/unicode/1f4db.png new file mode 100644 index 000000000..2b712dcd5 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4db.png differ diff --git a/app/assets/images/emoji/unicode/1f4dc.png b/app/assets/images/emoji/unicode/1f4dc.png new file mode 100644 index 000000000..c5a10e6b8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4dc.png differ diff --git a/app/assets/images/emoji/unicode/1f4dd.png b/app/assets/images/emoji/unicode/1f4dd.png new file mode 100644 index 000000000..fc97ddbc9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4dd.png differ diff --git a/app/assets/images/emoji/unicode/1f4de.png b/app/assets/images/emoji/unicode/1f4de.png new file mode 100644 index 000000000..36e21e012 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4de.png differ diff --git a/app/assets/images/emoji/unicode/1f4df.png b/app/assets/images/emoji/unicode/1f4df.png new file mode 100644 index 000000000..e3e1fc44e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4df.png differ diff --git a/app/assets/images/emoji/unicode/1f4e0.png b/app/assets/images/emoji/unicode/1f4e0.png new file mode 100644 index 000000000..62be2c958 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4e0.png differ diff --git a/app/assets/images/emoji/unicode/1f4e1.png b/app/assets/images/emoji/unicode/1f4e1.png new file mode 100644 index 000000000..3481cc2ef Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4e1.png differ diff --git a/app/assets/images/emoji/unicode/1f4e2.png b/app/assets/images/emoji/unicode/1f4e2.png new file mode 100644 index 000000000..752385e52 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4e2.png differ diff --git a/app/assets/images/emoji/unicode/1f4e3.png b/app/assets/images/emoji/unicode/1f4e3.png new file mode 100644 index 000000000..5d9319e72 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4e3.png differ diff --git a/app/assets/images/emoji/unicode/1f4e4.png b/app/assets/images/emoji/unicode/1f4e4.png new file mode 100644 index 000000000..7ad15e649 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4e4.png differ diff --git a/app/assets/images/emoji/unicode/1f4e5.png b/app/assets/images/emoji/unicode/1f4e5.png new file mode 100644 index 000000000..e2df0f897 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4e5.png differ diff --git a/app/assets/images/emoji/unicode/1f4e6.png b/app/assets/images/emoji/unicode/1f4e6.png new file mode 100644 index 000000000..26602af9d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4e6.png differ diff --git a/app/assets/images/emoji/unicode/1f4e7.png b/app/assets/images/emoji/unicode/1f4e7.png new file mode 100644 index 000000000..176a8e1e8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4e7.png differ diff --git a/app/assets/images/emoji/unicode/1f4e8.png b/app/assets/images/emoji/unicode/1f4e8.png new file mode 100644 index 000000000..afc827125 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4e8.png differ diff --git a/app/assets/images/emoji/unicode/1f4e9.png b/app/assets/images/emoji/unicode/1f4e9.png new file mode 100644 index 000000000..0e01fd5f0 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4e9.png differ diff --git a/app/assets/images/emoji/unicode/1f4ea.png b/app/assets/images/emoji/unicode/1f4ea.png new file mode 100644 index 000000000..a5982b69b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4ea.png differ diff --git a/app/assets/images/emoji/unicode/1f4eb.png b/app/assets/images/emoji/unicode/1f4eb.png new file mode 100644 index 000000000..8351e7076 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4eb.png differ diff --git a/app/assets/images/emoji/unicode/1f4ec.png b/app/assets/images/emoji/unicode/1f4ec.png new file mode 100644 index 000000000..dae345943 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4ec.png differ diff --git a/app/assets/images/emoji/unicode/1f4ed.png b/app/assets/images/emoji/unicode/1f4ed.png new file mode 100644 index 000000000..59f15c5d7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4ed.png differ diff --git a/app/assets/images/emoji/unicode/1f4ee.png b/app/assets/images/emoji/unicode/1f4ee.png new file mode 100644 index 000000000..ce04b7008 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4ee.png differ diff --git a/app/assets/images/emoji/unicode/1f4ef.png b/app/assets/images/emoji/unicode/1f4ef.png new file mode 100644 index 000000000..e9b713bbe Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4ef.png differ diff --git a/app/assets/images/emoji/unicode/1f4f0.png b/app/assets/images/emoji/unicode/1f4f0.png new file mode 100644 index 000000000..d171394e6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4f0.png differ diff --git a/app/assets/images/emoji/unicode/1f4f1.png b/app/assets/images/emoji/unicode/1f4f1.png new file mode 100644 index 000000000..df007103b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4f1.png differ diff --git a/app/assets/images/emoji/unicode/1f4f2.png b/app/assets/images/emoji/unicode/1f4f2.png new file mode 100644 index 000000000..837897f26 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4f2.png differ diff --git a/app/assets/images/emoji/unicode/1f4f3.png b/app/assets/images/emoji/unicode/1f4f3.png new file mode 100644 index 000000000..a716e96c6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4f3.png differ diff --git a/app/assets/images/emoji/unicode/1f4f4.png b/app/assets/images/emoji/unicode/1f4f4.png new file mode 100644 index 000000000..fa16c763c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4f4.png differ diff --git a/app/assets/images/emoji/unicode/1f4f5.png b/app/assets/images/emoji/unicode/1f4f5.png new file mode 100644 index 000000000..41df57cf8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4f5.png differ diff --git a/app/assets/images/emoji/unicode/1f4f6.png b/app/assets/images/emoji/unicode/1f4f6.png new file mode 100644 index 000000000..a4bd23ebf Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4f6.png differ diff --git a/app/assets/images/emoji/unicode/1f4f7.png b/app/assets/images/emoji/unicode/1f4f7.png new file mode 100644 index 000000000..397d03b39 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4f7.png differ diff --git a/app/assets/images/emoji/unicode/1f4f9.png b/app/assets/images/emoji/unicode/1f4f9.png new file mode 100644 index 000000000..274cecdd6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4f9.png differ diff --git a/app/assets/images/emoji/unicode/1f4fa.png b/app/assets/images/emoji/unicode/1f4fa.png new file mode 100644 index 000000000..803dc3d41 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4fa.png differ diff --git a/app/assets/images/emoji/unicode/1f4fb.png b/app/assets/images/emoji/unicode/1f4fb.png new file mode 100644 index 000000000..ea589efe3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4fb.png differ diff --git a/app/assets/images/emoji/unicode/1f4fc.png b/app/assets/images/emoji/unicode/1f4fc.png new file mode 100644 index 000000000..881081c17 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f4fc.png differ diff --git a/app/assets/images/emoji/unicode/1f500.png b/app/assets/images/emoji/unicode/1f500.png new file mode 100644 index 000000000..25cde18b2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f500.png differ diff --git a/app/assets/images/emoji/unicode/1f501.png b/app/assets/images/emoji/unicode/1f501.png new file mode 100644 index 000000000..80113b692 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f501.png differ diff --git a/app/assets/images/emoji/unicode/1f502.png b/app/assets/images/emoji/unicode/1f502.png new file mode 100644 index 000000000..3c47bcc1f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f502.png differ diff --git a/app/assets/images/emoji/unicode/1f503.png b/app/assets/images/emoji/unicode/1f503.png new file mode 100644 index 000000000..5f84d7e72 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f503.png differ diff --git a/app/assets/images/emoji/unicode/1f504.png b/app/assets/images/emoji/unicode/1f504.png new file mode 100644 index 000000000..1933ae18b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f504.png differ diff --git a/app/assets/images/emoji/unicode/1f505.png b/app/assets/images/emoji/unicode/1f505.png new file mode 100644 index 000000000..ea15bde4f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f505.png differ diff --git a/app/assets/images/emoji/unicode/1f506.png b/app/assets/images/emoji/unicode/1f506.png new file mode 100644 index 000000000..ba9de7d40 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f506.png differ diff --git a/app/assets/images/emoji/unicode/1f507.png b/app/assets/images/emoji/unicode/1f507.png new file mode 100644 index 000000000..4cf67c367 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f507.png differ diff --git a/app/assets/images/emoji/unicode/1f509.png b/app/assets/images/emoji/unicode/1f509.png new file mode 100644 index 000000000..6aa4dbff4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f509.png differ diff --git a/app/assets/images/emoji/unicode/1f50a.png b/app/assets/images/emoji/unicode/1f50a.png new file mode 100644 index 000000000..c884bd4f6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f50a.png differ diff --git a/app/assets/images/emoji/unicode/1f50b.png b/app/assets/images/emoji/unicode/1f50b.png new file mode 100644 index 000000000..aa7eedce4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f50b.png differ diff --git a/app/assets/images/emoji/unicode/1f50c.png b/app/assets/images/emoji/unicode/1f50c.png new file mode 100644 index 000000000..7a3d6cee6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f50c.png differ diff --git a/app/assets/images/emoji/unicode/1f50d.png b/app/assets/images/emoji/unicode/1f50d.png new file mode 100644 index 000000000..aa5b1d7c4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f50d.png differ diff --git a/app/assets/images/emoji/unicode/1f50e.png b/app/assets/images/emoji/unicode/1f50e.png new file mode 100644 index 000000000..6e6cf11e6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f50e.png differ diff --git a/app/assets/images/emoji/unicode/1f50f.png b/app/assets/images/emoji/unicode/1f50f.png new file mode 100644 index 000000000..375e67e82 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f50f.png differ diff --git a/app/assets/images/emoji/unicode/1f510.png b/app/assets/images/emoji/unicode/1f510.png new file mode 100644 index 000000000..e6fdf6cb2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f510.png differ diff --git a/app/assets/images/emoji/unicode/1f511.png b/app/assets/images/emoji/unicode/1f511.png new file mode 100644 index 000000000..34673213f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f511.png differ diff --git a/app/assets/images/emoji/unicode/1f512.png b/app/assets/images/emoji/unicode/1f512.png new file mode 100644 index 000000000..4892b0235 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f512.png differ diff --git a/app/assets/images/emoji/unicode/1f513.png b/app/assets/images/emoji/unicode/1f513.png new file mode 100644 index 000000000..22b429cd0 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f513.png differ diff --git a/app/assets/images/emoji/unicode/1f514.png b/app/assets/images/emoji/unicode/1f514.png new file mode 100644 index 000000000..69acceb28 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f514.png differ diff --git a/app/assets/images/emoji/unicode/1f515.png b/app/assets/images/emoji/unicode/1f515.png new file mode 100644 index 000000000..613b81cd2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f515.png differ diff --git a/app/assets/images/emoji/unicode/1f516.png b/app/assets/images/emoji/unicode/1f516.png new file mode 100644 index 000000000..dbee45c60 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f516.png differ diff --git a/app/assets/images/emoji/unicode/1f517.png b/app/assets/images/emoji/unicode/1f517.png new file mode 100644 index 000000000..ffb8f62ce Binary files /dev/null and b/app/assets/images/emoji/unicode/1f517.png differ diff --git a/app/assets/images/emoji/unicode/1f518.png b/app/assets/images/emoji/unicode/1f518.png new file mode 100644 index 000000000..63755eec2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f518.png differ diff --git a/app/assets/images/emoji/unicode/1f519.png b/app/assets/images/emoji/unicode/1f519.png new file mode 100644 index 000000000..0cde62876 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f519.png differ diff --git a/app/assets/images/emoji/unicode/1f51a.png b/app/assets/images/emoji/unicode/1f51a.png new file mode 100644 index 000000000..edb0bda24 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f51a.png differ diff --git a/app/assets/images/emoji/unicode/1f51b.png b/app/assets/images/emoji/unicode/1f51b.png new file mode 100644 index 000000000..3595387fb Binary files /dev/null and b/app/assets/images/emoji/unicode/1f51b.png differ diff --git a/app/assets/images/emoji/unicode/1f51c.png b/app/assets/images/emoji/unicode/1f51c.png new file mode 100644 index 000000000..9386615a3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f51c.png differ diff --git a/app/assets/images/emoji/unicode/1f51d.png b/app/assets/images/emoji/unicode/1f51d.png new file mode 100644 index 000000000..5aa4dd442 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f51d.png differ diff --git a/app/assets/images/emoji/unicode/1f51e.png b/app/assets/images/emoji/unicode/1f51e.png new file mode 100644 index 000000000..a789b3c62 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f51e.png differ diff --git a/app/assets/images/emoji/unicode/1f51f.png b/app/assets/images/emoji/unicode/1f51f.png new file mode 100644 index 000000000..71dac1c1c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f51f.png differ diff --git a/app/assets/images/emoji/unicode/1f520.png b/app/assets/images/emoji/unicode/1f520.png new file mode 100644 index 000000000..ffc0cba4b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f520.png differ diff --git a/app/assets/images/emoji/unicode/1f521.png b/app/assets/images/emoji/unicode/1f521.png new file mode 100644 index 000000000..5218470b6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f521.png differ diff --git a/app/assets/images/emoji/unicode/1f522.png b/app/assets/images/emoji/unicode/1f522.png new file mode 100644 index 000000000..c47c2e1f9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f522.png differ diff --git a/app/assets/images/emoji/unicode/1f523.png b/app/assets/images/emoji/unicode/1f523.png new file mode 100644 index 000000000..16bc1da92 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f523.png differ diff --git a/app/assets/images/emoji/unicode/1f524.png b/app/assets/images/emoji/unicode/1f524.png new file mode 100644 index 000000000..505d40a15 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f524.png differ diff --git a/app/assets/images/emoji/unicode/1f525.png b/app/assets/images/emoji/unicode/1f525.png new file mode 100644 index 000000000..f2a3149bb Binary files /dev/null and b/app/assets/images/emoji/unicode/1f525.png differ diff --git a/app/assets/images/emoji/unicode/1f526.png b/app/assets/images/emoji/unicode/1f526.png new file mode 100644 index 000000000..215940aa8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f526.png differ diff --git a/app/assets/images/emoji/unicode/1f527.png b/app/assets/images/emoji/unicode/1f527.png new file mode 100644 index 000000000..a87072ad1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f527.png differ diff --git a/app/assets/images/emoji/unicode/1f528.png b/app/assets/images/emoji/unicode/1f528.png new file mode 100644 index 000000000..6b75bc37b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f528.png differ diff --git a/app/assets/images/emoji/unicode/1f529.png b/app/assets/images/emoji/unicode/1f529.png new file mode 100644 index 000000000..bddfa72a7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f529.png differ diff --git a/app/assets/images/emoji/unicode/1f52a.png b/app/assets/images/emoji/unicode/1f52a.png new file mode 100644 index 000000000..18eade0ac Binary files /dev/null and b/app/assets/images/emoji/unicode/1f52a.png differ diff --git a/app/assets/images/emoji/unicode/1f52b.png b/app/assets/images/emoji/unicode/1f52b.png new file mode 100644 index 000000000..c49dc52c6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f52b.png differ diff --git a/app/assets/images/emoji/unicode/1f52c.png b/app/assets/images/emoji/unicode/1f52c.png new file mode 100644 index 000000000..f11d54c01 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f52c.png differ diff --git a/app/assets/images/emoji/unicode/1f52d.png b/app/assets/images/emoji/unicode/1f52d.png new file mode 100644 index 000000000..51fd8a07f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f52d.png differ diff --git a/app/assets/images/emoji/unicode/1f52e.png b/app/assets/images/emoji/unicode/1f52e.png new file mode 100644 index 000000000..6d2c6c42d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f52e.png differ diff --git a/app/assets/images/emoji/unicode/1f52f.png b/app/assets/images/emoji/unicode/1f52f.png new file mode 100644 index 000000000..010f8f5f9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f52f.png differ diff --git a/app/assets/images/emoji/unicode/1f530.png b/app/assets/images/emoji/unicode/1f530.png new file mode 100644 index 000000000..1f022d175 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f530.png differ diff --git a/app/assets/images/emoji/unicode/1f531.png b/app/assets/images/emoji/unicode/1f531.png new file mode 100644 index 000000000..d79a7b4cc Binary files /dev/null and b/app/assets/images/emoji/unicode/1f531.png differ diff --git a/app/assets/images/emoji/unicode/1f532.png b/app/assets/images/emoji/unicode/1f532.png new file mode 100644 index 000000000..7332e397c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f532.png differ diff --git a/app/assets/images/emoji/unicode/1f533.png b/app/assets/images/emoji/unicode/1f533.png new file mode 100644 index 000000000..63c7a3ef4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f533.png differ diff --git a/app/assets/images/emoji/unicode/1f534.png b/app/assets/images/emoji/unicode/1f534.png new file mode 100644 index 000000000..b391289b2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f534.png differ diff --git a/app/assets/images/emoji/unicode/1f535.png b/app/assets/images/emoji/unicode/1f535.png new file mode 100644 index 000000000..a5b4ad4aa Binary files /dev/null and b/app/assets/images/emoji/unicode/1f535.png differ diff --git a/app/assets/images/emoji/unicode/1f536.png b/app/assets/images/emoji/unicode/1f536.png new file mode 100644 index 000000000..46d52e5cb Binary files /dev/null and b/app/assets/images/emoji/unicode/1f536.png differ diff --git a/app/assets/images/emoji/unicode/1f537.png b/app/assets/images/emoji/unicode/1f537.png new file mode 100644 index 000000000..f4598ec0f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f537.png differ diff --git a/app/assets/images/emoji/unicode/1f538.png b/app/assets/images/emoji/unicode/1f538.png new file mode 100644 index 000000000..04941d37b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f538.png differ diff --git a/app/assets/images/emoji/unicode/1f539.png b/app/assets/images/emoji/unicode/1f539.png new file mode 100644 index 000000000..5a7b5d555 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f539.png differ diff --git a/app/assets/images/emoji/unicode/1f53a.png b/app/assets/images/emoji/unicode/1f53a.png new file mode 100644 index 000000000..8c4428da8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f53a.png differ diff --git a/app/assets/images/emoji/unicode/1f53b.png b/app/assets/images/emoji/unicode/1f53b.png new file mode 100644 index 000000000..94832f060 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f53b.png differ diff --git a/app/assets/images/emoji/unicode/1f53c.png b/app/assets/images/emoji/unicode/1f53c.png new file mode 100644 index 000000000..121733197 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f53c.png differ diff --git a/app/assets/images/emoji/unicode/1f53d.png b/app/assets/images/emoji/unicode/1f53d.png new file mode 100644 index 000000000..f7f2d5101 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f53d.png differ diff --git a/app/assets/images/emoji/unicode/1f550.png b/app/assets/images/emoji/unicode/1f550.png new file mode 100644 index 000000000..ca34e8975 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f550.png differ diff --git a/app/assets/images/emoji/unicode/1f551.png b/app/assets/images/emoji/unicode/1f551.png new file mode 100644 index 000000000..1a12524ee Binary files /dev/null and b/app/assets/images/emoji/unicode/1f551.png differ diff --git a/app/assets/images/emoji/unicode/1f552.png b/app/assets/images/emoji/unicode/1f552.png new file mode 100644 index 000000000..cd99bb155 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f552.png differ diff --git a/app/assets/images/emoji/unicode/1f553.png b/app/assets/images/emoji/unicode/1f553.png new file mode 100644 index 000000000..7274e8b07 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f553.png differ diff --git a/app/assets/images/emoji/unicode/1f554.png b/app/assets/images/emoji/unicode/1f554.png new file mode 100644 index 000000000..3ed5a81af Binary files /dev/null and b/app/assets/images/emoji/unicode/1f554.png differ diff --git a/app/assets/images/emoji/unicode/1f555.png b/app/assets/images/emoji/unicode/1f555.png new file mode 100644 index 000000000..ac38cb926 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f555.png differ diff --git a/app/assets/images/emoji/unicode/1f556.png b/app/assets/images/emoji/unicode/1f556.png new file mode 100644 index 000000000..6a138dfde Binary files /dev/null and b/app/assets/images/emoji/unicode/1f556.png differ diff --git a/app/assets/images/emoji/unicode/1f557.png b/app/assets/images/emoji/unicode/1f557.png new file mode 100644 index 000000000..6690cd74e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f557.png differ diff --git a/app/assets/images/emoji/unicode/1f558.png b/app/assets/images/emoji/unicode/1f558.png new file mode 100644 index 000000000..c4ad74609 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f558.png differ diff --git a/app/assets/images/emoji/unicode/1f559.png b/app/assets/images/emoji/unicode/1f559.png new file mode 100644 index 000000000..f710bef5c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f559.png differ diff --git a/app/assets/images/emoji/unicode/1f55a.png b/app/assets/images/emoji/unicode/1f55a.png new file mode 100644 index 000000000..fbc165b99 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f55a.png differ diff --git a/app/assets/images/emoji/unicode/1f55b.png b/app/assets/images/emoji/unicode/1f55b.png new file mode 100644 index 000000000..c1ca82f39 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f55b.png differ diff --git a/app/assets/images/emoji/unicode/1f55c.png b/app/assets/images/emoji/unicode/1f55c.png new file mode 100644 index 000000000..df9392019 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f55c.png differ diff --git a/app/assets/images/emoji/unicode/1f55d.png b/app/assets/images/emoji/unicode/1f55d.png new file mode 100644 index 000000000..f12c6912a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f55d.png differ diff --git a/app/assets/images/emoji/unicode/1f55e.png b/app/assets/images/emoji/unicode/1f55e.png new file mode 100644 index 000000000..1dc9628ea Binary files /dev/null and b/app/assets/images/emoji/unicode/1f55e.png differ diff --git a/app/assets/images/emoji/unicode/1f55f.png b/app/assets/images/emoji/unicode/1f55f.png new file mode 100644 index 000000000..7726aaea1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f55f.png differ diff --git a/app/assets/images/emoji/unicode/1f560.png b/app/assets/images/emoji/unicode/1f560.png new file mode 100644 index 000000000..e08d4ad2b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f560.png differ diff --git a/app/assets/images/emoji/unicode/1f561.png b/app/assets/images/emoji/unicode/1f561.png new file mode 100644 index 000000000..46f0681f1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f561.png differ diff --git a/app/assets/images/emoji/unicode/1f562.png b/app/assets/images/emoji/unicode/1f562.png new file mode 100644 index 000000000..18aab22fd Binary files /dev/null and b/app/assets/images/emoji/unicode/1f562.png differ diff --git a/app/assets/images/emoji/unicode/1f563.png b/app/assets/images/emoji/unicode/1f563.png new file mode 100644 index 000000000..ec3e382dd Binary files /dev/null and b/app/assets/images/emoji/unicode/1f563.png differ diff --git a/app/assets/images/emoji/unicode/1f564.png b/app/assets/images/emoji/unicode/1f564.png new file mode 100644 index 000000000..fd3522142 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f564.png differ diff --git a/app/assets/images/emoji/unicode/1f565.png b/app/assets/images/emoji/unicode/1f565.png new file mode 100644 index 000000000..84a3bc8fb Binary files /dev/null and b/app/assets/images/emoji/unicode/1f565.png differ diff --git a/app/assets/images/emoji/unicode/1f566.png b/app/assets/images/emoji/unicode/1f566.png new file mode 100644 index 000000000..415999ec8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f566.png differ diff --git a/app/assets/images/emoji/unicode/1f567.png b/app/assets/images/emoji/unicode/1f567.png new file mode 100644 index 000000000..a6527154d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f567.png differ diff --git a/app/assets/images/emoji/unicode/1f5fb.png b/app/assets/images/emoji/unicode/1f5fb.png new file mode 100644 index 000000000..4c313e583 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f5fb.png differ diff --git a/app/assets/images/emoji/unicode/1f5fc.png b/app/assets/images/emoji/unicode/1f5fc.png new file mode 100644 index 000000000..e1cbd7a3c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f5fc.png differ diff --git a/app/assets/images/emoji/unicode/1f5fd.png b/app/assets/images/emoji/unicode/1f5fd.png new file mode 100644 index 000000000..9ad902806 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f5fd.png differ diff --git a/app/assets/images/emoji/unicode/1f5fe.png b/app/assets/images/emoji/unicode/1f5fe.png new file mode 100644 index 000000000..459328035 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f5fe.png differ diff --git a/app/assets/images/emoji/unicode/1f5ff.png b/app/assets/images/emoji/unicode/1f5ff.png new file mode 100644 index 000000000..61a1a9c21 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f5ff.png differ diff --git a/app/assets/images/emoji/unicode/1f600.png b/app/assets/images/emoji/unicode/1f600.png new file mode 100644 index 000000000..0ef00d79d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f600.png differ diff --git a/app/assets/images/emoji/unicode/1f601.png b/app/assets/images/emoji/unicode/1f601.png new file mode 100644 index 000000000..591cfcef8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f601.png differ diff --git a/app/assets/images/emoji/unicode/1f602.png b/app/assets/images/emoji/unicode/1f602.png new file mode 100644 index 000000000..47df693d4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f602.png differ diff --git a/app/assets/images/emoji/unicode/1f603.png b/app/assets/images/emoji/unicode/1f603.png new file mode 100644 index 000000000..77b581d68 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f603.png differ diff --git a/app/assets/images/emoji/unicode/1f604.png b/app/assets/images/emoji/unicode/1f604.png new file mode 100644 index 000000000..81a839689 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f604.png differ diff --git a/app/assets/images/emoji/unicode/1f605.png b/app/assets/images/emoji/unicode/1f605.png new file mode 100644 index 000000000..3903f717f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f605.png differ diff --git a/app/assets/images/emoji/unicode/1f606.png b/app/assets/images/emoji/unicode/1f606.png new file mode 100644 index 000000000..11c91eb22 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f606.png differ diff --git a/app/assets/images/emoji/unicode/1f607.png b/app/assets/images/emoji/unicode/1f607.png new file mode 100644 index 000000000..503b614f8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f607.png differ diff --git a/app/assets/images/emoji/unicode/1f608.png b/app/assets/images/emoji/unicode/1f608.png new file mode 100644 index 000000000..d90404930 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f608.png differ diff --git a/app/assets/images/emoji/unicode/1f609.png b/app/assets/images/emoji/unicode/1f609.png new file mode 100644 index 000000000..756766dd3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f609.png differ diff --git a/app/assets/images/emoji/unicode/1f60a.png b/app/assets/images/emoji/unicode/1f60a.png new file mode 100644 index 000000000..1e9021cb6 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f60a.png differ diff --git a/app/assets/images/emoji/unicode/1f60b.png b/app/assets/images/emoji/unicode/1f60b.png new file mode 100644 index 000000000..fc39637ec Binary files /dev/null and b/app/assets/images/emoji/unicode/1f60b.png differ diff --git a/app/assets/images/emoji/unicode/1f60c.png b/app/assets/images/emoji/unicode/1f60c.png new file mode 100644 index 000000000..820cf315a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f60c.png differ diff --git a/app/assets/images/emoji/unicode/1f60d.png b/app/assets/images/emoji/unicode/1f60d.png new file mode 100644 index 000000000..0e5794270 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f60d.png differ diff --git a/app/assets/images/emoji/unicode/1f60e.png b/app/assets/images/emoji/unicode/1f60e.png new file mode 100644 index 000000000..1c468a1c9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f60e.png differ diff --git a/app/assets/images/emoji/unicode/1f60f.png b/app/assets/images/emoji/unicode/1f60f.png new file mode 100644 index 000000000..bc6e5082c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f60f.png differ diff --git a/app/assets/images/emoji/unicode/1f610.png b/app/assets/images/emoji/unicode/1f610.png new file mode 100644 index 000000000..682a1ba06 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f610.png differ diff --git a/app/assets/images/emoji/unicode/1f611.png b/app/assets/images/emoji/unicode/1f611.png new file mode 100644 index 000000000..1798f24de Binary files /dev/null and b/app/assets/images/emoji/unicode/1f611.png differ diff --git a/app/assets/images/emoji/unicode/1f612.png b/app/assets/images/emoji/unicode/1f612.png new file mode 100644 index 000000000..3722e6f57 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f612.png differ diff --git a/app/assets/images/emoji/unicode/1f613.png b/app/assets/images/emoji/unicode/1f613.png new file mode 100644 index 000000000..e894b7699 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f613.png differ diff --git a/app/assets/images/emoji/unicode/1f614.png b/app/assets/images/emoji/unicode/1f614.png new file mode 100644 index 000000000..2f3bad945 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f614.png differ diff --git a/app/assets/images/emoji/unicode/1f615.png b/app/assets/images/emoji/unicode/1f615.png new file mode 100644 index 000000000..18ff760ac Binary files /dev/null and b/app/assets/images/emoji/unicode/1f615.png differ diff --git a/app/assets/images/emoji/unicode/1f616.png b/app/assets/images/emoji/unicode/1f616.png new file mode 100644 index 000000000..a5877a0a7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f616.png differ diff --git a/app/assets/images/emoji/unicode/1f617.png b/app/assets/images/emoji/unicode/1f617.png new file mode 100644 index 000000000..eb049c80b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f617.png differ diff --git a/app/assets/images/emoji/unicode/1f618.png b/app/assets/images/emoji/unicode/1f618.png new file mode 100644 index 000000000..af9a80b7f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f618.png differ diff --git a/app/assets/images/emoji/unicode/1f619.png b/app/assets/images/emoji/unicode/1f619.png new file mode 100644 index 000000000..d85706e70 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f619.png differ diff --git a/app/assets/images/emoji/unicode/1f61a.png b/app/assets/images/emoji/unicode/1f61a.png new file mode 100644 index 000000000..449de1970 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f61a.png differ diff --git a/app/assets/images/emoji/unicode/1f61b.png b/app/assets/images/emoji/unicode/1f61b.png new file mode 100644 index 000000000..53c41433f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f61b.png differ diff --git a/app/assets/images/emoji/unicode/1f61c.png b/app/assets/images/emoji/unicode/1f61c.png new file mode 100644 index 000000000..6ae9d497d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f61c.png differ diff --git a/app/assets/images/emoji/unicode/1f61d.png b/app/assets/images/emoji/unicode/1f61d.png new file mode 100644 index 000000000..333716ee1 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f61d.png differ diff --git a/app/assets/images/emoji/unicode/1f61e.png b/app/assets/images/emoji/unicode/1f61e.png new file mode 100644 index 000000000..825520087 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f61e.png differ diff --git a/app/assets/images/emoji/unicode/1f61f.png b/app/assets/images/emoji/unicode/1f61f.png new file mode 100644 index 000000000..afd9283fc Binary files /dev/null and b/app/assets/images/emoji/unicode/1f61f.png differ diff --git a/app/assets/images/emoji/unicode/1f620.png b/app/assets/images/emoji/unicode/1f620.png new file mode 100644 index 000000000..34174f5e5 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f620.png differ diff --git a/app/assets/images/emoji/unicode/1f621.png b/app/assets/images/emoji/unicode/1f621.png new file mode 100644 index 000000000..c65ddff55 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f621.png differ diff --git a/app/assets/images/emoji/unicode/1f622.png b/app/assets/images/emoji/unicode/1f622.png new file mode 100644 index 000000000..6d0d9afd2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f622.png differ diff --git a/app/assets/images/emoji/unicode/1f623.png b/app/assets/images/emoji/unicode/1f623.png new file mode 100644 index 000000000..c7e433e8e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f623.png differ diff --git a/app/assets/images/emoji/unicode/1f624.png b/app/assets/images/emoji/unicode/1f624.png new file mode 100644 index 000000000..92f93bd10 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f624.png differ diff --git a/app/assets/images/emoji/unicode/1f625.png b/app/assets/images/emoji/unicode/1f625.png new file mode 100644 index 000000000..fa5f9e7f9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f625.png differ diff --git a/app/assets/images/emoji/unicode/1f626.png b/app/assets/images/emoji/unicode/1f626.png new file mode 100644 index 000000000..7f8b6c77b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f626.png differ diff --git a/app/assets/images/emoji/unicode/1f627.png b/app/assets/images/emoji/unicode/1f627.png new file mode 100644 index 000000000..c2edad796 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f627.png differ diff --git a/app/assets/images/emoji/unicode/1f628.png b/app/assets/images/emoji/unicode/1f628.png new file mode 100644 index 000000000..513fce47b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f628.png differ diff --git a/app/assets/images/emoji/unicode/1f629.png b/app/assets/images/emoji/unicode/1f629.png new file mode 100644 index 000000000..0c5475411 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f629.png differ diff --git a/app/assets/images/emoji/unicode/1f62a.png b/app/assets/images/emoji/unicode/1f62a.png new file mode 100644 index 000000000..df4f55efd Binary files /dev/null and b/app/assets/images/emoji/unicode/1f62a.png differ diff --git a/app/assets/images/emoji/unicode/1f62b.png b/app/assets/images/emoji/unicode/1f62b.png new file mode 100644 index 000000000..3a8eefe56 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f62b.png differ diff --git a/app/assets/images/emoji/unicode/1f62c.png b/app/assets/images/emoji/unicode/1f62c.png new file mode 100644 index 000000000..f78e9407d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f62c.png differ diff --git a/app/assets/images/emoji/unicode/1f62d.png b/app/assets/images/emoji/unicode/1f62d.png new file mode 100644 index 000000000..7d433183a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f62d.png differ diff --git a/app/assets/images/emoji/unicode/1f62e.png b/app/assets/images/emoji/unicode/1f62e.png new file mode 100644 index 000000000..e5283582c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f62e.png differ diff --git a/app/assets/images/emoji/unicode/1f62f.png b/app/assets/images/emoji/unicode/1f62f.png new file mode 100644 index 000000000..afa3f6686 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f62f.png differ diff --git a/app/assets/images/emoji/unicode/1f630.png b/app/assets/images/emoji/unicode/1f630.png new file mode 100644 index 000000000..b9e39bc60 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f630.png differ diff --git a/app/assets/images/emoji/unicode/1f631.png b/app/assets/images/emoji/unicode/1f631.png new file mode 100644 index 000000000..76bfc6b8a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f631.png differ diff --git a/app/assets/images/emoji/unicode/1f632.png b/app/assets/images/emoji/unicode/1f632.png new file mode 100644 index 000000000..858a83484 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f632.png differ diff --git a/app/assets/images/emoji/unicode/1f633.png b/app/assets/images/emoji/unicode/1f633.png new file mode 100644 index 000000000..9b49410c0 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f633.png differ diff --git a/app/assets/images/emoji/unicode/1f634.png b/app/assets/images/emoji/unicode/1f634.png new file mode 100644 index 000000000..a2f3bf757 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f634.png differ diff --git a/app/assets/images/emoji/unicode/1f635.png b/app/assets/images/emoji/unicode/1f635.png new file mode 100644 index 000000000..8001d6ff8 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f635.png differ diff --git a/app/assets/images/emoji/unicode/1f636.png b/app/assets/images/emoji/unicode/1f636.png new file mode 100644 index 000000000..d9ec7ca7d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f636.png differ diff --git a/app/assets/images/emoji/unicode/1f637.png b/app/assets/images/emoji/unicode/1f637.png new file mode 100644 index 000000000..05887e99c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f637.png differ diff --git a/app/assets/images/emoji/unicode/1f638.png b/app/assets/images/emoji/unicode/1f638.png new file mode 100644 index 000000000..ad333ba3b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f638.png differ diff --git a/app/assets/images/emoji/unicode/1f639.png b/app/assets/images/emoji/unicode/1f639.png new file mode 100644 index 000000000..6c60cb0ef Binary files /dev/null and b/app/assets/images/emoji/unicode/1f639.png differ diff --git a/app/assets/images/emoji/unicode/1f63a.png b/app/assets/images/emoji/unicode/1f63a.png new file mode 100644 index 000000000..dbf1b0276 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f63a.png differ diff --git a/app/assets/images/emoji/unicode/1f63b.png b/app/assets/images/emoji/unicode/1f63b.png new file mode 100644 index 000000000..eeba240e5 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f63b.png differ diff --git a/app/assets/images/emoji/unicode/1f63c.png b/app/assets/images/emoji/unicode/1f63c.png new file mode 100644 index 000000000..351565e24 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f63c.png differ diff --git a/app/assets/images/emoji/unicode/1f63d.png b/app/assets/images/emoji/unicode/1f63d.png new file mode 100644 index 000000000..adc62fbe3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f63d.png differ diff --git a/app/assets/images/emoji/unicode/1f63e.png b/app/assets/images/emoji/unicode/1f63e.png new file mode 100644 index 000000000..4325fd48d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f63e.png differ diff --git a/app/assets/images/emoji/unicode/1f63f.png b/app/assets/images/emoji/unicode/1f63f.png new file mode 100644 index 000000000..42d4c27ca Binary files /dev/null and b/app/assets/images/emoji/unicode/1f63f.png differ diff --git a/app/assets/images/emoji/unicode/1f640.png b/app/assets/images/emoji/unicode/1f640.png new file mode 100644 index 000000000..d94cd34ff Binary files /dev/null and b/app/assets/images/emoji/unicode/1f640.png differ diff --git a/app/assets/images/emoji/unicode/1f645.png b/app/assets/images/emoji/unicode/1f645.png new file mode 100644 index 000000000..d459a35bc Binary files /dev/null and b/app/assets/images/emoji/unicode/1f645.png differ diff --git a/app/assets/images/emoji/unicode/1f646.png b/app/assets/images/emoji/unicode/1f646.png new file mode 100644 index 000000000..e8b98194e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f646.png differ diff --git a/app/assets/images/emoji/unicode/1f647.png b/app/assets/images/emoji/unicode/1f647.png new file mode 100644 index 000000000..024cb6104 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f647.png differ diff --git a/app/assets/images/emoji/unicode/1f648.png b/app/assets/images/emoji/unicode/1f648.png new file mode 100644 index 000000000..0890a6222 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f648.png differ diff --git a/app/assets/images/emoji/unicode/1f649.png b/app/assets/images/emoji/unicode/1f649.png new file mode 100644 index 000000000..f97a1f9a0 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f649.png differ diff --git a/app/assets/images/emoji/unicode/1f64a.png b/app/assets/images/emoji/unicode/1f64a.png new file mode 100644 index 000000000..87944c4de Binary files /dev/null and b/app/assets/images/emoji/unicode/1f64a.png differ diff --git a/app/assets/images/emoji/unicode/1f64b.png b/app/assets/images/emoji/unicode/1f64b.png new file mode 100644 index 000000000..e1741a40e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f64b.png differ diff --git a/app/assets/images/emoji/unicode/1f64c.png b/app/assets/images/emoji/unicode/1f64c.png new file mode 100644 index 000000000..e03142bdc Binary files /dev/null and b/app/assets/images/emoji/unicode/1f64c.png differ diff --git a/app/assets/images/emoji/unicode/1f64d.png b/app/assets/images/emoji/unicode/1f64d.png new file mode 100644 index 000000000..6f34d5e15 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f64d.png differ diff --git a/app/assets/images/emoji/unicode/1f64e.png b/app/assets/images/emoji/unicode/1f64e.png new file mode 100644 index 000000000..c4a95c3b2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f64e.png differ diff --git a/app/assets/images/emoji/unicode/1f64f.png b/app/assets/images/emoji/unicode/1f64f.png new file mode 100644 index 000000000..f86c992d5 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f64f.png differ diff --git a/app/assets/images/emoji/unicode/1f680.png b/app/assets/images/emoji/unicode/1f680.png new file mode 100644 index 000000000..783078d37 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f680.png differ diff --git a/app/assets/images/emoji/unicode/1f681.png b/app/assets/images/emoji/unicode/1f681.png new file mode 100644 index 000000000..8e82a0d58 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f681.png differ diff --git a/app/assets/images/emoji/unicode/1f682.png b/app/assets/images/emoji/unicode/1f682.png new file mode 100644 index 000000000..549507766 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f682.png differ diff --git a/app/assets/images/emoji/unicode/1f683.png b/app/assets/images/emoji/unicode/1f683.png new file mode 100644 index 000000000..22361158f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f683.png differ diff --git a/app/assets/images/emoji/unicode/1f684.png b/app/assets/images/emoji/unicode/1f684.png new file mode 100644 index 000000000..8eca36845 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f684.png differ diff --git a/app/assets/images/emoji/unicode/1f685.png b/app/assets/images/emoji/unicode/1f685.png new file mode 100644 index 000000000..16651acff Binary files /dev/null and b/app/assets/images/emoji/unicode/1f685.png differ diff --git a/app/assets/images/emoji/unicode/1f686.png b/app/assets/images/emoji/unicode/1f686.png new file mode 100644 index 000000000..9c0d3ab64 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f686.png differ diff --git a/app/assets/images/emoji/unicode/1f687.png b/app/assets/images/emoji/unicode/1f687.png new file mode 100644 index 000000000..7f34f6be3 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f687.png differ diff --git a/app/assets/images/emoji/unicode/1f688.png b/app/assets/images/emoji/unicode/1f688.png new file mode 100644 index 000000000..bcfe801ee Binary files /dev/null and b/app/assets/images/emoji/unicode/1f688.png differ diff --git a/app/assets/images/emoji/unicode/1f689.png b/app/assets/images/emoji/unicode/1f689.png new file mode 100644 index 000000000..e77daa8a7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f689.png differ diff --git a/app/assets/images/emoji/unicode/1f68a.png b/app/assets/images/emoji/unicode/1f68a.png new file mode 100644 index 000000000..5eb29fb71 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f68a.png differ diff --git a/app/assets/images/emoji/unicode/1f68c.png b/app/assets/images/emoji/unicode/1f68c.png new file mode 100644 index 000000000..823aa39e4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f68c.png differ diff --git a/app/assets/images/emoji/unicode/1f68d.png b/app/assets/images/emoji/unicode/1f68d.png new file mode 100644 index 000000000..3695f7623 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f68d.png differ diff --git a/app/assets/images/emoji/unicode/1f68e.png b/app/assets/images/emoji/unicode/1f68e.png new file mode 100644 index 000000000..b9740a53f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f68e.png differ diff --git a/app/assets/images/emoji/unicode/1f68f.png b/app/assets/images/emoji/unicode/1f68f.png new file mode 100644 index 000000000..99af2322a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f68f.png differ diff --git a/app/assets/images/emoji/unicode/1f690.png b/app/assets/images/emoji/unicode/1f690.png new file mode 100644 index 000000000..c52cef234 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f690.png differ diff --git a/app/assets/images/emoji/unicode/1f691.png b/app/assets/images/emoji/unicode/1f691.png new file mode 100644 index 000000000..b740f45db Binary files /dev/null and b/app/assets/images/emoji/unicode/1f691.png differ diff --git a/app/assets/images/emoji/unicode/1f692.png b/app/assets/images/emoji/unicode/1f692.png new file mode 100644 index 000000000..9e6c59c99 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f692.png differ diff --git a/app/assets/images/emoji/unicode/1f693.png b/app/assets/images/emoji/unicode/1f693.png new file mode 100644 index 000000000..b8f17275e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f693.png differ diff --git a/app/assets/images/emoji/unicode/1f694.png b/app/assets/images/emoji/unicode/1f694.png new file mode 100644 index 000000000..af20e7eff Binary files /dev/null and b/app/assets/images/emoji/unicode/1f694.png differ diff --git a/app/assets/images/emoji/unicode/1f695.png b/app/assets/images/emoji/unicode/1f695.png new file mode 100644 index 000000000..60a50d365 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f695.png differ diff --git a/app/assets/images/emoji/unicode/1f696.png b/app/assets/images/emoji/unicode/1f696.png new file mode 100644 index 000000000..f78cf3103 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f696.png differ diff --git a/app/assets/images/emoji/unicode/1f697.png b/app/assets/images/emoji/unicode/1f697.png new file mode 100644 index 000000000..d70a2f062 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f697.png differ diff --git a/app/assets/images/emoji/unicode/1f698.png b/app/assets/images/emoji/unicode/1f698.png new file mode 100644 index 000000000..cb46de22c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f698.png differ diff --git a/app/assets/images/emoji/unicode/1f699.png b/app/assets/images/emoji/unicode/1f699.png new file mode 100644 index 000000000..978291e08 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f699.png differ diff --git a/app/assets/images/emoji/unicode/1f69a.png b/app/assets/images/emoji/unicode/1f69a.png new file mode 100644 index 000000000..3f25ba1f9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f69a.png differ diff --git a/app/assets/images/emoji/unicode/1f69b.png b/app/assets/images/emoji/unicode/1f69b.png new file mode 100644 index 000000000..81ec1f917 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f69b.png differ diff --git a/app/assets/images/emoji/unicode/1f69c.png b/app/assets/images/emoji/unicode/1f69c.png new file mode 100644 index 000000000..058fd3eda Binary files /dev/null and b/app/assets/images/emoji/unicode/1f69c.png differ diff --git a/app/assets/images/emoji/unicode/1f69d.png b/app/assets/images/emoji/unicode/1f69d.png new file mode 100644 index 000000000..913d30024 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f69d.png differ diff --git a/app/assets/images/emoji/unicode/1f69e.png b/app/assets/images/emoji/unicode/1f69e.png new file mode 100644 index 000000000..1f3d1aab5 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f69e.png differ diff --git a/app/assets/images/emoji/unicode/1f69f.png b/app/assets/images/emoji/unicode/1f69f.png new file mode 100644 index 000000000..aaa45f61f Binary files /dev/null and b/app/assets/images/emoji/unicode/1f69f.png differ diff --git a/app/assets/images/emoji/unicode/1f6a0.png b/app/assets/images/emoji/unicode/1f6a0.png new file mode 100644 index 000000000..5688bb239 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6a0.png differ diff --git a/app/assets/images/emoji/unicode/1f6a1.png b/app/assets/images/emoji/unicode/1f6a1.png new file mode 100644 index 000000000..38f6dfe23 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6a1.png differ diff --git a/app/assets/images/emoji/unicode/1f6a2.png b/app/assets/images/emoji/unicode/1f6a2.png new file mode 100644 index 000000000..5d2d8b602 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6a2.png differ diff --git a/app/assets/images/emoji/unicode/1f6a3.png b/app/assets/images/emoji/unicode/1f6a3.png new file mode 100644 index 000000000..fe8ae3ecd Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6a3.png differ diff --git a/app/assets/images/emoji/unicode/1f6a4.png b/app/assets/images/emoji/unicode/1f6a4.png new file mode 100644 index 000000000..da6689b3b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6a4.png differ diff --git a/app/assets/images/emoji/unicode/1f6a5.png b/app/assets/images/emoji/unicode/1f6a5.png new file mode 100644 index 000000000..42eaf7091 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6a5.png differ diff --git a/app/assets/images/emoji/unicode/1f6a6.png b/app/assets/images/emoji/unicode/1f6a6.png new file mode 100644 index 000000000..7a5ba35f0 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6a6.png differ diff --git a/app/assets/images/emoji/unicode/1f6a7.png b/app/assets/images/emoji/unicode/1f6a7.png new file mode 100644 index 000000000..523e9f10b Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6a7.png differ diff --git a/app/assets/images/emoji/unicode/1f6a8.png b/app/assets/images/emoji/unicode/1f6a8.png new file mode 100644 index 000000000..6cf4a775e Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6a8.png differ diff --git a/app/assets/images/emoji/unicode/1f6a9.png b/app/assets/images/emoji/unicode/1f6a9.png new file mode 100644 index 000000000..f9a3f32d7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6a9.png differ diff --git a/app/assets/images/emoji/unicode/1f6aa.png b/app/assets/images/emoji/unicode/1f6aa.png new file mode 100644 index 000000000..83c819ae4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6aa.png differ diff --git a/app/assets/images/emoji/unicode/1f6ab.png b/app/assets/images/emoji/unicode/1f6ab.png new file mode 100644 index 000000000..a8444d18d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6ab.png differ diff --git a/app/assets/images/emoji/unicode/1f6ac.png b/app/assets/images/emoji/unicode/1f6ac.png new file mode 100644 index 000000000..4aad6cbd7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6ac.png differ diff --git a/app/assets/images/emoji/unicode/1f6ad.png b/app/assets/images/emoji/unicode/1f6ad.png new file mode 100644 index 000000000..eb11d7911 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6ad.png differ diff --git a/app/assets/images/emoji/unicode/1f6ae.png b/app/assets/images/emoji/unicode/1f6ae.png new file mode 100644 index 000000000..c2e350c2d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6ae.png differ diff --git a/app/assets/images/emoji/unicode/1f6af.png b/app/assets/images/emoji/unicode/1f6af.png new file mode 100644 index 000000000..38c7ae7af Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6af.png differ diff --git a/app/assets/images/emoji/unicode/1f6b0.png b/app/assets/images/emoji/unicode/1f6b0.png new file mode 100644 index 000000000..e9fd56079 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6b0.png differ diff --git a/app/assets/images/emoji/unicode/1f6b1.png b/app/assets/images/emoji/unicode/1f6b1.png new file mode 100644 index 000000000..1b29d35b9 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6b1.png differ diff --git a/app/assets/images/emoji/unicode/1f6b2.png b/app/assets/images/emoji/unicode/1f6b2.png new file mode 100644 index 000000000..657386027 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6b2.png differ diff --git a/app/assets/images/emoji/unicode/1f6b3.png b/app/assets/images/emoji/unicode/1f6b3.png new file mode 100644 index 000000000..4b2621664 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6b3.png differ diff --git a/app/assets/images/emoji/unicode/1f6b4.png b/app/assets/images/emoji/unicode/1f6b4.png new file mode 100644 index 000000000..4e3e0549c Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6b4.png differ diff --git a/app/assets/images/emoji/unicode/1f6b5.png b/app/assets/images/emoji/unicode/1f6b5.png new file mode 100644 index 000000000..b69889756 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6b5.png differ diff --git a/app/assets/images/emoji/unicode/1f6b6.png b/app/assets/images/emoji/unicode/1f6b6.png new file mode 100644 index 000000000..7a2bfacfc Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6b6.png differ diff --git a/app/assets/images/emoji/unicode/1f6b7.png b/app/assets/images/emoji/unicode/1f6b7.png new file mode 100644 index 000000000..c35f530b2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6b7.png differ diff --git a/app/assets/images/emoji/unicode/1f6b8.png b/app/assets/images/emoji/unicode/1f6b8.png new file mode 100644 index 000000000..b0302ae62 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6b8.png differ diff --git a/app/assets/images/emoji/unicode/1f6b9.png b/app/assets/images/emoji/unicode/1f6b9.png new file mode 100644 index 000000000..abccfc9f2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6b9.png differ diff --git a/app/assets/images/emoji/unicode/1f6ba.png b/app/assets/images/emoji/unicode/1f6ba.png new file mode 100644 index 000000000..518b76a6d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6ba.png differ diff --git a/app/assets/images/emoji/unicode/1f6bb.png b/app/assets/images/emoji/unicode/1f6bb.png new file mode 100644 index 000000000..312ca3dc2 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6bb.png differ diff --git a/app/assets/images/emoji/unicode/1f6bc.png b/app/assets/images/emoji/unicode/1f6bc.png new file mode 100644 index 000000000..2e58725cf Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6bc.png differ diff --git a/app/assets/images/emoji/unicode/1f6bd.png b/app/assets/images/emoji/unicode/1f6bd.png new file mode 100644 index 000000000..e5cc4119a Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6bd.png differ diff --git a/app/assets/images/emoji/unicode/1f6be.png b/app/assets/images/emoji/unicode/1f6be.png new file mode 100644 index 000000000..dfe84d2a7 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6be.png differ diff --git a/app/assets/images/emoji/unicode/1f6bf.png b/app/assets/images/emoji/unicode/1f6bf.png new file mode 100644 index 000000000..94f82aac0 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6bf.png differ diff --git a/app/assets/images/emoji/unicode/1f6c0.png b/app/assets/images/emoji/unicode/1f6c0.png new file mode 100644 index 000000000..8f75d1d24 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6c0.png differ diff --git a/app/assets/images/emoji/unicode/1f6c1.png b/app/assets/images/emoji/unicode/1f6c1.png new file mode 100644 index 000000000..1c3f844ab Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6c1.png differ diff --git a/app/assets/images/emoji/unicode/1f6c2.png b/app/assets/images/emoji/unicode/1f6c2.png new file mode 100644 index 000000000..675b76d37 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6c2.png differ diff --git a/app/assets/images/emoji/unicode/1f6c3.png b/app/assets/images/emoji/unicode/1f6c3.png new file mode 100644 index 000000000..92691e311 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6c3.png differ diff --git a/app/assets/images/emoji/unicode/1f6c4.png b/app/assets/images/emoji/unicode/1f6c4.png new file mode 100644 index 000000000..59ae044a4 Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6c4.png differ diff --git a/app/assets/images/emoji/unicode/1f6c5.png b/app/assets/images/emoji/unicode/1f6c5.png new file mode 100644 index 000000000..1c08b464d Binary files /dev/null and b/app/assets/images/emoji/unicode/1f6c5.png differ diff --git a/app/assets/images/emoji/unicode/203c.png b/app/assets/images/emoji/unicode/203c.png new file mode 100644 index 000000000..7270f0afe Binary files /dev/null and b/app/assets/images/emoji/unicode/203c.png differ diff --git a/app/assets/images/emoji/unicode/2049.png b/app/assets/images/emoji/unicode/2049.png new file mode 100644 index 000000000..64304b9f5 Binary files /dev/null and b/app/assets/images/emoji/unicode/2049.png differ diff --git a/app/assets/images/emoji/unicode/2122.png b/app/assets/images/emoji/unicode/2122.png new file mode 100644 index 000000000..9ba71b75b Binary files /dev/null and b/app/assets/images/emoji/unicode/2122.png differ diff --git a/app/assets/images/emoji/unicode/2139.png b/app/assets/images/emoji/unicode/2139.png new file mode 100644 index 000000000..9cb8b09b2 Binary files /dev/null and b/app/assets/images/emoji/unicode/2139.png differ diff --git a/app/assets/images/emoji/unicode/2194.png b/app/assets/images/emoji/unicode/2194.png new file mode 100644 index 000000000..b9fd11c51 Binary files /dev/null and b/app/assets/images/emoji/unicode/2194.png differ diff --git a/app/assets/images/emoji/unicode/2195.png b/app/assets/images/emoji/unicode/2195.png new file mode 100644 index 000000000..b718c2145 Binary files /dev/null and b/app/assets/images/emoji/unicode/2195.png differ diff --git a/app/assets/images/emoji/unicode/2196.png b/app/assets/images/emoji/unicode/2196.png new file mode 100644 index 000000000..12aebd9a7 Binary files /dev/null and b/app/assets/images/emoji/unicode/2196.png differ diff --git a/app/assets/images/emoji/unicode/2197.png b/app/assets/images/emoji/unicode/2197.png new file mode 100644 index 000000000..0daf4e940 Binary files /dev/null and b/app/assets/images/emoji/unicode/2197.png differ diff --git a/app/assets/images/emoji/unicode/2198.png b/app/assets/images/emoji/unicode/2198.png new file mode 100644 index 000000000..2a15cc7cc Binary files /dev/null and b/app/assets/images/emoji/unicode/2198.png differ diff --git a/app/assets/images/emoji/unicode/2199.png b/app/assets/images/emoji/unicode/2199.png new file mode 100644 index 000000000..a4438cb6e Binary files /dev/null and b/app/assets/images/emoji/unicode/2199.png differ diff --git a/app/assets/images/emoji/unicode/21a9.png b/app/assets/images/emoji/unicode/21a9.png new file mode 100644 index 000000000..bc45dfefd Binary files /dev/null and b/app/assets/images/emoji/unicode/21a9.png differ diff --git a/app/assets/images/emoji/unicode/21aa.png b/app/assets/images/emoji/unicode/21aa.png new file mode 100644 index 000000000..8b4ea6e17 Binary files /dev/null and b/app/assets/images/emoji/unicode/21aa.png differ diff --git a/app/assets/images/emoji/unicode/231a.png b/app/assets/images/emoji/unicode/231a.png new file mode 100644 index 000000000..d503bb87c Binary files /dev/null and b/app/assets/images/emoji/unicode/231a.png differ diff --git a/app/assets/images/emoji/unicode/231b.png b/app/assets/images/emoji/unicode/231b.png new file mode 100644 index 000000000..405aab41b Binary files /dev/null and b/app/assets/images/emoji/unicode/231b.png differ diff --git a/app/assets/images/emoji/unicode/23e9.png b/app/assets/images/emoji/unicode/23e9.png new file mode 100644 index 000000000..b94a11726 Binary files /dev/null and b/app/assets/images/emoji/unicode/23e9.png differ diff --git a/app/assets/images/emoji/unicode/23ea.png b/app/assets/images/emoji/unicode/23ea.png new file mode 100644 index 000000000..13ba866ad Binary files /dev/null and b/app/assets/images/emoji/unicode/23ea.png differ diff --git a/app/assets/images/emoji/unicode/23eb.png b/app/assets/images/emoji/unicode/23eb.png new file mode 100644 index 000000000..d42979d4b Binary files /dev/null and b/app/assets/images/emoji/unicode/23eb.png differ diff --git a/app/assets/images/emoji/unicode/23ec.png b/app/assets/images/emoji/unicode/23ec.png new file mode 100644 index 000000000..2ecbebcda Binary files /dev/null and b/app/assets/images/emoji/unicode/23ec.png differ diff --git a/app/assets/images/emoji/unicode/23f0.png b/app/assets/images/emoji/unicode/23f0.png new file mode 100644 index 000000000..86ca8c8ed Binary files /dev/null and b/app/assets/images/emoji/unicode/23f0.png differ diff --git a/app/assets/images/emoji/unicode/23f3.png b/app/assets/images/emoji/unicode/23f3.png new file mode 100644 index 000000000..52c9eb704 Binary files /dev/null and b/app/assets/images/emoji/unicode/23f3.png differ diff --git a/app/assets/images/emoji/unicode/24c2.png b/app/assets/images/emoji/unicode/24c2.png new file mode 100644 index 000000000..7424665e2 Binary files /dev/null and b/app/assets/images/emoji/unicode/24c2.png differ diff --git a/app/assets/images/emoji/unicode/25aa.png b/app/assets/images/emoji/unicode/25aa.png new file mode 100644 index 000000000..a247751ec Binary files /dev/null and b/app/assets/images/emoji/unicode/25aa.png differ diff --git a/app/assets/images/emoji/unicode/25ab.png b/app/assets/images/emoji/unicode/25ab.png new file mode 100644 index 000000000..24ba879f4 Binary files /dev/null and b/app/assets/images/emoji/unicode/25ab.png differ diff --git a/app/assets/images/emoji/unicode/25b6.png b/app/assets/images/emoji/unicode/25b6.png new file mode 100644 index 000000000..fbfe711b6 Binary files /dev/null and b/app/assets/images/emoji/unicode/25b6.png differ diff --git a/app/assets/images/emoji/unicode/25c0.png b/app/assets/images/emoji/unicode/25c0.png new file mode 100644 index 000000000..2be422ba3 Binary files /dev/null and b/app/assets/images/emoji/unicode/25c0.png differ diff --git a/app/assets/images/emoji/unicode/25fb.png b/app/assets/images/emoji/unicode/25fb.png new file mode 100644 index 000000000..199808bcf Binary files /dev/null and b/app/assets/images/emoji/unicode/25fb.png differ diff --git a/app/assets/images/emoji/unicode/25fc.png b/app/assets/images/emoji/unicode/25fc.png new file mode 100644 index 000000000..204cce12c Binary files /dev/null and b/app/assets/images/emoji/unicode/25fc.png differ diff --git a/app/assets/images/emoji/unicode/25fd.png b/app/assets/images/emoji/unicode/25fd.png new file mode 100644 index 000000000..a115cdc49 Binary files /dev/null and b/app/assets/images/emoji/unicode/25fd.png differ diff --git a/app/assets/images/emoji/unicode/25fe.png b/app/assets/images/emoji/unicode/25fe.png new file mode 100644 index 000000000..25bfe9c45 Binary files /dev/null and b/app/assets/images/emoji/unicode/25fe.png differ diff --git a/app/assets/images/emoji/unicode/2600.png b/app/assets/images/emoji/unicode/2600.png new file mode 100644 index 000000000..d23c095e0 Binary files /dev/null and b/app/assets/images/emoji/unicode/2600.png differ diff --git a/app/assets/images/emoji/unicode/2601.png b/app/assets/images/emoji/unicode/2601.png new file mode 100644 index 000000000..b31c08c0b Binary files /dev/null and b/app/assets/images/emoji/unicode/2601.png differ diff --git a/app/assets/images/emoji/unicode/260e.png b/app/assets/images/emoji/unicode/260e.png new file mode 100644 index 000000000..87d2559b5 Binary files /dev/null and b/app/assets/images/emoji/unicode/260e.png differ diff --git a/app/assets/images/emoji/unicode/2611.png b/app/assets/images/emoji/unicode/2611.png new file mode 100644 index 000000000..f07a466c7 Binary files /dev/null and b/app/assets/images/emoji/unicode/2611.png differ diff --git a/app/assets/images/emoji/unicode/2614.png b/app/assets/images/emoji/unicode/2614.png new file mode 100644 index 000000000..1db722fa6 Binary files /dev/null and b/app/assets/images/emoji/unicode/2614.png differ diff --git a/app/assets/images/emoji/unicode/2615.png b/app/assets/images/emoji/unicode/2615.png new file mode 100644 index 000000000..57e1adcb0 Binary files /dev/null and b/app/assets/images/emoji/unicode/2615.png differ diff --git a/app/assets/images/emoji/unicode/261d.png b/app/assets/images/emoji/unicode/261d.png new file mode 100644 index 000000000..01896e214 Binary files /dev/null and b/app/assets/images/emoji/unicode/261d.png differ diff --git a/app/assets/images/emoji/unicode/263a.png b/app/assets/images/emoji/unicode/263a.png new file mode 100644 index 000000000..bbab82d3b Binary files /dev/null and b/app/assets/images/emoji/unicode/263a.png differ diff --git a/app/assets/images/emoji/unicode/2648.png b/app/assets/images/emoji/unicode/2648.png new file mode 100644 index 000000000..d676fd392 Binary files /dev/null and b/app/assets/images/emoji/unicode/2648.png differ diff --git a/app/assets/images/emoji/unicode/2649.png b/app/assets/images/emoji/unicode/2649.png new file mode 100644 index 000000000..6af582f69 Binary files /dev/null and b/app/assets/images/emoji/unicode/2649.png differ diff --git a/app/assets/images/emoji/unicode/264a.png b/app/assets/images/emoji/unicode/264a.png new file mode 100644 index 000000000..d926f6e88 Binary files /dev/null and b/app/assets/images/emoji/unicode/264a.png differ diff --git a/app/assets/images/emoji/unicode/264b.png b/app/assets/images/emoji/unicode/264b.png new file mode 100644 index 000000000..ea43a4a2a Binary files /dev/null and b/app/assets/images/emoji/unicode/264b.png differ diff --git a/app/assets/images/emoji/unicode/264c.png b/app/assets/images/emoji/unicode/264c.png new file mode 100644 index 000000000..e025933b2 Binary files /dev/null and b/app/assets/images/emoji/unicode/264c.png differ diff --git a/app/assets/images/emoji/unicode/264d.png b/app/assets/images/emoji/unicode/264d.png new file mode 100644 index 000000000..72e1763f5 Binary files /dev/null and b/app/assets/images/emoji/unicode/264d.png differ diff --git a/app/assets/images/emoji/unicode/264e.png b/app/assets/images/emoji/unicode/264e.png new file mode 100644 index 000000000..c9062dd2e Binary files /dev/null and b/app/assets/images/emoji/unicode/264e.png differ diff --git a/app/assets/images/emoji/unicode/264f.png b/app/assets/images/emoji/unicode/264f.png new file mode 100644 index 000000000..67fcea165 Binary files /dev/null and b/app/assets/images/emoji/unicode/264f.png differ diff --git a/app/assets/images/emoji/unicode/2650.png b/app/assets/images/emoji/unicode/2650.png new file mode 100644 index 000000000..8b5435baa Binary files /dev/null and b/app/assets/images/emoji/unicode/2650.png differ diff --git a/app/assets/images/emoji/unicode/2651.png b/app/assets/images/emoji/unicode/2651.png new file mode 100644 index 000000000..f2044e789 Binary files /dev/null and b/app/assets/images/emoji/unicode/2651.png differ diff --git a/app/assets/images/emoji/unicode/2652.png b/app/assets/images/emoji/unicode/2652.png new file mode 100644 index 000000000..cbff66edc Binary files /dev/null and b/app/assets/images/emoji/unicode/2652.png differ diff --git a/app/assets/images/emoji/unicode/2653.png b/app/assets/images/emoji/unicode/2653.png new file mode 100644 index 000000000..5a2da0a05 Binary files /dev/null and b/app/assets/images/emoji/unicode/2653.png differ diff --git a/app/assets/images/emoji/unicode/2660.png b/app/assets/images/emoji/unicode/2660.png new file mode 100644 index 000000000..133a1aba8 Binary files /dev/null and b/app/assets/images/emoji/unicode/2660.png differ diff --git a/app/assets/images/emoji/unicode/2663.png b/app/assets/images/emoji/unicode/2663.png new file mode 100644 index 000000000..bfab53656 Binary files /dev/null and b/app/assets/images/emoji/unicode/2663.png differ diff --git a/app/assets/images/emoji/unicode/2665.png b/app/assets/images/emoji/unicode/2665.png new file mode 100644 index 000000000..e89471538 Binary files /dev/null and b/app/assets/images/emoji/unicode/2665.png differ diff --git a/app/assets/images/emoji/unicode/2666.png b/app/assets/images/emoji/unicode/2666.png new file mode 100644 index 000000000..fe0827758 Binary files /dev/null and b/app/assets/images/emoji/unicode/2666.png differ diff --git a/app/assets/images/emoji/unicode/2668.png b/app/assets/images/emoji/unicode/2668.png new file mode 100644 index 000000000..a0bc9d75f Binary files /dev/null and b/app/assets/images/emoji/unicode/2668.png differ diff --git a/app/assets/images/emoji/unicode/267b.png b/app/assets/images/emoji/unicode/267b.png new file mode 100644 index 000000000..99104c0e9 Binary files /dev/null and b/app/assets/images/emoji/unicode/267b.png differ diff --git a/app/assets/images/emoji/unicode/267f.png b/app/assets/images/emoji/unicode/267f.png new file mode 100644 index 000000000..eddcdd797 Binary files /dev/null and b/app/assets/images/emoji/unicode/267f.png differ diff --git a/app/assets/images/emoji/unicode/2693.png b/app/assets/images/emoji/unicode/2693.png new file mode 100644 index 000000000..0c5192e64 Binary files /dev/null and b/app/assets/images/emoji/unicode/2693.png differ diff --git a/app/assets/images/emoji/unicode/26a0.png b/app/assets/images/emoji/unicode/26a0.png new file mode 100644 index 000000000..466658d99 Binary files /dev/null and b/app/assets/images/emoji/unicode/26a0.png differ diff --git a/app/assets/images/emoji/unicode/26a1.png b/app/assets/images/emoji/unicode/26a1.png new file mode 100644 index 000000000..260c531b9 Binary files /dev/null and b/app/assets/images/emoji/unicode/26a1.png differ diff --git a/app/assets/images/emoji/unicode/26aa.png b/app/assets/images/emoji/unicode/26aa.png new file mode 100644 index 000000000..da782ae29 Binary files /dev/null and b/app/assets/images/emoji/unicode/26aa.png differ diff --git a/app/assets/images/emoji/unicode/26ab.png b/app/assets/images/emoji/unicode/26ab.png new file mode 100644 index 000000000..e46f9df61 Binary files /dev/null and b/app/assets/images/emoji/unicode/26ab.png differ diff --git a/app/assets/images/emoji/unicode/26bd.png b/app/assets/images/emoji/unicode/26bd.png new file mode 100644 index 000000000..1e118b5b1 Binary files /dev/null and b/app/assets/images/emoji/unicode/26bd.png differ diff --git a/app/assets/images/emoji/unicode/26be.png b/app/assets/images/emoji/unicode/26be.png new file mode 100644 index 000000000..da004e2ea Binary files /dev/null and b/app/assets/images/emoji/unicode/26be.png differ diff --git a/app/assets/images/emoji/unicode/26c4.png b/app/assets/images/emoji/unicode/26c4.png new file mode 100644 index 000000000..a97902e53 Binary files /dev/null and b/app/assets/images/emoji/unicode/26c4.png differ diff --git a/app/assets/images/emoji/unicode/26c5.png b/app/assets/images/emoji/unicode/26c5.png new file mode 100644 index 000000000..020dd5ff6 Binary files /dev/null and b/app/assets/images/emoji/unicode/26c5.png differ diff --git a/app/assets/images/emoji/unicode/26ce.png b/app/assets/images/emoji/unicode/26ce.png new file mode 100644 index 000000000..4eef715bc Binary files /dev/null and b/app/assets/images/emoji/unicode/26ce.png differ diff --git a/app/assets/images/emoji/unicode/26d4.png b/app/assets/images/emoji/unicode/26d4.png new file mode 100644 index 000000000..cf2086a8e Binary files /dev/null and b/app/assets/images/emoji/unicode/26d4.png differ diff --git a/app/assets/images/emoji/unicode/26ea.png b/app/assets/images/emoji/unicode/26ea.png new file mode 100644 index 000000000..4c07c6b9e Binary files /dev/null and b/app/assets/images/emoji/unicode/26ea.png differ diff --git a/app/assets/images/emoji/unicode/26f2.png b/app/assets/images/emoji/unicode/26f2.png new file mode 100644 index 000000000..da126e648 Binary files /dev/null and b/app/assets/images/emoji/unicode/26f2.png differ diff --git a/app/assets/images/emoji/unicode/26f3.png b/app/assets/images/emoji/unicode/26f3.png new file mode 100644 index 000000000..cba2116a7 Binary files /dev/null and b/app/assets/images/emoji/unicode/26f3.png differ diff --git a/app/assets/images/emoji/unicode/26f5.png b/app/assets/images/emoji/unicode/26f5.png new file mode 100644 index 000000000..ff656dc62 Binary files /dev/null and b/app/assets/images/emoji/unicode/26f5.png differ diff --git a/app/assets/images/emoji/unicode/26fa.png b/app/assets/images/emoji/unicode/26fa.png new file mode 100644 index 000000000..5c0d20e48 Binary files /dev/null and b/app/assets/images/emoji/unicode/26fa.png differ diff --git a/app/assets/images/emoji/unicode/26fd.png b/app/assets/images/emoji/unicode/26fd.png new file mode 100644 index 000000000..54c29aeb1 Binary files /dev/null and b/app/assets/images/emoji/unicode/26fd.png differ diff --git a/app/assets/images/emoji/unicode/2702.png b/app/assets/images/emoji/unicode/2702.png new file mode 100644 index 000000000..020e05224 Binary files /dev/null and b/app/assets/images/emoji/unicode/2702.png differ diff --git a/app/assets/images/emoji/unicode/2705.png b/app/assets/images/emoji/unicode/2705.png new file mode 100644 index 000000000..61dc0583c Binary files /dev/null and b/app/assets/images/emoji/unicode/2705.png differ diff --git a/app/assets/images/emoji/unicode/2708.png b/app/assets/images/emoji/unicode/2708.png new file mode 100644 index 000000000..8407cb675 Binary files /dev/null and b/app/assets/images/emoji/unicode/2708.png differ diff --git a/app/assets/images/emoji/unicode/2709.png b/app/assets/images/emoji/unicode/2709.png new file mode 100644 index 000000000..3631861bb Binary files /dev/null and b/app/assets/images/emoji/unicode/2709.png differ diff --git a/app/assets/images/emoji/unicode/270a.png b/app/assets/images/emoji/unicode/270a.png new file mode 100644 index 000000000..ecc8874c2 Binary files /dev/null and b/app/assets/images/emoji/unicode/270a.png differ diff --git a/app/assets/images/emoji/unicode/270b.png b/app/assets/images/emoji/unicode/270b.png new file mode 100644 index 000000000..5e45c25a5 Binary files /dev/null and b/app/assets/images/emoji/unicode/270b.png differ diff --git a/app/assets/images/emoji/unicode/270c.png b/app/assets/images/emoji/unicode/270c.png new file mode 100644 index 000000000..f61267c28 Binary files /dev/null and b/app/assets/images/emoji/unicode/270c.png differ diff --git a/app/assets/images/emoji/unicode/270f.png b/app/assets/images/emoji/unicode/270f.png new file mode 100644 index 000000000..e624373b4 Binary files /dev/null and b/app/assets/images/emoji/unicode/270f.png differ diff --git a/app/assets/images/emoji/unicode/2712.png b/app/assets/images/emoji/unicode/2712.png new file mode 100644 index 000000000..29f6994c1 Binary files /dev/null and b/app/assets/images/emoji/unicode/2712.png differ diff --git a/app/assets/images/emoji/unicode/2714.png b/app/assets/images/emoji/unicode/2714.png new file mode 100644 index 000000000..336d2626d Binary files /dev/null and b/app/assets/images/emoji/unicode/2714.png differ diff --git a/app/assets/images/emoji/unicode/2716.png b/app/assets/images/emoji/unicode/2716.png new file mode 100644 index 000000000..13d666078 Binary files /dev/null and b/app/assets/images/emoji/unicode/2716.png differ diff --git a/app/assets/images/emoji/unicode/2728.png b/app/assets/images/emoji/unicode/2728.png new file mode 100644 index 000000000..92138828d Binary files /dev/null and b/app/assets/images/emoji/unicode/2728.png differ diff --git a/app/assets/images/emoji/unicode/2733.png b/app/assets/images/emoji/unicode/2733.png new file mode 100644 index 000000000..946a20333 Binary files /dev/null and b/app/assets/images/emoji/unicode/2733.png differ diff --git a/app/assets/images/emoji/unicode/2734.png b/app/assets/images/emoji/unicode/2734.png new file mode 100644 index 000000000..73dc6a0c9 Binary files /dev/null and b/app/assets/images/emoji/unicode/2734.png differ diff --git a/app/assets/images/emoji/unicode/2744.png b/app/assets/images/emoji/unicode/2744.png new file mode 100644 index 000000000..54b68ff4f Binary files /dev/null and b/app/assets/images/emoji/unicode/2744.png differ diff --git a/app/assets/images/emoji/unicode/2747.png b/app/assets/images/emoji/unicode/2747.png new file mode 100644 index 000000000..23a68ceb2 Binary files /dev/null and b/app/assets/images/emoji/unicode/2747.png differ diff --git a/app/assets/images/emoji/unicode/274c.png b/app/assets/images/emoji/unicode/274c.png new file mode 100644 index 000000000..b84f63557 Binary files /dev/null and b/app/assets/images/emoji/unicode/274c.png differ diff --git a/app/assets/images/emoji/unicode/274e.png b/app/assets/images/emoji/unicode/274e.png new file mode 100644 index 000000000..b47a0cece Binary files /dev/null and b/app/assets/images/emoji/unicode/274e.png differ diff --git a/app/assets/images/emoji/unicode/2753.png b/app/assets/images/emoji/unicode/2753.png new file mode 100644 index 000000000..63fd7f837 Binary files /dev/null and b/app/assets/images/emoji/unicode/2753.png differ diff --git a/app/assets/images/emoji/unicode/2754.png b/app/assets/images/emoji/unicode/2754.png new file mode 100644 index 000000000..57db41ead Binary files /dev/null and b/app/assets/images/emoji/unicode/2754.png differ diff --git a/app/assets/images/emoji/unicode/2755.png b/app/assets/images/emoji/unicode/2755.png new file mode 100644 index 000000000..a50d265e9 Binary files /dev/null and b/app/assets/images/emoji/unicode/2755.png differ diff --git a/app/assets/images/emoji/unicode/2757.png b/app/assets/images/emoji/unicode/2757.png new file mode 100644 index 000000000..4c560f5e3 Binary files /dev/null and b/app/assets/images/emoji/unicode/2757.png differ diff --git a/app/assets/images/emoji/unicode/2764.png b/app/assets/images/emoji/unicode/2764.png new file mode 100644 index 000000000..7d7790ce4 Binary files /dev/null and b/app/assets/images/emoji/unicode/2764.png differ diff --git a/app/assets/images/emoji/unicode/2795.png b/app/assets/images/emoji/unicode/2795.png new file mode 100644 index 000000000..61595387b Binary files /dev/null and b/app/assets/images/emoji/unicode/2795.png differ diff --git a/app/assets/images/emoji/unicode/2796.png b/app/assets/images/emoji/unicode/2796.png new file mode 100644 index 000000000..b8d3d82f2 Binary files /dev/null and b/app/assets/images/emoji/unicode/2796.png differ diff --git a/app/assets/images/emoji/unicode/2797.png b/app/assets/images/emoji/unicode/2797.png new file mode 100644 index 000000000..ac757a238 Binary files /dev/null and b/app/assets/images/emoji/unicode/2797.png differ diff --git a/app/assets/images/emoji/unicode/27a1.png b/app/assets/images/emoji/unicode/27a1.png new file mode 100644 index 000000000..e5cca853d Binary files /dev/null and b/app/assets/images/emoji/unicode/27a1.png differ diff --git a/app/assets/images/emoji/unicode/27b0.png b/app/assets/images/emoji/unicode/27b0.png new file mode 100644 index 000000000..8f051aca4 Binary files /dev/null and b/app/assets/images/emoji/unicode/27b0.png differ diff --git a/app/assets/images/emoji/unicode/27bf.png b/app/assets/images/emoji/unicode/27bf.png new file mode 100644 index 000000000..ef34df3a4 Binary files /dev/null and b/app/assets/images/emoji/unicode/27bf.png differ diff --git a/app/assets/images/emoji/unicode/2934.png b/app/assets/images/emoji/unicode/2934.png new file mode 100644 index 000000000..c8f670a1e Binary files /dev/null and b/app/assets/images/emoji/unicode/2934.png differ diff --git a/app/assets/images/emoji/unicode/2935.png b/app/assets/images/emoji/unicode/2935.png new file mode 100644 index 000000000..56dd3b9d3 Binary files /dev/null and b/app/assets/images/emoji/unicode/2935.png differ diff --git a/app/assets/images/emoji/unicode/2b05.png b/app/assets/images/emoji/unicode/2b05.png new file mode 100644 index 000000000..9d7d1b568 Binary files /dev/null and b/app/assets/images/emoji/unicode/2b05.png differ diff --git a/app/assets/images/emoji/unicode/2b06.png b/app/assets/images/emoji/unicode/2b06.png new file mode 100644 index 000000000..565ce2952 Binary files /dev/null and b/app/assets/images/emoji/unicode/2b06.png differ diff --git a/app/assets/images/emoji/unicode/2b07.png b/app/assets/images/emoji/unicode/2b07.png new file mode 100644 index 000000000..3956eb399 Binary files /dev/null and b/app/assets/images/emoji/unicode/2b07.png differ diff --git a/app/assets/images/emoji/unicode/2b1b.png b/app/assets/images/emoji/unicode/2b1b.png new file mode 100644 index 000000000..71da10de8 Binary files /dev/null and b/app/assets/images/emoji/unicode/2b1b.png differ diff --git a/app/assets/images/emoji/unicode/2b1c.png b/app/assets/images/emoji/unicode/2b1c.png new file mode 100644 index 000000000..60cb19a13 Binary files /dev/null and b/app/assets/images/emoji/unicode/2b1c.png differ diff --git a/app/assets/images/emoji/unicode/2b50.png b/app/assets/images/emoji/unicode/2b50.png new file mode 100644 index 000000000..1bfddc862 Binary files /dev/null and b/app/assets/images/emoji/unicode/2b50.png differ diff --git a/app/assets/images/emoji/unicode/2b55.png b/app/assets/images/emoji/unicode/2b55.png new file mode 100644 index 000000000..0ededebe3 Binary files /dev/null and b/app/assets/images/emoji/unicode/2b55.png differ diff --git a/app/assets/images/emoji/unicode/3030.png b/app/assets/images/emoji/unicode/3030.png new file mode 100644 index 000000000..77f626cc5 Binary files /dev/null and b/app/assets/images/emoji/unicode/3030.png differ diff --git a/app/assets/images/emoji/unicode/303d.png b/app/assets/images/emoji/unicode/303d.png new file mode 100644 index 000000000..45dc9b851 Binary files /dev/null and b/app/assets/images/emoji/unicode/303d.png differ diff --git a/app/assets/images/emoji/unicode/3297.png b/app/assets/images/emoji/unicode/3297.png new file mode 100644 index 000000000..dcbb1d229 Binary files /dev/null and b/app/assets/images/emoji/unicode/3297.png differ diff --git a/app/assets/images/emoji/unicode/3299.png b/app/assets/images/emoji/unicode/3299.png new file mode 100644 index 000000000..82e383a60 Binary files /dev/null and b/app/assets/images/emoji/unicode/3299.png differ diff --git a/app/assets/images/emoji/unlock.png b/app/assets/images/emoji/unlock.png new file mode 100644 index 000000000..22b429cd0 Binary files /dev/null and b/app/assets/images/emoji/unlock.png differ diff --git a/app/assets/images/emoji/up.png b/app/assets/images/emoji/up.png new file mode 100644 index 000000000..829219a86 Binary files /dev/null and b/app/assets/images/emoji/up.png differ diff --git a/app/assets/images/emoji/us.png b/app/assets/images/emoji/us.png new file mode 100644 index 000000000..38137669a Binary files /dev/null and b/app/assets/images/emoji/us.png differ diff --git a/app/assets/images/emoji/v.png b/app/assets/images/emoji/v.png new file mode 100644 index 000000000..f61267c28 Binary files /dev/null and b/app/assets/images/emoji/v.png differ diff --git a/app/assets/images/emoji/vertical_traffic_light.png b/app/assets/images/emoji/vertical_traffic_light.png new file mode 100644 index 000000000..7a5ba35f0 Binary files /dev/null and b/app/assets/images/emoji/vertical_traffic_light.png differ diff --git a/app/assets/images/emoji/vhs.png b/app/assets/images/emoji/vhs.png new file mode 100644 index 000000000..881081c17 Binary files /dev/null and b/app/assets/images/emoji/vhs.png differ diff --git a/app/assets/images/emoji/vibration_mode.png b/app/assets/images/emoji/vibration_mode.png new file mode 100644 index 000000000..a716e96c6 Binary files /dev/null and b/app/assets/images/emoji/vibration_mode.png differ diff --git a/app/assets/images/emoji/video_camera.png b/app/assets/images/emoji/video_camera.png new file mode 100644 index 000000000..274cecdd6 Binary files /dev/null and b/app/assets/images/emoji/video_camera.png differ diff --git a/app/assets/images/emoji/video_game.png b/app/assets/images/emoji/video_game.png new file mode 100644 index 000000000..59d45baea Binary files /dev/null and b/app/assets/images/emoji/video_game.png differ diff --git a/app/assets/images/emoji/violin.png b/app/assets/images/emoji/violin.png new file mode 100644 index 000000000..0dba5ba2b Binary files /dev/null and b/app/assets/images/emoji/violin.png differ diff --git a/app/assets/images/emoji/virgo.png b/app/assets/images/emoji/virgo.png new file mode 100644 index 000000000..72e1763f5 Binary files /dev/null and b/app/assets/images/emoji/virgo.png differ diff --git a/app/assets/images/emoji/volcano.png b/app/assets/images/emoji/volcano.png new file mode 100644 index 000000000..9b434539b Binary files /dev/null and b/app/assets/images/emoji/volcano.png differ diff --git a/app/assets/images/emoji/vs.png b/app/assets/images/emoji/vs.png new file mode 100644 index 000000000..863638850 Binary files /dev/null and b/app/assets/images/emoji/vs.png differ diff --git a/app/assets/images/emoji/walking.png b/app/assets/images/emoji/walking.png new file mode 100644 index 000000000..7a2bfacfc Binary files /dev/null and b/app/assets/images/emoji/walking.png differ diff --git a/app/assets/images/emoji/waning_crescent_moon.png b/app/assets/images/emoji/waning_crescent_moon.png new file mode 100644 index 000000000..30387780f Binary files /dev/null and b/app/assets/images/emoji/waning_crescent_moon.png differ diff --git a/app/assets/images/emoji/waning_gibbous_moon.png b/app/assets/images/emoji/waning_gibbous_moon.png new file mode 100644 index 000000000..8e324ec5f Binary files /dev/null and b/app/assets/images/emoji/waning_gibbous_moon.png differ diff --git a/app/assets/images/emoji/warning.png b/app/assets/images/emoji/warning.png new file mode 100644 index 000000000..466658d99 Binary files /dev/null and b/app/assets/images/emoji/warning.png differ diff --git a/app/assets/images/emoji/watch.png b/app/assets/images/emoji/watch.png new file mode 100644 index 000000000..d503bb87c Binary files /dev/null and b/app/assets/images/emoji/watch.png differ diff --git a/app/assets/images/emoji/water_buffalo.png b/app/assets/images/emoji/water_buffalo.png new file mode 100644 index 000000000..3bcde3edd Binary files /dev/null and b/app/assets/images/emoji/water_buffalo.png differ diff --git a/app/assets/images/emoji/watermelon.png b/app/assets/images/emoji/watermelon.png new file mode 100644 index 000000000..fc212be78 Binary files /dev/null and b/app/assets/images/emoji/watermelon.png differ diff --git a/app/assets/images/emoji/wave.png b/app/assets/images/emoji/wave.png new file mode 100644 index 000000000..e78402eb0 Binary files /dev/null and b/app/assets/images/emoji/wave.png differ diff --git a/app/assets/images/emoji/wavy_dash.png b/app/assets/images/emoji/wavy_dash.png new file mode 100644 index 000000000..77f626cc5 Binary files /dev/null and b/app/assets/images/emoji/wavy_dash.png differ diff --git a/app/assets/images/emoji/waxing_crescent_moon.png b/app/assets/images/emoji/waxing_crescent_moon.png new file mode 100644 index 000000000..c8f13dd31 Binary files /dev/null and b/app/assets/images/emoji/waxing_crescent_moon.png differ diff --git a/app/assets/images/emoji/waxing_gibbous_moon.png b/app/assets/images/emoji/waxing_gibbous_moon.png new file mode 100644 index 000000000..dd8c48458 Binary files /dev/null and b/app/assets/images/emoji/waxing_gibbous_moon.png differ diff --git a/app/assets/images/emoji/wc.png b/app/assets/images/emoji/wc.png new file mode 100644 index 000000000..dfe84d2a7 Binary files /dev/null and b/app/assets/images/emoji/wc.png differ diff --git a/app/assets/images/emoji/weary.png b/app/assets/images/emoji/weary.png new file mode 100644 index 000000000..0c5475411 Binary files /dev/null and b/app/assets/images/emoji/weary.png differ diff --git a/app/assets/images/emoji/wedding.png b/app/assets/images/emoji/wedding.png new file mode 100644 index 000000000..ead19d52c Binary files /dev/null and b/app/assets/images/emoji/wedding.png differ diff --git a/app/assets/images/emoji/whale.png b/app/assets/images/emoji/whale.png new file mode 100644 index 000000000..5bb113e42 Binary files /dev/null and b/app/assets/images/emoji/whale.png differ diff --git a/app/assets/images/emoji/whale2.png b/app/assets/images/emoji/whale2.png new file mode 100644 index 000000000..4af657b2f Binary files /dev/null and b/app/assets/images/emoji/whale2.png differ diff --git a/app/assets/images/emoji/wheelchair.png b/app/assets/images/emoji/wheelchair.png new file mode 100644 index 000000000..eddcdd797 Binary files /dev/null and b/app/assets/images/emoji/wheelchair.png differ diff --git a/app/assets/images/emoji/white_check_mark.png b/app/assets/images/emoji/white_check_mark.png new file mode 100644 index 000000000..61dc0583c Binary files /dev/null and b/app/assets/images/emoji/white_check_mark.png differ diff --git a/app/assets/images/emoji/white_circle.png b/app/assets/images/emoji/white_circle.png new file mode 100644 index 000000000..da782ae29 Binary files /dev/null and b/app/assets/images/emoji/white_circle.png differ diff --git a/app/assets/images/emoji/white_flower.png b/app/assets/images/emoji/white_flower.png new file mode 100644 index 000000000..c0929d0dd Binary files /dev/null and b/app/assets/images/emoji/white_flower.png differ diff --git a/app/assets/images/emoji/white_large_square.png b/app/assets/images/emoji/white_large_square.png new file mode 100644 index 000000000..60cb19a13 Binary files /dev/null and b/app/assets/images/emoji/white_large_square.png differ diff --git a/app/assets/images/emoji/white_medium_small_square.png b/app/assets/images/emoji/white_medium_small_square.png new file mode 100644 index 000000000..a115cdc49 Binary files /dev/null and b/app/assets/images/emoji/white_medium_small_square.png differ diff --git a/app/assets/images/emoji/white_medium_square.png b/app/assets/images/emoji/white_medium_square.png new file mode 100644 index 000000000..199808bcf Binary files /dev/null and b/app/assets/images/emoji/white_medium_square.png differ diff --git a/app/assets/images/emoji/white_small_square.png b/app/assets/images/emoji/white_small_square.png new file mode 100644 index 000000000..24ba879f4 Binary files /dev/null and b/app/assets/images/emoji/white_small_square.png differ diff --git a/app/assets/images/emoji/white_square_button.png b/app/assets/images/emoji/white_square_button.png new file mode 100644 index 000000000..63c7a3ef4 Binary files /dev/null and b/app/assets/images/emoji/white_square_button.png differ diff --git a/app/assets/images/emoji/wind_chime.png b/app/assets/images/emoji/wind_chime.png new file mode 100644 index 000000000..efacf5dd4 Binary files /dev/null and b/app/assets/images/emoji/wind_chime.png differ diff --git a/app/assets/images/emoji/wine_glass.png b/app/assets/images/emoji/wine_glass.png new file mode 100644 index 000000000..82b0f0005 Binary files /dev/null and b/app/assets/images/emoji/wine_glass.png differ diff --git a/app/assets/images/emoji/wink.png b/app/assets/images/emoji/wink.png new file mode 100644 index 000000000..756766dd3 Binary files /dev/null and b/app/assets/images/emoji/wink.png differ diff --git a/app/assets/images/emoji/wolf.png b/app/assets/images/emoji/wolf.png new file mode 100644 index 000000000..c60c96895 Binary files /dev/null and b/app/assets/images/emoji/wolf.png differ diff --git a/app/assets/images/emoji/woman.png b/app/assets/images/emoji/woman.png new file mode 100644 index 000000000..6bf0d2b12 Binary files /dev/null and b/app/assets/images/emoji/woman.png differ diff --git a/app/assets/images/emoji/womans_clothes.png b/app/assets/images/emoji/womans_clothes.png new file mode 100644 index 000000000..aa297c7b6 Binary files /dev/null and b/app/assets/images/emoji/womans_clothes.png differ diff --git a/app/assets/images/emoji/womans_hat.png b/app/assets/images/emoji/womans_hat.png new file mode 100644 index 000000000..4cb2e6a69 Binary files /dev/null and b/app/assets/images/emoji/womans_hat.png differ diff --git a/app/assets/images/emoji/womens.png b/app/assets/images/emoji/womens.png new file mode 100644 index 000000000..518b76a6d Binary files /dev/null and b/app/assets/images/emoji/womens.png differ diff --git a/app/assets/images/emoji/worried.png b/app/assets/images/emoji/worried.png new file mode 100644 index 000000000..afd9283fc Binary files /dev/null and b/app/assets/images/emoji/worried.png differ diff --git a/app/assets/images/emoji/wrench.png b/app/assets/images/emoji/wrench.png new file mode 100644 index 000000000..a87072ad1 Binary files /dev/null and b/app/assets/images/emoji/wrench.png differ diff --git a/app/assets/images/emoji/x.png b/app/assets/images/emoji/x.png new file mode 100644 index 000000000..b84f63557 Binary files /dev/null and b/app/assets/images/emoji/x.png differ diff --git a/app/assets/images/emoji/yellow_heart.png b/app/assets/images/emoji/yellow_heart.png new file mode 100644 index 000000000..fa41ce78a Binary files /dev/null and b/app/assets/images/emoji/yellow_heart.png differ diff --git a/app/assets/images/emoji/yen.png b/app/assets/images/emoji/yen.png new file mode 100644 index 000000000..139bc936e Binary files /dev/null and b/app/assets/images/emoji/yen.png differ diff --git a/app/assets/images/emoji/yum.png b/app/assets/images/emoji/yum.png new file mode 100644 index 000000000..fc39637ec Binary files /dev/null and b/app/assets/images/emoji/yum.png differ diff --git a/app/assets/images/emoji/zap.png b/app/assets/images/emoji/zap.png new file mode 100644 index 000000000..260c531b9 Binary files /dev/null and b/app/assets/images/emoji/zap.png differ diff --git a/app/assets/images/emoji/zero.png b/app/assets/images/emoji/zero.png new file mode 100644 index 000000000..15e7446c8 Binary files /dev/null and b/app/assets/images/emoji/zero.png differ diff --git a/app/assets/images/emoji/zzz.png b/app/assets/images/emoji/zzz.png new file mode 100644 index 000000000..30be04655 Binary files /dev/null and b/app/assets/images/emoji/zzz.png differ diff --git a/app/assets/images/gplus.png b/app/assets/images/facebook.png similarity index 88% rename from app/assets/images/gplus.png rename to app/assets/images/facebook.png index 80f6155e0..429c94254 100644 Binary files a/app/assets/images/gplus.png and b/app/assets/images/facebook.png differ diff --git a/app/assets/images/folder-submodule.png b/app/assets/images/folder-submodule.png new file mode 100644 index 000000000..651ee7423 Binary files /dev/null and b/app/assets/images/folder-submodule.png differ diff --git a/app/assets/images/gears.png b/app/assets/images/gears.png new file mode 100644 index 000000000..b2d1500d3 Binary files /dev/null and b/app/assets/images/gears.png differ diff --git a/app/assets/images/git.png b/app/assets/images/github.png similarity index 100% rename from app/assets/images/git.png rename to app/assets/images/github.png diff --git a/app/assets/images/google.png b/app/assets/images/google.png new file mode 100644 index 000000000..2ce2a7161 Binary files /dev/null and b/app/assets/images/google.png differ diff --git a/app/assets/images/profile-hover.png b/app/assets/images/profile-hover.png new file mode 100644 index 000000000..09427d06b Binary files /dev/null and b/app/assets/images/profile-hover.png differ diff --git a/app/assets/javascripts/angularjs/angular-moment.js b/app/assets/javascripts/angularjs/angular-moment.js new file mode 100644 index 000000000..939feb3fa --- /dev/null +++ b/app/assets/javascripts/angularjs/angular-moment.js @@ -0,0 +1,82 @@ +/* angular-moment.js / v0.2.0 / (c) 2013 Uri Shaked / MIT Licence */ + +angular.module('angularMoment', []) + .directive('amTimeAgo', ['$window', '$timeout', function ($window, $timeout) { + 'use strict'; + + return function (scope, element, attr) { + var activeTimeout = null; + var currentValue; + var currentFormat; + + function cancelTimer() { + if (activeTimeout) { + $timeout.cancel(activeTimeout); + activeTimeout = null; + } + } + + function updateTime(momentInstance) { + element.text(momentInstance.fromNow()); + var howOld = $window.moment().diff(momentInstance, 'minute'); + var secondsUntilUpdate = 3600; + if (howOld < 1) { + secondsUntilUpdate = 1; + } else if (howOld < 60) { + secondsUntilUpdate = 30; + } else if (howOld < 180) { + secondsUntilUpdate = 300; + } + + activeTimeout = $timeout(function () { + updateTime(momentInstance); + }, secondsUntilUpdate * 1000, false); + } + + function updateMoment() { + cancelTimer(); + updateTime($window.moment(currentValue, currentFormat)); + } + + scope.$watch(attr.amTimeAgo, function (value) { + if (typeof value === 'undefined' || value === null) { + return; + } + + if (angular.isNumber(value)) { + // Milliseconds since the epoch + value = new Date(value); + } + // else assume the given value is already a date + + currentValue = value; + updateMoment(); + }); + + attr.$observe('amFormat', function (format) { + currentFormat = format; + updateMoment(); + }); + + scope.$on('$destroy', function () { + cancelTimer(); + }); + }; + }]) + .filter('amDateFormat', ['$window', function ($window) { + 'use strict'; + + return function (value, format) { + if (typeof value === 'undefined' || value === null) { + return ''; + } + + if (angular.isNumber(value)) { + // Milliseconds since the epoch + value = new Date(value); + } + // else assume the given value is already a date + + return $window.moment(value).format(format); + }; + }]); \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/config.js.erb b/app/assets/javascripts/angularjs/config.js.erb new file mode 100644 index 000000000..f2fcf9ad9 --- /dev/null +++ b/app/assets/javascripts/angularjs/config.js.erb @@ -0,0 +1,48 @@ +var RosaABF = angular.module('RosaABF', ['ngResource', 'ng-rails-csrf', 'angular-i18n', 'angularMoment']); + +var DateTimeFormatter = function() { + + var UtcFormatter = function(api_time) { + return moment.utc(api_time * 1000).format('YYYY-MM-DD HH:mm:ss UTC'); + } + + return { + utc : UtcFormatter + } +} +RosaABF.factory("DateTimeFormatter", DateTimeFormatter); + +var LocalesHelper = function($locale) { + var locales = { + 'ru' : 'ru-ru', + 'en' : 'en-us' + } + return { + setLocale: function(locale) { + $locale.id = locales[locale]; + } + } +} +RosaABF.factory("LocalesHelper", ['$locale', LocalesHelper]); + +var SoundNotificationsHelper = function() { + var isOn = true; + var statusChangedSound = null; + soundManager.setup({ + // url: '/assets/swf/', + preferFlash: false, + onready: function() { + statusChangedSound = soundManager.createSound({url: "<%=asset_path('garbage_shattering.wav')%>"}); + } + }); + return { + buildStatusChanged: function() { + if (isOn && statusChangedSound) + statusChangedSound.play(); + }, + enabled: function(status) { + isOn = status; + } + } +} +RosaABF.factory('SoundNotificationsHelper', SoundNotificationsHelper); \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/controllers/build_list_controller.js.erb b/app/assets/javascripts/angularjs/controllers/build_list_controller.js.erb new file mode 100644 index 000000000..07eb79599 --- /dev/null +++ b/app/assets/javascripts/angularjs/controllers/build_list_controller.js.erb @@ -0,0 +1,72 @@ +RosaABF.controller('BuildListController', ['$scope', '$http', '$timeout', 'SoundNotificationsHelper', function($scope, $http, $timeout, SoundNotificationsHelper) { + + $scope.advisoriable_types = <%=BuildList::RELEASE_UPDATE_TYPES%>; + + $scope.id = $('#build_list_id').val(); + $scope.build_list = null; + $scope.subject = {}; // See: shared/build_results + $scope.attach_advisory = 'no'; + // Statuses: advisory_not_found, server_error, continue_input + $scope.search_status = 'continue_input'; + $scope.term = ''; + $scope.advisory = null; + + $scope.getBuildList = function() { + $http.get(Routes.build_list_path($scope.id, {format: 'json'})).success(function(results) { + var build_list = new BuildList(results.build_list); + if ($scope.build_list && $scope.build_list.status != build_list.status) + SoundNotificationsHelper.buildStatusChanged(); + $scope.build_list = $scope.subject = build_list; + }); + } + + $scope.cancelRefresh = null; + $scope.refresh = function() { + if ( $scope.attach_advisory == 'no' && + ( + !$scope.build_list || + !( + $scope.build_list.status == <%=BuildList::BUILD_PUBLISHED%> || + $scope.build_list.status == <%=BuildList::REJECTED_PUBLISH%> || + $scope.build_list.status == <%=BuildList::FAILED_PUBLISH%> || + $scope.build_list.status == <%=BuildList::BUILD_CANCELED%> || + $scope.build_list.status == <%=BuildList::BUILD_ERROR%> + ) + ) + ) { + $scope.getBuildList(); + } + $scope.cancelRefresh = $timeout($scope.refresh, 10000); + } + $scope.refresh(); + + $scope.search = function() { + var params = {query: $scope.term, bl_type: $scope.build_list.update_type, format: 'json'}; + $http.get(Routes.search_advisories_path(params)).success(function(results) { + $scope.search_status = 'continue_input'; + $scope.advisory = results; + $('#attach_advisory').find('.advisory_id').val($scope.advisory.advisory_id); + }).error(function(data, status, headers, config) { + $scope.search_status = status == 404 ? 'advisory_not_found' : 'server_error'; + $scope.advisory = null; + $('#attach_advisory').find('.advisory_id').val(''); + });; + + } + + $scope.updateTypeChanged = function() { + if (_.contains($scope.advisoriable_types, $scope.build_list.update_type)) { + if ($scope.advisory || $scope.term.length > 0) { $scope.search(); } + } else { + $scope.attach_advisory = 'no'; + } + } + + $scope.attachAdvisoryChanged = function() { + if (!_.contains($scope.advisoriable_types, $scope.build_list.update_type)) { + $scope.build_list.update_type = $scope.advisoriable_types[0]; + } + $('#build_list_update_type .nonadvisoriable').attr('disabled', ($scope.attach_advisory != 'no')); + } + +}]); \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/controllers/build_lists_controller.js.erb b/app/assets/javascripts/angularjs/controllers/build_lists_controller.js.erb new file mode 100644 index 000000000..f9d6da334 --- /dev/null +++ b/app/assets/javascripts/angularjs/controllers/build_lists_controller.js.erb @@ -0,0 +1,159 @@ +RosaABF.controller('BuildListsController', ['$scope', '$http', '$location', '$timeout', function($scope, $http, $location, $timeout) { + + $scope.params = null; + $scope.first_run = true; + $scope.server_status = null; + $scope.build_lists = []; + $scope.isRequest = false; // Disable 'Search' button + $scope.pages = []; + + $scope.opened = {}; + $scope.map_priorities = { + <%=BuildList::WAITING_FOR_RESPONSE%>: 11, + <%=BuildList::BUILD_PENDING%>: 10, + <%=BuildList::BUILD_CANCELING%>: 9, + <%=BuildList::BUILD_CANCELED%>: 8, + <%=BuildList::BUILD_STARTED%>: 7, + <%=BuildList::BUILD_PUBLISH%>: 6, + <%=BuildList::BUILD_PUBLISHED%>: 5, + <%=BuildList::BUILD_ERROR%>: 4, + <%=BuildList::SUCCESS%>: 3, + <%=BuildList::TESTS_FAILED%>: 2, + <%=BuildList::FAILED_PUBLISH%>: 1, + <%=BuildList::REJECTED_PUBLISH%>: 0 + }; + + + // Fixes: redirect to page after form submit + $("#monitoring_filter").on('submit', function(){ return false; }); + + $scope.getBuildLists = function() { + // Disable 'Search' button + $scope.isRequest = true; + + + $http.get(Routes.build_lists_path({format: 'json'}), {params: $location.search()}).success(function(results) { + // Render Server status + $scope.server_status = results.server_status; + + // TMP fields + var dictionary = results.dictionary; + var build_lists = []; + var groups = {}; + + var to_open = []; + // Grouping of build_lists + _.each(results.build_lists, function(r){ + var bl = new BuildList(r, dictionary); + var key = bl.project_id + '-'; + key += bl.group_id ? bl.group_id : (bl.commit_hash + '-' + bl.user_id); + if (groups[key]) { + groups[key].addRelated(bl); + } else { + groups[key] = bl; + build_lists.push(bl); + if ( bl.id in $scope.opened ) { to_open.push(bl); } + } + }); + + // Adds all build_lists into the table (group by group) + $scope.build_lists = []; + $scope.opened = {}; + _.each(build_lists, function(bl){ + if (bl.related.length > 1) { + var sorted_build_lists = _.sortBy(bl.related, function(b) { return $scope.map_priorities[b.status]; }); + bl.clearRelated(); + var first_in_group = sorted_build_lists[0]; + first_in_group.clearRelated(); + _.each(sorted_build_lists, function(b){ + if (b != first_in_group) { first_in_group.addRelated(b); } + $scope.build_lists.push(b); + }); + } else { + $scope.build_lists.push(bl); + } + }); + // Shows groups which have been opened before + _.each(to_open, function(bl){ $scope.showRelated(bl, true); }); + + // Render pagination + $scope.pages = results.pages; + // Enable 'Search' button + $scope.isRequest = false; + }).error(function(data, status, headers, config) { + // Enable 'Search' button + $scope.isRequest = false; + });; + } + + $scope.showRelated = function(build_list, disable_effect) { + build_list.relatedHidden = false; + _.each(build_list.related, function(bl){ + bl.show = true; + $scope.opened[bl.id] = true; + // Waits for render of build_lists + if (!disable_effect) + $timeout(function() { + $('#build-list-' + bl.id + ' td:visible').effect('highlight', {}, 1000); + }, 100); + }); + } + + $scope.hideRelated = function(build_list) { + build_list.relatedHidden = true; + _.each(build_list.related, function(bl){ + bl.show = false; + delete $scope.opened[bl.id]; + }); + build_list.show = true; + } + + $scope.cancelRefresh = null; + $scope.refresh = function(force) { + if ($('#autoreload').is(':checked') || force) { + var params = {}; + _.each($("#monitoring_filter").serializeArray(), function(a){ + if (a.value) { params[a.name] = a.value; } + }); + $location.search(params); + $scope.first_run = false; + $scope.getBuildLists(); + } + if (!force) + $scope.cancelRefresh = $timeout($scope.refresh, 60000); + } + + + $scope.$on('$locationChangeSuccess', function(event) { + $scope.updateParams(); + if (!$scope.first_run) { $scope.getBuildLists(); } + }); + + $scope.updateParams = function() { + var params = $location.search(); + $scope.params = { + page: params.page || 1, + per_page: params.per_page || 25, + filter: { + ownership: params['filter[ownership]'] || 'owned', + status: params['filter[status]'], + platform_id: params['filter[platform_id]'], + arch_id: params['filter[arch_id]'], + mass_build_id: params['filter[mass_build_id]'], + updated_at_start: params['filter[updated_at_start]'], + updated_at_end: params['filter[updated_at_end]'], + project_name: params['filter[project_name]'], + id: params['filter[id]'] + } + } + } + + $scope.goToPage = function(number) { + $location.search('page', number); + $scope.getBuildLists(); + } + + $scope.updateParams(); + // Waits for render of filters + $timeout($scope.refresh, 100); +}]); \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/controllers/collaborators_controller.js.erb b/app/assets/javascripts/angularjs/controllers/collaborators_controller.js.erb new file mode 100644 index 000000000..bb43b5803 --- /dev/null +++ b/app/assets/javascripts/angularjs/controllers/collaborators_controller.js.erb @@ -0,0 +1,65 @@ +RosaABF.controller('CollaboratorsController', ['$scope', 'ApiCollaborator', function($scope, ApiCollaborator) { + + $scope.def_role = "<%=Relation::ROLES.first%>"; + + $scope.popup = $('#add_collaborator_form .users-search-popup'); + $scope.owner = $('#owner_name').val(); + $scope.project = $('#project_name').val(); + $scope.resource = ApiCollaborator.resource; + $scope.collaborators = []; + + $scope.new_collaborators = []; + + $scope.initNewCollaborator = function(c) { + if (c) { + c.term = c.actor_name; + c.collaborator.role = $scope.def_role; + } else { + c = {collaborator: {role: $scope.def_role}}; + } + $scope.new_collaborator = c; + } + $scope.initNewCollaborator(); + + $scope.getCollaborators = function() { + $scope.collaborators = $scope.resource.query({owner: $scope.owner, project: $scope.project}); + } + $scope.getCollaborators(); + + $scope.update = function(collaborator) { + collaborator.$update(); + } + + $scope.deleteCollaborators = function() { + var collaborators = []; + _.each($scope.collaborators, function(collaborator){ + if(collaborator.removed) { + collaborator.$delete(); + } else { + collaborators.push(collaborator); + } + $scope.collaborators = collaborators; + }); + } + + $scope.search = function() { + if ($scope.new_collaborator.term.length > 2) { + $scope.new_collaborators = $scope.resource.find( + {owner: $scope.owner, project: $scope.project, term: $scope.new_collaborator.term}); + $scope.popup.show(); + } + } + + $scope.select = function(c) { + $scope.initNewCollaborator(c); + $scope.popup.hide(); + } + + $scope.add = function() { + $scope.new_collaborator.$save(function() { + $scope.collaborators.push($scope.new_collaborator); + $scope.initNewCollaborator(); + }); + } + +}]); \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/controllers/product_build_list_controller.js.erb b/app/assets/javascripts/angularjs/controllers/product_build_list_controller.js.erb new file mode 100644 index 000000000..33875bc07 --- /dev/null +++ b/app/assets/javascripts/angularjs/controllers/product_build_list_controller.js.erb @@ -0,0 +1,31 @@ +RosaABF.controller('ProductBuildListController', ['$scope', '$http', '$timeout', 'SoundNotificationsHelper', function($scope, $http, $timeout, SoundNotificationsHelper) { + + $scope.id = $('#product_build_list_id').val(); + $scope.pbl = null; + $scope.subject = {}; // See: shared/build_results + + $scope.getProductBuildList = function() { + $http.get(Routes.product_build_list_path($scope.id, {format: 'json'})).success(function(results) { + var product_build_list = results.product_build_list; + if ($scope.pbl && $scope.pbl.status != product_build_list.status) + SoundNotificationsHelper.buildStatusChanged(); + $scope.pbl = $scope.subject = product_build_list; + }); + } + + $scope.cancelRefresh = null; + $scope.refresh = function() { + if (!$scope.pbl || + !( + $scope.pbl.status == <%=ProductBuildList::BUILD_COMPLETED%> || + $scope.pbl.status == <%=ProductBuildList::BUILD_FAILED%> || + $scope.pbl.status == <%=ProductBuildList::BUILD_CANCELED%> + ) + ) { + $scope.getProductBuildList(); + } + $scope.cancelRefresh = $timeout($scope.refresh, 10000); + } + $scope.refresh(); + +}]); \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/controllers/project_branches_controller.js b/app/assets/javascripts/angularjs/controllers/project_branches_controller.js new file mode 100644 index 000000000..9fc754c6d --- /dev/null +++ b/app/assets/javascripts/angularjs/controllers/project_branches_controller.js @@ -0,0 +1,80 @@ +RosaABF.controller('ProjectBranchesController', ['$scope', '$http', 'ApiProject', function($scope, $http, ApiProject) { + + $scope.singleton = ApiProject.singleton; + $scope.branches = []; + + $scope.current_ref = null; + $scope.project_resource = null; + + $scope.init = function(owner_uname, project_name, ref) { + $scope.current_ref = ref; + + $scope.project_resource = ApiProject.resource.get( + {owner: owner_uname, project: project_name}, + function(results) { + $scope.project = new Project(results.project); + $scope.getBranches(); + } + ); + + } + + $scope.getBranches = function() { + + $scope.project_resource.$branches( + {owner: $scope.project.owner.uname, project: $scope.project.name}, + function(results) { + $scope.branches = []; + _.each(results.refs_list, function(ref){ + var result = new ProjectRef(ref); + if (result.ref == $scope.current_ref) { + $scope.branches.unshift(result); + } else { + $scope.branches.push(result); + } + }); + $scope.updateBranchesCount(); + } + ); + + } + + $scope.updateBranchesCount = function() { + $scope.singleton.project.branches_count = $scope.branches.length; + } + + $scope.create = function(branch) { + branch.ui_container = false; + $scope.project_resource.$create_branch( + { + owner: $scope.project.owner.uname, + project: $scope.project.name, + from_ref: branch.ref, + new_ref: branch.new_ref + }, function() { // on success + $scope.getBranches(); + }, function () { // on error + $scope.getBranches(); + } + ); + } + + $scope.destroy = function(branch) { + $scope.project_resource.$delete_branch( + {owner: $scope.project.owner.uname, project: $scope.project.name, ref: branch.ref}, + function() { // on success + var i = $scope.branches.indexOf(branch); + if(i != -1) { $scope.branches.splice(i, 1); } + + $scope.updateBranchesCount(); + // Removes branch from "Current branch/tag:" select box + $('#branch_selector option').filter(function() { + return this.value.match('.*\/branches\/' + branch.ref + '$'); + }).remove(); + }, function () { // on error + $scope.getBranches(); + } + ); + } + +}]); \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/controllers/project_repo_block_controller.js b/app/assets/javascripts/angularjs/controllers/project_repo_block_controller.js new file mode 100644 index 000000000..731fc754e --- /dev/null +++ b/app/assets/javascripts/angularjs/controllers/project_repo_block_controller.js @@ -0,0 +1,9 @@ +RosaABF.controller('ProjectRepoBlockController', ['$scope', 'ApiProject', function($scope, ApiProject) { + + $scope.singleton = ApiProject.singleton; + + $scope.init = function(branches) { + $scope.singleton.project.branches_count = branches; + } + +}]); \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/controllers/project_tags_controller.js b/app/assets/javascripts/angularjs/controllers/project_tags_controller.js new file mode 100644 index 000000000..9042eb4c6 --- /dev/null +++ b/app/assets/javascripts/angularjs/controllers/project_tags_controller.js @@ -0,0 +1,31 @@ +RosaABF.controller('ProjectTagsController', ['$scope', '$http', 'ApiProject', function($scope, $http, ApiProject) { + + $scope.tags = []; + $scope.project_resource = null; + + $scope.init = function(owner_uname, project_name) { + $scope.project_resource = ApiProject.resource.get( + {owner: owner_uname, project: project_name}, + function(results) { + $scope.project = new Project(results.project); + $scope.getTags(); + } + ); + + } + + $scope.getTags = function() { + + $scope.project_resource.$tags( + {owner: $scope.project.owner.uname, project: $scope.project.name}, + function(results) { + $scope.tags = []; + _.each(results.refs_list, function(ref){ + $scope.tags.push(new ProjectRef(ref)); + }); + } + ); + + } + +}]); \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/controllers/pull_request_controller.js b/app/assets/javascripts/angularjs/controllers/pull_request_controller.js new file mode 100644 index 000000000..4073d41c7 --- /dev/null +++ b/app/assets/javascripts/angularjs/controllers/pull_request_controller.js @@ -0,0 +1,93 @@ +RosaABF.controller('PullRequestController',['$scope', '$http', 'ApiPullRequest', 'ApiProject', 'DateTimeFormatter', function($scope, $http, ApiPullRequest, ApiProject, DateTimeFormatter) { + + $scope.project_resource = null; + + $scope.pull_params = null; + $scope.pull = null; + $scope.pull_resource = null; + + $scope.merged_at = null; + $scope.closed_at = null; + $scope.branch = null; + + $scope.can_delete_branch = false; + + $scope.init = function(owner_uname, project_name, serial_id) { + $scope.pull_params = { + owner: owner_uname, + project: project_name, + serial_id: serial_id + }; + $scope.getPullRequest(); + } + + $scope.getPullRequest = function() { + $scope.pull_resource = ApiPullRequest.resource.get($scope.pull_params, function(results) { + $scope.pull = results.pull_request; + if ($scope.pull.merged_at) { $scope.merged_at = DateTimeFormatter.utc($scope.pull.merged_at); } + if ($scope.pull.closed_at) { $scope.closed_at = DateTimeFormatter.utc($scope.pull.closed_at); } + }); + } + + // @param [from_ref] - sets only at first time + $scope.getBranch = function(from_ref) { + if (!$scope.project_resource) { + $scope.project_resource = ApiProject.resource.get($scope.pull_params); + } + // Fix: at first load + // Cannot read property 'from_ref' of null + if (!from_ref) { from_ref = $scope.pull.from_ref.ref; } + $scope.project_resource.$branches($scope.pull_params, function(results) { + var branch = null; + _.each(results.refs_list, function(b){ + if (b.ref == from_ref) { + branch = new ProjectRef(b); + return true; + } + }); + $scope.branch = branch; + }); + } + + $scope.reopen = function() { + $scope.pull_resource.$update({pull_request_action: 'reopen'}, function() { + $scope.getPullRequest(); + }); + } + + $scope.close = function() { + $scope.pull_resource.$update({pull_request_action: 'close'}, function() { + $scope.getPullRequest(); + }); + } + + $scope.merge = function() { + $scope.pull_resource.$merge(function() { + $scope.getPullRequest(); + }); + } + + $scope.deleteBranch = function() { + $scope.project_resource.$delete_branch($scope.branch_params(), + function() { $scope.branch = null; }, // success + function() { $scope.getBranch(); } // error + ); + } + + $scope.restoreBranch = function() { + $scope.project_resource.$restore_branch($scope.branch_params(), + function() { $scope.getBranch(); }, // success + function() { $scope.getBranch(); } // error + ); + } + + $scope.branch_params = function() { + return { + owner: $scope.pull_params.owner, + project: $scope.pull_params.project, + ref: $scope.pull.from_ref.ref, + sha: $scope.pull.from_ref.sha + } + } + +}]); \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/controllers/rosa_abf_controller.js b/app/assets/javascripts/angularjs/controllers/rosa_abf_controller.js new file mode 100644 index 000000000..b0d884682 --- /dev/null +++ b/app/assets/javascripts/angularjs/controllers/rosa_abf_controller.js @@ -0,0 +1,8 @@ +RosaABF.controller('RosaABFController', ['$scope', 'LocalesHelper', 'SoundNotificationsHelper', function($scope, LocalesHelper, SoundNotificationsHelper) { + + $scope.init = function(locale, sound_notifications) { + LocalesHelper.setLocale(locale); + SoundNotificationsHelper.enabled(sound_notifications); + } + +}]); \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/locales.js.erb b/app/assets/javascripts/angularjs/locales.js.erb new file mode 100644 index 000000000..5d5901301 --- /dev/null +++ b/app/assets/javascripts/angularjs/locales.js.erb @@ -0,0 +1,28 @@ +var _locales = { + <%I18n.locale = :ru%> + 'ru-ru': { + 'project.total_branches': [ + 'Всего %1 ветка', + 'Всего %1 ветки', + 'Всего %1 веток' + ], + 'project.total_tags': [ + 'Всего %1 тег', + 'Всего %1 тега', + 'Всего %1 тегов' + ], + <%= BuildList::STATUSES.map{|s| "'build_list.status.#{s}':'#{BuildList.human_status(s)}'"}.join(',') %> + }, + <%I18n.locale = :en%> + 'en-us': { + 'project.total_branches': [ + 'Total %1 branch', + 'Total %1 branches' + ], + 'project.total_tags': [ + 'Total %1 tag', + 'Total %1 tags' + ], + <%= BuildList::STATUSES.map{|s| "'build_list.status.#{s}':'#{BuildList.human_status(s)}'"}.join(',') %> + } +}; \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/models/build_list.js.erb b/app/assets/javascripts/angularjs/models/build_list.js.erb new file mode 100644 index 000000000..d268d0887 --- /dev/null +++ b/app/assets/javascripts/angularjs/models/build_list.js.erb @@ -0,0 +1,96 @@ +// dictionary - optional +var BuildList = function(atts, dictionary) { + var self = this; + var initialSettings = atts || {}; + //initial settings if passed in + for(var setting in initialSettings){ + if(initialSettings.hasOwnProperty(setting)) + self[setting] = initialSettings[setting]; + }; + + // Fields: + + if (dictionary) { + self.user = _.find(dictionary.users, function(i){ return i.id == self.user_id; }); + self.arch = _.find(dictionary.arches, function(i){ return i.id == self.arch_id; }); + self.project = _.find(dictionary.projects, function(i){ return i.id == self.project_id; }); + self.save_to_platform = _.find(dictionary.platforms, function(i){ return i.id == self.save_to_platform_id; }); + self.build_for_platform = _.find(dictionary.platforms, function(i){ return i.id == self.build_for_platform_id; }); + self.save_to_repository = _.find(dictionary.repositories, function(i){ return i.id == self.save_to_repository_id; }); + } + + if (self.save_to_platform) { + self.save_to_repository_name = self.save_to_platform.name + '/' + self.save_to_repository.name; + if (self.save_to_platform.personal && self.build_for_platform !== undefined) { + self.save_to_repository_name += ' (' + self.build_for_platform.name + ')' + } + self.save_to_repository_url = Routes.platform_repository_path(self.save_to_platform.name, self.save_to_repository.name); + } + + if (self.project) { + if (self.last_published_commit_hash) { + self.version_link_text = self.last_published_commit_hash + '...' + self.commit_hash; + self.version_link_url = Routes.diff_path(self.project.owner, self.project.name, self.version_link_text); + } else { + self.version_link_text = self.commit_hash || self.project_version; + self.version_link_url = Routes.commit_path(self.project.owner, self.project.name, self.version_link_text); + } + self.project.url = Routes.project_path(self.project.owner, self.project.name); + self.project.name_with_owner = self.project.owner + '/' + self.project.name; + } + + if (self.user) + self.user.url = Routes.user_path(self.user.uname); + self.url = Routes.build_list_path(self.id); + + switch (self.status) { // See: app/helpers/build_lists_helper.rb + case <%=BuildList::BUILD_PUBLISHED%>: + case <%=BuildList::BUILD_PUBLISHED_INTO_TESTING%>: + case <%=BuildList::SUCCESS%>: self.status_color = 'success'; break + case <%=BuildList::BUILD_ERROR%>: + case <%=BuildList::FAILED_PUBLISH%>: + case <%=BuildList::FAILED_PUBLISH_INTO_TESTING%>: + case <%=BuildList::REJECTED_PUBLISH%>: self.status_color = 'error'; break + case <%=BuildList::TESTS_FAILED%>: self.status_color = 'warning'; break + default: self.status_color = 'nocolor'; + } + + // == Logic + + // private fields + self.lastRelated = false; + + // public methods + + self.clearRelated = function() { + self.related = [self]; + self.show = true; + self.relatedHidden = true; + self.hasRelated = false; + } + self.clearRelated(); + + self.addRelated = function(bl) { + bl.show = false; + bl.lastRelated = true; + self.related.slice(-1)[0].lastRelated = false; + self.related.push(bl); + self.hasRelated = true; + } + + self.itemStatusColor = function(status) { + if (status == <%=BuildList::SUCCESS%>) + return 'success'; + else if (status == <%=BuildList::BUILD_ERROR%>) + return 'error'; + else + return ''; + } + self.humanStatus = function(status) { + return 'build_list.status.' + status; + } + self.human_status = self.humanStatus(self.status); + + //return the scope-safe instance + return self; +}; \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/models/project.js b/app/assets/javascripts/angularjs/models/project.js new file mode 100644 index 000000000..22e587cc1 --- /dev/null +++ b/app/assets/javascripts/angularjs/models/project.js @@ -0,0 +1,14 @@ +var Project = function(atts){ + var self = this; + var initialSettings = atts || {}; + //initial settings if passed in + for(var setting in initialSettings){ + if(initialSettings.hasOwnProperty(setting)) + self[setting] = initialSettings[setting]; + }; + + //with some logic... + + //return the scope-safe instance + return self; +}; \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/models/project_ref.js b/app/assets/javascripts/angularjs/models/project_ref.js new file mode 100644 index 000000000..ac0e02825 --- /dev/null +++ b/app/assets/javascripts/angularjs/models/project_ref.js @@ -0,0 +1,30 @@ +var ProjectRef = function(atts) { + var self = this; + var initialSettings = atts || {}; + //initial settings if passed in + for(var setting in initialSettings){ + if(initialSettings.hasOwnProperty(setting)) + self[setting] = initialSettings[setting]; + }; + + + + //with some logic... + self.isTag = self.object.type == 'tag'; + self.ui_container = false; + + self.path = function(project) { + return Routes.tree_path(project.owner.uname, project.name, self.ref); + } + + self.diff_path = function(project, current_ref) { + return Routes.diff_path(project.owner.uname, project.name, current_ref + '...' + self.ref); + } + + self.archive_path = function(project, type) { + return Routes.archive_path(project.owner.uname, project.name, project.name + '-' + self.ref, {format: type}); + } + + //return the scope-safe instance + return self; +}; \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/services/collaborator.js b/app/assets/javascripts/angularjs/services/collaborator.js new file mode 100644 index 000000000..c1d1198cb --- /dev/null +++ b/app/assets/javascripts/angularjs/services/collaborator.js @@ -0,0 +1,26 @@ +RosaABF.factory('ApiCollaborator', ['$resource', function($resource) { + + var CollaboratorResource = $resource( + '/:owner/:project/collaborators/:id?format=json', + { + owner: '@project.owner_uname', + project: '@project.name', + id: '@id' + }, + { + update: { + method: 'PUT', + isArray : false + }, + find: { + url: '/:owner/:project/collaborators/find.json', + method: 'GET', + isArray : true + } + } + ); + + return { + resource : CollaboratorResource + } +}]); \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/services/project.js b/app/assets/javascripts/angularjs/services/project.js new file mode 100644 index 000000000..a1fb48023 --- /dev/null +++ b/app/assets/javascripts/angularjs/services/project.js @@ -0,0 +1,43 @@ +RosaABF.factory("ApiProject", ['$resource', function($resource) { + + var ProjectResource = $resource( + '/:owner/:project?format=json', + {owner: '@project.owner.uname', project: '@project.name'}, + { + tags: { + url: '/:owner/:project/tags.json', + method: 'GET', + isArray : false + }, + branches: { + url: '/:owner/:project/branches.json', + method: 'GET', + isArray : false + }, + delete_branch: { + url: '/:owner/:project/branches/:ref.json', + method: 'DELETE', + isArray : false + }, + restore_branch: { + url: '/:owner/:project/branches/:ref.json', // ?sha= + method: 'PUT', + isArray : false + }, + create_branch: { + url: '/:owner/:project/branches.json', // ?new_ref=&from_ref= + method: 'POST', + isArray : false + } + } + ); + + return { + resource : ProjectResource, + singleton : { + project : { + branches_count : 0 + } + } + } +}]); \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/services/pull_request.js b/app/assets/javascripts/angularjs/services/pull_request.js new file mode 100644 index 000000000..d7fff32e5 --- /dev/null +++ b/app/assets/javascripts/angularjs/services/pull_request.js @@ -0,0 +1,26 @@ +RosaABF.factory("ApiPullRequest", ['$resource', function($resource) { + + var PullRequestResource = $resource( + '/:owner/:project/pull_requests/:serial_id.json', + { + owner: '@pull_request.to_ref.project.owner_uname', + project: '@pull_request.to_ref.project.name', + serial_id: '@pull_request.number' + }, + { + update: { + method: 'PUT', + isArray : false + }, + merge: { + url: '/:owner/:project/pull_requests/:serial_id/merge.json', + method: 'PUT', + isArray: false + } + } + ); + + return { + resource : PullRequestResource + } +}]); \ No newline at end of file diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 5a03bbe86..9946b1663 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -9,10 +9,20 @@ //= require_tree ./lib //= require underscore -//= require backbone -//= require backbone_rails_sync -//= require backbone_datalink -//= require backbone/rosa + +//= require js-routes +// require angular +//= require unstable/angular +// require angular-resource +//= require unstable/angular-resource +//= require ng-rails-csrf +//= require angular-i18n +//= require_tree ./angularjs +//= require moment + +// require soundmanager2 +//= require soundmanager2-nodebug-jsmin + //= require_self function disableNotifierCbx(global_cbx) { @@ -55,6 +65,7 @@ $(document).ready(function() { success: function(data){ button.fadeOut('slow').after(data); button.remove(); + updateTime(); } }); return false; @@ -80,4 +91,42 @@ $(document).ready(function() { } $('.md_and_cm code').each(function (code) { CodeMirrorRun(this); }); + + window.updateTime = function () { + $('.datetime_moment').each(function() { + $(this).html(moment($(this).attr('origin_datetime'), 'X').fromNow()); + }); + }; + + updateTime(); + setInterval( updateTime, 15000 ); + + window.updatePagination = function(link) { + var page = parseInt($('.pagination .current').text()); + if (link.hasClass('next_page')) { + page += 1; + } else { + if (link.hasClass('previous_page')) { + page -= 1; + } else { + page = link.text(); + } + } + $('.pagination .current').html(page); + }; + + window.isSearchUser = null; + window.search_items = function(path, fdata, dom) { + if (window.isSearchUser != null) { window.isSearchUser.abort(); } + window.isSearchUser = $.ajax({ + type: 'GET', + url: path, + data: fdata, + success: function(data) { + dom.html(data); + updateTime(); + } + }); + return false; + } }); diff --git a/app/assets/javascripts/backbone/additionals.js.erb b/app/assets/javascripts/backbone/additionals.js.erb deleted file mode 100644 index 36cd3700a..000000000 --- a/app/assets/javascripts/backbone/additionals.js.erb +++ /dev/null @@ -1 +0,0 @@ -Rosa.bootstrapedData.ROLES = <%= Relation::ROLES.to_json %>; diff --git a/app/assets/javascripts/backbone/models/.gitkeep b/app/assets/javascripts/backbone/models/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/assets/javascripts/backbone/models/advisory.js b/app/assets/javascripts/backbone/models/advisory.js deleted file mode 100644 index 74e15fb21..000000000 --- a/app/assets/javascripts/backbone/models/advisory.js +++ /dev/null @@ -1,96 +0,0 @@ -Rosa.Models.Advisory = Backbone.Model.extend({ - defaults: { - id: null, - description: null, - references: null, - update_type: null, - found: false - }, - - initialize: function() { - _.bindAll(this, 'findByAdvisoryID'); - - this.url = '/advisories'; - }, - - findByAdvisoryID: function(id, bl_type, options) { - var self = this; - - var urlError = function() { - throw new Error("A 'url' property or function must be specified"); - }; - - var typeError = function() { - throw new Error("A 'bl_type' must be 'security' or 'bugfix'"); - }; - - var idError = function() { - throw new Error("A 'id' must be a string at least 4 characters long"); - }; - - if ( (typeof(id) != "string") || (id.length < 4) ) { - idError(); - } - - if ( (bl_type == undefined) || (bl_type == null) || ((bl_type != 'security') && (bl_type != 'bugfix')) ) { - typeError(); - } - - options |= {}; - var data = _.extend({ - query: id, - bl_type: bl_type - }, {}); - - var params = _.extend({ - type: 'GET', - dataType: 'json', - beforeSend: function( xhr ) { - var token = $('meta[name="csrf-token"]').attr('content'); - if (token) xhr.setRequestHeader('X-CSRF-Token', token); - - self.trigger('search:start'); - } - }, options); - - if (!params.url) { - params.url = ((_.isFunction(this.url) ? this.url() : this.url) + '/search') || urlError(); - } - - params.data = data; - - var complete = options.complete; - params.complete = function(jqXHR, textStatus) { - //console.log(jqXHR); - - switch (jqXHR.status) { - case 200: - self.set(_.extend({ - found: true - }, JSON.parse(jqXHR.responseText)), {silent: true}); - self.trigger('search:end'); - break - - case 404: - self.set(self.defaults, {silent: true}); - self.trigger('search:end'); - break - - default: - self.set(self.defaults, {silent: true}); - self.trigger('search:failed'); - } - - if (complete) complete(jqXHR, textStatus); - } - - $.ajax(params); - - return this; - - } -}); - -Rosa.Collections.AdvisoriesCollection = Backbone.Collection.extend({ - model: Rosa.Models.Advisory -}); diff --git a/app/assets/javascripts/backbone/models/collaborator.js b/app/assets/javascripts/backbone/models/collaborator.js deleted file mode 100644 index e203bbf36..000000000 --- a/app/assets/javascripts/backbone/models/collaborator.js +++ /dev/null @@ -1,97 +0,0 @@ -Rosa.Models.Collaborator = Backbone.Model.extend({ - paramRoot: 'collaborator', - - defaults: { - id: null, - actor_id: null, - actor_name: null, - actor_type: null, - avatar: null, - actor_path: null, - project_id: null, - role: null, - removed: false - }, - - changeRole: function(r) { - var self = this; - this._prevState = this.get('role'); - this.save({role: r}, - {wait: true, - success: function(model, response) { - self.trigger('sync_success'); - }, - error: function(model, response) { - model.set({role: model._prevState}); - self.trigger('sync_failed'); - } - }); - return this; - }, - setRole: function(r) { - this.set({ role: r }); - return this; - }, - toggleRemoved: function() { - if (this.get('removed') === false) { - this.set({removed: true}); - } else { - this.set({removed: false}); - } - return this; - } -}); - -Rosa.Collections.CollaboratorsCollection = Backbone.Collection.extend({ - model: Rosa.Models.Collaborator, - - initialize: function(coll, opts) { - if (opts === undefined || opts['url'] === undefined) { - this.url = window.location.pathname; - } else { - this.url = opts['url']; - } - this.on('change:removed change:id add', this.sort, this); - }, - comparator: function(m) { - var res = '' - if (m.get('removed') === true) { - res = 0; - } else if (m.isNew()) { - res = 1; - } else { res = 2 } - return res + m.get('actor_name'); - }, - - removeMarked: function() { - var marked = this.where({removed: true}); - marked.forEach(function(el) { - el.destroy({wait: true, silent: true}); - }); - }, - - saveAndAdd: function(model) { - - model.urlRoot = this.url; - var self = this; - model.save({}, { - wait: true, - success: function(m) { - self.add(m.toJSON()); - } - }); - }, - - filterByName: function(term, options) { - if (term == "") return this; - console.log(term); - - var pattern = new RegExp(term, "i"); - - return _(this.filter(function(data) { - console.log(data.get("actor_name")); - console.log(pattern.test(data.get("actor_name"))); - return pattern.test(data.get("actor_name")); - })); - } -}); diff --git a/app/assets/javascripts/backbone/rosa.js b/app/assets/javascripts/backbone/rosa.js deleted file mode 100644 index 9ed5f3096..000000000 --- a/app/assets/javascripts/backbone/rosa.js +++ /dev/null @@ -1,15 +0,0 @@ -//= require_self -//= require ./additionals -//= require_tree ./templates -//= require_tree ./models -//= require_tree ./views -//= require_tree ./routers - -window.Rosa = { - Models: {}, - Collections: {}, - Routers: {}, - Views: {}, - - bootstrapedData: {} -} diff --git a/app/assets/javascripts/backbone/routers/.gitkeep b/app/assets/javascripts/backbone/routers/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/assets/javascripts/backbone/routers/build_lists_advisories_router.js b/app/assets/javascripts/backbone/routers/build_lists_advisories_router.js deleted file mode 100644 index 950db332d..000000000 --- a/app/assets/javascripts/backbone/routers/build_lists_advisories_router.js +++ /dev/null @@ -1,9 +0,0 @@ -Rosa.Routers.BuildListsAdvisoriesRouter = Backbone.Router.extend({ - routes: {}, - - initialize: function() { - this.advisoriesView = new Rosa.Views.BuildListAdvisoriesView({ model: new Rosa.Models.Advisory() }); - - this.advisoriesView.render(); - } -}); diff --git a/app/assets/javascripts/backbone/routers/collaborators_router.js b/app/assets/javascripts/backbone/routers/collaborators_router.js deleted file mode 100644 index b3f12ed69..000000000 --- a/app/assets/javascripts/backbone/routers/collaborators_router.js +++ /dev/null @@ -1,15 +0,0 @@ -Rosa.Routers.CollaboratorsRouter = Backbone.Router.extend({ - routes: {}, - - initialize: function() { - this.collaboratorsCollection = new Rosa.Collections.CollaboratorsCollection(Rosa.bootstrapedData.collaborators, { url: window.location.pathname }); - this.searchCollection = new Rosa.Collections.CollaboratorsCollection(null, { url: window.location.pathname + '/find' }); - this.tableView = new Rosa.Views.CollaboratorsView({ collection: this.collaboratorsCollection }); - this.addView = new Rosa.Views.AddCollaboratorView({ collection: this.searchCollection }); - - this.addView.on('collaborator_prepared', this.collaboratorsCollection.saveAndAdd, this.collaboratorsCollection); - - this.tableView.render(); - this.addView.render(); - } -}); diff --git a/app/assets/javascripts/backbone/templates/.gitkeep b/app/assets/javascripts/backbone/templates/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/assets/javascripts/backbone/templates/collaborators/collaborator.jst.hamljs b/app/assets/javascripts/backbone/templates/collaborators/collaborator.jst.hamljs deleted file mode 100644 index b1ad6b5c2..000000000 --- a/app/assets/javascripts/backbone/templates/collaborators/collaborator.jst.hamljs +++ /dev/null @@ -1,39 +0,0 @@ -%td - %span#niceCheckbox1.nicecheck-main{ style: "background-position: 0px 0px; " } - - if (removed === true) { - %input{ type: 'checkbox', checked: 'checked' } - - } else { - %input{ type: 'checkbox' } - - } - -%td - .img - %img{ src: avatar, alt: 'avatar' } - .forimg - %a{ href: actor_path } - = actor_name - -- var ROLES = Rosa.bootstrapedData.ROLES; - -- for (var i = 0; i < ROLES.length; i++) { -%td - .radio - - var radio_id = id + '_' + ROLES[i]; - - var radio_type = 'role[' + id + ']'; - - if (ROLES[i] === role) { - - if ( removed ) { - %input.niceRadio{type: 'radio', value: ROLES[i], id: radio_id, name: radio_type, disabled: 'disabled', checked: 'checked'} - - } else { - %input.niceRadio{type: 'radio', value: ROLES[i], id: radio_id, name: radio_type, checked: 'checked'} - - }; - - } else { - - if ( removed ) { - %input.niceRadio{type: 'radio', value: ROLES[i], id: radio_id, name: radio_type, disabled: 'disabled' } - - } else { - %input.niceRadio{type: 'radio', value: ROLES[i], id: radio_id, name: radio_type } - - }; - - } - .forradio - %label{ for: radio_id } - = ROLES[i] -- } diff --git a/app/assets/javascripts/backbone/templates/collaborators/searched_collaborator.jst.hamljs b/app/assets/javascripts/backbone/templates/collaborators/searched_collaborator.jst.hamljs deleted file mode 100644 index 7b8a3e86f..000000000 --- a/app/assets/javascripts/backbone/templates/collaborators/searched_collaborator.jst.hamljs +++ /dev/null @@ -1,7 +0,0 @@ -%a - .collaborator - .img - %img{width: '16px', src: avatar, alt: 'avatar'} - .name - = actor_name - .both diff --git a/app/assets/javascripts/backbone/templates/shared/autocomplete_result_empty.jst.hamljs b/app/assets/javascripts/backbone/templates/shared/autocomplete_result_empty.jst.hamljs deleted file mode 100644 index 8ed5f5d10..000000000 --- a/app/assets/javascripts/backbone/templates/shared/autocomplete_result_empty.jst.hamljs +++ /dev/null @@ -1,3 +0,0 @@ -%li.empty_result - %span - Nothing found diff --git a/app/assets/javascripts/backbone/views/.gitkeep b/app/assets/javascripts/backbone/views/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/assets/javascripts/backbone/views/add_collaborator_view.js b/app/assets/javascripts/backbone/views/add_collaborator_view.js deleted file mode 100644 index bdddba49f..000000000 --- a/app/assets/javascripts/backbone/views/add_collaborator_view.js +++ /dev/null @@ -1,92 +0,0 @@ -Rosa.Views.AddCollaboratorView = Backbone.View.extend({ - result_empty: JST['backbone/templates/shared/autocomplete_result_empty'], - - events: { - 'click #add_collaborator_button': 'addCollaborator' - }, - - initialize: function() { - _.bindAll(this, 'getData', 'renderAll', 'onFocus', 'selectItem', 'addCollaborator'); - - this.$el = $('#add_collaborator_form'); - this.$_search_input = this.$('#collaborator_name'); - this.$_image = this.$('.admin-search.withimage img'); - this.$_role = this.$('#role'); - - this.ac = this.$_search_input.autocomplete({ - minLength: 1, - source: this.getData, - focus: this.onFocus, - select: this.selectItem - }); - this.ac.data("autocomplete")._renderItem = this.addOne; - this.ac.data("autocomplete")._renderMenu = this.renderAll; - }, - - render: function() { - return this; - }, - - getData: function(request, response) { - var self = this; - var res = this.collection.fetch({ - data: {term: request.term}, - wait: true, - success: function(collection) { - self.$_image.hide(); - if (collection.length !== 0) { - response(collection.models); - } else { - response([{result_empty: true}]); - } - } - }); - }, - - addOne: function(ul, item) { - var v = new Rosa.Views.SearchedCollaboratorView({ model: item }); - return v.render().$el.appendTo(ul); - }, - - renderAll: function( ul, items ) { - var self = this; - if (items[0]['result_empty'] !== undefined && items[0]['result_empty'] === true) { - ul.removeClass('has_results').append(this.result_empty()); - } else { - ul.addClass('has_results'); - _.each( items, function( item ) { - self.addOne( ul, item ); - }); - var factor = (items.length > 10) ? 10 : items.length; - ul.height(ul.children('li').first() * factor); - } - }, - - onFocus: function( event, ui ) { - this.$_search_input.val(ui.item.get('actor_name')); - return false; - }, - - selectItem: function( event, ui ) { - var model = ui.item; - this.$_image.attr('src', model.get('avatar')).show(); - - this.__selected_item = model; - return false; - }, - - addCollaborator: function(e) { - e.preventDefault(); - var model = this.__selected_item; - - if ( model !== undefined ) { - model.setRole(this.$_role.val()); - this.trigger('collaborator_prepared', model); - this.__selected_item = undefined; - this.$_image.hide(); - this.$_search_input.val(''); - } - return false; - } - -}); diff --git a/app/assets/javascripts/backbone/views/build_list_advisories_view.js b/app/assets/javascripts/backbone/views/build_list_advisories_view.js deleted file mode 100644 index 0b950150f..000000000 --- a/app/assets/javascripts/backbone/views/build_list_advisories_view.js +++ /dev/null @@ -1,215 +0,0 @@ -Rosa.Views.BuildListAdvisoriesView = Backbone.View.extend({ - initialize: function() { - _.bindAll(this, 'showAdvisory', 'showPreview', 'showForm', - 'showSearch', 'hideAll', 'displayStatus', 'processSearch', - 'showInTypeSelect', 'typeSelectChange'); - - this.$el = $('#advisory_block'); - this._$type_select = $('#build_list_update_type'); - this._$publish_button = $('input[type="submit"][name="publish"]'); - - this._$form = this.$('#new_advisory_form'); - this._$preview = this.$('#advisory_preview'); - - this._$search = this.$('#advisory_search_block'); - this._$search_field = this.$('#advisory_search'); - this._$not_found = this.$('#advisory_search_block > .advisory_not_found'); - this._$server_error = this.$('#advisory_search_block > .server_error'); - this._$continue_input = this.$('#advisory_search_block > .continue_input'); - this._search_timer = null; - - this._$selector = this.$('#attach_advisory'); - - this._state_vars = {}; - this._state_vars = _.extend({ - checked_update_type: this._$type_select.val(), - header_text: this._$preview.children('h3').html() - }, this.state_vars); - - this._$selector.on('change', this.showAdvisory); - this._$search_field.on('input keyup', this.processSearch); - - this._$type_select.on('change', this.typeSelectChange); - - this.model.on('search:start', function() { - this._$publish_button.prop({disabled: true}); - }, this); - this.model.on('search:end', this.showPreview, this); - this.model.on('search:failed', this.handleSearchError, this); - }, - - showAdvisory: function(ev) { - this._$publish_button.prop({disabled: false}); - switch (this._$selector.val()) { - case 'no': - this.hideAll(); - this.showInTypeSelect('all'); - break - case 'new': - this.showForm(); - this.showInTypeSelect('advisoriable'); - break - default: - this.showSearch(); - this.showInTypeSelect('advisoriable'); - this._$publish_button.prop({disabled: true}); - } - }, - - typeSelectChange: function(ev) { - switch (this._$selector.val()) { - case 'no': - this._state_vars.checked_update_type = this._$selector.val(); - break - case 'new': - break - default: - this._$search_field.trigger('input'); - } - }, - - showInTypeSelect: function(type) { - var children = this._$type_select.children('option'); - if (type != 'all') { - var visible_ch = children.filter('.' + type); - var sel = children.filter(':selected'); - - children.prop('disabled', true); - visible_ch.prop('disabled', false); - if (sel.prop('disabled')) { - sel.prop('selected', false); - visible_ch.first().prop('selected', true); - } - } else { - children.prop('disabled', false).prop('selected', false); - children.filter('option[value="' + this._state_vars.checked_update_type + '"]').prop('selected', true); - } - }, - - processSearch: function(ev) { - if (ev.type == "keyup") { - if (ev.keyCode != 13) { - return - } else { - ev.preventDefault(); - } - } - - var TIMER_INTERVAL = 500; - - var self = this; - - var timerCallback = function() { - if (self._$search_field.val().length > 3) { - // real search - self.model.findByAdvisoryID(self._$search_field.val(), self._$type_select.val()); - } else { - // hide preview if nothing to show - if (self._$preview.is(':visible')) { - self._$preview.slideUp(); - } - self.displayStatus('found'); - } - }; - - if (this.model.get('advisory_id') == this._$search_field.val()) { - this.showPreview(); - return; - } - // timeout before real AJAX request - clearTimeout(this._search_timer); - this._search_timer = setTimeout(timerCallback, TIMER_INTERVAL); - }, - - showPreview: function(id) { - this._$publish_button.prop({disabled: false}); - if (this._$form.is(':visible')) { - this._$form.slideUp(); - } - var prev = this._$preview; - var adv = this.model; - if (adv.get('found')) { - this._$selector.children('option.advisory_id').val(adv.get('advisory_id')); - - prev.children('h3').html(this._state_vars.header_text + ' ' + adv.get('advisory_id')); - prev.children('.descr').html(adv.get('description')); - prev.children('.refs').html(adv.get('references')); - if (!this._$preview.is(':visible')) { - this._$preview.slideDown(); - } - this.displayStatus('found'); - } else { - if (this._$preview.is(':visible')) { - this._$preview.slideUp(); - } - this._$publish_button.prop({disabled: true}); - this.displayStatus('not_found'); - this._$selector.children('option.advisory_id').val(''); - } - }, - - showForm: function() { - if (this._$preview.is(':visible')) { - this._$preview.slideUp(); - } - if (this._$search.is(':visible')) { - this._$search.slideUp(); - } - if (!this._$form.is(':visible')) { - this._$form.slideDown(); - } - }, - - showSearch: function() { - if (this._$form.is(':visible')) { - this._$form.slideUp(); - } - if (!this._$search.is(':visible')) { - this._$search.slideDown(); - this._$search_field.trigger('input'); - } - }, - - handleSearchError: function() { - this._$publish_button.prop({disabled: true}); - this.displayStatus('error'); - if (this._$preview.is(':visible')) { - this._$preview.slideUp(); - } - if (this._$form.is(':visible')) { - this._$form.slideUp(); - } - }, - - hideAll: function() { - if (this._$preview.is(':visible')) { - this._$preview.slideUp(); - } - if (this._$search.is(':visible')) { - this._$search.slideUp(); - } - if (this._$form.is(':visible')) { - this._$form.slideUp(); - } - }, - - displayStatus: function(st) { - var ELEMS = { - 'found': this._$continue_input, - 'not_found': this._$not_found, - 'error': this._$server_error - }; - - this._$continue_input.hide(); - this._$not_found.hide(); - this._$server_error.hide(); - - ELEMS[st].show(); - }, - - render: function() { - this.showAdvisory(); - return this; - } - -}); diff --git a/app/assets/javascripts/backbone/views/collaborator_view.js b/app/assets/javascripts/backbone/views/collaborator_view.js deleted file mode 100644 index c785baa0e..000000000 --- a/app/assets/javascripts/backbone/views/collaborator_view.js +++ /dev/null @@ -1,69 +0,0 @@ -Rosa.Views.CollaboratorView = Backbone.View.extend({ - template: JST['backbone/templates/collaborators/collaborator'], - tagName: 'tr', - className: 'regular', - - events: { - 'change input[type="radio"]': 'changeRole', - 'change input[type="checkbox"]': 'toggleRemoved' - }, - - initialize: function() { - this.$el.attr('id', 'admin-table-members-row' + this.options.model.get('id') + this.options.model.get('actor_type')); - this.model.on('change', this.render, this); - this.model.on('destroy', this.hide, this); - this.model.on('sync_failed', this.syncError, this); - this.model.on('sync_success', this.syncSuccess, this); - }, - - render: function() { - if (this.model.get('removed')) { - this.$el.addClass('removed'); - } else { - this.$el.removeClass('removed'); - }; - this.$el.html(this.template(this.model.toJSON())); - return this; - }, - - changeRole: function(e) { - this.$('input[type="radio"]').attr('disabled', 'disabled'); - this.model.changeRole(e.target.value); - }, - - toggleRemoved: function(e) { - this.model.toggleRemoved(); - }, - - hide: function() { - this.remove(); - }, - - syncError: function() { - var self = this; - this.$el.addClass('sync_error'); - this.$('td').animate({ - 'background-color': '#FFFFFF' - }, { - duration: 800, - easing: 'easeInCirc', - complete: function() { - self.$el.removeClass('sync_error'); - } - }); - }, - - syncSuccess: function() { - var self = this; - this.$el.addClass('sync_success'); - this.$('td').animate({ - 'background-color': '#FFFFFF' - }, { - duration: 800, - easing: 'easeInCirc', - complete: function() { - self.$el.removeClass('sync_success'); - } - }); - } -}); diff --git a/app/assets/javascripts/backbone/views/collaborators_view.js b/app/assets/javascripts/backbone/views/collaborators_view.js deleted file mode 100644 index cf6aafe84..000000000 --- a/app/assets/javascripts/backbone/views/collaborators_view.js +++ /dev/null @@ -1,57 +0,0 @@ -Rosa.Views.CollaboratorsView = Backbone.View.extend({ - initialize: function() { - _.bindAll(this, 'deleterClick', 'processFilter', 'addOne'); - this.setupDeleter(); - this.setupFilter(); - this.$el = $('#collaborators > tbody'); - this.collection.on('reset', this.render, this); - this.collection.on('add', this.clearFilter, this); - }, - - addOne: function(collaborator) { - var cView = new Rosa.Views.CollaboratorView({ model: collaborator }); - this.$el.append(cView.render().$el); - }, - - render: function() { - this.clearFilter(); - this.$el.empty(); - this.collection.forEach(this.addOne, this); - this._$deleter.show(); - return this; - }, - - renderList: function(list) { - this.$el.empty(); - - list.each(this.addOne); - return this; - }, - - setupDeleter: function() { - this._$deleter = $('#collaborators_deleter'); - this._$deleter.on('click.deleter', this.deleterClick); - this._$deleter.attr('title', 'Remove selected rows'); - }, - - deleterClick: function() { - this.collection.removeMarked(); - }, - - setupFilter: function() { - this._$filter = $('#collaborators thead input[type="text"]'); - this._$filter.on('keyup', this.processFilter); - this.clearFilter(); - }, - - clearFilter: function() { - this._$filter.val(''); - }, - - processFilter: function() { - var term = this._$filter.val(); - var list = this.collection.filterByName(term, {excludeRemoved: true}); - console.log(list); - this.renderList(list); - } -}); diff --git a/app/assets/javascripts/backbone/views/project_modify_view.js b/app/assets/javascripts/backbone/views/project_modify_view.js deleted file mode 100644 index 2c11de294..000000000 --- a/app/assets/javascripts/backbone/views/project_modify_view.js +++ /dev/null @@ -1,30 +0,0 @@ -Rosa.Views.ProjectModifyView = Backbone.View.extend({ - initialize: function() { - _.bindAll(this, 'checkboxClick'); - - this.$checkbox_wrapper = $('#niceCheckbox1'); - this._$checkbox = this.$checkbox_wrapper.children('#project_is_package').first(); - this.$maintainer_form = $('#maintainer_form'); - this.$publish_form = $('#publish_form'); - this._$publish_checkbox = this.$publish_form.find('#project_publish_i686_into_x86_64').first(); - - this.$checkbox_wrapper.on('click', this.checkboxClick); - }, - - checkboxClick: function() { - if (this._$checkbox.is(':checked')) { - this.$maintainer_form.slideDown(); - this.$publish_form.slideDown(); - } else { - this.$maintainer_form.slideUp(); - this.$publish_form.slideUp(); - if (this._$publish_checkbox.is(':checked')) { - changeCheck(this.$publish_form.find('.niceCheck-main')); - } - } - }, - - render: function() { - this.checkboxClick(); - } -}); diff --git a/app/assets/javascripts/backbone/views/searched_collaborator_view.js b/app/assets/javascripts/backbone/views/searched_collaborator_view.js deleted file mode 100644 index 05911be42..000000000 --- a/app/assets/javascripts/backbone/views/searched_collaborator_view.js +++ /dev/null @@ -1,12 +0,0 @@ -Rosa.Views.SearchedCollaboratorView = Backbone.View.extend({ - template: JST['backbone/templates/collaborators/searched_collaborator'], - tagName: 'li', - className: 'item', - - render: function() { - this.$el.empty(); - this.$el.data( "item.autocomplete", this.model ) - .append(this.template(this.model.toJSON())); - return this; - } -}) diff --git a/app/assets/javascripts/extra/autocomplete-form.js b/app/assets/javascripts/extra/autocomplete-form.js new file mode 100644 index 000000000..9d92651d7 --- /dev/null +++ b/app/assets/javascripts/extra/autocomplete-form.js @@ -0,0 +1,46 @@ +$(document).ready(function() { + + $(document).on('click', '.autocomplete-form .button.add', function() { + var form = $(this).parent(); + var field = form.attr('field'); + var subject = $('#' + field + '_field'); + if (!subject.val()) { return false; } + var name = form.attr('subject_class') + '[' + field + ']' + '[]'; + var path = $('#' + field + '_field_path').val(); + var label = $('#' + field + '_field_label').val(); + + addDataToAutocompleteForm(form, path, label, name, subject.val()); + form.find('.autocomplete').val(''); + return false; + }); + + $(document).on('click', '.autocomplete-form .delete', function() { + $(this).parent().parent().remove(); + }); + + $('.autocomplete-form .dialog').dialog({ + autoOpen: false, + resizable: false, + width: 500 + }); + + $(document).on('click', '.autocomplete-form .icon-question-sign', function() { + var field = $(this).parent().attr('field'); + var dialog = $('#' + field + '_dialog'); + if (dialog.is(':visible')) { dialog.dialog('close'); } else { dialog.dialog('open'); } + }); + +}); + +function addDataToAutocompleteForm(form, path, label, name, value) { + var tr = '' + + '' + + '' + label + '' + + '' + + '' + + '' + + ' ' + + '' + + ''; + form.find('table tbody').append($(tr)); +} diff --git a/app/assets/javascripts/extra/build_list.js b/app/assets/javascripts/extra/build_list.js index 23ade8199..dd24f24ea 100644 --- a/app/assets/javascripts/extra/build_list.js +++ b/app/assets/javascripts/extra/build_list.js @@ -1,25 +1,31 @@ $(document).ready(function() { - // TODO: Refactor this handler!! It's too complicated. - $('#build_list_save_to_repository_id').change(function() { - var selected_option = $(this).find("option:selected"); + var new_form = $('#new_form'); + var ownership_btn = $('.btn.ownership'); + var perpage_btn = $('.btn.per_page'); + // TODO: Refactor this handler!! It's too complicated. + $(document).on('change', '#build_list_save_to_repository_id', function(){ + var selected_option = $(this).find("option:selected"); var platform_id = selected_option.attr('platform_id'); var rep_name = selected_option.text().match(/[\w-]+\/([\w-]+)/)[1]; var build_platform = $('#build_for_pl_' + platform_id); var all_repositories = $('.all_platforms input'); all_repositories.removeAttr('checked'); - var use_save_to_repository = $('#build_list_use_save_to_repository'); var auto_create_container = $('#build_list_auto_create_container'); + var extra_repos = $('.autocomplete-form.extra_repositories'); + updateExtraReposAndBuildLists(platform_id); + updatedDefaultArches(selected_option); + $('.autocomplete-form table tbody').empty(); if (build_platform.size() == 0) { all_repositories.removeAttr('disabled'); - use_save_to_repository.removeAttr('disabled'); auto_create_container.removeAttr('checked'); + addPersonalPlatformToExtraRepos(selected_option, extra_repos); + extra_repos.show(); } else { - updateExtraReposAndBuildLists(); - use_save_to_repository.attr('disabled', 'disabled').attr('checked', 'checked'); all_repositories.attr('disabled', 'disabled'); + extra_repos.hide(); var parent = build_platform.parent(); parent.find('input').removeAttr('disabled'); parent.find('input[rep_name="main"]').attr('checked', 'checked'); @@ -35,34 +41,12 @@ $(document).ready(function() { build_list_auto_publish.removeAttr('checked').attr('disabled', 'disabled'); auto_create_container.attr('checked', 'checked'); } - - var path = '/build_lists/autocomplete_to_extra_repos_and_builds?platform_id=' + platform_id; - $('#extra_repos').attr('data-autocomplete', path); }); - $('#build_list_save_to_repository_id').trigger('change'); + if($('#from_build_list_id').size() == 0) { + $('#build_list_save_to_repository_id').trigger('change'); + } - $('#extra-repos-and-build-lists #add').click(function() { - updateExtraReposAndBuildLists(); - return false; - }); - - $(document).on('click', '#extra-repos-and-build-lists .delete', function() { - $(this)[0].parentElement.parentElement.remove(); - }); - - $('#extra-repos-and-build-lists-dialog').dialog({ - autoOpen: false, - resizable: false, - width: 500 - }); - - $('#extra-repos-and-build-lists .icon-question-sign').click(function() { - var dialog = $('#extra-repos-and-build-lists-dialog'); - if (dialog.is(':visible')) { dialog.dialog('close'); } else { dialog.dialog('open'); } - }); - - var ownership_btn = $('.btn.ownership'); ownership_btn.click(function() { ownership_btn.removeClass('active'); $('#filter_ownership').val($(this).val()); @@ -70,7 +54,6 @@ $(document).ready(function() { return false; }); - var perpage_btn = $('.btn.per_page'); perpage_btn.click(function() { perpage_btn.removeClass('active'); $('#per_page').val($(this).val()); @@ -95,16 +78,54 @@ $(document).ready(function() { dateFormat: 'dd/mm/yy', showButtonPanel: true }); + + $(document).on('click', '#owner_filter_build_lists, #status_filter_build_lists', function(){ + $('#datatable').dataTable().fnDraw(); + }); + + $(document).on('click', '#clone_build_list', function() { + $.ajax({ + type: 'GET', + url: $(this).attr('href') + '&show=inline', + success: function(data){ + new_form.html(data); + $(document).scrollTop(new_form.offset().top); + }, + error: function(data){ + alert('error') // TODO remove + } + }); + return false; + }); }); - -function updateExtraReposAndBuildLists() { - $.get("/build_lists/update_extra_repos_and_builds", $('#new_build_list').serialize()) - .done(function(data) { - $("#extra-repos-and-build-lists table tbody").html(data); +function updatedDefaultArches(selected_option) { + $('input[name="arches[]"]').removeAttr('checked'); + _.each(selected_option.attr('default_arches').split(' '), function(id){ + $('#arches_' + id).attr('checked', 'checked'); }); } +function updateExtraReposAndBuildLists(save_to_platform_id) { + $.each($('.autocomplete-form'), function() { + var form = $(this); + var path = form.attr('path') + '?platform_id=' + save_to_platform_id; + form.find('.autocomplete').attr('data-autocomplete', path); + }); +} + +function addPersonalPlatformToExtraRepos(selected_option, extra_repos) { + var default_value = extra_repos.find('div[label="' + selected_option.text() + '"]'); + if (default_value.length == 0) { return; } + addDataToAutocompleteForm( + extra_repos, + default_value.attr('path'), + default_value.attr('label'), + default_value.attr('name'), + default_value.attr('value') + ); +} + function setBranchSelected(selected_option) { var pl_name = selected_option.text().match(/([\w-.]+)\/[\w-.]+/)[1]; var bl_version_sel = $('#build_list_project_version'); @@ -117,4 +138,4 @@ function setBranchSelected(selected_option) { // hack for FF to force render of select box. bl_version_sel[0].innerHTML = bl_version_sel[0].innerHTML; } -} \ No newline at end of file +} diff --git a/app/assets/javascripts/extra/flash_notifies.js b/app/assets/javascripts/extra/flash_notifies.js index 52c9fbf53..e9e2522a5 100644 --- a/app/assets/javascripts/extra/flash_notifies.js +++ b/app/assets/javascripts/extra/flash_notifies.js @@ -12,7 +12,10 @@ $(document).ready(function() { } $('#close-alert').click(function () { - setCookie("flash_notify_hash", FLASH_HASH_ID, FLASH_EXPIRES_AT); + var exdate=new Date(); + exdate.setDate(exdate.getDate() + 365); + var expires="expires="+exdate.toUTCString(); + setCookie("flash_notify_hash", FLASH_HASH_ID, expires); }); }); diff --git a/app/assets/javascripts/extra/fork.js b/app/assets/javascripts/extra/fork.js new file mode 100644 index 000000000..1c56b269f --- /dev/null +++ b/app/assets/javascripts/extra/fork.js @@ -0,0 +1,19 @@ +$(document).ready(function() { + var fork_name = $('#fork_name'); + var forks_path = $('#possible_forks_path'); + + fork_name.keyup(function(){ + $.ajax({ + type: 'GET', + url: forks_path.val(), + data: 'name=' + fork_name.val(), + success: function(data){ + $('#forks_list').html(data); + }, + error: function(data){ + alert('error'); // TODO remove + } + }); + }); + +}); \ No newline at end of file diff --git a/app/assets/javascripts/extra/log-wrapper.js b/app/assets/javascripts/extra/log-wrapper.js index 0204e9275..d449d233c 100644 --- a/app/assets/javascripts/extra/log-wrapper.js +++ b/app/assets/javascripts/extra/log-wrapper.js @@ -50,7 +50,7 @@ function initLogWrapper() { var hScroll = l.scrollLeft; var onBottom = Math.abs((l.clientHeight + vScroll - l.scrollHeight)) < getLineHeight(l); - $logCont.text(data.log); + CodeMirror.runMode(data.log.replace(/&/gi, '&'), "text/x-sh", document.getElementById("output")); $logCont.scrollLeft(hScroll); $logCont.scrollTop((onBottom || first_open) ? l.scrollHeight - l.clientHeight : vScroll); @@ -95,7 +95,7 @@ function initLogWrapper() { $autoload.on('change', reloadChange); $('#word_wrap').on('change', function() { - $logCont.attr({'wrap': ($(this).is(':checked')) ? 'soft' : 'off'}); + $logCont.css('white-space', ($(this).is(':checked')) ? 'normal' : 'pre'); }); $('#reload_interval').on('change', function() { @@ -104,8 +104,5 @@ function initLogWrapper() { t = setInterval($(this).val()); } }); - $('#load_lines').on('change', function() { - $logCont.data('load_lines', $(this).val()); - }).trigger('change'); loadLog(); } \ No newline at end of file diff --git a/app/assets/javascripts/extra/mass_build.js b/app/assets/javascripts/extra/mass_build.js index 177cea13f..de51192b3 100644 --- a/app/assets/javascripts/extra/mass_build.js +++ b/app/assets/javascripts/extra/mass_build.js @@ -1,5 +1,5 @@ $(document).ready(function() { - var projects_list = $('.form.mass_build #projects_list'); + var projects_list = $('.form.mass_build #mass_build_projects_list'); var repositories = $(".form.mass_build .left input:checkbox"); repositories.click(function(){ if (this.checked){ @@ -26,4 +26,12 @@ $(document).ready(function() { .attr('checked', false); } }); + + var autocomplete_repos = $('.autocomplete-form.extra_repositories #extra_repositories'); + var default_autocomplete_path = $('#autocomplete_extra_repos_path').val(); + $('#mass_build_build_for_platform_id').on('change', function() { + var path = default_autocomplete_path + '&build_for_platform_id=' + $(this).val(); + autocomplete_repos.attr('data-autocomplete', path); + }); + $('#mass_build_build_for_platform_id').trigger('change'); }); diff --git a/app/assets/javascripts/extra/profile.js b/app/assets/javascripts/extra/profile.js new file mode 100644 index 000000000..668632151 --- /dev/null +++ b/app/assets/javascripts/extra/profile.js @@ -0,0 +1,43 @@ +$(document).ready(function() { + var profile_table = $('.profile-table'); + var profile_path = $('#profile_path').text(); + var profile_vis_buttons = $('.profile-content .span12.sub-menu nav a'); + var profile_search_field = $('.profile-content .search #query_projects'); + + var load_profile_projects = function (page_number) { + var visibility = $('.profile-content .span12.sub-menu nav a.active').hasClass('public-projects') ? 'open' : 'hidden'; + var search = profile_search_field.val(); + page = page_number || $('.pagination .current').text(); + $.ajax({ + type: 'GET', + url: profile_path, + data: {visibility: visibility, search: search, page: page}, + success: function(data){ + profile_table.html(data); + updateTime(); + }, + error: function(data){ + alert('error') // TODO remove + } + }); + return false; + } + + profile_vis_buttons.live('click', function () { + profile_vis_buttons.toggleClass('active'); + return load_profile_projects(); + }); + + $(document).on('click','.profile-table .pagination a', function(){ + updatePagination($(this)); + return load_profile_projects(); + }); + + $('#query_projects').on('keyup', function() { + var visibility = $('.profile-content .span12.sub-menu nav a.active').hasClass('public-projects') ? 'open' : 'hidden'; + var search = profile_search_field.val(); + data = {visibility: visibility, search: search}; + return search_items(profile_path, data, profile_table); + }); +}); + diff --git a/app/assets/javascripts/extra/pull.js b/app/assets/javascripts/extra/pull.js index b802f251a..ff1d31286 100644 --- a/app/assets/javascripts/extra/pull.js +++ b/app/assets/javascripts/extra/pull.js @@ -2,17 +2,20 @@ $(document).ready(function() { var upd_action = $('#update_action').val(); var form = $('#new_pull_request'); + function updatePull(event, data) { + form.attr('action', upd_action) + .attr('method', 'get'); + $('#update_pull').fadeIn('fast'); + $('#create_pull').fadeOut('fast'); + }; + $('#pull_request_to_project').on('autocompleteselect', function(event, data){ var ref = $('#to_ref'); ref.parent().load(data.item.get_refs_url+' #to_ref', {"selected": ref.val()}); }); - $('#pull_request_to_project, input#to_refs, input#from_refs').on('autocompleteselect', function(event, data){ - form.attr('action', upd_action) - .attr('method', 'get'); - $('#update_pull').fadeIn('fast'); - $('#create_pull').fadeOut('fast'); - }); + $('#pull_request_to_project').on('autocompleteselect', updatePull); + $('select#to_ref, select#from_ref').on('change', updatePull); $('#pull_tabs a').on('click', function (e) { var href = $(this).attr("href"); diff --git a/app/assets/javascripts/extra/tracker.js b/app/assets/javascripts/extra/tracker.js index 7acee8c69..072e1fcdf 100644 --- a/app/assets/javascripts/extra/tracker.js +++ b/app/assets/javascripts/extra/tracker.js @@ -18,18 +18,7 @@ $(document).ready(function() { }); $("#table1.issues-table .pagination a").live('click', function() { - var a = $(this); - var page = parseInt($('.pagination .current').text()); - if (a.hasClass('next_page')) { - page += 1; - } else { - if (a.hasClass('previous_page')) { - page -= 1; - } else { - page = a.text(); - } - } - $('.pagination .current').html(page); + updatePagination($(this)); return send_index_tracker_request('GET'); }); @@ -107,6 +96,7 @@ $(document).ready(function() { success: function(data){ $('article').html(data); $(".niceRadio").each(function() { changeRadioStart(jQuery(this)) }); + updateTime(); }, error: function(data){ alert('error') // TODO remove @@ -115,22 +105,15 @@ $(document).ready(function() { return false; }; - var isSearchUser = null; $('#search_user').on('keyup', function() { - if (isSearchUser != null) { isSearchUser.abort(); } - isSearchUser = $.ajax({ - type: 'GET', - url: $('#search_user_path').attr('path'), - data: $(this).serialize(), - success: function(data){ - $('#manage_issue_users_list').html(data); - } - }); - return false; + path = $('#search_user_path').attr('path'); + data = $(this).serialize(); + dom = $('#manage_issue_users_list'); + return search_items(path, data, dom); }); - $('#assigned-popup .header .icon-remove-circle').live('click', function() { - $('#assigned-popup').hide(); + $('.users-search-popup .header .icon-remove-circle').live('click', function() { + $('.users-search-popup').hide(); }); $('#assigned-container .icon-share').live('click', function() { diff --git a/app/assets/javascripts/login.js b/app/assets/javascripts/login.js index 22dc39225..34f328c64 100644 --- a/app/assets/javascripts/login.js +++ b/app/assets/javascripts/login.js @@ -32,4 +32,6 @@ $(document).ready(function() { } return true; }); + + $('#recaptcha_response_field').val(''); }); diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 432f4a399..f54b1889b 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -3,4 +3,5 @@ @import "design/git"; @import "design/common"; @import "design/custom"; -@import "design/build_lists_monitoring"; \ No newline at end of file +@import "design/build_lists_monitoring"; +@import "design/profile"; diff --git a/app/assets/stylesheets/design/build_lists_monitoring.scss b/app/assets/stylesheets/design/build_lists_monitoring.scss index 4b1192ff0..6ff2fa878 100644 --- a/app/assets/stylesheets/design/build_lists_monitoring.scss +++ b/app/assets/stylesheets/design/build_lists_monitoring.scss @@ -120,9 +120,7 @@ article .all .top form .floatright a img { padding: 4px 12px; margin-bottom: 0; line-height: 20px; - color: #333333; text-align: center; - text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); vertical-align: middle; cursor: pointer; background-color: #f5f5f5; @@ -148,7 +146,6 @@ article .all .top form .floatright a img { /* bootstrap 3190*/ .btn:hover, .btn:active, .btn.active, .btn.disabled, .btn[disabled] { - color: #333333; background-color: #e6e6e6; } diff --git a/app/assets/stylesheets/design/custom.scss b/app/assets/stylesheets/design/custom.scss index 078a44535..c94067c43 100644 --- a/app/assets/stylesheets/design/custom.scss +++ b/app/assets/stylesheets/design/custom.scss @@ -75,7 +75,10 @@ header menu ul li a { td.actions ul { float: right; } - .text { font-size: 12px; } + .text { + font-size: 12px; + padding-top: 2px; + } td { border-bottom: 1px solid #a9c6dd; padding: 0 20px; @@ -170,7 +173,7 @@ article div.activity .top div.text .date { article div.activity div.fulltext { font-size: 12px; - padding-top: 10px; + padding-top: 5px; display: block; } @@ -571,6 +574,7 @@ table.tablesorter tr td.buttons a span.delete, span.delete { background: image-url('x.png') no-repeat 0 0 transparent; width: 12px; + height: 12px; display: inline-block; cursor: pointer; } @@ -735,11 +739,15 @@ table.tablesorter.list-users th.th4 { width: 50px; } +table.tablesorter.tokens th.th2 { + width: 200px; +} + div.admin-role { padding-right: 10px; } -article a.right_floated { +.right_floated { float: right; } @@ -886,12 +894,11 @@ ul.ui-autocomplete.has_results { float: left; padding-top: 6px; } -#add_collaborator_form div.img { - width: 25px; - height: 25px; -} - -#add_collaborator_form div.img img { +#collaborators { + .img, img { + width: 25px; + height: 25px; + } } #add_collaborator_form .admin-role .lineForm { @@ -986,6 +993,7 @@ textarea.placeholder { } .tab-content.pull_diff_fix { + overflow: inherit; display: block; } @@ -1003,12 +1011,12 @@ form.mass_build input[type="checkbox"] { height: 11px; } -div#new_advisory_form, +/*div#new_advisory_form, div#advisory_preview, div#advisory_search_block, div#advisory_search_block div.info { display: none; -} +}*/ div#advisory_search_block { padding-bottom: 15px; @@ -1184,6 +1192,13 @@ form.mass_build { } } +#new_build_list section.right { + width: 300px; + margin-right: 150px; +} + +.full-size { width: 100%; } + .toggle { overflow: hidden; padding-top: 10px; @@ -1456,18 +1471,18 @@ div.reloader { float: right; } -div.log-wrapper { +.log-wrapper { width: 565px; - div.log-header { + .log-header { margin-bottom: 10px; - div.text-wrap, span { + .text-wrap, span { height: 16px; margin: 0; padding: 0; } - div.text-wrap { + .text-wrap { width: 545px; float: left; @@ -1491,13 +1506,13 @@ div.log-wrapper { } } - div.log-body { + .log-body { - div.reloader, .log { + .reloader, .log { border: 1px solid #ccc; } - div.reloader { + .reloader { padding: 5px 10px; border-bottom: none; border-radius: 5px 5px 0 0; @@ -1522,15 +1537,6 @@ div.log-wrapper { border-left: 1px solid #ccc; } } - - tr.bottom { - td { - padding-left: 5px; - } - td.first { - border-top: 1px solid #ccc; - } - } } } @@ -1538,6 +1544,7 @@ div.log-wrapper { background: #f8f8f8; height: 500px; width: 543px; + white-space: pre; min-width: 543px; border-radius: 5px 0 5px 5px; overflow: auto; @@ -1565,7 +1572,7 @@ table.tablesorter.platform-maintainers.static-search thead tr.search th input[ty .md_and_cm { overflow: auto; - margin: 10px 0 10px; + margin: 5px 0 0; padding: 5px; border-radius: 4px 4px 4px 4px; background: #FFF; @@ -1598,8 +1605,7 @@ table.tablesorter.platform-maintainers.static-search thead tr.search th input[ty } p { - margin-top: 10px; - + margin-top: 10px !important; } } @@ -1788,11 +1794,6 @@ table#myTable thead tr.search th form.button_to div input { border: 1px solid #DDDDDD; } -#extra-repos-and-build-lists { - width: 330px; - .actions { width: 15px; } -} - .semi { opacity: 0.5; } @@ -1843,18 +1844,23 @@ table#myTable thead tr.search th form.button_to div input { margin-left: 10px; } -#extra-repos-and-build-lists { + +.autocomplete-form { width: 330px; .actions { width: 15px; } h3 { display: inline-block; } + .tablesorter { + width: auto; + min-width: 250px; + } } -#extra-repos-and-build-lists-dialog { +#extra_repositories_dialog, #extra_build_lists_dialog { font-size: 12px; text-align: left; p { margin: 0; } } -#ui-dialog-title-extra-repos-and-build-lists-dialog { +#ui-dialog-title-extra_repositories_dialog, #ui-dialog-title-extra_build_lists_dialog { font-size: 14px; } @@ -1909,8 +1915,17 @@ table#myTable thead tr.search th form.button_to div input { } } +.activity .state { + float: right; + padding: 3px 10px; + margin: 3px 0; + font-size: 12px; + color: white; + border-radius: 2px; +} + article .activity .top { - + .created { margin-left: 50px; span, a { @@ -1933,7 +1948,7 @@ article .activity .top { margin: 10px -7px; border-left: none; border-right: none; - } + } } #assigned-container { @@ -1955,8 +1970,7 @@ article .activity .top { } } - -#assigned-popup { +.users-search-popup { z-index: 1001; position: absolute; margin: 5px 0 0 130px; @@ -1990,7 +2004,7 @@ article .activity .top { .edit_assignee { display: none; } - #manage_issue_users_list { + .list { overflow-y: auto; overflow-x: hidden; max-height: 280px; @@ -2007,3 +2021,101 @@ article .activity .top { } } } + +.cm-s-default.md_and_cm p img { + max-width: 800px; +} + +.tablesorter.project { + .th1 { + width: auto; + } + th { text-align: center; } +} + +.activity-tabs { + margin: 10px 0 0 0; + background: none; + -webkit-box-shadow: 0; + -moz-box-shadow: 0; + box-shadow: 0; +} + +.build-list .button { + margin: 2px 0; +} + +#contents { + .path { + a { + margin-right: 20px; + } + .text { + padding: 5px 10px; + background-color: #dcecfa; + float: left; + height: 14px; + } + .arrow-right { + content: ''; + width: 0; + height: 0; + border: 12px solid transparent; + border-left: 12px solid #dcecfa; + float: left; + } + } + +} + +table tbody { + td.build-list-statuses { + background: #FFF; + + .status { + float: left; + border: 1px solid #DDD; + margin: 1px 2px; + } + .error { background: #fedede; } + .success { background: #e3edb7; } + .warning { background: #FCF8E3; } + .nocolor { background: #FFF; } + .expand { + position: absolute; + margin: -9px 0 0 -23px; + border: 1px solid #DDD; + border-right-color: #FFF; + padding: 1px; + cursor: pointer; + } + } + tr.group-start td { + border-top: 2px solid #125687; + } + tr.group-end td { + border-bottom: 2px solid #125687; + } +} + +#will_paginate .pagination { + div { + display: inline-block; + margin: 0 2px; + } + a { + text-decoration: none; + } + .disabled a { + color: #292929; + cursor: default; + } +} + +.fork_name { + height: 20px; + margin: 0 0 5px 10px; + width: 400px; + padding: 0 5px; + margin:auto; +} diff --git a/app/assets/stylesheets/design/main.scss b/app/assets/stylesheets/design/main.scss index d2742971c..29beb1cbf 100644 --- a/app/assets/stylesheets/design/main.scss +++ b/app/assets/stylesheets/design/main.scss @@ -1514,32 +1514,30 @@ div.fork p { /* Project main page */ -table.tablesorter.project .th1 { - width: 130px; - /*padding-left: 17px;*/ -} +.tablesorter.project { -table.tablesorter.project .th2 { - width: 110px; - /*padding-left: 17px;*/ -} - -table.tablesorter.project .th3 { - width: 450px; -} - -table.tablesorter.project .th4 { - /*padding-left: 17px;*/ -} - -table.tablesorter.project div.name { - float: left; - margin-top: 0px; -} - -table.tablesorter.project div.pic { - float: left; - padding-right: 5px; + .th1 { + width: 130px; + /*padding-left: 17px;*/ + } + .th2 { + width: 110px; + /*padding-left: 17px;*/ + } + .th3 { + width: 450px; + } + .th4 { + /*padding-left: 17px;*/ + } + .name { + float: left; + margin-top: 0px; + } + .pic { + float: left; + padding-right: 5px; + } } a.files-see { @@ -1674,49 +1672,49 @@ table.tablesorter.tracker td.td0{ padding-right: 0px; } +table.tablesorter.tracker .td3.td3-pull { + min-width: 130px; +} + table.tablesorter.tracker td.td3{ - min-width: 110px; padding-top: 10px; + min-width: 110px; + .code { + height: 23px; + width: 15px; + background: #FFF; + color: #009fe3; + border: 1px solid #83d1f2; + font-size: 16px; + padding: 5px 0px 0px 5px; + margin-right: 3px; + cursor: pointer; + } + .avatar { + margin-right: 8px; + cursor: pointer; + } + .answers { + height: 28px; + background: #FFF; + color: #009fe3; + border: 1px solid #83d1f2; + font-size: 16px; + cursor: pointer; + .pic { + float: left; + margin: 2px; + margin-top: 6px; + } + .count { + float: left; + margin: 4px 5px 0px 2px; + } + } } -table.tablesorter.tracker td.td3 div.code { - height: 23px; - width: 15px; - background: #FFF; - color: #009fe3; - border: 1px solid #83d1f2; - font-size: 16px; - padding: 5px 0px 0px 5px; +.tablesorter.tracker .td3 > a { float: left; - margin-right: 3px; - cursor: pointer; -} - -table.tablesorter.tracker td.td3 div.avatar { - float: left; - margin-right: 8px; - cursor: pointer; -} - -table.tablesorter.tracker td.td3 div.answers { - height: 28px; - background: #FFF; - color: #009fe3; - border: 1px solid #83d1f2; - font-size: 16px; - float: left; - cursor: pointer; -} - -table.tablesorter.tracker td.td3 div.answers div.pic { - float: left; - margin: 2px; - margin-top: 6px; -} - -table.tablesorter.tracker td.td3 div.answers div.count { - float: left; - margin: 4px 5px 0px 2px; } table.tablesorter.tracker div.smalltext { diff --git a/app/assets/stylesheets/design/profile.scss b/app/assets/stylesheets/design/profile.scss new file mode 100644 index 000000000..c38277c3d --- /dev/null +++ b/app/assets/stylesheets/design/profile.scss @@ -0,0 +1,145 @@ +.row { + .span3.profile { + .avatar { + float: left; + width: 81px; + height: 81px; + } + .base_info { + float: left; + width: 134px; + h3 { + margin: 0 0 0 10px; + } + p { + height: 35px; + margin-left: 10px; + } + a { + margin: 16px 0 0 10px; + } + } + p.first { + margin-top: 10px; + } + p.info { + max-width: 220px; + } + } +} +hr.profile_line { + margin: 20px 0; + width: 865px; +} +.profile-content { + border: 3px solid #D4D4D4; + .search { + border: 2px solid #D4D4D4; + float: left; + margin: 15px 10px; + width: 837px; + .pic { + background: url("/assets/search-button.png") repeat scroll 0 0 transparent; + float: left; + height: 22px; + width: 24px; + } + .field { + float: left; + margin: -1px 0 0; + width: 750px; + input { + background: none repeat scroll 0 0 transparent; + border: medium none; + font-family: Arial; + font-size: 12px; + height: 18px; + padding: 2px 0 0; + width: 700px; + } + input.gray { + color: #CFCFCF; + } + input.black { + color: #333333; + } + } + } + table { + border: none; + border-collapse:collapse; + margin: 0 9px 10px 9px; + width: 844px; + th { + padding-left: 10px; + width: 411px; + .project-link { + margin-top: 5px; + float: left; + } + } + tr.odd { + } + tr.even { + background: #EDEDED; + } + .row-fluid { + max-height: 16px; + .span3 { + font-size: 10px; + font-weight: normal; + max-height: 16px; + min-height: 16px; + } + .span3.datetime_moment { + margin-right: 15px; + color: gray; + } + } + } + .span12.content { + background: url(/assets/bg_blue.png); + height: 30px; + margin-bottom: 0px; + nav { + ul { + list-style: none; + padding-left: 0; + margin: 4px 0 0 5px; + li { + text-decoration: none; + padding: 0 10px 6px 0; + a { + color: white; + font-weight: bold; + font-size: 14px; + padding: 0 10px 9px 10px; + } + a.active { + background: image-url("profile-hover.png") repeat-x scroll 0 100% transparent; + } + } + } + } + } + .span12.sub-menu { + height: 30px; + background: #EDEDED; + margin: 0; + box-shadow: none; + padding-left: 0px; + nav { + ul { + list-style: none; + padding: 0; + margin: 6px 0 0 5px; + a { + padding: 0 10px 9px 10px; + } + a.active { + background: image-url("profile-hover.png") repeat-x scroll 0 100% transparent; + } + } + } + } +} diff --git a/app/assets/stylesheets/devise/registration.scss b/app/assets/stylesheets/devise/registration.scss index 5d7458f9b..ff51b72d0 100644 --- a/app/assets/stylesheets/devise/registration.scss +++ b/app/assets/stylesheets/devise/registration.scss @@ -1,94 +1,93 @@ html, body { - margin: 0; - padding: 0; - font-family: Tahoma, Geneva, Helvetica, sans-serif; - color: #565667; - background: #1f60a1 image-url("bg.png") repeat-x; - min-width: 940px; - min-height: 300px; - text-align: center; - height: 100%; + margin: 0; + padding: 0; + font-family: Tahoma, Geneva, Helvetica, sans-serif; + color: #565667; + background: #1f60a1 image-url("bg.png") repeat-x; + min-width: 940px; + min-height: 300px; + text-align: center; + height: 100%; } header, section, footer, aside, nav, article, menu { - display: block; + display: block; } input[type="text"]:focus { outline: none; } - + input[type="password"]:focus { outline: none; } - + input:focus { outline: none; } - + select:focus { outline: none; } a img { border: none; } - + .wrap { - width: 940px; - margin: 0 auto; - text-align: center; - min-height: 95%; + width: 940px; + margin: 0 auto; + text-align: center; + min-height: 95%; } .both { - clear: both; + clear: both; } - /* Header */ header div.logo { - background: image-url("logo.png") no-repeat 50% 100%; - height: 89px; - width: 233px; - margin: 0 auto; - text-align: center; - padding-top: 12%; + background: image-url("logo.png") no-repeat 50% 100%; + height: 89px; + width: 233px; + margin: 0 auto; + text-align: center; + padding-top: 12%; } header div.text { - color: #FFF; - font-size: 28px; - width: 800px; - text-align: left; - margin-left: 90px; - margin-top: 50px; - font-family: Arial; - text-shadow: 0px 1px 1px #000000; - filter: dropshadow(color=#000000, offx=0, offy=1); - padding-left: 0px; + color: #FFF; + font-size: 28px; + width: 800px; + text-align: left; + margin-left: 90px; + margin-top: 50px; + font-family: Arial; + text-shadow: 0px 1px 1px #000000; + filter: dropshadow(color=#000000, offx=0, offy=1); + padding-left: 0px; } .niceCheck { - width: 17px; - height: 17px; - display: inline-block; - cursor: pointer; - background: image-url("checkbox.png"); + width: 17px; + height: 17px; + display: inline-block; + cursor: pointer; + background: image-url("checkbox.png"); } .niceCheck input { - display: none; + display: none; } /* Content */ article { - width: 380px; - height: 254px; - background: #1c394c image-url("bg-signup.png") repeat-x; - border-radius: 5px; - border: 1px solid #38658c; - margin: 0 auto; - text-align: center; - margin-top: 75px; - -webkit-box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.5); - -moz-box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.5); - box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.5); + width: 380px; + height: 254px; + background: #1c394c image-url("bg-signup.png") repeat-x; + border-radius: 5px; + border: 1px solid #38658c; + margin: 0 auto; + text-align: center; + margin-top: 75px; + -webkit-box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.5); + -moz-box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.5); + box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.5); } a.button, input.button { - background: #125687; + background: #125687; background: image-url("button-green-normal.png"); border-radius: 3px; color: #FFF; @@ -98,34 +97,34 @@ a.button, input.button { font-weight: normal; padding: 5px 25px; text-align: center; - border: none; - height: 25px; - width: 106px; - text-decoration: none; + border: none; + height: 25px; + width: 106px; + text-decoration: none; } article div.left { - float: left; - font-size: 12px; - color: #FFF; - margin-left: 15px; - margin-top: 16px; + float: left; + font-size: 12px; + color: #FFF; + margin-left: 15px; + margin-top: 16px; } article div.right { - float: right; - margin-right: 15px; - padding-bottom: 2px; + float: right; + margin-right: 15px; + padding-bottom: 2px; } .first { - padding-top: 5px; + padding-top: 5px; } input.button { - padding: 3px 27px 8px 27px; - height: 25px; - width: auto; + padding: 3px 27px 8px 27px; + height: 25px; + width: auto; } a.button:hover, input.button:hover { @@ -140,146 +139,141 @@ a.button:active, input.button:active{ a.button:disabled, a.button.disabled, input.button:disabled, input.button.disabled{ background: #125687; - background: image-url("button-green-disabled.png"); - cursor: default; + background: image-url("button-green-disabled.png"); + cursor: default; } - + article h1 { - color: #FFF; - font-size: 18px; - font-weight: normal; - font-family: Tahoma; - margin-bottom: 5px; + color: #FFF; + font-size: 18px; + font-weight: normal; + font-family: Tahoma; + margin-bottom: 5px; } .registartion-input, .registartion-input-focus, .registartion-input-error, .registartion-input-no-focus { - height: 24px; - width: 217px; - border: 1px solid #8199a9; - border-radius: 2px; - color: #cfcfcf; - font-family: Tahoma; - font-size: 12px; - padding-left: 10px; - margin-top: 10px; + height: 24px; + width: 217px; + border: 1px solid #8199a9; + border-radius: 2px; + color: #cfcfcf; + font-family: Tahoma; + font-size: 12px; + padding-left: 10px; + margin-top: 10px; } .registartion-input-focus, .registartion-input-focus-signup { - color: #575756; - -webkit-box-shadow: 0px 0px 7px 2px rgba(126, 183, 237, 0.75); - -moz-box-shadow: 0px 0px 7px 2px rgba(126, 183, 237, 0.75); - box-shadow: 0px 0px 7px 2px rgba(126, 183, 237, 0.75); + color: #575756; + -webkit-box-shadow: 0px 0px 7px 2px rgba(126, 183, 237, 0.75); + -moz-box-shadow: 0px 0px 7px 2px rgba(126, 183, 237, 0.75); + box-shadow: 0px 0px 7px 2px rgba(126, 183, 237, 0.75); } .registartion-input-no-focus, .registartion-input-no-focus-signup { - color: #575756; + color: #575756; } .registartion-input-error, .registartion-input-error-signup { - border: 1px solid #bd4d40; - outline:1px solid #bd4d40; - outline-offset:-2px; + border: 1px solid #bd4d40; + outline:1px solid #bd4d40; + outline-offset:-2px; } - - div.in { - float: right; - margin-right: 15px; - margin-top: 12px; + float: right; + margin-right: 15px; + margin-top: 12px; } /* Footer */ footer { - height: 32px; - padding-left: 15px; - width: 900px; - margin: 0 auto; - text-align: center; + height: 32px; + padding-left: 15px; + width: 900px; + margin: 0 auto; + text-align: center; } footer ul { - margin: 0; - padding: 0; - list-style: none; - font-size: 12px; - color: #FFF; - padding-top: 10px; - text-align: center; + margin: 0; + padding: 0; + list-style: none; + font-size: 12px; + color: #FFF; + padding-top: 10px; + text-align: center; } footer ul li { - display: inline; + display: inline; } footer ul li a { - font-size: 12px; - color: #FFF; - text-decoration: none; + font-size: 12px; + color: #FFF; + text-decoration: none; } footer ul li a:hover { - text-decoration: underline; + text-decoration: underline; } - div.error { - width: auto; - height: auto; - font-size: 12px; - position: absolute; - margin-top: -159px; - margin-left: 582px; - display: none; - text-align: left; + width: auto; + height: auto; + font-size: 12px; + position: absolute; + margin-top: -159px; + margin-left: 582px; + display: none; + text-align: left; } div.error div.img { - background: image-url("error-arrow.png") 0% 5px no-repeat; - width: 19px; - height: 35px; - float: left; + background: image-url("error-arrow.png") 0% 5px no-repeat; + width: 19px; + height: 35px; + float: left; } - div.error div.msg { - background: #ededed; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - width: auto; - height: auto; - float: left; - -webkit-box-shadow: 0px 6px 3px -3px rgba(0, 0, 0, 0.2); - -moz-box-shadow: 0px 6px 3px -3px rgba(0, 0, 0, 0.2); - box-shadow: 0px 6px 3px -3px rgba(0, 0, 0, 0.2); - padding: 5px 20px 5px; + background: #ededed; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + width: auto; + height: auto; + float: left; + -webkit-box-shadow: 0px 6px 3px -3px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0px 6px 3px -3px rgba(0, 0, 0, 0.2); + box-shadow: 0px 6px 3px -3px rgba(0, 0, 0, 0.2); + padding: 5px 20px 5px; } - div.error p{ - margin:0; - padding: 0; - text-align: center; + margin:0; + padding: 0; + text-align: center; } div.error.login { - margin-top: -242px; - margin-left: 650px; + margin-top: -286px; + margin-left: 650px; } div.error.name { - margin-top: -202px; - margin-left: 650px; + margin-top: -248px; + margin-left: 650px; } div.error.email { - margin-top: -161px; - margin-left: 650px; + margin-top: -209px; + margin-left: 650px; } div.error.password { - margin-top: -101px; - margin-left: 650px; -} \ No newline at end of file + margin-top: -155px; + margin-left: 650px; +} diff --git a/app/assets/stylesheets/login.scss b/app/assets/stylesheets/login.scss index 6ac3c5092..9dea52902 100644 --- a/app/assets/stylesheets/login.scss +++ b/app/assets/stylesheets/login.scss @@ -1,3 +1,16 @@ @import 'devise/login'; -nav a { text-decoration: none;} \ No newline at end of file +nav a { text-decoration: none;} + +article { + height: auto; + width: 270px; + padding-bottom: 10px; + + a { + text-decoration: none; + } + .facebook { + margin-right: -4px; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/reg_session.scss b/app/assets/stylesheets/reg_session.scss index 025ea5b34..696780dac 100644 --- a/app/assets/stylesheets/reg_session.scss +++ b/app/assets/stylesheets/reg_session.scss @@ -21,3 +21,28 @@ div.error.forgot { div.error.reset { margin-top: -141px; margin-left: 645px; } + +article { + height: auto; + padding-bottom: 10px; + .hr { + margin-top: 10px; + border-bottom: 1px solid #264862; + border-top: 1px solid #264862; + height: 1px; + } + a { + text-decoration: none; + } + .facebook { + margin-right: -4px; + } + .other { + .left { + margin-top: 6px; + } + .right { + margin-top: 13px; + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/tour.scss b/app/assets/stylesheets/tour.scss index 4d1844468..0697bd030 100644 --- a/app/assets/stylesheets/tour.scss +++ b/app/assets/stylesheets/tour.scss @@ -3,7 +3,7 @@ html, body { padding: 0; font-family: Tahoma, Geneva, Helvetica, sans-serif; color: #292929; - background: #1f60a1 image-url("bg.png") repeat-x; + background: #1f60a1 image-url("bg.png") repeat-x; min-width: 940px; min-height: 600px; text-align: center; @@ -15,15 +15,15 @@ header, section, footer, aside, nav, article, menu { } input[type="text"]:focus { outline: none; } - + input[type="password"]:focus { outline: none; } - + input:focus { outline: none; } - + select:focus { outline: none; } a img { border: none; } - + .wrap { width: 940px; margin: 0 auto; @@ -31,7 +31,7 @@ a img { border: none; } border: 1px solid #3f668c; -webkit-box-shadow: 0px 0px 7px 0px rgba(0, 0, 0, 0.5); -moz-box-shadow: 0px 0px 7px 0px rgba(0, 0, 0, 0.5); - box-shadow: 0px 0px 7px 0px rgba(0, 0, 0, 0.5); + box-shadow: 0px 0px 7px 0px rgba(0, 0, 0, 0.5); background: #FFF; min-height: 92%; } @@ -47,7 +47,7 @@ a img { border: none; } header { -webkit-box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.4); -moz-box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.4); - box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.4); + box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.4); position: relative; z-index: 1000; } @@ -104,7 +104,7 @@ header menu ul li a:hover { } header menu ul li a.first { - + } header menu ul li a.active { @@ -118,7 +118,7 @@ header div.logo { margin-top: 5px; padding-left: 0px; padding-right: 0px; - + } /* Right part of top menu */ @@ -173,7 +173,7 @@ header div.search div.field input.black { header div.avatar { float:left; padding: 6px 10px 10px 10px; - + } header div.information div.active { @@ -275,7 +275,7 @@ header div.droplist a:hover{ } -.sub-menu nav { +.sub-menu nav { float: left; margin: 0px 0px 0px 0px; } @@ -453,3 +453,6 @@ article div.tour-gap { header menu ul li a { padding: 15px 8px 15px 8px; } + +@import "design/common"; + diff --git a/app/controllers/activity_feeds_controller.rb b/app/controllers/activity_feeds_controller.rb deleted file mode 100644 index 6f19cd212..000000000 --- a/app/controllers/activity_feeds_controller.rb +++ /dev/null @@ -1,14 +0,0 @@ -class ActivityFeedsController < ApplicationController - before_filter :authenticate_user! - - def index - @filter = t('feed_menu').has_key?(params[:filter].try(:to_sym)) ? params[:filter].to_sym : :all - @activity_feeds = current_user.activity_feeds - @activity_feeds = @activity_feeds.where(:kind => "ActivityFeed::#{@filter.upcase}".constantize) unless @filter == :all - @activity_feeds = @activity_feeds.paginate :page => params[:page] - respond_to do |format| - format.html { request.xhr? ? render('_list', :layout => false) : render('index') } - format.atom - end - end -end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 643bbf05a..3d42bfddc 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -57,13 +57,13 @@ class Admin::UsersController < Admin::BaseController def list if params[:system] != 'true' - colName, @users = ['users.name', 'users.uname', 'users.email'], @users.opened + colName, @users = %w(name uname email created_at), @users.opened else - colName, @users, @system_list = ['users.uname'], @users.system, true + colName, @users, @system_list = ['uname'], @users.system, true end sort_col = params[:iSortCol_0] || 0 sort_dir = params[:sSortDir_0]=="asc" ? 'asc' : 'desc' - order = "#{colName[sort_col.to_i]} #{sort_dir}" + order = "users.#{colName[sort_col.to_i]} #{sort_dir}" @users = @users.paginate(:page => (params[:iDisplayStart].to_i/params[:iDisplayLength].to_i).to_i + 1, :per_page => params[:iDisplayLength]) @total_users = @users.count diff --git a/app/controllers/advisories_controller.rb b/app/controllers/advisories_controller.rb index 876e9ec99..0181c57ea 100644 --- a/app/controllers/advisories_controller.rb +++ b/app/controllers/advisories_controller.rb @@ -20,9 +20,13 @@ class AdvisoriesController < ApplicationController def search @advisory = Advisory.by_update_type(params[:bl_type]).search_by_id(params[:query]).first - raise ActionController::RoutingError.new('Not Found') if @advisory.nil? - respond_to do |format| - format.json { render @advisory } + if @advisory.nil? + render :nothing => true, :status => 404 + else + # respond_to do |format| + # format.json { render @advisory } + # end + render @advisory end end end diff --git a/app/controllers/api/v1/advisories_controller.rb b/app/controllers/api/v1/advisories_controller.rb index 7380c124c..39c59bb41 100644 --- a/app/controllers/api/v1/advisories_controller.rb +++ b/app/controllers/api/v1/advisories_controller.rb @@ -6,7 +6,7 @@ class Api::V1::AdvisoriesController < Api::V1::BaseController authorize_resource :build_list, :only => [:create, :update] def index - @advisories = @advisories.scoped(:include => :platforms). + @advisories = @advisories.scoped(:include => [:platforms, :projects]). paginate(paginate_params) end diff --git a/app/controllers/api/v1/base_controller.rb b/app/controllers/api/v1/base_controller.rb index 722db3f56..4e9a20b7c 100644 --- a/app/controllers/api/v1/base_controller.rb +++ b/app/controllers/api/v1/base_controller.rb @@ -1,4 +1,5 @@ class Api::V1::BaseController < ApplicationController + include PaginateHelper #respond_to :json helper_method :member_path @@ -71,15 +72,6 @@ class Api::V1::BaseController < ApplicationController end end - def paginate_params - per_page = params[:per_page].to_i - per_page = 20 if per_page < 1 - per_page = 100 if per_page >100 - page = params[:page].to_i - page = nil if page == 0 - {:page => page, :per_page => per_page} - end - def render_json_response(subject, message, status = 200) id = status != 200 ? nil : subject.id diff --git a/app/controllers/api/v1/build_lists_controller.rb b/app/controllers/api/v1/build_lists_controller.rb index 3d2419820..3b41ae812 100644 --- a/app/controllers/api/v1/build_lists_controller.rb +++ b/app/controllers/api/v1/build_lists_controller.rb @@ -4,10 +4,10 @@ class Api::V1::BuildListsController < Api::V1::BaseController skip_before_filter :authenticate_user!, :only => [:show, :index] if APP_CONFIG['anonymous_access'] load_and_authorize_resource :project, :only => :index - load_and_authorize_resource :build_list, :only => [:show, :create, :cancel, :publish, :reject_publish, :create_container] + load_and_authorize_resource :build_list, :only => [:show, :create, :cancel, :publish, :reject_publish, :create_container, :publish_into_testing] def index - filter = BuildList::Filter.new(@project, current_user, params[:filter] || {}) + filter = BuildList::Filter.new(@project, current_user, current_ability, params[:filter] || {}) @build_lists = filter.find.scoped(:include => [:save_to_platform, :project, :user, :arch]) @build_lists = @build_lists.recent.paginate(paginate_params) end @@ -33,11 +33,11 @@ class Api::V1::BuildListsController < Api::V1::BaseController def publish @build_list.publisher = current_user - @build_list.save render_json :publish end def reject_publish + @build_list.publisher = current_user render_json :reject_publish end @@ -45,6 +45,11 @@ class Api::V1::BuildListsController < Api::V1::BaseController render_json :create_container, :publish_container end + def publish_into_testing + @build_list.publisher = current_user + render_json :publish_into_testing + end + private def render_json(action_name, action_method = nil) diff --git a/app/controllers/api/v1/issues_controller.rb b/app/controllers/api/v1/issues_controller.rb new file mode 100644 index 000000000..788401480 --- /dev/null +++ b/app/controllers/api/v1/issues_controller.rb @@ -0,0 +1,117 @@ +# -*- encoding : utf-8 -*- +class Api::V1::IssuesController < Api::V1::BaseController + respond_to :json + + before_filter :authenticate_user! + skip_before_filter :authenticate_user!, :only => [:index, :group_index, :show] if APP_CONFIG['anonymous_access'] + + load_and_authorize_resource :group, :only => :group_index, :find_by => :id, :parent => false + load_and_authorize_resource :project + load_and_authorize_resource :issue, :through => :project, :find_by => :serial_id, :only => [:show, :update, :create, :index] + + def index + @issues = @project.issues + render_issues_list + end + + def all_index + project_ids = get_all_project_ids Project.accessible_by(current_ability, :membered).pluck(:id) + @issues = Issue.where(:project_id => project_ids) + render_issues_list + end + + def user_index + project_ids = get_all_project_ids current_user.projects.pluck(:id) + @issues = Issue.where(:project_id => project_ids) + render_issues_list + end + + def group_index + project_ids = @group.projects.pluck(:id) + project_ids = Project.accessible_by(current_ability, :membered).where(:id => project_ids).pluck(:id) + @issues = Issue.where(:project_id => project_ids) + render_issues_list + end + + def show + redirect_to api_v1_project_pull_request_path(@project.id, @issue.serial_id) if @issue.pull_request + end + + def create + @issue.user = current_user + @issue.assignee = nil if cannot?(:write, @project) + create_subject @issue + end + + def update + unless can?(:write, @project) + params.delete :update_labels + [:assignee_id, :labelings, :labelings_attributes].each do |k| + params[:issue].delete k + end if params[:issue] + end + @issue.labelings.destroy_all if params[:update_labels] + if params[:issue] && status = params[:issue].delete(:status) + @issue.set_close(current_user) if status == 'closed' + @issue.set_open if status == 'open' + end + update_subject @issue + end + + private + + def render_issues_list + @issues = @issues.includes(:user, :assignee, :labels).without_pull_requests + if params[:status] == 'closed' + @issues = @issues.closed + else + @issues = @issues.opened + end + + if action_name == 'index' && params[:assignee].present? + case params[:assignee] + when 'none' + @issues = @issues.where(:assigned_id => nil) + when '*' + @issues = @issues.where('issues.assigned_id IS NOT NULL') + else + @issues = @issues.where('issues.assignees_issues.uname = ?', params[:assignee]) + end + end + + if %w[all_index user_index group_index].include?(action_name) + case params[:filter] + when 'created' + @issues = @issues.where(:user_id => current_user) + when 'all' + else + @issues = @issues.where(:assignee_id => current_user) + end + else + @issues.where('users.uname = ?', params[:creator]) if params[:creator].present? + end + + if params[:labels].present? + labels = params[:labels].split(',').map {|e| e.strip}.select {|e| e.present?} + @issues = @issues.where('labels.name IN (?)', labels) + end + + sort = params[:sort] == 'updated' ? 'issues.updated_at' : 'issues.created_at' + direction = params[:direction] == 'asc' ? 'ASC' : 'DESC' + @issues = @issues.order("#{sort} #{direction}") + + @issues = @issues.where('issues.created_at >= to_timestamp(?)', params[:since]) if params[:since] =~ /\A\d+\z/ + @issues = @issues.paginate(paginate_params) + render :index + end + + def get_all_project_ids default_project_ids + project_ids = [] + if ['created', 'all'].include? params[:filter] + # add own issues + project_ids = Project.accessible_by(current_ability, :show).joins(:issues). + where(:issues => {:user_id => current_user.id}).pluck('projects.id') + end + project_ids |= default_project_ids + end +end diff --git a/app/controllers/api/v1/jobs_controller.rb b/app/controllers/api/v1/jobs_controller.rb new file mode 100644 index 000000000..613e1cda0 --- /dev/null +++ b/app/controllers/api/v1/jobs_controller.rb @@ -0,0 +1,94 @@ +# -*- encoding : utf-8 -*- +class Api::V1::JobsController < Api::V1::BaseController + # QUEUES = %w(iso_worker_observer publish_observer rpm_worker_observer) + # QUEUE_CLASSES = %w(AbfWorker::IsoWorkerObserver AbfWorker::PublishObserver AbfWorker::RpmWorkerObserver) + QUEUES = %w(rpm_worker_observer) + QUEUE_CLASSES = %w(AbfWorker::RpmWorkerObserver) + + before_filter :authenticate_user! + + def shift + platform_ids = Platform.where(name: params[:platforms].split(',')).pluck(:id) if params[:platforms].present? + arch_ids = Arch.where(name: params[:arches].split(',')).pluck(:id) if params[:arches].present? + build_lists = BuildList.for_status(BuildList::BUILD_PENDING).scoped_to_arch(arch_ids). + oldest.order(:created_at) + build_lists = build_lists.for_platform(platform_ids) if platform_ids.present? + + if current_user.system? + if task = (Resque.pop('rpm_worker_default') || Resque.pop('rpm_worker')) + @build_list = BuildList.where(:id => task['args'][0]['id']).first + # Temporally + if @build_list.build_for_platform.name == 'red3' + @build_list.restart_job + @build_list = nil + end + end + end + + ActiveRecord::Base.transaction do + if current_user.system? + @build_list ||= build_lists.external_nodes(:everything).first + @build_list.touch if @build_list + else + @build_list = build_lists.external_nodes(:owned).for_user(current_user).first + @build_list ||= build_lists.external_nodes(:everything). + accessible_by(current_ability, :everything).readonly(false).first + + if @build_list + @build_list.builder = current_user + @build_list.save + end + end + end unless @build_list + + if @build_list + job = { + :worker_queue => @build_list.worker_queue_with_priority, + :worker_class => @build_list.worker_queue_class, + :worker_args => [@build_list.abf_worker_args] + } + end + render :json => { :job => job }.to_json + end + + def statistics + if params[:uid].present? + RpmBuildNode.create( + :id => params[:uid], + :user_id => current_user.id, + :system => current_user.system?, + :worker_count => params[:worker_count], + :busy_workers => params[:busy_workers] + ) rescue nil + end + render :nothing => true + end + + def status + render :text => Resque.redis.get(params[:key]) + end + + def logs + name = params[:name] + if name =~ /abfworker::rpm-worker/ + if current_user.system? || current_user.id == BuildList.where(:id => name.gsub(/[^\d]/, '')).first.try(:builder_id) + BuildList.log_server.setex name, 15, params[:logs] + end + end + render :nothing => true + end + + def feedback + worker_queue = params[:worker_queue] + worker_class = params[:worker_class] + if QUEUES.include?(worker_queue) && QUEUE_CLASSES.include?(worker_class) + worker_args = (params[:worker_args] || []).first || {} + worker_args = worker_args.merge(:feedback_from_user => current_user.id) + Resque.push worker_queue, 'class' => worker_class, 'args' => [worker_args] + render :nothing => true + else + render :nothing => true, :status => 403 + end + end + +end diff --git a/app/controllers/api/v1/maintainers_controller.rb b/app/controllers/api/v1/maintainers_controller.rb index cda1761f1..c6596ca69 100644 --- a/app/controllers/api/v1/maintainers_controller.rb +++ b/app/controllers/api/v1/maintainers_controller.rb @@ -5,7 +5,7 @@ class Api::V1::MaintainersController < Api::V1::BaseController def index @maintainers = BuildList::Package.includes(:project) .actual.by_platform(@platform) - .like_name(params[:filter].try(:[], :package_name)) + .like_name(params[:package_name]) .paginate(paginate_params) end end diff --git a/app/controllers/api/v1/platforms_controller.rb b/app/controllers/api/v1/platforms_controller.rb index a93c96905..2acbe71f7 100644 --- a/app/controllers/api/v1/platforms_controller.rb +++ b/app/controllers/api/v1/platforms_controller.rb @@ -1,8 +1,17 @@ class Api::V1::PlatformsController < Api::V1::BaseController before_filter :authenticate_user! + skip_before_filter :authenticate_user!, :only => :allowed skip_before_filter :authenticate_user!, :only => [:show, :platforms_for_build, :members] if APP_CONFIG['anonymous_access'] - load_and_authorize_resource + load_and_authorize_resource :except => :allowed + + def allowed + if Platform.allowed?(params[:path] || '', request) + render :nothing => true + else + render :nothing => true, :status => 403 + end + end def index @platforms = @platforms.accessible_by(current_ability, :related). diff --git a/app/controllers/api/v1/projects_controller.rb b/app/controllers/api/v1/projects_controller.rb index 359c85be3..89f6e0cbf 100644 --- a/app/controllers/api/v1/projects_controller.rb +++ b/app/controllers/api/v1/projects_controller.rb @@ -1,7 +1,7 @@ class Api::V1::ProjectsController < Api::V1::BaseController before_filter :authenticate_user! - skip_before_filter :authenticate_user!, :only => [:get_id, :show, :refs] if APP_CONFIG['anonymous_access'] + skip_before_filter :authenticate_user!, :only => [:get_id, :show, :refs_list] if APP_CONFIG['anonymous_access'] load_and_authorize_resource :project @@ -22,6 +22,7 @@ class Api::V1::ProjectsController < Api::V1::BaseController end def refs_list + @refs = @project.repo.branches + @project.repo.tags.select{ |t| t.commit } end def update @@ -41,7 +42,7 @@ class Api::V1::ProjectsController < Api::V1::BaseController else @project.owner = nil end - authorize! :update, @project.owner if @project.owner != current_user + authorize! :write, @project.owner if @project.owner != current_user create_subject @project end @@ -62,9 +63,9 @@ class Api::V1::ProjectsController < Api::V1::BaseController end def fork - owner = (Group.find params[:group_id] if params[:group].present?) || current_user - authorize! :update, owner if owner.class == Group - if forked = @project.fork(owner) and forked.valid? + owner = (Group.find params[:group_id] if params[:group_id].present?) || current_user + authorize! :write, owner if owner.class == Group + if forked = @project.fork(owner, params[:fork_name]) and forked.valid? render_json_response forked, 'Project has been forked successfully' else render_validation_error forked, 'Project has not been forked' diff --git a/app/controllers/api/v1/pull_requests_controller.rb b/app/controllers/api/v1/pull_requests_controller.rb new file mode 100644 index 000000000..63a3e5d1d --- /dev/null +++ b/app/controllers/api/v1/pull_requests_controller.rb @@ -0,0 +1,170 @@ +# -*- encoding : utf-8 -*- +class Api::V1::PullRequestsController < Api::V1::BaseController + respond_to :json + + before_filter :authenticate_user! + skip_before_filter :authenticate_user!, :only => [:show, :index, :group_index, :commits, :files] if APP_CONFIG['anonymous_access'] + + load_resource :group, :only => :group_index, :find_by => :id, :parent => false + load_resource :project + load_resource :issue, :through => :project, :find_by => :serial_id, :parent => false, :only => [:show, :index, :commits, :files, :merge, :update] + load_and_authorize_resource :instance_name => :pull, :through => :issue, :singleton => true, :only => [:show, :index, :commits, :files, :merge, :update] + + def index + @pulls = @project.pull_requests + @pulls_url = api_v1_project_pull_requests_path(@project, :format => :json) + render_pulls_list + end + + def all_index + project_ids = get_all_project_ids Project.accessible_by(current_ability, :membered).pluck(:id) + @pulls = PullRequest.where('pull_requests.to_project_id IN (?)', project_ids) + @pulls_url = api_v1_pull_requests_path :format => :json + render_pulls_list + end + + def user_index + project_ids = get_all_project_ids current_user.projects.pluck(:id) + @pulls = PullRequest.where('pull_requests.to_project_id IN (?)', project_ids) + @pulls_url = pull_requests_api_v1_user_path :format => :json + render_pulls_list + end + + def group_index + project_ids = @group.projects.pluck(:id) + project_ids = Project.accessible_by(current_ability, :membered).where(:id => project_ids).pluck(:id) + @pulls = PullRequest.where(:to_project_id => project_ids) + @pulls_url = pull_requests_api_v1_group_path + render_pulls_list + end + + def show + redirect_to api_v1_project_issue_path(@project.id, @issue.serial_id) if @pull.nil? + end + + def create + from_project = Project.find(pull_params[:from_project_id]) if pull_params[:from_project_id].present? + from_project ||= @project + authorize! :read, from_project + + @pull = @project.pull_requests.new + @pull.build_issue :title => pull_params[:title], :body => pull_params[:body] + @pull.from_project = @project + @pull.to_ref, @pull.from_ref = pull_params[:to_ref], pull_params[:from_ref] + @pull.issue.assignee_id = pull_params[:assignee_id] if can?(:write, @project) + @pull.issue.user, @pull.issue.project = current_user, @project + render_validation_error(@pull, "#{@pull.class.name} has not been created") && return unless @pull.valid? + + @pull.save # set pull id + @pull.reload + @pull.check(false) # don't make event transaction + if @pull.already? + @pull.destroy + error_message = I18n.t('projects.pull_requests.up_to_date', :to_ref => @pull.to_ref, :from_ref => @pull.from_ref) + render_json_response(@pull, error_message, 422) + else + @pull.send(@pull.status == 'blocked' ? 'block' : @pull.status) + render_json_response @pull, "#{@pull.class.name} has been created successfully" + end + end + + def update + @pull = @project.pull_requests.includes(:issue).where(:issues => {:serial_id => params[:id]}).first + authorize! :update, @pull + + if pull_params.present? + attrs = pull_params.slice(:title, :body) + attrs.merge!(:assignee_id => pull_params[:assignee_id]) if can?(:write, @project) + + if (action = pull_params[:status]) && %w(close reopen).include?(pull_params[:status]) + if @pull.send("can_#{action}?") + @pull.set_user_and_time current_user + need_check = true if action == 'reopen' && @pull.valid? + end + end + end + + class_name = @pull.class.name + if @pull.issue.update_attributes(attrs) + @pull.send(action) if action.present? + @pull.check if need_check + render_json_response @pull, "#{class_name} has been updated successfully" + else + render_validation_error @pull, "#{class_name} has not been updated" + end + end + + def commits + @commits = @pull.repo.commits_between(@pull.to_commit, @pull.from_commit).paginate(paginate_params) + end + + def files + @stats = @pull.diff_stats.zip(@pull.diff).paginate(paginate_params) + end + + def merge + class_name = @pull.class.name + if @pull.merge!(current_user) + render_json_response @pull, "#{class_name} has been merged successfully" + else + render_validation_error @pull, "#{class_name} has not been merged" + end + end + + private + + def render_pulls_list + @pulls = @pulls.includes(:issue => [:user, :assignee]) + if params[:status] == 'closed' + @pulls = @pulls.closed_or_merged + else + @pulls = @pulls.not_closed_or_merged + end + + if action_name == 'index' && params[:assignee].present? + case params[:assignee] + when 'none' + @pulls = @pulls.where('issues.assigned_id IS NULL') + when '*' + @pulls = @pulls.where('issues.assigned_id IS NOT NULL') + else + @pulls = @pulls.where('assignees_issues.uname = ?', params[:assignee]) + end + end + + if %w[all_index user_index group_index].include?(action_name) + case params[:filter] + when 'created' + @pulls = @pulls.where('issues.user_id = ?', current_user.id) + when 'all' + else + @pulls = @pulls.where('issues.assignee_id = ?', current_user.id) + end + else + @pulls.where('users.uname = ?', params[:creator]) if params[:creator].present? + end + + sort = params[:sort] == 'updated' ? 'issues.updated_at' : 'issues.created_at' + direction = params[:direction] == 'asc' ? 'ASC' : 'DESC' + @pulls = @pulls.order("#{sort} #{direction}") + + @pulls = @pulls.where('issues.created_at >= to_timestamp(?)', params[:since]) if params[:since] =~ /\A\d+\z/ + @pulls = @pulls.paginate(paginate_params) + render :index + end + + def get_all_project_ids default_project_ids + project_ids = [] + if ['created', 'all'].include? params[:filter] + # add own pulls + project_ids = Project.accessible_by(current_ability, :show).joins(:issues). + where(:issues => {:user_id => current_user.id}).pluck('projects.id') + end + project_ids |= default_project_ids + end + + + def pull_params + @pull_params ||= params[:pull_request] || {} + end +end diff --git a/app/controllers/api/v1/repositories_controller.rb b/app/controllers/api/v1/repositories_controller.rb index 064e81a26..2f2b4d630 100644 --- a/app/controllers/api/v1/repositories_controller.rb +++ b/app/controllers/api/v1/repositories_controller.rb @@ -32,14 +32,27 @@ class Api::V1::RepositoriesController < Api::V1::BaseController def key_pair end + def add_repo_lock_file + @repository.add_repo_lock_file + render_json_response @repository, "'.repo.lock' file has been added to repository successfully" + end + + def remove_repo_lock_file + @repository.remove_repo_lock_file + render_json_response @repository, "'.repo.lock' file has been removed from repository successfully" + end + def add_project - project = Project.where(:id => params[:project_id]).first - if project - begin - @repository.projects << project - render_json_response @repository, "Project '#{project.id}' has been added to repository successfully" - rescue ActiveRecord::RecordInvalid - render_validation_error @repository, t('flash.repository.project_not_added') + if project = Project.where(:id => params[:project_id]).first + if can?(:read, project) + begin + @repository.projects << project + render_json_response @repository, "Project '#{project.id}' has been added to repository successfully" + rescue ActiveRecord::RecordInvalid + render_validation_error @repository, t('flash.repository.project_not_added') + end + else + render_validation_error @repository, 'You have no access to read this project' end else render_validation_error @repository, "Project has not been added to repository" diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index dea404e3e..4fe14a061 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -7,7 +7,7 @@ class ApplicationController < ActionController::Base layout :layout_by_resource # Hack to prevent token auth on all pages except atom feed: - prepend_before_filter lambda { redirect_to(new_user_session_path) if params[:token] && params[:format] != 'atom'} + prepend_before_filter lambda { redirect_to(new_user_session_path) if params[:token] && params[:token].is_a?(String) && params[:format] != 'atom'} before_filter :set_locale before_filter lambda { EventLog.current_controller = self }, @@ -79,7 +79,7 @@ class ApplicationController < ActionController::Base end def layout_by_resource - if devise_controller? && !(params[:controller] == 'devise/registrations' && ['edit', 'update'].include?(params[:action])) + if devise_controller? "sessions" else "application" diff --git a/app/controllers/autocompletes_controller.rb b/app/controllers/autocompletes_controller.rb index 038fd4835..53a7a9e84 100644 --- a/app/controllers/autocompletes_controller.rb +++ b/app/controllers/autocompletes_controller.rb @@ -1,6 +1,45 @@ class AutocompletesController < ApplicationController before_filter :authenticate_user! - autocomplete :group, :uname - autocomplete :user, :uname + autocomplete :group, :uname + autocomplete :user, :uname + + def autocomplete_extra_build_list + bl = BuildList.for_extra_build_lists(params[:term], current_ability, save_to_platform).first + results << { :id => bl.id, + :value => bl.id, + :label => "#{bl.id} (#{bl.project.name} - #{bl.arch.name})", + :path => build_list_path(bl) + } if bl + render json: results.to_json + end + + def autocomplete_extra_repositories + # Only personal and build for platform repositories can be attached to the build + Platform.includes(:repositories).search(params[:term]).search_order + .accessible_by(current_ability, :read).limit(5) + .where("platforms.platform_type = 'personal' OR platforms.id = ?", + params[:build_for_platform_id].to_i).each do |platform| + platform.repositories.each do |repository| + label = "#{platform.name}/#{repository.name}" + results << { :id => repository.id, + :label => label, + :value => label, + :path => platform_repository_path(platform, repository) + } + end + end if save_to_platform.personal? + render json: results.to_json + end + + protected + + def save_to_platform + @save_to_platform ||= Platform.find(params[:platform_id]) + end + + def results + @results ||= [] + end + end diff --git a/app/controllers/groups/profile_controller.rb b/app/controllers/groups/profile_controller.rb index 4df5ab1cc..2bfb1d141 100644 --- a/app/controllers/groups/profile_controller.rb +++ b/app/controllers/groups/profile_controller.rb @@ -9,9 +9,19 @@ class Groups::ProfileController < Groups::BaseController end def show - @projects = @group.projects.by_visibilities(['open']). - search(params[:search]).search_order. - paginate(:page => params[:page], :per_page => 25) + @path, page = group_path, params[:page].to_i + @projects = @group.own_projects.opened.search(params[:search]).recent + if request.xhr? + if params[:visibility] != 'hidden' + @projects = @projects.opened + @hidden = true + else + @projects = @projects.by_visibilities('hidden').accessible_by(current_ability, :read) + end + render :partial => 'shared/profile_projects', :layout => nil, :locals => {:projects => paginate_projects(page)} + else + @projects = paginate_projects(page) + end end def new @@ -53,4 +63,10 @@ class Groups::ProfileController < Groups::BaseController Relation.by_actor(current_user).by_target(@group).destroy_all redirect_to groups_path end + + protected + + def paginate_projects(page) + @projects.paginate(:page => (page>0 ? page : nil), :per_page => 24) + end end diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb new file mode 100644 index 000000000..a2849ab85 --- /dev/null +++ b/app/controllers/home_controller.rb @@ -0,0 +1,62 @@ +# -*- encoding : utf-8 -*- +class HomeController < ApplicationController + before_filter :authenticate_user!, :only => [:activity, :issues, :pull_requests] + + def root + render 'pages/tour/abf-tour-project-description-1', :layout => 'tour' + end + + def activity + @filter = t('feed_menu').has_key?(params[:filter].try(:to_sym)) ? params[:filter].to_sym : :all + @activity_feeds = current_user.activity_feeds + @activity_feeds = @activity_feeds.where(:kind => "ActivityFeed::#{@filter.upcase}".constantize) unless @filter == :all + @activity_feeds = @activity_feeds.paginate :page => params[:page] + respond_to do |format| + format.html { request.xhr? ? render('_list', :layout => false) : render('activity') } + format.atom + end + end + + def issues + @created_issues = current_user.issues + @assigned_issues = Issue.where(:assignee_id => current_user.id) + pr_ids = Project.accessible_by(current_ability, :membered).uniq.pluck(:id) + @all_issues = Issue.where(:project_id => pr_ids) + @created_issues, @assigned_issues, @all_issues = + if action_name == 'issues' + [@created_issues.without_pull_requests, + @assigned_issues.without_pull_requests, + @all_issues.without_pull_requests] + else + [@created_issues.joins(:pull_request), + @assigned_issues.joins(:pull_request), + @all_issues.joins(:pull_request)] + end + + case params[:filter] + when 'created' + @issues = @created_issues + when 'assigned' + @issues = @assigned_issues + else + params[:filter] = 'all' # default + @issues = @all_issues + end + @filter = params[:filter] + @opened_issues, @closed_issues = @issues.not_closed_or_merged.count, @issues.closed_or_merged.count + + @status = params[:status] == 'closed' ? :closed : :open + @issues = @issues.send( (@status == :closed) ? :closed_or_merged : :not_closed_or_merged ) + + @sort = params[:sort] == 'updated' ? :updated : :created + @direction = params[:direction] == 'asc' ? :asc : :desc + @issues = @issues.order("issues.#{@sort}_at #{@direction}") + .includes(:assignee, :user, :pull_request).uniq + .paginate :per_page => 20, :page => params[:page] + render 'issues', :layout => request.xhr? ? 'with_sidebar' : 'application' + end + + def pull_requests + issues + end +end \ No newline at end of file diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index 3bbf48049..4aafa9d16 100644 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -1,10 +1,4 @@ class PagesController < ApplicationController - # before_filter :authenticate_user!, :except => [:show, :main, :forbidden] - # load_and_authorize_resource - - def root - render 'pages/tour/abf-tour-project-description-1', :layout => 'tour' - end def tour_inside @entries = case params[:id] diff --git a/app/controllers/platforms/contents_controller.rb b/app/controllers/platforms/contents_controller.rb new file mode 100644 index 000000000..62c9d566a --- /dev/null +++ b/app/controllers/platforms/contents_controller.rb @@ -0,0 +1,17 @@ +class Platforms::ContentsController < Platforms::BaseController + include PaginateHelper + + before_filter :authenticate_user! + skip_before_filter :authenticate_user! if APP_CONFIG['anonymous_access'] + + load_and_authorize_resource :platform + + def index + @path = params[:path].to_s + @term = params[:term] + @contents = PlatformContent.find_by_platform(@platform, @path, @term) + .paginate(paginate_params) + + end + +end diff --git a/app/controllers/platforms/key_pairs_controller.rb b/app/controllers/platforms/key_pairs_controller.rb index 4d9fb0ab0..8899c280c 100644 --- a/app/controllers/platforms/key_pairs_controller.rb +++ b/app/controllers/platforms/key_pairs_controller.rb @@ -1,4 +1,4 @@ -class Platforms::KeyPairsController < ApplicationController +class Platforms::KeyPairsController < Platforms::BaseController before_filter :authenticate_user! load_and_authorize_resource :platform, :only => [:index] diff --git a/app/controllers/platforms/mass_builds_controller.rb b/app/controllers/platforms/mass_builds_controller.rb index 27c261e95..06d8d0478 100644 --- a/app/controllers/platforms/mass_builds_controller.rb +++ b/app/controllers/platforms/mass_builds_controller.rb @@ -1,59 +1,52 @@ -#class MassBuildsController < ApplicationController class Platforms::MassBuildsController < Platforms::BaseController + before_filter :authenticate_user! skip_before_filter :authenticate_user!, :only => [:index, :get_list] if APP_CONFIG['anonymous_access'] - load_and_authorize_resource :platform - load_and_authorize_resource + load_resource :platform + load_and_authorize_resource :through => :platform, :shallow => true + - skip_load_and_authorize_resource :only => [:index, :create] - skip_load_and_authorize_resource :platform, :only => [:cancel, :failed_builds_list, :publish] - skip_authorize_resource :platform, :only => [:index, :create] + def new + end def create - mass_build = @platform.mass_builds.new(:arches => params[:arches], - :auto_publish => params[:auto_publish] || false, - :projects_list => params[:projects_list]) - mass_build.user = current_user - authorize! :create, mass_build + @mass_build.user, @mass_build.arches = current_user, params[:arches] - if mass_build.save + if @mass_build.save redirect_to(platform_mass_builds_path(@platform), :notice => t("flash.platform.build_all_success")) else - @auto_publish_selected = params[:auto_publish].present? - @mass_builds = MassBuild.by_platform(@platform).order('created_at DESC').paginate(:page => params[:page], :per_page => 20) - flash[:warning] = mass_build.errors.full_messages.join('. ') - flash[:error] = t("flash.platform.build_all_error") - render :index + flash[:warning] = @mass_build.errors.full_messages.join('. ') + flash[:error] = t('flash.platform.build_all_error') + render :action => :new end end def publish if params[:status] == 'test_failed' - @mass_build.publish_test_faild_builds current_user + @mass_build.publish_test_failed_builds current_user else @mass_build.publish_success_builds current_user end - redirect_to(platform_mass_builds_path(@mass_build.platform), :notice => t("flash.platform.publish_success")) + redirect_to(platform_mass_builds_path(@mass_build.save_to_platform), :notice => t("flash.platform.publish_success")) end def index - @mass_builds = MassBuild.by_platform(@platform).order('created_at DESC').paginate(:page => params[:page], :per_page => 20) - @auto_publish_selected = true + @mass_builds = MassBuild.by_platform(@platform).order('created_at DESC').paginate(:page => params[:page], :per_page => 20) end def cancel @mass_build.cancel_all flash[:notice] = t("flash.platform.cancel_mass_build") - redirect_to platform_mass_builds_path(@mass_build.platform) + redirect_to platform_mass_builds_path(@mass_build.save_to_platform) end def get_list - text = if params[:kind] == 'failed_builds_list' - @mass_build.generate_failed_builds_list - elsif ['projects_list', 'missed_projects_list'].include? params[:kind] - @mass_build.send params[:kind] - end + text = if params[:kind] == 'failed_builds_list' + @mass_build.generate_failed_builds_list + elsif ['projects_list', 'missed_projects_list'].include? params[:kind] + @mass_build.send params[:kind] + end render :text => text end end diff --git a/app/controllers/platforms/platforms_controller.rb b/app/controllers/platforms/platforms_controller.rb index 559d0dfd0..c0cb8f59a 100644 --- a/app/controllers/platforms/platforms_controller.rb +++ b/app/controllers/platforms/platforms_controller.rb @@ -1,11 +1,12 @@ class Platforms::PlatformsController < Platforms::BaseController + include FileStoreHelper before_filter :authenticate_user! skip_before_filter :authenticate_user!, :only => [:advisories, :members, :show] if APP_CONFIG['anonymous_access'] load_and_authorize_resource def index - @platforms = @platforms.accessible_by(current_ability, :related).paginate(:page => params[:page], :per_page => 20) + @platforms = @platforms.accessible_by(current_ability, :related).order(:name).paginate(:page => params[:page], :per_page => 20) end def show @@ -41,11 +42,31 @@ class Platforms::PlatformsController < Platforms::BaseController @admin_id = params[:admin_id] @admin_uname = params[:admin_uname] - if @platform.update_attributes( - :owner => @admin_id.blank? ? get_owner : User.find(@admin_id), - :description => params[:platform][:description], - :released => (params[:platform][:released] || @platform.released) - ) + platform_params = params[:platform] || {} + platform_params = platform_params.slice(:description, :platform_arch_settings_attributes, :released) + platform_params[:owner] = User.find(@admin_id) if @admin_id.present? + + if @platform.update_attributes(platform_params) + flash[:notice] = I18n.t("flash.platform.saved") + redirect_to @platform + else + flash[:error] = I18n.t("flash.platform.save_error") + flash[:warning] = @platform.errors.full_messages.join('. ') + render :action => :edit + end + end + + def regenerate_metadata + if @platform.regenerate + flash[:notice] = I18n.t('flash.platform.saved') + else + flash[:error] = I18n.t('flash.platform.save_error') + end + redirect_to edit_platform_path(@platform) + end + + def change_visibility + if @platform.change_visibility flash[:notice] = I18n.t("flash.platform.saved") redirect_to @platform else diff --git a/app/controllers/platforms/product_build_lists_controller.rb b/app/controllers/platforms/product_build_lists_controller.rb index ed494374a..41f0f15df 100644 --- a/app/controllers/platforms/product_build_lists_controller.rb +++ b/app/controllers/platforms/product_build_lists_controller.rb @@ -1,4 +1,6 @@ class Platforms::ProductBuildListsController < Platforms::BaseController + include FileStoreHelper + before_filter :authenticate_user! skip_before_filter :authenticate_user!, :only => [:index, :show, :log] if APP_CONFIG['anonymous_access'] before_filter :redirect_to_full_path_if_short_url, :only => :show diff --git a/app/controllers/platforms/products_controller.rb b/app/controllers/platforms/products_controller.rb index 7c5ed8c87..4356e84bb 100644 --- a/app/controllers/platforms/products_controller.rb +++ b/app/controllers/platforms/products_controller.rb @@ -4,7 +4,7 @@ class Platforms::ProductsController < Platforms::BaseController skip_before_filter :authenticate_user!, :only => [:index, :show] if APP_CONFIG['anonymous_access'] load_and_authorize_resource :platform - load_and_authorize_resource :product, :through => :platform + load_and_authorize_resource :product, :through => :platform, :except => :autocomplete_project before_filter :set_project, :only => [:create, :update] def index @@ -51,9 +51,9 @@ class Platforms::ProductsController < Platforms::BaseController end def autocomplete_project - items = Project.accessible_by(current_ability, :membered). - search(params[:term]).search_order - items.select! {|e| e.repo.branches.count > 0} + items = Project.accessible_by(current_ability, :membered) + .search(params[:term]).limit(20) + #items.select! {|e| e.repo.branches.count > 0} render :json => items.map{ |p| { :id => p.id, diff --git a/app/controllers/platforms/repositories_controller.rb b/app/controllers/platforms/repositories_controller.rb index 3900babe6..f45646057 100644 --- a/app/controllers/platforms/repositories_controller.rb +++ b/app/controllers/platforms/repositories_controller.rb @@ -1,4 +1,6 @@ class Platforms::RepositoriesController < Platforms::BaseController + include FileStoreHelper + before_filter :authenticate_user! skip_before_filter :authenticate_user!, :only => [:index, :show, :projects_list] if APP_CONFIG['anonymous_access'] @@ -7,12 +9,12 @@ class Platforms::RepositoriesController < Platforms::BaseController before_filter :set_members, :only => [:edit, :update] def index - @repositories = @repositories.paginate(:page => params[:page]) + @repositories = Repository.custom_sort(@repositories).paginate(:page => params[:page]) end def show - @projects = @repository.projects.recent.paginate :page => params[:project_page], :per_page => 30 - @projects = @projects.search(params[:query]).search_order if params[:query].present? + @projects = @repository.projects.recent.search(params[:query]) + .paginate(:page => params[:project_page], :per_page => 30) end def edit @@ -68,8 +70,7 @@ class Platforms::RepositoriesController < Platforms::BaseController end def create - @repository = Repository.new(params[:repository]) - @repository.platform_id = params[:platform_id] + @repository = @platform.repositories.build(params[:repository]) if @repository.save flash[:notice] = t('flash.repository.saved') redirect_to platform_repository_path(@platform, @repository) @@ -81,13 +82,22 @@ class Platforms::RepositoriesController < Platforms::BaseController end def add_project - if params[:project_id] + if projects_list = params.try(:[], :repository).try(:[], :projects_list) + @repository.add_projects projects_list, current_user + redirect_to platform_repository_path(@platform, @repository), :notice => t('flash.repository.projects_will_be_added') + return + end + if params[:project_id].present? @project = Project.find(params[:project_id]) - begin - @repository.projects << @project - flash[:notice] = t('flash.repository.project_added') - rescue ActiveRecord::RecordInvalid - flash[:error] = t('flash.repository.project_not_added') + if can?(:read, @project) + begin + @repository.projects << @project + flash[:notice] = t('flash.repository.project_added') + rescue ActiveRecord::RecordInvalid + flash[:error] = t('flash.repository.project_not_added') + end + else + flash[:error] = t('flash.repository.no_access_to_read_project') end redirect_to platform_repository_path(@platform, @repository) else @@ -116,7 +126,7 @@ class Platforms::RepositoriesController < Platforms::BaseController @projects = @repository.projects else @projects = Project.joins(owner_subquery).addable_to_repository(@repository.id) - @projects = @projects.by_visibilities('open') if @repository.platform.platform_type == 'main' + @projects = @projects.opened if @repository.platform.main? && !@repository.platform.hidden? end @projects = @projects.paginate( :page => (params[:iDisplayStart].to_i/(params[:iDisplayLength].present? ? params[:iDisplayLength] : 25).to_i).to_i + 1, @@ -124,8 +134,7 @@ class Platforms::RepositoriesController < Platforms::BaseController ) @total_projects = @projects.count - @projects = @projects.search(params[:sSearch]).search_order if params[:sSearch].present? - @projects = @projects.order(order) + @projects = @projects.search(params[:sSearch]).order(order) respond_to do |format| format.json { @@ -135,12 +144,18 @@ class Platforms::RepositoriesController < Platforms::BaseController end def remove_project - ProjectToRepository.where(:project_id => params[:project_id], :repository_id => @repository.id).destroy_all - redirect_to platform_repository_path(@platform, @repository), :notice => t('flash.repository.project_removed') + if projects_list = params.try(:[], :repository).try(:[], :projects_list) + @repository.remove_projects projects_list + redirect_to platform_repository_path(@platform, @repository), :notice => t('flash.repository.projects_will_be_removed') + end + if params[:project_id].present? + ProjectToRepository.where(:project_id => params[:project_id], :repository_id => @repository.id).destroy_all + redirect_to platform_repository_path(@platform, @repository), :notice => t('flash.repository.project_removed') + end end def regenerate_metadata - if AbfWorker::BuildListsPublishTaskManager.repository_regenerate_metadata @repository.id + if @repository.regenerate(params[:build_for_platform_id]) flash[:notice] = t('flash.repository.regenerate_in_queue') else flash[:error] = t('flash.repository.regenerate_already_in_queue') @@ -148,6 +163,17 @@ class Platforms::RepositoriesController < Platforms::BaseController redirect_to platform_repository_path(@platform, @repository) end + def sync_lock_file + if params[:remove] + @repository.remove_sync_lock_file + flash[:notice] = t('flash.repository.sync_lock_file_removed') + else + flash[:notice] = t('flash.repository.sync_lock_file_added') + @repository.add_sync_lock_file + end + redirect_to edit_platform_repository_path(@platform, @repository) + end + protected def set_members diff --git a/app/controllers/platforms/tokens_controller.rb b/app/controllers/platforms/tokens_controller.rb new file mode 100644 index 000000000..d192b9906 --- /dev/null +++ b/app/controllers/platforms/tokens_controller.rb @@ -0,0 +1,42 @@ +class Platforms::TokensController < Platforms::BaseController + before_filter :authenticate_user! + + load_resource :platform + load_and_authorize_resource :through => :platform, :shallow => true + + def index + authorize! :local_admin_manage, @platform + @tokens = @platform.tokens.includes(:creator, :updater) + .paginate(:per_page => 20, :page => params[:page]) + end + + def show + end + + def withdraw + if @token.block + @token.updater = current_user + @token.save + redirect_to :back, :notice => t('flash.tokens.withdraw_success') + else + redirect_to :back, :notice => t('flash.tokens.withdraw_fail') + end + end + + def new + end + + def create + @token = @platform.tokens.build params[:token] + @token.creator = current_user + if @token.save + flash[:notice] = t('flash.tokens.saved') + redirect_to platform_tokens_path(@platform) + else + flash[:error] = t('flash.tokens.save_error') + flash[:warning] = @token.errors.full_messages.join('. ') unless @token.errors.blank? + render :new + end + end + +end diff --git a/app/controllers/projects/base_controller.rb b/app/controllers/projects/base_controller.rb index bc51abb9e..878eb086e 100644 --- a/app/controllers/projects/base_controller.rb +++ b/app/controllers/projects/base_controller.rb @@ -12,7 +12,7 @@ class Projects::BaseController < ApplicationController end def find_project - @project = Project.find_by_owner_and_name!(params[:owner_name], params[:project_name]) if params[:owner_name] && params[:project_name] + @project = Project.find_by_owner_and_name!(params[:owner_name], params[:project_name]) if params[:owner_name].present? && params[:project_name].present? end def init_statistics diff --git a/app/controllers/projects/build_lists_controller.rb b/app/controllers/projects/build_lists_controller.rb index 743265d69..5e9f0abc9 100644 --- a/app/controllers/projects/build_lists_controller.rb +++ b/app/controllers/projects/build_lists_controller.rb @@ -1,8 +1,10 @@ class Projects::BuildListsController < Projects::BaseController - NESTED_ACTIONS = [:search, :index, :new, :create] + include FileStoreHelper + + NESTED_ACTIONS = [:index, :new, :create] before_filter :authenticate_user! - skip_before_filter :authenticate_user!, :only => [:show, :index, :search, :log] if APP_CONFIG['anonymous_access'] + skip_before_filter :authenticate_user!, :only => [:show, :index, :log] if APP_CONFIG['anonymous_access'] before_filter :find_build_list, :only => [:show, :publish, :cancel, :update, :log, :create_container] @@ -10,30 +12,41 @@ class Projects::BuildListsController < Projects::BaseController load_and_authorize_resource :build_list, :through => :project, :only => NESTED_ACTIONS, :shallow => true load_and_authorize_resource :except => NESTED_ACTIONS - def search - new_params = {:filter => {}} - params[:filter].each do |k,v| - new_params[:filter][k] = v unless v.empty? - end - new_params[:per_page] = params[:per_page] if params[:per_page].present? - redirect_to @project ? project_build_lists_path(@project, new_params) : build_lists_path(new_params) - end + before_filter :create_from_build_list, :only => :new def index - @action_url = @project ? search_project_build_lists_path(@project) : search_build_lists_path - @filter = BuildList::Filter.new(@project, current_user, params[:filter] || {}) + params[:filter].each{|k,v| params[:filter].delete(k) if v.blank? } if params[:filter] - page = params[:page].to_i == 0 ? nil : params[:page] - @per_page = BuildList::Filter::PER_PAGE.include?(params[:per_page].to_i) ? params[:per_page].to_i : 25 - @bls = @filter.find.recent.paginate :page => page, :per_page => @per_page - @build_lists = BuildList.where(:id => @bls.pluck("#{BuildList.table_name}.id")).recent - @build_lists = @build_lists.includes [:save_to_platform, :save_to_repository, :arch, :user, :project => [:owner]] + respond_to do |format| + format.html + format.json do + @filter = BuildList::Filter.new(@project, current_user, current_ability, params[:filter] || {}) + @bls = @filter.find.recent + .paginate( + :page => (params[:page].to_i == 0 ? nil : params[:page]), + :per_page => BuildList::Filter::PER_PAGE.include?(params[:per_page].to_i) ? params[:per_page].to_i : 25 + ) + @build_lists = BuildList.where(:id => @bls.pluck(:id)).recent + .includes( + :save_to_platform, + :save_to_repository, + :build_for_platform, + :user, + :source_packages, + :project + ) - @build_server_status = AbfWorker::StatusInspector.projects_status + @build_server_status = AbfWorker::StatusInspector.projects_status + end + end end def new - # @build_list = BuildList.new # @build_list already created by CanCan + if params[:show] == 'inline' && params[:build_list_id].present? + render '_new_form', :layout => false, :locals => {:project => @project, :build_list => @build_list} + else + render :new + end end def create @@ -45,10 +58,10 @@ class Projects::BuildListsController < Projects::BaseController params[:build_list][:save_to_platform_id] = @platform.id params[:build_list][:auto_publish] = false unless @repository.publish_without_qa? - build_for_platforms = Repository.select(:platform_id). where(:id => params[:build_list][:include_repos]).group(:platform_id).map(&:platform_id) + build_lists = [] Arch.where(:id => params[:arches]).each do |arch| Platform.main.where(:id => build_for_platforms).each do |build_for_platform| @build_list = @project.build_lists.build(params[:build_list]) @@ -57,7 +70,8 @@ class Projects::BuildListsController < Projects::BaseController @build_list.priority = current_user.build_priority # User builds more priority than mass rebuild with zero priority flash_options = {:project_version => @build_list.project_version, :arch => arch.name, :build_for_platform => build_for_platform.name} - if @build_list.save + if authorize!(:create, @build_list) && @build_list.save + build_lists << @build_list notices << t("flash.build_list.saved", flash_options) else errors << t("flash.build_list.save_error", flash_options) @@ -70,6 +84,7 @@ class Projects::BuildListsController < Projects::BaseController flash[:error] = errors.join('
').html_safe render :action => :new else + BuildList.where(:id => build_lists.map(&:id)).update_all(:group_id => build_lists[0].id) if build_lists.size > 1 flash[:notice] = notices.join('
').html_safe redirect_to project_build_lists_path(@project) end @@ -79,75 +94,6 @@ class Projects::BuildListsController < Projects::BaseController @item_groups = @build_list.items.group_by_level end - def update - if params[:publish].present? and can?(:publish, @build_list) - publish - elsif params[:reject_publish].present? and can?(:reject_publish, @build_list) - reject_publish - else - # King Arthur, we are under attack! - redirect_to :forbidden and return - end - end - - def create_container - if @build_list.publish_container - redirect_to :back, :notice => t('layout.build_lists.create_container_success') - else - redirect_to :back, :notice => t('layout.build_lists.create_container_fail') - end - end - - def cancel - if @build_list.cancel - redirect_to :back, :notice => t('layout.build_lists.will_be_canceled') - else - redirect_to :back, :notice => t('layout.build_lists.cancel_fail') - end - end - - def log - render :json => { - :log => @build_list.log(params[:load_lines]), - :building => @build_list.build_started? - } - end - - def autocomplete_to_extra_repos_and_builds - results, save_to_platform = [], Platform.find(params[:platform_id]) - bl = BuildList.where(:id => params[:term]).published_container.accessible_by(current_ability, :read) - if save_to_platform.main? - bl = bl.where(:save_to_platform_id => save_to_platform.id) - else - platforms = Platform.includes(:repositories).search(params[:term]). - accessible_by(current_ability, :read).search_order.limit(5) - platforms.each{ |p| p.repositories.each{ |r| results << {:id => r.id, :label => "#{p.name}/#{r.name}", :value => "#{p.name}/#{r.name}"} } } - end - bl = bl.first - results << {:id => "#{bl.id}-build-list", :value => bl.id, :label => "#{bl.id} (#{bl.project.name} - #{bl.arch.name})"} if bl - render json: results.to_json - end - - def update_extra_repos_and_builds - results, save_to_repository = [], Repository.find(params[:build_list][:save_to_repository_id]) - extra_repos = params[:build_list][:extra_repositories] || [] - extra_bls = params[:build_list][:extra_build_lists] || [] - (params[:extra_repo].gsub!(/-build-list$/, '') ? extra_bls : extra_repos) << params[:extra_repo] - build_lists = BuildList.where(:id => extra_bls).published_container.accessible_by(current_ability, :read) - if save_to_repository.platform.main? - build_lists = build_lists.where(:save_to_platform_id => save_to_repository.platform_id) - else - results.concat Repository.where(:id => extra_repos).accessible_by(current_ability, :read) - end - render :partial => 'extra', :collection => results.concat(build_lists) - end - - protected - - def find_build_list - @build_list = BuildList.find(params[:id]) - end - def publish @build_list.update_type = params[:build_list][:update_type] if params[:build_list][:update_type].present? @@ -169,22 +115,74 @@ class Projects::BuildListsController < Projects::BaseController redirect_to :back, :notice => t('layout.build_lists.publish_fail') and return end end - end @build_list.publisher = current_user - if @build_list.save && @build_list.can_publish? && @build_list.now_publish - redirect_to :back, :notice => t('layout.build_lists.publish_success') - else - redirect_to :back, :notice => t('layout.build_lists.publish_fail') - end + message = @build_list.publish ? 'success' : 'fail' + redirect_to :back, :notice => t("layout.build_lists.publish_#{message}") + end + + def publish_into_testing + @build_list.publisher = current_user + message = @build_list.publish_into_testing ? 'success' : 'fail' + redirect_to :back, :notice => t("layout.build_lists.publish_#{message}") end def reject_publish - if @build_list.reject_publish - redirect_to :back, :notice => t('layout.build_lists.reject_publish_success') - else - redirect_to :back, :notice => t('layout.build_lists.reject_publish_fail') - end + @build_list.publisher = current_user + message = @build_list.reject_publish ? 'success' : 'fail' + redirect_to :back, :notice => t("layout.build_lists.reject_publish_#{message}") + end + + def create_container + message = @build_list.publish_container ? 'success' : 'fail' + redirect_to :back, :notice => t("layout.build_lists.create_container_#{message}") + end + + def cancel + message = @build_list.cancel ? 'will_be_canceled' : 'cancel_fail' + redirect_to :back, :notice => t("layout.build_lists.#{message}") + end + + def log + render :json => { + :log => @build_list.log(params[:load_lines]), + :building => @build_list.build_started? + } + end + + def list + @build_lists = @project.build_lists + sort_col = params[:ol_0] || 7 + sort_dir = params[:sSortDir_0] == 'asc' ? 'asc' : 'desc' + order = "build_lists.updated_at #{sort_dir}" + + @build_lists = @build_lists.paginate(:page => (params[:iDisplayStart].to_i/params[:iDisplayLength].to_i).to_i + 1, :per_page => params[:iDisplayLength]) + @total_build_lists = @build_lists.count + @build_lists = @build_lists.where(:user_id => current_user) if params[:owner_filter] == 'true' + @build_lists = @build_lists.where(:status => [BuildList::BUILD_ERROR, BuildList::FAILED_PUBLISH, BuildList::REJECTED_PUBLISH]) if params[:status_filter] == 'true' + @build_lists = @build_lists.order(order) + + render :partial => 'build_lists_ajax', :layout => false + end + + + protected + + def find_build_list + @build_list = BuildList.find(params[:id]) + end + + def create_from_build_list + return if params[:build_list_id].blank? + build_list = BuildList.find(params[:build_list_id]) + + params[:build_list] ||= {} + keys = [:save_to_repository_id, :auto_publish, :include_repos, :extra_params, + :project_version, :update_type, :auto_create_container, + :extra_repositories, :extra_build_lists, :build_for_platform_id] + keys.each { |key| params[:build_list][key] = build_list.send(key) } + params[:arches] = [build_list.arch_id.to_s] + [:owner_filter, :status_filter].each { |t| params[t] = 'true' if %w(true undefined).exclude? params[t] } end end diff --git a/app/controllers/projects/collaborators_controller.rb b/app/controllers/projects/collaborators_controller.rb index d86e83e26..15fffe511 100644 --- a/app/controllers/projects/collaborators_controller.rb +++ b/app/controllers/projects/collaborators_controller.rb @@ -13,12 +13,6 @@ class Projects::CollaboratorsController < Projects::BaseController respond_with @collaborators end - def show - end - - def new - end - def find users = User.not_member_of(@project) groups = Group.not_member_of(@project) @@ -32,9 +26,6 @@ class Projects::CollaboratorsController < Projects::BaseController end end - def edit - end - def create @collaborator = Collaborator.new(params[:collaborator]) @collaborator.project = @project diff --git a/app/controllers/projects/comments_controller.rb b/app/controllers/projects/comments_controller.rb index cf019ace9..4a79b86d5 100644 --- a/app/controllers/projects/comments_controller.rb +++ b/app/controllers/projects/comments_controller.rb @@ -52,7 +52,7 @@ class Projects::CommentsController < Projects::BaseController end def find_or_build_comment - @comment = params[:id].present? && Comment.find(params[:id]) || + @comment = params[:id].present? && Comment.where(:automatic => false).find(params[:id]) || current_user.comments.build(params[:comment]) {|c| c.commentable = @commentable; c.project = @project} end end diff --git a/app/controllers/projects/git/base_controller.rb b/app/controllers/projects/git/base_controller.rb index eb843043b..b93b3c0ad 100644 --- a/app/controllers/projects/git/base_controller.rb +++ b/app/controllers/projects/git/base_controller.rb @@ -1,8 +1,8 @@ class Projects::Git::BaseController < Projects::BaseController before_filter :authenticate_user! skip_before_filter :authenticate_user!, :only => [:show, :index, :blame, :raw, :archive, :diff, :tags, :branches] if APP_CONFIG['anonymous_access'] - load_and_authorize_resource :project + load_and_authorize_resource :project before_filter :set_treeish_and_path before_filter :set_branch_and_tree diff --git a/app/controllers/projects/git/blobs_controller.rb b/app/controllers/projects/git/blobs_controller.rb index 6c930eb42..1d7ec3fcd 100644 --- a/app/controllers/projects/git/blobs_controller.rb +++ b/app/controllers/projects/git/blobs_controller.rb @@ -32,6 +32,7 @@ class Projects::Git::BlobsController < Projects::Git::BaseController def set_blob @blob = @tree / @path or raise Grit::NoSuchPathError + redirect_to tree_path(@project, :treeish => @treeish, :path => @path) if @blob.is_a? Grit::Tree @commit = @project.repo.log(@treeish, @path, :max_count => 1).first end end diff --git a/app/controllers/projects/git/trees_controller.rb b/app/controllers/projects/git/trees_controller.rb index a6df09571..7f570f3a0 100644 --- a/app/controllers/projects/git/trees_controller.rb +++ b/app/controllers/projects/git/trees_controller.rb @@ -1,12 +1,18 @@ class Projects::Git::TreesController < Projects::Git::BaseController before_filter lambda{redirect_to @project if params[:treeish] == @project.default_branch and params[:path].blank?}, :only => :show skip_before_filter :set_branch_and_tree, :set_treeish_and_path, :only => :archive + before_filter lambda { raise Grit::NoSuchPathError if params[:treeish] != @branch.try(:name) }, :only => [:branch, :destroy] + + skip_authorize_resource :project, :only => [:destroy, :restore_branch, :create] + before_filter lambda { authorize!(:write, @project) }, :only => [:destroy, :restore_branch, :create] def show - render('empty') and return if @project.is_empty? - @tree = @tree / @path if @path.present? - @commit = @branch.present? ? @branch.commit() : @project.repo.log(@treeish, @path, :max_count => 1).first - raise Grit::NoSuchPathError unless @commit + unless request.xhr? + render('empty') and return if @project.is_empty? + @tree = @tree / @path if @path.present? + @commit = @branch.present? ? @branch.commit() : @project.repo.log(@treeish, @path, :max_count => 1).first + raise Grit::NoSuchPathError unless @commit + end end def archive @@ -27,14 +33,42 @@ class Projects::Git::TreesController < Projects::Git::BaseController end def tags - @tags = @project.repo.tags.select{ |t| t.commit }.sort_by(&:name).reverse - render 'refs' + if request.xhr? + @refs = @project.repo.tags.select{ |t| t.commit }.sort_by(&:name).reverse + render :refs_list + else + respond_to do |format| + format.json { render :nothing => true, :status => 422 } + format.html + end + end + end + + def restore_branch + status = @project.create_branch(@treeish, params[:sha], current_user) ? 200 : 422 + render :nothing => true, :status => status + end + + def create + status = @project.create_branch(params[:new_ref], params[:from_ref], current_user) ? 200 : 422 + render :nothing => true, :status => status + end + + def destroy + status = @branch && @project.delete_branch(@branch, current_user) ? 200 : 422 + render :nothing => true, :status => status end def branches - raise Grit::NoSuchPathError if params[:treeish] != @branch.try(:name) # get wrong branch name to nonempty project - @branches = @project.repo.branches.sort_by(&:name).select{ |b| b.name != @branch.name }.unshift(@branch).compact if @branch - render 'refs' + if request.xhr? + @refs = @project.repo.branches.sort_by(&:name) + render :refs_list + else + respond_to do |format| + format.json { render :nothing => true, :status => 422 } + format.html + end + end end end diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb new file mode 100644 index 000000000..f1e12555e --- /dev/null +++ b/app/controllers/projects/hooks_controller.rb @@ -0,0 +1,46 @@ +# -*- encoding : utf-8 -*- +class Projects::HooksController < Projects::BaseController + before_filter :authenticate_user! + load_and_authorize_resource :project + load_and_authorize_resource :hook, :through => :project + + + def index + authorize! :edit, @project + @name = params[:name] + @hooks = @project.hooks.for_name(@name).order('name asc, created_at desc') + render(:show) if @name.present? + end + + def new + end + + def edit + end + + def create + if @hook.save + redirect_to project_hooks_path(@project, :name => @hook.name), :notice => t('flash.hook.created') + else + flash[:error] = t('flash.hook.save_error') + flash[:warning] = @hook.errors.full_messages.join('. ') + render :new + end + end + + def update + if @hook.update_attributes(params[:hook]) + redirect_to project_hooks_path(@project, :name => @hook.name), :notice => t('flash.hook.updated') + else + flash[:error] = t('flash.hook.save_error') + flash[:warning] = @hook.errors.full_messages.join('. ') + render :edit + end + end + + def destroy + @hook.destroy + redirect_to project_hooks_path(@project, :name => @hook.name) + end + +end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index c9681f1e3..873a914fc 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -12,7 +12,7 @@ class Projects::IssuesController < Projects::BaseController def index(status = 200) @labels = params[:labels] || [] @issues = @project.issues.without_pull_requests - @issues = @issues.where(:assignee_id => current_user.id) if @is_assigned_to_me = params[:filter] == 'to_me' + @issues = @issues.where(:assignee_id => current_user.id) if @is_assigned_to_me = params[:filter] == 'assigned' @issues = @issues.joins(:labels).where(:labels => {:name => @labels}) unless @labels == [] # Using mb_chars for correct transform to lowercase ('Русский Текст'.downcase => "Русский Текст") @issues = @issues.search(params[:search_issue]) if params[:search_issue] !~ /#{t('layout.issues.search')}/ @@ -37,9 +37,12 @@ class Projects::IssuesController < Projects::BaseController end def create - @assignee_uname = params[:assignee_uname] @issue.user_id = current_user.id + unless can?(:write, @project) + @issue.assignee_id = nil + @issue.labelings = [] + end if @issue.save @issue.subscribe_creator(current_user.id) flash[:notice] = I18n.t("flash.issue.saved") @@ -55,6 +58,12 @@ class Projects::IssuesController < Projects::BaseController end def update + unless can?(:write, @project) + params.delete :update_labels + [:assignee_id, :labelings, :labelings_attributes].each do |k| + params[:issue].delete k + end if params[:issue] + end @issue.labelings.destroy_all if params[:update_labels] if params[:issue] && status = params[:issue][:status] @issue.set_close(current_user) if status == 'closed' diff --git a/app/controllers/projects/projects_controller.rb b/app/controllers/projects/projects_controller.rb index f8c509109..9d74586b1 100644 --- a/app/controllers/projects/projects_controller.rb +++ b/app/controllers/projects/projects_controller.rb @@ -2,6 +2,7 @@ class Projects::ProjectsController < Projects::BaseController include ProjectsHelper before_filter :authenticate_user! load_and_authorize_resource :id_param => :project_name # to force member actions load + before_filter :who_owns, :only => [:new, :create, :mass_import, :run_mass_import] def index @projects = Project.accessible_by(current_ability, :membered) @@ -23,7 +24,27 @@ class Projects::ProjectsController < Projects::BaseController def new @project = Project.new - @who_owns = :me + end + + def mass_import + @project = Project.new(:mass_import => true) + end + + def run_mass_import + @project = Project.new params[:project] + @project.owner = choose_owner + authorize! :write, @project.owner if @project.owner.class == Group + authorize! :add_project, Repository.find(params[:project][:add_to_repository_id]) + @project.valid? + @project.errors.messages.slice! :url + if @project.errors.messages.blank? # We need only url validation + @project.init_mass_import + flash[:notice] = t('flash.project.mass_import_added_to_queue') + redirect_to projects_path + else + flash[:warning] = @project.errors.full_messages.join('. ') + render :mass_import + end end def edit @@ -32,8 +53,7 @@ class Projects::ProjectsController < Projects::BaseController def create @project = Project.new params[:project] @project.owner = choose_owner - @who_owns = (@project.owner_type == 'User' ? :me : :group) - authorize! :update, @project.owner if @project.owner.class == Group + authorize! :write, @project.owner if @project.owner.class == Group if @project.save flash[:notice] = t('flash.project.saved') @@ -66,16 +86,21 @@ class Projects::ProjectsController < Projects::BaseController def fork owner = (Group.find params[:group] if params[:group].present?) || current_user - authorize! :update, owner if owner.class == Group - if forked = @project.fork(owner) and forked.valid? + authorize! :write, owner if owner.class == Group + if forked = @project.fork(owner, params[:fork_name]) and forked.valid? redirect_to forked, :notice => t("flash.project.forked") else flash[:warning] = t("flash.project.fork_error") - flash[:error] = forked.errors.full_messages + flash[:error] = forked.errors.full_messages.join("\n") redirect_to @project end end + def possible_forks + render :partial => 'projects/git/base/forks', :layout => false, + :locals => { :owner => current_user, :name => (params[:name].presence || @project.name) } + end + def sections if request.post? if @project.update_attributes(params[:project]) @@ -114,6 +139,10 @@ class Projects::ProjectsController < Projects::BaseController protected + def who_owns + @who_owns = (@project.try(:owner_type) == 'User' ? :me : :group) + end + def prepare_list(projects, groups, owners) res = {} @@ -128,7 +157,7 @@ class Projects::ProjectsController < Projects::BaseController projects = projects.by_owners(groups, owners) end - projects = projects.search(params[:sSearch]).search_order if params[:sSearch].present? + projects = projects.search(params[:sSearch]) res[:filtered_count] = projects.count diff --git a/app/controllers/projects/pull_requests_controller.rb b/app/controllers/projects/pull_requests_controller.rb index 47a9714de..bc470aa6a 100644 --- a/app/controllers/projects/pull_requests_controller.rb +++ b/app/controllers/projects/pull_requests_controller.rb @@ -37,13 +37,14 @@ class Projects::PullRequestsController < Projects::BaseController authorize! :read, to_project @pull = to_project.pull_requests.new pull_params - @pull.issue.assignee_id = (params[:issue] || {})[:assignee_id] + @pull.issue.assignee_id = (params[:issue] || {})[:assignee_id] if can?(:write, to_project) @pull.issue.user, @pull.issue.project, @pull.from_project = current_user, to_project, @project @pull.from_project_owner_uname = @pull.from_project.owner.uname @pull.from_project_name = @pull.from_project.name if @pull.valid? # FIXME more clean/clever logics @pull.save # set pull id + @pull.reload @pull.check(false) # don't make event transaction if @pull.already? @pull.destroy @@ -65,28 +66,32 @@ class Projects::PullRequestsController < Projects::BaseController end end + def merge + status = @pull.merge!(current_user) ? 200 : 422 + render :nothing => true, :status => status + end + def update + status = 422 if (action = params[:pull_request_action]) && %w(close reopen).include?(params[:pull_request_action]) if @pull.send("can_#{action}?") @pull.set_user_and_time current_user @pull.send(action) @pull.check if @pull.open? + status = 200 end end - redirect_to project_pull_request_path(@pull.to_project, @pull) - end - - def merge - @pull.check - unless @pull.merge!(current_user) - flash.now[:error] = t('flash.pull_request.save_error') - flash.now[:warning] = @pull.errors.full_messages.join('. ') - end - redirect_to project_pull_request_path(@pull.to_project, @pull) + render :nothing => true, :status => status end def show - load_diff_commits_data + unless request.xhr? + if @pull.nil? + redirect_to project_issue_path(@project, @issue) + else + load_diff_commits_data + end + end end def index(status = 200) @@ -114,9 +119,12 @@ class Projects::PullRequestsController < Projects::BaseController end def autocomplete_to_project - items = Project.accessible_by(current_ability, :membered) | @project.ancestors - term = Regexp.new(Regexp.escape params[:term].downcase) - items.select! {|e| term.match(e.name_with_owner.downcase) && e.repo.branches.count > 0} + items = [] + term = params[:term].to_s.strip.downcase + [Project.accessible_by(current_ability, :membered), @project.ancestors].each do |p| + items.concat p.by_owner_and_name(term) + end + items = items.uniq{|i| i.id}.select{|e| e.repo.branches.count > 0} render :json => json_for_autocomplete_base(items) end @@ -128,7 +136,7 @@ class Projects::PullRequestsController < Projects::BaseController def json_for_autocomplete_base items items.collect do |project| - hash = {"id" => project.id.to_s, "label" => project.name_with_owner, "value" => project.name_with_owner} + hash = {:id => project.id.to_s, :label => project.name_with_owner, :value => project.name_with_owner} hash[:get_refs_url] = project_refs_list_path(project) hash end diff --git a/app/controllers/projects/wiki_controller.rb b/app/controllers/projects/wiki_controller.rb index b53b7aa28..5af88e8bd 100644 --- a/app/controllers/projects/wiki_controller.rb +++ b/app/controllers/projects/wiki_controller.rb @@ -244,7 +244,7 @@ class Projects::WikiController < Projects::BaseController when 'revert' then "Reverted page #{@name.to_s}" when 'revert_wiki' then "Reverted wiki" end - msg += " (#{params['format']})" if params['format'] + msg << " (#{params['format']})" if params['format'] end msg = 'Unhandled action' if !msg || msg.empty? { :message => msg } diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb index 55665e78d..85a80aa15 100644 --- a/app/controllers/users/omniauth_callbacks_controller.rb +++ b/app/controllers/users/omniauth_callbacks_controller.rb @@ -1,29 +1,63 @@ class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController - def open_id - # raise env['omniauth.auth'].inspect - generic + + def facebook + oauthorize 'Facebook' + end + + def google_oauth2 + oauthorize 'google_oauth2' + end + + def github + oauthorize 'GitHub' end def passthru render :file => "#{Rails.root}/public/404.html", :status => 404, :layout => false end + + private - protected - - def generic - authentication = Authentication.find_or_initialize_by_provider_and_uid(env['omniauth.auth']['provider'], env['omniauth.auth']['uid']) + def oauthorize(kind) + provider = kind.downcase + @user = find_for_ouath(env["omniauth.auth"], current_user) + if @user && @user.persisted? + flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => action_name.classify + sign_in_and_redirect @user, :event => :authentication + else + session["devise.#{provider}_data"] = env["omniauth.auth"] + redirect_to new_user_registration_url + end + end + + def find_for_ouath(auth, resource=nil) + provider, uid = auth['provider'], auth['uid'] + authentication = Authentication.find_or_initialize_by_provider_and_uid(provider, uid) if authentication.new_record? if user_signed_in? # New authentication method for current_user authentication.user = current_user - authentication.save else # Register new user from session - session["devise.omniauth_data"] = env["omniauth.auth"].except('extra') - flash[:notice] = I18n.t "devise.omniauth_callbacks.register" - redirect_to new_user_registration_url + case provider + when 'facebook' + name = auth['extra']['raw_info']['name'] + when 'google_oauth2', 'github' + name = auth['info']['nickname'] || auth['info']['name'] + else + raise 'Provider #{provider} not handled' + end + user = User.find_or_initialize_by_email(auth['info']['email']) + if user.new_record? + user.name = name + user.uname = name.gsub(/\s/, '').underscore + user.password = Devise.friendly_token[0,20] + user.confirmed_at = Time.zone.now + user.save + end + authentication.user = user end - else - flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => action_name.classify - sign_in_and_redirect authentication.user, :event => :authentication + authentication.save end + return authentication.user end + end diff --git a/app/controllers/users/profile_controller.rb b/app/controllers/users/profile_controller.rb index f17dcc0fa..e632e52b5 100644 --- a/app/controllers/users/profile_controller.rb +++ b/app/controllers/users/profile_controller.rb @@ -2,8 +2,24 @@ class Users::ProfileController < Users::BaseController skip_before_filter :authenticate_user!, :only => :show if APP_CONFIG['anonymous_access'] def show - @projects = @user.projects.by_visibilities(['open']). - search(params[:search]).search_order. - paginate(:page => params[:page], :per_page => 25) + @path, page = user_path, params[:page].to_i + @projects = @user.own_projects.search(params[:search]).recent + if request.xhr? + if params[:visibility] != 'hidden' + @projects = @projects.opened + @hidden = true + else + @projects = @projects.by_visibilities('hidden').accessible_by(current_ability, :read) + end + render :partial => 'shared/profile_projects', :layout => nil, :locals => {:projects => paginate_projects(page)} + else + @projects = paginate_projects(page) + end + end + + protected + + def paginate_projects(page) + @projects.paginate(:page => (page>0 ? page : nil), :per_page => 24) end end diff --git a/app/controllers/users/register_requests_controller.rb b/app/controllers/users/register_requests_controller.rb index 2112c3893..ee79d8940 100644 --- a/app/controllers/users/register_requests_controller.rb +++ b/app/controllers/users/register_requests_controller.rb @@ -3,7 +3,11 @@ class Users::RegisterRequestsController < ApplicationController layout 'invite' def new - render :invite + if APP_CONFIG['preregistration'] + render :invite + else + redirect_to new_user_registration_path + end end def create diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb new file mode 100644 index 000000000..a081a2a59 --- /dev/null +++ b/app/controllers/users/registrations_controller.rb @@ -0,0 +1,11 @@ +class Users::RegistrationsController < Devise::RegistrationsController + # POST /resource + def create + # Try stop bots + if params[:recaptcha_response_field].present? + respond_with(resource, :location => after_inactive_sign_up_path_for(resource)) + return + end + super + end +end \ No newline at end of file diff --git a/app/controllers/users/settings_controller.rb b/app/controllers/users/settings_controller.rb index 8c6fe18d1..70b3293e8 100644 --- a/app/controllers/users/settings_controller.rb +++ b/app/controllers/users/settings_controller.rb @@ -20,6 +20,12 @@ class Users::SettingsController < Users::BaseController end end + def reset_auth_token + @user.reset_authentication_token! + flash[:notice] = t("flash.user.reset_auth_token") + redirect_to profile_settings_path + end + def private if request.put? if @user.update_with_password(params[:user]) diff --git a/app/controllers/users/users_controller.rb b/app/controllers/users/users_controller.rb index b1961731c..e4eca24a6 100644 --- a/app/controllers/users/users_controller.rb +++ b/app/controllers/users/users_controller.rb @@ -1,5 +1,5 @@ class Users::UsersController < Users::BaseController - skip_before_filter :authenticate_user! + skip_before_filter :authenticate_user!, :only => [:allowed, :check, :discover] before_filter :find_user_by_key, :only => [:allowed, :discover] def allowed diff --git a/app/helpers/activity_feeds_helper.rb b/app/helpers/activity_feeds_helper.rb index b8a979c3b..060645432 100644 --- a/app/helpers/activity_feeds_helper.rb +++ b/app/helpers/activity_feeds_helper.rb @@ -10,7 +10,7 @@ module ActivityFeedsHelper feed_title = feed_title.gsub(/\s{2,}/, ' ').strip end - def user_link user, user_name - user.persisted? ? link_to(user_name, user_path(user)) : user_name + def user_link(user, user_name, full_url = false) + user.persisted? ? link_to(user_name, full_url ? user_url(user) : user_path(user)) : user_name end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index cdb8090fa..ee29e7545 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -37,18 +37,20 @@ module ApplicationHelper end end - def markdown(text) - unless @redcarpet - html_options = {filter_html: true, hard_wrap: true, with_toc_data: true} - options = {no_intraemphasis: true, tables: true, fenced_code_blocks: true, autolink: true, strikethrough: true, lax_html_blocks: true} - @redcarpet = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new(html_options), options) - end - @redcarpet.render(text).html_safe - end - def local_alert(text, type = 'error') html = "
#{text}" html << link_to('×', '#', :class => 'close close-alert', 'data-dismiss' => 'alert') html << '
' end + + # Why 42? Because it is the Answer! + def short_message(message, length = 42) + truncate(message, :length => length, :omission => '…') + end + + def datetime_moment(date, options = {}) + tag = options[:tag] || :div + klass = "datetime_moment #{options[:class]}" + content_tag(tag, nil, :class => klass, :title => date.strftime('%Y-%m-%d %H:%M:%S UTC'), :origin_datetime => date.to_i) + end end diff --git a/app/helpers/build_lists_helper.rb b/app/helpers/build_lists_helper.rb index fb31b0ddd..5e5e7ec4b 100644 --- a/app/helpers/build_lists_helper.rb +++ b/app/helpers/build_lists_helper.rb @@ -1,9 +1,11 @@ module BuildListsHelper + + # See: app/assets/javascripts/angularjs/models/build_list.js.erb def build_list_status_color(status) case status - when BuildList::BUILD_PUBLISHED, BuildList::SUCCESS + when BuildList::BUILD_PUBLISHED, BuildList::SUCCESS, BuildList::BUILD_PUBLISHED_INTO_TESTING 'success' - when BuildList::BUILD_ERROR, BuildList::PROJECT_VERSION_NOT_FOUND, BuildList::FAILED_PUBLISH, BuildList::REJECTED_PUBLISH + when BuildList::BUILD_ERROR, BuildList::FAILED_PUBLISH, BuildList::REJECTED_PUBLISH, BuildList::FAILED_PUBLISH_INTO_TESTING 'error' when BuildList::TESTS_FAILED 'warning' @@ -12,6 +14,40 @@ module BuildListsHelper end end + def availables_main_platforms + # Main platforms with repositories + Platform.main.accessible_by(current_ability, :show) + .includes(:repositories).where('repositories.id IS NOT NULL').order('platforms.name').uniq + end + + def save_to_repositories(project) + project.repositories.collect do |r| + [ + "#{r.platform.name}/#{r.name}", + r.id, + { + :publish_without_qa => r.publish_without_qa? ? 1 : 0, + :platform_id => r.platform.id, + :default_arches => (r.platform.platform_arch_settings.by_default.pluck(:arch_id).presence || Arch.where(:name => Arch::DEFAULT).pluck(:id)).join(' ') + } + ] + end.sort_by { |col| col[0] } + end + + def external_nodes + BuildList::EXTERNAL_NODES.map do |type| + [I18n.t("layout.build_lists.external_nodes.#{type}"), type] + end + end + + def mass_build_options + options_from_collection_for_select( + MassBuild.recent.limit(15), + :id, + :name + ) + end + def build_list_options_for_new_core [ [I18n.t("layout.true_"), 1], @@ -23,7 +59,7 @@ module BuildListsHelper case status when BuildList::SUCCESS 'success' - when BuildList::DEPENDENCIES_ERROR, BuildList::BUILD_ERROR, BuildList::Item::GIT_ERROR + when BuildList::BUILD_ERROR, BuildList::Item::GIT_ERROR #, BuildList::DEPENDENCIES_ERROR 'error' else '' @@ -45,17 +81,19 @@ module BuildListsHelper hash_size=5 if item.version =~ /^[\da-z]+$/ && item.name == item.build_list.project.name bl = item.build_list - link_to str_version ? "#{shortest_hash_id item.version, hash_size}" : shortest_hash_id(item.version, hash_size), - commit_path(bl.project.owner, bl.project, item.version) + { + :text => str_version ? "#{shortest_hash_id item.version, hash_size}" : shortest_hash_id(item.version, hash_size), + :href => commit_path(bl.project.owner, bl.project, item.version) + } else - '' + {} end end def build_list_version_link(bl, str_version = false) hash_size=5 if bl.commit_hash.present? - if bl.last_published_commit_hash.present? + if bl.last_published_commit_hash.present? && bl.last_published_commit_hash != bl.commit_hash link_to "#{shortest_hash_id bl.last_published_commit_hash, hash_size}...#{shortest_hash_id bl.commit_hash, hash_size}", diff_path(bl.project.owner, bl.project, bl.last_published_commit_hash) + "...#{bl.commit_hash}" else @@ -76,16 +114,20 @@ module BuildListsHelper end end - def container_url(full_path = true, build_list = @build_list) - p = '' - p << "http://#{request.host_with_port}" if full_path - p << "/downloads/#{build_list.save_to_platform.name}/container/#{build_list.id}" - p << "/#{build_list.arch.name}/#{build_list.save_to_repository.name}/release" if full_path && build_list.build_for_platform.distrib_type == 'mdv' - p.html_safe + def container_url(build_list = @build_list) + url = "#{APP_CONFIG['downloads_url']}/#{build_list.save_to_platform.name}/container/#{build_list.id}/" + url << "#{build_list.arch.name}/#{build_list.save_to_repository.name}/release/" if build_list.build_for_platform.try(:distrib_type) == 'mdv' + url.html_safe end def can_publish_in_future?(bl) - [BuildList::SUCCESS, BuildList::FAILED_PUBLISH, BuildList::BUILD_PUBLISHED, BuildList::TESTS_FAILED].include?(bl.status) + [ + BuildList::SUCCESS, + BuildList::FAILED_PUBLISH, + BuildList::BUILD_PUBLISHED, + BuildList::TESTS_FAILED, + BuildList::BUILD_PUBLISHED_INTO_TESTING + ].include?(bl.status) end def log_reload_time_options @@ -99,7 +141,7 @@ module BuildListsHelper end def get_version_release build_list - pkg = build_list.packages.where(:package_type => 'source', :project_id => build_list.project_id).first + pkg = build_list.source_packages.first "#{pkg.version}-#{pkg.release}" if pkg.present? end end diff --git a/app/helpers/commit_helper.rb b/app/helpers/commit_helper.rb index b471a845d..b48c9581d 100644 --- a/app/helpers/commit_helper.rb +++ b/app/helpers/commit_helper.rb @@ -1,5 +1,4 @@ module CommitHelper - def render_commit_stats(stats) res = [""] ind=0 @@ -37,15 +36,20 @@ module CommitHelper id[0..size-1] end - def short_commit_message(message) - # Why 42? Because it is the Answer! - truncate(message, :length => 42, :omission => "...") - end - def commit_author_link(author) name = author.name email = author.email u = User.where(:email => email).first u.present? ? link_to(name, user_path(u)) : mail_to(email, name) end + + def commits_pluralize(commits_count) + Russian.p(commits_count, *commits_pluralization_arr) + end + + protected + + def commits_pluralization_arr + pluralize ||= t('layout.commits.pluralize').map {|base, title| title.to_s} + end end diff --git a/app/helpers/contents_helper.rb b/app/helpers/contents_helper.rb new file mode 100644 index 000000000..3ac256546 --- /dev/null +++ b/app/helpers/contents_helper.rb @@ -0,0 +1,26 @@ +# -*- encoding : utf-8 -*- +module ContentsHelper + + def build_content_paths(platform, path) + paths = ['/'] + paths |= path.split('/').select(&:present?) + paths.uniq! + + compound_path = '' + paths.map do |p| + compound_path << p << '/' if p != '/' + link_to(platform_content_path(platform, compound_path), {:remote => true}) do + content_tag(:span, p, {:class => 'text'}) + + content_tag(:span, '', {:class => 'arrow-right'}) + end + end.join.html_safe + end + + def platform_content_path(platform, path, name = nil) + full_path = platform_contents_path(platform) + full_path << '/' << path if path.present? + full_path << ('/' << name) if name.present? + full_path + end + +end diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 4685248e3..bc2e34b0a 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -36,12 +36,12 @@ module DiffHelper end prepare(args.merge({:filepath => filepath, :comments => comments, :in_discussion => in_discussion})) - res = "
" - res += "" - res += renderer diff_display.data #diff_display.render(Git::Diff::InlineCallback.new comments, path) - res += tr_line_comments(comments) if in_discussion - res += "" - res += "
" + res = '' + res << '' + res << renderer(diff_display.data) #diff_display.render(Git::Diff::InlineCallback.new comments, path) + res << tr_line_comments(comments) if in_discussion + res << '' + res << '
' res.html_safe end @@ -213,11 +213,11 @@ module DiffHelper res = '' if line.inline_changes? prefix, changed, postfix = line.segments.map{|segment| escape(segment) } - res += "#{prefix}#{changed}#{postfix}" + res << "#{prefix}#{changed}#{postfix}" else - res += escape(line) + res << escape(line) end - res += '' + res << '' res end @@ -228,7 +228,7 @@ module DiffHelper def line_comment return if @no_commit_comment || (@in_discussion && @add_reply_id && @line_comments[0].data[:line].to_i != @num_line) - link_to image_tag('line_comment.png', :alt => t('layout.comments.new_header')), new_comment_path, :class => 'add_line-comment' + link_to image_tag('line_comment.png', :alt => t('layout.comments.new_header')), new_comment_path, :class => 'add_line-comment' if current_user end def render_line_comments @@ -254,7 +254,7 @@ module DiffHelper #{render 'projects/comments/comment', :comment => comment, :data => {:project => @project, :commentable => @commentable, :add_anchor => 'inline', :in_discussion => @in_discussion}} " end - res << link_to(t('layout.comments.new_inline'), new_comment_path, :class => 'new_inline_comment button') + res << link_to(t('layout.comments.new_inline'), new_comment_path, :class => 'new_inline_comment button') if current_user res << "" end diff --git a/app/helpers/file_store_helper.rb b/app/helpers/file_store_helper.rb new file mode 100644 index 000000000..bb12c287e --- /dev/null +++ b/app/helpers/file_store_helper.rb @@ -0,0 +1,18 @@ +# -*- encoding : utf-8 -*- +module FileStoreHelper + + def file_store_results_url(sha1, file_name) + url = "#{APP_CONFIG['file_store_url']}/api/v1/file_stores/#{sha1}" + url << '.log?show=true' if file_name =~ /.*\.(log|txt)$/ + url + end + + def link_to_file_store(file_name, sha1) + if sha1.present? + link_to file_name, file_store_results_url(sha1, file_name) + else + I18n.t('layout.no_') + end + end + +end diff --git a/app/helpers/git_helper.rb b/app/helpers/git_helper.rb index 6229ab618..68a589198 100644 --- a/app/helpers/git_helper.rb +++ b/app/helpers/git_helper.rb @@ -1,5 +1,25 @@ module GitHelper + def submodule_url(node, treeish) + # node.url(treeish) looks like: + # - http://0.0.0.0:3000/abf/git@abf.rosalinux.ru:abf/rhel-scripts.git + # - git://github.com/avokhmin/mdv-scripts.git + # - empty string if ".gitmodules" does not exist + url = node.url(treeish) + return nil if url.blank? + url.gsub!(/.git$/, '') + if url =~ /^git:/ + url.gsub!(/^git/, 'http') + elsif str = /git@.*:.*/.match(url) + str = str[0].gsub(/^git@/, '') + domen = str.gsub(/:.*/, '') + owner = str.gsub(/^#{domen}:/, '').gsub(/\/.*/, '') + project = str.gsub(/.*\//, '') + url = "http://#{domen}/#{owner}/#{project}" + end + url + end + def render_path # TODO: Looks ugly, rewrite with clear mind. if @path.present? @@ -12,15 +32,15 @@ module GitHelper parts = @path.split("/") current_path = parts.first - res += parts.length == 1 ? parts.first : link_to(parts.first, tree_path(@project, @treeish, current_path)) + " / " + res << (parts.length == 1 ? parts.first : link_to(parts.first, tree_path(@project, @treeish, current_path)) + " / ") parts[1..-2].each do |part| current_path = File.join([current_path, part].compact) - res += link_to(part, tree_path(@project, @treeish, current_path)) - res += " / " + res << link_to(part, tree_path(@project, @treeish, current_path)) + res << " / " end - res += parts.last if parts.length > 1 + res << parts.last if parts.length > 1 else res = "#{link_to @project.name, tree_path(@project)} /" end @@ -30,7 +50,7 @@ module GitHelper def render_line_numbers(n) res = "" - 1.upto(n) {|i| res += "#{i}
" } + 1.upto(n){ |i| res << "#{i}
" } res.html_safe end @@ -56,12 +76,12 @@ module GitHelper if params[:treeish].present? && !project.repo.branches_and_tags.map(&:name).include?(params[:treeish]) res << [I18n.t('layout.git.repositories.commits'), [params[:treeish].truncate(20)]] end - linking = Proc.new {|t| [t.name.truncate(20), url_for(p.merge :treeish => t.name).split('?', 2).first]} - res << [I18n.t('layout.git.repositories.branches'), project.repo.branches.map(&linking)] + linking = Proc.new {|name| [name.truncate(20), url_for(p.merge :treeish => name).split('?', 2).first]} + res << [I18n.t('layout.git.repositories.branches'), project.repo.branches.map(&:name).sort.map(&linking)] if tag_enabled - res << [I18n.t('layout.git.repositories.tags'), project.repo.tags.map(&linking)] + res << [I18n.t('layout.git.repositories.tags'), project.repo.tags.map(&:name).sort.map(&linking)] else - res << [I18n.t('layout.git.repositories.tags'), project.repo.tags.map {|t| [t.name.truncate(20), {:disabled => true}]}] + res << [I18n.t('layout.git.repositories.tags'), project.repo.tags.map(&:name).sort.map {|name| [name.truncate(20), {:disabled => true}]}] end grouped_options_for_select(res, current) end diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb new file mode 100644 index 000000000..6a9ad45e9 --- /dev/null +++ b/app/helpers/gitlab_markdown_helper.rb @@ -0,0 +1,62 @@ +# This module is based on +# https://github.com/gitlabhq/gitlabhq/blob/7665b1de7eed4addd7b94786c84e6674710e6377/app/helpers/gitlab_markdown_helper.rb +module GitlabMarkdownHelper + include Modules::Models::Markdown + + # Use this in places where you would normally use link_to(gfm(...), ...). + # + # It solves a problem occurring with nested links (i.e. + # "outer text gfm ref more outer text"). This will not be + # interpreted as intended. Browsers will parse something like + # "outer text gfm ref more outer text" (notice the last part is + # not linked any more). link_to_gfm corrects that. It wraps all parts to + # explicitly produce the correct linking behavior (i.e. + # "outer text gfm ref more outer text"). + def link_to_gfm(body, url, html_options = {}) + return "" if body.blank? + + escaped_body = if body =~ /^\.*?}m) do |match| + "#{match}#{link_to("", url, html_options)[0..-5]}" # "".length +1 + end + + link_to(gfm_body.html_safe, url, html_options) + end + + def markdown(text) + unless @markdown + gitlab_renderer = Redcarpet::Render::GitlabHTML.new(self, + # see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch- + filter_html: true, + with_toc_data: true, + hard_wrap: true) + @markdown = Redcarpet::Markdown.new(gitlab_renderer, + # see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use + no_intra_emphasis: true, + tables: true, + fenced_code_blocks: true, + autolink: true, + strikethrough: true, + lax_html_blocks: true, + space_after_headers: true, + superscript: true) + end + + @markdown.render(text).html_safe + end + + def render_wiki_content(wiki_page) + if wiki_page.format == :markdown + markdown(wiki_page.content) + else + wiki_page.formatted_content.html_safe + end + end +end diff --git a/app/helpers/hooks_helper.rb b/app/helpers/hooks_helper.rb new file mode 100644 index 000000000..14354af9c --- /dev/null +++ b/app/helpers/hooks_helper.rb @@ -0,0 +1,2 @@ +module HooksHelper +end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index b8811a9af..e87b7b5aa 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -1,8 +1,8 @@ module IssuesHelper - def tracker_search_field(name, txt) + def tracker_search_field(name, txt, classes = nil) str = "" + str << "onblur=\"if(this.value==''){this.value='#{txt}';this.className='gray #{classes}';}\"" + str << "onclick=\"if(this.value=='#{txt}'){this.value='';this.className='black #{classes}';}\" class=\"gray #{classes}\">" str.html_safe end end diff --git a/app/helpers/paginate_helper.rb b/app/helpers/paginate_helper.rb new file mode 100644 index 000000000..3397d14b4 --- /dev/null +++ b/app/helpers/paginate_helper.rb @@ -0,0 +1,23 @@ +# -*- encoding : utf-8 -*- +module PaginateHelper + + def paginate_params + per_page = params[:per_page].to_i + per_page = 20 if per_page < 1 + per_page = 100 if per_page >100 + page = params[:page].to_i + page = nil if page == 0 + {:page => page, :per_page => per_page} + end + + def angularjs_will_paginate(collection_or_options = nil, options = {}) + if collection_or_options.is_a? Hash + options, collection_or_options = collection_or_options, nil + end + options.merge!(renderer: AngularjsLinkRenderer) unless options[:renderer] + options.merge!(next_label: I18n.t('datatables.next_label')) unless options[:next_label] + options.merge!(previous_label: I18n.t('datatables.previous_label')) unless options[:previous_label] + will_paginate *[collection_or_options, options].compact + end + +end diff --git a/app/helpers/platforms_helper.rb b/app/helpers/platforms_helper.rb index 179a9d21a..a84c7761c 100644 --- a/app/helpers/platforms_helper.rb +++ b/app/helpers/platforms_helper.rb @@ -9,4 +9,20 @@ module PlatformsHelper platform.released? ? "#{platform.name} #{I18n.t("layout.platforms.released_suffix")}" : platform.name end + def platform_arch_settings(platform) + settings = platform.platform_arch_settings + arches = if (arch_ids = settings.map(&:arch_id)) && arch_ids.present? + Arch.where('id not in (?)', arch_ids) + else + Arch.all + end + settings |= arches.map do |arch| + platform.platform_arch_settings.build( + :arch_id => arch.id, + :time_living => PlatformArchSetting::DEFAULT_TIME_LIVING + ) + end + settings.sort_by{ |s| s.arch.name } + end + end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 2f0097dcc..2b58464a7 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -19,6 +19,15 @@ module ProjectsHelper end.sort_by{ |f| f[:uname] } end + def repositories_grouped_by_platform + groups = {} + Platform.accessible_by(current_ability, :related).order(:name).each do |platform| + next unless can?(:local_admin_manage, platform) + groups[platform.name] = Repository.custom_sort(platform.repositories).map{ |r| [r.name, r.id] } + end + groups + end + def git_repo_url(name) if current_user "#{request.protocol}#{current_user.uname}@#{request.host_with_port}/#{name}.git" diff --git a/app/helpers/pull_request_helper.rb b/app/helpers/pull_request_helper.rb index 602ccaa74..1690d317a 100644 --- a/app/helpers/pull_request_helper.rb +++ b/app/helpers/pull_request_helper.rb @@ -1,15 +1,15 @@ module PullRequestHelper def merge_activity comments, commits - common_comments, pull_comments = comments.partition {|c| c.data.blank?} + common_comments, pull_comments = comments.partition {|c| c.automatic || c.data.blank?} common_comments = common_comments.map{ |c| [c.created_at, c] } pull_comments = pull_comments.group_by(&:data).map{|data, c| [c.first.created_at, [data || {}, [c].flatten]]} commits = commits.map{ |c| [(c.committed_date || c.authored_date), c] } (common_comments + pull_comments + commits).sort_by{ |c| c[0] }.map{ |c| c[1] } end - def pull_status_label pull + def pull_status_label pull_status, options = {} statuses = {'ready' => 'success', 'closed' => 'important', 'merged' => 'important', 'blocked' => 'warning'} - content_tag :span, t("projects.pull_requests.statuses.#{pull.status}"), :class => "label-bootstrap label-#{statuses[pull.status]}" + content_tag :span, t("projects.pull_requests.statuses.#{pull_status}"), options.merge(:class => "state label-bootstrap label-#{statuses[pull_status]}") end def pull_status pull @@ -22,7 +22,7 @@ module PullRequestHelper end def pull_header pull - str = "#{t '.header'} #{t 'from'} \ + str = "#{t '.header'} #{t 'from'} \ #{show_ref pull, 'from'} \ #{t 'into'} \ #{show_ref pull, 'to'}" @@ -52,8 +52,8 @@ module PullRequestHelper def ref_selector_options(project, current) res = [] value = Proc.new {|t| [t.name.truncate(50), t.name]} - res << [I18n.t('layout.git.repositories.branches'), project.repo.branches.map(&value)] - res << [I18n.t('layout.git.repositories.tags'), project.repo.tags.map(&value)] + res << [I18n.t('layout.git.repositories.branches'), project.repo.branches.map(&value).sort] + res << [I18n.t('layout.git.repositories.tags'), project.repo.tags.map(&value).sort] grouped_options_for_select(res, current) end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 3ec7235a4..69d81a4da 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -10,7 +10,7 @@ module UsersHelper elsif subject.kind_of? Group image_path('ava-big.png') else - gravatar_url(subject.email, subject.avatar.styles[size].geometry.split('x').first) + gravatar_url(subject.email, User::AVATAR_SIZES[size]) end end diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb index 76c39038d..3abf35dcb 100644 --- a/app/helpers/wiki_helper.rb +++ b/app/helpers/wiki_helper.rb @@ -97,7 +97,7 @@ module WikiHelper end def date - @page.version.authored_date.strftime("%Y-%m-%d %H:%M:%S") + @page.version.authored_date end def format diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 3d902f54f..3a97f245a 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -1,4 +1,7 @@ class UserMailer < ActionMailer::Base + add_template_helper ActivityFeedsHelper + add_template_helper CommitHelper + default :from => "\"#{APP_CONFIG['project_name']}\" <#{APP_CONFIG['do-not-reply-email']}>" default_url_options.merge!(:protocol => 'https') if APP_CONFIG['mailer_https_url'] @@ -40,7 +43,7 @@ class UserMailer < ActionMailer::Base end def issue_assign_notification(issue, user) - @user, @issue = user, issue + @issue = issue mail( :to => email_with_name(user, user.email), :subject => subject_for_issue(@issue) @@ -50,10 +53,10 @@ class UserMailer < ActionMailer::Base end def build_list_notification(build_list, user) - I18n.locale = user.language if user.language + set_locale user @user, @build_list = user, build_list - subject = "[№ #{build_list.bs_id.present? ? build_list.bs_id : t("layout.build_lists.bs_id_not_set")}] " + subject = "[№ #{build_list.id}] " subject << (build_list.project ? build_list.project.name_with_owner : t("layout.projects.unexisted_project")) subject << " - #{build_list.human_status} " subject << I18n.t("notifications.subjects.for_arch", :arch => @build_list.arch.name) @@ -67,7 +70,7 @@ class UserMailer < ActionMailer::Base end def invite_approve_notification(register_request) - I18n.locale = register_request.language if register_request.language + set_locale register_request @register_request = register_request mail( :to => register_request.email, @@ -77,8 +80,32 @@ class UserMailer < ActionMailer::Base end end + def git_delete_branch_notification(user, options) + set_locale user + mail( + :to => user.email, + :subject => I18n.t('notifications.subjects.update_code', :project_name => "#{options[:project_owner]}/#{options[:project_name]}") + ) do |format| + format.html { render 'git_delete_branch_notification', :locals => options } + end + end + + def git_new_push_notification(user, options) + set_locale user + mail( + :to => user.email, + :subject => I18n.t('notifications.subjects.update_code', :project_name => "#{options[:project_owner]}/#{options[:project_name]}") + ) do |format| + format.html { render 'git_new_push_notification', :locals => options } + end + end + protected + def set_locale(user) + I18n.locale = user.language if user.language + end + def email_with_name(user, email = APP_CONFIG['do-not-reply-email']) "\"#{user.user_appeal}\" <#{email}>" end diff --git a/app/models/ability.rb b/app/models/ability.rb index 9673a8ebd..16156b7d6 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -14,23 +14,23 @@ class Ability # Shared rights between guests and registered users can [:show, :archive], Project, :visibility => 'open' can :get_id, Project, :visibility => 'open' # api - can :archive, Project, :visibility => 'open' + can(:refs_list, Project) {|project| can? :show, project} can :read, Issue, :project => {:visibility => 'open'} - can :read, PullRequest, :to_project => {:visibility => 'open'} - can :search, BuildList + can [:read, :commits, :files], PullRequest, :to_project => {:visibility => 'open'} can [:read, :log, :everything], BuildList, :project => {:visibility => 'open'} can [:read, :log], ProductBuildList#, :product => {:platform => {:visibility => 'open'}} # double nested hash don't work - can :read, Advisory + can [:read, :search], Advisory # Platforms block can [:show, :members, :advisories], Platform, :visibility => 'open' can :platforms_for_build, Platform, :visibility => 'open', :platform_type => 'main' - can(:get_list, MassBuild) {|mass_build| mass_build.platform.main? && can?(:show, mass_build.platform) } + can([:read, :get_list], MassBuild) {|mass_build| can?(:show, mass_build.save_to_platform) } can [:read, :projects_list, :projects], Repository, :platform => {:visibility => 'open'} can :read, Product, :platform => {:visibility => 'open'} can :show, Group can :show, User + can :possible_forks, Project if user.guest? # Guest rights # can [:new, :create], RegisterRequest @@ -48,36 +48,47 @@ class Ability end if user.user? + can :edit, User, :id => user.id can [:read, :create], Group can [:update, :manage_members, :members, :add_member, :remove_member, :update_member], Group do |group| group.actors.exists?(:actor_type => 'User', :actor_id => user.id, :role => 'admin') # or group.owner_id = user.id end + can :write, Group do |group| + group.actors.exists?(:actor_type => 'User', :actor_id => user.id, :role => ['writer', 'admin']) + end can :destroy, Group, :owner_id => user.id can :remove_user, Group can :create, Project + can([:mass_import, :run_mass_import], Project) if user.platforms.main.find{ |p| local_admin?(p) }.present? can :read, Project, :visibility => 'open' - can [:read, :archive], Project, :owner_type => 'User', :owner_id => user.id - can [:read, :archive], Project, :owner_type => 'Group', :owner_id => user.group_ids - can([:read, :membered, :get_id], Project, read_relations_for('projects')) {|project| local_reader? project} + can [:read, :archive, :membered, :get_id], Project, :owner_type => 'User', :owner_id => user.id + can [:read, :archive, :membered, :get_id], Project, :owner_type => 'Group', :owner_id => user.group_ids + can([:read, :archive, :membered, :get_id], Project, read_relations_for('projects')) {|project| local_reader? project} can(:write, Project) {|project| local_writer? project} # for grack can [:update, :sections, :manage_collaborators, :autocomplete_maintainers, :add_member, :remove_member, :update_member, :members], Project do |project| - local_admin? project - end + local_admin? project + end can(:fork, Project) {|project| can? :read, project} can(:fork, Project) {|project| project.owner_type == 'Group' and can? :update, project.owner} can(:destroy, Project) {|project| owner? project} can(:destroy, Project) {|project| project.owner_type == 'Group' and project.owner.actors.exists?(:actor_type => 'User', :actor_id => user.id, :role => 'admin')} can :remove_user, Project can :preview, Project - can(:refs_list, Project) {|project| can? :read, project} - can [:autocomplete_to_extra_repos_and_builds, :update_extra_repos_and_builds], BuildList + can([:read, :create, :edit, :destroy, :update], Hook) {|hook| can?(:edit, hook.project)} + can [:read, :log, :owned, :everything], BuildList, :user_id => user.id can [:read, :log, :related, :everything], BuildList, :project => {:owner_type => 'User', :owner_id => user.id} can [:read, :log, :related, :everything], BuildList, :project => {:owner_type => 'Group', :owner_id => user.group_ids} - can([:read, :log, :everything], BuildList, read_relations_for('build_lists', 'projects')) {|build_list| can? :read, build_list.project} - can([:create, :update], BuildList) {|build_list| build_list.project.is_package && can?(:write, build_list.project)} + can([:read, :log, :everything, :list], BuildList, read_relations_for('build_lists', 'projects')) {|build_list| can? :read, build_list.project} + + can(:publish_into_testing, BuildList) { |build_list| can?(:create, build_list) && build_list.save_to_platform.main? } + can(:create, BuildList) {|build_list| + build_list.project.is_package && + can?(:write, build_list.project) && + (build_list.build_for_platform.blank? || can?(:show, build_list.build_for_platform)) + } can(:publish, BuildList) do |build_list| if build_list.build_published? @@ -87,31 +98,40 @@ class Ability can?(:write, build_list.project) : local_admin?(build_list.save_to_platform) end end - can([:reject_publish, :create_container], BuildList) do |build_list| + can(:create_container, BuildList) do |build_list| local_admin?(build_list.save_to_platform) end + can(:reject_publish, BuildList) do |build_list| + build_list.save_to_repository.publish_without_qa ? + can?(:write, build_list.project) : local_admin?(build_list.save_to_platform) + end can([:cancel, :create_container], BuildList) {|build_list| can?(:write, build_list.project)} can [:read, :owned, :related, :members], Platform, :owner_type => 'User', :owner_id => user.id can [:read, :related, :members], Platform, :owner_type => 'Group', :owner_id => user.group_ids can([:read, :related, :members], Platform, read_relations_for('platforms')) {|platform| local_reader? platform} - can([:update, :destroy], Platform) {|platform| owner?(platform) } + can [:read, :related], Platform, :id => user.repositories.pluck(:platform_id) + can([:update, :destroy, :change_visibility], Platform) {|platform| owner?(platform) } can([:local_admin_manage, :members, :add_member, :remove_member, :remove_members] , Platform) {|platform| owner?(platform) || local_admin?(platform) } - can([:create, :publish], MassBuild) {|mass_build| (owner?(mass_build.platform) || local_admin?(mass_build.platform)) && mass_build.platform.main?} - can(:cancel, MassBuild) {|mass_build| (owner?(mass_build.platform) || local_admin?(mass_build.platform)) && !mass_build.stop_build && mass_build.platform.main?} + can([:create, :publish], MassBuild) {|mass_build| owner?(mass_build.save_to_platform) || local_admin?(mass_build.save_to_platform)} + can(:cancel, MassBuild) {|mass_build| (owner?(mass_build.save_to_platform) || local_admin?(mass_build.save_to_platform)) && !mass_build.stop_build} can [:read, :projects_list, :projects], Repository, :platform => {:owner_type => 'User', :owner_id => user.id} can [:read, :projects_list, :projects], Repository, :platform => {:owner_type => 'Group', :owner_id => user.group_ids} + can([:read, :projects_list, :projects], Repository, read_relations_for('repositories')) {|repository| can? :show, repository.platform} can([:read, :projects_list, :projects], Repository, read_relations_for('repositories', 'platforms')) {|repository| local_reader? repository.platform} - can([:create, :edit, :update, :destroy, :projects_list, :projects, :add_project, :remove_project, :regenerate_metadata], Repository) {|repository| local_admin? repository.platform} + can([:create, :edit, :update, :destroy, :projects_list, :projects, :add_project, :remove_project, :regenerate_metadata, :sync_lock_file, :add_repo_lock_file, :remove_repo_lock_file], Repository) {|repository| local_admin? repository.platform} can([:remove_members, :remove_member, :add_member, :signatures], Repository) {|repository| owner?(repository.platform) || local_admin?(repository.platform)} can([:add_project, :remove_project], Repository) {|repository| repository.members.exists?(:id => user.id)} can(:clear, Platform) {|platform| owner?(platform) && platform.personal?} - can([:change_visibility, :settings, :destroy, :edit, :update], Repository) {|repository| owner? repository.platform} + can(:regenerate_metadata, Platform) {|platform| owner?(platform) || local_admin?(platform)} + can([:settings, :destroy, :edit, :update], Repository) {|repository| owner? repository.platform} can([:create, :destroy], KeyPair) {|key_pair| owner?(key_pair.repository.platform) || local_admin?(key_pair.repository.platform)} + can([:read, :create, :withdraw], Token) {|token| local_admin?(token.subject)} + can :read, Product, :platform => {:owner_type => 'User', :owner_id => user.id, :platform_type => 'main'} can :read, Product, :platform => {:owner_type => 'Group', :owner_id => user.group_ids, :platform_type => 'main'} can(:read, Product, read_relations_for('products', 'platforms')) {|product| product.platform.main?} @@ -130,11 +150,12 @@ class Ability can(:update, Issue) {|issue| issue.user_id == user.id or local_admin?(issue.project)} cannot :manage, Issue, :project => {:has_issues => false} # switch off issues - can :read, PullRequest, :to_project => {:owner_type => 'User', :owner_id => user.id} - can :read, PullRequest, :to_project => {:owner_type => 'Group', :owner_id => user.group_ids} - can(:read, PullRequest, read_relations_for('pull_requests', 'to_projects')) {|pull| can? :read, pull.to_project rescue nil} + can [:read, :commits, :files], PullRequest, :to_project => {:owner_type => 'User', :owner_id => user.id} + can [:read, :commits, :files], PullRequest, :to_project => {:owner_type => 'Group', :owner_id => user.group_ids} + can([:read, :commits, :files], PullRequest, read_relations_for('pull_requests', 'to_projects')) {|pull| can? :read, pull.to_project} can :create, PullRequest - can([:update, :merge], PullRequest) {|pull| pull.user_id == user.id or local_admin?(pull.to_project)} + can(:update, PullRequest) {|pull| pull.user_id == user.id or local_admin?(pull.to_project)} + can(:merge, PullRequest) {|pull| local_admin?(pull.to_project)} can([:create, :new_line], Comment) {|comment| can? :read, comment.project} can([:update, :destroy], Comment) {|comment| comment.user == user or comment.project.owner == user or local_admin?(comment.project)} @@ -144,8 +165,10 @@ class Ability end # Shared cannot rights for all users (registered, admin) - cannot :destroy, Platform, :platform_type => 'personal' - cannot [:create, :destroy, :edit, :update, :add_project, :remove_project], Repository, :platform => {:platform_type => 'personal'} + cannot [:regenerate_metadata, :destroy], Platform, :platform_type => 'personal' + cannot [:create, :destroy], Repository, :platform => {:platform_type => 'personal'}, :name => 'main' + cannot [:remove_members, :remove_member, :add_member, :sync_lock_file, :add_repo_lock_file, :remove_repo_lock_file], Repository, :platform => {:platform_type => 'personal'} + cannot :clear, Platform, :platform_type => 'main' cannot :destroy, Issue @@ -154,14 +177,13 @@ class Ability cannot [:create, :update, :destroy, :clone], Product, :platform => {:platform_type => 'personal'} cannot [:clone], Platform, :platform_type => 'personal' - cannot :publish, BuildList, :new_core => false + cannot [:publish, :publish_into_testing], BuildList, :new_core => false cannot :create_container, BuildList, :new_core => false cannot(:publish, BuildList) {|build_list| !build_list.can_publish? } + cannot(:publish_into_testing, BuildList) {|build_list| !build_list.can_publish_into_testing? } + cannot :publish_into_testing, BuildList, :save_to_platform => {:platform_type => 'personal'} - cannot([:get_list, :create, :publish], MassBuild) {|mass_build| mass_build.platform.personal?} - cannot(:cancel, MassBuild) {|mass_build| mass_build.platform.personal? || mass_build.stop_build} - - cannot(:regenerate_metadata, Repository) {|repository| !repository.platform.main?} + cannot(:cancel, MassBuild) {|mass_build| mass_build.stop_build} if @user.system? can :key_pair, Repository @@ -178,14 +200,37 @@ class Ability end end - # TODO group_ids ?? def read_relations_for(table, parent = nil) key = parent ? "#{parent.singularize}_id" : 'id' parent ||= table + + # Removes duplicates from subquery + # + # ["#{table}.#{key} IN + # ( + # SELECT target_id FROM relations + # INNER JOIN #{parent} ON relations.target_type = :target_type AND relations.target_id = #{parent}.id + # WHERE relations.target_type = :target_type AND + # ( + # #{parent}.owner_type = 'User' AND #{parent}.owner_id != :user OR + # #{parent}.owner_type = 'Group' AND #{parent}.owner_id NOT IN (:groups) + # ) AND ( + # relations.actor_type = 'User' AND relations.actor_id = :user OR + # relations.actor_type = 'Group' AND relations.actor_id IN (:groups) + # ) + + # )", + # { + # :target_type => parent.classify, + # :user => @user.id, + # :groups => @user.group_ids + # } + # ] + ["#{table}.#{key} IN ( - SELECT target_id FROM relations WHERE relations.target_type = ? AND - (relations.actor_type = 'User' AND relations.actor_id = ? OR - relations.actor_type = 'Group' AND relations.actor_id IN (?)))", parent.classify, @user, @user.group_ids] + SELECT target_id FROM relations WHERE relations.target_type = ? AND + (relations.actor_type = 'User' AND relations.actor_id = ? OR + relations.actor_type = 'Group' AND relations.actor_id IN (?)))", parent.classify, @user, @user.group_ids] end def local_reader?(target) diff --git a/app/models/activity_feed.rb b/app/models/activity_feed.rb index 3e3419c5e..1052250f6 100644 --- a/app/models/activity_feed.rb +++ b/app/models/activity_feed.rb @@ -14,7 +14,7 @@ class ActivityFeed < ActiveRecord::Base self.per_page = 10 def partial - 'activity_feeds/partials/' + self.kind + 'home/partials/' + self.kind end end diff --git a/app/models/activity_feed_observer.rb b/app/models/activity_feed_observer.rb deleted file mode 100644 index eaa4d03a8..000000000 --- a/app/models/activity_feed_observer.rb +++ /dev/null @@ -1,170 +0,0 @@ -class ActivityFeedObserver < ActiveRecord::Observer - observe :issue, :comment, :user, :build_list - - def after_create(record) - case record.class.to_s - when 'User' - ActivityFeed.create( - :user => record, - :kind => 'new_user_notification', - :data => {:user_name => record.user_appeal, :user_email => record.email} - ) - - when 'Issue' - record.collect_recipients.each do |recipient| - next if record.user_id == recipient.id - UserMailer.new_issue_notification(record, recipient).deliver if recipient.notifier.can_notify && recipient.notifier.new_issue - ActivityFeed.create( - :user => recipient, - :kind => 'new_issue_notification', - :data => {:user_name => record.user.name, :user_email => record.user.email, :user_id => record.user_id,:issue_serial_id => record.serial_id, - :issue_title => record.title, :project_id => record.project.id, :project_name => record.project.name, :project_owner => record.project.owner.uname} - ) - end - - if record.assignee_id_changed? - UserMailer.new_issue_notification(record, record.assignee).deliver if record.assignee.notifier.issue_assign && record.assignee.notifier.can_notify - ActivityFeed.create( - :user => record.user, - :kind => 'issue_assign_notification', - :data => {:user_name => record.user.name, :user_email => record.user.email, :user_id => record.user_id, :issue_serial_id => record.serial_id, - :project_id => record.project.id, :issue_title => record.title, :project_name => record.project.name, :project_owner => record.project.owner.uname} - ) - end - - when 'Comment' - if record.issue_comment? - subscribes = record.commentable.subscribes - subscribes.each do |subscribe| - if record.user_id != subscribe.user_id - UserMailer.new_comment_notification(record, subscribe.user).deliver if record.can_notify_on_new_comment?(subscribe) - ActivityFeed.create( - :user => subscribe.user, - :kind => 'new_comment_notification', - :data => {:user_name => record.user.name, :user_email => record.user.email, :user_id => record.user_id, :comment_body => record.body, - :issue_title => record.commentable.title, :issue_serial_id => record.commentable.serial_id, :project_id => record.commentable.project.id, - :comment_id => record.id, :project_name => record.project.name, :project_owner => record.project.owner.uname} - ) - end - end - elsif record.commit_comment? - subscribes = Subscribe.comment_subscribes(record).where(:status => true) - subscribes.each do |subscribe| - next if record.own_comment?(subscribe.user) - if subscribe.user.notifier.can_notify and - ( (subscribe.project.owner?(subscribe.user) && subscribe.user.notifier.new_comment_commit_repo_owner) or - (subscribe.user.commentor?(record.commentable) && subscribe.user.notifier.new_comment_commit_commentor) or - (subscribe.user.committer?(record.commentable) && subscribe.user.notifier.new_comment_commit_owner) ) - UserMailer.new_comment_notification(record, subscribe.user).deliver - end - ActivityFeed.create( - :user => subscribe.user, - :kind => 'new_comment_commit_notification', - :data => {:user_name => record.user.name, :user_email => record.user.email, :user_id => record.user_id, :comment_body => record.body, - :commit_message => record.commentable.message, :commit_id => record.commentable.id, - :project_id => record.project.id, :comment_id => record.id, :project_name => record.project.name, :project_owner => record.project.owner.uname} - ) - end - end - - when 'GitHook' - return unless record.project - PullRequest.where("from_project_id = ? OR to_project_id = ?", record.project, record.project).needed_checking.each {|pull| pull.check} - - change_type = record.change_type - branch_name = record.refname.split('/').last - - if change_type == 'delete' - kind = 'git_delete_branch_notification' - options = {:project_id => record.project.id, :project_name => record.project.name, :branch_name => branch_name, - :change_type => change_type, :project_owner => record.project.owner.uname} - else - if record.message # online update - #FIXME using oldrev is a hack (only for online edit). - last_commits, commits = [[record.newrev, record.message]], [] - else - commits = record.project.repo.commits_between(record.oldrev, record.newrev) - last_commits = commits.last(3).collect { |commit| [commit.sha, commit.message] } - end - - kind = 'git_new_push_notification' - options = {:project_id => record.project.id, :project_name => record.project.name, :last_commits => last_commits.reverse, - :branch_name => branch_name, :change_type => change_type, :project_owner => record.project.owner.uname} - if commits.count > 3 - commits = commits[0...-3] - options.merge!({:other_commits_count => commits.count, :other_commits => "#{commits[0].sha[0..9]}...#{commits[-1].sha[0..9]}"}) - end - end - options.merge!({:user_id => record.user.id, :user_name => record.user.name, :user_email => record.user.email}) if record.user - - record.project.admins.each do |recipient| - next if record.user && record.user.id == recipient.id - ActivityFeed.create!( - :user => recipient, - :kind => kind, - :data => options - ) - end - - when 'Hash' # 'Gollum::Committer' - actor = User.find_by_uname! record[:actor_name] - project = Project.find record[:project_id] - - project.admins.each do |recipient| - ActivityFeed.create!( - :user => recipient, - :kind => 'wiki_new_commit_notification', - :data => {:user_id => actor.id, :user_name => actor.name, :user_email => actor.email, :project_id => project.id, - :project_name => project.name, :commit_sha => record[:commit_sha], :project_owner => project.owner.uname} - ) - end - end - end - - def after_update(record) - case record.class.to_s - when 'Issue' - if record.assignee_id && record.assignee_id_changed? - UserMailer.issue_assign_notification(record, record.assignee).deliver if record.assignee.notifier.issue_assign && record.assignee.notifier.can_notify - ActivityFeed.create( - :user => record.assignee, - :kind => 'issue_assign_notification', - :data => {:user_name => record.assignee.name, :user_email => record.assignee.email, :issue_serial_id => record.serial_id, :issue_title => record.title, - :project_id => record.project.id, :project_name => record.project.name, :project_owner => record.project.owner.uname} - ) - end - - when 'BuildList' - if ( record.status_changed? && - [BuildList::BUILD_PUBLISHED, - BuildList::SUCCESS, - BuildList::BUILD_ERROR, - BuildList::PROJECT_VERSION_NOT_FOUND, - BuildList::FAILED_PUBLISH, - BuildList::TESTS_FAILED - ].include?(record.status) - ) or (record.status == BuildList::BUILD_PENDING && record.bs_id_changed?) - record.project.admins.each do |recipient| - user = record.publisher || record.user - ActivityFeed.create( - :user => recipient, - :kind => 'build_list_notification', - :data => { - :task_num => record.bs_id, - :build_list_id => record.id, - :status => record.status, - :updated_at => record.updated_at, - :project_id => record.project_id, - :project_name => record.project.name, - :project_owner => record.project.owner.uname, - :user_name => user.name, - :user_email => user.email, - :user_id => user.id - } - ) - end - end - end - end - -end diff --git a/app/models/advisory.rb b/app/models/advisory.rb index 0c4261f71..b1cc0c6d1 100644 --- a/app/models/advisory.rb +++ b/app/models/advisory.rb @@ -26,7 +26,7 @@ class Advisory < ActiveRecord::Base self.platforms << build_list.save_to_platform unless platforms.include? build_list.save_to_platform self.projects << build_list.project unless projects.include? build_list.project build_list.advisory = self - save + save && build_list.save end # this method fetches and structurize packages attached to current advisory. diff --git a/app/models/arch.rb b/app/models/arch.rb index cebbac206..b14ec33fc 100644 --- a/app/models/arch.rb +++ b/app/models/arch.rb @@ -1,4 +1,6 @@ class Arch < ActiveRecord::Base + DEFAULT = %w[i586 x86_64] + has_many :build_lists, :dependent => :destroy validates :name, :presence => true, :uniqueness => true diff --git a/app/models/authentication.rb b/app/models/authentication.rb index 26ba03317..1a037f2c0 100644 --- a/app/models/authentication.rb +++ b/app/models/authentication.rb @@ -1,6 +1,6 @@ class Authentication < ActiveRecord::Base belongs_to :user - validates :provider, :uid, :presence => true + validates :provider, :uid, :user_id, :presence => true validates :uid, :uniqueness => {:scope => :provider, :case_sensitive => false} end diff --git a/app/models/avatar.rb b/app/models/avatar.rb index 822667b03..67a56878f 100644 --- a/app/models/avatar.rb +++ b/app/models/avatar.rb @@ -2,13 +2,14 @@ class Avatar < ActiveRecord::Base self.abstract_class = true MAX_AVATAR_SIZE = 5.megabyte + AVATAR_SIZES = {:micro => 16, :small => 30, :medium => 40, :big => 81} - has_attached_file :avatar, :styles => - { :micro => { :geometry => "16x16#", :format => :jpg, :convert_options => '-strip -background white -flatten -quality 70'}, - :small => { :geometry => "30x30#", :format => :jpg, :convert_options => '-strip -background white -flatten -quality 70'}, - :medium => { :geometry => "40x40#", :format => :jpg, :convert_options => '-strip -background white -flatten -quality 70'}, - :big => { :geometry => "81x81#", :format => :jpg, :convert_options => '-strip -background white -flatten -quality 70'} - } + AVATAR_SIZES_HASH = {}.tap do |styles| + AVATAR_SIZES.each do |name, size| + styles[name] = { :geometry => "#{size}x#{size}#", :format => :jpg, :convert_options => '-strip -background white -flatten -quality 70'} + end + end + has_attached_file :avatar, :styles => AVATAR_SIZES_HASH validates_inclusion_of :avatar_file_size, :in => (0..MAX_AVATAR_SIZE), :allow_nil => true attr_accessible :avatar diff --git a/app/models/build_list.rb b/app/models/build_list.rb index 39ca450c2..0ec8933ff 100644 --- a/app/models/build_list.rb +++ b/app/models/build_list.rb @@ -2,25 +2,31 @@ class BuildList < ActiveRecord::Base include Modules::Models::CommitAndVersion include Modules::Models::FileStoreClean include AbfWorker::ModelHelper + include Modules::Observers::ActivityFeed::BuildList belongs_to :project belongs_to :arch - belongs_to :save_to_platform, :class_name => 'Platform' + belongs_to :save_to_platform, :class_name => 'Platform' belongs_to :save_to_repository, :class_name => 'Repository' belongs_to :build_for_platform, :class_name => 'Platform' belongs_to :user - belongs_to :publisher, :class_name => 'User' + belongs_to :builder, :class_name => 'User' + belongs_to :publisher, :class_name => 'User' belongs_to :advisory belongs_to :mass_build, :counter_cache => true - has_many :items, :class_name => "BuildList::Item", :dependent => :destroy - has_many :packages, :class_name => "BuildList::Package", :dependent => :destroy + has_many :items, :class_name => '::BuildList::Item', :dependent => :destroy + has_many :packages, :class_name => '::BuildList::Package', :dependent => :destroy + has_many :source_packages, :class_name => '::BuildList::Package', :conditions => {:package_type => 'source'} - UPDATE_TYPES = %w[security bugfix enhancement recommended newpackage] - RELEASE_UPDATE_TYPES = %w[security bugfix] + UPDATE_TYPES = %w[bugfix security enhancement recommended newpackage] + RELEASE_UPDATE_TYPES = %w[bugfix security] + EXTRA_PARAMS = %w[cfg_options cfg_urpm_options build_src_rpm build_rpm] + EXTERNAL_NODES = %w[owned everything] validates :project_id, :project_version, :arch, :include_repos, :build_for_platform_id, :save_to_platform_id, :save_to_repository_id, :presence => true validates_numericality_of :priority, :greater_than_or_equal_to => 0 + validates :external_nodes, :inclusion => {:in => EXTERNAL_NODES}, :allow_blank => true validates :update_type, :inclusion => UPDATE_TYPES, :unless => Proc.new { |b| b.advisory.present? } validates :update_type, :inclusion => {:in => RELEASE_UPDATE_TYPES, :message => I18n.t('flash.build_list.frozen_platform')}, @@ -32,84 +38,75 @@ class BuildList < ActiveRecord::Base errors.add(:build_for_platform, I18n.t('flash.build_list.wrong_build_for_platform')) unless build_for_platform.main? } validate lambda { - errors.add(:save_to_repository, I18n.t('flash.build_list.wrong_repository')) unless save_to_repository_id.in? save_to_platform.repositories.map(&:id) + errors.add(:save_to_repository, I18n.t('flash.build_list.wrong_repository')) if save_to_repository.platform_id != save_to_platform.id } validate lambda { - include_repos.each {|ir| - errors.add(:save_to_repository, I18n.t('flash.build_list.wrong_include_repos')) unless build_for_platform.repository_ids.include? ir.to_i - } + errors.add(:save_to_repository, I18n.t('flash.build_list.wrong_include_repos')) if build_for_platform.repositories.where(:id => include_repos).count != include_repos.size } validate lambda { errors.add(:save_to_repository, I18n.t('flash.build_list.wrong_project')) unless save_to_repository.projects.exists?(project_id) } + before_validation lambda { self.include_repos = include_repos.uniq if include_repos.present? }, :on => :create before_validation :prepare_extra_repositories, :on => :create before_validation :prepare_extra_build_lists, :on => :create - - before_create :use_save_to_repository_for_main_platforms + before_validation :prepare_extra_params, :on => :create + before_validation lambda { self.auto_publish = false if external_nodes.present?; true }, :on => :create + before_validation lambda { self.auto_create_container = false if auto_publish?; true }, :on => :create attr_accessible :include_repos, :auto_publish, :build_for_platform_id, :commit_hash, :arch_id, :project_id, :save_to_repository_id, :update_type, - :save_to_platform_id, :project_version, :use_save_to_repository, - :auto_create_container, :extra_repositories, :extra_build_lists - LIVE_TIME = 4.week # for unpublished + :save_to_platform_id, :project_version, :auto_create_container, + :extra_repositories, :extra_build_lists, :extra_params, :external_nodes, + :include_testing_subrepository + + LIVE_TIME = 4.week # for unpublished MAX_LIVE_TIME = 3.month # for published - - SUCCESS = 0 - ERROR = 1 - - PROJECT_VERSION_NOT_FOUND = 4 - PROJECT_SOURCE_ERROR = 6 - DEPENDENCIES_ERROR = 555 - BUILD_ERROR = 666 - BUILD_STARTED = 3000 - BUILD_CANCELED = 5000 - WAITING_FOR_RESPONSE = 4000 - BUILD_PENDING = 2000 - BUILD_PUBLISHED = 6000 - BUILD_PUBLISH = 7000 - FAILED_PUBLISH = 8000 - REJECTED_PUBLISH = 9000 - BUILD_CANCELING = 10000 - TESTS_FAILED = 11000 - - STATUSES = [ WAITING_FOR_RESPONSE, - BUILD_CANCELED, - BUILD_PENDING, - BUILD_PUBLISHED, - BUILD_CANCELING, - BUILD_PUBLISH, - FAILED_PUBLISH, - REJECTED_PUBLISH, - SUCCESS, - BUILD_STARTED, - BUILD_ERROR, - PROJECT_VERSION_NOT_FOUND, - TESTS_FAILED - ] - - HUMAN_STATUSES = { WAITING_FOR_RESPONSE => :waiting_for_response, - BUILD_CANCELED => :build_canceled, - BUILD_CANCELING => :build_canceling, - BUILD_PENDING => :build_pending, - BUILD_PUBLISHED => :build_published, - BUILD_PUBLISH => :build_publish, - FAILED_PUBLISH => :failed_publish, - REJECTED_PUBLISH => :rejected_publish, - BUILD_ERROR => :build_error, - BUILD_STARTED => :build_started, - SUCCESS => :success, - PROJECT_VERSION_NOT_FOUND => :project_version_not_found, - TESTS_FAILED => :tests_failed - } + STATUSES, HUMAN_STATUSES = [], {} + [ + %w(SUCCESS 0), + # %w(ERROR 1), + # %w(PROJECT_SOURCE_ERROR 6), + # %w(DEPENDENCIES_ERROR 555), + %w(BUILD_ERROR 666), + %w(BUILD_STARTED 3000), + %w(BUILD_CANCELED 5000), + %w(WAITING_FOR_RESPONSE 4000), + %w(BUILD_PENDING 2000), + %w(BUILD_PUBLISHED 6000), + %w(BUILD_PUBLISH 7000), + %w(FAILED_PUBLISH 8000), + %w(REJECTED_PUBLISH 9000), + %w(BUILD_CANCELING 10000), + %w(TESTS_FAILED 11000), + %w(BUILD_PUBLISHED_INTO_TESTING 12000), + %w(BUILD_PUBLISH_INTO_TESTING 13000), + %w(FAILED_PUBLISH_INTO_TESTING 14000) + ].each do |kind, value| + value = value.to_i + const_set kind, value + STATUSES << value + HUMAN_STATUSES[value] = kind.downcase.to_sym + end + STATUSES.freeze + HUMAN_STATUSES.freeze scope :recent, order("#{table_name}.updated_at DESC") - scope :for_status, lambda {|status| where(:status => status) } + scope :for_extra_build_lists, lambda {|ids, current_ability, save_to_platform| + s = scoped + s = s.where(:id => ids).published_container.accessible_by(current_ability, :read) + s = s.where(:save_to_platform_id => save_to_platform.id) if save_to_platform && save_to_platform.main? + s + } + scope :for_status, lambda {|status| where(:status => status) if status.present? } scope :for_user, lambda { |user| where(:user_id => user.id) } + scope :not_owned_external_nodes, where("#{table_name}.external_nodes is null OR #{table_name}.external_nodes != ?", :owned) + scope :external_nodes, lambda { |type| where("#{table_name}.external_nodes = ?", type) } + scope :oldest, lambda { where("#{table_name}.updated_at < ?", Time.zone.now - 15.seconds) } scope :for_platform, lambda { |platform| where(:build_for_platform_id => platform) } - scope :by_mass_build, lambda { |mass_build| where(:mass_build_id => mass_build) } - scope :scoped_to_arch, lambda {|arch| where(:arch_id => arch) } - scope :scoped_to_save_platform, lambda {|pl_id| where(:save_to_platform_id => pl_id) } - scope :scoped_to_project_version, lambda {|project_version| where(:project_version => project_version) } + scope :by_mass_build, lambda { |mass_build| where(:mass_build_id => mass_build) } + scope :scoped_to_arch, lambda {|arch| where(:arch_id => arch) if arch.present? } + scope :scoped_to_save_platform, lambda {|pl_id| where(:save_to_platform_id => pl_id) if pl_id.present? } + scope :scoped_to_project_version, lambda {|project_version| where(:project_version => project_version) if project_version.present? } scope :scoped_to_is_circle, lambda {|is_circle| where(:is_circle => is_circle) } scope :for_creation_date_period, lambda{|start_date, end_date| s = scoped @@ -119,13 +116,13 @@ class BuildList < ActiveRecord::Base } scope :for_notified_date_period, lambda{|start_date, end_date| s = scoped - s = s.where(["#{table_name}.updated_at >= ?", start_date]) if start_date - s = s.where(["#{table_name}.updated_at <= ?", end_date]) if end_date + s = s.where("#{table_name}.updated_at >= ?", start_date) if start_date.present? + s = s.where("#{table_name}.updated_at <= ?", end_date) if end_date.present? s } - scope :scoped_to_project_name, lambda {|project_name| joins(:project).where('projects.name LIKE ?', "%#{project_name}%")} + scope :scoped_to_project_name, lambda {|project_name| joins(:project).where('projects.name LIKE ?', "%#{project_name}%") if project_name.present? } scope :scoped_to_new_core, lambda {|new_core| where(:new_core => new_core)} - scope :outdated, where("#{table_name}.created_at < ? AND #{table_name}.status <> ? OR #{table_name}.created_at < ?", Time.now - LIVE_TIME, BUILD_PUBLISHED, Time.now - MAX_LIVE_TIME) + scope :outdated, where("#{table_name}.created_at < ? AND #{table_name}.status NOT IN (?) OR #{table_name}.created_at < ?", Time.now - LIVE_TIME, [BUILD_PUBLISHED,BUILD_PUBLISHED_INTO_TESTING], Time.now - MAX_LIVE_TIME) scope :published_container, where(:container_status => BUILD_PUBLISHED) serialize :additional_repos @@ -133,8 +130,9 @@ class BuildList < ActiveRecord::Base serialize :results, Array serialize :extra_repositories, Array serialize :extra_build_lists, Array + serialize :extra_params, Hash - after_commit :place_build + after_commit :place_build, :on => :create after_destroy :remove_container state_machine :status, :initial => :waiting_for_response do @@ -154,6 +152,8 @@ class BuildList < ActiveRecord::Base end end + after_transition :on => :place_build, :do => :add_job_to_abf_worker_queue, + :if => lambda { |build_list| build_list.external_nodes.blank? } after_transition :on => :published, :do => [:set_version_and_tag, :actualize_packages] after_transition :on => :publish, :do => :set_publisher @@ -164,18 +164,11 @@ class BuildList < ActiveRecord::Base :unless => lambda { |build_list| build_list.auto_publish? } event :place_build do - transition :waiting_for_response => :build_pending, :if => lambda { |build_list| - build_list.add_to_queue == BuildList::SUCCESS - } - %w[BUILD_PENDING PROJECT_VERSION_NOT_FOUND].each do |code| - transition :waiting_for_response => code.downcase.to_sym, :if => lambda { |build_list| - build_list.add_to_queue == BuildList.const_get(code) - } - end + transition :waiting_for_response => :build_pending end event :start_build do - transition [ :build_pending, :project_version_not_found ] => :build_started + transition :build_pending => :build_started end event :cancel do @@ -197,14 +190,50 @@ class BuildList < ActiveRecord::Base end event :publish do - transition [:success, :failed_publish, :build_published, :tests_failed] => :build_publish - transition [:success, :failed_publish] => :failed_publish + transition [ + :success, + :failed_publish, + :build_published, + :tests_failed, + :failed_publish_into_testing, + :build_published_into_testing + ] => :build_publish + transition [:success, :failed_publish, :failed_publish_into_testing] => :failed_publish end event :reject_publish do - transition [:success, :failed_publish, :tests_failed] => :rejected_publish, :if => :can_reject_publish? + transition [ + :success, + :failed_publish, + :tests_failed, + :failed_publish_into_testing, + :build_published_into_testing + ] => :rejected_publish end + # ===== into testing - start + + event :published_into_testing do + transition [:build_publish_into_testing, :rejected_publish] => :build_published_into_testing + end + + event :fail_publish_into_testing do + transition [:build_publish_into_testing, :rejected_publish] => :failed_publish_into_testing + end + + event :publish_into_testing do + transition [ + :success, + :failed_publish, + :tests_failed, + :failed_publish_into_testing, + :build_published_into_testing + ] => :build_publish_into_testing + transition [:success, :failed_publish, :failed_publish_into_testing] => :failed_publish_into_testing + end + + # ===== into testing - end + event :build_success do transition [:build_started, :build_canceled] => :success end @@ -227,7 +256,7 @@ class BuildList < ActiveRecord::Base BUILD_PUBLISHED => :container_published, BUILD_PUBLISH => :container_publish, FAILED_PUBLISH => :container_failed_publish - } + }.freeze state_machine :container_status, :initial => :waiting_for_publish do @@ -275,7 +304,7 @@ class BuildList < ActiveRecord::Base end def can_create_container? - [SUCCESS, BUILD_PUBLISH, FAILED_PUBLISH, BUILD_PUBLISHED, TESTS_FAILED].include?(status) && [WAITING_FOR_RESPONSE, FAILED_PUBLISH].include?(container_status) + [SUCCESS, BUILD_PUBLISH, FAILED_PUBLISH, BUILD_PUBLISHED, TESTS_FAILED, BUILD_PUBLISHED_INTO_TESTING, FAILED_PUBLISH_INTO_TESTING].include?(status) && [WAITING_FOR_RESPONSE, FAILED_PUBLISH].include?(container_status) end #TODO: Share this checking on product owner. @@ -283,8 +312,32 @@ class BuildList < ActiveRecord::Base build_started? || build_pending? end + # Comparison between versions of current and last published build_list + # @return [Boolean] + # - false if no new packages + # - false if version of packages is less than version of pubished packages. + # - true if version of packages is equal to version of pubished packages (only if platform is not released). + # - true if version of packages is greater than version of pubished packages. + def has_new_packages? + if last_bl = last_published.joins(:source_packages).where(:build_list_packages => {:actual => true}).last + source_packages.each do |nsp| + sp = last_bl.source_packages.find{ |sp| nsp.name == sp.name } + return true unless sp + comparison = nsp.rpmvercmp(sp) + return comparison == 1 || (comparison == 0 && !save_to_platform.released?) + end + else + return true # no published packages + end + return false # no new packages + end + + def can_auto_publish? + auto_publish? && can_publish? && has_new_packages? + end + def can_publish? - [SUCCESS, FAILED_PUBLISH, BUILD_PUBLISHED, TESTS_FAILED].include?(status) && extra_build_lists_published? && save_to_repository.projects.exists?(:id => project_id) + [SUCCESS, FAILED_PUBLISH, BUILD_PUBLISHED, TESTS_FAILED, BUILD_PUBLISHED_INTO_TESTING, FAILED_PUBLISH_INTO_TESTING].include?(status) && extra_build_lists_published? && save_to_repository.projects.exists?(:id => project_id) end def extra_build_lists_published? @@ -293,17 +346,17 @@ class BuildList < ActiveRecord::Base BuildList.where(:id => extra_build_lists).where('status != ?', BUILD_PUBLISHED).count == 0 end - def can_reject_publish? - [SUCCESS, FAILED_PUBLISH, TESTS_FAILED].include?(status) && !save_to_repository.publish_without_qa + def human_average_build_time + I18n.t('layout.project_statistics.human_average_build_time', {:hours => (average_build_time/3600).to_i, :minutes => (average_build_time%3600/60).to_i}) end - def add_to_queue - # TODO: Investigate: why 2 tasks will be created without checking @state - unless @status - add_job_to_abf_worker_queue - update_column(:bs_id, id) - end - @status ||= BUILD_PENDING + def formatted_average_build_time + "%02d:%02d" % [average_build_time / 3600, average_build_time % 3600 / 60] + end + + def average_build_time + return 0 unless project + project.project_statistics.where(:arch_id => arch_id).pluck(:average_build_time).first || 0 end def self.human_status(status) @@ -349,7 +402,7 @@ class BuildList < ActiveRecord::Base end def human_duration - I18n.t("layout.build_lists.human_duration", {:hours => (duration/3600).to_i, :minutes => (duration%3600/60).to_i}) + I18n.t("layout.build_lists.human_duration", {:hours => (duration.to_i/3600).to_i, :minutes => (duration.to_i%3600/60).to_i}) end def in_work? @@ -373,12 +426,12 @@ class BuildList < ActiveRecord::Base new_core? ? abf_worker_log : I18n.t('layout.build_lists.log.not_available') end - def last_published + def last_published(testing = false) BuildList.where(:project_id => self.project_id, :save_to_repository_id => self.save_to_repository_id) .for_platform(self.build_for_platform_id) .scoped_to_arch(self.arch_id) - .for_status(BUILD_PUBLISHED) + .for_status(testing ? BUILD_PUBLISHED_INTO_TESTING : BUILD_PUBLISHED) .recent end @@ -386,6 +439,54 @@ class BuildList < ActiveRecord::Base packages.pluck(:sha1).compact | (results || []).map{ |r| r['sha1'] }.compact end + def abf_worker_args + repos = include_repos + include_repos_hash = {}.tap do |h| + Repository.where(:id => (repos | (extra_repositories || [])) ).each do |repo| + path, prefix = repo.platform.public_downloads_url( + repo.platform.main? ? nil : build_for_platform.name, + arch.name, + repo.name + ), "#{repo.platform.name}_#{repo.name}_" + h["#{prefix}release"] = insert_token_to_path(path + 'release', repo.platform) + h["#{prefix}updates"] = insert_token_to_path(path + 'updates', repo.platform) if repo.platform.main? + h["#{prefix}testing"] = insert_token_to_path(path + 'testing', repo.platform) if include_testing_subrepository? + end + end + host = EventLog.current_controller.request.host_with_port rescue ::Rosa::Application.config.action_mailer.default_url_options[:host] + BuildList.where(:id => extra_build_lists).each do |bl| + path = "#{APP_CONFIG['downloads_url']}/#{bl.save_to_platform.name}/container/" + path << "#{bl.id}/#{bl.arch.name}/#{bl.save_to_repository.name}/release" + include_repos_hash["container_#{bl.id}"] = insert_token_to_path(path, bl.save_to_platform) + end + + git_project_address = project.git_project_address user + # git_project_address.gsub!(/^http:\/\/(0\.0\.0\.0|localhost)\:[\d]+/, 'https://abf.rosalinux.ru') unless Rails.env.production? + + cmd_params = { + 'GIT_PROJECT_ADDRESS' => git_project_address, + 'COMMIT_HASH' => commit_hash, + 'EXTRA_CFG_OPTIONS' => extra_params['cfg_options'], + 'EXTRA_CFG_URPM_OPTIONS' => extra_params['cfg_urpm_options'], + 'EXTRA_BUILD_SRC_RPM_OPTIONS' => extra_params['build_src_rpm'], + 'EXTRA_BUILD_RPM_OPTIONS' => extra_params['build_rpm'] + }.map{ |k, v| "#{k}='#{v}'" }.join(' ') + + { + :id => id, + :time_living => (build_for_platform.platform_arch_settings.by_arch(arch).first.try(:time_living) || PlatformArchSetting::DEFAULT_TIME_LIVING), + :distrib_type => build_for_platform.distrib_type, + :cmd_params => cmd_params, + :include_repos => include_repos_hash, + :platform => { + :type => build_for_platform.distrib_type, + :name => build_for_platform.name, + :arch => arch.name + }, + :user => {:uname => user.uname, :email => user.email} + } + end + protected def create_container @@ -404,63 +505,23 @@ class BuildList < ActiveRecord::Base 'rpm_worker' end - def abf_worker_args - # TODO: remove when this will be not necessary - # "rosa2012.1/main" repository should be used in "conectiva" platform - repos = include_repos - repos |= ['146'] if build_for_platform_id == 376 - include_repos_hash = {}.tap do |h| - Repository.where(:id => (repos | (extra_repositories || [])) ).each do |repo| - path = repo.platform.public_downloads_url(nil, arch.name, repo.name) - # path.gsub!(/^http:\/\/(0\.0\.0\.0|localhost)\:[\d]+/, 'https://abf.rosalinux.ru') unless Rails.env.production? - # Path looks like: - # http://abf.rosalinux.ru/downloads/rosa-server2012/repository/x86_64/base/ - # so, we should append: - # - release - # - updates - h["#{repo.platform.name}_#{repo.name}_release"] = path + 'release' - h["#{repo.platform.name}_#{repo.name}_updates"] = path + 'updates' if repo.platform.main? - end + def insert_token_to_path(path, platform) + if platform.hidden? + path.gsub(/^http:\/\//, "http://#{user.authentication_token}:@") + else + path end - host = EventLog.current_controller.request.host_with_port rescue ::Rosa::Application.config.action_mailer.default_url_options[:host] - BuildList.where(:id => extra_build_lists).each do |bl| - path = "http://#{host}/downloads/#{bl.save_to_platform.name}/container/" - path << "#{bl.id}/#{bl.arch.name}/#{bl.save_to_repository.name}/release" - include_repos_hash["container_#{bl.id}"] = path - end - if save_to_platform.personal? && use_save_to_repository - include_repos_hash["#{save_to_platform.name}_release"] = save_to_platform. - urpmi_list(nil, nil, false, save_to_repository.name)["#{build_for_platform.name}"]["#{arch.name}"] - end - - git_project_address = project.git_project_address(user) - # git_project_address.gsub!(/^http:\/\/(0\.0\.0\.0|localhost)\:[\d]+/, 'https://abf.rosalinux.ru') unless Rails.env.production? - { - :id => id, - :arch => arch.name, - :time_living => 43200, # 12 hours - :distrib_type => build_for_platform.distrib_type, - :git_project_address => git_project_address, - :commit_hash => commit_hash, - :include_repos => include_repos_hash, - :bplname => build_for_platform.name, - :user => {:uname => user.uname, :email => user.email} - } end def notify_users unless mass_build_id - users = [] - if project # find associated users - users = project.all_members. - select{ |user| user.notifier.can_notify? && user.notifier.new_associated_build? } - end - if user.notifier.can_notify? && user.notifier.new_build? - users = users | [user] - end - users.each do |user| - UserMailer.build_list_notification(self, user).deliver - end + users = [user, publisher].compact.uniq.select{ |u| u.notifier.can_notify? && u.notifier.new_build? } + + # find associated users + users |= project.all_members.select do |u| + u.notifier.can_notify? && u.notifier.new_associated_build? + end if project + users.each{ |u| UserMailer.build_list_notification(self, u).deliver } end end # notify_users @@ -473,10 +534,6 @@ class BuildList < ActiveRecord::Base end end - def use_save_to_repository_for_main_platforms - self.use_save_to_repository = true if save_to_platform.main? - end - def set_publisher self.publisher ||= user save @@ -490,15 +547,15 @@ class BuildList < ActiveRecord::Base if save_to_platform && save_to_platform.main? self.extra_repositories = nil else - self.extra_repositories = Repository.where(:id => extra_repositories). + self.extra_repositories = Repository.joins(:platform). + where(:id => extra_repositories, :platforms => {:platform_type => 'personal'}). accessible_by(current_ability, :read).pluck('repositories.id') end end def prepare_extra_build_lists - bls = BuildList.where(:id => extra_build_lists).published_container.accessible_by(current_ability, :read) - if save_to_platform && save_to_platform.main? - bls = bls.where(:save_to_platform_id => save_to_platform.id) + bls = BuildList.for_extra_build_lists(extra_build_lists, current_ability, save_to_platform) + if save_to_platform if save_to_platform.distrib_type == 'rhel' bls = bls.where(' (build_lists.arch_id = ? AND projects.publish_i686_into_x86_64 is not true) OR @@ -507,9 +564,18 @@ class BuildList < ActiveRecord::Base else bls = bls.where(:arch_id => arch_id) end - end self.extra_build_lists = bls.pluck('build_lists.id') end + def prepare_extra_params + if extra_params.present? + params = extra_params.slice(*BuildList::EXTRA_PARAMS) + params.update(params) do |k,v| + v.strip.gsub(I18n.t("activerecord.attributes.build_list.extra_params.#{k}"), '').gsub(/[^\w\s-]/, '') + end + self.extra_params = params.select{ |k,v| v.present? } + end + end + end \ No newline at end of file diff --git a/app/models/build_list/filter.rb b/app/models/build_list/filter.rb index 7d7a0f4f6..197fbd58d 100644 --- a/app/models/build_list/filter.rb +++ b/app/models/build_list/filter.rb @@ -1,27 +1,29 @@ class BuildList::Filter PER_PAGE = [25, 50, 100] - def initialize(project, user, options = {}) - @project = project - @user = user + attr_reader :options + + def initialize(project, user, current_ability, options = {}) + @project, @user, @current_ability = project, user, current_ability set_options(options) end def find build_lists = @project ? @project.build_lists : BuildList.scoped - if @options[:bs_id] - build_lists = build_lists.where(:bs_id => @options[:bs_id]) + if @options[:id] + build_lists = build_lists.where(:id => @options[:id]) else - build_lists = build_lists.accessible_by(::Ability.new(@user), @options[:ownership].to_sym) if @options[:ownership] build_lists = build_lists.scoped_to_new_core(@options[:new_core] == '0' ? nil : true) if @options[:new_core].present? - build_lists = build_lists.for_status(@options[:status]) if @options[:status] - build_lists = build_lists.scoped_to_arch(@options[:arch_id]) if @options[:arch_id] - build_lists = build_lists.scoped_to_save_platform(@options[:platform_id]) if @options[:platform_id] - build_lists = build_lists.scoped_to_project_version(@options[:project_version]) if @options[:project_version] - build_lists = build_lists.scoped_to_project_name(@options[:project_name]) if @options[:project_name] build_lists = build_lists.by_mass_build(@options[:mass_build_id]) if @options[:mass_build_id] - build_lists = build_lists.for_notified_date_period(@options[:updated_at_start], @options[:updated_at_end]) if @options[:updated_at_start] || @options[:updated_at_end] + build_lists = build_lists.accessible_by(@current_ability, @options[:ownership].to_sym) if @options[:ownership] + + build_lists = build_lists.for_status(@options[:status]) + .scoped_to_arch(@options[:arch_id]) + .scoped_to_save_platform(@options[:platform_id]) + .scoped_to_project_version(@options[:project_version]) + .scoped_to_project_name(@options[:project_name]) + .for_notified_date_period(@options[:updated_at_start], @options[:updated_at_end]) end build_lists @@ -40,18 +42,18 @@ class BuildList::Filter def set_options(options) @options = HashWithIndifferentAccess.new(options.reverse_merge({ - :ownership => nil, - :status => nil, + :ownership => nil, + :status => nil, :updated_at_start => nil, - :updated_at_end => nil, - :arch_id => nil, - :platform_id => nil, - :is_circle => nil, - :project_version => nil, - :bs_id => nil, - :project_name => nil, - :mass_build_id => nil, - :new_core => nil + :updated_at_end => nil, + :arch_id => nil, + :platform_id => nil, + :is_circle => nil, + :project_version => nil, + :id => nil, + :project_name => nil, + :mass_build_id => nil, + :new_core => nil })) @options[:ownership] = @options[:ownership].presence || (@project || !@user ? 'everything' : 'owned') @@ -64,7 +66,7 @@ class BuildList::Filter @options[:arch_id] = @options[:arch_id].try(:to_i) @options[:platform_id] = @options[:platform_id].try(:to_i) @options[:is_circle] = @options[:is_circle].present? ? @options[:is_circle] == "1" : nil - @options[:bs_id] = @options[:bs_id].presence + @options[:id] = @options[:id].presence @options[:project_name] = @options[:project_name].presence @options[:mass_build_id] = @options[:mass_build_id].presence @options[:new_core] = @options[:new_core].presence diff --git a/app/models/build_list/item.rb b/app/models/build_list/item.rb index 1f2b6dfe3..ace1bf25a 100644 --- a/app/models/build_list/item.rb +++ b/app/models/build_list/item.rb @@ -6,11 +6,11 @@ class BuildList::Item < ActiveRecord::Base GIT_ERROR = 5 - STATUSES = [BuildList::SUCCESS, BuildList::DEPENDENCIES_ERROR, BuildList::BUILD_ERROR, BuildList::BUILD_STARTED, GIT_ERROR, BuildList::BUILD_CANCELED] + STATUSES = [BuildList::SUCCESS, BuildList::BUILD_ERROR, BuildList::BUILD_STARTED, GIT_ERROR, BuildList::BUILD_CANCELED] # BuildList::DEPENDENCIES_ERROR HUMAN_STATUSES = { nil => :unknown, GIT_ERROR => :git_error, - BuildList::DEPENDENCIES_ERROR => :dependencies_error, + # BuildList::DEPENDENCIES_ERROR => :dependencies_error, BuildList::SUCCESS => :success, BuildList::BUILD_STARTED => :build_started, BuildList::BUILD_ERROR => :build_error, diff --git a/app/models/build_list/package.rb b/app/models/build_list/package.rb index 1c10fb531..caffcfd03 100644 --- a/app/models/build_list/package.rb +++ b/app/models/build_list/package.rb @@ -5,7 +5,7 @@ class BuildList::Package < ActiveRecord::Base belongs_to :project belongs_to :platform - attr_accessible :fullname, :name, :release, :version, :sha1 + attr_accessible :fullname, :name, :release, :version, :sha1, :epoch validates :build_list_id, :project_id, :platform_id, :fullname, :package_type, :name, :release, :version, @@ -22,7 +22,34 @@ class BuildList::Package < ActiveRecord::Base scope :by_package_type, lambda {|type| where(:package_type => type) } scope :like_name, lambda {|name| where("#{table_name}.name ILIKE ?", "%#{name}%") if name.present?} + before_create :set_epoch + def assignee project.maintainer end + + + # Comparison between versions + # @param [BuildList::Package] other + # @return [Number] -1 if +other+ is greater than, 0 if +other+ is equal to, + # and +1 if other is less than version. + def rpmvercmp(other) + RPM::C.rpmvercmp to_vre_epoch_zero, other.to_vre_epoch_zero + end + + protected + + def set_epoch + self.epoch = nil if epoch.blank? || epoch == 0 + end + + # String representation in the form "e:v-r" + # @return [String] + # @note The epoch is included always. As 0 if not present + def to_vre_epoch_zero + evr = epoch.present? ? "#{epoch}:#{version}" : "0:#{version}" + evr << "-#{release}" if release.present? + evr + end + end diff --git a/app/models/build_list_observer.rb b/app/models/build_list_observer.rb index dc1921ecf..d9d4c0bc0 100644 --- a/app/models/build_list_observer.rb +++ b/app/models/build_list_observer.rb @@ -14,9 +14,14 @@ class BuildListObserver < ActiveRecord::Observer if record.status == BuildList::SUCCESS # Update project average build time - build_count = record.project.build_count - new_av_time = ( record.project.average_build_time * build_count + record.duration ) / ( build_count + 1 ) - record.project.update_attributes({ :average_build_time => new_av_time, :build_count => build_count + 1 }, :without_protection => true) + begin + statistic = record.project.project_statistics.find_or_create_by_arch_id(record.arch_id) + rescue ActiveRecord::RecordNotUnique + retry + end + build_count = statistic.build_count + new_av_time = ( statistic.average_build_time * build_count + record.duration ) / ( build_count + 1 ) + statistic.update_attributes(:average_build_time => new_av_time, :build_count => build_count + 1) end end end diff --git a/app/models/collaborator.rb b/app/models/collaborator.rb index b8333d1ad..3d78e3733 100644 --- a/app/models/collaborator.rb +++ b/app/models/collaborator.rb @@ -109,6 +109,7 @@ class Collaborator def destroy relation.try(:destroy) + @actor.check_assigned_issues @project end def attributes diff --git a/app/models/comment.rb b/app/models/comment.rb index 7a4c070ed..e8f0f1c03 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,4 +1,12 @@ class Comment < ActiveRecord::Base + include Modules::Observers::ActivityFeed::Comment + + # regexp take from http://code.google.com/p/concerto-platform/source/browse/v3/cms/lib/CodeMirror/mode/gfm/gfm.js?spec=svn861&r=861#71 + # User/Project#Num + # User#Num + # #Num + ISSUES_REGEX = /(?:[a-zA-Z0-9\-_]*\/)?(?:[a-zA-Z0-9\-_]*)?#[0-9]+/ + belongs_to :commentable, :polymorphic => true, :touch => true belongs_to :user belongs_to :project @@ -15,9 +23,7 @@ class Comment < ActiveRecord::Base attr_accessible :body, :data def commentable - # raise commentable_id.inspect - # raise commentable_id.to_s(16).inspect - commit_comment? ? project.repo.commit(commentable_id.to_s(16)) : super # TODO leading zero problem + commit_comment? ? project.repo.commit(Comment.hex_to_commit_hash commentable_id) : super end def commentable=(c) @@ -49,10 +55,6 @@ class Comment < ActiveRecord::Base user_id == user.id end - def can_notify_on_new_comment?(subscribe) - User.find(subscribe.user).notifier.new_comment && User.find(subscribe.user).notifier.can_notify - end - def actual_inline_comment?(diff = nil, force = false) unless force raise "This is not inline comment!" if data.blank? # for debug @@ -84,7 +86,7 @@ class Comment < ActiveRecord::Base end def pull_comment? - return true if commentable.is_a?(Issue) && commentable.pull_request.present? + commentable.is_a?(Issue) && commentable.pull_request.present? end def set_additional_data params @@ -128,6 +130,60 @@ class Comment < ActiveRecord::Base return true end + def self.create_link_on_issues_from_item item, commits = nil + linker = item.user + current_ability = Ability.new(linker) + + case + when item.is_a?(GitHook) + elements = commits + opts = {} + when item.is_a?(Issue) + elements = [[item, item.title], [item, item.body]] + opts = {:created_from_issue_id => item.id} + when item.commentable_type == 'Issue' + elements = [[item, item.body]] + opts = {:created_from_issue_id => item.commentable_id} + when item.commentable_type == 'Grit::Commit' + elements = [[item, item.body]] + opts = {:created_from_commit_hash => item.commentable_id} + else + raise "Unsupported item type #{item.class.name}!" + end + + elements.each do |element| + element[1].scan(ISSUES_REGEX).each do |hash| + issue = Issue.find_by_hash_tag hash, current_ability, item.project + next unless issue + # dont create link to the same issue + next if opts[:created_from_issue_id] == issue.id + opts = {:created_from_commit_hash => element[0].hex} if item.is_a?(GitHook) + # dont create duplicate link to issue + next if Comment.find_existing_automatic_comment issue, opts + # dont create link to outdated commit + next if item.is_a?(GitHook) && !item.project.repo.commit(element[0]) + comment = linker.comments.new :body => 'automatic comment' + comment.commentable, comment.project, comment.automatic = issue, issue.project, true + comment.data = {:from_project_id => item.project.id} + if opts[:created_from_commit_hash] + comment.created_from_commit_hash = opts[:created_from_commit_hash] + elsif opts[:created_from_issue_id] + comment.data.merge!(:comment_id => item.id) if item.is_a? Comment + comment.created_from_issue_id = opts[:created_from_issue_id] + else + raise 'Unsupported opts for automatic comment!' + end + comment.save + end + end + end + + def self.hex_to_commit_hash hex + # '079d'.hex.to_s(16) => "79d" + t = hex.to_s(16) + '0'*(40-t.length) << t # commit hash has 40-character + end + protected def subscribe_on_reply @@ -146,4 +202,10 @@ class Comment < ActiveRecord::Base end end end + + def self.find_existing_automatic_comment issue, opts + find_dup = opts.merge(:automatic => true, :commentable_type => issue.class.name, + :commentable_id => issue.id) + Comment.exists? find_dup + end end diff --git a/app/models/git_hook.rb b/app/models/git_hook.rb index 4dec8ff4b..c7adf7048 100644 --- a/app/models/git_hook.rb +++ b/app/models/git_hook.rb @@ -64,7 +64,7 @@ class GitHook end def self.process(*args) - ActivityFeedObserver.instance.after_create(args.size > 1 ? GitHook.new(*args) : args.first) + Modules::Observers::ActivityFeed::Git.create_notifications(args.size > 1 ? GitHook.new(*args) : args.first) end def find_user(user) diff --git a/app/models/group.rb b/app/models/group.rb index b71103ade..78983bdc9 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -18,6 +18,7 @@ class Group < Avatar scope :opened, where('1=1') scope :by_owner, lambda {|owner| where(:owner_id => owner.id)} scope :by_admin, lambda {|admin| joins(:actors).where(:'relations.role' => 'admin', :'relations.actor_id' => admin.id, :'relations.actor_type' => 'User')} + scope :by_admin_and_writer, lambda {|actor| joins(:actors).where(:'relations.role' => ['admin', 'writer'], :'relations.actor_id' => actor.id, :'relations.actor_type' => 'User')} attr_accessible :uname, :description attr_readonly :uname @@ -31,7 +32,7 @@ class Group < Avatar # include Modules::Models::Owner def self.can_own_project(user) - (by_owner(user) | by_admin(user)) + (by_owner(user) | by_admin_and_writer(user)) end def name @@ -50,6 +51,10 @@ class Group < Avatar false end + def fullname + return description.present? ? "#{uname} (#{description})" : uname + end + protected def add_owner_to_members diff --git a/app/models/hook.rb b/app/models/hook.rb new file mode 100644 index 000000000..f45c1c924 --- /dev/null +++ b/app/models/hook.rb @@ -0,0 +1,146 @@ +class Hook < ActiveRecord::Base + include Modules::Models::WebHooks + include Modules::Models::UrlHelper + include Rails.application.routes.url_helpers + belongs_to :project + + before_validation :cleanup_data + validates :project_id, :data, :presence => true + validates :name, :presence => true, :inclusion => {:in => NAMES} + + attr_accessible :data, :name + + serialize :data, Hash + + scope :for_name, lambda {|name| where(:name => name) if name.present? } + + def receive_issues(issue, action) + pull = issue.pull_request + return if action.to_sym == :create && pull + default_url_options + + payload = meta(issue.project, issue.user) + base_params = { + :number => issue.serial_id, + :state => issue.status, + :title => issue.title, + :body => issue.body, + :user => {:login => issue.user.uname}, + } + if pull + total_commits = pull.repo.commits_between(pull.to_commit, pull.from_commit).count + repo_owner = pull.to_project.owner.uname + post 'pull_request', { + :payload => payload.merge( + :action => (pull.ready? ? 'opened' : pull.status), + :pull_request => base_params.merge( + :commits => total_commits, + :head => {:label => "#{pull.from_project.owner.uname}:#{pull.from_ref}"}, + :base => {:label => "#{repo_owner}:#{pull.to_ref}"}, + :html_url => project_pull_request_url(pull.to_project, pull) + ) + ).to_json + } + else + post 'issues', { + :payload => payload.merge( + :action => (issue.closed? ? 'closed' : 'opened'), + :issue => base_params.merge( + :html_url => project_issue_url(issue.project, issue) + ) + ).to_json + } + end + end + later :receive_issues, :queue => :notification + + def receive_push(git_hook) + default_url_options + project = Project.find(git_hook['project']['project']['id']) + user = User.find(git_hook['user']['user']['id']) + payload = meta(project, user) + oldrev, newrev, change_type = git_hook.values_at *%w(oldrev newrev change_type) + + commits = [] + payload.merge!(:before => oldrev, :after => newrev) + if %w(delete create).exclude? change_type + payload.merge!( + :compare => diff_url(project, "#{oldrev[0..6]}...#{newrev[0..6]}") + ) + if oldrev == newrev + commits = [project.repo.commit(newrev)] + modified = commits.first.stats.files.map{|f| f[0]} + else + commits = project.repo.commits_between(oldrev, newrev) + end + end + + post 'push', { + :payload => payload.merge( + :ref => git_hook['refname'], + :commits => commits.map{ |commit| + files = changed_files commit + { + :id => commit.id, + :message => commit.message, + :distinct => true, + :url => commit_url(project, commit), + :removed => files[:removed], + :added => files[:added], + :modified => files[:modified], + :timestamp => commit.committed_date, + :author => {:name => commit.committer.name, :email => commit.committer.email} + } + } + ).to_json + } + end + later :receive_push, :queue => :notification + + protected + + def post(action, params) + github_services = APP_CONFIG['github_services'] + uri = URI "http://#{github_services['ip']}:#{github_services['port']}/#{name}/#{action}" + Net::HTTP.post_form uri, params.merge(:data => data.to_json) + rescue # Dont care about it + end + + def meta(project, user) + { + :repository => { + :name => project.name, + :url => project_url(project), + :owner => { :login => project.owner.uname } + }, + :sender => {:login => user.uname}, + :pusher => {:name => user.uname} + } + end + + def cleanup_data + if self.name.present? && fields = SCHEMA[self.name.to_sym] + new_data = {} + fields.each{ |type, field| new_data[field] = self.data[field] } + self.data = new_data + end + end + + def changed_files(commit) + removed, added, modified = [], [], [] + commit.show.each do |diff| + if diff.renamed_file + added << diff.b_path + removed << diff.a_path + elsif diff.new_file + added << diff.b_path + elsif diff.deleted_file + removed << diff.a_path + else + modified << diff.a_path + end + end + {:removed => removed, :added => added, :modified => modified} + end + +end diff --git a/app/models/issue.rb b/app/models/issue.rb index cb5efdf9e..210cc7f2f 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -1,4 +1,5 @@ class Issue < ActiveRecord::Base + include Modules::Observers::ActivityFeed::Issue STATUSES = ['open', 'closed'] belongs_to :project @@ -66,6 +67,17 @@ class Issue < ActiveRecord::Base recipients end + def self.find_by_hash_tag hash_tag, current_ability, project + hash_tag =~ /([a-zA-Z0-9\-_]*\/)?([a-zA-Z0-9\-_]*)?#([0-9]+)/ + owner_uname = Regexp.last_match[1].presence || Regexp.last_match[2].presence || project.owner.uname + project_name = Regexp.last_match[1] ? Regexp.last_match[2] : project.name + serial_id = Regexp.last_match[3] + project = Project.find_by_owner_and_name(owner_uname.chomp('/'), project_name) + return nil unless project + return nil unless current_ability.can? :show, project + project.issues.where(:serial_id => serial_id).first + end + protected def set_serial_id diff --git a/app/models/key_pair.rb b/app/models/key_pair.rb index 93c86c69b..96b00dc9b 100644 --- a/app/models/key_pair.rb +++ b/app/models/key_pair.rb @@ -13,14 +13,12 @@ class KeyPair < ActiveRecord::Base validate :check_keys before_create { |record| record.key_id = @fingerprint } - after_create { |record| - AbfWorker::BuildListsPublishTaskManager.resign_repository(record) unless record.repository.platform.personal? - } + after_create { |record| record.repository.resign } protected def check_keys - dir = Dir.mktmpdir('keys-', "#{APP_CONFIG['root_path']}/tmp") + dir = Dir.mktmpdir 'keys-', APP_CONFIG['tmpfs_path'] begin %w(pubring secring).each do |kind| filename = "#{dir}/#{kind}" diff --git a/app/models/mass_build.rb b/app/models/mass_build.rb index 01a7e0477..1515e5106 100644 --- a/app/models/mass_build.rb +++ b/app/models/mass_build.rb @@ -1,19 +1,26 @@ class MassBuild < ActiveRecord::Base - belongs_to :platform + belongs_to :build_for_platform, :class_name => 'Platform', :conditions => {:platform_type => 'main'} + belongs_to :save_to_platform, :class_name => 'Platform' belongs_to :user has_many :build_lists, :dependent => :destroy - scope :by_platform, lambda { |platform| where(:platform_id => platform.id) } + serialize :extra_repositories, Array + serialize :extra_build_lists, Array + + scope :recent, order("#{table_name}.created_at DESC") + scope :by_platform, lambda { |platform| where(:save_to_platform_id => platform.id) } scope :outdated, where("#{table_name}.created_at < ?", Time.now + 1.day - BuildList::MAX_LIVE_TIME) attr_accessor :arches - attr_accessible :arches, :auto_publish, :projects_list + attr_accessible :arches, :auto_publish, :projects_list, :build_for_platform_id, + :extra_repositories, :extra_build_lists, :increase_release_tag - validates :platform_id, :arch_names, :name, :user_id, :projects_list, :presence => true - validates_inclusion_of :auto_publish, :in => [true, false] + validates :save_to_platform_id, :build_for_platform_id, :arch_names, :name, :user_id, :presence => true + validates :projects_list, :length => {:maximum => 500_000}, :presence => true + validates_inclusion_of :auto_publish, :increase_release_tag, :in => [true, false] - after_create :build_all - before_validation :set_data + after_commit :build_all, :on => :create + before_validation :set_data, :on => :create COUNT_STATUSES = [ :build_lists, @@ -29,18 +36,17 @@ class MassBuild < ActiveRecord::Base def build_all # later with resque arches_list = arch_names ? Arch.where(:name => arch_names.split(', ')) : Arch.all - auto_publish ||= false projects_list.lines.each do |name| next if name.blank? name.chomp!; name.strip! - if project = Project.joins(:repositories).where('repositories.id in (?)', platform.repository_ids).find_by_name(name) + if project = Project.joins(:repositories).where('repositories.id in (?)', save_to_platform.repository_ids).find_by_name(name) begin return if self.reload.stop_build arches_list.each do |arch| - rep = (project.repositories & platform.repositories).first - project.build_for(platform, rep.id, user, arch, auto_publish, self.id, 0) + rep_id = (project.repository_ids & save_to_platform.repository_ids).first + project.build_for self, rep_id, arch end rescue RuntimeError, Exception end @@ -82,10 +88,10 @@ class MassBuild < ActiveRecord::Base end later :publish_success_builds, :queue => :clone_build - def publish_test_faild_builds(user) + def publish_test_failed_builds(user) publish user, BuildList::TESTS_FAILED end - later :publish_test_faild_builds, :queue => :clone_build + later :publish_test_failed_builds, :queue => :clone_build private @@ -93,14 +99,19 @@ class MassBuild < ActiveRecord::Base builds = build_lists.where(:status => statuses) builds.update_all(:publisher_id => user.id) builds.order(:id).find_in_batches(:batch_size => 50) do |bls| - bls.each{ |bl| bl.can_publish? && bl.now_publish } + bls.each{ |bl| bl.can_publish? && bl.has_new_packages? && bl.now_publish } end end def set_data - if new_record? - self.name = "#{Time.now.utc.to_date.strftime("%d.%b")}-#{platform.name}" - self.arch_names = Arch.where(:id => self.arches).map(&:name).join(", ") + if save_to_platform + self.name = "#{Time.now.utc.to_date.strftime("%d.%b")}-#{save_to_platform.name}" + self.build_for_platform = save_to_platform if save_to_platform.main? end + self.arch_names = Arch.where(:id => arches).map(&:name).join(", ") + + self.projects_list = projects_list.lines.map do |name| + name.chomp.strip if name.present? + end.compact.uniq.join("\r\n") if projects_list.present? end end diff --git a/app/models/platform.rb b/app/models/platform.rb index 48f0b46e5..45be84861 100644 --- a/app/models/platform.rb +++ b/app/models/platform.rb @@ -1,11 +1,22 @@ class Platform < ActiveRecord::Base - VISIBILITIES = ['open', 'hidden'] + extend FriendlyId + friendly_id :name + + include Modules::Models::FileStoreClean + include Modules::Models::RegenerationStatus + + VISIBILITIES = %w(open hidden) + NAME_PATTERN = /[\w\-\.]+/ + HUMAN_STATUSES = HUMAN_STATUSES.clone.freeze belongs_to :parent, :class_name => 'Platform', :foreign_key => 'parent_platform_id' belongs_to :owner, :polymorphic => true has_many :repositories, :dependent => :destroy has_many :products, :dependent => :destroy + has_many :tokens, :as => :subject, :dependent => :destroy + has_many :platform_arch_settings, :dependent => :destroy + has_many :repository_statuses has_many :relations, :as => :target, :dependent => :destroy has_many :actors, :as => :target, :class_name => 'Relation', :dependent => :destroy @@ -15,18 +26,22 @@ class Platform < ActiveRecord::Base has_many :packages, :class_name => "BuildList::Package", :dependent => :destroy - has_many :mass_builds + has_many :mass_builds, :foreign_key => :save_to_platform_id validates :description, :presence => true - validates :owner, :presence => true validates :visibility, :presence => true, :inclusion => {:in => VISIBILITIES} - validates :name, :uniqueness => {:case_sensitive => false}, :presence => true, :format => { :with => /\A[a-zA-Z0-9_\-\.]+\z/ } + validates :name, :uniqueness => {:case_sensitive => false}, :presence => true, :format => { :with => /\A#{NAME_PATTERN}\z/ } validates :distrib_type, :presence => true, :inclusion => {:in => APP_CONFIG['distr_types']} validate lambda { if released_was && !released errors.add(:released, I18n.t('flash.platform.released_status_can_not_be_changed')) end } + validate lambda { + if personal? && (owner_id_changed? || owner_type_changed?) + errors.add :owner, I18n.t('flash.platform.owner_can_not_be_changed') + end + }, :on => :update before_create :create_directory before_destroy :detele_directory @@ -45,32 +60,49 @@ class Platform < ActiveRecord::Base scope :by_type, lambda {|type| where(:platform_type => type) if type.present?} scope :main, by_type('main') scope :personal, by_type('personal') + scope :waiting_for_regeneration, where(:status => WAITING_FOR_REGENERATION) - attr_accessible :name, :distrib_type, :parent_platform_id, :platform_type, :owner, :visibility, :description, :released + accepts_nested_attributes_for :platform_arch_settings, :allow_destroy => true + attr_accessible :name, :distrib_type, :parent_platform_id, :platform_type, :owner, :visibility, :description, :released, :platform_arch_settings_attributes attr_readonly :name, :distrib_type, :parent_platform_id, :platform_type include Modules::Models::Owner + state_machine :status, :initial => :ready do + event :ready do + transition :regenerating => :ready + end + + event :regenerate do + transition :ready => :waiting_for_regeneration, :if => lambda{ |p| p.main? } + end + + event :start_regeneration do + transition :waiting_for_regeneration => :regenerating + end + + HUMAN_STATUSES.each do |code,name| + state name, :value => code + end + end + def clear system("rm -Rf #{ APP_CONFIG['root_path'] }/platforms/#{ self.name }/repository/*") end def urpmi_list(host = nil, pair = nil, add_commands = true, repository_name = 'main') host ||= default_host - blank_pair = {:login => 'login', :pass => 'password'} - pair = blank_pair if pair.blank? urpmi_commands = ActiveSupport::OrderedHash.new # TODO: rename method or create separate methods for mdv and rhel # Platform.main.opened.where(:distrib_type => APP_CONFIG['distr_types'].first).each do |pl| Platform.main.opened.each do |pl| urpmi_commands[pl.name] = {} - local_pair = pl.id != self.id ? blank_pair : pair - head = hidden? ? "http://#{local_pair[:login]}@#{local_pair[:pass]}:#{host}/private/" : "http://#{host}/downloads/" + # FIXME should support restricting access to the hidden platform Arch.all.each do |arch| tail = "/#{arch.name}/#{repository_name}/release" command = add_commands ? "urpmi.addmedia #{name} " : '' - command << "#{head}#{name}/repository/#{pl.name}#{tail}" + command << "#{APP_CONFIG['downloads_url']}/#{name}/repository/#{pl.name}#{tail}" urpmi_commands[pl.name][arch.name] = command end end @@ -94,24 +126,14 @@ class Platform < ActiveRecord::Base Rails.root.join("public", "downloads", name) end - def prefix_url(pub, options = {}) - options[:host] ||= default_host - pub ? "http://#{options[:host]}/downloads" : "http://#{options[:login]}:#{options[:password]}@#{options[:host]}/private" - end - - def public_downloads_url(host = nil, arch = nil, repo = nil, suffix = nil) - downloads_url prefix_url(true, :host => host), arch, repo, suffix - end - - def private_downloads_url(login, password, host = nil, arch = nil, repo = nil, suffix = nil) - downloads_url prefix_url(false, :host => host, :login => login, :password => password), arch, repo, suffix - end - - def downloads_url(prefix, arch = nil, repo = nil, suffix = nil) - "#{prefix}/#{name}/repository/".tap do |url| + # Returns URL to repository, for example: + # - http://abf-downloads.rosalinux.ru/rosa-server2012/repository/x86_64/base/ + # - http://abf-downloads.rosalinux.ru/uname_personal/repository/rosa-server2012/x86_64/base/ + def public_downloads_url(subplatform_name = nil, arch = nil, repo = nil) + "#{APP_CONFIG['downloads_url']}/#{name}/repository/".tap do |url| + url << "#{subplatform_name}/" if subplatform_name.present? url << "#{arch}/" if arch.present? url << "#{repo}/" if repo.present? - url << "#{suffix}/" if suffix.present? end end @@ -149,10 +171,8 @@ class Platform < ActiveRecord::Base def change_visibility if !hidden? update_attributes(:visibility => 'hidden') - remove_symlink_directory else update_attributes(:visibility => 'open') - symlink_directory end end @@ -185,6 +205,35 @@ class Platform < ActiveRecord::Base EventLog.current_controller.request.host_with_port rescue ::Rosa::Application.config.action_mailer.default_url_options[:host] end + # Checks access rights to platform and caching for 1 day. + def self.allowed?(path, request) + platform_name = path.gsub(/^[\/]+/, '') + .match(/^(#{NAME_PATTERN}\/|#{NAME_PATTERN}$)/) + + return true unless platform_name + platform_name = platform_name[0].gsub(/\//, '') + + if request.authorization.present? + token, pass = *ActionController::HttpAuthentication::Basic::user_name_and_password(request) + end + + Rails.cache.fetch([platform_name, token, :platform_allowed], :expires_in => 2.minutes) do + platform = Platform.find_by_name platform_name + next false unless platform + next true unless platform.hidden? + next false unless token + next true if platform.tokens.by_active.where(:authentication_token => token).exists? + + user = User.find_by_authentication_token token + current_ability = Ability.new(user) + if user && current_ability.can?(:show, platform) + true + else + false + end + end + end + protected def create_directory diff --git a/app/models/platform_arch_setting.rb b/app/models/platform_arch_setting.rb new file mode 100644 index 000000000..82b06820a --- /dev/null +++ b/app/models/platform_arch_setting.rb @@ -0,0 +1,19 @@ +# -*- encoding : utf-8 -*- +class PlatformArchSetting < ActiveRecord::Base + DEFAULT_TIME_LIVING = 43200 # seconds, 12 hours + MIN_TIME_LIVING = 600 # seconds, 10 minutes + MAX_TIME_LIVING = 360000 # seconds, 100 hours, 4 day and 4 hours + include Modules::Models::TimeLiving + + belongs_to :arch + belongs_to :platform + + validates :arch_id, :platform_id, :presence => true + validates :platform_id, :uniqueness => {:scope => :arch_id} + + scope :by_arch, lambda {|arch| where(:arch_id => arch) if arch.present?} + scope :by_default, where(:default => true) + + attr_accessible :arch_id, :platform_id, :default + +end diff --git a/app/models/platform_content.rb b/app/models/platform_content.rb new file mode 100644 index 000000000..1e6e8bbf3 --- /dev/null +++ b/app/models/platform_content.rb @@ -0,0 +1,90 @@ +class PlatformContent + + # ------------------ + # *** ATTRIBUTES *** + # ------------------ + + attr_reader :path + + # --------------- + # *** METHODS *** + # --------------- + + def initialize(platform, path) + @platform, @path = platform, path + end + + def build_list + return @build_list if !!@build_list + return nil if @path !~ /\/(release|updates)+\/[\w\-\.]+$/ + return nil unless repository_name = @path.match(/\/[\w]+\/(release|updates)\//) + repository_name = repository_name[0].gsub(/\/(release|updates)\/$/, '').gsub('/', '') + + repository = @platform.repositories.where(:name => repository_name).first + return nil unless repository + + if @platform.main? + build_for_platform = @platform + else + bfp_name = @path.match(/\/#{@platform.name}\/repository\/[\w]+\//) + return nil unless bfp_name + bfp_name = bfp_name[0].gsub(/\/#{@platform.name}\/repository\//, '').gsub('/', '') + build_for_platform = Platform.main.find_by_name bfp_name + return nil unless build_for_platform + end + + @build_list = BuildList.for_status(BuildList::BUILD_PUBLISHED) + .for_platform(build_for_platform) + .scoped_to_save_platform(@platform) + .where(:save_to_repository_id => repository) + .where(:build_list_packages => {:fullname => name, :actual => true}) + .joins(:packages) + .last + + return @build_list + end + + def name + @name ||= @path.gsub(/.*#{File::SEPARATOR}/, '') + end + + def size + @size ||= File.size(@path) rescue nil + end + + def is_folder? + @is_folder.nil? ? (@is_folder = File.directory?(@path)) : @is_folder + end + + def download_url + suffix = @path.gsub(/^#{@platform.path}/, '') + "#{APP_CONFIG['downloads_url']}/#{@platform.name}#{suffix}" + end + + # --------------------- + # *** CLASS METHODS *** + # --------------------- + + def self.find_by_platform(platform, path, term) + # Strip out the non-ascii character + term = (term || '').strip.gsub(/[\\\/]+/, '') + .gsub(/[^\w\-\+\.]/, '_') + + path = path.split(File::SEPARATOR).map(&:strip).select(&:present?) + .map{ |p| + # Strip out the non-ascii character + p.gsub(/[\\\/]+/, '') + .gsub(/^[\.]+/, '') + .gsub(/[^\w\-\.]/, '_') + } + .join(File::SEPARATOR) + results = Dir.glob(File.join(platform.path, path, "*#{term}*")) + if term + results = results.sort_by(&:length) + else + results = results.sort + end + results.map{ |p| PlatformContent.new(platform, p) } + end + +end \ No newline at end of file diff --git a/app/models/product_build_list.rb b/app/models/product_build_list.rb index 42653b902..edb7f2b50 100644 --- a/app/models/product_build_list.rb +++ b/app/models/product_build_list.rb @@ -2,6 +2,7 @@ class ProductBuildList < ActiveRecord::Base include Modules::Models::CommitAndVersion include Modules::Models::TimeLiving include Modules::Models::FileStoreClean + include Modules::Models::UrlHelper include AbfWorker::ModelHelper delegate :url_helpers, to: 'Rails.application.routes' @@ -21,7 +22,7 @@ class ProductBuildList < ActiveRecord::Base BUILD_PENDING, BUILD_CANCELED, BUILD_CANCELING - ] + ].freeze HUMAN_STATUSES = { BUILD_STARTED => :build_started, BUILD_COMPLETED => :build_completed, @@ -29,7 +30,7 @@ class ProductBuildList < ActiveRecord::Base BUILD_PENDING => :build_pending, BUILD_CANCELED => :build_canceled, BUILD_CANCELING => :build_canceling - } + }.freeze belongs_to :product belongs_to :project @@ -118,10 +119,6 @@ class ProductBuildList < ActiveRecord::Base [BUILD_STARTED, BUILD_PENDING].include? status end - def container_path - "/downloads/#{product.platform.name}/product/#{id}/" - end - def event_log_message {:product => product.name}.inspect end @@ -154,7 +151,7 @@ class ProductBuildList < ActiveRecord::Base def abf_worker_args file_name = "#{project.name}-#{commit_hash}" - opts = {:host => ActionMailer::Base.default_url_options[:host]} + opts = default_url_options opts.merge!({:user => user.authentication_token, :password => ''}) if user.present? srcpath = url_helpers.archive_url( project.owner, @@ -171,8 +168,11 @@ class ProductBuildList < ActiveRecord::Base :params => params, :time_living => time_living, :main_script => main_script, - :arch => arch.name, - :distrib_type => product.platform.distrib_type, + :platform => { + :type => product.platform.distrib_type, + :name => product.platform.name, + :arch => arch.name + }, :user => {:uname => user.try(:uname), :email => user.try(:email)} } end diff --git a/app/models/project.rb b/app/models/project.rb index 0a8291abf..9bf614d09 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1,7 +1,7 @@ class Project < ActiveRecord::Base VISIBILITIES = ['open', 'hidden'] MAX_OWN_PROJECTS = 32000 - NAME_REGEXP = /[a-zA-Z0-9_\-\+\.]+/ + NAME_REGEXP = /[\w\-\+\.]+/ belongs_to :owner, :polymorphic => true, :counter_cache => :own_projects_count belongs_to :maintainer, :class_name => "User" @@ -14,8 +14,10 @@ class Project < ActiveRecord::Base has_many :project_to_repositories, :dependent => :destroy has_many :repositories, :through => :project_to_repositories has_many :project_tags, :dependent => :destroy + has_many :project_statistics, :dependent => :destroy has_many :build_lists, :dependent => :destroy + has_many :hooks, :dependent => :destroy has_many :relations, :as => :target, :dependent => :destroy has_many :collaborators, :through => :relations, :source => :actor, :source_type => 'User' @@ -26,20 +28,39 @@ class Project < ActiveRecord::Base validates :name, :uniqueness => {:scope => [:owner_id, :owner_type], :case_sensitive => false}, :presence => true, - :format => {:with => /\A#{NAME_REGEXP}\z/, :message => I18n.t("activerecord.errors.project.uname")} - validates :owner, :presence => true + :format => {:with => /\A#{NAME_REGEXP}\z/, + :message => I18n.t("activerecord.errors.project.uname")} validates :maintainer_id, :presence => true, :unless => :new_record? + validates :url, :presence => true, :format => {:with => /\Ahttps?:\/\/[\S]+\z/}, :if => :mass_import + validates :add_to_repository_id, :presence => true, :if => :mass_import validates :visibility, :presence => true, :inclusion => {:in => VISIBILITIES} validate { errors.add(:base, :can_have_less_or_equal, :count => MAX_OWN_PROJECTS) if owner.projects.size >= MAX_OWN_PROJECTS } validate :check_default_branch + # throws validation error message from ProjectToRepository model into Project model + validate do |project| + project.project_to_repositories.each do |p_to_r| + next if p_to_r.valid? + p_to_r.errors.full_messages.each{ |msg| errors[:base] << msg } + end + errors.delete :project_to_repositories + end - attr_accessible :name, :description, :visibility, :srpm, :is_package, :default_branch, :has_issues, :has_wiki, :maintainer_id, :publish_i686_into_x86_64 - attr_readonly :name, :owner_id, :owner_type + attr_accessible :name, :description, :visibility, :srpm, :is_package, :default_branch, + :has_issues, :has_wiki, :maintainer_id, :publish_i686_into_x86_64, + :url, :srpms_list, :mass_import, :add_to_repository_id + attr_readonly :owner_id, :owner_type - scope :recent, order("#{table_name}.name ASC") + scope :recent, order("lower(#{table_name}.name) ASC") scope :search_order, order("CHAR_LENGTH(#{table_name}.name) ASC") - scope :search, lambda {|q| by_name("%#{q.to_s.strip}%")} + scope :search, lambda {|q| + q = q.to_s.strip + by_name("%#{q}%").search_order if q.present? + } scope :by_name, lambda {|name| where("#{table_name}.name ILIKE ?", name) if name.present?} + scope :by_owner_and_name, lambda { |*params| + term = params.map(&:strip).join('/').downcase + where("lower(concat(owner_uname, '/', name)) ILIKE ?", "%#{term}%") if term.present? + } scope :by_visibilities, lambda {|v| where(:visibility => v)} scope :opened, where(:visibility => 'open') scope :package, where(:is_package => true) @@ -57,22 +78,25 @@ class Project < ActiveRecord::Base } before_validation :truncate_name, :on => :create + before_save lambda { self.owner_uname = owner.uname if owner_uname.blank? || owner_id_changed? || owner_type_changed? } before_create :set_maintainer after_save :attach_to_personal_repository after_update :set_new_git_head + after_update lambda { update_path_to_project(name_was) }, :if => :name_changed? has_ancestry :orphan_strategy => :rootify #:adopt not available yet + attr_accessor :url, :srpms_list, :mass_import, :add_to_repository_id + include Modules::Models::Owner include Modules::Models::Git include Modules::Models::Wiki + include Modules::Models::UrlHelper class << self def find_by_owner_and_name(owner_name, project_name) - owner = User.find_by_uname(owner_name) || Group.find_by_uname(owner_name) || User.by_uname(owner_name).first || Group.by_uname(owner_name).first and - scoped = where(:owner_id => owner.id, :owner_type => owner.class) and - scoped.find_by_name(project_name) || scoped.by_name(project_name).first - # owner.projects.find_by_name(project_name) || owner.projects.by_name(project_name).first # TODO force this work? + where(:owner_uname => owner_name, :name => project_name).first || + by_owner_and_name(owner_name, project_name).first end def find_by_owner_and_name!(owner_name, project_name) @@ -80,6 +104,14 @@ class Project < ActiveRecord::Base end end + def init_mass_import + Project.perform_later :clone_build, :run_mass_import, url, srpms_list, visibility, owner, add_to_repository_id + end + + def name_with_owner + "#{owner_uname || owner.uname}/#{name}" + end + def to_param name end @@ -123,41 +155,54 @@ class Project < ActiveRecord::Base end def git_project_address auth_user - host ||= EventLog.current_controller.request.host_with_port rescue ::Rosa::Application.config.action_mailer.default_url_options[:host] - protocol = APP_CONFIG['mailer_https_url'] ? "https" : "http" rescue "http" - opts = {:host => host, :protocol => protocol} + opts = default_url_options opts.merge!({:user => auth_user.authentication_token, :password => ''}) unless self.public? - Rails.application.routes.url_helpers.project_url(self.owner.uname, self.name, opts) + ".git" + Rails.application.routes.url_helpers.project_url(self.owner.uname, self.name, opts) + '.git' #path #share by NFS end - def build_for(platform, repository_id, user, arch = Arch.find_by_name('i586'), auto_publish = false, mass_build_id = nil, priority = 0) + def build_for(mass_build, repository_id, arch = Arch.find_by_name('i586'), priority = 0) + build_for_platform = mass_build.build_for_platform + save_to_platform = mass_build.save_to_platform + user = mass_build.user # Select main and project platform repository(contrib, non-free and etc) # If main does not exist, will connect only project platform repository # If project platform repository is main, only main will be connect - main_rep_id = platform.repositories.find_by_name('main').try(:id) - build_reps_ids = [main_rep_id, repository_id].compact.uniq + main_rep_id = build_for_platform.repositories.find_by_name(%w(main base)).try(:id) + include_repos = ([main_rep_id] << (save_to_platform.main? ? repository_id : nil)).compact.uniq + + project_version = if repo.commits("#{save_to_platform.name}").try(:first).try(:id) + save_to_platform.name + elsif repo.commits("#{build_for_platform.name}").try(:first).try(:id) + build_for_platform.name + else + default_branch + end + + increase_release_tag(project_version, user, "MassBuild##{mass_build.id}: Increase release tag") if mass_build.increase_release_tag? - project_version = repo.commits("#{platform.name}").try(:first).try(:id) ? - platform.name : 'master' build_list = build_lists.build do |bl| - bl.save_to_platform = platform - bl.build_for_platform = platform - bl.update_type = 'newpackage' - bl.arch = arch - bl.project_version = project_version - bl.user = user - bl.auto_publish = auto_publish - bl.include_repos = build_reps_ids - bl.priority = priority - bl.mass_build_id = mass_build_id - bl.save_to_repository_id = repository_id + bl.save_to_platform = save_to_platform + bl.build_for_platform = build_for_platform + bl.update_type = 'newpackage' + bl.arch = arch + bl.project_version = project_version + bl.user = user + bl.auto_publish = mass_build.auto_publish? + bl.include_repos = include_repos + bl.extra_repositories = mass_build.extra_repositories + bl.extra_build_lists = mass_build.extra_build_lists + bl.priority = priority + bl.mass_build_id = mass_build.id + bl.save_to_repository_id = repository_id end build_list.save end - def fork(new_owner) + def fork(new_owner, new_name = name) + new_name = new_name.presence || name dup.tap do |c| + c.name = new_name c.parent_id = id c.owner = new_owner c.updated_at = nil; c.created_at = nil # :id = nil @@ -167,14 +212,6 @@ class Project < ActiveRecord::Base end end - def human_average_build_time - I18n.t("layout.projects.human_average_build_time", {:hours => (average_build_time/3600).to_i, :minutes => (average_build_time%3600/60).to_i}) - end - - def formatted_average_build_time - "%02d:%02d" % [average_build_time / 3600, average_build_time % 3600 / 60] - end - def destroy_project_from_repository(repository) AbfWorker::BuildListsPublishTaskManager.destroy_project_from_repository self, repository end @@ -226,8 +263,51 @@ class Project < ActiveRecord::Base @archive ||= create_archive treeish, format end + # Finds release tag and increase its: + # 'Release: %mkrel 4mdk' => 'Release: 5mdk' + # 'Release: 4' => 'Release: 5' + # Finds release macros and increase it: + # '%define release %mkrel 4mdk' => '%define release 5mdk' + # '%define release 4' => '%define release 5' + def self.replace_release_tag(content) + + build_new_release = Proc.new do |release, combine_release| + if combine_release.present? + r = combine_release.split('.').last.to_i + release << combine_release.gsub(/.[\d]+$/, '') << ".#{r + 1}" + else + release = release.to_i + 1 + end + release + end + + content.gsub(/^Release:(\s+)(%mkrel\s+)?(\d+)([.\d]+)?(mdk)?$/) do |line| + tab, mkrel, mdk = $1, $2, $5 + "Release:#{tab}#{build_new_release.call($3, $4)}#{mdk}" + end.gsub(/^%define\s+release:?(\s+)(%mkrel\s+)?(\d+)([.\d]+)?(mdk)?$/) do |line| + tab, mkrel, mdk = $1, $2, $5 + "%define release#{tab}#{build_new_release.call($3, $4)}#{mdk}" + end + end + protected + def increase_release_tag(project_version, user, message) + blob = repo.tree(project_version).contents.find{ |n| n.is_a?(Grit::Blob) && n.name =~ /.spec$/ } + return unless blob + + raw = Grit::GitRuby::Repository.new(repo.path).get_raw_object_by_sha1(blob.id) + content = self.class.replace_release_tag raw.content + return if content == raw.content + + update_file(blob.name, content.gsub("\r", ''), + :message => message, + :actor => user, + :head => project_version + ) + end + + def create_archive(treeish, format) file_name = "#{name}-#{treeish}" fullname = "#{file_name}.#{tag_file_format(format)}" @@ -249,11 +329,11 @@ class Project < ActiveRecord::Base end def attach_to_personal_repository - owner_rep = self.owner.personal_repository + owner_repos = self.owner.personal_platform.repositories if is_package - repositories << owner_rep unless repositories.exists?(:id => owner_rep) + repositories << self.owner.personal_repository unless repositories.exists?(:id => owner_repos.pluck(:id)) else - repositories.delete owner_rep + repositories.delete owner_repos end end @@ -267,6 +347,27 @@ class Project < ActiveRecord::Base `cd #{path} && git symbolic-ref HEAD refs/heads/#{self.default_branch}` if self.default_branch_changed? && self.repo.branches.map(&:name).include?(self.default_branch) end + def update_path_to_project(old_name) + new_name, new_path = name, path + self.name = old_name + old_path = path + self.name = new_name + FileUtils.mv old_path, new_path, :force => true if Dir.exists?(old_path) + + pull_requests_old_path = File.join(APP_CONFIG['git_path'], 'pull_requests', owner.uname, old_name) + if Dir.exists?(pull_requests_old_path) + FileUtils.mv pull_requests_old_path, + File.join(APP_CONFIG['git_path'], 'pull_requests', owner.uname, new_name), + :force => true + end + + PullRequest.where(:from_project_id => id).update_all(:from_project_name => new_name) + + PullRequest.where(:from_project_id => id).each{ |p| p.update_relations(old_name) } + pull_requests.where('from_project_id != to_project_id').each(&:update_relations) + end + later :update_path_to_project, :queue => :clone_build + def check_default_branch if self.repo.branches.count > 0 && self.repo.branches.map(&:name).exclude?(self.default_branch) errors.add :default_branch, I18n.t('activerecord.errors.project.default_branch') diff --git a/app/models/project_statistic.rb b/app/models/project_statistic.rb new file mode 100644 index 000000000..717c5d8ad --- /dev/null +++ b/app/models/project_statistic.rb @@ -0,0 +1,10 @@ +class ProjectStatistic < ActiveRecord::Base + + belongs_to :arch + belongs_to :project + + validates :arch_id, :project_id, :average_build_time, :build_count, :presence => true + validates :project_id, :uniqueness => {:scope => :arch_id} + + attr_accessible :average_build_time, :build_count +end diff --git a/app/models/project_to_repository.rb b/app/models/project_to_repository.rb index efbcbe8b0..1f128ca77 100644 --- a/app/models/project_to_repository.rb +++ b/app/models/project_to_repository.rb @@ -6,12 +6,12 @@ class ProjectToRepository < ActiveRecord::Base after_destroy lambda { project.destroy_project_from_repository(repository) }, :unless => lambda {Thread.current[:skip]} - validate :one_project_in_platform_repositories + validate :one_project_in_platform_repositories, :on => :create protected def one_project_in_platform_repositories - errors.add(:project, 'should be one in platform') if Project.joins(:repositories => :platform). - where('platforms.id = ?', repository.platform_id).by_name(project.name).count > 0 + errors.add(:base, I18n.t('activerecord.errors.project_to_repository.project')) if Project.joins(:repositories => :platform). + where('platforms.id = ?', repository.platform_id).by_name(project.name).exists? end end diff --git a/app/models/pull_request.rb b/app/models/pull_request.rb index 47a2ff04d..aaeec2e26 100644 --- a/app/models/pull_request.rb +++ b/app/models/pull_request.rb @@ -20,6 +20,8 @@ class PullRequest < ActiveRecord::Base attr_accessible :issue_attributes, :to_ref, :from_ref scope :needed_checking, includes(:issue).where(:issues => {:status => ['open', 'blocked', 'ready']}) + scope :not_closed_or_merged, needed_checking + scope :closed_or_merged, where(:issues => {:status => ['closed', 'merged']}) state_machine :status, :initial => :open do event :ready do @@ -47,6 +49,20 @@ class PullRequest < ActiveRecord::Base end end + def update_relations(old_from_project_name = nil) + FileUtils.mv path(old_from_project_name), path, :force => true if old_from_project_name + return unless Dir.exists?(path) + Dir.chdir(path) do + system 'git', 'remote', 'set-url', 'origin', to_project.path + system 'git', 'remote', 'set-url', 'head', from_project.path if cross_pull? + end + end + later :update_relations, :queue => :clone_build + + def cross_pull? + from_project_id != to_project_id + end + def check(do_transaction = true) if do_transaction && !valid? issue.set_close nil @@ -97,17 +113,13 @@ class PullRequest < ActiveRecord::Base end end - def path - last_part = [id, from_project_owner_uname, from_project_name].compact.join('-') + def path(suffix = from_project_name) + last_part = [id, from_project_owner_uname, suffix].compact.join('-') File.join(APP_CONFIG['git_path'], "#{new_record? ? 'temp_' : ''}pull_requests", to_project.owner.uname, to_project.name, last_part) end def from_branch - if to_project_id != from_project_id - "head_#{from_ref}" - else - from_ref - end + cross_pull? ? "head_#{from_ref}" : from_ref end def common_ancestor @@ -164,7 +176,7 @@ class PullRequest < ActiveRecord::Base def merge clone message = "Merge pull request ##{serial_id} from #{from_project_owner_uname}/#{from_project_name}:#{from_ref}\r\n #{title}" - %x(cd #{path} && git checkout #{to_ref} && git merge --no-ff #{from_branch} -m '#{message}') + %x(cd #{path} && git checkout #{to_ref.shellescape} && git merge --no-ff #{from_branch.shellescape} -m #{message.shellescape}) end def clone @@ -176,12 +188,12 @@ class PullRequest < ActiveRecord::Base `rm -rf #{path}` git.fs_mkdir('..') git.clone(options, to_project.path, path) - if to_project != from_project + if cross_pull? Dir.chdir(path) do system 'git', 'remote', 'add', 'head', from_project.path end end - clean # Need testing + #clean # Need testing end Dir.chdir(path) do @@ -196,6 +208,7 @@ class PullRequest < ActiveRecord::Base unless tags.include? from_ref system 'git', 'branch', '-D', from_branch system 'git', 'fetch', head, "+#{from_ref}:#{from_branch}" + system 'git', 'checkout', to_ref end end end diff --git a/app/models/relation.rb b/app/models/relation.rb index 28d16c69e..98ddf964a 100644 --- a/app/models/relation.rb +++ b/app/models/relation.rb @@ -33,7 +33,11 @@ class Relation < ActiveRecord::Base def self.remove_member(member, target) return false if target.respond_to?(:owner) && target.owner == member - Relation.by_actor(member).by_target(target).each{|r| r.destroy} + res = Relation.by_actor(member).by_target(target).each{|r| r.destroy} + if member.is_a?(User) && ['Project', 'Group'].include?(target.class.name) + member.check_assigned_issues target + end + res end protected diff --git a/app/models/repository.rb b/app/models/repository.rb index 9044848c2..62e6c6092 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1,4 +1,10 @@ class Repository < ActiveRecord::Base + extend FriendlyId + friendly_id :name + + LOCK_FILE_NAMES = {:sync => '.sync.lock', :repo => '.repo.lock'} + SORT = {'base' => 1, 'main' => 2, 'contrib' => 3, 'non-free' => 4, 'restricted' => 5} + belongs_to :platform has_many :relations, :as => :target, :dependent => :destroy @@ -7,6 +13,7 @@ class Repository < ActiveRecord::Base has_many :project_to_repositories, :dependent => :destroy, :validate => true has_many :projects, :through => :project_to_repositories + has_many :repository_statuses, :dependent => :destroy has_one :key_pair, :dependent => :destroy has_many :build_lists, :foreign_key => :save_to_repository_id, :dependent => :destroy @@ -16,10 +23,24 @@ class Repository < ActiveRecord::Base scope :recent, order("#{table_name}.name ASC") - before_destroy :detele_directory, :unless => lambda {Thread.current[:skip]} + before_destroy :detele_directory attr_accessible :name, :description, :publish_without_qa attr_readonly :name, :platform_id + attr_accessor :projects_list + + def regenerate(build_for_platform_id = nil) + build_for_platform = Platform.main.find build_for_platform_id if platform.personal? + status = repository_statuses.find_or_create_by_platform_id(build_for_platform.try(:id) || platform_id) + status.regenerate + end + + def resign + if platform.main? + status = repository_statuses.find_or_create_by_platform_id(platform_id) + status.resign + end + end def base_clone(attrs = {}) dup.tap do |c| @@ -36,12 +57,74 @@ class Repository < ActiveRecord::Base end later :clone_relations, :loner => true, :queue => :clone_build + def add_projects(list, user) + current_ability = Ability.new(user) + list.lines.each do |line| + begin + line.chomp!; line.strip! + owner, name = line.split('/') + next if owner.blank? || name.blank? + + project = Project.where(:owner_uname => owner, :name => name).accessible_by(current_ability, :read).first + projects << project if project + rescue RuntimeError, Exception + end + end + end + later :add_projects, :queue => :clone_build + + def remove_projects(list) + list.lines.each do |name| + begin + name.chomp!; name.strip! + next if name.blank? + project_to_repositories.where(:projects => { :name => name }).joins(:project).destroy_all + rescue RuntimeError, Exception + end + end + end + later :remove_projects, :queue => :clone_build + def full_clone(attrs = {}) base_clone(attrs).tap do |c| with_skip {c.save} and c.clone_relations(self) # later with resque end end + # Checks locking of sync + def sync_lock_file_exists? + lock_file_actions :check, :sync + end + + # Uses for locking sync + # Calls from UI + def add_sync_lock_file + lock_file_actions :add, :sync + end + + # Uses for unlocking sync + # Calls from UI + def remove_sync_lock_file + lock_file_actions :remove, :sync + end + + # Uses for locking publishing + # Calls from API + def add_repo_lock_file + lock_file_actions :add, :repo + end + + # Uses for unlocking publishing + # Calls from API + def remove_repo_lock_file + lock_file_actions :remove, :repo + end + + # Presence of `.repo.lock` file means that mirror is currently synchronising the repository state. + def repo_lock_file_exists? + lock_file_actions :check, :repo + end + def add_member(member, role = 'admin') Relation.add_member(member, self, role) end @@ -63,9 +146,30 @@ class Repository < ActiveRecord::Base end later :destroy, :queue => :clone_build + def self.custom_sort(repos) + repos.select{ |r| SORT.keys.include?(r.name) }.sort{ |a,b| SORT[a.name] <=> SORT[b.name] } | repos.sort_by(&:name) + end + protected + def lock_file_actions(action, lock_file) + result = false + (['SRPMS'] << Arch.pluck(:name)).each do |arch| + path = "#{platform.path}/repository/#{arch}/#{name}/#{LOCK_FILE_NAMES[lock_file]}" + case action + when :add + result ||= FileUtils.touch(path) rescue nil + when :remove + result ||= FileUtils.rm_f(path) + when :check + return true if File.exist?(path) + end + end + return result + end + def detele_directory + return unless platform repository_path = platform.path << '/repository' if platform.personal? Platform.main.pluck(:name).each do |main_platform_name| diff --git a/app/models/repository_status.rb b/app/models/repository_status.rb new file mode 100644 index 000000000..1b2c84630 --- /dev/null +++ b/app/models/repository_status.rb @@ -0,0 +1,84 @@ +class RepositoryStatus < ActiveRecord::Base + include Modules::Models::FileStoreClean + include Modules::Models::RegenerationStatus + + WAITING_FOR_RESIGN = 300 + PUBLISH = 400 + RESIGN = 500 + WAITING_FOR_RESIGN_AFTER_PUBLISH = 600 + WAITING_FOR_RESIGN_AFTER_REGENERATION = 700 + WAITING_FOR_REGENERATION_AFTER_PUBLISH = 800 + WAITING_FOR_REGENERATION_AFTER_RESIGN = 900 + WAITING_FOR_RESIGN_AND_REGENERATION_AFTER_PUBLISH = 1000 + WAITING_FOR_RESIGN_AND_REGENERATION = 1100 + + + HUMAN_STATUSES = HUMAN_STATUSES.clone.merge({ + WAITING_FOR_RESIGN => :waiting_for_resign, + PUBLISH => :publish, + RESIGN => :resign, + WAITING_FOR_RESIGN_AFTER_PUBLISH => :waiting_for_resign_after_publish, + WAITING_FOR_RESIGN_AFTER_REGENERATION => :waiting_for_resign_after_regeneration, + WAITING_FOR_REGENERATION_AFTER_PUBLISH => :waiting_for_regeneration_after_publish, + WAITING_FOR_REGENERATION_AFTER_RESIGN => :waiting_for_regeneration_after_resign, + WAITING_FOR_RESIGN_AND_REGENERATION_AFTER_PUBLISH => :waiting_for_resign_and_regeneration_after_publish, + WAITING_FOR_RESIGN_AND_REGENERATION => :waiting_for_resign_and_regeneration + }).freeze + + belongs_to :platform + belongs_to :repository + + validates :repository_id, :platform_id, :presence => true + validates :repository_id, :uniqueness => {:scope => :platform_id} + + attr_accessible :platform_id, :repository_id + + scope :platform_ready, where(:platforms => {:status => READY}).joins(:platform) + scope :for_regeneration, where(:status => WAITING_FOR_REGENERATION) + scope :for_resign, where(:status => [WAITING_FOR_RESIGN, WAITING_FOR_RESIGN_AND_REGENERATION]) + scope :not_ready, where('repository_statuses.status != ?', READY) + + state_machine :status, :initial => :ready do + event :ready do + transition [:regenerating, :publish, :resign] => :ready + transition [:waiting_for_resign_after_publish, :waiting_for_resign_after_regeneration] => :waiting_for_resign + transition [:waiting_for_regeneration_after_publish, :waiting_for_regeneration_after_resign] => :waiting_for_regeneration + transition :waiting_for_resign_and_regeneration_after_publish => :waiting_for_resign_and_regeneration + end + + event :regenerate do + transition :ready => :waiting_for_regeneration + transition :publish => :waiting_for_regeneration_after_publish + transition :resign => :waiting_for_regeneration_after_resign + transition :waiting_for_resign_after_publish => :waiting_for_resign_and_regeneration_after_publish + transition :waiting_for_resign => :waiting_for_resign_and_regeneration + end + + event :start_regeneration do + transition :waiting_for_regeneration => :regenerating + transition :waiting_for_resign_and_regeneration => :waiting_for_resign_after_regeneration + end + + event :resign do + transition :ready => :waiting_for_resign + transition :publish => :waiting_for_resign_after_publish + transition :waiting_for_regeneration => :waiting_for_resign_and_regeneration + transition :waiting_for_regeneration_after_publish => :waiting_for_resign_and_regeneration_after_publish + transition :regenerating => :waiting_for_resign_after_regeneration + end + + event :start_resign do + transition :waiting_for_resign => :resign + transition :waiting_for_resign_and_regeneration => :waiting_for_regeneration_after_resign + end + + event :publish do + transition :ready => :publish + end + + HUMAN_STATUSES.each do |code,name| + state name, :value => code + end + end + +end \ No newline at end of file diff --git a/app/models/rpm_build_node.rb b/app/models/rpm_build_node.rb new file mode 100644 index 000000000..c620f7521 --- /dev/null +++ b/app/models/rpm_build_node.rb @@ -0,0 +1,39 @@ +require 'ohm' +require 'ohm/expire' + +class RpmBuildNode < Ohm::Model + include Ohm::Expire + + TTL = 120 + + expire TTL + + attribute :user_id + attribute :worker_count + attribute :busy_workers + attribute :system + + def user + User.where(:id => user_id).first + end + + def self.cleanup! + RpmBuildNode.all.each do |n| + n.delete unless n.user_id + end + end + + def self.total_statistics + systems, others, busy = 0, 0, 0 + RpmBuildNode.all.select{ |n| n.user_id }.each do |n| + if n.system == 'true' + systems += n.worker_count.to_i + else + others += n.worker_count.to_i + end + busy += n.busy_workers.to_i + end + { :systems => systems, :others => others, :busy => busy } + end + +end \ No newline at end of file diff --git a/app/models/ssh_key.rb b/app/models/ssh_key.rb index a2c615585..ec09df1ce 100644 --- a/app/models/ssh_key.rb +++ b/app/models/ssh_key.rb @@ -22,7 +22,7 @@ class SshKey < ActiveRecord::Base def set_fingerprint return false unless key - file = Tempfile.new('key_file', "#{APP_CONFIG['root_path']}/tmp") + file = Tempfile.new('key_file', '/tmp') filename = file.path begin file.puts key diff --git a/app/models/token.rb b/app/models/token.rb new file mode 100644 index 000000000..b41879458 --- /dev/null +++ b/app/models/token.rb @@ -0,0 +1,32 @@ +# -*- encoding : utf-8 -*- +class Token < ActiveRecord::Base + belongs_to :subject, :polymorphic => true, :touch => true + belongs_to :creator, :class_name => 'User' + belongs_to :updater, :class_name => 'User' + + validates :creator_id, :subject_id, :subject_type, :presence => true + validates :authentication_token, :presence => true, :uniqueness => {:case_sensitive => true} + + default_scope order("#{table_name}.created_at desc") + scope :by_active, where(:status => 'active') + + before_validation :generate_token, :on => :create + + attr_accessible :description + + state_machine :status, :initial => :active do + event :block do + transition [:active, :blocked] => :blocked + end + end + + protected + + def generate_token + self.authentication_token = loop do + token = SecureRandom.urlsafe_base64(32) + break token unless Token.where(:authentication_token => token).exists? + end + end + +end diff --git a/app/models/user.rb b/app/models/user.rb index 00a410c1e..9d0c03dc4 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -6,6 +6,7 @@ class User < Avatar devise :database_authenticatable, :registerable, :omniauthable, :token_authenticatable,# :encryptable, :timeoutable :recoverable, :rememberable, :validatable, :lockable, :confirmable#, :reconfirmable, :trackable + devise :omniauthable, :omniauth_providers => [:facebook, :google_oauth2, :github] has_one :notifier, :class_name => 'SettingsNotifier', :dependent => :destroy #:notifier @@ -22,10 +23,13 @@ class User < Avatar has_many :projects, :through => :targets, :source => :target, :source_type => 'Project', :autosave => true has_many :groups, :through => :targets, :source => :target, :source_type => 'Group', :autosave => true has_many :platforms, :through => :targets, :source => :target, :source_type => 'Platform', :autosave => true + has_many :repositories, :through => :targets, :source => :target, :source_type => 'Repository' has_many :own_projects, :as => :owner, :class_name => 'Project', :dependent => :destroy has_many :own_groups, :foreign_key => :owner_id, :class_name => 'Group', :dependent => :destroy has_many :own_platforms, :as => :owner, :class_name => 'Platform', :dependent => :destroy + has_many :issues + has_many :assigned_issues, :foreign_key => :assignee_id, :class_name => 'Issue', :dependent => :nullify has_many :key_pairs has_many :ssh_keys, :dependent => :destroy @@ -36,7 +40,7 @@ class User < Avatar validates :language, :inclusion => {:in => LANGUAGES}, :allow_blank => true attr_accessible :email, :password, :password_confirmation, :current_password, :remember_me, :login, :name, :uname, :language, - :site, :company, :professional_experience, :location + :site, :company, :professional_experience, :location, :sound_notifications attr_readonly :uname attr_accessor :login @@ -55,6 +59,7 @@ class User < Avatar include Modules::Models::PersonalRepository include Modules::Models::ActsLikeMember + include Modules::Observers::ActivityFeed::User def admin? role == 'admin' @@ -97,20 +102,6 @@ class User < Avatar { :value => login.downcase, :orig_value => login }]).first end - def new_with_session(params, session) - super.tap do |user| - if data = session["devise.omniauth_data"] - if info = data['info'] and info.present? - user.email = info['email'].presence if user.email.blank? - user.uname ||= info['nickname'].presence || info['username'].presence - user.name ||= info['name'].presence || [info['first_name'], info['last_name']].join(' ').strip - end - user.password = Devise.friendly_token[0,20] # stub password - user.authentications.build :uid => data['uid'], :provider => data['provider'] - end - end - end - def auth_by_token_or_login_pass(user, pass) u = User.find_for_database_authentication(:login => user) u if u && !u.access_locked? && (u.authentication_token == user || u.valid_password?(pass)) @@ -153,6 +144,19 @@ class User < Avatar raise "unknown user #{self.uname} roles #{roles}" end + def check_assigned_issues target + if target.is_a? Project + assigned_issues.where(:project_id => target.id).update_all(:assignee_id => nil) + else + ability = Ability.new self + project_ids = Project.accessible_by(ability, :membered).uniq.pluck(:id) + + issues = assigned_issues + issues = issues.where('project_id not in (?)', project_ids) if project_ids.present? + issues.update_all(:assignee_id => nil) + end + end + protected def target_roles target diff --git a/app/presenters/comment_presenter.rb b/app/presenters/comment_presenter.rb index 22b2a937e..b421ec414 100644 --- a/app/presenters/comment_presenter.rb +++ b/app/presenters/comment_presenter.rb @@ -1,14 +1,35 @@ class CommentPresenter < ApplicationPresenter + include PullRequestHelper attr_accessor :comment, :options - attr_reader :header, :image, :date, :caption, :content, :buttons + attr_reader :header, :image, :date, :caption, :content, :buttons, :is_reference_to_issue, + :reference_project def initialize(comment, opts = {}) - @comment = comment - @user = comment.user - @options = opts + @is_reference_to_issue = !!(comment.automatic && comment.created_from_issue_id) # is it reference issue from another issue + @comment, @user, @options = comment, comment.user, opts - @content = @comment.body + unless @is_reference_to_issue + @content = @comment.body + else + issue = Issue.where(:id => comment.created_from_issue_id).first + @referenced_issue = issue.pull_request || issue + @reference_project = issue.project + if issue && (comment.data[:comment_id].nil? || Comment.exists?(comment.data[:comment_id])) + title = if issue == opts[:commentable] + "#{issue.serial_id}" + elsif issue.project.owner == opts[:commentable].project.owner + "#{issue.project.name}##{issue.serial_id}" + else + "#{issue.project.name_with_owner}##{issue.serial_id}" + end + title = "#{title}:" + issue_link = project_issue_path(issue.project, issue) + @content = "#{title} #{issue.title}".html_safe + else + @content = t 'layout.comments.removed' + end + end end def expandable? @@ -16,7 +37,7 @@ class CommentPresenter < ApplicationPresenter end def buttons? - true + !@is_reference_to_issue # dont show for automatic comment end def content? @@ -27,24 +48,33 @@ class CommentPresenter < ApplicationPresenter false end + def issue_referenced_state? + @referenced_issue # show state of the existing referenced issue + end + def buttons project, commentable = options[:project], options[:commentable] path = helpers.project_commentable_comment_path(project, commentable, comment) - res = [link_to(t("layout.link"), "#{helpers.project_commentable_path(project, commentable)}##{comment_anchor}", :class => "#{@options[:in_discussion].present? ? 'in_discussion_' : ''}link_to_comment").html_safe] + res = [link_to(t('layout.link'), "#{helpers.project_commentable_path(project, commentable)}##{comment_anchor}", :class => "#{@options[:in_discussion].present? ? 'in_discussion_' : ''}link_to_comment").html_safe] if controller.can? :update, @comment - res << link_to(t("layout.edit"), path, :id => "comment-#{comment.id}", :class => "edit_comment").html_safe + res << link_to(t('layout.edit'), path, :id => "comment-#{comment.id}", :class => "edit_comment").html_safe end if controller.can? :destroy, @comment - res << link_to(t("layout.delete"), path, :method => "delete", - :confirm => t("layout.comments.confirm_delete")).html_safe + res << link_to(t('layout.delete'), path, :method => "delete", + :confirm => t('layout.comments.confirm_delete')).html_safe end res end def header - res = link_to "#{@user.uname} (#{@user.name})", user_path(@user.uname) - res += ' ' + t("layout.comments.has_commented") + user_link = link_to @user.fullname, user_path(@user.uname) + res = unless @is_reference_to_issue + "#{user_link} #{t 'layout.comments.has_commented'}" + else + t 'layout.comments.reference', :user => user_link + end + res.html_safe end def image @@ -52,7 +82,7 @@ class CommentPresenter < ApplicationPresenter end def date - @date ||= I18n.l(@comment.updated_at, :format => :long) + @date ||= @comment.created_at end def comment_id? @@ -73,4 +103,12 @@ class CommentPresenter < ApplicationPresenter "#{before}comment#{@comment.id}" end + def issue_referenced_state + if @referenced_issue.is_a? Issue + statuses = {'open' => 'success', 'closed' => 'important'} + content_tag :span, t("layout.issues.status.#{@referenced_issue.status}"), :class => "state label-bootstrap label-#{statuses[@referenced_issue.status]}" + else + pull_status_label @referenced_issue.status + end.html_safe + end end diff --git a/app/presenters/git_presenters/commit_as_message_presenter.rb b/app/presenters/git_presenters/commit_as_message_presenter.rb index 610fe68b2..8c0d02f49 100644 --- a/app/presenters/git_presenters/commit_as_message_presenter.rb +++ b/app/presenters/git_presenters/commit_as_message_presenter.rb @@ -1,33 +1,55 @@ class GitPresenters::CommitAsMessagePresenter < ApplicationPresenter include CommitHelper - attr_accessor :commit, :options - attr_reader :header, :image, :date, :caption, :content, :expandable + attr_accessor :commit + attr_reader :header, :image, :date, :caption, :content, :expandable, + :is_reference_to_issue, :committer def initialize(commit, opts = {}) - @commit = commit - @options = opts + comment = opts[:comment] + @is_reference_to_issue = !!comment # is it reference issue from commit + @project = if comment + Project.where(:id => comment.data[:from_project_id]).first + else + opts[:project] + end + commit = commit || @project.repo.commit(Comment.hex_to_commit_hash comment.created_from_commit_hash) if @project + + if @project && commit + @committer = User.where(:email => commit.committer.email).first || commit.committer + @commit_hash = commit.id + @committed_date, @authored_date = commit.committed_date, commit.authored_date + @commit_message = commit.message + else + @committer = t('layout.commits.unknown_committer') + @commit_hash = Comment.hex_to_commit_hash comment.created_from_commit_hash + @committed_date = @authored_date = comment.created_at + @commit_message = t('layout.commits.deleted') + end prepare_message end def header - @header ||= if options[:project].present? - I18n.t("layout.messages.commits.header", - :committer => committer_link, :commit => commit_link, :project => options[:project].name) - end.html_safe + @header ||= if @is_reference_to_issue + I18n.t('layout.commits.reference', :committer => committer_link, :commit => commit_link) + elsif @project.present? + I18n.t('layout.messages.commits.header', + :committer => committer_link, :commit => commit_link, :project => @project.name) + end.html_safe end def image - c = committer - @image ||= if c.class == User - helpers.avatar_url(c, :medium) + @image ||= if committer.is_a? User + helpers.avatar_url(committer, :medium) + elsif committer.is_a? Grit::Actor + helpers.avatar_url_by_email(committer.email, :medium) else - helpers.avatar_url_by_email(c.email, :medium) + helpers.gravatar_url('Unknown', User::AVATAR_SIZES[:medium]) end end def date - @date ||= I18n.l(@commit.committed_date || @commit.authored_date, :format => :long) + @date ||= @committed_date || @authored_date end def expandable? @@ -50,33 +72,43 @@ class GitPresenters::CommitAsMessagePresenter < ApplicationPresenter false end + def issue_referenced_state? + false + end + + def reference_project + @project if @is_reference_to_issue + end + protected - def committer - @committer ||= User.where(:email => @commit.committer.email).first || @commit.committer + def committer_link + @committer_link ||= if committer.is_a? User + link_to committer.uname, user_path(committer) + elsif committer.is_a? Grit::Actor + mail_to committer.email, committer.name + else # unknown committer + committer end + end - def committer_link - @committer_link ||= if committer.is_a? User - link_to committer.uname, user_path(committer) - else - mail_to committer.email, committer.name - end + def commit_link + if @project + link_to shortest_hash_id(@commit_hash), commit_path(@project, @commit_hash) + else + shortest_hash_id(@commit_hash) end + end - def commit_link - link_to shortest_hash_id(@commit.id), commit_path(options[:project], @commit.id) + def prepare_message + (@caption, @content) = @commit_message.split("\n\n", 2) + @caption = 'empty message' unless @caption.present? + if @caption.length > 72 + tmp = '...' + @caption[69..-1] + @content = (@content.present?) ? tmp + @content : tmp + @caption = @caption[0..68] + '...' end - - def prepare_message - (@caption, @content) = @commit.message.split("\n\n", 2) - @caption = 'empty message' unless @caption.present? - if @caption.length > 72 - tmp = '...' + @caption[69..-1] - @content = (@content.present?) ? tmp + @content : tmp - @caption = @caption[0..68] + '...' - end # @content = @content.gsub("\n", "
").html_safe if @content - @content = simple_format(@content, {}, :sanitize => true).html_safe if @content - end + @content = simple_format(@content, {}, :sanitize => true).html_safe if @content + end end diff --git a/app/views/admin/flash_notifies/_form.html.haml b/app/views/admin/flash_notifies/_form.html.haml index c609dd20b..e7de4d9de 100644 --- a/app/views/admin/flash_notifies/_form.html.haml +++ b/app/views/admin/flash_notifies/_form.html.haml @@ -15,7 +15,7 @@ .both .button_block - = submit_tag t("layout.save") + = submit_tag t('layout.save'), :data => {'disable-with' => t('layout.saving')} %span.text_button_padding= t("layout.or") = link_to t("layout.cancel"), admin_flash_notifies_path, :class => "button" diff --git a/app/views/admin/users/_users_ajax.json.jbuilder b/app/views/admin/users/_users_ajax.json.jbuilder index 052003e36..363e968da 100644 --- a/app/views/admin/users/_users_ajax.json.jbuilder +++ b/app/views/admin/users/_users_ajax.json.jbuilder @@ -1,6 +1,5 @@ users = @users.map do |user| link_block = [ - (link_to t('layout.show'), user if can?(:read, user) && !@system_list), (link_to t('layout.edit'), edit_admin_user_path(user) if can?(:edit, user) && !@system_list), (link_to t('layout.users.reset_token'), reset_auth_token_admin_user_path(user), :method => :put, :confirm => t('layout.users.confirm_reset_token') if can?(:edit, user) && @system_list), (link_to t('layout.delete'), admin_user_path(user), :method => :delete, :confirm => t('layout.users.confirm_delete') if can? :destroy, user) @@ -9,8 +8,9 @@ users = @users.map do |user| if !@system_list [ user.name, - user.uname, + (can?(:read, user) ? link_to(user.uname, user) : user.uname), user.email, + user.created_at.to_date, content_tag(:span, user.role, :style => user.access_locked? ? 'background: #FEDEDE' : ''), link_block ] diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index 2ccbc25d6..632b16c10 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -2,7 +2,7 @@ -if @filter == 'system' - columns = [{:type => 'html'}, {:type => nil, :sortable => false, :searchable => false, :class => 'buttons'}] -else - - columns = [{:type => 'html'}, {:type => 'html'}, {:type => 'html'}, {:type => 'html', :sortable => false, :searchable => false}, {:type => nil, :sortable => false, :searchable => false, :class => 'buttons'}] + - columns = [{:type => 'html'}, {:type => 'html'}, {:type => 'html'}, {:type => 'html'}, {:type => 'html', :sortable => false, :searchable => false}, {:type => nil, :sortable => false, :searchable => false, :class => 'buttons'}] = raw datatable(columns, {:sort_by => "[0, 'asc']", :processing => t("layout.processing"), :search_label => t("layout.search.header"), :pagination_labels => {:previous => t("datatables.previous_label"), :next => t("datatables.next_label")}, :empty_label => t("datatables.empty_label"), @@ -23,6 +23,7 @@ %th.th1= t("activerecord.attributes.user.name") %th.th2= t("activerecord.attributes.user.uname") %th.th3= t("activerecord.attributes.user.email") + %th.th1= t("activerecord.attributes.user.created_at") %th.th4= t("activerecord.attributes.user.role") %th.last   %tbody diff --git a/app/views/admin/users/new.html.haml b/app/views/admin/users/new.html.haml index faa8fca22..d49a54e6a 100644 --- a/app/views/admin/users/new.html.haml +++ b/app/views/admin/users/new.html.haml @@ -9,7 +9,7 @@ .rightlist= f.text_field :uname .leftlist \  - .rightlist= submit_tag t("layout.save"), :data => {:"disable-with" => t("layout.saving")} + .rightlist= submit_tag t('layout.save'), :data => {'disable-with' => t('layout.saving')} .both -else = render "users/base/form", :f => f diff --git a/app/views/advisories/_list.html.haml b/app/views/advisories/_list.html.haml index 5f3ea4f52..a5b24b6b0 100644 --- a/app/views/advisories/_list.html.haml +++ b/app/views/advisories/_list.html.haml @@ -8,8 +8,8 @@ -# TODO: change filter to Backbone.js %th{:colspan => 3, :rowspan => 1} = form_tag advisories_path, :method => :get do |f| - = text_field_tag('q', params[:q], :placeholder => t("layout.advisories.search_by_id"), :class => params[:q].present? ? 'black' : 'gray') - %input{:type => 'submit', :value => t("layout.search.header")} + = text_field_tag('q', params[:q], :placeholder => t('layout.advisories.search_by_id'), :class => params[:q].present? ? 'black' : 'gray', :style => 'width:600px;') + = submit_tag t('layout.search.header'), :data => {'disable-with' => t('layout.processing')} =# link_to t('layout.back'), advisories_path, :class => 'button' = button_to t('layout.clear'), {:action => :index} , :method => :get diff --git a/app/views/api/v1/advisories/_advisory.json.jbuilder b/app/views/api/v1/advisories/_advisory.json.jbuilder index 5205ddc29..882db3411 100644 --- a/app/views/api/v1/advisories/_advisory.json.jbuilder +++ b/app/views/api/v1/advisories/_advisory.json.jbuilder @@ -1,12 +1,12 @@ json.id advisory.advisory_id json.(advisory, :description) -json.platforms advisory.platforms do |json_platform, platform| - json_platform.(platform, :id, :released) - json_platform.url api_v1_platform_path(platform.id, :format => :json) +json.platforms advisory.platforms do |platform| + json.(platform, :id, :released) + json.url api_v1_platform_path(platform.id, :format => :json) end -json.projects advisory.projects do |json_project, project| - json_project.(project, :id, :name) - json_project.fullname project.name_with_owner - json_project.url api_v1_project_path(project.id, :format => :json) +json.projects advisory.projects do |project| + json.(project, :id, :name) + json.fullname project.name_with_owner + json.url api_v1_project_path(project.id, :format => :json) end json.url api_v1_advisory_path(advisory.advisory_id, :format => :json) \ No newline at end of file diff --git a/app/views/api/v1/advisories/index.json.jbuilder b/app/views/api/v1/advisories/index.json.jbuilder index c0248c828..d2af972c9 100644 --- a/app/views/api/v1/advisories/index.json.jbuilder +++ b/app/views/api/v1/advisories/index.json.jbuilder @@ -1,4 +1,4 @@ -json.advisories @advisories do |json, advisory| - json.partial! 'advisory', :advisory => advisory, :json => json +json.advisories @advisories do |advisory| + json.partial! 'advisory', :advisory => advisory end json.url api_v1_advisories_path(:format => :json) \ No newline at end of file diff --git a/app/views/api/v1/advisories/show.json.jbuilder b/app/views/api/v1/advisories/show.json.jbuilder index 740d12afe..c1ec4dfa5 100644 --- a/app/views/api/v1/advisories/show.json.jbuilder +++ b/app/views/api/v1/advisories/show.json.jbuilder @@ -1,26 +1,24 @@ -json.advisory do |json| - json.partial! 'advisory', :advisory => @advisory, :json => json +json.advisory do + json.partial! 'advisory', :advisory => @advisory json.created_at @advisory.created_at.to_i json.updated_at @advisory.updated_at.to_i json.(@advisory, :update_type) json.references @advisory.references.split('\n') - json.build_lists @advisory.build_lists do |json_build_list, build_list| - json_build_list.(build_list, :id) - json_build_list.url api_v1_build_list_path(build_list.id, :format => :json) + json.build_lists @advisory.build_lists do |build_list| + json.(build_list, :id) + json.url api_v1_build_list_path(build_list.id, :format => :json) end - json.affected_in @packages_info do |json_platform, package_info| - json.partial! 'api/v1/platforms/platform', - :platform => package_info[0], :json => json_platform + json.affected_in @packages_info do |package_info| + json.partial! 'api/v1/platforms/platform', :platform => package_info[0] - json_platform.projects package_info[1] do |json_project, info| - json.partial! 'api/v1/projects/project', - :project => info[0], :json => json_project + json.projects package_info[1] do |info| + json.partial! 'api/v1/projects/project', :project => info[0] packages = info[1] - json_project.srpm packages[:srpm] - json_project.rpm packages[:rpm] + json.srpm packages[:srpm] + json.rpm packages[:rpm] end end diff --git a/app/views/api/v1/arches/index.json.jbuilder b/app/views/api/v1/arches/index.json.jbuilder index b51119646..a36416566 100644 --- a/app/views/api/v1/arches/index.json.jbuilder +++ b/app/views/api/v1/arches/index.json.jbuilder @@ -1,3 +1,3 @@ -json.architectures @arches do |json, arch| +json.architectures @arches do |arch| json.(arch, :id, :name) end \ No newline at end of file diff --git a/app/views/api/v1/build_lists/_repositories.json.jbuilder b/app/views/api/v1/build_lists/_repositories.json.jbuilder index 037b19276..e776bd258 100644 --- a/app/views/api/v1/build_lists/_repositories.json.jbuilder +++ b/app/views/api/v1/build_lists/_repositories.json.jbuilder @@ -1,9 +1,5 @@ -json.partial! 'api/v1/repositories/repository', - :repository => repository, - :json => json +json.partial! 'api/v1/repositories/repository', :repository => repository -json.platform do |json_str_platform| - json.partial! 'api/v1/platforms/platform', - :platform => repository.platform, - :json => json +json.platform do + json.partial! 'api/v1/platforms/platform', :platform => repository.platform end \ No newline at end of file diff --git a/app/views/api/v1/build_lists/index.json.jbuilder b/app/views/api/v1/build_lists/index.json.jbuilder index fb29bc472..ae5ddeb41 100644 --- a/app/views/api/v1/build_lists/index.json.jbuilder +++ b/app/views/api/v1/build_lists/index.json.jbuilder @@ -1,4 +1,4 @@ -json.build_lists @build_lists do |json, build_list| +json.build_lists @build_lists do |build_list| json.(build_list, :id, :status) json.url api_v1_build_list_path(build_list, :format => :json) end diff --git a/app/views/api/v1/build_lists/show.json.jbuilder b/app/views/api/v1/build_lists/show.json.jbuilder index a108c7f76..41eef4778 100644 --- a/app/views/api/v1/build_lists/show.json.jbuilder +++ b/app/views/api/v1/build_lists/show.json.jbuilder @@ -1,92 +1,85 @@ -json.build_list do |json| +json.build_list do json.(@build_list, :id, :container_status, :status, :duration) json.(@build_list, :update_type, :priority, :new_core) - json.(@build_list, :advisory, :mass_build, :use_save_to_repository) + json.(@build_list, :advisory, :mass_build) json.(@build_list, :auto_publish, :package_version, :commit_hash, :last_published_commit_hash, :auto_create_container) json.build_log_url log_build_list_path(@build_list) if @build_list.container_published? - json.container_path container_url(false) + json.container_path container_url else json.container_path '' end - json.arch do |json_arch| - json_arch.(@build_list.arch, :id, :name) + json.arch do + json.(@build_list.arch, :id, :name) end json.created_at @build_list.created_at.to_i json.updated_at @build_list.updated_at.to_i - json.project do |json_project| - json.partial! 'api/v1/projects/project', - :project => @build_list.project, :json => json_project + json.project do + json.partial! 'api/v1/projects/project', :project => @build_list.project end - json.save_to_repository do |json_save_to_repository| + json.save_to_repository do json.partial! 'api/v1/repositories/repository', - :repository => @build_list.save_to_repository, - :json => json_save_to_repository + :repository => @build_list.save_to_repository - json_save_to_repository.platform do |json_str_platform| + json.platform do json.partial! 'api/v1/platforms/platform', - :platform => @build_list.save_to_repository.platform, - :json => json_str_platform + :platform => @build_list.save_to_repository.platform end end - json.build_for_platform do |json_build_for_platform| + json.build_for_platform do json.partial! 'api/v1/platforms/platform', - :platform => @build_list.build_for_platform, - :json => json_build_for_platform + :platform => @build_list.build_for_platform end - json.user do |json_user| - json.partial! 'api/v1/shared/member', :member => @build_list.user, :tag => json_user + json.user do + json.partial! 'api/v1/shared/member', :member => @build_list.user end - json.publisher do |json_publisher| - json.partial! 'api/v1/shared/member', :member => @build_list.publisher, :tag => json_publisher + json.publisher do + json.partial! 'api/v1/shared/member', :member => @build_list.publisher end if @build_list.publisher inc_repos = Repository.includes(:platform).where(:id => @build_list.include_repos) - json.include_repos inc_repos do |json_include_repos, repo| - json.partial! 'repositories', - :repository => repo, - :json => json_include_repos + json.include_repos inc_repos do |repo| + json.partial! 'repositories', :repository => repo end extra_repos = Repository.includes(:platform).where(:id => @build_list.extra_repositories) - json.extra_repos extra_repos do |json_extra_repos, repo| - json.partial! 'repositories', - :repository => repo, - :json => json_extra_repos + json.extra_repositories extra_repos do |repo| + json.partial! 'repositories', :repository => repo end extra_build_lists = BuildList.where(:id => @build_list.extra_build_lists) - json.extra_build_lists extra_build_lists do |json_extra_build_lists, bl| - json_extra_build_lists.(bl, :id, :status) - json_extra_build_lists.container_path container_url(false, bl) - json_extra_build_lists.url api_v1_build_list_path(bl, :format => :json) + json.extra_build_lists extra_build_lists do |bl| + json.(bl, :id, :status) + json.container_path container_url(bl) + json.url api_v1_build_list_path(bl, :format => :json) end + json.extra_params @build_list.extra_params - json.advisory do |json_advisory| - json_advisory.name @build_list.advisory.advisory_id - json_advisory.(@build_list.advisory, :description) + json.advisory do + json.name @build_list.advisory.advisory_id + json.(@build_list.advisory, :description) end if @build_list.advisory - json.mass_build do |json_mass_build| - json_mass_build.(@build_list.mass_build, :id, :name) + json.mass_build do + json.(@build_list.mass_build, :id, :name) end if @build_list.mass_build - json.logs (@build_list.results || []) do |json_logs, result| - json_logs.file_name result['file_name'] - json_logs.size result['size'] - json_logs.url "#{APP_CONFIG['file_store_url']}/api/v1/file_stores/#{result['sha1']}" + json.logs (@build_list.results || []) do |result| + json.file_name result['file_name'] + json.size result['size'] + json.url "#{APP_CONFIG['file_store_url']}/api/v1/file_stores/#{result['sha1']}" end if @build_list.new_core? - json.packages @build_list.packages do |json_packages, package| - json_packages.partial! 'api/v1/maintainers/package', :package => package, :json => json_packages + json.packages @build_list.packages do |package| + json.partial! 'api/v1/maintainers/package', :package => package end json.url api_v1_build_list_path(@build_list, :format => :json) diff --git a/app/views/api/v1/groups/index.json.jbuilder b/app/views/api/v1/groups/index.json.jbuilder index fec845836..ed313a4e4 100644 --- a/app/views/api/v1/groups/index.json.jbuilder +++ b/app/views/api/v1/groups/index.json.jbuilder @@ -1,4 +1,4 @@ -json.groups @groups do |json, group| +json.groups @groups do |group| json.(group, :id, :uname, :own_projects_count, :description) json.created_at group.created_at.to_i json.updated_at group.updated_at.to_i diff --git a/app/views/api/v1/groups/members.json.jbuilder b/app/views/api/v1/groups/members.json.jbuilder index eb4ec3391..48e0ba8c4 100644 --- a/app/views/api/v1/groups/members.json.jbuilder +++ b/app/views/api/v1/groups/members.json.jbuilder @@ -1,4 +1,4 @@ -json.group do |json| +json.group do json.(@group, :id) json.partial! 'api/v1/shared/members' end diff --git a/app/views/api/v1/groups/show.json.jbuilder b/app/views/api/v1/groups/show.json.jbuilder index ebe02b9ec..ff4a982e2 100644 --- a/app/views/api/v1/groups/show.json.jbuilder +++ b/app/views/api/v1/groups/show.json.jbuilder @@ -1,4 +1,4 @@ -json.group do |json| +json.group do json.(@group, :id, :uname, :own_projects_count, :description) json.created_at @group.created_at.to_i json.updated_at @group.updated_at.to_i diff --git a/app/views/api/v1/issues/_issue.json.jbuilder b/app/views/api/v1/issues/_issue.json.jbuilder new file mode 100644 index 000000000..2c128fb11 --- /dev/null +++ b/app/views/api/v1/issues/_issue.json.jbuilder @@ -0,0 +1,11 @@ +json.number issue.serial_id +json.(issue, :title, :status) +json.labels issue.labels do |label| + json.partial! 'label', :label => label +end +json.assignee do + json.partial! 'api/v1/shared/member', :member => issue.assignee +end if issue.assignee + +json.url api_v1_project_issue_path(issue.project.id, issue.serial_id, :format => :json) + diff --git a/app/views/api/v1/issues/_label.json.jbuilder b/app/views/api/v1/issues/_label.json.jbuilder new file mode 100644 index 000000000..fdee26e0d --- /dev/null +++ b/app/views/api/v1/issues/_label.json.jbuilder @@ -0,0 +1 @@ +json.(label, :name, :color) diff --git a/app/views/api/v1/issues/index.json.jbuilder b/app/views/api/v1/issues/index.json.jbuilder new file mode 100644 index 000000000..9b43cc7be --- /dev/null +++ b/app/views/api/v1/issues/index.json.jbuilder @@ -0,0 +1,11 @@ +json.issues @issues do |issue| + json.partial! 'issue', :issue => issue + json.issue issue.body + json.partial! 'api/v1/shared/owner', :owner => issue.user + json.closed_at issue.closed_at.to_i + json.closed_by do + json.partial! 'api/v1/shared/member', :member => issue.closer + end if issue.closer + json.created_at issue.created_at.to_i + json.updated_at issue.updated_at.to_i +end diff --git a/app/views/api/v1/issues/show.json.jbuilder b/app/views/api/v1/issues/show.json.jbuilder new file mode 100644 index 000000000..f9f7e8648 --- /dev/null +++ b/app/views/api/v1/issues/show.json.jbuilder @@ -0,0 +1,11 @@ +json.issue do + json.partial! 'issue', :issue => @issue + json.issue @issue.body + json.partial! 'api/v1/shared/owner', :owner => @issue.user + json.closed_at @issue.closed_at.to_i + json.closed_by do + json.partial! 'api/v1/shared/member', :member => @issue.closer + end if @issue.closer + json.created_at @issue.created_at.to_i + json.updated_at @issue.updated_at.to_i +end diff --git a/app/views/api/v1/maintainers/_package.json.jbuilder b/app/views/api/v1/maintainers/_package.json.jbuilder index c4f92edc3..9940da2ce 100644 --- a/app/views/api/v1/maintainers/_package.json.jbuilder +++ b/app/views/api/v1/maintainers/_package.json.jbuilder @@ -1,4 +1,4 @@ -json.(package, :id, :name, :version, :release) +json.(package, :id, :name, :version, :release, :epoch) json.type package.package_type json.updated_at package.updated_at.to_i json.url (package.sha1 ? "#{APP_CONFIG['file_store_url']}/api/v1/file_stores/#{package.sha1}" : '' ) \ No newline at end of file diff --git a/app/views/api/v1/maintainers/index.json.jbuilder b/app/views/api/v1/maintainers/index.json.jbuilder index 3113b84af..e771a2d7a 100644 --- a/app/views/api/v1/maintainers/index.json.jbuilder +++ b/app/views/api/v1/maintainers/index.json.jbuilder @@ -1,15 +1,15 @@ -json.maintainers @maintainers do |json, maintainer| - json.project do |json_project| - json_project.partial! 'api/v1/projects/project', :project => maintainer.project, :json => json +json.maintainers @maintainers do |maintainer| + json.project do + json.partial! 'api/v1/projects/project', :project => maintainer.project end - json.package do |json_package| - json_package.partial! 'package', :package => maintainer, :json => json + json.package do + json.partial! 'package', :package => maintainer end - json.maintainer do |json_maintainer| + json.maintainer do if user = maintainer.try(:assignee) - json_maintainer.partial! 'maintainer', :maintainer => user, :json => json + json.partial! 'maintainer', :maintainer => user end end end diff --git a/app/views/api/v1/platforms/index.json.jbuilder b/app/views/api/v1/platforms/index.json.jbuilder index 7e5e5d71d..71fdd4976 100644 --- a/app/views/api/v1/platforms/index.json.jbuilder +++ b/app/views/api/v1/platforms/index.json.jbuilder @@ -1,10 +1,10 @@ -json.platforms @platforms do |json, platform| - json.partial! 'platform', :platform => platform, :json => json +json.platforms @platforms do |platform| + json.partial! 'platform', :platform => platform json.(platform, :platform_type, :visibility) json.partial! 'api/v1/shared/owner', :owner => platform.owner - json.repositories platform.repositories do |json_repos, repo| - json_repos.(repo, :id, :name) - json_repos.url api_v1_repository_path(repo.id, :format => :json) + json.repositories platform.repositories do |repo| + json.(repo, :id, :name) + json.url api_v1_repository_path(repo.id, :format => :json) end end diff --git a/app/views/api/v1/platforms/members.json.jbuilder b/app/views/api/v1/platforms/members.json.jbuilder index 7cfc6e9a1..ec4eca437 100644 --- a/app/views/api/v1/platforms/members.json.jbuilder +++ b/app/views/api/v1/platforms/members.json.jbuilder @@ -1,5 +1,5 @@ -json.platform do |json| - json.partial! 'platform', :platform => @platform, :json => json +json.platform do + json.partial! 'platform', :platform => @platform json.partial! 'api/v1/shared/members' end json.url members_api_v1_platform_path(@platform.id, :format => :json) \ No newline at end of file diff --git a/app/views/api/v1/platforms/show.json.jbuilder b/app/views/api/v1/platforms/show.json.jbuilder index a9fd620bc..b7dbb49a7 100644 --- a/app/views/api/v1/platforms/show.json.jbuilder +++ b/app/views/api/v1/platforms/show.json.jbuilder @@ -1,14 +1,14 @@ -json.platform do |json| - json.partial! 'platform', :platform => @platform, :json => json +json.platform do + json.partial! 'platform', :platform => @platform json.(@platform, :description, :parent_platform_id, :released, :visibility, :platform_type, :distrib_type) json.created_at @platform.created_at.to_i json.updated_at @platform.updated_at.to_i json.partial! 'api/v1/shared/owner', :owner => @platform.owner - json.repositories @platform.repositories do |json_repos, repo| - json_repos.(repo, :id, :name) - json_repos.url api_v1_repository_path(repo.id, :format => :json) + json.repositories @platform.repositories do |repo| + json.(repo, :id, :name) + json.url api_v1_repository_path(repo.id, :format => :json) end - json.products @platform.products do |json_products, product| - json.partial! 'api/v1/products/product', :product => product, :json => json_products + json.products @platform.products do |product| + json.partial! 'api/v1/products/product', :product => product end end diff --git a/app/views/api/v1/products/show.json.jbuilder b/app/views/api/v1/products/show.json.jbuilder index 1f8f3d91b..f74a1a0a8 100644 --- a/app/views/api/v1/products/show.json.jbuilder +++ b/app/views/api/v1/products/show.json.jbuilder @@ -1,11 +1,11 @@ -json.product do |json| - json.partial! 'product', :product => @product, :json => json - json.platform do |json_platform| - json.partial! 'api/v1/platforms/platform', :platform => @product.platform, :json => json_platform +json.product do + json.partial! 'product', :product => @product + json.platform do + json.partial! 'api/v1/platforms/platform', :platform => @product.platform end if @product.project.present? - json.project do |json_project| - json.partial! 'api/v1/projects/project', :project => @product.project, :json => json_project + json.project do + json.partial! 'api/v1/projects/project', :project => @product.project end end json.created_at @product.created_at.to_i diff --git a/app/views/api/v1/projects/get_id.json.jbuilder b/app/views/api/v1/projects/get_id.json.jbuilder index bc4ab3598..6dd156f60 100644 --- a/app/views/api/v1/projects/get_id.json.jbuilder +++ b/app/views/api/v1/projects/get_id.json.jbuilder @@ -1,5 +1,5 @@ -json.project do |json| - json.partial! 'project', :project => @project, :json => json +json.project do + json.partial! 'project', :project => @project json.(@project, :visibility) json.partial! 'api/v1/shared/owner', :owner => @project.owner end diff --git a/app/views/api/v1/projects/index.json.jbuilder b/app/views/api/v1/projects/index.json.jbuilder index 82c44c2ff..b6d4296c8 100644 --- a/app/views/api/v1/projects/index.json.jbuilder +++ b/app/views/api/v1/projects/index.json.jbuilder @@ -1,6 +1,6 @@ -json.projects @projects do |json, project| - json.partial! 'project', :project => project, :json => json - json.(project, :visibility, :description, :ancestry, :has_issues, :has_wiki, :default_branch, :is_package, :average_build_time, :publish_i686_into_x86_64) +json.projects @projects do |project| + json.partial! 'project', :project => project + json.(project, :visibility, :description, :ancestry, :has_issues, :has_wiki, :default_branch, :is_package, :publish_i686_into_x86_64) json.created_at project.created_at.to_i json.updated_at project.updated_at.to_i json.partial! 'api/v1/shared/owner', :owner => project.owner diff --git a/app/views/api/v1/projects/members.json.jbuilder b/app/views/api/v1/projects/members.json.jbuilder index 258b81ca7..d237705f9 100644 --- a/app/views/api/v1/projects/members.json.jbuilder +++ b/app/views/api/v1/projects/members.json.jbuilder @@ -1,5 +1,5 @@ -json.project do |json| - json.partial! 'project', :project => @project, :json => json +json.project do + json.partial! 'project', :project => @project json.partial! 'api/v1/shared/members' end json.url members_api_v1_project_path(@project.id, :format => :json) \ No newline at end of file diff --git a/app/views/api/v1/projects/refs_list.json.jbuilder b/app/views/api/v1/projects/refs_list.json.jbuilder index 7b913f98b..b41dacba7 100644 --- a/app/views/api/v1/projects/refs_list.json.jbuilder +++ b/app/views/api/v1/projects/refs_list.json.jbuilder @@ -1,8 +1,8 @@ -json.refs_list (@project.repo.branches + @project.repo.tags) do |json_grit, grit| - json_grit.ref grit.name - json_grit.object do |json_object| - json_object.type (grit.class.name =~ /Tag/ ? 'tag' : 'commit') - json_object.sha grit.commit.id +json.refs_list @refs do |grit| + json.ref grit.name + json.object do + json.type (grit.class.name =~ /Tag/ ? 'tag' : 'commit') + json.sha grit.commit.id end end json.url refs_list_api_v1_project_path(@project.id, :format => :json) \ No newline at end of file diff --git a/app/views/api/v1/projects/show.json.jbuilder b/app/views/api/v1/projects/show.json.jbuilder index ff64048bb..b820cf415 100644 --- a/app/views/api/v1/projects/show.json.jbuilder +++ b/app/views/api/v1/projects/show.json.jbuilder @@ -1,18 +1,23 @@ -json.project do |json| - json.partial! 'project', :project => @project, :json => json - json.(@project, :visibility, :description, :ancestry, :has_issues, :has_wiki, :default_branch, :is_package, :average_build_time, :publish_i686_into_x86_64) +json.project do + json.partial! 'project', :project => @project + json.(@project, :visibility, :description, :ancestry, :has_issues, :has_wiki, :default_branch, :is_package, :publish_i686_into_x86_64) json.created_at @project.created_at.to_i json.updated_at @project.updated_at.to_i json.partial! 'api/v1/shared/owner', :owner => @project.owner - json.maintainer do |json_maintainer| - json.partial! 'api/v1/shared/member', :member => @project.maintainer, :tag => json_maintainer + json.maintainer do + json.partial! 'api/v1/shared/member', :member => @project.maintainer end - json.repositories @project.repositories do |json_repos, repo| - json_repos.(repo, :id, :name) - json_repos.url api_v1_repository_path(repo.name, :format => :json) - json_repos.platform do |json_platform| - json_platform.(repo.platform, :id, :name) - json_platform.url api_v1_platform_path(repo.platform, :format => :json) + + json.project_statistics @project.project_statistics do |statistic| + json.(statistic, :average_build_time, :build_count, :arch_id) + end + + json.repositories @project.repositories do |repo| + json.(repo, :id, :name) + json.url api_v1_repository_path(repo.name, :format => :json) + json.platform do + json.(repo.platform, :id, :name) + json.url api_v1_platform_path(repo.platform, :format => :json) end end end \ No newline at end of file diff --git a/app/views/api/v1/pull_requests/_pull.json.jbuilder b/app/views/api/v1/pull_requests/_pull.json.jbuilder new file mode 100644 index 000000000..fbe7fb555 --- /dev/null +++ b/app/views/api/v1/pull_requests/_pull.json.jbuilder @@ -0,0 +1,24 @@ +json.number pull.serial_id +json.(pull, :title, :status) +json.to_ref do + json.ref pull.to_ref + json.sha pull.to_commit.try(:id) + json.project do + json.partial! 'api/v1/projects/project', :project => pull.to_project + end +end +json.from_ref do + json.ref pull.from_ref + json.sha pull.from_commit.try(:id) + json.project do + json.partial! 'api/v1/projects/project', :project => pull.from_project + end +end +json.partial! 'api/v1/shared/owner', :owner => pull.user +json.assignee do + json.partial! 'api/v1/shared/member', :member => pull.issue.assignee +end if pull.issue.assignee +json.mergeable pull.can_merging? +json.merged_at pull.issue.closed_at.to_i if pull.merged? + +json.url api_v1_project_pull_request_path(pull.to_project.id, pull.id, :format => :json) diff --git a/app/views/api/v1/pull_requests/commits.json.jbuilder b/app/views/api/v1/pull_requests/commits.json.jbuilder new file mode 100644 index 000000000..6ce77453d --- /dev/null +++ b/app/views/api/v1/pull_requests/commits.json.jbuilder @@ -0,0 +1,25 @@ +json.commits @commits do |commit| + json.sha commit.id + json.https_url commit_path(@project, commit.id) + json.author do + json.name commit.author.name + json.email commit.author.email + json.date commit.authored_date.to_i + end + json.committer do + json.name commit.committer.name + json.email commit.committer.email + json.date commit.committed_date.to_i + end + json.message commit.message + json.tree do + json.sha commit.id + json.https_url commit_path(@project, commit.id) + end + json.parents commit.parents do |parent| + json.sha parent.id + json.https_url commit_path(@project, parent.id) + end +end + +json.url commits_api_v1_project_pull_request_path(:format => :json) diff --git a/app/views/api/v1/pull_requests/files.json.jbuilder b/app/views/api/v1/pull_requests/files.json.jbuilder new file mode 100644 index 000000000..cc7fcbeee --- /dev/null +++ b/app/views/api/v1/pull_requests/files.json.jbuilder @@ -0,0 +1,24 @@ +json.files @stats do |stat| + fstat, diff = stat + commit_id = diff.deleted_file ? @pull.to_commit.id : @pull.from_commit.id + json.sha commit_id + json.filename diff.b_path + status = case + when diff.new_file + 'added' + when diff.deleted_file + 'deleted' + when diff.renamed_file + 'renamed' + else + 'modified' + end + json.status status + json.additions fstat.additions + json.deletions fstat.deletions + json.changes fstat.additions + fstat.deletions + json.blob_https_url blob_path(@project, commit_id, diff.b_path) + json.raw_https_url raw_path(@project, commit_id, diff.b_path) +end + +json.url files_api_v1_project_pull_request_path(:format => :json) diff --git a/app/views/api/v1/pull_requests/index.json.jbuilder b/app/views/api/v1/pull_requests/index.json.jbuilder new file mode 100644 index 000000000..df3fb3381 --- /dev/null +++ b/app/views/api/v1/pull_requests/index.json.jbuilder @@ -0,0 +1,5 @@ +json.pull_requests @pulls do |pull| + json.partial! 'pull', :pull => pull +end + +json.url @pulls_url diff --git a/app/views/api/v1/pull_requests/show.json.jbuilder b/app/views/api/v1/pull_requests/show.json.jbuilder new file mode 100644 index 000000000..c388af95e --- /dev/null +++ b/app/views/api/v1/pull_requests/show.json.jbuilder @@ -0,0 +1,17 @@ +json.pull_request do + json.partial! 'pull', :pull => @pull + json.body @pull.body + json.closed_at @pull.issue.closed_at.to_i if @pull.merged? || @pull.closed? + + if @pull.issue.closer + json.closed_by do + json.(@pull.issue.closer, :id, :name, :uname) + end + json.merged_by do + json.(@pull.issue.closer, :id, :name, :uname) + end if @pull.merged? + end + + json.created_at @pull.issue.created_at.to_i + json.updated_at @pull.issue.updated_at.to_i +end diff --git a/app/views/api/v1/repositories/key_pair.json.jbuilder b/app/views/api/v1/repositories/key_pair.json.jbuilder index a096c4f12..ea474b812 100644 --- a/app/views/api/v1/repositories/key_pair.json.jbuilder +++ b/app/views/api/v1/repositories/key_pair.json.jbuilder @@ -1,11 +1,11 @@ -json.repository do |json| - json.partial! 'repository', :repository => @repository, :json => json - json.key_pair do |json_key_pair| +json.repository do + json.partial! 'repository', :repository => @repository + json.key_pair do if @repository.key_pair - json_key_pair.(@repository.key_pair, :public, :secret) + json.(@repository.key_pair, :public, :secret) else - json_key_pair.public '' - json_key_pair.secret '' + json.public '' + json.secret '' end end end \ No newline at end of file diff --git a/app/views/api/v1/repositories/projects.json.jbuilder b/app/views/api/v1/repositories/projects.json.jbuilder index f911d6645..cafc750f3 100644 --- a/app/views/api/v1/repositories/projects.json.jbuilder +++ b/app/views/api/v1/repositories/projects.json.jbuilder @@ -1,8 +1,7 @@ -json.repository do |json| - json.partial! 'repository', :repository => @repository, :json => json - json.projects @projects do |json_project, project| - json.partial! 'api/v1/projects/project', - :project => project, :json => json_project +json.repository do + json.partial! 'repository', :repository => @repository + json.projects @projects do |project| + json.partial! 'api/v1/projects/project', :project => project end end json.url projects_api_v1_repository_path(@repository.id, :format => :json) \ No newline at end of file diff --git a/app/views/api/v1/repositories/show.json.jbuilder b/app/views/api/v1/repositories/show.json.jbuilder index fc6d54f77..7dd9ec1f3 100644 --- a/app/views/api/v1/repositories/show.json.jbuilder +++ b/app/views/api/v1/repositories/show.json.jbuilder @@ -1,10 +1,10 @@ -json.repository do |json| - json.partial! 'repository', :repository => @repository, :json => json +json.repository do + json.partial! 'repository', :repository => @repository json.(@repository, :description, :publish_without_qa) json.created_at @repository.created_at.to_i json.updated_at @repository.updated_at.to_i - json.platform do |json_platform| - json_platform.(@repository.platform, :id, :name) - json_platform.url api_v1_platform_path(@repository.platform, :format => :json) + json.platform do + json.(@repository.platform, :id, :name) + json.url api_v1_platform_path(@repository.platform, :format => :json) end end \ No newline at end of file diff --git a/app/views/api/v1/search/_groups.json.jbuilder b/app/views/api/v1/search/_groups.json.jbuilder index 56026b009..a0e8f8ff5 100644 --- a/app/views/api/v1/search/_groups.json.jbuilder +++ b/app/views/api/v1/search/_groups.json.jbuilder @@ -1,3 +1,3 @@ json.groups results do |group| - json.partial! 'member', :member => group, :json => json + json.partial! 'member', :member => group end \ No newline at end of file diff --git a/app/views/api/v1/search/_platforms.json.jbuilder b/app/views/api/v1/search/_platforms.json.jbuilder index 4aa9aff17..5d708234e 100644 --- a/app/views/api/v1/search/_platforms.json.jbuilder +++ b/app/views/api/v1/search/_platforms.json.jbuilder @@ -1,3 +1,3 @@ json.platforms results do |platform| - json.partial! 'api/v1/platforms/platform', :platform => platform, :json => json + json.partial! 'api/v1/platforms/platform', :platform => platform end \ No newline at end of file diff --git a/app/views/api/v1/search/_projects.json.jbuilder b/app/views/api/v1/search/_projects.json.jbuilder index 79cfaa435..37935c367 100644 --- a/app/views/api/v1/search/_projects.json.jbuilder +++ b/app/views/api/v1/search/_projects.json.jbuilder @@ -1,3 +1,3 @@ json.projects results do |project| - json.partial! 'api/v1/projects/project', :project => project, :json => json + json.partial! 'api/v1/projects/project', :project => project end \ No newline at end of file diff --git a/app/views/api/v1/search/_users.json.jbuilder b/app/views/api/v1/search/_users.json.jbuilder index 0d23c054d..714a1e4ed 100644 --- a/app/views/api/v1/search/_users.json.jbuilder +++ b/app/views/api/v1/search/_users.json.jbuilder @@ -1,3 +1,3 @@ json.users results do |user| - json.partial! 'member', :member => user, :json => json + json.partial! 'member', :member => user end \ No newline at end of file diff --git a/app/views/api/v1/search/index.json.jbuilder b/app/views/api/v1/search/index.json.jbuilder index 4c861bba7..f6afacba6 100644 --- a/app/views/api/v1/search/index.json.jbuilder +++ b/app/views/api/v1/search/index.json.jbuilder @@ -1,6 +1,6 @@ -json.results do |json| +json.results do @results.each do |tag, results| - json.partial! tag.dup, :results => results, :json => json + json.partial! tag.dup, :results => results end end json.url api_v1_search_index_path(:format => :json) \ No newline at end of file diff --git a/app/views/api/v1/shared/_member.json.jbuilder b/app/views/api/v1/shared/_member.json.jbuilder index 25d2d1cf5..69d85ec5a 100644 --- a/app/views/api/v1/shared/_member.json.jbuilder +++ b/app/views/api/v1/shared/_member.json.jbuilder @@ -1,3 +1,3 @@ -tag.(member, :id, :name) -tag.type member.class.name -tag.url member_path(member) \ No newline at end of file +json.(member, :id, :name, :uname) +json.type member.class.name +json.url member_path(member) diff --git a/app/views/api/v1/shared/_members.json.jbuilder b/app/views/api/v1/shared/_members.json.jbuilder index 00c8b9a93..a930a9db1 100644 --- a/app/views/api/v1/shared/_members.json.jbuilder +++ b/app/views/api/v1/shared/_members.json.jbuilder @@ -1,3 +1,3 @@ -json.members @members do |json_members, member| - json.partial! 'api/v1/shared/member', :member => member, :tag => json_members +json.members @members do |member| + json.partial! 'api/v1/shared/member', :member => member end \ No newline at end of file diff --git a/app/views/api/v1/shared/_owner.json.jbuilder b/app/views/api/v1/shared/_owner.json.jbuilder index e6983d550..9b34683b5 100644 --- a/app/views/api/v1/shared/_owner.json.jbuilder +++ b/app/views/api/v1/shared/_owner.json.jbuilder @@ -1,3 +1,3 @@ -json.owner do |json_owner| - json.partial! 'api/v1/shared/member', :member => owner, :tag => json_owner +json.owner do + json.partial! 'api/v1/shared/member', :member => owner end \ No newline at end of file diff --git a/app/views/api/v1/users/notifiers.json.jbuilder b/app/views/api/v1/users/notifiers.json.jbuilder index 273ebbacc..a1968e864 100644 --- a/app/views/api/v1/users/notifiers.json.jbuilder +++ b/app/views/api/v1/users/notifiers.json.jbuilder @@ -1,7 +1,8 @@ -json.user do |json| +json.user do json.(@user, :id) - json.notifiers do |json_notifiers| - json_notifiers.(@user.notifier, :can_notify, :new_comment, :new_comment_reply, :new_issue, :issue_assign, :new_comment_commit_owner, :new_comment_commit_repo_owner, :new_comment_commit_commentor, :new_build, :new_associated_build) + json.notifiers do + json.(@user.notifier, :can_notify, :new_comment, :new_comment_reply, :new_issue, :issue_assign, :new_comment_commit_owner, :new_comment_commit_repo_owner, :new_comment_commit_commentor, :new_build, :new_associated_build, + :update_code) end end diff --git a/app/views/api/v1/users/show.json.jbuilder b/app/views/api/v1/users/show.json.jbuilder index 15e4ed892..086afbb6b 100644 --- a/app/views/api/v1/users/show.json.jbuilder +++ b/app/views/api/v1/users/show.json.jbuilder @@ -1,4 +1,4 @@ -json.user do |json| +json.user do json.(@user, :id, :name, :email, :uname,:language, :own_projects_count, :professional_experience, :site, :company, :location, :build_priority) json.created_at @user.created_at.to_i json.updated_at @user.updated_at.to_i diff --git a/app/views/contacts/new.html.haml b/app/views/contacts/new.html.haml index adcac6264..4a62c5780 100644 --- a/app/views/contacts/new.html.haml +++ b/app/views/contacts/new.html.haml @@ -34,7 +34,7 @@ :class => @form.errors.messages[:message].present? ? 'error' : '' .form-actions - = f.submit t("layout.contact.send_request"), :class => 'button' + = f.submit t('layout.contact.send_request'), :class => 'button', :data => {'disable-with' => t('layout.processing')} %section.last %div diff --git a/app/views/devise/registrations/edit.html.haml b/app/views/devise/registrations/edit.html.haml deleted file mode 100644 index ce7f42306..000000000 --- a/app/views/devise/registrations/edit.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -%h3.fix.bpadding10= @user.uname - -= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put, :class => "form" }) do |f| - = render "users/form", :f => f - -.notify - %p= t('layout.users.public_data_edit_warning') - -:javascript - $('article .right').addClass('middlepadding'); - -- content_for :sidebar, render('users/sidebar') diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml index 5fb9371d6..4dd7b02d0 100644 --- a/app/views/devise/registrations/new.html.haml +++ b/app/views/devise/registrations/new.html.haml @@ -7,6 +7,7 @@ %article = form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :class => "form" }) do |f| = hidden_field_tag :invitation_token, @invitation_token + = f.password_field :password, :name => 'password', :style => 'display: none;' .left.first=t('activerecord.attributes.user.uname') .right.first = f.text_field :uname, :id => 'login', :class => "registartion-input #{uname_error ? 'registartion-input-error' : ''}" @@ -17,7 +18,11 @@ .both .left=t('activerecord.attributes.user.email') .right - = f.text_field :email, :id => 'email', :readonly => 'readonly', :class => "registartion-input #{email_error ? 'registartion-input-error' : ''}" + - klass = "registartion-input #{email_error ? 'registartion-input-error' : ''}" + - if APP_CONFIG['preregistration'] + = f.text_field :email, :id => 'email', :readonly => 'readonly', :class => klass + - else + = f.text_field :email, :id => 'email', :class => 'registartion-input', :class => klass .both .left=t('activerecord.attributes.user.password') .right @@ -30,6 +35,9 @@ .in =f.submit t("layout.devise.shared_links.sign_up"), :class => 'button', :id => 'btnLogin' .both + = hidden_field_tag :recaptcha_challenge_field + = hidden_field_tag :recaptcha_response_field, 'manual_challenge' + = render 'devise/shared/providers' =showDeviseHintError(:login, uname_error) =showDeviseHintError(:name, name_error) diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml index 55c16f02d..1598f8dcf 100644 --- a/app/views/devise/sessions/new.html.haml +++ b/app/views/devise/sessions/new.html.haml @@ -27,8 +27,7 @@ .text=t('devise.sessions.remember_me') .in=f.submit t('layout.devise.shared_links.sign_in'), :class => 'button', :id => 'btnLogin' %div{:style => "clear: both;"} - .hr - .both + = render 'devise/shared/providers' .forgot .password %p= link_to t("layout.devise.shared_links.forgot_password"), new_password_path(resource_name) diff --git a/app/views/devise/shared/_providers.haml b/app/views/devise/shared/_providers.haml new file mode 100644 index 000000000..d173f01d0 --- /dev/null +++ b/app/views/devise/shared/_providers.haml @@ -0,0 +1,11 @@ +- if devise_mapping.omniauthable? + .hr + .other + .left + %p= t('layout.sessions.sign_up_with') + .right + - resource_class.omniauth_providers.each do |provider| + = link_to omniauth_authorize_path(resource_name, provider) do + - provider = provider.to_s.gsub(/_oauth2/,'') + = image_tag("#{provider}.png", :alt => provider, :class => provider) + .both \ No newline at end of file diff --git a/app/views/groups/members/index.html.haml b/app/views/groups/members/index.html.haml index 2ce755b65..dc65e3463 100644 --- a/app/views/groups/members/index.html.haml +++ b/app/views/groups/members/index.html.haml @@ -30,7 +30,7 @@ .lineForm= select_tag 'role', options_for_collaborators_roles_select .both %br - = submit_tag t("layout.add"), :class => 'button' + = submit_tag t('layout.add'), :class => 'button', :data => {'disable-with' => t('layout.processing')} .hr.bottom .both diff --git a/app/views/groups/profile/_form.html.haml b/app/views/groups/profile/_form.html.haml index 4ac4eaf82..db6b6a95f 100644 --- a/app/views/groups/profile/_form.html.haml +++ b/app/views/groups/profile/_form.html.haml @@ -12,5 +12,5 @@ %br .leftlist \  -.rightlist= submit_tag t("layout.save") +.rightlist= submit_tag t('layout.save'), :data => {'disable-with' => t('layout.saving')} .both diff --git a/app/views/groups/profile/show.html.haml b/app/views/groups/profile/show.html.haml index 890b9c2c9..a4e7e436f 100644 --- a/app/views/groups/profile/show.html.haml +++ b/app/views/groups/profile/show.html.haml @@ -1,4 +1,8 @@ -set_meta_tags :title => title_object(@group) -- edit_link = can?(:edit, @group) ? link_to(t("layout.edit"), edit_group_path(@group), :class => 'button') : nil -= render 'shared/profile', :uname => @group.name, :group => @group, :search_path => group_path, :projects => @projects, :edit_link => edit_link +- edit_url = can?(:edit, @group) ? edit_group_path(@group) : nil += render 'shared/profile', :uname => @group.name, + :group => @group, + :search_path => group_path, + :projects => @projects, + :edit_url => edit_url diff --git a/app/views/activity_feeds/_feed_tabs.html.haml b/app/views/home/_feed_tabs.html.haml similarity index 87% rename from app/views/activity_feeds/_feed_tabs.html.haml rename to app/views/home/_feed_tabs.html.haml index e8b731174..2531a11d4 100644 --- a/app/views/activity_feeds/_feed_tabs.html.haml +++ b/app/views/home/_feed_tabs.html.haml @@ -1,4 +1,4 @@ -.sub-menu +.sub-menu.activity-tabs %nav %ul - (collection = t 'feed_menu').each do |base, title| diff --git a/app/views/activity_feeds/_list.html.haml b/app/views/home/_list.html.haml similarity index 100% rename from app/views/activity_feeds/_list.html.haml rename to app/views/home/_list.html.haml diff --git a/app/views/activity_feeds/_sidebar.html.haml b/app/views/home/_sidebar.html.haml similarity index 100% rename from app/views/activity_feeds/_sidebar.html.haml rename to app/views/home/_sidebar.html.haml diff --git a/app/views/home/_top_menu.html.haml b/app/views/home/_top_menu.html.haml new file mode 100644 index 000000000..43bae255c --- /dev/null +++ b/app/views/home/_top_menu.html.haml @@ -0,0 +1,7 @@ +- content_for :feed_tabs do + .sub-menu + %nav + %ul + %li= link_to t('activity_menu.activity_feed'), root_path, :class => action_name == 'activity' ? 'active' : '' + %li= link_to t('activity_menu.tracker'), issues_path, :class => action_name == 'issues' ? 'active' : '' + %li= link_to t('activity_menu.pull_requests'), pull_requests_path, :class => action_name == 'pull_requests' ? 'active' : '' \ No newline at end of file diff --git a/app/views/activity_feeds/index.atom.builder b/app/views/home/activity.atom.builder similarity index 100% rename from app/views/activity_feeds/index.atom.builder rename to app/views/home/activity.atom.builder diff --git a/app/views/activity_feeds/index.html.haml b/app/views/home/activity.html.haml similarity index 87% rename from app/views/activity_feeds/index.html.haml rename to app/views/home/activity.html.haml index a8bdf4758..ed75e4efe 100644 --- a/app/views/activity_feeds/index.html.haml +++ b/app/views/home/activity.html.haml @@ -2,7 +2,8 @@ %h3.fix = t("layout.activity_feed.header") = link_to image_tag("rss.ico", :width => '15px', :height => '15px', :class => 'atom_icon'), atom_activity_feeds_path(:format => 'atom', :token => current_user.authentication_token) + =render 'feed_tabs' =render 'list' - content_for :sidebar, render('sidebar') -- content_for :feed_tabs, render('feed_tabs') +- render 'top_menu' diff --git a/app/views/home/issues.html.haml b/app/views/home/issues.html.haml new file mode 100644 index 000000000..91e729667 --- /dev/null +++ b/app/views/home/issues.html.haml @@ -0,0 +1,12 @@ +-set_meta_tags :title => t("home.#{action_name}.title") +-render 'top_menu' +-content_for :sidebar do + =form_tag send("#{action_name}_path"), :id => 'filter_issues', :method => :get do + .bordered + %table + -%w[all assigned created].each do |filter| + %tr + %td.width18=radio_button_tag :myradio, filter, filter == @filter, {:id => 'myradio1', :class => 'niceRadio', :name => 'filter'} + %td.width135=t("layout.#{action_name}.#{filter}") + %td.width30.right=instance_variable_get("@#{filter}_issues").not_closed_or_merged.count +=render 'projects/issues/issues_table', :issues => @issues diff --git a/app/views/activity_feeds/partials/_build_list_notification.haml b/app/views/home/partials/_build_list_notification.haml similarity index 81% rename from app/views/activity_feeds/partials/_build_list_notification.haml rename to app/views/home/partials/_build_list_notification.haml index 9f1594004..1dcc17961 100644 --- a/app/views/activity_feeds/partials/_build_list_notification.haml +++ b/app/views/home/partials/_build_list_notification.haml @@ -3,7 +3,7 @@ .image= link_to(image_tag(avatar_url(user, :small), :alt => 'avatar'), user_path(user)) if user.persisted? .text %span - = raw t('notifications.bodies.build_task', :task_num => task_num, :task_link => build_list_path(build_list_id)) + = raw t('notifications.bodies.build_task', :id => build_list_id, :task_link => build_list_path(build_list_id)) = raw t('notifications.bodies.project', :project_link => link_to("#{project_owner}/#{project_name}", project_path(project_owner, project_name)) ) - message, error = case status - when BuildList::BUILD_PENDING @@ -15,5 +15,5 @@ - else ['failed', t("layout.build_lists.statuses.#{BuildList::HUMAN_STATUSES[status]}")] = raw t("notifications.bodies.build_status.#{message}", :error => error) .both - %span.date= updated_at + = datetime_moment updated_at, :tag => :span, :class => 'date' .both diff --git a/app/views/activity_feeds/partials/_git_delete_branch_notification.haml b/app/views/home/partials/_git_delete_branch_notification.haml similarity index 88% rename from app/views/activity_feeds/partials/_git_delete_branch_notification.haml rename to app/views/home/partials/_git_delete_branch_notification.haml index 3fe7cc451..c207d9df8 100644 --- a/app/views/activity_feeds/partials/_git_delete_branch_notification.haml +++ b/app/views/home/partials/_git_delete_branch_notification.haml @@ -7,5 +7,5 @@ = t('notifications.bodies.delete_branch', :branch_name => branch_name, :user_link => _user_link).html_safe = raw t("notifications.bodies.project", :project_link => link_to("#{project_owner}/#{project_name}", project_path(project_owner, project_name)) ) .both - %span.date= activity_feed.created_at + = datetime_moment activity_feed.created_at, :tag => :span, :class => 'date' .both diff --git a/app/views/activity_feeds/partials/_git_new_push_notification.haml b/app/views/home/partials/_git_new_push_notification.haml similarity index 77% rename from app/views/activity_feeds/partials/_git_new_push_notification.haml rename to app/views/home/partials/_git_new_push_notification.haml index 9f1fb007f..f9553f91f 100644 --- a/app/views/activity_feeds/partials/_git_new_push_notification.haml +++ b/app/views/home/partials/_git_new_push_notification.haml @@ -7,7 +7,7 @@ = raw t("notifications.bodies.#{change_type}_branch", {:branch_name => branch_name, :user_link => _user_link}) = raw t("notifications.bodies.project", :project_link => link_to("#{project_owner}/#{project_name}", project_path(project_owner, project_name)) ) .both - %span.date= activity_feed.created_at + = datetime_moment activity_feed.created_at, :tag => :span, :class => 'date' .both %span.subject - last_commits.each do |commit| @@ -15,6 +15,6 @@ = commit[1] %br - if defined? other_commits - -pluralize = t('layout.commits.pluralize').map {|base, title| title.to_s} %br - =link_to t('notifications.bodies.more_commits', :count => other_commits_count, :commits => Russian.p(other_commits_count, *pluralize)), diff_path(project_owner, project_name, :diff => other_commits) + =link_to t('notifications.bodies.more_commits', :count => other_commits_count, :commits => commits_pluralize(other_commits_count)), + diff_path(project_owner, project_name, :diff => other_commits) diff --git a/app/views/activity_feeds/partials/_issue_assign_notification.haml b/app/views/home/partials/_issue_assign_notification.haml similarity index 81% rename from app/views/activity_feeds/partials/_issue_assign_notification.haml rename to app/views/home/partials/_issue_assign_notification.haml index cf9df13d1..4c58df39b 100644 --- a/app/views/activity_feeds/partials/_issue_assign_notification.haml +++ b/app/views/home/partials/_issue_assign_notification.haml @@ -4,5 +4,5 @@ = raw t("notifications.bodies.issue_assign_notification", { :issue_link => link_to(issue_title, project_issue_path(project_owner, project_name, issue_serial_id))}) = raw t("notifications.bodies.project", :project_link => link_to("#{project_owner}/#{project_name}", project_path(project_owner, project_name)) ) .both - %span.date= activity_feed.created_at + = datetime_moment activity_feed.created_at, :tag => :span, :class => 'date' .both diff --git a/app/views/activity_feeds/partials/_new_comment_commit_notification.haml b/app/views/home/partials/_new_comment_commit_notification.haml similarity index 90% rename from app/views/activity_feeds/partials/_new_comment_commit_notification.haml rename to app/views/home/partials/_new_comment_commit_notification.haml index 09f23f4d9..e5401ee74 100644 --- a/app/views/activity_feeds/partials/_new_comment_commit_notification.haml +++ b/app/views/home/partials/_new_comment_commit_notification.haml @@ -7,6 +7,6 @@ = raw t("notifications.bodies.new_comment_notification.commit_content", {:commit_link => link_to(commit_message, commit_path(project_owner, project_name, commit_id) + "#comment#{comment_id}")}) = raw t("notifications.bodies.project", :project_link => link_to("#{project_owner}/#{project_name}", project_path(project_owner, project_name)) ) .both - %span.date= activity_feed.created_at + = datetime_moment activity_feed.created_at, :tag => :span, :class => 'date' .both %span.subject= comment_body diff --git a/app/views/activity_feeds/partials/_new_comment_notification.haml b/app/views/home/partials/_new_comment_notification.haml similarity index 90% rename from app/views/activity_feeds/partials/_new_comment_notification.haml rename to app/views/home/partials/_new_comment_notification.haml index ef4b4fa53..7a3cbcb01 100644 --- a/app/views/activity_feeds/partials/_new_comment_notification.haml +++ b/app/views/home/partials/_new_comment_notification.haml @@ -7,6 +7,6 @@ = raw t("notifications.bodies.new_comment_notification.content", {:issue_link => link_to(issue_title, project_issue_path(project_owner, project_name, issue_serial_id) + "#comment#{comment_id}")}) = raw t("notifications.bodies.project", :project_link => link_to("#{project_owner}/#{project_name}", project_path(project_owner, project_name)) ) .both - %span.date= activity_feed.created_at + = datetime_moment activity_feed.created_at, :tag => :span, :class => 'date' .both %span.subject= comment_body diff --git a/app/views/activity_feeds/partials/_new_issue_notification.haml b/app/views/home/partials/_new_issue_notification.haml similarity index 88% rename from app/views/activity_feeds/partials/_new_issue_notification.haml rename to app/views/home/partials/_new_issue_notification.haml index 76eb29e16..5b3fdb5c9 100644 --- a/app/views/activity_feeds/partials/_new_issue_notification.haml +++ b/app/views/home/partials/_new_issue_notification.haml @@ -6,6 +6,6 @@ = raw t("notifications.bodies.new_issue_notification", { :user_link => user_link(user, user_name), :issue_link => project_issue_path(project_owner, project_name, issue_serial_id)}) = raw t("notifications.bodies.project", :project_link => link_to("#{project_owner}/#{project_name}", project_path(project_owner, project_name)) ) .both - %span.date= activity_feed.created_at + = datetime_moment activity_feed.created_at, :tag => :span, :class => 'date' .both %span.subject= issue_title diff --git a/app/views/activity_feeds/partials/_new_user_notification.haml b/app/views/home/partials/_new_user_notification.haml similarity index 86% rename from app/views/activity_feeds/partials/_new_user_notification.haml rename to app/views/home/partials/_new_user_notification.haml index aaa894d5d..870fa68f9 100644 --- a/app/views/activity_feeds/partials/_new_user_notification.haml +++ b/app/views/home/partials/_new_user_notification.haml @@ -5,7 +5,7 @@ %span.name = link_to activity_feed.user.uname, user_path(activity_feed.user.uname) .both - %span.date= activity_feed.created_at + = datetime_moment activity_feed.created_at, :tag => :span, :class => 'date' .both .both %span.subject #{ t("notifications.bodies.new_user_notification.title", :user_name => user_name) } diff --git a/app/views/activity_feeds/partials/_wiki_new_commit_notification.haml b/app/views/home/partials/_wiki_new_commit_notification.haml similarity index 88% rename from app/views/activity_feeds/partials/_wiki_new_commit_notification.haml rename to app/views/home/partials/_wiki_new_commit_notification.haml index e431ce73e..4659792d0 100644 --- a/app/views/activity_feeds/partials/_wiki_new_commit_notification.haml +++ b/app/views/home/partials/_wiki_new_commit_notification.haml @@ -6,5 +6,5 @@ = raw t("notifications.bodies.wiki_new_commit_notification", {:user_link => user_link(user, user_name), :history_link => link_to("wiki", history_project_wiki_index_path(project_owner, project_name))}) = raw t("notifications.bodies.project", :project_link => link_to("#{project_owner}/#{project_name}", project_path(project_owner, project_name)) ) .both - %span.date= activity_feed.created_at + = datetime_moment activity_feed.created_at, :tag => :span, :class => 'date' .both diff --git a/app/views/layouts/_noscript.html.haml b/app/views/layouts/_noscript.html.haml new file mode 100644 index 000000000..76768e245 --- /dev/null +++ b/app/views/layouts/_noscript.html.haml @@ -0,0 +1,2 @@ +%noscript + %div{:style => 'font-size: 12px; color: red; font-weight: bold;'}= t 'layout.noscript_message' \ No newline at end of file diff --git a/app/views/layouts/_notifies.html.haml b/app/views/layouts/_notifies.html.haml index 82ca70300..1c1f9c98f 100644 --- a/app/views/layouts/_notifies.html.haml +++ b/app/views/layouts/_notifies.html.haml @@ -2,9 +2,8 @@ .flash_notify - if (flash_notify = FlashNotify.published.first) && flash_notify.should_show?(cookies[:flash_notify_hash]) .alert{:class => "alert-#{flash_notify.status}"} - = flash_notify.body I18n.locale + = flash_notify.body(I18n.locale).html_safe %a.close#close-alert{:'data-dismiss'=>"alert", :href=>"#"} × :javascript var FLASH_HASH_ID = "#{flash_notify.hash_id}"; - var FLASH_EXPIRES_AT = "#{Date.today + 1.year}"; diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 9f62ca9a2..7143d44d4 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -6,13 +6,14 @@ = stylesheet_link_tag "application" /[if lt IE 9] = javascript_include_tag 'https://html5shiv.googlecode.com/svn/trunk/html5.js' - = javascript_include_tag "application" + = javascript_include_tag 'application' + = javascript_include_tag 'moment/ru.js' if I18n.locale == :ru = csrf_meta_tag = display_meta_tags :site => APP_CONFIG['project_name'], :reverse => true, :separator => '-' - if user_signed_in? = auto_discovery_link_tag :atom, atom_activity_feeds_path(:format => 'atom', :token => current_user.authentication_token), :title => t("layout.atom_link_tag_title", :nickname => current_user.uname, :app_name => APP_CONFIG['project_name']) - %body + %body{'ng-app' => 'RosaABF', 'ng-controller' => 'RosaABFController', 'ng-init' => "init('#{I18n.locale}', #{!!current_user.try(:sound_notifications)})"} .wrap{:class => content_for?(:sidebar) ? 'columns' : ''} %header .left @@ -38,6 +39,7 @@ .a= link_to t('layout.logout'), destroy_user_session_path, :method => :delete - else .user + .profile= link_to t("layout.devise.shared_links.sign_up"), new_register_request_path .profile= link_to t("layout.devise.shared_links.sign_in"), new_user_session_path .right .both @@ -46,6 +48,7 @@ - if content_for?(:feed_tabs) = yield :feed_tabs .both + = render 'layouts/noscript' = render "layouts/flashes" = render "layouts/notifies" %article diff --git a/app/views/layouts/invite.html.haml b/app/views/layouts/invite.html.haml index 9a9d732b0..a0834a466 100644 --- a/app/views/layouts/invite.html.haml +++ b/app/views/layouts/invite.html.haml @@ -19,4 +19,4 @@ .both / Footer %footer= render "layouts/menu/bottom" - = render 'layouts/counters' unless current_user.try(:admin?) \ No newline at end of file + = render 'layouts/counters' if !current_user.try(:admin?) && Rails.env.production? diff --git a/app/views/layouts/sessions.html.haml b/app/views/layouts/sessions.html.haml index dec6eecf5..1a1a8b0b8 100644 --- a/app/views/layouts/sessions.html.haml +++ b/app/views/layouts/sessions.html.haml @@ -13,8 +13,8 @@ = display_meta_tags :site => APP_CONFIG['project_name'], :reverse => true, :separator => '-' %body - -# render "layouts/flashes" + = render 'layouts/noscript' = yield - = render 'layouts/counters' unless current_user.try(:admin?) + = render 'layouts/counters' if !current_user.try(:admin?) && Rails.env.production? %footer= render "layouts/menu/bottom" diff --git a/app/views/layouts/tour.html.haml b/app/views/layouts/tour.html.haml index 5d70da39d..631f0175f 100644 --- a/app/views/layouts/tour.html.haml +++ b/app/views/layouts/tour.html.haml @@ -42,13 +42,15 @@ .tour =image_tag "tour-top#{I18n.locale == :en ? '-eng' : ''}.png", :alt => 'ABF' .both + = render 'layouts/noscript' + = render 'layouts/flashes' %article = yield .both -# No idea why here was this div... -#.tour-gap %footer= render "layouts/menu/bottom" - = render 'layouts/counters' unless current_user.try(:admin?) + = render 'layouts/counters' if !current_user.try(:admin?) && Rails.env.production? :javascript $(document).ready(function() { $().piroBox_ext({ diff --git a/app/views/pages/root.html.haml b/app/views/pages/root.html.haml deleted file mode 100644 index 0817ea065..000000000 --- a/app/views/pages/root.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -.root - = form_tag search_index_path, :method => 'get' do - = text_field_tag 'query', params[:query], :placeholder => t("layout.search.header"), :class => 'exsearch' - = submit_tag t("layout.search.header") diff --git a/app/views/pages/tos.html.haml b/app/views/pages/tos.html.haml index f554d66e4..bebfa9852 100644 --- a/app/views/pages/tos.html.haml +++ b/app/views/pages/tos.html.haml @@ -40,12 +40,8 @@ You must be a human. Accounts registered by “bots” or other automated methods are not permitted. %li - You must provide your legal full name, a valid email address, and any other - information requested in order to complete the signup process. - %li - Your login may only be used by one person - a single login shared by multiple - people is not permitted. You may create separate logins for as many - people as your plan allows. + To register in ABF, you should provide your legal full name and a valid email address. + It is also possible to register using Google, Facebook or Github account. %li You are responsible for maintaining the security of your account and password. ROSA cannot and will not be liable for any loss or damage from your @@ -66,14 +62,13 @@ %ol %li - To cancel your account, we need a email request sent from the same address - as the. A email request to cancel your account is necessary. + To cancel your account, we need an email request sent from the same address as + the one you are registered with. An email request to cancel your account is necessary. %li - All of your Content will be immediately deleted from the Service upon - cancellation. This information cannot be recovered once your account is cancelled. - %li - If you cancel the Service before the end of your current paid up month, your - cancellation will take effect immediately and you will not be charged again. + All the content from your personal platform repositories will be immediately deleted + from the Service upon cancellation. This information cannot be recovered once your + account is canceled. Any content committed by you to repositories of other platforms + will remain in place until owners of that platforms cancel their accounts. %li ROSA, in its sole discretion, has the right to suspend or terminate your account and refuse any and all current or future use of the Service, or any other ROSA diff --git a/app/views/platforms/base/_sidebar.html.haml b/app/views/platforms/base/_sidebar.html.haml index b5a857be5..827837b02 100644 --- a/app/views/platforms/base/_sidebar.html.haml +++ b/app/views/platforms/base/_sidebar.html.haml @@ -10,11 +10,12 @@ = link_to t("layout.platforms.about"), platform_path(@platform) %li{:class => (contr == :repositories) ? 'active' : ''} = link_to t("layout.repositories.list_header"), platform_repositories_path(@platform) + %li{:class => (contr == :contents) ? 'active' : ''} + = link_to t('layout.platforms.contents'), platform_contents_path(@platform) - if can? :show, @platform %li{:class => (act == :index && contr == :maintainers) ? 'active' : nil} = link_to t("layout.platforms.maintainers"), platform_maintainers_path(@platform) - - if can? :show, @platform - %li{:class => (contr == :mass_builds && [:index, :create].include?(act)) ? 'active' : ''} + %li{:class => (contr == :mass_builds) ? 'active' : ''} = link_to t("layout.platforms.mass_build"), platform_mass_builds_path(@platform) - if can? :read, @platform.products.build %li{:class => (contr == :products) ? 'active' : ''} @@ -31,6 +32,8 @@ - if can? :edit, @platform %li{:class => (act == :index && contr == :key_pairs) ? 'active' : ''} = link_to t("layout.key_pairs.header"), platform_key_pairs_path(@platform) + %li{:class => (contr == :tokens) ? 'active' : ''} + = link_to t('layout.tokens.header'), platform_tokens_path(@platform) -#- if current_user.owner_of? @platform or current_user.admin? %li{:class => (act == :index && contr == :private_users) ? 'active' : ''} = link_to t("layout.platforms.private_users"), platform_private_users_path(@platform) diff --git a/app/views/platforms/contents/_contents.html.haml b/app/views/platforms/contents/_contents.html.haml new file mode 100644 index 000000000..a94551c71 --- /dev/null +++ b/app/views/platforms/contents/_contents.html.haml @@ -0,0 +1,34 @@ +#contents + + = form_for @platform, :url => platform_content_path(@platform, @path), :html => { :class => :form, :remote => true, :method => :get } do |f| + = tracker_search_field(:term, @term.present? ? @term : t('layout.platforms.search_contents')) + = f.submit t('layout.search.header'), :data => {'disable-with' => t('layout.processing')} + %br + + .path= build_content_paths(@platform, @path) + .both + + %table.tablesorter.project{:cellpadding => "0", :cellspacing => "0"} + %tbody + + - if @path.present? + %tr + %td= link_to '../', platform_content_path(@platform, @path, '../'), {:remote => true} + %td + %td + + - (@contents.select(&:is_folder?) | @contents).each do |content| + %tr + %td + - options = {:class => 'files-see'} + - if content.is_folder? + - pic = 'folder.png' + - path = platform_content_path(@platform, @path, content.name) + - options[:remote] = true + .pic= image_tag pic || 'code.png' + .name= link_to(content.name, path || content.download_url, options) + %td= link_to t('activerecord.models.build_list'), content.build_list if content.build_list + %td= number_to_human_size(content.size) unless content.is_folder? + .both + + = will_paginate @contents, {:remote => true} \ No newline at end of file diff --git a/app/views/platforms/contents/index.html.haml b/app/views/platforms/contents/index.html.haml new file mode 100644 index 000000000..84898f087 --- /dev/null +++ b/app/views/platforms/contents/index.html.haml @@ -0,0 +1,14 @@ +- set_meta_tags :title => [title_object(@platform), t('layout.platforms.contents')] += render 'platforms/base/submenu' += render 'platforms/base/sidebar' + +%h3 + = t('layout.platforms.contents_of') + = @platform.name + += render 'contents' + +:javascript + $(function(){ + $('.pagination a').attr('data-remote', 'true'); + }); \ No newline at end of file diff --git a/app/views/platforms/contents/index.js.haml b/app/views/platforms/contents/index.js.haml new file mode 100644 index 000000000..2612b957a --- /dev/null +++ b/app/views/platforms/contents/index.js.haml @@ -0,0 +1,2 @@ +$('#contents').html("#{escape_javascript(render 'contents')}"); +$('.pagination a').attr('data-remote', 'true'); \ No newline at end of file diff --git a/app/views/platforms/key_pairs/_new.html.haml b/app/views/platforms/key_pairs/_new.html.haml index b740fe218..c41dbb1d7 100644 --- a/app/views/platforms/key_pairs/_new.html.haml +++ b/app/views/platforms/key_pairs/_new.html.haml @@ -1,5 +1,3 @@ -= render 'platforms/base/sidebar' - %h3= t("layout.key_pairs.header") = form_for :key_pair, :url => platform_key_pairs_path(@platform), :method => :post, :html => { :class => :form } do |f| @@ -14,4 +12,4 @@ .both .button_block - = submit_tag t("layout.save") + = submit_tag t('layout.save'), :data => {'disable-with' => t('layout.saving')} diff --git a/app/views/platforms/key_pairs/index.html.haml b/app/views/platforms/key_pairs/index.html.haml index b04743a34..1d1959d5e 100644 --- a/app/views/platforms/key_pairs/index.html.haml +++ b/app/views/platforms/key_pairs/index.html.haml @@ -1,2 +1,6 @@ +- set_meta_tags :title => [title_object(@platform), t('layout.key_pairs.header')] += render 'platforms/base/submenu' += render 'platforms/base/sidebar' + = render 'new' if can? :edit, @platform = render 'list' diff --git a/app/views/platforms/maintainers/_list.html.haml b/app/views/platforms/maintainers/_list.html.haml index 6effeb766..153e5f88b 100644 --- a/app/views/platforms/maintainers/_list.html.haml +++ b/app/views/platforms/maintainers/_list.html.haml @@ -12,6 +12,7 @@ = form_tag platform_maintainers_path(@platform), :method => :get do |f| = text_field_tag('q', params[:q], :placeholder => t("layout.maintainers.search_by_package"), :class => params[:q].present? ? 'black' : 'gray') %input{:type => 'submit', :value => t("layout.search.header")} + = submit_tag t('layout.search.header'), :data => {'disable-with' => t('layout.processing')} = button_to t('layout.clear'), {:action => :index} , :method => :get %tbody diff --git a/app/views/platforms/mass_builds/_form.html.haml b/app/views/platforms/mass_builds/_form.html.haml deleted file mode 100644 index 88748ed12..000000000 --- a/app/views/platforms/mass_builds/_form.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -= form_for :build, :url => platform_mass_builds_path(@platform), :html => { :class => 'form mass_build', :method => :post } do |f| - %section.left - =render 'repos_or_list_choice' - %br - = f.submit t("layout.projects.build_button") - %section.right - %h3= t("activerecord.attributes.build_list.arch") - - Arch.recent.each do |arch| - .lefter - = check_box_tag "arches[]", arch.id, (params[:arches]||[]).include?(arch.id.to_s), :id => "arches_#{arch.id}" - = label_tag "arches_#{arch.id}", arch.name - .both - %h3= t("activerecord.attributes.build_list.preferences") - .both.bottom_20 - = check_box_tag :auto_publish, true, @auto_publish_selected, :id => 'auto_publish' - = label_tag :auto_publish, t('activerecord.attributes.build_list.auto_publish') -.both \ No newline at end of file diff --git a/app/views/platforms/mass_builds/_repos_or_list_choice.html.haml b/app/views/platforms/mass_builds/_repos_or_list_choice.html.haml deleted file mode 100644 index 96512af3d..000000000 --- a/app/views/platforms/mass_builds/_repos_or_list_choice.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -%h3=t("layout.mass_builds.repositories") --@platform.repositories.each do |rep| - .both - =check_box_tag "repositories[]", rep.id, (params[:repositories]||[]).include?(rep.id.to_s), :id => "repositories_#{rep.id}", :href => "#{projects_list_platform_repository_path(@platform, rep)}?text=true" - =label_tag "repositories_#{rep.id}", rep.name -%h3=t("layout.mass_builds.projects_list") -=text_area_tag :projects_list, nil - diff --git a/app/views/platforms/mass_builds/index.html.haml b/app/views/platforms/mass_builds/index.html.haml index d965430c2..03f756f50 100644 --- a/app/views/platforms/mass_builds/index.html.haml +++ b/app/views/platforms/mass_builds/index.html.haml @@ -1,7 +1,7 @@ = render 'platforms/base/submenu' = render 'platforms/base/sidebar' -= render 'form' if can? :edit, @platform += link_to t('layout.mass_builds.new'), new_platform_mass_build_path(@platform), :class => 'button' if can? :create, @platform.mass_builds.build %table.tablesorter.unbordered{:cellpadding => "0", :cellspacing => "0"} %thead @@ -46,15 +46,10 @@ -if mass_build.projects_list.present? = link_to_list @platform, mass_build, 'projects_list' .both - = t('activerecord.attributes.mass_build.arch_names') + ": " - = mass_build.arch_names - .both = t('activerecord.attributes.mass_build.user') + ": " = link_to mass_build.user.fullname, mass_build.user - .both - = t('activerecord.attributes.mass_build.auto_publish') + ": " - = mass_build.auto_publish - .both - = t('activerecord.attributes.mass_build.created_at') + ": " - = mass_build.created_at + - [:arch_names, :auto_publish, :increase_release_tag, :created_at].each do |field| + .both + = t("activerecord.attributes.mass_build.#{field}") + ": " + = mass_build.send field = will_paginate @mass_builds diff --git a/app/views/platforms/mass_builds/new.html.haml b/app/views/platforms/mass_builds/new.html.haml new file mode 100644 index 000000000..d7c1eaea0 --- /dev/null +++ b/app/views/platforms/mass_builds/new.html.haml @@ -0,0 +1,47 @@ +- set_meta_tags :title => [title_object(@platform), t('layout.mass_builds.new')] + += render 'platforms/base/submenu' += render 'platforms/base/sidebar' + += form_for [@platform, @mass_build], :html => { :class => 'form mass_build', :method => :post } do |f| + %section.left + %h3=t('layout.mass_builds.repositories') + - @platform.repositories.each do |rep| + .both + = check_box_tag "repositories[]", rep.id, (params[:repositories]||[]).include?(rep.id.to_s), :id => "repositories_#{rep.id}", :href => "#{projects_list_platform_repository_path(@platform, rep)}?text=true" + = label_tag "repositories_#{rep.id}", rep.name + %h3=t('layout.mass_builds.projects_list') + = f.text_area :projects_list + %br + = f.submit t('layout.projects.build_button'), :data => {'disable-with' => t('layout.processing')} + %section.right + %h3= t("activerecord.attributes.build_list.arch") + - Arch.recent.each do |arch| + .lefter + = check_box_tag "arches[]", arch.id, (params[:arches]||[]).include?(arch.id.to_s), :id => "arches_#{arch.id}" + = label_tag "arches_#{arch.id}", arch.name + .both + - if @platform.personal? + %h3= t('activerecord.attributes.build_list.build_for_platform') + = f.collection_select :build_for_platform_id, Platform.main, :id, :name + .both + = hidden_field_tag :autocomplete_extra_repos_path, + "#{autocomplete_extra_repositories_autocompletes_path}?#{{:platform_id => @mass_build.save_to_platform.try(:id)}.to_param}" + = render 'shared/autocomplete_form', + :field => :extra_repositories, + :field_class => Repository, + :placeholder => 'uxteam_personal', + :subject => @mass_build, + :autocomplete_path => autocomplete_extra_repositories_autocompletes_path + = render 'shared/autocomplete_form', + :field => :extra_build_lists, + :field_class => BuildList, + :placeholder => '1000000', + :subject => @mass_build, + :autocomplete_path => autocomplete_extra_build_list_autocompletes_path + %h3= t("activerecord.attributes.build_list.preferences") + - [:auto_publish, :increase_release_tag].each do |field| + .both + = f.check_box field + = f.label field +.both diff --git a/app/views/platforms/platforms/_form.html.haml b/app/views/platforms/platforms/_form.html.haml index 4508e03b6..9d7400dcd 100644 --- a/app/views/platforms/platforms/_form.html.haml +++ b/app/views/platforms/platforms/_form.html.haml @@ -26,10 +26,26 @@ = hidden_field_tag 'admin_id', @admin_id, :id => 'admin_id_field' .both + %h3= t('layout.platform_arch_settings.extra_settings') + + %table.tablesorter{:cellpadding => "0", :cellspacing => "0"} + %thead + %tr + %th.lpadding16= t("activerecord.models.arch") + %th.lpadding16= t("activerecord.attributes.platform_arch_setting.default") + %th.lpadding16= t("activerecord.attributes.platform_arch_setting.time_living") + %tbody + - platform_arch_settings(@platform).each do |setting| + %tr{:class => cycle("odd", "even")} + = f.fields_for :platform_arch_settings, setting do |s_form| + %td + = setting.arch.name + = s_form.hidden_field :arch_id + %td.center= s_form.check_box :default, :class => 'check_box' + %td.right= s_form.text_field :time_living, :value => setting.time_living / 60, :class => 'text_field', :size => 10 + .both + .button_block - = submit_tag t("layout.save") - -#%input.button{:type => "submit", :class => "button"} - -#= image_tag("choose.png", :alt => t("layout.save")) - -#= t("layout.clone") + = f.submit t('layout.save'), :data => {'disable-with' => t('layout.saving')} %span.text_button_padding= t("layout.or") = link_to t("layout.cancel"), @platform.new_record? ? root_path : platform_path(@platform), :class => "button" diff --git a/app/views/platforms/platforms/clone.html.haml b/app/views/platforms/platforms/clone.html.haml index e72f71a65..b4f65eee6 100644 --- a/app/views/platforms/platforms/clone.html.haml +++ b/app/views/platforms/platforms/clone.html.haml @@ -12,6 +12,6 @@ .both .button_block - = submit_tag t("layout.clone") + = submit_tag t('layout.clone'), :data => {'disable-with' => t('layout.processing')} %span.text_button_padding= t("layout.or") = link_to t("layout.cancel"), platform_path(@platform), :class => "button" \ No newline at end of file diff --git a/app/views/platforms/platforms/edit.html.haml b/app/views/platforms/platforms/edit.html.haml index 18442734e..9d8323e44 100644 --- a/app/views/platforms/platforms/edit.html.haml +++ b/app/views/platforms/platforms/edit.html.haml @@ -5,6 +5,35 @@ = form_for @platform, :url => platform_path(@platform), :html => { :class => :form } do |f| = render "form", :f => f +- if can? :change_visibility, @platform + .hr + .leftside= t('activerecord.attributes.platform.visibility') + .rightside= link_to t("layout.platforms.change_visibility_from_#{@platform.visibility}"), + change_visibility_platform_path(@platform), + :method => :post, + :confirm => t("layout.platforms.confirm_change_visibility"), + :class => 'button' + .both + +- if can? :regenerate_metadata, @platform + .hr + %h3= t('layout.platforms.metadata') + .leftlist= t('activerecord.attributes.regeneration_status.status') + .rightlist= t("layout.regeneration_statuses.statuses.#{@platform.human_status}") + .both + .leftlist= t('activerecord.attributes.regeneration_status.last_regenerated_at') + .rightlist= @platform.last_regenerated_at + .both + .leftlist= t('activerecord.attributes.regeneration_status.last_regenerated_status') + .rightlist= t("layout.regeneration_statuses.last_regenerated_statuses.#{@platform.human_regeneration_status}") + .both + .leftlist= t('activerecord.attributes.regeneration_status.last_regenerated_log_sha1') + .rightlist= link_to_file_store('regeneration.log', @platform.last_regenerated_log_sha1) + .both + .leftside + .rightside= link_to t('layout.regeneration_statuses.regenerate_metadata'), regenerate_metadata_platform_path(@platform), :method => :put, :confirm => t('layout.confirm'), :class => 'button' + .both + - if can? :destroy, @platform .hr .leftside= t("layout.platforms.delete_warning") @@ -12,5 +41,5 @@ - if can? :clear, @platform .hr .leftside= t("layout.repositories.clear_warning") - .rightside= link_to t("layout.repositories.clear"), clear_platform_path(@platform), :class => 'button', :confirm => t('layout.repositories.clear_confirm'), :method => :post + .rightside= link_to t("layout.repositories.clear"), clear_platform_path(@platform), :class => 'button', :confirm => t('layout.repositories.clear_confirm'), :method => :put .both diff --git a/app/views/platforms/platforms/show.html.haml b/app/views/platforms/platforms/show.html.haml index 0461c86d6..14aa01776 100644 --- a/app/views/platforms/platforms/show.html.haml +++ b/app/views/platforms/platforms/show.html.haml @@ -2,7 +2,9 @@ = render 'submenu' = render 'sidebar' -%h3.fix= "#{t("layout.platforms.about")} #{@platform.name}" +%h3.fix + = t 'layout.platforms.about' + = link_to @platform.name, platform_contents_path(@platform) %p= @platform.description diff --git a/app/views/platforms/product_build_lists/_filter.html.haml b/app/views/platforms/product_build_lists/_filter.html.haml index fa2824e48..5104d3208 100644 --- a/app/views/platforms/product_build_lists/_filter.html.haml +++ b/app/views/platforms/product_build_lists/_filter.html.haml @@ -1,5 +1,5 @@ - content_for :sidebar do - = render 'projects/build_lists/server_status', :queues => [:iso] + = render 'server_status' = form_tag product_build_lists_path, :method => :get, :html => {:class => :form} do .block @@ -11,4 +11,4 @@ = text_field_tag :product_id, params[:product_id] %br %br - = submit_tag t("layout.search.header") \ No newline at end of file + = submit_tag t('layout.search.header'), :data => {'disable-with' => t('layout.processing')} \ No newline at end of file diff --git a/app/views/platforms/product_build_lists/_product_build_list.html.haml b/app/views/platforms/product_build_lists/_product_build_list.html.haml index 3b1765fc1..dd5bd8107 100644 --- a/app/views/platforms/product_build_lists/_product_build_list.html.haml +++ b/app/views/platforms/product_build_lists/_product_build_list.html.haml @@ -10,4 +10,4 @@ %td= link_to(nil, pbl.container_path) unless pbl.project %td= link_to pbl.product.name, platform_product_path(platform, product) %td= link_to image_tag('x.png'), platform_product_product_build_list_path(platform, product, pbl), :method => :delete, :confirm => t("layout.confirm") if can?(:destroy, pbl) && pbl.can_destroy? && !pbl.project - %td= l(pbl.updated_at, :format => :long) \ No newline at end of file + = datetime_moment pbl.updated_at, :tag => :td \ No newline at end of file diff --git a/app/views/platforms/product_build_lists/_results.html.haml b/app/views/platforms/product_build_lists/_results.html.haml deleted file mode 100644 index 6e458cce5..000000000 --- a/app/views/platforms/product_build_lists/_results.html.haml +++ /dev/null @@ -1,21 +0,0 @@ -%h3= subject.class.human_attribute_name(subject.is_a?(BuildList) ? 'logs' : 'results') -- unless subject.results.present? - %h4.nomargin= t('layout.no_') -- else - %table.tablesorter.width565{:cellpadding => "0", :cellspacing => "0"} - %thead - %tr - %th= t("activerecord.attributes.product_build_list/results.file_name") - %th= t("activerecord.attributes.product_build_list/results.sha1") - %th= t("activerecord.attributes.product_build_list/results.size") - %tbody - - subject.results.each do |item| - %tr - - sha1 = item['sha1'] - - filename = item['file_name'] - - url = "#{APP_CONFIG['file_store_url']}/api/v1/file_stores/#{sha1}" - - url << '.log?show=true' if filename =~ /.*\.(log|txt)$/ - %td= link_to filename, url - %td= sha1 - %td= item['size'] -.both \ No newline at end of file diff --git a/app/views/platforms/product_build_lists/_server_status.html.haml b/app/views/platforms/product_build_lists/_server_status.html.haml new file mode 100644 index 000000000..e5c345d4c --- /dev/null +++ b/app/views/platforms/product_build_lists/_server_status.html.haml @@ -0,0 +1,19 @@ +.bordered.nopadding + %h3.medium= t('layout.build_lists.build_server_status.header') + + .table + .lefter= t("layout.build_lists.build_server_status.iso_workers") + .both + .table + .lefter= t("layout.build_lists.build_server_status.amount") + .righter= @build_server_status[:iso][:workers] + .both + - [:tasks, :build_tasks].each do |metric| + .table + .lefter= t("layout.build_lists.build_server_status.#{metric}") + .righter= @build_server_status[:iso][metric] + .both + %br + + + diff --git a/app/views/platforms/product_build_lists/new.html.haml b/app/views/platforms/product_build_lists/new.html.haml index 24743689f..4a9f130bf 100644 --- a/app/views/platforms/product_build_lists/new.html.haml +++ b/app/views/platforms/product_build_lists/new.html.haml @@ -14,7 +14,7 @@ = render 'platforms/products/def_fields', :f => f - = f.submit t("layout.projects.build_button") + = f.submit t('layout.projects.build_button'), :data => {'disable-with' => t('layout.processing')} %br %div diff --git a/app/views/platforms/product_build_lists/show.html.haml b/app/views/platforms/product_build_lists/show.html.haml index c27756272..563b43317 100644 --- a/app/views/platforms/product_build_lists/show.html.haml +++ b/app/views/platforms/product_build_lists/show.html.haml @@ -7,44 +7,49 @@ %h3= t("layout.product_build_lists.main_data") .both +%div{'ng-controller' => 'ProductBuildListController'} + = render 'show_field', :key => :id, :value => pbl.id + = hidden_field_tag :product_build_list_id, pbl.id + = render 'show_field', :key => :status, :value => '{{pbl.human_status}}' + - if pbl.user + = render 'show_field', :key => :user, :value => link_to(pbl.user.try(:fullname), pbl.user) -= render 'show_field', :key => :id, :value => pbl.id -= render 'show_field', :key => :status, :value => pbl.human_status -- if pbl.user - = render 'show_field', :key => :user, :value => link_to(pbl.user.try(:fullname), pbl.user) + = render 'show_field', :key => :product, :value => link_to(pbl.product.name, platform_product_path(platform, product)) -= render 'show_field', :key => :product, :value => link_to(pbl.product.name, platform_product_path(platform, product)) + = render 'show_field', :key => :project, :value => link_to(pbl.project.name_with_owner, project_path(pbl.project)) -= render 'show_field', :key => :project, :value => link_to(pbl.project.name_with_owner, project_path(pbl.project)) + = render 'show_field', :key => :project_version, :value => product_build_list_version_link(pbl, true) -= render 'show_field', :key => :project_version, :value => product_build_list_version_link(pbl, true) + - [:main_script, :params].each do |el| + = render 'show_field', :key => el, :value => pbl.send(el) -- [:main_script, :params].each do |el| - = render 'show_field', :key => el, :value => pbl.send(el) + = render 'show_field', :key => :time_living, :value => (pbl.time_living / 60) -= render 'show_field', :key => :time_living, :value => (pbl.time_living / 60) + = render 'show_field', :key => :autostarted, :value => t("layout.#{pbl.autostarted}_") -= render 'show_field', :key => :autostarted, :value => t("layout.#{pbl.autostarted}_") + = render 'show_field', :key => :notified_at, :value => '{{pbl.notified_at}}' -= render 'show_field', :key => :notified_at, :value => l(pbl.updated_at, :format => :long) - -- if pbl.can_cancel? && can?(:cancel, pbl) - = link_to t("layout.build_lists.cancel"), cancel_platform_product_product_build_list_path(pbl.product.platform, pbl.product, pbl), - :method => :put, :confirm => t("layout.confirm"), :class => 'button' - .both - %br - -- if pbl.build_completed? && can?(:update, pbl) - = form_for pbl, :url => platform_product_product_build_list_path(pbl.product.platform, pbl.product, pbl) do |f| - .leftlist= f.label :not_delete - .rightlist - = f.select :not_delete, [false, true].collect{|status| [t("layout.#{status}_"), status]}, {:selected => pbl.not_delete} - .both - %br - = submit_tag t('layout.update') + - if can?(:cancel, pbl) + = link_to t("layout.build_lists.cancel"), + cancel_platform_product_product_build_list_path(pbl.product.platform, pbl.product, pbl), + :method => :put, :confirm => t("layout.confirm"), :class => 'button', + 'ng-show' => 'pbl.can_cancel' .both -- unless pbl.not_delete - .flash_notify + %br + + - if can?(:update, pbl) + = form_for pbl, + :url => platform_product_product_build_list_path(pbl.product.platform,pbl.product, pbl), + :html => {'ng-show' => "pbl.status == #{ProductBuildList::BUILD_COMPLETED}"} do |f| + + .leftlist= f.label :not_delete + .rightlist + = f.select :not_delete, [false, true].collect{|status| [t("layout.#{status}_"), status]}, {:selected => pbl.not_delete} + .both + %br + = submit_tag t('layout.update'), :data => {'disable-with' => t('layout.processing')} + .both + .flash_notify{'ng-hide' => 'pbl.not_delete'} .alert.alert-error - days = pbl.autostarted? ? ProductBuildList::LIVE_TIME : ProductBuildList::MAX_LIVE_TIME - days = (pbl.created_at.to_date - days.ago.to_date).to_i @@ -54,10 +59,10 @@ = t('layout.product_build_lists.will_be_removed_today') .both -- if pbl.build_started? || pbl.build_canceling? - = render 'shared/log', { :build_started => true, :get_log_path => log_platform_product_product_build_list_path(pbl.product.platform, pbl.product, pbl) } + %div{'ng-show' => "pbl.status == #{ProductBuildList::BUILD_STARTED}"} + = render 'shared/log', { :build_started => true, :get_log_path => log_platform_product_product_build_list_path(pbl.product.platform, pbl.product, pbl) } -= render 'results', :subject => pbl + = render 'shared/build_results', :subject => pbl :javascript $(function(){ diff --git a/app/views/platforms/product_build_lists/show.json.jbuilder b/app/views/platforms/product_build_lists/show.json.jbuilder new file mode 100644 index 000000000..543fb0dce --- /dev/null +++ b/app/views/platforms/product_build_lists/show.json.jbuilder @@ -0,0 +1,14 @@ +json.product_build_list do + json.(@product_build_list, :id, :status, :human_status, :not_delete) + json.notified_at l(@product_build_list.updated_at, :format => :long) + + json.can_cancel @product_build_list.can_cancel? + + json.results @product_build_list.results do |result| + json.file_name result['file_name'] + json.sha1 result['sha1'] + json.size result['size'] + json.url file_store_results_url(result['sha1'], result['file_name']) + end if @product_build_list.results.present? + +end diff --git a/app/views/platforms/products/_form.html.haml b/app/views/platforms/products/_form.html.haml index 0095616ad..919fd0538 100644 --- a/app/views/platforms/products/_form.html.haml +++ b/app/views/platforms/products/_form.html.haml @@ -21,7 +21,7 @@ .both .button_block - = submit_tag t("layout.save") + = submit_tag t('layout.save'), :data => {'disable-with' => t('layout.saving')} %span.text_button_padding= t("layout.or") = link_to t("layout.cancel"), @product.new_record? ? platform_path(@platform) : platform_product_path(@platform, @product), :class => "button" diff --git a/app/views/platforms/repositories/_form.html.haml b/app/views/platforms/repositories/_form.html.haml index e8e3a1b91..0e430665c 100644 --- a/app/views/platforms/repositories/_form.html.haml +++ b/app/views/platforms/repositories/_form.html.haml @@ -10,15 +10,8 @@ .both .hr -- if ['edit', 'update'].include?(controller.action_name) && can?(:regenerate_metadata, @repository) - .leftside= t('layout.repositories.regenerate_metadata') - .rightside - = link_to t('layout.repositories.regenerate_metadata').split.first, regenerate_metadata_platform_repository_path(@platform, @repository), - :method => :put, :confirm => t('layout.confirm'), :class => :button -.hr{:style => 'padding-bottom:20px;'} -.both .button_block - = submit_tag t("layout.save") + = submit_tag t('layout.save'), :data => {'disable-with' => t('layout.saving')} %span.text_button_padding= t("layout.or") = link_to t("layout.cancel"), @repository.new_record? ? platform_repositories_path(@platform) : platform_repository_path(@platform, @repository), :class => "button" diff --git a/app/views/platforms/repositories/edit.html.haml b/app/views/platforms/repositories/edit.html.haml index 5a9f1b232..7e6698996 100644 --- a/app/views/platforms/repositories/edit.html.haml +++ b/app/views/platforms/repositories/edit.html.haml @@ -8,9 +8,57 @@ = render "form", :f => f %br -= render "shared/members_table", - :remove_members_path => remove_members_platform_repository_path(@platform, @repository), - :remove_member_path => remove_member_platform_repository_path(@platform, @repository), - :add_member_path => add_member_platform_repository_path(@platform, @repository), - :members => @members, - :editable_object => @repository +- if can?(:update, @repository) + .hr + %h3= t('layout.repositories.extra_actions') + + = form_for @repository, :url => regenerate_metadata_platform_repository_path(@platform, @repository), :html => { :class => :form, :method => :put } do |f| + .leftlist= t('layout.repositories.regenerate_metadata') + .rightlist + = select_tag :build_for_platform_id, options_from_collection_for_select(Platform.main, :id, :name) if @platform.personal? + = f.submit t('layout.repositories.regenerate_metadata'), :confirm => t('layout.confirm') + .both + + - if @repository.repository_statuses.present? + %table#myTable.tablesorter.platform-repos{:cellspacing => "0", :cellpadding => "0"} + %thead + %tr + %th= t('activerecord.attributes.regeneration_status.status') + %th= t('activerecord.attributes.regeneration_status.last_regenerated_status') + %th= t('activerecord.attributes.regeneration_status.last_regenerated_log_sha1') + %th= t('activerecord.attributes.regeneration_status.last_regenerated_at') + - unless @platform.main? + %th= t('activerecord.models.platform') + %tbody + - @repository.repository_statuses.sort_by{ |s| s.platform.name }.each do |status| + %tr{:class => cycle('odd', 'even')} + %td= t("layout.regeneration_statuses.statuses.#{status.human_status}") + %td= t("layout.regeneration_statuses.last_regenerated_statuses.#{status.human_regeneration_status}") + %td= link_to_file_store('regeneration.log', status.last_regenerated_log_sha1) + %td= status.last_regenerated_at + - unless @platform.main? + %td= status.platform.name + .both + + - if @platform.main? + - if @repository.sync_lock_file_exists? + - label = t('layout.repositories.remove_sync_lock_file') + - path = sync_lock_file_platform_repository_path(@platform, @repository, :remove => true) + - else + - label = t('layout.repositories.add_sync_lock_file') + - path = sync_lock_file_platform_repository_path(@platform, @repository) + = form_for @repository, :url => path, :html => { :class => :form, :method => :put } do |f| + .leftlist= t('layout.repositories.sync_lock_file_info') + .rightlist= f.submit label, :confirm => t('layout.confirm') + .both + .hr + + + +- if @platform.main? + = render "shared/members_table", + :remove_members_path => remove_members_platform_repository_path(@platform, @repository), + :remove_member_path => remove_member_platform_repository_path(@platform, @repository), + :add_member_path => add_member_platform_repository_path(@platform, @repository), + :members => @members, + :editable_object => @repository diff --git a/app/views/platforms/repositories/projects_list.html.haml b/app/views/platforms/repositories/projects_list.html.haml index 26de0610c..e744a5d90 100644 --- a/app/views/platforms/repositories/projects_list.html.haml +++ b/app/views/platforms/repositories/projects_list.html.haml @@ -1,6 +1,14 @@ = render 'submenu' = render 'sidebar' -%h3= raw "#{t("layout.repositories.add_project_to")}: #{link_to @repository.name, platform_repository_path(@platform, @repository)}" +%h3= raw "#{t("layout.repositories.add_projects_to")}: #{link_to @repository.name, platform_repository_path(@platform, @repository)}" + += form_for :repository, :url => add_project_platform_repository_path(@platform, @repository), :method => :put, :html => { :class => :form } do |f| + .leftlist= f.label :projects_list + .rightlist= f.text_area :projects_list + .both + .hr + .button_block + = submit_tag t('layout.add'), :data => {'disable-with' => t('layout.saving')} = render 'proj_list', :object => @projects diff --git a/app/views/platforms/repositories/remove_project.html.haml b/app/views/platforms/repositories/remove_project.html.haml new file mode 100644 index 000000000..1fc5b0ed1 --- /dev/null +++ b/app/views/platforms/repositories/remove_project.html.haml @@ -0,0 +1,12 @@ += render 'submenu' += render 'sidebar' + +%h3= raw "#{t("layout.repositories.remove_projects_from")}: #{link_to @repository.name, platform_repository_path(@platform, @repository)}" + += form_for :repository, :url => remove_project_platform_repository_path(@platform, @repository), :method => :delete, :html => { :class => :form } do |f| + .leftlist= f.label :projects_list + .rightlist= f.text_area :projects_list + .both + .hr + .button_block + = submit_tag t('layout.delete'), :data => {'disable-with' => t('layout.saving')} diff --git a/app/views/platforms/repositories/show.html.haml b/app/views/platforms/repositories/show.html.haml index 5bc491bfe..ddfba3750 100644 --- a/app/views/platforms/repositories/show.html.haml +++ b/app/views/platforms/repositories/show.html.haml @@ -16,5 +16,7 @@ %h3.fix= t("layout.projects.list_header") - if can? :add_project, @repository = link_to t("layout.projects.add"), add_project_platform_repository_path(@platform, @repository), :class => 'button' +- if can? :remove_project, @repository + = link_to t("layout.repositories.mass_delete"), remove_project_platform_repository_path(@platform, @repository), :class => 'button' = render 'proj_list' diff --git a/app/views/platforms/tokens/index.html.haml b/app/views/platforms/tokens/index.html.haml new file mode 100644 index 000000000..efbe92cfd --- /dev/null +++ b/app/views/platforms/tokens/index.html.haml @@ -0,0 +1,21 @@ +-set_meta_tags :title => [title_object(@platform), t('layout.tokens.header')] += render 'submenu' += render 'sidebar' += link_to t('layout.tokens.new'), new_platform_token_path(@platform), :class => 'button' + +%table#myTable.tablesorter.tokens{:cellspacing => "0", :cellpadding => "0"} + %thead + %tr + %th= t('activerecord.attributes.token.description') + %th.th2= t('activerecord.attributes.token.creator') + %th= t('activerecord.attributes.token.status') + %th + %tbody + - @tokens.each do |token| + %tr{:class => cycle('odd', 'even')} + %td= truncate token.description, :length => 50 + %td= link_to token.creator.try(:fullname), token.creator + %td= t("layout.tokens.statuses.#{token.status}") + %td= link_to t('layout.show'), platform_token_path(@platform, token) + += will_paginate @tokens diff --git a/app/views/platforms/tokens/new.html.haml b/app/views/platforms/tokens/new.html.haml new file mode 100644 index 000000000..c4d22153c --- /dev/null +++ b/app/views/platforms/tokens/new.html.haml @@ -0,0 +1,16 @@ +-set_meta_tags :title => [title_object(@platform), t('layout.tokens.new')] += render 'submenu' += render 'sidebar' + +%h3= t('layout.tokens.new') + += form_for @token, :url => platform_tokens_path(@platform), :method => :post, :html => { :class => :form } do |f| + .leftlist= f.label :description + .rightlist= f.text_area :description + + .both + .hr + .button_block + = submit_tag t('layout.save'), :data => {'disable-with' => t('layout.saving')} + %span.text_button_padding= t('layout.or') + = link_to t('layout.cancel'), platform_tokens_path(@platform), :class => 'button' diff --git a/app/views/platforms/tokens/show.html.haml b/app/views/platforms/tokens/show.html.haml new file mode 100644 index 000000000..59084b6c5 --- /dev/null +++ b/app/views/platforms/tokens/show.html.haml @@ -0,0 +1,41 @@ +-set_meta_tags :title => [title_object(@platform), t('layout.tokens.header')] += render 'submenu' += render 'sidebar' + +%h3.fix= "#{t('layout.tokens.about')} #{@platform.name}" + +%p= @token.description + +%table.tablesorter.unbordered + %tr + %td + %b= "#{t('activerecord.attributes.token.creator')}:" + %td= link_to @token.creator.try(:name), @token.creator + %tr + %td + %b= "#{t('activerecord.attributes.token.created_at')}:" + %td= @token.created_at + - if @token.updater + %tr + %td + %b= "#{t('activerecord.attributes.token.updater')}:" + %td= link_to @token.updater.try(:name), @token.updater if @token.updater + %tr + %td + %b= "#{t('activerecord.attributes.token.updated_at')}:" + %td= @token.updated_at + %tr + %td + %b= "#{t('activerecord.attributes.token.status')}:" + %td= t("layout.tokens.statuses.#{@token.status}") + %tr + %td + %b= "#{t('activerecord.attributes.token.authentication_token')}:" + %td= @token.authentication_token + +- if @token.active? + .buttons_block + = link_to t('layout.tokens.withdraw'), + withdraw_platform_token_path(@platform, @token), + :method => :post, :class => 'button', + :confirm => t('layout.tokens.withdraw_confirm') diff --git a/app/views/projects/base/_repo_block.html.haml b/app/views/projects/base/_repo_block.html.haml index 5f873fdf5..e641613dc 100644 --- a/app/views/projects/base/_repo_block.html.haml +++ b/app/views/projects/base/_repo_block.html.haml @@ -1,7 +1,7 @@ - act = action_name.to_sym; contr = controller_name.to_sym; treeish = project.default_head(params[:treeish]); branch = @branch.try(:name) || project.default_head -http_url = git_repo_url(project.name_with_owner) -ssh_url = git_ssh_repo_url(project.name_with_owner) -#description-top +#description-top{'ng-controller' => 'ProjectRepoBlockController', 'ng-init' => "init(#{project.repo.branches.count})"} -if @commit %ul.nav.zip %li#menu-archive.dropdown @@ -42,7 +42,7 @@ %li{:class => ('selected' if act == :index && contr == :commits )} = link_to t('project_menu.commits'), commits_path(project, treeish) %li{:class => ('selected' if act == :branches && contr == :trees )} - = link_to t('project_menu.branches', :count => project.repo.branches.count), branches_path(project, branch) + = link_to t('project_menu.branches', :count => '{{singleton.project.branches_count}}'), branch_path(project, branch) %li.tags{:class => ('selected' if act == :tags && contr == :trees )} = link_to t('project_menu.tags', :count => project.repo.tags.count), tags_path(project) .both diff --git a/app/views/projects/base/_sidebar.html.haml b/app/views/projects/base/_sidebar.html.haml index 1468d6b79..4b5429d7d 100644 --- a/app/views/projects/base/_sidebar.html.haml +++ b/app/views/projects/base/_sidebar.html.haml @@ -10,6 +10,8 @@ = link_to t("layout.projects.edit"), edit_project_path(@project) %li{:class => (act == :sections && contr == :projects) ? 'active' : ''} = link_to t("layout.projects.sections"), sections_project_path(@project) + %li{:class => (contr == :hooks) ? 'active' : ''} + = link_to t("layout.projects.hooks"), project_hooks_path(@project) - if can? :manage_collaborators, @project %li{:class => (act == :index && contr == :collaborators) ? 'active' : ''} = link_to t("layout.projects.edit_collaborators"), project_collaborators_path(@project) diff --git a/app/views/projects/build_lists/_build_list.html.haml b/app/views/projects/build_lists/_build_list.html.haml deleted file mode 100644 index 4452174c3..000000000 --- a/app/views/projects/build_lists/_build_list.html.haml +++ /dev/null @@ -1,23 +0,0 @@ -%tr{:id => "row#{build_list_counter}", :class => "#{build_list_status_color(build_list.status)}"} - %td= link_to (build_list.bs_id.present? ? build_list.bs_id : t("layout.build_lists.bs_id_not_set")), build_list - %td - = build_list.human_status - %br - - if BuildList::HUMAN_STATUSES[build_list.status].in? [:build_pending, :build_started, :build_publish] - %time.js-relative-date{:datetime => build_list.updated_at.strftime("%FT%T%:z"), :title => build_list.updated_at.strftime("%F %T")} - = build_list.updated_at.strftime "%F %T" - - if build_list.build_started? && ((build_list.project.average_build_time || 0) > 0) - \/ - %time - = build_list.project.formatted_average_build_time - - if build_list.project.present? - %td= link_to build_list.project.name_with_owner, build_list.project - %td= build_list_version_link(build_list) - - else - %td.centered{:colspan => 2}= t("layout.projects.unexisted_project") - %td= get_version_release build_list - -build_for=" (#{build_list.build_for_platform.name})" if build_list.build_for_platform && build_list.save_to_platform.personal? - %td= link_to "#{build_list.save_to_platform.name}/#{build_list.save_to_repository.name}#{build_for}", [build_list.save_to_platform, build_list.save_to_repository] - %td= build_list.arch.try(:name) || t("layout.arches.unexisted_arch") - %td= link_to build_list.user.try(:fullname), build_list.user - %td= build_list.updated_at.strftime('%d/%m/%Y') diff --git a/app/views/projects/build_lists/_build_lists_ajax.json.jbuilder b/app/views/projects/build_lists/_build_lists_ajax.json.jbuilder new file mode 100644 index 000000000..e56af59b0 --- /dev/null +++ b/app/views/projects/build_lists/_build_lists_ajax.json.jbuilder @@ -0,0 +1,21 @@ +build_lists = @build_lists.map do |build_list| + build_for = " (#{build_list.build_for_platform.name})" if build_list.build_for_platform && build_list.save_to_platform.personal? + [ + [link_to(build_list.id, build_list), + link_to(t('layout.clone'), new_project_build_list_path(@project, :build_list_id => build_list.id), + :id => 'clone_build_list') + ].join('
').html_safe, + build_list.human_status, + build_list_version_link(build_list), + get_version_release(build_list), + link_to("#{build_list.save_to_platform.name}/#{build_list.save_to_repository.name}#{build_for}", [build_list.save_to_platform, build_list.save_to_repository]), + build_list.arch.try(:name) || t('layout.arches.unexisted_arch'), + link_to(build_list.user.try(:fullname), build_list.user), + datetime_moment(build_list.updated_at).html_safe + ] +end + +json.sEcho params[:sEcho].to_i || -1 +json.iTotalRecords @total_build_lists +json.iTotalDisplayRecords @build_lists.count +json.aaData build_lists diff --git a/app/views/projects/build_lists/_extra.html.haml b/app/views/projects/build_lists/_extra.html.haml deleted file mode 100644 index 93afd9372..000000000 --- a/app/views/projects/build_lists/_extra.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -%tr - - if extra.is_a?(BuildList) - %td= link_to "#{extra.id} (#{extra.project.name} - #{extra.arch.name})", extra - - else - %td= link_to "#{extra.platform.name}/#{extra.name}", [extra.platform, extra] - %td.actions - - field = extra.is_a?(BuildList) ? 'extra_build_lists' : 'extra_repositories' - = hidden_field_tag "build_list[#{field}][]", extra.id - %span.delete   \ No newline at end of file diff --git a/app/views/projects/build_lists/_filter.html.haml b/app/views/projects/build_lists/_filter.html.haml index 97de8cb5a..b0fad9ee4 100644 --- a/app/views/projects/build_lists/_filter.html.haml +++ b/app/views/projects/build_lists/_filter.html.haml @@ -1,10 +1,11 @@ -html_options = {:class => 'sel80 medium input_cleanse', :tabindex => 2} .top.box .filter - = form_for :filter, :url => @action_url, :html => { :method => :post, :class => :form, :id => 'monitoring_filter' } do |f| + = form_for :filter, :html => {:class => :form, :id => 'monitoring_filter', 'ng-submit' => 'refresh(true)'}, :authenticity_token => false do |f| .column = render 'server_status' - = hidden_field_tag :servertime, Time.now.utc.to_i + = hidden_field_tag :owner_name, @project.try(:owner).try(:uname) + = hidden_field_tag :project_name, @project.try(:name) .reloader = label_tag :autoreload do = check_box_tag :autoreload, true, true @@ -13,50 +14,51 @@ - if current_user .bordered.nopadding %h3.medium= t("layout.build_lists.ownership.header") - =f.hidden_field :ownership + =f.hidden_field :ownership, :value => '{{params.filter.ownership}}' .btn-group - ['owned', (@project ? nil : 'related'), 'everything'].compact.each do |ownership| - -options = {:class => @filter.ownership == ownership ? 'active' : '', :value => ownership, :style => (@project ? 'width:50%;' : '')} + -options = {'ng-class' =>"{active: params.filter.ownership == '#{ownership}'}",:value => ownership, :style => (@project ? 'width:50%;' : '')} %button.btn.ownership{options}= t "layout.build_lists.ownership.#{ownership}" %h3.medium= t 'number_rows' - =hidden_field_tag :per_page, @per_page + =hidden_field_tag :per_page, '{{params.per_page}}' + =hidden_field_tag :page, '{{params.page}}' .btn-group -BuildList::Filter::PER_PAGE.each do |num| - %button.btn.per_page{:value => num, :class => @per_page == num ? 'active' : ''}=num + %button.btn.per_page{:value => num, 'ng-class' => "{active: params.per_page == #{num}}"}=num %h3.medium= t 'activerecord.attributes.build_list.status' .lineForm.aside - = f.select :status, BuildList::STATUSES.collect{|status| [BuildList.human_status(status), status]}, {:include_blank => true, :selected => @filter.status}, - html_options.merge(:id => 'status') + = f.select :status, BuildList::STATUSES.collect{|status| [BuildList.human_status(status), status]}, {:include_blank => true}, + html_options.merge(:id => 'status', 'ng-model' => 'params.filter.status') .both %br/ .column %h3.medium= t 'activerecord.models.platform' .lineForm.aside - = f.select :platform_id, Platform.main.collect{|pl| [pl.name, pl.id]}, {:include_blank => true, :selected => @filter.platform_id}, html_options.merge(:id => 'platform') + = f.select :platform_id, availables_main_platforms.collect{|pl| [pl.name, pl.id]}, {:include_blank => true}, html_options.merge(:id => 'platform', 'ng-model' => 'params.filter.platform_id') %h3.medium= t 'activerecord.attributes.build_list.arch' .lineForm.aside - = f.select :arch_id, Arch.recent.collect{|arch| [arch.name, arch.id]}, {:include_blank => true, :selected => @filter.arch_id}, html_options.merge(:id => 'architecture') + = f.select :arch_id, Arch.recent.collect{|arch| [arch.name, arch.id]}, {:include_blank => true}, html_options.merge(:id => 'architecture', 'ng-model' => 'params.filter.arch_id') %h3.medium= t 'activerecord.models.mass_build' .lineForm.aside - = f.select :mass_build_id, options_from_collection_for_select( MassBuild.all, :id, :name, @filter.mass_build_id ), {:include_blank => true, :selected => @filter.mass_build_id}, - html_options.merge(:id => 'mass_build') + = f.select :mass_build_id, mass_build_options, {:include_blank => true}, + html_options.merge(:id => 'mass_build', 'ng-model' => 'params.filter.mass_build_id') .column -[:updated_at_start, :updated_at_end].each do |attr| -class_name, message = attr == :updated_at_start ? %w[floatleft _on] : %w[floatright until] %div{:class => class_name} %h3.medium= t message - -date = @filter.send(attr) ? @filter.send(attr).strftime('%d/%m/%Y') : nil - =f.text_field attr, :readonly => "readonly", :size => 10, :class => 'mediumheight min input_cleanse', :value => date + =f.text_field attr, :readonly => "readonly", :size => 10, :class => 'mediumheight min input_cleanse', :value => "{{params.filter.#{attr}}}" =link_to image_tag('x.png', :alt => 'x', :class => 'semi'), "#filter_#{attr}", :id => 'updated_at_clear' .both - -%w[project_name bs_id].each do |filter| + -%w[project_name id].each do |filter| %h3.medium= t "layout.build_lists.#{filter}_search" - %input.mediumheight.input_cleanse{:id => "filter_#{filter}", :name => "filter[#{filter}]", :size => "30", :type => "text", :value => @filter.send(filter)} + %input.mediumheight.input_cleanse{:id => "filter_#{filter}", :name => "filter[#{filter}]", :size => "30", :type => "text", :value => "{{params.filter.#{filter}}}"} %br/ %br/ .butgrp - = f.submit t('layout.search.header') + = f.submit t('layout.search.header'), 'ng-hide' => 'isRequest' + = f.submit t('layout.processing'), :disabled => true, 'ng-show' => 'isRequest' = f.submit t('reset'), :id => 'filter_clear' -if @project and can?(:create, @project.build_lists.build) %input{:id => 'filter_new_build', :type => 'button', :onclick => "location.href='#{new_project_build_list_path(@project)}'", :value => t('layout.build_lists.new_header')} diff --git a/app/views/projects/build_lists/_include_repos.html.haml b/app/views/projects/build_lists/_include_repos.html.haml index 504b73cee..a2ce04b12 100644 --- a/app/views/projects/build_lists/_include_repos.html.haml +++ b/app/views/projects/build_lists/_include_repos.html.haml @@ -1,4 +1,9 @@ -- platform.repositories.each do |repo| +- Repository.custom_sort(platform.repositories).each do |repo| .both - = check_box_tag "build_list[include_repos][]", repo.id, repo.name == 'main' || @project.repositories.map(&:id).include?(repo.id), :id => "include_repos_#{repo.id}", :rep_name => repo.name + - if params[:build_list].try(:[], :include_repos).present? + - checked = (params[:build_list].try(:[], :include_repos).map(&:to_s) || []).include?(repo.id.to_s) + - else checked = false + = check_box_tag 'build_list[include_repos][]', repo.id, checked, + :disabled => ((params[:build_list].try(:[], :build_for_platform_id).to_i != platform.id) && @build_list.build_for_platform.try(:main?)), + :id => "include_repos_#{repo.id}", :rep_name => repo.name = label_tag "include_repos_#{repo.id}", repo.name diff --git a/app/views/projects/build_lists/_last_build_lists.html.haml b/app/views/projects/build_lists/_last_build_lists.html.haml new file mode 100644 index 000000000..256b034a5 --- /dev/null +++ b/app/views/projects/build_lists/_last_build_lists.html.haml @@ -0,0 +1,38 @@ +.all + %h3= t 'layout.build_lists.last_build_lists' + %h4= t 'layout.relations.filters' + .both + = check_box_tag :owner_filter_build_lists, true, (params[:owner_filter] || 'true') == 'true' + = label_tag t('layout.build_lists.only_my_build_lists') + .both + = check_box_tag :status_filter_build_lists, true, (params[:status_filter] || 'true') == 'true' + = label_tag t('layout.build_lists.failed_build_lists') + + - columns, default_column = [], {:type => 'html', :sortable => false, :searchable => false} + - 8.times { columns << default_column } + = raw datatable(columns, {:sort_by => "[7, 'asc']", :processing => t('layout.processing'), :search => 'false', + :pagination_labels => {:previous => t('datatables.previous_label'), :next => t('datatables.next_label')}, + :empty_label => t('datatables.empty_label'), + :info_label => t('datatables.info_label'), + :info_empty_label => t('datatables.info_empty_label'), + :filtered_label => t('datatables.filtered_label'), + :table_dom_id => 'datatable', + :auto_width => 'false', + :ajax_source => "#{url_for :controller => 'projects/build_lists', :action => :list}", + :additional_data => {:owner_filter => "' + $('#owner_filter_build_lists:checked').val() + '", + :status_filter => "' + $('#status_filter_build_lists:checked').val() + '"} }) + + %table#datatable.tablesorter.list-users{:cellspacing => 0, :cellpadding => 0} + %thead + %tr + %th.th1= t('activerecord.attributes.build_list.id') + %th.th2= t('activerecord.attributes.build_list.status') + %th.th2= t('diff') + %th.th2= t('activerecord.attributes.build_list.project_version') + %th.th2= t('activerecord.attributes.build_list.save_to_repository') + %th.th1= t('activerecord.attributes.build_list.arch_short') + %th.th2= t('activerecord.attributes.build_list.user') + %th.th1= t('activerecord.attributes.build_list.updated_at') + %tbody + %br + diff --git a/app/views/projects/build_lists/_new_form.html.haml b/app/views/projects/build_lists/_new_form.html.haml new file mode 100644 index 000000000..bd8d47fe9 --- /dev/null +++ b/app/views/projects/build_lists/_new_form.html.haml @@ -0,0 +1,62 @@ += form_for [@project, @build_list], :html => { :class => :form, :method => :post } do |f| + %section.left + %h3= t("activerecord.attributes.build_list.build_for_platform") + .all_platforms + - availables_main_platforms.each do |pl| + .both + %div{:id => "build_for_pl_#{pl.id}", :class => 'build_for_pl'}= pl.name + .offset25= render 'include_repos', :platform => pl + %section.right + %h3= t('activerecord.attributes.build_list.save_to_repository') + - selected = params[:build_list].try(:[], :save_to_repository_id) ? {:selected => params[:build_list][:save_to_repository_id]} : {} + .lineForm= f.select :save_to_repository_id, save_to_repositories(project), selected + %h3= t("activerecord.attributes.build_list.project_version") + .lineForm= f.select :project_version, versions_for_group_select(project), :selected => params[:build_list].try(:[], :project_version) || project.default_branch + %h3= t("activerecord.attributes.build_list.arch") + - Arch.recent.each do |arch| + .both + - checked = (params[:arches]||[]).include?(arch.id.to_s) || (params[:arches].blank? && controller.action_name == 'new' && Arch::DEFAULT.include?(arch.name)) + = check_box_tag "arches[]", arch.id, checked, :id => "arches_#{arch.id}" + = label_tag "arches_#{arch.id}", arch.name + %h3= t('activerecord.attributes.build_list.update_type') + - selected = params[:build_list].try(:[], :update_type) ? {:selected => params[:build_list][:update_type]} : {} + .lineForm= f.select :update_type, BuildList::UPDATE_TYPES, selected + + = render 'shared/autocomplete_form', + :field => :extra_repositories, + :field_class => Repository, + :placeholder => 'uxteam_personal', + :subject => @build_list, + :autocomplete_path => autocomplete_extra_repositories_autocompletes_path, + :default_values => project.repositories.select{ |r| r.platform.personal? } + = render 'shared/autocomplete_form', + :field => :extra_build_lists, + :field_class => BuildList, + :placeholder => '1000000', + :subject => build_list, + :autocomplete_path => autocomplete_extra_build_list_autocompletes_path + + %h3= t('activerecord.attributes.build_list.extra_params.label') + - BuildList::EXTRA_PARAMS.each do |field| + - text = params[:build_list].try(:[], :extra_params).try(:[], field) + = text_field_tag "build_list[extra_params][#{field}]", text, :placeholder => t("activerecord.attributes.build_list.extra_params.#{field}"), :class => 'full-size' + .both + + %h3= t("activerecord.attributes.build_list.preferences") + - [:auto_publish, :auto_create_container, :include_testing_subrepository].each do |kind| + .both + - checked, field = params[:build_list].try(:[], kind), "build_list[#{kind}]" + = hidden_field_tag field, false, :id => nil + = check_box_tag field, true, checked + = f.label kind + .both + - selected = params[:build_list].try(:[], :external_nodes) ? {:selected => params[:build_list][:external_nodes]} : {} + = f.select :external_nodes, external_nodes, selected.merge(:include_blank => true) + = f.label :external_nodes + .both + + %br + = hidden_field_tag :from_build_list_id, params[:build_list_id] if params[:build_list_id].present? + = f.submit t('layout.projects.build_button'), :data => {'disable-with' => t('layout.processing')} +.both + diff --git a/app/views/projects/build_lists/_platform_build_list.html.haml b/app/views/projects/build_lists/_platform_build_list.html.haml index 66e2c76a7..d21b445f7 100644 --- a/app/views/projects/build_lists/_platform_build_list.html.haml +++ b/app/views/projects/build_lists/_platform_build_list.html.haml @@ -1,5 +1,5 @@ %tr{:id => "row#{platform_build_list_counter}"} - %td= link_to (platform_build_list.bs_id.present? ? platform_build_list.bs_id : t("layout.build_lists.bs_id_not_set")), platform_build_list + %td= link_to platform_build_list.id, platform_build_list %td= platform_build_list.human_status %td= link_to platform_build_list.project.name, platform_build_list.project %td= platform_build_list.updated_at diff --git a/app/views/projects/build_lists/_recreate_build_list.html.haml b/app/views/projects/build_lists/_recreate_build_list.html.haml new file mode 100644 index 000000000..d6ce8ff0c --- /dev/null +++ b/app/views/projects/build_lists/_recreate_build_list.html.haml @@ -0,0 +1,5 @@ += form_for [build_list.project, build_list.project.build_lists.new], :html => { :class => :form, :method => :post } do |f| + = hidden_field_tag :build_list_id, build_list.id + = f.hidden_field :project_id + = f.submit t('layout.build_lists.recreate_build_list'), :data => {'disable-with' => t('layout.processing')} +.both \ No newline at end of file diff --git a/app/views/projects/build_lists/_server_status.html.haml b/app/views/projects/build_lists/_server_status.html.haml index 8a86a33e6..e1d56b0ab 100644 --- a/app/views/projects/build_lists/_server_status.html.haml +++ b/app/views/projects/build_lists/_server_status.html.haml @@ -1,13 +1,43 @@ -- queues ||= [:rpm, :publish] .bordered.nopadding %h3.medium= t('layout.build_lists.build_server_status.header') - - queues.each do |queue| + + .table + .lefter= t("layout.build_lists.build_server_status.rpm_workers") + .both + .table + .lefter= t("layout.build_lists.build_server_status.amount") + .both + .table + .lefter= t("layout.build_lists.build_server_status.abf") + .righter {{server_status.rpm.workers}} + .both + .table + .lefter= t("layout.build_lists.build_server_status.custom") + .righter {{server_status.rpm.other_workers}} + .both + .table + .lefter= t("layout.build_lists.build_server_status.tasks") + .both + - [['custom', :default_tasks], ['mass_build_tasks', :low_tasks], ['build_tasks', :build_tasks]].each do |label, metric| .table - .lefter= t("layout.build_lists.build_server_status.#{queue}_workers") + .lefter= t("layout.build_lists.build_server_status.#{label}") + .righter= "{{server_status.rpm.#{metric}}}" .both - - [:count, :tasks, :build_tasks].each do |metric| - .table - .lefter= t("layout.build_lists.build_server_status.#{metric}") - .righter= @build_server_status[queue][metric] - .both - %br + %br + + .table + .lefter= t("layout.build_lists.build_server_status.publish_workers") + .both + .table + .lefter= t("layout.build_lists.build_server_status.amount") + .righter= "{{server_status.publish.workers}}" + .both + - [:tasks, :build_tasks].each do |metric| + .table + .lefter= t("layout.build_lists.build_server_status.#{metric}") + .righter= "{{server_status.publish.#{metric}}}" + .both + %br + + + diff --git a/app/views/projects/build_lists/index.html.haml b/app/views/projects/build_lists/index.html.haml index 72d1f8154..e24b6b7ea 100644 --- a/app/views/projects/build_lists/index.html.haml +++ b/app/views/projects/build_lists/index.html.haml @@ -1,73 +1,73 @@ -set_meta_tags :title => t('.title') -= render 'filter' +%div{'ng-controller' => 'BuildListsController'} -%table.tablesorter{:cellpadding => "0", :cellspacing => "0"} - %thead - %tr - %th.lpadding16= t("activerecord.attributes.build_list.bs_id") - %th.lpadding16= t("activerecord.attributes.build_list.status") - %th.lpadding16= t("activerecord.attributes.build_list.project") - %th.lpadding16= t("diff") - %th.lpadding16= t("activerecord.attributes.build_list.project_version") - %th.lpadding16= t("activerecord.attributes.build_list.save_to_repository") - %th.lpadding6= t("activerecord.attributes.build_list.arch_short") - %th.lpadding16= t("activerecord.attributes.build_list.user") - %th.lpadding6= t("activerecord.attributes.build_list.updated_at") - %tbody= render :partial => 'projects/build_lists/build_list', :collection => @build_lists -.both + = render 'filter' -:javascript - $(function(){ - // from jQuery.timeago - var parseIso8601 = function(iso8601) { - var s = $.trim(iso8601); - s = s.replace(/\.\d+/,""); // remove milliseconds - s = s.replace(/-/,"/").replace(/-/,"/"); - s = s.replace(/T/," ").replace(/Z/," UTC"); - s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400 - return new Date(s); - } - var strTimeBetween = function(dt, dt1) { + %table.tablesorter{:cellpadding => "0", :cellspacing => "0"} + %thead + %tr + %th.lpadding16= t("activerecord.attributes.build_list.id") + %th.lpadding16= t("activerecord.attributes.build_list.status") + %th.lpadding16= t("activerecord.attributes.build_list.project") + %th.lpadding16= t("diff") + %th.lpadding16= t("activerecord.attributes.build_list.project_version") + %th.lpadding16= t("activerecord.attributes.build_list.save_to_repository") + %th.lpadding6= t("activerecord.attributes.build_list.arch_short") + %th.lpadding16= t("activerecord.attributes.build_list.user") + %th.lpadding6= t("activerecord.attributes.build_list.updated_at") + %tbody + %tr{'ng-repeat' => 'bl in build_lists', :class => '{{bl.status_color}}', :id => 'build-list-{{bl.id}}', 'ng-class' => "{'group-start': !bl.relatedHidden, 'group-end': bl.lastRelated}", 'ng-show' => 'bl.show'} - var formatNumber = function(num) { - return (num < 10) ? "0%d".replace("%d", num) : num - } + / id + %td.build-list-statuses + %a.expand{'ng-show' => 'bl.hasRelated'} + %span.icon-chevron-down{'ng-show' => 'bl.relatedHidden', 'ng-click' => 'showRelated(bl)'} + %span.icon-chevron-up{'ng-hide' => 'bl.relatedHidden', 'ng-click' => 'hideRelated(bl)'} + %a{'ng-href' => '{{bl.url}}' } {{bl.id}} + %div{'ng-show' => 'bl.hasRelated'} + .status{'ng-repeat' => 'related in bl.related', :class => '{{related.status_color}}'}   - var seconds = Math.abs(dt1 - dt) / 1000; - var minutes = seconds / 60; - var hours = minutes / 60; - minutes = Math.round(minutes % 60); - hours = Math.floor(hours); + / status + %td + {{bl.human_status | i18n}} + %br + %time{'ng-show' => 'bl.duration'} + {{bl.duration}} + %time{'ng-show' => 'bl.average_build_time'} + \/{{bl.average_build_time}} - return "%d:%d".replace("%d", formatNumber(hours)) - .replace("%d", formatNumber(minutes)); - } - // TODO Very, very ugly method. - var now = $('#servertime').val() * 1000; - $("time.js-relative-date").each(function() { - var time = parseIso8601($(this).attr('datetime')).getTime(); - $(this).text(strTimeBetween(time, now)); - }); + / project + %td.centered{'ng-hide' => 'bl.project', :colspan => 2} + = t('layout.projects.unexisted_project') + %td{'ng-show' => 'bl.project'} + %a{'ng-href' => '{{bl.project.url}}' } {{bl.project.name_with_owner}} - var t = null; + / diff + %td + %a{'ng-href' => '{{bl.version_link_url}}', 'ng-show' => 'bl.project'} + {{bl.version_link_text}} - var reloadChange = function() { - if ($(this).is(':checked')) { - //reload page every 1 minute - t = setTimeout(function() { - window.location.reload(); - }, 60000); - } else { - clearTimeout(t); - } - } + / project_version + %td {{bl.version_release}} - $('#autoreload').on('change', reloadChange) - .trigger('change'); + / save_to_repository + %td + %a{'ng-href' => '{{bl.save_to_repository_url}}' } {{bl.save_to_repository_name}} - }); + / arch_short + %td{'ng-show' => 'bl.arch'} {{bl.arch.name}} + %td{'ng-hide' => 'bl.arch'}= t('layout.arches.unexisted_arch') -= will_paginate @bls + / user + %td + %a{'ng-href' => '{{bl.user.url}}' } {{bl.user.fullname}} + + / updated_at + %td{'am-time-ago' => 'bl.updated_at', :title => '{{bl.updated_at_utc}}'} + + .both + + = render 'shared/angularjs_will_paginate' = render @project ? 'projects/base/submenu' : 'projects/build_lists/submenu' diff --git a/app/views/projects/build_lists/index.json.jbuilder b/app/views/projects/build_lists/index.json.jbuilder new file mode 100644 index 000000000..42d3d4fe9 --- /dev/null +++ b/app/views/projects/build_lists/index.json.jbuilder @@ -0,0 +1,44 @@ +now = Time.now.utc +users, projects, platforms, repositories = [], [], [], [] +json.build_lists @build_lists do |build_list| + json.(build_list, :id, :status, :project_id, :project_version, :save_to_platform_id, :save_to_repository_id, :user_id, :project_id, :build_for_platform_id, :arch_id, :group_id) + json.commit_hash build_list.commit_hash.first(5) + json.last_published_commit_hash build_list.last_published_commit_hash.first(5) if build_list.last_published_commit_hash + + if BuildList::HUMAN_STATUSES[build_list.status].in? [:build_pending, :build_started, :build_publish] + json.duration Time.diff(now, build_list.updated_at, '%h:%m')[:diff] + json.average_build_time build_list.formatted_average_build_time if build_list.build_started? && (build_list.average_build_time > 0) + end + + json.version_release get_version_release(build_list) + json.updated_at build_list.updated_at + json.updated_at_utc build_list.updated_at.strftime('%Y-%m-%d %H:%M:%S UTC') + users |= [build_list.user] + projects |= [build_list.project] + platforms |= [build_list.build_for_platform, build_list.save_to_platform] + repositories |= [build_list.save_to_repository] +end + +json.dictionary do + json.users users.compact do |user| + json.(user, :id, :uname, :fullname) + end + json.projects projects.compact do |project| + json.(project, :id, :name) + json.owner project.name_with_owner.gsub(/\/.*/, '') + end + json.platforms platforms.compact do |platform| + json.(platform, :id, :name) + json.personal platform.personal? + end + json.repositories repositories.compact do |repository| + json.(repository, :id, :name) + end + json.arches Arch.all do |arch| + json.(arch, :id, :name) + end +end + +json.server_status @build_server_status +json.filter @filter.try(:options) +json.pages angularjs_will_paginate(@bls) diff --git a/app/views/projects/build_lists/new.html.haml b/app/views/projects/build_lists/new.html.haml index 94b5aa3c3..974f0e091 100644 --- a/app/views/projects/build_lists/new.html.haml +++ b/app/views/projects/build_lists/new.html.haml @@ -1,61 +1,5 @@ -set_meta_tags :title => [title_object(@project), t('layout.build_lists.new_header')] -= form_for [@project, @build_list], :html => { :class => :form, :method => :post } do |f| - %section.left - %h3= t("activerecord.attributes.build_list.build_for_platform") - .all_platforms - - Platform.main.each do |pl| - - if pl.repository_ids.size > 0 - .both - =# check_box_tag "build_for_platforms[]", pl.id, (params[:build_for_platforms]||[]).include?(pl.id.to_s), :class => 'build_bpl_ids', :id => "bpls_#{pl.id}" - =# label_tag "bpls_#{pl.id}", pl.name - %div{:id => "build_for_pl_#{pl.id}", :class => 'build_for_pl'}= pl.name - .offset25= render 'include_repos', :platform => pl - %section.right - %h3= t("activerecord.attributes.build_list.save_to_repository") - .lineForm= f.select :save_to_repository_id, @project.repositories.collect{|r| ["#{r.platform.name}/#{r.name}", r.id, {:publish_without_qa => r.publish_without_qa? ? 1 : 0, :platform_id => r.platform.id}]} - %h3= t("activerecord.attributes.build_list.project_version") - .lineForm= f.select :project_version, versions_for_group_select(@project), :selected => params[:build_list].try(:fetch, :project_version) || @project.default_branch - %h3= t("activerecord.attributes.build_list.arch") - - Arch.recent.each do |arch| - .both - = check_box_tag "arches[]", arch.id, (params[:arches]||[]).include?(arch.id.to_s) || controller.action_name == 'new', :id => "arches_#{arch.id}" - = label_tag "arches_#{arch.id}", arch.name - %h3= t("activerecord.attributes.build_list.update_type") - .lineForm= f.select :update_type, BuildList::UPDATE_TYPES - - #extra-repos-and-build-lists - %h3= t("activerecord.attributes.build_list.extra_repos") - %span.icon-question-sign - %div#extra-repos-and-build-lists-dialog{:title => t("activerecord.attributes.build_list.extra_repos")} - %b= t('helpers.extra_repos_and_build_lists.header') - %br - %br - %b= t('helpers.extra_repos_and_build_lists.build_lists.header') - - (1..4).each do |index| - %p= t("helpers.extra_repos_and_build_lists.build_lists.message#{index}").html_safe - %br - %b= t('helpers.extra_repos_and_build_lists.repos.header') - %p= t('helpers.extra_repos_and_build_lists.repos.message1') - %br - %b= t('helpers.extra_repos_and_build_lists.repos.header2') - %p= t('helpers.extra_repos_and_build_lists.repos.message2') - %p= t('helpers.extra_repos_and_build_lists.repos.message3').html_safe - - = autocomplete_field_tag 'extra_repos', nil, autocomplete_to_extra_repos_and_builds_build_lists_path, :id_element => '#extra_repo_field' - = hidden_field_tag 'extra_repo', nil, :id => 'extra_repo_field' - = submit_tag t("layout.add"), :class => 'button', :id => 'add' - - %table.tablesorter{:cellpadding => "0", :cellspacing => "0"} - %tbody - = render :partial => 'extra', :collection => Repository.where(id: @build_list.extra_repositories) - = render :partial => 'extra', :collection => BuildList.where(id: @build_list.extra_build_lists) - - %h3= t("activerecord.attributes.build_list.preferences") - - [:auto_publish, :auto_create_container, :use_save_to_repository].each do |kind| - .both - = f.check_box kind - = f.label kind - %br - = f.submit t("layout.projects.build_button") - -= render 'projects/base/submenu' \ No newline at end of file +#new_form= render 'new_form', :project => @project, :build_list => @build_list +%br += render 'last_build_lists', :project => @project += render 'projects/base/submenu' diff --git a/app/views/projects/build_lists/show.html.haml b/app/views/projects/build_lists/show.html.haml index 94f8bcdcb..5cbc139ab 100644 --- a/app/views/projects/build_lists/show.html.haml +++ b/app/views/projects/build_lists/show.html.haml @@ -1,219 +1,247 @@ -set_meta_tags :title => [title_object(@build_list.project), t('activerecord.models.build_list')] -.notify.blue - %div{:class => build_list_status_color(@build_list.status)} - %p= @build_list.human_status - %p= @build_list.updated_at - .both -= form_for @build_list do |f| - %h3= t("layout.build_lists.main_data") - .leftlist= t("activerecord.attributes.build_list.container_path") - .rightlist - - if @build_list.build_published? - = raw "%s %s" % [t("layout.build_lists.container_published"), - link_to("#{@build_list.save_to_platform.name}/#{@build_list.save_to_repository.name}", - [@build_list.save_to_platform, @build_list.save_to_repository])] - - if @build_list.container_published? - = link_to container_url, container_url - - elsif @build_list.container_publish? +.build-list{'ng-controller' => 'BuildListController'} + .notify.blue + %div{:class => '{{build_list.status_color}}'} + %p {{build_list.human_status | i18n}} + %p{'am-time-ago' => 'build_list.updated_at', :title => '{{build_list.updated_at_utc}}'} + .both + =form_for @build_list, :url => publish_build_list_path(@build_list), :html => { :class => :form } do |f| + %h3= t("layout.build_lists.main_data") + .leftlist= t("activerecord.attributes.build_list.container_path") + .rightlist{'ng-show' => "build_list.container_status == #{BuildList::BUILD_PUBLISHED}"} + - url = container_url + = link_to url, url + .rightlist{'ng-show' => "build_list.container_status == #{BuildList::BUILD_PUBLISH}"} = t("layout.build_lists.creating") - .both + .both - .leftlist= t("activerecord.attributes.build_list.bs_id") - .rightlist= @build_list.bs_id.present? ? @build_list.bs_id : t("layout.build_lists.bs_id_not_set") - .both - .leftlist= t("activerecord.attributes.build_list.user") - .rightlist - = link_to @build_list.user.try(:fullname), @build_list.user - .both - .leftlist= t("activerecord.attributes.build_list.publisher") - .rightlist - = link_to @build_list.publisher.try(:fullname), @build_list.publisher if @build_list.publisher - .both - .leftlist= t("activerecord.attributes.build_list.build_for_platform") - .rightlist - - bfp = @build_list.build_for_platform - - if bfp.present? - = link_to(bfp.name, bfp) - - else - = t("layout.build_lists.platform_deleted") - .both - .leftlist= t("activerecord.attributes.build_list.save_to_repository") - .rightlist - = link_to "#{@build_list.save_to_platform.name}/#{@build_list.save_to_repository.name}", [@build_list.save_to_platform, @build_list.save_to_repository] - .both - .leftlist= t("activerecord.attributes.build_list.use_save_to_repository") - .rightlist= t("layout.#{@build_list.use_save_to_repository?}_") - .both - .leftlist= t("activerecord.attributes.build_list.include_repos") - .rightlist= (@build_list.include_repos||[]).map{|r| Repository.find(r).name}.join(', ') - .both - .leftlist= t("activerecord.attributes.build_list.update_type") - .rightlist - - if can?(:publish, @build_list) - = f.select :update_type, options_for_select(build_list_classified_update_types, @build_list.update_type) - - else - = @build_list.update_type - .both - .leftlist= t("activerecord.attributes.build_list.auto_publish") - .rightlist= t("layout.#{@build_list.auto_publish}_") - .both - .leftlist= t("activerecord.attributes.build_list.auto_create_container") - .rightlist= t("layout.#{@build_list.auto_create_container?}_") - .both - .leftlist= t("diff") - .rightlist= build_list_version_link(@build_list, true) - .both - .leftlist= t("activerecord.attributes.build_list.arch") - .rightlist= @build_list.arch.name - .both - .leftlist= t("activerecord.attributes.build_list.updated_at") - .rightlist= @build_list.updated_at - .both - .leftlist= t("activerecord.attributes.build_list.is_circle") - .rightlist= t("layout.#{@build_list.is_circle?}_") - .both - .leftlist= t("activerecord.attributes.build_list.new_core") - .rightlist= t("layout.#{@build_list.new_core?}_") - .both - - - if @build_list.extra_build_lists.present? || @build_list.extra_repositories.present? - .leftlist= t("activerecord.attributes.build_list.extra_repos") + .leftlist= t("activerecord.attributes.build_list.id") .rightlist - - Repository.where(:id => @build_list.extra_repositories).each do |repo| - %p= link_to "#{repo.platform.name}/#{repo.name}", [repo.platform, repo] - - BuildList.where(:id => @build_list.extra_build_lists).each do |bl| - %p= link_to "#{bl.id} (#{bl.project.name} - #{bl.arch.name})", bl + = @build_list.id + = hidden_field_tag :build_list_id, @build_list.id .both - - - if @build_list.mass_build_id.present? - .leftlist= t("activerecord.attributes.mass_build_id") - .rightlist= link_to @build_list.mass_build.name, platform_mass_builds_path(@build_list.save_to_platform) - .both - - - - if @build_list.advisory.present? - .leftlist= t("layout.build_lists.attached_advisory") - .rightlist= link_to @build_list.advisory.advisory_id, advisory_path(@build_list.advisory) - .both - - if !@build_list.in_work? && @build_list.started_at - %br - .leftlist - .rightlist= @build_list.human_duration - .both - - if @build_list.in_work? - %br - .leftlist + .leftlist= t("activerecord.attributes.build_list.user") .rightlist - = "#{@build_list.human_current_duration} / #{@build_list.project.human_average_build_time}" + = link_to @build_list.user.try(:fullname), @build_list.user .both - - - if @build_list.can_cancel? && can?(:cancel, @build_list) - = link_to t("layout.build_lists.cancel"), cancel_build_list_path(@build_list), - :method => :put, :confirm => t("layout.confirm"), :class => 'button' - - - if @build_list.save_to_platform.released && @build_list.advisory.nil? && can?(:publish, @build_list) - #advisory_block - .leftlist= label_tag :attach_advisory, t("layout.build_lists.attached_advisory") + .leftlist= t("activerecord.attributes.build_list.publisher") + .rightlist{'ng-show' => 'build_list.publisher'} + %a{'ng-href' => '{{build_list.publisher.path}}' } {{build_list.publisher.fullname}} + .both + .leftlist= t("activerecord.attributes.build_list.build_for_platform") + .rightlist + - if bfp = @build_list.build_for_platform + = link_to(bfp.name, bfp) + - else + = t("layout.build_lists.platform_deleted") + .both + .leftlist= t("activerecord.attributes.build_list.save_to_repository") + .rightlist + = link_to "#{@build_list.save_to_platform.name}/#{@build_list.save_to_repository.name}", [@build_list.save_to_platform, @build_list.save_to_repository] + .both + .leftlist= t("activerecord.attributes.build_list.include_testing_subrepository") + .rightlist= t("layout.#{@build_list.include_testing_subrepository?}_") + .both + .leftlist= t("activerecord.attributes.build_list.include_repos") + .rightlist= (@build_list.include_repos||[]).map{|r| Repository.where(:id => r).first.try(:name)}.join(', ') + .both + .leftlist= t("activerecord.attributes.build_list.update_type") + .rightlist + = f.select :update_type, options_for_select(build_list_classified_update_types, + @build_list.update_type), {}, 'ng-model' => 'build_list.update_type', + 'ng-change' => 'updateTypeChanged()', 'ng-show' => 'build_list.can_publish' + %div{'ng-hide' => 'build_list.can_publish'}= @build_list.update_type + .both + .leftlist= t("activerecord.attributes.build_list.auto_publish") + .rightlist= t("layout.#{@build_list.auto_publish}_") + .both + .leftlist= t("activerecord.attributes.build_list.auto_create_container") + .rightlist= t("layout.#{@build_list.auto_create_container?}_") + .both + .leftlist= t("activerecord.attributes.build_list.project_version") + .rightlist= link_to @build_list.project_version, tree_path(@build_list.project, @build_list.project_version) + .both + .leftlist= t("diff") + .rightlist= build_list_version_link(@build_list, true) + .both + .leftlist= t("activerecord.attributes.build_list.arch") + .rightlist= @build_list.arch.name + .both + .leftlist= t("activerecord.attributes.build_list.updated_at") + .rightlist {{build_list.updated_at_utc}} + .both + - if @build_list.external_nodes.present? + .leftlist= t("activerecord.attributes.build_list.external_nodes") + .rightlist= I18n.t("layout.build_lists.external_nodes.#{@build_list.external_nodes}") + .both + .leftlist= t("activerecord.attributes.build_list.builder") + .rightlist{'ng-show' => 'build_list.builder'} + %a{'ng-href' => '{{build_list.builder.path}}' } {{build_list.builder.fullname}} + .both + .leftlist= t("activerecord.attributes.build_list.is_circle") + .rightlist= t("layout.#{@build_list.is_circle?}_") + .both + .leftlist= t("activerecord.attributes.build_list.new_core") + .rightlist= t("layout.#{@build_list.new_core?}_") + .both + - if @build_list.extra_build_lists.present? || @build_list.extra_repositories.present? + .leftlist= t("activerecord.attributes.build_list.extra_repositories") .rightlist - = select_tag :attach_advisory, advisories_select_options(@advisories) - %p.hint_text= t("layout.advisories.publication_info", :update_types => BuildList::RELEASE_UPDATE_TYPES.join(', ')) + - Repository.where(:id => @build_list.extra_repositories).each do |repo| + %p= link_to "#{repo.platform.name}/#{repo.name}", [repo.platform, repo] + - BuildList.where(:id => @build_list.extra_build_lists).each do |bl| + %p= link_to "#{bl.id} (#{bl.project.name} - #{bl.arch.name})", bl .both - #advisory_search_block - %h3= t("layout.advisories.search_by_id") - .leftlist= label_tag :advisory_search, t("layout.advisories.search_hint") + - if @build_list.extra_params.present? + .leftlist + %h4.nomargin= t('activerecord.attributes.build_list.extra_params.label') + .rightlist + .both + - @build_list.extra_params.each do |k, v| + .leftlist= t("activerecord.attributes.build_list.extra_params.#{k}") + .rightlist= v + .both + + + - if @build_list.mass_build_id.present? + .leftlist= t("activerecord.attributes.mass_build_id") + .rightlist= link_to @build_list.mass_build.name, platform_mass_builds_path(@build_list.save_to_platform) + .both + + %div{'ng-show' => 'build_list.advisory'} + .leftlist= t("layout.build_lists.attached_advisory") + .rightlist + %a{'ng-href' => '{{build_list.advisory.path}}' } + {{build_list.advisory.advisory_id}} + .both + + %div{'ng-show' => 'build_list.human_duration'} + %br + .leftlist + .rightlist {{build_list.human_duration }} + .both + + - if @build_list.save_to_platform.released + #advisory_block{'ng-show' => 'build_list.can_publish && !build_list.advisory'} + .leftlist= label_tag :attach_advisory, t("layout.build_lists.attached_advisory") .rightlist - %input#advisory_search{:type => 'text'} - %p.hint_text= t("layout.advisories.advisory_id_info", :advisory_format => advisory_id_for_hint) - .both - - %w(advisory_not_found server_error continue_input).each do |el| - .info{:class => el} - %p= t("layout.advisories.banners.#{el}") - - #new_advisory_form - = f.fields_for @build_list.build_advisory do |f| - = render :partial => 'advisories/form', :locals => {:f => f} - - #advisory_preview - %h3= t("activerecord.models.advisory") << ' ' - - .leftlist= t("activerecord.attributes.advisory.description") - .rightlist.descr   + = select_tag :attach_advisory, advisories_select_options(@advisories), 'ng-model' => 'attach_advisory', 'ng-change' => 'attachAdvisoryChanged()' + %p.hint_text= t("layout.advisories.publication_info", :update_types => BuildList::RELEASE_UPDATE_TYPES.join(', ')) .both - .leftlist= t("activerecord.attributes.advisory.references") - .rightlist.refs   - .both - :javascript - $(function() { - var r = new Rosa.Routers.BuildListsAdvisoriesRouter(); - }); + #advisory_search_block{'ng-show' => 'attach_advisory != "no" && attach_advisory != "new"'} + %h3= t("layout.advisories.search_by_id") + .leftlist= label_tag :advisory_search, t("layout.advisories.search_hint") + .rightlist + %input#advisory_search{:type => 'text', 'ng-model' => 'term', 'ng-keyup' => 'search()'} + %p.hint_text= t("layout.advisories.advisory_id_info", :advisory_format => advisory_id_for_hint) + .both + - I18n.t('layout.advisories.banners').keys.each do |key| + .info{:class => key, 'ng-show' => "search_status == '#{key}'"} + %p= t("layout.advisories.banners.#{key}") - - if @build_list.build_started? - = render 'shared/log', { :build_started => true, :get_log_path => log_build_list_path(@build_list) } + #new_advisory_form{'ng-show' => 'attach_advisory == "new"'} + = f.fields_for @build_list.build_advisory do |f| + = render :partial => 'advisories/form', :locals => {:f => f} - - unless @build_list.extra_build_lists_published? - .flash_notify - .alert.alert-error= t('layout.build_lists.publish_with_extra_fail') - .both + #advisory_preview{'ng-show' => 'attach_advisory != "no" && attach_advisory != "new"'} + %h3= t('activerecord.models.advisory') << ' {{advisory.advisory_id}}' - - if can?(:publish, @build_list) - - if @build_list.build_published? - = submit_tag t("layout.publish_again"), :confirm => t("layout.publish_again_warning"), :name => 'publish' - - elsif can_publish_in_future?(@build_list) && @build_list.extra_build_lists_published? - - confirm = @build_list.tests_failed? ? t('layout.build_lists.tests_failed') : t('layout.confirm') - = submit_tag t("layout.publish"), :confirm => confirm, :name => 'publish' - - if @build_list.can_reject_publish? && can?(:reject_publish, @build_list) - = submit_tag t("layout.reject_publish"), :confirm => t("layout.confirm"), :name => 'reject_publish' - - if @build_list.can_create_container? && can?(:create_container, @build_list) - = link_to t("layout.build_lists.create_container"), create_container_build_list_path(@build_list), - :method => :put, :confirm => t("layout.confirm"), :class => 'button' + .leftlist= t('activerecord.attributes.advisory.description') + .rightlist.descr {{advisory.description}} + .both -.hr -%h3= t("layout.build_lists.items_header") -- if @item_groups.blank? - %h4.nomargin= t("layout.build_lists.no_items_data") -- @item_groups.each_with_index do |group, level| - - group.each do |item| - %h4.nomargin= "#{item.name} ##{level}" + .leftlist= t('activerecord.attributes.advisory.references') + .rightlist.refs {{advisory.references}} + .both + + %div{'ng-hide' => 'build_list.extra_build_lists_published'} + .flash_notify + .alert.alert-error= t('layout.build_lists.publish_with_extra_fail') + .both + + - if can?(:cancel, @build_list) + = link_to t("layout.build_lists.cancel"), cancel_build_list_path(@build_list), + :method => :put, :confirm => t("layout.confirm"), :class => 'button', + 'ng-show' => 'build_list.can_cancel' + = submit_tag t('layout.publish_again'), + :confirm => t("layout.publish_again_warning"), + :name => 'publish', + 'ng-show' => "build_list.can_publish && build_list.status == #{BuildList::BUILD_PUBLISHED}" + = submit_tag t("layout.publish"), + :confirm => t('layout.build_lists.tests_failed'), :name => 'publish', + 'ng-show' => "build_list.can_publish && build_list.can_publish_in_future && build_list.extra_build_lists_published && build_list.status == #{BuildList::TESTS_FAILED}" + = submit_tag t("layout.publish"), + :confirm => t('layout.confirm'), :name => 'publish', + 'ng-show' => "build_list.can_publish && build_list.can_publish_in_future && build_list.extra_build_lists_published && build_list.status != #{BuildList::TESTS_FAILED} && build_list.status != #{BuildList::BUILD_PUBLISHED}" + = link_to t('layout.publish_into_testing'), publish_into_testing_build_list_path(@build_list), + :method => :put, :confirm => t("layout.confirm"), + :class => 'button', + 'ng-show' => 'build_list.can_publish_into_testing' + - if can?(:reject_publish, @build_list) + = link_to t('layout.reject_publish'), reject_publish_build_list_path(@build_list), + :method => :put, :confirm => t("layout.confirm"), + :class => 'button', + 'ng-show' => 'build_list.can_reject_publish' + - if can?(:create_container, @build_list) + = link_to t("layout.build_lists.create_container"), + create_container_build_list_path(@build_list), + :method => :put, :confirm => t("layout.confirm"), + :class => 'button', + 'ng-show' => 'build_list.can_create_container' + - if can? :create, @build_list + = link_to t('layout.build_lists.recreate_build_list'), new_project_build_list_path(@build_list.project, :build_list_id => @build_list.id), :class => 'button' + + %div{'ng-show' => "build_list.status == #{BuildList::BUILD_STARTED}"} + = render 'shared/log', { :build_started => true, :get_log_path => log_build_list_path(@build_list) } + + .hr + %h3= t("layout.build_lists.items_header") + %h4.nomargin{'ng-hide' => 'build_list.item_groups'} + = t("layout.build_lists.no_items_data") + %div{'ng-repeat' => 'group in build_list.item_groups'} + %div{'ng-repeat' => 'item in group'} + %h4.nomargin {{item.name + ' #' + item.level}} + %table.tablesorter.width565{:cellpadding => "0", :cellspacing => "0"} + %thead + %tr + %th= t("activerecord.attributes.build_list/item.name") + %th= t("activerecord.attributes.build_list/item.version") + %th= t("activerecord.attributes.build_list/item.status") + %tbody + %tr{:class => "{{build_list.itemStatusColor(item.status)}}"} + %td {{item.name}} + %td + %a{'ng-href' => '{{item.path.href}}' } {{item.path.text}} + %td {{build_list.humanStatus(item.status) | i18n}} + .both + + %div{'ng-show' => 'build_list.packages'} + .hr + %h3= t("layout.build_lists.packages_header") %table.tablesorter.width565{:cellpadding => "0", :cellspacing => "0"} %thead %tr - %th= t("activerecord.attributes.build_list/item.name") - %th= t("activerecord.attributes.build_list/item.version") - %th= t("activerecord.attributes.build_list/item.status") + %th= t("activerecord.attributes.build_list/package.fullname") + %th= t("activerecord.attributes.build_list/package.name") + %th= t("activerecord.attributes.build_list/package.epoch") + %th= t("activerecord.attributes.build_list/package.version") + %th= t("activerecord.attributes.build_list/package.release") %tbody - %tr{:class => build_list_item_status_color(item.status)} - %td= item.name - %td= build_list_item_version_link item - %td= item.human_status -.both + %tr{'ng-repeat' => 'package in build_list.packages'} + %td{'ng-show' => 'package.url'} + %a{'ng-href' => "{{package.url}}" } {{package.fullname}} + %td{'ng-hide' => 'package.url'} {{package.fullname}} + %td {{package.name}} + %td {{package.epoch}} + %td {{package.version}} + %td {{package.release}} + .both -- if @build_list.packages.present? - .hr - %h3= t("layout.build_lists.packages_header") - %table.tablesorter.width565{:cellpadding => "0", :cellspacing => "0"} - %thead - %tr - %th= t("activerecord.attributes.build_list/package.fullname") - %th= t("activerecord.attributes.build_list/package.name") - %th= t("activerecord.attributes.build_list/package.version") - %th= t("activerecord.attributes.build_list/package.release") - %tbody - - @build_list.packages.each do |package| - %tr - - if package.sha1.present? - %td= link_to package.fullname, "#{APP_CONFIG['file_store_url']}/api/v1/file_stores/#{package.sha1}" - - else - %td= package.fullname - %td= package.name - %td= package.version - %td= package.release - .both - -- if @build_list.new_core? - .hr - = render 'platforms/product_build_lists/results', :subject => @build_list + - if @build_list.new_core? + .hr + = render 'shared/build_results', :subject => @build_list :javascript $('article .all').addClass('bigpadding'); diff --git a/app/views/projects/build_lists/show.json.jbuilder b/app/views/projects/build_lists/show.json.jbuilder new file mode 100644 index 000000000..4a9b46b16 --- /dev/null +++ b/app/views/projects/build_lists/show.json.jbuilder @@ -0,0 +1,62 @@ +json.build_list do + json.(@build_list, :id, :container_status, :status) + json.(@build_list, :update_type) + json.updated_at @build_list.updated_at + json.updated_at_utc @build_list.updated_at.strftime('%Y-%m-%d %H:%M:%S UTC') + + if !@build_list.in_work? && @build_list.started_at + json.human_duration @build_list.human_duration + elsif @build_list.in_work? + json.human_duration "#{@build_list.human_current_duration} / #{@build_list.human_average_build_time}" + end + + json.can_publish can?(:publish, @build_list) + json.can_publish_into_testing can?(:publish_into_testing, @build_list) && @build_list.can_publish_into_testing? + json.can_cancel @build_list.can_cancel? + json.can_create_container @build_list.can_create_container? + json.can_reject_publish @build_list.can_reject_publish? + + json.extra_build_lists_published @build_list.extra_build_lists_published? + json.can_publish_in_future can_publish_in_future?(@build_list) + + + json.container_path container_url if @build_list.container_published? + + json.publisher do + json.fullname @build_list.publisher.try(:fullname) + json.path user_path(@build_list.publisher) + end if @build_list.publisher + + json.builder do + json.fullname @build_list.builder.try(:fullname) + json.path user_path(@build_list.builder) + end if @build_list.builder + + json.advisory do + json.(@build_list.advisory, :description, :advisory_id) + json.path advisory_path(@build_list.advisory) + end if @build_list.advisory + + json.results @build_list.results do |result| + json.file_name result['file_name'] + json.sha1 result['sha1'] + json.size result['size'] + json.url file_store_results_url(result['sha1'], result['file_name']) + end if @build_list.new_core? && @build_list.results.present? + + json.packages @build_list.packages do |package| + json.(package, :id, :name, :fullname, :release, :version, :sha1, :epoch) + json.url "#{APP_CONFIG['file_store_url']}/api/v1/file_stores/#{package.sha1}" if package.sha1 + end if @build_list.packages.present? + + json.item_groups do |group| + @item_groups.each_with_index do |group, level| + json.group group do |item| + json.(item, :name, :status) + json.path build_list_item_version_link item + json.level level + end + end + end if @item_groups.present? + +end diff --git a/app/views/projects/collaborators/_collaborator.json.jbuilder b/app/views/projects/collaborators/_collaborator.json.jbuilder index f1d105a32..55790d266 100644 --- a/app/views/projects/collaborators/_collaborator.json.jbuilder +++ b/app/views/projects/collaborators/_collaborator.json.jbuilder @@ -1,10 +1,9 @@ -json.id collaborator.id - -json.actor_id collaborator.actor_id -json.actor_name collaborator.actor_name -json.actor_type collaborator.actor_type +json.(collaborator, :id, :actor_name) +json.collaborator do # attr_accessible for AngularJS + json.(collaborator, :role, :actor_id, :actor_type, :project_id) +end +json.project do + json.(collaborator.project, :name, :owner_uname) +end json.avatar avatar_url(collaborator.actor) -json.actor_path participant_path(collaborator.actor) - -json.project_id collaborator.project_id -json.role collaborator.role +json.actor_path participant_path(collaborator.actor) \ No newline at end of file diff --git a/app/views/projects/collaborators/_collaborators.json.jbuilder b/app/views/projects/collaborators/_collaborators.json.jbuilder index 9e8d099a2..f7fba1505 100644 --- a/app/views/projects/collaborators/_collaborators.json.jbuilder +++ b/app/views/projects/collaborators/_collaborators.json.jbuilder @@ -1,12 +1,3 @@ -json.array!(collaborators) do |json, cb| - json.id cb.id - - json.actor_id cb.actor_id - json.actor_name cb.actor_name - json.actor_type cb.actor_type - json.avatar avatar_url(cb.actor) - json.actor_path participant_path(cb.actor) - - json.project_id cb.project_id - json.role cb.role +json.array!(collaborators) do |collaborator| + json.partial! 'projects/collaborators/collaborator', :collaborator => collaborator end diff --git a/app/views/projects/collaborators/add.html.haml b/app/views/projects/collaborators/add.html.haml deleted file mode 100644 index 028862297..000000000 --- a/app/views/projects/collaborators/add.html.haml +++ /dev/null @@ -1,53 +0,0 @@ -.block - .secondary-navigation - %ul.wat-cf - %li.first= link_to t("layout.collaborators.list"), project_collaborators_path(@project) - %li.active= link_to t("layout.collaborators.add"), add_project_collaborators_path(@project) - .content - .inner - = form_tag do - %h2.title= t("layout.users.list_header") - %table.table - %tr - %th.first ID - %th= t("activerecord.attributes.user.name") - %th= t("activerecord.attributes.user.email") - %th= t("activerecord.attributes.user.uname") - %th.last= t("layout.collaborators.add") - - @users.each do |user| - %tr{:class => cycle("odd", "even")} - %td - = user.id - %td - = link_to user.name, user_path(user) - %td - = user.email - %td - = user.uname - %td.last - = check_box_tag "user[#{user.id}]", '1', (@project.collaborators.include? user) ? :checked : nil - = label_tag "user[#{user.id}]", t("layout.collaborators.add") - %h2.title= t("layout.groups.list_header") - %table.table - %tr - %th.first ID - %th= t("activerecord.attributes.group.name") - %th= t("activerecord.attributes.group.uname") - %th.last= t("layout.collaborators.add") - - @groups.each do |group| - %tr{:class => cycle("odd", "even")} - %td - = group.id - %td - = link_to group.name, group_path(group) - %td - = group.uname - %td.last - = check_box_tag "group[#{group.id}]", '1', (@project.groups.include? group) ? :checked : nil - = label_tag "group[#{group.id}]", t("layout.collaborators.add") - .group.navform.wat-cf - %button.button{:type => "submit"} - = image_tag("choose.png", :alt => t("layout.save")) - = t("layout.save") - %span.text_button_padding= t("layout.or") - = link_to t("layout.cancel"), project_collaborators_path(@project), :class => "text_button_padding link_button" diff --git a/app/views/projects/collaborators/edit.html.haml b/app/views/projects/collaborators/edit.html.haml deleted file mode 100644 index 0474cec64..000000000 --- a/app/views/projects/collaborators/edit.html.haml +++ /dev/null @@ -1,93 +0,0 @@ --set_meta_tags :title => [title_object(@project), t('layout.projects.members')] -= render 'sidebar' -= render 'submenu' - -%a{:name => 'users'} -%h3= t("layout.users.list_header") - -= form_tag add_project_collaborators_path(@project) do - .admin-search - = autocomplete_field_tag 'member_id', params[:member_id], autocomplete_user_uname_autocompletes_path, :id_element => '#member_id_field' - .admin-role - .lineForm - = select_tag 'role', options_for_collaborators_roles_select - = hidden_field_tag 'member_id', nil, :id => 'member_id_field' - = submit_tag t("layout.add"), :class => 'button' - .both - -= form_tag project_collaborators_path(@project), :id => 'members_form', :delete_url => remove_project_collaborators_path(@project) do - = hidden_field_tag "_method", "post" - %table.tablesorter{:cellpadding => "0", :cellspacing => "0"} - %thead - %tr - %th - \  - %th - = t("layout.collaborators.members") - %th{:colspan => "3"} - = t("layout.collaborators.roles") - %tbody - - @users.each_with_index do |user, num| - %tr{:id => "admin-table-members-row#{num}"} - %td - %span#niceCheckbox1.niceCheck-main{ :style => "background-position: 0px 0px; "} - = check_box_tag "user_remove[#{user.id}][]" - %td - .img - = image_tag avatar_url(user) - .forimg= link_to user.fullname, user_path(user) - - Relation::ROLES.each_with_index do |role, i| - %td - .radio - = radio_button_tag "user[#{user.id}]", role, ((@project.relations.exists? :actor_id => user.id, :actor_type => 'User', :role => role) ? :checked : nil), :class => 'niceRadio' - .forradio= t("layout.collaborators.role_names.#{ role }") - = link_to_function t("layout.delete_selected"), "deleteAdminMember();", :class => 'button' - = link_to_function t("layout.save"), "saveAdminMember();", :class => 'button right_floated' - .both -%br - -.hr.bottom -.both - -%a{:name => 'groups'} -%h3= t("layout.groups.list_header") - -= form_tag add_project_collaborators_path(@project) do - .admin-search - = autocomplete_field_tag 'group_id', params[:group_id], autocomplete_group_uname_autocompletes_path, :id_element => '#group_id_field' - .admin-role - .lineForm - = select_tag 'role', options_for_collaborators_roles_select, :id => 'group_role' - = hidden_field_tag 'group_id', nil, :id => 'group_id_field' - = submit_tag t("layout.add"), :class => 'button' - .both - -= form_tag project_collaborators_path(@project), :id => 'groups_form', :delete_url => remove_project_collaborators_path(@project) do - = hidden_field_tag "_method", "post", :id => 'groups_method' - %table.tablesorter{:cellpadding => "0", :cellspacing => "0"} - %thead - %tr - %th - \  - %th - = t("layout.collaborators.members") - %th{:colspan => "3"} - = t("layout.collaborators.roles") - %tbody - - @groups.each_with_index do |group, num| - %tr{:id => "admin-table-members-row#{num + @users.size + 1}"} - %td - %span#niceCheckbox1.niceCheck-main{ :style => "background-position: 0px 0px; "} - = check_box_tag "group_remove[#{group.id}][]" - %td - .forimg= link_to "#{group.uname}", group_path(group) - - Relation::ROLES.each_with_index do |role, i| - %td - .radio - = radio_button_tag "group[#{group.id}]", role, ((@project.relations.exists? :actor_id => group.id, :actor_type => 'Group', :role => role) ? :checked : nil), :class => 'niceRadio' - .forradio= t("layout.collaborators.role_names.#{ role }") - = link_to_function t("layout.delete_selected"), "deleteAdminGroup();", :class => 'button' - = link_to_function t("layout.save"), "saveAdminGroup();", :class => 'button right_floated' - .both -.both - diff --git a/app/views/projects/collaborators/index.html.haml b/app/views/projects/collaborators/index.html.haml index 7b9a468ae..6e633abab 100644 --- a/app/views/projects/collaborators/index.html.haml +++ b/app/views/projects/collaborators/index.html.haml @@ -5,40 +5,70 @@ %a{:name => 'users'} %h3= t("layout.users.list_header") -#add_collaborator_form - .admin-search.withimage - .img - %img{ :alt => 'avatar', :src => '', :style => 'display: none;' } - = text_field_tag :collaborator_name, nil +#collaborators{'ng-controller' => 'CollaboratorsController'} + = hidden_field_tag :owner_name, @project.try(:owner).try(:uname) + = hidden_field_tag :project_name, @project.try(:name) + + #add_collaborator_form + .admin-search.withimage + .img + %img{:alt => 'avatar', 'ng-src' => '{{new_collaborator.avatar}}', 'ng-show' => 'new_collaborator.avatar'} + = text_field_tag :collaborator_name, nil, 'ng-model' => 'new_collaborator.term', 'ng-keyup' => 'search()' + + .admin-role + .lineForm + = select_tag 'role', options_for_collaborators_roles_select, 'ng-model' => 'new_collaborator.collaborator.role' + .admin-add + %a.button{:rel => 'nofollow', :href => '', 'ng-click' => 'add()'} + = t('layout.add') .both - .admin-role - .lineForm - = select_tag 'role', options_for_collaborators_roles_select - .admin-add - %a{:id => 'add_collaborator_button', :class => 'button', :rel => 'nofollow', :href => 'javascript:void(0)'} - = t('layout.add') - .both -%table#collaborators.tablesorter{:cellpadding => "0", :cellspacing => "0"} - %thead - %tr - %th.centered - %span#collaborators_deleter.hidden - %span.delete    - %th - = t("layout.collaborators.members") - %th{:colspan => "3"} - = t("layout.collaborators.roles") - %tr.search - %th{:colspan => "5"} - %input{ :type => "text", :placeholder => "#{ t('layout.filter_by_name') }"} - %tbody -%br + .users-search-popup + .header + .title= t('layout.issues.search_user') + %span.icon-remove-circle + .list + .people{'ng-repeat' => 'c in new_collaborators', 'ng-click' => 'select(c)'} + .avatar + %img{width: '16px', 'ng-src' => '{{c.avatar}}', alt: 'avatar'} + .name {{c.actor_name}} + .both + + .nothing{'ng-hide' => 'new_collaborators.length > 0'}= t('layout.issues.nothing_to_show') + .both + .both -.both + %table.tablesorter{:cellpadding => "0", :cellspacing => "0"} + %thead + %tr + %th.centered + %span.delete{'ng-click' => 'deleteCollaborators()'}    + %th + = t("layout.collaborators.members") + %th{:colspan => "3"} + = t("layout.collaborators.roles") + %tr.search + %th{:colspan => "5"} + %input{:type => "text", :placeholder => "#{t('layout.filter_by_name')}", 'ng-model' => 'query.actor_name'} -:javascript - $(function() { - Rosa.bootstrapedData.collaborators = #{ render 'collaborators.json.jbuilder', :collaborators => @collaborators }; - r = new Rosa.Routers.CollaboratorsRouter(); - }); + %tbody + %tr{'ng-repeat' => 'c in collaborators | filter:query'} + %td + %input{:type => 'checkbox', 'ng-model' => 'c.removed' } + %td + .img + %img{'ng-src' => '{{c.avatar}}', alt: 'avatar' } + .forimg + %a{'ng-href' => '{{c.actor_path}}'} {{c.actor_name}} + + - Relation::ROLES.each do |role| + %td + .radio + %input{:type => 'radio', 'ng-model' => 'c.collaborator.role', :value => role, 'ng-click' => 'update(c)'} + .forradio + %label= t("layout.collaborators.role_names.#{role}") + + + %br + + .both \ No newline at end of file diff --git a/app/views/projects/comments/_comment.html.haml b/app/views/projects/comments/_comment.html.haml index 0c25b2ac3..88c0e9f78 100644 --- a/app/views/projects/comments/_comment.html.haml +++ b/app/views/projects/comments/_comment.html.haml @@ -1,10 +1,11 @@ - CommentPresenter.present(comment, data) do |presenter| = render 'shared/feed_message', :presenter => presenter -#open-comment.comment.hidden{:class => "comment-#{comment.id}"} - =render 'projects/comments/button_md_help' - %h3.tmargin0= t("layout.comments.edit_header") - = form_for comment, :url => project_commentable_comment_path(data[:project], data[:commentable], comment), :html => { :class => 'form edit_comment' } do |f| - = render "projects/comments/form", :f => f, :id => "#{data[:add_id]}edit_#{comment.id}" - .comment-left - =link_to t('layout.cancel'), '#', :id => "comment-#{comment.id}", :class => 'cancel_edit_comment button' - .both +-unless comment.automatic + #open-comment.comment.hidden{:class => "comment-#{comment.id}"} + =render 'projects/comments/button_md_help' + %h3.tmargin0= t("layout.comments.edit_header") + = form_for comment, :url => project_commentable_comment_path(data[:project], data[:commentable], comment), :html => { :class => 'form edit_comment' } do |f| + = render "projects/comments/form", :f => f, :id => "#{data[:add_id]}edit_#{comment.id}" + .comment-left + =link_to t('layout.cancel'), '#', :id => "comment-#{comment.id}", :class => 'cancel_edit_comment button' + .both diff --git a/app/views/projects/comments/_form.html.haml b/app/views/projects/comments/_form.html.haml index f37286e42..07e6b688d 100644 --- a/app/views/projects/comments/_form.html.haml +++ b/app/views/projects/comments/_form.html.haml @@ -1,2 +1,2 @@ =render 'projects/comments/body', :f => f, :id => id -.comment-right= submit_tag t("layout.save") +.comment-right= submit_tag t('layout.save'), :data => {'disable-with' => t('layout.saving')} diff --git a/app/views/projects/comments/_list.html.haml b/app/views/projects/comments/_list.html.haml index e63505aa7..f7edaff1d 100644 --- a/app/views/projects/comments/_list.html.haml +++ b/app/views/projects/comments/_list.html.haml @@ -2,5 +2,9 @@ .hr %h3#block-list= t("layout.comments.comments_header") - list.each do |comment| - = render 'projects/comments/comment', :comment => comment, :data => {:project => project, :commentable => commentable} + -unless comment.created_from_commit_hash + = render 'projects/comments/comment', :comment => comment, :data => {:project => project, :commentable => commentable} + -else + - GitPresenters::CommitAsMessagePresenter.present(nil, :comment => comment) do |presenter| + = render 'shared/feed_message', :presenter => presenter, :item_no => "-#{comment.id}" = render "projects/comments/markdown_help" diff --git a/app/views/projects/comments/_markdown_help.html.haml b/app/views/projects/comments/_markdown_help.html.haml index 28cf08dd8..3c9a27af4 100644 --- a/app/views/projects/comments/_markdown_help.html.haml +++ b/app/views/projects/comments/_markdown_help.html.haml @@ -17,23 +17,27 @@ _This will also be italic_ **This text will be bold** __This will also be bold__ + %p=link_to t('layout.comments.md_cheatsheet.emoji_header'), 'http://www.emoji-cheat-sheet.com/', :target => '_blank' + %pre + =":smile:" + =image_tag(image_path('emoji/smile.png'), :class => 'emoji', :title => 'smile', :alt => 'smile', :size => "20x20") + =" :+1:" + =image_tag(image_path('emoji/+1.png'), :class => 'emoji', :title => '+1', :alt => '+1', :size => "20x20") .col %h3=t 'layout.comments.md_cheatsheet.lists' - %p=t 'layout.comments.md_cheatsheet.unordered' + %p{:style => 'float:left;margin-left:20px;'}=t 'layout.comments.md_cheatsheet.unordered' + %p{:style => 'float:right;margin-right:40px;'}=t 'layout.comments.md_cheatsheet.ordered' + .both %pre :preserve - * Item 1 - * Item 2 - * Item 2a - * Item 2b - %p=t 'layout.comments.md_cheatsheet.ordered' + * Item 1 1. Item 1 + * Item 2 2. Item 2 + * Item 2a 3. Item 3 + * Item 2b * Item 3a + * Item 3b + %p=t 'layout.comments.md_cheatsheet.reference_format' %pre - :preserve - 1. Item 1 - 2. Item 2 - 3. Item 3 - * Item 3a - * Item 3b + =preserve t('layout.comments.md_cheatsheet.reference_format_example').html_safe .col %h3=t 'layout.comments.md_cheatsheet.miscellaneous' %p=t 'layout.comments.md_cheatsheet.images' @@ -84,3 +88,4 @@ :preserve I think you should use an `[addr]` element here instead. + .both diff --git a/app/views/projects/git/base/_choose_fork.html.haml b/app/views/projects/git/base/_choose_fork.html.haml index e627dd77c..f71676821 100644 --- a/app/views/projects/git/base/_choose_fork.html.haml +++ b/app/views/projects/git/base/_choose_fork.html.haml @@ -1,12 +1,15 @@ -- if owner.own_projects.exists? :name => @project.name - - is_group = owner.class == Group ? "(#{t 'activerecord.models.group'})" : '' +- is_group = owner.class == Group ? "(#{t 'activerecord.models.group'})" : '' +- full_name = "#{owner.uname}/#{name} #{is_group}" + +- if owner.own_projects.exists? :name => name %p.center =t 'layout.projects.already_exists' - =link_to "#{owner.uname}/#{@project.name} #{is_group}", project_path(owner, @project.name) + =link_to full_name, project_path(owner, name) - else = form_for @project, :url => fork_project_path(@project), :html => { :class => :form, :multipart => true, :method => :post } do |f| = hidden_field_tag :group, owner.id if owner.class == Group - =f.submit t('layout.projects.fork_to', :to => "#{owner.uname} #{is_group}"), :class => 'btn btn-primary disabled', 'data-loading-text' => t('layout.processing'), :id => 'create_fork' + = hidden_field_tag :fork_name, name, :name => 'fork_name' + =f.submit t('layout.projects.fork_to', :to => full_name), :class => 'btn btn-primary disabled', 'data-loading-text' => t('layout.processing'), :id => 'create_fork' :javascript $('#create_fork').click(function () { diff --git a/app/views/projects/git/base/_fork.html.haml b/app/views/projects/git/base/_fork.html.haml index be9cf54d4..6f15afbcb 100644 --- a/app/views/projects/git/base/_fork.html.haml +++ b/app/views/projects/git/base/_fork.html.haml @@ -1,3 +1,5 @@ += hidden_field_tag :possible_forks_path, possible_forks_project_path(@project) + - if can? :write, @project .r{:style => "display: block"} =link_to t("projects.pull_requests.show.pull"), new_project_pull_request_path(@project, :treeish => @treeish), :id => 'send_pull_request', :class => 'button' @@ -8,11 +10,10 @@ .modal-header %a.close{"data-dismiss" => "modal"} × %h3=t 'layout.projects.fork_modal_header' - .modal-body.modal-body-fork - =render 'choose_fork', :owner => current_user - %hr.bootstrap - - Group.can_own_project(current_user).each do |group| - =render 'choose_fork', :owner => group - %hr.bootstrap -- if can? :create, @project.build_lists.new + = hidden_field_tag :possible_forks, possible_forks_project_path(@project) + %div{:align => 'center'}= text_field_tag 'fork_name', @project.name, :id => 'fork_name', :class => 'fork_name' + #forks_list.modal-body.modal-body-fork + = render 'forks', :owner => current_user, :name => @project.name + +- if @project.is_package && can?(:create, @project.build_lists.new) .r{:style => "display: block"}= link_to t('layout.projects.new_build_list'), new_project_build_list_path(@project), :class => 'button' diff --git a/app/views/projects/git/base/_forks.html.haml b/app/views/projects/git/base/_forks.html.haml new file mode 100644 index 000000000..a4d442860 --- /dev/null +++ b/app/views/projects/git/base/_forks.html.haml @@ -0,0 +1,5 @@ +=render 'projects/git/base/choose_fork', :owner => current_user, :name => name +%hr.bootstrap +- Group.can_own_project(current_user).each do |group| + =render 'projects/git/base/choose_fork', :owner => group, :name => name + %hr.bootstrap \ No newline at end of file diff --git a/app/views/projects/git/blobs/_blame_table.html.haml b/app/views/projects/git/blobs/_blame_table.html.haml index fc0a608a8..eb79fd716 100644 --- a/app/views/projects/git/blobs/_blame_table.html.haml +++ b/app/views/projects/git/blobs/_blame_table.html.haml @@ -12,7 +12,7 @@ (#{commit_author_link(committer)}) %br %span.date= commit_date(elem[0].committed_date) - %span.message{:title => elem[0].message}= short_commit_message(elem[0].message) + %span.message{:title => elem[0].message}= short_message(elem[0].message) %td.lines = index diff --git a/app/views/projects/git/blobs/_editor.html.haml b/app/views/projects/git/blobs/_editor.html.haml index 3cd15157b..9ed1345f9 100644 --- a/app/views/projects/git/blobs/_editor.html.haml +++ b/app/views/projects/git/blobs/_editor.html.haml @@ -13,7 +13,7 @@ %br %br - = submit_tag t("layout.save"), :title => t("layout.save") + = submit_tag t('layout.save'), :title => t('layout.save'), :data => {'disable-with' => t('layout.saving')} = t("layout.or") = link_to t("layout.cancel"), blob_path(@project, @treeish, @path), :class => 'button' diff --git a/app/views/projects/git/commits/_commits_small.html.haml b/app/views/projects/git/commits/_commits_small.html.haml index 3302753fb..dc4b84831 100644 --- a/app/views/projects/git/commits/_commits_small.html.haml +++ b/app/views/projects/git/commits/_commits_small.html.haml @@ -7,8 +7,7 @@ %tr %td %img{:height => 16, :alt => "avatar", :src => presenter.image} - %td.date - = presenter.date + = datetime_moment presenter.date, :tag => :td, :class => :date %td.name = presenter.header %td.subject diff --git a/app/views/projects/git/trees/_branch.html.haml b/app/views/projects/git/trees/_branch.html.haml deleted file mode 100644 index 7d21b547a..000000000 --- a/app/views/projects/git/trees/_branch.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -- base = branch.name == @branch.name -%tr{:class => (base ? 'base' : '')} - %td.name= link_to branch.name, tree_path(@project, branch.name) - %td.actions - %ul.actions - - if base - %li.text= t('layout.projects.base_branch') - - else - %li - = link_to t('layout.projects.compare'), diff_path(@project, "#{@branch.name}...#{branch.name}") \ No newline at end of file diff --git a/app/views/projects/git/trees/_header.html.haml b/app/views/projects/git/trees/_header.html.haml deleted file mode 100644 index edda53ee0..000000000 --- a/app/views/projects/git/trees/_header.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -- subjects_name = action_name.to_s - -%h3= t("layout.projects.#{subjects_name}") -- if subject.blank? - %p= t("layout.projects.no_#{subjects_name}") -- elsif subject.count == 1 - %p= t("layout.projects.showing_#{subjects_name.singularize}") -- else - %p= t("layout.projects.showing_#{subjects_name}", :count => subject.count) -.both \ No newline at end of file diff --git a/app/views/projects/git/trees/_show.html.haml b/app/views/projects/git/trees/_show.html.haml index 35bae8bea..88e88fe1f 100644 --- a/app/views/projects/git/trees/_show.html.haml +++ b/app/views/projects/git/trees/_show.html.haml @@ -20,18 +20,31 @@ %td==   %td==   %td==   - - @project.tree_info(@tree, @treeish, @path).each_pair do |entry, commit| + - @project.tree_info(@tree, @treeish, @path).each do |node, node_path, commit| %tr %td - - entry_path = File.join([@path.present? ? @path : nil, entry.name].compact) - - if entry.is_a? Grit::Blob - .pic= image_tag 'code.png' - .name= link_to(entry.name, blob_path(@project, @treeish, entry_path), :class => 'files-see') + - if node.is_a? Grit::Submodule + .pic= image_tag 'folder-submodule.png' + .name + - if url = submodule_url(node, @treeish) + = link_to(node.name, url, :class => 'files-see') + = '@' + = link_to(node.id[0..6], "#{url}/tree/#{node.id}", :class => 'files-see') + - else + = "#{node.name} @ #{node.id[0..6]}" - else - .pic= image_tag 'folder.png' - .name= link_to(entry.name, tree_path(@project, @treeish, entry_path), :class => 'files-see') - %td - %span{:style => "display: none;"}= date = commit.committed_date || commit.authored_date - = l(date, :format => :short) - %td= commit.short_message - %td= (commit.committer || commit.author).name \ No newline at end of file + - options = [@project, @treeish, node_path] + - if node.is_a?(Grit::Tree) + - pic = 'folder.png' + - path = tree_path *options + .pic= image_tag pic || 'code.png' + .name= link_to(node.name, path || blob_path(*options), :class => 'files-see') + + - if commit + = datetime_moment(commit.committed_date || commit.authored_date, :tag => :td) + %td= commit.short_message + %td= (commit.committer || commit.author).name + - else + %td + %td + %td diff --git a/app/views/projects/git/trees/_tag.html.haml b/app/views/projects/git/trees/_tag.html.haml deleted file mode 100644 index 66421ead6..000000000 --- a/app/views/projects/git/trees/_tag.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -%li - = link_to t('layout.projects.browse_code'), tree_path(@project, tag.name), :class => 'detail-link' - - file_name = "#{@project.name}-#{tag.name}" - - %w(zip tar.gz).each do |type| - = link_to t('layout.projects.source_code', :type => type), archive_path(@project, file_name, type), :class => 'detail-link' - %p.name - %b= tag.name - .date= tag.commit.authored_date.strftime('%d.%m.%Y') diff --git a/app/views/projects/git/trees/branches.html.haml b/app/views/projects/git/trees/branches.html.haml new file mode 100644 index 000000000..304ce7515 --- /dev/null +++ b/app/views/projects/git/trees/branches.html.haml @@ -0,0 +1,43 @@ +-set_meta_tags :title => "#{title_object @project}" + += render 'submenu' += render 'repo_block', :project => @project + +%div{'ng-controller' => 'ProjectBranchesController', 'ng-init' => "init('#{@project.owner.uname}', '#{@project.name}','#{@branch.try(:name)}')"} + + %h3= t('layout.projects.branches') + %p{'ng-show' => '!branches.length'}= t('layout.projects.no_branches') + %p{'ng-show' => 'branches.length > 0'} {{'project.total_branches' | i18n:'plural':branches.length}} + .both + %br + + %input.mediumheight{'ng-model' => 'query.ref'} + .both + + %table#project-branches + %tbody + %tr{'ng-repeat' => 'branch in branches | filter:query', 'ng-class' => '{base: branch.ref == current_ref}'} + %td.name + %a{'ng-href' => '{{branch.path(project)}}' } {{branch.ref}} + %td.actions + %ul.actions + - if can?(:write, @project) + %li{'ng-hide' => 'branch.ref == current_ref || branch.ui_container'} + %a{:href => '', 'ng-click' => 'destroy(branch)'} + = t('layout.projects.delete_branch') + %li{'ng-hide' => 'branch.ui_container'} + %a{:href => '', 'ng-click' => 'branch.ui_container = true'} + = t('layout.projects.new_branch') + %li{'ng-show' => 'branch.ui_container'} + %form{'ng-submit' => 'create(branch)'} + %input{'ng-model' => 'branch.new_ref'} + %input{:type => 'submit', :value =>t('layout.create')} + %a{:href => '', 'ng-click' => 'branch.ui_container = false'} + = t('layout.cancel') + %li{'ng-hide' => 'branch.ref == current_ref || branch.ui_container'} + %a{'ng-href' => '{{branch.diff_path(project, current_ref)}}' } + = t('layout.projects.compare') + %li.text{'ng-show' => 'branch.ref == current_ref && !branch.ui_container'} + = t('layout.projects.base_branch') + + diff --git a/app/views/projects/git/trees/refs.html.haml b/app/views/projects/git/trees/refs.html.haml deleted file mode 100644 index 586087c02..000000000 --- a/app/views/projects/git/trees/refs.html.haml +++ /dev/null @@ -1,14 +0,0 @@ --set_meta_tags :title => "#{title_object @project}" - -= render 'submenu' -= render 'repo_block', :project => @project -= render 'header', :subject => (@branches || @tags) - -- if @tags.present? - %div#project-tags - %ol.release-list - = render :partial => 'tag', :collection => @tags -- elsif @branches.present? - %table#project-branches - %tbody - = render :partial => 'branch', :collection => @branches diff --git a/app/views/projects/git/trees/refs_list.json.jbuilder b/app/views/projects/git/trees/refs_list.json.jbuilder new file mode 100644 index 000000000..c53ee0a8a --- /dev/null +++ b/app/views/projects/git/trees/refs_list.json.jbuilder @@ -0,0 +1,8 @@ +json.refs_list @refs do |grit| + json.ref grit.name + json.object do + json.type (grit.class.name =~ /Tag/ ? 'tag' : 'commit') + json.sha grit.commit.id + json.authored_date grit.commit.authored_date.to_i + end +end \ No newline at end of file diff --git a/app/views/projects/git/trees/show.json.jbuilder b/app/views/projects/git/trees/show.json.jbuilder new file mode 100644 index 000000000..b01b52153 --- /dev/null +++ b/app/views/projects/git/trees/show.json.jbuilder @@ -0,0 +1,8 @@ +json.project do + json.(@project, :id, :name) + json.fullname @project.name_with_owner + + json.owner do + json.(@project.owner, :id, :name, :uname) + end +end \ No newline at end of file diff --git a/app/views/projects/git/trees/tags.html.haml b/app/views/projects/git/trees/tags.html.haml new file mode 100644 index 000000000..f8a074c12 --- /dev/null +++ b/app/views/projects/git/trees/tags.html.haml @@ -0,0 +1,27 @@ +-set_meta_tags :title => "#{title_object @project}" + += render 'submenu' += render 'repo_block', :project => @project + +%div{'ng-controller' => 'ProjectTagsController', 'ng-init' => "init('#{@project.owner.uname}', '#{@project.name}')"} + + %h3= t('layout.projects.tags') + %p{'ng-show' => '!tags.length'}= t('layout.projects.no_tags') + %p{'ng-show' => 'tags.length > 0'} {{'project.total_tags' | i18n:'plural':tags.length}} + .both + %br + + %input.mediumheight{'ng-model' => 'query.ref'} + .both + + #project-tags + %ol.release-list + %li{'ng-repeat' => 'tag in tags | filter:query'} + %a.detail-link{'ng-href' => '{{tag.path(project)}}' } + = t('layout.projects.browse_code') + - %w(zip tar.gz).each do |type| + %a.detail-link{'ng-href' => "{{tag.archive_path(project, '#{type}')}}" } + = t('layout.projects.source_code', :type => type) + %p.name + %b {{tag.ref}} + .date {{tag.object.authored_date * 1000 | date:'yyyy.MM.dd'}} diff --git a/app/views/projects/hooks/_form.html.haml b/app/views/projects/hooks/_form.html.haml new file mode 100644 index 000000000..95104573d --- /dev/null +++ b/app/views/projects/hooks/_form.html.haml @@ -0,0 +1,12 @@ += hidden_field_tag 'hook[name]', @hook.name +- Hook::SCHEMA[@hook.name.to_sym].each do |type, field| + .leftlist= t("activerecord.attributes.hook.data.#{field}") + .rightlist + - name, value = "hook[data][#{field}]", @hook.data[field] + - if type == :boolean + = check_box_tag name, value, 1 + - elsif type == :password + = password_field name, value + - else + = text_field_tag name, value + .both \ No newline at end of file diff --git a/app/views/projects/hooks/docs/_irc.en.html.haml b/app/views/projects/hooks/docs/_irc.en.html.haml new file mode 100644 index 000000000..8a4af7cd2 --- /dev/null +++ b/app/views/projects/hooks/docs/_irc.en.html.haml @@ -0,0 +1,23 @@ +:markdown + + 1. `server` - IRC server, IE. irc.freenode.net + 2. `port` - Normally 6667 (No SSL) or 9999 (SSL) (optional). These are the defaults based on whether the value of `ssl` checkbox. + 3. `room` - Supports single or multiple rooms (comma separated). Also supports room passwords (room_name::password). Prefixing '#' to the room is optional. + 4. `password` - Server password (optional) + 5. `nick` - Nickname (optional) + 6. `long_url` - If enabled, displays full compare/commit url's. If disabled uses git.io. + 7. `message_without_join` - If enabled prevents joining and immediately leaving the channel. + 8. `no_colors` - Disables color support for messages + 9. `notice` - Enables notice support. Make sure you configure channel to support notices ("/mode #yourchannelname -n" or "/msg chanserv set mlock -n") + 10. `branch_regexes` - Regular expressions for branch name matching (optional, comma separated). For example, only master => `^master$`, master & starts with bug => `master,^bug`. + 11. Configure your IRC channel to allow external messages (/mode #yourchannelname -n). + + For freenode, try the following: + + # server: irc.freenode.net + # port: 6667 + # room: #yourroom + # message_without_join: checked + # long_url: checked + # notice: checked + # NOTE: Ensure you enable notice support (see above) diff --git a/app/views/projects/hooks/docs/_irc.ru.html.haml b/app/views/projects/hooks/docs/_irc.ru.html.haml new file mode 100644 index 000000000..189a0bcdc --- /dev/null +++ b/app/views/projects/hooks/docs/_irc.ru.html.haml @@ -0,0 +1,23 @@ +:markdown + + 1. `server` - IRC сервер, например irc.freenode.net + 2. `port` - Стандартный 6667 (без SSL) или 9999 (SSL) (опционально). Это значения по умолчанию в зависимости от значения `ssl` чекбокса. + 3. `room` - Поддержка одной или нескольких комнат (через запятую). Также поддерживается пароль к комнате (room_name::password). Префикс '#' у комнаты опционально. + 4. `password` - пароль к серверу (опционально) + 5. `nick` - никнейм (опционально) + 6. `long_url` - Если включено, показывает полные compare/commit url's. Если отключено, использует git.io. + 7. `message_without_join` - Если включено, предварительно заходит на канал и немедленно покидает его. + 8. `no_colors` - Отключает поддержку цветов для сообщений + 9. `notice` - Включает поддержку уведомлений. Убедитесь, что ваш канал поддерживает уведомления ("/mode #yourchannelname -n" или "/msg chanserv set mlock -n") + 10. `branch_regexes` - Регулярное выражение для фильтрации по имении ветки (опционально, через запятую). Например, только master => `^master$`, master & начинающиеся на bug => `master,^bug`. + 11. Сконфигурируйте ваш IRC канал на поддержку внешних сообщений (/mode #yourchannelname -n). + + Для freenode, подходит следующее: + + # server: irc.freenode.net + # port: 6667 + # room: #yourroom + # message_without_join: checked + # long_url: checked + # notice: checked + # ЗАМЕЧАНИЕ: Убедитесь, что поддержка уведомлений включена (см. выше) diff --git a/app/views/projects/hooks/docs/_jabber.en.html.haml b/app/views/projects/hooks/docs/_jabber.en.html.haml new file mode 100644 index 000000000..443e90455 --- /dev/null +++ b/app/views/projects/hooks/docs/_jabber.en.html.haml @@ -0,0 +1,4 @@ +:markdown + + 1. **User** is the Jabber ID (e.g.: myusername@jabber.org). Multiple users can be added by separating them with commas. Currently, there is a maximum of 25 users. + 2. You should add `abf-notification@rosalab.ru` user into your contact list. \ No newline at end of file diff --git a/app/views/projects/hooks/docs/_jabber.ru.html.haml b/app/views/projects/hooks/docs/_jabber.ru.html.haml new file mode 100644 index 000000000..2fb5101ad --- /dev/null +++ b/app/views/projects/hooks/docs/_jabber.ru.html.haml @@ -0,0 +1,4 @@ +:markdown + + 1. **User** это Jabber ID (например: myusername@jabber.org). Несколько пользователей могут быть добавленны через запятую (максимум 25 пользователей). + 2. Вы должны добавить `abf-notification@rosalab.ru` пользователя в ваш список контактов. \ No newline at end of file diff --git a/app/views/projects/hooks/docs/_web.en.html.haml b/app/views/projects/hooks/docs/_web.en.html.haml new file mode 100644 index 000000000..f2bef3247 --- /dev/null +++ b/app/views/projects/hooks/docs/_web.en.html.haml @@ -0,0 +1,3 @@ +:markdown + + We’ll hit these URLs with POST requests when you push to us, passing along information about the push. \ No newline at end of file diff --git a/app/views/projects/hooks/docs/_web.ru.html.haml b/app/views/projects/hooks/docs/_web.ru.html.haml new file mode 100644 index 000000000..a3e46286a --- /dev/null +++ b/app/views/projects/hooks/docs/_web.ru.html.haml @@ -0,0 +1,3 @@ +:markdown + + Мы будем отправлять POST запрос на эти URLs, когда вы обновите проект, передавая информацию об обновлении. \ No newline at end of file diff --git a/app/views/projects/hooks/edit.html.haml b/app/views/projects/hooks/edit.html.haml new file mode 100644 index 000000000..230d0334a --- /dev/null +++ b/app/views/projects/hooks/edit.html.haml @@ -0,0 +1,13 @@ +-set_meta_tags :title => [title_object(@project), t('layout.projects.hooks')] += render 'submenu' += render 'sidebar' + +%h1= t("layout.hooks.services.#{@hook.name}") + += form_for @hook, :url => project_hook_path(@project, @hook), :method => :put, :html => { :class => :form } do |f| + = render 'form' + .actions + = f.submit t('layout.update'), :data => {'disable-with' => t('layout.processing')} + +%br += render "projects/hooks/docs/#{@hook.name}" \ No newline at end of file diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml new file mode 100644 index 000000000..ba4df47c1 --- /dev/null +++ b/app/views/projects/hooks/index.html.haml @@ -0,0 +1,20 @@ +-set_meta_tags :title => [title_object(@project), t('layout.projects.hooks')] += render 'submenu' += render 'sidebar' + +%h1= t('layout.projects.hooks') + +%table#datatable.tablesorter{:cellpadding => "0", :cellspacing => "0"} + %thead + %tr + %th.th1= t('attributes.name') + %th + %tbody + - Hook::NAMES.each do |name| + %tr + %td + = link_to project_hooks_path(@project, :name => name) do + = t("layout.hooks.services.#{name}") + = "(#{@hooks.where(:name => name).count})" + %td.right + = link_to t('layout.create'), new_project_hook_path(@project, :hook => {:name => name}), :class => 'button' diff --git a/app/views/projects/hooks/new.html.haml b/app/views/projects/hooks/new.html.haml new file mode 100644 index 000000000..80751cd5f --- /dev/null +++ b/app/views/projects/hooks/new.html.haml @@ -0,0 +1,13 @@ +-set_meta_tags :title => [title_object(@project), t('layout.projects.hooks')] += render 'submenu' += render 'sidebar' + +%h1= t("layout.hooks.services.#{@hook.name}") + += form_for @hook, :url => project_hooks_path(@project), :method => :post, :html => { :class => :form } do |f| + = render 'form' + .actions + = f.submit t('layout.create'), :data => {'disable-with' => t('layout.processing')} + +%br += render "projects/hooks/docs/#{@hook.name}" \ No newline at end of file diff --git a/app/views/projects/hooks/show.html.haml b/app/views/projects/hooks/show.html.haml new file mode 100644 index 000000000..25b57f9f5 --- /dev/null +++ b/app/views/projects/hooks/show.html.haml @@ -0,0 +1,26 @@ +-set_meta_tags :title => [title_object(@project), t('layout.projects.hooks')] += render 'submenu' += render 'sidebar' + +%h1= t("layout.hooks.services.#{@name}") + += link_to t('layout.create'), new_project_hook_path(@project, :hook => {:name => @name}), :class => 'button' +.booth +%br + +- @hooks.all.each do |hook| + - schema = Hook::SCHEMA[hook.name.to_sym] + - hook.data.each do |field, value| + .leftlist= t("activerecord.attributes.hook.data.#{field}") + .rightlist + - if schema.find{ |type, attr| type == :password && field.to_sym == attr } + = '******' + - else + = value + .both + + .leftlist + .rightlist + = link_to t('layout.edit'), edit_project_hook_path(@project, hook), :class => 'button' + = link_to t('layout.delete'), project_hook_path(@project, hook), :method => :delete, :confirm => t('layout.confirm'), :class => 'button' + .hr diff --git a/app/views/projects/issues/_assigned_popup.html.haml b/app/views/projects/issues/_assigned_popup.html.haml index 8f31e036e..51d06bf7a 100644 --- a/app/views/projects/issues/_assigned_popup.html.haml +++ b/app/views/projects/issues/_assigned_popup.html.haml @@ -1,4 +1,4 @@ -#assigned-popup +#assigned-popup.users-search-popup .header .title= t('layout.issues.assign_someone') %span.icon-remove-circle @@ -10,5 +10,5 @@ - unless [:new, :create].include?(action_name.to_sym) = form_for :issue, :url => [@project, @issue], :method => :put, :html => { :class => 'edit_assignee issue'} do |f| = hidden_field_tag "user-default_assignee", nil, :name => 'issue[assignee_id]' - #manage_issue_users_list + .list = render 'projects/issues/search_collaborators' \ No newline at end of file diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index a0948aebb..871b4d272 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -1,14 +1,15 @@ =render 'title_body', :f => f, :id => 'new' -.leftlist= t('activerecord.attributes.issue.assignee') + ':' -#assigned-container.rightlist - =render 'user_container', :user => @issue.assignee -.both -.leftlist= t('layout.issues.labels') + ':' -.rightlist - %span#flag-span.small-text= t('layout.issues.choose_labels_on_left') - #issue_labels -.both +- if can?(:write, @project) + .leftlist= t('activerecord.attributes.issue.assignee') + ':' + #assigned-container.rightlist + =render 'user_container', :user => @issue.assignee + .both + .leftlist= t('layout.issues.labels') + ':' + .rightlist + %span#flag-span.small-text= t('layout.issues.choose_labels_on_left') + #issue_labels + .both .leftlist .rightlist - %input{:type => "submit", :value => t(@issue.new_record? ? 'layout.create' : 'layout.update')} + = submit_tag t(@issue.new_record? ? 'layout.create' : 'layout.update'), :data => {'disable-with' => t('layout.processing')} .both diff --git a/app/views/projects/issues/_header.html.haml b/app/views/projects/issues/_header.html.haml index 2f56a9a2e..b6895a9b3 100644 --- a/app/views/projects/issues/_header.html.haml +++ b/app/views/projects/issues/_header.html.haml @@ -3,7 +3,7 @@ .image =image_tag(avatar_url(@issue.user, :medium), :alt => 'avatar') if @issue.user .created - %span=@issue.created_at.to_s(:long) + = datetime_moment @issue.created_at, :tag => :span - if @issue.user %span= t('layout.by') %span.name=link_to(@issue.user.fullname, user_path(@issue.user)) diff --git a/app/views/projects/issues/_index_sidebar.html.haml b/app/views/projects/issues/_index_sidebar.html.haml index 51e7e8e18..154ee8ba1 100644 --- a/app/views/projects/issues/_index_sidebar.html.haml +++ b/app/views/projects/issues/_index_sidebar.html.haml @@ -5,12 +5,12 @@ %table %tr %td.width18=radio_button_tag :myradio, 'all', !@is_assigned_to_me, {:id => 'myradio1', :class => 'niceRadio', :name => 'filter'} - %td.width135=t("layout.issues.all") - %td.width30.right=@project.issues.without_pull_requests.count + %td.width135=t('layout.issues.all') + %td.width30.right=@project.issues.without_pull_requests.not_closed_or_merged.count %tr - %td=radio_button_tag :myradio, 'to_me', @is_assigned_to_me, {:id => 'myradio1', :class => 'niceRadio', :name => 'filter'} - %td=t("layout.issues.to_me") - %td.width30.right=@project.issues.without_pull_requests.where(:assignee_id => current_user.id).count + %td=radio_button_tag :myradio, 'assigned', @is_assigned_to_me, {:id => 'myradio1', :class => 'niceRadio', :name => 'filter'} + %td=t('layout.issues.assigned') + %td.width30.right=@project.issues.without_pull_requests.where(:assignee_id => current_user.id).not_closed_or_merged.count =form_tag project_issues_path(@project), :id => 'search_issue', :class => 'ajax_search_form', :method => :get do .bordered.bpadding20 - search = params[:search_issue].present? ? params[:search_issue] : t('layout.issues.search') diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 3d2d0025e..cb7cdc363 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -1,4 +1,4 @@ --path = polymorphic_path [@project, issue.pull_request ? issue.pull_request : issue] +- path = polymorphic_path [@project || issue.project, issue.pull_request ? issue.pull_request : issue] %tr#row1{:name => "row", :class => issue.labels.map(&:name).compact} %td.td0 @@ -6,23 +6,22 @@ %td.td1=issue.serial_id %td %a{:href => path} - %div.issue_title=issue.title + %div.issue_title + -prefix = @project ? '' : "#{issue.project.name}: " + ="#{prefix} #{issue.title}".html_safe .smalltext - =issue.send(@sort == :created ? :created_at : :updated_at).to_s(:long) + = datetime_moment issue.send(@sort == :created ? :created_at : :updated_at) =t("layout.by") if issue.user =link_to(issue.user.uname, user_path(issue.user)) if issue.user -issue.labels.each do |label| .left.nomargin .label.selected.tracker.left .labeltext.selected{:style => "background: ##{label.color};"}=label.name - %td.td3 - -if issue.pull_request + %td.td3{:class => (issue.pull_request ? 'td3-pull' : '')} + - if issue.pull_request %a{:href => path} .code # - .avatar - =link_to image_tag(avatar_url(issue.assignee), :alt => 'avatar'), user_path(issue.assignee) if issue.assignee - %a{:href => "#{path}#block-list"} - .answers - .pic= image_tag 'answers.png' - .count=issue.comments.count - .both + = link_to image_tag(avatar_url(issue.assignee), :alt => 'avatar', :class => 'avatar'), user_path(issue.assignee) if issue.assignee + %a.answers{:href => "#{path}#block-list"} + = image_tag 'answers.png', :class => 'pic' + .count=issue.comments.where(:automatic => false).count diff --git a/app/views/projects/issues/_manage_sidebar.html.haml b/app/views/projects/issues/_manage_sidebar.html.haml index 98144b2d2..1ba6fa829 100644 --- a/app/views/projects/issues/_manage_sidebar.html.haml +++ b/app/views/projects/issues/_manage_sidebar.html.haml @@ -1,8 +1,8 @@ -content_for :sidebar do - - can_manage = can?(:update, @issue) && @issue.persisted? || @issue.new_record? - if @issue.persisted? .bordered.nopadding %h3=t('activerecord.attributes.issue.status') + - can_manage = can?(:update, @issue) #switcher.issue_status{:class => "#{@issue.closed? ? 'switcher-off' : 'switcher'} #{can_manage ? "switch_issue_status" : ''}"} .swleft=t('layout.issues.status.open') .swright=t('layout.issues.status.closed') @@ -11,7 +11,7 @@ =hidden_field_tag "issue_status", @issue.closed? ? 'closed' : 'open', :name => "issue[status]" .block %h3=t('layout.issues.labels') - - if can_manage + - if can?(:write, @project) .current_labels - (@project.labels || []).each do |label| - is_issue_label = @issue.labels.include? label @@ -29,6 +29,6 @@ .label.nopointer .labeltext.selected{:style => "background: ##{label.color};"}=label.name .both - - if can_manage && @issue.persisted? + - if can?(:write, @project) && @issue.persisted? =link_to(t('layout.issues.label_manage'), '#', :class => "button tmargin10 manage_labels") =link_to(t('layout.issues.done'), '#', :class => "button tmargin10 update_labels", :style => 'display:none') diff --git a/app/views/projects/issues/_status.html.haml b/app/views/projects/issues/_status.html.haml index ecccf8157..1bf7b4602 100644 --- a/app/views/projects/issues/_status.html.haml +++ b/app/views/projects/issues/_status.html.haml @@ -4,6 +4,6 @@ .state=t('layout.issues.status.closed') .text .avatar= image_tag(avatar_url(@issue.closer), :alt => 'avatar') - .name="#{@issue.closer.fullname} #{t('layout.issues.at')} #{@issue.closed_at.to_s(:long)}" + .name="#{@issue.closer.fullname} #{datetime_moment(@issue.closed_at, :tag => :span)}".html_safe .both %br/ diff --git a/app/views/projects/issues/_user_container.html.haml b/app/views/projects/issues/_user_container.html.haml index 47b78037d..21c1ca3a6 100644 --- a/app/views/projects/issues/_user_container.html.haml +++ b/app/views/projects/issues/_user_container.html.haml @@ -6,5 +6,5 @@ %span= t('layout.issues.is_assigned') - else %span= t('layout.issues.no_one_is_assigned') - -if can?(:update, @issue) || @issue.new_record? + -if can?(:write, @project) %span.icon-share \ No newline at end of file diff --git a/app/views/projects/projects/_filters.html.haml b/app/views/projects/projects/_filters.html.haml index 12577ac68..6b578a3b1 100644 --- a/app/views/projects/projects/_filters.html.haml +++ b/app/views/projects/projects/_filters.html.haml @@ -6,6 +6,10 @@ - if can?(:create, Project) .bordered.bpadding20 = link_to t('layout.projects.new'), new_project_path, :class => 'button' + - if can?(:mass_import, Project) + .both + %br + = link_to t('layout.projects.mass_import'), mass_import_projects_path, :class => 'button' .bordered.bpadding20 %h3=t('layout.relations.filters') - options_for_filters(@all_projects, @groups, @owners).each do |options| diff --git a/app/views/projects/projects/_form.html.haml b/app/views/projects/projects/_form.html.haml index 27cb0ae02..04b3f4cb9 100644 --- a/app/views/projects/projects/_form.html.haml +++ b/app/views/projects/projects/_form.html.haml @@ -1,79 +1,69 @@ - act = controller.action_name.to_sym -- if [:new, :create].include? act +%div{'ng-controller' => 'ProjectFromController'} .leftlist= f.label :name - .rightlist= f.text_field :name, :class => 'text_field', :disabled => f.object.try(:persisted?) + .rightlist= f.text_field :name, :class => 'text_field' .both -.leftlist= f.label :description -.rightlist= f.text_area :description, :class => 'text_field', :cols => 80 -.both -- if [:new, :create].include? act - .leftlist= f.label :owner - .rightlist - = label_tag t("activerecord.attributes.project.who_owns.me") - - if Group.can_own_project(current_user).count > 0 - = radio_button_tag :who_owns, 'me', @who_owns == :me #{}.merge( (@who_owns == :me) ? {:checked => 'checked'} : {} ) - = label_tag t("activerecord.attributes.project.who_owns.group") - = radio_button_tag :who_owns, 'group', @who_owns == :group #{}.merge( (@who_owns == :group) ? {:checked => 'checked'} : {} ) - -# TODO: Make our own select_box helper with new design, blackjack and bitches! - = select_tag :owner_id, options_from_collection_for_select( Group.can_own_project(current_user), :id, :name ) - - else - = hidden_field_tag :who_owns, :me + .leftlist= f.label :description + .rightlist= f.text_area :description, :class => 'text_field', :cols => 80 .both + - if [:new, :create].include? act + = render 'owner', :f => f -.leftlist= f.label :visibility -.rightlist - =# f.select :visibility, Project::VISIBILITIES - - Project::VISIBILITIES.each do |visibility| - = f.radio_button :visibility, visibility, :class => 'niceRadio' - - if visibility == 'open' - = image_tag("unlock.png") - - else - = image_tag("lock.png") - = t("activerecord.attributes.project.visibilities.#{visibility}") -.both -.leftlist - \  -.rightlist - .check - %span#niceCheckbox1.niceCheck-main= f.check_box :is_package - .forcheck= f.label :is_package - .both - - unless [:new, :create].include? act - #publish_form{:class => @project.publish_i686_into_x86_64 ? '' : 'hidden'} - .check - %span.niceCheck-main= f.check_box :publish_i686_into_x86_64 - .forcheck= f.label :publish_i686_into_x86_64 - .both - -.both -- if [:edit, :update].include? act - .leftlist= f.label :default_branch + .leftlist= f.label :visibility .rightlist - = f.select :default_branch, - options_from_collection_for_select( @project.repo.branches, - :name, :name, @project.default_branch), - :class => 'sel80', :id => 'branch_selector' + - Project::VISIBILITIES.each do |visibility| + = f.radio_button :visibility, visibility, :class => 'niceRadio' + - if visibility == 'open' + = image_tag("unlock.png") + - else + = image_tag("lock.png") + = t("activerecord.attributes.project.visibilities.#{visibility}") .both - #maintainer_form{:class => @project.is_package ? '' : 'hidden'} - = f.hidden_field :maintainer_id, :value => @project.maintainer_id - .leftlist - = f.label :maintainer + .leftlist + \  + .rightlist + .check= f.check_box :is_package, 'ng-model' => 'project.is_package', 'ng-change' => 'project.publish_i686_into_x86_64 = false' + .forcheck= f.label :is_package + .both + - unless [:new, :create].include? act + #publish_form{'ng-show' => 'project.is_package'} + .check= f.check_box :publish_i686_into_x86_64, 'ng-model' => 'project.publish_i686_into_x86_64' + .forcheck= f.label :publish_i686_into_x86_64 + .both + + .both + - if [:edit, :update].include? act + .leftlist= f.label :default_branch .rightlist - -# TODO: Maybe use something like Chosen with filter and prepopulated - -# list of potential maintainers? - = autocomplete_field_tag :maintainer_name, @project.maintainer.fullname, - autocomplete_maintainers_path(@project.owner, @project), - :id_element => '#project_maintainer_id', - :placeholder => @project.maintainer.fullname -- if [:new, :create].include? act - .leftlist= f.label :srpm - .rightlist= f.file_field :srpm, :class => 'file_field' + = f.select :default_branch, + options_from_collection_for_select( @project.repo.branches, + :name, :name, @project.default_branch), + :class => 'sel80', :id => 'branch_selector' + .both + #maintainer_form{'ng-show' => 'project.is_package'} + = f.hidden_field :maintainer_id, :value => @project.maintainer_id + .leftlist + = f.label :maintainer + .rightlist + -# TODO: Maybe use something like Chosen with filter and prepopulated + -# list of potential maintainers? + = autocomplete_field_tag :maintainer_name, @project.maintainer.fullname, + autocomplete_maintainers_path(@project.owner, @project), + :id_element => '#project_maintainer_id', + :placeholder => @project.maintainer.fullname + - if [:new, :create].include? act + .leftlist= f.label :srpm + .rightlist= f.file_field :srpm, :class => 'file_field' + .both + .leftlist + \  + .rightlist= submit_tag t('layout.save'), :class => 'button', :data => {'disable-with' => t('layout.saving')} .both -.leftlist - \  -.rightlist= submit_tag t("layout.save"), :class => 'button' -.both + :javascript - $(function() { - ( new Rosa.Views.ProjectModifyView ).render(); - }); + RosaABF.controller('ProjectFromController', ['$scope', function($scope) { + $scope.project = { + is_package: #{@project.is_package}, + publish_i686_into_x86_64: #{@project.publish_i686_into_x86_64} + } + }]); \ No newline at end of file diff --git a/app/views/projects/projects/_owner.html.haml b/app/views/projects/projects/_owner.html.haml new file mode 100644 index 000000000..3d1d19653 --- /dev/null +++ b/app/views/projects/projects/_owner.html.haml @@ -0,0 +1,12 @@ +.leftlist= f.label :owner +.rightlist + = label_tag t("activerecord.attributes.project.who_owns.me") + - if Group.can_own_project(current_user).count > 0 + = radio_button_tag :who_owns, 'me', @who_owns == :me #{}.merge( (@who_owns == :me) ? {:checked => 'checked'} : {} ) + = label_tag t("activerecord.attributes.project.who_owns.group") + = radio_button_tag :who_owns, 'group', @who_owns == :group #{}.merge( (@who_owns == :group) ? {:checked => 'checked'} : {} ) + -# TODO: Make our own select_box helper with new design, blackjack and bitches! + = select_tag :owner_id, options_from_collection_for_select( Group.can_own_project(current_user), :id, :name ) + - else + = hidden_field_tag :who_owns, :me +.both \ No newline at end of file diff --git a/app/views/projects/projects/mass_import.html.haml b/app/views/projects/projects/mass_import.html.haml new file mode 100644 index 000000000..796ca274a --- /dev/null +++ b/app/views/projects/projects/mass_import.html.haml @@ -0,0 +1,27 @@ +%h3.bpadding10= title t("layout.projects.mass_import") += form_for @project, :url => run_mass_import_projects_path, :html => { :class => :form } do |f| + = f.hidden_field :mass_import + .leftlist= f.label :url + .rightlist= f.text_field :url + .both + .leftlist= f.label :srpms_list + .rightlist= f.text_area :srpms_list + .both + .leftlist= f.label :add_to_repository_id + .rightlist= f.select :add_to_repository_id, repositories_grouped_by_platform + .both + = render 'owner', :f => f + + .leftlist= f.label :visibility + .rightlist + - Project::VISIBILITIES.each do |visibility| + = f.radio_button :visibility, visibility, :class => 'niceRadio' + - if visibility == 'open' + = image_tag("unlock.png") + - else + = image_tag("lock.png") + = t("activerecord.attributes.project.visibilities.#{visibility}") + .both + .hr + .button_block + = f.submit t('layout.add'), :data => {'disable-with' => t('layout.saving')} \ No newline at end of file diff --git a/app/views/projects/projects/sections.html.haml b/app/views/projects/projects/sections.html.haml index 6bae42c0f..7289ecfb9 100644 --- a/app/views/projects/projects/sections.html.haml +++ b/app/views/projects/projects/sections.html.haml @@ -17,7 +17,7 @@ %br %span{:style => "font-size: 11px;"}= t("layout.projects.has_wiki_description") .both - .padd25= submit_tag t("layout.save"), :class => 'button' + .padd25= submit_tag t('layout.save'), :class => 'button', :data => {'disable-with' => t('layout.saving')} .both :javascript diff --git a/app/views/projects/pull_requests/_activity.html.haml b/app/views/projects/pull_requests/_activity.html.haml index cbde0e05b..7141c49db 100644 --- a/app/views/projects/pull_requests/_activity.html.haml +++ b/app/views/projects/pull_requests/_activity.html.haml @@ -5,7 +5,11 @@ -if item.is_a? Comment =render 'projects/git/commits/commits_small', :commits => commits_queue if commits_queue.present? -commits_queue.clear - =render 'projects/comments/comment', :comment => item, :data => {:project => @project, :commentable => @commentable} + -unless item.created_from_commit_hash + = render 'projects/comments/comment', :comment => item, :data => {:project => @project, :commentable => @commentable} + -else + - GitPresenters::CommitAsMessagePresenter.present(nil, :comment => item) do |presenter| + = render 'shared/feed_message', :presenter => presenter, :item_no => "-#{item.id}" -elsif item.is_a? Grit::Commit -commits_queue << item -elsif item.is_a? Array @@ -22,4 +26,3 @@ =render 'projects/pull_requests/discussion_comments', :item => item, :diff_counter => diff_counter - diff_counter += 1 =render 'projects/git/commits/commits_small', :commits => commits_queue if commits_queue.present? - diff --git a/app/views/projects/pull_requests/_index_sidebar.html.haml b/app/views/projects/pull_requests/_index_sidebar.html.haml index 05c051d32..df07380c7 100644 --- a/app/views/projects/pull_requests/_index_sidebar.html.haml +++ b/app/views/projects/pull_requests/_index_sidebar.html.haml @@ -6,11 +6,11 @@ %tr %td.width18=radio_button_tag :myradio, 'all', !@is_assigned_to_me, {:id => 'myradio1', :class => 'niceRadio', :name => 'filter'} %td.width135=t("layout.pull_requests.all") - %td.width30.right=@project.issues.joins(:pull_request).count + %td.width30.right=@project.issues.joins(:pull_request).not_closed_or_merged.count %tr %td=radio_button_tag :myradio, 'to_me', @is_assigned_to_me, {:id => 'myradio1', :class => 'niceRadio', :name => 'filter'} %td=t("layout.pull_requests.to_me") - %td.width30.right=@project.issues.joins(:pull_request).where(:assignee_id => current_user.id).count + %td.width30.right=@project.issues.joins(:pull_request).where(:assignee_id => current_user.id).not_closed_or_merged.count =form_tag project_pull_requests_path(@project), :id => 'filter_pull_requests', :method => :get, :class => 'ajax_search_form' do .bordered.bpadding20 - search = params[:search_pull_request].present? ? params[:search_pull_request] : t('layout.pull_requests.search') diff --git a/app/views/projects/pull_requests/_pull_diff.html.haml b/app/views/projects/pull_requests/_pull_diff.html.haml index 60fbe55ff..baf8b9ece 100644 --- a/app/views/projects/pull_requests/_pull_diff.html.haml +++ b/app/views/projects/pull_requests/_pull_diff.html.haml @@ -2,9 +2,8 @@ .file %a{:name => "diff-#{pull_diff_counter}"} .top - .l= h(pull_diff.renamed_file ? "#{pull_diff.a_path.rtruncate 60} -> #{pull_diff.b_path.rtruncate 60}" : pull_diff.a_path.rtruncate(120)) - - if pull_diff.b_path.present? - .r= link_to "view file @ #{short_hash_id(commit_id)}", blob_path(@project, commit_id, pull_diff.b_path) + .l= h(pull_diff.renamed_file ? "#{pull_diff.a_path.rtruncate 60}=>#{pull_diff.b_path.rtruncate 60}" : pull_diff.b_path.rtruncate(120)) + .r= link_to "view file @ #{short_hash_id(commit_id)}", blob_path(@project, commit_id, pull_diff.b_path) .clear -if pull_diff.diff.present? && !(@pull.repo.tree(commit_id) / pull_diff.b_path).binary? .diff_data=render_diff(pull_diff, :diff_counter => pull_diff_counter, :comments => @comments) diff --git a/app/views/projects/pull_requests/_status.html.haml b/app/views/projects/pull_requests/_status.html.haml index de98651d4..84fbeb1d1 100644 --- a/app/views/projects/pull_requests/_status.html.haml +++ b/app/views/projects/pull_requests/_status.html.haml @@ -1,14 +1,36 @@ -- if can?(:merge, @pull) && @pull.can_merging? - %br - =form_for PullRequest.new, :url => merge_project_pull_request_path(@project, @pull), :html => { :method => :put, :class => :form } do |f| - =f.submit t 'projects.pull_requests.ready' --else - .flash - %div{:class => @pull.ready? ? 'notice' : 'alert'} - =pull_status @pull +- if can?(:merge, @pull) + %a.button{:href => '', 'ng-click' => 'merge()', 'ng-show' => "pull.status == 'ready'"} + = t 'projects.pull_requests.ready' + .both + +.flash{'ng-show' => '!pull.mergeable'} + .notice{'ng-show' => "pull.status == 'blocked'"} + = t "projects.pull_requests.blocked" + .alert{'ng-show' => "pull.status == 'merged'"} + = t("projects.pull_requests.merged", + :user => '{{pull.merged_by.uname}}', + :to_ref => show_ref(@pull, 'to'), + :from_ref => show_ref(@pull, 'from'), + :time => '{{merged_at}}').html_safe + .alert{'ng-show' => "pull.status == 'closed'"} + = t("projects.pull_requests.closed", + :user => '{{pull.closed_by.uname}}', + :time => '{{closed_at}}') +.both + + +- if !@pull.cross_pull? && can?(:write, @project) + %div{'ng-init' => "getBranch('#{@pull.from_ref}')", 'ng-show' => "pull.status == 'closed' || pull.status == 'merged'"} + %a.button{:href => '', 'ng-click' => 'deleteBranch()', 'ng-show' => "branch && branch.object.sha == pull.from_ref.sha"} + = t('layout.projects.delete_branch') + %a.button{:href => '', 'ng-click' => 'restoreBranch()', 'ng-hide' => "branch"} + = t('layout.projects.restore_branch') + .both + + -if can? :update, @pull - -if action = @pull.can_close? ? 'close' : ('reopen' if @pull.can_reopen?) - %br - =form_for :pull, :url => [@project, @pull], :html => { :id => 'do_pull_action',:method => :put, :class => :form } do |f| - =hidden_field_tag "pull_request_action", action - =f.submit t ".#{action}" + %br + %a.button{:href => '', 'ng-click' => 'reopen()', 'ng-show' => "pull.status == 'closed'"} + = t '.reopen' + %a.button{:href => '', 'ng-click' => 'close()', 'ng-show' => "pull.status == 'ready' || pull.status == 'open' || pull.status == 'blocked'"} + = t '.close' \ No newline at end of file diff --git a/app/views/projects/pull_requests/new.html.haml b/app/views/projects/pull_requests/new.html.haml index a4d30a2d8..96f559cec 100644 --- a/app/views/projects/pull_requests/new.html.haml +++ b/app/views/projects/pull_requests/new.html.haml @@ -21,7 +21,7 @@ =render 'ref_select', :kind => 'to', :project => @pull.to_project, :current => @pull.to_ref .both .leftlist.big-list - .rightlist=f.submit t('.update'), :class => 'btn btn-primary disabled', 'data-loading-text' => t('layout.processing'), :style => @pull.already? ? '' : 'display: none;', :id => 'update_pull' + .rightlist=f.submit t('.update'), :class => 'btn btn-primary', :style => @pull.already? ? '' : 'display: none;', :id => 'update_pull', :data => {'disable-with' => t('layout.processing')} .both -unless @pull.already? =f.fields_for :issue do |issue| @@ -32,14 +32,14 @@ %div{:class => @pull.ready? ? 'notice' : 'alert'} =pull_status @pull .both - - .leftlist.big-list= t('activerecord.attributes.issue.assignee') + ':' - #assigned-container.rightlist - =render 'projects/issues/user_container', :user => @pull.assignee - .both + - if can?(:write, @pull.to_project) + .leftlist.big-list= t('activerecord.attributes.issue.assignee') + ':' + #assigned-container.rightlist + =render 'projects/issues/user_container', :user => @pull.assignee + .both .leftlist.big-list .rightlist - =f.submit t('.submit'), :class => 'btn btn-primary disabled', 'data-loading-text' => t('layout.processing'), :id => 'create_pull' unless @pull.already? + =f.submit t('.submit'), :class => 'btn btn-primary', :id => 'create_pull', :data => {'disable-with' => t('layout.processing')} unless @pull.already? .both =render 'diff_commits_tabs' if !@pull.already? && @stats != nil diff --git a/app/views/projects/pull_requests/show.html.haml b/app/views/projects/pull_requests/show.html.haml index c1de1d3b9..9e653b0c3 100644 --- a/app/views/projects/pull_requests/show.html.haml +++ b/app/views/projects/pull_requests/show.html.haml @@ -1,19 +1,23 @@ -ar = 'activerecord.attributes.pull_requests' -set_meta_tags :title => [title_object(@project), t('.title', :name => @pull.title.truncate(40), :user => @pull.user.try(:uname))] = render :partial => 'submenu' -%h3.bpadding10 - =pull_status_label @pull - =pull_header @pull -#repo-wrapper - =render 'nav_tabs' - .tab-content.pull_diff_fix - #discussion.tab-pane.active - =render 'projects/issues/header' - =render 'activity' - %br - =render "projects/comments/add", :project => @project, :commentable => @issue if current_user - .pull_status - =render 'status' - =render 'diff_commits_tabs' unless @pull.already? -=hidden_field_tag :preview_url, project_md_preview_path(@project) -= render "projects/comments/markdown_help" + +%div{'ng-controller' => 'PullRequestController', 'ng-init' => "init('#{@project.owner.uname}', '#{@project.name}', '#{@pull.serial_id}')"} + + %h3.bpadding10 + - PullRequest::STATUSES.each do |status| + = pull_status_label status, {'ng-show' => "pull.status == '#{status}'", :style => 'display: none;'} + = pull_header @pull + #repo-wrapper + =render 'nav_tabs' + .tab-content.pull_diff_fix + #discussion.tab-pane.active + =render 'projects/issues/header' + =render 'activity' + %br + =render "projects/comments/add", :project => @project, :commentable => @issue if current_user + .pull_status + =render 'status' + =render 'diff_commits_tabs' unless @pull.already? + =hidden_field_tag :preview_url, project_md_preview_path(@project) + = render "projects/comments/markdown_help" diff --git a/app/views/projects/pull_requests/show.json.jbuilder b/app/views/projects/pull_requests/show.json.jbuilder new file mode 100644 index 000000000..2496d7742 --- /dev/null +++ b/app/views/projects/pull_requests/show.json.jbuilder @@ -0,0 +1,41 @@ +json.pull_request do + + json.number @pull.serial_id + json.(@pull, :status) + json.to_ref do + json.ref @pull.to_ref + json.sha @pull.to_commit.try(:id) + json.project do + json.(@pull.to_project, :id, :name) + json.owner_uname @pull.to_project.owner.uname + end + end + json.from_ref do + json.ref @pull.from_ref + json.sha @pull.from_commit.try(:id) + json.project do + json.(@pull.from_project, :id, :name) + json.owner_uname @pull.to_project.owner.uname + end + end + + json.owner do + json.(@pull.user, :id, :name, :uname) + end + + json.assignee do + json.(@pull.issue.assignee, :id, :name, :uname) + end if @pull.issue.assignee + json.mergeable @pull.can_merging? + json.merged_at @pull.issue.closed_at.to_i if @pull.merged? + + json.closed_at @pull.issue.closed_at.to_i if @pull.merged? || @pull.closed? + if @pull.issue.closer + json.closed_by do + json.(@pull.issue.closer, :id, :name, :uname) + end + json.merged_by do + json.(@pull.issue.closer, :id, :name, :uname) + end if @pull.merged? + end +end diff --git a/app/views/projects/wiki/_editor.html.haml b/app/views/projects/wiki/_editor.html.haml index cddc9323d..1869eaf58 100644 --- a/app/views/projects/wiki/_editor.html.haml +++ b/app/views/projects/wiki/_editor.html.haml @@ -33,7 +33,7 @@ %span.jaws %br - = submit_tag t("wiki.save_button"), :id => "gollum-editor-submit", :title => t("wiki.save_changes") + = submit_tag t('wiki.save_button'), :id => 'gollum-editor-submit', :title => t('wiki.save_changes'), :data => {'disable-with' => t('layout.saving')} = link_to t("wiki.preview"), "javascript:void(0)", :id => "gollum-editor-preview", :class => "minibutton", :title => t("wiki.preview_title"), :'data-url' => preview_project_wiki_index_path(@project) / - content_for :javascripts do diff --git a/app/views/projects/wiki/_history.html.haml b/app/views/projects/wiki/_history.html.haml index 253717874..6b7c91127 100644 --- a/app/views/projects/wiki/_history.html.haml +++ b/app/views/projects/wiki/_history.html.haml @@ -16,7 +16,7 @@ %span.username= user.present? ? user.fullname : v.author.name .both %td.td3 - %span.wiki-gray= "#{l v.committed_date.to_date, :format => :long}:" + = datetime_moment v.committed_date, :tag => :span, :class => 'wiki-gray' = v.message - if @name = raw "[#{link_to v.id[0..6], versioned_project_wiki_path(@project, escaped_name, v.id), :title => t("wiki.view_commit")}]" diff --git a/app/views/projects/wiki/_page.html.haml b/app/views/projects/wiki/_page.html.haml index e7b51c2e0..e759c99ff 100644 --- a/app/views/projects/wiki/_page.html.haml +++ b/app/views/projects/wiki/_page.html.haml @@ -12,8 +12,7 @@ %p#last-edit = t("wiki.last_edited_by") %b= user_link_by_user User.where(:email => author_email).first - = time_ago_in_words date.to_time - 4.hours, true - = t("layout.time.ago") + = datetime_moment date, :tag => :span - unless action_name == 'preview' or cannot? :write, @project %p#delete-link = link_to project_wiki_path(@project, escaped_name), :method => :delete, :confirm => t("layout.confirm") do diff --git a/app/views/search/_form_advanced.html.haml b/app/views/search/_form_advanced.html.haml index ab704ec69..7615e109e 100644 --- a/app/views/search/_form_advanced.html.haml +++ b/app/views/search/_form_advanced.html.haml @@ -1,5 +1,5 @@ = form_tag search_index_path, :method => 'get' do .leftside= text_field_tag 'query', @query, :placeholder => t("layout.search.header"), :class => 'exsearch' .lineForm.leftside.rmargin10= select_tag 'type', options_for_select(t('layout.search.types').invert, @type), :class => 'sel80 cusel', :id => 'selSearch' - .leftside= submit_tag t("layout.search.header"), :class => 'button width100' + .leftside= submit_tag t('layout.search.header'), :class => 'button width100', :data => {'disable-with' => t('layout.processing')} .both \ No newline at end of file diff --git a/app/views/shared/_angularjs_will_paginate.html.haml b/app/views/shared/_angularjs_will_paginate.html.haml new file mode 100644 index 000000000..d2193cce9 --- /dev/null +++ b/app/views/shared/_angularjs_will_paginate.html.haml @@ -0,0 +1,14 @@ +#will_paginate{'ng-show' => 'pages'} + .pagination + %div{'ng-class' => "{'disabled': !page.active}", 'ng-repeat' => 'page in pages', 'ng-switch' => 'page.type'} + %a{'ng-switch-when' => 'previous_page', 'ng-click' => 'goToPage(page.number)', :href => ''} + = t('datatables.previous_label') + %a{'ng-switch-when' => 'first', :href => ''} + {{page.number}} + %a{'ng-switch-when' => 'page', 'ng-click' => 'goToPage(page.number)', :href => ''} + {{page.number}} + %a{'ng-switch-when' => 'more', 'ng-click' => 'goToPage(page.number)', :href => ''} … + %a{'ng-switch-when' => 'last', 'ng-click' => 'goToPage(page.number)', :href => ''} + {{page.number}} + %a{'ng-switch-when' => 'next_page', 'ng-click' => 'goToPage(page.number)', :href => ''} + = t('datatables.next_label') \ No newline at end of file diff --git a/app/views/shared/_autocomplete_form.html.haml b/app/views/shared/_autocomplete_form.html.haml new file mode 100644 index 000000000..cef3191bc --- /dev/null +++ b/app/views/shared/_autocomplete_form.html.haml @@ -0,0 +1,44 @@ +-# + Variables: + subject + field + field_class + autocomplete_path + placeholder + default_values (optional) + +- default_values ||= [] +- subject_class = subject.class.name.underscore +.autocomplete-form{:subject_class => subject_class, :field => field, :path => autocomplete_path, :class => field} + %h3= t("activerecord.attributes.build_list.#{field}") + %span.icon-question-sign + - if !(params[:show] == 'inline' && params[:build_list_id].present?) + .dialog{:title => t("activerecord.attributes.build_list.#{field}"), :id => "#{field}_dialog"} + = render "shared/autocomplete_docs/#{field}" + .both + = autocomplete_field_tag field, nil, "#{autocomplete_path}?#{{:platform_id => subject.save_to_platform.try(:id)}.to_param}", :placeholder => placeholder, :class => 'autocomplete', :update_elements => {:id => "##{field}_field", :path => "##{field}_field_path", :label => "##{field}_field_label"} + = hidden_field_tag field, nil, :id => "#{field}_field" + = hidden_field_tag field, nil, :id => "#{field}_field_path" + = hidden_field_tag field, nil, :id => "#{field}_field_label" + = submit_tag t('layout.add'), :class => 'button add' + + %table.tablesorter{:cellpadding => "0", :cellspacing => "0"} + %tbody + - field_name = "#{subject_class}[#{field}][]" + - field_class.where(:id => params[:build_list].try(:[], field) ).each do |extra| + %tr + - if extra.is_a?(BuildList) + %td= link_to "#{extra.id} (#{extra.project.name} - #{extra.arch.name})", extra + - else + %td= link_to "#{extra.platform.name}/#{extra.name}", [extra.platform, extra] + %td.actions + - field = extra.is_a?(BuildList) ? 'extra_build_lists' : 'extra_repositories' + = hidden_field_tag field_name, extra.id + %span.delete   + .default-values + - field_class.where(:id => default_values).each do |extra| + .hidden{:label => "#{extra.platform.name}/#{extra.name}", + :path => url_for([extra.platform, extra]), + :name => field_name, :value => extra.id} + + diff --git a/app/views/shared/_build_results.html.haml b/app/views/shared/_build_results.html.haml new file mode 100644 index 000000000..7d68edc0d --- /dev/null +++ b/app/views/shared/_build_results.html.haml @@ -0,0 +1,15 @@ +%h3= subject.class.human_attribute_name(subject.is_a?(BuildList) ? 'logs' : 'results') +%h4.nomargin{'ng-hide' => 'subject.results'}= t('layout.no_') +%table.tablesorter.width565{:cellpadding => "0", :cellspacing => "0", 'ng-show' => 'subject.results'} + %thead + %tr + %th= t("activerecord.attributes.product_build_list/results.file_name") + %th= t("activerecord.attributes.product_build_list/results.sha1") + %th= t("activerecord.attributes.product_build_list/results.size") + %tbody + %tr{'ng-repeat' => 'item in subject.results'} + %td + %a{'ng-href' => '{{item.url}}' } {{item.file_name}} + %td {{item.sha1}} + %td {{item.size}} +.both \ No newline at end of file diff --git a/app/views/shared/_feed_message.html.haml b/app/views/shared/_feed_message.html.haml index 414b4cc9b..c498b07a2 100644 --- a/app/views/shared/_feed_message.html.haml +++ b/app/views/shared/_feed_message.html.haml @@ -1,23 +1,28 @@ -.activity{:id => presenter.comment_id? ? presenter.comment_anchor : ''} - .top - - if presenter.buttons? - %span.buttons= raw presenter.buttons.join(' | ').html_safe - .image - %img{:alt => "avatar", :src => presenter.image} - .text - -#.imaged move up a line. - %span.name= presenter.header - .both - %span.date= presenter.date - .both - - if presenter.caption? - %span.subject= presenter.caption - - if presenter.expandable? and presenter.content? - %span.data-expander.collapsed{:id => "expand#{item_no}"}   +-if !presenter.is_reference_to_issue || can?(:show, presenter.reference_project) + .activity{:id => presenter.comment_id? ? presenter.comment_anchor : ''} + .top + - if presenter.buttons? + %span.buttons= raw presenter.buttons.join(' | ').html_safe + .image + %img{:alt => "avatar", :src => presenter.image} + .text + -#.imaged move up a line. + %span.name= presenter.header .both - .both - - if presenter.content? - .fulltext{:class => "#{presenter.expandable? ? "hidden" : ''} #{presenter.caption? ? "" : "alone"}", - :id => presenter.expandable? ? "content-expand#{item_no}" : ''} - .cm-s-default.md_and_cm=markdown presenter.content + = datetime_moment presenter.date, :tag => :span, :class => :date + .both + - if presenter.caption? + %span.subject= presenter.caption + - if presenter.expandable? and presenter.content? + %span.data-expander.collapsed{:id => "expand#{item_no}"}   + .both .both + - if presenter.content? + %div + =presenter.issue_referenced_state if presenter.issue_referenced_state? + .fulltext{:class => "#{presenter.expandable? ? "hidden" : ''} #{presenter.caption? ? "" : "alone"}", + :id => presenter.expandable? ? "content-expand#{item_no}" : ''} + .md_and_cm{:class => presenter.is_reference_to_issue ? '' : 'cm-s-default'} + =presenter.is_reference_to_issue ? presenter.content : markdown(presenter.content) + .both + diff --git a/app/views/shared/_log.html.haml b/app/views/shared/_log.html.haml index 6ff0518d7..30bc027ec 100644 --- a/app/views/shared/_log.html.haml +++ b/app/views/shared/_log.html.haml @@ -28,19 +28,15 @@ = check_box_tag :autoreload, true, build_started = t("layout.build_lists.log.autoreload") = select_tag :reload_interval, log_reload_time_options - %tr.bottom - %td.first - - if download_log_url - = link_to t("layout.build_lists.log.download"), download_log_url, :id => :log_url - %td.last{ :class => build_started ? nil : :hidden } - = label_tag :load_lines do - = raw t("layout.build_lists.log.load_lines", :count => select_tag(:load_lines, log_reload_lines_options)) + + .both + #output.cm-s-default.log{ :readonly => :readonly, + :data => { :url => get_log_path, :log_type => :build } } + %pre#code .both - %textarea.log{ :readonly => :readonly, :wrap => 'off', - :data => { :url => get_log_path, :log_type => :build } } - = t("layout.build_lists.log.not_available") .both + :javascript $(function() { initLogWrapper(); diff --git a/app/views/shared/_members_table.html.haml b/app/views/shared/_members_table.html.haml index 306e9e8aa..816287a83 100644 --- a/app/views/shared/_members_table.html.haml +++ b/app/views/shared/_members_table.html.haml @@ -25,8 +25,8 @@ %td.buttons = link_to "#{remove_member_path}?member_id=#{user.id}", :method => :delete, :confirm => t("layout.confirm") do %span.delete   - - if can? :remove_memvers, editable_object - = submit_tag t("layout.delete"), :class => 'button' + - if can? :remove_members, editable_object + = submit_tag t("layout.delete"), :class => 'button', :data => {'disable-with' => t('layout.processing')} .both - if can? :add_member, editable_object @@ -35,5 +35,5 @@ .admin-search = autocomplete_field_tag 'member_id', params[:member_id], autocomplete_user_uname_autocompletes_path, :id_element => '#member_id_field' = hidden_field_tag 'member_id', nil, :id => 'member_id_field' - = submit_tag t("layout.add"), :class => 'button' + = submit_tag t('layout.add'), :class => 'button', :data => {'disable-with' => t('layout.processing')} .both diff --git a/app/views/shared/_profile.html.haml b/app/views/shared/_profile.html.haml index 6f8c14778..51a229d0d 100644 --- a/app/views/shared/_profile.html.haml +++ b/app/views/shared/_profile.html.haml @@ -1,38 +1,54 @@ -- edit_link ||= nil +- edit_url ||= nil - user ||= nil - group ||= nil - name ||= uname -.avatar - = image_tag avatar_url(user || group, :big) - - if edit_link - %br - = edit_link -.info - %h3= title uname - = name - - if user - %br - = mail_to user.email, user.email, :encode => "javascript" - %br - - if user - %h4= t("activerecord.attributes.user.professional_experience") + ":" - %p= user.professional_experience - - else - %h4= t("activerecord.attributes.group.description") + ":" - %p= group.description -.content - %h4= t("layout.projects.public_projects_list") + ":" - %p - =form_tag search_path, :id => 'filter_projects', :method => :get do - =tracker_search_field(:search, t('layout.find_project')) - %br - %p - - projects.each do |project| - = link_to project.name, project - %br - %br - = will_paginate projects - %br +- desc, desc_title = if user + - [user.professional_experience, t('activerecord.attributes.user.professional_experience')] +- else + - [group.description , t('activerecord.attributes.group.description')] +- max_length = 35 -:javascript - $('article .all').addClass('verybigpadding'); += hidden_field_tag :profile_path, @profile_path +.row + .span3.profile + .avatar= image_tag avatar_url(user || group, :big), :alt => (user || group).uname + .base_info + %h3{:title => uname}= title short_message(uname, 16) + %p{:title => user.try(:name)} + - if user + = short_message(user.name, 28) + = link_to image_tag('gears.png'), edit_url if edit_url.present? + .both + - if user + %p.info.first + - message = "#{t 'activerecord.attributes.user.email'}: " + = message + = mail_to user.email, short_message(user.email, max_length - message.length), :encode => 'javascript', :title => user.email + %p.info + - message = "#{t 'activerecord.attributes.user.site'}: " + = message + = link_to short_message(user.site, max_length - message.length), user.site, :title => user.site + %p.info{:title => user.company}= short_message("#{t 'activerecord.attributes.user.company'}: #{user.company}", max_length) + %p.info{:title => user.location}= short_message("#{t 'activerecord.attributes.user.location'}: #{user.location}", max_length) + .span6 + %h3{:style => 'margin-top: 0;'}= desc_title + = desc + +%hr.profile_line{:color => 'dfe8ef', :size => '3'} + +.row-fluid.profile-content + .span12.content + %nav + %ul + %li + = link_to t('layout.projects.list_header'), '#', :class => 'projects active' + .span12.sub-menu + %nav + %ul + %li= link_to t('layout.projects.public'), '#', :class => "public-projects #{!@hidden ? 'active' : ''}" + %li= link_to t('layout.projects.private'), '#', :class => "private-projects #{@hidden ? 'active' : ''}" + .search + .pic + .field= text_field_tag :query_projects, @query, :placeholder => t('layout.find_project') + .both + .profile-table= render 'shared/profile_projects', :projects => projects diff --git a/app/views/shared/_profile_projects.html.haml b/app/views/shared/_profile_projects.html.haml new file mode 100644 index 000000000..c832cadab --- /dev/null +++ b/app/views/shared/_profile_projects.html.haml @@ -0,0 +1,17 @@ +- pr_groups = projects.in_groups(2) +%table + %tbody + - pr_groups[0].each_with_index do |project, ind| + %tr{:class => ind.odd? ? 'odd' : 'even'} + - [project, pr_groups[1][ind]].each do |project| + %th + - if project.present? + .project-link= link_to short_message(project.name, 60), project, :title => project.name + .both + .row-fluid + = datetime_moment project.updated_at, :class => :span3 + - commits_count = project.total_commits_count + .span3= "#{commits_count > 10000 ? '10000+' : commits_count} #{commits_pluralize(commits_count)}" +%br +%div{:style => 'margin: 10px;'}= will_paginate projects + diff --git a/app/views/shared/_search_form.html.haml b/app/views/shared/_search_form.html.haml deleted file mode 100644 index 656cc717b..000000000 --- a/app/views/shared/_search_form.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -= form_tag '#', :method => :get do#platform_repository_path(@platform, @repository), :method => :get do - .group - = label_tag :query, t("layout.search_by_name"), :class => :label - = text_field_tag :query - %button.search{:type => "submit"}= t("layout.search.header") \ No newline at end of file diff --git a/app/views/shared/autocomplete_docs/_extra_build_lists.en.html.haml b/app/views/shared/autocomplete_docs/_extra_build_lists.en.html.haml new file mode 100644 index 000000000..5feb8d951 --- /dev/null +++ b/app/views/shared/autocomplete_docs/_extra_build_lists.en.html.haml @@ -0,0 +1,12 @@ +:markdown + + __Ability to attach at assembly packages of build lists as additional sources of packages.__ +
+ + Requirements for attaching build lists: + + - only build list with container can be connected; + - for main platform You can connect only build lists which destined for same platform; + - only build lists with the same architecture will be connected or oriented to the both architectures (the property Publish i686 packages into x86_64 repository (only for rhel) in the settings of project is true). + + _Example: only build lists with x86_64 architecture will be connected to x86_64._ \ No newline at end of file diff --git a/app/views/shared/autocomplete_docs/_extra_build_lists.ru.html.haml b/app/views/shared/autocomplete_docs/_extra_build_lists.ru.html.haml new file mode 100644 index 000000000..2b44d61e4 --- /dev/null +++ b/app/views/shared/autocomplete_docs/_extra_build_lists.ru.html.haml @@ -0,0 +1,12 @@ +:markdown + + __Возможность подключить для сборки пакеты из сборочных заданий как дополнительные источники пакетов.__ +
+ + Требования для подключаемых сборочных заданий: + + - можно подключить только задание с контейнером; + - для основных платформ возможно подключить только задания, которые предназначены для этой же платформы; + - будут подключены только задания с архитектурой, соотвествующей создаваемой, или предназначенные для обеих архитектур (если установлено соответствующее свойство в настройках rhel-проекта). + + _Пример: для x86_64 архитектуры будут подключены только сборочные задания, собранные для x86_64._ \ No newline at end of file diff --git a/app/views/shared/autocomplete_docs/_extra_repositories.en.html.haml b/app/views/shared/autocomplete_docs/_extra_repositories.en.html.haml new file mode 100644 index 000000000..d11733dfa --- /dev/null +++ b/app/views/shared/autocomplete_docs/_extra_repositories.en.html.haml @@ -0,0 +1,14 @@ +:markdown + + __Ability to attach at assembly repositories of platforms as additional sources of packages.__ +
+ + Requirements for attaching repositories: + + - attaching repositories from other platform available only for build list into the personal platform. + + How to attach a repository: + + - write the name of platform, choose repository from the list for attaching and click to "Add" button. + + _Example: correct input "uxteam_personal", wrong - "uxteam_personal/main"._ \ No newline at end of file diff --git a/app/views/shared/autocomplete_docs/_extra_repositories.ru.html.haml b/app/views/shared/autocomplete_docs/_extra_repositories.ru.html.haml new file mode 100644 index 000000000..c26de1d1e --- /dev/null +++ b/app/views/shared/autocomplete_docs/_extra_repositories.ru.html.haml @@ -0,0 +1,14 @@ +:markdown + + __Возможность подключить для сборки репозитории платформ как дополнительные источники пакетов.__ +
+ + Требования для репозиториев: + + - использовать репозитории из других персональных платформ возможно только для сборки в персональную платформу. + + Как подключить репозиторий: + + - добавьте имя платформы, затем из списка выберете репозиторий для поключения и нажмите кнопку "Добавить". + + _Пример: правильно ввести "uxteam_personal", неправильно: "uxteam_personal/main"._ \ No newline at end of file diff --git a/app/views/user_mailer/build_list_notification.en.haml b/app/views/user_mailer/build_list_notification.en.haml index afd0752d6..b2baaf6df 100644 --- a/app/views/user_mailer/build_list_notification.en.haml +++ b/app/views/user_mailer/build_list_notification.en.haml @@ -10,6 +10,6 @@ = "#{@build_list.arch.name}." %p More detailed information you can get by link: - = link_to "task [№ #{@build_list.bs_id ? @build_list.bs_id : t("layout.build_lists.bs_id_not_set")}]", build_list_url(@build_list) + = link_to "task [№ #{@build_list.id}]", build_list_url(@build_list) = render 'footer' diff --git a/app/views/user_mailer/build_list_notification.ru.haml b/app/views/user_mailer/build_list_notification.ru.haml index 21401beaa..34ea53d51 100644 --- a/app/views/user_mailer/build_list_notification.ru.haml +++ b/app/views/user_mailer/build_list_notification.ru.haml @@ -10,6 +10,6 @@ = "#{@build_list.arch.name}." %p Более подробную информацию можно получить по ссылке: - = link_to "задание [№ #{@build_list.bs_id ? @build_list.bs_id : t("layout.build_lists.bs_id_not_set")}]", build_list_url(@build_list) + = link_to "задание [№ #{@build_list.id}]", build_list_url(@build_list) = render 'footer' diff --git a/app/views/user_mailer/git_delete_branch_notification.html.haml b/app/views/user_mailer/git_delete_branch_notification.html.haml new file mode 100644 index 000000000..31b46c01f --- /dev/null +++ b/app/views/user_mailer/git_delete_branch_notification.html.haml @@ -0,0 +1,8 @@ +- user = User.where(:email => user_email).first || User.new(:email => user_email) if defined?(user_email) + +%p + - _user_link = defined?(user_email) ? user_link(user, defined?(user_name) ? user_name : user_email, true) : nil + = t('notifications.bodies.delete_branch', :branch_name => branch_name, :user_link => _user_link).html_safe + = raw t("notifications.bodies.project", :project_link => link_to("#{project_owner}/#{project_name}", project_url(project_owner, project_name)) ) + += render 'footer' \ No newline at end of file diff --git a/app/views/user_mailer/git_new_push_notification.html.haml b/app/views/user_mailer/git_new_push_notification.html.haml new file mode 100644 index 000000000..62d24ec8f --- /dev/null +++ b/app/views/user_mailer/git_new_push_notification.html.haml @@ -0,0 +1,20 @@ +- user = User.where(:email => user_email).first || User.new(:email => user_email) if defined?(user_email) + + +%p + - _user_link = defined?(user_email) ? user_link(user, defined?(user_name) ? user_name : user_email, true) : nil + = raw t("notifications.bodies.#{change_type}_branch", {:branch_name => branch_name, :user_link => _user_link}) + = raw t("notifications.bodies.project", :project_link => link_to("#{project_owner}/#{project_name}", project_url(project_owner, project_name)) ) + + +%p + - last_commits.each do |commit| + = link_to shortest_hash_id(commit[0]), commit_url(project_owner, project_name, commit[0]) + = commit[1] + %br + - if defined? other_commits + %br + =link_to t('notifications.bodies.more_commits', :count => other_commits_count, :commits => commits_pluralize(other_commits_count)), + diff_url(project_owner, project_name, :diff => other_commits) + += render 'footer' \ No newline at end of file diff --git a/app/views/user_mailer/issue_assign_notification.en.haml b/app/views/user_mailer/issue_assign_notification.en.haml index 3aa4a049a..91522375b 100644 --- a/app/views/user_mailer/issue_assign_notification.en.haml +++ b/app/views/user_mailer/issue_assign_notification.en.haml @@ -1,7 +1,3 @@ -%p== Hello, #{@user.user_appeal}. - - %p You have been assigned to issue #{ link_to @issue.title, project_issue_url(@issue.project, @issue) } - -= render 'footer' += render 'footer' \ No newline at end of file diff --git a/app/views/user_mailer/issue_assign_notification.ru.haml b/app/views/user_mailer/issue_assign_notification.ru.haml index 5b5dcd7d5..616f120de 100644 --- a/app/views/user_mailer/issue_assign_notification.ru.haml +++ b/app/views/user_mailer/issue_assign_notification.ru.haml @@ -1,7 +1,3 @@ -%p== Здравствуйте, #{@user.user_appeal}. - - %p Вам была назначена задача #{ link_to @issue.title, project_issue_url(@issue.project, @issue) } - -= render 'footer' += render 'footer' \ No newline at end of file diff --git a/app/views/users/base/_form.html.haml b/app/views/users/base/_form.html.haml index e661a9191..c4cccf559 100644 --- a/app/views/users/base/_form.html.haml +++ b/app/views/users/base/_form.html.haml @@ -32,7 +32,10 @@ .leftlist= f.label :professional_experience, t("activerecord.attributes.user.professional_experience") .rightlist= f.text_area :professional_experience .both +.leftlist= f.label :sound_notifications, t("activerecord.attributes.user.sound_notifications") +.rightlist= f.check_box :sound_notifications +.both .leftlist \  -.rightlist= submit_tag t("layout.save"), :data => {:"disable-with" => t("layout.saving")} +.rightlist= submit_tag t('layout.save'), :data => {'disable-with' => t('layout.saving')} .both diff --git a/app/views/users/profile/show.html.haml b/app/views/users/profile/show.html.haml index 5c16bf2fa..914a2d558 100644 --- a/app/views/users/profile/show.html.haml +++ b/app/views/users/profile/show.html.haml @@ -1,4 +1,10 @@ -set_meta_tags :title => title_object(@user) -- edit_link = can?(:edit, @user) ? link_to(t("layout.users.settings"), current_user == @user ? profile_settings_path : edit_admin_user_path(@user), :class => 'button width81') : nil -= render 'shared/profile', :uname => @user.uname, :name => @user.name, :user => @user, :search_path => user_path, :projects => @projects, :edit_link => edit_link \ No newline at end of file +- edit_url = can?(:edit, @user) ? (current_user == @user ? profile_settings_path : edit_admin_user_path(@user)) : nil + += render 'shared/profile', :uname => @user.uname, + :name => @user.name, + :user => @user, + :search_path => user_path, + :projects => @projects, + :edit_url => edit_url \ No newline at end of file diff --git a/app/views/users/settings/notifiers.html.haml b/app/views/users/settings/notifiers.html.haml index d5917480a..78bb398c6 100644 --- a/app/views/users/settings/notifiers.html.haml +++ b/app/views/users/settings/notifiers.html.haml @@ -9,7 +9,7 @@ = form_for @user.notifier, :url => notifiers_settings_path, :html => {:class => :form} do |f| = render 'notifier', :f => f, :field => :can_notify %h3= t("layout.settings.notifiers.code_header") - - [:new_comment_commit_owner, :new_comment_commit_repo_owner, :new_comment_commit_commentor].each do |field| + - [:update_code, :new_comment_commit_owner, :new_comment_commit_repo_owner, :new_comment_commit_commentor].each do |field| = render 'notifier', :f => f, :field => field %h3= t("layout.settings.notifiers.tracker_header") - [:new_comment, :new_comment_reply, :new_issue, :issue_assign].each do |field| @@ -21,7 +21,7 @@ %br .leftside.w25 \  - .leftside.w420= submit_tag t("layout.save") + .leftside.w420= submit_tag t('layout.save'), :data => {'disable-with' => t('layout.saving')} .both :javascript diff --git a/app/views/users/settings/private.html.haml b/app/views/users/settings/private.html.haml index 3553c4d44..2eb30eeb3 100644 --- a/app/views/users/settings/private.html.haml +++ b/app/views/users/settings/private.html.haml @@ -13,7 +13,16 @@ .both .leftlist \  - .rightlist= submit_tag t('layout.save') + .rightlist= submit_tag t('layout.save'), :data => {'disable-with' => t('layout.saving')} + .both +%br + += form_for @user, :url => reset_auth_token_settings_path, :html => {:class => :form} do |f| + .leftlist= f.label :authentication_token + .rightlist= @user.authentication_token + .both + .leftlist + .rightlist= submit_tag t('layout.users.reset_token'), :data => {'disable-with' => t('layout.saving')} .both :javascript diff --git a/app/views/users/ssh_keys/index.html.haml b/app/views/users/ssh_keys/index.html.haml index 9f744fab1..e1764966f 100644 --- a/app/views/users/ssh_keys/index.html.haml +++ b/app/views/users/ssh_keys/index.html.haml @@ -21,6 +21,6 @@ .leftlist= f.label :key, t('activerecord.attributes.ssh_key.key') .rightlist= f.text_area :key %br - = f.submit t('layout.add')#, :class => 'button' + = f.submit t('layout.add'), :data => {'disable-with' => t('layout.processing')} - content_for :sidebar, render('sidebar') diff --git a/config/application.rb b/config/application.rb index 0ccdcd359..77751f953 100644 --- a/config/application.rb +++ b/config/application.rb @@ -33,7 +33,7 @@ module Rosa # config.plugins = [ :exception_notification, :ssl_requirement, :all ] # Activate observers that should always be running. - config.active_record.observers = :event_log_observer, :activity_feed_observer, :build_list_observer + config.active_record.observers = :event_log_observer, :build_list_observer # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. diff --git a/config/application.yml.sample b/config/application.yml.sample index 07a3e369a..f2824c609 100644 --- a/config/application.yml.sample +++ b/config/application.yml.sample @@ -2,16 +2,32 @@ common: &common project_name: ABF repo_project_name: ABF anonymous_access: true + preregistration: false file_store_url: 'http://file-store.rosalinux.ru' distr_types: ['mdv', 'rhel', 'nau5'] + allowed_addresses: + - 127.0.0.100 abf_worker: publish_workers_count: 2 + log_server: + host: 127.0.0.1 + port: 6379 keys: key_pair_secret_key: 'key_pair_secret_key' airbrake_api_key: 'airbrake_api_key' + logentries_key: 'logentries_key' devise_pepper: 'devise_pepper' secret_token: 'secret_token' - + github: + id: 'APP_ID' + secret: 'APP_SECRET' + google: + id: 'APP_ID' + secret: 'APP_SECRET' + facebook: + id: 'APP_ID' + secret: 'APP_SECRET' + downloads_url: 'http://abf-downloads.rosalinux.ru' wiki_formats: markdown: "Markdown" textile: "Textile" @@ -41,17 +57,26 @@ development: <<: *common root_path: /var/rosa git_path: /var/rosa + tmpfs_path: /dev/shm do-not-reply-email: do-not-reply@localhost + github_services: + ip: 127.0.0.1 + port: 1234 production: <<: *common root_path: /share git_path: /mnt/gitstore + tmpfs_path: /dev/shm do-not-reply-email: do-not-reply@abf.rosalinux.ru mailer_https_url: false + github_services: + ip: 127.0.0.1 + port: 1234 test: <<: *common + tmpfs_path: "use Rails.root/tmp/test_root in spec" root_path: "use Rails.root/tmp/test_root in spec" git_path: "use Rails.root/tmp/test_root in spec" do-not-reply-email: do-not-reply@localhost diff --git a/config/application.yml.travis b/config/application.yml.travis new file mode 100644 index 000000000..6715379ef --- /dev/null +++ b/config/application.yml.travis @@ -0,0 +1,59 @@ +common: &common + project_name: ABF + repo_project_name: ABF + anonymous_access: true + preregistration: false + file_store_url: 'http://file-store.rosalinux.ru' + distr_types: ['mdv', 'rhel', 'nau5'] + allowed_addresses: + - 127.0.0.100 + abf_worker: + publish_workers_count: 2 + log_server: + host: 127.0.0.1 + port: 6379 + keys: + key_pair_secret_key: 'key_pair_secret_key' + airbrake_api_key: 'airbrake_api_key' + devise_pepper: 'e295a79fb7966e94a6e8b184ba65791a' + secret_token: 'e295a79fb7966e94a6e8b184ba65791a' + github: + id: 'APP_ID' + secret: 'APP_SECRET' + google: + id: 'APP_ID' + secret: 'APP_SECRET' + facebook: + id: 'APP_ID' + secret: 'APP_SECRET' + downloads_url: 'http://abf-downloads.rosalinux.ru' + wiki_formats: + markdown: "Markdown" + textile: "Textile" + # rdoc: "RDoc" + org: "Org-mode" + # creole: "Creole" + # mediawiki: "MediaWiki" + feedback: + email: + - 'test@example.com' + - 'test1@example.com' + # or + # email: 'test@example.com' + + # optional parameters + cc: + - 'test2@example.com' # or like email + bcc: + - 'test3@example.com' # or like email + subject_prefixes: # or one line + - 'from_feedback' + subject_postfixes: # or one line + - 'sample_postfix' + shell_user: 'git' + +test: + <<: *common + root_path: /home/travis/build/rosa-abf/rosa-build/tmp/test_root + git_path: /home/travis/build/rosa-abf/rosa-build/tmp/test_root + do-not-reply-email: do-not-reply@localhost diff --git a/config/boot.rb b/config/boot.rb index 4489e5868..6397af4d8 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -2,5 +2,6 @@ require 'rubygems' # Set up gems listed in the Gemfile. ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +ENV['NEWRELIC_DISPATCHER'] ||= 'puma' require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) diff --git a/config/database.yml.sample b/config/database.yml.sample index e49b4c8db..7b27709d7 100644 --- a/config/database.yml.sample +++ b/config/database.yml.sample @@ -6,3 +6,10 @@ production: pool: 5 username: rosa password: rosa + +test: + adapter: postgresql + encoding: unicode + template: template0 + database: rosa_build_test + username: postgres \ No newline at end of file diff --git a/config/deploy.rb.sample b/config/deploy.rb.sample index 27889acf3..f78974b31 100644 --- a/config/deploy.rb.sample +++ b/config/deploy.rb.sample @@ -9,7 +9,6 @@ set :default_environment, { require 'rvm/capistrano' require 'bundler/capistrano' -require 'airbrake/capistrano' set :whenever_command, "bundle exec whenever" # require "whenever/capistrano" @@ -33,7 +32,8 @@ set :repository, "git@github.com:uname/repository.git" set :deploy_via, :remote_cache require './lib/recipes/nginx' -require './lib/recipes/unicorn' +# require './lib/recipes/unicorn' +require 'puma/capistrano' #require './lib/recipes/bluepill' set :workers_count, 4 require './lib/recipes/resque' @@ -85,6 +85,7 @@ after "deploy:start", "resque:start" after "deploy:restart", "resque:restart" after "deploy:restart", "deploy:cleanup" +after :deploy, 'notify_rollbar' namespace :rake_tasks do Cape do @@ -92,6 +93,14 @@ namespace :rake_tasks do end end +task :notify_rollbar, :roles => :app do + set :revision, `git log -n 1 --pretty=format:"%H"` + set :local_user, `whoami` + set :rollbar_token, 'rollbar_token' + rails_env = fetch(:rails_env, 'production') + run "curl https://api.rollbar.com/api/1/deploy/ -F access_token=#{rollbar_token} -F environment=#{rails_env} -F revision=#{revision} -F local_username=#{local_user} >/dev/null 2>&1", :once => true +end + namespace :update do desc "Copy remote production shared files to localhost" task :shared do diff --git a/config/environments/development.rb b/config/environments/development.rb index 6809679f4..c75da8c27 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,3 +1,19 @@ +# https://github.com/rails/rails/issues/2639#issuecomment-6591735 +class DisableAssetsLogger + def initialize(app) + @app = app + Rails.application.assets.logger = Logger.new('/dev/null') + end + + def call(env) + previous_level = Rails.logger.level + Rails.logger.level = Logger::ERROR if env['PATH_INFO'].index("/assets/") == 0 + @app.call(env) + ensure + Rails.logger.level = previous_level + end +end + Rosa::Application.configure do # Settings specified here will take precedence over those in config/application.rb @@ -6,6 +22,8 @@ Rosa::Application.configure do # since you don't have to restart the webserver when you make code changes. config.cache_classes = false + config.cache_store = :redis_store, 'redis://localhost:6379/0/cache', { expires_in: 10.minutes } + # Log error messages when you accidentally call methods on nil. config.whiny_nils = true @@ -39,4 +57,6 @@ Rosa::Application.configure do # Log the query plan for queries taking more than this (works with SQLite, MySQL, and PostgreSQL) config.active_record.auto_explain_threshold_in_seconds = 0.5 + + config.middleware.insert_before Rails::Rack::Logger, DisableAssetsLogger end diff --git a/config/environments/production.rb b/config/environments/production.rb index eef80d48e..03bf1daf2 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -26,6 +26,7 @@ Rosa::Application.configure do # Use a different cache store in production # config.cache_store = :mem_cache_store + config.cache_store = :redis_store, 'redis://localhost:6379/0/cache', { expires_in: 10.minutes } # Disable Rails's static asset server # In production, Apache or nginx will already do this @@ -36,7 +37,7 @@ Rosa::Application.configure do # Disable delivery errors, bad email addresses will be ignored # config.action_mailer.raise_delivery_errors = false - config.action_mailer.default_url_options = { :host => 'abf.rosalinux.ru' } + config.action_mailer.default_url_options = { :host => 'abf.io' } config.delivery_method = :sendmail # Enable threaded mode @@ -62,5 +63,5 @@ Rosa::Application.configure do config.assets.digest = true # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) - config.assets.precompile += %w(login.css login.js reg_session.css tour.css tour.js gollum/editor/langs/*.js) + config.assets.precompile += %w(login.css login.js reg_session.css tour.css tour.js gollum/editor/langs/*.js moment/ru.js) end diff --git a/config/environments/staging.rb b/config/environments/staging.rb new file mode 100644 index 000000000..1b56c5299 --- /dev/null +++ b/config/environments/staging.rb @@ -0,0 +1,67 @@ +# -*- encoding : utf-8 -*- +Rosa::Application.configure do + # Settings specified here will take precedence over those in config/application.rb + + # The production environment is meant for finished, "live" apps. + # Code is not reloaded between requests + config.cache_classes = true + + # Full error reports are disabled and caching is turned on + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Specifies the header that your server uses for sending files +# config.action_dispatch.x_sendfile_header = "X-Sendfile" + + # For nginx: + config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' + + # If you have no front-end server that supports something like X-Sendfile, + # just comment this out and Rails will serve the files + + # See everything in the log (default is :info) + # config.log_level = :debug + + # Use a different logger for distributed setups + # config.logger = SyslogLogger.new + + # Use a different cache store in production + # config.cache_store = :mem_cache_store + + # Disable Rails's static asset server + # In production, Apache or nginx will already do this + config.serve_static_assets = false + + # Enable serving of images, stylesheets, and javascripts from an asset server + # config.action_controller.asset_host = "http://assets.example.com" + + # Disable delivery errors, bad email addresses will be ignored + # config.action_mailer.raise_delivery_errors = false + config.action_mailer.default_url_options = { :host => 'abf.rosalinux.ru' } + config.delivery_method = :sendmail + + # Enable threaded mode + # config.threadsafe! + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation can not be found) + config.i18n.fallbacks = true + + # Send deprecation notices to registered listeners + config.active_support.deprecation = :notify + + # Force SSL + # config.force_ssl = true + + # Compress JavaScripts and CSS + config.assets.compress = true + + # Don't fallback to assets pipeline if a precompiled asset is missed + config.assets.compile = false + + # Generate digests for assets URLs + config.assets.digest = true + + # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) + config.assets.precompile += %w(login.css login.js reg_session.css tour.css tour.js gollum/editor/langs/*.js moment/ru.js) +end diff --git a/config/initializers/a_app_config.rb b/config/initializers/a_app_config.rb index 2517aeb60..d0cc9192c 100644 --- a/config/initializers/a_app_config.rb +++ b/config/initializers/a_app_config.rb @@ -1 +1,5 @@ -APP_CONFIG = YAML.load_file("#{Rails.root}/config/application.yml")[Rails.env] \ No newline at end of file +APP_CONFIG = YAML.load_file("#{Rails.root}/config/application.yml")[Rails.env] +# Remove '/' from the end of url +APP_CONFIG.keys.select {|key| key =~ /_url\Z/}.each {|key| APP_CONFIG[key] = APP_CONFIG[key].chomp('/') if APP_CONFIG[key].respond_to?(:chomp)} +# Paginates a static array +require 'will_paginate/array' diff --git a/config/initializers/airbrake.rb b/config/initializers/airbrake.rb index 69ed3db93..d95c4f37d 100644 --- a/config/initializers/airbrake.rb +++ b/config/initializers/airbrake.rb @@ -1,3 +1,8 @@ +require 'airbrake' + Airbrake.configure do |config| config.api_key = APP_CONFIG['keys']['airbrake_api_key'] -end rescue nil + config.host = 'errbit.rosalinux.ru' + config.port = 80 + config.secure = config.port == 443 +end diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 6d592eee8..1517e6c7d 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -187,8 +187,13 @@ Devise.setup do |config| # Add a new OmniAuth provider. Check the wiki for more information on setting # up on your models and hooks. # config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo' - require 'openid/store/filesystem' - config.omniauth :openid, :name => 'open_id' #, :store => OpenID::Store::Filesystem.new('./tmp') + + # require 'openid/store/filesystem' + # config.omniauth :openid, :name => 'open_id' #, :store => OpenID::Store::Filesystem.new('./tmp') + + config.omniauth :facebook, APP_CONFIG['keys']['facebook']['id'], APP_CONFIG['keys']['facebook']['secret'] + config.omniauth :google_oauth2, APP_CONFIG['keys']['google']['id'], APP_CONFIG['keys']['google']['secret'], {:access_type => 'offline', :approval_prompt => ''} + config.omniauth :github, APP_CONFIG['keys']['github']['id'], APP_CONFIG['keys']['github']['secret'], {:scope => 'user:email'} # ==> Warden configuration # If you want to use other strategies, that are not supported by Devise, or diff --git a/config/initializers/jsroutes.rb b/config/initializers/jsroutes.rb new file mode 100644 index 000000000..0394aae60 --- /dev/null +++ b/config/initializers/jsroutes.rb @@ -0,0 +1,3 @@ +JsRoutes.setup do |config| + config.exclude = [/token/, /admin/, /api/, /key/] +end \ No newline at end of file diff --git a/config/initializers/localeapp.rb b/config/initializers/localeapp.rb new file mode 100644 index 000000000..f9d8db4f4 --- /dev/null +++ b/config/initializers/localeapp.rb @@ -0,0 +1,5 @@ +if Rails.env.development? + Localeapp.configure do |config| + config.sending_environments = [] + end +end \ No newline at end of file diff --git a/config/initializers/logentries.rb b/config/initializers/logentries.rb new file mode 100644 index 000000000..a7d195dda --- /dev/null +++ b/config/initializers/logentries.rb @@ -0,0 +1,8 @@ +# require 'le' + +# key = APP_CONFIG['keys']['logentries_key'] +# if Rails.env.development? +# Rails.logger = Le.new key, :debug => true +# else +# Rails.logger = Le.new key +# end \ No newline at end of file diff --git a/config/initializers/logglier.rb b/config/initializers/logglier.rb new file mode 100644 index 000000000..67bda1aaa --- /dev/null +++ b/config/initializers/logglier.rb @@ -0,0 +1,5 @@ +# require 'logglier' + +# if Rails.env.production? +# Rails.logger = Logglier.new("https://logs-01.loggly.com/inputs/#{APP_CONFIG['keys']['logglier_key']}/tag/ruby/", :threaded => true) +# end \ No newline at end of file diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb index c85b642b3..51d3edb4e 100644 --- a/config/initializers/mime_types.rb +++ b/config/initializers/mime_types.rb @@ -8,12 +8,13 @@ Mime::Type.register "text/plain", 'diff' Mime::Type.register "text/plain", 'patch' # add rpm spec as mime type for *.spec files -[["text/x-python", ['py'], '8bit'], - ["text/x-rpm-spec", ['spec'], '8bit'], - ["text/x-csrc", ['h', 'c'], '8bit'], - ["text/x-c++src", ['cpp'], '8bit'], - ["text/x-diff", ['diff'], '8bit'], - ["text/x-markdown", ['md'], '8bit'] +[["text/x-python", ['py'], '8bit'], + ["text/x-rpm-spec", ['spec'], '8bit'], + ["application/x-csrc", ['h', 'c'], '8bit'], + ["application/x-c++src", ['cpp', 'cc'], '8bit'], + ["application/x-csharp", ['cs'], '8bit'], + ["text/x-diff", ['diff'], '8bit'], + ["text/x-markdown", ['md'], '8bit'] ].each do |type| MIME::Types.add MIME::Type.from_array(type) end diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb new file mode 100644 index 000000000..d5f26a3d8 --- /dev/null +++ b/config/initializers/omniauth.rb @@ -0,0 +1 @@ +OmniAuth.config.logger = Rails.logger \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml index be090706d..246f0a180 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -40,6 +40,7 @@ en: publish: Publish publish_again: Publish again publish_again_warning: Secondary publication will be able to break relationships in the repository. Be careful! + publish_into_testing: '[testing] Publish' reject_publish: Reject add: Add upload: Upload @@ -52,6 +53,7 @@ en: atom_link_tag_title: Private feed for %{nickname} | %{app_name} preview: Preview link: Link + noscript_message: You need javascript to properly use this site settings: label: Settings @@ -87,6 +89,7 @@ en: sessions: sign_in_header: Sign in + sign_up_with: or sign in with private_users: list: List @@ -151,11 +154,11 @@ en: group_already_added: Group already added successfully_added: Member %s successfully added error_in_adding: Member %s adding error - wrong_user: User with nickname '%{uname}' not found! + wrong_user: "User with nickname '%{uname}' not found!" blob: - successfully_updated: File '%{name}' successfully updated - updating_error: Error updating file '%{name}' + successfully_updated: "File '%{name}' successfully updated" + updating_error: "Error updating file '%{name}'" attributes: password: Password @@ -185,6 +188,7 @@ en: new_comment_reply: New reply of comment notifications new_issue: New task notifications issue_assign: New task assignment notifications + update_code: Notify about changes of code in my projects new_comment_commit_owner: Notify about comments to my commit new_comment_commit_repo_owner: Notify about comments to my repository commits new_comment_commit_commentor: Notify about comments after my commit diff --git a/config/locales/layout/commits.en.yml b/config/locales/layout/commits.en.yml index 33b5b42c3..359d0f4d8 100644 --- a/config/locales/layout/commits.en.yml +++ b/config/locales/layout/commits.en.yml @@ -6,4 +6,7 @@ en: pluralize: commit: commit commits: commits - commits2: commits \ No newline at end of file + commits2: commits + reference: "%{committer} referenced this issue from a commit %{commit}" + deleted: Commit has since been removed from the git-repository and is no longer available. + unknown_committer: Unknown \ No newline at end of file diff --git a/config/locales/layout/commits.ru.yml b/config/locales/layout/commits.ru.yml index 3e88b0da3..4f3f5d4a8 100644 --- a/config/locales/layout/commits.ru.yml +++ b/config/locales/layout/commits.ru.yml @@ -6,4 +6,7 @@ ru: pluralize: commit: коммит commits: коммита - commits2: коммитов \ No newline at end of file + commits2: коммитов + reference: "%{committer} ссылается на данную задачу в коммите %{commit}" + deleted: Коммит был удален и более недоступен в git-репозитории. + unknown_committer: Неизвестный \ No newline at end of file diff --git a/config/locales/menu.en.yml b/config/locales/menu.en.yml index a2b0ccdec..ebd4d1c86 100644 --- a/config/locales/menu.en.yml +++ b/config/locales/menu.en.yml @@ -35,6 +35,10 @@ en: readme: Readme settings: Settings pull_requests: Pull Requests (%{count}) + activity_menu: + activity_feed: Activity Feed + tracker: Tracker + pull_requests: Pull Requests feed_menu: all: All code: Code diff --git a/config/locales/menu.ru.yml b/config/locales/menu.ru.yml index dcd696821..9bf4f1f16 100644 --- a/config/locales/menu.ru.yml +++ b/config/locales/menu.ru.yml @@ -35,6 +35,10 @@ ru: readme: Readme settings: Настройки pull_requests: Пул реквесты (%{count}) + activity_menu: + activity_feed: Лента активности + tracker: Трекер + pull_requests: Пул реквесты feed_menu: all: Все code: Код diff --git a/config/locales/models/activity_feed.en.yml b/config/locales/models/activity_feed.en.yml index 52f70eef8..221fcf0ca 100644 --- a/config/locales/models/activity_feed.en.yml +++ b/config/locales/models/activity_feed.en.yml @@ -12,6 +12,7 @@ en: notifications: subjects: + update_code: "[%{project_name}] Update of project" new_comment_notification: New comment to your task new_commit_comment_notification: New comment to commit new_issue_notification: New task added to project @@ -39,7 +40,7 @@ en: delete_branch: '%{user_link} deleted a %{branch_name}' create_branch: '%{user_link} created a new branch %{branch_name}' update_branch: '%{user_link} pushed to branch %{branch_name}' - build_task: 'Build task #%{task_num}' + build_task: 'Build task #%{id}' build_status: published: published successfully success: completed successfully diff --git a/config/locales/models/activity_feed.ru.yml b/config/locales/models/activity_feed.ru.yml index 5aed74727..3a4bd70e4 100644 --- a/config/locales/models/activity_feed.ru.yml +++ b/config/locales/models/activity_feed.ru.yml @@ -12,6 +12,7 @@ ru: notifications: subjects: + update_code: "[%{project_name}] Обновление проекта" new_comment_notification: Новый комментарий к Вашей задаче new_commit_comment_notification: Новый комментарий к коммиту new_issue_notification: Новая задача добавлена к проекту @@ -40,7 +41,7 @@ ru: delete_branch: '%{user_link} удалил ветку %{branch_name}' create_branch: '%{user_link} создал новую ветку %{branch_name}' update_branch: '%{user_link} внес изменения в ветку %{branch_name}' - build_task: 'Сборочное задание №%{task_num}' + build_task: 'Сборочное задание №%{id}' build_status: published: успешно опубликовано success: успешно собрано diff --git a/config/locales/models/advisory.en.yml b/config/locales/models/advisory.en.yml index fe3e1a16f..db8ccbef4 100644 --- a/config/locales/models/advisory.en.yml +++ b/config/locales/models/advisory.en.yml @@ -1,7 +1,7 @@ en: layout: advisories: - atom_header: Advisories + atom_title: Advisories list_header: Advisories form_header: New advisory project_name: Project diff --git a/config/locales/models/build_list.en.yml b/config/locales/models/build_list.en.yml index 02bed316b..8abaa2253 100644 --- a/config/locales/models/build_list.en.yml +++ b/config/locales/models/build_list.en.yml @@ -5,9 +5,10 @@ en: build_list_item: Build list item attributes: build_list: - bs_id: Id + id: Id name: Name - extra_repos: Extra repositories and build lists + extra_repositories: Extra repositories + extra_build_lists: Extra build lists auto_create_container: Create container automatically container_path: Container path status: Status @@ -20,22 +21,31 @@ en: is_circle: Recurrent build updated_at: Notified at additional_repos: Additional repositories + include_testing_subrepository: "Include 'testing' subrepository" include_repos: Included repositories created_at: Created on save_to_repository: Save to repository - use_save_to_repository: Use 'save to repository' for assembly build_for_platform: Build for platform update_type: Update type - auto_publish: Automated publising + auto_publish: Automated publishing project_version: Version user: User publisher: Publisher + builder: Builder preferences: Preferences started_at: Build started at duration: Build duration in seconds mass_build_id: Mass build commit_hash: Commit hash logs: Logs + external_nodes: External nodes + + extra_params: + label: Extra params + cfg_options: "'urpmi_options' for *.cfg file" + cfg_urpm_options: "'urpm_options' for *.cfg file" + build_src_rpm: Build src.rpm + build_rpm: Build rpm build_list/item: name: Name @@ -47,6 +57,7 @@ en: build_list/package: name: Name fullname: Fullname + epoch: Epoch release: Release version: Version @@ -60,9 +71,8 @@ en: created_at_end: "Build to start until:" updated_at_start: "Last update from BS on:" updated_at_end: " Last update from BS until:" - bs_id_search: 'Search by Id' + id_search: 'Search by Id' project_name_search: Search by project name - bs_id_not_set: Id has not been configured yet items_header: Build items packages_header: Packages no_items_data: No data @@ -73,7 +83,9 @@ en: create_container_success: 'Container is queued for creating' create_container_fail: 'Errors during container creating!' publish_success: 'Build is queued for publishing' + publish_into_testing_success: 'Build is queued for publishing' publish_fail: 'Errors during build publishing!' + publish_into_testing_fail: 'Errors during build publishing!' publish_with_extra_fail: 'All extra build lists should be published before publishing this build list!' cancel_success: 'Build canceled' cancel_fail: 'Errors during build cancelation!' @@ -97,14 +109,21 @@ en: related: Related everything: All + external_nodes: + owned: My + everything: All + build_server_status: header: Build server status - count: '- count' + amount: '- amount' + abf: '-- ABF' tasks: '- tasks in queue' + custom: "-- user's" + mass_build_tasks: "-- mass build's" build_tasks: '- tasks in execution' - rpm_workers: Workers for building - iso_workers: Workers for building - publish_workers: Workers for publishing + rpm_workers: Workers of building + iso_workers: Workers of building + publish_workers: Workers of publishing items: statuses: @@ -132,7 +151,9 @@ en: success: Build complete build_started: Build started platform_pending: Platform pending - project_version_not_found: Project version not found + build_published_into_testing: '[testing] Build has been published' + build_publish_into_testing: '[testing] Build is being published' + failed_publish_into_testing: '[testing] Publishing error' log: build_log: Build Log @@ -152,21 +173,11 @@ en: show_filter: Show filters hide_filter: Hide filters - helpers: - extra_repos_and_build_lists: - header: 'Ability to attach at assembly additional sources of packages: repositories of platforms and packages of build lists.' - build_lists: - header: 'Requirements for attaching build lists:' - message1: '- only build list with container can be connected;' - message2: '- for main platform You can connect only build lists which destined for same platform;' - message3: '- only build lists with the same architecture will be connected or oriented to the both architectures (the property Publish i686 packages into x86_64 repository (only for rhel) in the settings of project is true).' - message4: 'Example: only build lists with x86_64 architecture will be connected to x86_64' - repos: - header: 'Requirements for attaching repositories:' - message1: '- attaching repositories from other platform available only for build list into the personal platform.' - header2: 'How to attach a repository:' - message2: '- write the name of platform, choose repository from the list for attaching and click to "Add" button.' - message3: 'Example: correct input "uxteam_personal", wrong - "uxteam_personal/main".' + last_build_lists: Last Build Lists + recreate_build_list: Recreate Build List + only_my_build_lists: Only My + failed_build_lists: Only Failed + flash: build_list: saved: Build list for project version '%{project_version}', platform '%{build_for_platform}' and architecture '%{arch}' has been created successfully diff --git a/config/locales/models/build_list.ru.yml b/config/locales/models/build_list.ru.yml index c3c93fbed..1e4a2cc5e 100644 --- a/config/locales/models/build_list.ru.yml +++ b/config/locales/models/build_list.ru.yml @@ -5,9 +5,10 @@ ru: build_list_item: Элемент сборочного листа attributes: build_list: - bs_id: Id + id: Id name: Название - extra_repos: Дополнительные репозитории и сборки + extra_repositories: Дополнительные репозитории + extra_build_lists: Дополнительные сборки auto_create_container: Создать контейнер автоматически container_path: Путь до контейнера status: Статус @@ -20,21 +21,30 @@ ru: is_circle: Циклическая сборка updated_at: Информация получена additional_repos: Дополнительные репозитории + include_testing_subrepository: "Подключить 'testing' подрепозиторий" include_repos: Подключаемые репозитории created_at: Создан save_to_repository: Сохранить в репозиторий - use_save_to_repository: Использовать 'cохранить в репозиторий' для сборки build_for_platform: Собрано для платформы update_type: Критичность обновления auto_publish: Автоматическая публикация project_version: Версия user: Пользователь publisher: Публикатор + builder: Сборщик preferences: Настройки duration: Длительность билда в секундах mass_build_id: Массовая сборка commit_hash: Хэш коммита logs: Логи + external_nodes: Дополнительные ноды + + extra_params: + label: Дополнительные параметры + cfg_options: "'urpmi_options' для *.cfg файла" + cfg_urpm_options: "'urpm_options' для *.cfg файла" + build_src_rpm: Сборка src.rpm + build_rpm: Сборка rpm build_list/item: name: Название @@ -46,6 +56,7 @@ ru: build_list/package: name: Название fullname: Полное имя + epoch: Эпоха release: Релиз version: Версия @@ -59,9 +70,8 @@ ru: created_at_end: "Время постановки на сборку по:" updated_at_start: "Время последнего обновления от BS с:" updated_at_end: "Время последнего обновления от BS по:" - bs_id_search: 'Поиск по Id' + id_search: 'Поиск по Id' project_name_search: Поиск по названию проекта - bs_id_not_set: Id еще не присвоен items_header: Элементы сборки packages_header: Пакеты no_items_data: Данных нет @@ -74,7 +84,9 @@ ru: cancel_success: 'Сборка отменена.' cancel_fail: 'При отмене сборки произошла ошибка!' publish_success: 'Сборка поставлена в очередь на публикацию.' + publish_into_testing_success: 'Сборка поставлена в очередь на публикацию.' publish_fail: 'При публикации сборки произошла ошибка!' + publish_into_testing_fail: 'При публикации сборки произошла ошибка!' publish_with_extra_fail: 'Все дополнительные сборки должны быть опубликованы до публикации этой сборки!' reject_publish_success: 'Публикация отклонена' reject_publish_fail: 'Не удалось отклонить публикацию сборки' @@ -96,14 +108,21 @@ ru: related: Связанные everything: Все + external_nodes: + owned: Мои + everything: Все + build_server_status: header: Статус сборочного сервера - count: '- число' + amount: '- количество' + abf: '-- ABF' tasks: '- заданий в очереди' + custom: '-- пользовательских' + mass_build_tasks: '-- массовой сборки' build_tasks: '- заданий выполняется' - rpm_workers: Воркеров для сборки - iso_workers: Воркеров для сборки - publish_workers: Воркеров для публикации + rpm_workers: Воркеры сборки + iso_workers: Воркеры сборки + publish_workers: Воркеры публикации items: statuses: @@ -131,7 +150,10 @@ ru: success: собран build_started: собирается platform_pending: платформа в процессе создания - project_version_not_found: версия не найдена + build_published_into_testing: '[testing] опубликован' + build_publish_into_testing: '[testing] публикуется' + failed_publish_into_testing: '[testing] ошибка публикации' + log: build_log: Лог сборки @@ -151,21 +173,11 @@ ru: show_filter: Показать фильтры hide_filter: Скрыть фильтры - helpers: - extra_repos_and_build_lists: - header: 'Возможность подключить для сборки дополнительные источники пакетов: репозитории из платформ и пакеты из сборочных заданий.' - build_lists: - header: 'Требования для подключемых сборочных заданий:' - message1: '- только задание с контейнером может быть подключен;' - message2: '- для основных платформ возможно подключить только задания, которые предназначены для этой же платформы;' - message3: '- будут подключены только задания с архитектурой, соотвествующей создаваемой, или предназначененные для обеих архитектур (установлено свойство Публиковать i686 пакеты в x86_64 репозиторий (только для rhel) в настройках проекта).' - message4: 'Пример: для x86_64 архитектуры будут подключены только сборочные задания, собранные для x86_64' - repos: - header: 'Требования для репозиториев:' - message1: '- использовать репозитории из других платформ возможно только для сборки в персональную платформу.' - header2: 'Как подключить репозиторий:' - message2: '- добавьте имя платформы, затем из списка выберете репозиторий для поключения и нажмите кнопку "Добавить".' - message3: 'Пример: правильно ввести "uxteam_personal", неправильно: "uxteam_personal/main".' + last_build_lists: Последние сборки + recreate_build_list: Пересоздать сборку + only_my_build_lists: Только мои + failed_build_lists: Только сбойные + flash: build_list: saved: Билд лист для версии '%{project_version}', платформы '%{build_for_platform}' и архитектуры '%{arch}' создан успешно diff --git a/config/locales/models/comment.en.yml b/config/locales/models/comment.en.yml index 8f3e5a4a6..ed1f6f504 100644 --- a/config/locales/models/comment.en.yml +++ b/config/locales/models/comment.en.yml @@ -25,6 +25,22 @@ en: syntax_highlighting: Syntax highlighting indent_code: indent your code 4 spaces inline_code: Inline code for comments + emoji_header: Emoji Cheat Sheet + reference_format: Reference Format + reference_format_example: | + for members: @abf + for issues and pull requests: + #123 + abf#123 + abf/rosa-build#123 + for commits: 123456 + + issues: Issues + pull_requests: Pull Requests + members: Members + commits: Commits + reference: "%{user} referenced this issue" + removed: Comment has since been removed and is no longer available. flash: comment: diff --git a/config/locales/models/comment.ru.yml b/config/locales/models/comment.ru.yml index f481d904c..0b8c199a4 100644 --- a/config/locales/models/comment.ru.yml +++ b/config/locales/models/comment.ru.yml @@ -25,6 +25,21 @@ ru: syntax_highlighting: Подсветка синтаксиса indent_code: Отступ кода на 4 пробела inline_code: Встроенный код в строке + emoji_header: Шпаргалка по Emoji + reference_format: Формат ссылок + reference_format_example: | + для участников: @abf + для задач и пул реквестов: + #123 + abf#123 + abf/rosa-build#123 + для коммитов: 123456 + issues: Задачи + pull_requests: Пул реквесты + members: Участники + commits: Коммиты + reference: "%{user} ссылается на данную задачу" + removed: Комментарий был удален и более недоступен. flash: comment: diff --git a/config/locales/models/hook.en.yml b/config/locales/models/hook.en.yml new file mode 100644 index 000000000..db154d105 --- /dev/null +++ b/config/locales/models/hook.en.yml @@ -0,0 +1,39 @@ +en: + layout: + hooks: + services: + web: WebHook URLs + hipchat: HipChat + irc: IRC + jabber: Jabber + + flash: + hook: + save_error: Unable to save hook + created: Hook was successfully created + updated: Hook was successfully updated + + activerecord: + attributes: + hook: + data: + url: URL + auth_token: Auth Token + room: Room + restrict_to_branch: Restrict To Branch + notify: Notify + server: Server + port: Port + nick: Nick + branch_regexes: Branch Regexes + password: Password + ssl: SSL + message_without_join: Message Without Join + no_colors: No Colors + long_url: Long Url + notice: Notice + user: User + token: Token + secret: Secret + digest: Digest + short_format: Short Format diff --git a/config/locales/models/hook.ru.yml b/config/locales/models/hook.ru.yml new file mode 100644 index 000000000..f29c162b3 --- /dev/null +++ b/config/locales/models/hook.ru.yml @@ -0,0 +1,39 @@ +ru: + layout: + hooks: + services: + web: WebHook URLs + hipchat: HipChat + irc: IRC + jabber: Jabber + + flash: + hook: + save_error: Не удалось сохранить хук + created: Хук успешно сохранен + updated: Хук успешно обновлен + + activerecord: + attributes: + hook: + data: + url: URL + auth_token: Auth Token + room: Room + restrict_to_branch: Restrict To Branch + notify: Notify + server: Server + port: Port + nick: Nick + branch_regexes: Branch Regexes + password: Password + ssl: SSL + message_without_join: Message Without Join + no_colors: No Colors + long_url: Long Url + notice: Notice + user: User + token: Token + secret: Secret + digest: Digest + short_format: Short Format diff --git a/config/locales/models/issue.en.yml b/config/locales/models/issue.en.yml index c06dfd908..c6ed5d582 100644 --- a/config/locales/models/issue.en.yml +++ b/config/locales/models/issue.en.yml @@ -1,5 +1,7 @@ en: activerecord: + models: + issue: Issue attributes: issue: title: Name @@ -18,7 +20,8 @@ en: assign_someone: Assign someone to this issue list: List all: All issues - to_me: Assigned to me + assigned: Assigned to me + created: Created by you edit: Edit search: Find issue... comments_header: Comments diff --git a/config/locales/models/issue.ru.yml b/config/locales/models/issue.ru.yml index 1e1e3c190..427f95510 100644 --- a/config/locales/models/issue.ru.yml +++ b/config/locales/models/issue.ru.yml @@ -1,5 +1,7 @@ ru: activerecord: + models: + issue: Задача attributes: issue: title: Название @@ -18,7 +20,8 @@ ru: assign_someone: Назначить кого-либо на задачу list: Список all: Все задачи - to_me: Назначенные мне + assigned: Назначенные мне + created: Созданные мной edit: Редактировать search: Найти задачу... comments_header: Комментарии diff --git a/config/locales/models/mass_build.en.yml b/config/locales/models/mass_build.en.yml index bafb62b01..9336ab05a 100644 --- a/config/locales/models/mass_build.en.yml +++ b/config/locales/models/mass_build.en.yml @@ -1,6 +1,7 @@ en: layout: mass_builds: + new: New mass build publish_success: Publish success builds publish_test_failed: Publish test failed builds repositories: Repositories @@ -22,6 +23,7 @@ en: updated_at: Updated arch_names: Architectures user: User - auto_publish: Auto Publish + auto_publish: Automated publishing + increase_release_tag: Increase release tag repositories: Repositories projects_list: Projects list diff --git a/config/locales/models/mass_build.ru.yml b/config/locales/models/mass_build.ru.yml index 974e7f09d..f28c4ede5 100644 --- a/config/locales/models/mass_build.ru.yml +++ b/config/locales/models/mass_build.ru.yml @@ -1,6 +1,7 @@ ru: layout: mass_builds: + new: Новая массовая сборка publish_success: Опубликовать успешные сборки publish_test_failed: Опубликовать сборки с проваленными тестами repositories: Репозитории @@ -22,6 +23,7 @@ ru: updated_at: Обновлен arch_names: Архитектуры user: Пользователь - auto_publish: Авто Публикация + auto_publish: Автоматическая публикация + increase_release_tag: Увеличить release тег repositories: Репозитории projects_list: Список проектов diff --git a/config/locales/models/platform.en.yml b/config/locales/models/platform.en.yml index b834312d1..d4722ed81 100644 --- a/config/locales/models/platform.en.yml +++ b/config/locales/models/platform.en.yml @@ -1,6 +1,9 @@ en: layout: platforms: + contents: Contents + contents_of: Contents of + search_contents: Search name of file/folder... admin_id: Owner build_all: Build all list: List @@ -46,10 +49,15 @@ en: mass_build: Mass build build_task: Build Task refresh_button: Refresh + change_visibility_from_hidden: Change status to "Public" + change_visibility_from_open: Change status to "Private" + confirm_change_visibility: Are you sure you want to change visibility of this platform? + metadata: Metadata for Software Center flash: platform: released_status_can_not_be_changed: Released status can't be changed if platform has been released + owner_can_not_be_changed: Owner of personal platform can't be changed saved: Platform saved created: Platform created save_error: Platform save error @@ -72,6 +80,8 @@ en: models: platform: Platform attributes: + platform/platform_arch_settings: + time_living: Max time build (in minutes) platform: name: Name description: Description diff --git a/config/locales/models/platform.ru.yml b/config/locales/models/platform.ru.yml index a59233458..7b8db4de3 100644 --- a/config/locales/models/platform.ru.yml +++ b/config/locales/models/platform.ru.yml @@ -1,6 +1,9 @@ ru: layout: platforms: + contents: Содержимое + contents_of: Содержимое + search_contents: Найти имя файла/папки... admin_id: Владелец build_all: Собрать все list: Список @@ -46,10 +49,15 @@ ru: build_task: Сборочное задание refresh_button: Обновить project: Проект + change_visibility_from_hidden: Сменить статус на "Публичный" + change_visibility_from_open: Сменить статус на "Приватный" + confirm_change_visibility: Вы уверены, что хотите сменить статус этой платформы? + metadata: Метаданные для Software Center flash: platform: released_status_can_not_be_changed: Released статус платформы не может быть изменен, если платформа уже выпущена + owner_can_not_be_changed: Нельзя сменить владельца персональной платформы saved: Платформа успешно сохранена created: Платформа успешно добавлена save_error: Не удалось сохранить платформу @@ -72,6 +80,8 @@ ru: models: platform: Платформа attributes: + platform/platform_arch_settings: + time_living: Максимальное время сборки (в минутах) platform: name: Название description: Описание diff --git a/config/locales/models/platform_arch_settings.en.yml b/config/locales/models/platform_arch_settings.en.yml new file mode 100644 index 000000000..5db539702 --- /dev/null +++ b/config/locales/models/platform_arch_settings.en.yml @@ -0,0 +1,13 @@ +en: + layout: + platform_arch_settings: + extra_settings: Extra settings + + flash: + platform_arch_settings: + + activerecord: + attributes: + platform_arch_setting: + default: Use by default at build + time_living: Max time build (in minutes) diff --git a/config/locales/models/platform_arch_settings.ru.yml b/config/locales/models/platform_arch_settings.ru.yml new file mode 100644 index 000000000..f6aa351cf --- /dev/null +++ b/config/locales/models/platform_arch_settings.ru.yml @@ -0,0 +1,13 @@ +ru: + layout: + platform_arch_settings: + extra_settings: Дополнительные настройки + + flash: + platform_arch_settings: + + activerecord: + attributes: + platform_arch_setting: + default: Использовать по умолчанию при сборке + time_living: Максимальное время сборки (в минутах) diff --git a/config/locales/models/project.en.yml b/config/locales/models/project.en.yml index 58aa0dd1a..586f0c5fb 100644 --- a/config/locales/models/project.en.yml +++ b/config/locales/models/project.en.yml @@ -1,17 +1,16 @@ en: layout: projects: + mass_import: Mass import branches: Branches - showing_branches: Showing %{count} branches - showing_branch: Showing 1 branch + delete_branch: Delete branch + restore_branch: Restore branch no_branches: No branches base_branch: Base branch compare: Compare browse_code: Browse code source_code: Source code (%{type}) tags: Tags - showing_tags: Showing %{count} tags - showing_tag: Showing 1 tag no_tags: No tags add: Add public_projects_list: Public projects list @@ -30,6 +29,7 @@ en: new_build_list: New build confirm_delete: Are you sure you want to delete this project? new: New project + new_branch: New branch location: Location git_repo_location: Path to git repo current_project_header: Current project @@ -39,6 +39,7 @@ en: members: Members groups: Groups edit_collaborators: Members + hooks: Service hooks role: Project role remove_user: Leave project issues: Issues @@ -47,7 +48,6 @@ en: sections: Sections has_issue_description: Tracker adds a lightweight issue management system tightly integrated with your repository. has_wiki_description: Wikis are the simplest way to allow other users to contribute content. Any user can create and edit pages for documentation, examples, support or anything you wish. - human_average_build_time: Expected time is %{hours} h. %{minutes} min. git_global_setup: Git global setup create_repository: Create Repository move_files_to_folder: Move files you need to the project or create them. @@ -72,8 +72,12 @@ en: current_commit: Current commit files_in_project: Files in + public: Public + private: Private + flash: project: + mass_import_added_to_queue: Mass import added to queue saved: Project saved save_error: Unable to save project save_warning_ssh_key: Project owner must provide a SSH key in his profile @@ -87,6 +91,9 @@ en: project: Project attributes: project: + url: URL + add_to_repository_id: Add to repository + srpms_list: SRPMs list name: Name description: Descripton owner: Owner @@ -100,7 +107,7 @@ en: updated_at: Updated on has_issues: Issues tracker enabled has_wiki: Wiki enabled - srpm: Import code from src.rpm + srpm: Import code from src.rpm (optional) who_owns: me: Myself group: Group diff --git a/config/locales/models/project.ru.yml b/config/locales/models/project.ru.yml index 1e7b20ec9..12821d64c 100644 --- a/config/locales/models/project.ru.yml +++ b/config/locales/models/project.ru.yml @@ -1,17 +1,16 @@ ru: layout: projects: + mass_import: Массовый импорт branches: Ветки - showing_branches: Показано %{count} веток - showing_branch: Показана 1 ветка - no_branch: Нет веток + delete_branch: Удалить ветку + restore_branch: Восстановить ветку + no_branches: Нет веток base_branch: Текущая ветка compare: Сравнить browse_code: Просмотреть код source_code: Исходный код (%{type}) tags: Теги - showing_tags: Показано %{count} тегов - showing_tag: Показан 1 тег no_tags: Нет тегов add: Добавить public_projects_list: Список публичных проектов @@ -30,6 +29,7 @@ ru: new_build_list: Новая сборка confirm_delete: Вы уверены, что хотите удалить этот проект? new: Новый проект + new_branch: Новая ветка location: Расположение git_repo_location: Путь к git-репозиторию current_project_header: Текущий проект @@ -39,6 +39,7 @@ ru: members: Участники groups: Группы edit_collaborators: Участники + hooks: Веб хуки role: Роль в проекте remove_user: Покинуть проект issues: Задачи @@ -47,7 +48,6 @@ ru: sections: Разделы has_issue_description: Трэкер предоставляет лекговесный менеджер для задач по разработке Вашего проекта. has_wiki_description: Wiki - это самый простой способ предоставить другим вносить свой вклад в развитие Вашего проекта. Каждый пользователь нашего сервиса может использовать Wiki для документирования, примеров, поддержки или всего другого, в чем у Вас появится необходимость. - human_average_build_time: 'Ожидаемое время: %{hours} ч. %{minutes} мин.' git_global_setup: Общие настройки Git create_repository: Создание репозитория move_files_to_folder: Переместите нужные файлы в проект или создайте их. @@ -72,8 +72,12 @@ ru: cloning: Клонирование этого репозитория remote: Добавление этого репозитория как удаленного к существующему локальному репозиторию + public: Публичные + private: Приватные + flash: project: + mass_import_added_to_queue: Массовый импорт добавлен в очередь saved: Проект успешно сохранен save_error: Не удалось сохранить проект save_warning_ssh_key: Владельцу проекта необходимо указать в профиле свой SSH ключ @@ -87,6 +91,9 @@ ru: project: Проект attributes: project: + url: URL + add_to_repository_id: Добавить в репозиторий + srpms_list: Список SRPMs name: Название description: Описание owner: Владелец @@ -100,7 +107,7 @@ ru: updated_at: Обновлен has_issues: Включить трэкер has_wiki: Включить Wiki - srpm: Импортировать код из src.rpm + srpm: Импортировать код из src.rpm (опционально) who_owns: me: Я group: Группа diff --git a/config/locales/models/project_statistic.en.yml b/config/locales/models/project_statistic.en.yml new file mode 100644 index 000000000..aff286d3e --- /dev/null +++ b/config/locales/models/project_statistic.en.yml @@ -0,0 +1,14 @@ +en: + layout: + project_statistics: + human_average_build_time: Expected time is %{hours} h. %{minutes} min. + + flash: + project_statistic: + + + activerecord: + models: + project_statistic: Project statistic + attributes: + project_statistic: \ No newline at end of file diff --git a/config/locales/models/project_statistic.ru.yml b/config/locales/models/project_statistic.ru.yml new file mode 100644 index 000000000..71e41b735 --- /dev/null +++ b/config/locales/models/project_statistic.ru.yml @@ -0,0 +1,16 @@ +ru: + layout: + project_statistics: + human_average_build_time: 'Ожидаемое время: %{hours} ч. %{minutes} мин.' + + flash: + project_statistic: + + + activerecord: + models: + project_statistic: Статистика проекта + attributes: + project_statistic: + + diff --git a/config/locales/models/project_to_repository.en.yml b/config/locales/models/project_to_repository.en.yml new file mode 100644 index 000000000..553d2e8f3 --- /dev/null +++ b/config/locales/models/project_to_repository.en.yml @@ -0,0 +1,5 @@ +en: + activerecord: + errors: + project_to_repository: + project: Project already exists in platform \ No newline at end of file diff --git a/config/locales/models/project_to_repository.ru.yml b/config/locales/models/project_to_repository.ru.yml new file mode 100644 index 000000000..503a54f54 --- /dev/null +++ b/config/locales/models/project_to_repository.ru.yml @@ -0,0 +1,5 @@ +ru: + activerecord: + errors: + project_to_repository: + project: Проект уже присутствует в платформе diff --git a/config/locales/models/pull_request.en.yml b/config/locales/models/pull_request.en.yml index 156bf8192..57a0f3a7a 100644 --- a/config/locales/models/pull_request.en.yml +++ b/config/locales/models/pull_request.en.yml @@ -45,6 +45,8 @@ en: save_error: Unable to save pull request activerecord: + models: + pull_request: Pull Request attributes: pull_requests: to_ref: Source @@ -58,4 +60,6 @@ en: search: Find pull request... all: All requests to_me: Assigned to me + assigned: Assigned to me + created: Created by you view_full_changes: View full changes diff --git a/config/locales/models/pull_request.ru.yml b/config/locales/models/pull_request.ru.yml index 26dd0f761..2a9c64888 100644 --- a/config/locales/models/pull_request.ru.yml +++ b/config/locales/models/pull_request.ru.yml @@ -21,7 +21,7 @@ ru: ready: Данный пул реквест можно смержить автоматически. merged: | %{user} смержил %{to_ref} - с %{from_ref} в %{time}' + с %{from_ref} в %{time} closed: '%{user} закрыл пул реквест в %{time}' is_big: Этот пул реквест слишком большой! Мы показываем только последние %{count} коммитов. open: '' @@ -46,6 +46,8 @@ ru: save_error: Не удалось сохранить пул реквест activerecord: + models: + pull_request: Пул реквест attributes: pull_requests: to_ref: Приемник @@ -59,4 +61,6 @@ ru: search: Найти пул реквест... all: Все пул реквесты to_me: Назначенные мне + assigned: Назначенные мне + created: Созданные мной view_full_changes: Посмотреть все изменения diff --git a/config/locales/models/regeneration_status.en.yml b/config/locales/models/regeneration_status.en.yml new file mode 100644 index 000000000..8ac264eba --- /dev/null +++ b/config/locales/models/regeneration_status.en.yml @@ -0,0 +1,31 @@ +en: + layout: + regeneration_statuses: + regenerate_metadata: Regenerate metadata + statuses: + no_data: No data + ready: No actions + waiting_for_regeneration: Waiting for regeneration metadata + regenerating: Regeneration of metadata + waiting_for_resign: Waiting for resign of packages + publish: Publish + resign: Resign of packages + waiting_for_resign_after_publish: Publish, waiting for resign of packages + waiting_for_resign_after_regeneration: Regeneration of metadata, waiting for resign of packages + waiting_for_regeneration_after_publish: Publish, waiting for regeneration metadata + waiting_for_regeneration_after_resign: Resign of packages, waiting for regeneration metadata + waiting_for_resign_and_regeneration_after_publish: Publish, waiting for resign of packages and regeneration metadata + waiting_for_resign_and_regeneration: Waiting for resign of packages and regeneration metadata + last_regenerated_statuses: + no_data: No data + completed: Completed succesfully + failed: Completed not succesfully + canceled: Canceled + + activerecord: + attributes: + regeneration_status: + last_regenerated_at: Last regeneration of metadata + last_regenerated_status: Last status of regeneration metadata + last_regenerated_log_sha1: Last log of regeneration metadata + status: Current status diff --git a/config/locales/models/regeneration_status.ru.yml b/config/locales/models/regeneration_status.ru.yml new file mode 100644 index 000000000..03878398b --- /dev/null +++ b/config/locales/models/regeneration_status.ru.yml @@ -0,0 +1,31 @@ +ru: + layout: + regeneration_statuses: + regenerate_metadata: Регенерировать метаданные + statuses: + no_data: Нет данных + ready: Нет действий + waiting_for_regeneration: Ожидание регенерации метаданных + regenerating: Идет регенерация метаданных + waiting_for_resign: Ожидание подписи пакетов + publish: Идет публикация + resign: Идет подпись пакетов + waiting_for_resign_after_publish: Идет публикация, ожидание подписи пакетов + waiting_for_resign_after_regeneration: Идет регенерация метаданных, ожидание подписи пакетов + waiting_for_regeneration_after_publish: Идет публикация, ожидание регенерации метаданных + waiting_for_regeneration_after_resign: Идет подпись пакетов, ожидание регенерации метаданных + waiting_for_resign_and_regeneration_after_publish: Идет публикация, ожидание подписи пакетов и регенерации метаданных + waiting_for_resign_and_regeneration: Ожидание подписи пакетов и регенерации метаданных + last_regenerated_statuses: + no_data: Нет данных + completed: Завершена успешно + failed: Завершена с ошибкой + canceled: Отменена + + activerecord: + attributes: + regeneration_status: + last_regenerated_at: Последняя регенерация метаданных + last_regenerated_status: Статус последней регенерации метаданных + last_regenerated_log_sha1: Последний лог регенерации метаданных + status: Текущее состояние diff --git a/config/locales/models/repository.en.yml b/config/locales/models/repository.en.yml index 11dcae4db..a431aea3d 100644 --- a/config/locales/models/repository.en.yml +++ b/config/locales/models/repository.en.yml @@ -1,7 +1,12 @@ en: layout: repositories: - add_project_to: Add project to repository + extra_actions: Extra actions + add_sync_lock_file: "Add '.sync.lock' file" + remove_sync_lock_file: "Remove '.sync.lock' file" + sync_lock_file_info: "Presence of '.sync.lock' file means that repository is not ready for sync with mirror" + add_projects_to: Add projects to repository + remove_projects_from: Remove projects from repository edit: Settings list: List about: About repository @@ -19,24 +24,29 @@ en: clear_confirm: Are you sure you want to clear this platform? clear_warning: Attention! Cleared packages cannot be restored! regenerate_metadata: Regenerate metadata + mass_delete: Mass delete personal_repositories: settings_header: Settings - change_visibility_from_hidden: Change status to "Public" - change_visibility_from_open: Change status to "Private" settings: Settings show: My repository private_users: Private repository users flash: repository: + sync_lock_file_added: "'.sync.lock' file has been added to repository" + sync_lock_file_removed: "'.sync.lock' file has been removed from repository" + projects_will_be_removed: Projects added to queue for removing + projects_will_be_added: Projects added to queue for adding + no_access_to_read_project: You have no access to read this project + saved: Repository added updated: Repository updated save_error: Unable to add repository update_error: Unable to update repository destroyed: Repository deleted project_added: Project added to repository - project_not_added: Project adding error. A project with such name already exists in this repository. Remove the old project first + project_not_added: Project adding error. A project with such name already exists in one repository of platform. Remove the old project first project_removed: Project deleted project_not_removed: Unable to delete project from repository clear: Platform successfully cleared! @@ -51,6 +61,7 @@ en: repository: Repository attributes: repository: + projects_list: Projects list name: Name description: Description publish_without_qa: Publication without QA diff --git a/config/locales/models/repository.ru.yml b/config/locales/models/repository.ru.yml index f5e5a58fa..9474f1ffb 100644 --- a/config/locales/models/repository.ru.yml +++ b/config/locales/models/repository.ru.yml @@ -1,7 +1,12 @@ ru: layout: repositories: - add_project_to: Добавить проект к репозиторию + extra_actions: Дополнительные действия + add_sync_lock_file: "Добавить '.sync.lock' файл" + remove_sync_lock_file: "Удалить '.sync.lock' файл" + sync_lock_file_info: "Наличие '.sync.lock' файла означает, что репозиторий не готов для синхронизации с зеркалом" + add_projects_to: Добавить проекты к репозиторию + remove_projects_from: Удалить проекты из репозитория edit: Настройки list: Список about: О репозитории @@ -19,24 +24,29 @@ ru: clear_confirm: Уверены, что хотите очистить платформу? clear_warning: Внимание! Очищенные пакеты не могут быть восстановлены! regenerate_metadata: Регенерировать метаданные + mass_delete: Массовое удаление personal_repositories: settings_header: Настройки - change_visibility_from_hidden: Сменить статус на "Публичный" - change_visibility_from_open: Сменить статус на "Приватный" settings: Настройки show: Мой репозиторий private_users: Пользователи приватного репозитория flash: repository: + sync_lock_file_added: "'.sync.lock' файл добавлен в репозиторий" + sync_lock_file_removed: "'.sync.lock' файл удален из репозитория" + projects_will_be_removed: Проекты добавлены в очередь на удаление + projects_will_be_added: Проекты добавлены в очередь на добавление + no_access_to_read_project: У вас нет прав на чтение этого проекта + saved: Репозиторий успешно добавлен updated: Репозиторий успешно обновлен save_error: Не удалось добавить репозиторий update_error: Не удалось обновить репозиторий destroyed: Репозиторий успешно удален project_added: Проект добавлен к репозиторию - project_not_added: Не удалось добавить проект. В этом репозитории уже есть проект с таким именем. Сначала нужно удалить старый проект + project_not_added: Не удалось добавить проект. В одном из репозиториев платформы уже есть проект с таким именем. Сначала нужно удалить старый проект project_removed: Проект удален из репозитория project_not_removed: Не удалось удалить проект из репозитория clear: Платформа успешно очищена! @@ -51,6 +61,7 @@ ru: repository: Репозиторий attributes: repository: + projects_list: Список проектов name: Название description: Описание publish_without_qa: Публикация без участия QA diff --git a/config/locales/models/time_living.en.yml b/config/locales/models/time_living.en.yml index c7c4cd11f..0659b4b91 100644 --- a/config/locales/models/time_living.en.yml +++ b/config/locales/models/time_living.en.yml @@ -1,4 +1,4 @@ en: flash: time_living: - numericality_error: must be 2 to 720 minutes \ No newline at end of file + numericality_error: must be %{min} to %{max} minutes \ No newline at end of file diff --git a/config/locales/models/time_living.ru.yml b/config/locales/models/time_living.ru.yml index ea0ef50b2..e8600ca10 100644 --- a/config/locales/models/time_living.ru.yml +++ b/config/locales/models/time_living.ru.yml @@ -1,4 +1,4 @@ ru: flash: time_living: - numericality_error: должно быть от 2 до 720 минут \ No newline at end of file + numericality_error: должно быть от %{min} до %{max} минут \ No newline at end of file diff --git a/config/locales/models/token.en.yml b/config/locales/models/token.en.yml new file mode 100644 index 000000000..31a55aeb3 --- /dev/null +++ b/config/locales/models/token.en.yml @@ -0,0 +1,32 @@ +en: + layout: + tokens: + header: Tokens + new: New token + about: Token of platform + withdraw: Withdraw + withdraw_confirm: Are you sure you want to withdraw this token? + + statuses: + active: Active + blocked: Withdrawed + + flash: + tokens: + saved: Token added + save_error: Unable to add token + withdraw_success: Token withdrawed + withdraw_fail: Unable to withdraw token + + activerecord: + models: + token: Token + attributes: + token: + description: Description + status: Status + authentication_token: Token + created_at: Created + updated_at: Updated + creator: Creator + updater: Updater diff --git a/config/locales/models/token.ru.yml b/config/locales/models/token.ru.yml new file mode 100644 index 000000000..26da79171 --- /dev/null +++ b/config/locales/models/token.ru.yml @@ -0,0 +1,32 @@ +ru: + layout: + tokens: + header: Токены + new: Новый токен + about: Токен платформы + withdraw: Отозвать + withdraw_confirm: Уверены, что хотите отозвать токен? + + statuses: + active: Активный + blocked: Отозван + + flash: + tokens: + saved: Токен успешно добавлен + save_error: Не удалось добавить токен + withdraw_success: Токен успешно отозван! + withdraw_fail: Не удалось отозвать токен! + + activerecord: + models: + token: Токен + attributes: + token: + description: Описание + status: Статус + authentication_token: Токен + created_at: Создан + updated_at: Обновлен + creator: Создал + updater: Обновил diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 1afe052cd..7bf16e90a 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -40,6 +40,7 @@ ru: publish: Опубликовать publish_again: Опубликовать снова publish_again_warning: Повторная публикация может привести к нарушению зависимостей в репозитории. Будьте осторожны! + publish_into_testing: '[testing] Опубликовать' reject_publish: Отклонить add: Добавить upload: Загрузить @@ -52,6 +53,7 @@ ru: atom_link_tag_title: Приватная лента для %{nickname} | %{app_name} preview: Предосмотр link: Ссылка + noscript_message: Включите javascript для корректной работы сайта settings: label: 'Настройки' @@ -87,6 +89,7 @@ ru: sessions: sign_in_header: Вход в систему + sign_up_with: или войти с помощью private_users: list: Список @@ -151,11 +154,11 @@ ru: group_already_added: Группа уже добавлена successfully_added: Участник %s успешно добавлен error_in_adding: Ошибка при добавлении участника %s - wrong_user: Пользователь с ником '%{uname}' не найден. + wrong_user: "Пользователь с ником '%{uname}' не найден." blob: - successfully_updated: Файл '%{name}' успешно обновлен - updating_error: Ошибка обновления файла '%{name}' + successfully_updated: "Файл '%{name}' успешно обновлен" + updating_error: "Ошибка обновления файла '%{name}'" attributes: password: Пароль @@ -176,7 +179,6 @@ ru: models: private_user: Приватный пользователь product_build_list: Сборочный лист продукта - auto_build_list: Автоматическая пересборка пакетов attributes: settings: @@ -186,6 +188,7 @@ ru: new_comment_reply: Оповещать о новом ответе на мой комментарий new_issue: Оповещать о новых задачах в моих проектах issue_assign: Оповещать, когда на меня выставляют задачу + update_code: Оповещать об изменении кода в моих проектах new_comment_commit_owner: Оповещать о комментариях к моему коммиту new_comment_commit_repo_owner: Оповещать о комментариях к коммитам в моем репозитории new_comment_commit_commentor: Оповещать о комментариях к коммиту после моего diff --git a/config/locales/title.en.yml b/config/locales/title.en.yml index 01c37f6b7..887e2ada2 100644 --- a/config/locales/title.en.yml +++ b/config/locales/title.en.yml @@ -33,3 +33,8 @@ en: product_build_lists: index: title: 'Products Monitoring' + home: + issues: + title: 'Your Issues' + pull_requests: + title: 'Your Pull Requests' diff --git a/config/locales/title.ru.yml b/config/locales/title.ru.yml index 736eb83c9..b991fc6a3 100644 --- a/config/locales/title.ru.yml +++ b/config/locales/title.ru.yml @@ -33,3 +33,8 @@ ru: product_build_lists: index: title: 'Мониторинг продуктов' + home: + issues: + title: 'Ваши задачи' + pull_requests: + title: 'Ваши пул реквесты' diff --git a/config/locales/users.en.yml b/config/locales/users.en.yml index b910b21f6..670b678d2 100644 --- a/config/locales/users.en.yml +++ b/config/locales/users.en.yml @@ -42,6 +42,7 @@ en: role: Role created_at: Created updated_at: Updated + sound_notifications: Enable sound notifications role: System role language: Language password: Password @@ -52,3 +53,4 @@ en: company: Company avatar: Avatar avatar_file_size: Avatar file size + authentication_token: API token diff --git a/config/locales/users.ru.yml b/config/locales/users.ru.yml index bd30181e5..a79c1bf57 100644 --- a/config/locales/users.ru.yml +++ b/config/locales/users.ru.yml @@ -42,6 +42,7 @@ ru: role: Роль created_at: Создан updated_at: Обновлен + sound_notifications: Включить звуковые оповещения role: Роль в системе language: Язык password: Пароль @@ -52,3 +53,4 @@ ru: company: Компания avatar: Аватар avatar_file_size: Размер аватара + authentication_token: API токен diff --git a/config/newrelic.yml.sample b/config/newrelic.yml.sample index 5c716151d..15ad09c1d 100644 --- a/config/newrelic.yml.sample +++ b/config/newrelic.yml.sample @@ -217,6 +217,7 @@ test: production: <<: *default_settings monitor_mode: true + dispatcher: 'puma' # Many applications have a staging environment which behaves # identically to production. Support for that environment is provided @@ -225,3 +226,4 @@ staging: <<: *default_settings monitor_mode: true app_name: ABF (Staging) + dispatcher: 'puma' diff --git a/config/puma/production.rb b/config/puma/production.rb new file mode 100644 index 000000000..38f02c028 --- /dev/null +++ b/config/puma/production.rb @@ -0,0 +1,28 @@ +# -*- encoding : utf-8 -*- +base_path = "/srv/rosa_build" +pidfile File.join(base_path, 'shared', 'pids', 'unicorn.pid') +state_path File.join(base_path, 'shared', 'pids', 'puma.state') +bind 'unix:///tmp/rosa_build_unicorn.sock' + +environment ENV['RAILS_ENV'] || 'production' +threads *(ENV['PUMA_THREADS'] || '1,4').split(',') +workers ENV['PUMA_WORKERS'] || 7 + + +preload_app! + +on_worker_boot do + if defined?(ActiveRecord::Base) + ActiveSupport.on_load(:active_record) do + ActiveRecord::Base.connection.disconnect! rescue ActiveRecord::ConnectionNotEstablished + ActiveRecord::Base.establish_connection + end + # QC::Conn.connect + Rails.logger.info('Connected to PG') + end + + # Redis.connect! + # Rails.logger.info('Connected to Redis') +end + +activate_control_app 'unix:///tmp/rosa_build_pumactl.sock' \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 25b9c93ac..cfefca5ab 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -7,7 +7,10 @@ Rosa::Application.routes.draw do devise_scope :users do get '/users/auth/:provider' => 'users/omniauth_callbacks#passthru' end - devise_for :users, :controllers => {:omniauth_callbacks => 'users/omniauth_callbacks'} + devise_for :users, :controllers => {:omniauth_callbacks => 'users/omniauth_callbacks'}, :skip => [:registrations] do + get 'users/sign_up' => 'users/registrations#new', :as => :new_user_registration + post 'users' => 'users/registrations#create', :as => :user_registration + end namespace :api do namespace :v1 do @@ -19,12 +22,14 @@ Rosa::Application.routes.draw do put :reject_publish put :cancel put :create_container + put :publish_into_testing } end resources :arches, :only => [:index] resources :platforms, :only => [:index, :show, :update, :destroy, :create] do collection { get :platforms_for_build + get :allowed } member { get :members @@ -37,13 +42,15 @@ Rosa::Application.routes.draw do end resources :repositories, :only => [:show, :update, :destroy] do member { - get :projects - get :key_pair - put :add_member - delete :remove_member - put :add_project - delete :remove_project - put :signatures + get :projects + get :key_pair + put :add_member + delete :remove_member + put :add_project + delete :remove_project + put :signatures + put :add_repo_lock_file + delete :remove_repo_lock_file } end resources :projects, :only => [:index, :show, :update, :create, :destroy] do @@ -57,6 +64,14 @@ Rosa::Application.routes.draw do put :update_member } resources :build_lists, :only => :index + resources :issues, :only => [:index, :create, :show, :update] + resources :pull_requests, :only => [:index, :create, :show, :update] do + member { + get :commits + get :files + put :merge + } + end end resources :users, :only => [:show] get 'user' => 'users#show_current_user' @@ -64,6 +79,8 @@ Rosa::Application.routes.draw do member { get :notifiers put :notifiers + get '/issues' => 'issues#user_index' + get '/pull_requests' => 'pull_requests#user_index' } end resources :groups, :only => [:index, :show, :update, :create, :destroy] do @@ -72,6 +89,8 @@ Rosa::Application.routes.draw do put :add_member delete :remove_member put :update_member + get '/issues' => 'issues#group_index' + get '/pull_requests' => 'pull_requests#group_index' } end resources :products, :only => [:show, :update, :create, :destroy] do @@ -80,7 +99,20 @@ Rosa::Application.routes.draw do resources :product_build_lists, :only => [:index, :show, :destroy, :create, :update] do put :cancel, :on => :member end + + resources :jobs do + collection do + get :shift + get :status + put :feedback + put :logs + put :statistics + end + end + #resources :ssh_keys, :only => [:index, :create, :destroy] + get 'issues' => 'issues#all_index' + get 'pull_requests' => 'pull_requests#all_index' end end @@ -89,16 +121,19 @@ Rosa::Application.routes.draw do get '/forbidden' => 'pages#forbidden', :as => 'forbidden' get '/terms-of-service' => 'pages#tos', :as => 'tos' get '/tour/:id' => 'pages#tour_inside', :as => 'tour_inside', :id => /projects|sources|builds/ - match '/invite.html' => redirect('/register_requests/new') + #match '/invite.html' => redirect('/register_requests/new') + + get '/activity_feeds.:format' => 'home#activity', :as => 'atom_activity_feeds', :format => /atom/ + get '/issues' => 'home#issues' + get '/pull_requests' => 'home#pull_requests' - get '/activity_feeds.:format' => 'activity_feeds#index', :as => 'atom_activity_feeds', :format => /atom/ if APP_CONFIG['anonymous_access'] authenticated do - root :to => 'activity_feeds#index' + root :to => 'home#activity' end - root :to => 'pages#root' + root :to => 'home#root' else - root :to => 'activity_feeds#index' + root :to => 'home#activity' end namespace :admin do @@ -128,20 +163,25 @@ Rosa::Application.routes.draw do end scope :module => 'platforms' do - resources :platforms do + resources :platforms, :constraints => {:id => Platform::NAME_PATTERN} do resources :private_users, :except => [:show, :destroy, :update] member do + put :regenerate_metadata put :clear get :clone get :members post :remove_members # fixme: change post to delete + post :change_visibility delete :remove_member - put :add_member + post :add_member post :make_clone get :advisories end - resources :mass_builds, :only => [:create, :index] do + resources :contents, :only => [:index] + match '/contents/*path' => 'contents#index', :format => false + + resources :mass_builds, :only => [:create, :new, :index] do member do post :cancel post :publish @@ -151,16 +191,24 @@ Rosa::Application.routes.draw do resources :repositories do member do - get :add_project - delete :remove_project - get :projects_list - post :remove_members # fixme: change post to delete - delete :remove_member - post :add_member - put :regenerate_metadata + get :add_project + put :add_project + get :remove_project + delete :remove_project + get :projects_list + post :remove_members # fixme: change post to delete + delete :remove_member + post :add_member + put :regenerate_metadata + put :sync_lock_file end end resources :key_pairs, :only => [:create, :index, :destroy] + resources :tokens, :only => [:create, :index, :show, :new] do + member do + post :withdraw + end + end resources :products do resources :product_build_lists, :only => [:create, :destroy, :new, :show, :update] do member { @@ -181,6 +229,8 @@ Rosa::Application.routes.draw do collection do get :autocomplete_user_uname get :autocomplete_group_uname + get :autocomplete_extra_build_list + get :autocomplete_extra_repositories end end @@ -197,6 +247,7 @@ Rosa::Application.routes.draw do put :private get :notifiers put :notifiers + put :reset_auth_token end end resources :register_requests, :only => [:new, :create], :format => /ru|en/ #view support only two languages @@ -222,20 +273,23 @@ Rosa::Application.routes.draw do end scope :module => 'projects' do - resources :build_lists, :only => [:index, :show, :update] do + resources :build_lists, :only => [:index, :show] do member do put :cancel put :create_container get :log + put :publish + put :reject_publish + put :publish_into_testing end - collection { - get :autocomplete_to_extra_repos_and_builds - get :update_extra_repos_and_builds - post :search - } end - resources :projects, :only => [:index, :new, :create] + resources :projects, :only => [:index, :new, :create] do + collection do + post :run_mass_import + get :mass_import + end + end scope ':owner_name/:project_name', :constraints => {:project_name => Project::NAME_REGEXP} do # project scope :as => 'project' do resources :wiki do @@ -261,7 +315,7 @@ Rosa::Application.routes.draw do match 'compare/:versions' => 'wiki#compare', :versions => /([a-f0-9\^]{6,40})(\.\.\.[a-f0-9\^]{6,40})/, :as => :compare_versions, :via => :get end end - resources :issues, :except => :edit do + resources :issues, :except => [:destroy, :edit] do resources :comments, :only => [:edit, :create, :update, :destroy] resources :subscribes, :only => [:create, :destroy] collection do @@ -271,12 +325,14 @@ Rosa::Application.routes.draw do end post "/labels/:label_id" => "issues#destroy_label", :as => :issues_delete_label post "/labels/:label_id/update" => "issues#update_label", :as => :issues_update_label + resources :build_lists, :only => [:index, :new, :create] do - collection { post :search } + get :list, :on => :collection end resources :collaborators do get :find, :on => :collection end + resources :hooks, :except => :show resources :pull_requests, :except => :destroy do get :autocomplete_to_project, :on => :collection put :merge, :on => :member @@ -293,6 +349,7 @@ Rosa::Application.routes.draw do delete '/' => 'projects#destroy' # Member post '/fork' => 'projects#fork', :as => :fork_project + get '/possible_forks' => 'projects#possible_forks', :as => :possible_forks_project get '/sections' => 'projects#sections', :as => :sections_project post '/sections' => 'projects#sections' delete '/remove_user' => 'projects#remove_user', :as => :remove_user_project @@ -304,7 +361,11 @@ Rosa::Application.routes.draw do # Tags get '/tags' => "git/trees#tags", :as => :tags # Branches - get '/branches/:treeish' => "git/trees#branches", :as => :branches + get '/branches' => "git/trees#branches", :as => :branches + get '/branches/:treeish' => "git/trees#branches", :as => :branch + delete '/branches/:treeish' => "git/trees#destroy", :as => :branch + put '/branches/:treeish' => "git/trees#restore_branch", :as => :branch + post '/branches' => "git/trees#create", :as => :branches # Commits get '/commits/:treeish(/*path)' => "git/commits#index", :as => :commits, :format => false get '/commit/:id(.:format)' => "git/commits#show", :as => :commit diff --git a/config/schedule.rb b/config/schedule.rb index 44a7142b2..20dbff353 100644 --- a/config/schedule.rb +++ b/config/schedule.rb @@ -6,10 +6,9 @@ # runner "Download.parse_and_remove_nginx_log" #end -# TODO: Uncomment when all needed product build lists will be updated. -# every :day, :at => '4:10 am' do -# rake "product_build_list:clear:outdated", :output => 'log/product_build_list_clear.log' -# end +every :day, :at => '4:10 am' do + rake "product_build_list:clear:outdated", :output => 'log/product_build_list_clear.log' +end every :day, :at => '4:00 am' do rake "import:sync:all", :output => 'log/sync.log' @@ -31,6 +30,10 @@ every 3.minute do runner 'AbfWorker::BuildListsPublishTaskManager.new.run', :output => 'log/task_manager.log' end +every 1.minute do + runner 'RpmBuildNode.cleanup!' +end + every :day, :at => '4am' do runner 'Product.autostart_iso_builds_once_a_12_hours', :output => 'log/autostart_iso_builds.log' runner 'Product.autostart_iso_builds_once_a_day', :output => 'log/autostart_iso_builds.log' diff --git a/db/migrate/20130319172358_add_automatic_to_comments.rb b/db/migrate/20130319172358_add_automatic_to_comments.rb new file mode 100644 index 000000000..a3f2d24bc --- /dev/null +++ b/db/migrate/20130319172358_add_automatic_to_comments.rb @@ -0,0 +1,9 @@ +class AddAutomaticToComments < ActiveRecord::Migration + def change + add_column :comments, :automatic, :boolean, :default => false + + add_index :comments, :commentable_type + add_index :comments, :automatic + add_index :comments, :commentable_id + end +end diff --git a/db/migrate/20130326165628_add_add_info_to_comments.rb b/db/migrate/20130326165628_add_add_info_to_comments.rb new file mode 100644 index 000000000..5a88f8414 --- /dev/null +++ b/db/migrate/20130326165628_add_add_info_to_comments.rb @@ -0,0 +1,9 @@ +class AddAddInfoToComments < ActiveRecord::Migration + def change + add_column :comments, :created_from_commit_hash, :decimal, :precision => 50, :scale => 0 + add_column :comments, :created_from_issue_id, :integer + + add_index :comments, :created_from_issue_id + add_index :comments, :created_from_commit_hash + end +end diff --git a/db/migrate/20130403202853_add_name_with_owner_to_project.rb b/db/migrate/20130403202853_add_name_with_owner_to_project.rb new file mode 100644 index 000000000..7f7cd58f2 --- /dev/null +++ b/db/migrate/20130403202853_add_name_with_owner_to_project.rb @@ -0,0 +1,22 @@ +class AddNameWithOwnerToProject < ActiveRecord::Migration + def up + add_column :projects, :owner_uname, :string + + execute <<-SQL + UPDATE projects SET owner_uname = owners.uname + FROM users as owners + WHERE projects.owner_type = 'User' AND projects.owner_id = owners.id + SQL + + execute <<-SQL + UPDATE projects SET owner_uname = owners.uname + FROM groups as owners + WHERE projects.owner_type = 'Group' AND projects.owner_id = owners.id + SQL + change_column :projects, :owner_uname, :string, :null => false + end + + def down + remove_column :projects, :owner_uname + end +end diff --git a/db/migrate/20130412124536_create_hooks.rb b/db/migrate/20130412124536_create_hooks.rb new file mode 100644 index 000000000..cdfbc1857 --- /dev/null +++ b/db/migrate/20130412124536_create_hooks.rb @@ -0,0 +1,11 @@ +class CreateHooks < ActiveRecord::Migration + def change + create_table :hooks do |t| + t.text :data + t.integer :project_id + t.string :name + + t.timestamps + end + end +end diff --git a/db/migrate/20130417162427_add_user_index_to_issue.rb b/db/migrate/20130417162427_add_user_index_to_issue.rb new file mode 100644 index 000000000..b11c42283 --- /dev/null +++ b/db/migrate/20130417162427_add_user_index_to_issue.rb @@ -0,0 +1,5 @@ +class AddUserIndexToIssue < ActiveRecord::Migration + def change + add_index :issues, :user_id + end +end diff --git a/db/migrate/20130527181351_drop_use_save_to_repository_from_build_lists.rb b/db/migrate/20130527181351_drop_use_save_to_repository_from_build_lists.rb new file mode 100644 index 000000000..8207562ad --- /dev/null +++ b/db/migrate/20130527181351_drop_use_save_to_repository_from_build_lists.rb @@ -0,0 +1,9 @@ +class DropUseSaveToRepositoryFromBuildLists < ActiveRecord::Migration + def up + remove_column :build_lists, :use_save_to_repository + end + + def down + add_column :build_lists, :use_save_to_repository, :boolean, :default => true + end +end diff --git a/db/migrate/20130603124853_add_extra_repos_and_build_lists_to_mass_build.rb b/db/migrate/20130603124853_add_extra_repos_and_build_lists_to_mass_build.rb new file mode 100644 index 000000000..68abdc439 --- /dev/null +++ b/db/migrate/20130603124853_add_extra_repos_and_build_lists_to_mass_build.rb @@ -0,0 +1,22 @@ +class AddExtraReposAndBuildListsToMassBuild < ActiveRecord::Migration + class MassBuild < ActiveRecord::Base + end + + def up + add_column :mass_builds, :save_to_platform_id, :integer + MassBuild.update_all('save_to_platform_id = platform_id') + change_column :mass_builds, :save_to_platform_id, :integer, :null => false + change_column :mass_builds, :platform_id, :integer, :null => false + rename_column :mass_builds, :platform_id, :build_for_platform_id + add_column :mass_builds, :extra_repositories, :text + add_column :mass_builds, :extra_build_lists, :text + end + + def down + remove_column :mass_builds, :extra_repositories + remove_column :mass_builds, :extra_build_lists + remove_column :mass_builds, :save_to_platform_id + rename_column :mass_builds, :build_for_platform_id, :platform_id + end + +end diff --git a/db/migrate/20130624095928_create_tokens.rb b/db/migrate/20130624095928_create_tokens.rb new file mode 100644 index 000000000..a3bde8ba5 --- /dev/null +++ b/db/migrate/20130624095928_create_tokens.rb @@ -0,0 +1,19 @@ +class CreateTokens < ActiveRecord::Migration + + def change + create_table :tokens do |t| + t.integer :subject_id, :null => false + t.string :subject_type, :null => false + t.integer :creator_id, :null => false + t.integer :updater_id + t.string :status, :default => 'active' + t.text :description + t.string :authentication_token, :null => false + + t.timestamps + end + add_index :tokens, :authentication_token, :unique => true + add_index :tokens, [:subject_id, :subject_type] + end + +end diff --git a/db/migrate/20130701121313_drop_bs_id_from_build_lists.rb b/db/migrate/20130701121313_drop_bs_id_from_build_lists.rb new file mode 100644 index 000000000..d5db043d5 --- /dev/null +++ b/db/migrate/20130701121313_drop_bs_id_from_build_lists.rb @@ -0,0 +1,17 @@ +class DropBsIdFromBuildLists < ActiveRecord::Migration + + class BuildList < ActiveRecord::Base + end + + def up + remove_index :build_lists, :bs_id + remove_column :build_lists, :bs_id + end + + def down + add_column :build_lists, :bs_id, :integer + add_index :build_lists, :bs_id, :unique => true + BuildList.update_all('bs_id = id') + end + +end diff --git a/db/migrate/20130717112337_add_index_to_relation.rb b/db/migrate/20130717112337_add_index_to_relation.rb new file mode 100644 index 000000000..978d498bd --- /dev/null +++ b/db/migrate/20130717112337_add_index_to_relation.rb @@ -0,0 +1,6 @@ +class AddIndexToRelation < ActiveRecord::Migration + def change + add_index :relations, [:actor_type, :actor_id] + add_index :relations, [:target_type, :target_id] + end +end diff --git a/db/migrate/20130724105821_create_platform_arch_settings.rb b/db/migrate/20130724105821_create_platform_arch_settings.rb new file mode 100644 index 000000000..d83af5364 --- /dev/null +++ b/db/migrate/20130724105821_create_platform_arch_settings.rb @@ -0,0 +1,29 @@ +class CreatePlatformArchSettings < ActiveRecord::Migration + + def up + create_table :platform_arch_settings do |t| + t.integer :platform_id, :null => false + t.integer :arch_id, :null => false + t.integer :time_living, :null => false + t.boolean :default + t.timestamps + end + add_index :platform_arch_settings, [:platform_id, :arch_id], :unique => true + + arch_ids = Arch.where(:name => %w(i586 x86_64)).pluck(:id) + Platform.main.each do |platform| + arch_ids.each do |arch_id| + platform.platform_arch_settings.create( + :arch_id => arch_id, + :default => true, + :time_living => PlatformArchSetting::DEFAULT_TIME_LIVING / 60 + ) + end + end + + end + + def down + drop_table :platform_arch_settings + end +end diff --git a/db/migrate/20130731120901_create_project_statistics.rb b/db/migrate/20130731120901_create_project_statistics.rb new file mode 100644 index 000000000..3d7103210 --- /dev/null +++ b/db/migrate/20130731120901_create_project_statistics.rb @@ -0,0 +1,13 @@ +class CreateProjectStatistics < ActiveRecord::Migration + def change + create_table :project_statistics do |t| + t.integer :average_build_time, :null => false, :default => 0 + t.integer :build_count, :null => false, :default => 0 + t.integer :arch_id, :null => false + t.integer :project_id, :null => false + + t.timestamps + end + add_index :project_statistics, [:project_id, :arch_id], :unique => true + end +end diff --git a/db/migrate/20130731130518_drop_average_build_time_and_build_count_from_project.rb b/db/migrate/20130731130518_drop_average_build_time_and_build_count_from_project.rb new file mode 100644 index 000000000..723d331aa --- /dev/null +++ b/db/migrate/20130731130518_drop_average_build_time_and_build_count_from_project.rb @@ -0,0 +1,11 @@ +class DropAverageBuildTimeAndBuildCountFromProject < ActiveRecord::Migration + def up + remove_column :projects, :average_build_time + remove_column :projects, :build_count + end + + def down + add_column :projects, :average_build_time, :integer, :null => false, :default => 0 + add_column :projects, :build_count, :integer, :null => false, :default => 0 + end +end diff --git a/db/migrate/20130820195938_add_sound_notifications_to_user.rb b/db/migrate/20130820195938_add_sound_notifications_to_user.rb new file mode 100644 index 000000000..a7b32696c --- /dev/null +++ b/db/migrate/20130820195938_add_sound_notifications_to_user.rb @@ -0,0 +1,5 @@ +class AddSoundNotificationsToUser < ActiveRecord::Migration + def change + add_column :users, :sound_notifications, :boolean, :default => true + end +end diff --git a/db/migrate/20130822154741_create_repository_statuses.rb b/db/migrate/20130822154741_create_repository_statuses.rb new file mode 100644 index 000000000..3687c9532 --- /dev/null +++ b/db/migrate/20130822154741_create_repository_statuses.rb @@ -0,0 +1,14 @@ +class CreateRepositoryStatuses < ActiveRecord::Migration + def change + create_table :repository_statuses do |t| + t.integer :repository_id, :null => false + t.integer :platform_id, :null => false + t.integer :status, :default => 0 + t.datetime :last_regenerated_at + t.integer :last_regenerated_status + + t.timestamps + end + add_index :repository_statuses, [:repository_id, :platform_id], :unique => true + end +end diff --git a/db/migrate/20130822160501_add_status_to_platform.rb b/db/migrate/20130822160501_add_status_to_platform.rb new file mode 100644 index 000000000..674858e73 --- /dev/null +++ b/db/migrate/20130822160501_add_status_to_platform.rb @@ -0,0 +1,7 @@ +class AddStatusToPlatform < ActiveRecord::Migration + def change + add_column :platforms, :status, :integer, :default => 0 + add_column :platforms, :last_regenerated_at, :datetime + add_column :platforms, :last_regenerated_status, :integer + end +end diff --git a/db/migrate/20130827144022_add_last_regenerated_log_sha1_to_platform_and_repository_status.rb b/db/migrate/20130827144022_add_last_regenerated_log_sha1_to_platform_and_repository_status.rb new file mode 100644 index 000000000..1445541b7 --- /dev/null +++ b/db/migrate/20130827144022_add_last_regenerated_log_sha1_to_platform_and_repository_status.rb @@ -0,0 +1,6 @@ +class AddLastRegeneratedLogSha1ToPlatformAndRepositoryStatus < ActiveRecord::Migration + def change + add_column :platforms, :last_regenerated_log_sha1, :string + add_column :repository_statuses, :last_regenerated_log_sha1, :string + end +end diff --git a/db/migrate/20130829161042_add_group_id_to_build_list.rb b/db/migrate/20130829161042_add_group_id_to_build_list.rb new file mode 100644 index 000000000..6662f14b0 --- /dev/null +++ b/db/migrate/20130829161042_add_group_id_to_build_list.rb @@ -0,0 +1,5 @@ +class AddGroupIdToBuildList < ActiveRecord::Migration + def change + add_column :build_lists, :group_id, :integer + end +end diff --git a/db/migrate/20130912113545_add_epoch_to_build_list_package.rb b/db/migrate/20130912113545_add_epoch_to_build_list_package.rb new file mode 100644 index 000000000..cb17ac841 --- /dev/null +++ b/db/migrate/20130912113545_add_epoch_to_build_list_package.rb @@ -0,0 +1,5 @@ +class AddEpochToBuildListPackage < ActiveRecord::Migration + def change + add_column :build_list_packages, :epoch, :integer + end +end diff --git a/db/migrate/20130918164616_add_increase_release_tag_to_mass_build.rb b/db/migrate/20130918164616_add_increase_release_tag_to_mass_build.rb new file mode 100644 index 000000000..f1197eaf3 --- /dev/null +++ b/db/migrate/20130918164616_add_increase_release_tag_to_mass_build.rb @@ -0,0 +1,5 @@ +class AddIncreaseReleaseTagToMassBuild < ActiveRecord::Migration + def change + add_column :mass_builds, :increase_release_tag, :boolean, :default => false, :null => false + end +end diff --git a/db/migrate/20130920133720_add_extra_params_to_build_list.rb b/db/migrate/20130920133720_add_extra_params_to_build_list.rb new file mode 100644 index 000000000..6811767fb --- /dev/null +++ b/db/migrate/20130920133720_add_extra_params_to_build_list.rb @@ -0,0 +1,5 @@ +class AddExtraParamsToBuildList < ActiveRecord::Migration + def change + add_column :build_lists, :extra_params, :text + end +end diff --git a/db/migrate/20131016190149_add_external_nodes_into_build_list.rb b/db/migrate/20131016190149_add_external_nodes_into_build_list.rb new file mode 100644 index 000000000..7d88c3356 --- /dev/null +++ b/db/migrate/20131016190149_add_external_nodes_into_build_list.rb @@ -0,0 +1,5 @@ +class AddExternalNodesIntoBuildList < ActiveRecord::Migration + def change + add_column :build_lists, :external_nodes, :string + end +end diff --git a/db/migrate/20131017171426_add_builder_into_build_list.rb b/db/migrate/20131017171426_add_builder_into_build_list.rb new file mode 100644 index 000000000..4d989bcf5 --- /dev/null +++ b/db/migrate/20131017171426_add_builder_into_build_list.rb @@ -0,0 +1,5 @@ +class AddBuilderIntoBuildList < ActiveRecord::Migration + def change + add_column :build_lists, :builder_id, :integer + end +end diff --git a/db/migrate/20131022082357_remove_default_status_from_platform.rb b/db/migrate/20131022082357_remove_default_status_from_platform.rb new file mode 100644 index 000000000..0fd2822ec --- /dev/null +++ b/db/migrate/20131022082357_remove_default_status_from_platform.rb @@ -0,0 +1,5 @@ +class RemoveDefaultStatusFromPlatform < ActiveRecord::Migration + def change + change_column :platforms, :status, :integer, :default => nil + end +end diff --git a/db/migrate/20131022082416_remove_default_status_from_product_build_list.rb b/db/migrate/20131022082416_remove_default_status_from_product_build_list.rb new file mode 100644 index 000000000..8846e5a2e --- /dev/null +++ b/db/migrate/20131022082416_remove_default_status_from_product_build_list.rb @@ -0,0 +1,5 @@ +class RemoveDefaultStatusFromProductBuildList < ActiveRecord::Migration + def change + change_column :product_build_lists, :status, :integer, :default => nil + end +end diff --git a/db/migrate/20131107152408_add_use_testing_repo_to_build_list.rb b/db/migrate/20131107152408_add_use_testing_repo_to_build_list.rb new file mode 100644 index 000000000..5c8c04a99 --- /dev/null +++ b/db/migrate/20131107152408_add_use_testing_repo_to_build_list.rb @@ -0,0 +1,5 @@ +class AddUseTestingRepoToBuildList < ActiveRecord::Migration + def change + add_column :build_lists, :include_testing_subrepository, :boolean + end +end diff --git a/db/migrate/20131122130913_add_user_index_to_activity_feed.rb b/db/migrate/20131122130913_add_user_index_to_activity_feed.rb new file mode 100644 index 000000000..7e391ac1f --- /dev/null +++ b/db/migrate/20131122130913_add_user_index_to_activity_feed.rb @@ -0,0 +1,5 @@ +class AddUserIndexToActivityFeed < ActiveRecord::Migration + def change + add_index :activity_feeds, [:user_id, :kind] + end +end diff --git a/db/migrate/20131126154305_add_new_commit_to_settings_notifier.rb b/db/migrate/20131126154305_add_new_commit_to_settings_notifier.rb new file mode 100644 index 000000000..cb4b49f6f --- /dev/null +++ b/db/migrate/20131126154305_add_new_commit_to_settings_notifier.rb @@ -0,0 +1,5 @@ +class AddNewCommitToSettingsNotifier < ActiveRecord::Migration + def change + add_column :settings_notifiers, :update_code, :boolean, :default => false + end +end diff --git a/db/schema.rb b/db/schema.rb index 723c833d8..f90c5de5a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130328112110) do +ActiveRecord::Schema.define(:version => 20131126154305) do create_table "activity_feeds", :force => true do |t| t.integer "user_id", :null => false @@ -21,6 +21,8 @@ ActiveRecord::Schema.define(:version => 20130328112110) do t.datetime "updated_at" end + add_index "activity_feeds", ["user_id", "kind"], :name => "index_activity_feeds_on_user_id_and_kind" + create_table "advisories", :force => true do |t| t.string "advisory_id" t.text "description", :default => "" @@ -95,6 +97,7 @@ ActiveRecord::Schema.define(:version => 20130328112110) do t.datetime "updated_at", :null => false t.boolean "actual", :default => false t.string "sha1" + t.integer "epoch" end add_index "build_list_packages", ["actual", "platform_id"], :name => "index_build_list_packages_on_actual_and_platform_id" @@ -104,7 +107,6 @@ ActiveRecord::Schema.define(:version => 20130328112110) do add_index "build_list_packages", ["project_id"], :name => "index_build_list_packages_on_project_id" create_table "build_lists", :force => true do |t| - t.integer "bs_id" t.integer "status" t.string "project_version" t.integer "project_id" @@ -112,7 +114,7 @@ ActiveRecord::Schema.define(:version => 20130328112110) do t.datetime "notified_at" t.datetime "created_at" t.datetime "updated_at" - t.boolean "is_circle", :default => false + t.boolean "is_circle", :default => false t.text "additional_repos" t.string "name" t.string "update_type" @@ -120,30 +122,33 @@ ActiveRecord::Schema.define(:version => 20130328112110) do t.integer "save_to_platform_id" t.text "include_repos" t.integer "user_id" - t.boolean "auto_publish", :default => true + t.boolean "auto_publish", :default => true t.string "package_version" t.string "commit_hash" - t.integer "priority", :default => 0, :null => false + t.integer "priority", :default => 0, :null => false t.datetime "started_at" t.integer "duration" t.integer "advisory_id" t.integer "mass_build_id" t.integer "save_to_repository_id" t.text "results" - t.boolean "new_core", :default => true + t.boolean "new_core", :default => true t.string "last_published_commit_hash" t.integer "container_status" - t.boolean "use_save_to_repository", :default => true - t.boolean "auto_create_container", :default => false + t.boolean "auto_create_container", :default => false t.text "extra_repositories" t.text "extra_build_lists" t.integer "publisher_id" + t.integer "group_id" + t.text "extra_params" + t.string "external_nodes" + t.integer "builder_id" + t.boolean "include_testing_subrepository" end add_index "build_lists", ["advisory_id"], :name => "index_build_lists_on_advisory_id" add_index "build_lists", ["arch_id"], :name => "index_build_lists_on_arch_id" add_index "build_lists", ["project_id", "save_to_repository_id", "build_for_platform_id", "arch_id"], :name => "maintainer_search_index" - add_index "build_lists", ["bs_id"], :name => "index_build_lists_on_bs_id", :unique => true add_index "build_lists", ["project_id"], :name => "index_build_lists_on_project_id" create_table "comments", :force => true do |t| @@ -152,11 +157,20 @@ ActiveRecord::Schema.define(:version => 20130328112110) do t.text "body" t.datetime "created_at" t.datetime "updated_at" - t.decimal "commentable_id", :precision => 50, :scale => 0 + t.decimal "commentable_id", :precision => 50, :scale => 0 t.integer "project_id" t.text "data" + t.boolean "automatic", :default => false + t.decimal "created_from_commit_hash", :precision => 50, :scale => 0 + t.integer "created_from_issue_id" end + add_index "comments", ["automatic"], :name => "index_comments_on_automatic" + add_index "comments", ["commentable_id"], :name => "index_comments_on_commentable_id" + add_index "comments", ["commentable_type"], :name => "index_comments_on_commentable_type" + add_index "comments", ["created_from_commit_hash"], :name => "index_comments_on_created_from_commit_hash" + add_index "comments", ["created_from_issue_id"], :name => "index_comments_on_created_from_issue_id" + create_table "event_logs", :force => true do |t| t.integer "user_id" t.string "user_name" @@ -195,6 +209,14 @@ ActiveRecord::Schema.define(:version => 20130328112110) do t.datetime "avatar_updated_at" end + create_table "hooks", :force => true do |t| + t.text "data" + t.integer "project_id" + t.string "name" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "issues", :force => true do |t| t.integer "serial_id" t.integer "project_id" @@ -210,6 +232,7 @@ ActiveRecord::Schema.define(:version => 20130328112110) do end add_index "issues", ["project_id", "serial_id"], :name => "index_issues_on_project_id_and_serial_id", :unique => true + add_index "issues", ["user_id"], :name => "index_issues_on_user_id" create_table "key_pairs", :force => true do |t| t.text "public", :null => false @@ -254,7 +277,7 @@ ActiveRecord::Schema.define(:version => 20130328112110) do add_index "labels", ["project_id"], :name => "index_labels_on_project_id" create_table "mass_builds", :force => true do |t| - t.integer "platform_id" + t.integer "build_for_platform_id", :null => false t.string "name" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false @@ -274,20 +297,39 @@ ActiveRecord::Schema.define(:version => 20130328112110) do t.boolean "new_core", :default => true t.integer "success_count", :default => 0, :null => false t.integer "build_canceled_count", :default => 0, :null => false + t.integer "save_to_platform_id", :null => false + t.text "extra_repositories" + t.text "extra_build_lists" + t.boolean "increase_release_tag", :default => false, :null => false end + create_table "platform_arch_settings", :force => true do |t| + t.integer "platform_id", :null => false + t.integer "arch_id", :null => false + t.integer "time_living", :null => false + t.boolean "default" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "platform_arch_settings", ["platform_id", "arch_id"], :name => "index_platform_arch_settings_on_platform_id_and_arch_id", :unique => true + create_table "platforms", :force => true do |t| t.string "description" - t.string "name", :null => false + t.string "name", :null => false t.integer "parent_platform_id" t.datetime "created_at" t.datetime "updated_at" - t.boolean "released", :default => false, :null => false + t.boolean "released", :default => false, :null => false t.integer "owner_id" t.string "owner_type" - t.string "visibility", :default => "open", :null => false - t.string "platform_type", :default => "main", :null => false - t.string "distrib_type", :null => false + t.string "visibility", :default => "open", :null => false + t.string "platform_type", :default => "main", :null => false + t.string "distrib_type", :null => false + t.integer "status" + t.datetime "last_regenerated_at" + t.integer "last_regenerated_status" + t.string "last_regenerated_log_sha1" end add_index "platforms", ["name"], :name => "index_platforms_on_name", :unique => true, :case_sensitive => false @@ -303,7 +345,7 @@ ActiveRecord::Schema.define(:version => 20130328112110) do create_table "product_build_lists", :force => true do |t| t.integer "product_id" - t.integer "status", :default => 2, :null => false + t.integer "status", :null => false t.datetime "created_at" t.datetime "updated_at" t.integer "project_id" @@ -347,6 +389,17 @@ ActiveRecord::Schema.define(:version => 20130328112110) do add_index "project_imports", ["platform_id", "name"], :name => "index_project_imports_on_name_and_platform_id", :unique => true, :case_sensitive => false + create_table "project_statistics", :force => true do |t| + t.integer "average_build_time", :default => 0, :null => false + t.integer "build_count", :default => 0, :null => false + t.integer "arch_id", :null => false + t.integer "project_id", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "project_statistics", ["project_id", "arch_id"], :name => "index_project_statistics_on_project_id_and_arch_id", :unique => true + create_table "project_tags", :force => true do |t| t.integer "project_id" t.string "commit_id" @@ -383,10 +436,9 @@ ActiveRecord::Schema.define(:version => 20130328112110) do t.boolean "has_wiki", :default => false t.string "default_branch", :default => "master" t.boolean "is_package", :default => true, :null => false - t.integer "average_build_time", :default => 0, :null => false - t.integer "build_count", :default => 0, :null => false t.integer "maintainer_id" t.boolean "publish_i686_into_x86_64", :default => false + t.string "owner_uname", :null => false end add_index "projects", ["owner_id", "name", "owner_type"], :name => "index_projects_on_name_and_owner_id_and_owner_type", :unique => true, :case_sensitive => false @@ -431,6 +483,9 @@ ActiveRecord::Schema.define(:version => 20130328112110) do t.string "role" end + add_index "relations", ["actor_type", "actor_id"], :name => "index_relations_on_actor_type_and_actor_id" + add_index "relations", ["target_type", "target_id"], :name => "index_relations_on_target_type_and_target_id" + create_table "repositories", :force => true do |t| t.string "description", :null => false t.integer "platform_id", :null => false @@ -442,8 +497,21 @@ ActiveRecord::Schema.define(:version => 20130328112110) do add_index "repositories", ["platform_id"], :name => "index_repositories_on_platform_id" + create_table "repository_statuses", :force => true do |t| + t.integer "repository_id", :null => false + t.integer "platform_id", :null => false + t.integer "status", :default => 0 + t.datetime "last_regenerated_at" + t.integer "last_regenerated_status" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "last_regenerated_log_sha1" + end + + add_index "repository_statuses", ["repository_id", "platform_id"], :name => "index_repository_statuses_on_repository_id_and_platform_id", :unique => true + create_table "settings_notifiers", :force => true do |t| - t.integer "user_id", :null => false + t.integer "user_id", :null => false t.boolean "can_notify", :default => true t.boolean "new_comment", :default => true t.boolean "new_comment_reply", :default => true @@ -456,6 +524,7 @@ ActiveRecord::Schema.define(:version => 20130328112110) do t.boolean "new_comment_commit_commentor", :default => true t.boolean "new_build", :default => true t.boolean "new_associated_build", :default => true + t.boolean "update_code", :default => false end create_table "ssh_keys", :force => true do |t| @@ -480,6 +549,21 @@ ActiveRecord::Schema.define(:version => 20130328112110) do t.decimal "subscribeable_id", :precision => 50, :scale => 0 end + create_table "tokens", :force => true do |t| + t.integer "subject_id", :null => false + t.string "subject_type", :null => false + t.integer "creator_id", :null => false + t.integer "updater_id" + t.string "status", :default => "active" + t.text "description" + t.string "authentication_token", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "tokens", ["authentication_token"], :name => "index_tokens_on_authentication_token", :unique => true + add_index "tokens", ["subject_id", "subject_type"], :name => "index_tokens_on_subject_id_and_subject_type" + create_table "users", :force => true do |t| t.string "name" t.string "email", :default => "", :null => false @@ -510,6 +594,7 @@ ActiveRecord::Schema.define(:version => 20130328112110) do t.datetime "confirmation_sent_at" t.string "authentication_token" t.integer "build_priority", :default => 50 + t.boolean "sound_notifications", :default => true end add_index "users", ["authentication_token"], :name => "index_users_on_authentication_token" diff --git a/lib/abf_worker/build_lists_publish_task_manager.rb b/lib/abf_worker/build_lists_publish_task_manager.rb index 58dc6e8b7..ec8f57ed6 100644 --- a/lib/abf_worker/build_lists_publish_task_manager.rb +++ b/lib/abf_worker/build_lists_publish_task_manager.rb @@ -2,14 +2,10 @@ module AbfWorker class BuildListsPublishTaskManager REDIS_MAIN_KEY = 'abf-worker::build-lists-publish-task-manager::' - %w(RESIGN_REPOSITORIES - PROJECTS_FOR_CLEANUP + %w(PROJECTS_FOR_CLEANUP LOCKED_PROJECTS_FOR_CLEANUP - LOCKED_REPOSITORIES - LOCKED_REP_AND_PLATFORMS LOCKED_BUILD_LISTS - PACKAGES_FOR_CLEANUP - REGENERATE_METADATA).each do |kind| + PACKAGES_FOR_CLEANUP).each do |kind| const_set kind, "#{REDIS_MAIN_KEY}#{kind.downcase.gsub('_', '-')}" end @@ -19,21 +15,31 @@ module AbfWorker end def run + create_tasks_for_regenerate_metadata_for_software_center create_tasks_for_resign_repositories create_tasks_for_repository_regenerate_metadata create_tasks_for_build_rpms + create_tasks_for_build_rpms true end class << self def destroy_project_from_repository(project, repository) if repository.platform.personal? Platform.main.each do |main_platform| - redis.lpush PROJECTS_FOR_CLEANUP, "#{project.id}-#{repository.id}-#{main_platform.id}" + key = "#{project.id}-#{repository.id}-#{main_platform.id}" + redis.lpush PROJECTS_FOR_CLEANUP, key gather_old_packages project.id, repository.id, main_platform.id + + redis.lpush PROJECTS_FOR_CLEANUP, ('testing-' << key) + gather_old_packages project.id, repository.id, main_platform.id, true end else - redis.lpush PROJECTS_FOR_CLEANUP, "#{project.id}-#{repository.id}-#{repository.platform.id}" + key = "#{project.id}-#{repository.id}-#{repository.platform.id}" + redis.lpush PROJECTS_FOR_CLEANUP, key gather_old_packages project.id, repository.id, repository.platform.id + + redis.lpush PROJECTS_FOR_CLEANUP, ('testing-' << key) + gather_old_packages project.id, repository.id, repository.platform.id, true end end @@ -51,25 +57,14 @@ module AbfWorker end end - def resign_repository(key_pair) - redis.lpush RESIGN_REPOSITORIES, key_pair.repository_id - end - - def repository_regenerate_metadata(repository_id) - return false if Resque.redis.lrange(REGENERATE_METADATA, 0, -1).include? repository_id.to_s - redis.lpush REGENERATE_METADATA, repository_id - end - - def unlock_repository(repository_id) - redis.lrem LOCKED_REPOSITORIES, 0, repository_id - end - def unlock_build_list(build_list) redis.lrem LOCKED_BUILD_LISTS, 0, build_list.id end - def unlock_rep_and_platform(lock_str) - redis.lrem LOCKED_REP_AND_PLATFORMS, 0, lock_str + def packages_structure + structure = {:sources => [], :binaries => {}} + Arch.pluck(:name).each{ |name| structure[:binaries][name.to_sym] = [] } + structure end def redis @@ -80,35 +75,40 @@ module AbfWorker platform_path = "#{build_list.save_to_platform.path}/container/#{build_list.id}" system "rm -rf #{platform_path} && mkdir -p #{platform_path}" - packages = {:sources => [], :binaries => {:x86_64 => [], :i586 => []}} + packages = packages_structure packages[:sources] = build_list.packages.by_package_type('source').pluck(:sha1).compact packages[:binaries][build_list.arch.name.to_sym] = build_list.packages.by_package_type('binary').pluck(:sha1).compact distrib_type = build_list.build_for_platform.distrib_type cmd_params = { - 'RELEASED' => false, - 'REPOSITORY_NAME' => build_list.save_to_repository.name, - 'TYPE' => distrib_type, - 'IS_CONTAINER' => true, - 'ID' => build_list.id, - 'PLATFORM_NAME' => build_list.save_to_platform.name + 'RELEASED' => false, + 'REPOSITORY_NAME' => build_list.save_to_repository.name, + 'TYPE' => distrib_type, + 'IS_CONTAINER' => true, + 'ID' => build_list.id, + 'SAVE_TO_PLATFORM' => build_list.save_to_platform.name, + 'BUILD_FOR_PLATFORM' => build_list.build_for_platform.name }.map{ |k, v| "#{k}=#{v}" }.join(' ') - + # Low priority Resque.push( - 'publish_worker_default', - 'class' => 'AbfWorker::PublishWorkerDefault', + 'publish_worker', + 'class' => 'AbfWorker::PublishWorker', 'args' => [{ :id => build_list.id, - :arch => build_list.arch.name, - :distrib_type => distrib_type, :cmd_params => cmd_params, - :platform => {:platform_path => platform_path}, + :main_script => 'build.sh', + :rollback_script => 'rollback.sh', + :platform => { + :platform_path => platform_path, + :type => distrib_type, + :name => build_list.build_for_platform.name, + :arch => build_list.arch.name + }, :repository => {:id => build_list.save_to_repository_id}, - :type => :publish, :time_living => 9600, # 160 min :packages => packages, - :old_packages => {:sources => [], :binaries => {:x86_64 => [], :i586 => []}}, + :old_packages => packages_structure, :build_list_ids => [build_list.id], :projects_for_cleanup => [], :extra => {:create_container => true} @@ -116,11 +116,12 @@ module AbfWorker ) end - def gather_old_packages(project_id, repository_id, platform_id) + def gather_old_packages(project_id, repository_id, platform_id, testing = false) build_lists_for_cleanup = [] + status = testing ? BuildList::BUILD_PUBLISHED_INTO_TESTING : BuildList::BUILD_PUBLISHED Arch.pluck(:id).each do |arch_id| bl = BuildList.where(:project_id => project_id). - where(:new_core => true, :status => BuildList::BUILD_PUBLISHED). + where(:new_core => true, :status => status). where(:save_to_repository_id => repository_id). where(:build_for_platform_id => platform_id). where(:arch_id => arch_id). @@ -128,14 +129,14 @@ module AbfWorker build_lists_for_cleanup << bl if bl end - old_packages = {:sources => [], :binaries => {:x86_64 => [], :i586 => []}} + old_packages = packages_structure build_lists_for_cleanup.each do |bl| - bl.last_published.includes(:packages).limit(2).each{ |old_bl| + bl.last_published(testing).includes(:packages).limit(2).each{ |old_bl| fill_packages(old_bl, old_packages, :fullname) } end - - redis.hset PACKAGES_FOR_CLEANUP, "#{project_id}-#{repository_id}-#{platform_id}", old_packages.to_json + key = (testing ? 'testing-' : '') << "#{project_id}-#{repository_id}-#{platform_id}" + redis.hset PACKAGES_FOR_CLEANUP, key, old_packages.to_json end def fill_packages(bl, results_map, field = :sha1) @@ -154,18 +155,15 @@ module AbfWorker private - - def locked_repositories - @redis.lrange LOCKED_REPOSITORIES, 0, -1 - end - def create_tasks_for_resign_repositories - resign_repos = @redis.lrange RESIGN_REPOSITORIES, 0, -1 - - Repository.where(:id => (resign_repos - locked_repositories)).each do |r| - @redis.lrem RESIGN_REPOSITORIES, 0, r.id - @redis.lpush LOCKED_REPOSITORIES, r.id - + RepositoryStatus.platform_ready + .for_resign + .includes(:repository => :platform) + .readonly(false) + .each do |repository_status| + r = repository_status.repository + # Checks mirror sync status + next if r.repo_lock_file_exists? distrib_type = r.platform.distrib_type cmd_params = { @@ -179,75 +177,74 @@ module AbfWorker 'class' => "AbfWorker::PublishWorkerDefault", 'args' => [{ :id => r.id, - :arch => 'x86_64', - :distrib_type => distrib_type, :cmd_params => cmd_params, - :platform => {:platform_path => "#{r.platform.path}/repository"}, + :main_script => 'resign.sh', + :platform => { + :platform_path => "#{r.platform.path}/repository", + :type => distrib_type, + :name => r.platform.name, + :arch => 'x86_64' + }, :repository => {:id => r.id}, - :type => :resign, :skip_feedback => true, - :time_living => 9600 # 160 min + :time_living => 9600, # 160 min + :extra => {:repository_status_id => repository_status.id, :resign => true} }] - ) + ) if repository_status.start_resign end end - def create_tasks_for_build_rpms + def create_tasks_for_build_rpms(testing = false) available_repos = BuildList. select('MIN(updated_at) as min_updated_at, save_to_repository_id, build_for_platform_id'). - where(:new_core => true, :status => BuildList::BUILD_PUBLISH). + where(:new_core => true, :status => (testing ? BuildList::BUILD_PUBLISH_INTO_TESTING : BuildList::BUILD_PUBLISH)). group(:save_to_repository_id, :build_for_platform_id). order(:min_updated_at). limit(@workers_count * 2) # because some repos may be locked - locked_rep = locked_repositories + locked_rep = RepositoryStatus.not_ready.joins(:platform). + where(:platforms => {:platform_type => 'main'}).pluck(:repository_id) available_repos = available_repos.where('save_to_repository_id NOT IN (?)', locked_rep) unless locked_rep.empty? - counter = 1 - - # looks like: - # ['save_to_repository_id-build_for_platform_id', ...] - locked_rep_and_pl = @redis.lrange(LOCKED_REP_AND_PLATFORMS, 0, -1) - for_cleanup = @redis.lrange(PROJECTS_FOR_CLEANUP, 0, -1).map do |key| - pr, rep, pl = *key.split('-') - if locked_rep.present? && locked_rep.include?(rep) - nil - else - [rep.to_i, pl.to_i] - end + next if testing && key !~ /^testing-/ + rep, pl = *key.split('-').last(2) + locked_rep.present? && locked_rep.include?(rep.to_i) ? nil : [rep.to_i, pl.to_i] end.compact - available_repos = available_repos.map{ |bl| [bl.save_to_repository_id, bl.build_for_platform_id] } | for_cleanup + counter = 1 + available_repos = available_repos.map{ |bl| [bl.save_to_repository_id, bl.build_for_platform_id] } | for_cleanup available_repos.each do |save_to_repository_id, build_for_platform_id| - next if locked_rep_and_pl.include?("#{save_to_repository_id}-#{build_for_platform_id}") + next if RepositoryStatus.not_ready.where(:repository_id => save_to_repository_id, :platform_id => build_for_platform_id).exists? break if counter > @workers_count - counter += 1 if create_rpm_build_task(save_to_repository_id, build_for_platform_id) + counter += 1 if create_rpm_build_task(save_to_repository_id, build_for_platform_id, testing) end end - def create_rpm_build_task(save_to_repository_id, build_for_platform_id) - projects_for_cleanup = @redis.lrange(PROJECTS_FOR_CLEANUP, 0, -1). - select{ |k| k =~ /#{save_to_repository_id}\-#{build_for_platform_id}$/ } + def create_rpm_build_task(save_to_repository_id, build_for_platform_id, testing) + key = "#{save_to_repository_id}-#{build_for_platform_id}" + projects_for_cleanup = @redis.lrange(PROJECTS_FOR_CLEANUP, 0, -1).select do |k| + (testing && k =~ /^testing-[\d]+-#{key}$/) || (!testing && k =~ /^[\d]+-#{key}$/) + end # We should not to publish new builds into repository # if project of builds has been removed from repository. BuildList.where( - :project_id => projects_for_cleanup.map{ |k| k.split('-')[0] }.uniq, + :project_id => projects_for_cleanup.map{ |k| k.split('-')[testing ? 1 : 0] }.uniq, :save_to_repository_id => save_to_repository_id, - :status => BuildList::BUILD_PUBLISH + :status => [BuildList::BUILD_PUBLISH, BuildList::BUILD_PUBLISH_INTO_TESTING] ).update_all(:status => BuildList::FAILED_PUBLISH) build_lists = BuildList. - where(:new_core => true, :status => BuildList::BUILD_PUBLISH). + where(:new_core => true, :status => (testing ? BuildList::BUILD_PUBLISH_INTO_TESTING : BuildList::BUILD_PUBLISH)). where(:save_to_repository_id => save_to_repository_id). where(:build_for_platform_id => build_for_platform_id). order(:updated_at) locked_ids = @redis.lrange(LOCKED_BUILD_LISTS, 0, -1) build_lists = build_lists.where('build_lists.id NOT IN (?)', locked_ids) unless locked_ids.empty? - build_lists = build_lists.limit(50) + build_lists = build_lists.limit(150) - old_packages = {:sources => [], :binaries => {:x86_64 => [], :i586 => []}} + old_packages = self.class.packages_structure projects_for_cleanup.each do |key| @redis.lrem PROJECTS_FOR_CLEANUP, 0, key @@ -255,8 +252,8 @@ module AbfWorker next unless packages packages = JSON.parse packages old_packages[:sources] |= packages['sources'] - [:x86_64, :i586].each do |arch| - old_packages[:binaries][arch] |= packages['binaries'][arch.to_s] + Arch.pluck(:name).each do |arch| + old_packages[:binaries][arch.to_sym] |= packages['binaries'][arch] end end @@ -264,6 +261,12 @@ module AbfWorker return false if !bl && old_packages[:sources].empty? save_to_repository = Repository.find save_to_repository_id + # Checks mirror sync status + return false if save_to_repository.repo_lock_file_exists? || !save_to_repository.platform.ready? + + repository_status = save_to_repository.repository_statuses.find_or_create_by_platform_id(build_for_platform_id) + return false unless repository_status.publish + save_to_platform = save_to_repository.platform build_for_platform = Platform.find build_for_platform_id platform_path = "#{save_to_platform.path}/repository" @@ -276,35 +279,43 @@ module AbfWorker distrib_type = build_for_platform.distrib_type cmd_params = { - 'RELEASED' => save_to_platform.released, - 'REPOSITORY_NAME' => save_to_repository.name, - 'TYPE' => distrib_type + 'RELEASED' => save_to_platform.released, + 'REPOSITORY_NAME' => save_to_repository.name, + 'TYPE' => distrib_type, + 'SAVE_TO_PLATFORM' => save_to_platform.name, + 'BUILD_FOR_PLATFORM' => build_for_platform.name, + 'TESTING' => testing }.map{ |k, v| "#{k}=#{v}" }.join(' ') - lock_str = "#{save_to_repository_id}-#{build_for_platform_id}" options = { - :id => (bl ? bl.id : Time.now.to_i), - :arch => (bl ? bl.arch.name : 'x86_64'), - :distrib_type => distrib_type, - :cmd_params => cmd_params, - :platform => {:platform_path => platform_path}, + :id => (bl ? bl.id : Time.now.to_i), + :cmd_params => cmd_params, + :main_script => 'build.sh', + :rollback_script => 'rollback.sh', + :platform => { + :platform_path => platform_path, + :type => distrib_type, + :name => build_for_platform.name, + :arch => (bl ? bl.arch.name : 'x86_64') + }, :repository => {:id => save_to_repository_id}, - :type => :publish, :time_living => 9600, # 160 min - :extra => {:lock_str => lock_str} + :extra => {:repository_status_id => repository_status.id} } - packages = {:sources => [], :binaries => {:x86_64 => [], :i586 => []}} - build_list_ids = [] - - new_sources = {} + packages, build_list_ids, new_sources = self.class.packages_structure, [], {} build_lists.each do |bl| # remove duplicates of sources for different arches bl.packages.by_package_type('source').each{ |s| new_sources["#{s.fullname}"] = s.sha1 } self.class.fill_packages(bl, packages) - bl.last_published.includes(:packages).limit(2).each{ |old_bl| + bl.last_published(testing).includes(:packages).limit(2).each{ |old_bl| self.class.fill_packages(old_bl, old_packages, :fullname) } + # TODO: do more flexible + # Removes old packages which already in the main repo + bl.last_published(false).includes(:packages).limit(3).each{ |old_bl| + self.class.fill_packages(old_bl, old_packages, :fullname) + } if testing build_list_ids << bl.id @redis.lpush(LOCKED_BUILD_LISTS, bl.id) end @@ -314,9 +325,9 @@ module AbfWorker worker_queue, 'class' => worker_class, 'args' => [options.merge({ - :packages => packages, - :old_packages => old_packages, - :build_list_ids => build_list_ids, + :packages => packages, + :old_packages => old_packages, + :build_list_ids => build_list_ids, :projects_for_cleanup => projects_for_cleanup })] ) @@ -325,53 +336,92 @@ module AbfWorker @redis.lpush LOCKED_PROJECTS_FOR_CLEANUP, key end - @redis.lpush(LOCKED_REP_AND_PLATFORMS, lock_str) return true end + def create_tasks_for_regenerate_metadata_for_software_center + Platform.main.waiting_for_regeneration.each do |platform| + repos = platform.repositories + statuses = RepositoryStatus.where(:platform_id => platform.id) + next if repos.find{ |r| r.repo_lock_file_exists? } + next if statuses.present? && + statuses.map{ |s| s.ready? || s.can_start_regeneration? || s.can_start_resign? }.uniq != [true] + + cmd_params = { + 'RELEASED' => platform.released, + 'REPOSITORY_NAMES' => platform.repositories.map(&:name).join(','), + 'TYPE' => platform.distrib_type, + 'REGENERATE_PLATFORM_METADATA' => true, + 'SAVE_TO_PLATFORM' => platform.name, + 'BUILD_FOR_PLATFORM' => platform.name + }.map{ |k, v| "#{k}=#{v}" }.join(' ') + + Resque.push( + 'publish_worker_default', + 'class' => 'AbfWorker::PublishWorkerDefault', + 'args' => [{ + :id => Time.now.to_i, + :cmd_params => cmd_params, + :main_script => 'regenerate_platform_metadata.sh', + :platform => { + :platform_path => "#{platform.path}/repository", + :type => platform.distrib_type, + :name => platform.name, + :arch => 'x86_64' + }, + :time_living => 9600, # 160 min + :extra => {:platform_id => platform.id, :regenerate_platform => true} + }] + ) if platform.start_regeneration + + end + end + def create_tasks_for_repository_regenerate_metadata - worker_queue = 'publish_worker_default' - worker_class = 'AbfWorker::PublishWorkerDefault' - regen_repos = @redis.lrange REGENERATE_METADATA, 0, -1 - locked_rep_and_pl = @redis.lrange(LOCKED_REP_AND_PLATFORMS, 0, -1) + RepositoryStatus.platform_ready + .for_regeneration + .includes(:repository => :platform) + .readonly(false) + .each do |repository_status| + rep = repository_status.repository + # Checks mirror sync status + next if rep.repo_lock_file_exists? - Repository.where(:id => regen_repos).each do |rep| - lock_str = "#{rep.id}-#{rep.platform_id}" - next if locked_rep_and_pl.include?("#{rep.id}-#{rep.platform_id}") - @redis.lrem REGENERATE_METADATA, 0, rep.id - - platform_path = "#{rep.platform.path}/repository" - distrib_type = rep.platform.distrib_type - cmd_params = { - 'RELEASED' => rep.platform.released, - 'REPOSITORY_NAME' => rep.name, - 'TYPE' => distrib_type, - 'REGENERATE_METADATA' => true + build_for_platform = repository_status.platform + cmd_params = { + 'RELEASED' => rep.platform.released, + 'REPOSITORY_NAME' => rep.name, + 'TYPE' => build_for_platform.distrib_type, + 'REGENERATE_METADATA' => true, + 'SAVE_TO_PLATFORM' => rep.platform.name, + 'BUILD_FOR_PLATFORM' => build_for_platform.name }.map{ |k, v| "#{k}=#{v}" }.join(' ') - options = { - :id => Time.now.to_i, - :arch => 'x86_64', - :distrib_type => distrib_type, - :cmd_params => cmd_params, - :platform => {:platform_path => platform_path}, - :repository => {:id => rep.id}, - :type => :publish, - :time_living => 9600, # 160 min - :skip_feedback => true, - :extra => {:lock_str => lock_str, :regenerate => true} - } + platform_path = "#{rep.platform.path}/repository" + if rep.platform.personal? + platform_path << '/' << build_for_platform.name + system "mkdir -p #{platform_path}" + end Resque.push( - worker_queue, - 'class' => worker_class, - 'args' => [options.merge({ - })] - ) - - @redis.lpush(LOCKED_REP_AND_PLATFORMS, lock_str) + 'publish_worker_default', + 'class' => 'AbfWorker::PublishWorkerDefault', + 'args' => [{ + :id => Time.now.to_i, + :cmd_params => cmd_params, + :main_script => 'build.sh', + :rollback_script => 'rollback.sh', + :platform => { + :platform_path => platform_path, + :type => build_for_platform.distrib_type, + :name => build_for_platform.name, + :arch => 'x86_64' + }, + :time_living => 9600, # 160 min + :extra => {:repository_status_id => repository_status.id, :regenerate => true} + }] + ) if repository_status.start_regeneration end - return true end end end diff --git a/lib/abf_worker/model_helper.rb b/lib/abf_worker/model_helper.rb index 9d8de6653..9aa000095 100644 --- a/lib/abf_worker/model_helper.rb +++ b/lib/abf_worker/model_helper.rb @@ -1,59 +1,79 @@ -module AbfWorker - module ModelHelper - # In model which contains this helper should be: - # - #abf_worker_args - # - #build_canceled - - def abf_worker_log - Resque.redis.get(service_queue) || I18n.t('layout.build_lists.log.not_available') - end - - def add_job_to_abf_worker_queue - Resque.push( - worker_queue_with_priority, - 'class' => worker_queue_class, - 'args' => [abf_worker_args] - ) - end - - def cancel_job - deleted = Resque::Job.destroy( - worker_queue_with_priority, - worker_queue_class, - abf_worker_args - ) - if deleted == 1 - build_canceled - else - send_stop_signal - end - true - end - - def worker_queue_with_priority(queue = nil) - queue ||= abf_worker_base_queue - queue << '_' << abf_worker_priority if abf_worker_priority.present? - queue - end - - def worker_queue_class(queue_class = nil) - queue_class ||= "AbfWorker::#{abf_worker_base_queue.classify}" - queue_class << abf_worker_priority.capitalize - end - - private - - def send_stop_signal - Resque.redis.setex( - "#{service_queue}::live-inspector", - 240, # Data will be removed from Redis after 240 sec. - 'USR1' # Immediately kill child but don't exit - ) - end - - def service_queue - "abfworker::#{abf_worker_base_queue.gsub(/\_/, '-')}-#{id}" - end +module AbfWorker::ModelHelper + # In model which contains this helper should be: + # - #abf_worker_args + # - #build_canceled + def self.included(base) + base.extend(ClassMethods) end + + module ClassMethods + def log_server + @log_server ||= Redis.new( + :host => APP_CONFIG['abf_worker']['log_server']['host'], + :port => APP_CONFIG['abf_worker']['log_server']['port'] + ) + end + end + + def abf_worker_log + self.class.log_server.get(service_queue) || I18n.t('layout.build_lists.log.not_available') + end + + def add_job_to_abf_worker_queue + Resque.push( + worker_queue_with_priority, + 'class' => worker_queue_class, + 'args' => [abf_worker_args] + ) + end + + def restart_job + redis = Resque.redis + redis.lpush "queue:#{worker_queue_with_priority}", + Resque.encode({'class' => worker_queue_class, 'args' => [abf_worker_args]}) + end + + def cancel_job + if destroy_from_resque_queue == 1 + build_canceled + else + send_stop_signal + end + true + end + + def destroy_from_resque_queue + Resque::Job.destroy( + worker_queue_with_priority, + worker_queue_class, + abf_worker_args + ) + end + + def worker_queue_with_priority(queue = nil) + queue ||= abf_worker_base_queue + queue << '_' << abf_worker_priority if abf_worker_priority.present? + queue + end + + def worker_queue_class(queue_class = nil) + queue_class ||= "AbfWorker::#{abf_worker_base_queue.classify}" + queue_class << abf_worker_priority.capitalize + end + + private + + def send_stop_signal + Resque.redis.setex( + "#{service_queue}::live-inspector", + 240, # Data will be removed from Redis after 240 sec. + 'USR1' # Immediately kill child but don't exit + ) + end + + def service_queue + "abfworker::#{abf_worker_base_queue.gsub(/\_/, '-')}-#{id}" + end + end \ No newline at end of file diff --git a/lib/abf_worker/publish_observer.rb b/lib/abf_worker/publish_observer.rb index a6166535a..7ad128c9a 100644 --- a/lib/abf_worker/publish_observer.rb +++ b/lib/abf_worker/publish_observer.rb @@ -9,12 +9,26 @@ module AbfWorker def perform return if status == STARTED # do nothing when publication started - if options['type'] == 'resign' - AbfWorker::BuildListsPublishTaskManager.unlock_repository options['id'] - else - if options['extra']['regenerate'] # Regenerate metadata - AbfWorker::BuildListsPublishTaskManager.unlock_rep_and_platform options['extra']['lock_str'] - elsif options['extra']['create_container'] # Container has been created + extra = options['extra'] + repository_status = RepositoryStatus.where(:id => extra['repository_status_id']).first + begin + + if extra['regenerate'] || extra['regenerate_platform'] + log_sha1 = (options['results'].try(:first) || {}).fetch('sha1', nil) + end + + if extra['regenerate'] # Regenerate metadata + repository_status.last_regenerated_at = Time.now.utc + repository_status.last_regenerated_status = status + repository_status.last_regenerated_log_sha1 = log_sha1 + elsif extra['regenerate_platform'] # Regenerate metadata for Software Center + if platform = Platform.where(:id => extra['platform_id']).first + platform.last_regenerated_at = Time.now.utc + platform.last_regenerated_status = status + platform.last_regenerated_log_sha1 = log_sha1 + platform.ready + end + elsif extra['create_container'] # Container has been created case status when COMPLETED subject.published_container @@ -22,9 +36,11 @@ module AbfWorker subject.fail_publish_container end update_results - else + elsif !extra['resign'] # Simple publish update_rpm_builds end + ensure + repository_status.ready if repository_status.present? end end @@ -36,10 +52,18 @@ module AbfWorker update_results build_list case status when COMPLETED - # 'update_column' - when project of build_list has been removed from repository - build_list.published || build_list.update_column(:status, BuildList::BUILD_PUBLISHED) + if build_list.build_publish? + # 'update_column' - when project of build_list has been removed from repository + build_list.published || build_list.update_column(:status, BuildList::BUILD_PUBLISHED) + elsif build_list.build_publish_into_testing? + build_list.published_into_testing || build_list.update_column(:status, BuildList::BUILD_PUBLISHED_INTO_TESTING) + end when FAILED, CANCELED - build_list.fail_publish || build_list.update_column(:status, BuildList::FAILED_PUBLISH) + if build_list.build_publish? + build_list.fail_publish || build_list.update_column(:status, BuildList::FAILED_PUBLISH) + elsif build_list.build_publish_into_testing? + build_list.fail_publish_into_testing || build_list.update_column(:status, BuildList::FAILED_PUBLISH_INTO_TESTING) + end end AbfWorker::BuildListsPublishTaskManager.unlock_build_list build_list end @@ -50,8 +74,6 @@ module AbfWorker when FAILED, CANCELED AbfWorker::BuildListsPublishTaskManager.cleanup_failed options['projects_for_cleanup'] end - - AbfWorker::BuildListsPublishTaskManager.unlock_rep_and_platform options['extra']['lock_str'] end def update_results(build_list = subject) diff --git a/lib/abf_worker/rpm_worker_observer.rb b/lib/abf_worker/rpm_worker_observer.rb index bc128adfc..3e9201f1c 100644 --- a/lib/abf_worker/rpm_worker_observer.rb +++ b/lib/abf_worker/rpm_worker_observer.rb @@ -10,6 +10,10 @@ module AbfWorker def perform return if restart_task + if options['feedback_from_user'] + user = User.find options['feedback_from_user'] + return if !user.system? && subject.builder != user + end item = find_or_create_item fill_container_data if status != STARTED @@ -17,7 +21,7 @@ module AbfWorker case status when COMPLETED subject.build_success - subject.now_publish if subject.auto_publish? && subject.can_publish? + subject.now_publish if subject.can_auto_publish? when FAILED subject.build_error item.update_attributes({:status => BuildList::BUILD_ERROR}) @@ -55,7 +59,7 @@ module AbfWorker else redis.lpush RESTARTED_BUILD_LISTS, subject.id subject.update_column(:status, BuildList::BUILD_PENDING) - subject.add_job_to_abf_worker_queue + subject.restart_job if subject.external_nodes.blank? return true end end diff --git a/lib/abf_worker/status_inspector.rb b/lib/abf_worker/status_inspector.rb index 6aa722c01..54e10fcad 100644 --- a/lib/abf_worker/status_inspector.rb +++ b/lib/abf_worker/status_inspector.rb @@ -3,9 +3,17 @@ module AbfWorker class << self def projects_status - get_status(:rpm, :publish) { |w, worker| - w.to_s =~ /#{worker}_worker_default/ - } + Rails.cache.fetch([AbfWorker::StatusInspector, :projects_status], :expires_in => 10.seconds) do + result = get_status(:rpm, :publish) { |w, worker| w.to_s =~ /#{worker}_worker_default/ } + nodes = RpmBuildNode.total_statistics + result[:rpm][:workers] += nodes[:systems] + result[:rpm][:build_tasks] += nodes[:busy] + result[:rpm][:other_workers] = nodes[:others] + external_bls = BuildList.for_status(BuildList::BUILD_PENDING).external_nodes(:everything).count + result[:rpm][:default_tasks] += external_bls + result[:rpm][:tasks] += external_bls + result + end end def products_status @@ -28,10 +36,13 @@ module AbfWorker def status_of_worker(workers, worker) redis, key = Resque.redis, "queue:#{worker}_worker" + default_tasks, tasks = redis.llen("#{key}_default"), redis.llen(key) { - :count => workers.count, - :build_tasks => workers.select{ |w| w.working? }.count, - :tasks => (redis.llen("#{key}_default") + redis.llen(key)) + :workers => workers.count, + :build_tasks => workers.select{ |w| w.working? }.count, + :default_tasks => default_tasks, + :low_tasks => tasks, + :tasks => (default_tasks + tasks) } end diff --git a/lib/api_defender.rb b/lib/api_defender.rb index dd3489a7b..de24917d2 100644 --- a/lib/api_defender.rb +++ b/lib/api_defender.rb @@ -51,7 +51,9 @@ class ApiDefender < Rack::Throttle::Hourly # only API calls should be throttled def need_defense?(request) - request.env['PATH_INFO'] =~ /^\/api\/v1\// && !system_user?(request) + APP_CONFIG['allowed_addresses'].exclude?(request.ip) && + request.env['PATH_INFO'] =~ /^\/api\/v1\// && + !system_user?(request) end def authorized?(request) diff --git a/lib/ext/angularjs_link_renderer.rb b/lib/ext/angularjs_link_renderer.rb new file mode 100644 index 000000000..d7b508800 --- /dev/null +++ b/lib/ext/angularjs_link_renderer.rb @@ -0,0 +1,43 @@ +class Array + def html_safe + self + end +end + + +class AngularjsLinkRenderer < WillPaginate::ActionView::LinkRenderer + + def to_html + pagination.map do |item| + item.is_a?(Fixnum) ? page_number(item) : send(item) + end + end + + protected + + def page_number(page) + unless page == current_page + {:active => true, :number => page, :type => :page} + else + {:active => false, :number => page, :type => :first} + end + end + + def gap + nil + end + + def next_page + num = @collection.current_page < @collection.total_pages && @collection.current_page + 1 + previous_or_next_page(num, @options[:next_label], :next_page) + end + + def previous_or_next_page(page, text, classname) + if page + {:active => true, :number => page, :type => classname} + else + {:active => false, :number => page, :type => classname} + end + end + +end diff --git a/lib/ext/core/string.rb b/lib/ext/core/string.rb index 3266b0557..c7c887c28 100644 --- a/lib/ext/core/string.rb +++ b/lib/ext/core/string.rb @@ -23,15 +23,13 @@ class String # same as reverse.truncate.reverse def rtruncate(length, options = {}) - text = self.dup + chars = self.dup.mb_chars + return self if chars.length <= length options[:omission] ||= "..." options[:separator] ||= '/' - length_with_room_for_omission = length - options[:omission].mb_chars.length - chars = text.mb_chars - stop = options[:separator] ? - (chars.index(options[:separator].mb_chars, length_with_room_for_omission) || length_with_room_for_omission) : length_with_room_for_omission - - (chars.length > length ? "#{options[:omission]}#{chars[-(stop+1)...-1]}" : text).to_s + start = chars.length - length + options[:omission].mb_chars.length + stop = options[:separator] ? (chars.index(options[:separator].mb_chars, start) || start) : start + "#{options[:omission]}#{chars[stop..-1]}".to_s end end diff --git a/lib/ext/git/grit.rb b/lib/ext/git/grit.rb index c62bacf81..6df6c1e03 100644 --- a/lib/ext/git/grit.rb +++ b/lib/ext/git/grit.rb @@ -8,6 +8,12 @@ module Grit end end + class Submodule + def binary? + false + end + end + class Blob include Linguist::BlobHelper diff --git a/lib/ext/preregistration.rb b/lib/ext/preregistration.rb index aeb3fdb6c..661984341 100644 --- a/lib/ext/preregistration.rb +++ b/lib/ext/preregistration.rb @@ -50,10 +50,10 @@ module Preregistration end end - end #RegistrationsController - end #Devise -end #Preregistration + end # RegistrationsController + end # Devise +end # Preregistration Rails.application.config.to_prepare do - ::Devise::RegistrationsController.send :include, Preregistration::Devise::RegistrationsController + ::Devise::RegistrationsController.send :include, Preregistration::Devise::RegistrationsController if APP_CONFIG['preregistration'] end diff --git a/lib/ext/rails/reserved_name_validator.rb b/lib/ext/rails/reserved_name_validator.rb index 7d578e6c4..cfc1a1a5e 100644 --- a/lib/ext/rails/reserved_name_validator.rb +++ b/lib/ext/rails/reserved_name_validator.rb @@ -3,8 +3,8 @@ class ReservedNameValidator < ActiveModel::EachValidator about account add admin administrator api autocomplete_group_uname app apps archive archives auth blog - config connect contact create commit commits - dashboard delete direct_messages downloads + config connect contact create commit commits + dashboard delete direct_messages download downloads edit email faq favorites feed feeds follow followers following help home @@ -12,18 +12,17 @@ class ReservedNameValidator < ActiveModel::EachValidator jobs login log-in log_in logout log-out log_out logs map maps - new + new none oauth oauth_clients openid privacy register remove replies rss root save search sessions settings signup sign-up sign_up signin sign-in sign_in signout sign-out sign_out sitemap ssl subscribe - teams terms test trends tree - unfollow unsubscribe url user + teams terms test tour trends tree + unfollow unsubscribe upload uploads url user widget widgets wiki xfn xmpp - tour } def reserved_names diff --git a/lib/modules/models/file_store_clean.rb b/lib/modules/models/file_store_clean.rb index 003eaedf8..86412c92e 100644 --- a/lib/modules/models/file_store_clean.rb +++ b/lib/modules/models/file_store_clean.rb @@ -29,6 +29,11 @@ module Modules end end end + + def later_destroy_files_from_file_store(args) + destroy_files_from_file_store(args) + end + later :later_destroy_files_from_file_store, :queue => :clone_build end def self.file_exist_on_file_store?(sha1) diff --git a/lib/modules/models/git.rb b/lib/modules/models/git.rb index b76ba2af6..a756519ca 100644 --- a/lib/modules/models/git.rb +++ b/lib/modules/models/git.rb @@ -1,3 +1,7 @@ +require 'nokogiri' +require 'open-uri' +require 'iconv' + module Modules module Models module Git @@ -32,6 +36,26 @@ module Modules repo.tags.map(&:name) + repo.branches.map(&:name) end + def create_branch(new_ref, from_ref, user) + return false if new_ref.blank? || from_ref.blank? || !(from_commit = repo.commit(from_ref)) + status, out, err = repo.git.native(:branch, {:process_info => true}, new_ref, from_commit.id) + if status == 0 + Resque.enqueue(GitHook, owner.uname, name, from_commit.id, GitHook::ZERO, "refs/heads/#{new_ref}", 'commit', "user-#{user.id}", nil) + return true + end + return false + + end + + def delete_branch(branch, user) + return false if default_branch == branch.name + message = repo.git.native(:branch, {}, '-D', branch.name) + if message.present? + Resque.enqueue(GitHook, owner.uname, name, GitHook::ZERO, branch.commit.id, "refs/heads/#{branch.name}", 'commit', "user-#{user.id}", message) + end + return message.present? + end + def update_file(path, data, options = {}) head = options[:head].to_s || default_branch actor = get_actor(options[:actor]) @@ -69,24 +93,19 @@ module Modules end def tree_info(tree, treeish = nil, path = nil) - treeish ||= tree.id - # initialize result as hash of => nil - res = (tree.trees.sort + tree.blobs.sort).inject({}){|h, e| h.merge!({e => nil})} - # fills result vith commits that describes this file - res = res.inject(res) do |h, (entry, commit)| - if commit.nil? and entry.respond_to?(:name) # only if commit == nil - # ... find last commit corresponds to this file ... - c = repo.log(treeish, File.join([path, entry.name].compact), :max_count => 1).first - # ... and add it to result. - h[entry] = c - # find another files, that linked to this commit and set them their commit - # c.diffs.map{|diff| diff.b_path.split(File::SEPARATOR, 2).first}.each do |name| - # h.each_pair do |k, v| - # h[k] = c if k.name == name and v.nil? - # end - # end - end - h + return [] unless tree + grouped = tree.contents.sort_by{|c| c.name.downcase}.group_by(&:class) + [ + grouped[Grit::Tree], + grouped[Grit::Blob], + grouped[Grit::Submodule] + ].compact.flatten.map do |node| + node_path = File.join([path.present? ? path : nil, node.name].compact) + [ + node, + node_path, + repo.log(treeish, node_path, :max_count => 1).first + ] end end @@ -100,6 +119,11 @@ module Modules repo.branches.count == 0 end + def total_commits_count + return 0 if is_empty? + %x(cd #{path} && git rev-list --all | wc -l).to_i + end + protected def build_path(dir) @@ -153,10 +177,70 @@ module Modules end module ClassMethods + MAX_SRC_SIZE = 1024*1024*256 + def process_hook(owner_uname, repo, newrev, oldrev, ref, newrev_type, user = nil, message = nil) rec = GitHook.new(owner_uname, repo, newrev, oldrev, ref, newrev_type, user, message) - ActivityFeedObserver.instance.after_create rec + Modules::Observers::ActivityFeed::Git.create_notifications rec end + + def run_mass_import(url, srpms_list, visibility, owner, add_to_repository_id) + doc = Nokogiri::HTML(open(url)) + links = doc.css("a[href$='.src.rpm']") + return if links.count == 0 + filter = srpms_list.lines.map(&:chomp).map(&:strip).select(&:present?) + + repository = Repository.find add_to_repository_id + platform = repository.platform + dir = Dir.mktmpdir 'mass-import-', APP_CONFIG['tmpfs_path'] + links.each do |link| + begin + package = link.attributes['href'].value + package.chomp!; package.strip! + + next if package.size == 0 || package !~ /^[\w\.\-]+$/ + next if filter.present? && !filter.include?(package) + + uri = URI "#{url}/#{package}" + srpm_file = "#{dir}/#{package}" + Net::HTTP.start(uri.host) do |http| + if http.request_head(uri.path)['content-length'].to_i < MAX_SRC_SIZE + f = open(srpm_file, 'wb') + http.request_get(uri.path) do |resp| + resp.read_body{ |segment| f.write(segment) } + end + f.close + end + end + if name = `rpm -q --qf '[%{Name}]' -p #{srpm_file}` and $?.success? and name.present? + next if owner.projects.exists?(:name => name) + description = ::Iconv.conv('UTF-8//IGNORE', 'UTF-8', `rpm -q --qf '[%{Description}]' -p #{srpm_file}`) + project = owner.projects.build( + :name => name, + :description => description, + :visibility => visibility, + :is_package => false # See: Hook for #attach_to_personal_repository + ) + project.owner = owner + if project.save + repository.projects << project rescue nil + project.update_attributes(:is_package => true) + project.import_srpm srpm_file, platform.name + end + end + rescue => e + f.close if defined?(f) + Airbrake.notify_or_ignore(e, :link => link.to_s, :url => url, :owner => owner) + ensure + File.delete srpm_file if srpm_file + end + end + rescue => e + Airbrake.notify_or_ignore(e, :url => url, :owner => owner) + ensure + FileUtils.remove_entry_secure dir if dir + end + end end end diff --git a/lib/modules/models/markdown.rb b/lib/modules/models/markdown.rb new file mode 100644 index 000000000..6edc9f182 --- /dev/null +++ b/lib/modules/models/markdown.rb @@ -0,0 +1,193 @@ +# This module is based on +# https://github.com/gitlabhq/gitlabhq/blob/397c3da9758c03a215a308c011f94261d9c61cfa/lib/gitlab/markdown.rb +module Modules + module Models + # Custom parser for GitLab-flavored Markdown + # + # It replaces references in the text with links to the appropriate items in + # GitLab. + # + # Supported reference formats are: + # * @foo for team members + # * for issues & pull requests: + # * #123 + # * abf#123 + # * abf/rosa-build#123 + # * 123456 for commits + # + # It also parses Emoji codes to insert images. See + # http://www.emoji-cheat-sheet.com/ for a list of the supported icons. + # + # Examples + # + # >> gfm("Hey @david, can you fix this?") + # => "Hey @david, can you fix this?" + # + # >> gfm("Commit 35d5f7c closes #1234") + # => "Commit 35d5f7c closes #1234" + # + # >> gfm(":trollface:") + # => "\":trollface:\" + module Markdown + include IssuesHelper + + attr_reader :html_options + + # Public: Parse the provided text with GitLab-Flavored Markdown + # + # text - the source text + # html_options - extra options for the reference links as given to link_to + # + # Note: reference links will only be generated if @project is set + def gfm(text, html_options = {}) + return text if text.nil? + + # Duplicate the string so we don't alter the original, then call to_str + # to cast it back to a String instead of a SafeBuffer. This is required + # for gsub calls to work as we need them to. + text = text.dup.to_str + + @html_options = html_options + + # Extract pre blocks so they are not altered + # from http://github.github.com/github-flavored-markdown/ + text.gsub!(%r{
.*?
|.*?}m) { |match| extract_piece(match) } + # Extract links with probably parsable hrefs + text.gsub!(%r{.*?}m) { |match| extract_piece(match) } + # Extract images with probably parsable src + text.gsub!(%r{}m) { |match| extract_piece(match) } + + # TODO: add popups with additional information + + text = parse(text) + + # Insert pre block extractions + text.gsub!(/\{gfm-extraction-(\h{32})\}/) do + insert_piece($1) + end + + sanitize text.html_safe, attributes: ActionView::Base.sanitized_allowed_attributes + %w(id class) + end + + private + + def extract_piece(text) + @extractions ||= {} + + md5 = Digest::MD5.hexdigest(text) + @extractions[md5] = text + "{gfm-extraction-#{md5}}" + end + + def insert_piece(id) + @extractions[id] + end + + # Private: Parses text for references and emoji + # + # text - Text to parse + # + # Note: reference links will only be generated if @project is set + # + # Returns parsed text + def parse(text) + parse_references(text) if @project + parse_emoji(text) + + text + end + + REFERENCE_PATTERN = %r{ + (?[\W\/])? # Prefix + ( # Reference + @(?[a-zA-Z][a-zA-Z0-9_\-\.]*) # User/Group uname + |(?(?:[a-zA-Z0-9\-_]*\/)?(?:[a-zA-Z0-9\-_]*)?\#[0-9]+) # Issue ID + |(?[\h]{6,40}) # Commit ID + ) + (?\W)? # Suffix + }x.freeze + + TYPES = [:user, :issue, :commit].freeze + + def parse_references(text) + # parse reference links + text.gsub!(REFERENCE_PATTERN) do |match| + prefix = $~[:prefix] + suffix = $~[:suffix] + type = TYPES.select{|t| !$~[t].nil?}.first + identifier = $~[type] + + # Avoid HTML entities + if prefix && suffix && prefix[0] == '&' && suffix[-1] == ';' + match + elsif ref_link = reference_link(type, identifier) + "#{prefix}#{ref_link}#{suffix}" + else + match + end + end + end + + EMOJI_PATTERN = %r{(:(\S+):)}.freeze + + def parse_emoji(text) + # parse emoji + text.gsub!(EMOJI_PATTERN) do |match| + if valid_emoji?($2) + image_tag(image_path("emoji/#{$2}.png"), class: 'emoji', title: $1, alt: $1, size: "20x20") + else + match + end + end + end + + # Private: Checks if an emoji icon exists in the image asset directory + # + # emoji - Identifier of the emoji as a string (e.g., "+1", "heart") + # + # Returns boolean + def valid_emoji?(emoji) + Emoji.names.include? emoji + end + + # Private: Dispatches to a dedicated processing method based on reference + # + # reference - Object reference ("@1234", "!567", etc.) + # identifier - Object identifier (Issue ID, SHA hash, etc.) + # + # Returns string rendered by the processing method + def reference_link(type, identifier) + send("reference_#{type}", identifier) + end + + def reference_user(identifier) + member = User.where(uname: identifier).first || Group.where(uname: identifier).first + if member + link_to("@#{identifier}", "/#{identifier}", html_options.merge(title: member.fullname, class: "gfm gfm-member #{html_options[:class]}")) + end + end + + def reference_issue(identifier) + if issue = Issue.find_by_hash_tag(identifier, current_ability, @project) + if issue.pull_request + title = "#{PullRequest.model_name.human}: #{issue.title}" + url = project_pull_request_path(issue.project, issue.pull_request) + else + title = "#{Issue.model_name.human}: #{issue.title}" + url = project_issue_path(issue.project.owner, issue.project.name, issue.serial_id) + end + link_to(identifier, url, html_options.merge(title: title, class: "gfm gfm-issue #{html_options[:class]}")) + end + end + + def reference_commit(identifier) + if commit = @project.repo.commit(identifier) + link_to shortest_hash_id(commit.id), commit_path(@project, commit.id) + title = GitPresenters::CommitAsMessagePresenter.present(commit, :project => @project) do |presenter| + link_to(identifier, commit_path(@project, commit), html_options.merge(title: presenter.caption, class: "gfm gfm-commit #{html_options[:class]}")) + end + end + end + end + end +end diff --git a/lib/modules/models/owner.rb b/lib/modules/models/owner.rb index 7d89715d4..fc451c4ff 100644 --- a/lib/modules/models/owner.rb +++ b/lib/modules/models/owner.rb @@ -4,12 +4,10 @@ module Modules extend ActiveSupport::Concern included do + validates :owner, :presence => true after_create lambda { relations.create :actor_id => owner.id, :actor_type => owner.class.to_s, :role => 'admin' } end - def name_with_owner - "#{owner.respond_to?(:uname) ? owner.uname : owner.name}/#{self.name}" - end end end end diff --git a/lib/modules/models/regeneration_status.rb b/lib/modules/models/regeneration_status.rb new file mode 100644 index 000000000..af22bc9d0 --- /dev/null +++ b/lib/modules/models/regeneration_status.rb @@ -0,0 +1,49 @@ +# -*- encoding : utf-8 -*- +module Modules + module Models + module RegenerationStatus + extend ActiveSupport::Concern + + READY = 0 + WAITING_FOR_REGENERATION = 100 + REGENERATING = 200 + + HUMAN_STATUSES = { + READY => :ready, + WAITING_FOR_REGENERATION => :waiting_for_regeneration, + REGENERATING => :regenerating + } + + HUMAN_REGENERATION_STATUSES = { + AbfWorker::BaseObserver::COMPLETED => :completed, + AbfWorker::BaseObserver::FAILED => :failed, + AbfWorker::BaseObserver::CANCELED => :canceled + }.freeze + + included do + after_update :cleanup_file_store + + def sha1_of_file_store_files + files = [] + files << last_regenerated_log_sha1 if last_regenerated_log_sha1.present? + files + end + + def human_regeneration_status + self.class::HUMAN_REGENERATION_STATUSES[last_regenerated_status] || :no_data + end + + def human_status + self.class::HUMAN_STATUSES[status] || :no_data + end + + def cleanup_file_store + old_log_sha1 = last_regenerated_log_sha1_was + if old_log_sha1.present? && old_log_sha1 != last_regenerated_log_sha1 + later_destroy_files_from_file_store([old_log_sha1]) + end + end + end + end + end +end \ No newline at end of file diff --git a/lib/modules/models/time_living.rb b/lib/modules/models/time_living.rb index 7957682a3..0dd79d45c 100644 --- a/lib/modules/models/time_living.rb +++ b/lib/modules/models/time_living.rb @@ -10,9 +10,16 @@ module Modules }, :presence => true validate lambda { + # MIN_TIME_LIVING <= time_living <= MAX_TIME_LIVING or # 2 min <= time_living <= 12 hours - if 120 > time_living.to_i || time_living.to_i > 43200 - errors.add(:time_living, I18n.t('flash.time_living.numericality_error')) + # time_living in seconds + min = self.class.const_defined?(:MIN_TIME_LIVING) ? self.class::MIN_TIME_LIVING : 120 + max = self.class.const_defined?(:MAX_TIME_LIVING) ? self.class::MAX_TIME_LIVING : 43200 + if min > time_living.to_i || time_living.to_i > max + errors.add :time_living, I18n.t('flash.time_living.numericality_error', + :min => (min / 60), + :max => (max / 60) + ) end } diff --git a/lib/modules/models/url_helper.rb b/lib/modules/models/url_helper.rb new file mode 100644 index 000000000..76057b522 --- /dev/null +++ b/lib/modules/models/url_helper.rb @@ -0,0 +1,8 @@ +# -*- encoding : utf-8 -*- +module Modules::Models::UrlHelper + def default_url_options + host ||= EventLog.current_controller.request.host_with_port rescue ::Rosa::Application.config.action_mailer.default_url_options[:host] + protocol ||= APP_CONFIG['mailer_https_url'] ? 'https' : 'http' rescue 'http' + {:host => host, :protocol => protocol} + end +end diff --git a/lib/modules/models/web_hooks.rb b/lib/modules/models/web_hooks.rb new file mode 100644 index 000000000..2eaa450b7 --- /dev/null +++ b/lib/modules/models/web_hooks.rb @@ -0,0 +1,55 @@ +# -*- encoding : utf-8 -*- +module Modules::Models::WebHooks + + class << self + protected + + def add_hook(name) + NAMES << name.to_s + @schema = [] + yield if block_given? + SCHEMA[name] = @schema + @schema = [] + end + + def add_to_schema(type, attrs) + attrs.each do |attr| + @schema << [type, attr.to_sym] + end + end + + def boolean(*attrs) + add_to_schema :boolean, attrs + end + + def string(*attrs) + add_to_schema :string, attrs + end + + def password(*attrs) + add_to_schema :password, attrs + end + end + + NAMES = [] + SCHEMA = {} + add_hook :web do + string :url + end + # temporarily disabled + # add_hook :hipchat do + # string :auth_token, :room, :restrict_to_branch + # boolean :notify + # end + add_hook :irc do + string :server, :port, :room, :nick, :branch_regexes + password :password + boolean :ssl, :message_without_join, :no_colors, :long_url, :notice + end + add_hook :jabber do + string :user + end + SCHEMA.freeze + NAMES.freeze + +end diff --git a/lib/modules/observers/activity_feed/build_list.rb b/lib/modules/observers/activity_feed/build_list.rb new file mode 100644 index 000000000..5a4d764cb --- /dev/null +++ b/lib/modules/observers/activity_feed/build_list.rb @@ -0,0 +1,45 @@ +# -*- encoding : utf-8 -*- +module Modules::Observers::ActivityFeed::BuildList + extend ActiveSupport::Concern + + included do + after_update :build_list_notifications + end + + private + + def build_list_notifications + if mass_build.blank? && ( # Do not show mass build activity in activity feeds + status_changed? && [ + BuildList::BUILD_PENDING, + BuildList::BUILD_PUBLISHED, + BuildList::SUCCESS, + BuildList::BUILD_ERROR, + BuildList::FAILED_PUBLISH, + BuildList::TESTS_FAILED + ].include?(status) + ) + + updater = publisher || user + (project.admins | [publisher].compact).each do |recipient| + ActivityFeed.create( + :user => recipient, + :kind => 'build_list_notification', + :data => { + :build_list_id => id, + :status => status, + :updated_at => updated_at, + :project_id => project_id, + :project_name => project.name, + :project_owner => project.owner.uname, + :user_name => updater.name, + :user_email => updater.email, + :user_id => updater.id + } + ) + end + + end + end + +end \ No newline at end of file diff --git a/lib/modules/observers/activity_feed/comment.rb b/lib/modules/observers/activity_feed/comment.rb new file mode 100644 index 000000000..0327fcb12 --- /dev/null +++ b/lib/modules/observers/activity_feed/comment.rb @@ -0,0 +1,70 @@ +# -*- encoding : utf-8 -*- +module Modules::Observers::ActivityFeed::Comment + extend ActiveSupport::Concern + + included do + after_commit :new_comment_notifications, :on => :create + # dont remove outdated issues link + after_update -> { Comment.create_link_on_issues_from_item(self) } + end + + private + + def new_comment_notifications + return if automatic? + if issue_comment? + commentable.subscribes.each do |subscribe| + if user_id != subscribe.user_id + UserMailer.new_comment_notification(self, subscribe.user).deliver if can_notify_on_new_comment?(subscribe) + ActivityFeed.create( + :user => subscribe.user, + :kind => 'new_comment_notification', + :data => { + :user_name => user.name, + :user_email => user.email, + :user_id => user_id, + :comment_body => body, + :issue_title => commentable.title, + :issue_serial_id => commentable.serial_id, + :project_id => commentable.project.id, + :comment_id => id, + :project_name => project.name, + :project_owner => project.owner.uname + } + ) + end + end + elsif commit_comment? + Subscribe.comment_subscribes(self).where(:status => true).each do |subscribe| + next if own_comment?(subscribe.user) + if subscribe.user.notifier.can_notify and + ( (subscribe.project.owner?(subscribe.user) && subscribe.user.notifier.new_comment_commit_repo_owner) or + (subscribe.user.commentor?(self.commentable) && subscribe.user.notifier.new_comment_commit_commentor) or + (subscribe.user.committer?(self.commentable) && subscribe.user.notifier.new_comment_commit_owner) ) + UserMailer.new_comment_notification(self, subscribe.user).deliver + end + ActivityFeed.create( + :user => subscribe.user, + :kind => 'new_comment_commit_notification', + :data => { + :user_name => user.name, + :user_email => user.email, + :user_id => user_id, + :comment_body => body, + :commit_message => commentable.message, + :commit_id => commentable.id, + :project_id => project.id, + :comment_id => id, + :project_name => project.name, + :project_owner => project.owner.uname} + ) + end + end + Comment.create_link_on_issues_from_item(self) + end + + def can_notify_on_new_comment?(subscribe) + User.find(subscribe.user).notifier.new_comment && User.find(subscribe.user).notifier.can_notify + end + +end \ No newline at end of file diff --git a/lib/modules/observers/activity_feed/git.rb b/lib/modules/observers/activity_feed/git.rb new file mode 100644 index 000000000..c2fde9af9 --- /dev/null +++ b/lib/modules/observers/activity_feed/git.rb @@ -0,0 +1,68 @@ +# -*- encoding : utf-8 -*- +module Modules::Observers::ActivityFeed::Git + + def self.create_notifications(record) + + case record.class.to_s + when 'GitHook' + return unless record.project + PullRequest.where("from_project_id = ? OR to_project_id = ?", record.project, record.project).needed_checking.each {|pull| pull.check} + record.project.hooks.each{ |h| h.receive_push(record) } + + change_type = record.change_type + branch_name = record.refname.split('/').last + + if change_type == 'delete' + kind = 'git_delete_branch_notification' + options = {:project_id => record.project.id, :project_name => record.project.name, :branch_name => branch_name, + :change_type => change_type, :project_owner => record.project.owner.uname} + else + if record.message # online update + last_commits, commits = [[record.newrev, record.message]], [] + all_commits = last_commits + else + commits = record.project.repo.commits_between(record.oldrev, record.newrev) + all_commits = commits.collect { |commit| [commit.sha, commit.message] } + last_commits = all_commits.last(3).reverse + end + + kind = 'git_new_push_notification' + options = {:project_id => record.project.id, :project_name => record.project.name, :last_commits => last_commits, + :branch_name => branch_name, :change_type => change_type, :project_owner => record.project.owner.uname} + if commits.count > 3 + commits = commits[0...-3] + options.merge!({:other_commits_count => commits.count, :other_commits => "#{commits[0].sha[0..9]}...#{commits[-1].sha[0..9]}"}) + end + Comment.create_link_on_issues_from_item(record, all_commits) if all_commits.count > 0 + end + options.merge!({:user_id => record.user.id, :user_name => record.user.name, :user_email => record.user.email}) if record.user + + record.project.admins.each do |recipient| + next if record.user && record.user.id == recipient.id + ActivityFeed.create!( + :user => recipient, + :kind => kind, + :data => options + ) + if recipient.notifier.can_notify && recipient.notifier.update_code + UserMailer.send(kind, recipient, options).deliver + end + end + + when 'Hash' # 'Gollum::Committer' + actor = User.find_by_uname! record[:actor_name] + project = Project.find record[:project_id] + + project.admins.each do |recipient| + ActivityFeed.create!( + :user => recipient, + :kind => 'wiki_new_commit_notification', + :data => {:user_id => actor.id, :user_name => actor.name, :user_email => actor.email, :project_id => project.id, + :project_name => project.name, :commit_sha => record[:commit_sha], :project_owner => project.owner.uname} + ) + end + end + + end + +end \ No newline at end of file diff --git a/lib/modules/observers/activity_feed/issue.rb b/lib/modules/observers/activity_feed/issue.rb new file mode 100644 index 000000000..031a0f09d --- /dev/null +++ b/lib/modules/observers/activity_feed/issue.rb @@ -0,0 +1,67 @@ +# -*- encoding : utf-8 -*- +module Modules::Observers::ActivityFeed::Issue + extend ActiveSupport::Concern + + included do + after_commit :new_issue_notifications, :on => :create + + after_commit :send_assign_notifications, :on => :create, :if => Proc.new { |i| i.assignee } + after_commit -> { send_assign_notifications(:update) }, :on => :update + + after_commit :send_hooks, :on => :create + after_commit -> { send_hooks(:update) }, :on => :update, :if => Proc.new { |i| i.previous_changes['status'].present? } + end + + private + + def new_issue_notifications + collect_recipients.each do |recipient| + next if user_id == recipient.id + if recipient.notifier.can_notify && recipient.notifier.new_issue && assignee_id != recipient.id + UserMailer.new_issue_notification(self, recipient).deliver + end + ActivityFeed.create( + :user => recipient, + :kind => 'new_issue_notification', + :data => { + :user_name => user.name, + :user_email => user.email, + :user_id => user_id, + :issue_serial_id => serial_id, + :issue_title => title, + :project_id => project.id, + :project_name => project.name, + :project_owner => project.owner.uname + } + ) + end + Comment.create_link_on_issues_from_item(self) + end + + def send_assign_notifications(action = :create) + if(action == :create && assignee_id) || previous_changes['assignee_id'].present? + if assignee.notifier.issue_assign && assignee.notifier.can_notify + UserMailer.issue_assign_notification(self, assignee).deliver + end + ActivityFeed.create( + :user => assignee, + :kind => 'issue_assign_notification', + :data => { + :user_name => assignee.name, + :user_email => assignee.email, + :issue_serial_id => serial_id, + :issue_title => title, + :project_id => project.id, + :project_name => project.name, + :project_owner => project.owner.uname + } + ) + end + # dont remove outdated issues link + Comment.create_link_on_issues_from_item(self) if previous_changes['title'].present? || previous_changes['body'].present? + end + + def send_hooks(action = :create) + project.hooks.each{ |h| h.receive_issues(self, action) } + end +end diff --git a/lib/modules/observers/activity_feed/user.rb b/lib/modules/observers/activity_feed/user.rb new file mode 100644 index 000000000..971378b30 --- /dev/null +++ b/lib/modules/observers/activity_feed/user.rb @@ -0,0 +1,19 @@ +# -*- encoding : utf-8 -*- +module Modules::Observers::ActivityFeed::User + extend ActiveSupport::Concern + + included do + after_commit :new_user_notification, :on => :create + end + + private + + def new_user_notification + ActivityFeed.create( + :user => self, + :kind => 'new_user_notification', + :data => {:user_name => self.user_appeal, :user_email => self.email} + ) + end + +end \ No newline at end of file diff --git a/lib/plugins/rpm.rb b/lib/plugins/rpm.rb new file mode 100644 index 000000000..488ee23b3 --- /dev/null +++ b/lib/plugins/rpm.rb @@ -0,0 +1,19 @@ +require 'ffi' + +module RPM + module C + + extend ::FFI::Library + + begin + ffi_lib ['librpm.so.3', 'librpm.so.2', 'librpm.so.1', 'rpm'] + rescue LoadError => e + raise( + "Can't find rpm libs on your system: #{e.message}" + ) + end + + attach_function 'rpmvercmp', [:string, :string], :int + + end +end \ No newline at end of file diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb new file mode 100644 index 000000000..ec836dd0f --- /dev/null +++ b/lib/redcarpet/render/gitlab_html.rb @@ -0,0 +1,37 @@ +# This class is based on +# https://github.com/gitlabhq/gitlabhq/blob/2bc78739a7aa9d7e5109281fc45dbd41a1a576d4/lib/gitlab/markdown.rb +class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML + + attr_reader :template + alias_method :h, :template + + def initialize(template, options = {}) + @template = template + @project = @template.instance_variable_get("@project") + super options + end + + def block_code(code, language) + # New lines are placed to fix an rendering issue + # with code wrapped inside

tag for next case: + # + # # Title kinda h1 + # + # ruby code here + # + code_class = "class=\"#{language.downcase}\"" if language.present? + <<-HTML + +
#{code}
+ + HTML + end + + def link(link, title, content) + h.link_to_gfm(content, link, title: title) + end + + def postprocess(full_document) + h.gfm(full_document) + end +end diff --git a/lib/tasks/add_branch.rake b/lib/tasks/add_branch.rake index 47c0bf9ed..c8055d8c4 100644 --- a/lib/tasks/add_branch.rake +++ b/lib/tasks/add_branch.rake @@ -12,6 +12,21 @@ namespace :add_branch do FileUtils.rm_rf tmp_path end + desc "Add branch for group projects" + task :group => :environment do + src_branch = ENV['SRC_BRANCH'] + dst_branch = ENV['DST_BRANCH'] + group = ENV['GROUP'] + say "START add branch #{dst_branch} from #{src_branch} in #{group} group" + Group.find_by_uname(group).projects.find_each do |p| + next if p.repo.branches.map(&:name).include?(dst_branch) + next if p.repo.branches.map(&:name).exclude?(src_branch) + say "===== Process #{p.name} project" + Rake::Task['add_branch:fork_branch'].execute(:path => p.path, :src_branch => src_branch, :dst_branch => dst_branch) + end + say 'DONE' + end + desc "Add branch for platform projects" task :platform => :environment do src_branch = ENV['SRC_BRANCH'] || 'import_mandriva2011' diff --git a/lib/tasks/build.rake b/lib/tasks/build.rake deleted file mode 100644 index c0f414df6..000000000 --- a/lib/tasks/build.rake +++ /dev/null @@ -1,25 +0,0 @@ -require 'highline/import' -require 'open-uri' - -namespace :build do - desc "Build projects from list" - task :projects => :environment do - source = ENV['SOURCE'] || 'http://dl.dropbox.com/u/984976/rebuild_list.txt' - owner = User.find_by_uname!(ENV['OWNER'] || 'warpc') - platform = Platform.find_by_name!(ENV['PLATFORM'] || 'rosa2012lts') - arch = Arch.find_by_name!(ENV['ARCH'] || 'i586') - - say "START build projects from #{source} for platform=#{platform.name}, owner=#{owner.uname}, arch=#{arch.name}" - open(source).readlines.each do |name| - name.chomp!; name.strip! #; name.downcase! - if p = Project.joins(:repositories).where('repositories.id IN (?)', platform.repositories).find_by_name(name) - # Old code p.build_for(platform, owner, arch) - say "== Build #{p.name} ==" - else - say "== Not found #{name} ==" - end - sleep 0.2 - end - say 'DONE' - end -end diff --git a/lib/tasks/import.rake b/lib/tasks/import.rake index 309315e27..d63718df1 100644 --- a/lib/tasks/import.rake +++ b/lib/tasks/import.rake @@ -26,12 +26,13 @@ namespace :import do say 'DONE' end - # bundle exec rake import:srpm RAILS_ENV=production BASE=/share/platforms/naulinux5x_personal/tmp/SRPMS LIST=https://dl.dropbox.com/u/984976/nauschool5x.srpms.txt OWNER=naulinux PLATFORM=naulinux REPO=main CLEAR=true > log/srpm_naulinux.log & + # bundle exec rake import:srpm RAILS_ENV=production BASE=/share/platforms/naulinux5x_personal/tmp/SRPMS LIST=https://dl.dropbox.com/u/984976/nauschool5x.srpms.txt OWNER=naulinux PLATFORM=naulinux REPO=main CLEAR=true HIDDEN=true > log/srpm_naulinux.log & desc 'Import SRPMs as projects' task :srpm => :environment do base = ENV['BASE'] || '/share/alt_repos/rsync' list = ENV['LIST'] #|| 'https://dl.dropbox.com/u/984976/alt_import.txt' mask = ENV['MASK'] || '*.src.rpm' + hidden = ENV['HIDDEN'] == 'true' ? true : false owner = User.find_by_uname(ENV['OWNER']) || Group.find_by_uname!(ENV['OWNER'] || 'altlinux') platform = Platform.find_by_name!(ENV['PLATFORM'] || 'altlinux5') repo = platform.repositories.find_by_name!(ENV['REPO'] || 'main') @@ -44,14 +45,14 @@ namespace :import do if name = `rpm -q --qf '[%{Name}]' -p #{srpm_file}` and $?.success? and name.present? if clear # simply add project = Project.find_or_create_by_name_and_owner_type_and_owner_id(name, owner.class.to_s, owner.id) - repo.projects << project + repo.projects << project rescue nil else # check if project already added if project = repo.projects.find_by_name(name) || repo.projects.by_name(name).first # fallback to speedup print "Found project '#{project.name_with_owner}' in '#{platform.name}/#{repo.name}'." elsif scoped = Project.where(:owner_id => owner.id, :owner_type => owner.class) and project = scoped.find_by_name(name) || scoped.by_name(name).first begin - repo.projects << project + repo.projects << project rescue nil rescue Exception => e print "Add project '#{project.name_with_owner}' to '#{platform.name}/#{repo.name}' FAILED: #{e.message}." else @@ -60,10 +61,11 @@ namespace :import do else description = `rpm -q --qf '[%{Description}]' -p #{srpm_file}` project = Project.create!(:name => name, :description => description) {|p| p.owner = owner} - repo.projects << project + repo.projects << project rescue nil print "Create project #{project.name_with_owner} in #{platform.name}/#{repo.name} OK." end end + project.update_attributes(:visibility => 'hidden') if hidden project.import_srpm(srpm_file, platform.name) print " Code import complete!" else diff --git a/lib/tasks/new_core.rake b/lib/tasks/new_core.rake index bdaf4bab0..a38753d2c 100644 --- a/lib/tasks/new_core.rake +++ b/lib/tasks/new_core.rake @@ -1,58 +1,4 @@ namespace :new_core do - desc 'Sets bs_id field for all BuildList which use new_core' - task :update_bs_id => :environment do - say "[#{Time.zone.now}] Starting to update bs_id..." - - BuildList.select(:id). - where(:new_core => true, :bs_id => nil). - find_in_batches(:batch_size => 500) do | bls | - - puts "[#{Time.zone.now}] - where build_lists.id from #{bls.first.id} to #{bls.last.id}" - BuildList.where(:id => bls.map(&:id), :bs_id => nil). - update_all("bs_id = id") - end - - say "[#{Time.zone.now}] done" - end - - desc 'Publish mass-build 73' - task :publish_mass_build_73 => :environment do - say "[#{Time.zone.now}] Starting to publish mass-build 317..." - - bl = BuildList.where(:mass_build_id => 73).first - platform_repository_folder = "#{bl.save_to_platform.path}/repository" - BuildList.where(:mass_build_id => 73). - where(:status => [ - BuildList::SUCCESS, - BuildList::FAILED_PUBLISH - ]). - order(:id). - find_in_batches(:batch_size => 1) do | bls | - - bl = bls.first - puts "[#{Time.zone.now}] - where build_lists.id #{bl.id}" - - sha1 = bl.results.find{ |r| r['file_name'] =~ /.*\.tar\.gz$/ }['sha1'] - - system "cd #{platform_repository_folder} && curl -L -O #{APP_CONFIG['file_store_url']}/api/v1/file_stores/#{sha1}" - system "cd #{platform_repository_folder} && tar -xzf #{sha1}" - system "rm -f #{platform_repository_folder}/#{sha1}" - - archive_folder = "#{platform_repository_folder}/archives" - system "sudo chown root:root #{archive_folder}/SRC_RPM/*" - system "sudo chmod 0666 #{archive_folder}/SRC_RPM/*" - system "sudo chown root:root #{archive_folder}/RPM/*" - system "sudo chmod 0666 #{archive_folder}/RPM/*" - - system "sudo mv #{archive_folder}/SRC_RPM/* #{platform_repository_folder}/SRPMS/main/release/" - system "sudo mv #{archive_folder}/RPM/* #{platform_repository_folder}/#{bl.arch.name}/main/release/" - - system "sudo rm -rf #{archive_folder}" - bl.update_column(:status, BuildList::BUILD_PUBLISH) - end - - say "[#{Time.zone.now}] done" - end desc 'Extracts all rpms from BuildList container and updates BuildList::Package#sha1 field' task :update_packages => :environment do diff --git a/lib/tasks/projects.rake b/lib/tasks/projects.rake index bfe2d982f..973123b73 100644 --- a/lib/tasks/projects.rake +++ b/lib/tasks/projects.rake @@ -26,3 +26,20 @@ namespace :project do task :maintainer => 'maintainer:set_to_owner' end + +namespace :projects do + desc 'Add projects from one platform repository to another' + task :copy_to_repo => :environment do + source_platform = Platform.find_by_name!(ENV['SRC_PLATFORM']) + dest_platform = Platform.find_by_name!(ENV['DST_PLATFORM']) + source_repo = source_platform.repositories.find_by_name!(ENV['SRC_REPO']) + dest_repo = dest_platform.repositories.find_by_name!(ENV['DST_REPO']) + + say "Add from repo '#{source_platform.name}/#{source_repo.name}' to repo '#{dest_platform.name}/#{dest_repo.name}'." + source_repo.projects.each do |pr| + say "project #{pr.name}" + dest_repo.projects << pr rescue print ' Add to repo failed!' + end + say 'DONE' + end +end \ No newline at end of file diff --git a/lib/tasks/remove_branch.rake b/lib/tasks/remove_branch.rake new file mode 100644 index 000000000..89281d5a1 --- /dev/null +++ b/lib/tasks/remove_branch.rake @@ -0,0 +1,14 @@ +namespace :remove_branch do + desc "Remove branch for group projects" + task :group => :environment do + branch = ENV['BRANCH'] + group = ENV['GROUP'] + say "START remove branch #{branch} from #{group} group" + Group.find_by_uname(group).projects.find_each do |p| + next if p.repo.branches.map(&:name).exclude?(branch) + say "===== Process #{p.name} project" + p.repo.git.native(:branch, {}, '-D', branch) + end + say 'DONE' + end +end diff --git a/spec/controllers/activity_feeds_controller_spec.rb b/spec/controllers/activity_feeds_controller_spec.rb deleted file mode 100644 index 54ce3026a..000000000 --- a/spec/controllers/activity_feeds_controller_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'spec_helper' - -describe ActivityFeedsController do - -end diff --git a/spec/controllers/advisories_controller_spec.rb b/spec/controllers/advisories_controller_spec.rb new file mode 100644 index 000000000..f2006fd93 --- /dev/null +++ b/spec/controllers/advisories_controller_spec.rb @@ -0,0 +1,11 @@ +# -*- encoding : utf-8 -*- +require 'spec_helper' + +describe AdvisoriesController do + context 'for all' do + it "should be able to perform search action" do + get :search + response.should_not redirect_to(forbidden_path) + end + end +end \ No newline at end of file diff --git a/spec/controllers/api/v1/advisories_controller_spec.rb b/spec/controllers/api/v1/advisories_controller_spec.rb index e4f9048bb..dcc24b12e 100644 --- a/spec/controllers/api/v1/advisories_controller_spec.rb +++ b/spec/controllers/api/v1/advisories_controller_spec.rb @@ -86,7 +86,7 @@ describe Api::V1::AdvisoriesController do stub_symlink_methods @advisory = FactoryGirl.create(:advisory) - @build_list = FactoryGirl.create(:build_list_core) + @build_list = FactoryGirl.create(:build_list) @build_list.save_to_platform.update_column(:released, true) @build_list.save_to_repository.update_column(:publish_without_qa, false) @build_list.update_column(:status, BuildList::BUILD_PUBLISHED) diff --git a/spec/controllers/api/v1/build_lists_controller_spec.rb b/spec/controllers/api/v1/build_lists_controller_spec.rb index 42a65f1e4..47e0dca86 100644 --- a/spec/controllers/api/v1/build_lists_controller_spec.rb +++ b/spec/controllers/api/v1/build_lists_controller_spec.rb @@ -100,7 +100,7 @@ describe Api::V1::BuildListsController do Arch.destroy_all User.destroy_all - @build_list = FactoryGirl.create(:build_list_core) + @build_list = FactoryGirl.create(:build_list) @params = @build_list.attributes.symbolize_keys @project = @build_list.project @platform = @build_list.save_to_platform @@ -114,9 +114,8 @@ describe Api::V1::BuildListsController do @build_list.save_to_platform.relations.create(:role => 'admin', :actor => @owner_user) # Why it's really need it?? # Create and show params: - @create_params = {:build_list => @build_list.attributes.symbolize_keys.except(:bs_id) - .merge(:qwerty=>'!')} # wrong parameter - @create_params = @create_params.merge(:arches => [@params[:arch_id]], :build_for_platforms => [@params[:build_for_platform_id]], :format => :json) + @create_params = {:build_list => @build_list.attributes.symbolize_keys.merge(:qwerty=>'!')} # wrong parameter + @create_params = @create_params.merge(:arches => [@params[:arch_id]], :build_for_platform_id => @platform.id, :format => :json) any_instance_of(Project, :versions => ['v1.0', 'v2.0']) http_login(@user) @@ -151,14 +150,14 @@ describe Api::V1::BuildListsController do context "if it has another status" do before do - @build_list.update_column(:status, BuildList::PROJECT_VERSION_NOT_FOUND) + @build_list.update_column(:status, BuildList::SUCCESS) do_cancel end it_should_behave_like 'validation error via build list api', I18n.t('layout.build_lists.cancel_fail') it "should not change status of build list" do - @build_list.reload.status.should == BuildList::PROJECT_VERSION_NOT_FOUND + @build_list.reload.status.should == BuildList::SUCCESS end end end @@ -238,6 +237,107 @@ describe Api::V1::BuildListsController do end end + context 'do publish_into_testing' do + def do_publish_into_testing + put :publish_into_testing, :id => @build_list, :format => :json + end + + context 'if user is project && platform owner' do + before(:each) do + http_login(@owner_user) + end + + context "if it has :failed_publish status" do + before do + @build_list.update_column(:status, BuildList::FAILED_PUBLISH) + do_publish_into_testing + end + it "should return correct json message" do + response.body.should == { :build_list => {:id => @build_list.id, :message => I18n.t('layout.build_lists.publish_success')} }.to_json + end + + it 'should return 200 response code' do + response.should be_success + end + + it "should change status of build list" do + @build_list.reload.status.should == BuildList::BUILD_PUBLISH_INTO_TESTING + end + end + + context "if it has :build_published_into_testing status" do + before do + @build_list.update_column(:status, BuildList::BUILD_PUBLISHED_INTO_TESTING) + do_publish_into_testing + end + + it "should return correct json message" do + response.body.should == { :build_list => {:id => @build_list.id, :message => I18n.t('layout.build_lists.publish_success')} }.to_json + end + + it 'should return 200 response code' do + response.should be_success + end + + it "should change status of build list" do + @build_list.reload.status.should == BuildList::BUILD_PUBLISH_INTO_TESTING + end + end + + context "if it has another status" do + before(:each) do + @build_list.update_column(:status, BuildList::BUILD_CANCELED) + do_publish_into_testing + end + + it "should return access violation message" do + response.body.should == {"message" => "Access violation to this page!"}.to_json + end + + it "should not change status of build list" do + @build_list.reload.status.should == BuildList::BUILD_CANCELED + end + end + + end + + context 'if user is not project owner' do + + context "if it has :build_published_into_testing status" do + before do + @build_list.update_column(:status, BuildList::BUILD_PUBLISHED_INTO_TESTING) + do_publish_into_testing + end + + it 'should not be able to perform create action' do + response.body.should == {"message" => "Access violation to this page!"}.to_json + end + + it 'should return 403 response code' do + response.status.should == 403 + end + + it "should not change status of build list" do + @build_list.reload.status.should == BuildList::BUILD_PUBLISHED_INTO_TESTING + end + end + + context "if it has :failed_publish status" do + before do + @build_list.update_column(:status, BuildList::FAILED_PUBLISH_INTO_TESTING) + do_publish_into_testing + end + it "should return access violation message" do + response.body.should == {"message" => "Access violation to this page!"}.to_json + end + + it "should not change status of build list" do + @build_list.reload.status.should == BuildList::FAILED_PUBLISH_INTO_TESTING + end + end + end + end + context "do publish" do def do_publish put :publish, :id => @build_list, :format => :json @@ -287,7 +387,7 @@ describe Api::V1::BuildListsController do context "if it has another status" do before(:each) do - @build_list.update_column(:status, BuildList::PROJECT_VERSION_NOT_FOUND) + @build_list.update_column(:status, BuildList::BUILD_CANCELED) do_publish end @@ -296,7 +396,7 @@ describe Api::V1::BuildListsController do end it "should not change status of build list" do - @build_list.reload.status.should == BuildList::PROJECT_VERSION_NOT_FOUND + @build_list.reload.status.should == BuildList::BUILD_CANCELED end end @@ -387,14 +487,14 @@ describe Api::V1::BuildListsController do context "if it has another status" do before(:each) do - @build_list.update_column(:status, BuildList::PROJECT_VERSION_NOT_FOUND) + @build_list.update_column(:status, BuildList::BUILD_CANCELED) do_reject_publish end it_should_behave_like 'validation error via build list api', I18n.t('layout.build_lists.reject_publish_fail') it "should not change status of build list" do - @build_list.reload.status.should == BuildList::PROJECT_VERSION_NOT_FOUND + @build_list.reload.status.should == BuildList::BUILD_CANCELED end end end @@ -415,6 +515,49 @@ describe Api::V1::BuildListsController do @build_list.reload.status.should == BuildList::SUCCESS end end + + context 'if user is project reader' do + before(:each) do + @another_user = FactoryGirl.create(:user) + @build_list.update_column(:status, BuildList::SUCCESS) + @build_list.save_to_repository.update_column(:publish_without_qa, true) + @build_list.project.collaborators.create(:actor_type => 'User', :actor_id => @another_user.id, :role => 'reader') + http_login(@another_user) + do_reject_publish + end + + it "should return access violation message" do + response.body.should == {"message" => "Access violation to this page!"}.to_json + end + + it "should not change status of build list" do + do_reject_publish + @build_list.reload.status.should == BuildList::SUCCESS + end + end + + context 'if user is project writer' do + before(:each) do + @another_user = FactoryGirl.create(:user) + @build_list.update_column(:status, BuildList::SUCCESS) + @build_list.save_to_repository.update_column(:publish_without_qa, true) + @build_list.project.relations.create!(:actor_type => 'User', :actor_id => @another_user.id, :role => 'writer') + http_login(@another_user) + do_reject_publish + end + + it "should return correct json message" do + response.body.should == { :build_list => {:id => @build_list.id, :message => I18n.t('layout.build_lists.reject_publish_success')} }.to_json + end + + it 'should return 200 response code' do + response.should be_success + end + + it "should reject publish build list" do + @build_list.reload.status.should == BuildList::REJECTED_PUBLISH + end + end end context 'for open project' do @@ -423,10 +566,25 @@ describe Api::V1::BuildListsController do context 'if user is project owner' do before(:each) {http_login(@owner_user)} it_should_behave_like 'create build list via api' + + context 'no ability to read build_for_platform' do + before do + repository = FactoryGirl.create(:repository) + repository.platform.change_visibility + Platform.where(:id => @platform.id).update_all(:platform_type => 'personal') + @create_params[:build_list].merge!({ + :include_repos => [repository.id], + :build_for_platform_id => repository.platform_id + }) + end + it_should_behave_like 'not create build list via api' + end + end context 'if user is project read member' do before(:each) {http_login(@member_user)} + it_should_behave_like 'not create build list via api' end end @@ -455,7 +613,7 @@ describe Api::V1::BuildListsController do Arch.destroy_all User.destroy_all - @build_list = FactoryGirl.create(:build_list_core) + @build_list = FactoryGirl.create(:build_list) @params = @build_list.attributes.symbolize_keys @project = @build_list.project @platform = @build_list.save_to_platform @@ -466,8 +624,8 @@ describe Api::V1::BuildListsController do @member_user = FactoryGirl.create(:user) # Create and show params: - @create_params = {:build_list => @build_list.attributes.symbolize_keys.except(:bs_id)} - @create_params = @create_params.merge(:arches => [@params[:arch_id]], :build_for_platforms => [@params[:build_for_platform_id]], :format => :json) + @create_params = {:build_list => @build_list.attributes.symbolize_keys} + @create_params = @create_params.merge(:arches => [@params[:arch_id]], :build_for_platform_id => @platform.id, :format => :json) any_instance_of(Project, :versions => ['v1.0', 'v2.0']) # Groups: @@ -479,8 +637,11 @@ describe Api::V1::BuildListsController do @user = FactoryGirl.create(:user) @group.actors.create :role => 'reader', :actor_id => @user.id, :actor_type => 'User' + old_path = @project.path @project.owner = @owner_group @project.save + # Move GIT repo into new folder + system "mkdir -p #{@project.path} && mv -f #{old_path}/* #{@project.path}/" @project.relations.create :role => 'reader', :actor_id => @member_group.id, :actor_type => 'Group' @project.relations.create :role => 'admin', :actor_id => @owner_group.id, :actor_type => 'Group' @@ -533,22 +694,22 @@ describe Api::V1::BuildListsController do @user = FactoryGirl.create(:user) # Build Lists: - @build_list1 = FactoryGirl.create(:build_list_core) + @build_list1 = FactoryGirl.create(:build_list) - @build_list2 = FactoryGirl.create(:build_list_core) + @build_list2 = FactoryGirl.create(:build_list) @build_list2.project.update_column(:visibility, 'hidden') project = FactoryGirl.create(:project_with_commit, :visibility => 'hidden', :owner => @user) - @build_list3 = FactoryGirl.create(:build_list_core_with_attaching_project, :project => project) + @build_list3 = FactoryGirl.create(:build_list_with_attaching_project, :project => project) - @build_list4 = FactoryGirl.create(:build_list_core) + @build_list4 = FactoryGirl.create(:build_list) @build_list4.project.update_column(:visibility, 'hidden') @build_list4.project.relations.create! :role => 'reader', :actor_id => @user.id, :actor_type => 'User' - @filter_build_list1 = FactoryGirl.create(:build_list_core) - @filter_build_list2 = FactoryGirl.create(:build_list_core) - @filter_build_list3 = FactoryGirl.create(:build_list_core) - @filter_build_list4 = FactoryGirl.create(:build_list_core, :updated_at => (Time.now - 1.day), + @filter_build_list1 = FactoryGirl.create(:build_list) + @filter_build_list2 = FactoryGirl.create(:build_list) + @filter_build_list3 = FactoryGirl.create(:build_list) + @filter_build_list4 = FactoryGirl.create(:build_list, :updated_at => (Time.now - 1.day), :project => @build_list3.project, :save_to_platform => @build_list3.save_to_platform, :arch => @build_list3.arch) end @@ -590,8 +751,8 @@ describe Api::V1::BuildListsController do http_login FactoryGirl.create(:admin) end - it 'should filter by bs_id' do - get :index, :filter => {:bs_id => @filter_build_list1.bs_id, :project_name => 'fdsfdf', :any_other_field => 'do not matter'}, :format => :json + it 'should filter by id' do + get :index, :filter => {:id => @filter_build_list1.id, :project_name => 'fdsfdf', :any_other_field => 'do not matter'}, :format => :json assigns[:build_lists].should include(@filter_build_list1) assigns[:build_lists].should_not include(@filter_build_list2) assigns[:build_lists].should_not include(@filter_build_list3) @@ -619,7 +780,7 @@ describe Api::V1::BuildListsController do context "for user" do before(:each) do - @build_list = FactoryGirl.create(:build_list_core) + @build_list = FactoryGirl.create(:build_list) @params = @build_list.attributes.symbolize_keys @project = @build_list.project @@ -675,7 +836,7 @@ describe Api::V1::BuildListsController do context "for group" do before(:each) do @platform = FactoryGirl.create(:platform_with_repos) - @build_list = FactoryGirl.create(:build_list_core, :save_to_platform => @platform) + @build_list = FactoryGirl.create(:build_list, :save_to_platform => @platform) @project = @build_list.project @params = @build_list.attributes.symbolize_keys @@ -741,6 +902,5 @@ describe Api::V1::BuildListsController do end end end - end end diff --git a/spec/controllers/api/v1/issues_controller_spec.rb b/spec/controllers/api/v1/issues_controller_spec.rb new file mode 100644 index 000000000..f7342a774 --- /dev/null +++ b/spec/controllers/api/v1/issues_controller_spec.rb @@ -0,0 +1,222 @@ +# -*- encoding : utf-8 -*- +require 'spec_helper' + +describe Api::V1::IssuesController do + before(:all) do + stub_symlink_methods + stub_redis + any_instance_of(Project, :versions => ['v1.0', 'v2.0']) + + @project = FactoryGirl.create(:project_with_commit) + @issue = FactoryGirl.create(:issue, :project => @project) + + @membered_issue = FactoryGirl.create(:issue) + @membered_project = @membered_issue.project + @membered_project.relations.create(:role => 'reader', :actor => @issue.user) + + @open_issue = FactoryGirl.create(:issue) + @open_project = @open_issue.project + + @own_hidden_project = FactoryGirl.create(:project, :owner => @issue.user) + @own_hidden_project.update_column :visibility, 'hidden' + @own_hidden_issue = FactoryGirl.create(:issue, :project => @own_hidden_project, :assignee => @issue.user) + + @hidden_issue = FactoryGirl.create(:issue) + @hidden_project = @hidden_issue.project + @hidden_project.update_column :visibility, 'hidden' + + @create_params = {:issue => {:title => 'title', :body => 'body'}, :project_id => @project.id, :format => :json} + @update_params = {:issue => {:title => 'new title'}, :project_id => @project.id, :id => @issue.serial_id, :format => :json} + + @pull = @project.pull_requests.new :issue_attributes => {:title => 'test', :body => 'testing'} + @pull.issue.user, @pull.issue.project = @project.owner, @pull.to_project + @pull.to_ref = 'master' + @pull.from_project, @pull.from_ref = @project, 'non_conflicts' + @pull.save + end + + context 'read and accessible abilities' do + context 'for user' do + before(:each) do + http_login(@issue.user) + end + + it 'can show issue in own project' do + get :show, :project_id => @project.id, :id => @issue.serial_id, :format => :json + response.should be_success + end + + it 'should render right template for show action' do + get :show, :project_id => @project.id, :id => @issue.serial_id, :format => :json + response.should render_template('api/v1/issues/show') + end + + it 'can show issue in open project' do + get :show, :project_id => @open_project.id, :id => @open_issue.serial_id, :format => :json + response.should be_success + end + + it 'can show issue in own hidden project' do + get :show, :project_id => @own_hidden_project.id, :id => @own_hidden_issue.serial_id, :format => :json + response.should be_success + end + + it "can't show issue in hidden project" do + get :show, :project_id => @hidden_project.id, :id => @hidden_issue.serial_id, :format => :json + response.status.should == 403 + end + + it 'should return three issues' do + get :all_index, :filter => 'all', :format => :json + assigns[:issues].should include(@issue) + assigns[:issues].should include(@own_hidden_issue) + assigns[:issues].should include(@membered_issue) + end + + it 'should render right template for all index action' do + get :all_index, :format => :json + response.should render_template('api/v1/issues/index') + end + + it 'should return only assigned issue' do + get :user_index, :format => :json + assigns[:issues].should include(@own_hidden_issue) + assigns[:issues].should have(1).item + end + + it 'should render right template for user index action' do + get :user_index, :format => :json + response.should render_template('api/v1/issues/index') + end + + it 'should return 404' do + get :show, :project_id => @project.id, :id => 999999, :format => :json + response.status.should == 404 + end + + it 'should redirect to pull request page' do + get :show, :project_id => @project.id, :id => @pull.serial_id, :format => :json + response.should redirect_to(api_v1_project_pull_request_path(@project.id, @pull.serial_id)) + end + end + + context 'for anonymous user' do + it 'can show issue in open project', :anonymous_access => true do + get :show, :project_id => @project.id, :id => @issue.serial_id, :format => :json + response.should be_success + end + + it "can't show issue in hidden project", :anonymous_access => true do + get :show, :project_id => @hidden_project.id, :id => @hidden_issue.serial_id, :format => :json + response.status.should == 403 + end + + it 'should not return any issues' do + get :all_index, :filter => 'all', :format => :json + response.status.should == 401 + end + end + end + + context 'create accessibility' do + context 'for user' do + before(:each) do + http_login(@issue.user) + end + + it 'can create issue in own project' do + lambda { post :create, @create_params}.should change{ Issue.count }.by(1) + end + + it 'can create issue in own hidden project' do + lambda { post :create, @create_params.merge(:project_id => @own_hidden_project.id)}.should change{ Issue.count }.by(1) + end + + it 'can create issue in open project' do + lambda { post :create, @create_params.merge(:project_id => @open_project.id)}.should change{ Issue.count }.by(1) + end + + it "can't create issue in hidden project" do + lambda { post :create, @create_params.merge(:project_id => @hidden_project.id)}.should change{ Issue.count }.by(0) + end + + it 'can assignee issue in own project' do + post :create, @create_params.deep_merge(:project_id => @own_hidden_project, :issue => {:assignee_id => @issue.user.id}) + @own_hidden_project.issues.reload.last.assignee.id.should == @issue.user.id + end + + it "can't assignee issue in open project" do + post :create, @create_params.deep_merge(:project_id => @open_project.id, :issue => {:assignee_id => @issue.user.id}) + @open_project.issues.reload.last.assignee.should be_nil + end + end + + context 'for anonymous user' do + it "can't create issue in project", :anonymous_access => true do + lambda { post :create, @create_params}.should change{ Issue.count }.by(0) + end + + it "can't create issue in hidden project", :anonymous_access => true do + lambda { post :create, @create_params.merge(:project_id => @hidden_project.id)}.should change{ Issue.count }.by(0) + end + end + end + + context 'update accessibility' do + context 'for user' do + before(:each) do + http_login(@issue.user) + end + + it 'can update issue in own project' do + put :update, @update_params + @issue.reload.title.should == 'new title' + end + + it 'can update issue in own hidden project' do + put :update, @update_params.merge(:project_id => @own_hidden_project.id, :id => @own_hidden_issue.serial_id) + @own_hidden_issue.reload.title.should == 'new title' + end + + it "can't update issue in open project" do + put :update, @update_params.merge(:project_id => @open_project.id, :id => @open_issue.serial_id) + @open_issue.reload.title.should_not == 'new title' + end + + it "can't update issue in hidden project" do + put :update, @update_params.merge(:project_id => @hidden_project.id, :id => @hidden_issue.serial_id) + @hidden_issue.reload.title.should_not == 'title' + end + + it "can't assignee issue in open project" do + post :create, @update_params.deep_merge(:project_id => @open_project.id, :issue => {:assignee_id => @issue.user.id}) + @open_issue.reload.assignee.id.should_not == @issue.user.id + end + + it 'can assignee issue in own project' do + post :create, @update_params.deep_merge(:issue => {:assignee_id => @issue.user.id}) + @issue.reload.assignee.id.should_not == @issue.user.id + end + end + + context 'for anonymous user' do + before(:each) do + @count = Issue.count + end + it "can't update issue in project", :anonymous_access => true do + put :update, @update_params + response.status.should == 401 + end + + it "can't update issue in hidden project", :anonymous_access => true do + put :update, @update_params.merge(:project_id => @hidden_project.id, :id => @hidden_issue.serial_id) + response.status.should == 401 + end + end + end + + after(:all) do + User.destroy_all + Platform.destroy_all + end +end diff --git a/spec/controllers/api/v1/maintainers_controller_spec.rb b/spec/controllers/api/v1/maintainers_controller_spec.rb index 837539ec9..6d28e2aad 100644 --- a/spec/controllers/api/v1/maintainers_controller_spec.rb +++ b/spec/controllers/api/v1/maintainers_controller_spec.rb @@ -1,21 +1,42 @@ require 'spec_helper' +shared_examples_for 'api maintainers user with reader rights' do + it 'should be able to perform index action' do + get :index, :platform_id => package.platform_id, :format => :json + should render_template(:index) + end + + it 'loads all of the maintainers into @maintainers' do + get :index, :platform_id => package.platform_id, :format => :json + assigns(:maintainers).should have(2).items + assigns(:maintainers).should include(package, package2) + end + + it 'loads all of the maintainers into @maintainers when search by name' do + get :index, :platform_id => package.platform_id, :package_name => 'package1', :format => :json + assigns(:maintainers).should have(1).item + assigns(:maintainers).should include(package) + end + +end + describe Api::V1::MaintainersController do before do stub_symlink_methods + FactoryGirl.create(:build_list_package, :platform => package.platform) end + let(:package) { FactoryGirl.create(:build_list_package, :name => 'package1', :actual => true) } + let!(:package2) { FactoryGirl.create(:build_list_package, :platform => package.platform, :actual => true) } - let(:package) { FactoryGirl.create(:build_list_package) } context 'for guest' do - it "should be able to perform index action", :anonymous_access => true do - get :index, :platform_id => package.platform_id, :format => :json - should render_template(:index) - end - - it 'should be able to perform get_id action', :anonymous_access => false do - get :index, :platform_id => package.platform_id, :format => :json - response.status.should == 401 + if APP_CONFIG['anonymous_access'] + it_should_behave_like 'api maintainers user with reader rights' + else + it 'should not be able to perform index action', :anonymous_access => false do + get :index, :platform_id => package.platform_id, :format => :json + response.status.should == 401 + end end end @@ -24,9 +45,6 @@ describe Api::V1::MaintainersController do http_login(FactoryGirl.create(:user)) end - it "should be able to perform index action" do - get :index, :platform_id => package.platform_id, :format => :json - should render_template(:index) - end + it_should_behave_like 'api maintainers user with reader rights' end end diff --git a/spec/controllers/api/v1/platforms_controller_spec.rb b/spec/controllers/api/v1/platforms_controller_spec.rb index 355c04869..18553c737 100644 --- a/spec/controllers/api/v1/platforms_controller_spec.rb +++ b/spec/controllers/api/v1/platforms_controller_spec.rb @@ -224,13 +224,17 @@ describe Api::V1::PlatformsController do response.status.should == 401 end - [:show, :platforms_for_build].each do |action| - it "should not be able to perform #{ action } action", :anonymous_access => false do - get action, :format => :json - response.status.should == 401 - end + it "should not be able to perform platforms_for_build action", :anonymous_access => false do + get :platforms_for_build, :format => :json + response.status.should == 401 end + it "should not be able to perform show action", :anonymous_access => false do + get :show, :id => @platform, :format => :json + response.status.should == 401 + end + + it 'should be able to perform members action', :anonymous_access => true do get :members, :id => @platform.id, :format => :json response.should render_template(:members) @@ -241,6 +245,91 @@ describe Api::V1::PlatformsController do it_should_behave_like 'api platform user without member rights' it_should_behave_like 'api platform user without owner rights' it_should_behave_like 'api platform user without global admin rights' + + + context 'perform allowed action' do + it 'ensures that status 200 if platform empty' do + get :allowed + response.status.should == 200 + end + + it 'ensures that status 403 if platform does not exist' do + get :allowed, :path => "/rosa-server/repository/SRPMS/base/release/repodata/" + response.status.should == 403 + end + + it 'ensures that status 200 if platform open' do + get :allowed, :path => "/#{@platform.name}/repository/SRPMS/base/release/repodata/" + response.status.should == 200 + end + + context 'for hidden platform' do + before { @platform.change_visibility } + + it 'ensures that status 403 if no token' do + get :allowed, :path => "/#{@platform.name}/repository/SRPMS/base/release/repodata/" + response.status.should == 403 + end + + it 'ensures that status 403 if no token and a lot of "/"' do + get :allowed, :path => "///#{@platform.name}///repository/SRPMS/base/release/repodata/" + response.status.should == 403 + end + + it 'ensures that status 200 if token correct and a lot of "/"' do + token = FactoryGirl.create(:platform_token, :subject => @platform) + http_login token.authentication_token, '' + get :allowed, :path => "///#{@platform.name}///repository/SRPMS/base/release/repodata/" + response.status.should == 200 + end + + it 'ensures that status 403 on access to root of platform if no token' do + get :allowed, :path => "///#{@platform.name}" + response.status.should == 403 + end + + it 'ensures that status 200 on access to root of platform if token correct' do + token = FactoryGirl.create(:platform_token, :subject => @platform) + http_login token.authentication_token, '' + get :allowed, :path => "///#{@platform.name}" + response.status.should == 200 + end + + it 'ensures that status 403 if wrong token' do + http_login 'KuKu', '' + get :allowed, :path => "/#{@platform.name}/repository/SRPMS/base/release/repodata/" + response.status.should == 403 + end + + it 'ensures that status 200 if token correct' do + token = FactoryGirl.create(:platform_token, :subject => @platform) + http_login token.authentication_token, '' + get :allowed, :path => "/#{@platform.name}/repository/SRPMS/base/release/repodata/" + response.status.should == 200 + end + + it 'ensures that status 403 if token correct but blocked' do + token = FactoryGirl.create(:platform_token, :subject => @platform) + token.block + http_login token.authentication_token, '' + get :allowed, :path => "/#{@platform.name}/repository/SRPMS/base/release/repodata/" + response.status.should == 403 + end + + it 'ensures that status 200 if user token correct and user has ability to read platform' do + http_login @platform.owner.authentication_token, '' + get :allowed, :path => "/#{@platform.name}/repository/SRPMS/base/release/repodata/" + response.status.should == 200 + end + + it 'ensures that status 403 if user token correct but user has no ability to read platform' do + user = FactoryGirl.create(:user) + http_login user.authentication_token, '' + get :allowed, :path => "/#{@platform.name}/repository/SRPMS/base/release/repodata/" + response.status.should == 403 + end + end + end end context 'for global admin' do @@ -311,6 +400,38 @@ describe Api::V1::PlatformsController do it_should_behave_like 'api platform user without global admin rights' end + context 'for member of repository' do + before do + http_login(@user) + repository = FactoryGirl.create(:repository, :platform => @platform) + repository.add_member(@user) + personal_repository = FactoryGirl.create(:repository, :platform => @personal_platform) + personal_repository.add_member(@user) + end + + context 'perform index action with type param' do + render_views + %w(main personal).each do |type| + it "ensures that filter by type = #{type} returns true result" do + get :index, :format => :json, :type => "#{type}" + JSON.parse(response.body)['platforms'].map{ |p| p['platform_type'] }. + uniq.should == ["#{type}"] + end + end + end + + it 'should not be able to perform members action for hidden platform' do + @platform.update_column(:visibility, 'hidden') + get :members, :id => @platform.id, :format => :json + response.status.should == 403 + end + it_should_behave_like 'api platform user with reader rights' + it_should_behave_like 'api platform user with reader rights for hidden platform' + it_should_behave_like 'api platform user without member rights' + it_should_behave_like 'api platform user without owner rights' + it_should_behave_like 'api platform user without global admin rights' + end + context 'for simple user' do before do http_login(@user) diff --git a/spec/controllers/api/v1/projects_controller_spec.rb b/spec/controllers/api/v1/projects_controller_spec.rb index 65e5055e2..49526ed8f 100644 --- a/spec/controllers/api/v1/projects_controller_spec.rb +++ b/spec/controllers/api/v1/projects_controller_spec.rb @@ -61,6 +61,17 @@ shared_examples_for 'api projects user with fork rights' do it 'ensures that project has been forked' do lambda { post :fork, :id => @project.id, :format => :json }.should change{ Project.count }.by(1) end + + it 'should be able to perform fork action with different name' do + post :fork, :id => @project.id, :fork_name => (@project.name + '_forked'), :format => :json + response.should be_success + end + + it 'ensures that project has been forked' do + new_name = @project.name + '_forked' + lambda { post :fork, :id => @project.id, :fork_name => new_name, :format => :json }.should + change{ Project.where(:name => new_name).count }.by(1) + end end shared_examples_for 'api projects user with fork rights for hidden project' do @@ -235,7 +246,6 @@ shared_examples_for 'api projects user without admin rights' do @project.members.should include(member) end end - end shared_examples_for 'api projects user with owner rights' do @@ -313,6 +323,18 @@ describe Api::V1::ProjectsController do it 'ensures that project has been created' do lambda { post :create, params, :format => :json }.should change{ Project.count }.by(1) end + + it 'writer group should be able to create project for their group' do + group = FactoryGirl.create(:group) + group.actors.create(:actor_type => 'User', :actor_id => @user.id, :role => 'writer') + lambda { post :create, params.deep_merge({:project => {:owner_type => 'Group', :owner_id => group.id}})}.should change{ Project.count }.by(1) + end + + it 'reader group should not be able to create project for their group' do + group = FactoryGirl.create(:group) + group.actors.create(:actor_type => 'User', :actor_id => @user.id, :role => 'reader') + lambda { post :create, params.deep_merge({:project => {:owner_type => 'Group', :owner_id => group.id}})}.should change{ Project.count }.by(0) + end end it_should_behave_like 'api projects user with reader rights' @@ -321,6 +343,38 @@ describe Api::V1::ProjectsController do it_should_behave_like 'api projects user without fork rights for hidden project' it_should_behave_like 'api projects user without admin rights' it_should_behave_like 'api projects user without owner rights' + + context 'group writer' do + it 'should be able to fork project to their group' do + group = FactoryGirl.create(:group) + group.actors.create(:actor_type => 'User', :actor_id => @user.id, :role => 'writer') + lambda {post :fork, :id => @project.id, :group_id => group.id}.should change{ Project.count }.by(1) + end + + it 'should be able to fork project with different name to their group' do + group = FactoryGirl.create(:group) + group.actors.create(:actor_type => 'User', :actor_id => @user.id, :role => 'writer') + new_name = @project.name + '_forked' + lambda { post :fork, :id => @project.id, :group_id => group.id, :fork_name => new_name }.should + change { Project.where(:name => new_name).count }.by(1) + end + end + + context 'group reader' do + it 'should not be able to fork project to their group' do + group = FactoryGirl.create(:group) + group.actors.create(:actor_type => 'User', :actor_id => @user.id, :role => 'reader') + lambda {post :fork, :id => @project.id, :group_id => group.id}.should change{ Project.count }.by(0) + end + + it 'should not be able to fork project with different name to their group' do + group = FactoryGirl.create(:group) + new_name = @project.name + '_forked' + group.actors.create(:actor_type => 'User', :actor_id => @user.id, :role => 'reader') + lambda { post :fork, :id => @project.id, :group_id => group.id, :fork_name => new_name }.should + change{ Project.where(:name => new_name.count) }.by(0) + end + end end context 'for admin' do diff --git a/spec/controllers/api/v1/pull_requests_controller.rb b/spec/controllers/api/v1/pull_requests_controller.rb new file mode 100644 index 000000000..e3469a172 --- /dev/null +++ b/spec/controllers/api/v1/pull_requests_controller.rb @@ -0,0 +1,324 @@ +# -*- encoding : utf-8 -*- +require 'spec_helper' + +def create_pull to_ref, from_ref, owner, project = @project + pull = project.pull_requests.new :issue_attributes => {:title => 'test', :body => 'testing'} + pull.issue.user, pull.issue.project = owner, pull.to_project + pull.to_ref, pull.from_ref, pull.from_project = to_ref, from_ref, project + pull.save; pull.check + pull +end + +describe Api::V1::PullRequestsController do + before(:all) do + stub_symlink_methods + stub_redis + @project = FactoryGirl.create(:project_with_commit) + @pull = create_pull 'master', 'non_conflicts', @project.owner + + @another_project = FactoryGirl.create(:project_with_commit) + @another_pull = create_pull 'master', 'non_conflicts', @another_project.owner, @another_project + + @hidden_project = FactoryGirl.create(:project_with_commit) + @hidden_project.update_column :visibility, 'hidden' + @hidden_pull = create_pull 'master', 'non_conflicts', @hidden_project.owner, @hidden_project + + @own_hidden_project = FactoryGirl.create(:project_with_commit, :owner => @project.owner) + @own_hidden_project.update_column :visibility, 'hidden' + @own_hidden_pull = create_pull 'master', 'non_conflicts', @own_hidden_project.owner, @own_hidden_project + @own_hidden_pull.issue.update_column :assignee_id, @project.owner.id + + @membered_project = FactoryGirl.create(:project_with_commit) + @membered_pull = create_pull 'master', 'non_conflicts', @membered_project.owner, @membered_project + @membered_project.relations.create(:role => 'reader', :actor => @pull.user) + + @create_params = {:pull_request => {:title => 'title', :body => 'body', + :from_ref => 'conflicts', :to_ref => 'master'}, + :project_id => @project.id, :format => :json} + + @update_params = {:pull_request => {:title => 'new title'}, + :project_id => @project.id, :id => @pull.serial_id, :format => :json} + + @issue = FactoryGirl.create(:issue, :project => @project) + end + + context 'read and accessible abilities' do + context 'for user' do + before(:each) do + http_login(@project.owner) + end + + it 'can show pull request in own project' do + get :show, :project_id => @project.id, :id => @pull.serial_id, :format => :json + response.should be_success + end + + it 'should render right template for show action' do + get :show, :project_id => @project.id, :id => @pull.serial_id, :format => :json + response.should render_template('api/v1/pull_requests/show') + end + + it 'can show pull request in open project' do + get :show, :project_id => @another_project.id, :id => @another_pull.serial_id, :format => :json + response.should be_success + end + + it 'can show pull request in own hidden project' do + get :show, :project_id => @own_hidden_project.id, :id => @own_hidden_pull.serial_id, :format => :json + response.should be_success + end + + it 'cant show pull request in hidden project' do + get :show, :project_id => @hidden_project.id, :id => @hidden_pull.serial_id, :format => :json + response.status.should == 403 + end + + it 'should return three pull requests' do + get :all_index, :filter => 'all', :format => :json + assigns[:pulls].should include(@pull) + assigns[:pulls].should include(@own_hidden_pull) + assigns[:pulls].should include(@membered_pull) + end + + it 'should render right template for all index action' do + get :all_index, :format => :json + response.should render_template('api/v1/pull_requests/index') + end + + it 'should return only assigned pull request' do + get :user_index, :format => :json + assigns[:pulls].should include(@own_hidden_pull) + assigns[:pulls].should have(1).item + end + + it 'should render right template for user index action' do + get :user_index, :format => :json + response.should render_template('api/v1/pull_requests/index') + end + + %w(commits files).each do |action| + it "can show pull request #{action} in own project" do + get action, :project_id => @project.id, :id => @pull.serial_id, :format => :json + response.should be_success + end + + it "should render right template for commits action" do + get action, :project_id => @project.id, :id => @pull.serial_id, :format => :json + response.should render_template("api/v1/pull_requests/#{action}") + end + + it "can't show pull request #{action} in hidden project" do + get action, :project_id => @hidden_project.id, :id => @hidden_pull.serial_id, :format => :json + response.should_not be_success + end + end + + it 'should return 404' do + get :show, :project_id => @project.id, :id => 999999, :format => :json + response.status.should == 404 + end + + it 'should redirect to issue page' do + get :show, :project_id => @project.id, :id => @issue.serial_id, :format => :json + response.should redirect_to(api_v1_project_issue_path(@project.id, @issue.serial_id)) + end + end + + context 'for anonymous user' do + it 'can show pull request in open project', :anonymous_access => true do + get :show, :project_id => @project.id, :id => @pull.serial_id, :format => :json + response.should be_success + end + + it 'cant show pull request in hidden project', :anonymous_access => true do + @project.update_column :visibility, 'hidden' + get :show, :project_id => @project.id, :id => @pull.serial_id, :format => :json + response.status.should == 403 + end + + it 'should not return any pull requests' do + get :all_index, :filter => 'all', :format => :json + response.status.should == 401 + end + + %w(commits files).each do |action| + it "can show pull request #{action} in project", :anonymous_access => true do + get action, :project_id => @project.id, :id => @pull.serial_id, :format => :json + response.should be_success + end + + it "should render right template for commits action", :anonymous_access => true do + get action, :project_id => @project.id, :id => @pull.serial_id, :format => :json + response.should render_template("api/v1/pull_requests/#{action}") + end + + it "can't show pull request #{action} in hidden project", :anonymous_access => true do + get action, :project_id => @hidden_project.id, :id => @hidden_pull.serial_id, :format => :json + response.should_not be_success + end + end + end + end + + context 'create accessibility' do + context 'for user' do + before(:each) do + http_login(@pull.user) + end + + it 'can create pull request in own project' do + lambda { post :create, @create_params }.should change{ PullRequest.count }.by(1) + end + + it 'can create pull request in own hidden project' do + lambda { post :create, @create_params.merge(:project_id => @own_hidden_project.id) }.should + change{ PullRequest.count }.by(1) + end + + it 'can create pull request in open project' do + lambda { post :create, @create_params.merge(:project_id => @another_project.id) }.should + change{ PullRequest.count }.by(1) + end + + it 'cant create pull request in hidden project' do + lambda { post :create, @create_params.merge(:project_id => @hidden_project.id) }.should + change{ PullRequest.count }.by(0) + end + end + + context 'for anonymous user' do + it 'cant create pull request in project', :anonymous_access => true do + lambda { post :create, @create_params }.should change{ PullRequest.count }.by(0) + end + + it 'cant create pull request in hidden project', :anonymous_access => true do + lambda { post :create, @create_params.merge(:project_id => @hidden_project.id) }.should + change{ PullRequest.count }.by(0) + end + end + end + + context 'update accessibility' do + context 'for user' do + before(:each) do + http_login(@project.owner) + end + + it 'can update pull request in own project' do + put :update, @update_params + @pull.reload.title.should == 'new title' + end + + it 'can update pull request in own hidden project' do + put :update, @update_params.merge(:project_id => @own_hidden_project.id, :id => @own_hidden_pull.serial_id) + @own_hidden_pull.reload.title.should == 'new title' + end + + it 'cant update pull request in open project' do + put :update, @update_params.merge(:project_id => @another_project.id, :id => @another_pull.serial_id) + @another_pull.reload.title.should_not == 'new title' + end + + it 'cant update pull request in hidden project' do + put :update, @update_params.merge(:project_id => @hidden_project.id, :id => @hidden_pull.serial_id) + @hidden_pull.reload.title.should_not == 'title' + end + + it 'can merge pull request in own project' do + put :merge, :project_id => @project.id, :id => @pull.serial_id, :format => :json + @pull.reload.status.should == 'merged' + response.should be_success + end + + it 'can merge pull request in own hidden project' do + put :merge, :project_id => @own_hidden_project.id, :id => @own_hidden_pull.serial_id, :format => :json + @own_hidden_pull.reload.status.should == 'merged' + response.should be_success + end + + it 'cant merge pull request in open project' do + put :merge, :project_id => @another_project.id, :id => @another_pull.serial_id, :format => :json + @another_pull.reload.status.should == 'ready' + response.status.should == 403 + end + + it 'cant merge pull request in hidden project' do + put :merge, :project_id => @hidden_project.id, :id => @hidden_pull.serial_id, :format => :json + @hidden_pull.reload.status.should == 'ready' + response.status.should == 403 + end + end + + context 'for anonymous user' do + it 'cant update pull request in project', :anonymous_access => true do + put :update, @update_params + response.status.should == 401 + end + + it 'cant update pull request in hidden project', :anonymous_access => true do + put :update, @update_params.merge(:project_id => @hidden_project.id, :id => @hidden_pull.serial_id) + response.status.should == 401 + end + + it 'cant merge pull request in open project' do + put :merge, :project_id => @another_project.id, :id => @another_pull.serial_id, :format => :json + @another_pull.reload.status.should == 'ready' + response.status.should == 401 + end + + it 'cant merge pull request in hidden project' do + put :merge, :project_id => @hidden_project.id, :id => @hidden_pull.serial_id, :format => :json + @hidden_pull.reload.status.should == 'ready' + response.status.should == 401 + end + end + end + + context 'send email messages' do + before(:each) do + @project_reader = FactoryGirl.create :user + @project.relations.create!(:actor_type => 'User', :actor_id => @project_reader.id, :role => 'reader') + @project_admin = FactoryGirl.create :user + @project.relations.create!(:actor_type => 'User', :actor_id => @project_admin.id, :role => 'admin') + @project_writer = FactoryGirl.create :user + @project.relations.create!(:actor_type => 'User', :actor_id => @project_writer.id, :role => 'writer') + + http_login(@project_writer) + ActionMailer::Base.deliveries = [] + end + + it 'should send two email messages to project admins' do + post :create, @create_params + @project.pull_requests.last.issue.send(:new_issue_notifications) + @project.pull_requests.last.issue.send(:send_assign_notifications) + ActionMailer::Base.deliveries.count.should == 2 + end + + it 'should send two email messages to admins and one to assignee' do + post :create, @create_params.deep_merge(:pull_request => {:assignee_id => @project_reader.id}) + @project.pull_requests.last.issue.send(:new_issue_notifications) + @project.pull_requests.last.issue.send(:send_assign_notifications) + ActionMailer::Base.deliveries.count.should == 3 + end + + it 'should send email message to new assignee' do + http_login(@project_admin) + put :update, @update_params.deep_merge(:pull_request => {:assignee_id => @project_reader.id}) + @project.pull_requests.last.issue.send(:send_assign_notifications) + ActionMailer::Base.deliveries.count.should == 1 + end + + it 'should not duplicate email message' do + post :create, @create_params.deep_merge(:pull_request => {:assignee_id => @project_admin.id}) + @project.pull_requests.last.issue.send(:new_issue_notifications) + @project.pull_requests.last.issue.send(:send_assign_notifications) + ActionMailer::Base.deliveries.count.should == 2 # send only to admins + ActionMailer::Base.deliveries.first.to != ActionMailer::Base.deliveries.last.to + end + end + + after(:all) do + User.destroy_all + Platform.destroy_all + end +end diff --git a/spec/controllers/api/v1/repositories_controller_spec.rb b/spec/controllers/api/v1/repositories_controller_spec.rb index cb812db39..bdf88710b 100644 --- a/spec/controllers/api/v1/repositories_controller_spec.rb +++ b/spec/controllers/api/v1/repositories_controller_spec.rb @@ -61,6 +61,15 @@ shared_examples_for 'api repository user with writer rights' do end end + context 'api repository user with add/remove repo_lock_file rights' do + [:add_repo_lock_file, :remove_repo_lock_file].each do |action| + it "should be able to perform #{action} action" do + put action, :id => @repository.id, :format => :json + response.should be_success + end + end + end + context 'api repository user with add_member rights' do let(:member) { FactoryGirl.create(:user) } before do @@ -98,15 +107,44 @@ shared_examples_for 'api repository user with writer rights' do it 'ensures that repository of main platform has been destroyed' do lambda { delete :destroy, :id => @repository.id, :format => :json }.should change{ Repository.count }.by(-1) end - it 'should not be able to perform destroy action for repository of personal platform' do - delete :destroy, :id => @personal_repository.id, :format => :json - response.should_not be_success + + context 'repository with name "main" of personal platform' do + # hook for "ActiveRecord::ActiveRecordError: name is marked as readonly" + before { Repository.where(:id => @personal_repository.id).update_all("name = 'main'") } + it 'should not be able to perform destroy action' do + delete :destroy, :id => @personal_repository.id, :format => :json + response.should_not be_success + end + it 'ensures that repository has not been destroyed' do + lambda { delete :destroy, :id => @personal_repository.id, :format => :json }.should_not change{ Repository.count } + end end - it 'ensures that repository of personal platform has not been destroyed' do - lambda { delete :destroy, :id => @personal_repository.id, :format => :json }.should_not change{ Repository.count } + it 'should be able to perform destroy action for repository with name not "main" of personal platform' do + delete :destroy, :id => @personal_repository.id, :format => :json + response.should be_success + end + it 'ensures that repository with name not "main" of personal platform has been destroyed' do + lambda { delete :destroy, :id => @personal_repository.id, :format => :json }.should change{ Repository.count }.by(-1) end end + context 'api repository user with update signatures rights' do + before do + kp = FactoryGirl.build(:key_pair) + put :signatures, :id => @repository.id, :repository => {:public => kp.public, :secret => kp.secret}, :format => :json + end + it 'should be able to perform signatures action' do + response.should be_success + end + it 'ensures that signatures has been updated' do + @repository.key_pair.should_not be_nil + end + end + +end + +shared_examples_for 'api repository user with project manage rights' do + context 'api repository user with add_project rights' do before { put :add_project, :id => @repository.id, :project_id => @project.id, :format => :json } it 'should be able to perform add_project action' do @@ -131,19 +169,6 @@ shared_examples_for 'api repository user with writer rights' do end end - context 'api repository user with update signatures rights' do - before do - kp = FactoryGirl.build(:key_pair) - put :signatures, :id => @repository.id, :repository => {:public => kp.public, :secret => kp.secret}, :format => :json - end - it 'should be able to perform signatures action' do - response.should be_success - end - it 'ensures that signatures has been updated' do - @repository.key_pair.should_not be_nil - end - end - end shared_examples_for 'api repository user without writer rights' do @@ -162,6 +187,15 @@ shared_examples_for 'api repository user without writer rights' do end end + context 'api repository user without add/remove repo_lock_file rights' do + [:add_repo_lock_file, :remove_repo_lock_file].each do |action| + it "should not be able to perform #{action} action" do + put action, :id => @repository.id, :format => :json + response.should_not be_success + end + end + end + context 'api repository user without add_member rights' do let(:member) { FactoryGirl.create(:user) } before do @@ -208,6 +242,22 @@ shared_examples_for 'api repository user without writer rights' do end end + context 'api repository user without update signatures rights' do + before do + kp = FactoryGirl.build(:key_pair) + put :signatures, :id => @repository.id, :repository => {:public => kp.public, :secret => kp.secret}, :format => :json + end + it 'should not be able to perform signatures action' do + response.should_not be_success + end + it 'ensures that signatures has not been updated' do + @repository.key_pair.should be_nil + end + end + +end + +shared_examples_for 'api repository user without project manage rights' do context 'api repository user without add_project rights' do before { put :add_project, :id => @repository.id, :project_id => @project.id, :format => :json } it 'should not be able to perform add_project action' do @@ -231,20 +281,6 @@ shared_examples_for 'api repository user without writer rights' do @repository.projects.should include(@project) end end - - context 'api repository user without update signatures rights' do - before do - kp = FactoryGirl.build(:key_pair) - put :signatures, :id => @repository.id, :repository => {:public => kp.public, :secret => kp.secret}, :format => :json - end - it 'should not be able to perform signatures action' do - response.should_not be_success - end - it 'ensures that signatures has not been updated' do - @repository.key_pair.should be_nil - end - end - end @@ -271,6 +307,7 @@ describe Api::V1::RepositoriesController do it_should_behave_like 'api repository user with show rights' end it_should_behave_like 'api repository user without writer rights' + it_should_behave_like 'api repository user without project manage rights' it_should_behave_like 'api repository user without key_pair rights' it 'should not be able to perform projects action', :anonymous_access => false do @@ -295,9 +332,11 @@ describe Api::V1::RepositoriesController do before(:each) do @user = FactoryGirl.create(:user) http_login(@user) - platform = @repository.platform - platform.owner = @user; platform.save - @repository.platform.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') + [@repository, @personal_repository].each do |repository| + platform = repository.platform + platform.owner = @user; platform.save + repository.platform.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') + end end it_should_behave_like 'api repository user with reader rights' @@ -316,6 +355,22 @@ describe Api::V1::RepositoriesController do it_should_behave_like 'api repository user without reader rights for hidden platform' it_should_behave_like 'api repository user with show rights' it_should_behave_like 'api repository user without writer rights' + it_should_behave_like 'api repository user without project manage rights' + it_should_behave_like 'api repository user without key_pair rights' + end + + context 'for member of repository' do + before(:each) do + @user = FactoryGirl.create(:user) + @repository.add_member @user + http_login @user + end + + it_should_behave_like 'api repository user with reader rights' + it_should_behave_like 'api repository user with reader rights for hidden platform' + it_should_behave_like 'api repository user with show rights' + it_should_behave_like 'api repository user with project manage rights' + it_should_behave_like 'api repository user without writer rights' it_should_behave_like 'api repository user without key_pair rights' end diff --git a/spec/controllers/autocompletes_controller_spec.rb b/spec/controllers/autocompletes_controller_spec.rb index ac1f61e19..a80d70991 100644 --- a/spec/controllers/autocompletes_controller_spec.rb +++ b/spec/controllers/autocompletes_controller_spec.rb @@ -4,8 +4,9 @@ describe AutocompletesController do before {stub_symlink_methods} context 'for user' do + let(:user) { FactoryGirl.create(:user) } before do - set_session_for(FactoryGirl.create(:user)) + set_session_for user end it 'should be able to perform autocomplete_group_uname action' do @@ -18,6 +19,44 @@ describe AutocompletesController do response.should be_success end + context 'autocomplete_extra_build_list' do + let(:build_list) { FactoryGirl.create(:build_list, :user => user) } + let(:params) { { :term => build_list.id, + :platform_id => build_list.save_to_platform_id } } + + it 'no data when build_list without container' do + get :autocomplete_extra_build_list, params + response.body.should == '[]' + end + + it 'shows data when build_list with container' do + build_list.update_column(:container_status, BuildList::BUILD_PUBLISHED) + get :autocomplete_extra_build_list, params + response.body.should_not == '[]' + end + end + + context 'autocomplete_extra_repositories' do + let(:repository) { FactoryGirl.create(:repository) } + let(:params) { { :term => repository.platform.name, + :platform_id => repository.platform_id } } + + before do + repository.platform.add_member(user) + end + + it 'no data when repository of main platform' do + get :autocomplete_extra_repositories, params + response.body.should == '[]' + end + + it 'shows data when repository of personal platform' do + Platform.update_all(:platform_type => 'personal') + get :autocomplete_extra_repositories, params + response.body.should_not == '[]' + end + end + end context 'for guest' do @@ -26,14 +65,16 @@ describe AutocompletesController do set_session_for(User.new) end - it 'should not be able to perform autocomplete_group_uname action' do - get :autocomplete_group_uname - response.should redirect_to(new_user_session_path) - end - - it 'should not be able to perform autocomplete_user_uname action' do - get :autocomplete_user_uname - response.should redirect_to(new_user_session_path) + [ + :autocomplete_group_uname, + :autocomplete_user_uname, + :autocomplete_extra_build_list, + :autocomplete_extra_repositories + ].each do |action| + it "should not be able to perform #{action} action" do + get action + response.should redirect_to(new_user_session_path) + end end end diff --git a/spec/controllers/groups/profile_controller_spec.rb b/spec/controllers/groups/profile_controller_spec.rb index 93119ad35..567ae26e2 100644 --- a/spec/controllers/groups/profile_controller_spec.rb +++ b/spec/controllers/groups/profile_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' shared_examples_for 'group user with project show rights' do it 'should be able to perform show action' do - get :show, :id => @group + get :show, :uname => @group.uname response.should render_template(:show) end end diff --git a/spec/controllers/platforms/contents_controller_spec.rb b/spec/controllers/platforms/contents_controller_spec.rb new file mode 100644 index 000000000..e5cca7814 --- /dev/null +++ b/spec/controllers/platforms/contents_controller_spec.rb @@ -0,0 +1,98 @@ +# -*- encoding : utf-8 -*- +require 'spec_helper' + +shared_examples_for 'content platform user without show rights for hidden platform' do + it 'should not be able to perform index action' do + @platform.update_column(:visibility, 'hidden') + get :index, :platform_id => @platform + response.should_not be_success + end +end + +shared_examples_for 'content platform user with show rights for hidden platform' do + it 'should be able to perform index action' do + @platform.update_column(:visibility, 'hidden') + get :index, :platform_id => @platform + response.should be_success + end +end + +shared_examples_for 'content platform user with show rights' do + it 'should be able to perform index action for main platform' do + get :index, :platform_id => @platform + response.should be_success + end + + it 'should be able to perform index action for personal platform' do + get :index, :platform_id => @personal_platform + response.should be_success + end +end + +describe Platforms::ContentsController do + before do + stub_symlink_methods + + @platform = FactoryGirl.create(:platform) + @personal_platform = FactoryGirl.create(:platform, :platform_type => 'personal') + + @user = FactoryGirl.create(:user) + end + + context 'for guest' do + + it 'should not be able to perform index action for main platform', :anonymous_access => false do + get :index, :platform_id => @platform + response.should_not be_success + end + + it 'should not be able to perform index action for personal platform', :anonymous_access => false do + get :index, :platform_id => @personal_platform + response.should_not be_success + end + + it_should_behave_like 'content platform user with show rights' if APP_CONFIG['anonymous_access'] + it_should_behave_like 'content platform user without show rights for hidden platform' + end + + context 'for global admin' do + before do + http_login(FactoryGirl.create(:admin)) + end + + it_should_behave_like 'content platform user with show rights' + it_should_behave_like 'content platform user with show rights for hidden platform' + end + + context 'for owner user' do + before do + http_login(@user) + @platform.owner = @user; @platform.save + @platform.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') + end + + it_should_behave_like 'content platform user with show rights' + it_should_behave_like 'content platform user with show rights for hidden platform' + end + + context 'for member of platform' do + before do + http_login(@user) + @platform.add_member(@user) + @personal_platform.add_member(@user) + end + + it_should_behave_like 'content platform user with show rights' + it_should_behave_like 'content platform user with show rights for hidden platform' + end + + context 'for simple user' do + before do + http_login(@user) + end + + it_should_behave_like 'content platform user with show rights' + it_should_behave_like 'content platform user without show rights for hidden platform' + end + +end diff --git a/spec/controllers/platforms/key_pairs_controller_spec.rb b/spec/controllers/platforms/key_pairs_controller_spec.rb index cef278b35..bb21c11ac 100644 --- a/spec/controllers/platforms/key_pairs_controller_spec.rb +++ b/spec/controllers/platforms/key_pairs_controller_spec.rb @@ -78,7 +78,7 @@ describe Platforms::KeyPairsController do @create_params = { :platform_id => @platform, :key_pair => { - :repository_id => @repository, + :repository_id => @repository.id, :public => kp.public, :secret => kp.secret } diff --git a/spec/controllers/platforms/mass_builds_controller_spec.rb b/spec/controllers/platforms/mass_builds_controller_spec.rb index bc70d7054..d5fbc4b06 100644 --- a/spec/controllers/platforms/mass_builds_controller_spec.rb +++ b/spec/controllers/platforms/mass_builds_controller_spec.rb @@ -6,6 +6,11 @@ shared_examples_for 'mass_build platform owner' do response.should render_template(:index) end + it 'should be able to perform new action' do + get :new, :platform_id => @platform + response.should render_template(:new) + end + it 'should be able to perform create action' do post :create, @create_params response.should redirect_to(platform_mass_builds_path(@platform)) @@ -45,19 +50,19 @@ shared_examples_for 'mass_build platform owner' do get :get_list, :platform_id => @platform, :id => @mass_build, :kind => 'failed_builds_list' response.should be_success end +end - context 'for personal platform' do - before(:each) do - Platform.update_all(:platform_type => 'personal') - end - - [:cancel, :get_list, :create, :publish].each do |action| - it "should not be able to perform #{ action } action" do - get action, :platform_id => @platform, :id => @mass_build.id - response.should redirect_to(forbidden_path) - end +shared_examples_for 'mass_build platform owner of personal platform' do + before(:each) do + Platform.update_all(:platform_type => 'personal') + repository = FactoryGirl.create(:repository) + @mass_build.build_lists.each do |bl| + bl.build_for_platform = repository.platform + bl.include_repos = [repository.id] + bl.save end end + it_should_behave_like 'mass_build platform owner' end shared_examples_for 'mass_build platform reader' do @@ -71,6 +76,11 @@ shared_examples_for 'mass_build platform reader' do response.should be_success end + it "should not be able to perform new action" do + get :new, :platform_id => @platform + response.should redirect_to(forbidden_path) + end + it "should not be able to perform create action" do get :create, :platform_id => @platform response.should redirect_to(forbidden_path) @@ -112,14 +122,17 @@ describe Platforms::MassBuildsController do @repository.projects << project @create_params = { - :platform_id => @platform, - :projects_list => @repository.projects.map(&:name).join("\n"), - :arches => [Arch.first.id], - :auto_publish => true + :mass_build => { + :projects_list => @repository.projects.map(&:name).join("\n"), + :auto_publish => true, + :build_for_platform_id => @platform + }, + :platform_id => @platform, + :arches => [Arch.first.id], } - @mass_build = FactoryGirl.create(:mass_build, :platform => @platform, :user => @user, :projects_list => project.name) - FactoryGirl.create(:build_list_core, :mass_build => @mass_build, :status => BuildList::SUCCESS) + @mass_build = FactoryGirl.create(:mass_build, :save_to_platform => @platform, :user => @user, :projects_list => project.name) + FactoryGirl.create(:build_list, :mass_build => @mass_build, :status => BuildList::SUCCESS) end context 'for guest' do @@ -144,12 +157,12 @@ describe Platforms::MassBuildsController do response.should redirect_to(new_user_session_path) end - it "should not be able to perform create action" do - get :create, :platform_id => @platform + it "should not be able to perform new action" do + get :new, :platform_id => @platform response.should redirect_to(new_user_session_path) end - [:cancel, :publish].each do |action| + [:cancel, :publish, :create].each do |action| it "should not be able to perform #{action} action" do post action, :platform_id => @platform, :id => @mass_build response.should redirect_to(new_user_session_path) @@ -180,6 +193,7 @@ describe Platforms::MassBuildsController do end it_should_behave_like 'mass_build platform owner' + it_should_behave_like 'mass_build platform owner of personal platform' end context 'for owner user' do @@ -192,6 +206,7 @@ describe Platforms::MassBuildsController do end it_should_behave_like 'mass_build platform owner' + it_should_behave_like 'mass_build platform owner of personal platform' end context 'for admin user' do @@ -202,6 +217,7 @@ describe Platforms::MassBuildsController do end it_should_behave_like 'mass_build platform owner' + it_should_behave_like 'mass_build platform owner of personal platform' end context 'for reader user' do diff --git a/spec/controllers/platforms/platforms_controller_spec.rb b/spec/controllers/platforms/platforms_controller_spec.rb index dee03b79c..e4498d871 100644 --- a/spec/controllers/platforms/platforms_controller_spec.rb +++ b/spec/controllers/platforms/platforms_controller_spec.rb @@ -28,6 +28,22 @@ shared_examples_for 'platform user with owner rights' do end end + context 'perform change_visibility action' do + before do + @visibility = @platform.visibility + post :change_visibility, :id => @platform.id + end + + it 'should be able to perform action' do + response.should redirect_to(platform_path(@platform)) + end + + it 'ensures that visibility of platform has been changed' do + @platform.reload + @platform.visibility.should_not == @visibility + end + end + context 'platform user with destroy rights for main platforms only' do it 'should be able to perform destroy action for main platform' do delete :destroy, :id => @platform.id @@ -61,6 +77,22 @@ shared_examples_for 'platform user without owner rights' do end end + context 'perform change_visibility action' do + before do + @visibility = @platform.visibility + post :change_visibility, :id => @platform.id + end + + it 'should not be able to perform action' do + response.should_not be_success + end + + it 'ensures that visibility of platform has not been changed' do + @platform.reload + @platform.visibility.should == @visibility + end + end + context 'platform user without destroy rights' do it 'should not be able to perform destroy action for main platform' do delete :destroy, :id => @platform.id @@ -345,6 +377,22 @@ describe Platforms::PlatformsController do it_should_behave_like 'platform user without global admin rights' end + context 'for member of repository' do + before do + http_login(@user) + repository = FactoryGirl.create(:repository, :platform => @platform) + repository.add_member(@user) + personal_repository = FactoryGirl.create(:repository, :platform => @personal_platform) + personal_repository.add_member(@user) + end + + it_should_behave_like 'platform user with reader rights' + it_should_behave_like 'platform user with reader rights for hidden platform' + it_should_behave_like 'platform user without member rights' + it_should_behave_like 'platform user without owner rights' + it_should_behave_like 'platform user without global admin rights' + end + context 'for simple user' do before do http_login(@user) diff --git a/spec/controllers/platforms/products_controller_spec.rb b/spec/controllers/platforms/products_controller_spec.rb index 5ade7f3ad..61560f955 100644 --- a/spec/controllers/platforms/products_controller_spec.rb +++ b/spec/controllers/platforms/products_controller_spec.rb @@ -4,7 +4,7 @@ shared_examples_for 'admin user' do it 'should be able to create product' do lambda { post :create, @create_params }.should change{ Product.count }.by(1) - response.should redirect_to(platform_product_path( Product.last.platform.id, Product.last )) + response.should redirect_to(platform_product_path( Product.last.platform, Product.last )) end it 'should be able to update product' do @@ -30,7 +30,7 @@ describe Platforms::ProductsController do @product = FactoryGirl.create(:product, :platform => @platform) @project = FactoryGirl.create(:project) - params = {:platform_id => @platform.id, :src_project => @project.name_with_owner} + params = {:platform_id => @platform, :src_project => @project.name_with_owner} @create_params = params.merge({:product => {:name => 'pro', :time_living => 150}}) @update_params = params.merge({:product => {:name => 'pro2'}}) diff --git a/spec/controllers/platforms/repositories_controller_spec.rb b/spec/controllers/platforms/repositories_controller_spec.rb index a2f96d5df..68993464a 100644 --- a/spec/controllers/platforms/repositories_controller_spec.rb +++ b/spec/controllers/platforms/repositories_controller_spec.rb @@ -1,13 +1,5 @@ require 'spec_helper' -shared_examples_for 'not destroy personal repository' do - it 'should not be able to destroy personal repository' do - lambda { delete :destroy, :id => @personal_repository.id, :platform_id => - @personal_repository.platform.id}.should change{ Repository.count }.by(0) - response.should redirect_to(redirect_path) - end -end - shared_examples_for 'user with change projects in repository rights' do it 'should be able to see add_project page' do @@ -29,6 +21,20 @@ shared_examples_for 'user with change projects in repository rights' do end +shared_examples_for 'user with rights of add/remove sync_lock_file to repository' do + it 'should be able to perform sync_lock_file action' do + put :sync_lock_file, :id => @repository.id, :platform_id => @platform.id + response.should redirect_to(edit_platform_repository_path(@platform, @repository)) + end +end + +shared_examples_for 'user without rights of add/remove sync_lock_file to repository' do + it 'should not be able to perform #{action} action' do + put :sync_lock_file, :id => @repository.id, :platform_id => @platform.id + response.should redirect_to(redirect_path) + end +end + shared_examples_for 'user without change projects in repository rights' do it 'should not be able to add project to repository' do get :add_project, :id => @repository.id, :platform_id => @platform.id, :project_id => @project.id @@ -36,6 +42,12 @@ shared_examples_for 'user without change projects in repository rights' do @repository.projects.should_not include(@project) end + it 'should not be able to perform regenerate_metadata action' do + put :regenerate_metadata, :id => @repository.id, :platform_id => @platform.id + response.should redirect_to(redirect_path) + @repository.repository_statuses.should have(:no).items + end + it 'should not be able to remove project from repository' do delete :remove_project, :id => @repository.id, :platform_id => @platform.id, :project_id => @project.id response.should redirect_to(redirect_path) @@ -49,6 +61,18 @@ shared_examples_for 'registered user or guest' do response.should redirect_to(redirect_path) end + it 'should not be able to perform regenerate_metadata action' do + put :regenerate_metadata, :id => @repository.id, :platform_id => @platform.id + response.should redirect_to(redirect_path) + @repository.repository_statuses.should have(:no).items + end + + it 'should not be able to perform regenerate_metadata action of personal repository' do + put :regenerate_metadata, :id => @personal_repository.id, :platform_id => @personal_repository.platform.id + response.should redirect_to(redirect_path) + @personal_repository.repository_statuses.should have(:no).items + end + it 'should not be able to perform create action' do post :create, @create_params lambda { post :create, @create_params }.should change{ Repository.count }.by(0) @@ -89,12 +113,16 @@ shared_examples_for 'registered user or guest' do end it 'should not be able to destroy repository in main platform' do - delete :destroy, :id => @repository.id + delete :destroy, :id => @repository.id, :platform_id => @platform.id response.should redirect_to(redirect_path) lambda { delete :destroy, :id => @repository.id }.should_not change{ Repository.count }.by(-1) end - it_should_behave_like 'not destroy personal repository' + it 'should not be able to destroy personal repository' do + lambda { delete :destroy, :id => @personal_repository.id, :platform_id => @personal_repository.platform.id} + .should change{ Repository.count }.by(0) + response.should redirect_to(redirect_path) + end end shared_examples_for 'registered user' do @@ -118,19 +146,40 @@ end shared_examples_for 'platform admin user' do it_should_behave_like 'registered user' + it_should_behave_like 'user with rights of add/remove sync_lock_file to repository' it 'should be able to perform new action' do get :new, :platform_id => @platform.id response.should render_template(:new) end + it 'should be able to perform regenerate_metadata action' do + put :regenerate_metadata, :id => @repository.id, :platform_id => @platform.id + response.should redirect_to(platform_repository_path(@platform, @repository)) + @repository.repository_statuses.find_by_platform_id(@platform.id). + waiting_for_regeneration?.should be_true + end + + it 'should be able to perform regenerate_metadata action of personal repository' do + put :regenerate_metadata, :id => @personal_repository.id, :platform_id => @personal_repository.platform.id, :build_for_platform_id => @platform.id + response.should redirect_to(platform_repository_path(@personal_repository.platform, @personal_repository)) + @personal_repository.repository_statuses.find_by_platform_id(@platform.id). + waiting_for_regeneration?.should be_true + end + + it 'should not be able to perform regenerate_metadata action of personal repository when build_for_platform does not exist' do + put :regenerate_metadata, :id => @personal_repository.id, :platform_id => @personal_repository.platform.id + response.should render_template(:file => "#{Rails.root}/public/404.html") + @personal_repository.repository_statuses.should have(:no).items + end + it 'should be able to create repository' do lambda { post :create, @create_params }.should change{ Repository.count }.by(1) response.should redirect_to(platform_repository_path(@platform, Repository.last)) end it 'should be able to destroy repository in main platform' do - lambda { delete :destroy, :id => @repository.id }.should change{ Repository.count }.by(-1) + lambda { delete :destroy, :id => @repository.id, :platform_id => @platform.id }.should change{ Repository.count }.by(-1) response.should redirect_to(platform_repositories_path(@repository.platform)) end @@ -162,15 +211,27 @@ shared_examples_for 'platform admin user' do @repository.members.should_not include(@another_user, another_user2) end - it_should_behave_like 'user with change projects in repository rights' - it_should_behave_like 'not destroy personal repository' do - let(:redirect_path) { forbidden_path } + it 'should not be able to destroy personal repository with name "main"' do + # hook for "ActiveRecord::ActiveRecordError: name is marked as readonly" + Repository.where(:id => @personal_repository.id).update_all("name = 'main'") + lambda { delete :destroy, :id => @personal_repository.id, :platform_id => @personal_repository.platform.id} + .should change{ Repository.count }.by(0) + response.should redirect_to(forbidden_path) end + + it 'should be able to destroy personal repository with name not "main"' do + lambda { delete :destroy, :id => @personal_repository.id, :platform_id => @personal_repository.platform.id} + .should change{ Repository.count }.by(-1) + response.should redirect_to(platform_repositories_path(@personal_repository.platform)) + end + + it_should_behave_like 'user with change projects in repository rights' end describe Platforms::RepositoriesController do before(:each) do stub_symlink_methods + stub_redis @platform = FactoryGirl.create(:platform) @repository = FactoryGirl.create(:repository, :platform => @platform) @@ -194,6 +255,7 @@ describe Platforms::RepositoriesController do let(:redirect_path) { new_user_session_path } it_should_behave_like 'registered user or guest' it_should_behave_like 'user without change projects in repository rights' + it_should_behave_like 'user without rights of add/remove sync_lock_file to repository' it "should not be able to perform show action", :anonymous_access => false do get :show, :id => @repository @@ -218,6 +280,7 @@ describe Platforms::RepositoriesController do let(:redirect_path) { forbidden_path } it_should_behave_like 'registered user or guest' it_should_behave_like 'user without change projects in repository rights' + it_should_behave_like 'user without rights of add/remove sync_lock_file to repository' end context 'for admin' do @@ -233,6 +296,10 @@ describe Platforms::RepositoriesController do context 'for platform owner user' do before(:each) do @user = @repository.platform.owner + platform = @personal_repository.platform + platform.owner = @user + # Owner of personal platform can't be changed + platform.save(:validate => false) set_session_for(@user) end @@ -241,7 +308,9 @@ describe Platforms::RepositoriesController do context 'for platform member user' do before(:each) do - @platform.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') + [@repository, @personal_repository].each do |repo| + repo.platform.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') + end end it_should_behave_like 'platform admin user' @@ -249,7 +318,9 @@ describe Platforms::RepositoriesController do context 'for repository member user' do before(:each) do - @repository.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') + [@repository, @personal_repository].each do |repo| + repo.add_member @user + end end it_should_behave_like 'registered user' @@ -257,6 +328,21 @@ describe Platforms::RepositoriesController do let(:redirect_path) { forbidden_path } it_should_behave_like 'registered user or guest' it_should_behave_like 'user with change projects in repository rights' + it_should_behave_like 'user without rights of add/remove sync_lock_file to repository' + + context 'for hidden platform' do + before do + @platform.update_column(:visibility, 'hidden') + @personal_repository.platform.update_column(:visibility, 'hidden') + end + it_should_behave_like 'registered user' + + let(:redirect_path) { forbidden_path } + it_should_behave_like 'registered user or guest' + it_should_behave_like 'user with change projects in repository rights' + it_should_behave_like 'user without rights of add/remove sync_lock_file to repository' + end + end end diff --git a/spec/controllers/platforms/tokens_controller_spec.rb b/spec/controllers/platforms/tokens_controller_spec.rb new file mode 100644 index 000000000..5e27049d9 --- /dev/null +++ b/spec/controllers/platforms/tokens_controller_spec.rb @@ -0,0 +1,127 @@ +require 'spec_helper' + +def create_key_pair(repository, user) + @key_pair = FactoryGirl.create(:key_pair, :repository => repository, :user => user) +end + +shared_examples_for 'token of platform for owner' do + [:index, :new].each do |action| + it "should be able to perform #{action} action" do + get action, :platform_id => @platform + response.should render_template(action) + end + end + + it 'should not be able to perform show action' do + get :show, :platform_id => @platform, :id => @platform_token + response.should render_template(:show) + end + + it 'should be able to perform create action' do + post :create, @create_params + response.should redirect_to(platform_tokens_path(@platform)) + end + + it 'should create key pair into db on create action' do + lambda { post :create, @create_params }.should change{Token.count}.by(1) + end +end + +shared_examples_for 'token of platform for simple user or guest' do + [:index, :new].each do |action| + it "should not be able to perform #{ action } action" do + get action, :platform_id => @platform + response.should redirect_to(redirected_url) + end + end + + it 'should not be able to perform show action' do + get :show, :platform_id => @platform, :id => @platform_token + response.should redirect_to(redirected_url) + end + + it 'should not be able to perform show action' do + post :create, @create_params + response.should redirect_to(redirected_url) + end + + it 'should not change objects count on create success' do + lambda { post :create, @create_params }.should change{ Token.count }.by(0) + end +end + +describe Platforms::TokensController do + before do + stub_symlink_methods + + @platform = FactoryGirl.create(:platform) + @user = FactoryGirl.create(:user) + @platform_token = FactoryGirl.create(:platform_token, :subject => @platform) + @create_params = { + :platform_id => @platform, + :tokens => { + :description => 'description' + } + } + end + + it_should_behave_like 'token of platform for simple user or guest' do + let(:redirected_url) { new_user_session_path } + end + + context 'for global admin' do + before(:each) do + @admin = FactoryGirl.create(:admin) + @user = FactoryGirl.create(:user) + set_session_for(@admin) + end + + it_should_behave_like 'token of platform for owner' + end + + context 'for owner user' do + before(:each) do + @user = FactoryGirl.create(:user) + set_session_for(@user) + + @platform.owner = @user + @platform.save + end + + it_should_behave_like 'token of platform for owner' + end + + context 'for admin user' do + before(:each) do + @user = FactoryGirl.create(:user) + set_session_for(@user) + @platform.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') + end + + it_should_behave_like 'token of platform for owner' + end + + context 'for reader user' do + before do + @user = FactoryGirl.create(:user) + set_session_for(@user) + @platform.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'reader') + end + + it_should_behave_like 'token of platform for simple user or guest' do + let(:redirected_url) { forbidden_url } + end + end + + context 'for simple user' do + before do + @user = FactoryGirl.create(:user) + set_session_for(@user) + end + + it_should_behave_like 'token of platform for simple user or guest' do + let(:redirected_url) { forbidden_url } + end + end + +end diff --git a/spec/controllers/projects/build_lists_controller_spec.rb b/spec/controllers/projects/build_lists_controller_spec.rb index 27af17e06..efc6ef466 100644 --- a/spec/controllers/projects/build_lists_controller_spec.rb +++ b/spec/controllers/projects/build_lists_controller_spec.rb @@ -14,34 +14,6 @@ describe Projects::BuildListsController do end end - shared_examples_for 'show extra_repos_and_builds actions' do - it 'shows data when perform autocomplete_to_extra_repos_and_builds action' do - @build_list.update_column(:container_status, BuildList::BUILD_PUBLISHED) - get :autocomplete_to_extra_repos_and_builds, {:term => @build_list.id, :platform_id => @build_list.save_to_platform_id} - response.body.should_not == '[]' - end - - it 'shows data when perform update_extra_repos_and_builds action' do - @build_list.update_column(:container_status, BuildList::BUILD_PUBLISHED) - get :update_extra_repos_and_builds, {:build_list => {:save_to_repository_id => @build_list.save_to_repository_id, :extra_build_lists => [@build_list.id]}, :extra_repo => ''} - response.body.should_not == ' ' - end - end - - shared_examples_for 'not show extra_repos_and_builds actions' do - it 'no data when perform autocomplete_to_extra_repos_and_builds action' do - @build_list.update_column(:container_status, BuildList::BUILD_PUBLISHED) - get :autocomplete_to_extra_repos_and_builds, {:term => @build_list.id, :platform_id => @build_list.save_to_platform_id} - response.body.should == '[]' - end - - it 'no data when perform update_extra_repos_and_builds action' do - @build_list.update_column(:container_status, BuildList::BUILD_PUBLISHED) - get :update_extra_repos_and_builds, {:build_list => {:save_to_repository_id => @build_list.save_to_repository_id, :extra_build_lists => [@build_list.id]}, :extra_repo => ''} - response.body.should == ' ' - end - end - shared_examples_for 'not show build list' do it 'should not be able to perform show action' do get :show, @show_params @@ -89,7 +61,7 @@ describe Projects::BuildListsController do end end - shared_examples_for 'not create build list' do + shared_examples_for 'not create build list' do |skip_new = false| before { @project.update_attribute(:repositories, @platform.repositories) } @@ -97,13 +69,12 @@ describe Projects::BuildListsController do it 'should not be able to perform new action' do get :new, :owner_name => @project.owner.uname, :project_name => @project.name response.should redirect_to(forbidden_url) - end + end unless skip_new it 'should not be able to perform create action' do post :create, {:owner_name => @project.owner.uname, :project_name => @project.name}.merge(@create_params) response.should redirect_to(forbidden_url) end - end before { stub_symlink_methods } @@ -136,17 +107,12 @@ describe Projects::BuildListsController do response.should redirect_to(new_user_session_path) end - [:autocomplete_to_extra_repos_and_builds, :update_extra_repos_and_builds].each do |action| - it "should not be able to perform #{action} action" do - get action - response.should redirect_to(new_user_session_path) - end - end end context 'for user' do before(:each) do - @build_list = FactoryGirl.create(:build_list_core) + any_instance_of(BuildList, :current_duration => 100) + @build_list = FactoryGirl.create(:build_list) @project = @build_list.project @owner_user = @project.owner @member_user = FactoryGirl.create(:user) @@ -156,19 +122,115 @@ describe Projects::BuildListsController do @user = FactoryGirl.create(:user) set_session_for(@user) @show_params = {:owner_name => @project.owner.uname, :project_name => @project.name, :id => @build_list.id} + @build_list.save_to_repository.update_column(:publish_without_qa, false) + @request.env['HTTP_REFERER'] = build_list_path(@build_list) + end + + context "do reject_publish" do + before(:each) {@build_list.save_to_repository.update_column(:publish_without_qa, true)} + + def do_reject_publish + put :reject_publish, :id => @build_list + end + + context 'if user is project owner' do + before(:each) do + set_session_for(@owner_user) + @build_list.update_column(:status, BuildList::SUCCESS) + @build_list.save_to_platform.update_column(:released, true) + do_reject_publish + end + + context "if it has :success status" do + it 'should return 302 response code' do + response.status.should == 302 + end + + it "should reject publish build list" do + @build_list.reload.status.should == BuildList::REJECTED_PUBLISH + end + end + + context "if it has another status" do + before(:each) do + @build_list.update_column(:status, BuildList::BUILD_ERROR) + do_reject_publish + end + + it "should not change status of build list" do + @build_list.reload.status.should == BuildList::BUILD_ERROR + end + end + end + + context 'if user is not project owner' do + before(:each) do + @build_list.update_column(:status, BuildList::SUCCESS) + @build_list.save_to_platform.update_column(:released, true) + do_reject_publish + end + + it "should redirect to forbidden page" do + response.should redirect_to(forbidden_url) + end + + it "should not change status of build list" do + do_reject_publish + @build_list.reload.status.should == BuildList::SUCCESS + end + end + + context 'if user is project reader' do + before(:each) do + @another_user = FactoryGirl.create(:user) + @build_list.update_column(:status, BuildList::SUCCESS) + @build_list.save_to_repository.update_column(:publish_without_qa, true) + @build_list.project.collaborators.create(:actor_type => 'User', :actor_id => @another_user.id, :role => 'reader') + set_session_for(@another_user) + do_reject_publish + end + + it "should redirect to forbidden page" do + response.should redirect_to(forbidden_url) + end + + it "should not change status of build list" do + do_reject_publish + @build_list.reload.status.should == BuildList::SUCCESS + end + end + + context 'if user is project writer' do + before(:each) do + @writer_user = FactoryGirl.create(:user) + @build_list.update_column(:status, BuildList::SUCCESS) + @build_list.save_to_repository.update_column(:publish_without_qa, true) + @build_list.project.relations.create!(:actor_type => 'User', :actor_id => @writer_user.id, :role => 'writer') + set_session_for(@writer_user) + do_reject_publish + end + + it 'should return 302 response code' do + response.status.should == 302 + end + + it "should reject publish build list" do + @build_list.reload.status.should == BuildList::REJECTED_PUBLISH + end + end end context 'for all build lists' do before(:each) do - @build_list1 = FactoryGirl.create(:build_list_core) + @build_list1 = FactoryGirl.create(:build_list) - @build_list2 = FactoryGirl.create(:build_list_core) + @build_list2 = FactoryGirl.create(:build_list) @build_list2.project.update_column(:visibility, 'hidden') project = FactoryGirl.create(:project_with_commit, :visibility => 'hidden', :owner => @user) - @build_list3 = FactoryGirl.create(:build_list_core_with_attaching_project, :project => project) + @build_list3 = FactoryGirl.create(:build_list_with_attaching_project, :project => project) - @build_list4 = FactoryGirl.create(:build_list_core) + @build_list4 = FactoryGirl.create(:build_list) @build_list4.project.update_column(:visibility, 'hidden') @build_list4.project.relations.create! :role => 'reader', :actor_id => @user.id, :actor_type => 'User' end @@ -190,13 +252,23 @@ describe Projects::BuildListsController do context 'for open project' do it_should_behave_like 'show build list' it_should_behave_like 'not create build list' - it_should_behave_like 'show extra_repos_and_builds actions' context 'if user is project owner' do before(:each) {set_session_for(@owner_user)} it_should_behave_like 'show build list' it_should_behave_like 'create build list' + context 'no ability to read build_for_platform' do + before do + repository = FactoryGirl.create(:repository) + repository.platform.change_visibility + Platform.where(:id => @platform.id).update_all(:platform_type => 'personal') + @create_params[:build_list].merge!({:include_repos => [repository.id]}) + @create_params[:build_for_platforms] = [repository.platform_id] + end + it_should_behave_like 'not create build list', true + end + end context 'if user is project read member' do @@ -214,21 +286,17 @@ describe Projects::BuildListsController do it_should_behave_like 'not show build list' it_should_behave_like 'not create build list' - it_should_behave_like 'not show extra_repos_and_builds actions' context 'if user is project owner' do before(:each) {set_session_for(@owner_user)} it_should_behave_like 'show build list' it_should_behave_like 'create build list' - it_should_behave_like 'show extra_repos_and_builds actions' end context 'if user is project read member' do before(:each) {set_session_for(@member_user)} it_should_behave_like 'show build list' it_should_behave_like 'not create build list' - it_should_behave_like 'show extra_repos_and_builds actions' - end end end @@ -254,15 +322,15 @@ describe Projects::BuildListsController do context 'for all build lists' do before(:each) do - @build_list1 = FactoryGirl.create(:build_list_core) + @build_list1 = FactoryGirl.create(:build_list) - @build_list2 = FactoryGirl.create(:build_list_core) + @build_list2 = FactoryGirl.create(:build_list) @build_list2.project.update_column(:visibility, 'hidden') project = FactoryGirl.create(:project_with_commit, :visibility => 'hidden', :owner => @user) - @build_list3 = FactoryGirl.create(:build_list_core_with_attaching_project, :project => project) + @build_list3 = FactoryGirl.create(:build_list_with_attaching_project, :project => project) - @build_list4 = FactoryGirl.create(:build_list_core) + @build_list4 = FactoryGirl.create(:build_list) @build_list4.project.update_column(:visibility, 'hidden') @build_list4.project.relations.create! :role => 'reader', :actor_id => @user.id, :actor_type => 'User' end @@ -284,7 +352,6 @@ describe Projects::BuildListsController do context 'for open project' do it_should_behave_like 'show build list' it_should_behave_like 'not create build list' - it_should_behave_like 'show extra_repos_and_builds actions' context 'if user is group owner' do before(:each) {set_session_for(@owner_user)} @@ -307,20 +374,17 @@ describe Projects::BuildListsController do it_should_behave_like 'not show build list' it_should_behave_like 'not create build list' - it_should_behave_like 'not show extra_repos_and_builds actions' context 'if user is group owner' do before(:each) {set_session_for(@owner_user)} it_should_behave_like 'show build list' it_should_behave_like 'create build list' - it_should_behave_like 'show extra_repos_and_builds actions' end context 'if user is group read member' do before(:each) {set_session_for(@member_user)} it_should_behave_like 'show build list' it_should_behave_like 'not create build list' - it_should_behave_like 'show extra_repos_and_builds actions' end end @@ -332,16 +396,16 @@ describe Projects::BuildListsController do before(:each) do set_session_for FactoryGirl.create(:admin) - @build_list1 = FactoryGirl.create(:build_list_core) - @build_list2 = FactoryGirl.create(:build_list_core) - @build_list3 = FactoryGirl.create(:build_list_core) - @build_list4 = FactoryGirl.create(:build_list_core, :updated_at => (Time.now - 1.day), + @build_list1 = FactoryGirl.create(:build_list) + @build_list2 = FactoryGirl.create(:build_list) + @build_list3 = FactoryGirl.create(:build_list) + @build_list4 = FactoryGirl.create(:build_list, :updated_at => (Time.now - 1.day), :project => @build_list3.project, :save_to_platform => @build_list3.save_to_platform, :arch => @build_list3.arch) end - it 'should filter by bs_id' do - get :index, :filter => {:bs_id => @build_list1.bs_id, :project_name => 'fdsfdf', :any_other_field => 'do not matter'} + it 'should filter by id' do + get :index, :filter => {:id => @build_list1.id, :project_name => 'fdsfdf', :any_other_field => 'do not matter'}, :format => :json assigns[:build_lists].should include(@build_list1) assigns[:build_lists].should_not include(@build_list2) assigns[:build_lists].should_not include(@build_list3) @@ -349,7 +413,7 @@ describe Projects::BuildListsController do it 'should filter by project_name' do # Project.where(:id => build_list2.project.id).update_all(:name => 'project_name') - get :index, :filter => {:project_name => @build_list2.project.name, :ownership => 'everything'} + get :index, :filter => {:project_name => @build_list2.project.name, :ownership => 'everything'}, :format => :json assigns[:build_lists].should_not include(@build_list1) assigns[:build_lists].should include(@build_list2) assigns[:build_lists].should_not include(@build_list3) @@ -357,7 +421,7 @@ describe Projects::BuildListsController do it 'should filter by project_name and update_date' do get :index, :filter => {:project_name => @build_list3.project.name, :ownership => 'everything', - "updated_at_start" => @build_list3.updated_at.strftime('%d/%m/%Y')} + "updated_at_start" => @build_list3.updated_at.strftime('%d/%m/%Y')}, :format => :json assigns[:build_lists].should_not include(@build_list1) assigns[:build_lists].should_not include(@build_list2) assigns[:build_lists].should include(@build_list3) diff --git a/spec/controllers/projects/git/git_trees_controller_spec.rb b/spec/controllers/projects/git/git_trees_controller_spec.rb index 48684360d..ddf639f70 100644 --- a/spec/controllers/projects/git/git_trees_controller_spec.rb +++ b/spec/controllers/projects/git/git_trees_controller_spec.rb @@ -6,7 +6,6 @@ describe Projects::Git::TreesController do stub_symlink_methods @project = FactoryGirl.create(:project) - @another_user = FactoryGirl.create(:user) @params = { :owner_name => @project.owner.uname, :project_name => @project.name, :treeish => "#{@project.name}-master"} @@ -36,6 +35,22 @@ describe Projects::Git::TreesController do get :archive, @params.merge(:format => 'tar.gz') response.code.should == '401' end + + it 'should not be able to perform destroy action' do + delete :destroy, @params.merge(:treeish => 'master') + response.should_not be_success + end + + it 'should not be able to perform restore_branch action' do + put :restore_branch, @params.merge(:treeish => 'master') + response.should_not be_success + end + + it 'should not be able to perform create action' do + post :create, @params.merge(:treeish => '', :from_ref => 'master', :new_ref => 'master-1') + response.should_not be_success + end + end context 'for other user' do @@ -59,6 +74,21 @@ describe Projects::Git::TreesController do response.should be_success end + it 'should not be able to perform destroy action' do + delete :destroy, @params.merge(:treeish => 'master') + response.should_not be_success + end + + it 'should not be able to perform restore_branch action' do + put :restore_branch, @params.merge(:treeish => 'master') + response.should_not be_success + end + + it 'should not be able to perform create action' do + post :create, @params.merge(:treeish => '', :from_ref => 'master', :new_ref => 'master-1') + response.should_not be_success + end + [:tags, :branches].each do |action| it "should be able to perform #{action} action" do get action, @params.merge(:treeish => 'master') @@ -67,5 +97,33 @@ describe Projects::Git::TreesController do end end + context 'for writer user' do + before(:each) do + user = FactoryGirl.create(:user) + @project.relations.create!(:actor_type => 'User', :actor_id => user.id, :role => 'writer') + set_session_for user + end + + it 'should be able to perform destroy action' do + delete :destroy, @params.merge(:treeish => 'conflicts') + response.should be_success + end + + it 'should not be able to perform destroy action for master branch' do + delete :destroy, @params.merge(:treeish => 'master') + response.should_not be_success + end + + it 'should be able to perform restore_branch action' do + put :restore_branch, @params.merge(:treeish => 'master-1', :sha => 'master') + response.should be_success + end + + it 'should be able to perform create action' do + post :create, @params.merge(:treeish => '', :from_ref => 'master', :new_ref => 'master-1') + response.should be_success + end + end + after(:all) {clean_projects_dir} end diff --git a/spec/controllers/projects/hooks_controller_spec.rb b/spec/controllers/projects/hooks_controller_spec.rb new file mode 100644 index 000000000..36e299ef5 --- /dev/null +++ b/spec/controllers/projects/hooks_controller_spec.rb @@ -0,0 +1,205 @@ +# -*- encoding : utf-8 -*- +require 'spec_helper' + +shared_examples_for 'hooks user with project admin rights' do + it 'should be able to perform index action' do + get :index, {:owner_name => @project.owner.uname, :project_name => @project.name} + response.should be_success + end + + it 'should be able to perform new action' do + get :new, {:owner_name => @project.owner.uname, :project_name => @project.name, :hook => {:name => 'web'}} + response.should be_success + end + + it 'should be able to perform edit action' do + get :new, {:owner_name => @project.owner.uname, :project_name => @project.name, :id => @hook.id} + response.should be_success + end + + it 'should be able to perform update action' do + put :update, {:owner_name => @project.owner.uname, :project_name => @project.name, :id => @hook.id}.merge(@update_params) + response.should redirect_to(project_hooks_path(@project, :name => 'web')) + end + + it 'should be able to perform create action' do + post :create, {:owner_name => @project.owner.uname, :project_name => @project.name}.merge(@create_params) + response.should redirect_to(project_hooks_path(@project, :name => 'web')) + end +end + +shared_examples_for 'hooks user without project admin rights' do + it 'should not be able to perform index action' do + get :index, {:owner_name => @project.owner.uname, :project_name => @project.name} + response.should redirect_to(forbidden_path) + end + + it 'should not be able to perform new action' do + get :new, {:owner_name => @project.owner.uname, :project_name => @project.name, :hook => {:name => 'web'}} + response.should redirect_to(forbidden_path) + end + + it 'should not be able to perform edit action' do + get :new, {:owner_name => @project.owner.uname, :project_name => @project.name, :id => @hook.id} + response.should redirect_to(forbidden_path) + end + + it 'should not be able to perform update action' do + put :update, {:owner_name => @project.owner.uname, :project_name => @project.name, :id => @hook.id}.merge(@update_params) + response.should redirect_to(forbidden_path) + end + + it 'should not be able to perform create action' do + post :create, {:owner_name => @project.owner.uname, :project_name => @project.name}.merge(@create_params) + response.should redirect_to(forbidden_path) + end +end + +describe Projects::HooksController do + + before(:each) do + stub_symlink_methods + + @project = FactoryGirl.create(:project) + @hook = FactoryGirl.create(:hook, :project => @project) + + @create_params = {:hook => {:name => 'web', :data => {:url => 'create'}}} + @update_params = {:hook => {:data => {:url => 'update'}}} + + @user = FactoryGirl.create(:user) + set_session_for(@user) + end + + context 'registered user' do + it_should_behave_like 'hooks user without project admin rights' + end # context 'registered user' + + context 'for project members' do + + context 'for global admin' do + before do + @user.role = "admin" + @user.save + end + + it_should_behave_like 'hooks user with project admin rights' + end + + context 'for owner user' do + before do + @user = @project.owner + set_session_for(@user) # owner should be user + end + it_should_behave_like 'hooks user with project admin rights' + end + + context 'for reader user' do + before do + @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'reader') + end + it_should_behave_like 'hooks user without project admin rights' + end + + context 'for writer user' do + before do + @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'writer') + end + it_should_behave_like 'hooks user without project admin rights' + end + + end # context 'for project members' + + context 'for group' do + before do + @group = FactoryGirl.create(:group) + end + + context 'group is owner of the project' do + before do + @project = FactoryGirl.create(:project, :owner => @group) + @hook = FactoryGirl.create(:hook, :project => @project) + end + + context 'group member user with reader role' do + before do + @group.actors.create(:actor_id => @user.id, :actor_type => 'User', :role => 'reader') + end + + it_should_behave_like 'hooks user without project admin rights' + + context 'user should has best role' do + before do + @project.relations.create :actor_id => @user.id, :actor_type => @user.class.to_s, :role => 'admin' + end + it_should_behave_like 'hooks user with project admin rights' + end + end + + context 'group member user with admin role' do + before do + @group.actors.create(:actor_id => @user.id, :actor_type => 'User', :role => 'admin') + end + + it_should_behave_like 'hooks user with project admin rights' + end + end + + context 'group is member of the project' do + context 'with admin rights' do + before do + @project.relations.create :actor_id => @group.id, :actor_type => @group.class.to_s, :role => 'admin' + end + + context 'group member user with reader role' do + before do + @group.actors.create(:actor_id => @user.id, :actor_type => 'User', :role => 'reader') + end + + it_should_behave_like 'hooks user with project admin rights' + + context 'user should has best role' do + before do + @project.relations.create :actor_id => @user.id, :actor_type => @user.class.to_s, :role => 'reader' + end + it_should_behave_like 'hooks user with project admin rights' + end + end + + context 'group member user with admin role' do + before do + @group.actors.create(:actor_id => @user.id, :actor_type => 'User', :role => 'admin') + end + + it_should_behave_like 'hooks user with project admin rights' + end + end + + context 'with reader rights' do + before do + @project.relations.create :actor_id => @group.id, :actor_type => @group.class.to_s, :role => 'reader' + end + + context 'group member user with reader role' do + before do + @group.actors.create(:actor_id => @user.id, :actor_type => 'User', :role => 'reader') + end + it_should_behave_like 'hooks user without project admin rights' + + context 'user should has best role' do + before do + @project.relations.create :actor_id => @user.id, :actor_type => @user.class.to_s, :role => 'admin' + end + it_should_behave_like 'hooks user with project admin rights' + end + end + + context 'group member user with admin role' do + before do + @group.actors.create(:actor_id => @user.id, :actor_type => 'User', :role => 'admin') + end + it_should_behave_like 'hooks user without project admin rights' + end + end + end + end +end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 8785910f3..a541faecd 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -1,13 +1,14 @@ require 'spec_helper' shared_context "issues controller" do - before(:each) do + before do stub_symlink_methods - @project = FactoryGirl.create(:project) + @project = FactoryGirl.create(:project_with_commit) @issue_user = FactoryGirl.create(:user) @issue = FactoryGirl.create(:issue, :project_id => @project.id, :assignee_id => @issue_user.id) + @label = FactoryGirl.create(:label, :project_id => @project.id) @project_with_turned_off_issues = FactoryGirl.create(:project, :has_issues => false) @turned_of_issue = FactoryGirl.create(:issue, :project_id => @project_with_turned_off_issues.id, :assignee_id => @issue_user.id) @@ -19,10 +20,10 @@ shared_context "issues controller" do :owner_name => @project.owner.uname, :project_name => @project.name, :issue => { :title => "issue1", - :body => "issue body" - }, - :assignee_id => @issue_user.id, - :assignee_uname => @issue_user.uname + :body => "issue body", + :labelings_attributes => { @label.id => {:label_id => @label.id}}, + :assignee_id => @issue_user.id + } } @update_params = { @@ -32,6 +33,11 @@ shared_context "issues controller" do } } + @pull = @project.pull_requests.new :issue_attributes => {:title => 'test', :body => 'testing'} + @pull.issue.user, @pull.issue.project = @project.owner, @pull.to_project + @pull.to_ref = 'master' + @pull.from_project, @pull.from_ref = @project, 'non_conflicts' + @pull.save end end @@ -55,9 +61,7 @@ shared_examples_for 'issue user with project reader rights' do get :index, :owner_name => @project.owner.uname, :project_name => @project.name response.should render_template(:index) end -end -shared_examples_for 'issue user with project writer rights' do it 'should be able to perform create action' do post :create, @create_params response.should redirect_to(project_issues_path(@project)) @@ -68,6 +72,30 @@ shared_examples_for 'issue user with project writer rights' do end end +shared_examples_for 'issue user with project writer rights' do + it 'should be able to perform index action on hidden project' do + @project.update_attributes(:visibility => 'hidden') + get :index, :owner_name => @project.owner.uname, :project_name => @project.name + response.should render_template(:index) + end + + it 'should create issue object into db' do + lambda{ post :create, @create_params }.should change{ Issue.count }.by(1) + end + + context 'perform create action' do + before { post :create, @create_params } + + it 'user should be assigned to issue' do + @project.issues.last.assignee_id.should_not be_nil + end + + it 'label should be attached to issue' do + @project.issues.last.labels.should have(1).item + end + end +end + shared_examples_for 'user with issue update rights' do it 'should be able to perform update action' do put :update, {:id => @issue.serial_id}.merge(@update_params) @@ -105,12 +133,12 @@ end shared_examples_for 'project with issues turned off' do it 'should not be able to perform index action' do - get :index, :project_id => @project_with_turned_off_issues.id + get :index, :owner_name => @project_with_turned_off_issues.owner.uname, :project_name => @project_with_turned_off_issues.name response.should redirect_to(forbidden_path) end it 'should not be able to perform show action' do - get :show, :project_id => @project_with_turned_off_issues.id, :id => @turned_of_issue.serial_id + get :show, :owner_name => @project_with_turned_off_issues.owner.uname, :project_name => @project_with_turned_off_issues.name, :id => @turned_of_issue.serial_id response.should redirect_to(forbidden_path) end end @@ -166,11 +194,22 @@ describe Projects::IssuesController do it_should_behave_like 'issue user with project guest rights' it_should_behave_like 'issue user with project reader rights' - it_should_behave_like 'issue user with project writer rights' it_should_behave_like 'user without issue update rights' it_should_behave_like 'project with issues turned off' it_should_behave_like 'user without issue destroy rights' + context 'perform create action' do + before { post :create, @create_params } + + it 'user should not be assigned to issue' do + @project.issues.last.assignee_id.should be_nil + end + + it 'label should not be attached to issue' do + @project.issues.last.labels.should have(:no).items + end + end + # it 'should not be able to perform create action on project' do # post :create, @create_params # response.should redirect_to(forbidden_path) @@ -179,6 +218,16 @@ describe Projects::IssuesController do # it 'should not create issue object into db' do # lambda{ post :create, @create_params }.should change{ Issue.count }.by(0) # end + + it 'should return 404' do + get :show, :owner_name => @project.owner.uname, :project_name => @project.name, :id => 999999 + render_template(:file => "#{Rails.root}/public/404.html") + end + + it 'should redirect to pull request page' do + get :show, :owner_name => @project.owner.uname, :project_name => @project.name, :id => @pull.serial_id + response.should redirect_to(project_pull_request_path(@project, @pull)) + end end context 'for project writer user' do diff --git a/spec/controllers/projects/projects_controller_spec.rb b/spec/controllers/projects/projects_controller_spec.rb index 8fa430e6c..b1a5868e5 100644 --- a/spec/controllers/projects/projects_controller_spec.rb +++ b/spec/controllers/projects/projects_controller_spec.rb @@ -20,11 +20,10 @@ shared_examples_for 'projects user with reader rights' do :group => group.id}.should change{ Project.count }.by(1) end - # it 'should be able to view project' do - # get :show, :owner_name => @project.owner.uname, :project_name => @project.name - # assigns(:project).should eq @project - # end - + it 'should be able to fork project with different name' do + post :fork, :owner_name => @project.owner.uname, :project_name => @project.name, :fork_name => 'another_name' + response.should redirect_to(project_path(Project.where(:name => 'another_name').last)) + end end shared_examples_for 'projects user with project admin rights' do @@ -60,6 +59,32 @@ shared_examples_for 'projects user without project admin rights' do @project.reload.has_issues.should == has_issues response.should redirect_to(forbidden_path) end + + it 'writer group should be able to fork project to their group' do + group = FactoryGirl.create(:group) + group.actors.create(:actor_type => 'User', :actor_id => @user.id, :role => 'writer') + lambda {post :fork, :owner_name => @project.owner.uname, :project_name => @project.name, + :group => group.id}.should change{ Project.count }.by(1) + end + + it 'reader group should not be able to fork project to their group' do + group = FactoryGirl.create(:group) + group.actors.create(:actor_type => 'User', :actor_id => @user.id, :role => 'reader') + lambda {post :fork, :owner_name => @project.owner.uname, :project_name => @project.name, + :group => group.id}.should change{ Project.count }.by(0) + end + + it 'writer group should be able to create project to their group' do + group = FactoryGirl.create(:group) + group.actors.create(:actor_type => 'User', :actor_id => @user.id, :role => 'writer') + lambda {post :create, @create_params.merge(:who_owns => 'group', :owner_id => group.id)}.should change{ Project.count }.by(1) + end + + it 'reader group should not be able to create project to their group' do + group = FactoryGirl.create(:group) + group.actors.create(:actor_type => 'User', :actor_id => @user.id, :role => 'reader') + lambda {post :create, @create_params.merge(:who_owns => 'group', :owner_id => group.id)}.should change{ Project.count }.by(0) + end end describe Projects::ProjectsController do @@ -76,7 +101,7 @@ describe Projects::ProjectsController do set_session_for(@user) end - context 'for system users' do + context 'for users' do context 'guest' do @@ -137,11 +162,10 @@ describe Projects::ProjectsController do group = FactoryGirl.create(:group, :owner => @user) lambda { post :create, @create_params.merge({:who_owns => 'group', :owner_id => group.id})}.should change{ Project.count }.by(1) end - end end # context 'registered user' - end # context 'for system users' + end # context 'for users' context 'for project members' do diff --git a/spec/controllers/projects/pull_requests_controller_spec.rb b/spec/controllers/projects/pull_requests_controller_spec.rb index dfe2cea7f..526a6c33f 100644 --- a/spec/controllers/projects/pull_requests_controller_spec.rb +++ b/spec/controllers/projects/pull_requests_controller_spec.rb @@ -30,6 +30,8 @@ shared_context "pull request controller" do @user = FactoryGirl.create(:user) set_session_for(@user) + + @issue = FactoryGirl.create(:issue, :project => @project) end end @@ -93,12 +95,13 @@ end shared_examples_for 'user with pull request update rights' do it 'should be able to perform update action' do put :update, @update_params - response.should redirect_to(project_pull_request_path(@pull.to_project, @pull)) + response.should be_success end it 'should be able to perform merge action' do + @pull.check put :merge, @update_params - response.should redirect_to(project_pull_request_path(@pull.to_project, @pull)) + response.should be_success end let(:pull) { @project.pull_requests.find(@pull) } @@ -134,11 +137,6 @@ shared_examples_for 'user without pull request update rights' do response.should redirect_to(controller.current_user ? forbidden_path : new_user_session_path) end - it 'should not be able to perform merge action' do - put :merge, @update_params - response.should redirect_to(controller.current_user ? forbidden_path : new_user_session_path) - end - let(:pull) { @project.pull_requests.find(@pull) } it 'should not update pull request status' do put :update, @update_params @@ -153,12 +151,19 @@ shared_examples_for 'user without pull request update rights' do put :update, @wrong_update_params pull.issue.body.should_not =='updating' end + + it 'should be able to perform merge action' do + @pull.check + put :merge, @update_params + response.should_not be_success + end + end shared_examples_for 'pull request when project with issues turned off' do before { @project.update_attributes(:has_issues => false) } it 'should be able to perform index action' do - get :index, :project_id => @project.id + get :index, :owner_name => @project.owner.uname, :project_name => @project.name response.should render_template(:index) end @@ -216,6 +221,16 @@ describe Projects::PullRequestsController do it_should_behave_like 'pull request user with project reader rights' it_should_behave_like 'user without pull request update rights' it_should_behave_like 'pull request when project with issues turned off' + + it 'should return 404' do + get :show, :owner_name => @project.owner.uname, :project_name => @project.name, :id => 999999 + render_template(:file => "#{Rails.root}/public/404.html") + end + + it 'should redirect to issue page' do + get :show, :owner_name => @project.owner.uname, :project_name => @project.name, :id => @issue.serial_id + response.should redirect_to(project_issue_path(@project, @issue)) + end end context 'for project writer user' do @@ -281,4 +296,40 @@ describe Projects::PullRequestsController do it_should_behave_like 'user without pull request update rights' end + + context 'send email messages' do + before(:each) do + @project_reader = FactoryGirl.create :user + @project.relations.create!(:actor_type => 'User', :actor_id => @project_reader.id, :role => 'reader') + @project_admin = FactoryGirl.create :user + @project.relations.create!(:actor_type => 'User', :actor_id => @project_admin.id, :role => 'admin') + @project_writer = FactoryGirl.create :user + @project.relations.create!(:actor_type => 'User', :actor_id => @project_writer.id, :role => 'writer') + + set_session_for(@project_writer) + ActionMailer::Base.deliveries = [] + end + + it 'should send two email messages to project admins' do + post :create, @create_params + @project.pull_requests.last.issue.send(:new_issue_notifications) + @project.pull_requests.last.issue.send(:send_assign_notifications) + ActionMailer::Base.deliveries.count.should == 2 + end + + it 'should send two email messages to admins and one to assignee' do + post :create, @create_params.deep_merge(:issue => {:assignee_id => @project_reader.id}) + @project.pull_requests.last.issue.send(:new_issue_notifications) + @project.pull_requests.last.issue.send(:send_assign_notifications) + ActionMailer::Base.deliveries.count.should == 3 + end + + it 'should not duplicate email message' do + post :create, @create_params.deep_merge(:issue => {:assignee_id => @project_admin.id}) + @project.pull_requests.last.issue.send(:new_issue_notifications) + @project.pull_requests.last.issue.send(:send_assign_notifications) + ActionMailer::Base.deliveries.count.should == 2 # send only to admins + ActionMailer::Base.deliveries.first.to != ActionMailer::Base.deliveries.last.to + end + end end diff --git a/spec/factories/build_lists.rb b/spec/factories/build_lists.rb index 9f706d461..7ff38250d 100644 --- a/spec/factories/build_lists.rb +++ b/spec/factories/build_lists.rb @@ -21,15 +21,7 @@ FactoryGirl.define do before(:create) { |bl| attach_project_to_build_list bl } end - factory :build_list_core, :parent => :build_list do - bs_id { FactoryGirl.generate(:integer) } - end - - factory :build_list_core_with_attaching_project, :parent => :build_list_core do - before(:create) { |bl| attach_project_to_build_list bl } - end - - factory :build_list_by_group_project, :parent => :build_list_core do + factory :build_list_by_group_project, :parent => :build_list do project { |bl| pr = FactoryGirl.create(:group_project_with_commit) bl.save_to_platform.repositories.first.projects << pr diff --git a/spec/factories/comments.rb b/spec/factories/comments.rb index 5de44fa25..ee85deacc 100644 --- a/spec/factories/comments.rb +++ b/spec/factories/comments.rb @@ -3,5 +3,7 @@ FactoryGirl.define do body { FactoryGirl.generate(:string) } association :user, :factory => :user association :commentable, :factory => :issue + project { |c| c.commentable.project } + after(:create) { |c| c.send(:new_comment_notifications) } end end diff --git a/spec/factories/hook.rb b/spec/factories/hook.rb new file mode 100644 index 000000000..d7fa39637 --- /dev/null +++ b/spec/factories/hook.rb @@ -0,0 +1,8 @@ +# -*- encoding : utf-8 -*- +FactoryGirl.define do + factory :hook do + name 'web' + association :project, :factory => :project + data { |hook| hook.data = {:url => 'url'} } + end +end diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb index a0b075b09..66340557c 100644 --- a/spec/factories/issues.rb +++ b/spec/factories/issues.rb @@ -6,5 +6,9 @@ FactoryGirl.define do association :user, :factory => :user association :assignee, :factory => :user status "open" + # Hooks for #after_commit + after(:create) { |i| i.send(:new_issue_notifications) } + after(:create) { |i| i.send(:send_assign_notifications) } + after(:create) { |i| i.send(:send_hooks) } end end diff --git a/spec/factories/label.rb b/spec/factories/label.rb new file mode 100644 index 000000000..b985a2d98 --- /dev/null +++ b/spec/factories/label.rb @@ -0,0 +1,8 @@ +# -*- encoding : utf-8 -*- +FactoryGirl.define do + factory :label do + name { FactoryGirl.generate(:string) } + color 'FFF' + association :project, :factory => :project + end +end \ No newline at end of file diff --git a/spec/factories/labeling.rb b/spec/factories/labeling.rb new file mode 100644 index 000000000..51d1f6d70 --- /dev/null +++ b/spec/factories/labeling.rb @@ -0,0 +1,7 @@ +# -*- encoding : utf-8 -*- +FactoryGirl.define do + factory :labeling do + association :project, :factory => :project + association :label, :factory => :label + end +end \ No newline at end of file diff --git a/spec/factories/mass_build.rb b/spec/factories/mass_build.rb index b73409fe0..dc6a08972 100644 --- a/spec/factories/mass_build.rb +++ b/spec/factories/mass_build.rb @@ -1,10 +1,9 @@ FactoryGirl.define do factory :mass_build do - association :platform - #name FactoryGirl.generate(:name) + association :save_to_platform, :factory => :platform association :user projects_list "first" - arches { [ Arch.first.id ] } + arches { [ Arch.find_or_create_by_name('x86_64').id ] } auto_publish true stop_build false end diff --git a/spec/factories/project_statistic.rb b/spec/factories/project_statistic.rb new file mode 100644 index 000000000..ec381ef09 --- /dev/null +++ b/spec/factories/project_statistic.rb @@ -0,0 +1,7 @@ +# -*- encoding : utf-8 -*- +FactoryGirl.define do + factory :project_statistic do + association :project, :factory => :project + association :arch, :factory => :arch + end +end diff --git a/spec/factories/token.rb b/spec/factories/token.rb new file mode 100644 index 000000000..cee8561b5 --- /dev/null +++ b/spec/factories/token.rb @@ -0,0 +1,10 @@ +# -*- encoding : utf-8 -*- +FactoryGirl.define do + factory :token do + association :creator, :factory => :user + end + + factory :platform_token, :parent => :token do + association :subject, :factory => :platform + end +end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 2fe9899a1..4d06ab4f3 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -6,6 +6,7 @@ FactoryGirl.define do password '123456' password_confirmation {|u| u.password} confirmed_at { Time.now.utc } + after(:create) { |u| u.send(:new_user_notification) } end factory :admin, :parent => :user do diff --git a/spec/helpers/activity_feeds_helper_spec.rb b/spec/helpers/activity_feeds_helper_spec.rb deleted file mode 100644 index 623c526ec..000000000 --- a/spec/helpers/activity_feeds_helper_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -# Specs in this file have access to a helper object that includes -# the ActivityFeedsHelper. For example: -# -# describe ActivityFeedsHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# helper.concat_strings("this","that").should == "this that" -# end -# end -# end -describe ActivityFeedsHelper do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/integration/api_defender_spec.rb b/spec/integration/api_defender_spec.rb index c369942af..ecc8e71ae 100644 --- a/spec/integration/api_defender_spec.rb +++ b/spec/integration/api_defender_spec.rb @@ -119,4 +119,18 @@ describe ApiDefender do response.status.should == 200 end end + + context 'for allowed addresses' do + let(:remote_addr) { APP_CONFIG['allowed_addresses'].first } + it 'should not return the limit usage for allowed address' do + get "/api/v1/users/#{@user.id}.json", {}, {'REMOTE_ADDR' => remote_addr } + response.headers['X-RateLimit-Limit'].should_not == @rate_limit.to_s + end + + it 'should not forbidden allowed address' do + (@rate_limit+1).times { get "/api/v1/users/#{@user.id}.json", {}, {'REMOTE_ADDR' => remote_addr } } + response.status.should == 200 + end + end + end diff --git a/spec/lib/abf-worker/build_lists_publish_task_manager_spec.rb b/spec/lib/abf-worker/build_lists_publish_task_manager_spec.rb index dc95cea49..4a64ea82c 100644 --- a/spec/lib/abf-worker/build_lists_publish_task_manager_spec.rb +++ b/spec/lib/abf-worker/build_lists_publish_task_manager_spec.rb @@ -7,193 +7,172 @@ describe AbfWorker::BuildListsPublishTaskManager do end before do - init_test_root + stub_redis stub_symlink_methods - FactoryGirl.create(:build_list_core, :new_core => true) + FactoryGirl.create(:build_list) end subject { AbfWorker::BuildListsPublishTaskManager } - let(:build_list) { FactoryGirl.create(:build_list_core, :new_core => true) } + let(:build_list) { FactoryGirl.create(:build_list) } - describe 'when no items for publishing' do - before do - stub_redis - subject.new.run - end + context 'when no items for publishing' do + before { subject.new.run } - %w(RESIGN_REPOSITORIES - PROJECTS_FOR_CLEANUP + %w(PROJECTS_FOR_CLEANUP LOCKED_PROJECTS_FOR_CLEANUP - LOCKED_REPOSITORIES - LOCKED_REP_AND_PLATFORMS LOCKED_BUILD_LISTS).each do |kind| - it "ensure that no '#{kind.downcase.gsub('_', ' ')}'" do + it "ensures that no '#{kind.downcase.gsub('_', ' ')}'" do @redis_instance.lrange(subject.const_get(kind), 0, -1).should be_empty end end %w(publish_worker_default publish_worker).each do |kind| - it "ensure that no tasks in '#{kind}' queue" do + it "ensures that no tasks in '#{kind}' queue" do @redis_instance.lrange(kind, 0, -1).should be_empty end end end - describe 'when one build_list for publishing' do + context 'when one build_list for publishing' do before do - stub_redis build_list.update_column(:status, BuildList::BUILD_PUBLISH) 2.times{ subject.new.run } end - %w(RESIGN_REPOSITORIES - PROJECTS_FOR_CLEANUP - LOCKED_PROJECTS_FOR_CLEANUP - LOCKED_REPOSITORIES).each do |kind| - + %w(PROJECTS_FOR_CLEANUP LOCKED_PROJECTS_FOR_CLEANUP).each do |kind| it "ensure that no '#{kind.downcase.gsub('_', ' ')}'" do @redis_instance.lrange(subject.const_get(kind), 0, -1).should be_empty end end - it "ensure that 'locked rep and platforms' has only one item" do - queue = @redis_instance.lrange(subject::LOCKED_REP_AND_PLATFORMS, 0, -1) - queue.should have(1).item - queue.should include("#{build_list.save_to_repository_id}-#{build_list.build_for_platform_id}") + it "ensures that repository_status has status publish" do + build_list.save_to_repository.repository_statuses. + find_by_platform_id(build_list.build_for_platform_id).publish?. + should be_true end - it "ensure that 'locked build lists' has only one item" do + it "ensures that 'locked build lists' has only one item" do queue = @redis_instance.lrange(subject::LOCKED_BUILD_LISTS, 0, -1) queue.should have(1).item queue.should include(build_list.id.to_s) end - it "ensure that new task for publishing has been created" do + it "ensures that new task for publishing has been created" do @redis_instance.lrange('queue:publish_worker_default', 0, -1).should have(1).item end end - describe 'grouping build lists for publishing into same repository' do - let(:build_list2) { FactoryGirl.create(:build_list_core, + context 'grouping build lists for publishing into same repository' do + let(:build_list2) { FactoryGirl.create(:build_list, :new_core => true, :save_to_platform => build_list.save_to_platform, :save_to_repository => build_list.save_to_repository, :build_for_platform => build_list.build_for_platform ) } before do - stub_redis build_list.update_column(:status, BuildList::BUILD_PUBLISH) build_list2.update_column(:status, BuildList::BUILD_PUBLISH) 2.times{ subject.new.run } end - %w(RESIGN_REPOSITORIES - PROJECTS_FOR_CLEANUP - LOCKED_PROJECTS_FOR_CLEANUP - LOCKED_REPOSITORIES).each do |kind| - - it "ensure that no '#{kind.downcase.gsub('_', ' ')}'" do + %w(PROJECTS_FOR_CLEANUP LOCKED_PROJECTS_FOR_CLEANUP).each do |kind| + it "ensures that no '#{kind.downcase.gsub('_', ' ')}'" do @redis_instance.lrange(subject.const_get(kind), 0, -1).should be_empty end end - it "ensure that 'locked rep and platforms' has only one item" do - queue = @redis_instance.lrange(subject::LOCKED_REP_AND_PLATFORMS, 0, -1) - queue.should have(1).item - queue.should include("#{build_list.save_to_repository_id}-#{build_list.build_for_platform_id}") + it "ensures that only one repository_status has status publish" do + RepositoryStatus.where(:status => RepositoryStatus::PUBLISH).should have(1).item end - it "ensure that 'locked build lists' has 2 items" do + it "ensures that 'locked build lists' has 2 items" do queue = @redis_instance.lrange(subject::LOCKED_BUILD_LISTS, 0, -1) queue.should have(2).item queue.should include(build_list.id.to_s, build_list2.id.to_s) end - it "ensure that new task for publishing has been created" do + it "ensures that new task for publishing has been created" do @redis_instance.lrange('queue:publish_worker_default', 0, -1).should have(1).item end end - describe 'creates not more than 4 tasks for publishing' do + context 'creates not more than 4 tasks for publishing' do before do - stub_redis build_list.update_column(:status, BuildList::BUILD_PUBLISH) 4.times { - bl = FactoryGirl.create(:build_list_core, :new_core => true) + bl = FactoryGirl.create(:build_list, :new_core => true) bl.update_column(:status, BuildList::BUILD_PUBLISH) } 2.times{ subject.new.run } end - it "ensure that 'locked rep and platforms' has 4 items" do - @redis_instance.lrange(subject::LOCKED_REP_AND_PLATFORMS, 0, -1).should have(4).items + it "ensures that 4 repository_statuses have status publish" do + RepositoryStatus.where(:status => RepositoryStatus::PUBLISH).should have(4).items end - it "ensure that 'locked build lists' has 4 items" do + it "ensures that 'locked build lists' has 4 items" do @redis_instance.lrange(subject::LOCKED_BUILD_LISTS, 0, -1).should have(4).items end - it "ensure that new tasks for publishing has been created" do + it "ensures that new tasks for publishing has been created" do @redis_instance.lrange('queue:publish_worker_default', 0, -1).should have(4).items end end - describe 'creates task for removing project from repository' do + context 'creates task for removing project from repository' do before do - stub_redis build_list.update_column(:status, BuildList::BUILD_PUBLISHED) FactoryGirl.create(:build_list_package, :build_list => build_list) ProjectToRepository.where(:project_id => build_list.project_id, :repository_id => build_list.save_to_repository_id).destroy_all 2.times{ subject.new.run } end - %w(RESIGN_REPOSITORIES - PROJECTS_FOR_CLEANUP - LOCKED_REPOSITORIES - LOCKED_BUILD_LISTS).each do |kind| - - it "ensure that no '#{kind.downcase.gsub('_', ' ')}'" do + %w(LOCKED_BUILD_LISTS).each do |kind| + it "ensures that no '#{kind.downcase.gsub('_', ' ')}'" do @redis_instance.lrange(subject.const_get(kind), 0, -1).should be_empty end end - it "ensure that 'locked rep and platforms' has only one item" do - queue = @redis_instance.lrange(subject::LOCKED_REP_AND_PLATFORMS, 0, -1) + it "ensures that has only 'projects for cleanup' for testing subrepo" do + queue = @redis_instance.lrange(subject::PROJECTS_FOR_CLEANUP, 0, -1) queue.should have(1).item - queue.should include("#{build_list.save_to_repository_id}-#{build_list.build_for_platform_id}") + queue.should include("testing-#{build_list.project_id}-#{build_list.save_to_repository_id}-#{build_list.build_for_platform_id}") end - it "ensure that 'locked projects for cleanup' has only one item" do + it "ensures that only one repository_status has status publish" do + RepositoryStatus.where(:status => RepositoryStatus::PUBLISH).should have(1).item + end + + it "ensures that 'locked projects for cleanup' has only one item" do queue = @redis_instance.lrange(subject::LOCKED_PROJECTS_FOR_CLEANUP, 0, -1) queue.should have(1).item queue.should include("#{build_list.project_id}-#{build_list.save_to_repository_id}-#{build_list.build_for_platform_id}") end - it "ensure that new task for publishing has been created" do + it "ensures that new task for publishing has been created" do @redis_instance.lrange('queue:publish_worker_default', 0, -1).should have(1).item end end - describe 'grouping build lists for publishing and tasks for removing project from repository' do - let(:build_list2) { FactoryGirl.create(:build_list_core, + context 'grouping build lists for publishing and tasks for removing project from repository' do + let(:build_list2) { FactoryGirl.create(:build_list, :new_core => true, :save_to_platform => build_list.save_to_platform, :save_to_repository => build_list.save_to_repository, :build_for_platform => build_list.build_for_platform ) } - let(:build_list3) { FactoryGirl.create(:build_list_core, + let(:build_list3) { FactoryGirl.create(:build_list, :new_core => true, :save_to_platform => build_list.save_to_platform, :save_to_repository => build_list.save_to_repository, :build_for_platform => build_list.build_for_platform ) } before do - stub_redis build_list.update_column(:status, BuildList::BUILD_PUBLISH) build_list2.update_column(:status, BuildList::BUILD_PUBLISHED) build_list3.update_column(:status, BuildList::BUILD_PUBLISHED) @@ -201,61 +180,48 @@ describe AbfWorker::BuildListsPublishTaskManager do 2.times{ subject.new.run } end - %w(RESIGN_REPOSITORIES - PROJECTS_FOR_CLEANUP - LOCKED_REPOSITORIES).each do |kind| - - it "ensure that no '#{kind.downcase.gsub('_', ' ')}'" do - @redis_instance.lrange(subject.const_get(kind), 0, -1).should be_empty - end - end - - it "ensure that 'locked rep and platforms' has only one item" do - queue = @redis_instance.lrange(subject::LOCKED_REP_AND_PLATFORMS, 0, -1) + it "ensures that no 'projects for cleanup' for main repo" do + queue = @redis_instance.lrange(subject::PROJECTS_FOR_CLEANUP, 0, -1) queue.should have(1).item - queue.should include("#{build_list.save_to_repository_id}-#{build_list.build_for_platform_id}") + queue.should include("testing-#{build_list3.project_id}-#{build_list3.save_to_repository_id}-#{build_list3.build_for_platform_id}") end - it "ensure that 'locked projects for cleanup' has only one item" do + it "ensures that only one repository_status has status publish" do + RepositoryStatus.where(:status => RepositoryStatus::PUBLISH).should have(1).item + end + + it "ensures that 'locked projects for cleanup' has only one item" do queue = @redis_instance.lrange(subject::LOCKED_PROJECTS_FOR_CLEANUP, 0, -1) queue.should have(1).item queue.should include("#{build_list3.project_id}-#{build_list3.save_to_repository_id}-#{build_list3.build_for_platform_id}") end - it "ensure that new task for publishing has been created" do + it "ensures that new task for publishing has been created" do @redis_instance.lrange('queue:publish_worker_default', 0, -1).should have(1).item end - it "ensure that 'locked build lists' has only one item" do + it "ensures that 'locked build lists' has only one item" do queue = @redis_instance.lrange(subject::LOCKED_BUILD_LISTS, 0, -1) queue.should have(1).item queue.should include(build_list.id.to_s) end end - describe 'resign packages in repository' do + context 'resign packages in repository' do before do - stub_redis build_list.update_column(:status, BuildList::BUILD_PUBLISH) FactoryGirl.create(:key_pair, :repository => build_list.save_to_repository) 2.times{ subject.new.run } end - %w(RESIGN_REPOSITORIES - PROJECTS_FOR_CLEANUP - LOCKED_PROJECTS_FOR_CLEANUP - LOCKED_REP_AND_PLATFORMS - LOCKED_BUILD_LISTS).each do |kind| - + %w(PROJECTS_FOR_CLEANUP LOCKED_PROJECTS_FOR_CLEANUP LOCKED_BUILD_LISTS).each do |kind| it "ensure that no '#{kind.downcase.gsub('_', ' ')}'" do @redis_instance.lrange(subject.const_get(kind), 0, -1).should be_empty end end - it "ensure that 'locked repositories' has only one item" do - queue = @redis_instance.lrange(subject::LOCKED_REPOSITORIES, 0, -1) - queue.should have(1).item - queue.should include(build_list.save_to_repository_id.to_s) + it "ensures that only one repository_status has status resign" do + RepositoryStatus.where(:status => RepositoryStatus::RESIGN).should have(1).item end it "ensure that new task for resign has been created" do @@ -264,6 +230,41 @@ describe AbfWorker::BuildListsPublishTaskManager do end + context 'regenerate metadata' do + context 'for repository of main platform' do + let(:repository) { FactoryGirl.create(:repository) } + before do + repository.regenerate + subject.new.run + end + + it "ensures that only one repository_status has status regenerating" do + RepositoryStatus.where(:status => RepositoryStatus::REGENERATING).should have(1).item + end + + it 'ensures that new task has been created' do + @redis_instance.lrange('queue:publish_worker_default', 0, -1).should have(1).item + end + end + + context 'for repository of personal platform' do + let(:main_platform) { FactoryGirl.create(:platform) } + let(:repository) { FactoryGirl.create(:personal_repository) } + before do + repository.regenerate main_platform.id + subject.new.run + end + + it "ensures that only one repository_status has status regenerating" do + RepositoryStatus.where(:status => RepositoryStatus::REGENERATING).should have(1).item + end + + it 'ensures that new task has been created' do + @redis_instance.lrange('queue:publish_worker_default', 0, -1).should have(1).item + end + end + + end after(:all) do APP_CONFIG['abf_worker']['publish_workers_count'] = @publish_workers_count diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb index 7183cced0..b0167e089 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -1,7 +1,6 @@ require "spec_helper" describe UserMailer do - pending "add some examples to (or delete) #{__FILE__}" context 'On Issue create' do before(:each) do @@ -13,7 +12,7 @@ describe UserMailer do any_instance_of(Project, :versions => ['v1.0', 'v2.0']) @issue = FactoryGirl.create(:issue, :project_id => @project.id, :assignee_id => @issue_user.id, :user => @issue_user) - @email = UserMailer.new_issue_notification(@issue, @issue_user).deliver + @email = UserMailer.new_issue_notification(@issue, @issue_user).deliver! end it 'should have correct subject' do @@ -48,7 +47,7 @@ describe UserMailer do any_instance_of(Project, :versions => ['v1.0', 'v2.0']) @issue = FactoryGirl.create(:issue, :project_id => @project.id, :assignee_id => @issue_user.id, :user => @issue_user) - @email = UserMailer.issue_assign_notification(@issue, @user).deliver + @email = UserMailer.issue_assign_notification(@issue, @user).deliver! end it 'should have correct subject' do @@ -63,10 +62,6 @@ describe UserMailer do @email.from.should == [APP_CONFIG['do-not-reply-email']] end - it 'should assign user name' do - @email.body.encoded.should match(@user.name) - end - it 'should assign issue title' do @email.body.encoded.should match(@issue.title) end @@ -85,7 +80,7 @@ describe UserMailer do @issue = FactoryGirl.create(:issue, :project_id => @project.id, :assignee_id => @issue_user.id, :user => @issue_user) @comment = FactoryGirl.create(:comment, :commentable => @issue, :user_id => @user.id, :project => @project) - @email = UserMailer.new_comment_notification(@comment, @issue_user).deliver + @email = UserMailer.new_comment_notification(@comment, @issue_user).deliver! end it 'should have correct subject' do diff --git a/spec/models/advisory_spec.rb b/spec/models/advisory_spec.rb index abbb38c8e..2f2e6f8ce 100644 --- a/spec/models/advisory_spec.rb +++ b/spec/models/advisory_spec.rb @@ -1,5 +1,47 @@ require 'spec_helper' -describe Advisory do - pending "add some examples to (or delete) #{__FILE__}" +shared_examples_for 'attach advisory to build_list' do + + it 'ensure that advisory has been attached to build_list' do + build_list.reload.advisory.should == advisory + end + + it 'ensure that save_to_platform of build_list has been attached to advisory' do + build_list.save_to_platform.advisories.should include(advisory) + end + + it 'ensure that projects of build_list has been attached to advisory' do + build_list.project.advisories.should include(advisory) + end + +end + +describe Advisory do + before { stub_symlink_methods } + context 'attach_build_list' do + let(:build_list) { FactoryGirl.create(:build_list) } + + context 'attach new advisory to build_list' do + let(:advisory) { FactoryGirl.build(:advisory) } + before do + advisory.attach_build_list(build_list) + end + + it_should_behave_like 'attach advisory to build_list' + + it 'ensure that advisory has been created' do + Advisory.should have(1).item + end + end + + context 'attach old advisory to build_list' do + let(:advisory) { FactoryGirl.create(:advisory) } + before do + advisory.attach_build_list(build_list) + end + + it_should_behave_like 'attach advisory to build_list' + end + + end end diff --git a/spec/models/build_list/package_spec.rb b/spec/models/build_list/package_spec.rb new file mode 100644 index 000000000..3ed6e1c8c --- /dev/null +++ b/spec/models/build_list/package_spec.rb @@ -0,0 +1,31 @@ +# -*- encoding : utf-8 -*- +require 'spec_helper' + +describe BuildList::Package do + before { stub_symlink_methods } + + it 'is valid' do + FactoryGirl.create(:build_list_package).should be_persisted + end + + context '#set_epoch' do + let(:package) { FactoryGirl.build(:build_list_package) } + + ['', '(none)'].each do |epoch| + it "ensures that epoch is set to nil when epoch is '#{epoch}'" do + package.epoch = epoch + package.save + package.epoch.should be_nil + end + + end + + it "ensures that valid epoch has been setted" do + package.epoch = '55' + package.save + package.epoch.should == 55 + end + + end + +end diff --git a/spec/models/build_list_spec.rb b/spec/models/build_list_spec.rb index 7dd4dfbdc..6817f481b 100644 --- a/spec/models/build_list_spec.rb +++ b/spec/models/build_list_spec.rb @@ -1,6 +1,7 @@ require 'spec_helper' describe BuildList do + before { stub_symlink_methods } context 'validates that repository contains project' do it 'when repository contains project' do @@ -15,9 +16,8 @@ describe BuildList do end context "#notify_users" do - before { stub_symlink_methods } let!(:user) { FactoryGirl.create(:user) } - let!(:build_list) { FactoryGirl.create(:build_list_core, + let!(:build_list) { FactoryGirl.create(:build_list, :user => user, :auto_publish => false) } let!(:build_list_package) { FactoryGirl.create(:build_list_package, @@ -140,7 +140,7 @@ describe BuildList do it "doesn't get 2 notification by email when user associated to project and created task" do project = FactoryGirl.create(:project_with_commit, :owner => user) - bl = FactoryGirl.create(:build_list_core_with_attaching_project, + bl = FactoryGirl.create(:build_list_with_attaching_project, :user => user, :auto_publish => true, :project => project @@ -154,4 +154,83 @@ describe BuildList do end # notify_users + context '#has_new_packages?' do + let!(:build_list) { FactoryGirl.create( :build_list, + :status => BuildList::SUCCESS, + :auto_publish => true) } + let!(:build_list_package) { FactoryGirl.create( :build_list_package, + :build_list => build_list, + :version => '3.1.12', + :release => 6, + :platform => build_list.save_to_platform, + :project => build_list.project) } + let!(:published_build_list) { FactoryGirl.create( :build_list, + :project => build_list.project, + :status => BuildList::BUILD_PUBLISHED, + :save_to_platform => build_list.save_to_platform, + :arch => build_list.arch) } + let!(:published_build_list_package) { FactoryGirl.create( :build_list_package, + :build_list => published_build_list, + :platform => published_build_list.save_to_platform, + :actual => true, + :version => '3.1.12', + :release => 6, + :project => published_build_list.project) } + + it 'ensures that return false if version of packages are same and platform is released' do + build_list.save_to_platform.update_attributes(:released => true) + build_list.has_new_packages?.should be_false + end + + it 'ensures that return true if version of packages are same and platform is not released' do + build_list.has_new_packages?.should be_true + end + + context 'ensures that return false if version of published package >' do + + it 'published: 3.1.13, new: 3.1.12' do + published_build_list_package.update_attributes(:version => '3.1.13') + build_list.has_new_packages?.should be_false + end + + it 'published: 3.1.12, new: 3.0.999' do + build_list_package.update_attributes(:version => '3.0.999') + build_list.has_new_packages?.should be_false + end + + it 'published: 3.0.0, new: 3.0.rc1' do + published_build_list_package.update_attributes(:version => '3.0.0') + build_list_package.update_attributes(:version => '3.0.rc1') + build_list.has_new_packages?.should be_false + end + + end + + context 'ensures that return true if version of published package <' do + + it 'published: 3.1.11, new: 3.1.12' do + published_build_list_package.update_attributes(:version => '3.1.11') + build_list.has_new_packages?.should be_true + end + + it 'published: 3.0.999, new: 3.1.12' do + published_build_list_package.update_attributes(:version => '3.0.999') + build_list.has_new_packages?.should be_true + end + + it 'published: 3.0.rc1, new: 3.0.0' do + published_build_list_package.update_attributes(:version => '3.0.rc1') + build_list_package.update_attributes(:version => '3.0.0') + build_list.has_new_packages?.should be_true + end + + end + + it 'ensures that return true if release of published package <' do + published_build_list_package.update_attributes(:release => 5) + build_list.has_new_packages?.should be_true + end + + end + end diff --git a/spec/models/cancan_spec.rb b/spec/models/cancan_spec.rb index 8cdc804da..8474e9697 100644 --- a/spec/models/cancan_spec.rb +++ b/spec/models/cancan_spec.rb @@ -2,12 +2,12 @@ require 'spec_helper' require "cancan/matchers" def admin_create - @admin = FactoryGirl.create(:admin) + @admin = FactoryGirl.create(:admin) @ability = Ability.new(@admin) end def user_create - @user = FactoryGirl.create(:user) + @user = FactoryGirl.create(:user) @ability = Ability.new(@user) end @@ -16,47 +16,46 @@ def guest_create end describe CanCan do - - let(:personal_platform) { FactoryGirl.create(:platform, :platform_type => 'personal') } - let(:personal_repository) { FactoryGirl.create(:personal_repository) } - let(:open_platform) { FactoryGirl.create(:platform, :visibility => 'open') } - let(:hidden_platform) { FactoryGirl.create(:platform, :visibility => 'hidden') } - let(:register_request) { FactoryGirl.create(:register_request) } + let(:open_platform) { FactoryGirl.create(:platform, :visibility => 'open') } before(:each) do stub_symlink_methods end - context 'Site admin' do - before(:each) do - admin_create - end - - it 'should manage all' do - #(@ability.can? :manage, :all).should be_true - @ability.should be_able_to(:manage, :all) - end - - it 'should not be able to destroy personal platforms' do - @ability.should_not be_able_to(:destroy, personal_platform) - end - - it 'should not be able to destroy personal repositories' do - @ability.should_not be_able_to(:destroy, personal_repository) - end - end - - context 'Site guest' do - before(:each) do - guest_create - end - - it 'should not be able to read open platform' do - @ability.should_not be_able_to(:read, open_platform) + context 'Site admin' do + let(:personal_platform) { FactoryGirl.create(:platform, :platform_type => 'personal') } + let(:personal_repository_main) { FactoryGirl.create(:personal_repository, :name => 'main') } + let(:personal_repository) { FactoryGirl.create(:personal_repository) } + before(:each) do + admin_create end - it 'should not be able to read hidden platform' do - @ability.should_not be_able_to(:read, hidden_platform) + it 'should manage all' do + #(@ability.can? :manage, :all).should be_true + @ability.should be_able_to(:manage, :all) + end + + it 'should not be able to destroy personal platforms' do + @ability.should_not be_able_to(:destroy, personal_platform) + end + + it 'should not be able to destroy personal repositories with name "main"' do + @ability.should_not be_able_to(:destroy, personal_repository_main) + end + it 'should be able to destroy personal repositories with name not "main"' do + @ability.should be_able_to(:destroy, personal_repository) + end + end + + context 'Site guest' do + let(:register_request) { FactoryGirl.create(:register_request) } + + before(:each) do + guest_create + end + + it 'should not be able to read open platform' do + @ability.should_not be_able_to(:read, open_platform) end [:publish, :cancel, :reject_publish, :create_container].each do |action| @@ -65,6 +64,12 @@ describe CanCan do end end + [:mass_import, :run_mass_import].each do |action| + it "should not be able to #{ action } project" do + @ability.should_not be_able_to(action, Project) + end + end + it 'should not be able to update register request' do @ability.should_not be_able_to(:update, register_request) end @@ -77,10 +82,10 @@ describe CanCan do @ability.should_not be_able_to(:destroy, register_request) end - pending 'should be able to register new user' do # while self registration is closed - @ability.should be_able_to(:create, User) - end - end + pending 'should be able to register new user' do # while self registration is closed + @ability.should be_able_to(:create, User) + end + end context 'Site user' do before(:each) do @@ -88,11 +93,17 @@ describe CanCan do end [Platform, Repository].each do |model_name| - it "should not be able to read #{model_name}" do + it "should be able to read #{model_name}" do @ability.should be_able_to(:read, model_name) end end + [:mass_import, :run_mass_import].each do |action| + it "should not be able to #{ action } project" do + @ability.should_not be_able_to(action, Project) + end + end + it "shoud be able to show user profile" do @ability.should be_able_to(:show, User) end @@ -228,6 +239,27 @@ describe CanCan do end end + context 'through group-member' do + before(:each) do + @group_member = FactoryGirl.create(:group) + @project.relations.create!(:actor_id => @group_member.id, :actor_type => 'Group', :role => 'reader') + @group_member_ability = Ability.new(@group_member.owner) + end + + it 'should be able to read open project' do + @group_member_ability.should be_able_to(:read, @project) + end + + it 'should be able to read closed project' do + @project.update_attribute :visibility, 'hidden' + @group_member_ability.should be_able_to(:read, @project) + end + + it 'should include hidden project in list' do + @project.update_attribute :visibility, 'hidden' + Project.accessible_by(@group_member_ability, :show).where(:projects => {:id => @project.id}).count.should == 1 + end + end end context 'platform relations' do @@ -239,9 +271,16 @@ describe CanCan do before(:each) do @platform.owner = @user @platform.save + @ability = Ability.new(@user) end - [:read, :update, :destroy].each do |action| + [:mass_import, :run_mass_import].each do |action| + it "should be able to #{ action } project" do + @ability.should be_able_to(action, Project) + end + end + + [:read, :update, :destroy, :change_visibility].each do |action| it "should be able to #{action} platform" do @ability.should be_able_to(action, @platform) end @@ -251,6 +290,13 @@ describe CanCan do context 'with read rights' do before(:each) do @platform.relations.create!(:actor_id => @user.id, :actor_type => 'User', :role => 'reader') + @ability = Ability.new(@user) + end + + [:mass_import, :run_mass_import].each do |action| + it "should not be able to #{ action } project" do + @ability.should_not be_able_to(action, Project) + end end it "should be able to read platform" do @@ -270,7 +316,7 @@ describe CanCan do @repository.platform.save end - [:read, :create, :update, :destroy, :add_project, :remove_project, :change_visibility, :settings].each do |action| + [:read, :create, :update, :destroy, :add_project, :remove_project, :settings].each do |action| it "should be able to #{action} repository" do @ability.should be_able_to(action, @repository) end diff --git a/spec/models/comment_for_commit_spec.rb b/spec/models/comment_for_commit_spec.rb index 417995b8f..9ebc36b1f 100644 --- a/spec/models/comment_for_commit_spec.rb +++ b/spec/models/comment_for_commit_spec.rb @@ -5,6 +5,10 @@ def create_comment user FactoryGirl.create(:comment, :user => user, :commentable => @commit, :project => @project) end +def create_comment_in_commit commit, project, body + FactoryGirl.create(:comment, :user => @user, :commentable => commit, :project => project, :body => body) +end + def set_comments_data_for_commit @ability = Ability.new(@user) @@ -262,5 +266,63 @@ describe Comment do should_not_send_email(commentor: @user) end end + + context 'automatic issue linking' do + before(:each) do + @same_name_project = FactoryGirl.create(:project, :name => @project.name) + @issue_in_same_name_project = FactoryGirl.create(:issue, :project => @same_name_project, :user => @same_name_project.owner) + @another_project = FactoryGirl.create(:project, :owner => @user) + @other_user_project = FactoryGirl.create(:project) + @issue = FactoryGirl.create(:issue, :project => @project, :user => @user) + @second_issue = FactoryGirl.create(:issue, :project => @project, :user => @user) + @issue_in_another_project = FactoryGirl.create(:issue, :project => @another_project, :user => @user) + @issue_in_other_user_project = FactoryGirl.create(:issue, :project => @other_user_project, :user => @other_user_project.owner) + end + + it 'should create automatic comment' do + create_comment_in_commit(@commit, @project, "test link to ##{@issue.serial_id}; [##{@second_issue.serial_id}]") + Comment.where(:automatic => true, :commentable_type => 'Issue', + :commentable_id => @second_issue.id, + :created_from_commit_hash => @commit.id.hex).count.should == 1 + end + + it 'should create automatic comment in the another project issue' do + body = "[#{@another_project.name_with_owner}##{@issue_in_another_project.serial_id}]" + create_comment_in_commit(@commit, @project, body) + Comment.where(:automatic => true, :commentable_type => 'Issue', + :commentable_id => @issue_in_another_project.id, + :created_from_commit_hash => @commit.id.hex).count.should == 1 + end + + it 'should create automatic comment in the same name project issue' do + body = "[#{@same_name_project.owner.uname}##{@issue_in_same_name_project.serial_id}]" + create_comment_in_commit(@commit, @project, body) + Comment.where(:automatic => true, :commentable_type => 'Issue', + :commentable_id => @issue_in_same_name_project.id, + :created_from_commit_hash => @commit.id.hex).count.should == 1 + end + + it 'should not create duplicate automatic comment' do + create_comment_in_commit(@commit, @project, "test link to [##{@second_issue.serial_id}]") + create_comment_in_commit(@commit, @project, "test duplicate link to [##{@second_issue.serial_id}]") + Comment.where(:automatic => true, :commentable_type => 'Issue', + :commentable_id => @second_issue.id, + :created_from_commit_hash => @commit.id.hex).count.should == 1 + end + + it 'should not create duplicate automatic comment from one' do + create_comment_in_commit(@commit, @project, "test link to [##{@second_issue.serial_id}]; ##{@second_issue.serial_id}") + Comment.where(:automatic => true, :commentable_type => 'Issue', + :commentable_id => @second_issue.id, + :created_from_commit_hash => @commit.id.hex).count.should == 1 + end + it 'should create two automatic comment' do + body = "test ##{@second_issue.serial_id}" + + " && [#{@another_project.name_with_owner}##{@issue_in_another_project.serial_id}]" + create_comment_in_commit(@commit, @project, body) + Comment.where(:automatic => true, + :created_from_commit_hash => @commit.id.hex).count.should == 2 + end + end end end diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index 47e8db74e..713bb6497 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -13,6 +13,10 @@ def set_commentable_data any_instance_of(Project, :versions => ['v1.0', 'v2.0']) end +def create_comment_in_issue issue, body + FactoryGirl.create(:comment, :user => issue.user, :commentable => issue, :project => issue.project, :body => body) +end + describe Comment do before { stub_symlink_methods } context 'for global admin user' do @@ -99,5 +103,95 @@ describe Comment do @comment.should_not allow_mass_assignment_of :project_id end end + + context 'automatic issue linking' do + before(:each) do + @same_name_project = FactoryGirl.create(:project, :name => @project.name) + @issue_in_same_name_project = FactoryGirl.create(:issue, :project => @same_name_project, :user => @same_name_project.owner) + @another_project = FactoryGirl.create(:project, :owner => @user) + @other_user_project = FactoryGirl.create(:project) + @issue = FactoryGirl.create(:issue, :project => @project, :user => @user) + @second_issue = FactoryGirl.create(:issue, :project => @project, :user => @user) + @issue_in_another_project = FactoryGirl.create(:issue, :project => @another_project, :user => @user) + @issue_in_other_user_project = FactoryGirl.create(:issue, :project => @other_user_project, :user => @other_user_project.owner) + end + + it 'should create automatic comment' do + create_comment_in_issue(@issue, "test link to ##{@issue.serial_id}; [##{@second_issue.serial_id}]") + Comment.where(:automatic => true, :commentable_type => 'Issue', + :commentable_id => @second_issue.id, + :created_from_issue_id => @issue.id).count.should == 1 + end + + it 'should not create automatic comment to the same issue' do + create_comment_in_issue(@issue, "test link to ##{@issue.serial_id}; [##{@second_issue.serial_id}]") + Comment.where(:automatic => true, + :created_from_issue_id => @issue.id).count.should == 1 + end + + it 'should create automatic comment in the another project issue' do + body = "[#{@another_project.name_with_owner}##{@issue_in_another_project.serial_id}]" + create_comment_in_issue(@issue, body) + Comment.where(:automatic => true, :commentable_type => 'Issue', + :commentable_id => @issue_in_another_project.id, + :created_from_issue_id => @issue.id).count.should == 1 + end + + it 'should create automatic comment in the same name project issue' do + body = "[#{@same_name_project.owner.uname}##{@issue_in_same_name_project.serial_id}]" + create_comment_in_issue(@issue, body) + Comment.where(:automatic => true, :commentable_type => 'Issue', + :commentable_id => @issue_in_same_name_project.id, + :created_from_issue_id => @issue.id).count.should == 1 + end + + it 'should not create duplicate automatic comment' do + create_comment_in_issue(@issue, "test link to [##{@second_issue.serial_id}]") + create_comment_in_issue(@issue, "test duplicate link to [##{@second_issue.serial_id}]") + Comment.where(:automatic => true, :commentable_type => 'Issue', + :commentable_id => @second_issue.id, + :created_from_issue_id => @issue.id).count.should == 1 + end + + it 'should not create duplicate automatic comment from one' do + create_comment_in_issue(@issue, "test link to [##{@second_issue.serial_id}]; ##{@second_issue.serial_id}") + Comment.where(:automatic => true, :commentable_type => 'Issue', + :commentable_id => @second_issue.id, + :created_from_issue_id => @issue.id).count.should == 1 + end + + it 'should create two automatic comment' do + body = "test ##{@second_issue.serial_id}" + + " && [#{@another_project.name_with_owner}##{@issue_in_another_project.serial_id}]" + create_comment_in_issue(@issue, body) + Comment.where(:automatic => true, + :created_from_issue_id => @issue.id).count.should == 2 + end + + it 'should create automatic comment by issue title' do + issue = FactoryGirl.create(:issue, :project => @project, :user => @user, + :title => "link to ##{@issue.serial_id}") + Comment.where(:automatic => true, + :created_from_issue_id => issue.id).count.should == 1 + end + + it 'should create automatic comment from issue body' do + issue = FactoryGirl.create(:issue, :project => @project, :user => @user, + :body => "link to ##{@issue.serial_id}") + Comment.where(:automatic => true, + :created_from_issue_id => issue.id).count.should == 1 + end + + it 'should create only one automatic comment from issue title and body' do + issue = FactoryGirl.create(:issue, :project => @project, :user => @user, + :title => "link to ##{@issue.serial_id} in title", + :body => "link to ##{@issue.serial_id} in body") + Comment.where(:automatic => true, + :created_from_issue_id => issue.id).count.should == 1 + end + + + + end end end diff --git a/spec/models/activity_feed_observer_spec.rb b/spec/models/hook_spec.rb similarity index 71% rename from spec/models/activity_feed_observer_spec.rb rename to spec/models/hook_spec.rb index 378ee2377..42515cea0 100644 --- a/spec/models/activity_feed_observer_spec.rb +++ b/spec/models/hook_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -describe ActivityFeedObserver do +describe Hook do pending "add some examples to (or delete) #{__FILE__}" end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index ba688a33d..2ea3e032c 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -32,6 +32,31 @@ describe Issue do create_issue(@user) ActionMailer::Base.deliveries.count.should == 0 end + + it 'should create automatic comment from another issue' do + create_issue(@user) + another_issue = FactoryGirl.create(:issue, :project => @project, :title => "[##{@issue.serial_id}]") + Comment.where(:automatic => true, :commentable_type => 'Issue', + :created_from_issue_id => another_issue.id).count.should == 1 + end + + it 'should create automatic comment after updating another issue body' do + create_issue(@user) + another_issue = FactoryGirl.create(:issue, :project => @project) + another_issue.update_attribute(:title, "[##{@issue.serial_id}]") + another_issue.send(:send_assign_notifications) + + Comment.where(:automatic => true, :commentable_type => 'Issue', + :created_from_issue_id => another_issue.id).count.should == 1 + end + + it 'should send email message to new assignee' do + create_issue(@user) + ActionMailer::Base.deliveries = [] + @issue.update_attribute :assignee_id, @user.id + @issue.send(:send_assign_notifications, :update) + ActionMailer::Base.deliveries.count.should == 1 + end end context 'for member-group' do @@ -40,8 +65,8 @@ describe Issue do @project = FactoryGirl.create(:project, :owner => @user) @group = FactoryGirl.create(:group) - reader = FactoryGirl.create :user - @group.actors.create(:actor_type => 'User', :actor_id => reader.id, :role => 'reader') + @reader = FactoryGirl.create :user + @group.actors.create(:actor_type => 'User', :actor_id => @reader.id, :role => 'reader') end it 'should send an e-mail to all members of the admin group' do @@ -64,6 +89,32 @@ describe Issue do create_issue(@stranger) ActionMailer::Base.deliveries.count.should == 1 # 1 project owner end + + it 'should reset issue assignee after remove him from group' do + @project.relations.create!(:actor_type => 'Group', :actor_id => @group.id, :role => 'reader') + create_issue(@group.owner) + @issue.update_column :assignee_id, @reader.id + @group.remove_member @reader + @issue.reload.assignee_id.should == nil + end + + it 'should not reset issue assignee' do + @project.relations.create!(:actor_type => 'Group', :actor_id => @group.id, :role => 'reader') + @project.relations.create!(:actor_type => 'User', :actor_id => @reader.id, :role => 'reader') + create_issue(@group.owner) + @issue.update_column :assignee_id, @reader.id + @group.remove_member @reader + @issue.reload.assignee_id.should == @reader.id + end + + it 'should reset issue assignee after remove him from project' do + @project.relations.create!(:actor_type => 'User', :actor_id => @reader.id, :role => 'reader') + create_issue(@reader) + @issue.update_column :assignee_id, @reader.id + @project.remove_member @reader # via api + @issue.reload.assignee_id.should == nil + end + end context 'Group project' do diff --git a/spec/models/mass_build_spec.rb b/spec/models/mass_build_spec.rb index e7bb6c4d2..3e213af38 100644 --- a/spec/models/mass_build_spec.rb +++ b/spec/models/mass_build_spec.rb @@ -1,5 +1,41 @@ require 'spec_helper' describe MassBuild do - pending "add some examples to (or delete) #{__FILE__}" + before { stub_symlink_methods } + + context 'ensures that validations and associations exist' do + before do + # Need for validate_uniqueness_of check + FactoryGirl.create(:mass_build) + end + + it { should belong_to(:build_for_platform) } + it { should belong_to(:save_to_platform) } + it { should belong_to(:user) } + it { should have_many(:build_lists)} + + it { should validate_presence_of(:save_to_platform_id)} + it { should validate_presence_of(:build_for_platform_id)} + it { should validate_presence_of(:user_id)} + it { should validate_presence_of(:arch_names)} + it { should validate_presence_of(:name)} + + it { should validate_presence_of(:projects_list)} + it { should ensure_length_of(:projects_list).is_at_most(500_000) } + + + it { should_not allow_mass_assignment_of(:name) } + it { should_not allow_mass_assignment_of(:arch_names) } + end + + it 'ensures that projects_list contains unique projects' do + projects_list = %(at + at + ab + ) + mass_build = FactoryGirl.create(:mass_build, :projects_list => projects_list) + list = mass_build.projects_list.split(/[\r]*\n/) + list.should have(2).items + list.should include('at', 'ab') + end end diff --git a/spec/models/platform_content.rb b/spec/models/platform_content.rb new file mode 100644 index 000000000..1edb31e8f --- /dev/null +++ b/spec/models/platform_content.rb @@ -0,0 +1,71 @@ +# -*- encoding : utf-8 -*- +require 'spec_helper' + +describe PlatformContent do + subject { PlatformContent } + + before { stub_symlink_methods } + let!(:platform) { FactoryGirl.create(:platform) } + + context '#find_by_platform' do + before do + File.open(File.join(platform.path, 'test001'), "w") + File.open(File.join(platform.path, 'test002'), "w") + end + + it 'ensures that finds files' do + # + /repository folder + subject.find_by_platform(platform, '', '').should have(3).items + end + + context 'ensures that finds files by name' do + it { subject.find_by_platform(platform, '', 'test').should have(2).items } + it { subject.find_by_platform(platform, '', 'test001').should have(1).item } + it { subject.find_by_platform(platform, 'repository', 'test').should have(:no).items } + end + + end + + context '#is_folder?' do + it 'ensures that returns true for folder' do + subject.find_by_platform(platform, '', 'repository').first.is_folder? + .should be_true + end + + it 'ensures that returns false for file' do + File.open(File.join(platform.path, 'test001'), "w") + subject.find_by_platform(platform, '', 'test').first.is_folder? + .should be_false + end + end + + context '#build_list' do + let!(:package) { FactoryGirl.create(:build_list_package, :actual => true) } + let(:platform) { package.build_list.save_to_platform } + let(:repository) { platform.repositories.first } + + before do + File.open(File.join(platform.path, 'test001'), "w") + + package.build_list.update_column(:status, BuildList::BUILD_PUBLISHED) + path = File.join platform.path, 'repository', 'SRPMS', repository.name, 'release' + FileUtils.mkdir_p path + File.open(File.join(path, package.fullname), "w") + + path = File.join path, 'repodata' + FileUtils.mkdir_p path + File.open(File.join(path, package.fullname), "w") + end + + context 'ensures that returns nil for simple file' do + it { subject.find_by_platform(platform, '', 'test').first.build_list.should be_nil } + it { subject.find_by_platform(platform, "repository/SRPMS/#{repository.name}/release/repodata", '').first.build_list.should be_nil } + end + + it 'ensures that returns build_list for package' do + subject.find_by_platform(platform, "repository/SRPMS/#{repository.name}/release", package.fullname) + .first.build_list.should == package.build_list + end + end + +end diff --git a/spec/models/platform_spec.rb b/spec/models/platform_spec.rb index 6eabc8798..0a7b5eb3c 100644 --- a/spec/models/platform_spec.rb +++ b/spec/models/platform_spec.rb @@ -1,48 +1,70 @@ require 'spec_helper' describe Platform do - before(:all) do - stub_symlink_methods - Platform.delete_all - User.delete_all - init_test_root - # Need for validate_uniqueness_of check - FactoryGirl.create(:platform) + before { stub_symlink_methods } + + context 'ensures that validations and associations exist' do + before do + # Need for validate_uniqueness_of check + FactoryGirl.create(:platform) + end + + it { should belong_to(:owner) } + it { should have_many(:members)} + it { should have_many(:repositories)} + it { should have_many(:products)} + + it { should validate_presence_of(:name)} + it { should validate_uniqueness_of(:name).case_insensitive } + it { should allow_value('Basic_platform-name-1234').for(:name) } + it { should_not allow_value('.!').for(:name) } + it { should validate_presence_of(:description) } + it { should validate_presence_of(:distrib_type) } + it { should validate_presence_of(:visibility) } + + Platform::VISIBILITIES.each do |value| + it {should allow_value(value).for(:visibility)} + end + it {should_not allow_value('custom_status').for(:visibility)} + + it { should have_readonly_attribute(:name) } + it { should have_readonly_attribute(:distrib_type) } + it { should have_readonly_attribute(:parent_platform_id) } + it { should have_readonly_attribute(:platform_type) } + + it { should_not allow_mass_assignment_of(:repositories) } + it { should_not allow_mass_assignment_of(:products) } + it { should_not allow_mass_assignment_of(:members) } + it { should_not allow_mass_assignment_of(:parent) } + + it {should_not allow_value("How do you do...\nmy_platform").for(:name)} end - it { should belong_to(:owner) } - it { should have_many(:members)} - it { should have_many(:repositories)} - it { should have_many(:products)} - - it { should validate_presence_of(:name)} - it { should validate_uniqueness_of(:name).case_insensitive } - it { should validate_format_of(:name).with('Basic_platform-name-1234') } - it { should validate_format_of(:name).not_with('.!') } - it { should validate_presence_of(:description) } - it { should validate_presence_of(:distrib_type) } - it { should validate_presence_of(:visibility) } - - Platform::VISIBILITIES.each do |value| - it {should allow_value(value).for(:visibility)} + it 'ensures that folder of platform will be removed after destroy' do + platform = FactoryGirl.create :platform + FileUtils.mkdir_p platform.path + platform.destroy + Dir.exists?(platform.path).should be_false end - it {should_not allow_value('custom_status').for(:visibility)} - it { should have_readonly_attribute(:name) } - it { should have_readonly_attribute(:distrib_type) } - it { should have_readonly_attribute(:parent_platform_id) } - it { should have_readonly_attribute(:platform_type) } - - it { should_not allow_mass_assignment_of(:repositories) } - it { should_not allow_mass_assignment_of(:products) } - it { should_not allow_mass_assignment_of(:members) } - it { should_not allow_mass_assignment_of(:parent) } - - it {should_not allow_value("How do you do...\nmy_platform").for(:name)} - - after(:all) do - Platform.delete_all - User.delete_all - clear_test_root + it 'ensures that owner of personal platform can not be changed' do + platform = FactoryGirl.create :personal_platform + owner = platform.owner + platform.owner = FactoryGirl.create :user + platform.save.should be_false end + + it 'ensures that owner of platform of group can not be changed' do + group = FactoryGirl.create :group + platform = FactoryGirl.create :personal_platform, :owner => group + platform.owner = FactoryGirl.create :user + platform.save.should be_false + end + + it 'ensures that owner of main platform can be changed' do + platform = FactoryGirl.create :platform + platform.owner = FactoryGirl.create :user + platform.save.should be_true + end + end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 31bd3b874..8aec0a4e3 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1,11 +1,16 @@ require 'spec_helper' describe Project do - before do - stub_symlink_methods - @root_project = FactoryGirl.create(:project) - @child_project = @root_project.fork(FactoryGirl.create(:user)) - @child_child_project = @child_project.fork(FactoryGirl.create(:user)) + before { stub_symlink_methods } + + context 'creation' do + let(:root_project) { FactoryGirl.create(:project) } + let(:child_project) { root_project.fork(FactoryGirl.create(:user)) } + let(:child_child_project) { child_project.fork(FactoryGirl.create(:user)) } + + it { root_project } + it { child_project } + it { child_child_project } end context 'for destroy' do @@ -85,4 +90,95 @@ describe Project do lambda {FactoryGirl.create(:project, :name => "...\nbeatiful_name\n for project")}.should raise_error(ActiveRecord::RecordInvalid) end end + + it 'ensures that path to git repository has been changed after rename of project' do + project = FactoryGirl.create(:project_with_commit) + project.update_attributes(:name => "#{project.name}-new") + Dir.exists?(project.path).should be_true + end + + context 'manage branches' do + let!(:project) { FactoryGirl.create(:project_with_commit) } + let(:branch) { project.repo.branches.detect{|b| b.name == 'conflicts'} } + let(:master) { project.repo.branches.detect{|b| b.name == 'master'} } + let(:user) { FactoryGirl.create(:user) } + before { stub_redis } + + context '#delete_branch' do + it 'ensures that returns true on success' do + project.delete_branch(branch, user).should be_true + end + + it 'ensures that branch has been deleted' do + lambda { project.delete_branch(branch, user) }.should change{ project.repo.branches.count }.by(-1) + end + + it 'ensures that returns false on delete master' do + project.delete_branch(master, user).should be_false + end + + it 'ensures that master has not been deleted' do + lambda { project.delete_branch(master, user) }.should change{ project.repo.branches.count }.by(0) + end + + it 'ensures that returns false on delete wrong branch' do + project.delete_branch(branch, user) + project.delete_branch(branch, user).should be_false + end + end + + context '#create_branch' do + before do + project.delete_branch(branch, user) + end + + it 'ensures that returns true on success' do + project.create_branch(branch.name, branch.commit.id, user).should be_true + end + + it 'ensures that branch has been created' do + lambda { project.create_branch(branch.name, branch.commit.id, user) }.should change{ project.repo.branches.count }.by(1) + end + + it 'ensures that returns false on create wrong branch' do + project.create_branch(branch.name, GitHook::ZERO, user).should be_false + end + end + + end + + context '#replace_release_tag' do + + [ + ['Release: %mkrel 4mdk', 'Release: 5mdk'], + ['Release: 4', 'Release: 5'], + ['Release: 4.1', 'Release: 4.2'], + ['Release: 5.4.2', 'Release: 5.4.3'], + ['Release: 5.4.2mdk', 'Release: 5.4.3mdk'], + ['Release: %mkrel 5.4.31mdk', 'Release: 5.4.32mdk'], + ['%define release %mkrel 4mdk', '%define release 5mdk'], + ['%define release 4', '%define release 5'], + ['%define release 4.1', '%define release 4.2'], + ['%define release 5.4.2', '%define release 5.4.3'], + ['%define release 5.4.31mdk', '%define release 5.4.32mdk'] + ].each do |items| + it "ensures that replace '#{items[0]}' => '#{items[1]}'" do + Project.replace_release_tag(items[0]).should == items[1] + end + end + end + + it '#run_mass_import' do + owner = FactoryGirl.create(:user) + repository = FactoryGirl.create(:repository) + url = 'http://abf-downloads.rosalinux.ru/abf_personal/repository/test-mass-import' + visibility = 'open' + + Project.run_mass_import(url, "abf-worker-service-1-3.src.rpm\nredir-2.2.1-7.res6.src.rpm\n", visibility, owner, repository.id) + + Project.count.should == 2 + repository.projects.should have(2).items + owner.projects.should have(2).items + end + end diff --git a/spec/models/project_statistic_spec.rb b/spec/models/project_statistic_spec.rb new file mode 100644 index 000000000..e13a0ec20 --- /dev/null +++ b/spec/models/project_statistic_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe ProjectStatistic do + + context 'ensures that validations and associations exist' do + it { should belong_to(:project) } + it { should belong_to(:arch) } + + it { should validate_presence_of(:project_id) } + it { should validate_presence_of(:arch_id) } + it { should validate_presence_of(:average_build_time) } + it { should validate_presence_of(:build_count) } + + it { should_not allow_mass_assignment_of(:project_id) } + it { should_not allow_mass_assignment_of(:arch_id) } + + it 'uniqueness of project_id and arch_id' do + FactoryGirl.create(:project_statistic) + should validate_uniqueness_of(:project_id).scoped_to(:arch_id) + end + + end + +end diff --git a/spec/models/project_to_repository_spec.rb b/spec/models/project_to_repository_spec.rb index fd595d3b1..437ca789e 100644 --- a/spec/models/project_to_repository_spec.rb +++ b/spec/models/project_to_repository_spec.rb @@ -20,7 +20,8 @@ describe ProjectToRepository do stub_redis @first_repo.project_to_repositories.destroy_all queue = @redis_instance.lrange(AbfWorker::BuildListsPublishTaskManager::PROJECTS_FOR_CLEANUP, 0, -1) - queue.should have(1).item - queue.should include("#{@project.id}-#{@first_repo.id}-#{@platform.id}") + queue.should have(2).item + key = "#{@project.id}-#{@first_repo.id}-#{@platform.id}" + queue.should include(key, "testing-#{key}") end end diff --git a/spec/models/pull_request_spec.rb b/spec/models/pull_request_spec.rb index 40b234c7f..634b9531f 100644 --- a/spec/models/pull_request_spec.rb +++ b/spec/models/pull_request_spec.rb @@ -14,7 +14,6 @@ def set_data_for_pull end describe PullRequest do - context 'for owner user' do before do stub_symlink_methods @@ -33,6 +32,13 @@ describe PullRequest do @other_pull.save end + it 'ensures that path to pull_request repository has been changed after rename of project' do + @pull.check + @project.update_attributes(:name => "#{@project.name}-new") + @pull.reload + Dir.exists?(@pull.path).should be_true + end + it 'master should merge with non_conflicts branch' do @pull.check @pull.status.should == 'ready' diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 5de451ad8..006cbbb2c 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1,10 +1,74 @@ require 'spec_helper' describe Repository do + before { stub_symlink_methods } + + context 'ensures that validations and associations exist' do + before do + # Need for validate_uniqueness_of check + FactoryGirl.create(:repository) + end + + it { should belong_to(:platform) } + it { should have_many(:project_to_repositories).validate(true) } + it { should have_many(:projects).through(:project_to_repositories) } + + it { should validate_presence_of(:name) } + it { should validate_uniqueness_of(:name).case_insensitive.scoped_to(:platform_id) } + it { should allow_value('basic_repository-name-1234').for(:name) } + it { should_not allow_value('.!').for(:name) } + it { should_not allow_value('Main').for(:name) } + it { should_not allow_value("!!\nbang_bang\n!!").for(:name) } + it { should validate_presence_of(:description) } + + it { should have_readonly_attribute(:name) } + it { should have_readonly_attribute(:platform_id) } + + it { should_not allow_mass_assignment_of(:platform) } + it { should_not allow_mass_assignment_of(:platform_id) } + + end + + context '#sync_lock_file_exists?, #add_sync_lock_file, #remove_sync_lock_file, #add_repo_lock_file, #remove_repo_lock_file' do + let(:repository) { FactoryGirl.create(:repository) } + let(:path) { "#{repository.platform.path}/repository/SRPMS/#{repository.name}" } + before { FileUtils.mkdir_p path } + + it 'ensures that #sync_lock_file_exists? returns false if .sync.lock file does not exist' do + repository.sync_lock_file_exists?.should be_false + end + + it 'ensures that #sync_lock_file_exists? returns true if .sync.lock file does exist' do + FileUtils.touch "#{path}/.sync.lock" + repository.sync_lock_file_exists?.should be_true + end + + it 'ensures that #add_sync_lock_file creates .sync.lock file' do + repository.add_sync_lock_file + File.exist?("#{path}/.sync.lock").should be_true + end + + it 'ensures that #remove_sync_lock_file removes .sync.lock file' do + FileUtils.touch "#{path}/.sync.lock" + repository.remove_sync_lock_file + File.exist?("#{path}/.sync.lock").should be_false + end + + it 'ensures that #add_repo_lock_file creates .repo.lock file' do + repository.add_repo_lock_file + File.exist?("#{path}/.repo.lock").should be_true + end + + it 'ensures that #remove_repo_lock_file removes .repo.lock file' do + FileUtils.touch "#{path}/.repo.lock" + repository.remove_repo_lock_file + File.exist?("#{path}/.repo.lock").should be_false + end + + end context 'when create with same owner that platform' do - before (:each) do - stub_symlink_methods + before do @platform = FactoryGirl.create(:platform) @params = {:name => 'tst_platform', :description => 'test platform'} end @@ -15,39 +79,57 @@ describe Repository do end end - before(:all) do - stub_symlink_methods - Platform.delete_all - User.delete_all - Repository.delete_all - init_test_root - # Need for validate_uniqueness_of check - FactoryGirl.create(:repository) + context 'ensures that folder of repository will be removed after destroy' do + let(:arch) { FactoryGirl.create(:arch) } + let(:types) { ['SRPM', arch.name] } + + it "repository of main platform" do + FactoryGirl.create(:arch) + r = FactoryGirl.create(:repository) + paths = types. + map{ |type| "#{r.platform.path}/repository/#{type}/#{r.name}" }. + each{ |path| FileUtils.mkdir_p path } + r.destroy + paths.each{ |path| Dir.exists?(path).should be_false } + end + + it "repository of personal platform" do + FactoryGirl.create(:arch) + main_platform = FactoryGirl.create(:platform) + r = FactoryGirl.create(:personal_repository) + paths = types. + map{ |type| "#{r.platform.path}/repository/#{main_platform.name}/#{type}/#{r.name}" }. + each{ |path| FileUtils.mkdir_p path } + r.destroy + paths.each{ |path| Dir.exists?(path).should be_false } + end + end - it { should belong_to(:platform) } - it { should have_many(:project_to_repositories).validate(true) } - it { should have_many(:projects).through(:project_to_repositories) } + context '#add_projects' do + it 'user has ability to read of adding project' do + repository = FactoryGirl.create(:repository) + project = FactoryGirl.create(:project) + repository.add_projects("#{project.owner.uname}/#{project.name}", FactoryGirl.create(:user)) + repository.projects.should have(1).item + end - it { should validate_presence_of(:name) } - it { should validate_uniqueness_of(:name).case_insensitive.scoped_to(:platform_id) } - it { should validate_format_of(:name).with('basic_repository-name-1234') } - it { should validate_format_of(:name).not_with('.!') } - it { should validate_format_of(:name).not_with('Main') } - it { should validate_format_of(:name).not_with("!!\nbang_bang\n!!") } - it { should validate_presence_of(:description) } + it 'user has no ability to read of adding project' do + repository = FactoryGirl.create(:repository) + project = FactoryGirl.create(:project, :visibility => 'hidden') + repository.add_projects("#{project.owner.uname}/#{project.name}", FactoryGirl.create(:user)) + repository.projects.should have(:no).items + end + end - it { should have_readonly_attribute(:name) } - it { should have_readonly_attribute(:platform_id) } - - it { should_not allow_mass_assignment_of(:platform) } - it { should_not allow_mass_assignment_of(:platform_id) } - - after(:all) do - Platform.delete_all - User.delete_all - Repository.delete_all - clear_test_root + it '#remove_projects' do + stub_redis + repository = FactoryGirl.create(:repository) + project = FactoryGirl.create(:project) + repository.projects << project + repository.remove_projects(project.name) + repository.reload + repository.projects.should have(:no).items end end diff --git a/spec/models/token_spec.rb b/spec/models/token_spec.rb new file mode 100644 index 000000000..8c3092028 --- /dev/null +++ b/spec/models/token_spec.rb @@ -0,0 +1,32 @@ +# -*- encoding : utf-8 -*- +require 'spec_helper' + +describe Token do + before { stub_symlink_methods } + + describe 'platform token' do + let!(:platform_token) { FactoryGirl.create(:platform_token) } + + context 'ensures that validations and associations exist' do + it { should belong_to(:subject) } + it { should belong_to(:creator) } + + it { should validate_presence_of(:creator_id) } + it { should validate_presence_of(:subject_id) } + it { should validate_presence_of(:subject_type) } + + it { should_not allow_mass_assignment_of(:authentication_token) } + it { should_not allow_mass_assignment_of(:creator_id) } + it { should_not allow_mass_assignment_of(:subject_id) } + it { should_not allow_mass_assignment_of(:subject_type) } + + it 'ensures that authentication_token unique' do + token = FactoryGirl.create(:platform_token) + token.authentication_token = platform_token.authentication_token + token.valid?.should be_false + end + end + end + + +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 333f6c315..3203da2d8 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -27,6 +27,8 @@ RSpec.configure do |config| config.filter_run_excluding :anonymous_access => !(APP_CONFIG['anonymous_access']) + config.before(:all) { init_test_root } + config.after(:all) { clear_test_root } end def set_session_for(user=nil) @@ -35,9 +37,10 @@ def set_session_for(user=nil) sign_in current_user end -def http_login(user=nil) +def http_login(user=nil, password = '123456') # FIXME: password constant is a bad choice... - request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user.email,'123456') + email = user.is_a?(String) ? user : user.try(:email) + request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(email, password) end def stub_symlink_methods @@ -46,8 +49,9 @@ def stub_symlink_methods end Resque.inline = true -APP_CONFIG['root_path'] = "#{Rails.root}/tmp/test_root" -APP_CONFIG['git_path'] = "#{Rails.root}/tmp/test_root" +APP_CONFIG['root_path'] = "#{Rails.root}/tmp/test_root" +APP_CONFIG['git_path'] = "#{Rails.root}/tmp/test_root" +APP_CONFIG['tmpfs_path'] = "#{Rails.root}/tmp/test_root" def init_test_root clear_test_root @@ -64,8 +68,6 @@ def stub_redis stub(Resque).redis { @redis_instance } end -init_test_root - def fill_project project %x(mkdir -p #{project.path} && cp -Rf #{Rails.root}/spec/tests.git/* #{project.path}) # maybe FIXME ? end diff --git a/travis.sh b/travis.sh new file mode 100755 index 000000000..663004ebf --- /dev/null +++ b/travis.sh @@ -0,0 +1,20 @@ +#!/bin/bash -e + +rspec="bundle exec rspec" + +if [ $SPEC_GROUP = 'controllers' ] +then + $rspec spec/controllers/*_spec.rb spec/controllers/admin/ spec/controllers/groups/ spec/controllers/projects/ spec/controllers/users/ +elif [ $SPEC_GROUP = 'platform_controllers' ] +then + $rspec spec/controllers/platforms/ +elif [ $SPEC_GROUP = 'api' ] +then + $rspec spec/controllers/api/ +elif [ $SPEC_GROUP = 'models' ] +then + $rspec spec/models/ +elif [ $SPEC_GROUP = 'others' ] +then + $rspec spec/helpers/ spec/integration/ spec/lib/ spec/mailers/ spec/mailers/ spec/routing/ +fi diff --git a/vendor/assets/audios/garbage_shattering.wav b/vendor/assets/audios/garbage_shattering.wav new file mode 100644 index 000000000..9953752ad Binary files /dev/null and b/vendor/assets/audios/garbage_shattering.wav differ diff --git a/vendor/assets/javascripts/moment/ru.js b/vendor/assets/javascripts/moment/ru.js new file mode 100644 index 000000000..ce93b6375 --- /dev/null +++ b/vendor/assets/javascripts/moment/ru.js @@ -0,0 +1,125 @@ +// moment.js language configuration +// language : russian (ru) +// author : Viktorminator : https://github.com/Viktorminator +// Author : Menelion Elensúle : https://github.com/Oire + +function plural(word, num) { + var forms = word.split('_'); + return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]); +} + +function relativeTimeWithPlural(number, withoutSuffix, key) { + var format = { + 'mm': 'минута_минуты_минут', + 'hh': 'час_часа_часов', + 'dd': 'день_дня_дней', + 'MM': 'месяц_месяца_месяцев', + 'yy': 'год_года_лет' + }; + if (key === 'm') { + return withoutSuffix ? 'минута' : 'минуту'; + } + else { + return number + ' ' + plural(format[key], +number); + } +} + +function monthsCaseReplace(m, format) { + var months = { + 'nominative': 'январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь'.split('_'), + 'accusative': 'января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря'.split('_') + }, + + nounCase = (/D[oD]? *MMMM?/).test(format) ? + 'accusative' : + 'nominative'; + + return months[nounCase][m.month()]; +} + +function weekdaysCaseReplace(m, format) { + var weekdays = { + 'nominative': 'воскресенье_понедельник_вторник_среда_четверг_пятница_суббота'.split('_'), + 'accusative': 'воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу'.split('_') + }, + + nounCase = (/\[ ?[Вв] ?(?:прошлую|следующую)? ?\] ?dddd/).test(format) ? + 'accusative' : + 'nominative'; + + return weekdays[nounCase][m.day()]; +} + +moment.lang('ru', { + months : monthsCaseReplace, + monthsShort : "янв_фев_мар_апр_май_июн_июл_авг_сен_окт_ноя_дек".split("_"), + weekdays : weekdaysCaseReplace, + weekdaysShort : "вск_пнд_втр_срд_чтв_птн_сбт".split("_"), + weekdaysMin : "вс_пн_вт_ср_чт_пт_сб".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD.MM.YYYY", + LL : "D MMMM YYYY г.", + LLL : "D MMMM YYYY г., LT", + LLLL : "dddd, D MMMM YYYY г., LT" + }, + calendar : { + sameDay: '[Сегодня в] LT', + nextDay: '[Завтра в] LT', + lastDay: '[Вчера в] LT', + nextWeek: function () { + return this.day() === 2 ? '[Во] dddd [в] LT' : '[В] dddd [в] LT'; + }, + lastWeek: function () { + switch (this.day()) { + case 0: + return '[В прошлое] dddd [в] LT'; + case 1: + case 2: + case 4: + return '[В прошлый] dddd [в] LT'; + case 3: + case 5: + case 6: + return '[В прошлую] dddd [в] LT'; + } + }, + sameElse: 'L' + }, + relativeTime : { + future : "через %s", + past : "%s назад", + s : "несколько секунд", + m : relativeTimeWithPlural, + mm : relativeTimeWithPlural, + h : "час", + hh : relativeTimeWithPlural, + d : "день", + dd : relativeTimeWithPlural, + M : "месяц", + MM : relativeTimeWithPlural, + y : "год", + yy : relativeTimeWithPlural + }, + + ordinal: function (number, period) { + switch (period) { + case 'M': + case 'd': + case 'DDD': + return number + '-й'; + case 'D': + return number + '-го'; + case 'w': + case 'W': + return number + '-я'; + default: + return number; + } + }, + + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); diff --git a/vendor/assets/stylesheets/bootstrap.css b/vendor/assets/stylesheets/bootstrap.css index 27a4aa4bf..2a171d492 100644 --- a/vendor/assets/stylesheets/bootstrap.css +++ b/vendor/assets/stylesheets/bootstrap.css @@ -840,4 +840,297 @@ a.badge:hover { } .icon-remove-circle { background-position: -168px -96px; -} \ No newline at end of file +} +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.input-block-level { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.row { + margin-left: -20px; + *zoom: 1; +} +.row:before, +.row:after { + display: table; + content: ""; + line-height: 0; +} +.row:after { + clear: both; +} +[class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; +} +.container, +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} +.span12 { + width: 940px; +} +.span11 { + width: 860px; +} +.span10 { + width: 780px; +} +.span9 { + width: 700px; +} +.span8 { + width: 620px; +} +.span7 { + width: 540px; +} +.span6 { + width: 460px; +} +.span5 { + width: 380px; +} +.span4 { + width: 300px; +} +.span3 { + width: 220px; +} +.span2 { + width: 140px; +} +.span1 { + width: 60px; +} +.offset12 { + margin-left: 980px; +} +.offset11 { + margin-left: 900px; +} +.offset10 { + margin-left: 820px; +} +.offset9 { + margin-left: 740px; +} +.offset8 { + margin-left: 660px; +} +.offset7 { + margin-left: 580px; +} +.offset6 { + margin-left: 500px; +} +.offset5 { + margin-left: 420px; +} +.offset4 { + margin-left: 340px; +} +.offset3 { + margin-left: 260px; +} +.offset2 { + margin-left: 180px; +} +.offset1 { + margin-left: 100px; +} +.row-fluid { + width: 100%; + *zoom: 1; +} +.row-fluid:before, +.row-fluid:after { + display: table; + content: ""; + line-height: 0; +} +.row-fluid:after { + clear: both; +} +.row-fluid [class*="span"] { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + float: left; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; +} +.row-fluid [class*="span"]:first-child { + margin-left: 0; +} +.row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.127659574468085%; +} +.row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; +} +.row-fluid .span11 { + width: 91.48936170212765%; + *width: 91.43617021276594%; +} +.row-fluid .span10 { + width: 82.97872340425532%; + *width: 82.92553191489361%; +} +.row-fluid .span9 { + width: 74.46808510638297%; + *width: 74.41489361702126%; +} +.row-fluid .span8 { + width: 65.95744680851064%; + *width: 65.90425531914893%; +} +.row-fluid .span7 { + width: 57.44680851063829%; + *width: 57.39361702127659%; +} +.row-fluid .span6 { + width: 48.93617021276595%; + *width: 48.88297872340425%; +} +.row-fluid .span5 { + width: 40.42553191489362%; + *width: 40.37234042553192%; +} +.row-fluid .span4 { + width: 31.914893617021278%; + *width: 31.861702127659576%; +} +.row-fluid .span3 { + width: 23.404255319148934%; + *width: 23.351063829787233%; +} +.row-fluid .span2 { + width: 14.893617021276595%; + *width: 14.840425531914894%; +} +.row-fluid .span1 { + width: 6.382978723404255%; + *width: 6.329787234042553%; +} +.row-fluid .offset12 { + margin-left: 104.25531914893617%; + *margin-left: 104.14893617021275%; +} +.row-fluid .offset12:first-child { + margin-left: 102.12765957446808%; + *margin-left: 102.02127659574467%; +} +.row-fluid .offset11 { + margin-left: 95.74468085106382%; + *margin-left: 95.6382978723404%; +} +.row-fluid .offset11:first-child { + margin-left: 93.61702127659574%; + *margin-left: 93.51063829787232%; +} +.row-fluid .offset10 { + margin-left: 87.23404255319149%; + *margin-left: 87.12765957446807%; +} +.row-fluid .offset10:first-child { + margin-left: 85.1063829787234%; + *margin-left: 84.99999999999999%; +} +.row-fluid .offset9 { + margin-left: 78.72340425531914%; + *margin-left: 78.61702127659572%; +} +.row-fluid .offset9:first-child { + margin-left: 76.59574468085106%; + *margin-left: 76.48936170212764%; +} +.row-fluid .offset8 { + margin-left: 70.2127659574468%; + *margin-left: 70.10638297872339%; +} +.row-fluid .offset8:first-child { + margin-left: 68.08510638297872%; + *margin-left: 67.9787234042553%; +} +.row-fluid .offset7 { + margin-left: 61.70212765957446%; + *margin-left: 61.59574468085106%; +} +.row-fluid .offset7:first-child { + margin-left: 59.574468085106375%; + *margin-left: 59.46808510638297%; +} +.row-fluid .offset6 { + margin-left: 53.191489361702125%; + *margin-left: 53.085106382978715%; +} +.row-fluid .offset6:first-child { + margin-left: 51.063829787234035%; + *margin-left: 50.95744680851063%; +} +.row-fluid .offset5 { + margin-left: 44.68085106382979%; + *margin-left: 44.57446808510638%; +} +.row-fluid .offset5:first-child { + margin-left: 42.5531914893617%; + *margin-left: 42.4468085106383%; +} +.row-fluid .offset4 { + margin-left: 36.170212765957444%; + *margin-left: 36.06382978723405%; +} +.row-fluid .offset4:first-child { + margin-left: 34.04255319148936%; + *margin-left: 33.93617021276596%; +} +.row-fluid .offset3 { + margin-left: 27.659574468085104%; + *margin-left: 27.5531914893617%; +} +.row-fluid .offset3:first-child { + margin-left: 25.53191489361702%; + *margin-left: 25.425531914893618%; +} +.row-fluid .offset2 { + margin-left: 19.148936170212764%; + *margin-left: 19.04255319148936%; +} +.row-fluid .offset2:first-child { + margin-left: 17.02127659574468%; + *margin-left: 16.914893617021278%; +} +.row-fluid .offset1 { + margin-left: 10.638297872340425%; + *margin-left: 10.53191489361702%; +} +.row-fluid .offset1:first-child { + margin-left: 8.51063829787234%; + *margin-left: 8.404255319148938%; +} +[class*="span"].hide, +.row-fluid [class*="span"].hide { + display: none; +} +[class*="span"].pull-right, +.row-fluid [class*="span"].pull-right { + float: right; +}