diff --git a/.travis.yml b/.travis.yml index 38104caec..34b265377 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: ruby rvm: - - 2.1.1 + - 2.1.3 bundler_args: --without development env: - SPEC_GROUP=controllers diff --git a/Gemfile b/Gemfile index 4114eb8b7..e5207bafa 100644 --- a/Gemfile +++ b/Gemfile @@ -1,14 +1,14 @@ source 'https://rubygems.org' -gem 'rails', '4.0.5' +gem 'rails', '4.0.9' gem 'activeadmin', github: 'gregbell/active_admin' gem 'pg', '~> 0.17.1' -gem 'schema_plus', '~> 1.4.0' +gem 'schema_plus', '~> 1.5' ######## gem 'protected_attributes' ######## -gem 'devise', '~> 3.2.3' +gem 'devise', '~> 3.3' gem 'omniauth' gem 'omniauth-facebook' gem 'omniauth-google-oauth2' @@ -17,10 +17,10 @@ gem 'omniauth-github' # gem 'cancan', '1.6.10' gem 'cancan', git: 'git://github.com/rosa-abf/cancan.git', tag: '1.6.10-abf' -gem 'ancestry', '~> 2.0.0' -gem 'paperclip', '~> 4.1.1' +gem 'ancestry', '~> 2.1' +gem 'paperclip', '~> 4.2' gem 'resque', '~> 1.25' -gem 'resque-status', '~> 0.4.2' +gem 'resque-status', '~> 0.4' gem 'resque_mailer', '~> 2.2' gem 'resque-scheduler', '~> 2.5.4' gem 'perform_later', git: 'git://github.com/KensoDev/perform_later.git' # should be after resque_mailer @@ -30,14 +30,14 @@ gem 'state_machine', '~> 1.2' gem 'redis-rails', '~> 4.0' gem 'grack', git: 'git://github.com/rosa-abf/grack.git', require: 'git_http' -gem 'grit', git: 'git://github.com/rosa-abf/grit.git' -gem 'charlock_holmes', '~> 0.6.9' -gem 'github-linguist', '~> 2.10', require: 'linguist' +gem 'grit', git: 'git://github.com/rosa-abf/grit.git', tag: '2.6.16' +gem 'charlock_holmes', '~> 0.7' +gem 'github-linguist', '~> 3.1', require: 'linguist' gem 'diff-display', '~> 0.0.1' # Wiki -gem "gollum-lib", '~> 2.0' -gem "redcarpet", '~> 3.1.1' +gem "gollum-lib", '~> 3.0' +gem "redcarpet", '~> 3.1' gem 'creole' gem 'rdiscount' # gem 'org-ruby' @@ -47,25 +47,25 @@ gem 'wikicloth' gem 'newrelic_rpm' gem 'whenever', '~> 0.9.0', require: false -gem 'jbuilder', '~> 2.0.3' +gem 'jbuilder', '~> 2.1' gem 'rails3-jquery-autocomplete', '~> 1.0' -gem 'will_paginate', '~> 3.0.5' -gem 'meta-tags', '~> 1.5', require: 'meta_tags' +gem 'will_paginate', '~> 3.0' +gem 'meta-tags', '~> 2.0', require: 'meta_tags' gem "haml-rails", '~> 0.5' -gem 'jquery-rails', '~> 2.3.0' +gem 'jquery-rails', '~> 2.3' gem 'jquery-migrate-rails' gem 'ruby-haml-js', '~> 0.0.5' -gem 'friendly_id', '~> 5.0.3' +gem 'friendly_id', '~> 5.0' gem 'rack-throttle', '~> 0.3.0' -gem 'rest-client', '~> 1.6.7' -gem 'ohm', '~> 1.3.2' +gem 'rest-client', '~> 1.7' +gem 'ohm', '~> 1.3.2' # Ohm 2 breaks the compatibility with previous versions. gem 'ohm-expire', '~> 0.1.3' gem 'ffi', '~> 1.9.3' -gem 'attr_encrypted', '~> 1.3.2' -gem "gemoji", "~> 1.2.1", require: 'emoji/railtie' +gem 'attr_encrypted', '~> 1.3' +gem "gemoji", "~> 2.1" # AngularJS related stuff gem 'underscore-rails' @@ -81,26 +81,24 @@ gem 'ngmin-rails' gem 'time_diff' -gem 'sass-rails', '~> 4.0.1' -gem 'coffee-rails', '~> 4.0.1' -gem 'compass-rails', '~> 1.1.6' +gem 'sass-rails', '~> 4.0' +gem 'coffee-rails', '~> 4.1' +gem 'bootstrap-sass', '~> 3.2' +gem 'font-awesome-rails', '~> 4.2' +gem 'zeroclipboard-rails', '~> 0.1.0' + +gem 'compass-rails', '~> 2.0' gem 'uglifier', '~> 2.5' gem 'therubyracer', '~> 0.12.1', platforms: [:mri, :rbx] -gem 'therubyrhino', '~> 1.73.1', platforms: :jruby - -gem 'bootstrap-sass', '~> 3.1.1' -gem 'font-awesome-rails', '~> 4.1' -gem 'codemirror-rails' - +gem 'therubyrhino', '~> 2.0', platforms: :jruby gem 'sitemap_generator' -gem 'zeroclipboard-rails' +gem 'codemirror-rails', '~> 4.5' + group :production do - gem "airbrake", '~> 3.1.2' - gem 'bluepill', '~> 0.0.60', require: false - # gem 'le' - gem 'logglier' + gem "airbrake", '~> 3.1' + #gem 'bluepill', '~> 0.0.60', require: false gem 'puma' end @@ -133,6 +131,7 @@ group :test do gem 'shoulda' gem 'shoulda-matchers' gem 'mock_redis', '~> 0.11' + gem 'webmock' gem 'rake' gem 'test_after_commit' gem 'timecop' diff --git a/Gemfile.lock b/Gemfile.lock index df0485453..9be1acfb0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,19 +10,18 @@ GIT GIT remote: git://github.com/gregbell/active_admin.git - revision: 5ac883c59978799829a8c085fab47f858574c42f + revision: 5e9917a5c85d0697114ce9f6166da48595c4d1ba specs: activeadmin (1.0.0.pre) - arbre (~> 1.0) + arbre (~> 1.0, >= 1.0.2) bourbon coffee-rails - devise (~> 3.2) - formtastic (~> 2.3.0.rc2) - inherited_resources (~> 1.3) + formtastic (~> 2.3.0.rc3) + inherited_resources (~> 1.4.1) jquery-rails - jquery-ui-rails + jquery-ui-rails (~> 5.0) kaminari (~> 0.15) - rails (>= 3.2, <= 4.1) + rails (>= 3.2, < 4.2) ransack (~> 1.0) sass-rails @@ -41,7 +40,8 @@ GIT GIT remote: git://github.com/rosa-abf/grit.git - revision: baa333d93615085a8d2304d1f5135bf48f37ed3d + revision: a9548c92188cc307e7af1dd41a733e7000a783a9 + tag: 2.6.16 specs: grit (2.5.0) diff-lcs (~> 1.1) @@ -52,63 +52,59 @@ GEM remote: https://rubygems.org/ specs: RedCloth (4.2.9) - actionmailer (4.0.5) - actionpack (= 4.0.5) + actionmailer (4.0.9) + actionpack (= 4.0.9) mail (~> 2.5.4) - actionpack (4.0.5) - activesupport (= 4.0.5) + actionpack (4.0.9) + activesupport (= 4.0.9) builder (~> 3.1.0) erubis (~> 2.7.0) rack (~> 1.5.2) rack-test (~> 0.6.2) - activemodel (4.0.5) - activesupport (= 4.0.5) + activemodel (4.0.9) + activesupport (= 4.0.9) builder (~> 3.1.0) - activerecord (4.0.5) - activemodel (= 4.0.5) + activerecord (4.0.9) + activemodel (= 4.0.9) activerecord-deprecated_finders (~> 1.0.2) - activesupport (= 4.0.5) + activesupport (= 4.0.9) arel (~> 4.0.0) activerecord-deprecated_finders (1.0.3) - activesupport (4.0.5) + activesupport (4.0.9) i18n (~> 0.6, >= 0.6.9) minitest (~> 4.2) multi_json (~> 1.3) thread_safe (~> 0.1) tzinfo (~> 0.3.37) - airbrake (3.1.16) + addressable (2.3.6) + airbrake (3.2.1) builder multi_json - ancestry (2.0.0) + ancestry (2.1.0) activerecord (>= 3.0.0) angular-i18n (0.1.2) - angular-rails-templates (0.0.7) + angular-rails-templates (0.1.3) railties (>= 3.1) sprockets - angular-ui-bootstrap-rails (0.10.0) - angularjs-rails (1.2.15) - arbre (1.0.1) + tilt + angular-ui-bootstrap-rails (0.11.2) + angularjs-rails (1.2.22) + arbre (1.0.2) activesupport (>= 3.0.0) arel (4.0.2) - attr_encrypted (1.3.2) + attr_encrypted (1.3.3) encryptor (>= 1.3.0) bcrypt (3.1.7) - bcrypt-ruby (3.1.5) - bcrypt (>= 3.1.3) - better_errors (1.1.0) + better_errors (2.0.0) coderay (>= 1.0.0) erubis (>= 2.6.6) + rack (>= 0.9.0) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) - bluepill (0.0.64) - activesupport (>= 3.0.0) - daemons (~> 1.1.4) - i18n (>= 0.5.0) - state_machine (~> 1.1) - bootstrap-sass (3.1.1.0) + bootstrap-sass (3.2.0.2) + sass (~> 3.2) + bourbon (3.2.3) sass (~> 3.2) - bourbon (3.1.8) - sass (>= 3.2.0) thor builder (3.1.4) callsite (0.0.11) @@ -120,34 +116,36 @@ GEM net-ssh (>= 2.0.14) net-ssh-gateway (>= 1.1.0) capistrano_colors (0.5.5) - charlock_holmes (0.6.9.4) + charlock_holmes (0.7.3) chronic (0.10.2) - chunky_png (1.3.0) + chunky_png (1.3.1) climate_control (0.0.3) activesupport (>= 3.0) - cocaine (0.5.3) + cocaine (0.5.4) climate_control (>= 0.0.3, < 1.0) - codemirror-rails (4.0) + codemirror-rails (4.5) railties (>= 3.0, < 5) coderay (1.1.0) - coffee-rails (4.0.1) + coffee-rails (4.1.0) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.0) - coffee-script (2.2.0) + coffee-script (2.3.0) coffee-script-source execjs - coffee-script-source (1.7.0) - compass (0.12.3) + coffee-script-source (1.8.0) + compass (0.12.7) chunky_png (~> 1.2) fssm (>= 0.2.7) - sass (= 3.2.14) - compass-rails (1.1.6) + sass (~> 3.2.19) + compass-rails (2.0.0) compass (>= 0.12.2) + crack (0.4.2) + safe_yaml (~> 1.0.0) creole (0.5.0) daemons (1.1.9) debug_inspector (0.0.2) - devise (3.2.3) - bcrypt-ruby (~> 3.0) + devise (3.3.0) + bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 3.2.6, < 5) thread_safe (~> 0.1) @@ -158,7 +156,7 @@ GEM erubis (2.7.0) escape_utils (1.0.1) eventmachine (1.0.3) - execjs (2.0.2) + execjs (2.2.2) expression_parser (0.9.0) factory_girl (4.4.0) activesupport (>= 3.0.0) @@ -168,33 +166,35 @@ GEM faraday (0.9.0) multipart-post (>= 1.2, < 3) ffi (1.9.3) - font-awesome-rails (4.1.0.0) + font-awesome-rails (4.2.0.0) railties (>= 3.2, < 5.0) - formtastic (2.3.0.rc2) + formtastic (2.3.0) actionpack (>= 3.0) - friendly_id (5.0.3) + friendly_id (5.0.4) activerecord (>= 4.0.0) fssm (0.2.10) - gemoji (1.2.1) - github-linguist (2.10.11) - charlock_holmes (~> 0.6.6) - escape_utils (>= 0.3.1) + gemoji (2.1.0) + github-linguist (3.1.2) + charlock_holmes (~> 0.7.3) + escape_utils (~> 1.0.1) mime-types (~> 1.19) - pygments.rb (~> 0.5.4) - github-markup (0.7.5) - gitlab-grit (2.6.0) - charlock_holmes (~> 0.6.9) + pygments.rb (~> 0.6.0) + rugged (~> 0.21.0) + github-markup (1.1.2) + posix-spawn (~> 0.3.8) + gitlab-grit (2.6.10) + charlock_holmes (~> 0.6) diff-lcs (~> 1.1) mime-types (~> 1.15) - posix-spawn (~> 0.3.6) - gli (2.9.0) - gollum-lib (2.0.0) - github-markup (>= 0.7.5, < 1.0.0) - gitlab-grit (= 2.6.0) - nokogiri (~> 1.6.0) - rouge (~> 1.3.1) - sanitize (~> 2.0.6) - stringex (~> 2.1.0) + posix-spawn (~> 0.3) + gli (2.12.0) + gollum-lib (3.0.0) + github-markup (~> 1.1.0) + gitlab-grit (~> 2.6.5) + nokogiri (~> 1.6.1) + rouge (~> 1.3.3) + sanitize (~> 2.1.0) + stringex (~> 2.5.1) haml (4.0.5) tilt haml-rails (0.5.3) @@ -205,41 +205,39 @@ GEM has_scope (0.6.0.rc) actionpack (>= 3.2, < 5) activesupport (>= 3.2, < 5) - hashie (2.0.5) + hashie (3.3.1) highline (1.6.21) hike (1.2.3) - hirb (0.7.1) - i18n (0.6.9) + hirb (0.7.2) + i18n (0.6.11) inherited_resources (1.4.1) has_scope (~> 0.6.0.rc) responders (~> 1.0.0.rc) - jbuilder (2.0.4) - activesupport (>= 3.0.0) - multi_json (>= 1.2.0) + jbuilder (2.1.3) + activesupport (>= 3.0.0, < 5) + multi_json (~> 1.2) jquery-migrate-rails (1.2.1) jquery-rails (2.3.0) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) - jquery-ui-rails (4.2.0) + jquery-ui-rails (5.0.0) railties (>= 3.2.16) - js-routes (0.9.7) + js-routes (0.9.9) railties (>= 3.2) sprockets-rails json (1.8.1) - jwt (0.1.11) - multi_json (>= 1.5) - kaminari (0.15.1) + jwt (1.0.0) + kaminari (0.16.1) actionpack (>= 3.0.0) activesupport (>= 3.0.0) libv8 (3.16.14.3) - localeapp (0.7.1) + localeapp (0.8.1) gli i18n json rack rest-client ya2yaml - logglier (0.2.11) mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) @@ -253,40 +251,41 @@ GEM skinny (>= 0.1.2) sqlite3-ruby thin - meta-tags (1.5.0) - actionpack - meta_request (0.2.8) - callsite - rack-contrib - railties + meta-tags (2.0.0) + actionpack (>= 3.0.0) + meta_request (0.3.4) + callsite (~> 0.0, >= 0.0.11) + rack-contrib (~> 1.1) + railties (>= 3.0.0, < 5.0.0) mime-types (1.25.1) - mini_portile (0.5.2) + mini_portile (0.6.0) minitest (4.7.5) - mock_redis (0.11.0) - momentjs-rails (2.5.1) + mock_redis (0.13.2) + momentjs-rails (2.8.1) railties (>= 3.1) mono_logger (1.1.0) - multi_json (1.10.0) + multi_json (1.10.1) multi_xml (0.5.5) multipart-post (2.0.0) nest (1.1.2) redis - net-scp (1.1.2) + net-scp (1.2.1) net-ssh (>= 2.6.5) net-sftp (2.1.2) net-ssh (>= 2.6.5) - net-ssh (2.8.0) + net-ssh (2.9.1) net-ssh-gateway (1.2.0) net-ssh (>= 2.6.5) - newrelic_rpm (3.7.3.204) + netrc (0.7.7) + newrelic_rpm (3.9.3.241) ng-rails-csrf (0.1.0) ngmin-rails (0.4.0) rails (>= 3.1) - nokogiri (1.6.1) - mini_portile (~> 0.5.0) - oauth2 (0.9.3) + nokogiri (1.6.3.1) + mini_portile (= 0.6.0) + oauth2 (1.0.0) faraday (>= 0.8, < 0.10) - jwt (~> 0.1.8) + jwt (~> 1.0) multi_json (~> 1.3) multi_xml (~> 0.5) rack (~> 1.2) @@ -296,75 +295,75 @@ GEM scrivener (~> 0.0.3) ohm-expire (0.1.3.2) ohm (>= 0.1.5) - omniauth (1.2.1) - hashie (>= 1.2, < 3) + omniauth (1.2.2) + hashie (>= 1.2, < 4) rack (~> 1.0) - omniauth-facebook (1.6.0) - omniauth-oauth2 (~> 1.1) - omniauth-github (1.1.1) + omniauth-facebook (2.0.0) + omniauth-oauth2 (~> 1.2) + omniauth-github (1.1.2) omniauth (~> 1.0) omniauth-oauth2 (~> 1.1) - omniauth-google-oauth2 (0.2.2) - omniauth (~> 1.0) - omniauth-oauth2 - omniauth-oauth2 (1.1.2) + omniauth-google-oauth2 (0.2.5) + omniauth (> 1.0) + omniauth-oauth2 (~> 1.1) + omniauth-oauth2 (1.2.0) faraday (>= 0.8, < 0.10) multi_json (~> 1.3) - oauth2 (~> 0.9.3) + oauth2 (~> 1.0) omniauth (~> 1.2) orm_adapter (0.5.0) - paperclip (4.1.1) + paperclip (4.2.0) activemodel (>= 3.0.0) activesupport (>= 3.0.0) cocaine (~> 0.5.3) mime-types pg (0.17.1) - polyamorous (0.6.4) + polyamorous (1.1.0) activerecord (>= 3.0) - polyglot (0.3.4) - posix-spawn (0.3.8) - protected_attributes (1.0.7) + polyglot (0.3.5) + posix-spawn (0.3.9) + protected_attributes (1.0.8) activemodel (>= 4.0.1, < 5.0) - puma (2.8.2) + puma (2.9.1) rack (>= 1.1, < 2.0) - pygments.rb (0.5.4) + pygments.rb (0.6.0) posix-spawn (~> 0.3.6) yajl-ruby (~> 1.1.0) rack (1.5.2) rack-contrib (1.1.0) rack (>= 0.9.1) - rack-protection (1.5.2) + rack-protection (1.5.3) rack rack-test (0.6.2) rack (>= 1.0) rack-throttle (0.3.0) rack (>= 1.0.0) - rails (4.0.5) - actionmailer (= 4.0.5) - actionpack (= 4.0.5) - activerecord (= 4.0.5) - activesupport (= 4.0.5) + rails (4.0.9) + actionmailer (= 4.0.9) + actionpack (= 4.0.9) + activerecord (= 4.0.9) + activesupport (= 4.0.9) bundler (>= 1.3.0, < 2.0) - railties (= 4.0.5) - sprockets-rails (~> 2.0.0) + railties (= 4.0.9) + sprockets-rails (~> 2.0) rails3-generators (1.0.0) railties (>= 3.0.0) - rails3-jquery-autocomplete (1.0.12) + rails3-jquery-autocomplete (1.0.14) rails (>= 3.0) - railties (4.0.5) - actionpack (= 4.0.5) - activesupport (= 4.0.5) + railties (4.0.9) + actionpack (= 4.0.9) + activesupport (= 4.0.9) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rake (10.3.1) - ransack (1.2.1) + rake (10.3.2) + ransack (1.3.0) actionpack (>= 3.0) activerecord (>= 3.0) activesupport (>= 3.0) i18n - polyamorous (~> 0.6.0) - rdiscount (2.1.7) - redcarpet (3.1.1) + polyamorous (~> 1.1) + rdiscount (2.1.7.1) + redcarpet (3.1.2) redis (3.0.7) redis-actionpack (4.0.0) actionpack (~> 4) @@ -373,8 +372,8 @@ GEM redis-activesupport (4.0.0) activesupport (~> 4) redis-store (~> 1.1.0) - redis-namespace (1.4.1) - redis (~> 3.0.4) + redis-namespace (1.5.1) + redis (~> 3.0, >= 3.0.4) redis-rack (1.5.0) rack (~> 1.5) redis-store (~> 1.1.0) @@ -398,20 +397,21 @@ GEM redis (~> 3.0.4) resque (~> 1.25.1) rufus-scheduler (~> 2.0.24) - resque-status (0.4.2) + resque-status (0.4.3) resque (~> 1.19) resque_mailer (2.2.6) actionmailer (>= 3.0) - rest-client (1.6.7) - mime-types (>= 1.16) + rest-client (1.7.2) + mime-types (>= 1.16, < 3.0) + netrc (~> 0.7) rinku (1.7.3) - rouge (1.3.3) + rouge (1.3.4) rr (1.1.2) rspec-core (2.14.8) rspec-expectations (2.14.5) diff-lcs (>= 1.1.3, < 2.0) rspec-mocks (2.14.6) - rspec-rails (2.14.1) + rspec-rails (2.14.2) actionpack (>= 3.0) activemodel (>= 3.0) activesupport (>= 3.0) @@ -424,20 +424,22 @@ GEM sprockets (>= 2.0.0) rufus-scheduler (2.0.24) tzinfo (>= 0.3.22) + rugged (0.21.0) russian (0.6.0) i18n (>= 0.5.0) - rvm-capistrano (1.5.1) + rvm-capistrano (1.5.3) capistrano (~> 2.15.4) - sanitize (2.0.6) + safe_yaml (1.0.3) + sanitize (2.1.0) nokogiri (>= 1.4.4) - sass (3.2.14) + sass (3.2.19) sass-rails (4.0.2) railties (>= 4.0.0, < 5.0) sass (~> 3.2.0) sprockets (~> 2.8, <= 2.11.0) sprockets-rails (~> 2.0.0) - schema_plus (1.4.1) - rails (>= 3.2) + schema_plus (1.5.3) + activerecord (>= 3.2) valuable scrivener (0.0.3) shotgun (0.9) @@ -445,21 +447,21 @@ GEM shoulda (3.5.0) shoulda-context (~> 1.0, >= 1.0.1) shoulda-matchers (>= 1.4.1, < 3.0) - shoulda-context (1.1.6) - shoulda-matchers (2.5.0) + shoulda-context (1.2.1) + shoulda-matchers (2.6.2) activesupport (>= 3.0.0) - sinatra (1.4.4) + sinatra (1.4.5) rack (~> 1.4) rack-protection (~> 1.4) tilt (~> 1.3, >= 1.3.4) - sitemap_generator (5.0.1) + sitemap_generator (5.0.5) builder skinny (0.2.3) eventmachine (~> 1.0.0) thin (~> 1.5.0) - skype (0.2.7) + skype (0.2.8) tmp_cache - soundmanager-rails (1.0.0) + soundmanager-rails (1.0.1) sprockets (2.11.0) hike (~> 1.2) multi_json (~> 1.0) @@ -473,8 +475,9 @@ GEM sqlite3-ruby (1.3.3) sqlite3 (>= 1.3.3) state_machine (1.2.0) - stringex (2.1.2) - test_after_commit (0.2.3) + stringex (2.5.2) + test_after_commit (0.2.5) + activerecord (>= 3.2) therubyracer (0.12.1) libv8 (~> 3.16.14.0) ref @@ -483,7 +486,7 @@ GEM eventmachine (>= 0.12.6) rack (>= 1.0.0) thor (0.19.1) - thread_safe (0.3.3) + thread_safe (0.3.4) tilt (1.4.1) time_diff (0.3.0) activesupport @@ -493,16 +496,19 @@ GEM treetop (1.4.15) polyglot polyglot (>= 0.3.1) - tzinfo (0.3.39) - uglifier (2.5.0) + tzinfo (0.3.41) + uglifier (2.5.3) execjs (>= 0.3.0) json (>= 1.8.0) - underscore-rails (1.6.0) + underscore-rails (1.7.0) valuable (0.9.8) vegas (0.1.11) rack (>= 1.0.0) warden (1.2.3) rack (>= 1.0) + webmock (1.18.0) + addressable (>= 2.3.6) + crack (>= 0.3.2) whenever (0.9.2) activesupport (>= 2.3.4) chronic (>= 0.6.3) @@ -510,7 +516,7 @@ GEM builder expression_parser rinku - will_paginate (3.0.5) + will_paginate (3.0.7) ya2yaml (0.31) yajl-ruby (1.1.0) zeroclipboard-rails (0.1.0) @@ -522,48 +528,46 @@ PLATFORMS DEPENDENCIES RedCloth activeadmin! - airbrake (~> 3.1.2) - ancestry (~> 2.0.0) + airbrake (~> 3.1) + ancestry (~> 2.1) angular-i18n (= 0.1.2) angular-rails-templates angular-ui-bootstrap-rails angularjs-rails (~> 1.2.15) - attr_encrypted (~> 1.3.2) + attr_encrypted (~> 1.3) better_errors binding_of_caller - bluepill (~> 0.0.60) - bootstrap-sass (~> 3.1.1) + bootstrap-sass (~> 3.2) cancan! cape capistrano capistrano_colors - charlock_holmes (~> 0.6.9) - codemirror-rails - coffee-rails (~> 4.0.1) - compass-rails (~> 1.1.6) + charlock_holmes (~> 0.7) + codemirror-rails (~> 4.5) + coffee-rails (~> 4.1) + compass-rails (~> 2.0) creole - devise (~> 3.2.3) + devise (~> 3.3) diff-display (~> 0.0.1) factory_girl_rails (~> 4.4.1) ffi (~> 1.9.3) - font-awesome-rails (~> 4.1) - friendly_id (~> 5.0.3) - gemoji (~> 1.2.1) - github-linguist (~> 2.10) - gollum-lib (~> 2.0) + font-awesome-rails (~> 4.2) + friendly_id (~> 5.0) + gemoji (~> 2.1) + github-linguist (~> 3.1) + gollum-lib (~> 3.0) grack! grit! haml-rails (~> 0.5) highline (~> 1.6.20) hirb - jbuilder (~> 2.0.3) + jbuilder (~> 2.1) jquery-migrate-rails - jquery-rails (~> 2.3.0) + jquery-rails (~> 2.3) js-routes localeapp - logglier mailcatcher - meta-tags (~> 1.5) + meta-tags (~> 2.0) meta_request mock_redis (~> 0.11) momentjs-rails @@ -576,31 +580,31 @@ DEPENDENCIES omniauth-facebook omniauth-github omniauth-google-oauth2 - paperclip (~> 4.1.1) + paperclip (~> 4.2) perform_later! pg (~> 0.17.1) protected_attributes puma rack-throttle (~> 0.3.0) - rails (= 4.0.5) + rails (= 4.0.9) rails3-generators rails3-jquery-autocomplete (~> 1.0) rake rdiscount - redcarpet (~> 3.1.1) + redcarpet (~> 3.1) redis-rails (~> 4.0) resque (~> 1.25) resque-scheduler (~> 2.5.4) - resque-status (~> 0.4.2) + resque-status (~> 0.4) resque_mailer (~> 2.2) - rest-client (~> 1.6.7) + rest-client (~> 1.7) rr (~> 1.1.2) rspec-rails (~> 2.14.1) ruby-haml-js (~> 0.0.5) russian (~> 0.6.0) rvm-capistrano - sass-rails (~> 4.0.1) - schema_plus (~> 1.4.0) + sass-rails (~> 4.0) + schema_plus (~> 1.5) shotgun shoulda shoulda-matchers @@ -610,12 +614,13 @@ DEPENDENCIES state_machine (~> 1.2) test_after_commit therubyracer (~> 0.12.1) - therubyrhino (~> 1.73.1) + therubyrhino (~> 2.0) time_diff timecop uglifier (~> 2.5) underscore-rails + webmock whenever (~> 0.9.0) wikicloth - will_paginate (~> 3.0.5) - zeroclipboard-rails + will_paginate (~> 3.0) + zeroclipboard-rails (~> 0.1.0) diff --git a/Rakefile b/Rakefile index 562c9b3d7..d36b66ef1 100644 --- a/Rakefile +++ b/Rakefile @@ -5,5 +5,6 @@ require File.expand_path('../config/application', __FILE__) require 'rake' require 'resque/tasks' require 'resque_scheduler/tasks' +load 'tasks/emoji.rake' Rosa::Application.load_tasks diff --git a/app/admin/build_scripts.rb b/app/admin/build_scripts.rb index 59e720c17..1113a6718 100644 --- a/app/admin/build_scripts.rb +++ b/app/admin/build_scripts.rb @@ -23,7 +23,7 @@ ActiveAdmin.register BuildScript do end column :updated_at - default_actions + actions end show do diff --git a/app/admin/event_logs.rb b/app/admin/event_logs.rb index 2359d6aba..e6b8c2e75 100644 --- a/app/admin/event_logs.rb +++ b/app/admin/event_logs.rb @@ -31,7 +31,7 @@ ActiveAdmin.register EventLog do end - default_actions + actions end end diff --git a/app/admin/flash_notifies.rb b/app/admin/flash_notifies.rb index 957e0429e..5953676c2 100644 --- a/app/admin/flash_notifies.rb +++ b/app/admin/flash_notifies.rb @@ -12,7 +12,7 @@ ActiveAdmin.register FlashNotify do end column :published - default_actions + actions end form do |f| diff --git a/app/admin/node_instructions.rb b/app/admin/node_instructions.rb index 8644ed153..71cf7ef30 100644 --- a/app/admin/node_instructions.rb +++ b/app/admin/node_instructions.rb @@ -21,7 +21,7 @@ ActiveAdmin.register NodeInstruction do end column :updated_at - default_actions + actions end form do |f| diff --git a/app/admin/register_requests.rb b/app/admin/register_requests.rb index f2374d391..6fd19f1c3 100644 --- a/app/admin/register_requests.rb +++ b/app/admin/register_requests.rb @@ -14,7 +14,7 @@ ActiveAdmin.register RegisterRequest do column :more column :created_at - default_actions + actions end end diff --git a/app/admin/users.rb b/app/admin/users.rb index a5d7c0e6b..f9bd9660d 100644 --- a/app/admin/users.rb +++ b/app/admin/users.rb @@ -26,7 +26,7 @@ ActiveAdmin.register User do column :created_at column :role - default_actions + actions end form do |f| diff --git a/app/assets/images/emoji/+1.png b/app/assets/images/emoji/+1.png deleted file mode 100644 index 3a43ecae2..000000000 Binary files a/app/assets/images/emoji/+1.png and /dev/null differ diff --git a/app/assets/images/emoji/-1.png b/app/assets/images/emoji/-1.png deleted file mode 100644 index e44c04219..000000000 Binary files a/app/assets/images/emoji/-1.png and /dev/null differ diff --git a/app/assets/images/emoji/100.png b/app/assets/images/emoji/100.png deleted file mode 100644 index bce9ab14f..000000000 Binary files a/app/assets/images/emoji/100.png and /dev/null differ diff --git a/app/assets/images/emoji/1234.png b/app/assets/images/emoji/1234.png deleted file mode 100644 index c47c2e1f9..000000000 Binary files a/app/assets/images/emoji/1234.png and /dev/null differ diff --git a/app/assets/images/emoji/8ball.png b/app/assets/images/emoji/8ball.png deleted file mode 100644 index c2c710d45..000000000 Binary files a/app/assets/images/emoji/8ball.png and /dev/null differ diff --git a/app/assets/images/emoji/a.png b/app/assets/images/emoji/a.png deleted file mode 100644 index 4908a44fc..000000000 Binary files a/app/assets/images/emoji/a.png and /dev/null differ diff --git a/app/assets/images/emoji/ab.png b/app/assets/images/emoji/ab.png deleted file mode 100644 index 2a5222047..000000000 Binary files a/app/assets/images/emoji/ab.png and /dev/null differ diff --git a/app/assets/images/emoji/abc.png b/app/assets/images/emoji/abc.png deleted file mode 100644 index 505d40a15..000000000 Binary files a/app/assets/images/emoji/abc.png and /dev/null differ diff --git a/app/assets/images/emoji/abcd.png b/app/assets/images/emoji/abcd.png deleted file mode 100644 index 5218470b6..000000000 Binary files a/app/assets/images/emoji/abcd.png and /dev/null differ diff --git a/app/assets/images/emoji/accept.png b/app/assets/images/emoji/accept.png deleted file mode 100644 index 2d2009031..000000000 Binary files a/app/assets/images/emoji/accept.png and /dev/null differ diff --git a/app/assets/images/emoji/aerial_tramway.png b/app/assets/images/emoji/aerial_tramway.png deleted file mode 100644 index 38f6dfe23..000000000 Binary files a/app/assets/images/emoji/aerial_tramway.png and /dev/null differ diff --git a/app/assets/images/emoji/airplane.png b/app/assets/images/emoji/airplane.png deleted file mode 100644 index 8407cb675..000000000 Binary files a/app/assets/images/emoji/airplane.png and /dev/null differ diff --git a/app/assets/images/emoji/alarm_clock.png b/app/assets/images/emoji/alarm_clock.png deleted file mode 100644 index 86ca8c8ed..000000000 Binary files a/app/assets/images/emoji/alarm_clock.png and /dev/null differ diff --git a/app/assets/images/emoji/alien.png b/app/assets/images/emoji/alien.png deleted file mode 100644 index e3fd76a78..000000000 Binary files a/app/assets/images/emoji/alien.png and /dev/null differ diff --git a/app/assets/images/emoji/ambulance.png b/app/assets/images/emoji/ambulance.png deleted file mode 100644 index b740f45db..000000000 Binary files a/app/assets/images/emoji/ambulance.png and /dev/null differ diff --git a/app/assets/images/emoji/anchor.png b/app/assets/images/emoji/anchor.png deleted file mode 100644 index 0c5192e64..000000000 Binary files a/app/assets/images/emoji/anchor.png and /dev/null differ diff --git a/app/assets/images/emoji/angel.png b/app/assets/images/emoji/angel.png deleted file mode 100644 index da52c310c..000000000 Binary files a/app/assets/images/emoji/angel.png and /dev/null differ diff --git a/app/assets/images/emoji/anger.png b/app/assets/images/emoji/anger.png deleted file mode 100644 index 6fb4dca18..000000000 Binary files a/app/assets/images/emoji/anger.png and /dev/null differ diff --git a/app/assets/images/emoji/angry.png b/app/assets/images/emoji/angry.png deleted file mode 100644 index 34174f5e5..000000000 Binary files a/app/assets/images/emoji/angry.png and /dev/null differ diff --git a/app/assets/images/emoji/anguished.png b/app/assets/images/emoji/anguished.png deleted file mode 100644 index c2edad796..000000000 Binary files a/app/assets/images/emoji/anguished.png and /dev/null differ diff --git a/app/assets/images/emoji/ant.png b/app/assets/images/emoji/ant.png deleted file mode 100644 index b92d1cc14..000000000 Binary files a/app/assets/images/emoji/ant.png and /dev/null differ diff --git a/app/assets/images/emoji/apple.png b/app/assets/images/emoji/apple.png deleted file mode 100644 index 08aa17b95..000000000 Binary files a/app/assets/images/emoji/apple.png and /dev/null differ diff --git a/app/assets/images/emoji/aquarius.png b/app/assets/images/emoji/aquarius.png deleted file mode 100644 index cbff66edc..000000000 Binary files a/app/assets/images/emoji/aquarius.png and /dev/null differ diff --git a/app/assets/images/emoji/aries.png b/app/assets/images/emoji/aries.png deleted file mode 100644 index d676fd392..000000000 Binary files a/app/assets/images/emoji/aries.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_backward.png b/app/assets/images/emoji/arrow_backward.png deleted file mode 100644 index 2be422ba3..000000000 Binary files a/app/assets/images/emoji/arrow_backward.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_double_down.png b/app/assets/images/emoji/arrow_double_down.png deleted file mode 100644 index 2ecbebcda..000000000 Binary files a/app/assets/images/emoji/arrow_double_down.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_double_up.png b/app/assets/images/emoji/arrow_double_up.png deleted file mode 100644 index d42979d4b..000000000 Binary files a/app/assets/images/emoji/arrow_double_up.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_down.png b/app/assets/images/emoji/arrow_down.png deleted file mode 100644 index 3956eb399..000000000 Binary files a/app/assets/images/emoji/arrow_down.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_down_small.png b/app/assets/images/emoji/arrow_down_small.png deleted file mode 100644 index f7f2d5101..000000000 Binary files a/app/assets/images/emoji/arrow_down_small.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_forward.png b/app/assets/images/emoji/arrow_forward.png deleted file mode 100644 index fbfe711b6..000000000 Binary files a/app/assets/images/emoji/arrow_forward.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_heading_down.png b/app/assets/images/emoji/arrow_heading_down.png deleted file mode 100644 index 56dd3b9d3..000000000 Binary files a/app/assets/images/emoji/arrow_heading_down.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_heading_up.png b/app/assets/images/emoji/arrow_heading_up.png deleted file mode 100644 index c8f670a1e..000000000 Binary files a/app/assets/images/emoji/arrow_heading_up.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_left.png b/app/assets/images/emoji/arrow_left.png deleted file mode 100644 index 9d7d1b568..000000000 Binary files a/app/assets/images/emoji/arrow_left.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_lower_left.png b/app/assets/images/emoji/arrow_lower_left.png deleted file mode 100644 index a4438cb6e..000000000 Binary files a/app/assets/images/emoji/arrow_lower_left.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_lower_right.png b/app/assets/images/emoji/arrow_lower_right.png deleted file mode 100644 index 2a15cc7cc..000000000 Binary files a/app/assets/images/emoji/arrow_lower_right.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_right.png b/app/assets/images/emoji/arrow_right.png deleted file mode 100644 index e5cca853d..000000000 Binary files a/app/assets/images/emoji/arrow_right.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_right_hook.png b/app/assets/images/emoji/arrow_right_hook.png deleted file mode 100644 index 8b4ea6e17..000000000 Binary files a/app/assets/images/emoji/arrow_right_hook.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_up.png b/app/assets/images/emoji/arrow_up.png deleted file mode 100644 index 565ce2952..000000000 Binary files a/app/assets/images/emoji/arrow_up.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_up_down.png b/app/assets/images/emoji/arrow_up_down.png deleted file mode 100644 index b718c2145..000000000 Binary files a/app/assets/images/emoji/arrow_up_down.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_up_small.png b/app/assets/images/emoji/arrow_up_small.png deleted file mode 100644 index 121733197..000000000 Binary files a/app/assets/images/emoji/arrow_up_small.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_upper_left.png b/app/assets/images/emoji/arrow_upper_left.png deleted file mode 100644 index 12aebd9a7..000000000 Binary files a/app/assets/images/emoji/arrow_upper_left.png and /dev/null differ diff --git a/app/assets/images/emoji/arrow_upper_right.png b/app/assets/images/emoji/arrow_upper_right.png deleted file mode 100644 index 0daf4e940..000000000 Binary files a/app/assets/images/emoji/arrow_upper_right.png and /dev/null differ diff --git a/app/assets/images/emoji/arrows_clockwise.png b/app/assets/images/emoji/arrows_clockwise.png deleted file mode 100644 index 5f84d7e72..000000000 Binary files a/app/assets/images/emoji/arrows_clockwise.png and /dev/null differ diff --git a/app/assets/images/emoji/arrows_counterclockwise.png b/app/assets/images/emoji/arrows_counterclockwise.png deleted file mode 100644 index 1933ae18b..000000000 Binary files a/app/assets/images/emoji/arrows_counterclockwise.png and /dev/null differ diff --git a/app/assets/images/emoji/art.png b/app/assets/images/emoji/art.png deleted file mode 100644 index d45212b03..000000000 Binary files a/app/assets/images/emoji/art.png and /dev/null differ diff --git a/app/assets/images/emoji/articulated_lorry.png b/app/assets/images/emoji/articulated_lorry.png deleted file mode 100644 index 81ec1f917..000000000 Binary files a/app/assets/images/emoji/articulated_lorry.png and /dev/null differ diff --git a/app/assets/images/emoji/astonished.png b/app/assets/images/emoji/astonished.png deleted file mode 100644 index 858a83484..000000000 Binary files a/app/assets/images/emoji/astonished.png and /dev/null differ diff --git a/app/assets/images/emoji/athletic_shoe.png b/app/assets/images/emoji/athletic_shoe.png deleted file mode 100644 index 45b82e61c..000000000 Binary files a/app/assets/images/emoji/athletic_shoe.png and /dev/null differ diff --git a/app/assets/images/emoji/atm.png b/app/assets/images/emoji/atm.png deleted file mode 100644 index c2846e792..000000000 Binary files a/app/assets/images/emoji/atm.png and /dev/null differ diff --git a/app/assets/images/emoji/b.png b/app/assets/images/emoji/b.png deleted file mode 100644 index 8742b3d2e..000000000 Binary files a/app/assets/images/emoji/b.png and /dev/null differ diff --git a/app/assets/images/emoji/baby.png b/app/assets/images/emoji/baby.png deleted file mode 100644 index 3b29da40b..000000000 Binary files a/app/assets/images/emoji/baby.png and /dev/null differ diff --git a/app/assets/images/emoji/baby_bottle.png b/app/assets/images/emoji/baby_bottle.png deleted file mode 100644 index 1b2cfe5e3..000000000 Binary files a/app/assets/images/emoji/baby_bottle.png and /dev/null differ diff --git a/app/assets/images/emoji/baby_chick.png b/app/assets/images/emoji/baby_chick.png deleted file mode 100644 index 9be8d2930..000000000 Binary files a/app/assets/images/emoji/baby_chick.png and /dev/null differ diff --git a/app/assets/images/emoji/baby_symbol.png b/app/assets/images/emoji/baby_symbol.png deleted file mode 100644 index 2e58725cf..000000000 Binary files a/app/assets/images/emoji/baby_symbol.png and /dev/null differ diff --git a/app/assets/images/emoji/back.png b/app/assets/images/emoji/back.png deleted file mode 100644 index 0cde62876..000000000 Binary files a/app/assets/images/emoji/back.png and /dev/null differ diff --git a/app/assets/images/emoji/baggage_claim.png b/app/assets/images/emoji/baggage_claim.png deleted file mode 100644 index 59ae044a4..000000000 Binary files a/app/assets/images/emoji/baggage_claim.png and /dev/null differ diff --git a/app/assets/images/emoji/balloon.png b/app/assets/images/emoji/balloon.png deleted file mode 100644 index a4d3207b8..000000000 Binary files a/app/assets/images/emoji/balloon.png and /dev/null differ diff --git a/app/assets/images/emoji/ballot_box_with_check.png b/app/assets/images/emoji/ballot_box_with_check.png deleted file mode 100644 index f07a466c7..000000000 Binary files a/app/assets/images/emoji/ballot_box_with_check.png and /dev/null differ diff --git a/app/assets/images/emoji/bamboo.png b/app/assets/images/emoji/bamboo.png deleted file mode 100644 index fc858d0fc..000000000 Binary files a/app/assets/images/emoji/bamboo.png and /dev/null differ diff --git a/app/assets/images/emoji/banana.png b/app/assets/images/emoji/banana.png deleted file mode 100644 index a0563afb9..000000000 Binary files a/app/assets/images/emoji/banana.png and /dev/null differ diff --git a/app/assets/images/emoji/bangbang.png b/app/assets/images/emoji/bangbang.png deleted file mode 100644 index 7270f0afe..000000000 Binary files a/app/assets/images/emoji/bangbang.png and /dev/null differ diff --git a/app/assets/images/emoji/bank.png b/app/assets/images/emoji/bank.png deleted file mode 100644 index 1faa8777e..000000000 Binary files a/app/assets/images/emoji/bank.png and /dev/null differ diff --git a/app/assets/images/emoji/bar_chart.png b/app/assets/images/emoji/bar_chart.png deleted file mode 100644 index 7871cc603..000000000 Binary files a/app/assets/images/emoji/bar_chart.png and /dev/null differ diff --git a/app/assets/images/emoji/barber.png b/app/assets/images/emoji/barber.png deleted file mode 100644 index a10cb2322..000000000 Binary files a/app/assets/images/emoji/barber.png and /dev/null differ diff --git a/app/assets/images/emoji/baseball.png b/app/assets/images/emoji/baseball.png deleted file mode 100644 index da004e2ea..000000000 Binary files a/app/assets/images/emoji/baseball.png and /dev/null differ diff --git a/app/assets/images/emoji/basketball.png b/app/assets/images/emoji/basketball.png deleted file mode 100644 index ef694bec4..000000000 Binary files a/app/assets/images/emoji/basketball.png and /dev/null differ diff --git a/app/assets/images/emoji/bath.png b/app/assets/images/emoji/bath.png deleted file mode 100644 index 8f75d1d24..000000000 Binary files a/app/assets/images/emoji/bath.png and /dev/null differ diff --git a/app/assets/images/emoji/bathtub.png b/app/assets/images/emoji/bathtub.png deleted file mode 100644 index 1c3f844ab..000000000 Binary files a/app/assets/images/emoji/bathtub.png and /dev/null differ diff --git a/app/assets/images/emoji/battery.png b/app/assets/images/emoji/battery.png deleted file mode 100644 index aa7eedce4..000000000 Binary files a/app/assets/images/emoji/battery.png and /dev/null differ diff --git a/app/assets/images/emoji/bear.png b/app/assets/images/emoji/bear.png deleted file mode 100644 index f5afe920e..000000000 Binary files a/app/assets/images/emoji/bear.png and /dev/null differ diff --git a/app/assets/images/emoji/bee.png b/app/assets/images/emoji/bee.png deleted file mode 100644 index f53733953..000000000 Binary files a/app/assets/images/emoji/bee.png and /dev/null differ diff --git a/app/assets/images/emoji/beer.png b/app/assets/images/emoji/beer.png deleted file mode 100644 index cd78bed74..000000000 Binary files a/app/assets/images/emoji/beer.png and /dev/null differ diff --git a/app/assets/images/emoji/beers.png b/app/assets/images/emoji/beers.png deleted file mode 100644 index cc5e4ab5a..000000000 Binary files a/app/assets/images/emoji/beers.png and /dev/null differ diff --git a/app/assets/images/emoji/beetle.png b/app/assets/images/emoji/beetle.png deleted file mode 100644 index 222577ca7..000000000 Binary files a/app/assets/images/emoji/beetle.png and /dev/null differ diff --git a/app/assets/images/emoji/beginner.png b/app/assets/images/emoji/beginner.png deleted file mode 100644 index 1f022d175..000000000 Binary files a/app/assets/images/emoji/beginner.png and /dev/null differ diff --git a/app/assets/images/emoji/bell.png b/app/assets/images/emoji/bell.png deleted file mode 100644 index 69acceb28..000000000 Binary files a/app/assets/images/emoji/bell.png and /dev/null differ diff --git a/app/assets/images/emoji/bento.png b/app/assets/images/emoji/bento.png deleted file mode 100644 index c6d99e89b..000000000 Binary files a/app/assets/images/emoji/bento.png and /dev/null differ diff --git a/app/assets/images/emoji/bicyclist.png b/app/assets/images/emoji/bicyclist.png deleted file mode 100644 index 4e3e0549c..000000000 Binary files a/app/assets/images/emoji/bicyclist.png and /dev/null differ diff --git a/app/assets/images/emoji/bike.png b/app/assets/images/emoji/bike.png deleted file mode 100644 index 657386027..000000000 Binary files a/app/assets/images/emoji/bike.png and /dev/null differ diff --git a/app/assets/images/emoji/bikini.png b/app/assets/images/emoji/bikini.png deleted file mode 100644 index 4ff63b40f..000000000 Binary files a/app/assets/images/emoji/bikini.png and /dev/null differ diff --git a/app/assets/images/emoji/bird.png b/app/assets/images/emoji/bird.png deleted file mode 100644 index e6be8c027..000000000 Binary files a/app/assets/images/emoji/bird.png and /dev/null differ diff --git a/app/assets/images/emoji/birthday.png b/app/assets/images/emoji/birthday.png deleted file mode 100644 index 36e8edcbe..000000000 Binary files a/app/assets/images/emoji/birthday.png and /dev/null differ diff --git a/app/assets/images/emoji/black_circle.png b/app/assets/images/emoji/black_circle.png deleted file mode 100644 index e46f9df61..000000000 Binary files a/app/assets/images/emoji/black_circle.png and /dev/null differ diff --git a/app/assets/images/emoji/black_joker.png b/app/assets/images/emoji/black_joker.png deleted file mode 100644 index 4c78f3614..000000000 Binary files a/app/assets/images/emoji/black_joker.png and /dev/null differ diff --git a/app/assets/images/emoji/black_large_square.png b/app/assets/images/emoji/black_large_square.png deleted file mode 100644 index 71da10de8..000000000 Binary files a/app/assets/images/emoji/black_large_square.png and /dev/null differ diff --git a/app/assets/images/emoji/black_medium_small_square.png b/app/assets/images/emoji/black_medium_small_square.png deleted file mode 100644 index 25bfe9c45..000000000 Binary files a/app/assets/images/emoji/black_medium_small_square.png and /dev/null differ diff --git a/app/assets/images/emoji/black_medium_square.png b/app/assets/images/emoji/black_medium_square.png deleted file mode 100644 index 204cce12c..000000000 Binary files a/app/assets/images/emoji/black_medium_square.png and /dev/null differ diff --git a/app/assets/images/emoji/black_nib.png b/app/assets/images/emoji/black_nib.png deleted file mode 100644 index 29f6994c1..000000000 Binary files a/app/assets/images/emoji/black_nib.png and /dev/null differ diff --git a/app/assets/images/emoji/black_small_square.png b/app/assets/images/emoji/black_small_square.png deleted file mode 100644 index a247751ec..000000000 Binary files a/app/assets/images/emoji/black_small_square.png and /dev/null differ diff --git a/app/assets/images/emoji/black_square_button.png b/app/assets/images/emoji/black_square_button.png deleted file mode 100644 index 7332e397c..000000000 Binary files a/app/assets/images/emoji/black_square_button.png and /dev/null differ diff --git a/app/assets/images/emoji/blossom.png b/app/assets/images/emoji/blossom.png deleted file mode 100644 index 55a97353b..000000000 Binary files a/app/assets/images/emoji/blossom.png and /dev/null differ diff --git a/app/assets/images/emoji/blowfish.png b/app/assets/images/emoji/blowfish.png deleted file mode 100644 index a1d47cb7e..000000000 Binary files a/app/assets/images/emoji/blowfish.png and /dev/null differ diff --git a/app/assets/images/emoji/blue_book.png b/app/assets/images/emoji/blue_book.png deleted file mode 100644 index e2b9e8c79..000000000 Binary files a/app/assets/images/emoji/blue_book.png and /dev/null differ diff --git a/app/assets/images/emoji/blue_car.png b/app/assets/images/emoji/blue_car.png deleted file mode 100644 index 978291e08..000000000 Binary files a/app/assets/images/emoji/blue_car.png and /dev/null differ diff --git a/app/assets/images/emoji/blue_heart.png b/app/assets/images/emoji/blue_heart.png deleted file mode 100644 index baa29b31b..000000000 Binary files a/app/assets/images/emoji/blue_heart.png and /dev/null differ diff --git a/app/assets/images/emoji/blush.png b/app/assets/images/emoji/blush.png deleted file mode 100644 index 1e9021cb6..000000000 Binary files a/app/assets/images/emoji/blush.png and /dev/null differ diff --git a/app/assets/images/emoji/boar.png b/app/assets/images/emoji/boar.png deleted file mode 100644 index 8196ad4a1..000000000 Binary files a/app/assets/images/emoji/boar.png and /dev/null differ diff --git a/app/assets/images/emoji/boat.png b/app/assets/images/emoji/boat.png deleted file mode 100644 index ff656dc62..000000000 Binary files a/app/assets/images/emoji/boat.png and /dev/null differ diff --git a/app/assets/images/emoji/bomb.png b/app/assets/images/emoji/bomb.png deleted file mode 100644 index 3289787dc..000000000 Binary files a/app/assets/images/emoji/bomb.png and /dev/null differ diff --git a/app/assets/images/emoji/book.png b/app/assets/images/emoji/book.png deleted file mode 100644 index 8b698415c..000000000 Binary files a/app/assets/images/emoji/book.png and /dev/null differ diff --git a/app/assets/images/emoji/bookmark.png b/app/assets/images/emoji/bookmark.png deleted file mode 100644 index dbee45c60..000000000 Binary files a/app/assets/images/emoji/bookmark.png and /dev/null differ diff --git a/app/assets/images/emoji/bookmark_tabs.png b/app/assets/images/emoji/bookmark_tabs.png deleted file mode 100644 index 0c4e3bf17..000000000 Binary files a/app/assets/images/emoji/bookmark_tabs.png and /dev/null differ diff --git a/app/assets/images/emoji/books.png b/app/assets/images/emoji/books.png deleted file mode 100644 index dca06a1ad..000000000 Binary files a/app/assets/images/emoji/books.png and /dev/null differ diff --git a/app/assets/images/emoji/boom.png b/app/assets/images/emoji/boom.png deleted file mode 100644 index bddeb8f49..000000000 Binary files a/app/assets/images/emoji/boom.png and /dev/null differ diff --git a/app/assets/images/emoji/boot.png b/app/assets/images/emoji/boot.png deleted file mode 100644 index 58d0fdbcd..000000000 Binary files a/app/assets/images/emoji/boot.png and /dev/null differ diff --git a/app/assets/images/emoji/bouquet.png b/app/assets/images/emoji/bouquet.png deleted file mode 100644 index ce637832e..000000000 Binary files a/app/assets/images/emoji/bouquet.png and /dev/null differ diff --git a/app/assets/images/emoji/bow.png b/app/assets/images/emoji/bow.png deleted file mode 100644 index 024cb6104..000000000 Binary files a/app/assets/images/emoji/bow.png and /dev/null differ diff --git a/app/assets/images/emoji/bowling.png b/app/assets/images/emoji/bowling.png deleted file mode 100644 index 13d8ece2e..000000000 Binary files a/app/assets/images/emoji/bowling.png and /dev/null differ diff --git a/app/assets/images/emoji/boy.png b/app/assets/images/emoji/boy.png deleted file mode 100644 index f79f1f298..000000000 Binary files a/app/assets/images/emoji/boy.png and /dev/null differ diff --git a/app/assets/images/emoji/bread.png b/app/assets/images/emoji/bread.png deleted file mode 100644 index 7e7c63753..000000000 Binary files a/app/assets/images/emoji/bread.png and /dev/null differ diff --git a/app/assets/images/emoji/bride_with_veil.png b/app/assets/images/emoji/bride_with_veil.png deleted file mode 100644 index dd0b0cfda..000000000 Binary files a/app/assets/images/emoji/bride_with_veil.png and /dev/null differ diff --git a/app/assets/images/emoji/bridge_at_night.png b/app/assets/images/emoji/bridge_at_night.png deleted file mode 100644 index 495b06c3d..000000000 Binary files a/app/assets/images/emoji/bridge_at_night.png and /dev/null differ diff --git a/app/assets/images/emoji/briefcase.png b/app/assets/images/emoji/briefcase.png deleted file mode 100644 index 46e82b001..000000000 Binary files a/app/assets/images/emoji/briefcase.png and /dev/null differ diff --git a/app/assets/images/emoji/broken_heart.png b/app/assets/images/emoji/broken_heart.png deleted file mode 100644 index a1bc850ec..000000000 Binary files a/app/assets/images/emoji/broken_heart.png and /dev/null differ diff --git a/app/assets/images/emoji/bug.png b/app/assets/images/emoji/bug.png deleted file mode 100644 index c2eaf7a70..000000000 Binary files a/app/assets/images/emoji/bug.png and /dev/null differ diff --git a/app/assets/images/emoji/bulb.png b/app/assets/images/emoji/bulb.png deleted file mode 100644 index 23afca1c7..000000000 Binary files a/app/assets/images/emoji/bulb.png and /dev/null differ diff --git a/app/assets/images/emoji/bullettrain_front.png b/app/assets/images/emoji/bullettrain_front.png deleted file mode 100644 index 16651acff..000000000 Binary files a/app/assets/images/emoji/bullettrain_front.png and /dev/null differ diff --git a/app/assets/images/emoji/bullettrain_side.png b/app/assets/images/emoji/bullettrain_side.png deleted file mode 100644 index 8eca36845..000000000 Binary files a/app/assets/images/emoji/bullettrain_side.png and /dev/null differ diff --git a/app/assets/images/emoji/bus.png b/app/assets/images/emoji/bus.png deleted file mode 100644 index 823aa39e4..000000000 Binary files a/app/assets/images/emoji/bus.png and /dev/null differ diff --git a/app/assets/images/emoji/busstop.png b/app/assets/images/emoji/busstop.png deleted file mode 100644 index 99af2322a..000000000 Binary files a/app/assets/images/emoji/busstop.png and /dev/null differ diff --git a/app/assets/images/emoji/bust_in_silhouette.png b/app/assets/images/emoji/bust_in_silhouette.png deleted file mode 100644 index d13139869..000000000 Binary files a/app/assets/images/emoji/bust_in_silhouette.png and /dev/null differ diff --git a/app/assets/images/emoji/busts_in_silhouette.png b/app/assets/images/emoji/busts_in_silhouette.png deleted file mode 100644 index 1f3aabcff..000000000 Binary files a/app/assets/images/emoji/busts_in_silhouette.png and /dev/null differ diff --git a/app/assets/images/emoji/cactus.png b/app/assets/images/emoji/cactus.png deleted file mode 100644 index 5a2c3cc72..000000000 Binary files a/app/assets/images/emoji/cactus.png and /dev/null differ diff --git a/app/assets/images/emoji/cake.png b/app/assets/images/emoji/cake.png deleted file mode 100644 index efeb9b4b2..000000000 Binary files a/app/assets/images/emoji/cake.png and /dev/null differ diff --git a/app/assets/images/emoji/calendar.png b/app/assets/images/emoji/calendar.png deleted file mode 100644 index 900b868bb..000000000 Binary files a/app/assets/images/emoji/calendar.png and /dev/null differ diff --git a/app/assets/images/emoji/calling.png b/app/assets/images/emoji/calling.png deleted file mode 100644 index 837897f26..000000000 Binary files a/app/assets/images/emoji/calling.png and /dev/null differ diff --git a/app/assets/images/emoji/camel.png b/app/assets/images/emoji/camel.png deleted file mode 100644 index 496c186ae..000000000 Binary files a/app/assets/images/emoji/camel.png and /dev/null differ diff --git a/app/assets/images/emoji/camera.png b/app/assets/images/emoji/camera.png deleted file mode 100644 index 397d03b39..000000000 Binary files a/app/assets/images/emoji/camera.png and /dev/null differ diff --git a/app/assets/images/emoji/cancer.png b/app/assets/images/emoji/cancer.png deleted file mode 100644 index ea43a4a2a..000000000 Binary files a/app/assets/images/emoji/cancer.png and /dev/null differ diff --git a/app/assets/images/emoji/candy.png b/app/assets/images/emoji/candy.png deleted file mode 100644 index 33722f236..000000000 Binary files a/app/assets/images/emoji/candy.png and /dev/null differ diff --git a/app/assets/images/emoji/capital_abcd.png b/app/assets/images/emoji/capital_abcd.png deleted file mode 100644 index ffc0cba4b..000000000 Binary files a/app/assets/images/emoji/capital_abcd.png and /dev/null differ diff --git a/app/assets/images/emoji/capricorn.png b/app/assets/images/emoji/capricorn.png deleted file mode 100644 index f2044e789..000000000 Binary files a/app/assets/images/emoji/capricorn.png and /dev/null differ diff --git a/app/assets/images/emoji/car.png b/app/assets/images/emoji/car.png deleted file mode 100644 index d70a2f062..000000000 Binary files a/app/assets/images/emoji/car.png and /dev/null differ diff --git a/app/assets/images/emoji/card_index.png b/app/assets/images/emoji/card_index.png deleted file mode 100644 index 374e94e9e..000000000 Binary files a/app/assets/images/emoji/card_index.png and /dev/null differ diff --git a/app/assets/images/emoji/carousel_horse.png b/app/assets/images/emoji/carousel_horse.png deleted file mode 100644 index 765d2c0a8..000000000 Binary files a/app/assets/images/emoji/carousel_horse.png and /dev/null differ diff --git a/app/assets/images/emoji/cat.png b/app/assets/images/emoji/cat.png deleted file mode 100644 index 09b9ef79a..000000000 Binary files a/app/assets/images/emoji/cat.png and /dev/null differ diff --git a/app/assets/images/emoji/cat2.png b/app/assets/images/emoji/cat2.png deleted file mode 100644 index 977c992c5..000000000 Binary files a/app/assets/images/emoji/cat2.png and /dev/null differ diff --git a/app/assets/images/emoji/cd.png b/app/assets/images/emoji/cd.png deleted file mode 100644 index baff835c4..000000000 Binary files a/app/assets/images/emoji/cd.png and /dev/null differ diff --git a/app/assets/images/emoji/chart.png b/app/assets/images/emoji/chart.png deleted file mode 100644 index ac2c4bb09..000000000 Binary files a/app/assets/images/emoji/chart.png and /dev/null differ diff --git a/app/assets/images/emoji/chart_with_downwards_trend.png b/app/assets/images/emoji/chart_with_downwards_trend.png deleted file mode 100644 index 65b82f044..000000000 Binary files a/app/assets/images/emoji/chart_with_downwards_trend.png and /dev/null differ diff --git a/app/assets/images/emoji/chart_with_upwards_trend.png b/app/assets/images/emoji/chart_with_upwards_trend.png deleted file mode 100644 index de3e9ba7b..000000000 Binary files a/app/assets/images/emoji/chart_with_upwards_trend.png and /dev/null differ diff --git a/app/assets/images/emoji/checkered_flag.png b/app/assets/images/emoji/checkered_flag.png deleted file mode 100644 index ead4a68dd..000000000 Binary files a/app/assets/images/emoji/checkered_flag.png and /dev/null differ diff --git a/app/assets/images/emoji/cherries.png b/app/assets/images/emoji/cherries.png deleted file mode 100644 index 8d3e044f2..000000000 Binary files a/app/assets/images/emoji/cherries.png and /dev/null differ diff --git a/app/assets/images/emoji/cherry_blossom.png b/app/assets/images/emoji/cherry_blossom.png deleted file mode 100644 index e03155499..000000000 Binary files a/app/assets/images/emoji/cherry_blossom.png and /dev/null differ diff --git a/app/assets/images/emoji/chestnut.png b/app/assets/images/emoji/chestnut.png deleted file mode 100644 index 066fb6bf6..000000000 Binary files a/app/assets/images/emoji/chestnut.png and /dev/null differ diff --git a/app/assets/images/emoji/chicken.png b/app/assets/images/emoji/chicken.png deleted file mode 100644 index 6d25c0ef4..000000000 Binary files a/app/assets/images/emoji/chicken.png and /dev/null differ diff --git a/app/assets/images/emoji/children_crossing.png b/app/assets/images/emoji/children_crossing.png deleted file mode 100644 index b0302ae62..000000000 Binary files a/app/assets/images/emoji/children_crossing.png and /dev/null differ diff --git a/app/assets/images/emoji/chocolate_bar.png b/app/assets/images/emoji/chocolate_bar.png deleted file mode 100644 index c7ec19d07..000000000 Binary files a/app/assets/images/emoji/chocolate_bar.png and /dev/null differ diff --git a/app/assets/images/emoji/christmas_tree.png b/app/assets/images/emoji/christmas_tree.png deleted file mode 100644 index d813b9593..000000000 Binary files a/app/assets/images/emoji/christmas_tree.png and /dev/null differ diff --git a/app/assets/images/emoji/church.png b/app/assets/images/emoji/church.png deleted file mode 100644 index 4c07c6b9e..000000000 Binary files a/app/assets/images/emoji/church.png and /dev/null differ diff --git a/app/assets/images/emoji/cinema.png b/app/assets/images/emoji/cinema.png deleted file mode 100644 index a990ccf99..000000000 Binary files a/app/assets/images/emoji/cinema.png and /dev/null differ diff --git a/app/assets/images/emoji/circus_tent.png b/app/assets/images/emoji/circus_tent.png deleted file mode 100644 index 4af8719aa..000000000 Binary files a/app/assets/images/emoji/circus_tent.png and /dev/null differ diff --git a/app/assets/images/emoji/city_sunrise.png b/app/assets/images/emoji/city_sunrise.png deleted file mode 100644 index 91ca2a40b..000000000 Binary files a/app/assets/images/emoji/city_sunrise.png and /dev/null differ diff --git a/app/assets/images/emoji/city_sunset.png b/app/assets/images/emoji/city_sunset.png deleted file mode 100644 index 7cb178a2c..000000000 Binary files a/app/assets/images/emoji/city_sunset.png and /dev/null differ diff --git a/app/assets/images/emoji/cl.png b/app/assets/images/emoji/cl.png deleted file mode 100644 index 15ac67525..000000000 Binary files a/app/assets/images/emoji/cl.png and /dev/null differ diff --git a/app/assets/images/emoji/clap.png b/app/assets/images/emoji/clap.png deleted file mode 100644 index d01c982a7..000000000 Binary files a/app/assets/images/emoji/clap.png and /dev/null differ diff --git a/app/assets/images/emoji/clapper.png b/app/assets/images/emoji/clapper.png deleted file mode 100644 index 4e1dc111d..000000000 Binary files a/app/assets/images/emoji/clapper.png and /dev/null differ diff --git a/app/assets/images/emoji/clipboard.png b/app/assets/images/emoji/clipboard.png deleted file mode 100644 index e2c74e6df..000000000 Binary files a/app/assets/images/emoji/clipboard.png and /dev/null differ diff --git a/app/assets/images/emoji/clock1.png b/app/assets/images/emoji/clock1.png deleted file mode 100644 index ca34e8975..000000000 Binary files a/app/assets/images/emoji/clock1.png and /dev/null differ diff --git a/app/assets/images/emoji/clock10.png b/app/assets/images/emoji/clock10.png deleted file mode 100644 index f710bef5c..000000000 Binary files a/app/assets/images/emoji/clock10.png and /dev/null differ diff --git a/app/assets/images/emoji/clock1030.png b/app/assets/images/emoji/clock1030.png deleted file mode 100644 index 84a3bc8fb..000000000 Binary files a/app/assets/images/emoji/clock1030.png and /dev/null differ diff --git a/app/assets/images/emoji/clock11.png b/app/assets/images/emoji/clock11.png deleted file mode 100644 index fbc165b99..000000000 Binary files a/app/assets/images/emoji/clock11.png and /dev/null differ diff --git a/app/assets/images/emoji/clock1130.png b/app/assets/images/emoji/clock1130.png deleted file mode 100644 index 415999ec8..000000000 Binary files a/app/assets/images/emoji/clock1130.png and /dev/null differ diff --git a/app/assets/images/emoji/clock12.png b/app/assets/images/emoji/clock12.png deleted file mode 100644 index c1ca82f39..000000000 Binary files a/app/assets/images/emoji/clock12.png and /dev/null differ diff --git a/app/assets/images/emoji/clock1230.png b/app/assets/images/emoji/clock1230.png deleted file mode 100644 index a6527154d..000000000 Binary files a/app/assets/images/emoji/clock1230.png and /dev/null differ diff --git a/app/assets/images/emoji/clock130.png b/app/assets/images/emoji/clock130.png deleted file mode 100644 index df9392019..000000000 Binary files a/app/assets/images/emoji/clock130.png and /dev/null differ diff --git a/app/assets/images/emoji/clock2.png b/app/assets/images/emoji/clock2.png deleted file mode 100644 index 1a12524ee..000000000 Binary files a/app/assets/images/emoji/clock2.png and /dev/null differ diff --git a/app/assets/images/emoji/clock230.png b/app/assets/images/emoji/clock230.png deleted file mode 100644 index f12c6912a..000000000 Binary files a/app/assets/images/emoji/clock230.png and /dev/null differ diff --git a/app/assets/images/emoji/clock3.png b/app/assets/images/emoji/clock3.png deleted file mode 100644 index cd99bb155..000000000 Binary files a/app/assets/images/emoji/clock3.png and /dev/null differ diff --git a/app/assets/images/emoji/clock330.png b/app/assets/images/emoji/clock330.png deleted file mode 100644 index 1dc9628ea..000000000 Binary files a/app/assets/images/emoji/clock330.png and /dev/null differ diff --git a/app/assets/images/emoji/clock4.png b/app/assets/images/emoji/clock4.png deleted file mode 100644 index 7274e8b07..000000000 Binary files a/app/assets/images/emoji/clock4.png and /dev/null differ diff --git a/app/assets/images/emoji/clock430.png b/app/assets/images/emoji/clock430.png deleted file mode 100644 index 7726aaea1..000000000 Binary files a/app/assets/images/emoji/clock430.png and /dev/null differ diff --git a/app/assets/images/emoji/clock5.png b/app/assets/images/emoji/clock5.png deleted file mode 100644 index 3ed5a81af..000000000 Binary files a/app/assets/images/emoji/clock5.png and /dev/null differ diff --git a/app/assets/images/emoji/clock530.png b/app/assets/images/emoji/clock530.png deleted file mode 100644 index e08d4ad2b..000000000 Binary files a/app/assets/images/emoji/clock530.png and /dev/null differ diff --git a/app/assets/images/emoji/clock6.png b/app/assets/images/emoji/clock6.png deleted file mode 100644 index ac38cb926..000000000 Binary files a/app/assets/images/emoji/clock6.png and /dev/null differ diff --git a/app/assets/images/emoji/clock630.png b/app/assets/images/emoji/clock630.png deleted file mode 100644 index 46f0681f1..000000000 Binary files a/app/assets/images/emoji/clock630.png and /dev/null differ diff --git a/app/assets/images/emoji/clock7.png b/app/assets/images/emoji/clock7.png deleted file mode 100644 index 6a138dfde..000000000 Binary files a/app/assets/images/emoji/clock7.png and /dev/null differ diff --git a/app/assets/images/emoji/clock730.png b/app/assets/images/emoji/clock730.png deleted file mode 100644 index 18aab22fd..000000000 Binary files a/app/assets/images/emoji/clock730.png and /dev/null differ diff --git a/app/assets/images/emoji/clock8.png b/app/assets/images/emoji/clock8.png deleted file mode 100644 index 6690cd74e..000000000 Binary files a/app/assets/images/emoji/clock8.png and /dev/null differ diff --git a/app/assets/images/emoji/clock830.png b/app/assets/images/emoji/clock830.png deleted file mode 100644 index ec3e382dd..000000000 Binary files a/app/assets/images/emoji/clock830.png and /dev/null differ diff --git a/app/assets/images/emoji/clock9.png b/app/assets/images/emoji/clock9.png deleted file mode 100644 index c4ad74609..000000000 Binary files a/app/assets/images/emoji/clock9.png and /dev/null differ diff --git a/app/assets/images/emoji/clock930.png b/app/assets/images/emoji/clock930.png deleted file mode 100644 index fd3522142..000000000 Binary files a/app/assets/images/emoji/clock930.png and /dev/null differ diff --git a/app/assets/images/emoji/closed_book.png b/app/assets/images/emoji/closed_book.png deleted file mode 100644 index 484029c5e..000000000 Binary files a/app/assets/images/emoji/closed_book.png and /dev/null differ diff --git a/app/assets/images/emoji/closed_lock_with_key.png b/app/assets/images/emoji/closed_lock_with_key.png deleted file mode 100644 index e6fdf6cb2..000000000 Binary files a/app/assets/images/emoji/closed_lock_with_key.png and /dev/null differ diff --git a/app/assets/images/emoji/closed_umbrella.png b/app/assets/images/emoji/closed_umbrella.png deleted file mode 100644 index 072c5c217..000000000 Binary files a/app/assets/images/emoji/closed_umbrella.png and /dev/null differ diff --git a/app/assets/images/emoji/cloud.png b/app/assets/images/emoji/cloud.png deleted file mode 100644 index b31c08c0b..000000000 Binary files a/app/assets/images/emoji/cloud.png and /dev/null differ diff --git a/app/assets/images/emoji/clubs.png b/app/assets/images/emoji/clubs.png deleted file mode 100644 index bfab53656..000000000 Binary files a/app/assets/images/emoji/clubs.png and /dev/null differ diff --git a/app/assets/images/emoji/cocktail.png b/app/assets/images/emoji/cocktail.png deleted file mode 100644 index 28b45ea51..000000000 Binary files a/app/assets/images/emoji/cocktail.png and /dev/null differ diff --git a/app/assets/images/emoji/coffee.png b/app/assets/images/emoji/coffee.png deleted file mode 100644 index 57e1adcb0..000000000 Binary files a/app/assets/images/emoji/coffee.png and /dev/null differ diff --git a/app/assets/images/emoji/cold_sweat.png b/app/assets/images/emoji/cold_sweat.png deleted file mode 100644 index b9e39bc60..000000000 Binary files a/app/assets/images/emoji/cold_sweat.png and /dev/null differ diff --git a/app/assets/images/emoji/collision.png b/app/assets/images/emoji/collision.png deleted file mode 100644 index bddeb8f49..000000000 Binary files a/app/assets/images/emoji/collision.png and /dev/null differ diff --git a/app/assets/images/emoji/computer.png b/app/assets/images/emoji/computer.png deleted file mode 100644 index d4d268762..000000000 Binary files a/app/assets/images/emoji/computer.png and /dev/null differ diff --git a/app/assets/images/emoji/confetti_ball.png b/app/assets/images/emoji/confetti_ball.png deleted file mode 100644 index bd293e3d8..000000000 Binary files a/app/assets/images/emoji/confetti_ball.png and /dev/null differ diff --git a/app/assets/images/emoji/confounded.png b/app/assets/images/emoji/confounded.png deleted file mode 100644 index a5877a0a7..000000000 Binary files a/app/assets/images/emoji/confounded.png and /dev/null differ diff --git a/app/assets/images/emoji/confused.png b/app/assets/images/emoji/confused.png deleted file mode 100644 index 18ff760ac..000000000 Binary files a/app/assets/images/emoji/confused.png and /dev/null differ diff --git a/app/assets/images/emoji/congratulations.png b/app/assets/images/emoji/congratulations.png deleted file mode 100644 index dcbb1d229..000000000 Binary files a/app/assets/images/emoji/congratulations.png and /dev/null differ diff --git a/app/assets/images/emoji/construction.png b/app/assets/images/emoji/construction.png deleted file mode 100644 index 523e9f10b..000000000 Binary files a/app/assets/images/emoji/construction.png and /dev/null differ diff --git a/app/assets/images/emoji/construction_worker.png b/app/assets/images/emoji/construction_worker.png deleted file mode 100644 index 4d6486047..000000000 Binary files a/app/assets/images/emoji/construction_worker.png and /dev/null differ diff --git a/app/assets/images/emoji/convenience_store.png b/app/assets/images/emoji/convenience_store.png deleted file mode 100644 index 671696c2d..000000000 Binary files a/app/assets/images/emoji/convenience_store.png and /dev/null differ diff --git a/app/assets/images/emoji/cookie.png b/app/assets/images/emoji/cookie.png deleted file mode 100644 index 653edb258..000000000 Binary files a/app/assets/images/emoji/cookie.png and /dev/null differ diff --git a/app/assets/images/emoji/cool.png b/app/assets/images/emoji/cool.png deleted file mode 100644 index 937dcd792..000000000 Binary files a/app/assets/images/emoji/cool.png and /dev/null differ diff --git a/app/assets/images/emoji/cop.png b/app/assets/images/emoji/cop.png deleted file mode 100644 index 43a5a84f8..000000000 Binary files a/app/assets/images/emoji/cop.png and /dev/null differ diff --git a/app/assets/images/emoji/copyright.png b/app/assets/images/emoji/copyright.png deleted file mode 100644 index d59f580a9..000000000 Binary files a/app/assets/images/emoji/copyright.png and /dev/null differ diff --git a/app/assets/images/emoji/corn.png b/app/assets/images/emoji/corn.png deleted file mode 100644 index fe5d8b128..000000000 Binary files a/app/assets/images/emoji/corn.png and /dev/null differ diff --git a/app/assets/images/emoji/couple.png b/app/assets/images/emoji/couple.png deleted file mode 100644 index 9e51f40e1..000000000 Binary files a/app/assets/images/emoji/couple.png and /dev/null differ diff --git a/app/assets/images/emoji/couple_with_heart.png b/app/assets/images/emoji/couple_with_heart.png deleted file mode 100644 index c503f40a9..000000000 Binary files a/app/assets/images/emoji/couple_with_heart.png and /dev/null differ diff --git a/app/assets/images/emoji/couplekiss.png b/app/assets/images/emoji/couplekiss.png deleted file mode 100644 index d02790822..000000000 Binary files a/app/assets/images/emoji/couplekiss.png and /dev/null differ diff --git a/app/assets/images/emoji/cow.png b/app/assets/images/emoji/cow.png deleted file mode 100644 index 12e1ab6c0..000000000 Binary files a/app/assets/images/emoji/cow.png and /dev/null differ diff --git a/app/assets/images/emoji/cow2.png b/app/assets/images/emoji/cow2.png deleted file mode 100644 index 594c92155..000000000 Binary files a/app/assets/images/emoji/cow2.png and /dev/null differ diff --git a/app/assets/images/emoji/credit_card.png b/app/assets/images/emoji/credit_card.png deleted file mode 100644 index be1c1dd30..000000000 Binary files a/app/assets/images/emoji/credit_card.png and /dev/null differ diff --git a/app/assets/images/emoji/crescent_moon.png b/app/assets/images/emoji/crescent_moon.png deleted file mode 100644 index afdb450d1..000000000 Binary files a/app/assets/images/emoji/crescent_moon.png and /dev/null differ diff --git a/app/assets/images/emoji/crocodile.png b/app/assets/images/emoji/crocodile.png deleted file mode 100644 index 7435d5ab3..000000000 Binary files a/app/assets/images/emoji/crocodile.png and /dev/null differ diff --git a/app/assets/images/emoji/crossed_flags.png b/app/assets/images/emoji/crossed_flags.png deleted file mode 100644 index 2ffbb2627..000000000 Binary files a/app/assets/images/emoji/crossed_flags.png and /dev/null differ diff --git a/app/assets/images/emoji/crown.png b/app/assets/images/emoji/crown.png deleted file mode 100644 index 39da1d528..000000000 Binary files a/app/assets/images/emoji/crown.png and /dev/null differ diff --git a/app/assets/images/emoji/cry.png b/app/assets/images/emoji/cry.png deleted file mode 100644 index 6d0d9afd2..000000000 Binary files a/app/assets/images/emoji/cry.png and /dev/null differ diff --git a/app/assets/images/emoji/crying_cat_face.png b/app/assets/images/emoji/crying_cat_face.png deleted file mode 100644 index 42d4c27ca..000000000 Binary files a/app/assets/images/emoji/crying_cat_face.png and /dev/null differ diff --git a/app/assets/images/emoji/crystal_ball.png b/app/assets/images/emoji/crystal_ball.png deleted file mode 100644 index 6d2c6c42d..000000000 Binary files a/app/assets/images/emoji/crystal_ball.png and /dev/null differ diff --git a/app/assets/images/emoji/cupid.png b/app/assets/images/emoji/cupid.png deleted file mode 100644 index 498728476..000000000 Binary files a/app/assets/images/emoji/cupid.png and /dev/null differ diff --git a/app/assets/images/emoji/curly_loop.png b/app/assets/images/emoji/curly_loop.png deleted file mode 100644 index 8f051aca4..000000000 Binary files a/app/assets/images/emoji/curly_loop.png and /dev/null differ diff --git a/app/assets/images/emoji/currency_exchange.png b/app/assets/images/emoji/currency_exchange.png deleted file mode 100644 index d5ee21fc6..000000000 Binary files a/app/assets/images/emoji/currency_exchange.png and /dev/null differ diff --git a/app/assets/images/emoji/curry.png b/app/assets/images/emoji/curry.png deleted file mode 100644 index 7983c706a..000000000 Binary files a/app/assets/images/emoji/curry.png and /dev/null differ diff --git a/app/assets/images/emoji/custard.png b/app/assets/images/emoji/custard.png deleted file mode 100644 index 9f843b4c1..000000000 Binary files a/app/assets/images/emoji/custard.png and /dev/null differ diff --git a/app/assets/images/emoji/customs.png b/app/assets/images/emoji/customs.png deleted file mode 100644 index 92691e311..000000000 Binary files a/app/assets/images/emoji/customs.png and /dev/null differ diff --git a/app/assets/images/emoji/cyclone.png b/app/assets/images/emoji/cyclone.png deleted file mode 100644 index 6c49f64b2..000000000 Binary files a/app/assets/images/emoji/cyclone.png and /dev/null differ diff --git a/app/assets/images/emoji/dancer.png b/app/assets/images/emoji/dancer.png deleted file mode 100644 index 6885a0bc3..000000000 Binary files a/app/assets/images/emoji/dancer.png and /dev/null differ diff --git a/app/assets/images/emoji/dancers.png b/app/assets/images/emoji/dancers.png deleted file mode 100644 index 2dfb451a7..000000000 Binary files a/app/assets/images/emoji/dancers.png and /dev/null differ diff --git a/app/assets/images/emoji/dango.png b/app/assets/images/emoji/dango.png deleted file mode 100644 index 2d042aebe..000000000 Binary files a/app/assets/images/emoji/dango.png and /dev/null differ diff --git a/app/assets/images/emoji/dart.png b/app/assets/images/emoji/dart.png deleted file mode 100644 index 0438fe54f..000000000 Binary files a/app/assets/images/emoji/dart.png and /dev/null differ diff --git a/app/assets/images/emoji/dash.png b/app/assets/images/emoji/dash.png deleted file mode 100644 index dc2c0a8f4..000000000 Binary files a/app/assets/images/emoji/dash.png and /dev/null differ diff --git a/app/assets/images/emoji/date.png b/app/assets/images/emoji/date.png deleted file mode 100644 index 6ad2efa5f..000000000 Binary files a/app/assets/images/emoji/date.png and /dev/null differ diff --git a/app/assets/images/emoji/deciduous_tree.png b/app/assets/images/emoji/deciduous_tree.png deleted file mode 100644 index 9bb16bdfe..000000000 Binary files a/app/assets/images/emoji/deciduous_tree.png and /dev/null differ diff --git a/app/assets/images/emoji/department_store.png b/app/assets/images/emoji/department_store.png deleted file mode 100644 index 68d959c50..000000000 Binary files a/app/assets/images/emoji/department_store.png and /dev/null 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 deleted file mode 100644 index dfd1098b3..000000000 Binary files a/app/assets/images/emoji/diamond_shape_with_a_dot_inside.png and /dev/null differ diff --git a/app/assets/images/emoji/diamonds.png b/app/assets/images/emoji/diamonds.png deleted file mode 100644 index fe0827758..000000000 Binary files a/app/assets/images/emoji/diamonds.png and /dev/null differ diff --git a/app/assets/images/emoji/disappointed.png b/app/assets/images/emoji/disappointed.png deleted file mode 100644 index 825520087..000000000 Binary files a/app/assets/images/emoji/disappointed.png and /dev/null differ diff --git a/app/assets/images/emoji/disappointed_relieved.png b/app/assets/images/emoji/disappointed_relieved.png deleted file mode 100644 index fa5f9e7f9..000000000 Binary files a/app/assets/images/emoji/disappointed_relieved.png and /dev/null differ diff --git a/app/assets/images/emoji/dizzy.png b/app/assets/images/emoji/dizzy.png deleted file mode 100644 index 55213d2dd..000000000 Binary files a/app/assets/images/emoji/dizzy.png and /dev/null differ diff --git a/app/assets/images/emoji/dizzy_face.png b/app/assets/images/emoji/dizzy_face.png deleted file mode 100644 index 8001d6ff8..000000000 Binary files a/app/assets/images/emoji/dizzy_face.png and /dev/null differ diff --git a/app/assets/images/emoji/do_not_litter.png b/app/assets/images/emoji/do_not_litter.png deleted file mode 100644 index 38c7ae7af..000000000 Binary files a/app/assets/images/emoji/do_not_litter.png and /dev/null differ diff --git a/app/assets/images/emoji/dog.png b/app/assets/images/emoji/dog.png deleted file mode 100644 index 389a02bf2..000000000 Binary files a/app/assets/images/emoji/dog.png and /dev/null differ diff --git a/app/assets/images/emoji/dog2.png b/app/assets/images/emoji/dog2.png deleted file mode 100644 index c7f6a24ac..000000000 Binary files a/app/assets/images/emoji/dog2.png and /dev/null differ diff --git a/app/assets/images/emoji/dollar.png b/app/assets/images/emoji/dollar.png deleted file mode 100644 index 63de88495..000000000 Binary files a/app/assets/images/emoji/dollar.png and /dev/null differ diff --git a/app/assets/images/emoji/dolls.png b/app/assets/images/emoji/dolls.png deleted file mode 100644 index 47ce33900..000000000 Binary files a/app/assets/images/emoji/dolls.png and /dev/null differ diff --git a/app/assets/images/emoji/dolphin.png b/app/assets/images/emoji/dolphin.png deleted file mode 100644 index 9326077a9..000000000 Binary files a/app/assets/images/emoji/dolphin.png and /dev/null differ diff --git a/app/assets/images/emoji/door.png b/app/assets/images/emoji/door.png deleted file mode 100644 index 83c819ae4..000000000 Binary files a/app/assets/images/emoji/door.png and /dev/null differ diff --git a/app/assets/images/emoji/doughnut.png b/app/assets/images/emoji/doughnut.png deleted file mode 100644 index ccf869129..000000000 Binary files a/app/assets/images/emoji/doughnut.png and /dev/null differ diff --git a/app/assets/images/emoji/dragon.png b/app/assets/images/emoji/dragon.png deleted file mode 100644 index e399d60e1..000000000 Binary files a/app/assets/images/emoji/dragon.png and /dev/null differ diff --git a/app/assets/images/emoji/dragon_face.png b/app/assets/images/emoji/dragon_face.png deleted file mode 100644 index e5e556bd1..000000000 Binary files a/app/assets/images/emoji/dragon_face.png and /dev/null differ diff --git a/app/assets/images/emoji/dress.png b/app/assets/images/emoji/dress.png deleted file mode 100644 index 6434e2e2f..000000000 Binary files a/app/assets/images/emoji/dress.png and /dev/null differ diff --git a/app/assets/images/emoji/dromedary_camel.png b/app/assets/images/emoji/dromedary_camel.png deleted file mode 100644 index c8c7b9ffa..000000000 Binary files a/app/assets/images/emoji/dromedary_camel.png and /dev/null differ diff --git a/app/assets/images/emoji/droplet.png b/app/assets/images/emoji/droplet.png deleted file mode 100644 index 9eff46339..000000000 Binary files a/app/assets/images/emoji/droplet.png and /dev/null differ diff --git a/app/assets/images/emoji/dvd.png b/app/assets/images/emoji/dvd.png deleted file mode 100644 index 363c83d01..000000000 Binary files a/app/assets/images/emoji/dvd.png and /dev/null differ diff --git a/app/assets/images/emoji/e-mail.png b/app/assets/images/emoji/e-mail.png deleted file mode 100644 index 176a8e1e8..000000000 Binary files a/app/assets/images/emoji/e-mail.png and /dev/null differ diff --git a/app/assets/images/emoji/ear.png b/app/assets/images/emoji/ear.png deleted file mode 100644 index 2bbbf10c9..000000000 Binary files a/app/assets/images/emoji/ear.png and /dev/null differ diff --git a/app/assets/images/emoji/ear_of_rice.png b/app/assets/images/emoji/ear_of_rice.png deleted file mode 100644 index a9bba5c2c..000000000 Binary files a/app/assets/images/emoji/ear_of_rice.png and /dev/null differ diff --git a/app/assets/images/emoji/earth_africa.png b/app/assets/images/emoji/earth_africa.png deleted file mode 100644 index 44ce5ecb6..000000000 Binary files a/app/assets/images/emoji/earth_africa.png and /dev/null differ diff --git a/app/assets/images/emoji/earth_americas.png b/app/assets/images/emoji/earth_americas.png deleted file mode 100644 index 97d717671..000000000 Binary files a/app/assets/images/emoji/earth_americas.png and /dev/null differ diff --git a/app/assets/images/emoji/earth_asia.png b/app/assets/images/emoji/earth_asia.png deleted file mode 100644 index 95ec357ca..000000000 Binary files a/app/assets/images/emoji/earth_asia.png and /dev/null differ diff --git a/app/assets/images/emoji/egg.png b/app/assets/images/emoji/egg.png deleted file mode 100644 index c3de6ae4e..000000000 Binary files a/app/assets/images/emoji/egg.png and /dev/null differ diff --git a/app/assets/images/emoji/eggplant.png b/app/assets/images/emoji/eggplant.png deleted file mode 100644 index 566d6a844..000000000 Binary files a/app/assets/images/emoji/eggplant.png and /dev/null differ diff --git a/app/assets/images/emoji/eight_pointed_black_star.png b/app/assets/images/emoji/eight_pointed_black_star.png deleted file mode 100644 index 73dc6a0c9..000000000 Binary files a/app/assets/images/emoji/eight_pointed_black_star.png and /dev/null differ diff --git a/app/assets/images/emoji/eight_spoked_asterisk.png b/app/assets/images/emoji/eight_spoked_asterisk.png deleted file mode 100644 index 946a20333..000000000 Binary files a/app/assets/images/emoji/eight_spoked_asterisk.png and /dev/null differ diff --git a/app/assets/images/emoji/electric_plug.png b/app/assets/images/emoji/electric_plug.png deleted file mode 100644 index 7a3d6cee6..000000000 Binary files a/app/assets/images/emoji/electric_plug.png and /dev/null differ diff --git a/app/assets/images/emoji/elephant.png b/app/assets/images/emoji/elephant.png deleted file mode 100644 index 5ca04570e..000000000 Binary files a/app/assets/images/emoji/elephant.png and /dev/null differ diff --git a/app/assets/images/emoji/email.png b/app/assets/images/emoji/email.png deleted file mode 100644 index 3631861bb..000000000 Binary files a/app/assets/images/emoji/email.png and /dev/null differ diff --git a/app/assets/images/emoji/end.png b/app/assets/images/emoji/end.png deleted file mode 100644 index edb0bda24..000000000 Binary files a/app/assets/images/emoji/end.png and /dev/null differ diff --git a/app/assets/images/emoji/envelope.png b/app/assets/images/emoji/envelope.png deleted file mode 100644 index 3631861bb..000000000 Binary files a/app/assets/images/emoji/envelope.png and /dev/null differ diff --git a/app/assets/images/emoji/envelope_with_arrow.png b/app/assets/images/emoji/envelope_with_arrow.png deleted file mode 100644 index 0e01fd5f0..000000000 Binary files a/app/assets/images/emoji/envelope_with_arrow.png and /dev/null differ diff --git a/app/assets/images/emoji/euro.png b/app/assets/images/emoji/euro.png deleted file mode 100644 index 1c5904b71..000000000 Binary files a/app/assets/images/emoji/euro.png and /dev/null differ diff --git a/app/assets/images/emoji/european_castle.png b/app/assets/images/emoji/european_castle.png deleted file mode 100644 index 8229b8a8a..000000000 Binary files a/app/assets/images/emoji/european_castle.png and /dev/null differ diff --git a/app/assets/images/emoji/european_post_office.png b/app/assets/images/emoji/european_post_office.png deleted file mode 100644 index 0f65b1453..000000000 Binary files a/app/assets/images/emoji/european_post_office.png and /dev/null differ diff --git a/app/assets/images/emoji/evergreen_tree.png b/app/assets/images/emoji/evergreen_tree.png deleted file mode 100644 index ae8ad1037..000000000 Binary files a/app/assets/images/emoji/evergreen_tree.png and /dev/null differ diff --git a/app/assets/images/emoji/exclamation.png b/app/assets/images/emoji/exclamation.png deleted file mode 100644 index 4c560f5e3..000000000 Binary files a/app/assets/images/emoji/exclamation.png and /dev/null differ diff --git a/app/assets/images/emoji/expressionless.png b/app/assets/images/emoji/expressionless.png deleted file mode 100644 index 1798f24de..000000000 Binary files a/app/assets/images/emoji/expressionless.png and /dev/null differ diff --git a/app/assets/images/emoji/eyeglasses.png b/app/assets/images/emoji/eyeglasses.png deleted file mode 100644 index a3cf75a27..000000000 Binary files a/app/assets/images/emoji/eyeglasses.png and /dev/null differ diff --git a/app/assets/images/emoji/eyes.png b/app/assets/images/emoji/eyes.png deleted file mode 100644 index dc2216f63..000000000 Binary files a/app/assets/images/emoji/eyes.png and /dev/null differ diff --git a/app/assets/images/emoji/facepunch.png b/app/assets/images/emoji/facepunch.png deleted file mode 100644 index 2d41fd37e..000000000 Binary files a/app/assets/images/emoji/facepunch.png and /dev/null differ diff --git a/app/assets/images/emoji/factory.png b/app/assets/images/emoji/factory.png deleted file mode 100644 index 640463479..000000000 Binary files a/app/assets/images/emoji/factory.png and /dev/null differ diff --git a/app/assets/images/emoji/fallen_leaf.png b/app/assets/images/emoji/fallen_leaf.png deleted file mode 100644 index d49f9c175..000000000 Binary files a/app/assets/images/emoji/fallen_leaf.png and /dev/null differ diff --git a/app/assets/images/emoji/family.png b/app/assets/images/emoji/family.png deleted file mode 100644 index b4b365f3a..000000000 Binary files a/app/assets/images/emoji/family.png and /dev/null differ diff --git a/app/assets/images/emoji/fast_forward.png b/app/assets/images/emoji/fast_forward.png deleted file mode 100644 index b94a11726..000000000 Binary files a/app/assets/images/emoji/fast_forward.png and /dev/null differ diff --git a/app/assets/images/emoji/fax.png b/app/assets/images/emoji/fax.png deleted file mode 100644 index 62be2c958..000000000 Binary files a/app/assets/images/emoji/fax.png and /dev/null differ diff --git a/app/assets/images/emoji/fearful.png b/app/assets/images/emoji/fearful.png deleted file mode 100644 index 513fce47b..000000000 Binary files a/app/assets/images/emoji/fearful.png and /dev/null differ diff --git a/app/assets/images/emoji/feet.png b/app/assets/images/emoji/feet.png deleted file mode 100644 index 89b9fec9e..000000000 Binary files a/app/assets/images/emoji/feet.png and /dev/null differ diff --git a/app/assets/images/emoji/ferris_wheel.png b/app/assets/images/emoji/ferris_wheel.png deleted file mode 100644 index 54a1dcfa1..000000000 Binary files a/app/assets/images/emoji/ferris_wheel.png and /dev/null differ diff --git a/app/assets/images/emoji/file_folder.png b/app/assets/images/emoji/file_folder.png deleted file mode 100644 index 4d8bebf8a..000000000 Binary files a/app/assets/images/emoji/file_folder.png and /dev/null differ diff --git a/app/assets/images/emoji/fire.png b/app/assets/images/emoji/fire.png deleted file mode 100644 index f2a3149bb..000000000 Binary files a/app/assets/images/emoji/fire.png and /dev/null differ diff --git a/app/assets/images/emoji/fire_engine.png b/app/assets/images/emoji/fire_engine.png deleted file mode 100644 index 9e6c59c99..000000000 Binary files a/app/assets/images/emoji/fire_engine.png and /dev/null differ diff --git a/app/assets/images/emoji/fireworks.png b/app/assets/images/emoji/fireworks.png deleted file mode 100644 index b4eccd577..000000000 Binary files a/app/assets/images/emoji/fireworks.png and /dev/null differ diff --git a/app/assets/images/emoji/first_quarter_moon.png b/app/assets/images/emoji/first_quarter_moon.png deleted file mode 100644 index f38c23693..000000000 Binary files a/app/assets/images/emoji/first_quarter_moon.png and /dev/null 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 deleted file mode 100644 index 85ae2ce72..000000000 Binary files a/app/assets/images/emoji/first_quarter_moon_with_face.png and /dev/null differ diff --git a/app/assets/images/emoji/fish.png b/app/assets/images/emoji/fish.png deleted file mode 100644 index dc2a3f52d..000000000 Binary files a/app/assets/images/emoji/fish.png and /dev/null differ diff --git a/app/assets/images/emoji/fish_cake.png b/app/assets/images/emoji/fish_cake.png deleted file mode 100644 index a8f22614d..000000000 Binary files a/app/assets/images/emoji/fish_cake.png and /dev/null differ diff --git a/app/assets/images/emoji/fishing_pole_and_fish.png b/app/assets/images/emoji/fishing_pole_and_fish.png deleted file mode 100644 index d84609c3b..000000000 Binary files a/app/assets/images/emoji/fishing_pole_and_fish.png and /dev/null differ diff --git a/app/assets/images/emoji/fist.png b/app/assets/images/emoji/fist.png deleted file mode 100644 index ecc8874c2..000000000 Binary files a/app/assets/images/emoji/fist.png and /dev/null differ diff --git a/app/assets/images/emoji/flags.png b/app/assets/images/emoji/flags.png deleted file mode 100644 index 540164e84..000000000 Binary files a/app/assets/images/emoji/flags.png and /dev/null differ diff --git a/app/assets/images/emoji/flashlight.png b/app/assets/images/emoji/flashlight.png deleted file mode 100644 index 215940aa8..000000000 Binary files a/app/assets/images/emoji/flashlight.png and /dev/null differ diff --git a/app/assets/images/emoji/floppy_disk.png b/app/assets/images/emoji/floppy_disk.png deleted file mode 100644 index 4ad56315a..000000000 Binary files a/app/assets/images/emoji/floppy_disk.png and /dev/null differ diff --git a/app/assets/images/emoji/flower_playing_cards.png b/app/assets/images/emoji/flower_playing_cards.png deleted file mode 100644 index cc46a6a1f..000000000 Binary files a/app/assets/images/emoji/flower_playing_cards.png and /dev/null differ diff --git a/app/assets/images/emoji/flushed.png b/app/assets/images/emoji/flushed.png deleted file mode 100644 index 9b49410c0..000000000 Binary files a/app/assets/images/emoji/flushed.png and /dev/null differ diff --git a/app/assets/images/emoji/foggy.png b/app/assets/images/emoji/foggy.png deleted file mode 100644 index 3c7b8b04b..000000000 Binary files a/app/assets/images/emoji/foggy.png and /dev/null differ diff --git a/app/assets/images/emoji/football.png b/app/assets/images/emoji/football.png deleted file mode 100644 index 0e4e168fa..000000000 Binary files a/app/assets/images/emoji/football.png and /dev/null differ diff --git a/app/assets/images/emoji/footprints.png b/app/assets/images/emoji/footprints.png deleted file mode 100644 index d7a25614f..000000000 Binary files a/app/assets/images/emoji/footprints.png and /dev/null differ diff --git a/app/assets/images/emoji/fork_and_knife.png b/app/assets/images/emoji/fork_and_knife.png deleted file mode 100644 index 8ba4bc653..000000000 Binary files a/app/assets/images/emoji/fork_and_knife.png and /dev/null differ diff --git a/app/assets/images/emoji/fountain.png b/app/assets/images/emoji/fountain.png deleted file mode 100644 index da126e648..000000000 Binary files a/app/assets/images/emoji/fountain.png and /dev/null differ diff --git a/app/assets/images/emoji/four_leaf_clover.png b/app/assets/images/emoji/four_leaf_clover.png deleted file mode 100644 index f2014bea4..000000000 Binary files a/app/assets/images/emoji/four_leaf_clover.png and /dev/null differ diff --git a/app/assets/images/emoji/free.png b/app/assets/images/emoji/free.png deleted file mode 100644 index c886cf249..000000000 Binary files a/app/assets/images/emoji/free.png and /dev/null differ diff --git a/app/assets/images/emoji/fried_shrimp.png b/app/assets/images/emoji/fried_shrimp.png deleted file mode 100644 index c8c284bf1..000000000 Binary files a/app/assets/images/emoji/fried_shrimp.png and /dev/null differ diff --git a/app/assets/images/emoji/fries.png b/app/assets/images/emoji/fries.png deleted file mode 100644 index cfef66966..000000000 Binary files a/app/assets/images/emoji/fries.png and /dev/null differ diff --git a/app/assets/images/emoji/frog.png b/app/assets/images/emoji/frog.png deleted file mode 100644 index cfe11b18f..000000000 Binary files a/app/assets/images/emoji/frog.png and /dev/null differ diff --git a/app/assets/images/emoji/frowning.png b/app/assets/images/emoji/frowning.png deleted file mode 100644 index 7f8b6c77b..000000000 Binary files a/app/assets/images/emoji/frowning.png and /dev/null differ diff --git a/app/assets/images/emoji/fuelpump.png b/app/assets/images/emoji/fuelpump.png deleted file mode 100644 index 54c29aeb1..000000000 Binary files a/app/assets/images/emoji/fuelpump.png and /dev/null differ diff --git a/app/assets/images/emoji/full_moon.png b/app/assets/images/emoji/full_moon.png deleted file mode 100644 index 8ff657a25..000000000 Binary files a/app/assets/images/emoji/full_moon.png and /dev/null differ diff --git a/app/assets/images/emoji/full_moon_with_face.png b/app/assets/images/emoji/full_moon_with_face.png deleted file mode 100644 index 94395a408..000000000 Binary files a/app/assets/images/emoji/full_moon_with_face.png and /dev/null differ diff --git a/app/assets/images/emoji/game_die.png b/app/assets/images/emoji/game_die.png deleted file mode 100644 index 4136e78ec..000000000 Binary files a/app/assets/images/emoji/game_die.png and /dev/null differ diff --git a/app/assets/images/emoji/gem.png b/app/assets/images/emoji/gem.png deleted file mode 100644 index 8a5d8dad5..000000000 Binary files a/app/assets/images/emoji/gem.png and /dev/null differ diff --git a/app/assets/images/emoji/gemini.png b/app/assets/images/emoji/gemini.png deleted file mode 100644 index d926f6e88..000000000 Binary files a/app/assets/images/emoji/gemini.png and /dev/null differ diff --git a/app/assets/images/emoji/ghost.png b/app/assets/images/emoji/ghost.png deleted file mode 100644 index 671dd0c9e..000000000 Binary files a/app/assets/images/emoji/ghost.png and /dev/null differ diff --git a/app/assets/images/emoji/gift.png b/app/assets/images/emoji/gift.png deleted file mode 100644 index 552cfdc2b..000000000 Binary files a/app/assets/images/emoji/gift.png and /dev/null differ diff --git a/app/assets/images/emoji/gift_heart.png b/app/assets/images/emoji/gift_heart.png deleted file mode 100644 index f31c26a3f..000000000 Binary files a/app/assets/images/emoji/gift_heart.png and /dev/null differ diff --git a/app/assets/images/emoji/girl.png b/app/assets/images/emoji/girl.png deleted file mode 100644 index ea4126941..000000000 Binary files a/app/assets/images/emoji/girl.png and /dev/null differ diff --git a/app/assets/images/emoji/globe_with_meridians.png b/app/assets/images/emoji/globe_with_meridians.png deleted file mode 100644 index b19864667..000000000 Binary files a/app/assets/images/emoji/globe_with_meridians.png and /dev/null differ diff --git a/app/assets/images/emoji/goat.png b/app/assets/images/emoji/goat.png deleted file mode 100644 index 4be9cf304..000000000 Binary files a/app/assets/images/emoji/goat.png and /dev/null differ diff --git a/app/assets/images/emoji/golf.png b/app/assets/images/emoji/golf.png deleted file mode 100644 index cba2116a7..000000000 Binary files a/app/assets/images/emoji/golf.png and /dev/null differ diff --git a/app/assets/images/emoji/grapes.png b/app/assets/images/emoji/grapes.png deleted file mode 100644 index 0f9f007a1..000000000 Binary files a/app/assets/images/emoji/grapes.png and /dev/null differ diff --git a/app/assets/images/emoji/green_apple.png b/app/assets/images/emoji/green_apple.png deleted file mode 100644 index 337205cd1..000000000 Binary files a/app/assets/images/emoji/green_apple.png and /dev/null differ diff --git a/app/assets/images/emoji/green_book.png b/app/assets/images/emoji/green_book.png deleted file mode 100644 index e86651e5c..000000000 Binary files a/app/assets/images/emoji/green_book.png and /dev/null differ diff --git a/app/assets/images/emoji/green_heart.png b/app/assets/images/emoji/green_heart.png deleted file mode 100644 index 7289cb814..000000000 Binary files a/app/assets/images/emoji/green_heart.png and /dev/null differ diff --git a/app/assets/images/emoji/grey_exclamation.png b/app/assets/images/emoji/grey_exclamation.png deleted file mode 100644 index a50d265e9..000000000 Binary files a/app/assets/images/emoji/grey_exclamation.png and /dev/null differ diff --git a/app/assets/images/emoji/grey_question.png b/app/assets/images/emoji/grey_question.png deleted file mode 100644 index 57db41ead..000000000 Binary files a/app/assets/images/emoji/grey_question.png and /dev/null differ diff --git a/app/assets/images/emoji/grimacing.png b/app/assets/images/emoji/grimacing.png deleted file mode 100644 index f78e9407d..000000000 Binary files a/app/assets/images/emoji/grimacing.png and /dev/null differ diff --git a/app/assets/images/emoji/grin.png b/app/assets/images/emoji/grin.png deleted file mode 100644 index 591cfcef8..000000000 Binary files a/app/assets/images/emoji/grin.png and /dev/null differ diff --git a/app/assets/images/emoji/grinning.png b/app/assets/images/emoji/grinning.png deleted file mode 100644 index 0ef00d79d..000000000 Binary files a/app/assets/images/emoji/grinning.png and /dev/null differ diff --git a/app/assets/images/emoji/guardsman.png b/app/assets/images/emoji/guardsman.png deleted file mode 100644 index b67b335d6..000000000 Binary files a/app/assets/images/emoji/guardsman.png and /dev/null differ diff --git a/app/assets/images/emoji/guitar.png b/app/assets/images/emoji/guitar.png deleted file mode 100644 index 2b7fa43c9..000000000 Binary files a/app/assets/images/emoji/guitar.png and /dev/null differ diff --git a/app/assets/images/emoji/gun.png b/app/assets/images/emoji/gun.png deleted file mode 100644 index c49dc52c6..000000000 Binary files a/app/assets/images/emoji/gun.png and /dev/null differ diff --git a/app/assets/images/emoji/haircut.png b/app/assets/images/emoji/haircut.png deleted file mode 100644 index 902d273f6..000000000 Binary files a/app/assets/images/emoji/haircut.png and /dev/null differ diff --git a/app/assets/images/emoji/hamburger.png b/app/assets/images/emoji/hamburger.png deleted file mode 100644 index 9f1a3fdff..000000000 Binary files a/app/assets/images/emoji/hamburger.png and /dev/null differ diff --git a/app/assets/images/emoji/hammer.png b/app/assets/images/emoji/hammer.png deleted file mode 100644 index 6b75bc37b..000000000 Binary files a/app/assets/images/emoji/hammer.png and /dev/null differ diff --git a/app/assets/images/emoji/hamster.png b/app/assets/images/emoji/hamster.png deleted file mode 100644 index ada9c3108..000000000 Binary files a/app/assets/images/emoji/hamster.png and /dev/null differ diff --git a/app/assets/images/emoji/hand.png b/app/assets/images/emoji/hand.png deleted file mode 100644 index 5e45c25a5..000000000 Binary files a/app/assets/images/emoji/hand.png and /dev/null differ diff --git a/app/assets/images/emoji/handbag.png b/app/assets/images/emoji/handbag.png deleted file mode 100644 index d7adf04dd..000000000 Binary files a/app/assets/images/emoji/handbag.png and /dev/null differ diff --git a/app/assets/images/emoji/hankey.png b/app/assets/images/emoji/hankey.png deleted file mode 100644 index 73a4dc840..000000000 Binary files a/app/assets/images/emoji/hankey.png and /dev/null differ diff --git a/app/assets/images/emoji/hatched_chick.png b/app/assets/images/emoji/hatched_chick.png deleted file mode 100644 index 39c25bc7c..000000000 Binary files a/app/assets/images/emoji/hatched_chick.png and /dev/null differ diff --git a/app/assets/images/emoji/hatching_chick.png b/app/assets/images/emoji/hatching_chick.png deleted file mode 100644 index 005a55519..000000000 Binary files a/app/assets/images/emoji/hatching_chick.png and /dev/null differ diff --git a/app/assets/images/emoji/headphones.png b/app/assets/images/emoji/headphones.png deleted file mode 100644 index ad83000e6..000000000 Binary files a/app/assets/images/emoji/headphones.png and /dev/null differ diff --git a/app/assets/images/emoji/hear_no_evil.png b/app/assets/images/emoji/hear_no_evil.png deleted file mode 100644 index f97a1f9a0..000000000 Binary files a/app/assets/images/emoji/hear_no_evil.png and /dev/null differ diff --git a/app/assets/images/emoji/heart.png b/app/assets/images/emoji/heart.png deleted file mode 100644 index 7d7790ce4..000000000 Binary files a/app/assets/images/emoji/heart.png and /dev/null differ diff --git a/app/assets/images/emoji/heart_decoration.png b/app/assets/images/emoji/heart_decoration.png deleted file mode 100644 index b40a48675..000000000 Binary files a/app/assets/images/emoji/heart_decoration.png and /dev/null differ diff --git a/app/assets/images/emoji/heart_eyes.png b/app/assets/images/emoji/heart_eyes.png deleted file mode 100644 index 0e5794270..000000000 Binary files a/app/assets/images/emoji/heart_eyes.png and /dev/null differ diff --git a/app/assets/images/emoji/heart_eyes_cat.png b/app/assets/images/emoji/heart_eyes_cat.png deleted file mode 100644 index eeba240e5..000000000 Binary files a/app/assets/images/emoji/heart_eyes_cat.png and /dev/null differ diff --git a/app/assets/images/emoji/heartbeat.png b/app/assets/images/emoji/heartbeat.png deleted file mode 100644 index b6628f6fa..000000000 Binary files a/app/assets/images/emoji/heartbeat.png and /dev/null differ diff --git a/app/assets/images/emoji/heartpulse.png b/app/assets/images/emoji/heartpulse.png deleted file mode 100644 index a7491cbea..000000000 Binary files a/app/assets/images/emoji/heartpulse.png and /dev/null differ diff --git a/app/assets/images/emoji/hearts.png b/app/assets/images/emoji/hearts.png deleted file mode 100644 index e89471538..000000000 Binary files a/app/assets/images/emoji/hearts.png and /dev/null differ diff --git a/app/assets/images/emoji/heavy_check_mark.png b/app/assets/images/emoji/heavy_check_mark.png deleted file mode 100644 index 336d2626d..000000000 Binary files a/app/assets/images/emoji/heavy_check_mark.png and /dev/null differ diff --git a/app/assets/images/emoji/heavy_division_sign.png b/app/assets/images/emoji/heavy_division_sign.png deleted file mode 100644 index ac757a238..000000000 Binary files a/app/assets/images/emoji/heavy_division_sign.png and /dev/null differ diff --git a/app/assets/images/emoji/heavy_dollar_sign.png b/app/assets/images/emoji/heavy_dollar_sign.png deleted file mode 100644 index 361e26aef..000000000 Binary files a/app/assets/images/emoji/heavy_dollar_sign.png and /dev/null differ diff --git a/app/assets/images/emoji/heavy_exclamation_mark.png b/app/assets/images/emoji/heavy_exclamation_mark.png deleted file mode 100644 index 4c560f5e3..000000000 Binary files a/app/assets/images/emoji/heavy_exclamation_mark.png and /dev/null differ diff --git a/app/assets/images/emoji/heavy_minus_sign.png b/app/assets/images/emoji/heavy_minus_sign.png deleted file mode 100644 index b8d3d82f2..000000000 Binary files a/app/assets/images/emoji/heavy_minus_sign.png and /dev/null differ diff --git a/app/assets/images/emoji/heavy_multiplication_x.png b/app/assets/images/emoji/heavy_multiplication_x.png deleted file mode 100644 index 13d666078..000000000 Binary files a/app/assets/images/emoji/heavy_multiplication_x.png and /dev/null differ diff --git a/app/assets/images/emoji/heavy_plus_sign.png b/app/assets/images/emoji/heavy_plus_sign.png deleted file mode 100644 index 61595387b..000000000 Binary files a/app/assets/images/emoji/heavy_plus_sign.png and /dev/null differ diff --git a/app/assets/images/emoji/helicopter.png b/app/assets/images/emoji/helicopter.png deleted file mode 100644 index 8e82a0d58..000000000 Binary files a/app/assets/images/emoji/helicopter.png and /dev/null differ diff --git a/app/assets/images/emoji/herb.png b/app/assets/images/emoji/herb.png deleted file mode 100644 index de1ff1b73..000000000 Binary files a/app/assets/images/emoji/herb.png and /dev/null differ diff --git a/app/assets/images/emoji/hibiscus.png b/app/assets/images/emoji/hibiscus.png deleted file mode 100644 index 32a3774c0..000000000 Binary files a/app/assets/images/emoji/hibiscus.png and /dev/null differ diff --git a/app/assets/images/emoji/high_brightness.png b/app/assets/images/emoji/high_brightness.png deleted file mode 100644 index ba9de7d40..000000000 Binary files a/app/assets/images/emoji/high_brightness.png and /dev/null differ diff --git a/app/assets/images/emoji/high_heel.png b/app/assets/images/emoji/high_heel.png deleted file mode 100644 index 525b6a0dd..000000000 Binary files a/app/assets/images/emoji/high_heel.png and /dev/null differ diff --git a/app/assets/images/emoji/hocho.png b/app/assets/images/emoji/hocho.png deleted file mode 100644 index 18eade0ac..000000000 Binary files a/app/assets/images/emoji/hocho.png and /dev/null differ diff --git a/app/assets/images/emoji/honey_pot.png b/app/assets/images/emoji/honey_pot.png deleted file mode 100644 index 73278898a..000000000 Binary files a/app/assets/images/emoji/honey_pot.png and /dev/null differ diff --git a/app/assets/images/emoji/honeybee.png b/app/assets/images/emoji/honeybee.png deleted file mode 100644 index f53733953..000000000 Binary files a/app/assets/images/emoji/honeybee.png and /dev/null differ diff --git a/app/assets/images/emoji/horse.png b/app/assets/images/emoji/horse.png deleted file mode 100644 index 78d580ad3..000000000 Binary files a/app/assets/images/emoji/horse.png and /dev/null differ diff --git a/app/assets/images/emoji/horse_racing.png b/app/assets/images/emoji/horse_racing.png deleted file mode 100644 index e3bbaec1d..000000000 Binary files a/app/assets/images/emoji/horse_racing.png and /dev/null differ diff --git a/app/assets/images/emoji/hospital.png b/app/assets/images/emoji/hospital.png deleted file mode 100644 index c05c49377..000000000 Binary files a/app/assets/images/emoji/hospital.png and /dev/null differ diff --git a/app/assets/images/emoji/hotel.png b/app/assets/images/emoji/hotel.png deleted file mode 100644 index d29f276a1..000000000 Binary files a/app/assets/images/emoji/hotel.png and /dev/null differ diff --git a/app/assets/images/emoji/hotsprings.png b/app/assets/images/emoji/hotsprings.png deleted file mode 100644 index a0bc9d75f..000000000 Binary files a/app/assets/images/emoji/hotsprings.png and /dev/null differ diff --git a/app/assets/images/emoji/hourglass.png b/app/assets/images/emoji/hourglass.png deleted file mode 100644 index 405aab41b..000000000 Binary files a/app/assets/images/emoji/hourglass.png and /dev/null differ diff --git a/app/assets/images/emoji/hourglass_flowing_sand.png b/app/assets/images/emoji/hourglass_flowing_sand.png deleted file mode 100644 index 52c9eb704..000000000 Binary files a/app/assets/images/emoji/hourglass_flowing_sand.png and /dev/null differ diff --git a/app/assets/images/emoji/house.png b/app/assets/images/emoji/house.png deleted file mode 100644 index 95b9ee094..000000000 Binary files a/app/assets/images/emoji/house.png and /dev/null differ diff --git a/app/assets/images/emoji/house_with_garden.png b/app/assets/images/emoji/house_with_garden.png deleted file mode 100644 index eccbfe943..000000000 Binary files a/app/assets/images/emoji/house_with_garden.png and /dev/null differ diff --git a/app/assets/images/emoji/hushed.png b/app/assets/images/emoji/hushed.png deleted file mode 100644 index afa3f6686..000000000 Binary files a/app/assets/images/emoji/hushed.png and /dev/null differ diff --git a/app/assets/images/emoji/ice_cream.png b/app/assets/images/emoji/ice_cream.png deleted file mode 100644 index 190be0165..000000000 Binary files a/app/assets/images/emoji/ice_cream.png and /dev/null differ diff --git a/app/assets/images/emoji/icecream.png b/app/assets/images/emoji/icecream.png deleted file mode 100644 index 871ce0976..000000000 Binary files a/app/assets/images/emoji/icecream.png and /dev/null differ diff --git a/app/assets/images/emoji/id.png b/app/assets/images/emoji/id.png deleted file mode 100644 index 47437a76d..000000000 Binary files a/app/assets/images/emoji/id.png and /dev/null differ diff --git a/app/assets/images/emoji/ideograph_advantage.png b/app/assets/images/emoji/ideograph_advantage.png deleted file mode 100644 index e79af7844..000000000 Binary files a/app/assets/images/emoji/ideograph_advantage.png and /dev/null differ diff --git a/app/assets/images/emoji/imp.png b/app/assets/images/emoji/imp.png deleted file mode 100644 index 48e570105..000000000 Binary files a/app/assets/images/emoji/imp.png and /dev/null differ diff --git a/app/assets/images/emoji/inbox_tray.png b/app/assets/images/emoji/inbox_tray.png deleted file mode 100644 index e2df0f897..000000000 Binary files a/app/assets/images/emoji/inbox_tray.png and /dev/null differ diff --git a/app/assets/images/emoji/incoming_envelope.png b/app/assets/images/emoji/incoming_envelope.png deleted file mode 100644 index afc827125..000000000 Binary files a/app/assets/images/emoji/incoming_envelope.png and /dev/null differ diff --git a/app/assets/images/emoji/information_desk_person.png b/app/assets/images/emoji/information_desk_person.png deleted file mode 100644 index 52c0a50a3..000000000 Binary files a/app/assets/images/emoji/information_desk_person.png and /dev/null differ diff --git a/app/assets/images/emoji/information_source.png b/app/assets/images/emoji/information_source.png deleted file mode 100644 index 9cb8b09b2..000000000 Binary files a/app/assets/images/emoji/information_source.png and /dev/null differ diff --git a/app/assets/images/emoji/innocent.png b/app/assets/images/emoji/innocent.png deleted file mode 100644 index 503b614f8..000000000 Binary files a/app/assets/images/emoji/innocent.png and /dev/null differ diff --git a/app/assets/images/emoji/interrobang.png b/app/assets/images/emoji/interrobang.png deleted file mode 100644 index 64304b9f5..000000000 Binary files a/app/assets/images/emoji/interrobang.png and /dev/null differ diff --git a/app/assets/images/emoji/iphone.png b/app/assets/images/emoji/iphone.png deleted file mode 100644 index df007103b..000000000 Binary files a/app/assets/images/emoji/iphone.png and /dev/null differ diff --git a/app/assets/images/emoji/izakaya_lantern.png b/app/assets/images/emoji/izakaya_lantern.png deleted file mode 100644 index 18730ad55..000000000 Binary files a/app/assets/images/emoji/izakaya_lantern.png and /dev/null differ diff --git a/app/assets/images/emoji/jack_o_lantern.png b/app/assets/images/emoji/jack_o_lantern.png deleted file mode 100644 index 1f7667ea4..000000000 Binary files a/app/assets/images/emoji/jack_o_lantern.png and /dev/null differ diff --git a/app/assets/images/emoji/japan.png b/app/assets/images/emoji/japan.png deleted file mode 100644 index 459328035..000000000 Binary files a/app/assets/images/emoji/japan.png and /dev/null differ diff --git a/app/assets/images/emoji/japanese_castle.png b/app/assets/images/emoji/japanese_castle.png deleted file mode 100644 index f225ab217..000000000 Binary files a/app/assets/images/emoji/japanese_castle.png and /dev/null differ diff --git a/app/assets/images/emoji/japanese_goblin.png b/app/assets/images/emoji/japanese_goblin.png deleted file mode 100644 index bd21b1875..000000000 Binary files a/app/assets/images/emoji/japanese_goblin.png and /dev/null differ diff --git a/app/assets/images/emoji/japanese_ogre.png b/app/assets/images/emoji/japanese_ogre.png deleted file mode 100644 index e9f5471c9..000000000 Binary files a/app/assets/images/emoji/japanese_ogre.png and /dev/null differ diff --git a/app/assets/images/emoji/jeans.png b/app/assets/images/emoji/jeans.png deleted file mode 100644 index d721cea54..000000000 Binary files a/app/assets/images/emoji/jeans.png and /dev/null differ diff --git a/app/assets/images/emoji/joy.png b/app/assets/images/emoji/joy.png deleted file mode 100644 index 47df693d4..000000000 Binary files a/app/assets/images/emoji/joy.png and /dev/null differ diff --git a/app/assets/images/emoji/joy_cat.png b/app/assets/images/emoji/joy_cat.png deleted file mode 100644 index 6c60cb0ef..000000000 Binary files a/app/assets/images/emoji/joy_cat.png and /dev/null differ diff --git a/app/assets/images/emoji/key.png b/app/assets/images/emoji/key.png deleted file mode 100644 index 34673213f..000000000 Binary files a/app/assets/images/emoji/key.png and /dev/null differ diff --git a/app/assets/images/emoji/keycap_ten.png b/app/assets/images/emoji/keycap_ten.png deleted file mode 100644 index 71dac1c1c..000000000 Binary files a/app/assets/images/emoji/keycap_ten.png and /dev/null differ diff --git a/app/assets/images/emoji/kimono.png b/app/assets/images/emoji/kimono.png deleted file mode 100644 index 34ffe137d..000000000 Binary files a/app/assets/images/emoji/kimono.png and /dev/null differ diff --git a/app/assets/images/emoji/kiss.png b/app/assets/images/emoji/kiss.png deleted file mode 100644 index 4ae2c2b5d..000000000 Binary files a/app/assets/images/emoji/kiss.png and /dev/null differ diff --git a/app/assets/images/emoji/kissing.png b/app/assets/images/emoji/kissing.png deleted file mode 100644 index eb049c80b..000000000 Binary files a/app/assets/images/emoji/kissing.png and /dev/null differ diff --git a/app/assets/images/emoji/kissing_cat.png b/app/assets/images/emoji/kissing_cat.png deleted file mode 100644 index adc62fbe3..000000000 Binary files a/app/assets/images/emoji/kissing_cat.png and /dev/null differ diff --git a/app/assets/images/emoji/kissing_closed_eyes.png b/app/assets/images/emoji/kissing_closed_eyes.png deleted file mode 100644 index 449de1970..000000000 Binary files a/app/assets/images/emoji/kissing_closed_eyes.png and /dev/null differ diff --git a/app/assets/images/emoji/kissing_heart.png b/app/assets/images/emoji/kissing_heart.png deleted file mode 100644 index af9a80b7f..000000000 Binary files a/app/assets/images/emoji/kissing_heart.png and /dev/null differ diff --git a/app/assets/images/emoji/kissing_smiling_eyes.png b/app/assets/images/emoji/kissing_smiling_eyes.png deleted file mode 100644 index d85706e70..000000000 Binary files a/app/assets/images/emoji/kissing_smiling_eyes.png and /dev/null differ diff --git a/app/assets/images/emoji/koala.png b/app/assets/images/emoji/koala.png deleted file mode 100644 index e17bd3cf5..000000000 Binary files a/app/assets/images/emoji/koala.png and /dev/null differ diff --git a/app/assets/images/emoji/koko.png b/app/assets/images/emoji/koko.png deleted file mode 100644 index 3bef28c9f..000000000 Binary files a/app/assets/images/emoji/koko.png and /dev/null differ diff --git a/app/assets/images/emoji/lantern.png b/app/assets/images/emoji/lantern.png deleted file mode 100644 index 18730ad55..000000000 Binary files a/app/assets/images/emoji/lantern.png and /dev/null differ diff --git a/app/assets/images/emoji/large_blue_circle.png b/app/assets/images/emoji/large_blue_circle.png deleted file mode 100644 index a5b4ad4aa..000000000 Binary files a/app/assets/images/emoji/large_blue_circle.png and /dev/null differ diff --git a/app/assets/images/emoji/large_blue_diamond.png b/app/assets/images/emoji/large_blue_diamond.png deleted file mode 100644 index f4598ec0f..000000000 Binary files a/app/assets/images/emoji/large_blue_diamond.png and /dev/null differ diff --git a/app/assets/images/emoji/large_orange_diamond.png b/app/assets/images/emoji/large_orange_diamond.png deleted file mode 100644 index 46d52e5cb..000000000 Binary files a/app/assets/images/emoji/large_orange_diamond.png and /dev/null differ diff --git a/app/assets/images/emoji/last_quarter_moon.png b/app/assets/images/emoji/last_quarter_moon.png deleted file mode 100644 index 355e3c3f7..000000000 Binary files a/app/assets/images/emoji/last_quarter_moon.png and /dev/null 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 deleted file mode 100644 index 9ece82dfe..000000000 Binary files a/app/assets/images/emoji/last_quarter_moon_with_face.png and /dev/null differ diff --git a/app/assets/images/emoji/laughing.png b/app/assets/images/emoji/laughing.png deleted file mode 100644 index 11c91eb22..000000000 Binary files a/app/assets/images/emoji/laughing.png and /dev/null differ diff --git a/app/assets/images/emoji/leaves.png b/app/assets/images/emoji/leaves.png deleted file mode 100644 index 801e578e6..000000000 Binary files a/app/assets/images/emoji/leaves.png and /dev/null differ diff --git a/app/assets/images/emoji/ledger.png b/app/assets/images/emoji/ledger.png deleted file mode 100644 index e4f72acea..000000000 Binary files a/app/assets/images/emoji/ledger.png and /dev/null differ diff --git a/app/assets/images/emoji/left_luggage.png b/app/assets/images/emoji/left_luggage.png deleted file mode 100644 index 1c08b464d..000000000 Binary files a/app/assets/images/emoji/left_luggage.png and /dev/null differ diff --git a/app/assets/images/emoji/left_right_arrow.png b/app/assets/images/emoji/left_right_arrow.png deleted file mode 100644 index b9fd11c51..000000000 Binary files a/app/assets/images/emoji/left_right_arrow.png and /dev/null differ diff --git a/app/assets/images/emoji/leftwards_arrow_with_hook.png b/app/assets/images/emoji/leftwards_arrow_with_hook.png deleted file mode 100644 index bc45dfefd..000000000 Binary files a/app/assets/images/emoji/leftwards_arrow_with_hook.png and /dev/null differ diff --git a/app/assets/images/emoji/lemon.png b/app/assets/images/emoji/lemon.png deleted file mode 100644 index 9814dc959..000000000 Binary files a/app/assets/images/emoji/lemon.png and /dev/null differ diff --git a/app/assets/images/emoji/leo.png b/app/assets/images/emoji/leo.png deleted file mode 100644 index e025933b2..000000000 Binary files a/app/assets/images/emoji/leo.png and /dev/null differ diff --git a/app/assets/images/emoji/leopard.png b/app/assets/images/emoji/leopard.png deleted file mode 100644 index 8abfc4a27..000000000 Binary files a/app/assets/images/emoji/leopard.png and /dev/null differ diff --git a/app/assets/images/emoji/libra.png b/app/assets/images/emoji/libra.png deleted file mode 100644 index c9062dd2e..000000000 Binary files a/app/assets/images/emoji/libra.png and /dev/null differ diff --git a/app/assets/images/emoji/light_rail.png b/app/assets/images/emoji/light_rail.png deleted file mode 100644 index bcfe801ee..000000000 Binary files a/app/assets/images/emoji/light_rail.png and /dev/null differ diff --git a/app/assets/images/emoji/link.png b/app/assets/images/emoji/link.png deleted file mode 100644 index ffb8f62ce..000000000 Binary files a/app/assets/images/emoji/link.png and /dev/null differ diff --git a/app/assets/images/emoji/lips.png b/app/assets/images/emoji/lips.png deleted file mode 100644 index 826ed1102..000000000 Binary files a/app/assets/images/emoji/lips.png and /dev/null differ diff --git a/app/assets/images/emoji/lipstick.png b/app/assets/images/emoji/lipstick.png deleted file mode 100644 index 82f990c56..000000000 Binary files a/app/assets/images/emoji/lipstick.png and /dev/null differ diff --git a/app/assets/images/emoji/lock.png b/app/assets/images/emoji/lock.png deleted file mode 100644 index 4892b0235..000000000 Binary files a/app/assets/images/emoji/lock.png and /dev/null differ diff --git a/app/assets/images/emoji/lock_with_ink_pen.png b/app/assets/images/emoji/lock_with_ink_pen.png deleted file mode 100644 index 375e67e82..000000000 Binary files a/app/assets/images/emoji/lock_with_ink_pen.png and /dev/null differ diff --git a/app/assets/images/emoji/lollipop.png b/app/assets/images/emoji/lollipop.png deleted file mode 100644 index ba55e7093..000000000 Binary files a/app/assets/images/emoji/lollipop.png and /dev/null differ diff --git a/app/assets/images/emoji/loop.png b/app/assets/images/emoji/loop.png deleted file mode 100644 index ef34df3a4..000000000 Binary files a/app/assets/images/emoji/loop.png and /dev/null differ diff --git a/app/assets/images/emoji/loudspeaker.png b/app/assets/images/emoji/loudspeaker.png deleted file mode 100644 index 752385e52..000000000 Binary files a/app/assets/images/emoji/loudspeaker.png and /dev/null differ diff --git a/app/assets/images/emoji/love_hotel.png b/app/assets/images/emoji/love_hotel.png deleted file mode 100644 index 44d7db828..000000000 Binary files a/app/assets/images/emoji/love_hotel.png and /dev/null differ diff --git a/app/assets/images/emoji/love_letter.png b/app/assets/images/emoji/love_letter.png deleted file mode 100644 index e29981f44..000000000 Binary files a/app/assets/images/emoji/love_letter.png and /dev/null differ diff --git a/app/assets/images/emoji/low_brightness.png b/app/assets/images/emoji/low_brightness.png deleted file mode 100644 index ea15bde4f..000000000 Binary files a/app/assets/images/emoji/low_brightness.png and /dev/null differ diff --git a/app/assets/images/emoji/m.png b/app/assets/images/emoji/m.png deleted file mode 100644 index 7424665e2..000000000 Binary files a/app/assets/images/emoji/m.png and /dev/null differ diff --git a/app/assets/images/emoji/mag.png b/app/assets/images/emoji/mag.png deleted file mode 100644 index aa5b1d7c4..000000000 Binary files a/app/assets/images/emoji/mag.png and /dev/null differ diff --git a/app/assets/images/emoji/mag_right.png b/app/assets/images/emoji/mag_right.png deleted file mode 100644 index 6e6cf11e6..000000000 Binary files a/app/assets/images/emoji/mag_right.png and /dev/null differ diff --git a/app/assets/images/emoji/mahjong.png b/app/assets/images/emoji/mahjong.png deleted file mode 100644 index f51ce65fd..000000000 Binary files a/app/assets/images/emoji/mahjong.png and /dev/null differ diff --git a/app/assets/images/emoji/mailbox.png b/app/assets/images/emoji/mailbox.png deleted file mode 100644 index 8351e7076..000000000 Binary files a/app/assets/images/emoji/mailbox.png and /dev/null differ diff --git a/app/assets/images/emoji/mailbox_closed.png b/app/assets/images/emoji/mailbox_closed.png deleted file mode 100644 index a5982b69b..000000000 Binary files a/app/assets/images/emoji/mailbox_closed.png and /dev/null differ diff --git a/app/assets/images/emoji/mailbox_with_mail.png b/app/assets/images/emoji/mailbox_with_mail.png deleted file mode 100644 index dae345943..000000000 Binary files a/app/assets/images/emoji/mailbox_with_mail.png and /dev/null differ diff --git a/app/assets/images/emoji/mailbox_with_no_mail.png b/app/assets/images/emoji/mailbox_with_no_mail.png deleted file mode 100644 index 59f15c5d7..000000000 Binary files a/app/assets/images/emoji/mailbox_with_no_mail.png and /dev/null differ diff --git a/app/assets/images/emoji/man.png b/app/assets/images/emoji/man.png deleted file mode 100644 index d9bfa26a6..000000000 Binary files a/app/assets/images/emoji/man.png and /dev/null 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 deleted file mode 100644 index 7aad74b55..000000000 Binary files a/app/assets/images/emoji/man_with_gua_pi_mao.png and /dev/null differ diff --git a/app/assets/images/emoji/man_with_turban.png b/app/assets/images/emoji/man_with_turban.png deleted file mode 100644 index 036604caf..000000000 Binary files a/app/assets/images/emoji/man_with_turban.png and /dev/null differ diff --git a/app/assets/images/emoji/mans_shoe.png b/app/assets/images/emoji/mans_shoe.png deleted file mode 100644 index ecba9ba7d..000000000 Binary files a/app/assets/images/emoji/mans_shoe.png and /dev/null differ diff --git a/app/assets/images/emoji/maple_leaf.png b/app/assets/images/emoji/maple_leaf.png deleted file mode 100644 index 4e9b47207..000000000 Binary files a/app/assets/images/emoji/maple_leaf.png and /dev/null differ diff --git a/app/assets/images/emoji/mask.png b/app/assets/images/emoji/mask.png deleted file mode 100644 index 05887e99c..000000000 Binary files a/app/assets/images/emoji/mask.png and /dev/null differ diff --git a/app/assets/images/emoji/massage.png b/app/assets/images/emoji/massage.png deleted file mode 100644 index dd30d1597..000000000 Binary files a/app/assets/images/emoji/massage.png and /dev/null differ diff --git a/app/assets/images/emoji/meat_on_bone.png b/app/assets/images/emoji/meat_on_bone.png deleted file mode 100644 index d6b311b6b..000000000 Binary files a/app/assets/images/emoji/meat_on_bone.png and /dev/null differ diff --git a/app/assets/images/emoji/mega.png b/app/assets/images/emoji/mega.png deleted file mode 100644 index 5d9319e72..000000000 Binary files a/app/assets/images/emoji/mega.png and /dev/null differ diff --git a/app/assets/images/emoji/melon.png b/app/assets/images/emoji/melon.png deleted file mode 100644 index 11c13cbbd..000000000 Binary files a/app/assets/images/emoji/melon.png and /dev/null differ diff --git a/app/assets/images/emoji/memo.png b/app/assets/images/emoji/memo.png deleted file mode 100644 index fc97ddbc9..000000000 Binary files a/app/assets/images/emoji/memo.png and /dev/null differ diff --git a/app/assets/images/emoji/mens.png b/app/assets/images/emoji/mens.png deleted file mode 100644 index abccfc9f2..000000000 Binary files a/app/assets/images/emoji/mens.png and /dev/null differ diff --git a/app/assets/images/emoji/metro.png b/app/assets/images/emoji/metro.png deleted file mode 100644 index 7f34f6be3..000000000 Binary files a/app/assets/images/emoji/metro.png and /dev/null differ diff --git a/app/assets/images/emoji/microphone.png b/app/assets/images/emoji/microphone.png deleted file mode 100644 index ce19a2bb6..000000000 Binary files a/app/assets/images/emoji/microphone.png and /dev/null differ diff --git a/app/assets/images/emoji/microscope.png b/app/assets/images/emoji/microscope.png deleted file mode 100644 index f11d54c01..000000000 Binary files a/app/assets/images/emoji/microscope.png and /dev/null differ diff --git a/app/assets/images/emoji/milky_way.png b/app/assets/images/emoji/milky_way.png deleted file mode 100644 index 901090a12..000000000 Binary files a/app/assets/images/emoji/milky_way.png and /dev/null differ diff --git a/app/assets/images/emoji/minibus.png b/app/assets/images/emoji/minibus.png deleted file mode 100644 index c52cef234..000000000 Binary files a/app/assets/images/emoji/minibus.png and /dev/null differ diff --git a/app/assets/images/emoji/minidisc.png b/app/assets/images/emoji/minidisc.png deleted file mode 100644 index e19cc5d01..000000000 Binary files a/app/assets/images/emoji/minidisc.png and /dev/null differ diff --git a/app/assets/images/emoji/mobile_phone_off.png b/app/assets/images/emoji/mobile_phone_off.png deleted file mode 100644 index fa16c763c..000000000 Binary files a/app/assets/images/emoji/mobile_phone_off.png and /dev/null differ diff --git a/app/assets/images/emoji/money_with_wings.png b/app/assets/images/emoji/money_with_wings.png deleted file mode 100644 index 135e3981e..000000000 Binary files a/app/assets/images/emoji/money_with_wings.png and /dev/null differ diff --git a/app/assets/images/emoji/moneybag.png b/app/assets/images/emoji/moneybag.png deleted file mode 100644 index 5546c04ba..000000000 Binary files a/app/assets/images/emoji/moneybag.png and /dev/null differ diff --git a/app/assets/images/emoji/monkey.png b/app/assets/images/emoji/monkey.png deleted file mode 100644 index 640703597..000000000 Binary files a/app/assets/images/emoji/monkey.png and /dev/null differ diff --git a/app/assets/images/emoji/monkey_face.png b/app/assets/images/emoji/monkey_face.png deleted file mode 100644 index 6964cf4d5..000000000 Binary files a/app/assets/images/emoji/monkey_face.png and /dev/null differ diff --git a/app/assets/images/emoji/monorail.png b/app/assets/images/emoji/monorail.png deleted file mode 100644 index 913d30024..000000000 Binary files a/app/assets/images/emoji/monorail.png and /dev/null differ diff --git a/app/assets/images/emoji/moon.png b/app/assets/images/emoji/moon.png deleted file mode 100644 index dd8c48458..000000000 Binary files a/app/assets/images/emoji/moon.png and /dev/null differ diff --git a/app/assets/images/emoji/mortar_board.png b/app/assets/images/emoji/mortar_board.png deleted file mode 100644 index 2e811b097..000000000 Binary files a/app/assets/images/emoji/mortar_board.png and /dev/null differ diff --git a/app/assets/images/emoji/mount_fuji.png b/app/assets/images/emoji/mount_fuji.png deleted file mode 100644 index 4c313e583..000000000 Binary files a/app/assets/images/emoji/mount_fuji.png and /dev/null differ diff --git a/app/assets/images/emoji/mountain_bicyclist.png b/app/assets/images/emoji/mountain_bicyclist.png deleted file mode 100644 index b69889756..000000000 Binary files a/app/assets/images/emoji/mountain_bicyclist.png and /dev/null differ diff --git a/app/assets/images/emoji/mountain_cableway.png b/app/assets/images/emoji/mountain_cableway.png deleted file mode 100644 index 5688bb239..000000000 Binary files a/app/assets/images/emoji/mountain_cableway.png and /dev/null differ diff --git a/app/assets/images/emoji/mountain_railway.png b/app/assets/images/emoji/mountain_railway.png deleted file mode 100644 index 1f3d1aab5..000000000 Binary files a/app/assets/images/emoji/mountain_railway.png and /dev/null differ diff --git a/app/assets/images/emoji/mouse.png b/app/assets/images/emoji/mouse.png deleted file mode 100644 index 8ff162e2d..000000000 Binary files a/app/assets/images/emoji/mouse.png and /dev/null differ diff --git a/app/assets/images/emoji/mouse2.png b/app/assets/images/emoji/mouse2.png deleted file mode 100644 index 2d777e5e1..000000000 Binary files a/app/assets/images/emoji/mouse2.png and /dev/null differ diff --git a/app/assets/images/emoji/movie_camera.png b/app/assets/images/emoji/movie_camera.png deleted file mode 100644 index 9c1438409..000000000 Binary files a/app/assets/images/emoji/movie_camera.png and /dev/null differ diff --git a/app/assets/images/emoji/moyai.png b/app/assets/images/emoji/moyai.png deleted file mode 100644 index 61a1a9c21..000000000 Binary files a/app/assets/images/emoji/moyai.png and /dev/null differ diff --git a/app/assets/images/emoji/muscle.png b/app/assets/images/emoji/muscle.png deleted file mode 100644 index 19f92efb6..000000000 Binary files a/app/assets/images/emoji/muscle.png and /dev/null differ diff --git a/app/assets/images/emoji/mushroom.png b/app/assets/images/emoji/mushroom.png deleted file mode 100644 index 5eeed8e79..000000000 Binary files a/app/assets/images/emoji/mushroom.png and /dev/null differ diff --git a/app/assets/images/emoji/musical_keyboard.png b/app/assets/images/emoji/musical_keyboard.png deleted file mode 100644 index 93647a4a3..000000000 Binary files a/app/assets/images/emoji/musical_keyboard.png and /dev/null differ diff --git a/app/assets/images/emoji/musical_note.png b/app/assets/images/emoji/musical_note.png deleted file mode 100644 index 68b261bcb..000000000 Binary files a/app/assets/images/emoji/musical_note.png and /dev/null differ diff --git a/app/assets/images/emoji/musical_score.png b/app/assets/images/emoji/musical_score.png deleted file mode 100644 index 0c927d32f..000000000 Binary files a/app/assets/images/emoji/musical_score.png and /dev/null differ diff --git a/app/assets/images/emoji/mute.png b/app/assets/images/emoji/mute.png deleted file mode 100644 index 4cf67c367..000000000 Binary files a/app/assets/images/emoji/mute.png and /dev/null differ diff --git a/app/assets/images/emoji/nail_care.png b/app/assets/images/emoji/nail_care.png deleted file mode 100644 index 6a66e63d2..000000000 Binary files a/app/assets/images/emoji/nail_care.png and /dev/null differ diff --git a/app/assets/images/emoji/name_badge.png b/app/assets/images/emoji/name_badge.png deleted file mode 100644 index 2b712dcd5..000000000 Binary files a/app/assets/images/emoji/name_badge.png and /dev/null differ diff --git a/app/assets/images/emoji/necktie.png b/app/assets/images/emoji/necktie.png deleted file mode 100644 index 80461c66f..000000000 Binary files a/app/assets/images/emoji/necktie.png and /dev/null differ diff --git a/app/assets/images/emoji/negative_squared_cross_mark.png b/app/assets/images/emoji/negative_squared_cross_mark.png deleted file mode 100644 index b47a0cece..000000000 Binary files a/app/assets/images/emoji/negative_squared_cross_mark.png and /dev/null differ diff --git a/app/assets/images/emoji/neutral_face.png b/app/assets/images/emoji/neutral_face.png deleted file mode 100644 index 682a1ba06..000000000 Binary files a/app/assets/images/emoji/neutral_face.png and /dev/null differ diff --git a/app/assets/images/emoji/new.png b/app/assets/images/emoji/new.png deleted file mode 100644 index 28d1570e0..000000000 Binary files a/app/assets/images/emoji/new.png and /dev/null differ diff --git a/app/assets/images/emoji/new_moon.png b/app/assets/images/emoji/new_moon.png deleted file mode 100644 index 540239b1f..000000000 Binary files a/app/assets/images/emoji/new_moon.png and /dev/null differ diff --git a/app/assets/images/emoji/new_moon_with_face.png b/app/assets/images/emoji/new_moon_with_face.png deleted file mode 100644 index b9aff7a06..000000000 Binary files a/app/assets/images/emoji/new_moon_with_face.png and /dev/null differ diff --git a/app/assets/images/emoji/newspaper.png b/app/assets/images/emoji/newspaper.png deleted file mode 100644 index d171394e6..000000000 Binary files a/app/assets/images/emoji/newspaper.png and /dev/null differ diff --git a/app/assets/images/emoji/ng.png b/app/assets/images/emoji/ng.png deleted file mode 100644 index 2ca180ae3..000000000 Binary files a/app/assets/images/emoji/ng.png and /dev/null differ diff --git a/app/assets/images/emoji/no_bell.png b/app/assets/images/emoji/no_bell.png deleted file mode 100644 index 613b81cd2..000000000 Binary files a/app/assets/images/emoji/no_bell.png and /dev/null differ diff --git a/app/assets/images/emoji/no_bicycles.png b/app/assets/images/emoji/no_bicycles.png deleted file mode 100644 index 4b2621664..000000000 Binary files a/app/assets/images/emoji/no_bicycles.png and /dev/null differ diff --git a/app/assets/images/emoji/no_entry.png b/app/assets/images/emoji/no_entry.png deleted file mode 100644 index cf2086a8e..000000000 Binary files a/app/assets/images/emoji/no_entry.png and /dev/null differ diff --git a/app/assets/images/emoji/no_entry_sign.png b/app/assets/images/emoji/no_entry_sign.png deleted file mode 100644 index a8444d18d..000000000 Binary files a/app/assets/images/emoji/no_entry_sign.png and /dev/null differ diff --git a/app/assets/images/emoji/no_good.png b/app/assets/images/emoji/no_good.png deleted file mode 100644 index d459a35bc..000000000 Binary files a/app/assets/images/emoji/no_good.png and /dev/null differ diff --git a/app/assets/images/emoji/no_mobile_phones.png b/app/assets/images/emoji/no_mobile_phones.png deleted file mode 100644 index 41df57cf8..000000000 Binary files a/app/assets/images/emoji/no_mobile_phones.png and /dev/null differ diff --git a/app/assets/images/emoji/no_mouth.png b/app/assets/images/emoji/no_mouth.png deleted file mode 100644 index d9ec7ca7d..000000000 Binary files a/app/assets/images/emoji/no_mouth.png and /dev/null differ diff --git a/app/assets/images/emoji/no_pedestrians.png b/app/assets/images/emoji/no_pedestrians.png deleted file mode 100644 index c35f530b2..000000000 Binary files a/app/assets/images/emoji/no_pedestrians.png and /dev/null differ diff --git a/app/assets/images/emoji/no_smoking.png b/app/assets/images/emoji/no_smoking.png deleted file mode 100644 index eb11d7911..000000000 Binary files a/app/assets/images/emoji/no_smoking.png and /dev/null differ diff --git a/app/assets/images/emoji/non-potable_water.png b/app/assets/images/emoji/non-potable_water.png deleted file mode 100644 index 1b29d35b9..000000000 Binary files a/app/assets/images/emoji/non-potable_water.png and /dev/null differ diff --git a/app/assets/images/emoji/nose.png b/app/assets/images/emoji/nose.png deleted file mode 100644 index ad17c16c2..000000000 Binary files a/app/assets/images/emoji/nose.png and /dev/null differ diff --git a/app/assets/images/emoji/notebook.png b/app/assets/images/emoji/notebook.png deleted file mode 100644 index 07ea6087e..000000000 Binary files a/app/assets/images/emoji/notebook.png and /dev/null differ diff --git a/app/assets/images/emoji/notebook_with_decorative_cover.png b/app/assets/images/emoji/notebook_with_decorative_cover.png deleted file mode 100644 index 4f3b14c85..000000000 Binary files a/app/assets/images/emoji/notebook_with_decorative_cover.png and /dev/null differ diff --git a/app/assets/images/emoji/notes.png b/app/assets/images/emoji/notes.png deleted file mode 100644 index a13147fae..000000000 Binary files a/app/assets/images/emoji/notes.png and /dev/null differ diff --git a/app/assets/images/emoji/nut_and_bolt.png b/app/assets/images/emoji/nut_and_bolt.png deleted file mode 100644 index bddfa72a7..000000000 Binary files a/app/assets/images/emoji/nut_and_bolt.png and /dev/null differ diff --git a/app/assets/images/emoji/o.png b/app/assets/images/emoji/o.png deleted file mode 100644 index 0ededebe3..000000000 Binary files a/app/assets/images/emoji/o.png and /dev/null differ diff --git a/app/assets/images/emoji/o2.png b/app/assets/images/emoji/o2.png deleted file mode 100644 index d85f9fb98..000000000 Binary files a/app/assets/images/emoji/o2.png and /dev/null differ diff --git a/app/assets/images/emoji/ocean.png b/app/assets/images/emoji/ocean.png deleted file mode 100644 index f8d520cd4..000000000 Binary files a/app/assets/images/emoji/ocean.png and /dev/null differ diff --git a/app/assets/images/emoji/octopus.png b/app/assets/images/emoji/octopus.png deleted file mode 100644 index 52ce64b46..000000000 Binary files a/app/assets/images/emoji/octopus.png and /dev/null differ diff --git a/app/assets/images/emoji/oden.png b/app/assets/images/emoji/oden.png deleted file mode 100644 index 73add1c73..000000000 Binary files a/app/assets/images/emoji/oden.png and /dev/null differ diff --git a/app/assets/images/emoji/office.png b/app/assets/images/emoji/office.png deleted file mode 100644 index 3f20b5642..000000000 Binary files a/app/assets/images/emoji/office.png and /dev/null differ diff --git a/app/assets/images/emoji/ok.png b/app/assets/images/emoji/ok.png deleted file mode 100644 index 6433d1a90..000000000 Binary files a/app/assets/images/emoji/ok.png and /dev/null differ diff --git a/app/assets/images/emoji/ok_hand.png b/app/assets/images/emoji/ok_hand.png deleted file mode 100644 index 3177439dc..000000000 Binary files a/app/assets/images/emoji/ok_hand.png and /dev/null differ diff --git a/app/assets/images/emoji/ok_woman.png b/app/assets/images/emoji/ok_woman.png deleted file mode 100644 index e8b98194e..000000000 Binary files a/app/assets/images/emoji/ok_woman.png and /dev/null differ diff --git a/app/assets/images/emoji/older_man.png b/app/assets/images/emoji/older_man.png deleted file mode 100644 index 149f0cfb8..000000000 Binary files a/app/assets/images/emoji/older_man.png and /dev/null differ diff --git a/app/assets/images/emoji/older_woman.png b/app/assets/images/emoji/older_woman.png deleted file mode 100644 index f839565f4..000000000 Binary files a/app/assets/images/emoji/older_woman.png and /dev/null differ diff --git a/app/assets/images/emoji/on.png b/app/assets/images/emoji/on.png deleted file mode 100644 index 3595387fb..000000000 Binary files a/app/assets/images/emoji/on.png and /dev/null differ diff --git a/app/assets/images/emoji/oncoming_automobile.png b/app/assets/images/emoji/oncoming_automobile.png deleted file mode 100644 index cb46de22c..000000000 Binary files a/app/assets/images/emoji/oncoming_automobile.png and /dev/null differ diff --git a/app/assets/images/emoji/oncoming_bus.png b/app/assets/images/emoji/oncoming_bus.png deleted file mode 100644 index 3695f7623..000000000 Binary files a/app/assets/images/emoji/oncoming_bus.png and /dev/null differ diff --git a/app/assets/images/emoji/oncoming_police_car.png b/app/assets/images/emoji/oncoming_police_car.png deleted file mode 100644 index af20e7eff..000000000 Binary files a/app/assets/images/emoji/oncoming_police_car.png and /dev/null differ diff --git a/app/assets/images/emoji/oncoming_taxi.png b/app/assets/images/emoji/oncoming_taxi.png deleted file mode 100644 index f78cf3103..000000000 Binary files a/app/assets/images/emoji/oncoming_taxi.png and /dev/null differ diff --git a/app/assets/images/emoji/open_book.png b/app/assets/images/emoji/open_book.png deleted file mode 100644 index 8b698415c..000000000 Binary files a/app/assets/images/emoji/open_book.png and /dev/null differ diff --git a/app/assets/images/emoji/open_file_folder.png b/app/assets/images/emoji/open_file_folder.png deleted file mode 100644 index 2bbbbf5e7..000000000 Binary files a/app/assets/images/emoji/open_file_folder.png and /dev/null differ diff --git a/app/assets/images/emoji/open_hands.png b/app/assets/images/emoji/open_hands.png deleted file mode 100644 index 2cc25bd41..000000000 Binary files a/app/assets/images/emoji/open_hands.png and /dev/null differ diff --git a/app/assets/images/emoji/open_mouth.png b/app/assets/images/emoji/open_mouth.png deleted file mode 100644 index e5283582c..000000000 Binary files a/app/assets/images/emoji/open_mouth.png and /dev/null differ diff --git a/app/assets/images/emoji/ophiuchus.png b/app/assets/images/emoji/ophiuchus.png deleted file mode 100644 index 4eef715bc..000000000 Binary files a/app/assets/images/emoji/ophiuchus.png and /dev/null differ diff --git a/app/assets/images/emoji/orange_book.png b/app/assets/images/emoji/orange_book.png deleted file mode 100644 index 49650d59e..000000000 Binary files a/app/assets/images/emoji/orange_book.png and /dev/null differ diff --git a/app/assets/images/emoji/outbox_tray.png b/app/assets/images/emoji/outbox_tray.png deleted file mode 100644 index 7ad15e649..000000000 Binary files a/app/assets/images/emoji/outbox_tray.png and /dev/null differ diff --git a/app/assets/images/emoji/ox.png b/app/assets/images/emoji/ox.png deleted file mode 100644 index f76698024..000000000 Binary files a/app/assets/images/emoji/ox.png and /dev/null differ diff --git a/app/assets/images/emoji/package.png b/app/assets/images/emoji/package.png deleted file mode 100644 index 26602af9d..000000000 Binary files a/app/assets/images/emoji/package.png and /dev/null differ diff --git a/app/assets/images/emoji/page_facing_up.png b/app/assets/images/emoji/page_facing_up.png deleted file mode 100644 index 64cd2e1b2..000000000 Binary files a/app/assets/images/emoji/page_facing_up.png and /dev/null differ diff --git a/app/assets/images/emoji/page_with_curl.png b/app/assets/images/emoji/page_with_curl.png deleted file mode 100644 index bf8f979d3..000000000 Binary files a/app/assets/images/emoji/page_with_curl.png and /dev/null differ diff --git a/app/assets/images/emoji/pager.png b/app/assets/images/emoji/pager.png deleted file mode 100644 index e3e1fc44e..000000000 Binary files a/app/assets/images/emoji/pager.png and /dev/null differ diff --git a/app/assets/images/emoji/palm_tree.png b/app/assets/images/emoji/palm_tree.png deleted file mode 100644 index d534785ef..000000000 Binary files a/app/assets/images/emoji/palm_tree.png and /dev/null differ diff --git a/app/assets/images/emoji/panda_face.png b/app/assets/images/emoji/panda_face.png deleted file mode 100644 index a794fb17f..000000000 Binary files a/app/assets/images/emoji/panda_face.png and /dev/null differ diff --git a/app/assets/images/emoji/paperclip.png b/app/assets/images/emoji/paperclip.png deleted file mode 100644 index 774412dc1..000000000 Binary files a/app/assets/images/emoji/paperclip.png and /dev/null differ diff --git a/app/assets/images/emoji/parking.png b/app/assets/images/emoji/parking.png deleted file mode 100644 index c24af81cc..000000000 Binary files a/app/assets/images/emoji/parking.png and /dev/null differ diff --git a/app/assets/images/emoji/part_alternation_mark.png b/app/assets/images/emoji/part_alternation_mark.png deleted file mode 100644 index 45dc9b851..000000000 Binary files a/app/assets/images/emoji/part_alternation_mark.png and /dev/null differ diff --git a/app/assets/images/emoji/partly_sunny.png b/app/assets/images/emoji/partly_sunny.png deleted file mode 100644 index 020dd5ff6..000000000 Binary files a/app/assets/images/emoji/partly_sunny.png and /dev/null differ diff --git a/app/assets/images/emoji/passport_control.png b/app/assets/images/emoji/passport_control.png deleted file mode 100644 index 675b76d37..000000000 Binary files a/app/assets/images/emoji/passport_control.png and /dev/null differ diff --git a/app/assets/images/emoji/paw_prints.png b/app/assets/images/emoji/paw_prints.png deleted file mode 100644 index 89b9fec9e..000000000 Binary files a/app/assets/images/emoji/paw_prints.png and /dev/null differ diff --git a/app/assets/images/emoji/peach.png b/app/assets/images/emoji/peach.png deleted file mode 100644 index ee2139ecb..000000000 Binary files a/app/assets/images/emoji/peach.png and /dev/null differ diff --git a/app/assets/images/emoji/pear.png b/app/assets/images/emoji/pear.png deleted file mode 100644 index f24aca8c0..000000000 Binary files a/app/assets/images/emoji/pear.png and /dev/null differ diff --git a/app/assets/images/emoji/pencil.png b/app/assets/images/emoji/pencil.png deleted file mode 100644 index fc97ddbc9..000000000 Binary files a/app/assets/images/emoji/pencil.png and /dev/null differ diff --git a/app/assets/images/emoji/pencil2.png b/app/assets/images/emoji/pencil2.png deleted file mode 100644 index e624373b4..000000000 Binary files a/app/assets/images/emoji/pencil2.png and /dev/null differ diff --git a/app/assets/images/emoji/penguin.png b/app/assets/images/emoji/penguin.png deleted file mode 100644 index d8edbcb8f..000000000 Binary files a/app/assets/images/emoji/penguin.png and /dev/null differ diff --git a/app/assets/images/emoji/pensive.png b/app/assets/images/emoji/pensive.png deleted file mode 100644 index 2f3bad945..000000000 Binary files a/app/assets/images/emoji/pensive.png and /dev/null differ diff --git a/app/assets/images/emoji/performing_arts.png b/app/assets/images/emoji/performing_arts.png deleted file mode 100644 index 899fbe5a7..000000000 Binary files a/app/assets/images/emoji/performing_arts.png and /dev/null differ diff --git a/app/assets/images/emoji/persevere.png b/app/assets/images/emoji/persevere.png deleted file mode 100644 index c7e433e8e..000000000 Binary files a/app/assets/images/emoji/persevere.png and /dev/null differ diff --git a/app/assets/images/emoji/person_frowning.png b/app/assets/images/emoji/person_frowning.png deleted file mode 100644 index 6f34d5e15..000000000 Binary files a/app/assets/images/emoji/person_frowning.png and /dev/null differ diff --git a/app/assets/images/emoji/person_with_blond_hair.png b/app/assets/images/emoji/person_with_blond_hair.png deleted file mode 100644 index c144301cb..000000000 Binary files a/app/assets/images/emoji/person_with_blond_hair.png and /dev/null differ diff --git a/app/assets/images/emoji/person_with_pouting_face.png b/app/assets/images/emoji/person_with_pouting_face.png deleted file mode 100644 index c4a95c3b2..000000000 Binary files a/app/assets/images/emoji/person_with_pouting_face.png and /dev/null differ diff --git a/app/assets/images/emoji/phone.png b/app/assets/images/emoji/phone.png deleted file mode 100644 index 87d2559b5..000000000 Binary files a/app/assets/images/emoji/phone.png and /dev/null differ diff --git a/app/assets/images/emoji/pig.png b/app/assets/images/emoji/pig.png deleted file mode 100644 index f7f273c73..000000000 Binary files a/app/assets/images/emoji/pig.png and /dev/null differ diff --git a/app/assets/images/emoji/pig2.png b/app/assets/images/emoji/pig2.png deleted file mode 100644 index fec3374d7..000000000 Binary files a/app/assets/images/emoji/pig2.png and /dev/null differ diff --git a/app/assets/images/emoji/pig_nose.png b/app/assets/images/emoji/pig_nose.png deleted file mode 100644 index 38d612446..000000000 Binary files a/app/assets/images/emoji/pig_nose.png and /dev/null differ diff --git a/app/assets/images/emoji/pill.png b/app/assets/images/emoji/pill.png deleted file mode 100644 index cd84a78ff..000000000 Binary files a/app/assets/images/emoji/pill.png and /dev/null differ diff --git a/app/assets/images/emoji/pineapple.png b/app/assets/images/emoji/pineapple.png deleted file mode 100644 index d6f8e2876..000000000 Binary files a/app/assets/images/emoji/pineapple.png and /dev/null differ diff --git a/app/assets/images/emoji/pisces.png b/app/assets/images/emoji/pisces.png deleted file mode 100644 index 5a2da0a05..000000000 Binary files a/app/assets/images/emoji/pisces.png and /dev/null differ diff --git a/app/assets/images/emoji/pizza.png b/app/assets/images/emoji/pizza.png deleted file mode 100644 index 460367d02..000000000 Binary files a/app/assets/images/emoji/pizza.png and /dev/null differ diff --git a/app/assets/images/emoji/point_down.png b/app/assets/images/emoji/point_down.png deleted file mode 100644 index 658c6d918..000000000 Binary files a/app/assets/images/emoji/point_down.png and /dev/null differ diff --git a/app/assets/images/emoji/point_left.png b/app/assets/images/emoji/point_left.png deleted file mode 100644 index fee9cac4d..000000000 Binary files a/app/assets/images/emoji/point_left.png and /dev/null differ diff --git a/app/assets/images/emoji/point_right.png b/app/assets/images/emoji/point_right.png deleted file mode 100644 index b04e2849d..000000000 Binary files a/app/assets/images/emoji/point_right.png and /dev/null differ diff --git a/app/assets/images/emoji/point_up.png b/app/assets/images/emoji/point_up.png deleted file mode 100644 index 01896e214..000000000 Binary files a/app/assets/images/emoji/point_up.png and /dev/null differ diff --git a/app/assets/images/emoji/point_up_2.png b/app/assets/images/emoji/point_up_2.png deleted file mode 100644 index 196d109a8..000000000 Binary files a/app/assets/images/emoji/point_up_2.png and /dev/null differ diff --git a/app/assets/images/emoji/police_car.png b/app/assets/images/emoji/police_car.png deleted file mode 100644 index b8f17275e..000000000 Binary files a/app/assets/images/emoji/police_car.png and /dev/null differ diff --git a/app/assets/images/emoji/poodle.png b/app/assets/images/emoji/poodle.png deleted file mode 100644 index adac80bd9..000000000 Binary files a/app/assets/images/emoji/poodle.png and /dev/null differ diff --git a/app/assets/images/emoji/poop.png b/app/assets/images/emoji/poop.png deleted file mode 100644 index 73a4dc840..000000000 Binary files a/app/assets/images/emoji/poop.png and /dev/null differ diff --git a/app/assets/images/emoji/post_office.png b/app/assets/images/emoji/post_office.png deleted file mode 100644 index 43b59e30e..000000000 Binary files a/app/assets/images/emoji/post_office.png and /dev/null differ diff --git a/app/assets/images/emoji/postal_horn.png b/app/assets/images/emoji/postal_horn.png deleted file mode 100644 index e9b713bbe..000000000 Binary files a/app/assets/images/emoji/postal_horn.png and /dev/null differ diff --git a/app/assets/images/emoji/postbox.png b/app/assets/images/emoji/postbox.png deleted file mode 100644 index ce04b7008..000000000 Binary files a/app/assets/images/emoji/postbox.png and /dev/null differ diff --git a/app/assets/images/emoji/potable_water.png b/app/assets/images/emoji/potable_water.png deleted file mode 100644 index e9fd56079..000000000 Binary files a/app/assets/images/emoji/potable_water.png and /dev/null differ diff --git a/app/assets/images/emoji/pouch.png b/app/assets/images/emoji/pouch.png deleted file mode 100644 index 0bc5879fc..000000000 Binary files a/app/assets/images/emoji/pouch.png and /dev/null differ diff --git a/app/assets/images/emoji/poultry_leg.png b/app/assets/images/emoji/poultry_leg.png deleted file mode 100644 index 43ad85965..000000000 Binary files a/app/assets/images/emoji/poultry_leg.png and /dev/null differ diff --git a/app/assets/images/emoji/pound.png b/app/assets/images/emoji/pound.png deleted file mode 100644 index f8be91d7a..000000000 Binary files a/app/assets/images/emoji/pound.png and /dev/null differ diff --git a/app/assets/images/emoji/pouting_cat.png b/app/assets/images/emoji/pouting_cat.png deleted file mode 100644 index 4325fd48d..000000000 Binary files a/app/assets/images/emoji/pouting_cat.png and /dev/null differ diff --git a/app/assets/images/emoji/pray.png b/app/assets/images/emoji/pray.png deleted file mode 100644 index f86c992d5..000000000 Binary files a/app/assets/images/emoji/pray.png and /dev/null differ diff --git a/app/assets/images/emoji/princess.png b/app/assets/images/emoji/princess.png deleted file mode 100644 index 1ebb2ce9b..000000000 Binary files a/app/assets/images/emoji/princess.png and /dev/null differ diff --git a/app/assets/images/emoji/punch.png b/app/assets/images/emoji/punch.png deleted file mode 100644 index 2d41fd37e..000000000 Binary files a/app/assets/images/emoji/punch.png and /dev/null differ diff --git a/app/assets/images/emoji/purple_heart.png b/app/assets/images/emoji/purple_heart.png deleted file mode 100644 index d5f875043..000000000 Binary files a/app/assets/images/emoji/purple_heart.png and /dev/null differ diff --git a/app/assets/images/emoji/purse.png b/app/assets/images/emoji/purse.png deleted file mode 100644 index 8f06a2b93..000000000 Binary files a/app/assets/images/emoji/purse.png and /dev/null differ diff --git a/app/assets/images/emoji/pushpin.png b/app/assets/images/emoji/pushpin.png deleted file mode 100644 index 540c4ecb8..000000000 Binary files a/app/assets/images/emoji/pushpin.png and /dev/null 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 deleted file mode 100644 index c2e350c2d..000000000 Binary files a/app/assets/images/emoji/put_litter_in_its_place.png and /dev/null differ diff --git a/app/assets/images/emoji/question.png b/app/assets/images/emoji/question.png deleted file mode 100644 index 63fd7f837..000000000 Binary files a/app/assets/images/emoji/question.png and /dev/null differ diff --git a/app/assets/images/emoji/rabbit.png b/app/assets/images/emoji/rabbit.png deleted file mode 100644 index 5cb3ef6f0..000000000 Binary files a/app/assets/images/emoji/rabbit.png and /dev/null differ diff --git a/app/assets/images/emoji/rabbit2.png b/app/assets/images/emoji/rabbit2.png deleted file mode 100644 index 5bc993e79..000000000 Binary files a/app/assets/images/emoji/rabbit2.png and /dev/null differ diff --git a/app/assets/images/emoji/racehorse.png b/app/assets/images/emoji/racehorse.png deleted file mode 100644 index 4d09c64de..000000000 Binary files a/app/assets/images/emoji/racehorse.png and /dev/null differ diff --git a/app/assets/images/emoji/radio.png b/app/assets/images/emoji/radio.png deleted file mode 100644 index ea589efe3..000000000 Binary files a/app/assets/images/emoji/radio.png and /dev/null differ diff --git a/app/assets/images/emoji/radio_button.png b/app/assets/images/emoji/radio_button.png deleted file mode 100644 index 63755eec2..000000000 Binary files a/app/assets/images/emoji/radio_button.png and /dev/null differ diff --git a/app/assets/images/emoji/rage.png b/app/assets/images/emoji/rage.png deleted file mode 100644 index c65ddff55..000000000 Binary files a/app/assets/images/emoji/rage.png and /dev/null differ diff --git a/app/assets/images/emoji/railway_car.png b/app/assets/images/emoji/railway_car.png deleted file mode 100644 index 22361158f..000000000 Binary files a/app/assets/images/emoji/railway_car.png and /dev/null differ diff --git a/app/assets/images/emoji/rainbow.png b/app/assets/images/emoji/rainbow.png deleted file mode 100644 index 6b1faa037..000000000 Binary files a/app/assets/images/emoji/rainbow.png and /dev/null differ diff --git a/app/assets/images/emoji/raised_hand.png b/app/assets/images/emoji/raised_hand.png deleted file mode 100644 index 5e45c25a5..000000000 Binary files a/app/assets/images/emoji/raised_hand.png and /dev/null differ diff --git a/app/assets/images/emoji/raised_hands.png b/app/assets/images/emoji/raised_hands.png deleted file mode 100644 index e03142bdc..000000000 Binary files a/app/assets/images/emoji/raised_hands.png and /dev/null differ diff --git a/app/assets/images/emoji/raising_hand.png b/app/assets/images/emoji/raising_hand.png deleted file mode 100644 index e1741a40e..000000000 Binary files a/app/assets/images/emoji/raising_hand.png and /dev/null differ diff --git a/app/assets/images/emoji/ram.png b/app/assets/images/emoji/ram.png deleted file mode 100644 index 5ea7bfbc0..000000000 Binary files a/app/assets/images/emoji/ram.png and /dev/null differ diff --git a/app/assets/images/emoji/ramen.png b/app/assets/images/emoji/ramen.png deleted file mode 100644 index 78dc7d537..000000000 Binary files a/app/assets/images/emoji/ramen.png and /dev/null differ diff --git a/app/assets/images/emoji/rat.png b/app/assets/images/emoji/rat.png deleted file mode 100644 index 1c463dfde..000000000 Binary files a/app/assets/images/emoji/rat.png and /dev/null differ diff --git a/app/assets/images/emoji/recycle.png b/app/assets/images/emoji/recycle.png deleted file mode 100644 index 99104c0e9..000000000 Binary files a/app/assets/images/emoji/recycle.png and /dev/null differ diff --git a/app/assets/images/emoji/red_car.png b/app/assets/images/emoji/red_car.png deleted file mode 100644 index d70a2f062..000000000 Binary files a/app/assets/images/emoji/red_car.png and /dev/null differ diff --git a/app/assets/images/emoji/red_circle.png b/app/assets/images/emoji/red_circle.png deleted file mode 100644 index b391289b2..000000000 Binary files a/app/assets/images/emoji/red_circle.png and /dev/null differ diff --git a/app/assets/images/emoji/registered.png b/app/assets/images/emoji/registered.png deleted file mode 100644 index e5394109a..000000000 Binary files a/app/assets/images/emoji/registered.png and /dev/null differ diff --git a/app/assets/images/emoji/relaxed.png b/app/assets/images/emoji/relaxed.png deleted file mode 100644 index bbab82d3b..000000000 Binary files a/app/assets/images/emoji/relaxed.png and /dev/null differ diff --git a/app/assets/images/emoji/relieved.png b/app/assets/images/emoji/relieved.png deleted file mode 100644 index 820cf315a..000000000 Binary files a/app/assets/images/emoji/relieved.png and /dev/null differ diff --git a/app/assets/images/emoji/repeat.png b/app/assets/images/emoji/repeat.png deleted file mode 100644 index 80113b692..000000000 Binary files a/app/assets/images/emoji/repeat.png and /dev/null differ diff --git a/app/assets/images/emoji/repeat_one.png b/app/assets/images/emoji/repeat_one.png deleted file mode 100644 index 3c47bcc1f..000000000 Binary files a/app/assets/images/emoji/repeat_one.png and /dev/null differ diff --git a/app/assets/images/emoji/restroom.png b/app/assets/images/emoji/restroom.png deleted file mode 100644 index 312ca3dc2..000000000 Binary files a/app/assets/images/emoji/restroom.png and /dev/null differ diff --git a/app/assets/images/emoji/revolving_hearts.png b/app/assets/images/emoji/revolving_hearts.png deleted file mode 100644 index ea3317c47..000000000 Binary files a/app/assets/images/emoji/revolving_hearts.png and /dev/null differ diff --git a/app/assets/images/emoji/rewind.png b/app/assets/images/emoji/rewind.png deleted file mode 100644 index 13ba866ad..000000000 Binary files a/app/assets/images/emoji/rewind.png and /dev/null differ diff --git a/app/assets/images/emoji/ribbon.png b/app/assets/images/emoji/ribbon.png deleted file mode 100644 index 63ee5ba5a..000000000 Binary files a/app/assets/images/emoji/ribbon.png and /dev/null differ diff --git a/app/assets/images/emoji/rice.png b/app/assets/images/emoji/rice.png deleted file mode 100644 index f4773edec..000000000 Binary files a/app/assets/images/emoji/rice.png and /dev/null differ diff --git a/app/assets/images/emoji/rice_ball.png b/app/assets/images/emoji/rice_ball.png deleted file mode 100644 index 04f8a8806..000000000 Binary files a/app/assets/images/emoji/rice_ball.png and /dev/null differ diff --git a/app/assets/images/emoji/rice_cracker.png b/app/assets/images/emoji/rice_cracker.png deleted file mode 100644 index 954c901e9..000000000 Binary files a/app/assets/images/emoji/rice_cracker.png and /dev/null differ diff --git a/app/assets/images/emoji/rice_scene.png b/app/assets/images/emoji/rice_scene.png deleted file mode 100644 index 14361988d..000000000 Binary files a/app/assets/images/emoji/rice_scene.png and /dev/null differ diff --git a/app/assets/images/emoji/ring.png b/app/assets/images/emoji/ring.png deleted file mode 100644 index 8a57fd68b..000000000 Binary files a/app/assets/images/emoji/ring.png and /dev/null differ diff --git a/app/assets/images/emoji/rocket.png b/app/assets/images/emoji/rocket.png deleted file mode 100644 index 783078d37..000000000 Binary files a/app/assets/images/emoji/rocket.png and /dev/null differ diff --git a/app/assets/images/emoji/roller_coaster.png b/app/assets/images/emoji/roller_coaster.png deleted file mode 100644 index 9180b9861..000000000 Binary files a/app/assets/images/emoji/roller_coaster.png and /dev/null differ diff --git a/app/assets/images/emoji/rooster.png b/app/assets/images/emoji/rooster.png deleted file mode 100644 index fab23ad36..000000000 Binary files a/app/assets/images/emoji/rooster.png and /dev/null differ diff --git a/app/assets/images/emoji/rose.png b/app/assets/images/emoji/rose.png deleted file mode 100644 index 3479fbcbb..000000000 Binary files a/app/assets/images/emoji/rose.png and /dev/null differ diff --git a/app/assets/images/emoji/rotating_light.png b/app/assets/images/emoji/rotating_light.png deleted file mode 100644 index 6cf4a775e..000000000 Binary files a/app/assets/images/emoji/rotating_light.png and /dev/null differ diff --git a/app/assets/images/emoji/round_pushpin.png b/app/assets/images/emoji/round_pushpin.png deleted file mode 100644 index e498e92cf..000000000 Binary files a/app/assets/images/emoji/round_pushpin.png and /dev/null differ diff --git a/app/assets/images/emoji/rowboat.png b/app/assets/images/emoji/rowboat.png deleted file mode 100644 index fe8ae3ecd..000000000 Binary files a/app/assets/images/emoji/rowboat.png and /dev/null differ diff --git a/app/assets/images/emoji/rugby_football.png b/app/assets/images/emoji/rugby_football.png deleted file mode 100644 index f8db67d70..000000000 Binary files a/app/assets/images/emoji/rugby_football.png and /dev/null differ diff --git a/app/assets/images/emoji/runner.png b/app/assets/images/emoji/runner.png deleted file mode 100644 index 1ecfd9059..000000000 Binary files a/app/assets/images/emoji/runner.png and /dev/null differ diff --git a/app/assets/images/emoji/running.png b/app/assets/images/emoji/running.png deleted file mode 100644 index 1ecfd9059..000000000 Binary files a/app/assets/images/emoji/running.png and /dev/null differ diff --git a/app/assets/images/emoji/running_shirt_with_sash.png b/app/assets/images/emoji/running_shirt_with_sash.png deleted file mode 100644 index 0d68bba09..000000000 Binary files a/app/assets/images/emoji/running_shirt_with_sash.png and /dev/null differ diff --git a/app/assets/images/emoji/sa.png b/app/assets/images/emoji/sa.png deleted file mode 100644 index 387f098b9..000000000 Binary files a/app/assets/images/emoji/sa.png and /dev/null differ diff --git a/app/assets/images/emoji/sagittarius.png b/app/assets/images/emoji/sagittarius.png deleted file mode 100644 index 8b5435baa..000000000 Binary files a/app/assets/images/emoji/sagittarius.png and /dev/null differ diff --git a/app/assets/images/emoji/sailboat.png b/app/assets/images/emoji/sailboat.png deleted file mode 100644 index ff656dc62..000000000 Binary files a/app/assets/images/emoji/sailboat.png and /dev/null differ diff --git a/app/assets/images/emoji/sake.png b/app/assets/images/emoji/sake.png deleted file mode 100644 index 1f69907e5..000000000 Binary files a/app/assets/images/emoji/sake.png and /dev/null differ diff --git a/app/assets/images/emoji/sandal.png b/app/assets/images/emoji/sandal.png deleted file mode 100644 index aa62cca5d..000000000 Binary files a/app/assets/images/emoji/sandal.png and /dev/null differ diff --git a/app/assets/images/emoji/santa.png b/app/assets/images/emoji/santa.png deleted file mode 100644 index a2240c07e..000000000 Binary files a/app/assets/images/emoji/santa.png and /dev/null differ diff --git a/app/assets/images/emoji/satellite.png b/app/assets/images/emoji/satellite.png deleted file mode 100644 index 3481cc2ef..000000000 Binary files a/app/assets/images/emoji/satellite.png and /dev/null differ diff --git a/app/assets/images/emoji/satisfied.png b/app/assets/images/emoji/satisfied.png deleted file mode 100644 index 11c91eb22..000000000 Binary files a/app/assets/images/emoji/satisfied.png and /dev/null differ diff --git a/app/assets/images/emoji/saxophone.png b/app/assets/images/emoji/saxophone.png deleted file mode 100644 index 011559a76..000000000 Binary files a/app/assets/images/emoji/saxophone.png and /dev/null differ diff --git a/app/assets/images/emoji/school.png b/app/assets/images/emoji/school.png deleted file mode 100644 index afd922bf1..000000000 Binary files a/app/assets/images/emoji/school.png and /dev/null differ diff --git a/app/assets/images/emoji/school_satchel.png b/app/assets/images/emoji/school_satchel.png deleted file mode 100644 index edfb19aec..000000000 Binary files a/app/assets/images/emoji/school_satchel.png and /dev/null differ diff --git a/app/assets/images/emoji/scissors.png b/app/assets/images/emoji/scissors.png deleted file mode 100644 index 020e05224..000000000 Binary files a/app/assets/images/emoji/scissors.png and /dev/null differ diff --git a/app/assets/images/emoji/scorpius.png b/app/assets/images/emoji/scorpius.png deleted file mode 100644 index 67fcea165..000000000 Binary files a/app/assets/images/emoji/scorpius.png and /dev/null differ diff --git a/app/assets/images/emoji/scream.png b/app/assets/images/emoji/scream.png deleted file mode 100644 index 76bfc6b8a..000000000 Binary files a/app/assets/images/emoji/scream.png and /dev/null differ diff --git a/app/assets/images/emoji/scream_cat.png b/app/assets/images/emoji/scream_cat.png deleted file mode 100644 index d94cd34ff..000000000 Binary files a/app/assets/images/emoji/scream_cat.png and /dev/null differ diff --git a/app/assets/images/emoji/scroll.png b/app/assets/images/emoji/scroll.png deleted file mode 100644 index c5a10e6b8..000000000 Binary files a/app/assets/images/emoji/scroll.png and /dev/null differ diff --git a/app/assets/images/emoji/seat.png b/app/assets/images/emoji/seat.png deleted file mode 100644 index d1cb864b4..000000000 Binary files a/app/assets/images/emoji/seat.png and /dev/null differ diff --git a/app/assets/images/emoji/secret.png b/app/assets/images/emoji/secret.png deleted file mode 100644 index 82e383a60..000000000 Binary files a/app/assets/images/emoji/secret.png and /dev/null differ diff --git a/app/assets/images/emoji/see_no_evil.png b/app/assets/images/emoji/see_no_evil.png deleted file mode 100644 index 0890a6222..000000000 Binary files a/app/assets/images/emoji/see_no_evil.png and /dev/null differ diff --git a/app/assets/images/emoji/seedling.png b/app/assets/images/emoji/seedling.png deleted file mode 100644 index f0eb5a6b9..000000000 Binary files a/app/assets/images/emoji/seedling.png and /dev/null differ diff --git a/app/assets/images/emoji/shaved_ice.png b/app/assets/images/emoji/shaved_ice.png deleted file mode 100644 index 0d0b382c2..000000000 Binary files a/app/assets/images/emoji/shaved_ice.png and /dev/null differ diff --git a/app/assets/images/emoji/sheep.png b/app/assets/images/emoji/sheep.png deleted file mode 100644 index c7277d289..000000000 Binary files a/app/assets/images/emoji/sheep.png and /dev/null differ diff --git a/app/assets/images/emoji/shell.png b/app/assets/images/emoji/shell.png deleted file mode 100644 index 3145b5649..000000000 Binary files a/app/assets/images/emoji/shell.png and /dev/null differ diff --git a/app/assets/images/emoji/ship.png b/app/assets/images/emoji/ship.png deleted file mode 100644 index 5d2d8b602..000000000 Binary files a/app/assets/images/emoji/ship.png and /dev/null differ diff --git a/app/assets/images/emoji/shirt.png b/app/assets/images/emoji/shirt.png deleted file mode 100644 index 297a6d63e..000000000 Binary files a/app/assets/images/emoji/shirt.png and /dev/null differ diff --git a/app/assets/images/emoji/shit.png b/app/assets/images/emoji/shit.png deleted file mode 100644 index 73a4dc840..000000000 Binary files a/app/assets/images/emoji/shit.png and /dev/null differ diff --git a/app/assets/images/emoji/shoe.png b/app/assets/images/emoji/shoe.png deleted file mode 100644 index ecba9ba7d..000000000 Binary files a/app/assets/images/emoji/shoe.png and /dev/null differ diff --git a/app/assets/images/emoji/shower.png b/app/assets/images/emoji/shower.png deleted file mode 100644 index 94f82aac0..000000000 Binary files a/app/assets/images/emoji/shower.png and /dev/null differ diff --git a/app/assets/images/emoji/signal_strength.png b/app/assets/images/emoji/signal_strength.png deleted file mode 100644 index a4bd23ebf..000000000 Binary files a/app/assets/images/emoji/signal_strength.png and /dev/null differ diff --git a/app/assets/images/emoji/six_pointed_star.png b/app/assets/images/emoji/six_pointed_star.png deleted file mode 100644 index 010f8f5f9..000000000 Binary files a/app/assets/images/emoji/six_pointed_star.png and /dev/null differ diff --git a/app/assets/images/emoji/ski.png b/app/assets/images/emoji/ski.png deleted file mode 100644 index c97de3ed9..000000000 Binary files a/app/assets/images/emoji/ski.png and /dev/null differ diff --git a/app/assets/images/emoji/skull.png b/app/assets/images/emoji/skull.png deleted file mode 100644 index bd4ee3829..000000000 Binary files a/app/assets/images/emoji/skull.png and /dev/null differ diff --git a/app/assets/images/emoji/sleeping.png b/app/assets/images/emoji/sleeping.png deleted file mode 100644 index a2f3bf757..000000000 Binary files a/app/assets/images/emoji/sleeping.png and /dev/null differ diff --git a/app/assets/images/emoji/sleepy.png b/app/assets/images/emoji/sleepy.png deleted file mode 100644 index df4f55efd..000000000 Binary files a/app/assets/images/emoji/sleepy.png and /dev/null differ diff --git a/app/assets/images/emoji/slot_machine.png b/app/assets/images/emoji/slot_machine.png deleted file mode 100644 index 26f114830..000000000 Binary files a/app/assets/images/emoji/slot_machine.png and /dev/null differ diff --git a/app/assets/images/emoji/small_blue_diamond.png b/app/assets/images/emoji/small_blue_diamond.png deleted file mode 100644 index 5a7b5d555..000000000 Binary files a/app/assets/images/emoji/small_blue_diamond.png and /dev/null differ diff --git a/app/assets/images/emoji/small_orange_diamond.png b/app/assets/images/emoji/small_orange_diamond.png deleted file mode 100644 index 04941d37b..000000000 Binary files a/app/assets/images/emoji/small_orange_diamond.png and /dev/null differ diff --git a/app/assets/images/emoji/small_red_triangle.png b/app/assets/images/emoji/small_red_triangle.png deleted file mode 100644 index 8c4428da8..000000000 Binary files a/app/assets/images/emoji/small_red_triangle.png and /dev/null differ diff --git a/app/assets/images/emoji/small_red_triangle_down.png b/app/assets/images/emoji/small_red_triangle_down.png deleted file mode 100644 index 94832f060..000000000 Binary files a/app/assets/images/emoji/small_red_triangle_down.png and /dev/null differ diff --git a/app/assets/images/emoji/smile.png b/app/assets/images/emoji/smile.png deleted file mode 100644 index 81a839689..000000000 Binary files a/app/assets/images/emoji/smile.png and /dev/null differ diff --git a/app/assets/images/emoji/smile_cat.png b/app/assets/images/emoji/smile_cat.png deleted file mode 100644 index ad333ba3b..000000000 Binary files a/app/assets/images/emoji/smile_cat.png and /dev/null differ diff --git a/app/assets/images/emoji/smiley.png b/app/assets/images/emoji/smiley.png deleted file mode 100644 index 77b581d68..000000000 Binary files a/app/assets/images/emoji/smiley.png and /dev/null differ diff --git a/app/assets/images/emoji/smiley_cat.png b/app/assets/images/emoji/smiley_cat.png deleted file mode 100644 index dbf1b0276..000000000 Binary files a/app/assets/images/emoji/smiley_cat.png and /dev/null differ diff --git a/app/assets/images/emoji/smiling_imp.png b/app/assets/images/emoji/smiling_imp.png deleted file mode 100644 index d90404930..000000000 Binary files a/app/assets/images/emoji/smiling_imp.png and /dev/null differ diff --git a/app/assets/images/emoji/smirk.png b/app/assets/images/emoji/smirk.png deleted file mode 100644 index bc6e5082c..000000000 Binary files a/app/assets/images/emoji/smirk.png and /dev/null differ diff --git a/app/assets/images/emoji/smirk_cat.png b/app/assets/images/emoji/smirk_cat.png deleted file mode 100644 index 351565e24..000000000 Binary files a/app/assets/images/emoji/smirk_cat.png and /dev/null differ diff --git a/app/assets/images/emoji/smoking.png b/app/assets/images/emoji/smoking.png deleted file mode 100644 index 4aad6cbd7..000000000 Binary files a/app/assets/images/emoji/smoking.png and /dev/null differ diff --git a/app/assets/images/emoji/snail.png b/app/assets/images/emoji/snail.png deleted file mode 100644 index e75e69a84..000000000 Binary files a/app/assets/images/emoji/snail.png and /dev/null differ diff --git a/app/assets/images/emoji/snake.png b/app/assets/images/emoji/snake.png deleted file mode 100644 index ef58933e2..000000000 Binary files a/app/assets/images/emoji/snake.png and /dev/null differ diff --git a/app/assets/images/emoji/snowboarder.png b/app/assets/images/emoji/snowboarder.png deleted file mode 100644 index aeda5c8d8..000000000 Binary files a/app/assets/images/emoji/snowboarder.png and /dev/null differ diff --git a/app/assets/images/emoji/snowflake.png b/app/assets/images/emoji/snowflake.png deleted file mode 100644 index 54b68ff4f..000000000 Binary files a/app/assets/images/emoji/snowflake.png and /dev/null differ diff --git a/app/assets/images/emoji/snowman.png b/app/assets/images/emoji/snowman.png deleted file mode 100644 index a97902e53..000000000 Binary files a/app/assets/images/emoji/snowman.png and /dev/null differ diff --git a/app/assets/images/emoji/sob.png b/app/assets/images/emoji/sob.png deleted file mode 100644 index 7d433183a..000000000 Binary files a/app/assets/images/emoji/sob.png and /dev/null differ diff --git a/app/assets/images/emoji/soccer.png b/app/assets/images/emoji/soccer.png deleted file mode 100644 index 1e118b5b1..000000000 Binary files a/app/assets/images/emoji/soccer.png and /dev/null differ diff --git a/app/assets/images/emoji/soon.png b/app/assets/images/emoji/soon.png deleted file mode 100644 index 9386615a3..000000000 Binary files a/app/assets/images/emoji/soon.png and /dev/null differ diff --git a/app/assets/images/emoji/sos.png b/app/assets/images/emoji/sos.png deleted file mode 100644 index e3e16ef73..000000000 Binary files a/app/assets/images/emoji/sos.png and /dev/null differ diff --git a/app/assets/images/emoji/sound.png b/app/assets/images/emoji/sound.png deleted file mode 100644 index 6aa4dbff4..000000000 Binary files a/app/assets/images/emoji/sound.png and /dev/null differ diff --git a/app/assets/images/emoji/space_invader.png b/app/assets/images/emoji/space_invader.png deleted file mode 100644 index 384049167..000000000 Binary files a/app/assets/images/emoji/space_invader.png and /dev/null differ diff --git a/app/assets/images/emoji/spades.png b/app/assets/images/emoji/spades.png deleted file mode 100644 index 133a1aba8..000000000 Binary files a/app/assets/images/emoji/spades.png and /dev/null differ diff --git a/app/assets/images/emoji/spaghetti.png b/app/assets/images/emoji/spaghetti.png deleted file mode 100644 index 08de243f5..000000000 Binary files a/app/assets/images/emoji/spaghetti.png and /dev/null differ diff --git a/app/assets/images/emoji/sparkle.png b/app/assets/images/emoji/sparkle.png deleted file mode 100644 index 23a68ceb2..000000000 Binary files a/app/assets/images/emoji/sparkle.png and /dev/null differ diff --git a/app/assets/images/emoji/sparkler.png b/app/assets/images/emoji/sparkler.png deleted file mode 100644 index 4aabd7e0e..000000000 Binary files a/app/assets/images/emoji/sparkler.png and /dev/null differ diff --git a/app/assets/images/emoji/sparkles.png b/app/assets/images/emoji/sparkles.png deleted file mode 100644 index 92138828d..000000000 Binary files a/app/assets/images/emoji/sparkles.png and /dev/null differ diff --git a/app/assets/images/emoji/sparkling_heart.png b/app/assets/images/emoji/sparkling_heart.png deleted file mode 100644 index 0826bbc06..000000000 Binary files a/app/assets/images/emoji/sparkling_heart.png and /dev/null differ diff --git a/app/assets/images/emoji/speak_no_evil.png b/app/assets/images/emoji/speak_no_evil.png deleted file mode 100644 index 87944c4de..000000000 Binary files a/app/assets/images/emoji/speak_no_evil.png and /dev/null differ diff --git a/app/assets/images/emoji/speech_balloon.png b/app/assets/images/emoji/speech_balloon.png deleted file mode 100644 index 2896c2788..000000000 Binary files a/app/assets/images/emoji/speech_balloon.png and /dev/null differ diff --git a/app/assets/images/emoji/speedboat.png b/app/assets/images/emoji/speedboat.png deleted file mode 100644 index da6689b3b..000000000 Binary files a/app/assets/images/emoji/speedboat.png and /dev/null differ diff --git a/app/assets/images/emoji/squirrel.png b/app/assets/images/emoji/squirrel.png deleted file mode 100644 index a58a47f62..000000000 Binary files a/app/assets/images/emoji/squirrel.png and /dev/null differ diff --git a/app/assets/images/emoji/star.png b/app/assets/images/emoji/star.png deleted file mode 100644 index 1bfddc862..000000000 Binary files a/app/assets/images/emoji/star.png and /dev/null differ diff --git a/app/assets/images/emoji/star2.png b/app/assets/images/emoji/star2.png deleted file mode 100644 index 8b40ff4c8..000000000 Binary files a/app/assets/images/emoji/star2.png and /dev/null differ diff --git a/app/assets/images/emoji/station.png b/app/assets/images/emoji/station.png deleted file mode 100644 index e77daa8a7..000000000 Binary files a/app/assets/images/emoji/station.png and /dev/null differ diff --git a/app/assets/images/emoji/statue_of_liberty.png b/app/assets/images/emoji/statue_of_liberty.png deleted file mode 100644 index 9ad902806..000000000 Binary files a/app/assets/images/emoji/statue_of_liberty.png and /dev/null differ diff --git a/app/assets/images/emoji/steam_locomotive.png b/app/assets/images/emoji/steam_locomotive.png deleted file mode 100644 index 549507766..000000000 Binary files a/app/assets/images/emoji/steam_locomotive.png and /dev/null differ diff --git a/app/assets/images/emoji/stew.png b/app/assets/images/emoji/stew.png deleted file mode 100644 index 6e80b4a9c..000000000 Binary files a/app/assets/images/emoji/stew.png and /dev/null differ diff --git a/app/assets/images/emoji/straight_ruler.png b/app/assets/images/emoji/straight_ruler.png deleted file mode 100644 index af8cb4bcf..000000000 Binary files a/app/assets/images/emoji/straight_ruler.png and /dev/null differ diff --git a/app/assets/images/emoji/strawberry.png b/app/assets/images/emoji/strawberry.png deleted file mode 100644 index 13eb827ab..000000000 Binary files a/app/assets/images/emoji/strawberry.png and /dev/null differ diff --git a/app/assets/images/emoji/stuck_out_tongue.png b/app/assets/images/emoji/stuck_out_tongue.png deleted file mode 100644 index 53c41433f..000000000 Binary files a/app/assets/images/emoji/stuck_out_tongue.png and /dev/null 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 deleted file mode 100644 index 333716ee1..000000000 Binary files a/app/assets/images/emoji/stuck_out_tongue_closed_eyes.png and /dev/null 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 deleted file mode 100644 index 6ae9d497d..000000000 Binary files a/app/assets/images/emoji/stuck_out_tongue_winking_eye.png and /dev/null differ diff --git a/app/assets/images/emoji/sun_with_face.png b/app/assets/images/emoji/sun_with_face.png deleted file mode 100644 index ee276636f..000000000 Binary files a/app/assets/images/emoji/sun_with_face.png and /dev/null differ diff --git a/app/assets/images/emoji/sunflower.png b/app/assets/images/emoji/sunflower.png deleted file mode 100644 index d9bad194a..000000000 Binary files a/app/assets/images/emoji/sunflower.png and /dev/null differ diff --git a/app/assets/images/emoji/sunglasses.png b/app/assets/images/emoji/sunglasses.png deleted file mode 100644 index 1c468a1c9..000000000 Binary files a/app/assets/images/emoji/sunglasses.png and /dev/null differ diff --git a/app/assets/images/emoji/sunny.png b/app/assets/images/emoji/sunny.png deleted file mode 100644 index d23c095e0..000000000 Binary files a/app/assets/images/emoji/sunny.png and /dev/null differ diff --git a/app/assets/images/emoji/sunrise.png b/app/assets/images/emoji/sunrise.png deleted file mode 100644 index ec58dcc94..000000000 Binary files a/app/assets/images/emoji/sunrise.png and /dev/null differ diff --git a/app/assets/images/emoji/sunrise_over_mountains.png b/app/assets/images/emoji/sunrise_over_mountains.png deleted file mode 100644 index ebc3db146..000000000 Binary files a/app/assets/images/emoji/sunrise_over_mountains.png and /dev/null differ diff --git a/app/assets/images/emoji/surfer.png b/app/assets/images/emoji/surfer.png deleted file mode 100644 index b067e8cb3..000000000 Binary files a/app/assets/images/emoji/surfer.png and /dev/null differ diff --git a/app/assets/images/emoji/sushi.png b/app/assets/images/emoji/sushi.png deleted file mode 100644 index 0d179bd97..000000000 Binary files a/app/assets/images/emoji/sushi.png and /dev/null differ diff --git a/app/assets/images/emoji/suspension_railway.png b/app/assets/images/emoji/suspension_railway.png deleted file mode 100644 index aaa45f61f..000000000 Binary files a/app/assets/images/emoji/suspension_railway.png and /dev/null differ diff --git a/app/assets/images/emoji/sweat.png b/app/assets/images/emoji/sweat.png deleted file mode 100644 index e894b7699..000000000 Binary files a/app/assets/images/emoji/sweat.png and /dev/null differ diff --git a/app/assets/images/emoji/sweat_drops.png b/app/assets/images/emoji/sweat_drops.png deleted file mode 100644 index a83b3e960..000000000 Binary files a/app/assets/images/emoji/sweat_drops.png and /dev/null differ diff --git a/app/assets/images/emoji/sweat_smile.png b/app/assets/images/emoji/sweat_smile.png deleted file mode 100644 index 3903f717f..000000000 Binary files a/app/assets/images/emoji/sweat_smile.png and /dev/null differ diff --git a/app/assets/images/emoji/sweet_potato.png b/app/assets/images/emoji/sweet_potato.png deleted file mode 100644 index 32117fa9c..000000000 Binary files a/app/assets/images/emoji/sweet_potato.png and /dev/null differ diff --git a/app/assets/images/emoji/swimmer.png b/app/assets/images/emoji/swimmer.png deleted file mode 100644 index d3878a065..000000000 Binary files a/app/assets/images/emoji/swimmer.png and /dev/null differ diff --git a/app/assets/images/emoji/symbols.png b/app/assets/images/emoji/symbols.png deleted file mode 100644 index 16bc1da92..000000000 Binary files a/app/assets/images/emoji/symbols.png and /dev/null differ diff --git a/app/assets/images/emoji/syringe.png b/app/assets/images/emoji/syringe.png deleted file mode 100644 index e7e7ab6e3..000000000 Binary files a/app/assets/images/emoji/syringe.png and /dev/null differ diff --git a/app/assets/images/emoji/tada.png b/app/assets/images/emoji/tada.png deleted file mode 100644 index 7411b5266..000000000 Binary files a/app/assets/images/emoji/tada.png and /dev/null differ diff --git a/app/assets/images/emoji/tanabata_tree.png b/app/assets/images/emoji/tanabata_tree.png deleted file mode 100644 index 473346410..000000000 Binary files a/app/assets/images/emoji/tanabata_tree.png and /dev/null differ diff --git a/app/assets/images/emoji/tangerine.png b/app/assets/images/emoji/tangerine.png deleted file mode 100644 index fc9d4f82a..000000000 Binary files a/app/assets/images/emoji/tangerine.png and /dev/null differ diff --git a/app/assets/images/emoji/taurus.png b/app/assets/images/emoji/taurus.png deleted file mode 100644 index 6af582f69..000000000 Binary files a/app/assets/images/emoji/taurus.png and /dev/null differ diff --git a/app/assets/images/emoji/taxi.png b/app/assets/images/emoji/taxi.png deleted file mode 100644 index 60a50d365..000000000 Binary files a/app/assets/images/emoji/taxi.png and /dev/null differ diff --git a/app/assets/images/emoji/tea.png b/app/assets/images/emoji/tea.png deleted file mode 100644 index 3ece0b708..000000000 Binary files a/app/assets/images/emoji/tea.png and /dev/null differ diff --git a/app/assets/images/emoji/telephone.png b/app/assets/images/emoji/telephone.png deleted file mode 100644 index 87d2559b5..000000000 Binary files a/app/assets/images/emoji/telephone.png and /dev/null differ diff --git a/app/assets/images/emoji/telephone_receiver.png b/app/assets/images/emoji/telephone_receiver.png deleted file mode 100644 index 36e21e012..000000000 Binary files a/app/assets/images/emoji/telephone_receiver.png and /dev/null differ diff --git a/app/assets/images/emoji/telescope.png b/app/assets/images/emoji/telescope.png deleted file mode 100644 index 51fd8a07f..000000000 Binary files a/app/assets/images/emoji/telescope.png and /dev/null differ diff --git a/app/assets/images/emoji/tennis.png b/app/assets/images/emoji/tennis.png deleted file mode 100644 index 278d904ee..000000000 Binary files a/app/assets/images/emoji/tennis.png and /dev/null differ diff --git a/app/assets/images/emoji/tent.png b/app/assets/images/emoji/tent.png deleted file mode 100644 index 5c0d20e48..000000000 Binary files a/app/assets/images/emoji/tent.png and /dev/null differ diff --git a/app/assets/images/emoji/thought_balloon.png b/app/assets/images/emoji/thought_balloon.png deleted file mode 100644 index 701bdf0f6..000000000 Binary files a/app/assets/images/emoji/thought_balloon.png and /dev/null differ diff --git a/app/assets/images/emoji/thumbsdown.png b/app/assets/images/emoji/thumbsdown.png deleted file mode 100644 index e44c04219..000000000 Binary files a/app/assets/images/emoji/thumbsdown.png and /dev/null differ diff --git a/app/assets/images/emoji/thumbsup.png b/app/assets/images/emoji/thumbsup.png deleted file mode 100644 index 3a43ecae2..000000000 Binary files a/app/assets/images/emoji/thumbsup.png and /dev/null differ diff --git a/app/assets/images/emoji/ticket.png b/app/assets/images/emoji/ticket.png deleted file mode 100644 index cdacf1a70..000000000 Binary files a/app/assets/images/emoji/ticket.png and /dev/null differ diff --git a/app/assets/images/emoji/tiger.png b/app/assets/images/emoji/tiger.png deleted file mode 100644 index d6cc84a3b..000000000 Binary files a/app/assets/images/emoji/tiger.png and /dev/null differ diff --git a/app/assets/images/emoji/tiger2.png b/app/assets/images/emoji/tiger2.png deleted file mode 100644 index b0c7d8dc3..000000000 Binary files a/app/assets/images/emoji/tiger2.png and /dev/null differ diff --git a/app/assets/images/emoji/tired_face.png b/app/assets/images/emoji/tired_face.png deleted file mode 100644 index 3a8eefe56..000000000 Binary files a/app/assets/images/emoji/tired_face.png and /dev/null differ diff --git a/app/assets/images/emoji/tm.png b/app/assets/images/emoji/tm.png deleted file mode 100644 index 9ba71b75b..000000000 Binary files a/app/assets/images/emoji/tm.png and /dev/null differ diff --git a/app/assets/images/emoji/toilet.png b/app/assets/images/emoji/toilet.png deleted file mode 100644 index e5cc4119a..000000000 Binary files a/app/assets/images/emoji/toilet.png and /dev/null differ diff --git a/app/assets/images/emoji/tokyo_tower.png b/app/assets/images/emoji/tokyo_tower.png deleted file mode 100644 index e1cbd7a3c..000000000 Binary files a/app/assets/images/emoji/tokyo_tower.png and /dev/null differ diff --git a/app/assets/images/emoji/tomato.png b/app/assets/images/emoji/tomato.png deleted file mode 100644 index a129700bb..000000000 Binary files a/app/assets/images/emoji/tomato.png and /dev/null differ diff --git a/app/assets/images/emoji/tongue.png b/app/assets/images/emoji/tongue.png deleted file mode 100644 index b0bab1207..000000000 Binary files a/app/assets/images/emoji/tongue.png and /dev/null differ diff --git a/app/assets/images/emoji/top.png b/app/assets/images/emoji/top.png deleted file mode 100644 index 5aa4dd442..000000000 Binary files a/app/assets/images/emoji/top.png and /dev/null differ diff --git a/app/assets/images/emoji/tophat.png b/app/assets/images/emoji/tophat.png deleted file mode 100644 index 7d27134d6..000000000 Binary files a/app/assets/images/emoji/tophat.png and /dev/null differ diff --git a/app/assets/images/emoji/tractor.png b/app/assets/images/emoji/tractor.png deleted file mode 100644 index 058fd3eda..000000000 Binary files a/app/assets/images/emoji/tractor.png and /dev/null differ diff --git a/app/assets/images/emoji/traffic_light.png b/app/assets/images/emoji/traffic_light.png deleted file mode 100644 index 42eaf7091..000000000 Binary files a/app/assets/images/emoji/traffic_light.png and /dev/null differ diff --git a/app/assets/images/emoji/train.png b/app/assets/images/emoji/train.png deleted file mode 100644 index 22361158f..000000000 Binary files a/app/assets/images/emoji/train.png and /dev/null differ diff --git a/app/assets/images/emoji/train2.png b/app/assets/images/emoji/train2.png deleted file mode 100644 index 9c0d3ab64..000000000 Binary files a/app/assets/images/emoji/train2.png and /dev/null differ diff --git a/app/assets/images/emoji/tram.png b/app/assets/images/emoji/tram.png deleted file mode 100644 index 5eb29fb71..000000000 Binary files a/app/assets/images/emoji/tram.png and /dev/null differ diff --git a/app/assets/images/emoji/triangular_flag_on_post.png b/app/assets/images/emoji/triangular_flag_on_post.png deleted file mode 100644 index f9a3f32d7..000000000 Binary files a/app/assets/images/emoji/triangular_flag_on_post.png and /dev/null differ diff --git a/app/assets/images/emoji/triangular_ruler.png b/app/assets/images/emoji/triangular_ruler.png deleted file mode 100644 index 383677cb7..000000000 Binary files a/app/assets/images/emoji/triangular_ruler.png and /dev/null differ diff --git a/app/assets/images/emoji/trident.png b/app/assets/images/emoji/trident.png deleted file mode 100644 index d79a7b4cc..000000000 Binary files a/app/assets/images/emoji/trident.png and /dev/null differ diff --git a/app/assets/images/emoji/triumph.png b/app/assets/images/emoji/triumph.png deleted file mode 100644 index 92f93bd10..000000000 Binary files a/app/assets/images/emoji/triumph.png and /dev/null differ diff --git a/app/assets/images/emoji/trolleybus.png b/app/assets/images/emoji/trolleybus.png deleted file mode 100644 index b9740a53f..000000000 Binary files a/app/assets/images/emoji/trolleybus.png and /dev/null differ diff --git a/app/assets/images/emoji/trophy.png b/app/assets/images/emoji/trophy.png deleted file mode 100644 index 95d3b63f5..000000000 Binary files a/app/assets/images/emoji/trophy.png and /dev/null differ diff --git a/app/assets/images/emoji/tropical_drink.png b/app/assets/images/emoji/tropical_drink.png deleted file mode 100644 index 55ca9eeda..000000000 Binary files a/app/assets/images/emoji/tropical_drink.png and /dev/null differ diff --git a/app/assets/images/emoji/tropical_fish.png b/app/assets/images/emoji/tropical_fish.png deleted file mode 100644 index a6d734987..000000000 Binary files a/app/assets/images/emoji/tropical_fish.png and /dev/null differ diff --git a/app/assets/images/emoji/truck.png b/app/assets/images/emoji/truck.png deleted file mode 100644 index 3f25ba1f9..000000000 Binary files a/app/assets/images/emoji/truck.png and /dev/null differ diff --git a/app/assets/images/emoji/trumpet.png b/app/assets/images/emoji/trumpet.png deleted file mode 100644 index 8d4703fc2..000000000 Binary files a/app/assets/images/emoji/trumpet.png and /dev/null differ diff --git a/app/assets/images/emoji/tshirt.png b/app/assets/images/emoji/tshirt.png deleted file mode 100644 index 297a6d63e..000000000 Binary files a/app/assets/images/emoji/tshirt.png and /dev/null differ diff --git a/app/assets/images/emoji/tulip.png b/app/assets/images/emoji/tulip.png deleted file mode 100644 index b3ee1102a..000000000 Binary files a/app/assets/images/emoji/tulip.png and /dev/null differ diff --git a/app/assets/images/emoji/turtle.png b/app/assets/images/emoji/turtle.png deleted file mode 100644 index 04d1d9684..000000000 Binary files a/app/assets/images/emoji/turtle.png and /dev/null differ diff --git a/app/assets/images/emoji/tv.png b/app/assets/images/emoji/tv.png deleted file mode 100644 index 803dc3d41..000000000 Binary files a/app/assets/images/emoji/tv.png and /dev/null differ diff --git a/app/assets/images/emoji/twisted_rightwards_arrows.png b/app/assets/images/emoji/twisted_rightwards_arrows.png deleted file mode 100644 index 25cde18b2..000000000 Binary files a/app/assets/images/emoji/twisted_rightwards_arrows.png and /dev/null differ diff --git a/app/assets/images/emoji/two_hearts.png b/app/assets/images/emoji/two_hearts.png deleted file mode 100644 index b189e9aea..000000000 Binary files a/app/assets/images/emoji/two_hearts.png and /dev/null differ diff --git a/app/assets/images/emoji/two_men_holding_hands.png b/app/assets/images/emoji/two_men_holding_hands.png deleted file mode 100644 index d1099f21f..000000000 Binary files a/app/assets/images/emoji/two_men_holding_hands.png and /dev/null differ diff --git a/app/assets/images/emoji/two_women_holding_hands.png b/app/assets/images/emoji/two_women_holding_hands.png deleted file mode 100644 index 619646c4e..000000000 Binary files a/app/assets/images/emoji/two_women_holding_hands.png and /dev/null differ diff --git a/app/assets/images/emoji/u5272.png b/app/assets/images/emoji/u5272.png deleted file mode 100644 index 2148253fc..000000000 Binary files a/app/assets/images/emoji/u5272.png and /dev/null differ diff --git a/app/assets/images/emoji/u5408.png b/app/assets/images/emoji/u5408.png deleted file mode 100644 index 03ab0d874..000000000 Binary files a/app/assets/images/emoji/u5408.png and /dev/null differ diff --git a/app/assets/images/emoji/u55b6.png b/app/assets/images/emoji/u55b6.png deleted file mode 100644 index ba946d3f3..000000000 Binary files a/app/assets/images/emoji/u55b6.png and /dev/null differ diff --git a/app/assets/images/emoji/u6307.png b/app/assets/images/emoji/u6307.png deleted file mode 100644 index 6557f5672..000000000 Binary files a/app/assets/images/emoji/u6307.png and /dev/null differ diff --git a/app/assets/images/emoji/u6708.png b/app/assets/images/emoji/u6708.png deleted file mode 100644 index e4dfe5aa7..000000000 Binary files a/app/assets/images/emoji/u6708.png and /dev/null differ diff --git a/app/assets/images/emoji/u6709.png b/app/assets/images/emoji/u6709.png deleted file mode 100644 index cd8fb3f62..000000000 Binary files a/app/assets/images/emoji/u6709.png and /dev/null differ diff --git a/app/assets/images/emoji/u6e80.png b/app/assets/images/emoji/u6e80.png deleted file mode 100644 index 5df1cb878..000000000 Binary files a/app/assets/images/emoji/u6e80.png and /dev/null differ diff --git a/app/assets/images/emoji/u7121.png b/app/assets/images/emoji/u7121.png deleted file mode 100644 index 25f694ed3..000000000 Binary files a/app/assets/images/emoji/u7121.png and /dev/null differ diff --git a/app/assets/images/emoji/u7533.png b/app/assets/images/emoji/u7533.png deleted file mode 100644 index fc4a9901b..000000000 Binary files a/app/assets/images/emoji/u7533.png and /dev/null differ diff --git a/app/assets/images/emoji/u7981.png b/app/assets/images/emoji/u7981.png deleted file mode 100644 index f550a573d..000000000 Binary files a/app/assets/images/emoji/u7981.png and /dev/null differ diff --git a/app/assets/images/emoji/u7a7a.png b/app/assets/images/emoji/u7a7a.png deleted file mode 100644 index c05f5cff7..000000000 Binary files a/app/assets/images/emoji/u7a7a.png and /dev/null differ diff --git a/app/assets/images/emoji/uk.png b/app/assets/images/emoji/uk.png deleted file mode 100644 index 2a62c7a08..000000000 Binary files a/app/assets/images/emoji/uk.png and /dev/null differ diff --git a/app/assets/images/emoji/umbrella.png b/app/assets/images/emoji/umbrella.png deleted file mode 100644 index 1db722fa6..000000000 Binary files a/app/assets/images/emoji/umbrella.png and /dev/null differ diff --git a/app/assets/images/emoji/unamused.png b/app/assets/images/emoji/unamused.png deleted file mode 100644 index 3722e6f57..000000000 Binary files a/app/assets/images/emoji/unamused.png and /dev/null differ diff --git a/app/assets/images/emoji/underage.png b/app/assets/images/emoji/underage.png deleted file mode 100644 index a789b3c62..000000000 Binary files a/app/assets/images/emoji/underage.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/0023.png b/app/assets/images/emoji/unicode/0023.png deleted file mode 100644 index 6765d7d3c..000000000 Binary files a/app/assets/images/emoji/unicode/0023.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/0031.png b/app/assets/images/emoji/unicode/0031.png deleted file mode 100644 index 2d1f9f8c4..000000000 Binary files a/app/assets/images/emoji/unicode/0031.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/0032.png b/app/assets/images/emoji/unicode/0032.png deleted file mode 100644 index c191f8a32..000000000 Binary files a/app/assets/images/emoji/unicode/0032.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/0033.png b/app/assets/images/emoji/unicode/0033.png deleted file mode 100644 index 55644c990..000000000 Binary files a/app/assets/images/emoji/unicode/0033.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/0034.png b/app/assets/images/emoji/unicode/0034.png deleted file mode 100644 index 14782ba23..000000000 Binary files a/app/assets/images/emoji/unicode/0034.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/0035.png b/app/assets/images/emoji/unicode/0035.png deleted file mode 100644 index 794321aa2..000000000 Binary files a/app/assets/images/emoji/unicode/0035.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/0036.png b/app/assets/images/emoji/unicode/0036.png deleted file mode 100644 index 568805565..000000000 Binary files a/app/assets/images/emoji/unicode/0036.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/0037.png b/app/assets/images/emoji/unicode/0037.png deleted file mode 100644 index 354e89ae7..000000000 Binary files a/app/assets/images/emoji/unicode/0037.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/0038.png b/app/assets/images/emoji/unicode/0038.png deleted file mode 100644 index 7bdb42232..000000000 Binary files a/app/assets/images/emoji/unicode/0038.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/0039.png b/app/assets/images/emoji/unicode/0039.png deleted file mode 100644 index 8006cc909..000000000 Binary files a/app/assets/images/emoji/unicode/0039.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/1f1e8.png b/app/assets/images/emoji/unicode/1f1e8.png deleted file mode 100644 index b30dcc53d..000000000 Binary files a/app/assets/images/emoji/unicode/1f1e8.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/1f1e9.png b/app/assets/images/emoji/unicode/1f1e9.png deleted file mode 100644 index 16a28548c..000000000 Binary files a/app/assets/images/emoji/unicode/1f1e9.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/1f1ea.png b/app/assets/images/emoji/unicode/1f1ea.png deleted file mode 100644 index 71b30bff3..000000000 Binary files a/app/assets/images/emoji/unicode/1f1ea.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/1f1eb.png b/app/assets/images/emoji/unicode/1f1eb.png deleted file mode 100644 index 6311c9115..000000000 Binary files a/app/assets/images/emoji/unicode/1f1eb.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/1f1ec.png b/app/assets/images/emoji/unicode/1f1ec.png deleted file mode 100644 index 2a62c7a08..000000000 Binary files a/app/assets/images/emoji/unicode/1f1ec.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/1f1ee.png b/app/assets/images/emoji/unicode/1f1ee.png deleted file mode 100644 index 70bc9f324..000000000 Binary files a/app/assets/images/emoji/unicode/1f1ee.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/1f1ef.png b/app/assets/images/emoji/unicode/1f1ef.png deleted file mode 100644 index b786efbbd..000000000 Binary files a/app/assets/images/emoji/unicode/1f1ef.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/1f1f0.png b/app/assets/images/emoji/unicode/1f1f0.png deleted file mode 100644 index b4c0c1b67..000000000 Binary files a/app/assets/images/emoji/unicode/1f1f0.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/1f1f7.png b/app/assets/images/emoji/unicode/1f1f7.png deleted file mode 100644 index 55fcf3549..000000000 Binary files a/app/assets/images/emoji/unicode/1f1f7.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/1f303.png b/app/assets/images/emoji/unicode/1f303.png deleted file mode 100644 index 097a84241..000000000 Binary files a/app/assets/images/emoji/unicode/1f303.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/1f4e9.png b/app/assets/images/emoji/unicode/1f4e9.png deleted file mode 100644 index 0e01fd5f0..000000000 Binary files a/app/assets/images/emoji/unicode/1f4e9.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/1f50a.png b/app/assets/images/emoji/unicode/1f50a.png deleted file mode 100644 index c884bd4f6..000000000 Binary files a/app/assets/images/emoji/unicode/1f50a.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/1f51d.png b/app/assets/images/emoji/unicode/1f51d.png deleted file mode 100644 index 5aa4dd442..000000000 Binary files a/app/assets/images/emoji/unicode/1f51d.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/1f528.png b/app/assets/images/emoji/unicode/1f528.png deleted file mode 100644 index 6b75bc37b..000000000 Binary files a/app/assets/images/emoji/unicode/1f528.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/1f684.png b/app/assets/images/emoji/unicode/1f684.png deleted file mode 100644 index 8eca36845..000000000 Binary files a/app/assets/images/emoji/unicode/1f684.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/1f685.png b/app/assets/images/emoji/unicode/1f685.png deleted file mode 100644 index 16651acff..000000000 Binary files a/app/assets/images/emoji/unicode/1f685.png and /dev/null differ diff --git a/app/assets/images/emoji/unicode/1f687.png b/app/assets/images/emoji/unicode/1f687.png deleted file mode 100644 index 7f34f6be3..000000000 Binary files a/app/assets/images/emoji/unicode/1f687.png and /dev/null differ diff --git a/app/assets/images/emoji/unlock.png b/app/assets/images/emoji/unlock.png deleted file mode 100644 index 22b429cd0..000000000 Binary files a/app/assets/images/emoji/unlock.png and /dev/null differ diff --git a/app/assets/images/emoji/up.png b/app/assets/images/emoji/up.png deleted file mode 100644 index 829219a86..000000000 Binary files a/app/assets/images/emoji/up.png and /dev/null differ diff --git a/app/assets/images/emoji/us.png b/app/assets/images/emoji/us.png deleted file mode 100644 index 38137669a..000000000 Binary files a/app/assets/images/emoji/us.png and /dev/null differ diff --git a/app/assets/images/emoji/v.png b/app/assets/images/emoji/v.png deleted file mode 100644 index f61267c28..000000000 Binary files a/app/assets/images/emoji/v.png and /dev/null differ diff --git a/app/assets/images/emoji/vertical_traffic_light.png b/app/assets/images/emoji/vertical_traffic_light.png deleted file mode 100644 index 7a5ba35f0..000000000 Binary files a/app/assets/images/emoji/vertical_traffic_light.png and /dev/null differ diff --git a/app/assets/images/emoji/vhs.png b/app/assets/images/emoji/vhs.png deleted file mode 100644 index 881081c17..000000000 Binary files a/app/assets/images/emoji/vhs.png and /dev/null differ diff --git a/app/assets/images/emoji/vibration_mode.png b/app/assets/images/emoji/vibration_mode.png deleted file mode 100644 index a716e96c6..000000000 Binary files a/app/assets/images/emoji/vibration_mode.png and /dev/null differ diff --git a/app/assets/images/emoji/video_camera.png b/app/assets/images/emoji/video_camera.png deleted file mode 100644 index 274cecdd6..000000000 Binary files a/app/assets/images/emoji/video_camera.png and /dev/null differ diff --git a/app/assets/images/emoji/video_game.png b/app/assets/images/emoji/video_game.png deleted file mode 100644 index 59d45baea..000000000 Binary files a/app/assets/images/emoji/video_game.png and /dev/null differ diff --git a/app/assets/images/emoji/violin.png b/app/assets/images/emoji/violin.png deleted file mode 100644 index 0dba5ba2b..000000000 Binary files a/app/assets/images/emoji/violin.png and /dev/null differ diff --git a/app/assets/images/emoji/virgo.png b/app/assets/images/emoji/virgo.png deleted file mode 100644 index 72e1763f5..000000000 Binary files a/app/assets/images/emoji/virgo.png and /dev/null differ diff --git a/app/assets/images/emoji/volcano.png b/app/assets/images/emoji/volcano.png deleted file mode 100644 index 9b434539b..000000000 Binary files a/app/assets/images/emoji/volcano.png and /dev/null differ diff --git a/app/assets/images/emoji/vs.png b/app/assets/images/emoji/vs.png deleted file mode 100644 index 863638850..000000000 Binary files a/app/assets/images/emoji/vs.png and /dev/null differ diff --git a/app/assets/images/emoji/walking.png b/app/assets/images/emoji/walking.png deleted file mode 100644 index 7a2bfacfc..000000000 Binary files a/app/assets/images/emoji/walking.png and /dev/null differ diff --git a/app/assets/images/emoji/waning_crescent_moon.png b/app/assets/images/emoji/waning_crescent_moon.png deleted file mode 100644 index 30387780f..000000000 Binary files a/app/assets/images/emoji/waning_crescent_moon.png and /dev/null differ diff --git a/app/assets/images/emoji/waning_gibbous_moon.png b/app/assets/images/emoji/waning_gibbous_moon.png deleted file mode 100644 index 8e324ec5f..000000000 Binary files a/app/assets/images/emoji/waning_gibbous_moon.png and /dev/null differ diff --git a/app/assets/images/emoji/warning.png b/app/assets/images/emoji/warning.png deleted file mode 100644 index 466658d99..000000000 Binary files a/app/assets/images/emoji/warning.png and /dev/null differ diff --git a/app/assets/images/emoji/watch.png b/app/assets/images/emoji/watch.png deleted file mode 100644 index d503bb87c..000000000 Binary files a/app/assets/images/emoji/watch.png and /dev/null differ diff --git a/app/assets/images/emoji/water_buffalo.png b/app/assets/images/emoji/water_buffalo.png deleted file mode 100644 index 3bcde3edd..000000000 Binary files a/app/assets/images/emoji/water_buffalo.png and /dev/null differ diff --git a/app/assets/images/emoji/watermelon.png b/app/assets/images/emoji/watermelon.png deleted file mode 100644 index fc212be78..000000000 Binary files a/app/assets/images/emoji/watermelon.png and /dev/null differ diff --git a/app/assets/images/emoji/wave.png b/app/assets/images/emoji/wave.png deleted file mode 100644 index e78402eb0..000000000 Binary files a/app/assets/images/emoji/wave.png and /dev/null differ diff --git a/app/assets/images/emoji/wavy_dash.png b/app/assets/images/emoji/wavy_dash.png deleted file mode 100644 index 77f626cc5..000000000 Binary files a/app/assets/images/emoji/wavy_dash.png and /dev/null differ diff --git a/app/assets/images/emoji/waxing_crescent_moon.png b/app/assets/images/emoji/waxing_crescent_moon.png deleted file mode 100644 index c8f13dd31..000000000 Binary files a/app/assets/images/emoji/waxing_crescent_moon.png and /dev/null differ diff --git a/app/assets/images/emoji/waxing_gibbous_moon.png b/app/assets/images/emoji/waxing_gibbous_moon.png deleted file mode 100644 index dd8c48458..000000000 Binary files a/app/assets/images/emoji/waxing_gibbous_moon.png and /dev/null differ diff --git a/app/assets/images/emoji/wc.png b/app/assets/images/emoji/wc.png deleted file mode 100644 index dfe84d2a7..000000000 Binary files a/app/assets/images/emoji/wc.png and /dev/null differ diff --git a/app/assets/images/emoji/weary.png b/app/assets/images/emoji/weary.png deleted file mode 100644 index 0c5475411..000000000 Binary files a/app/assets/images/emoji/weary.png and /dev/null differ diff --git a/app/assets/images/emoji/wedding.png b/app/assets/images/emoji/wedding.png deleted file mode 100644 index ead19d52c..000000000 Binary files a/app/assets/images/emoji/wedding.png and /dev/null differ diff --git a/app/assets/images/emoji/whale.png b/app/assets/images/emoji/whale.png deleted file mode 100644 index 5bb113e42..000000000 Binary files a/app/assets/images/emoji/whale.png and /dev/null differ diff --git a/app/assets/images/emoji/whale2.png b/app/assets/images/emoji/whale2.png deleted file mode 100644 index 4af657b2f..000000000 Binary files a/app/assets/images/emoji/whale2.png and /dev/null differ diff --git a/app/assets/images/emoji/wheelchair.png b/app/assets/images/emoji/wheelchair.png deleted file mode 100644 index eddcdd797..000000000 Binary files a/app/assets/images/emoji/wheelchair.png and /dev/null differ diff --git a/app/assets/images/emoji/white_check_mark.png b/app/assets/images/emoji/white_check_mark.png deleted file mode 100644 index 61dc0583c..000000000 Binary files a/app/assets/images/emoji/white_check_mark.png and /dev/null differ diff --git a/app/assets/images/emoji/white_circle.png b/app/assets/images/emoji/white_circle.png deleted file mode 100644 index da782ae29..000000000 Binary files a/app/assets/images/emoji/white_circle.png and /dev/null differ diff --git a/app/assets/images/emoji/white_flower.png b/app/assets/images/emoji/white_flower.png deleted file mode 100644 index c0929d0dd..000000000 Binary files a/app/assets/images/emoji/white_flower.png and /dev/null differ diff --git a/app/assets/images/emoji/white_large_square.png b/app/assets/images/emoji/white_large_square.png deleted file mode 100644 index 60cb19a13..000000000 Binary files a/app/assets/images/emoji/white_large_square.png and /dev/null differ diff --git a/app/assets/images/emoji/white_medium_small_square.png b/app/assets/images/emoji/white_medium_small_square.png deleted file mode 100644 index a115cdc49..000000000 Binary files a/app/assets/images/emoji/white_medium_small_square.png and /dev/null differ diff --git a/app/assets/images/emoji/white_medium_square.png b/app/assets/images/emoji/white_medium_square.png deleted file mode 100644 index 199808bcf..000000000 Binary files a/app/assets/images/emoji/white_medium_square.png and /dev/null differ diff --git a/app/assets/images/emoji/white_small_square.png b/app/assets/images/emoji/white_small_square.png deleted file mode 100644 index 24ba879f4..000000000 Binary files a/app/assets/images/emoji/white_small_square.png and /dev/null differ diff --git a/app/assets/images/emoji/white_square_button.png b/app/assets/images/emoji/white_square_button.png deleted file mode 100644 index 63c7a3ef4..000000000 Binary files a/app/assets/images/emoji/white_square_button.png and /dev/null differ diff --git a/app/assets/images/emoji/wind_chime.png b/app/assets/images/emoji/wind_chime.png deleted file mode 100644 index efacf5dd4..000000000 Binary files a/app/assets/images/emoji/wind_chime.png and /dev/null differ diff --git a/app/assets/images/emoji/wine_glass.png b/app/assets/images/emoji/wine_glass.png deleted file mode 100644 index 82b0f0005..000000000 Binary files a/app/assets/images/emoji/wine_glass.png and /dev/null differ diff --git a/app/assets/images/emoji/wink.png b/app/assets/images/emoji/wink.png deleted file mode 100644 index 756766dd3..000000000 Binary files a/app/assets/images/emoji/wink.png and /dev/null differ diff --git a/app/assets/images/emoji/wolf.png b/app/assets/images/emoji/wolf.png deleted file mode 100644 index c60c96895..000000000 Binary files a/app/assets/images/emoji/wolf.png and /dev/null differ diff --git a/app/assets/images/emoji/woman.png b/app/assets/images/emoji/woman.png deleted file mode 100644 index 6bf0d2b12..000000000 Binary files a/app/assets/images/emoji/woman.png and /dev/null differ diff --git a/app/assets/images/emoji/womans_clothes.png b/app/assets/images/emoji/womans_clothes.png deleted file mode 100644 index aa297c7b6..000000000 Binary files a/app/assets/images/emoji/womans_clothes.png and /dev/null differ diff --git a/app/assets/images/emoji/womans_hat.png b/app/assets/images/emoji/womans_hat.png deleted file mode 100644 index 4cb2e6a69..000000000 Binary files a/app/assets/images/emoji/womans_hat.png and /dev/null differ diff --git a/app/assets/images/emoji/womens.png b/app/assets/images/emoji/womens.png deleted file mode 100644 index 518b76a6d..000000000 Binary files a/app/assets/images/emoji/womens.png and /dev/null differ diff --git a/app/assets/images/emoji/worried.png b/app/assets/images/emoji/worried.png deleted file mode 100644 index afd9283fc..000000000 Binary files a/app/assets/images/emoji/worried.png and /dev/null differ diff --git a/app/assets/images/emoji/wrench.png b/app/assets/images/emoji/wrench.png deleted file mode 100644 index a87072ad1..000000000 Binary files a/app/assets/images/emoji/wrench.png and /dev/null differ diff --git a/app/assets/images/emoji/x.png b/app/assets/images/emoji/x.png deleted file mode 100644 index b84f63557..000000000 Binary files a/app/assets/images/emoji/x.png and /dev/null differ diff --git a/app/assets/images/emoji/yellow_heart.png b/app/assets/images/emoji/yellow_heart.png deleted file mode 100644 index fa41ce78a..000000000 Binary files a/app/assets/images/emoji/yellow_heart.png and /dev/null differ diff --git a/app/assets/images/emoji/yen.png b/app/assets/images/emoji/yen.png deleted file mode 100644 index 139bc936e..000000000 Binary files a/app/assets/images/emoji/yen.png and /dev/null differ diff --git a/app/assets/images/emoji/yum.png b/app/assets/images/emoji/yum.png deleted file mode 100644 index fc39637ec..000000000 Binary files a/app/assets/images/emoji/yum.png and /dev/null differ diff --git a/app/assets/images/emoji/zap.png b/app/assets/images/emoji/zap.png deleted file mode 100644 index 260c531b9..000000000 Binary files a/app/assets/images/emoji/zap.png and /dev/null differ diff --git a/app/assets/images/emoji/zero.png b/app/assets/images/emoji/zero.png deleted file mode 100644 index 15e7446c8..000000000 Binary files a/app/assets/images/emoji/zero.png and /dev/null differ diff --git a/app/assets/images/emoji/zzz.png b/app/assets/images/emoji/zzz.png deleted file mode 100644 index 30be04655..000000000 Binary files a/app/assets/images/emoji/zzz.png and /dev/null differ diff --git a/app/assets/images/loading-large.gif b/app/assets/images/loading-large.gif new file mode 100644 index 000000000..926c3d9d8 Binary files /dev/null and b/app/assets/images/loading-large.gif differ diff --git a/app/assets/images/loading-small.gif b/app/assets/images/loading-small.gif new file mode 100644 index 000000000..e6fdced3e Binary files /dev/null and b/app/assets/images/loading-small.gif differ diff --git a/app/assets/javascripts/angular-new/controllers/build_list_controller.js.erb b/app/assets/javascripts/angular-new/controllers/build_list_controller.js.erb index 27f628a10..c2ce569d2 100644 --- a/app/assets/javascripts/angular-new/controllers/build_list_controller.js.erb +++ b/app/assets/javascripts/angular-new/controllers/build_list_controller.js.erb @@ -27,22 +27,27 @@ RosaABF.controller('BuildListController', ['$scope', '$http', '$timeout', 'Sound }); } + $scope.canRefresh = function() { + if ($scope.attach_advisory != 'no') { return false; } + if (!$scope.build_list) { return true; } + + var show_dependent_projects = _.find($scope.build_list.packages, function(p){ + return p.show_dependent_projects; + }); + if (show_dependent_projects) { return false; } + if (!( + $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%> + )) { return true; } + return false; + } + $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(); - } + if ($scope.canRefresh()) { $scope.getBuildList(); } $scope.cancelRefresh = $timeout($scope.refresh, 10000); } $scope.refresh(); diff --git a/app/assets/javascripts/angular-new/models/build_list.js.erb b/app/assets/javascripts/angular-new/models/build_list.js.erb index bdefd242f..a9e942d3d 100644 --- a/app/assets/javascripts/angular-new/models/build_list.js.erb +++ b/app/assets/javascripts/angular-new/models/build_list.js.erb @@ -50,6 +50,8 @@ var BuildList = function(atts, dictionary) { case <%=BuildList::BUILD_ERROR%>: case <%=BuildList::FAILED_PUBLISH%>: case <%=BuildList::FAILED_PUBLISH_INTO_TESTING%>: + case <%=BuildList::PACKAGES_FAIL%>: + case <%=BuildList::UNPERMITTED_ARCH%>: case <%=BuildList::REJECTED_PUBLISH%>: self.status_color = 'danger'; break case <%=BuildList::TESTS_FAILED%>: self.status_color = 'warning'; break default: self.status_color = 'nocolor'; diff --git a/app/assets/javascripts/angularjs/controllers/statistics_controller.js.coffee b/app/assets/javascripts/angularjs/controllers/statistics_controller.js.coffee new file mode 100644 index 000000000..ac0837712 --- /dev/null +++ b/app/assets/javascripts/angularjs/controllers/statistics_controller.js.coffee @@ -0,0 +1,142 @@ +RosaABF.controller 'StatisticsController', ['$scope', '$http', ($scope, $http) -> + + $scope.range = 'last_30_days' + $scope.range_start = $('#range_start').attr('value') + $scope.range_end = $('#range_end').attr('value') + $scope.loading = true + $scope.statistics = {} + $scope.statistics_path = '/statistics' + + $scope.colors = [ + '56, 132, 158', + '77, 169, 68', + '241, 128, 73', + '174, 199, 232', + '255, 187, 120', + '152, 223, 138', + '214, 39, 40', + '31, 119, 180' + ] + $scope.charts = {} + + + $scope.init = -> + $('#range-form .date_picker').datepicker + 'dateFormat': 'yy-mm-dd' + maxDate: 0 + minDate: -366 + showButtonPanel: true + + $scope.rangeChange() + true + + $scope.prepareRange = -> + range_start = new Date($scope.range_start) + range_end = new Date($scope.range_end) + + if range_start > range_end + tmp = $scope.range_start + $scope.range_start = $scope.range_end + $scope.range_end = tmp + + + $scope.rangeChange = -> + $scope.loading = true + $scope.statistics = {} + + $scope.prepareRange() + $('.doughnut-legend').remove() + + params = + range: $scope.range + range_start: $scope.range_start + range_end: $scope.range_end + format: 'json' + + $http.get($scope.statistics_path, params: params).success (results) -> + $scope.statistics = results + + $scope.loading = false + + # BuildLists + if $scope.statistics.build_lists + $scope.initBuildListsChart() + + # PullRequests + if $scope.statistics.pull_requests + $scope.initPullRequestsChart() + + # Issues + if $scope.statistics.issues + $scope.initIssuesChart() + + .error (data, status, headers, config) -> + console.log 'error:' + $scope.loading = false + + $scope.dateChart = (id, collections) -> + new_collections = $.grep collections, ( c ) -> + return c + + if collections.length == new_collections.length + $scope.charts[id].destroy() if $scope.charts[id] + + points = collections[0] + factor = points.length // 45 + 1 + + tooltipTitles = [] + labels = _.map points, (d, index) -> + x = d.x + tooltipTitles.push x + if index %% factor == 0 + x + else + '' + + datasets = _.map collections, (collection, index) -> + data = _.map collection, (d) -> + d.y + + dataset = + fillColor: "rgba(#{ $scope.colors[index] }, 0.5)" + strokeColor: "rgba(#{ $scope.colors[index] }, 1)" + pointColor: "rgba(#{ $scope.colors[index] }, 1)" + pointStrokeColor: "#fff" + data: data + + data = + datasets: datasets + # We display only limited count of labels on X axis, but tooltips should have titles + # See: Chart.js "Added by avokhmin" + labels: labels + tooltipTitles: tooltipTitles + + options = + responsive: true + + context = $(id)[0].getContext('2d') + $scope.charts[id] = new Chart(context).Line(data, options) + + $scope.initBuildListsChart = -> + $scope.dateChart '#build_lists_chart', [ + $scope.statistics.build_lists.build_started, + $scope.statistics.build_lists.success, + $scope.statistics.build_lists.build_error, + $scope.statistics.build_lists.build_published + ] + + $scope.initPullRequestsChart = -> + $scope.dateChart '#pull_requests_chart', [ + $scope.statistics.pull_requests.open, + $scope.statistics.pull_requests.closed, + $scope.statistics.pull_requests.approved + ] + + $scope.initIssuesChart = -> + $scope.dateChart '#issues_chart', [ + $scope.statistics.issues.open, + $scope.statistics.issues.closed, + $scope.statistics.issues.approved + ] + +] \ No newline at end of file diff --git a/app/assets/javascripts/extra/build_list.js b/app/assets/javascripts/extra/build_list.js index 6466ba1f4..4efc0d5f2 100644 --- a/app/assets/javascripts/extra/build_list.js +++ b/app/assets/javascripts/extra/build_list.js @@ -1,10 +1,88 @@ $(document).ready(function() { 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]; - if($('#from_build_list_id').size() == 0) { - $('#build_list_save_to_repository_id').trigger('change'); - } + var build_platform = $('#build_for_pl_' + platform_id); + var all_repositories = $('.all_platforms input'); + all_repositories.removeAttr('checked'); + 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'); + auto_create_container.removeAttr('checked'); + addPersonalPlatformToExtraRepos(selected_option, extra_repos); + extra_repos.show(); + + var version_sel = $('#build_list_project_version').val(); + var parent = $('div.build_for_pl').filter(function (){ + return $(this).text() == version_sel; + }).parent(); + parent.find('input').removeAttr('disabled'); + parent.find('input[rep_name="main"]').attr('checked', 'checked'); + } else { + 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'); + parent.find('input[rep_name="base"]').attr('checked', 'checked'); + if (rep_name != 'main') { + parent.find('input[rep_name="' + rep_name + '"]').attr('checked', 'checked'); + } + setBranchSelected(selected_option); + } + var build_list_auto_publish_status = $('#build_list_auto_publish_status'); + if (selected_option.attr('publish_without_qa') == '1') { + build_list_auto_publish_status.removeAttr('disabled').val('default'); + } else { + build_list_auto_publish_status.val('none').attr('disabled', 'disabled'); + auto_create_container.attr('checked', 'checked'); + } + }); + + ownership_btn.click(function() { + ownership_btn.removeClass('active'); + $('#filter_ownership').val($(this).val()); + $(this).addClass('active'); + return false; + }); + + perpage_btn.click(function() { + perpage_btn.removeClass('active'); + $('#per_page').val($(this).val()); + $(this).addClass('active'); + return false; + }); + + $('a#updated_at_clear').click(function() { + $($(this).attr('href')).val(''); + return false; + }); + + $('#filter_clear').click(function() { + $('#monitoring_filter .input_cleanse').val(''); + $('.btn-group .btn').removeClass('active'); + if(ownership_btn.length > 0) { ownership_btn[0].click(); } + perpage_btn[0].click(); + return false; + }); + + $('.mediumheight.min').datepicker({ + dateFormat: 'dd/mm/yy', + showButtonPanel: true + }); $(document).on('click', '#owner_filter_build_lists, #status_filter_build_lists', function(){ $('#datatable').dataTable().fnDraw(); @@ -24,4 +102,55 @@ $(document).ready(function() { }); return false; }); + + $(document).on('change', '#build_list_project_version', function(){ + var save_to_repo = $('#build_list_save_to_repository_id'); + var platform_id = save_to_repo.find("option:selected").attr('platform_id'); + var build_platform = $('#build_for_pl_' + platform_id); + + if (build_platform.size() == 0) { + save_to_repo.trigger('change'); + } + }); }); + +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'); + var branch_pl_opt = bl_version_sel.find('option[value="' + pl_name + '"]'); + // If there is branch we need - set it selected: + if (branch_pl_opt.size() > 0) { + bl_version_sel.find('option[selected]').removeAttr('selected'); + branch_pl_opt.attr('selected', 'selected'); + bl_version_sel.val(branch_pl_opt); + // hack for FF to force render of select box. + bl_version_sel[0].innerHTML = bl_version_sel[0].innerHTML; + } +} diff --git a/app/assets/javascripts/lib/Chart.js b/app/assets/javascripts/lib/Chart.js new file mode 100644 index 000000000..e65251edd --- /dev/null +++ b/app/assets/javascripts/lib/Chart.js @@ -0,0 +1,3383 @@ +/*! + * Chart.js + * http://chartjs.org/ + * Version: 1.0.1-beta.4 + * + * Copyright 2014 Nick Downie + * Released under the MIT license + * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md + */ + + +(function(){ + + "use strict"; + + //Declare root variable - window in the browser, global on the server + var root = this, + previous = root.Chart; + + //Occupy the global variable of Chart, and create a simple base class + var Chart = function(context){ + var chart = this; + this.canvas = context.canvas; + + this.ctx = context; + + //Variables global to the chart + var width = this.width = context.canvas.width; + var height = this.height = context.canvas.height; + this.aspectRatio = this.width / this.height; + //High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale. + helpers.retinaScale(this); + + return this; + }; + //Globally expose the defaults to allow for user updating/changing + Chart.defaults = { + global: { + // Boolean - Whether to animate the chart + animation: true, + + // Number - Number of animation steps + animationSteps: 60, + + // String - Animation easing effect + animationEasing: "easeOutQuart", + + // Boolean - If we should show the scale at all + showScale: true, + + // Boolean - If we want to override with a hard coded scale + scaleOverride: false, + + // ** Required if scaleOverride is true ** + // Number - The number of steps in a hard coded scale + scaleSteps: null, + // Number - The value jump in the hard coded scale + scaleStepWidth: null, + // Number - The scale starting value + scaleStartValue: null, + + // String - Colour of the scale line + scaleLineColor: "rgba(0,0,0,.1)", + + // Number - Pixel width of the scale line + scaleLineWidth: 1, + + // Boolean - Whether to show labels on the scale + scaleShowLabels: true, + + // Interpolated JS string - can access value + scaleLabel: "<%=value%>", + + // Boolean - Whether the scale should stick to integers, and not show any floats even if drawing space is there + scaleIntegersOnly: true, + + // Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value + scaleBeginAtZero: false, + + // String - Scale label font declaration for the scale label + scaleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", + + // Number - Scale label font size in pixels + scaleFontSize: 12, + + // String - Scale label font weight style + scaleFontStyle: "normal", + + // String - Scale label font colour + scaleFontColor: "#666", + + // Boolean - whether or not the chart should be responsive and resize when the browser does. + responsive: false, + + // Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container + maintainAspectRatio: true, + + // Boolean - Determines whether to draw tooltips on the canvas or not - attaches events to touchmove & mousemove + showTooltips: true, + + // Array - Array of string names to attach tooltip events + tooltipEvents: ["mousemove", "touchstart", "touchmove", "mouseout"], + + // String - Tooltip background colour + tooltipFillColor: "rgba(0,0,0,0.8)", + + // String - Tooltip label font declaration for the scale label + tooltipFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", + + // Number - Tooltip label font size in pixels + tooltipFontSize: 14, + + // String - Tooltip font weight style + tooltipFontStyle: "normal", + + // String - Tooltip label font colour + tooltipFontColor: "#fff", + + // String - Tooltip title font declaration for the scale label + tooltipTitleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", + + // Number - Tooltip title font size in pixels + tooltipTitleFontSize: 14, + + // String - Tooltip title font weight style + tooltipTitleFontStyle: "bold", + + // String - Tooltip title font colour + tooltipTitleFontColor: "#fff", + + // Number - pixel width of padding around tooltip text + tooltipYPadding: 6, + + // Number - pixel width of padding around tooltip text + tooltipXPadding: 6, + + // Number - Size of the caret on the tooltip + tooltipCaretSize: 8, + + // Number - Pixel radius of the tooltip border + tooltipCornerRadius: 6, + + // Number - Pixel offset from point x to tooltip edge + tooltipXOffset: 10, + + // String - Template string for single tooltips + tooltipTemplate: "<%if (label){%><%=label%>: <%}%><%= value %>", + + // String - Template string for single tooltips + multiTooltipTemplate: "<%= value %>", + + // String - Colour behind the legend colour block + multiTooltipKeyBackground: '#fff', + + // Function - Will fire on animation progression. + onAnimationProgress: function(){}, + + // Function - Will fire on animation completion. + onAnimationComplete: function(){} + + } + }; + + //Create a dictionary of chart types, to allow for extension of existing types + Chart.types = {}; + + //Global Chart helpers object for utility methods and classes + var helpers = Chart.helpers = {}; + + //-- Basic js utility methods + var each = helpers.each = function(loopable,callback,self){ + var additionalArgs = Array.prototype.slice.call(arguments, 3); + // Check to see if null or undefined firstly. + if (loopable){ + if (loopable.length === +loopable.length){ + var i; + for (i=0; i= 0; i--) { + var currentItem = arrayToSearch[i]; + if (filterCallback(currentItem)){ + return currentItem; + } + }; + }, + inherits = helpers.inherits = function(extensions){ + //Basic javascript inheritance based on the model created in Backbone.js + var parent = this; + var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function(){ return parent.apply(this, arguments); }; + + var Surrogate = function(){ this.constructor = ChartElement;}; + Surrogate.prototype = parent.prototype; + ChartElement.prototype = new Surrogate(); + + ChartElement.extend = inherits; + + if (extensions) extend(ChartElement.prototype, extensions); + + ChartElement.__super__ = parent.prototype; + + return ChartElement; + }, + noop = helpers.noop = function(){}, + uid = helpers.uid = (function(){ + var id=0; + return function(){ + return "chart-" + id++; + }; + })(), + warn = helpers.warn = function(str){ + //Method for warning of errors + if (window.console && typeof window.console.warn == "function") console.warn(str); + }, + amd = helpers.amd = (typeof root.define == 'function' && root.define.amd), + //-- Math methods + isNumber = helpers.isNumber = function(n){ + return !isNaN(parseFloat(n)) && isFinite(n); + }, + max = helpers.max = function(array){ + return Math.max.apply( Math, array ); + }, + min = helpers.min = function(array){ + return Math.min.apply( Math, array ); + }, + cap = helpers.cap = function(valueToCap,maxValue,minValue){ + if(isNumber(maxValue)) { + if( valueToCap > maxValue ) { + return maxValue; + } + } + else if(isNumber(minValue)){ + if ( valueToCap < minValue ){ + return minValue; + } + } + return valueToCap; + }, + getDecimalPlaces = helpers.getDecimalPlaces = function(num){ + if (num%1!==0 && isNumber(num)){ + return num.toString().split(".")[1].length; + } + else { + return 0; + } + }, + toRadians = helpers.radians = function(degrees){ + return degrees * (Math.PI/180); + }, + // Gets the angle from vertical upright to the point about a centre. + getAngleFromPoint = helpers.getAngleFromPoint = function(centrePoint, anglePoint){ + var distanceFromXCenter = anglePoint.x - centrePoint.x, + distanceFromYCenter = anglePoint.y - centrePoint.y, + radialDistanceFromCenter = Math.sqrt( distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); + + + var angle = Math.PI * 2 + Math.atan2(distanceFromYCenter, distanceFromXCenter); + + //If the segment is in the top left quadrant, we need to add another rotation to the angle + if (distanceFromXCenter < 0 && distanceFromYCenter < 0){ + angle += Math.PI*2; + } + + return { + angle: angle, + distance: radialDistanceFromCenter + }; + }, + aliasPixel = helpers.aliasPixel = function(pixelWidth){ + return (pixelWidth % 2 === 0) ? 0 : 0.5; + }, + splineCurve = helpers.splineCurve = function(FirstPoint,MiddlePoint,AfterPoint,t){ + //Props to Rob Spencer at scaled innovation for his post on splining between points + //http://scaledinnovation.com/analytics/splines/aboutSplines.html + var d01=Math.sqrt(Math.pow(MiddlePoint.x-FirstPoint.x,2)+Math.pow(MiddlePoint.y-FirstPoint.y,2)), + d12=Math.sqrt(Math.pow(AfterPoint.x-MiddlePoint.x,2)+Math.pow(AfterPoint.y-MiddlePoint.y,2)), + fa=t*d01/(d01+d12),// scaling factor for triangle Ta + fb=t*d12/(d01+d12); + return { + inner : { + x : MiddlePoint.x-fa*(AfterPoint.x-FirstPoint.x), + y : MiddlePoint.y-fa*(AfterPoint.y-FirstPoint.y) + }, + outer : { + x: MiddlePoint.x+fb*(AfterPoint.x-FirstPoint.x), + y : MiddlePoint.y+fb*(AfterPoint.y-FirstPoint.y) + } + }; + }, + calculateOrderOfMagnitude = helpers.calculateOrderOfMagnitude = function(val){ + return Math.floor(Math.log(val) / Math.LN10); + }, + calculateScaleRange = helpers.calculateScaleRange = function(valuesArray, drawingSize, textSize, startFromZero, integersOnly){ + + //Set a minimum step of two - a point at the top of the graph, and a point at the base + var minSteps = 2, + maxSteps = Math.floor(drawingSize/(textSize * 1.5)), + skipFitting = (minSteps >= maxSteps); + + var maxValue = max(valuesArray), + minValue = min(valuesArray); + + // We need some degree of seperation here to calculate the scales if all the values are the same + // Adding/minusing 0.5 will give us a range of 1. + if (maxValue === minValue){ + maxValue += 0.5; + // So we don't end up with a graph with a negative start value if we've said always start from zero + if (minValue >= 0.5 && !startFromZero){ + minValue -= 0.5; + } + else{ + // Make up a whole number above the values + maxValue += 0.5; + } + } + + var valueRange = Math.abs(maxValue - minValue), + rangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange), + graphMax = Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude), + graphMin = (startFromZero) ? 0 : Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude), + graphRange = graphMax - graphMin, + stepValue = Math.pow(10, rangeOrderOfMagnitude), + numberOfSteps = Math.round(graphRange / stepValue); + + //If we have more space on the graph we'll use it to give more definition to the data + while((numberOfSteps > maxSteps || (numberOfSteps * 2) < maxSteps) && !skipFitting) { + if(numberOfSteps > maxSteps){ + stepValue *=2; + numberOfSteps = Math.round(graphRange/stepValue); + // Don't ever deal with a decimal number of steps - cancel fitting and just use the minimum number of steps. + if (numberOfSteps % 1 !== 0){ + skipFitting = true; + } + } + //We can fit in double the amount of scale points on the scale + else{ + //If user has declared ints only, and the step value isn't a decimal + if (integersOnly && rangeOrderOfMagnitude >= 0){ + //If the user has said integers only, we need to check that making the scale more granular wouldn't make it a float + if(stepValue/2 % 1 === 0){ + stepValue /=2; + numberOfSteps = Math.round(graphRange/stepValue); + } + //If it would make it a float break out of the loop + else{ + break; + } + } + //If the scale doesn't have to be an int, make the scale more granular anyway. + else{ + stepValue /=2; + numberOfSteps = Math.round(graphRange/stepValue); + } + + } + } + + if (skipFitting){ + numberOfSteps = minSteps; + stepValue = graphRange / numberOfSteps; + } + + return { + steps : numberOfSteps, + stepValue : stepValue, + min : graphMin, + max : graphMin + (numberOfSteps * stepValue) + }; + + }, + /* jshint ignore:start */ + // Blows up jshint errors based on the new Function constructor + //Templating methods + //Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/ + template = helpers.template = function(templateString, valuesObject){ + // If templateString is function rather than string-template - call the function for valuesObject + if(templateString instanceof Function){ + return templateString(valuesObject); + } + + var cache = {}; + function tmpl(str, data){ + // Figure out if we're getting a template, or if we need to + // load the template - and be sure to cache the result. + var fn = !/\W/.test(str) ? + cache[str] = cache[str] : + + // Generate a reusable function that will serve as a template + // generator (and which will be cached). + new Function("obj", + "var p=[],print=function(){p.push.apply(p,arguments);};" + + + // Introduce the data as local variables using with(){} + "with(obj){p.push('" + + + // Convert the template into pure JavaScript + str + .replace(/[\r\t\n]/g, " ") + .split("<%").join("\t") + .replace(/((^|%>)[^\t]*)'/g, "$1\r") + .replace(/\t=(.*?)%>/g, "',$1,'") + .split("\t").join("');") + .split("%>").join("p.push('") + .split("\r").join("\\'") + + "');}return p.join('');" + ); + + // Provide some basic currying to the user + return data ? fn( data ) : fn; + } + return tmpl(templateString,valuesObject); + }, + /* jshint ignore:end */ + generateLabels = helpers.generateLabels = function(templateString,numberOfSteps,graphMin,stepValue){ + var labelsArray = new Array(numberOfSteps); + if (labelTemplateString){ + each(labelsArray,function(val,index){ + labelsArray[index] = template(templateString,{value: (graphMin + (stepValue*(index+1)))}); + }); + } + return labelsArray; + }, + //--Animation methods + //Easing functions adapted from Robert Penner's easing equations + //http://www.robertpenner.com/easing/ + easingEffects = helpers.easingEffects = { + linear: function (t) { + return t; + }, + easeInQuad: function (t) { + return t * t; + }, + easeOutQuad: function (t) { + return -1 * t * (t - 2); + }, + easeInOutQuad: function (t) { + if ((t /= 1 / 2) < 1) return 1 / 2 * t * t; + return -1 / 2 * ((--t) * (t - 2) - 1); + }, + easeInCubic: function (t) { + return t * t * t; + }, + easeOutCubic: function (t) { + return 1 * ((t = t / 1 - 1) * t * t + 1); + }, + easeInOutCubic: function (t) { + if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t; + return 1 / 2 * ((t -= 2) * t * t + 2); + }, + easeInQuart: function (t) { + return t * t * t * t; + }, + easeOutQuart: function (t) { + return -1 * ((t = t / 1 - 1) * t * t * t - 1); + }, + easeInOutQuart: function (t) { + if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t * t; + return -1 / 2 * ((t -= 2) * t * t * t - 2); + }, + easeInQuint: function (t) { + return 1 * (t /= 1) * t * t * t * t; + }, + easeOutQuint: function (t) { + return 1 * ((t = t / 1 - 1) * t * t * t * t + 1); + }, + easeInOutQuint: function (t) { + if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t * t * t; + return 1 / 2 * ((t -= 2) * t * t * t * t + 2); + }, + easeInSine: function (t) { + return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1; + }, + easeOutSine: function (t) { + return 1 * Math.sin(t / 1 * (Math.PI / 2)); + }, + easeInOutSine: function (t) { + return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1); + }, + easeInExpo: function (t) { + return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1)); + }, + easeOutExpo: function (t) { + return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1); + }, + easeInOutExpo: function (t) { + if (t === 0) return 0; + if (t === 1) return 1; + if ((t /= 1 / 2) < 1) return 1 / 2 * Math.pow(2, 10 * (t - 1)); + return 1 / 2 * (-Math.pow(2, -10 * --t) + 2); + }, + easeInCirc: function (t) { + if (t >= 1) return t; + return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1); + }, + easeOutCirc: function (t) { + return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t); + }, + easeInOutCirc: function (t) { + if ((t /= 1 / 2) < 1) return -1 / 2 * (Math.sqrt(1 - t * t) - 1); + return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1); + }, + easeInElastic: function (t) { + var s = 1.70158; + var p = 0; + var a = 1; + if (t === 0) return 0; + if ((t /= 1) == 1) return 1; + if (!p) p = 1 * 0.3; + if (a < Math.abs(1)) { + a = 1; + s = p / 4; + } else s = p / (2 * Math.PI) * Math.asin(1 / a); + return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); + }, + easeOutElastic: function (t) { + var s = 1.70158; + var p = 0; + var a = 1; + if (t === 0) return 0; + if ((t /= 1) == 1) return 1; + if (!p) p = 1 * 0.3; + if (a < Math.abs(1)) { + a = 1; + s = p / 4; + } else s = p / (2 * Math.PI) * Math.asin(1 / a); + return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1; + }, + easeInOutElastic: function (t) { + var s = 1.70158; + var p = 0; + var a = 1; + if (t === 0) return 0; + if ((t /= 1 / 2) == 2) return 1; + if (!p) p = 1 * (0.3 * 1.5); + if (a < Math.abs(1)) { + a = 1; + s = p / 4; + } else s = p / (2 * Math.PI) * Math.asin(1 / a); + if (t < 1) return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); + return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1; + }, + easeInBack: function (t) { + var s = 1.70158; + return 1 * (t /= 1) * t * ((s + 1) * t - s); + }, + easeOutBack: function (t) { + var s = 1.70158; + return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1); + }, + easeInOutBack: function (t) { + var s = 1.70158; + if ((t /= 1 / 2) < 1) return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)); + return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); + }, + easeInBounce: function (t) { + return 1 - easingEffects.easeOutBounce(1 - t); + }, + easeOutBounce: function (t) { + if ((t /= 1) < (1 / 2.75)) { + return 1 * (7.5625 * t * t); + } else if (t < (2 / 2.75)) { + return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75); + } else if (t < (2.5 / 2.75)) { + return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375); + } else { + return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375); + } + }, + easeInOutBounce: function (t) { + if (t < 1 / 2) return easingEffects.easeInBounce(t * 2) * 0.5; + return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5; + } + }, + //Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ + requestAnimFrame = helpers.requestAnimFrame = (function(){ + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback) { + return window.setTimeout(callback, 1000 / 60); + }; + })(), + cancelAnimFrame = helpers.cancelAnimFrame = (function(){ + return window.cancelAnimationFrame || + window.webkitCancelAnimationFrame || + window.mozCancelAnimationFrame || + window.oCancelAnimationFrame || + window.msCancelAnimationFrame || + function(callback) { + return window.clearTimeout(callback, 1000 / 60); + }; + })(), + animationLoop = helpers.animationLoop = function(callback,totalSteps,easingString,onProgress,onComplete,chartInstance){ + + var currentStep = 0, + easingFunction = easingEffects[easingString] || easingEffects.linear; + + var animationFrame = function(){ + currentStep++; + var stepDecimal = currentStep/totalSteps; + var easeDecimal = easingFunction(stepDecimal); + + callback.call(chartInstance,easeDecimal,stepDecimal, currentStep); + onProgress.call(chartInstance,easeDecimal,stepDecimal); + if (currentStep < totalSteps){ + chartInstance.animationFrame = requestAnimFrame(animationFrame); + } else{ + onComplete.apply(chartInstance); + } + }; + requestAnimFrame(animationFrame); + }, + //-- DOM methods + getRelativePosition = helpers.getRelativePosition = function(evt){ + var mouseX, mouseY; + var e = evt.originalEvent || evt, + canvas = evt.currentTarget || evt.srcElement, + boundingRect = canvas.getBoundingClientRect(); + + if (e.touches){ + mouseX = e.touches[0].clientX - boundingRect.left; + mouseY = e.touches[0].clientY - boundingRect.top; + + } + else{ + mouseX = e.clientX - boundingRect.left; + mouseY = e.clientY - boundingRect.top; + } + + return { + x : mouseX, + y : mouseY + }; + + }, + addEvent = helpers.addEvent = function(node,eventType,method){ + if (node.addEventListener){ + node.addEventListener(eventType,method); + } else if (node.attachEvent){ + node.attachEvent("on"+eventType, method); + } else { + node["on"+eventType] = method; + } + }, + removeEvent = helpers.removeEvent = function(node, eventType, handler){ + if (node.removeEventListener){ + node.removeEventListener(eventType, handler, false); + } else if (node.detachEvent){ + node.detachEvent("on"+eventType,handler); + } else{ + node["on" + eventType] = noop; + } + }, + bindEvents = helpers.bindEvents = function(chartInstance, arrayOfEvents, handler){ + // Create the events object if it's not already present + if (!chartInstance.events) chartInstance.events = {}; + + each(arrayOfEvents,function(eventName){ + chartInstance.events[eventName] = function(){ + handler.apply(chartInstance, arguments); + }; + addEvent(chartInstance.chart.canvas,eventName,chartInstance.events[eventName]); + }); + }, + unbindEvents = helpers.unbindEvents = function (chartInstance, arrayOfEvents) { + each(arrayOfEvents, function(handler,eventName){ + removeEvent(chartInstance.chart.canvas, eventName, handler); + }); + }, + getMaximumWidth = helpers.getMaximumWidth = function(domNode){ + var container = domNode.parentNode; + // TODO = check cross browser stuff with this. + return container.clientWidth; + }, + getMaximumHeight = helpers.getMaximumHeight = function(domNode){ + var container = domNode.parentNode; + // TODO = check cross browser stuff with this. + return container.clientHeight; + }, + getMaximumSize = helpers.getMaximumSize = helpers.getMaximumWidth, // legacy support + retinaScale = helpers.retinaScale = function(chart){ + var ctx = chart.ctx, + width = chart.canvas.width, + height = chart.canvas.height; + + if (window.devicePixelRatio) { + ctx.canvas.style.width = width + "px"; + ctx.canvas.style.height = height + "px"; + ctx.canvas.height = height * window.devicePixelRatio; + ctx.canvas.width = width * window.devicePixelRatio; + ctx.scale(window.devicePixelRatio, window.devicePixelRatio); + } + }, + //-- Canvas methods + clear = helpers.clear = function(chart){ + chart.ctx.clearRect(0,0,chart.width,chart.height); + }, + fontString = helpers.fontString = function(pixelSize,fontStyle,fontFamily){ + return fontStyle + " " + pixelSize+"px " + fontFamily; + }, + longestText = helpers.longestText = function(ctx,font,arrayOfStrings){ + ctx.font = font; + var longest = 0; + each(arrayOfStrings,function(string){ + var textWidth = ctx.measureText(string).width; + longest = (textWidth > longest) ? textWidth : longest; + }); + return longest; + }, + drawRoundedRectangle = helpers.drawRoundedRectangle = function(ctx,x,y,width,height,radius){ + ctx.beginPath(); + ctx.moveTo(x + radius, y); + ctx.lineTo(x + width - radius, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + radius); + ctx.lineTo(x + width, y + height - radius); + ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); + ctx.lineTo(x + radius, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - radius); + ctx.lineTo(x, y + radius); + ctx.quadraticCurveTo(x, y, x + radius, y); + ctx.closePath(); + }; + + + //Store a reference to each instance - allowing us to globally resize chart instances on window resize. + //Destroy method on the chart will remove the instance of the chart from this reference. + Chart.instances = {}; + + Chart.Type = function(data,options,chart){ + this.options = options; + this.chart = chart; + this.id = uid(); + //Add the chart instance to the global namespace + Chart.instances[this.id] = this; + + // Initialize is always called when a chart type is created + // By default it is a no op, but it should be extended + if (options.responsive){ + this.resize(); + } + this.initialize.call(this,data); + }; + + //Core methods that'll be a part of every chart type + extend(Chart.Type.prototype,{ + initialize : function(){return this;}, + clear : function(){ + clear(this.chart); + return this; + }, + stop : function(){ + // Stops any current animation loop occuring + helpers.cancelAnimFrame.call(root, this.animationFrame); + return this; + }, + resize : function(callback){ + this.stop(); + var canvas = this.chart.canvas, + newWidth = getMaximumWidth(this.chart.canvas), + newHeight = this.options.maintainAspectRatio ? newWidth / this.chart.aspectRatio : getMaximumHeight(this.chart.canvas); + + canvas.width = this.chart.width = newWidth; + canvas.height = this.chart.height = newHeight; + + retinaScale(this.chart); + + if (typeof callback === "function"){ + callback.apply(this, Array.prototype.slice.call(arguments, 1)); + } + return this; + }, + reflow : noop, + render : function(reflow){ + if (reflow){ + this.reflow(); + } + if (this.options.animation && !reflow){ + helpers.animationLoop( + this.draw, + this.options.animationSteps, + this.options.animationEasing, + this.options.onAnimationProgress, + this.options.onAnimationComplete, + this + ); + } + else{ + this.draw(); + this.options.onAnimationComplete.call(this); + } + return this; + }, + generateLegend : function(){ + return template(this.options.legendTemplate,this); + }, + destroy : function(){ + this.clear(); + unbindEvents(this, this.events); + delete Chart.instances[this.id]; + }, + showTooltip : function(ChartElements, forceRedraw){ + // Only redraw the chart if we've actually changed what we're hovering on. + if (typeof this.activeElements === 'undefined') this.activeElements = []; + + var isChanged = (function(Elements){ + var changed = false; + + if (Elements.length !== this.activeElements.length){ + changed = true; + return changed; + } + + each(Elements, function(element, index){ + if (element !== this.activeElements[index]){ + changed = true; + } + }, this); + return changed; + }).call(this, ChartElements); + + if (!isChanged && !forceRedraw){ + return; + } + else{ + this.activeElements = ChartElements; + } + this.draw(); + if (ChartElements.length > 0){ + // If we have multiple datasets, show a MultiTooltip for all of the data points at that index + // Added by avokhmin: Uses MultiTooltip for all charts + if (this.datasets && this.datasets.length >= 1) { + var dataArray, + dataIndex; + + for (var i = this.datasets.length - 1; i >= 0; i--) { + dataArray = this.datasets[i].points || this.datasets[i].bars || this.datasets[i].segments; + dataIndex = indexOf(dataArray, ChartElements[0]); + if (dataIndex !== -1){ + break; + } + } + var tooltipLabels = [], + tooltipColors = [], + medianPosition = (function(index) { + + // Get all the points at that particular index + var Elements = [], + dataCollection, + xPositions = [], + yPositions = [], + xMax, + yMax, + xMin, + yMin; + helpers.each(this.datasets, function(dataset){ + dataCollection = dataset.points || dataset.bars || dataset.segments; + if (dataCollection[dataIndex] && dataCollection[dataIndex].hasValue()){ + Elements.push(dataCollection[dataIndex]); + } + }); + + helpers.each(Elements, function(element) { + xPositions.push(element.x); + yPositions.push(element.y); + + + //Include any colour information about the element + tooltipLabels.push(helpers.template(this.options.multiTooltipTemplate, element)); + tooltipColors.push({ + fill: element._saved.fillColor || element.fillColor, + stroke: element._saved.strokeColor || element.strokeColor + }); + + }, this); + + yMin = min(yPositions); + yMax = max(yPositions); + + xMin = min(xPositions); + xMax = max(xPositions); + + return { + x: (xMin > this.chart.width/2) ? xMin : xMax, + y: (yMin + yMax)/2 + }; + }).call(this, dataIndex); + + new Chart.MultiTooltip({ + x: medianPosition.x, + y: medianPosition.y, + xPadding: this.options.tooltipXPadding, + yPadding: this.options.tooltipYPadding, + xOffset: this.options.tooltipXOffset, + fillColor: this.options.tooltipFillColor, + textColor: this.options.tooltipFontColor, + fontFamily: this.options.tooltipFontFamily, + fontStyle: this.options.tooltipFontStyle, + fontSize: this.options.tooltipFontSize, + titleTextColor: this.options.tooltipTitleFontColor, + titleFontFamily: this.options.tooltipTitleFontFamily, + titleFontStyle: this.options.tooltipTitleFontStyle, + titleFontSize: this.options.tooltipTitleFontSize, + cornerRadius: this.options.tooltipCornerRadius, + labels: tooltipLabels, + legendColors: tooltipColors, + legendColorBackground : this.options.multiTooltipKeyBackground, + // Added by avokhmin + title: ChartElements[0].tooltipTitle || ChartElements[0].label, + chart: this.chart, + ctx: this.chart.ctx + }).draw(); + + } else { + each(ChartElements, function(Element) { + var tooltipPosition = Element.tooltipPosition(); + new Chart.Tooltip({ + x: Math.round(tooltipPosition.x), + y: Math.round(tooltipPosition.y), + xPadding: this.options.tooltipXPadding, + yPadding: this.options.tooltipYPadding, + fillColor: this.options.tooltipFillColor, + textColor: this.options.tooltipFontColor, + fontFamily: this.options.tooltipFontFamily, + fontStyle: this.options.tooltipFontStyle, + fontSize: this.options.tooltipFontSize, + caretHeight: this.options.tooltipCaretSize, + cornerRadius: this.options.tooltipCornerRadius, + text: template(this.options.tooltipTemplate, Element), + chart: this.chart + }).draw(); + }, this); + } + } + return this; + }, + toBase64Image : function(){ + return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments); + } + }); + + Chart.Type.extend = function(extensions){ + + var parent = this; + + var ChartType = function(){ + return parent.apply(this,arguments); + }; + + //Copy the prototype object of the this class + ChartType.prototype = clone(parent.prototype); + //Now overwrite some of the properties in the base class with the new extensions + extend(ChartType.prototype, extensions); + + ChartType.extend = Chart.Type.extend; + + if (extensions.name || parent.prototype.name){ + + var chartName = extensions.name || parent.prototype.name; + //Assign any potential default values of the new chart type + + //If none are defined, we'll use a clone of the chart type this is being extended from. + //I.e. if we extend a line chart, we'll use the defaults from the line chart if our new chart + //doesn't define some defaults of their own. + + var baseDefaults = (Chart.defaults[parent.prototype.name]) ? clone(Chart.defaults[parent.prototype.name]) : {}; + + Chart.defaults[chartName] = extend(baseDefaults,extensions.defaults); + + Chart.types[chartName] = ChartType; + + //Register this new chart type in the Chart prototype + Chart.prototype[chartName] = function(data,options){ + var config = merge(Chart.defaults.global, Chart.defaults[chartName], options || {}); + return new ChartType(data,config,this); + }; + } else{ + warn("Name not provided for this chart, so it hasn't been registered"); + } + return parent; + }; + + Chart.Element = function(configuration){ + extend(this,configuration); + this.initialize.apply(this,arguments); + this.save(); + }; + extend(Chart.Element.prototype,{ + initialize : function(){}, + restore : function(props){ + if (!props){ + extend(this,this._saved); + } else { + each(props,function(key){ + this[key] = this._saved[key]; + },this); + } + return this; + }, + save : function(){ + this._saved = clone(this); + delete this._saved._saved; + return this; + }, + update : function(newProps){ + each(newProps,function(value,key){ + this._saved[key] = this[key]; + this[key] = value; + },this); + return this; + }, + transition : function(props,ease){ + each(props,function(value,key){ + this[key] = ((value - this._saved[key]) * ease) + this._saved[key]; + },this); + return this; + }, + tooltipPosition : function(){ + return { + x : this.x, + y : this.y + }; + }, + hasValue: function(){ + return isNumber(this.value); + } + }); + + Chart.Element.extend = inherits; + + + Chart.Point = Chart.Element.extend({ + display: true, + inRange: function(chartX,chartY){ + var hitDetectionRange = this.hitDetectionRadius + this.radius; + return ((Math.pow(chartX-this.x, 2)+Math.pow(chartY-this.y, 2)) < Math.pow(hitDetectionRange,2)); + }, + draw : function(){ + if (this.display){ + var ctx = this.ctx; + ctx.beginPath(); + + ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2); + ctx.closePath(); + + ctx.strokeStyle = this.strokeColor; + ctx.lineWidth = this.strokeWidth; + + ctx.fillStyle = this.fillColor; + + ctx.fill(); + ctx.stroke(); + } + + + //Quick debug for bezier curve splining + //Highlights control points and the line between them. + //Handy for dev - stripped in the min version. + + // ctx.save(); + // ctx.fillStyle = "black"; + // ctx.strokeStyle = "black" + // ctx.beginPath(); + // ctx.arc(this.controlPoints.inner.x,this.controlPoints.inner.y, 2, 0, Math.PI*2); + // ctx.fill(); + + // ctx.beginPath(); + // ctx.arc(this.controlPoints.outer.x,this.controlPoints.outer.y, 2, 0, Math.PI*2); + // ctx.fill(); + + // ctx.moveTo(this.controlPoints.inner.x,this.controlPoints.inner.y); + // ctx.lineTo(this.x, this.y); + // ctx.lineTo(this.controlPoints.outer.x,this.controlPoints.outer.y); + // ctx.stroke(); + + // ctx.restore(); + + + + } + }); + + Chart.Arc = Chart.Element.extend({ + inRange : function(chartX,chartY){ + + var pointRelativePosition = helpers.getAngleFromPoint(this, { + x: chartX, + y: chartY + }); + + //Check if within the range of the open/close angle + var betweenAngles = (pointRelativePosition.angle >= this.startAngle && pointRelativePosition.angle <= this.endAngle), + withinRadius = (pointRelativePosition.distance >= this.innerRadius && pointRelativePosition.distance <= this.outerRadius); + + return (betweenAngles && withinRadius); + //Ensure within the outside of the arc centre, but inside arc outer + }, + tooltipPosition : function(){ + var centreAngle = this.startAngle + ((this.endAngle - this.startAngle) / 2), + rangeFromCentre = (this.outerRadius - this.innerRadius) / 2 + this.innerRadius; + return { + x : this.x + (Math.cos(centreAngle) * rangeFromCentre), + y : this.y + (Math.sin(centreAngle) * rangeFromCentre) + }; + }, + draw : function(animationPercent){ + + var easingDecimal = animationPercent || 1; + + var ctx = this.ctx; + + ctx.beginPath(); + + ctx.arc(this.x, this.y, this.outerRadius, this.startAngle, this.endAngle); + + ctx.arc(this.x, this.y, this.innerRadius, this.endAngle, this.startAngle, true); + + ctx.closePath(); + ctx.strokeStyle = this.strokeColor; + ctx.lineWidth = this.strokeWidth; + + ctx.fillStyle = this.fillColor; + + ctx.fill(); + ctx.lineJoin = 'bevel'; + + if (this.showStroke){ + ctx.stroke(); + } + } + }); + + Chart.Rectangle = Chart.Element.extend({ + draw : function(){ + var ctx = this.ctx, + halfWidth = this.width/2, + leftX = this.x - halfWidth, + rightX = this.x + halfWidth, + top = this.base - (this.base - this.y), + halfStroke = this.strokeWidth / 2; + + // Canvas doesn't allow us to stroke inside the width so we can + // adjust the sizes to fit if we're setting a stroke on the line + if (this.showStroke){ + leftX += halfStroke; + rightX -= halfStroke; + top += halfStroke; + } + + ctx.beginPath(); + + ctx.fillStyle = this.fillColor; + ctx.strokeStyle = this.strokeColor; + ctx.lineWidth = this.strokeWidth; + + // It'd be nice to keep this class totally generic to any rectangle + // and simply specify which border to miss out. + ctx.moveTo(leftX, this.base); + ctx.lineTo(leftX, top); + ctx.lineTo(rightX, top); + ctx.lineTo(rightX, this.base); + ctx.fill(); + if (this.showStroke){ + ctx.stroke(); + } + }, + height : function(){ + return this.base - this.y; + }, + inRange : function(chartX,chartY){ + return (chartX >= this.x - this.width/2 && chartX <= this.x + this.width/2) && (chartY >= this.y && chartY <= this.base); + } + }); + + Chart.Tooltip = Chart.Element.extend({ + draw : function(){ + + var ctx = this.chart.ctx; + + ctx.font = fontString(this.fontSize,this.fontStyle,this.fontFamily); + + this.xAlign = "center"; + this.yAlign = "above"; + + //Distance between the actual element.y position and the start of the tooltip caret + var caretPadding = 2; + + var tooltipWidth = ctx.measureText(this.text).width + 2*this.xPadding, + tooltipRectHeight = this.fontSize + 2*this.yPadding, + tooltipHeight = tooltipRectHeight + this.caretHeight + caretPadding; + + if (this.x + tooltipWidth/2 >this.chart.width){ + this.xAlign = "left"; + } else if (this.x - tooltipWidth/2 < 0){ + this.xAlign = "right"; + } + + if (this.y - tooltipHeight < 0){ + this.yAlign = "below"; + } + + + var tooltipX = this.x - tooltipWidth/2, + tooltipY = this.y - tooltipHeight; + + ctx.fillStyle = this.fillColor; + + switch(this.yAlign) + { + case "above": + //Draw a caret above the x/y + ctx.beginPath(); + ctx.moveTo(this.x,this.y - caretPadding); + ctx.lineTo(this.x + this.caretHeight, this.y - (caretPadding + this.caretHeight)); + ctx.lineTo(this.x - this.caretHeight, this.y - (caretPadding + this.caretHeight)); + ctx.closePath(); + ctx.fill(); + break; + case "below": + tooltipY = this.y + caretPadding + this.caretHeight; + //Draw a caret below the x/y + ctx.beginPath(); + ctx.moveTo(this.x, this.y + caretPadding); + ctx.lineTo(this.x + this.caretHeight, this.y + caretPadding + this.caretHeight); + ctx.lineTo(this.x - this.caretHeight, this.y + caretPadding + this.caretHeight); + ctx.closePath(); + ctx.fill(); + break; + } + + switch(this.xAlign) + { + case "left": + tooltipX = this.x - tooltipWidth + (this.cornerRadius + this.caretHeight); + break; + case "right": + tooltipX = this.x - (this.cornerRadius + this.caretHeight); + break; + } + + drawRoundedRectangle(ctx,tooltipX,tooltipY,tooltipWidth,tooltipRectHeight,this.cornerRadius); + + ctx.fill(); + + ctx.fillStyle = this.textColor; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillText(this.text, tooltipX + tooltipWidth/2, tooltipY + tooltipRectHeight/2); + } + }); + + Chart.MultiTooltip = Chart.Element.extend({ + initialize : function(){ + this.font = fontString(this.fontSize,this.fontStyle,this.fontFamily); + + this.titleFont = fontString(this.titleFontSize,this.titleFontStyle,this.titleFontFamily); + + this.height = (this.labels.length * this.fontSize) + ((this.labels.length-1) * (this.fontSize/2)) + (this.yPadding*2) + this.titleFontSize *1.5; + + this.ctx.font = this.titleFont; + + var titleWidth = this.ctx.measureText(this.title).width, + //Label has a legend square as well so account for this. + labelWidth = longestText(this.ctx,this.font,this.labels) + this.fontSize + 3, + longestTextWidth = max([labelWidth,titleWidth]); + + this.width = longestTextWidth + (this.xPadding*2); + + + var halfHeight = this.height/2; + + //Check to ensure the height will fit on the canvas + //The three is to buffer form the very + if (this.y - halfHeight < 0 ){ + this.y = halfHeight; + } else if (this.y + halfHeight > this.chart.height){ + this.y = this.chart.height - halfHeight; + } + + //Decide whether to align left or right based on position on canvas + if (this.x > this.chart.width/2){ + this.x -= this.xOffset + this.width; + } else { + this.x += this.xOffset; + } + + + }, + getLineHeight : function(index){ + var baseLineHeight = this.y - (this.height/2) + this.yPadding, + afterTitleIndex = index-1; + + //If the index is zero, we're getting the title + if (index === 0){ + return baseLineHeight + this.titleFontSize/2; + } else{ + return baseLineHeight + ((this.fontSize*1.5*afterTitleIndex) + this.fontSize/2) + this.titleFontSize * 1.5; + } + + }, + draw : function(){ + drawRoundedRectangle(this.ctx,this.x,this.y - this.height/2,this.width,this.height,this.cornerRadius); + var ctx = this.ctx; + ctx.fillStyle = this.fillColor; + ctx.fill(); + ctx.closePath(); + + ctx.textAlign = "left"; + ctx.textBaseline = "middle"; + ctx.fillStyle = this.titleTextColor; + ctx.font = this.titleFont; + + ctx.fillText(this.title,this.x + this.xPadding, this.getLineHeight(0)); + + ctx.font = this.font; + helpers.each(this.labels,function(label,index){ + ctx.fillStyle = this.textColor; + ctx.fillText(label,this.x + this.xPadding + this.fontSize + 3, this.getLineHeight(index + 1)); + + //A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas) + //ctx.clearRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); + //Instead we'll make a white filled block to put the legendColour palette over. + + ctx.fillStyle = this.legendColorBackground; + ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); + + ctx.fillStyle = this.legendColors[index].fill; + ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); + + + },this); + } + }); + + Chart.Scale = Chart.Element.extend({ + initialize : function(){ + this.fit(); + }, + buildYLabels : function(){ + this.yLabels = []; + + var stepDecimalPlaces = getDecimalPlaces(this.stepValue); + + for (var i=0; i<=this.steps; i++){ + this.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)})); + } + this.yLabelWidth = (this.display && this.showLabels) ? longestText(this.ctx,this.font,this.yLabels) : 0; + }, + addXLabel : function(label){ + this.xLabels.push(label); + this.valuesCount++; + this.fit(); + }, + removeXLabel : function(){ + this.xLabels.shift(); + this.valuesCount--; + this.fit(); + }, + // Fitting loop to rotate x Labels and figure out what fits there, and also calculate how many Y steps to use + fit: function(){ + // First we need the width of the yLabels, assuming the xLabels aren't rotated + + // To do that we need the base line at the top and base of the chart, assuming there is no x label rotation + this.startPoint = (this.display) ? this.fontSize : 0; + this.endPoint = (this.display) ? this.height - (this.fontSize * 1.5) - 5 : this.height; // -5 to pad labels + + // Apply padding settings to the start and end point. + this.startPoint += this.padding; + this.endPoint -= this.padding; + + // Cache the starting height, so can determine if we need to recalculate the scale yAxis + var cachedHeight = this.endPoint - this.startPoint, + cachedYLabelWidth; + + // Build the current yLabels so we have an idea of what size they'll be to start + /* + * This sets what is returned from calculateScaleRange as static properties of this class: + * + this.steps; + this.stepValue; + this.min; + this.max; + * + */ + this.calculateYRange(cachedHeight); + + // With these properties set we can now build the array of yLabels + // and also the width of the largest yLabel + this.buildYLabels(); + + this.calculateXLabelRotation(); + + while((cachedHeight > this.endPoint - this.startPoint)){ + cachedHeight = this.endPoint - this.startPoint; + cachedYLabelWidth = this.yLabelWidth; + + this.calculateYRange(cachedHeight); + this.buildYLabels(); + + // Only go through the xLabel loop again if the yLabel width has changed + if (cachedYLabelWidth < this.yLabelWidth){ + this.calculateXLabelRotation(); + } + } + + }, + calculateXLabelRotation : function(){ + //Get the width of each grid by calculating the difference + //between x offsets between 0 and 1. + + this.ctx.font = this.font; + + var firstWidth = this.ctx.measureText(this.xLabels[0]).width, + lastWidth = this.ctx.measureText(this.xLabels[this.xLabels.length - 1]).width, + firstRotated, + lastRotated; + + + this.xScalePaddingRight = lastWidth/2 + 3; + this.xScalePaddingLeft = (firstWidth/2 > this.yLabelWidth + 10) ? firstWidth/2 : this.yLabelWidth + 10; + + this.xLabelRotation = 0; + if (this.display){ + var originalLabelWidth = longestText(this.ctx,this.font,this.xLabels), + cosRotation, + firstRotatedWidth; + this.xLabelWidth = originalLabelWidth; + //Allow 3 pixels x2 padding either side for label readability + var xGridWidth = Math.floor(this.calculateX(1) - this.calculateX(0)) - 6; + + //Max label rotate should be 90 - also act as a loop counter + while ((this.xLabelWidth > xGridWidth && this.xLabelRotation === 0) || (this.xLabelWidth > xGridWidth && this.xLabelRotation <= 90 && this.xLabelRotation > 0)){ + cosRotation = Math.cos(toRadians(this.xLabelRotation)); + + firstRotated = cosRotation * firstWidth; + lastRotated = cosRotation * lastWidth; + + // We're right aligning the text now. + if (firstRotated + this.fontSize / 2 > this.yLabelWidth + 8){ + this.xScalePaddingLeft = firstRotated + this.fontSize / 2; + } + this.xScalePaddingRight = this.fontSize/2; + + + this.xLabelRotation++; + this.xLabelWidth = cosRotation * originalLabelWidth; + + } + if (this.xLabelRotation > 0){ + this.endPoint -= Math.sin(toRadians(this.xLabelRotation))*originalLabelWidth + 3; + } + } + else{ + this.xLabelWidth = 0; + this.xScalePaddingRight = this.padding; + this.xScalePaddingLeft = this.padding; + } + + }, + // Needs to be overidden in each Chart type + // Otherwise we need to pass all the data into the scale class + calculateYRange: noop, + drawingArea: function(){ + return this.startPoint - this.endPoint; + }, + calculateY : function(value){ + var scalingFactor = this.drawingArea() / (this.min - this.max); + return this.endPoint - (scalingFactor * (value - this.min)); + }, + calculateX : function(index){ + var isRotated = (this.xLabelRotation > 0), + // innerWidth = (this.offsetGridLines) ? this.width - offsetLeft - this.padding : this.width - (offsetLeft + halfLabelWidth * 2) - this.padding, + innerWidth = this.width - (this.xScalePaddingLeft + this.xScalePaddingRight), + valueWidth = innerWidth/(this.valuesCount - ((this.offsetGridLines) ? 0 : 1)), + valueOffset = (valueWidth * index) + this.xScalePaddingLeft; + + if (this.offsetGridLines){ + valueOffset += (valueWidth/2); + } + + return Math.round(valueOffset); + }, + update : function(newProps){ + helpers.extend(this, newProps); + this.fit(); + }, + draw : function(){ + var ctx = this.ctx, + yLabelGap = (this.endPoint - this.startPoint) / this.steps, + xStart = Math.round(this.xScalePaddingLeft); + if (this.display){ + ctx.fillStyle = this.textColor; + ctx.font = this.font; + each(this.yLabels,function(labelString,index){ + var yLabelCenter = this.endPoint - (yLabelGap * index), + linePositionY = Math.round(yLabelCenter); + + ctx.textAlign = "right"; + ctx.textBaseline = "middle"; + if (this.showLabels){ + ctx.fillText(labelString,xStart - 10,yLabelCenter); + } + ctx.beginPath(); + if (index > 0){ + // This is a grid line in the centre, so drop that + ctx.lineWidth = this.gridLineWidth; + ctx.strokeStyle = this.gridLineColor; + } else { + // This is the first line on the scale + ctx.lineWidth = this.lineWidth; + ctx.strokeStyle = this.lineColor; + } + + linePositionY += helpers.aliasPixel(ctx.lineWidth); + + ctx.moveTo(xStart, linePositionY); + ctx.lineTo(this.width, linePositionY); + ctx.stroke(); + ctx.closePath(); + + ctx.lineWidth = this.lineWidth; + ctx.strokeStyle = this.lineColor; + ctx.beginPath(); + ctx.moveTo(xStart - 5, linePositionY); + ctx.lineTo(xStart, linePositionY); + ctx.stroke(); + ctx.closePath(); + + },this); + + each(this.xLabels,function(label,index){ + var xPos = this.calculateX(index) + aliasPixel(this.lineWidth), + // Check to see if line/bar here and decide where to place the line + linePos = this.calculateX(index - (this.offsetGridLines ? 0.5 : 0)) + aliasPixel(this.lineWidth), + isRotated = (this.xLabelRotation > 0); + + ctx.beginPath(); + + if (index > 0){ + // This is a grid line in the centre, so drop that + ctx.lineWidth = this.gridLineWidth; + ctx.strokeStyle = this.gridLineColor; + } else { + // This is the first line on the scale + ctx.lineWidth = this.lineWidth; + ctx.strokeStyle = this.lineColor; + } + ctx.moveTo(linePos,this.endPoint); + ctx.lineTo(linePos,this.startPoint - 3); + ctx.stroke(); + ctx.closePath(); + + + ctx.lineWidth = this.lineWidth; + ctx.strokeStyle = this.lineColor; + + + // Small lines at the bottom of the base grid line + ctx.beginPath(); + ctx.moveTo(linePos,this.endPoint); + ctx.lineTo(linePos,this.endPoint + 5); + ctx.stroke(); + ctx.closePath(); + + ctx.save(); + ctx.translate(xPos,(isRotated) ? this.endPoint + 12 : this.endPoint + 8); + ctx.rotate(toRadians(this.xLabelRotation)*-1); + ctx.font = this.font; + ctx.textAlign = (isRotated) ? "right" : "center"; + ctx.textBaseline = (isRotated) ? "middle" : "top"; + ctx.fillText(label, 0, 0); + ctx.restore(); + },this); + + } + } + + }); + + Chart.RadialScale = Chart.Element.extend({ + initialize: function(){ + this.size = min([this.height, this.width]); + this.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2); + }, + calculateCenterOffset: function(value){ + // Take into account half font size + the yPadding of the top value + var scalingFactor = this.drawingArea / (this.max - this.min); + + return (value - this.min) * scalingFactor; + }, + update : function(){ + if (!this.lineArc){ + this.setScaleSize(); + } else { + this.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2); + } + this.buildYLabels(); + }, + buildYLabels: function(){ + this.yLabels = []; + + var stepDecimalPlaces = getDecimalPlaces(this.stepValue); + + for (var i=0; i<=this.steps; i++){ + this.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)})); + } + }, + getCircumference : function(){ + return ((Math.PI*2) / this.valuesCount); + }, + setScaleSize: function(){ + /* + * Right, this is really confusing and there is a lot of maths going on here + * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 + * + * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif + * + * Solution: + * + * We assume the radius of the polygon is half the size of the canvas at first + * at each index we check if the text overlaps. + * + * Where it does, we store that angle and that index. + * + * After finding the largest index and angle we calculate how much we need to remove + * from the shape radius to move the point inwards by that x. + * + * We average the left and right distances to get the maximum shape radius that can fit in the box + * along with labels. + * + * Once we have that, we can find the centre point for the chart, by taking the x text protrusion + * on each side, removing that from the size, halving it and adding the left x protrusion width. + * + * This will mean we have a shape fitted to the canvas, as large as it can be with the labels + * and position it in the most space efficient manner + * + * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif + */ + + + // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. + // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points + var largestPossibleRadius = min([(this.height/2 - this.pointLabelFontSize - 5), this.width/2]), + pointPosition, + i, + textWidth, + halfTextWidth, + furthestRight = this.width, + furthestRightIndex, + furthestRightAngle, + furthestLeft = 0, + furthestLeftIndex, + furthestLeftAngle, + xProtrusionLeft, + xProtrusionRight, + radiusReductionRight, + radiusReductionLeft, + maxWidthRadius; + this.ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily); + for (i=0;i furthestRight) { + furthestRight = pointPosition.x + halfTextWidth; + furthestRightIndex = i; + } + if (pointPosition.x - halfTextWidth < furthestLeft) { + furthestLeft = pointPosition.x - halfTextWidth; + furthestLeftIndex = i; + } + } + else if (i < this.valuesCount/2) { + // Less than half the values means we'll left align the text + if (pointPosition.x + textWidth > furthestRight) { + furthestRight = pointPosition.x + textWidth; + furthestRightIndex = i; + } + } + else if (i > this.valuesCount/2){ + // More than half the values means we'll right align the text + if (pointPosition.x - textWidth < furthestLeft) { + furthestLeft = pointPosition.x - textWidth; + furthestLeftIndex = i; + } + } + } + + xProtrusionLeft = furthestLeft; + + xProtrusionRight = Math.ceil(furthestRight - this.width); + + furthestRightAngle = this.getIndexAngle(furthestRightIndex); + + furthestLeftAngle = this.getIndexAngle(furthestLeftIndex); + + radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI/2); + + radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI/2); + + // Ensure we actually need to reduce the size of the chart + radiusReductionRight = (isNumber(radiusReductionRight)) ? radiusReductionRight : 0; + radiusReductionLeft = (isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0; + + this.drawingArea = largestPossibleRadius - (radiusReductionLeft + radiusReductionRight)/2; + + //this.drawingArea = min([maxWidthRadius, (this.height - (2 * (this.pointLabelFontSize + 5)))/2]) + this.setCenterPoint(radiusReductionLeft, radiusReductionRight); + + }, + setCenterPoint: function(leftMovement, rightMovement){ + + var maxRight = this.width - rightMovement - this.drawingArea, + maxLeft = leftMovement + this.drawingArea; + + this.xCenter = (maxLeft + maxRight)/2; + // Always vertically in the centre as the text height doesn't change + this.yCenter = (this.height/2); + }, + + getIndexAngle : function(index){ + var angleMultiplier = (Math.PI * 2) / this.valuesCount; + // Start from the top instead of right, so remove a quarter of the circle + + return index * angleMultiplier - (Math.PI/2); + }, + getPointPosition : function(index, distanceFromCenter){ + var thisAngle = this.getIndexAngle(index); + return { + x : (Math.cos(thisAngle) * distanceFromCenter) + this.xCenter, + y : (Math.sin(thisAngle) * distanceFromCenter) + this.yCenter + }; + }, + draw: function(){ + if (this.display){ + var ctx = this.ctx; + each(this.yLabels, function(label, index){ + // Don't draw a centre value + if (index > 0){ + var yCenterOffset = index * (this.drawingArea/this.steps), + yHeight = this.yCenter - yCenterOffset, + pointPosition; + + // Draw circular lines around the scale + if (this.lineWidth > 0){ + ctx.strokeStyle = this.lineColor; + ctx.lineWidth = this.lineWidth; + + if(this.lineArc){ + ctx.beginPath(); + ctx.arc(this.xCenter, this.yCenter, yCenterOffset, 0, Math.PI*2); + ctx.closePath(); + ctx.stroke(); + } else{ + ctx.beginPath(); + for (var i=0;i= 0; i--) { + if (this.angleLineWidth > 0){ + var outerPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max)); + ctx.beginPath(); + ctx.moveTo(this.xCenter, this.yCenter); + ctx.lineTo(outerPosition.x, outerPosition.y); + ctx.stroke(); + ctx.closePath(); + } + // Extra 3px out for some label spacing + var pointLabelPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max) + 5); + ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily); + ctx.fillStyle = this.pointLabelFontColor; + + var labelsCount = this.labels.length, + halfLabelsCount = this.labels.length/2, + quarterLabelsCount = halfLabelsCount/2, + upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount), + exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount); + if (i === 0){ + ctx.textAlign = 'center'; + } else if(i === halfLabelsCount){ + ctx.textAlign = 'center'; + } else if (i < halfLabelsCount){ + ctx.textAlign = 'left'; + } else { + ctx.textAlign = 'right'; + } + + // Set the correct text baseline based on outer positioning + if (exactQuarter){ + ctx.textBaseline = 'middle'; + } else if (upperHalf){ + ctx.textBaseline = 'bottom'; + } else { + ctx.textBaseline = 'top'; + } + + ctx.fillText(this.labels[i], pointLabelPosition.x, pointLabelPosition.y); + } + } + } + } + }); + + // Attach global event to resize each chart instance when the browser resizes + helpers.addEvent(window, "resize", (function(){ + // Basic debounce of resize function so it doesn't hurt performance when resizing browser. + var timeout; + return function(){ + clearTimeout(timeout); + timeout = setTimeout(function(){ + each(Chart.instances,function(instance){ + // If the responsive flag is set in the chart instance config + // Cascade the resize event down to the chart. + if (instance.options.responsive){ + instance.resize(instance.render, true); + } + }); + }, 50); + }; + })()); + + + if (amd) { + define(function(){ + return Chart; + }); + } else if (typeof module === 'object' && module.exports) { + module.exports = Chart; + } + + root.Chart = Chart; + + Chart.noConflict = function(){ + root.Chart = previous; + return Chart; + }; + +}).call(this); + +(function(){ + "use strict"; + + var root = this, + Chart = root.Chart, + helpers = Chart.helpers; + + + var defaultConfig = { + //Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value + scaleBeginAtZero : true, + + //Boolean - Whether grid lines are shown across the chart + scaleShowGridLines : true, + + //String - Colour of the grid lines + scaleGridLineColor : "rgba(0,0,0,.05)", + + //Number - Width of the grid lines + scaleGridLineWidth : 1, + + //Boolean - If there is a stroke on each bar + barShowStroke : true, + + //Number - Pixel width of the bar stroke + barStrokeWidth : 2, + + //Number - Spacing between each of the X value sets + barValueSpacing : 5, + + //Number - Spacing between data sets within X values + barDatasetSpacing : 1, + + //String - A legend template + legendTemplate : "
    -legend\"><% for (var i=0; i
  • \"><%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>
" + + }; + + + Chart.Type.extend({ + name: "Bar", + defaults : defaultConfig, + initialize: function(data){ + + //Expose options as a scope variable here so we can access it in the ScaleClass + var options = this.options; + + this.ScaleClass = Chart.Scale.extend({ + offsetGridLines : true, + calculateBarX : function(datasetCount, datasetIndex, barIndex){ + //Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar + var xWidth = this.calculateBaseWidth(), + xAbsolute = this.calculateX(barIndex) - (xWidth/2), + barWidth = this.calculateBarWidth(datasetCount); + + return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth/2; + }, + calculateBaseWidth : function(){ + return (this.calculateX(1) - this.calculateX(0)) - (2*options.barValueSpacing); + }, + calculateBarWidth : function(datasetCount){ + //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset + var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing); + + return (baseWidth / datasetCount); + } + }); + + this.datasets = []; + + //Set up tooltip events on the chart + if (this.options.showTooltips){ + helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ + var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : []; + + this.eachBars(function(bar){ + bar.restore(['fillColor', 'strokeColor']); + }); + helpers.each(activeBars, function(activeBar){ + activeBar.fillColor = activeBar.highlightFill; + activeBar.strokeColor = activeBar.highlightStroke; + }); + this.showTooltip(activeBars); + }); + } + + //Declare the extension of the default point, to cater for the options passed in to the constructor + this.BarClass = Chart.Rectangle.extend({ + strokeWidth : this.options.barStrokeWidth, + showStroke : this.options.barShowStroke, + ctx : this.chart.ctx + }); + + //Iterate through each of the datasets, and build this into a property of the chart + helpers.each(data.datasets,function(dataset,datasetIndex){ + + var datasetObject = { + label : dataset.label || null, + fillColor : dataset.fillColor, + strokeColor : dataset.strokeColor, + bars : [] + }; + + this.datasets.push(datasetObject); + + helpers.each(dataset.data,function(dataPoint,index){ + //Add a new point for each piece of data, passing any required data to draw. + datasetObject.bars.push(new this.BarClass({ + value : dataPoint, + label : data.labels[index], + datasetLabel: dataset.label, + strokeColor : dataset.strokeColor, + fillColor : dataset.fillColor, + highlightFill : dataset.highlightFill || dataset.fillColor, + highlightStroke : dataset.highlightStroke || dataset.strokeColor + })); + },this); + + },this); + + this.buildScale(data.labels); + + this.BarClass.prototype.base = this.scale.endPoint; + + this.eachBars(function(bar, index, datasetIndex){ + helpers.extend(bar, { + width : this.scale.calculateBarWidth(this.datasets.length), + x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index), + y: this.scale.endPoint + }); + bar.save(); + }, this); + + this.render(); + }, + update : function(){ + this.scale.update(); + // Reset any highlight colours before updating. + helpers.each(this.activeElements, function(activeElement){ + activeElement.restore(['fillColor', 'strokeColor']); + }); + + this.eachBars(function(bar){ + bar.save(); + }); + this.render(); + }, + eachBars : function(callback){ + helpers.each(this.datasets,function(dataset, datasetIndex){ + helpers.each(dataset.bars, callback, this, datasetIndex); + },this); + }, + getBarsAtEvent : function(e){ + var barsArray = [], + eventPosition = helpers.getRelativePosition(e), + datasetIterator = function(dataset){ + barsArray.push(dataset.bars[barIndex]); + }, + barIndex; + + for (var datasetIndex = 0; datasetIndex < this.datasets.length; datasetIndex++) { + for (barIndex = 0; barIndex < this.datasets[datasetIndex].bars.length; barIndex++) { + if (this.datasets[datasetIndex].bars[barIndex].inRange(eventPosition.x,eventPosition.y)){ + helpers.each(this.datasets, datasetIterator); + return barsArray; + } + } + } + + return barsArray; + }, + buildScale : function(labels){ + var self = this; + + var dataTotal = function(){ + var values = []; + self.eachBars(function(bar){ + values.push(bar.value); + }); + return values; + }; + + var scaleOptions = { + templateString : this.options.scaleLabel, + height : this.chart.height, + width : this.chart.width, + ctx : this.chart.ctx, + textColor : this.options.scaleFontColor, + fontSize : this.options.scaleFontSize, + fontStyle : this.options.scaleFontStyle, + fontFamily : this.options.scaleFontFamily, + valuesCount : labels.length, + beginAtZero : this.options.scaleBeginAtZero, + integersOnly : this.options.scaleIntegersOnly, + calculateYRange: function(currentHeight){ + var updatedRanges = helpers.calculateScaleRange( + dataTotal(), + currentHeight, + this.fontSize, + this.beginAtZero, + this.integersOnly + ); + helpers.extend(this, updatedRanges); + }, + xLabels : labels, + font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily), + lineWidth : this.options.scaleLineWidth, + lineColor : this.options.scaleLineColor, + gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0, + gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)", + padding : (this.options.showScale) ? 0 : (this.options.barShowStroke) ? this.options.barStrokeWidth : 0, + showLabels : this.options.scaleShowLabels, + display : this.options.showScale + }; + + if (this.options.scaleOverride){ + helpers.extend(scaleOptions, { + calculateYRange: helpers.noop, + steps: this.options.scaleSteps, + stepValue: this.options.scaleStepWidth, + min: this.options.scaleStartValue, + max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) + }); + } + + this.scale = new this.ScaleClass(scaleOptions); + }, + addData : function(valuesArray,label){ + //Map the values array for each of the datasets + helpers.each(valuesArray,function(value,datasetIndex){ + //Add a new point for each piece of data, passing any required data to draw. + this.datasets[datasetIndex].bars.push(new this.BarClass({ + value : value, + label : label, + x: this.scale.calculateBarX(this.datasets.length, datasetIndex, this.scale.valuesCount+1), + y: this.scale.endPoint, + width : this.scale.calculateBarWidth(this.datasets.length), + base : this.scale.endPoint, + strokeColor : this.datasets[datasetIndex].strokeColor, + fillColor : this.datasets[datasetIndex].fillColor + })); + },this); + + this.scale.addXLabel(label); + //Then re-render the chart. + this.update(); + }, + removeData : function(){ + this.scale.removeXLabel(); + //Then re-render the chart. + helpers.each(this.datasets,function(dataset){ + dataset.bars.shift(); + },this); + this.update(); + }, + reflow : function(){ + helpers.extend(this.BarClass.prototype,{ + y: this.scale.endPoint, + base : this.scale.endPoint + }); + var newScaleProps = helpers.extend({ + height : this.chart.height, + width : this.chart.width + }); + this.scale.update(newScaleProps); + }, + draw : function(ease){ + var easingDecimal = ease || 1; + this.clear(); + + var ctx = this.chart.ctx; + + this.scale.draw(easingDecimal); + + //Draw all the bars for each dataset + helpers.each(this.datasets,function(dataset,datasetIndex){ + helpers.each(dataset.bars,function(bar,index){ + if (bar.hasValue()){ + bar.base = this.scale.endPoint; + //Transition then draw + bar.transition({ + x : this.scale.calculateBarX(this.datasets.length, datasetIndex, index), + y : this.scale.calculateY(bar.value), + width : this.scale.calculateBarWidth(this.datasets.length) + }, easingDecimal).draw(); + } + },this); + + },this); + } + }); + + +}).call(this); +(function(){ + "use strict"; + + var root = this, + Chart = root.Chart, + //Cache a local reference to Chart.helpers + helpers = Chart.helpers; + + var defaultConfig = { + //Boolean - Whether we should show a stroke on each segment + segmentShowStroke : true, + + //String - The colour of each segment stroke + segmentStrokeColor : "#fff", + + //Number - The width of each segment stroke + segmentStrokeWidth : 2, + + //The percentage of the chart that we cut out of the middle. + percentageInnerCutout : 50, + + //Number - Amount of animation steps + animationSteps : 100, + + //String - Animation easing effect + animationEasing : "easeOutBounce", + + //Boolean - Whether we animate the rotation of the Doughnut + animateRotate : true, + + //Boolean - Whether we animate scaling the Doughnut from the centre + animateScale : false, + + //String - A legend template + legendTemplate : "
    -legend\"><% for (var i=0; i
  • \"><%if(segments[i].label){%><%=segments[i].label%><%}%>
  • <%}%>
" + + }; + + + Chart.Type.extend({ + //Passing in a name registers this chart in the Chart namespace + name: "Doughnut", + //Providing a defaults will also register the deafults in the chart namespace + defaults : defaultConfig, + //Initialize is fired when the chart is initialized - Data is passed in as a parameter + //Config is automatically merged by the core of Chart.js, and is available at this.options + initialize: function(data){ + + //Declare segments as a static property to prevent inheriting across the Chart type prototype + this.segments = []; + this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2; + + this.SegmentArc = Chart.Arc.extend({ + ctx : this.chart.ctx, + x : this.chart.width/2, + y : this.chart.height/2 + }); + + //Set up tooltip events on the chart + if (this.options.showTooltips){ + helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ + var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : []; + + helpers.each(this.segments,function(segment){ + segment.restore(["fillColor"]); + }); + helpers.each(activeSegments,function(activeSegment){ + activeSegment.fillColor = activeSegment.highlightColor; + }); + this.showTooltip(activeSegments); + }); + } + this.calculateTotal(data); + + helpers.each(data,function(datapoint, index){ + this.addData(datapoint, index, true); + },this); + + this.render(); + }, + getSegmentsAtEvent : function(e){ + var segmentsArray = []; + + var location = helpers.getRelativePosition(e); + + helpers.each(this.segments,function(segment){ + if (segment.inRange(location.x,location.y)) segmentsArray.push(segment); + },this); + return segmentsArray; + }, + addData : function(segment, atIndex, silent){ + var index = atIndex || this.segments.length; + this.segments.splice(index, 0, new this.SegmentArc({ + value : segment.value, + outerRadius : (this.options.animateScale) ? 0 : this.outerRadius, + innerRadius : (this.options.animateScale) ? 0 : (this.outerRadius/100) * this.options.percentageInnerCutout, + fillColor : segment.color, + highlightColor : segment.highlight || segment.color, + showStroke : this.options.segmentShowStroke, + strokeWidth : this.options.segmentStrokeWidth, + strokeColor : this.options.segmentStrokeColor, + startAngle : Math.PI * 1.5, + circumference : (this.options.animateRotate) ? 0 : this.calculateCircumference(segment.value), + label : segment.label + })); + if (!silent){ + this.reflow(); + this.update(); + } + }, + calculateCircumference : function(value){ + return (Math.PI*2)*(value / this.total); + }, + calculateTotal : function(data){ + this.total = 0; + helpers.each(data,function(segment){ + this.total += segment.value; + },this); + }, + update : function(){ + this.calculateTotal(this.segments); + + // Reset any highlight colours before updating. + helpers.each(this.activeElements, function(activeElement){ + activeElement.restore(['fillColor']); + }); + + helpers.each(this.segments,function(segment){ + segment.save(); + }); + this.render(); + }, + + removeData: function(atIndex){ + var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1; + this.segments.splice(indexToDelete, 1); + this.reflow(); + this.update(); + }, + + reflow : function(){ + helpers.extend(this.SegmentArc.prototype,{ + x : this.chart.width/2, + y : this.chart.height/2 + }); + this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2; + helpers.each(this.segments, function(segment){ + segment.update({ + outerRadius : this.outerRadius, + innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout + }); + }, this); + }, + draw : function(easeDecimal){ + var animDecimal = (easeDecimal) ? easeDecimal : 1; + this.clear(); + helpers.each(this.segments,function(segment,index){ + segment.transition({ + circumference : this.calculateCircumference(segment.value), + outerRadius : this.outerRadius, + innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout + },animDecimal); + + segment.endAngle = segment.startAngle + segment.circumference; + + segment.draw(); + if (index === 0){ + segment.startAngle = Math.PI * 1.5; + } + //Check to see if it's the last segment, if not get the next and update the start angle + if (index < this.segments.length-1){ + this.segments[index+1].startAngle = segment.endAngle; + } + },this); + + } + }); + + Chart.types.Doughnut.extend({ + name : "Pie", + defaults : helpers.merge(defaultConfig,{percentageInnerCutout : 0}) + }); + +}).call(this); +(function(){ + "use strict"; + + var root = this, + Chart = root.Chart, + helpers = Chart.helpers; + + var defaultConfig = { + + ///Boolean - Whether grid lines are shown across the chart + scaleShowGridLines : true, + + //String - Colour of the grid lines + scaleGridLineColor : "rgba(0,0,0,.05)", + + //Number - Width of the grid lines + scaleGridLineWidth : 1, + + //Boolean - Whether the line is curved between points + bezierCurve : true, + + //Number - Tension of the bezier curve between points + bezierCurveTension : 0.4, + + //Boolean - Whether to show a dot for each point + pointDot : true, + + //Number - Radius of each point dot in pixels + pointDotRadius : 4, + + //Number - Pixel width of point dot stroke + pointDotStrokeWidth : 1, + + //Number - amount extra to add to the radius to cater for hit detection outside the drawn point + pointHitDetectionRadius : 20, + + //Boolean - Whether to show a stroke for datasets + datasetStroke : true, + + //Number - Pixel width of dataset stroke + datasetStrokeWidth : 2, + + //Boolean - Whether to fill the dataset with a colour + datasetFill : true, + + //String - A legend template + legendTemplate : "
    -legend\"><% for (var i=0; i
  • \"><%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>
" + + }; + + + Chart.Type.extend({ + name: "Line", + defaults : defaultConfig, + initialize: function(data){ + //Declare the extension of the default point, to cater for the options passed in to the constructor + this.PointClass = Chart.Point.extend({ + strokeWidth : this.options.pointDotStrokeWidth, + radius : this.options.pointDotRadius, + display: this.options.pointDot, + hitDetectionRadius : this.options.pointHitDetectionRadius, + ctx : this.chart.ctx, + inRange : function(mouseX){ + return (Math.pow(mouseX-this.x, 2) < Math.pow(this.radius + this.hitDetectionRadius,2)); + } + }); + + this.datasets = []; + + //Set up tooltip events on the chart + if (this.options.showTooltips){ + helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ + var activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : []; + this.eachPoints(function(point){ + point.restore(['fillColor', 'strokeColor']); + }); + helpers.each(activePoints, function(activePoint){ + activePoint.fillColor = activePoint.highlightFill; + activePoint.strokeColor = activePoint.highlightStroke; + }); + this.showTooltip(activePoints); + }); + } + + //Iterate through each of the datasets, and build this into a property of the chart + helpers.each(data.datasets,function(dataset){ + + var datasetObject = { + label : dataset.label || null, + fillColor : dataset.fillColor, + strokeColor : dataset.strokeColor, + pointColor : dataset.pointColor, + pointStrokeColor : dataset.pointStrokeColor, + points : [] + }; + + this.datasets.push(datasetObject); + + + helpers.each(dataset.data,function(dataPoint,index){ + //Add a new point for each piece of data, passing any required data to draw. + datasetObject.points.push(new this.PointClass({ + value : dataPoint, + label : data.labels[index], + // Added by avokhmin + tooltipTitle : data.tooltipTitles[index], + datasetLabel: dataset.label, + strokeColor : dataset.pointStrokeColor, + fillColor : dataset.pointColor, + highlightFill : dataset.pointHighlightFill || dataset.pointColor, + highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor + })); + },this); + + this.buildScale(data.labels); + + + this.eachPoints(function(point, index){ + helpers.extend(point, { + x: this.scale.calculateX(index), + y: this.scale.endPoint + }); + point.save(); + }, this); + + },this); + + + this.render(); + }, + update : function(){ + this.scale.update(); + // Reset any highlight colours before updating. + helpers.each(this.activeElements, function(activeElement){ + activeElement.restore(['fillColor', 'strokeColor']); + }); + this.eachPoints(function(point){ + point.save(); + }); + this.render(); + }, + eachPoints : function(callback){ + helpers.each(this.datasets,function(dataset){ + helpers.each(dataset.points,callback,this); + },this); + }, + getPointsAtEvent : function(e){ + var pointsArray = [], + eventPosition = helpers.getRelativePosition(e); + helpers.each(this.datasets,function(dataset){ + helpers.each(dataset.points,function(point){ + if (point.inRange(eventPosition.x,eventPosition.y)) pointsArray.push(point); + }); + },this); + return pointsArray; + }, + buildScale : function(labels){ + var self = this; + + var dataTotal = function(){ + var values = []; + self.eachPoints(function(point){ + values.push(point.value); + }); + + return values; + }; + + var scaleOptions = { + templateString : this.options.scaleLabel, + height : this.chart.height, + width : this.chart.width, + ctx : this.chart.ctx, + textColor : this.options.scaleFontColor, + fontSize : this.options.scaleFontSize, + fontStyle : this.options.scaleFontStyle, + fontFamily : this.options.scaleFontFamily, + valuesCount : labels.length, + beginAtZero : this.options.scaleBeginAtZero, + integersOnly : this.options.scaleIntegersOnly, + calculateYRange : function(currentHeight){ + var updatedRanges = helpers.calculateScaleRange( + dataTotal(), + currentHeight, + this.fontSize, + this.beginAtZero, + this.integersOnly + ); + helpers.extend(this, updatedRanges); + }, + xLabels : labels, + font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily), + lineWidth : this.options.scaleLineWidth, + lineColor : this.options.scaleLineColor, + gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0, + gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)", + padding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth, + showLabels : this.options.scaleShowLabels, + display : this.options.showScale + }; + + if (this.options.scaleOverride){ + helpers.extend(scaleOptions, { + calculateYRange: helpers.noop, + steps: this.options.scaleSteps, + stepValue: this.options.scaleStepWidth, + min: this.options.scaleStartValue, + max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) + }); + } + + + this.scale = new Chart.Scale(scaleOptions); + }, + addData : function(valuesArray,label){ + //Map the values array for each of the datasets + + helpers.each(valuesArray,function(value,datasetIndex){ + //Add a new point for each piece of data, passing any required data to draw. + this.datasets[datasetIndex].points.push(new this.PointClass({ + value : value, + label : label, + x: this.scale.calculateX(this.scale.valuesCount+1), + y: this.scale.endPoint, + strokeColor : this.datasets[datasetIndex].pointStrokeColor, + fillColor : this.datasets[datasetIndex].pointColor + })); + },this); + + this.scale.addXLabel(label); + //Then re-render the chart. + this.update(); + }, + removeData : function(){ + this.scale.removeXLabel(); + //Then re-render the chart. + helpers.each(this.datasets,function(dataset){ + dataset.points.shift(); + },this); + this.update(); + }, + reflow : function(){ + var newScaleProps = helpers.extend({ + height : this.chart.height, + width : this.chart.width + }); + this.scale.update(newScaleProps); + }, + draw : function(ease){ + var easingDecimal = ease || 1; + this.clear(); + + var ctx = this.chart.ctx; + + // Some helper methods for getting the next/prev points + var hasValue = function(item){ + return item.value !== null; + }, + nextPoint = function(point, collection, index){ + return helpers.findNextWhere(collection, hasValue, index) || point; + }, + previousPoint = function(point, collection, index){ + return helpers.findPreviousWhere(collection, hasValue, index) || point; + }; + + this.scale.draw(easingDecimal); + + + helpers.each(this.datasets,function(dataset){ + var pointsWithValues = helpers.where(dataset.points, hasValue); + + //Transition each point first so that the line and point drawing isn't out of sync + //We can use this extra loop to calculate the control points of this dataset also in this loop + + helpers.each(dataset.points, function(point, index){ + if (point.hasValue()){ + point.transition({ + y : this.scale.calculateY(point.value), + x : this.scale.calculateX(index) + }, easingDecimal); + } + },this); + + + // Control points need to be calculated in a seperate loop, because we need to know the current x/y of the point + // This would cause issues when there is no animation, because the y of the next point would be 0, so beziers would be skewed + if (this.options.bezierCurve){ + helpers.each(pointsWithValues, function(point, index){ + var tension = (index > 0 && index < pointsWithValues.length - 1) ? this.options.bezierCurveTension : 0; + point.controlPoints = helpers.splineCurve( + previousPoint(point, pointsWithValues, index), + point, + nextPoint(point, pointsWithValues, index), + tension + ); + + // Prevent the bezier going outside of the bounds of the graph + + // Cap puter bezier handles to the upper/lower scale bounds + if (point.controlPoints.outer.y > this.scale.endPoint){ + point.controlPoints.outer.y = this.scale.endPoint; + } + else if (point.controlPoints.outer.y < this.scale.startPoint){ + point.controlPoints.outer.y = this.scale.startPoint; + } + + // Cap inner bezier handles to the upper/lower scale bounds + if (point.controlPoints.inner.y > this.scale.endPoint){ + point.controlPoints.inner.y = this.scale.endPoint; + } + else if (point.controlPoints.inner.y < this.scale.startPoint){ + point.controlPoints.inner.y = this.scale.startPoint; + } + },this); + } + + + //Draw the line between all the points + ctx.lineWidth = this.options.datasetStrokeWidth; + ctx.strokeStyle = dataset.strokeColor; + ctx.beginPath(); + + helpers.each(pointsWithValues, function(point, index){ + if (index === 0){ + ctx.moveTo(point.x, point.y); + } + else{ + if(this.options.bezierCurve){ + var previous = previousPoint(point, pointsWithValues, index); + + ctx.bezierCurveTo( + previous.controlPoints.outer.x, + previous.controlPoints.outer.y, + point.controlPoints.inner.x, + point.controlPoints.inner.y, + point.x, + point.y + ); + } + else{ + ctx.lineTo(point.x,point.y); + } + } + }, this); + + ctx.stroke(); + + if (this.options.datasetFill && pointsWithValues.length > 0){ + //Round off the line by going to the base of the chart, back to the start, then fill. + ctx.lineTo(pointsWithValues[pointsWithValues.length - 1].x, this.scale.endPoint); + ctx.lineTo(pointsWithValues[0].x, this.scale.endPoint); + ctx.fillStyle = dataset.fillColor; + ctx.closePath(); + ctx.fill(); + } + + //Now draw the points over the line + //A little inefficient double looping, but better than the line + //lagging behind the point positions + helpers.each(pointsWithValues,function(point){ + point.draw(); + }); + },this); + } + }); + + +}).call(this); +(function(){ + "use strict"; + + var root = this, + Chart = root.Chart, + //Cache a local reference to Chart.helpers + helpers = Chart.helpers; + + var defaultConfig = { + //Boolean - Show a backdrop to the scale label + scaleShowLabelBackdrop : true, + + //String - The colour of the label backdrop + scaleBackdropColor : "rgba(255,255,255,0.75)", + + // Boolean - Whether the scale should begin at zero + scaleBeginAtZero : true, + + //Number - The backdrop padding above & below the label in pixels + scaleBackdropPaddingY : 2, + + //Number - The backdrop padding to the side of the label in pixels + scaleBackdropPaddingX : 2, + + //Boolean - Show line for each value in the scale + scaleShowLine : true, + + //Boolean - Stroke a line around each segment in the chart + segmentShowStroke : true, + + //String - The colour of the stroke on each segement. + segmentStrokeColor : "#fff", + + //Number - The width of the stroke value in pixels + segmentStrokeWidth : 2, + + //Number - Amount of animation steps + animationSteps : 100, + + //String - Animation easing effect. + animationEasing : "easeOutBounce", + + //Boolean - Whether to animate the rotation of the chart + animateRotate : true, + + //Boolean - Whether to animate scaling the chart from the centre + animateScale : false, + + //String - A legend template + legendTemplate : "
    -legend\"><% for (var i=0; i
  • \"><%if(segments[i].label){%><%=segments[i].label%><%}%>
  • <%}%>
" + }; + + + Chart.Type.extend({ + //Passing in a name registers this chart in the Chart namespace + name: "PolarArea", + //Providing a defaults will also register the deafults in the chart namespace + defaults : defaultConfig, + //Initialize is fired when the chart is initialized - Data is passed in as a parameter + //Config is automatically merged by the core of Chart.js, and is available at this.options + initialize: function(data){ + this.segments = []; + //Declare segment class as a chart instance specific class, so it can share props for this instance + this.SegmentArc = Chart.Arc.extend({ + showStroke : this.options.segmentShowStroke, + strokeWidth : this.options.segmentStrokeWidth, + strokeColor : this.options.segmentStrokeColor, + ctx : this.chart.ctx, + innerRadius : 0, + x : this.chart.width/2, + y : this.chart.height/2 + }); + this.scale = new Chart.RadialScale({ + display: this.options.showScale, + fontStyle: this.options.scaleFontStyle, + fontSize: this.options.scaleFontSize, + fontFamily: this.options.scaleFontFamily, + fontColor: this.options.scaleFontColor, + showLabels: this.options.scaleShowLabels, + showLabelBackdrop: this.options.scaleShowLabelBackdrop, + backdropColor: this.options.scaleBackdropColor, + backdropPaddingY : this.options.scaleBackdropPaddingY, + backdropPaddingX: this.options.scaleBackdropPaddingX, + lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0, + lineColor: this.options.scaleLineColor, + lineArc: true, + width: this.chart.width, + height: this.chart.height, + xCenter: this.chart.width/2, + yCenter: this.chart.height/2, + ctx : this.chart.ctx, + templateString: this.options.scaleLabel, + valuesCount: data.length + }); + + this.updateScaleRange(data); + + this.scale.update(); + + helpers.each(data,function(segment,index){ + this.addData(segment,index,true); + },this); + + //Set up tooltip events on the chart + if (this.options.showTooltips){ + helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ + var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : []; + helpers.each(this.segments,function(segment){ + segment.restore(["fillColor"]); + }); + helpers.each(activeSegments,function(activeSegment){ + activeSegment.fillColor = activeSegment.highlightColor; + }); + this.showTooltip(activeSegments); + }); + } + + this.render(); + }, + getSegmentsAtEvent : function(e){ + var segmentsArray = []; + + var location = helpers.getRelativePosition(e); + + helpers.each(this.segments,function(segment){ + if (segment.inRange(location.x,location.y)) segmentsArray.push(segment); + },this); + return segmentsArray; + }, + addData : function(segment, atIndex, silent){ + var index = atIndex || this.segments.length; + + this.segments.splice(index, 0, new this.SegmentArc({ + fillColor: segment.color, + highlightColor: segment.highlight || segment.color, + label: segment.label, + value: segment.value, + outerRadius: (this.options.animateScale) ? 0 : this.scale.calculateCenterOffset(segment.value), + circumference: (this.options.animateRotate) ? 0 : this.scale.getCircumference(), + startAngle: Math.PI * 1.5 + })); + if (!silent){ + this.reflow(); + this.update(); + } + }, + removeData: function(atIndex){ + var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1; + this.segments.splice(indexToDelete, 1); + this.reflow(); + this.update(); + }, + calculateTotal: function(data){ + this.total = 0; + helpers.each(data,function(segment){ + this.total += segment.value; + },this); + this.scale.valuesCount = this.segments.length; + }, + updateScaleRange: function(datapoints){ + var valuesArray = []; + helpers.each(datapoints,function(segment){ + valuesArray.push(segment.value); + }); + + var scaleSizes = (this.options.scaleOverride) ? + { + steps: this.options.scaleSteps, + stepValue: this.options.scaleStepWidth, + min: this.options.scaleStartValue, + max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) + } : + helpers.calculateScaleRange( + valuesArray, + helpers.min([this.chart.width, this.chart.height])/2, + this.options.scaleFontSize, + this.options.scaleBeginAtZero, + this.options.scaleIntegersOnly + ); + + helpers.extend( + this.scale, + scaleSizes, + { + size: helpers.min([this.chart.width, this.chart.height]), + xCenter: this.chart.width/2, + yCenter: this.chart.height/2 + } + ); + + }, + update : function(){ + this.calculateTotal(this.segments); + + helpers.each(this.segments,function(segment){ + segment.save(); + }); + this.render(); + }, + reflow : function(){ + helpers.extend(this.SegmentArc.prototype,{ + x : this.chart.width/2, + y : this.chart.height/2 + }); + this.updateScaleRange(this.segments); + this.scale.update(); + + helpers.extend(this.scale,{ + xCenter: this.chart.width/2, + yCenter: this.chart.height/2 + }); + + helpers.each(this.segments, function(segment){ + segment.update({ + outerRadius : this.scale.calculateCenterOffset(segment.value) + }); + }, this); + + }, + draw : function(ease){ + var easingDecimal = ease || 1; + //Clear & draw the canvas + this.clear(); + helpers.each(this.segments,function(segment, index){ + segment.transition({ + circumference : this.scale.getCircumference(), + outerRadius : this.scale.calculateCenterOffset(segment.value) + },easingDecimal); + + segment.endAngle = segment.startAngle + segment.circumference; + + // If we've removed the first segment we need to set the first one to + // start at the top. + if (index === 0){ + segment.startAngle = Math.PI * 1.5; + } + + //Check to see if it's the last segment, if not get the next and update the start angle + if (index < this.segments.length - 1){ + this.segments[index+1].startAngle = segment.endAngle; + } + segment.draw(); + }, this); + this.scale.draw(); + } + }); + +}).call(this); +(function(){ + "use strict"; + + var root = this, + Chart = root.Chart, + helpers = Chart.helpers; + + + + Chart.Type.extend({ + name: "Radar", + defaults:{ + //Boolean - Whether to show lines for each scale point + scaleShowLine : true, + + //Boolean - Whether we show the angle lines out of the radar + angleShowLineOut : true, + + //Boolean - Whether to show labels on the scale + scaleShowLabels : false, + + // Boolean - Whether the scale should begin at zero + scaleBeginAtZero : true, + + //String - Colour of the angle line + angleLineColor : "rgba(0,0,0,.1)", + + //Number - Pixel width of the angle line + angleLineWidth : 1, + + //String - Point label font declaration + pointLabelFontFamily : "'Arial'", + + //String - Point label font weight + pointLabelFontStyle : "normal", + + //Number - Point label font size in pixels + pointLabelFontSize : 10, + + //String - Point label font colour + pointLabelFontColor : "#666", + + //Boolean - Whether to show a dot for each point + pointDot : true, + + //Number - Radius of each point dot in pixels + pointDotRadius : 3, + + //Number - Pixel width of point dot stroke + pointDotStrokeWidth : 1, + + //Number - amount extra to add to the radius to cater for hit detection outside the drawn point + pointHitDetectionRadius : 20, + + //Boolean - Whether to show a stroke for datasets + datasetStroke : true, + + //Number - Pixel width of dataset stroke + datasetStrokeWidth : 2, + + //Boolean - Whether to fill the dataset with a colour + datasetFill : true, + + //String - A legend template + legendTemplate : "
    -legend\"><% for (var i=0; i
  • \"><%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>
" + + }, + + initialize: function(data){ + this.PointClass = Chart.Point.extend({ + strokeWidth : this.options.pointDotStrokeWidth, + radius : this.options.pointDotRadius, + display: this.options.pointDot, + hitDetectionRadius : this.options.pointHitDetectionRadius, + ctx : this.chart.ctx + }); + + this.datasets = []; + + this.buildScale(data); + + //Set up tooltip events on the chart + if (this.options.showTooltips){ + helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ + var activePointsCollection = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : []; + + this.eachPoints(function(point){ + point.restore(['fillColor', 'strokeColor']); + }); + helpers.each(activePointsCollection, function(activePoint){ + activePoint.fillColor = activePoint.highlightFill; + activePoint.strokeColor = activePoint.highlightStroke; + }); + + this.showTooltip(activePointsCollection); + }); + } + + //Iterate through each of the datasets, and build this into a property of the chart + helpers.each(data.datasets,function(dataset){ + + var datasetObject = { + label: dataset.label || null, + fillColor : dataset.fillColor, + strokeColor : dataset.strokeColor, + pointColor : dataset.pointColor, + pointStrokeColor : dataset.pointStrokeColor, + points : [] + }; + + this.datasets.push(datasetObject); + + helpers.each(dataset.data,function(dataPoint,index){ + //Add a new point for each piece of data, passing any required data to draw. + var pointPosition; + if (!this.scale.animation){ + pointPosition = this.scale.getPointPosition(index, this.scale.calculateCenterOffset(dataPoint)); + } + datasetObject.points.push(new this.PointClass({ + value : dataPoint, + label : data.labels[index], + datasetLabel: dataset.label, + x: (this.options.animation) ? this.scale.xCenter : pointPosition.x, + y: (this.options.animation) ? this.scale.yCenter : pointPosition.y, + strokeColor : dataset.pointStrokeColor, + fillColor : dataset.pointColor, + highlightFill : dataset.pointHighlightFill || dataset.pointColor, + highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor + })); + },this); + + },this); + + this.render(); + }, + eachPoints : function(callback){ + helpers.each(this.datasets,function(dataset){ + helpers.each(dataset.points,callback,this); + },this); + }, + + getPointsAtEvent : function(evt){ + var mousePosition = helpers.getRelativePosition(evt), + fromCenter = helpers.getAngleFromPoint({ + x: this.scale.xCenter, + y: this.scale.yCenter + }, mousePosition); + + var anglePerIndex = (Math.PI * 2) /this.scale.valuesCount, + pointIndex = Math.round((fromCenter.angle - Math.PI * 1.5) / anglePerIndex), + activePointsCollection = []; + + // If we're at the top, make the pointIndex 0 to get the first of the array. + if (pointIndex >= this.scale.valuesCount || pointIndex < 0){ + pointIndex = 0; + } + + if (fromCenter.distance <= this.scale.drawingArea){ + helpers.each(this.datasets, function(dataset){ + activePointsCollection.push(dataset.points[pointIndex]); + }); + } + + return activePointsCollection; + }, + + buildScale : function(data){ + this.scale = new Chart.RadialScale({ + display: this.options.showScale, + fontStyle: this.options.scaleFontStyle, + fontSize: this.options.scaleFontSize, + fontFamily: this.options.scaleFontFamily, + fontColor: this.options.scaleFontColor, + showLabels: this.options.scaleShowLabels, + showLabelBackdrop: this.options.scaleShowLabelBackdrop, + backdropColor: this.options.scaleBackdropColor, + backdropPaddingY : this.options.scaleBackdropPaddingY, + backdropPaddingX: this.options.scaleBackdropPaddingX, + lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0, + lineColor: this.options.scaleLineColor, + angleLineColor : this.options.angleLineColor, + angleLineWidth : (this.options.angleShowLineOut) ? this.options.angleLineWidth : 0, + // Point labels at the edge of each line + pointLabelFontColor : this.options.pointLabelFontColor, + pointLabelFontSize : this.options.pointLabelFontSize, + pointLabelFontFamily : this.options.pointLabelFontFamily, + pointLabelFontStyle : this.options.pointLabelFontStyle, + height : this.chart.height, + width: this.chart.width, + xCenter: this.chart.width/2, + yCenter: this.chart.height/2, + ctx : this.chart.ctx, + templateString: this.options.scaleLabel, + labels: data.labels, + valuesCount: data.datasets[0].data.length + }); + + this.scale.setScaleSize(); + this.updateScaleRange(data.datasets); + this.scale.buildYLabels(); + }, + updateScaleRange: function(datasets){ + var valuesArray = (function(){ + var totalDataArray = []; + helpers.each(datasets,function(dataset){ + if (dataset.data){ + totalDataArray = totalDataArray.concat(dataset.data); + } + else { + helpers.each(dataset.points, function(point){ + totalDataArray.push(point.value); + }); + } + }); + return totalDataArray; + })(); + + + var scaleSizes = (this.options.scaleOverride) ? + { + steps: this.options.scaleSteps, + stepValue: this.options.scaleStepWidth, + min: this.options.scaleStartValue, + max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) + } : + helpers.calculateScaleRange( + valuesArray, + helpers.min([this.chart.width, this.chart.height])/2, + this.options.scaleFontSize, + this.options.scaleBeginAtZero, + this.options.scaleIntegersOnly + ); + + helpers.extend( + this.scale, + scaleSizes + ); + + }, + addData : function(valuesArray,label){ + //Map the values array for each of the datasets + this.scale.valuesCount++; + helpers.each(valuesArray,function(value,datasetIndex){ + var pointPosition = this.scale.getPointPosition(this.scale.valuesCount, this.scale.calculateCenterOffset(value)); + this.datasets[datasetIndex].points.push(new this.PointClass({ + value : value, + label : label, + x: pointPosition.x, + y: pointPosition.y, + strokeColor : this.datasets[datasetIndex].pointStrokeColor, + fillColor : this.datasets[datasetIndex].pointColor + })); + },this); + + this.scale.labels.push(label); + + this.reflow(); + + this.update(); + }, + removeData : function(){ + this.scale.valuesCount--; + this.scale.labels.shift(); + helpers.each(this.datasets,function(dataset){ + dataset.points.shift(); + },this); + this.reflow(); + this.update(); + }, + update : function(){ + this.eachPoints(function(point){ + point.save(); + }); + this.reflow(); + this.render(); + }, + reflow: function(){ + helpers.extend(this.scale, { + width : this.chart.width, + height: this.chart.height, + size : helpers.min([this.chart.width, this.chart.height]), + xCenter: this.chart.width/2, + yCenter: this.chart.height/2 + }); + this.updateScaleRange(this.datasets); + this.scale.setScaleSize(); + this.scale.buildYLabels(); + }, + draw : function(ease){ + var easeDecimal = ease || 1, + ctx = this.chart.ctx; + this.clear(); + this.scale.draw(); + + helpers.each(this.datasets,function(dataset){ + + //Transition each point first so that the line and point drawing isn't out of sync + helpers.each(dataset.points,function(point,index){ + if (point.hasValue()){ + point.transition(this.scale.getPointPosition(index, this.scale.calculateCenterOffset(point.value)), easeDecimal); + } + },this); + + + + //Draw the line between all the points + ctx.lineWidth = this.options.datasetStrokeWidth; + ctx.strokeStyle = dataset.strokeColor; + ctx.beginPath(); + helpers.each(dataset.points,function(point,index){ + if (index === 0){ + ctx.moveTo(point.x,point.y); + } + else{ + ctx.lineTo(point.x,point.y); + } + },this); + ctx.closePath(); + ctx.stroke(); + + ctx.fillStyle = dataset.fillColor; + ctx.fill(); + + //Now draw the points over the line + //A little inefficient double looping, but better than the line + //lagging behind the point positions + helpers.each(dataset.points,function(point){ + if (point.hasValue()){ + point.draw(); + } + }); + + },this); + + } + + }); + + + + + +}).call(this); \ No newline at end of file diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 9327f4fc5..3f16fa64e 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -6,3 +6,4 @@ @import "design/custom"; @import "design/build_lists_monitoring"; @import "design/profile"; +@import "design/statistics"; diff --git a/app/assets/stylesheets/design/custom.scss b/app/assets/stylesheets/design/custom.scss index 637213d62..eb116fe68 100644 --- a/app/assets/stylesheets/design/custom.scss +++ b/app/assets/stylesheets/design/custom.scss @@ -1182,11 +1182,12 @@ div#advisory_search_block div.continue_input { form.mass_build { section.left { margin-left: 10px; - width: 350px; + width: 250px; } section.right { margin-right: 50px; + width: 300px; } input[type="submit"] { margin: 10px 0 10px 0; @@ -2048,7 +2049,7 @@ article .activity .top { } table tbody { - td.build-list-statuses { + td.build-list-statuses, td.package { background: #FFF; .status { diff --git a/app/assets/stylesheets/design/git.scss b/app/assets/stylesheets/design/git.scss index 8428c17b1..76b0bf35d 100644 --- a/app/assets/stylesheets/design/git.scss +++ b/app/assets/stylesheets/design/git.scss @@ -279,3 +279,17 @@ table.blame td.message .message { background: #ECECEC; color: #000; } + +table.diff td.diff-image { + text-align: center; + + span.diff-image { + text-align: center; + margin: 0; + padding: 0; + + img { + margin-top: 5px; + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/design/statistics.scss b/app/assets/stylesheets/design/statistics.scss new file mode 100644 index 000000000..fa0973116 --- /dev/null +++ b/app/assets/stylesheets/design/statistics.scss @@ -0,0 +1,14 @@ +#manage-statistics { + .graph-key-color1 { background-color: #38849e; } + .graph-key-color2 { background-color: #4da944; } + .graph-key-color3 { background-color: #f18049; } + .graph-key-color4 { background-color: #aec7e8; } + .graph-key-color5 { background-color: #ffbb78; } + + .graph-wrapper h3 span { + display: inline-block; + width: 10px; + height: 10px; + margin-left: 15px; + } +} \ No newline at end of file diff --git a/app/controllers/api/v1/advisories_controller.rb b/app/controllers/api/v1/advisories_controller.rb index 3fb5b0dd3..0d555dbf6 100644 --- a/app/controllers/api/v1/advisories_controller.rb +++ b/app/controllers/api/v1/advisories_controller.rb @@ -6,12 +6,13 @@ class Api::V1::AdvisoriesController < Api::V1::BaseController authorize_resource :build_list, only: [:create, :update] def index - @advisories = @advisories.includes(:platforms, :projects). - paginate(paginate_params) + @advisories = @advisories.includes(:platforms, :projects).paginate(paginate_params) + respond_to :json end def show @packages_info = @advisory.fetch_packages_info + respond_to :json end def create diff --git a/app/controllers/api/v1/arches_controller.rb b/app/controllers/api/v1/arches_controller.rb index 813b5d27b..28bc61caf 100644 --- a/app/controllers/api/v1/arches_controller.rb +++ b/app/controllers/api/v1/arches_controller.rb @@ -3,6 +3,7 @@ class Api::V1::ArchesController < Api::V1::BaseController def index @arches = Arch.order(:id).paginate(paginate_params) + respond_to :json end -end \ No newline at end of file +end diff --git a/app/controllers/api/v1/base_controller.rb b/app/controllers/api/v1/base_controller.rb index f06fb29ee..739803f4f 100644 --- a/app/controllers/api/v1/base_controller.rb +++ b/app/controllers/api/v1/base_controller.rb @@ -1,6 +1,6 @@ class Api::V1::BaseController < ApplicationController include PaginateHelper - #respond_to :json + respond_to :json helper_method :member_path diff --git a/app/controllers/api/v1/build_lists_controller.rb b/app/controllers/api/v1/build_lists_controller.rb index efcb5fff4..f1b69b9a1 100644 --- a/app/controllers/api/v1/build_lists_controller.rb +++ b/app/controllers/api/v1/build_lists_controller.rb @@ -1,16 +1,20 @@ class Api::V1::BuildListsController < Api::V1::BaseController - before_filter :authenticate_user! skip_before_filter :authenticate_user!, only: [:show, :index] if APP_CONFIG['anonymous_access'] load_resource :project, only: :index, parent: false load_and_authorize_resource :build_list, only: [:show, :create, :cancel, :publish, :reject_publish, :create_container, :publish_into_testing, :rerun_tests] + def show + respond_to :json + end + def index authorize!(:show, @project) if @project filter = BuildList::Filter.new(@project, current_user, current_ability, params[:filter] || {}) @build_lists = filter.find.includes(:save_to_platform, :project, :user, :arch) @build_lists = @build_lists.recent.paginate(paginate_params) + respond_to :json end def create diff --git a/app/controllers/api/v1/groups_controller.rb b/app/controllers/api/v1/groups_controller.rb index f5c19ef0d..076b485d4 100644 --- a/app/controllers/api/v1/groups_controller.rb +++ b/app/controllers/api/v1/groups_controller.rb @@ -7,15 +7,17 @@ class Api::V1::GroupsController < Api::V1::BaseController def index # accessible_by(current_ability) @groups = current_user.groups.paginate(paginate_params) + respond_to :json end def show + respond_to :json end def members - @members = @group.members. - where('actor_id != ?', @group.owner_id). - order('name').paginate(paginate_params) + @members = @group.members.where('actor_id != ?', @group.owner_id) + .order('name').paginate(paginate_params) + respond_to :json end def update @@ -46,4 +48,4 @@ class Api::V1::GroupsController < Api::V1::BaseController update_member_in_subject @group, :actors end -end \ No newline at end of file +end diff --git a/app/controllers/api/v1/issues_controller.rb b/app/controllers/api/v1/issues_controller.rb index 3d2e40cc6..fa90360b2 100644 --- a/app/controllers/api/v1/issues_controller.rb +++ b/app/controllers/api/v1/issues_controller.rb @@ -1,6 +1,4 @@ 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'] @@ -34,7 +32,11 @@ class Api::V1::IssuesController < Api::V1::BaseController end def show - redirect_to api_v1_project_pull_request_path(@project.id, @issue.serial_id) if @issue.pull_request + if @issue.pull_request + redirect_to api_v1_project_pull_request_path(@project.id, @issue.serial_id) + else + respond_to :json + end end def create @@ -102,7 +104,10 @@ class Api::V1::IssuesController < Api::V1::BaseController @issues = @issues.where('issues.created_at >= to_timestamp(?)', params[:since]) if params[:since] =~ /\A\d+\z/ @issues = @issues.paginate(paginate_params) - render :index + + respond_to do |format| + format.json { render :index } + end end def get_all_project_ids default_project_ids diff --git a/app/controllers/api/v1/jobs_controller.rb b/app/controllers/api/v1/jobs_controller.rb index a40714285..2af2eedbf 100644 --- a/app/controllers/api/v1/jobs_controller.rb +++ b/app/controllers/api/v1/jobs_controller.rb @@ -7,24 +7,20 @@ class Api::V1::JobsController < Api::V1::BaseController before_filter :authenticate_user! def shift - - @build_list = BuildList.next_build if current_user.system? + @build_list = BuildList.next_build(arch_ids, platform_ids) if current_user.system? if @build_list set_builder else - 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.scoped_to_arch(arch_ids). for_status([BuildList::BUILD_PENDING, BuildList::RERUN_TESTS]). - scoped_to_arch(arch_ids). + for_platform(platform_ids). oldest.order(:created_at) - build_lists = build_lists.for_platform(platform_ids) if platform_ids.present? ActiveRecord::Base.transaction do if current_user.system? @build_list ||= build_lists.external_nodes(:everything).first else - @build_list = build_lists.external_nodes(:owned).for_user(current_user).first + @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 end @@ -85,6 +81,20 @@ class Api::V1::JobsController < Api::V1::BaseController protected + def platform_ids + @platform_ids ||= begin + platforms = params[:platforms].to_s.split(',') + platforms.present? ? Platform.where(name: platforms).pluck(:id) : [] + end + end + + def arch_ids + @arch_ids ||= begin + arches = params[:arches].to_s.split(',') + arches.present? ? Arch.where(name: arches).pluck(:id) : [] + end + end + def set_builder return unless @build_list @build_list.builder = current_user diff --git a/app/controllers/api/v1/maintainers_controller.rb b/app/controllers/api/v1/maintainers_controller.rb index c6596ca69..13170768e 100644 --- a/app/controllers/api/v1/maintainers_controller.rb +++ b/app/controllers/api/v1/maintainers_controller.rb @@ -7,5 +7,6 @@ class Api::V1::MaintainersController < Api::V1::BaseController .actual.by_platform(@platform) .like_name(params[:package_name]) .paginate(paginate_params) + respond_to :json end end diff --git a/app/controllers/api/v1/platforms_controller.rb b/app/controllers/api/v1/platforms_controller.rb index 52bc77c46..6b070a1e2 100644 --- a/app/controllers/api/v1/platforms_controller.rb +++ b/app/controllers/api/v1/platforms_controller.rb @@ -16,16 +16,20 @@ class Api::V1::PlatformsController < Api::V1::BaseController end def index - @platforms = @platforms.accessible_by(current_ability, :related). - by_type(params[:type]).paginate(paginate_params) + @platforms = @platforms.accessible_by(current_ability, :related) + .by_type(params[:type]).paginate(paginate_params) + respond_to :json end def show + respond_to :json end def platforms_for_build - @platforms = Platform.main.opened.paginate(paginate_params) - render :index + @platforms = Platform.availables_main_platforms(current_user, current_ability).paginate(paginate_params) + respond_to do |format| + format.json { render :index } + end end def create @@ -44,6 +48,7 @@ class Api::V1::PlatformsController < Api::V1::BaseController def members @members = @platform.members.order('name').paginate(paginate_params) + respond_to :json end def add_member diff --git a/app/controllers/api/v1/product_build_lists_controller.rb b/app/controllers/api/v1/product_build_lists_controller.rb index 6375d1382..b581a43e0 100644 --- a/app/controllers/api/v1/product_build_lists_controller.rb +++ b/app/controllers/api/v1/product_build_lists_controller.rb @@ -13,6 +13,7 @@ class Api::V1::ProductBuildListsController < Api::V1::BaseController end @product_build_lists = @product_build_lists.joins(:product, :project, :arch) @product_build_lists = @product_build_lists.recent.paginate(paginate_params) + respond_to :json end def create @@ -24,6 +25,7 @@ class Api::V1::ProductBuildListsController < Api::V1::BaseController end def show + respond_to :json end def update diff --git a/app/controllers/api/v1/products_controller.rb b/app/controllers/api/v1/products_controller.rb index 1ca2c2bc7..605781c73 100644 --- a/app/controllers/api/v1/products_controller.rb +++ b/app/controllers/api/v1/products_controller.rb @@ -13,6 +13,7 @@ class Api::V1::ProductsController < Api::V1::BaseController end def show + respond_to :json end def destroy diff --git a/app/controllers/api/v1/projects_controller.rb b/app/controllers/api/v1/projects_controller.rb index 94276314d..bf67f75b4 100644 --- a/app/controllers/api/v1/projects_controller.rb +++ b/app/controllers/api/v1/projects_controller.rb @@ -6,8 +6,9 @@ class Api::V1::ProjectsController < Api::V1::BaseController load_and_authorize_resource :project def index - @projects = Project.accessible_by(current_ability, :membered). - paginate(paginate_params) + @projects = Project.accessible_by(current_ability, :membered) + .paginate(paginate_params) + respond_to :json end def get_id @@ -16,13 +17,16 @@ class Api::V1::ProjectsController < Api::V1::BaseController else raise ActiveRecord::RecordNotFound end + respond_to :json end def show + respond_to :json end def refs_list @refs = @project.repo.branches + @project.repo.tags.select{ |t| t.commit } + respond_to :json end def update @@ -48,6 +52,7 @@ class Api::V1::ProjectsController < Api::V1::BaseController def members @members = @project.collaborators.order('uname').paginate(paginate_params) + respond_to :json end def add_member diff --git a/app/controllers/api/v1/pull_requests_controller.rb b/app/controllers/api/v1/pull_requests_controller.rb index 837673b37..9c3054f35 100644 --- a/app/controllers/api/v1/pull_requests_controller.rb +++ b/app/controllers/api/v1/pull_requests_controller.rb @@ -39,6 +39,7 @@ class Api::V1::PullRequestsController < Api::V1::BaseController def show redirect_to api_v1_project_issue_path(@project.id, @issue.serial_id) if @pull.nil? + respond_to :json end def create @@ -95,10 +96,12 @@ class Api::V1::PullRequestsController < Api::V1::BaseController def commits @commits = @pull.repo.commits_between(@pull.to_commit, @pull.from_commit).paginate(paginate_params) + respond_to :json end def files @stats = @pull.diff_stats.zip(@pull.diff).paginate(paginate_params) + respond_to :json end def merge @@ -149,7 +152,10 @@ class Api::V1::PullRequestsController < Api::V1::BaseController @pulls = @pulls.where('issues.created_at >= to_timestamp(?)', params[:since]) if params[:since] =~ /\A\d+\z/ @pulls = @pulls.paginate(paginate_params) - render :index + + respond_to do |format| + format.json { render :index } + end end def get_all_project_ids default_project_ids diff --git a/app/controllers/api/v1/repositories_controller.rb b/app/controllers/api/v1/repositories_controller.rb index 3a53c08dd..353cdb447 100644 --- a/app/controllers/api/v1/repositories_controller.rb +++ b/app/controllers/api/v1/repositories_controller.rb @@ -1,4 +1,5 @@ class Api::V1::RepositoriesController < Api::V1::BaseController + respond_to :csv, only: :packages before_filter :authenticate_user! skip_before_filter :authenticate_user!, only: [:show, :projects] if APP_CONFIG['anonymous_access'] @@ -6,11 +7,12 @@ class Api::V1::RepositoriesController < Api::V1::BaseController load_and_authorize_resource :repository, through: :platform, shallow: true def show + respond_to :json end def projects - @projects = @repository.projects. - recent.paginate(paginate_params) + @projects = @repository.projects.recent.paginate(paginate_params) + respond_to :json end def update @@ -30,6 +32,7 @@ class Api::V1::RepositoriesController < Api::V1::BaseController end def key_pair + respond_to :json end # Only one request per 15 minutes for each platform diff --git a/app/controllers/api/v1/search_controller.rb b/app/controllers/api/v1/search_controller.rb index cc02c6d25..68d2d012a 100644 --- a/app/controllers/api/v1/search_controller.rb +++ b/app/controllers/api/v1/search_controller.rb @@ -5,7 +5,9 @@ class Api::V1::SearchController < Api::V1::BaseController @results = Search.by_term_and_type( params[:query], (params[:type] || 'all'), + current_ability, paginate_params ) + respond_to :json end end \ No newline at end of file diff --git a/app/controllers/api/v1/users_controller.rb b/app/controllers/api/v1/users_controller.rb index fed2c8e22..12cd893a5 100644 --- a/app/controllers/api/v1/users_controller.rb +++ b/app/controllers/api/v1/users_controller.rb @@ -7,10 +7,13 @@ class Api::V1::UsersController < Api::V1::BaseController def show @user = User.opened.find params[:id] # dont show system users + respond_to :json end def show_current_user - render :show + respond_to do |format| + format.json { render :show } + end end def update @@ -35,7 +38,7 @@ class Api::V1::UsersController < Api::V1::BaseController render_json_response @user, error_message(@user.notifier, 'User notification settings have not been updated'), 422 end else - render :notifiers + respond_to :json end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3d40068c3..7d75399bc 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,6 +1,8 @@ class ApplicationController < ActionController::Base - AIRBRAKE_IGNORE = [ActionController::InvalidAuthenticityToken, - AbstractController::ActionNotFound] + AIRBRAKE_IGNORE = [ + ActionController::InvalidAuthenticityToken, + AbstractController::ActionNotFound + ] protect_from_forgery @@ -21,6 +23,7 @@ class ApplicationController < ActionController::Base rescue_from ActiveRecord::RecordNotFound, # ActionController::RoutingError, # see: config/routes.rb: ActionController::UnknownController, + ActionController::UnknownFormat, AbstractController::ActionNotFound, with: :render_404 end diff --git a/app/controllers/autocompletes_controller.rb b/app/controllers/autocompletes_controller.rb index 53a7a9e84..aec8143bc 100644 --- a/app/controllers/autocompletes_controller.rb +++ b/app/controllers/autocompletes_controller.rb @@ -14,6 +14,17 @@ class AutocompletesController < ApplicationController render json: results.to_json end + def autocomplete_extra_mass_build + mb = MassBuild.where(id: params[:term]).first + results << { + id: mb.id, + value: mb.id, + label: "#{mb.id} - #{mb.name}", + path: platform_mass_build_path(mb.save_to_platform, mb) + } if mb && can?(:show, mb) + 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 diff --git a/app/controllers/platforms/mass_builds_controller.rb b/app/controllers/platforms/mass_builds_controller.rb index b08a1d5cd..fe45cbd8e 100644 --- a/app/controllers/platforms/mass_builds_controller.rb +++ b/app/controllers/platforms/mass_builds_controller.rb @@ -1,4 +1,5 @@ class Platforms::MassBuildsController < Platforms::BaseController + include DatatableHelper before_filter :authenticate_user! skip_before_filter :authenticate_user!, only: [:index, :get_list] if APP_CONFIG['anonymous_access'] @@ -8,10 +9,19 @@ class Platforms::MassBuildsController < Platforms::BaseController def new + if params[:mass_build_id].present? + @mass_build = @platform.mass_builds.find(params[:mass_build_id]).dup + @mass_build.arches = Arch.where(name: @mass_build.arch_names.split(', ')).pluck(:id) + end + @mass_build.arches ||= @platform.platform_arch_settings.by_default.pluck(:arch_id) + @mass_build.arches.map!(&:to_s) + end + + def show end def create - @mass_build.user, @mass_build.arches = current_user, params[:arches] + @mass_build.user, @mass_build.arches = current_user, params[:arches] || [] if @mass_build.save redirect_to(platform_mass_builds_path(@platform), notice: t("flash.platform.build_all_success")) @@ -32,7 +42,16 @@ class Platforms::MassBuildsController < Platforms::BaseController end def index - @mass_builds = MassBuild.by_platform(@platform).order('created_at DESC').paginate(page: params[:page], per_page: 20) + respond_to do |format| + format.html {} + format.json { + @mass_builds = @platform.mass_builds + @total_mass_builds = @mass_builds.count + @mass_builds = @mass_builds.order("id #{sort_dir}") + .search(params[:sSearch]) + .paginate(page: page, per_page: per_page) + } + end end def cancel @@ -42,11 +61,14 @@ class Platforms::MassBuildsController < Platforms::BaseController end def get_list - text = if %w(failed_builds_list tests_failed_builds_list).include? params[:kind] - @mass_build.send "generate_#{params[:kind]}" - elsif %w(projects_list missed_projects_list).include? params[:kind] - @mass_build.send params[:kind] - end + + text = + case params[:kind] + when 'failed_builds_list', 'tests_failed_builds_list', 'success_builds_list' + @mass_build.send "generate_#{params[:kind]}" + when 'projects_list', 'missed_projects_list' + @mass_build.send params[:kind] + end render text: text end end diff --git a/app/controllers/platforms/product_build_lists_controller.rb b/app/controllers/platforms/product_build_lists_controller.rb index 2e3d1a145..5c7dc2891 100644 --- a/app/controllers/platforms/product_build_lists_controller.rb +++ b/app/controllers/platforms/product_build_lists_controller.rb @@ -39,8 +39,10 @@ class Platforms::ProductBuildListsController < Platforms::BaseController end def log + worker_log = @product_build_list.abf_worker_log + render json: { - log: Pygments.highlight(@product_build_list.abf_worker_log, lexer: 'sh'), + log: ( Pygments.highlight(worker_log, lexer: 'sh') rescue worker_log), building: @product_build_list.build_started? } end @@ -78,7 +80,7 @@ class Platforms::ProductBuildListsController < Platforms::BaseController @product_build_lists = @product_build_lists.for_status(params[:status]) if params[:status].present? end @product_build_lists = @product_build_lists.recent.paginate page: params[:page] - @build_server_status = AbfWorker::StatusInspector.products_status + @build_server_status = AbfWorkerStatusPresenter.new.products_status end protected diff --git a/app/controllers/platforms/repositories_controller.rb b/app/controllers/platforms/repositories_controller.rb index 27734252f..c2051a27a 100644 --- a/app/controllers/platforms/repositories_controller.rb +++ b/app/controllers/platforms/repositories_controller.rb @@ -1,4 +1,5 @@ class Platforms::RepositoriesController < Platforms::BaseController + include DatatableHelper include FileStoreHelper include RepositoriesHelper @@ -115,10 +116,6 @@ class Platforms::RepositoriesController < Platforms::BaseController FROM groups ) AS owner ON projects.owner_id = owner.id AND projects.owner_type = owner.type" - colName = ['projects.name'] - sort_col = params[:iSortCol_0] || 0 - sort_dir = params[:sSortDir_0] == 'asc' ? 'asc' : 'desc' - order = "#{colName[sort_col.to_i]} #{sort_dir}" if params[:added] == "true" @projects = @repository.projects @@ -126,14 +123,11 @@ class Platforms::RepositoriesController < Platforms::BaseController @projects = Project.joins(owner_subquery).addable_to_repository(@repository.id) @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, - per_page: params[:iDisplayLength].present? ? params[:iDisplayLength] : 25 - ) + @projects = @projects.paginate(page: page, per_page: per_page) @total_projects = @projects.count @projects = @projects.by_owner(params[:owner_name]). - search(params[:sSearch]).order(order) + search(params[:sSearch]).order("projects.name #{sort_dir}") respond_to do |format| format.json { diff --git a/app/controllers/projects/build_lists_controller.rb b/app/controllers/projects/build_lists_controller.rb index 9e0ead2a8..2ed9562fe 100644 --- a/app/controllers/projects/build_lists_controller.rb +++ b/app/controllers/projects/build_lists_controller.rb @@ -1,13 +1,14 @@ class Projects::BuildListsController < Projects::BaseController - layout 'bootstrap', only: [:index, :show, :new] include FileStoreHelper + layout 'bootstrap', only: [:index, :show, :new] + NESTED_ACTIONS = [:index, :new, :create] before_filter :authenticate_user! 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] + before_filter :find_build_list, only: [:show, :publish, :cancel, :update, :log, :create_container, :dependent_projects] load_and_authorize_resource :project, only: [:new, :create] load_resource :project, only: :index, parent: false @@ -40,7 +41,7 @@ class Projects::BuildListsController < Projects::BaseController :source_packages, project: :project_statistics) - @build_server_status = AbfWorker::StatusInspector.projects_status + @build_server_status = AbfWorkerStatusPresenter.new.projects_status end end end @@ -124,6 +125,35 @@ class Projects::BuildListsController < Projects::BaseController do_and_back(:publish, 'publish_') end + def dependent_projects + raise CanCan::AccessDenied if @build_list.save_to_platform.personal? + + if request.post? + prs = params[:build_list] + if prs.present? && prs[:projects].present? && prs[:arches].present? + project_ids = prs[:projects].select{ |k, v| v == '1' }.keys + arch_ids = prs[:arches]. select{ |k, v| v == '1' }.keys + + Resque.enqueue( + BuildLists::DependentPackagesJob, + @build_list.id, + current_user.id, + project_ids, + arch_ids, + { + auto_publish_status: prs[:auto_publish_status], + auto_create_container: prs[:auto_create_container], + include_testing_subrepository: prs[:include_testing_subrepository], + use_cached_chroot: prs[:use_cached_chroot], + use_extra_tests: prs[:use_extra_tests] + } + ) + flash[:notice] = t('flash.build_list.dependent_projects_job_added_to_queue') + redirect_to build_list_path(@build_list) + end + end + end + def publish_into_testing @build_list.publisher = current_user do_and_back(:publish_into_testing, 'publish_') @@ -155,15 +185,11 @@ class Projects::BuildListsController < Projects::BaseController 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]) + @build_lists = @build_lists.paginate(page: page, per_page: per_page) @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) + @build_lists = @build_lists.order("build_lists.updated_at #{sort_dir}") render partial: 'build_lists_ajax', layout: false end @@ -204,8 +230,8 @@ class Projects::BuildListsController < Projects::BaseController keys = [ :save_to_repository_id, :auto_publish_status, :include_repos, :extra_params, :project_version, :update_type, :auto_create_container, - :extra_repositories, :extra_build_lists, :build_for_platform, - :use_cached_chroot, :use_extra_tests + :extra_repositories, :extra_build_lists, :build_for_platform_id, + :use_cached_chroot, :use_extra_tests, :save_buildroot ] keys.each { |key| params[:build_list][key] = build_list.send(key) } params[:arches] = [build_list.arch_id.to_s] diff --git a/app/controllers/projects/projects_controller.rb b/app/controllers/projects/projects_controller.rb index d88acff5f..de12d7451 100644 --- a/app/controllers/projects/projects_controller.rb +++ b/app/controllers/projects/projects_controller.rb @@ -1,4 +1,5 @@ class Projects::ProjectsController < Projects::BaseController + include DatatableHelper include ProjectsHelper layout 'bootstrap', only: [:index] before_filter :authenticate_user! diff --git a/app/controllers/projects/pull_requests_controller.rb b/app/controllers/projects/pull_requests_controller.rb index f1a2f1680..c916f4e48 100644 --- a/app/controllers/projects/pull_requests_controller.rb +++ b/app/controllers/projects/pull_requests_controller.rb @@ -121,7 +121,10 @@ class Projects::PullRequestsController < Projects::BaseController def autocomplete_to_project items = [] term = params[:term].to_s.strip.downcase - [Project.accessible_by(current_ability, :membered), @project.ancestors].each do |p| + [ Project.where(id: @project.pull_requests.last.try(:to_project_id)), + @project.ancestors, + Project.accessible_by(current_ability, :membered) + ].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} @@ -154,7 +157,7 @@ class Projects::PullRequestsController < Projects::BaseController def find_destination_project bang=true project = Project.find_by_owner_and_name params[:to_project] raise ActiveRecord::RecordNotFound if bang && !project - project || @project + project || @project.pull_requests.last.try(:to_project) || @project.root end def set_attrs diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 03b69a6d6..596069078 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -8,6 +8,7 @@ class SearchController < ApplicationController Search.by_term_and_type( @query, @type, + current_ability, {page: params[:page]} ).each do |k, v| var = :"@#{k}" diff --git a/app/controllers/statistics_controller.rb b/app/controllers/statistics_controller.rb new file mode 100644 index 000000000..fed34f05c --- /dev/null +++ b/app/controllers/statistics_controller.rb @@ -0,0 +1,75 @@ +class StatisticsController < ApplicationController + + RANGES = [ + RANGE_TWENTY_FOUR_HOURS = 'twenty_four_hours', + RANGE_LAST_7_DAYS = 'last_7_days', + RANGE_LAST_30_DAYS = 'last_30_days', + RANGE_LAST_60_DAYS = 'last_60_days', + RANGE_LAST_90_DAYS = 'last_90_days', + RANGE_LAST_180_DAYS = 'last_180_days', + RANGE_LAST_YEAR = 'last_year', + RANGE_CUSTOM = 'custom', + ] + + def index + respond_to do |format| + format.html + format.json do + init_variables + render json: StatisticPresenter.new(range_start: @range_start, range_end: @range_end, unit: @unit) + end + end + end + + private + + def init_variables + case params[:range] + when RANGE_TWENTY_FOUR_HOURS + @range_end = Time.now.utc + @range_start = @range_end - 1.day + @unit = :hour + when RANGE_LAST_7_DAYS + @range_end = Date.today + @range_start = @range_end - 7.days + @unit = :day + when RANGE_LAST_30_DAYS + @range_end = Date.today + @range_start = @range_end - 30.days + @unit = :day + when RANGE_LAST_60_DAYS + @range_end = Date.today + @range_start = @range_end - 30.days + @unit = :day + when RANGE_LAST_90_DAYS + @range_end = Date.today + @range_start = @range_end - 90.days + @unit = :day + when RANGE_LAST_180_DAYS + @range_end = Date.today + @range_start = @range_end - 180.days + @unit = :month + when RANGE_LAST_YEAR + @range_end = Date.today + @range_start = @range_end - 1.year + @unit = :month + when RANGE_CUSTOM + @range_start = Time.zone.parse(params[:range_start]).utc + @range_end = Time.zone.parse(params[:range_end]).utc + diff = @range_end - @range_start + @unit = + if diff <= 24.hours + :hour + elsif diff <= 90.days + :day + else + :month + end + else + raise ActiveRecord::RecordNotFound + end + rescue ArgumentError + raise ActiveRecord::RecordNotFound + end + +end \ No newline at end of file diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb index 9baf44b7c..551232059 100644 --- a/app/controllers/users/registrations_controller.rb +++ b/app/controllers/users/registrations_controller.rb @@ -5,6 +5,7 @@ class Users::RegistrationsController < Devise::RegistrationsController def create # Try stop bots if params[:recaptcha_response_field].present? + build_resource(sign_up_params) respond_with(resource, location: after_inactive_sign_up_path_for(resource)) return end diff --git a/app/helpers/build_lists_helper.rb b/app/helpers/build_lists_helper.rb index 37f754541..2858cc6ad 100644 --- a/app/helpers/build_lists_helper.rb +++ b/app/helpers/build_lists_helper.rb @@ -5,7 +5,7 @@ module BuildListsHelper case status when BuildList::BUILD_PUBLISHED, BuildList::SUCCESS, BuildList::BUILD_PUBLISHED_INTO_TESTING 'success' - when BuildList::BUILD_ERROR, BuildList::FAILED_PUBLISH, BuildList::REJECTED_PUBLISH, BuildList::FAILED_PUBLISH_INTO_TESTING + when BuildList::BUILD_ERROR, BuildList::FAILED_PUBLISH, BuildList::REJECTED_PUBLISH, BuildList::FAILED_PUBLISH_INTO_TESTING, BuildList::PACKAGES_FAIL, BuildList::UNPERMITTED_ARCH 'error' when BuildList::TESTS_FAILED 'warning' @@ -14,10 +14,38 @@ module BuildListsHelper end end + def can_run_dependent_build_lists?(build_list) + build_list.save_to_platform.main? && + build_list.save_to_platform.distrib_type == 'mdv' + end + def availables_main_platforms Platform.availables_main_platforms current_user, current_ability end + def dependent_projects(package) + return [] if package.dependent_packages.blank? + + packages = BuildList::Package. + select('build_list_packages.project_id, build_list_packages.name'). + joins(:build_list). + where( + platform_id: package.platform, + name: package.dependent_packages, + package_type: package.package_type, + build_lists: { status: BuildList::BUILD_PUBLISHED } + ). + group('build_list_packages.project_id, build_list_packages.name'). + reorder(:project_id).group_by(&:project_id) + + Project.where(id: packages.keys).recent.map do |project| + [ + project, + packages[project.id].map(&:name).sort + ] + end + end + def save_to_repositories(project) project.repositories.collect do |r| [ @@ -32,6 +60,14 @@ module BuildListsHelper end.sort_by { |col| col[0] } end + def selected_save_to_repositories(project, select_options, params) + selected = params[:build_list].try(:[], :save_to_repository_id) + return { selected: selected } if selected.present? + version = params[:build_list].try(:[], :project_version) || project.default_branch + res = select_options.select { |r| r[0] =~ /#{version}\// }[0].try :[], 1 + res.present? ? { selected: res } : {} + end + def external_nodes BuildList::EXTERNAL_NODES.map do |type| [I18n.t("layout.build_lists.external_nodes.#{type}"), type] @@ -45,10 +81,8 @@ module BuildListsHelper end def mass_build_options - options_from_collection_for_select( - MassBuild.recent.limit(15), - :id, - :name + options_for_select( + MassBuild.recent.limit(15).pluck(:name, :id).unshift([t(:none), -1]) ) end diff --git a/app/helpers/datatable_helper.rb b/app/helpers/datatable_helper.rb new file mode 100644 index 000000000..6fa5d0055 --- /dev/null +++ b/app/helpers/datatable_helper.rb @@ -0,0 +1,14 @@ +module DatatableHelper + def page + (params[:iDisplayStart].to_i/(params[:iDisplayLength].present? ? params[:iDisplayLength] : 25).to_i).to_i + 1 + end + + def per_page + params[:iDisplayLength].present? ? params[:iDisplayLength] : 25 + end + + def sort_dir + params[:sSortDir_0] == 'asc' ? 'asc' : 'desc' + end + +end diff --git a/app/helpers/git_helper.rb b/app/helpers/git_helper.rb index d750b3ba7..8059f62b7 100644 --- a/app/helpers/git_helper.rb +++ b/app/helpers/git_helper.rb @@ -94,8 +94,9 @@ module GitHelper end def split_commits_by_date(commits) - commits.sort{|x, y| y.authored_date <=> x.authored_date}.inject({}) do |h, commit| - dt = commit.authored_date + # See: https://github.com/gitlabhq/gitlabhq/blob/master/app/views/projects/commits/_commits.html.haml#L1 + commits.sort{|x, y| y.committed_date <=> x.committed_date}.inject({}) do |h, commit| + dt = commit.committed_date h[dt.year] ||= {} h[dt.year][dt.month] ||= {} h[dt.year][dt.month][dt.day] ||= [] @@ -105,18 +106,26 @@ module GitHelper end def blob_highlight(blob) - if blob.mime_type == 'text/rpm-spec' - Pygments.highlight blob.data, lexer: 'spec' - else - blob.colorize - end.html_safe + return if blob.nil? || blob.data.blank? + result = if blob.mime_type == 'text/rpm-spec' + Pygments.highlight blob.data, lexer: 'spec' + else + blob.colorize + end + result.present? ? result.html_safe : blob.data + rescue MentosError, Yajl::ParseError => e + blob.data.html_safe end def blame_highlight(blob, text) - if blob.mime_type == 'text/rpm-spec' - Pygments.highlight(text, lexer: 'spec') - else - blob.lexer.highlight text - end.html_safe + return if blob.nil? || text.blank? + result = if blob.mime_type == 'text/rpm-spec' + Pygments.highlight(text, lexer: 'spec') + else + blob.lexer.highlight text + end + result.present? ? result.html_safe : text + rescue MentosError, Yajl::ParseError => e + text.html_safe end end diff --git a/app/helpers/markdown_helper.rb b/app/helpers/markdown_helper.rb index 51fb67551..e02eca6f6 100644 --- a/app/helpers/markdown_helper.rb +++ b/app/helpers/markdown_helper.rb @@ -92,8 +92,6 @@ module MarkdownHelper def parse(text) parse_references(text) if @project parse_emoji(text) - - text end REFERENCE_PATTERN = %r{ @@ -127,26 +125,14 @@ module MarkdownHelper 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") + text.gsub(/:([\w+-]+):/) do |match| + if emoji = Emoji.find_by_alias($1) + image_tag("/images/emoji/#{emoji.image_filename}", 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 if text.present? end # Private: Dispatches to a dedicated processing method based on reference diff --git a/app/helpers/mass_build_helper.rb b/app/helpers/mass_build_helper.rb index d44968261..d3821129f 100644 --- a/app/helpers/mass_build_helper.rb +++ b/app/helpers/mass_build_helper.rb @@ -1,7 +1,60 @@ module MassBuildHelper + + COLUMNS = [ + { + sortable: true + }, + { + type: 'html', + sortable: false, + searchable: false + }, + { + sortable: false, + searchable: false + }, + { + sortable: false, + searchable: false + }, + { + sortable: false, + searchable: false, + class: 'buttons' + } + ] + def link_to_list platform, mass_build, which link_to t("layout.mass_builds.#{which}"), - get_list_platform_mass_build_path(@platform, mass_build, kind: which, format: :txt), + get_list_platform_mass_build_path(platform, mass_build, kind: which, format: :txt), target: "_blank" if can?(:get_list, mass_build) end + + def link_to_mass_build(mass_build) + link_to mass_build.name, build_lists_path+"#?#{{filter: {mass_build_id: mass_build.id, ownership: 'everything'}}.to_param}" + end + + def mass_builds_datatable(platform) + datatable( + COLUMNS, + { + sort_by: "[0, 'desc']", + search_label: '', + placeholder: t('layout.mass_builds.placeholder.description'), + processing: t('layout.processing'), + 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: platform_mass_builds_path(platform, format: :json) + } + ) + end + end diff --git a/app/helpers/statistics_helper.rb b/app/helpers/statistics_helper.rb new file mode 100644 index 000000000..d4737a240 --- /dev/null +++ b/app/helpers/statistics_helper.rb @@ -0,0 +1,9 @@ +module StatisticsHelper + + def statistics_range_options + options_for_select( + StatisticsController::RANGES.map { |r| [I18n.t(r, scope: 'statistics.helper.period'), r] } + ) + end + +end diff --git a/lib/abf_worker/base_observer.rb b/app/jobs/abf_worker/base_observer.rb similarity index 100% rename from lib/abf_worker/base_observer.rb rename to app/jobs/abf_worker/base_observer.rb diff --git a/lib/abf_worker/iso_worker_observer.rb b/app/jobs/abf_worker/iso_worker_observer.rb similarity index 100% rename from lib/abf_worker/iso_worker_observer.rb rename to app/jobs/abf_worker/iso_worker_observer.rb diff --git a/lib/abf_worker/publish_observer.rb b/app/jobs/abf_worker/publish_observer.rb similarity index 90% rename from lib/abf_worker/publish_observer.rb rename to app/jobs/abf_worker/publish_observer.rb index f70489981..9600ac904 100644 --- a/lib/abf_worker/publish_observer.rb +++ b/app/jobs/abf_worker/publish_observer.rb @@ -38,7 +38,7 @@ module AbfWorker elsif !extra['resign'] # Simple publish bls = extra['build_lists_for_cleanup_from_testing'] if status != COMPLETED && bls.present? - AbfWorker::BuildListsPublishTaskManager.cleanup_packages_from_testing( + AbfWorkerService::Base.cleanup_packages_from_testing( repository_status.platform_id, repository_status.repository_id, bls @@ -72,14 +72,14 @@ module AbfWorker 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 + AbfWorkerService::Base.unlock_build_list build_list end case status when COMPLETED - AbfWorker::BuildListsPublishTaskManager.cleanup_completed options['projects_for_cleanup'] + AbfWorkerService::Base.cleanup_completed options['projects_for_cleanup'] when FAILED, CANCELED - AbfWorker::BuildListsPublishTaskManager.cleanup_failed options['projects_for_cleanup'] + AbfWorkerService::Base.cleanup_failed options['projects_for_cleanup'] end end diff --git a/lib/abf_worker/rpm_worker_observer.rb b/app/jobs/abf_worker/rpm_worker_observer.rb similarity index 89% rename from lib/abf_worker/rpm_worker_observer.rb rename to app/jobs/abf_worker/rpm_worker_observer.rb index 85f568719..75a38f9bd 100644 --- a/lib/abf_worker/rpm_worker_observer.rb +++ b/app/jobs/abf_worker/rpm_worker_observer.rb @@ -2,6 +2,11 @@ module AbfWorker class RpmWorkerObserver < AbfWorker::BaseObserver RESTARTED_BUILD_LISTS = 'abf-worker::rpm-worker-observer::restarted-build-lists' + # EXIT CODES: + # 6 - Unpermitted architecture + # other - Build error + EXIT_CODE_UNPERMITTED_ARCHITECTURE = 6 + @queue = :rpm_worker_observer def self.perform(options) @@ -29,7 +34,14 @@ module AbfWorker subject.publish_into_testing end when FAILED - subject.build_error + + case options['exit_status'].to_i + when EXIT_CODE_UNPERMITTED_ARCHITECTURE + subject.unpermitted_arch + else + subject.build_error + end + item.update_attributes({status: BuildList::BUILD_ERROR}) unless rerunning_tests when STARTED subject.start_build diff --git a/app/jobs/build_lists/clean_buildroot_job.rb b/app/jobs/build_lists/clean_buildroot_job.rb new file mode 100644 index 000000000..90cdfae63 --- /dev/null +++ b/app/jobs/build_lists/clean_buildroot_job.rb @@ -0,0 +1,31 @@ +module BuildLists + class CleanBuildrootJob + @queue = :middle + + FILENAME = 'rpm-buildroot.tar.gz' + + def self.perform + build_lists = BuildList.where(save_buildroot: true). + for_status(BuildList::BUILD_ERROR). + where('updated_at < ?', Time.now - 1.hour). + where('results ~ ?', "file_name: #{FILENAME}") + + build_lists.find_each do |build_list| + buildroots = build_list.results.select do |r| + r['file_name'] == FILENAME + end + build_list.results -= buildroots + build_list.save(validate: false) + + clean_file_store buildroots + end + end + + def self.clean_file_store(buildroots) + buildroots.each do |r| + FileStoreService::File.new(sha1: r['sha1']).destroy + end + end + + end +end \ No newline at end of file diff --git a/app/jobs/build_lists/create_container_job.rb b/app/jobs/build_lists/create_container_job.rb new file mode 100644 index 000000000..236fb81e9 --- /dev/null +++ b/app/jobs/build_lists/create_container_job.rb @@ -0,0 +1,12 @@ +module BuildLists + class CreateContainerJob + @queue = :middle + + def self.perform(build_list_id) + build_list = BuildList.find(build_list_id) + container = AbfWorkerService::Container.new(build_list) + container.create! + end + + end +end \ No newline at end of file diff --git a/app/jobs/build_lists/dependent_packages_job.rb b/app/jobs/build_lists/dependent_packages_job.rb new file mode 100644 index 000000000..1d2b75b8e --- /dev/null +++ b/app/jobs/build_lists/dependent_packages_job.rb @@ -0,0 +1,57 @@ +module BuildLists + class DependentPackagesJob + @queue = :middle + + def self.perform(build_list_id, user_id, project_ids, arch_ids, options) + build_list = BuildList.find(build_list_id) + return if build_list.save_to_platform.personal? + user = User.find(user_id) + ability = Ability.new(user) + + return unless ability.can?(:show, build_list) + + arches = Arch.where(id: arch_ids).to_a + Project.where(id: project_ids).to_a.each do |project| + next unless ability.can?(:write, project) + + build_for_platform = save_to_platform = build_list.build_for_platform + save_to_repository = save_to_platform.repositories.find{ |r| r.projects.exists?(project.id) } + next unless save_to_repository + + project_version = project.project_version_for(save_to_platform, build_for_platform) + project.increase_release_tag(project_version, user, "BuildList##{build_list.id}: Increase release tag") + + arches.each do |arch| + bl = project.build_lists.build + bl.arch = arch + bl.save_to_repository = save_to_repository + bl.priority = user.build_priority # User builds more priority than mass rebuild with zero priority + bl.project_version = project_version + bl.user = user + bl.include_repos = [build_for_platform.repositories.main.first.try(:id)].compact + bl.include_repos |= [save_to_repository.id] + %i( + build_for_platform + update_type + save_to_platform + extra_build_lists + extra_params + external_nodes + group_id + ).each { |field| bl.send("#{field}=", build_list.send(field)) } + + %w( + auto_publish_status + auto_create_container + include_testing_subrepository + use_cached_chroot + use_extra_tests + ).each { |field| bl.send("#{field}=", options[field]) } + + ability.can?(:create, bl) && bl.save + end + end + end + + end +end \ No newline at end of file diff --git a/app/jobs/build_lists/queues_monitoring_job.rb b/app/jobs/build_lists/queues_monitoring_job.rb new file mode 100644 index 000000000..3c329d3f7 --- /dev/null +++ b/app/jobs/build_lists/queues_monitoring_job.rb @@ -0,0 +1,47 @@ +module BuildLists + class QueuesMonitoringJob + @queue = :middle + + def self.perform + Redis.current.smembers('resque:queues').each do |key| + next if key !~ /(user|mass)_build_/ + + queue = "resque:queue:#{key}" + id = key.gsub(/[^\d]/, '') + + if Redis.current.llen(queue) == 0 + if key =~ /^user/ + last_updated_at = BuildList.select(:updated_at). + where(user_id: id).order('updated_at DESC').first + else + last_updated_at = MassBuild.select(:updated_at).where(id: 250).first + end + last_updated_at = last_updated_at.try(:updated_at) + # cleans queue if no activity and tasks for this queue + clean(key) if !last_updated_at || (last_updated_at + 5.minutes) < Time.zone.now + else + # ensures that user/mass-build in the set from which we select next jobs + set_key = key =~ /^user/ ? BuildList::USER_BUILDS_SET : BuildList::MASS_BUILDS_SET + Redis.current.sadd set_key, id + end + + end + end + + def self.clean(key) + queue = "resque:queue:#{key}" + # See [#watch]: https://github.com/redis/redis-rb/blob/master/lib/redis.rb#L2012 + Redis.current.watch(queue) do + if Redis.current.llen(queue) == 0 + Redis.current.multi do |multi| + multi.del queue + multi.srem 'resque:queues', key + end + else + Redis.current.unwatch + end + end + end + + end +end \ No newline at end of file diff --git a/app/jobs/build_lists_publish_task_manager_job.rb b/app/jobs/build_lists_publish_task_manager_job.rb deleted file mode 100644 index 4a615dfec..000000000 --- a/app/jobs/build_lists_publish_task_manager_job.rb +++ /dev/null @@ -1,8 +0,0 @@ -class BuildListsPublishTaskManagerJob - @queue = :middle - - def self.perform - AbfWorker::BuildListsPublishTaskManager.new.run - end - -end diff --git a/app/jobs/build_lists_queues_monitoring_job.rb b/app/jobs/build_lists_queues_monitoring_job.rb deleted file mode 100644 index ecea75cf0..000000000 --- a/app/jobs/build_lists_queues_monitoring_job.rb +++ /dev/null @@ -1,45 +0,0 @@ -class BuildListsQueuesMonitoringJob - @queue = :middle - - def self.perform - Redis.current.smembers('resque:queues').each do |key| - next if key !~ /(user|mass)_build_/ - - queue = "resque:queue:#{key}" - id = key.gsub(/[^\d]/, '') - - if Redis.current.llen(queue) == 0 - if key =~ /^user/ - last_updated_at = BuildList.select(:updated_at). - where(user_id: id).order('updated_at DESC').first - else - last_updated_at = MassBuild.select(:updated_at).where(id: 250).first - end - last_updated_at = last_updated_at.try(:updated_at) - # cleans queue if no activity and tasks for this queue - clean(key) if !last_updated_at || (last_updated_at + 5.minutes) < Time.zone.now - else - # ensures that user/mass-build in the set from which we select next jobs - set_key = key =~ /^user/ ? BuildList::USER_BUILDS_SET : BuildList::MASS_BUILDS_SET - Redis.current.sadd set_key, id - end - - end - end - - def self.clean(key) - queue = "resque:queue:#{key}" - # See [#watch]: https://github.com/redis/redis-rb/blob/master/lib/redis.rb#L2012 - Redis.current.watch(queue) do - if Redis.current.llen(queue) == 0 - Redis.current.multi do |multi| - multi.del queue - multi.srem 'resque:queues', key - end - else - Redis.current.unwatch - end - end - end - -end diff --git a/app/jobs/create_empty_metadata_job.rb b/app/jobs/create_empty_metadata_job.rb new file mode 100644 index 000000000..c81577050 --- /dev/null +++ b/app/jobs/create_empty_metadata_job.rb @@ -0,0 +1,70 @@ +class CreateEmptyMetadataJob < Struct.new(:class_name, :id) + @queue = :low + + def perform + case class_name + when Platform.name + create_empty_metadata_for_platform + when Repository.name + create_empty_metadata_for_repository Repository.find(id) + end + end + + def self.perform(class_name, id) + new(class_name, id).perform + end + + private + + def create_empty_metadata_for_platform + platform = Platform.main.opened.find id + @platforms = [platform] + repositories = Repository.joins(:platform). + where(platforms: { platform_type: Platform::TYPE_PERSONAL }) + repositories.find_each do |r| + create_empty_metadata_for_repository r + end + end + + def create_empty_metadata_for_repository(repository) + @platforms = [repository.platform] if repository.platform.main? + platforms.each do |platform| + arch_names.each do |arch_name| + %w(release updates).each do |type| + path = "#{ repository.platform.path }/repository/" + path << "#{ platform.name }/" if repository.platform.personal? + path << "#{ arch_name }/#{ repository.name }/#{ type }" + create_empty_metadata(platform, path) + end + end + end + end + + def create_empty_metadata(platform, path) + case platform.distrib_type + when 'rhel' + path << '/repodata/' + when 'mdv' + path << '/media_info/' + else + return + end + if Dir["#{ path }/*"].empty? + system "mkdir -p -m 0777 #{ path }" + system "cp -f #{ empty_metadatas(platform) }/* #{ path }/" + end + end + + def empty_metadatas(platform) + Rails.root.join('public', 'metadatas', platform.distrib_type).to_s + end + + def arch_names + @arch_names ||= Arch.pluck(:name) + end + + def platforms + @platforms ||= Platform.main.opened.to_a + end + +end diff --git a/app/jobs/destroy_project_from_repository_job.rb b/app/jobs/destroy_project_from_repository_job.rb new file mode 100644 index 000000000..4f27d76f0 --- /dev/null +++ b/app/jobs/destroy_project_from_repository_job.rb @@ -0,0 +1,8 @@ +class DestroyProjectFromRepositoryJob + + def self.perform(project, repository) + service = AbfWorkerService::Repository.new(repository) + service.destroy_project!(project) + end + +end diff --git a/app/jobs/publish_task_manager_job.rb b/app/jobs/publish_task_manager_job.rb new file mode 100644 index 000000000..a1cfc988c --- /dev/null +++ b/app/jobs/publish_task_manager_job.rb @@ -0,0 +1,42 @@ +class PublishTaskManagerJob + @queue = :middle + + def self.perform + PublishTaskManagerJob.new.perform + end + + def perform + regenerate_metadata_for_software_center + resign_repositories + regenerate_metadata + AbfWorkerService::Rpm.publish! + end + + protected + + def regenerate_metadata_for_software_center + Platform.main.waiting_for_regeneration.each do |platform| + AbfWorkerService::PlatformMetadata.new(platform).regenerate! + end + end + + def resign_repositories + statuses = RepositoryStatus.platform_ready. + for_resign.includes(repository: :platform).readonly(false) + + statuses.each do |repository_status| + AbfWorkerService::Repository.new(repository_status.repository).resign!(repository_status) + end + end + + def regenerate_metadata + statuses = RepositoryStatus.platform_ready. + for_regeneration.includes(repository: :platform).readonly(false) + + statuses.each do |repository_status| + AbfWorkerService::RepositoryMetadata.new(repository_status).regenerate! + end + end + + +end \ No newline at end of file diff --git a/app/jobs/run_extra_mass_builds_job.rb b/app/jobs/run_extra_mass_builds_job.rb new file mode 100644 index 000000000..00168ad83 --- /dev/null +++ b/app/jobs/run_extra_mass_builds_job.rb @@ -0,0 +1,30 @@ +class RunExtraMassBuildsJob + @queue = :low + + def self.perform + RunExtraMassBuildsJob.new.perform + end + + def perform + MassBuild.where(status: MassBuild::BUILD_PENDING).find_each do |mb| + next if mb.extra_mass_builds.blank? + emb = MassBuild.where(status: MassBuild::SUCCESS, id: mb.extra_mass_builds).to_a + next if emb.size != mb.extra_mass_builds.size + + next if emb.find{ |mb| not_ready?(mb) } + mb.build_all + end + end + + private + + # Returns true if mass build has not published packages or packages without container + def not_ready?(mb) + mb.build_lists.count != mb.build_lists.where( + 'status = ? OR container_status = ?', + BuildList::BUILD_PUBLISHED, + BuildList::BUILD_PUBLISHED + ).count + end + +end \ No newline at end of file diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 459dcab5a..381151de5 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -31,12 +31,12 @@ class UserMailer < ActionMailer::Base end end - def new_issue_notification(issue, user) - @user, @issue = user, issue + def new_issue_notification(issue_id, user_id) + @user, @issue = User.find(user_id), Issue.find(issue_id) mail( - to: email_with_name(user, user.email), - subject: subject_for_issue(issue, true), - from: email_with_name(issue.user) + to: email_with_name(@user, @user.email), + subject: subject_for_issue(@issue, true), + from: email_with_name(@issue.user) ) do |format| format.html end diff --git a/app/models/ability.rb b/app/models/ability.rb index 4c33f0534..ad104e178 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -114,8 +114,8 @@ class Ability 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 [:read, :related], Platform, id: user.repositories.pluck(:platform_id) - can([:update, :destroy, :change_visibility, :remove_file], Platform) {|platform| owner?(platform) } - can([:local_admin_manage, :members, :add_member, :remove_member, :remove_members] , Platform) {|platform| owner?(platform) || local_admin?(platform) } + can([:update, :destroy, :change_visibility], Platform) {|platform| owner?(platform) } + can([:local_admin_manage, :members, :add_member, :remove_member, :remove_members, :remove_file] , Platform) {|platform| owner?(platform) || local_admin?(platform) } 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} @@ -154,8 +154,8 @@ class Ability 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, PullRequest) {|pull| pull.user_id == user.id or local_admin?(pull.to_project)} - can(:merge, PullRequest) {|pull| local_admin?(pull.to_project)} + can(:update, PullRequest) {|pull| pull.user_id == user.id or local_writer?(pull.to_project)} + can(:merge, PullRequest) {|pull| local_writer?(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)} diff --git a/app/models/build_list.rb b/app/models/build_list.rb index 6f6b4439b..630a9534c 100644 --- a/app/models/build_list.rb +++ b/app/models/build_list.rb @@ -1,7 +1,7 @@ class BuildList < ActiveRecord::Base include CommitAndVersion include FileStoreClean - include AbfWorker::ModelHelper + include AbfWorkerMethods include Feed::BuildList include BuildListObserver include EventLoggable @@ -15,20 +15,28 @@ class BuildList < ActiveRecord::Base belongs_to :builder, class_name: 'User' belongs_to :publisher, class_name: 'User' belongs_to :advisory - belongs_to :mass_build, counter_cache: true, touch: true + 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 :source_packages, -> { where(package_type: 'source') }, class_name: '::BuildList::Package' - UPDATE_TYPES = %w[bugfix security enhancement recommended newpackage] - RELEASE_UPDATE_TYPES = %w[bugfix security] + UPDATE_TYPES = [ + UPDATE_TYPE_BUGFIX = 'bugfix', + UPDATE_TYPE_SECURITY = 'security', + UPDATE_TYPE_ENHANCEMENT = 'enhancement', + UPDATE_TYPE_RECOMMENDED = 'recommended', + UPDATE_TYPE_NEWPACKAGE = 'newpackage' + ] + + RELEASE_UPDATE_TYPES = [UPDATE_TYPE_BUGFIX, UPDATE_TYPE_SECURITY] EXTRA_PARAMS = %w[cfg_options cfg_urpm_options build_src_rpm build_rpm] EXTERNAL_NODES = %w[owned everything] - AUTO_PUBLISH_STATUS_NONE = 'none' - AUTO_PUBLISH_STATUS_DEFAULT = 'default' - AUTO_PUBLISH_STATUS_TESTING = 'testing' - AUTO_PUBLISH_STATUSES = [AUTO_PUBLISH_STATUS_NONE, AUTO_PUBLISH_STATUS_DEFAULT, AUTO_PUBLISH_STATUS_TESTING] + AUTO_PUBLISH_STATUSES = [ + AUTO_PUBLISH_STATUS_NONE = 'none', + AUTO_PUBLISH_STATUS_DEFAULT = 'default', + AUTO_PUBLISH_STATUS_TESTING = 'testing' + ] validates :project_id, :project_version, @@ -71,7 +79,7 @@ class BuildList < ActiveRecord::Base :save_to_platform_id, :project_version, :auto_create_container, :extra_repositories, :extra_build_lists, :extra_params, :external_nodes, :include_testing_subrepository, :auto_publish_status, - :use_cached_chroot, :use_extra_tests + :use_cached_chroot, :use_extra_tests, :save_buildroot LIVE_TIME = 4.week # for unpublished MAX_LIVE_TIME = 3.month # for published @@ -82,6 +90,7 @@ class BuildList < ActiveRecord::Base # %w(PROJECT_SOURCE_ERROR 6), # %w(DEPENDENCIES_ERROR 555), %w(BUILD_ERROR 666), + %w(PACKAGES_FAIL 777), %w(BUILD_STARTED 3000), %w(BUILD_CANCELED 5000), %w(WAITING_FOR_RESPONSE 4000), @@ -96,7 +105,8 @@ class BuildList < ActiveRecord::Base %w(TESTS_FAILED 11000), %w(BUILD_PUBLISHED_INTO_TESTING 12000), %w(BUILD_PUBLISH_INTO_TESTING 13000), - %w(FAILED_PUBLISH_INTO_TESTING 14000) + %w(FAILED_PUBLISH_INTO_TESTING 14000), + %w(UNPERMITTED_ARCH 15000) ].each do |kind, value| value = value.to_i const_set kind, value @@ -116,15 +126,15 @@ class BuildList < ActiveRecord::Base scope :for_status, ->(status) { where(status: status) if status.present? } scope :for_user, ->(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, ->(type) { where("#{table_name}.external_nodes = ?", type) } - scope :oldest, -> { where("#{table_name}.updated_at < ?", Time.zone.now - 15.seconds) } - scope :for_platform, ->(platform) { where(build_for_platform_id: platform) } - scope :by_mass_build, ->(mass_build) { where(mass_build_id: mass_build) } - scope :scoped_to_arch, ->(arch) { where(arch_id: arch) if arch.present? } - scope :scoped_to_save_platform, ->(pl_id) { where(save_to_platform_id: pl_id) if pl_id.present? } - scope :scoped_to_project_version, ->(pr_version) { where(project_version: pr_version) if pr_version.present? } - scope :scoped_to_is_circle, ->(is_circle) { where(is_circle: is_circle) } - scope :for_creation_date_period, ->(start_date, end_date) { + scope :external_nodes, ->(type) { where("#{table_name}.external_nodes = ?", type) } + scope :oldest, -> { where("#{table_name}.updated_at < ?", Time.zone.now - 15.seconds) } + scope :for_platform, ->(platform) { where(build_for_platform_id: platform) if platform.present? } + scope :by_mass_build, ->(mass_build) { where(mass_build_id: mass_build) } + scope :scoped_to_arch, ->(arch) { where(arch_id: arch) if arch.present? } + scope :scoped_to_save_platform, ->(pl_id) { where(save_to_platform_id: pl_id) if pl_id.present? } + scope :scoped_to_project_version, ->(pr_version) { where(project_version: pr_version) if pr_version.present? } + scope :scoped_to_is_circle, ->(is_circle) { where(is_circle: is_circle) } + scope :for_creation_date_period, ->(start_date, end_date) { s = all s = s.where(["#{table_name}.created_at >= ?", start_date]) if start_date s = s.where(["#{table_name}.created_at <= ?", end_date]) if end_date @@ -163,7 +173,7 @@ class BuildList < ActiveRecord::Base end after_transition on: :published, - do: [:set_version_and_tag, :actualize_packages] + do: %i(set_version_and_tag actualize_packages) after_transition on: :publish, do: :set_publisher after_transition(on: :publish) do |build_list, transition| if transition.from == BUILD_PUBLISHED_INTO_TESTING @@ -172,7 +182,7 @@ class BuildList < ActiveRecord::Base end after_transition on: :cancel, do: :cancel_job - after_transition on: [:published, :fail_publish, :build_error, :tests_failed], do: :notify_users + after_transition on: %i(published fail_publish build_error tests_failed unpermitted_arch), do: :notify_users after_transition on: :build_success, do: :notify_users, unless: ->(build_list) { build_list.auto_publish? || build_list.auto_publish_into_testing? } @@ -180,6 +190,10 @@ class BuildList < ActiveRecord::Base transition waiting_for_response: :build_pending end + event :unpermitted_arch do + transition [:build_started, :build_canceling, :build_canceled] => :unpermitted_arch + end + event :rerun_tests do transition %i(success tests_failed) => :rerun_tests end @@ -358,7 +372,7 @@ class BuildList < ActiveRecord::Base # @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 equal to version of pubished packages (only if platform is not released or platform is RHEL). # - 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 @@ -366,7 +380,8 @@ class BuildList < ActiveRecord::Base 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?) + return true if comparison == 1 + return comparison == 0 && ( !save_to_platform.released? || save_to_platform.distrib_type == 'rhel' ) end else return true # no published packages @@ -491,7 +506,8 @@ class BuildList < ActiveRecord::Base def log(load_lines) if new_core? - Pygments.highlight abf_worker_log, lexer: 'sh' + worker_log = abf_worker_log + Pygments.highlight(worker_log, lexer: 'sh') rescue worker_log else I18n.t('layout.build_lists.log.not_available') end @@ -539,6 +555,7 @@ class BuildList < ActiveRecord::Base 'GIT_PROJECT_ADDRESS' => git_project_address, 'COMMIT_HASH' => commit_hash, 'USE_EXTRA_TESTS' => use_extra_tests?, + 'SAVE_BUILDROOT' => save_buildroot?, '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'], @@ -571,30 +588,44 @@ class BuildList < ActiveRecord::Base end def cleanup_packages_from_testing - AbfWorker::BuildListsPublishTaskManager.cleanup_packages_from_testing( + AbfWorkerService::Base.cleanup_packages_from_testing( build_for_platform_id, save_to_repository_id, id ) end - def self.next_build - kind_id = Redis.current.spop(USER_BUILDS_SET) - key = "user_build_#{kind_id}_rpm_worker_default" if kind_id - task = Resque.pop(key) if key - Redis.current.sadd(USER_BUILDS_SET, kind_id) if task + def self.next_build(arch_ids, platform_ids) + kind_id = Redis.current.spop(USER_BUILDS_SET) + key = "user_build_#{kind_id}_rpm_worker_default" if kind_id + build_list = next_build_from_queue(kind_id, key, arch_ids, platform_ids) if key + Redis.current.sadd(USER_BUILDS_SET, kind_id) if build_list + kind_id ||= Redis.current.spop(MASS_BUILDS_SET) + key ||= "mass_build_#{kind_id}_rpm_worker" if kind_id + build_list ||= next_build_from_queue(kind_id, key, arch_ids, platform_ids, true) if key + Redis.current.sadd(MASS_BUILDS_SET, kind_id) if build_list && key =~ /^mass_build/ - kind_id ||= Redis.current.spop(MASS_BUILDS_SET) - key ||= "mass_build_#{kind_id}_rpm_worker" if kind_id - task ||= Resque.pop(key) if key - Redis.current.sadd(MASS_BUILDS_SET, kind_id) if task && key =~ /^mass_build/ + build_list.delayed_add_job_to_abf_worker_queue if build_list.present? + build_list + end - if task - build_list = BuildList.where(id: task['args'][0]['id']).first - build_list.delayed_add_job_to_abf_worker_queue - build_list + def self.next_build_from_queue(kind_id, key, arch_ids, platform_ids, mass_build = false) + if kind_id && (arch_ids.present? || platform_ids.present?) + build_list = BuildList.where(user_id: kind_id). + scoped_to_arch(arch_ids). + for_status([BuildList::BUILD_PENDING, BuildList::RERUN_TESTS]). + for_platform(platform_ids) + build_list = build_list.where.not(mass_build_id: nil) if mass_build + build_list = build_list.oldest.order(:created_at).first + + build_list = nil if build_list && build_list.destroy_from_resque_queue != 1 + elsif key + task = Resque.pop(key) end + + build_list ||= BuildList.where(id: task['args'][0]['id']).first if task + build_list end def delayed_add_job_to_abf_worker_queue(*args) @@ -602,25 +633,25 @@ class BuildList < ActiveRecord::Base end later :delayed_add_job_to_abf_worker_queue, delay: 60, queue: :middle - protected - def valid_branch_for_publish? - return true if save_to_platform.personal? || - save_to_repository.publish_builds_only_from_branch.blank? || - ( project_version == save_to_repository.publish_builds_only_from_branch ) - - project.repo.git.native(:branch, {}, '--contains', commit_hash). - gsub(/\*/, '').split(/\n/).map(&:strip). - include?(save_to_repository.publish_builds_only_from_branch) + @valid_branch_for_publish ||= begin + save_to_platform.personal? || + save_to_repository.publish_builds_only_from_branch.blank? || + ( project_version == save_to_repository.publish_builds_only_from_branch ) || + project.repo.git.native(:branch, {}, '--contains', commit_hash). + gsub(/\*/, '').split(/\n/).map(&:strip). + include?(save_to_repository.publish_builds_only_from_branch) + end end + protected def create_container - AbfWorker::BuildListsPublishTaskManager.create_container_for self + Resque.enqueue(BuildLists::CreateContainerJob, id) end def remove_container - system "rm -rf #{save_to_platform.path}/container/#{id}" if save_to_platform + AbfWorkerService::Container.new(self).destroy! if save_to_platform end def abf_worker_priority @@ -680,6 +711,11 @@ class BuildList < ActiveRecord::Base end def prepare_extra_build_lists + if mass_build && mass_build.extra_mass_builds.present? + extra_build_lists << BuildList.where(mass_build_id: mass_build.extra_mass_builds).pluck(:id) + extra_build_lists.flatten! + end + return if extra_build_lists.blank? 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' diff --git a/app/models/build_list/filter.rb b/app/models/build_list/filter.rb index 249b8eebc..200d83961 100644 --- a/app/models/build_list/filter.rb +++ b/app/models/build_list/filter.rb @@ -15,7 +15,9 @@ class BuildList::Filter build_lists = build_lists.where(id: @options[:id]) else build_lists = build_lists.scoped_to_new_core(@options[:new_core] == '0' ? nil : true) if @options[:new_core].present? - build_lists = build_lists.by_mass_build(@options[:mass_build_id]) if @options[:mass_build_id] + if @options[:mass_build_id] + build_lists = build_lists.by_mass_build(@options[:mass_build_id] == '-1' ? nil : @options[:mass_build_id]) + end build_lists = build_lists.accessible_by(@current_ability, @options[:ownership].to_sym) if @options[:ownership] build_lists = build_lists.for_status(@options[:status]) diff --git a/app/models/build_list/package.rb b/app/models/build_list/package.rb index 82e111ad2..a60dc30aa 100644 --- a/app/models/build_list/package.rb +++ b/app/models/build_list/package.rb @@ -6,7 +6,6 @@ class BuildList::Package < ActiveRecord::Base belongs_to :platform serialize :dependent_packages, Array - serialize :dependent_projects, Array attr_accessible :fullname, :name, :release, :version, :sha1, :epoch, :dependent_packages @@ -27,7 +26,6 @@ class BuildList::Package < ActiveRecord::Base before_create :set_epoch before_create :normalize_dependent_packages - after_commit(on: :create) { |p| p.find_dependent_projects if p.dependent_packages.present? } # later with resque def assignee project.maintainer @@ -50,11 +48,6 @@ class BuildList::Package < ActiveRecord::Base end end - def find_dependent_projects - # TODO - end - later :find_dependent_projects, queue: :middle - protected def normalize_dependent_packages diff --git a/app/models/build_script.rb b/app/models/build_script.rb index 26cea8721..44e0c43c3 100644 --- a/app/models/build_script.rb +++ b/app/models/build_script.rb @@ -40,7 +40,7 @@ class BuildScript < ActiveRecord::Base old_sha1, new_commit = sha1, last_commit archive = project.archive_by_treeish_and_format(treeish, FORMAT) - new_sha1 = FileStoreClean.save_file_to_file_store(archive) + new_sha1 = FileStoreService::File.new(data: archive).save if new_sha1.present? update_attributes(sha1: new_sha1, commit: new_commit) destroy_files_from_file_store(old_sha1) if old_sha1.present? diff --git a/lib/abf_worker/model_helper.rb b/app/models/concerns/abf_worker_methods.rb similarity index 91% rename from lib/abf_worker/model_helper.rb rename to app/models/concerns/abf_worker_methods.rb index 2aba51664..30f4e6e19 100644 --- a/lib/abf_worker/model_helper.rb +++ b/app/models/concerns/abf_worker_methods.rb @@ -1,15 +1,9 @@ -module AbfWorker::ModelHelper - # In model which contains this helper should be: - # - #abf_worker_args - # - #build_canceled +module AbfWorkerMethods + extend ActiveSupport::Concern MASS_BUILDS_SET = 'abf-worker::mass-builds' USER_BUILDS_SET = 'abf-worker::user-builds' - def self.included(base) - base.extend(ClassMethods) - end - module ClassMethods def log_server @log_server ||= Redis.new( @@ -18,10 +12,9 @@ module AbfWorker::ModelHelper ) end - def self.next_build + def next_build raise NotImplementedError end - end def abf_worker_log @@ -106,4 +99,5 @@ module AbfWorker::ModelHelper "abfworker::#{abf_worker_base_queue.gsub(/\_/, '-')}-#{id}" end -end \ No newline at end of file + +end diff --git a/app/models/concerns/build_list_observer.rb b/app/models/concerns/build_list_observer.rb index fc3596cd7..2ff664d5b 100644 --- a/app/models/concerns/build_list_observer.rb +++ b/app/models/concerns/build_list_observer.rb @@ -3,20 +3,30 @@ module BuildListObserver included do before_update :update_average_build_time + before_update :update_statistic end private + def update_statistic + Statistic.statsd_increment( + activity_at: Time.now, + key: "build_list.#{status}", + project_id: project_id, + user_id: user_id, + ) if status_changed? + end + def update_average_build_time if status_changed? - started_at = Time.now if status == self.class::BUILD_STARTED + self.started_at = Time.now if status == self.class::BUILD_STARTED if [self.class::BUILD_ERROR, self.class::SUCCESS, self.class::BUILD_CANCELING, self.class::TESTS_FAILED, self.class::BUILD_CANCELED].include? status # stores time interval beetwin build start and finish in seconds - duration = current_duration if started_at + self.duration = current_duration if self.started_at if status == self.class::SUCCESS # Update project average build time diff --git a/app/models/concerns/empty_metadata.rb b/app/models/concerns/empty_metadata.rb new file mode 100644 index 000000000..3f3707480 --- /dev/null +++ b/app/models/concerns/empty_metadata.rb @@ -0,0 +1,13 @@ +module EmptyMetadata + extend ActiveSupport::Concern + + included do + after_create :create_empty_metadata + end + + def create_empty_metadata + return if is_a?(Platform) && ( personal? || hidden? ) + Resque.enqueue(CreateEmptyMetadataJob, self.class.name, id) + end + +end diff --git a/app/models/concerns/feed/issue.rb b/app/models/concerns/feed/issue.rb index 5d18f8f35..f57f0fce0 100644 --- a/app/models/concerns/feed/issue.rb +++ b/app/models/concerns/feed/issue.rb @@ -17,7 +17,7 @@ module Feed::Issue 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 + UserMailer.new_issue_notification(id, recipient.id).deliver end ActivityFeed.create( user: recipient, diff --git a/app/models/concerns/file_store_clean.rb b/app/models/concerns/file_store_clean.rb index 7568ce835..6a2f2ed08 100644 --- a/app/models/concerns/file_store_clean.rb +++ b/app/models/concerns/file_store_clean.rb @@ -14,17 +14,8 @@ module FileStoreClean def destroy_files_from_file_store(args = sha1_of_file_store_files) files = *args - token = FileStoreClean.file_store_authentication_token - uri = URI APP_CONFIG['file_store_url'] - Net::HTTP.start(uri.host, uri.port) do |http| - files.each do |sha1| - begin - req = Net::HTTP::Delete.new("/api/v1/file_stores/#{sha1}.json") - req.basic_auth token, '' - http.request(req) - rescue # Dont care about it - end - end + files.each do |sha1| + FileStoreService::File.new(sha1: sha1).destroy end end @@ -34,51 +25,4 @@ module FileStoreClean later :later_destroy_files_from_file_store, queue: :middle end - def self.file_store_authentication_token - User.find_by(uname: 'file_store').authentication_token - end - - # @param [Hash] data: - # - [String] path - path to file - # - [String] fullname - file name - def self.save_file_to_file_store(data) - sha1_hash = Digest::SHA1.hexdigest(File.read(data[:path])) - return sha1_hash if file_exist_on_file_store?(sha1_hash) - - begin - resource = RestClient::Resource.new( - "#{APP_CONFIG['file_store_url']}/api/v1/upload", - user: file_store_authentication_token - ) - - file = File.new(data[:path]) - # Hook for RestClient - # See: [RestClient::Payload#create_file_field](https://github.com/rest-client/rest-client/blob/master/lib/restclient/payload.rb#L202-L215) - file.define_singleton_method(:original_filename) { data[:fullname] } - resp = resource.post(file_store: { file: file }) - resp = JSON(resp) - rescue RestClient::UnprocessableEntity => e # 422, file already exist - return sha1_hash - rescue # Dont care about it - return nil - end - if resp.respond_to?(:[]) && resp['sha1_hash'].present? - resp['sha1_hash'] - else - nil - end - end - - def self.file_exist_on_file_store?(sha1) - begin - resp = JSON(RestClient.get "#{APP_CONFIG['file_store_url']}/api/v1/file_stores.json", params: {hash: sha1}) - rescue # Dont care about it - resp = [] - end - if resp[0].respond_to?('[]') && resp[0]['file_name'] && resp[0]['sha1_hash'] - true - else - false - end - end end diff --git a/app/models/concerns/git.rb b/app/models/concerns/git.rb index a19fb1917..5c097374a 100644 --- a/app/models/concerns/git.rb +++ b/app/models/concerns/git.rb @@ -34,6 +34,14 @@ module Git repo.tags.map(&:name) + repo.branches.map(&:name) end + def find_blob_and_raw_of_spec_file(project_version) + 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) + [blob, raw] + 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) @@ -198,7 +206,7 @@ module Git package = link.attributes['href'].value package.chomp!; package.strip! - next if package.size == 0 || package !~ /^[\w\.\-]+$/ + next if package.size == 0 || package !~ Project::NAME_REGEXP next if filter.present? && !filter.include?(package) uri = URI "#{url}/#{package}" diff --git a/app/models/concerns/personal_repository.rb b/app/models/concerns/personal_repository.rb index ed03d9094..f7865d18d 100644 --- a/app/models/concerns/personal_repository.rb +++ b/app/models/concerns/personal_repository.rb @@ -7,17 +7,17 @@ module PersonalRepository def create_personal_repository begin - pl = own_platforms.build - pl.owner = self - pl.name = "#{self.uname}_personal" - pl.description = "#{self.uname}_personal" - pl.platform_type = 'personal' - pl.distrib_type = APP_CONFIG['distr_types'].first - pl.visibility = 'open' + pl = own_platforms.build + pl.owner = self + pl.name = "#{self.uname}_personal" + pl.description = "#{self.uname}_personal" + pl.platform_type = Platform::TYPE_PERSONAL + pl.distrib_type = APP_CONFIG['distr_types'].first + pl.visibility = Platform::VISIBILITY_OPEN pl.save! - rep = pl.repositories.build - rep.name = 'main' + rep = pl.repositories.build + rep.name = 'main' rep.description = 'main' rep.save! rescue Exception => e diff --git a/app/models/key_pair.rb b/app/models/key_pair.rb index 525a1ecd5..b0fd168f9 100644 --- a/app/models/key_pair.rb +++ b/app/models/key_pair.rb @@ -1,3 +1,4 @@ +require 'open3' class KeyPair < ActiveRecord::Base belongs_to :repository belongs_to :user diff --git a/app/models/mass_build.rb b/app/models/mass_build.rb index 096ccb748..bca7ee22e 100644 --- a/app/models/mass_build.rb +++ b/app/models/mass_build.rb @@ -1,4 +1,35 @@ class MassBuild < ActiveRecord::Base + + AUTO_PUBLISH_STATUSES = %w(none default testing) + + STATUSES, HUMAN_STATUSES = [], {} + [ + %w(SUCCESS 0), + %w(BUILD_STARTED 3000), + %w(BUILD_PENDING 2000), + ].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 + + state_machine :status, initial: :build_pending do + event :start do + transition build_pending: :build_started + end + + event :done do + transition build_started: :success + end + + HUMAN_STATUSES.each do |code,name| + state name, value: code + end + end + belongs_to :build_for_platform, -> { where(platform_type: 'main') }, class_name: 'Platform' belongs_to :save_to_platform, class_name: 'Platform' belongs_to :user @@ -6,15 +37,17 @@ class MassBuild < ActiveRecord::Base serialize :extra_repositories, Array serialize :extra_build_lists, Array + serialize :extra_mass_builds, Array - scope :recent, -> { order(created_at: :desc) } - scope :by_platform, -> (platform) { where(save_to_platform_id: platform.id) } - scope :outdated, -> { where("#{table_name}.created_at < ?", Time.now + 1.day - BuildList::MAX_LIVE_TIME) } + scope :recent, -> { order(created_at: :desc) } + scope :outdated, -> { where("#{table_name}.created_at < ?", Time.now + 1.day - BuildList::MAX_LIVE_TIME) } + scope :search, -> (q) { where("#{table_name}.description ILIKE ?", "%#{q}%") if q.present? } attr_accessor :arches - attr_accessible :arches, :auto_publish, :projects_list, :build_for_platform_id, + attr_accessible :arches, :auto_publish_status, :projects_list, :build_for_platform_id, :extra_repositories, :extra_build_lists, :increase_release_tag, - :use_cached_chroot, :use_extra_tests + :use_cached_chroot, :use_extra_tests, :description, :extra_mass_builds, + :include_testing_subrepository, :auto_create_container validates :save_to_platform_id, :build_for_platform_id, @@ -27,13 +60,18 @@ class MassBuild < ActiveRecord::Base presence: true, length: { maximum: 500_000 } - validates :auto_publish, - :increase_release_tag, + validates :description, + length: { maximum: 255 } + + validates :auto_publish_status, + inclusion: { in: AUTO_PUBLISH_STATUSES } + + validates :increase_release_tag, :use_cached_chroot, :use_extra_tests, inclusion: { in: [true, false] } - after_commit :build_all, on: :create + after_commit :build_all, on: :create, if: Proc.new { |mb| mb.extra_mass_builds.blank? } before_validation :set_data, on: :create COUNT_STATUSES = %i( @@ -48,6 +86,7 @@ class MassBuild < ActiveRecord::Base ) def build_all + return unless start # later with resque arches_list = arch_names ? Arch.where(name: arch_names.split(', ')) : Arch.all @@ -72,6 +111,7 @@ class MassBuild < ActiveRecord::Base update_column :missed_projects_list, list end end + done end later :build_all, queue: :low @@ -83,6 +123,10 @@ class MassBuild < ActiveRecord::Base generate_list BuildList::TESTS_FAILED end + def generate_success_builds_list + generate_list BuildList::SUCCESS + end + def cancel_all update_column(:stop_build, true) build_lists.find_each(batch_size: 100) do |bl| diff --git a/app/models/platform.rb b/app/models/platform.rb index 96b34c238..0bf0772f1 100644 --- a/app/models/platform.rb +++ b/app/models/platform.rb @@ -6,14 +6,23 @@ class Platform < ActiveRecord::Base include RegenerationStatus include Owner include EventLoggable + include EmptyMetadata - CACHED_CHROOT_PRODUCT_NAME = 'cached-chroot' - AUTOMATIC_METADATA_REGENERATIONS = %w(day week) - VISIBILITIES = %w(open hidden) - NAME_PATTERN = /[\w\-\.]+/ - HUMAN_STATUSES = HUMAN_STATUSES.clone.freeze self.per_page = 20 + CACHED_CHROOT_PRODUCT_NAME = 'cached-chroot' + AUTOMATIC_METADATA_REGENERATIONS = %w(day week) + VISIBILITIES = [ + VISIBILITY_OPEN = 'open', + VISIBILITY_HIDDEN = 'hidden' + ] + NAME_PATTERN = /[\w\-\.]+/ + HUMAN_STATUSES = HUMAN_STATUSES.clone.freeze + TYPES = [ + TYPE_PERSONAL = 'personal', + TYPE_MAIN = 'main' + ] + belongs_to :parent, class_name: 'Platform', foreign_key: 'parent_platform_id' belongs_to :owner, polymorphic: true @@ -33,16 +42,36 @@ class Platform < ActiveRecord::Base has_many :mass_builds, foreign_key: :save_to_platform_id - validates :description, presence: true - validates :visibility, presence: true, inclusion: { in: VISIBILITIES } - validates :automatic_metadata_regeneration, inclusion: { in: AUTOMATIC_METADATA_REGENERATIONS }, allow_blank: true - 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'] } + validates :description, + presence: true + + validates :visibility, + presence: true, + inclusion: { in: VISIBILITIES } + + validates :platform_type, + presence: true, + inclusion: { in: TYPES } + + validates :automatic_metadata_regeneration, + inclusion: { in: AUTOMATIC_METADATA_REGENERATIONS }, + allow_blank: true + + 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 -> { if released_was && !released errors.add(:released, I18n.t('flash.platform.released_status_can_not_be_changed')) end } + validate -> { if personal? && (owner_id_changed? || owner_type_changed?) errors.add :owner, I18n.t('flash.platform.owner_can_not_be_changed') @@ -55,22 +84,32 @@ class Platform < ActiveRecord::Base after_update :freeze_platform_and_update_repos after_update :update_owner_relation - after_create -> { symlink_directory unless hidden? } + after_create -> { symlink_directory unless hidden? } after_destroy -> { remove_symlink_directory unless hidden? } - scope :search_order, -> { order(:name) } - scope :search, ->(q) { where("#{table_name}.name ILIKE ?", "%#{q.to_s.strip}%") } - scope :by_visibilities, ->(v) { where(visibility: v) } - scope :opened, -> { where(visibility: 'open') } - scope :hidden, -> { where(visibility: 'hidden') } - scope :by_type, ->(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) } + scope :search_order, -> { order(:name) } + scope :search, -> (q) { where("#{table_name}.name ILIKE ?", "%#{q.to_s.strip}%") } + scope :by_visibilities, -> (v) { where(visibility: v) } + scope :opened, -> { where(visibility: VISIBILITY_OPEN) } + scope :hidden, -> { where(visibility: VISIBILITY_HIDDEN) } + scope :by_type, -> (type) { where(platform_type: type) if type.present? } + scope :main, -> { by_type(TYPE_MAIN) } + scope :personal, -> { by_type(TYPE_PERSONAL) } + scope :waiting_for_regeneration, -> { where(status: WAITING_FOR_REGENERATION) } 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, :automatic_metadata_regeneration - attr_readonly :name, :distrib_type, :parent_platform_id, :platform_type + attr_accessible :name, + :distrib_type, + :parent_platform_id, + :platform_type, + :owner, + :visibility, + :description, + :released, + :platform_arch_settings_attributes, + :automatic_metadata_regeneration + + attr_readonly :name, :distrib_type, :parent_platform_id, :platform_type state_machine :status, initial: :ready do @@ -145,15 +184,15 @@ class Platform < ActiveRecord::Base end def hidden? - visibility == 'hidden' + visibility == VISIBILITY_HIDDEN end def personal? - platform_type == 'personal' + platform_type == TYPE_PERSONAL end def main? - platform_type == 'main' + platform_type == TYPE_MAIN end def base_clone(attrs = {}) # :description, :name, :owner @@ -176,10 +215,10 @@ class Platform < ActiveRecord::Base end def change_visibility - if !hidden? - update_attributes(visibility: 'hidden') + if hidden? + update_attributes(visibility: VISIBILITY_OPEN) else - update_attributes(visibility: 'open') + update_attributes(visibility: VISIBILITY_HIDDEN) end end @@ -191,6 +230,7 @@ class Platform < ActiveRecord::Base File.open(File.join(symlink_path, "#{name}.#{arch.name}.list"), 'w') {|f| f.write(str) } end end + later :symlink_directory, queue: :middle def remove_symlink_directory system("rm -Rf #{symlink_path}") @@ -263,7 +303,6 @@ class Platform < ActiveRecord::Base system("mkdir -p -m 0777 #{build_path([name, 'repository'])}") end - def build_path(dir) File.join(APP_CONFIG['root_path'], 'platforms', dir) end diff --git a/app/models/product_build_list.rb b/app/models/product_build_list.rb index 8f3e78951..5c8464928 100644 --- a/app/models/product_build_list.rb +++ b/app/models/product_build_list.rb @@ -3,7 +3,7 @@ class ProductBuildList < ActiveRecord::Base include TimeLiving include FileStoreClean include UrlHelper - include AbfWorker::ModelHelper + include AbfWorkerMethods include EventLoggable delegate :url_helpers, to: 'Rails.application.routes' diff --git a/app/models/project.rb b/app/models/project.rb index 044574de7..968ad763f 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -183,21 +183,23 @@ class Project < ActiveRecord::Base increase_release_tag(project_version, user, "MassBuild##{mass_build.id}: Increase release tag") if increase_rt build_list = build_lists.build do |bl| - 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_status = mass_build.auto_publish? ? BuildList::AUTO_PUBLISH_STATUS_DEFAULT : BuildList::AUTO_PUBLISH_STATUS_NONE - 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 - bl.use_cached_chroot = mass_build.use_cached_chroot? - bl.use_extra_tests = mass_build.use_extra_tests? + 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_status = mass_build.auto_publish_status + bl.auto_create_container = mass_build.auto_create_container + 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 + bl.include_testing_subrepository = mass_build.include_testing_subrepository? + bl.use_cached_chroot = mass_build.use_cached_chroot? + bl.use_extra_tests = mass_build.use_extra_tests? end build_list.save end @@ -225,10 +227,6 @@ class Project < ActiveRecord::Base end end - def destroy_project_from_repository(repository) - AbfWorker::BuildListsPublishTaskManager.destroy_project_from_repository self, repository - end - def default_head treeish = nil # maybe need change 'head'? # Attention! # repo.commit(nil) => @@ -245,10 +243,10 @@ class Project < ActiveRecord::Base format_id = ProjectTag::FORMATS["#{tag_file_format(format)}"] project_tag = project_tags.where(tag_name: tag.name, format_id: format_id).first - return project_tag.sha1 if project_tag && project_tag.commit_id == tag.commit.id && FileStoreClean.file_exist_on_file_store?(project_tag.sha1) + return project_tag.sha1 if project_tag && project_tag.commit_id == tag.commit.id && FileStoreService::File.new(sha1: project_tag.sha1).exist? archive = archive_by_treeish_and_format tag.name, format - sha1 = FileStoreClean.save_file_to_file_store(archive) + sha1 = FileStoreService::File.new(data: archive).save return nil if sha1.blank? if project_tag @@ -319,7 +317,7 @@ class Project < ActiveRecord::Base build_list = p.build_lists.build do |bl| bl.save_to_platform = repository.platform bl.build_for_platform = platform - bl.update_type = 'newpackage' + bl.update_type = BuildList::UPDATE_TYPE_NEWPACKAGE bl.arch_id = arch_id bl.project_version = p.project_version_for(repository.platform, platform) bl.user = user @@ -339,13 +337,10 @@ class Project < ActiveRecord::Base 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$/ } + blob, raw = find_blob_and_raw_of_spec_file(project_version) 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 @@ -356,6 +351,7 @@ class Project < ActiveRecord::Base ) end + protected def create_archive(treeish, format) file_name = "#{name}-#{treeish}" diff --git a/app/models/project_to_repository.rb b/app/models/project_to_repository.rb index 3f272d63d..83dbf97c3 100644 --- a/app/models/project_to_repository.rb +++ b/app/models/project_to_repository.rb @@ -8,7 +8,7 @@ class ProjectToRepository < ActiveRecord::Base scope :autostart_enabled, -> { where("autostart_options -> 'enabled' = 'true'") } - after_destroy -> { project.destroy_project_from_repository(repository) }, unless: -> { Thread.current[:skip] } + after_destroy :destroy_project_from_repository, unless: -> { Thread.current[:skip] } validate :one_project_in_platform_repositories, on: :create @@ -28,6 +28,10 @@ class ProjectToRepository < ActiveRecord::Base protected + def destroy_project_from_repository + DestroyProjectFromRepositoryJob.perform(project, repository) + end + def one_project_in_platform_repositories if Project.joins(repositories: :platform).where('platforms.id = ?', repository.platform_id).by_name(project.name).exists? errors.add(:base, I18n.t('activerecord.errors.project_to_repository.project')) diff --git a/app/models/pull_request.rb b/app/models/pull_request.rb index b482a2e97..843012cd5 100644 --- a/app/models/pull_request.rb +++ b/app/models/pull_request.rb @@ -135,7 +135,7 @@ class PullRequest < ActiveRecord::Base # FIXME maybe move to warpc/grit? def diff - @diff ||= repo.diff(to_commit.id, from_commit.id) + @diff ||= Grit::Commit.diff(repo, to_commit.id, from_commit.id) end def set_user_and_time user @@ -180,6 +180,7 @@ class PullRequest < ActiveRecord::Base end def clone + return if from_project.nil? git = Grit::Git.new(path) if new_record? || !git.exist? #~ FileUtils.mkdir_p(path) diff --git a/app/models/repository.rb b/app/models/repository.rb index cb9fc2b36..c337f9750 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -3,6 +3,7 @@ class Repository < ActiveRecord::Base friendly_id :name, use: [:finders] include EventLoggable + include EmptyMetadata LOCK_FILE_NAMES = { sync: '.sync.lock', repo: '.repo.lock' } SORT = { 'base' => 1, 'main' => 2, 'contrib' => 3, 'non-free' => 4, 'restricted' => 5 } @@ -28,7 +29,7 @@ class Repository < ActiveRecord::Base scope :recent, -> { order(:name) } scope :main, -> { where(name: %w(main base)) } - before_destroy :detele_directory + before_destroy :detele_directory attr_accessible :name, :description, @@ -66,6 +67,7 @@ class Repository < ActiveRecord::Base end end later :clone_relations, loner: true, queue: :low + def add_projects(list, user) current_ability = Ability.new(user) list.lines.each do |line| diff --git a/app/models/search.rb b/app/models/search.rb index e62de9dd5..b25a9f046 100644 --- a/app/models/search.rb +++ b/app/models/search.rb @@ -1,13 +1,13 @@ class Search TYPES = ['projects', 'users', 'groups', 'platforms'] - def self.by_term_and_type(term, type, paginate_params) + def self.by_term_and_type(term, type, ability, paginate_params) results = {} case type when 'all' - TYPES.each{ |t| results[t] = find_collection(t, term, paginate_params) } + TYPES.each{ |t| results[t] = find_collection(t, term, ability, paginate_params) } when *TYPES - results[type] = find_collection(type, term, paginate_params) + results[type] = find_collection(type, term, ability, paginate_params) end results end @@ -15,11 +15,15 @@ class Search class << self protected - def find_collection(type, term, paginate_params) - type.classify.constantize.opened. - search(term). - search_order. - paginate(paginate_params) + def find_collection(type, term, ability, paginate_params) + scope = if type == 'users' + User.opened + else + type.classify.constantize.accessible_by(ability, :show) + end + scope.search(term). + search_order. + paginate(paginate_params) end end end \ No newline at end of file diff --git a/app/models/statistic.rb b/app/models/statistic.rb new file mode 100644 index 000000000..0d876831a --- /dev/null +++ b/app/models/statistic.rb @@ -0,0 +1,88 @@ +class Statistic < ActiveRecord::Base + belongs_to :user + belongs_to :project + + validates :user_id, + uniqueness: { scope: [:project_id, :key, :activity_at] }, + presence: true + + validates :email, + presence: true + + validates :project_id, + presence: true + + validates :project_name_with_owner, + presence: true + + validates :key, + presence: true + + validates :counter, + presence: true + + validates :activity_at, + presence: true + + attr_accessible :user_id, + :email, + :project_id, + :project_name_with_owner, + :key, + :counter, + :activity_at + + scope :for_period, -> (start_date, end_date) { where(activity_at: (start_date..end_date)) } + + scope :build_lists_started, -> { where(key: "build_list.#{BuildList::BUILD_STARTED}") } + scope :build_lists_success, -> { where(key: "build_list.#{BuildList::SUCCESS}") } + scope :build_lists_error, -> { where(key: "build_list.#{BuildList::BUILD_ERROR}") } + scope :build_lists_published, -> { where(key: "build_list.#{BuildList::BUILD_PUBLISHED}") } + + + + def self.now_statsd_increment(activity_at: nil, user_id: nil, project_id: nil, key: nil) + # Truncates a DateTime to the minute + activity_at = activity_at.utc.change(min: 0) + user = User.find user_id + project = Project.find project_id + Statistic.create( + user_id: user_id, + email: user.email, + project_id: project_id, + project_name_with_owner: project.name_with_owner, + key: key, + activity_at: activity_at + ) + ensure + Statistic.where( + user_id: user_id, + project_id: project_id, + key: key, + activity_at: activity_at + ).update_all('counter = counter + 1') + end + + # TODO: remove later + def self.fill_in_build_lists + BuildList.find_each do |bl| + Statistic.now_statsd_increment({ + activity_at: bl.created_at, + key: "build_list.#{BuildList::BUILD_STARTED}", + project_id: bl.project_id, + user_id: bl.user_id, + }) + Statistic.now_statsd_increment({ + activity_at: bl.updated_at, + key: "build_list.#{bl.status}", + project_id: bl.project_id, + user_id: bl.user_id, + }) + end + end + + def self.statsd_increment(options = {}) + Statistic.perform_later(:middle, :now_statsd_increment, options) + end + +end diff --git a/app/models/user.rb b/app/models/user.rb index ed10519fd..e0913a290 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -51,7 +51,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, :sound_notifications + :site, :company, :professional_experience, :location, :sound_notifications, :hide_email attr_readonly :uname attr_accessor :login diff --git a/app/presenters/abf_worker_status_presenter.rb b/app/presenters/abf_worker_status_presenter.rb new file mode 100644 index 000000000..da0ae4512 --- /dev/null +++ b/app/presenters/abf_worker_status_presenter.rb @@ -0,0 +1,59 @@ +class AbfWorkerStatusPresenter < ApplicationPresenter + + def initialize + end + + def projects_status + Rails.cache.fetch([AbfWorkerStatusPresenter, :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 + count_of_tasks('user_build_') + + mass_build_tasks = count_of_tasks('mass_build_') + result[:rpm][:low_tasks] += mass_build_tasks + result[:rpm][:tasks] += external_bls + mass_build_tasks + result + end + end + + def products_status + get_status(:iso) { |w, worker| + str = w.to_s + str =~ /iso_worker/ && str !~ /observer/ + } + end + + protected + + def count_of_tasks(regexp) + Redis.current.smembers('resque:queues'). + select{ |q| q =~ /#{regexp}/ }. + map{ |q| Redis.current.llen("resque:queue:#{q}") }.sum + end + + def get_status(*queues) + status = {} + queues.each do |worker| + workers = Resque.workers.select{ |w| yield w, worker } + status[worker] = status_of_worker workers, worker + end + status + end + + def status_of_worker(workers, worker) + key = "resque:queue:#{worker}_worker" + default_tasks, tasks = Redis.current.llen("#{key}_default"), Redis.current.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 +end diff --git a/app/presenters/git_presenters/commit_as_message_presenter.rb b/app/presenters/git_presenters/commit_as_message_presenter.rb index 420b45ca5..2248fb9ac 100644 --- a/app/presenters/git_presenters/commit_as_message_presenter.rb +++ b/app/presenters/git_presenters/commit_as_message_presenter.rb @@ -61,7 +61,7 @@ class GitPresenters::CommitAsMessagePresenter < ApplicationPresenter end def content? - !content.blank? + content.present? end def caption? @@ -108,7 +108,7 @@ class GitPresenters::CommitAsMessagePresenter < ApplicationPresenter @content = (@content.present?) ? tmp + @content : tmp @caption = @caption[0..68] + '...' end -# @content = @content.gsub("\n", "
").html_safe if @content + # @content = @content.gsub("\n", "
").html_safe if @content @content = simple_format(@content, {}, sanitize: true).html_safe if @content end end diff --git a/app/presenters/statistic_presenter.rb b/app/presenters/statistic_presenter.rb new file mode 100644 index 000000000..dafe97563 --- /dev/null +++ b/app/presenters/statistic_presenter.rb @@ -0,0 +1,71 @@ +class StatisticPresenter < ApplicationPresenter + + attr_accessor :range_start, :range_end, :unit + + def initialize(range_start: nil, range_end: nil, unit: nil) + @range_start = range_start + @range_end = range_end + @unit = unit + end + + def as_json(options = nil) + { + build_lists: { + build_started: prepare_collection(build_lists_started), + success: prepare_collection(build_lists_success), + build_error: prepare_collection(build_lists_error), + build_published: prepare_collection(build_lists_published), + + build_started_count: build_lists_started.sum(&:count), + success_count: build_lists_success.sum(&:count), + build_error_count: build_lists_error.sum(&:count), + build_published_count: build_lists_published.sum(&:count), + } + } + end + + private + + def scope + @scope ||= Statistic.for_period(range_start, range_end) + end + + def build_lists + @build_lists ||= scope. + select("SUM(counter) as count, date_trunc('#{ unit }', activity_at) as activity_at"). + group("date_trunc('#{ unit }', activity_at)").order('activity_at') + end + + def build_lists_started + @build_lists_started ||= build_lists.build_lists_started.to_a + end + + def build_lists_success + @build_lists_success ||= build_lists.build_lists_success.to_a + end + + def build_lists_error + @build_lists_error ||= build_lists.build_lists_error.to_a + end + + def build_lists_published + @build_lists_published ||= build_lists.build_lists_published.to_a + end + + def prepare_collection(items) + data = [] + to = range_start + while to <= range_end + from = to - 1.send(unit) + y = items.find{ |i| i.activity_at > from && i.activity_at <= to }.try(:count) + data << { x: to.strftime(format), y: y || 0 } + to += 1.send(unit) + end + data + end + + def format + @format ||= unit == :hour ? '%H:%M' : '%Y-%m-%d' + end + +end diff --git a/app/services/abf_worker_service/base.rb b/app/services/abf_worker_service/base.rb new file mode 100644 index 000000000..13e183de9 --- /dev/null +++ b/app/services/abf_worker_service/base.rb @@ -0,0 +1,83 @@ +module AbfWorkerService + class Base + + REDIS_MAIN_KEY = 'abf-worker::build-lists-publish-task-manager::' + + %w( + PROJECTS_FOR_CLEANUP + LOCKED_PROJECTS_FOR_CLEANUP + LOCKED_BUILD_LISTS + PACKAGES_FOR_CLEANUP + REP_AND_PLS_OF_BUILD_LISTS_FOR_CLEANUP_FROM_TESTING + BUILD_LISTS_FOR_CLEANUP_FROM_TESTING + ).each do |kind| + const_set kind, "#{REDIS_MAIN_KEY}#{kind.downcase.gsub('_', '-')}" + end + + def self.cleanup_completed(projects_for_cleanup) + projects_for_cleanup.each do |key| + Redis.current.lrem LOCKED_PROJECTS_FOR_CLEANUP, 0, key + Redis.current.hdel PACKAGES_FOR_CLEANUP, key + end + end + + def self.cleanup_failed(projects_for_cleanup) + projects_for_cleanup.each do |key| + Redis.current.lrem LOCKED_PROJECTS_FOR_CLEANUP, 0, key + Redis.current.lpush PROJECTS_FOR_CLEANUP, key + end + end + + def self.cleanup_packages_from_testing(platform_id, repository_id, *build_lists) + return if build_lists.blank? + rep_pl = "#{repository_id}-#{platform_id}" + key = "#{BUILD_LISTS_FOR_CLEANUP_FROM_TESTING}-#{rep_pl}" + Redis.current.sadd REP_AND_PLS_OF_BUILD_LISTS_FOR_CLEANUP_FROM_TESTING, rep_pl + Redis.current.sadd key, build_lists + end + + def self.unlock_build_list(build_list) + Redis.current.lrem LOCKED_BUILD_LISTS, 0, build_list.id + end + + protected + + def packages_structure + structure = {sources: [], binaries: {}} + Arch.pluck(:name).each{ |name| structure[:binaries][name.to_sym] = [] } + structure + end + + def fill_packages(bl, results_map, field = :sha1) + results_map[:sources] |= bl.packages.by_package_type('source').pluck(field).compact if field != :sha1 + + binaries = bl.packages.by_package_type('binary').pluck(field).compact + arch = bl.arch.name.to_sym + results_map[:binaries][arch] |= binaries + # Publish/remove i686 RHEL packages into/from x86_64 + if arch == :i586 && bl.build_for_platform.distrib_type == 'rhel' && bl.project.publish_i686_into_x86_64? + results_map[:binaries][:x86_64] |= binaries + end + end + + def filter_build_lists_without_packages(*build_lists) + ids = [] + build_lists = build_lists.flatten.select do |build_list| + sha1 = build_list.packages.pluck(:sha1).find do |sha1| + !FileStoreService::File.new(sha1: sha1).exist? + end + if sha1.present? + ids << build_list.id + false + else + true + end + end + + BuildList.where(id: ids).update_all(status: BuildList::PACKAGES_FAIL) + + build_lists + end + + end +end \ No newline at end of file diff --git a/app/services/abf_worker_service/container.rb b/app/services/abf_worker_service/container.rb new file mode 100644 index 000000000..dee8813a5 --- /dev/null +++ b/app/services/abf_worker_service/container.rb @@ -0,0 +1,81 @@ +module AbfWorkerService + class Container < Base + + attr_accessor :build_list + + def initialize(build_list) + @build_list = build_list + end + + def create! + cleanup_folder + + if filter_build_lists_without_packages(build_list).blank? + build_list.fail_publish_container + return + end + + Resque.push( + 'publish_worker', # Low priority + 'class' => 'AbfWorker::PublishWorker', + 'args' => [{ + id: build_list.id, + cmd_params: cmd_params, + 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}, + time_living: 9600, # 160 min + packages: packages, + old_packages: packages_structure, + build_list_ids: [build_list.id], + projects_for_cleanup: [], + extra: {create_container: true} + }] + ) + end + + def destroy! + system "rm -rf #{platform_path}" + end + + protected + + def cmd_params + { + '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(' ') + end + + def cleanup_folder + system "rm -rf #{platform_path} && mkdir -p #{platform_path}" + end + + def platform_path + @platform_path ||= "#{build_list.save_to_platform.path}/container/#{build_list.id}" + end + + def distrib_type + @distrib_type ||= build_list.build_for_platform.distrib_type + end + + def packages + structure = packages_structure + structure[:sources] = build_list.packages.by_package_type('source').pluck(:sha1).compact + structure[:binaries][build_list.arch.name.to_sym] = build_list.packages.by_package_type('binary').pluck(:sha1).compact + structure + end + + end +end \ No newline at end of file diff --git a/app/services/abf_worker_service/platform_metadata.rb b/app/services/abf_worker_service/platform_metadata.rb new file mode 100644 index 000000000..d59757d7f --- /dev/null +++ b/app/services/abf_worker_service/platform_metadata.rb @@ -0,0 +1,59 @@ +module AbfWorkerService + class PlatformMetadata < Base + + attr_accessor :platform + + def initialize(platform) + @platform = platform + end + + def regenerate! + return unless can_regenerate? + + 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 + + protected + + def can_regenerate? + repos = platform.repositories + return false if repos.find{ |r| r.repo_lock_file_exists? } + + statuses = RepositoryStatus.where(platform_id: platform.id) + return true if statuses.blank? + + statuses = statuses.map do |s| + s.ready? || s.can_start_regeneration? || s.can_start_resign? + end.uniq + statuses == [true] + end + + def 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(' ') + end + + end +end \ No newline at end of file diff --git a/app/services/abf_worker_service/repository.rb b/app/services/abf_worker_service/repository.rb new file mode 100644 index 000000000..3e05616a4 --- /dev/null +++ b/app/services/abf_worker_service/repository.rb @@ -0,0 +1,92 @@ +module AbfWorkerService + class Repository < Base + + attr_accessor :repository + + def initialize(repository) + @repository = repository + end + + def destroy_project!(project) + if repository.platform.personal? + Platform.main.each do |main_platform| + key = "#{project.id}-#{repository.id}-#{main_platform.id}" + Redis.current.lpush PROJECTS_FOR_CLEANUP, key + gather_old_packages project.id, repository.id, main_platform.id + + Redis.current.lpush PROJECTS_FOR_CLEANUP, ('testing-' << key) + gather_old_packages project.id, repository.id, main_platform.id, true + end + else + key = "#{project.id}-#{repository.id}-#{repository.platform_id}" + Redis.current.lpush PROJECTS_FOR_CLEANUP, key + gather_old_packages project.id, repository.id, repository.platform_id + + Redis.current.lpush PROJECTS_FOR_CLEANUP, ('testing-' << key) + gather_old_packages project.id, repository.id, repository.platform_id, true + end + end + + def resign!(repository_status) + return if repository.repo_lock_file_exists? + + Resque.push( + 'publish_worker_default', + 'class' => "AbfWorker::PublishWorkerDefault", + 'args' => [{ + id: repository.id, + cmd_params: cmd_params, + main_script: 'resign.sh', + platform: { + platform_path: "#{repository.platform.path}/repository", + type: distrib_type, + name: repository.platform.name, + arch: 'x86_64' + }, + repository: {id: repository.id}, + skip_feedback: true, + time_living: 9600, # 160 min + extra: {repository_status_id: repository_status.id, resign: true} + }] + ) if repository_status.start_resign + end + + protected + + def cmd_params + { + 'RELEASED' => repository.platform.released, + 'REPOSITORY_NAME' => repository.name, + 'TYPE' => distrib_type + }.map{ |k, v| "#{k}=#{v}" }.join(' ') + end + + def distrib_type + @distrib_type ||= repository.platform.distrib_type + end + + 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: status). + where(save_to_repository_id: repository_id). + where(build_for_platform_id: platform_id). + where(arch_id: arch_id). + order(:updated_at).first + build_lists_for_cleanup << bl if bl + end + + old_packages = packages_structure + build_lists_for_cleanup.each do |bl| + bl.last_published(testing).includes(:packages).limit(2).each do |old_bl| + fill_packages(old_bl, old_packages, :fullname) + end + end + key = (testing ? 'testing-' : '') << "#{project_id}-#{repository_id}-#{platform_id}" + Redis.current.hset PACKAGES_FOR_CLEANUP, key, old_packages.to_json + end + + end +end \ No newline at end of file diff --git a/app/services/abf_worker_service/repository_metadata.rb b/app/services/abf_worker_service/repository_metadata.rb new file mode 100644 index 000000000..880f2fa5b --- /dev/null +++ b/app/services/abf_worker_service/repository_metadata.rb @@ -0,0 +1,62 @@ +module AbfWorkerService + class RepositoryMetadata < Base + + attr_accessor :repository, :repository_status + + def initialize(repository_status) + @repository_status = repository_status + @repository = repository_status.repository + end + + def regenerate! + # Checks mirror sync status + return if repository.repo_lock_file_exists? + + platform_path = "#{repository.platform.path}/repository" + if repository.platform.personal? + platform_path << '/' << build_for_platform.name + system "mkdir -p #{platform_path}" + end + + Resque.push( + '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' + }, + repository: {id: repository.id}, + # time_living: 9600, # 160 min + time_living: 14400, # 240 min + extra: {repository_status_id: repository_status.id, regenerate: true} + }] + ) if repository_status.start_regeneration + + end + + protected + + def build_for_platform + @build_for_platform ||= repository_status.platform + end + + def cmd_params + { + 'RELEASED' => repository.platform.released, + 'REPOSITORY_NAME' => repository.name, + 'TYPE' => build_for_platform.distrib_type, + 'REGENERATE_METADATA' => true, + 'SAVE_TO_PLATFORM' => repository.platform.name, + 'BUILD_FOR_PLATFORM' => build_for_platform.name + }.map{ |k, v| "#{k}=#{v}" }.join(' ') + end + + end +end \ No newline at end of file diff --git a/app/services/abf_worker_service/rpm.rb b/app/services/abf_worker_service/rpm.rb new file mode 100644 index 000000000..64dca5dc7 --- /dev/null +++ b/app/services/abf_worker_service/rpm.rb @@ -0,0 +1,241 @@ +module AbfWorkerService + class Rpm < Base + + WORKERS_COUNT = APP_CONFIG['abf_worker']['publish_workers_count'] + + attr_accessor :save_to_repository_id, :build_for_platform_id, :testing + + def initialize(save_to_repository_id, build_for_platform_id, testing) + @save_to_repository_id = save_to_repository_id + @build_for_platform_id = build_for_platform_id + @testing = testing + end + + def self.publish! + build_rpms + build_rpms(true) + end + + def self.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: (testing ? BuildList::BUILD_PUBLISH_INTO_TESTING : BuildList::BUILD_PUBLISH)). + group(:save_to_repository_id, :build_for_platform_id). + order('min_updated_at ASC'). + limit(WORKERS_COUNT * 2) # because some repos may be locked + + 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? + + for_cleanup = Redis.current.lrange(PROJECTS_FOR_CLEANUP, 0, -1).map do |key| + 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 + + for_cleanup_from_testing = Redis.current.smembers(REP_AND_PLS_OF_BUILD_LISTS_FOR_CLEANUP_FROM_TESTING).map do |key| + next if Redis.current.scard("#{BUILD_LISTS_FOR_CLEANUP_FROM_TESTING}-#{key}") == 0 + rep, pl = *key.split('-') + locked_rep.present? && locked_rep.include?(rep.to_i) ? nil : [rep.to_i, pl.to_i] + end.compact if testing + for_cleanup_from_testing ||= [] + + counter = 1 + available_repos = available_repos.map{ |bl| [bl.save_to_repository_id, bl.build_for_platform_id] } | for_cleanup | for_cleanup_from_testing + available_repos.each do |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 + service = AbfWorkerService::Rpm.new( + save_to_repository_id, + build_for_platform_id, + testing + ) + counter += 1 if service.create + end + end + + def create + key = "#{save_to_repository_id}-#{build_for_platform_id}" + projects_for_cleanup = Redis.current.lrange(PROJECTS_FOR_CLEANUP, 0, -1).select do |k| + (testing && k =~ /^testing-[\d]+-#{key}$/) || (!testing && k =~ /^[\d]+-#{key}$/) + end + + prepare_build_lists(projects_for_cleanup) + + projects_for_cleanup.each do |key| + Redis.current.lrem PROJECTS_FOR_CLEANUP, 0, key + packages = Redis.current.hget PACKAGES_FOR_CLEANUP, key + next unless packages + packages = JSON.parse packages + old_packages[:sources] |= packages['sources'] + Arch.pluck(:name).each do |arch| + old_packages[:binaries][arch.to_sym] |= packages['binaries'][arch] || [] + end + end + + if testing + build_lists_for_cleanup_from_testing = Redis.current.smembers("#{BUILD_LISTS_FOR_CLEANUP_FROM_TESTING}-#{save_to_repository_id}-#{build_for_platform_id}") + BuildList.where(id: build_lists_for_cleanup_from_testing).each do |b| + fill_packages(b, old_packages, :fullname) + end if build_lists_for_cleanup_from_testing.present? + end + build_lists_for_cleanup_from_testing ||= [] + + bl = build_lists[0] + return false if !bl && old_packages[:sources].empty? && old_packages[:binaries].values.flatten.empty? + + # 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 + + options = { + 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}, + time_living: 9600, # 160 min + extra: { + repository_status_id: repository_status.id, + build_lists_for_cleanup_from_testing: build_lists_for_cleanup_from_testing + } + } + + packages, build_list_ids, new_sources = fill_in_packages + push(options.merge({ + packages: packages, + old_packages: old_packages, + build_list_ids: build_list_ids, + projects_for_cleanup: projects_for_cleanup + })) + lock_projects(projects_for_cleanup) + cleanup(build_lists_for_cleanup_from_testing) + return true + end + + protected + + def platform_path + @platform_path ||= begin + path = "#{save_to_platform.path}/repository" + if save_to_platform.personal? + path << '/' << build_for_platform.name + system "mkdir -p #{path}" + end + path + end + end + + def cmd_params + { + '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(' ') + end + + def save_to_repository + @save_to_repository ||= ::Repository.find(save_to_repository_id) + end + + def save_to_platform + @save_to_platform ||= save_to_repository.platform + end + + def build_for_platform + @build_for_platform ||= Platform.find(build_for_platform_id) + end + + def distrib_type + @distrib_type ||= build_for_platform.distrib_type + end + + def old_packages + @old_packages ||= packages_structure + end + + def fill_in_packages + packages, build_list_ids, new_sources = 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 } + fill_packages(bl, packages) + bl.last_published(testing).includes(:packages).limit(2).each{ |old_bl| + 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| + fill_packages(old_bl, old_packages, :fullname) + } if testing + build_list_ids << bl.id + Redis.current.lpush(LOCKED_BUILD_LISTS, bl.id) + end + packages[:sources] = new_sources.values.compact + + [packages, build_list_ids, new_sources] + end + + def lock_projects(projects_for_cleanup) + projects_for_cleanup.each do |key| + Redis.current.lpush LOCKED_PROJECTS_FOR_CLEANUP, key + end + end + + def cleanup(build_lists_for_cleanup_from_testing) + rep_pl = "#{save_to_repository_id}-#{build_for_platform_id}" + r_key = "#{BUILD_LISTS_FOR_CLEANUP_FROM_TESTING}-#{rep_pl}" + build_lists_for_cleanup_from_testing.each do |key| + Redis.current.srem r_key, key + end + if Redis.current.scard(r_key) == 0 + Redis.current.srem REP_AND_PLS_OF_BUILD_LISTS_FOR_CLEANUP_FROM_TESTING, rep_pl + end + end + + def push(options) + Resque.push( + 'publish_worker_default', + 'class' => 'AbfWorker::PublishWorkerDefault', + 'args' => [options] + ) + end + + def prepare_build_lists(projects_for_cleanup) + # 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('-')[testing ? 1 : 0] }.uniq, + save_to_repository_id: save_to_repository_id, + status: [BuildList::BUILD_PUBLISH, BuildList::BUILD_PUBLISH_INTO_TESTING] + ).update_all(status: BuildList::FAILED_PUBLISH) + end + + def build_lists + @build_lists ||= begin + build_lists = BuildList. + 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.current.lrange(LOCKED_BUILD_LISTS, 0, -1) + build_lists = build_lists.where('build_lists.id NOT IN (?)', locked_ids) if locked_ids.present? + build_lists = build_lists.limit(150) + filter_build_lists_without_packages(build_lists.to_a) + end + end + + end +end \ No newline at end of file diff --git a/app/services/file_store_service.rb b/app/services/file_store_service.rb new file mode 100644 index 000000000..9bf7fcff1 --- /dev/null +++ b/app/services/file_store_service.rb @@ -0,0 +1,70 @@ +module FileStoreService + class File + + URL = APP_CONFIG['file_store_url'] + + attr_accessor :sha1, :data + + # @param [String] sha1 + # @param [Hash] data: + # - [String] path - path to file + # - [String] fullname - file name + def initialize(sha1: nil, data: {}) + @sha1, @data = sha1, data + end + + def exist? + resp = JSON(RestClient.get "#{URL}/api/v1/file_stores.json", params: {hash: sha1}) + if resp[0].respond_to?('[]') && resp[0]['file_name'] && resp[0]['sha1_hash'] + true + else + false + end + rescue # Dont care about it + return false + end + + def save + sha1 = Digest::SHA1.hexdigest(::File.read(data[:path])) + return sha1 if exist? + + resource = RestClient::Resource.new("#{URL}/api/v1/upload", user: token) + file = ::File.new(data[:path]) + # Hook for RestClient + # See: [RestClient::Payload#create_file_field](https://github.com/rest-client/rest-client/blob/master/lib/restclient/payload.rb#L202-L215) + fullname = data[:fullname] + file.define_singleton_method(:original_filename) { fullname } + resp = resource.post(file_store: { file: file }) + resp = JSON(resp) + + if resp.respond_to?(:[]) && resp['sha1_hash'].present? + resp['sha1_hash'] + else + nil + end + rescue RestClient::UnprocessableEntity => e # 422, file already exist + return sha1 + rescue # Dont care about it + return nil + end + + def destroy + uri = URI(URL) + Net::HTTP.start(uri.host, uri.port) do |http| + req = Net::HTTP::Delete.new("/api/v1/file_stores/#{sha1}.json") + req.basic_auth token, '' + http.request(req) + end + rescue # Dont care about it + end + + protected + + def token + Rails.cache.fetch([FileStoreService::File, :token], expires_in: 10.minutes) do + User.find_by(uname: 'file_store').authentication_token + end + end + + end +end \ No newline at end of file diff --git a/app/views/home/partials/_wiki_new_commit_notification.haml b/app/views/home/partials/_wiki_new_commit_notification.haml index 0b2c0c32b..6ddc48aa9 100644 --- a/app/views/home/partials/_wiki_new_commit_notification.haml +++ b/app/views/home/partials/_wiki_new_commit_notification.haml @@ -1,10 +1,10 @@ -user= User.where(email: user_email).first || User.new(email: user_email) if defined?(user_email) +- name_with_owner = "#{project_owner}/#{project_name}" .top .image= link_to(image_tag(avatar_url(user, :small), alt: 'avatar'), user_path(user)) if user.persisted? .text %span - = 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))}) - - name_with_owner = "#{project_owner}/#{project_name}" + = 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(name_with_owner))}) = raw t("notifications.bodies.project", project_link: link_to(name_with_owner, project_path(name_with_owner)) ) .both = datetime_moment activity_feed.created_at, tag: :span, class: 'date' diff --git a/app/views/platforms/mass_builds/index.html.haml b/app/views/platforms/mass_builds/index.html.haml index 3644856d7..ff9a95647 100644 --- a/app/views/platforms/mass_builds/index.html.haml +++ b/app/views/platforms/mass_builds/index.html.haml @@ -3,58 +3,16 @@ = 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"} + += raw mass_builds_datatable(@platform) + +%table#datatable.tablesorter{cellspacing: 0, cellpadding: 0} %thead %tr %th.lpadding16= t('activerecord.attributes.mass_build.id') %th.lpadding16= t('activerecord.attributes.mass_build.name') - %th.lpadding16= t("layout.mass_builds.statuses") - %th.lpadding16= t("layout.mass_builds.lists") - %th.lpadding16= t("layout.mass_builds.actions") - %th.lpadding16= t("layout.mass_builds.extended_data") - - @mass_builds.each do |mass_build| - %tr - %td= mass_build.id - %td= link_to mass_build.name, build_lists_path(filter: {mass_build_id: mass_build.id, ownership: 'everything'}) - %td.min_width_120 - - MassBuild::COUNT_STATUSES.each do |status| - = link_to t("layout.build_lists.statuses.#{status}") + ": ", build_lists_path(filter: {mass_build_id: mass_build.id, ownership: 'everything'}.merge(status != :build_lists ? {status: BuildList.status_by_human(status)} : {})) - = mass_build.send "#{status}_count" - .both - -if mass_build.projects_list.present? - =link_to_list @platform, mass_build, 'missed_projects_list' - = mass_build.send 'missed_projects_count' - %td - - if mass_build.projects_list.present? - = link_to_list @platform, mass_build, 'projects_list' - .both - %br - = link_to_list @platform, mass_build, 'failed_builds_list' - .both - %br - = link_to_list @platform, mass_build, 'tests_failed_builds_list' - - %td.right.mass-build-actions - - if can?(:publish, mass_build) - - unless mass_build.auto_publish? - = link_to t('layout.mass_builds.publish_success'), - publish_platform_mass_build_path(@platform, mass_build.id), - method: :post, data: { confirm: t("layout.confirm") }, class: 'button' - = link_to t('layout.mass_builds.publish_test_failed'), - publish_platform_mass_build_path(@platform, mass_build.id, status: 'test_failed'), - method: :post, data: { confirm: t("layout.confirm") }, class: 'button' - - if can?(:cancel, mass_build) - = link_to t('layout.cancel'), - cancel_platform_mass_build_path(@platform, mass_build.id), - method: :post, class: 'button', - data: { confirm: t('layout.mass_builds.cancel_confirm') } - %td - %a.toggle_btn{href: "#toggle_#{ mass_build.id }", :'data-target' => "#toggle_#{ mass_build.id }"}= t("layout.mass_builds.extended_data") - .toggle{id: "toggle_#{ mass_build.id }"} - = t('activerecord.attributes.mass_build.user') + ": " - = link_to mass_build.user.fullname, mass_build.user - - %i(arch_names auto_publish increase_release_tag use_cached_chroot use_extra_tests created_at).each do |field| - .both - = t("activerecord.attributes.mass_build.#{field}") + ": " - = mass_build.send field -= will_paginate @mass_builds + %th.th2= t('activerecord.attributes.mass_build.description') + %th.lpadding16= t('activerecord.attributes.mass_build.created_at') + %th.buttons   + %tbody +%br diff --git a/app/views/platforms/mass_builds/index.json.jbuilder b/app/views/platforms/mass_builds/index.json.jbuilder new file mode 100644 index 000000000..3728a2c91 --- /dev/null +++ b/app/views/platforms/mass_builds/index.json.jbuilder @@ -0,0 +1,18 @@ +mass_builds = @mass_builds.map do |mass_build| + [ + mass_build.id, + + link_to_mass_build(mass_build), + + mass_build.description, + + mass_build.created_at.to_s, + + link_to(t('layout.show'), platform_mass_build_path(@platform, mass_build.id)) + ] +end + +json.sEcho params[:sEcho].to_i || -1 +json.iTotalRecords @total_mass_builds +json.iTotalDisplayRecords @mass_builds.count +json.aaData mass_builds || [] diff --git a/app/views/platforms/mass_builds/new.html.haml b/app/views/platforms/mass_builds/new.html.haml index bf8b62bed..d4be8ee51 100644 --- a/app/views/platforms/mass_builds/new.html.haml +++ b/app/views/platforms/mass_builds/new.html.haml @@ -10,6 +10,9 @@ .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('activerecord.attributes.mass_build.description') + = f.text_area :description + %br %h3=t('layout.mass_builds.projects_list') = f.text_area :projects_list %br @@ -18,7 +21,7 @@ %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}" + = check_box_tag "arches[]", arch.id, @mass_build.arches.include?(arch.id.to_s), id: "arches_#{arch.id}" = label_tag "arches_#{arch.id}", arch.name .both - if @platform.personal? @@ -33,6 +36,12 @@ placeholder: 'uxteam_personal', subject: @mass_build, autocomplete_path: autocomplete_extra_repositories_autocompletes_path + = render 'shared/autocomplete_form', + field: :extra_mass_builds, + field_class: MassBuild, + placeholder: '1000000', + subject: @mass_build, + autocomplete_path: autocomplete_extra_mass_build_autocompletes_path = render 'shared/autocomplete_form', field: :extra_build_lists, field_class: BuildList, @@ -40,7 +49,10 @@ subject: @mass_build, autocomplete_path: autocomplete_extra_build_list_autocompletes_path %h3= t("activerecord.attributes.build_list.preferences") - - %i(auto_publish increase_release_tag use_cached_chroot use_extra_tests).each do |field| + .both + = f.select :auto_publish_status, auto_publish_statuses + = f.label :auto_publish_status + - %i(auto_create_container increase_release_tag include_testing_subrepository use_cached_chroot use_extra_tests).each do |field| .both = f.check_box field = f.label field diff --git a/app/views/platforms/mass_builds/show.html.haml b/app/views/platforms/mass_builds/show.html.haml new file mode 100644 index 000000000..6b5f5bbcd --- /dev/null +++ b/app/views/platforms/mass_builds/show.html.haml @@ -0,0 +1,136 @@ +-set_meta_tags title: title_object(@mass_build) += render 'submenu' += render 'sidebar' + +%h3.fix + = t 'activerecord.models.mass_build' + +%table.tablesorter.unbordered + %tr + %td + %b= t('activerecord.attributes.mass_build.name') + %td= link_to_mass_build(@mass_build) + %tr + %td + %b= t('activerecord.attributes.mass_build.id') + %td= @mass_build.id + %tr + %td + %b= t('activerecord.attributes.mass_build.arch_names') + %td= @mass_build.arch_names + %tr + %td + %b= t('activerecord.attributes.mass_build.build_for_platform') + %td= link_to @mass_build.build_for_platform.name, platform_path(@mass_build.build_for_platform) + %tr + %td + %b= t('activerecord.attributes.mass_build.auto_publish_status') + %td= t("layout.build_lists.auto_publish_status.#{@mass_build.auto_publish_status}") + %tr + %td + %b= t('activerecord.attributes.mass_build.auto_create_container') + %td= t("layout.#{@mass_build.auto_create_container?}_") + %tr + %td + %b= t('activerecord.attributes.mass_build.increase_release_tag') + %td= t("layout.#{@mass_build.increase_release_tag?}_") + %tr + %td + %b= t('activerecord.attributes.mass_build.include_testing_subrepository') + %td= t("layout.#{@mass_build.include_testing_subrepository?}_") + %tr + %td + %b= t('activerecord.attributes.mass_build.use_cached_chroot') + %td= t("layout.#{@mass_build.use_cached_chroot?}_") + %tr + %td + %b= t('activerecord.attributes.mass_build.use_extra_tests') + %td= t("layout.#{@mass_build.use_extra_tests?}_") + %tr + %td + %b= t('activerecord.attributes.mass_build.created_at') + %td= @mass_build.created_at + %tr + %td + %b= t('activerecord.attributes.mass_build.description') + %td= @mass_build.description + + %tr + %td + %b= t("activerecord.attributes.build_list.extra_repositories") + %td + - if @mass_build.extra_repositories.present? + - Repository.where(id: @mass_build.extra_repositories).each do |repo| + %p= link_to "#{repo.platform.name}/#{repo.name}", [repo.platform, repo] + .both + - else + = t('layout.false_') + + %tr + %td + %b= t("activerecord.attributes.build_list.extra_mass_builds") + %td + - if @mass_build.extra_mass_builds.present? + - MassBuild.where(id: @mass_build.extra_mass_builds).each do |mb| + %p= link_to "#{mb.id} - #{mb.name}", platform_mass_build_path(mb.save_to_platform, mb) + .both + - else + = t('layout.false_') + + %tr + %td + %b= t("activerecord.attributes.build_list.extra_build_lists") + %td + - if @mass_build.extra_build_lists.present? + - BuildList.where(id: @mass_build.extra_build_lists).each do |bl| + %p= link_to "#{bl.id} (#{bl.project.name} - #{bl.arch.name})", bl + .both + - else + = t('layout.false_') + + %tr + %td + %td + %tr + %td + %b= t('layout.mass_builds.statuses') + %td + - MassBuild::COUNT_STATUSES.each do |status| + - path = build_lists_path+"#?#{{filter: {mass_build_id: @mass_build.id, ownership: 'everything'}.merge(status != :build_lists ? {status: BuildList.status_by_human(status)} : {})}.to_param}" + %tr + %td + = link_to t("layout.build_lists.statuses.#{status}") + ": ", path + %td= @mass_build.send "#{status}_count" + + %tr + %td + %td + %tr + %td + %b= t('layout.mass_builds.lists') + %td + - if @mass_build.projects_list.present? + = link_to_list @platform, @mass_build, 'projects_list' + .both + = link_to_list @platform, @mass_build, 'failed_builds_list' + .both + = link_to_list @platform, @mass_build, 'tests_failed_builds_list' + .both + = link_to_list @platform, @mass_build, 'success_builds_list' + +.buttons_block + - if can?(:publish, @mass_build) + - unless @mass_build.auto_publish_status == BuildList::AUTO_PUBLISH_STATUS_DEFAULT + = link_to t('layout.mass_builds.publish_success'), + publish_platform_mass_build_path(@platform, @mass_build.id), + method: :post, data: { confirm: t("layout.confirm") }, class: 'button' + = link_to t('layout.mass_builds.publish_test_failed'), + publish_platform_mass_build_path(@platform, @mass_build.id, status: 'test_failed'), + method: :post, data: { confirm: t("layout.confirm") }, class: 'button' + - if can?(:cancel, @mass_build) + = link_to t('layout.cancel'), + cancel_platform_mass_build_path(@platform, @mass_build.id), + method: :post, class: 'button', + data: { confirm: t('layout.mass_builds.cancel_confirm') } + - if can? :create, @mass_build + = link_to t('layout.mass_builds.recreate'), new_platform_mass_build_path(@platform, mass_build_id: @mass_build.id), class: 'button' diff --git a/app/views/platforms/platforms/_form.html.haml b/app/views/platforms/platforms/_form.html.haml index 459ffeda7..0251c5e22 100644 --- a/app/views/platforms/platforms/_form.html.haml +++ b/app/views/platforms/platforms/_form.html.haml @@ -12,6 +12,10 @@ .rightlist= f.select :distrib_type, options_for_select(APP_CONFIG['distr_types']) .both + .leftlist= f.label :visibility, class: :label + .rightlist= f.select :visibility, options_for_select(Platform::VISIBILITIES.map{ |v| [t("activerecord.attributes.platform.visibility_types.#{v}"), v] }) + .both + -#.leftlist= f.label :parent, class: :label .rightlist= f.collection_select :parent_platform_id, Platform.all, :id, :description, include_blank: true .both @@ -26,24 +30,25 @@ = hidden_field_tag 'admin_id', @admin_id, id: 'admin_id_field' .both - %h3= t('layout.platform_arch_settings.extra_settings') + - if ['edit', 'update'].include? controller.action_name + %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 + %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 = f.submit t('layout.save'), data: {'disable-with' => t('layout.saving')} diff --git a/app/views/projects/base/_layout.html.haml b/app/views/projects/base/_layout.html.haml index 45d5531ad..81ffcac11 100644 --- a/app/views/projects/base/_layout.html.haml +++ b/app/views/projects/base/_layout.html.haml @@ -4,9 +4,10 @@ = render 'repo_block', project: @project = render 'about_block', project: @project - %h3= t 'layout.projects.last_commit' - - GitPresenters::CommitAsMessagePresenter.present(@commit, project: @project) do |presenter| - .panel.panel-default - .panel-body= render 'shared/feed_commit', presenter: presenter, item_no: 1 + - if @commit.present? + %h3= t 'layout.projects.last_commit' + - GitPresenters::CommitAsMessagePresenter.present(@commit, project: @project) do |presenter| + .panel.panel-default + .panel-body= render 'shared/feed_commit', presenter: presenter, item_no: 1 .clearfix = yield diff --git a/app/views/projects/build_lists/_new_form.html.haml b/app/views/projects/build_lists/_new_form.html.haml index a4543ef17..e9597eb11 100644 --- a/app/views/projects/build_lists/_new_form.html.haml +++ b/app/views/projects/build_lists/_new_form.html.haml @@ -1,78 +1,72 @@ -= form_for [@project, @build_list], html: { class: 'form', method: :post } do |f| - .row - .col-md-4.col-md-offset-2 - %h3= t 'activerecord.attributes.build_list.build_for_platform' - - build_for_platform = params[:build_list].try :[], :build_for_platform - - build_for_platform_id = build_for_platform.try(:id).presence || 'null' - - save_to_repository_id = params[:build_list].try(:[], :save_to_repository_id).presence || 'null' - .all_platforms{ 'ng-init' => "init('#{build_for_platform_id}', #{save_to_repository_id})" } - - availables_main_platforms.each do |pl| - %div{ id: "build_for_pl_#{pl.id}", 'ng-hide' => "needHidePlatform('#{pl.id}')" } - %h4{ class: 'build_for_pl' }= pl.name - = render 'include_repos', platform: pl, build_for_platform: build_for_platform - .col-md-5 - %h3= t 'activerecord.attributes.build_list.save_to_repository' - = f.select :save_to_repository_id, save_to_repositories(project), - { selected: params[:build_list].try(:[], :save_to_repository_id) }, - class: 'form-control', 'ng-change' => 'change_save_to_repository()', - 'ng-model' => 'save_to_repository_id' - %h3= t 'activerecord.attributes.build_list.project_version' - = f.select :project_version, versions_for_group_select(project), - { selected: params[:build_list].try(:[], :project_version) || project.default_branch }, - class: 'form-control' - %h3= t 'activerecord.attributes.build_list.arch' - - Arch.recent.each do |arch| += 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') + - select_options = save_to_repositories(project) + - selected = selected_save_to_repositories(project, select_options, params) + .lineForm= f.select :save_to_repository_id, select_options, 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)) - .checkbox - %label{ for: "arches_#{arch.id}" } - = check_box_tag "arches[]", arch.id, checked, id: "arches_#{arch.id}" - = 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 - %h3= t 'activerecord.attributes.build_list.update_type' - - selected = params[:build_list].try(:[], :update_type) ? {selected: params[:build_list][:update_type]} : {} - = f.select :update_type, BuildList::UPDATE_TYPES, selected, class: 'form-control' + = 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 - = 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.extra_params.label' - - BuildList::EXTRA_PARAMS.each do |field| - - text = params[:build_list].try(:[], :extra_params).try(:[], field) - .form-group - = text_field_tag "build_list[extra_params][#{field}]", text, - placeholder: t("activerecord.attributes.build_list.extra_params.#{field}"), class: 'form-control' + %h3= t("activerecord.attributes.build_list.preferences") + - selected = params[:build_list].try(:[], :auto_publish_status) ? {selected: params[:build_list][:auto_publish_status]} : {} + = f.select :auto_publish_status, auto_publish_statuses, selected + = f.label :auto_publish_status + - %i(auto_create_container include_testing_subrepository use_cached_chroot use_extra_tests save_buildroot).each do |kind| + .both + - checked = params[:build_list].try(:[], kind) + - checked = @build_list.send(kind) if checked.nil? + = f.check_box kind, checked: 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 - %h3= t 'activerecord.attributes.build_list.preferences' - .row - = f.label :auto_publish_status, class: 'col-xs-6' - = f.label :external_nodes, class: 'col-xs-6' - .clearfix - - selected = params[:build_list].try(:[], :auto_publish_status) ? {selected: params[:build_list][:auto_publish_status]} : {} - .col-xs-6 - = f.select :auto_publish_status, auto_publish_statuses, selected, class: 'form-control' - .col-xs-6 - - selected = params[:build_list].try(:[], :external_nodes) ? {selected: params[:build_list][:external_nodes]} : {} - = f.select :external_nodes, external_nodes, selected.merge(include_blank: true), class: 'form-control' + %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')} - - %i(auto_create_container include_testing_subrepository use_cached_chroot use_extra_tests).each do |kind| - .checkbox - %label{ for: kind } - - checked = params[:build_list].try(:[], kind) - - checked = @build_list.send(kind) if checked.nil? - = f.check_box kind, checked: checked - = BuildList.human_attribute_name kind + - if params[:build_list_id].blank? + :javascript + $(function() { + $('#build_list_save_to_repository_id').trigger('change'); + }); - = 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')}, - class: 'btn btn-primary' +.both diff --git a/app/views/projects/build_lists/dependent_projects.html.haml b/app/views/projects/build_lists/dependent_projects.html.haml new file mode 100644 index 000000000..b00bab5f0 --- /dev/null +++ b/app/views/projects/build_lists/dependent_projects.html.haml @@ -0,0 +1,62 @@ +-set_meta_tags title: [title_object(@build_list.project), t('activerecord.models.build_list')] + +:javascript + $('article .all').addClass('bigpadding'); + += render 'submenu' + +%h3= t('.title') + += form_for @build_list, url: dependent_projects_build_list_path(@build_list), html: { class: :form, method: :post } do |f| + + %h3= t('.projects') + - @build_list.packages.each do |package| + - dependent_projects(package).each do |project, packages| + .leftlist + = link_to project.name_with_owner, project_path(project.name_with_owner) + .both + %span + = t('.packages') + %i= packages * ', ' + + .rightlist + = f.check_box :project_id, name: "build_list[projects][#{project.id}]", checked: true + .both + + %h3= t('.architecture') + + - Arch.recent.each do |arch| + .leftlist= arch.name + .rightlist + - arches = @build_list.build_for_platform.platform_arch_settings.by_default.pluck(:arch_id) + = f.check_box :arch_id, name: "build_list[arches][#{arch.id}]", checked: arches.include?(arch.id) + .both + + %h3= t('.preferences') + .leftlist= f.label :auto_publish_status + .rightlist= f.select :auto_publish_status, auto_publish_statuses + .both + + .leftlist= f.label :auto_create_container + .rightlist= f.check_box :auto_create_container + .both + + .leftlist= f.label :include_testing_subrepository + .rightlist= f.check_box :include_testing_subrepository + .both + + .leftlist= f.label :use_cached_chroot + .rightlist= f.check_box :use_cached_chroot + .both + + .leftlist= f.label :use_extra_tests + .rightlist= f.check_box :use_extra_tests + .both + + .hr + + .button_block + = submit_tag t('layout.create'), + data: {'disable-with' => t('layout.saving'), confirm: t('.are_you_sure') } + %span.text_button_padding= t('layout.or') + = link_to t('layout.cancel'), build_list_path(@build_list), class: 'button' diff --git a/app/views/projects/build_lists/show.html.haml b/app/views/projects/build_lists/show.html.haml index c7dc90de0..7415be631 100644 --- a/app/views/projects/build_lists/show.html.haml +++ b/app/views/projects/build_lists/show.html.haml @@ -1,299 +1,307 @@ -set_meta_tags title: [title_object(@build_list.project), t('activerecord.models.build_list')] -.container.build-list{ 'ng-controller' => 'BuildListController', 'ng-cloak' => true } +.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 - .panel.panel-primary - .panel-heading - .build-list-status.pull-left{ 'ng-class' => '"bg-" + build_list.status_color' } - {{build_list.human_status | i18n}} - .pull-right.build-list-time{ 'am-time-ago' => 'build_list.updated_at', - title: "{{build_list.updated_at | amDateFormat:'ddd, LLL'}}" } - .clearfix - .panel-body - %h3.offset0= t 'layout.build_lists.main_data' - .clearfix - =form_for @build_list, url: publish_build_list_path(@build_list), html: { class: :form } do |f| - %table.table.table-borderless.table-hover + .leftlist= t("activerecord.attributes.build_list.id") + .rightlist + = @build_list.id + = hidden_field_tag :build_list_id, @build_list.id + .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{'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 + .ng-hide{ 'ng-show' => 'update_type_errors' } + .flash_notify + .alert{ 'ng-class' => "{ 'alert-error': update_type_errors }" } + {{ update_type_errors }} + .both + .both + .leftlist= t("activerecord.attributes.build_list.auto_publish_status") + .rightlist= t("layout.build_lists.auto_publish_status.#{@build_list.auto_publish_status}") + .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.use_cached_chroot") + .rightlist= t("layout.#{@build_list.use_cached_chroot?}_") + .both + .leftlist= t("activerecord.attributes.build_list.use_extra_tests") + .rightlist= t("layout.#{@build_list.use_extra_tests?}_") + .both + .leftlist= t("activerecord.attributes.build_list.save_buildroot") + .rightlist= t("layout.#{@build_list.save_buildroot?}_") + .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 + - 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 + + - 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") + - path = platform_mass_build_path(@build_list.save_to_platform, @build_list.mass_build_id) + .rightlist= link_to @build_list.mass_build.name, path + + .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 + = 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 + + #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}") + + #new_advisory_form{'ng-show' => 'attach_advisory == "new"'} + = f.fields_for @build_list.build_advisory do |f| + = render partial: 'advisories/form', locals: {f: f} + + #advisory_preview{'ng-show' => 'attach_advisory != "no" && attach_advisory != "new"'} + %h3= t('activerecord.models.advisory') << ' {{advisory.advisory_id}}' + + .leftlist= t('activerecord.attributes.advisory.description') + .rightlist.descr {{advisory.description}} + .both + + .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 + + - unless @build_list.valid_branch_for_publish? + .flash_notify + .alert.alert-error= t('layout.build_lists.wrong_branch_for_publish_html', branch: @build_list.save_to_repository.publish_builds_only_from_branch) + .both + + %div{'ng-show' => 'build_list.can_publish && build_list.can_publish_in_future && !build_list.can_publish_into_repository'} + .flash_notify + .alert.alert-error= t('flash.build_list.not_all_build_lists_success') + .both + + - if can?(:cancel, @build_list) + = link_to t("layout.build_lists.cancel"), cancel_build_list_path(@build_list), + method: :put, data: { confirm: t("layout.confirm") }, class: 'button', + 'ng-show' => 'build_list.can_cancel' + = submit_tag t('layout.publish_again'), + data: { 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"), + data: { 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"), + data: { 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, data: { 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, data: { confirm: t("layout.confirm") }, + class: 'button', + 'ng-show' => 'build_list.can_reject_publish' + - if can?(:rerun_tests, @build_list) + = link_to t('layout.build_lists.rerun_tests'), + rerun_tests_build_list_path(@build_list), + method: :put, + data: { confirm: t("layout.confirm") }, + class: 'button', + 'ng-show' => "build_list.status == #{BuildList::TESTS_FAILED} || build_list.status == #{BuildList::SUCCESS}" + - if can?(:create_container, @build_list) + = link_to t("layout.build_lists.create_container"), + create_container_build_list_path(@build_list), + method: :put, data: { 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' + - if can_run_dependent_build_lists?(@build_list) + = link_to t('projects.build_lists.dependent_projects.title'), + dependent_projects_build_list_path(@build_list), + class: 'button', + 'ng-show' => 'build_list.dependent_projects_exists' + + %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 - %td= t 'activerecord.attributes.build_list.container_path' + %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 - %div{'ng-show' => "build_list.container_status == #{BuildList::BUILD_PUBLISHED}"} - - url = container_url - = link_to url, url - %div{'ng-show' => "build_list.container_status == #{BuildList::BUILD_PUBLISH}"} - = t 'layout.build_lists.creating' - %tr - %td= t 'activerecord.attributes.build_list.id' - %td - = @build_list.id - = hidden_field_tag :build_list_id, @build_list.id - %tr - %td= t 'activerecord.attributes.build_list.user' - %td= link_to @build_list.user.try(:fullname), @build_list.user - %tr - %td= t 'activerecord.attributes.build_list.publisher' - %td - %a{ 'ng-show' => 'build_list.publisher', - 'ng-href' => '{{build_list.publisher.path}}' } - {{build_list.publisher.fullname}} - %tr - %td= t 'activerecord.attributes.build_list.build_for_platform' - %td - - if bfp = @build_list.build_for_platform - = link_to(bfp.name, bfp) - - else - = t 'layout.build_lists.platform_deleted' - %tr - %td= t 'activerecord.attributes.build_list.save_to_repository' - %td - = link_to "#{@build_list.save_to_platform.name}/#{@build_list.save_to_repository.name}", - [@build_list.save_to_platform, @build_list.save_to_repository] - %tr - %td= t 'activerecord.attributes.build_list.include_testing_subrepository' - %td= t "layout.#{@build_list.include_testing_subrepository?}_" - %tr - %td= t 'activerecord.attributes.build_list.include_repos' - %td= (@build_list.include_repos||[]).map{|r| Repository.where(id: r).first.try(:name)}.join(', ') - %tr - %td= t 'activerecord.attributes.build_list.update_type' - %td - = 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 - %tr.ng-hide{ 'ng-show' => 'update_type_errors' } - %td{ colspan: 2 } - .alert{ 'ng-class' => "{ 'alert-danger': update_type_errors }" } - {{ update_type_errors }} - %tr - %td= t 'activerecord.attributes.build_list.auto_publish_status' - %td= t "layout.build_lists.auto_publish_status.#{@build_list.auto_publish_status}" - %tr - %td= t("activerecord.attributes.build_list.auto_create_container") - %td= t "layout.#{@build_list.auto_create_container?}_" - %tr - %td= t 'activerecord.attributes.build_list.use_cached_chroot' - %td= t "layout.#{@build_list.use_cached_chroot?}_" - %tr - %td= t 'activerecord.attributes.build_list.use_extra_tests' - %td= t "layout.#{@build_list.use_extra_tests?}_" - %tr - %td= t 'activerecord.attributes.build_list.project_version' - %td= link_to @build_list.project_version, tree_path(@build_list.project, @build_list.project_version) - %tr - %td= t 'diff' - %td= build_list_version_link(@build_list, true) - %tr - %td= t 'activerecord.attributes.build_list.arch' - %td= @build_list.arch.name - %tr - %td= t 'activerecord.attributes.build_list.updated_at' - %td {{build_list.updated_at_utc}} - - if @build_list.external_nodes.present? - %tr - %td= t 'activerecord.attributes.build_list.external_nodes' - %td= I18n.t "layout.build_lists.external_nodes.#{@build_list.external_nodes}" - %tr - %td= t 'activerecord.attributes.build_list.builder' - %td - %a{ 'ng-show' => 'build_list.builder', 'ng-href' => '{{build_list.builder.path}}' } - {{build_list.builder.fullname}} - %tr - %td= t 'activerecord.attributes.build_list.is_circle' - %td= t "layout.#{@build_list.is_circle?}_" - %tr - %td= t 'activerecord.attributes.build_list.new_core' - %td= t "layout.#{@build_list.new_core?}_" - - if @build_list.extra_build_lists.present? || @build_list.extra_repositories.present? - %tr - %td= t 'activerecord.attributes.build_list.extra_repositories' - %td - - 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 - - if @build_list.extra_params.present? - %tr - %td - %h4= t 'activerecord.attributes.build_list.extra_params.label' - %td - %tr - - @build_list.extra_params.each do |k, v| - %td= t "activerecord.attributes.build_list.extra_params.#{k}" - %td= v + %a{'ng-href' => '{{item.path.href}}' } {{item.path.text}} + %td {{build_list.humanStatus(item.status) | i18n}} + .both - - if @build_list.mass_build_id.present? - %tr - %td= t 'activerecord.attributes.mass_build_id' - %td= link_to @build_list.mass_build.name, platform_mass_builds_path(@build_list.save_to_platform) + %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/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{'ng-repeat-start' => 'package in build_list.packages'} + %td.package + %a.expand{'ng-show' => 'package.dependent_projects' } + %span.icon-chevron-up{'ng-show' => 'package.show_dependent_projects', + 'ng-click' => 'package.show_dependent_projects = false' } + %span.icon-chevron-down{'ng-hide' => 'package.show_dependent_projects', + 'ng-click' => 'package.show_dependent_projects = true' } + %div{'ng-if' => '!package.url'} {{package.fullname}} + %a{ 'ng-if' => 'package.url', + 'ng-href' => "{{package.url}}" } + {{package.fullname}} - %tr{ 'ng-show' => 'build_list.advisory' } - %td= t 'layout.build_lists.attached_advisory' - %td - %a{'ng-href' => '{{build_list.advisory.path}}' } - {{build_list.advisory.advisory_id}} + %td {{package.name}} + %td {{package.epoch}} + %td {{package.version}} + %td {{package.release}} - %tr{'ng-show' => 'build_list.human_duration'} - %td - %td {{build_list.human_duration }} + %tr{'ng-repeat-end' => '', + 'ng-show' => 'package.show_dependent_projects', + 'ng-repeat' => 'project in package.dependent_projects' } + %td + %a{'ng-href' => "{{project.url}}" } {{project.name}} + %td + %p{'ng-repeat' => 'package in project.dependent_packages'} + {{package}} + %td{ colspan: 3 } + %a{'ng-href' => '{{project.new_url}}' } + = t('layout.build_lists.create_build_list') + .both - - if @build_list.save_to_platform.released? - %tr#advisory_block{'ng-show' => 'build_list.can_publish && !build_list.advisory'} - %td= label_tag :attach_advisory, t('layout.build_lists.attached_advisory') - %td - = select_tag :attach_advisory, advisories_select_options(@advisories), 'ng-model' => 'attach_advisory', 'ng-change' => 'attachAdvisoryChanged()' - %span.help-block= t 'layout.advisories.publication_info', update_types: BuildList::RELEASE_UPDATE_TYPES.join(', ') + - if @build_list.new_core? + .hr + = render 'shared/build_results', subject: @build_list - %tr#advisory_search_block{'ng-show' => 'attach_advisory != "no" && attach_advisory != "new"'} - %td - %h4= t 'layout.advisories.search_by_id' - %td - = label_tag :advisory_search, t('layout.advisories.search_hint') - %input#advisory_search{type: 'text', 'ng-model' => 'term', 'ng-keyup' => 'search()'} - %span.help-block= t 'layout.advisories.advisory_id_info', advisory_format: advisory_id_for_hint - %p.bg-warning.text-center{ 'ng-show' => "search_status === 'advisory_not_found'" } - = t 'layout.advisories.banners.advisory_not_found' - %p.bg-danger.text-center{ 'ng-show' => "search_status === 'server_error'" } - = t 'layout.advisories.banners.server_error' - %p.bg-success.text-center{ 'ng-show' => "search_status === 'continue_input'" } - = t 'layout.advisories.banners.continue_input' - - %tr#new_advisory_form{'ng-show' => 'attach_advisory == "new"'} - = f.fields_for @build_list.build_advisory do |f| - =# render partial: 'advisories/form', locals: {f: f} - %td - %h4= t 'layout.advisories.form_header' - %td - .form-group - = f.label :description, t('activerecord.attributes.advisory.description') - = f.text_area :description, class: 'form-control', cols: 80 - .form-group - = f.label :references, t('activerecord.attributes.advisory.references') - %p.text-muted - = t 'layout.advisories.ref_comment' - = f.text_area :references, class: 'form-control', cols: 80 - - %tr.advisory_preview{ 'ng-show' => 'attach_advisory != "no" && attach_advisory != "new"' } - %td{ colspan: 2 } - %h4= t('activerecord.models.advisory') << ' {{advisory.advisory_id}}' - %tr{ 'ng-show' => 'attach_advisory != "no" && attach_advisory != "new"' } - %td - %td - %h5.advisory-description= t 'activerecord.attributes.advisory.description' - %div{ 'ng-bind-html' => 'advisory.description' } - - %h5= t 'activerecord.attributes.advisory.references' - {{advisory.references}} - - %tr{ 'ng-hide' => 'build_list.extra_build_lists_published' } - %td{ colspan: 2 } - .alert.alert-danger= t 'layout.build_lists.publish_with_extra_fail' - - %tr{ 'ng-show' => 'build_list.can_publish && build_list.can_publish_in_future && !build_list.can_publish_into_repository' } - %td{ colspan: 2 } - .alert.alert-danger= t 'flash.build_list.not_all_build_lists_success' - - - if can?(:cancel, @build_list) - = link_to t('layout.build_lists.cancel'), cancel_build_list_path(@build_list), - method: :put, data: { confirm: t('layout.confirm') }, class: 'btn btn-primary', - 'ng-show' => 'build_list.can_cancel' - = submit_tag t('layout.publish_again'), - data: { confirm: t("layout.publish_again_warning") }, - name: 'publish', class: 'btn btn-primary', - 'ng-show' => "build_list.can_publish && build_list.status == #{BuildList::BUILD_PUBLISHED}" - = submit_tag t('layout.publish'), - data: { confirm: t('layout.build_lists.tests_failed') }, name: 'publish', class: 'btn btn-primary', - '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'), - data: { confirm: t('layout.confirm') }, name: 'publish', class: 'btn btn-primary', - '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, data: { confirm: t('layout.confirm') }, - class: 'btn btn-primary', - '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, data: { confirm: t('layout.confirm') }, - class: 'btn btn-primary', - 'ng-show' => 'build_list.can_reject_publish' - - if can?(:rerun_tests, @build_list) - = link_to t('layout.build_lists.rerun_tests'), - rerun_tests_build_list_path(@build_list), - method: :put, - data: { confirm: t("layout.confirm") }, - class: 'btn btn-primary', - 'ng-show' => "build_list.status == #{BuildList::TESTS_FAILED} || build_list.status == #{BuildList::SUCCESS}" - - if can?(:create_container, @build_list) - = link_to t('layout.build_lists.create_container'), - create_container_build_list_path(@build_list), - method: :put, data: { confirm: t('layout.confirm') }, - class: 'btn btn-primary', - '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: 'btn btn-primary' - .clearfix - %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 - %accordion - %accordion-group{ 'is-open' => '$parent.isOpenLog' } - %accordion-heading - = t 'layout.build_lists.log.build_log' - %i.pull-right.glyphicon{ 'ng-class' => "{'glyphicon-chevron-down': isOpenLog, 'glyphicon-chevron-right': !isOpenLog}" } - - = label_tag :autoreload do - %input{ type: 'checkbox', 'ng-model' => 'log_autoreload', - 'ng-change' => 'change_log_autoreload(log_autoreload)' } - = t 'layout.build_lists.log.autoreload' - = select_tag :reload_interval, log_reload_time_options, 'ng-model' => 'log_reload_interval', - 'ng-change' => 'change_log_interval(log_reload_interval)' - - .offset10{ 'ng-bind-html' => 'build_log' } - - %hr - %h3= t 'layout.build_lists.items_header' - %h4{'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 {{item.name + ' #' + item.level}} - %table.table - %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}} - - - %div{'ng-show' => 'build_list.packages'} - .hr - %h3= t 'layout.build_lists.packages_header' - %table.table.table-hover - %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.epoch' - %th= t 'activerecord.attributes.build_list/package.version' - %th= t 'activerecord.attributes.build_list/package.release' - %tbody - %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}} - - - - if @build_list.new_core? - .hr - = render 'shared/build_results', subject: @build_list +:javascript + $('article .all').addClass('bigpadding'); = render 'submenu' diff --git a/app/views/projects/build_lists/show.json.jbuilder b/app/views/projects/build_lists/show.json.jbuilder index 7b0f13f13..fcf8fb9d6 100644 --- a/app/views/projects/build_lists/show.json.jbuilder +++ b/app/views/projects/build_lists/show.json.jbuilder @@ -50,16 +50,24 @@ json.build_list do json.url file_store_results_url(result['sha1'], result['file_name']) end if @build_list.new_core? && @build_list.results.present? + dependent_projects_exists = false 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 - json.dependent_projects Project.where(id: package.dependent_projects).to_a do |project| - json.project_path project_path(project) - json.new_project_build_list_path new_project_build_list_path(@project) - end + json.dependent_projects dependent_projects(package) do |project, packages| + json.url project_path(project.name_with_owner) + json.name project.name_with_owner + json.dependent_packages packages + json.new_url new_project_build_list_path(project) + + dependent_projects_exists = true + end if @build_list.save_to_platform.main? + end if @build_list.packages.present? + json.dependent_projects_exists dependent_projects_exists + json.item_groups do |group| @item_groups.each_with_index do |group, level| json.group group do |item| diff --git a/app/views/projects/comments/_markdown_help.html.haml b/app/views/projects/comments/_markdown_help.html.haml index abfb8e709..0289c5f45 100644 --- a/app/views/projects/comments/_markdown_help.html.haml +++ b/app/views/projects/comments/_markdown_help.html.haml @@ -20,9 +20,11 @@ %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") + =image_tag("/images/emoji/#{Emoji.find_by_alias('smile').image_filename}", + class: 'emoji', title: 'smile', alt: 'smile', size: "20x20") =" :+1:" - =image_tag(image_path('emoji/+1.png'), class: 'emoji', title: '+1', alt: '+1', size: "20x20") + =image_tag("/images/emoji/#{Emoji.find_by_alias('+1').image_filename}", + class: 'emoji', title: '+1', alt: '+1', size: "20x20") .col %h3=t 'layout.comments.md_cheatsheet.lists' %p{style: 'float:left;margin-left:20px;'}=t 'layout.comments.md_cheatsheet.unordered' diff --git a/app/views/projects/git/base/_fork.html.haml b/app/views/projects/git/base/_fork.html.haml index d5f245bc1..32f1c1396 100644 --- a/app/views/projects/git/base/_fork.html.haml +++ b/app/views/projects/git/base/_fork.html.haml @@ -23,4 +23,5 @@ - if @project.is_package && can?(:create, @project.build_lists.new) .pull-right.roffset5 - = link_to t('layout.projects.new_build_list'), new_project_build_list_path(@project), class: 'btn btn-primary' + - params = { build_list: { project_version: @treeish}} + = link_to t('layout.projects.new_build_list'), new_project_build_list_path(@project, params), class: 'btn btn-primary' diff --git a/app/views/projects/git/blobs/_render_as_image.html.haml b/app/views/projects/git/blobs/_render_as_image.html.haml index ae1ef4884..e4285d450 100644 --- a/app/views/projects/git/blobs/_render_as_image.html.haml +++ b/app/views/projects/git/blobs/_render_as_image.html.haml @@ -1,8 +1,6 @@ -%table.table.blob +%table.table.blob.diff %tr %td.lines - %td.blob - :plain -
-
-
\ No newline at end of file + %td.blob.diff-image + %span.diff-image + %img{ src: raw_path(@project, @treeish, @path), style: 'max-width: 600px;' } diff --git a/app/views/projects/git/commits/_commit_diff.html.haml b/app/views/projects/git/commits/_commit_diff.html.haml index d9943b7f9..00f54bb1b 100644 --- a/app/views/projects/git/commits/_commit_diff.html.haml +++ b/app/views/projects/git/commits/_commit_diff.html.haml @@ -6,6 +6,17 @@ - if commit_diff.b_path.present? .r= link_to "view file @ #{short_hash_id(commit_id)}", blob_path(@project, commit_id, commit_diff.b_path) .clear - -if commit_diff.diff.present? && !(@project.repo.tree(commit_id) / commit_diff.a_path).binary? - ="a_path=#{commit_diff.a_path}; b_path=#{commit_diff.b_path}" if (@project.repo.tree(commit_id) / commit_diff.b_path).nil? - .diff_data=render_diff(commit_diff, diff_counter: commit_diff_counter, comments: @comments) + - if commit_diff.diff.present? + - blob = @project.repo.tree(commit_id) / commit_diff.a_path + - if blob.render_as == :image + %table.diff.inline{ cellspacing: 0, cellpadding: 0 } + %tr + %td.lines + %td.blob.diff-image + %span.diff-image + %img{ src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}", + style: 'max-width: 600px;' } + - elsif !blob.binary? + - if (@project.repo.tree(commit_id) / commit_diff.b_path).nil? + ="a_path=#{commit_diff.a_path}; b_path=#{commit_diff.b_path}" + .diff_data=render_diff(commit_diff, diff_counter: commit_diff_counter, comments: @comments) diff --git a/app/views/projects/git/commits/_commits.html.haml b/app/views/projects/git/commits/_commits.html.haml index d80491d41..ddbd6526f 100644 --- a/app/views/projects/git/commits/_commits.html.haml +++ b/app/views/projects/git/commits/_commits.html.haml @@ -1,3 +1,4 @@ +- project ||= @project - commits = split_commits_by_date(commits) - cur_year = Date.today.year - counter = 1 @@ -7,7 +8,7 @@ - by_day.each_pair do |day, commits| .panel.panel-info .panel-heading - %h3.panel-title= raw l(commits.first.authored_date, format: :date_block_format) + %h3.panel-title= raw l(commits.first.committed_date, format: :date_block_format) %ul.list-group - commits.each_with_index do |commit| %li.list-group-item diff --git a/app/views/projects/git/commits/diff.html.haml b/app/views/projects/git/commits/diff.html.haml index bddcd80e1..4eb8d28dd 100644 --- a/app/views/projects/git/commits/diff.html.haml +++ b/app/views/projects/git/commits/diff.html.haml @@ -15,7 +15,7 @@ .both -begin =render_diff_stats @stats - -@project.repo.diff(@commit1.id, @commit.id).each_with_index do |commit_diff, diff_counter| + - Grit::Commit.diff(@project.repo, @common_ancestor.id, @commit.id).each_with_index do |commit_diff, diff_counter| - commit_id = commit_diff.deleted_file ? @common_ancestor.id : @commit.id .file %a{name: "diff-#{diff_counter}"} @@ -24,7 +24,17 @@ - if commit_diff.b_path.present? .r= link_to "view file @ #{short_hash_id(commit_id)}", blob_path(@project, commit_id, commit_diff.b_path) .clear - -if commit_diff.diff.present? && !(@project.repo.tree(commit_id) / commit_diff.b_path).binary? - .diff_data=render_diff(commit_diff, diff_counter: diff_counter) + -if commit_diff.diff.present? + .diff_data + - blob = @project.repo.tree(commit_id) / commit_diff.b_path + - if blob.render_as == :image + %table.diff.inline{ cellspacing: 0, cellpadding: 0 } + %tr + %td.lines + %td.blob.diff-image + %span.diff-image + %img{ src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}", + style: 'max-width: 600px;' } + = render_diff(commit_diff, diff_counter: diff_counter) unless blob.binary? - rescue Grit::Git::GitTimeout %p= t 'layout.git.repositories.commit_diff_too_big' diff --git a/app/views/projects/pull_requests/_diff_commits_tabs.html.haml b/app/views/projects/pull_requests/_diff_commits_tabs.html.haml index 6cc922c31..7eb57c7c0 100644 --- a/app/views/projects/pull_requests/_diff_commits_tabs.html.haml +++ b/app/views/projects/pull_requests/_diff_commits_tabs.html.haml @@ -18,4 +18,4 @@ #commits.tab-pane - if @total_commits > @commits.count %div= t("projects.pull_requests.is_big", count: @commits.count) - = render partial: 'projects/git/commits/commits', object: @commits + = render partial: 'projects/git/commits/commits', object: @commits, locals: { project: @pull.from_project } diff --git a/app/views/projects/pull_requests/_pull_diff.html.haml b/app/views/projects/pull_requests/_pull_diff.html.haml index 440cf4de7..afbc97420 100644 --- a/app/views/projects/pull_requests/_pull_diff.html.haml +++ b/app/views/projects/pull_requests/_pull_diff.html.haml @@ -3,7 +3,8 @@ %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.b_path.rtruncate(120)) - .r= link_to "view file @ #{short_hash_id(commit_id)}", blob_path(@project, commit_id, pull_diff.b_path) + - if @pull.from_project.present? + .r= link_to "view file @ #{short_hash_id(commit_id)}", blob_path(@pull.from_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/shared/_profile.html.haml b/app/views/shared/_profile.html.haml index 08ca0d24e..9a0c09081 100644 --- a/app/views/shared/_profile.html.haml +++ b/app/views/shared/_profile.html.haml @@ -20,11 +20,12 @@ = 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 + - unless user.hide_email? + %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{ class: user.hide_email? ? 'first' : '' } - message = "#{t 'activerecord.attributes.user.site'}: " = message = link_to short_message(user.site, max_length - message.length), user.site, title: user.site diff --git a/app/views/shared/autocomplete_docs/_extra_mass_builds.en.html.haml b/app/views/shared/autocomplete_docs/_extra_mass_builds.en.html.haml new file mode 100644 index 000000000..8f3328b3a --- /dev/null +++ b/app/views/shared/autocomplete_docs/_extra_mass_builds.en.html.haml @@ -0,0 +1,3 @@ +:markdown + + __Ability to attach at assembly packages of mass builds as additional sources of packages.__ \ No newline at end of file diff --git a/app/views/shared/autocomplete_docs/_extra_mass_builds.ru.html.haml b/app/views/shared/autocomplete_docs/_extra_mass_builds.ru.html.haml new file mode 100644 index 000000000..80fd5c9b0 --- /dev/null +++ b/app/views/shared/autocomplete_docs/_extra_mass_builds.ru.html.haml @@ -0,0 +1,3 @@ +:markdown + + __Возможность подключить для сборки пакеты из массовой сборки как дополнительные источники пакетов.__ \ No newline at end of file diff --git a/app/views/statistics/_build_lists.html.haml b/app/views/statistics/_build_lists.html.haml new file mode 100644 index 000000000..789086f17 --- /dev/null +++ b/app/views/statistics/_build_lists.html.haml @@ -0,0 +1,55 @@ +.row + .span8 + .graph-wrapper + %h3 + %span.graph-key-color1 + = t('.build_started_title') + %span.graph-key-color2 + = t('.success_title') + %span.graph-key-color3 + = t('.build_error_title') + %span.graph-key-color4 + = t('.build_published_title') + .centered.graph-loading{ ng_show: 'loading' } + = image_tag 'loading-large.gif' + .no-data{ ng_hide: 'loading || statistics.build_lists' } + = t('.no_data') + %canvas#build_lists_chart{ ng_show: 'statistics.build_lists' } + + .span3 + .panel-wrapper + %h3 + = t('.total_build_started') + .panel-data + = image_tag 'loading-small.gif', ng_show: 'loading' + .no-data{ ng_hide: 'loading || statistics.build_lists.build_started_count >= 0' } + = t('.no_data') + {{ statistics.build_lists.build_started_count | number }} + + .panel-wrapper + %h3 + = t('.total_success') + .panel-data + = image_tag 'loading-small.gif', ng_show: 'loading' + .no-data{ ng_hide: 'loading || statistics.build_lists.success_count >= 0' } + = t('.no_data') + {{ statistics.build_lists.success_count | number }} + + .panel-wrapper + %h3 + = t('.total_build_error') + .panel-data + = image_tag 'loading-small.gif', ng_show: 'loading' + .no-data{ ng_hide: 'loading || statistics.build_lists.build_error_count >= 0' } + = t('.no_data') + {{ statistics.build_lists.build_error_count | number }} + + .panel-wrapper + %h3 + = t('.total_build_published') + .panel-data + = image_tag 'loading-small.gif', ng_show: 'loading' + .no-data{ ng_hide: 'loading || statistics.build_lists.build_published_count >= 0' } + = t('.no_data') + {{ statistics.build_lists.build_published_count | number }} + diff --git a/app/views/statistics/_filter.html.haml b/app/views/statistics/_filter.html.haml new file mode 100644 index 000000000..943e7eece --- /dev/null +++ b/app/views/statistics/_filter.html.haml @@ -0,0 +1,14 @@ +.row + .span11.flash_notify + + %h3.text-info + = t('.header') + + = form_tag '#', class: 'form-inline alert alert-info centered', id: 'range-form' do + %label.control-label + = t('.range_label') + = select_tag 'range', statistics_range_options, id: 'range_select', class: 'select input-medium', ng_model: 'range', ng_change: 'rangeChange()' + %span{ ng_show: "range == 'custom'" } + = text_field_tag :range_start, Date.today - 1.month, class: 'date_picker input-medium', placeholder: t('.range_start_placeholder'), ng_model: 'range_start', ng_change: 'rangeChange()', readonly: true + = t('.range_separator') + = text_field_tag :range_end, Date.today, class: 'date_picker input-medium', placeholder: t('.range_end_placeholder'), ng_model: 'range_end', ng_change: 'rangeChange()', readonly: true \ No newline at end of file diff --git a/app/views/statistics/index.html.haml b/app/views/statistics/index.html.haml new file mode 100644 index 000000000..e3d5a6d20 --- /dev/null +++ b/app/views/statistics/index.html.haml @@ -0,0 +1,7 @@ +- set_meta_tags title: t('.header') + +#manage-statistics{ ng_controller: 'StatisticsController', ng_init: 'init()' } + + = render 'filter' + = render 'build_lists' + diff --git a/app/views/users/base/_form.html.haml b/app/views/users/base/_form.html.haml index 594c859c7..2b2c4fbef 100644 --- a/app/views/users/base/_form.html.haml +++ b/app/views/users/base/_form.html.haml @@ -16,6 +16,9 @@ .leftlist= f.label :email, t("activerecord.attributes.user.email") .rightlist= f.text_field :email .both +.leftlist= f.label :hide_email, t("activerecord.attributes.user.hide_email") +.rightlist= f.check_box :hide_email +.both .leftlist= f.label :site, t("activerecord.attributes.user.site") .rightlist= f.text_field :site .both diff --git a/config/application.rb b/config/application.rb index aa273317c..b685e9555 100644 --- a/config/application.rb +++ b/config/application.rb @@ -32,6 +32,8 @@ module Rosa # config.autoload_paths += %W(#{config.root}/extras) config.autoload_paths += %W(#{config.root}/app/presenters) config.autoload_paths += %W(#{config.root}/app/jobs) + config.autoload_paths += %W(#{config.root}/app/jobs/concerns) + config.autoload_paths += %W(#{config.root}/app/services/abf_worker) # Only load the plugins named here, in the order given (default is alphabetical). # :all can be used as a placeholder for all plugins not explicitly named. diff --git a/config/application.yml.sample b/config/application.yml.sample index 58a80ea86..8e74406a3 100644 --- a/config/application.yml.sample +++ b/config/application.yml.sample @@ -16,7 +16,6 @@ common: &common key_pair_secret_key: 'key_pair_secret_key' node_instruction_secret_key: 'node_instruction_secret_key' airbrake_api_key: 'airbrake_api_key' - logentries_key: 'logentries_key' secret_token: 'secret_token' secret_key_base: 'secret_key_base' devise: diff --git a/config/initializers/logentries.rb b/config/initializers/logentries.rb deleted file mode 100644 index 2c9ba46c9..000000000 --- a/config/initializers/logentries.rb +++ /dev/null @@ -1,8 +0,0 @@ -# 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 deleted file mode 100644 index f0a9eaa34..000000000 --- a/config/initializers/logglier.rb +++ /dev/null @@ -1,5 +0,0 @@ -# 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/schema_plus.rb b/config/initializers/schema_plus.rb new file mode 100644 index 000000000..090c40242 --- /dev/null +++ b/config/initializers/schema_plus.rb @@ -0,0 +1,3 @@ +SchemaPlus.setup do |config| + config.foreign_keys.auto_create = false +end \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml index 4d59613b5..beda90d11 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -196,4 +196,5 @@ en: reset: Reset _on: 'On' until: Until + none: None close: Close diff --git a/config/locales/menu.en.yml b/config/locales/menu.en.yml index ebd4d1c86..53e7d9aea 100644 --- a/config/locales/menu.en.yml +++ b/config/locales/menu.en.yml @@ -9,6 +9,7 @@ en: build_lists: Task monitoring groups: Groups advisories: Advisories + statistics: Statistics bottom_menu: copyright: ROSA Lab © %{year} about: About the company diff --git a/config/locales/menu.ru.yml b/config/locales/menu.ru.yml index 9bf4f1f16..0beabbae7 100644 --- a/config/locales/menu.ru.yml +++ b/config/locales/menu.ru.yml @@ -9,6 +9,7 @@ ru: build_lists: Мониторинг задач groups: Группы advisories: Бюллетени + statistics: Статистика bottom_menu: copyright: ROSA Лаб. © %{year} about: О компании @@ -47,7 +48,7 @@ ru: wiki: Вики platform_menu: settings: Настройки - admins_menu_header: Центр управления + admins_menu_header: ЦУ admins_menu: users: Пользователи register_requests: Инвайты diff --git a/config/locales/models/build_list.en.yml b/config/locales/models/build_list.en.yml index 863857acd..443dd6dba 100644 --- a/config/locales/models/build_list.en.yml +++ b/config/locales/models/build_list.en.yml @@ -9,9 +9,11 @@ en: name: Name extra_repositories: Extra repositories extra_build_lists: Extra build lists + extra_mass_builds: Extra mass builds auto_create_container: Create container automatically use_cached_chroot: Use cached chroot use_extra_tests: Use extra tests + save_buildroot: Save RPM build root container_path: Container path status: Status project_id: Project @@ -63,9 +65,21 @@ en: release: Release version: Version + projects: + build_lists: + dependent_projects: + title: Create Build Lists of dependent projects + projects: Projects + packages: "Packages:" + architecture: Architecture + preferences: Preferences + are_you_sure: Are you sure what would like to run build lists? Release tag will be increased automatically. + layout: build_lists: create_container: Create container + create_build_list: Create Build List + are_you_sure_to_run_build_lists: Are you sure what would like to run build lists? Release tag will be increased automatically. platform_deleted: platform has been deleted filter_header: Filter current: Curent @@ -101,6 +115,7 @@ en: new_header: New build main_data: Main data tests_failed: Some tests failed. Be careful! + wrong_branch_for_publish_html: Build can be published only from %{branch} branch human_current_duration: Build currently takes %{hours} h. %{minutes} min. human_duration: Built in %{hours} h. %{minutes} min. @@ -156,6 +171,7 @@ en: build_publish: Build is being published failed_publish: Publishing error dependencies_fail: Dependences not found + packages_fail: Packages not found waiting_for_response: Waiting for response build_pending: Build pending build_canceling: Build is canceling @@ -166,6 +182,7 @@ en: 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' + unpermitted_arch: Unpermitted architecture log: build_log: Build Log @@ -208,3 +225,4 @@ en: wrong_include_repos: Include repos have to belongs to build for platform wrong_commit_hash: "Unable find commit '%{commit_hash}' in project" not_all_build_lists_success: 'Project has been builded successfully not for all default arches' + dependent_projects_job_added_to_queue: Task for creation of build lists has been added to queue. diff --git a/config/locales/models/build_list.ru.yml b/config/locales/models/build_list.ru.yml index cd22f504f..a8fe4da20 100644 --- a/config/locales/models/build_list.ru.yml +++ b/config/locales/models/build_list.ru.yml @@ -9,9 +9,11 @@ ru: name: Название extra_repositories: Дополнительные репозитории extra_build_lists: Дополнительные сборки + extra_mass_builds: Дополнительные массовые сборки auto_create_container: Создать контейнер автоматически use_cached_chroot: Использовать кэшированный chroot use_extra_tests: Использовать дополнительные тесты + save_buildroot: Сохранить RPM build root container_path: Путь до контейнера status: Статус project_id: Проект @@ -62,9 +64,20 @@ ru: release: Релиз version: Версия + projects: + build_lists: + dependent_projects: + title: Создать сборку зависимых проектов + projects: Проекты + packages: "Пакеты:" + architecture: Архитектура + preferences: Настройки + are_you_sure: Вы уверены что хотите запустить сборку? Release тег будет увеличен автоматически. + layout: build_lists: create_container: Создать контейнер + create_build_list: Создать сборку platform_deleted: платформа была удалена filter_header: Фильтр current: Текущие @@ -100,6 +113,7 @@ ru: new_header: Новая сборка main_data: Основные данные tests_failed: Некоторые тесты провалились. Будьте осторожны! + wrong_branch_for_publish_html: Публикация разрешена только для сборок из %{branch} ветки human_current_duration: Сборка длится уже %{hours} ч. %{minutes} мин. human_duration: Собрано за %{hours} ч. %{minutes} мин. @@ -155,6 +169,7 @@ ru: build_publish: публикуется failed_publish: ошибка публикации dependencies_fail: зависимости не найдены + packages_fail: пакеты не найдены waiting_for_response: ожидает ответа build_pending: ожидает сборку build_canceled: сборка отменена @@ -165,7 +180,7 @@ ru: build_published_into_testing: '[testing] опубликован' build_publish_into_testing: '[testing] публикуется' failed_publish_into_testing: '[testing] ошибка публикации' - + unpermitted_arch: Недопустимая архитектура log: build_log: Лог сборки @@ -208,3 +223,4 @@ ru: wrong_include_repos: Включаемые репозитории должны принадлежать платформе для сборки wrong_commit_hash: "Невозможно найти коммит '%{commit_hash}' в проекте" not_all_build_lists_success: Проект собран успешно не для всех дефолтных архитектур + dependent_projects_job_added_to_queue: Задача на создание билд листов добавлена в очередь. diff --git a/config/locales/models/group.en.yml b/config/locales/models/group.en.yml index f8e015de3..4f1c443c5 100644 --- a/config/locales/models/group.en.yml +++ b/config/locales/models/group.en.yml @@ -13,7 +13,7 @@ en: confirm_delete: Are you sure you want to remove this group? edit_members: Members group: Group - description: Descripton + description: Description leave_group: Leave group projects_list: Projects list public_profile: Public profile diff --git a/config/locales/models/mass_build.en.yml b/config/locales/models/mass_build.en.yml index 107f6d4b0..f7ad88208 100644 --- a/config/locales/models/mass_build.en.yml +++ b/config/locales/models/mass_build.en.yml @@ -2,18 +2,23 @@ en: layout: mass_builds: new: New mass build + recreate: Recreate mass build publish_success: Publish success builds publish_test_failed: Publish test failed builds repositories: Repositories extended_data: Extended data lists: Lists failed_builds_list: Failed Builds List + success_builds_list: Completed Build Lists tests_failed_builds_list: Tests failed Builds List statuses: Statuses actions: Actions cancel_confirm: Are you sure you want to cancel mass build? projects_list: Projects list missed_projects_list: 'Missed projects: ' + + placeholder: + description: Description activerecord: models: mass_build: Mass Build @@ -21,13 +26,17 @@ en: mass_build: id: Id name: Name + description: Description created_at: Created updated_at: Updated arch_names: Architectures user: User - auto_publish: Automated publishing + auto_publish_status: Automated publishing + auto_create_container: Create container automatically increase_release_tag: Increase release tag use_cached_chroot: Use cached chroot use_extra_tests: Use extra tests + include_testing_subrepository: "Include 'testing' subrepository" repositories: Repositories projects_list: Projects list + build_for_platform: Build for platform diff --git a/config/locales/models/mass_build.ru.yml b/config/locales/models/mass_build.ru.yml index 7ec4bd4ab..33084f8cc 100644 --- a/config/locales/models/mass_build.ru.yml +++ b/config/locales/models/mass_build.ru.yml @@ -2,18 +2,24 @@ ru: layout: mass_builds: new: Новая массовая сборка + recreate: Пересоздать массовую сборку publish_success: Опубликовать успешные сборки publish_test_failed: Опубликовать сборки с проваленными тестами repositories: Репозитории extended_data: Параметры задания lists: Списки failed_builds_list: Сборки с ошибками + success_builds_list: Успешные сборки tests_failed_builds_list: Сборки с ошибками в тестах statuses: Статусы actions: Действия cancel_confirm: Вы уверены, что хотите отменить массовую сборку? projects_list: Проекты missed_projects_list: 'Несуществующие проекты: ' + + placeholder: + description: Описание + activerecord: models: mass_build: Массовая Сборка @@ -21,13 +27,17 @@ ru: mass_build: id: Id name: Название + description: Описание created_at: Создан updated_at: Обновлен arch_names: Архитектуры user: Пользователь - auto_publish: Автоматическая публикация + auto_publish_status: Автоматическая публикация + auto_create_container: Создать контейнер автоматически increase_release_tag: Увеличить release тег use_cached_chroot: Использовать кэшированный chroot use_extra_tests: Использовать дополнительные тесты + include_testing_subrepository: "Подключить 'testing' подрепозиторий" repositories: Репозитории projects_list: Список проектов + build_for_platform: Собрано для платформы diff --git a/config/locales/models/project.en.yml b/config/locales/models/project.en.yml index bfdfd49a8..0b7966956 100644 --- a/config/locales/models/project.en.yml +++ b/config/locales/models/project.en.yml @@ -101,7 +101,7 @@ en: add_to_repository_id: Add to repository srpms_list: SRPMs list name: Name - description: Descripton + description: Description owner: Owner visibility: Visibility visibility_types: diff --git a/config/locales/models/statistics.en.yml b/config/locales/models/statistics.en.yml new file mode 100644 index 000000000..0246ffe98 --- /dev/null +++ b/config/locales/models/statistics.en.yml @@ -0,0 +1,34 @@ +en: + statistics: + index: + header: "Statistics" + + filter: + header: "Statistics" + range_label: "Display data for" + range_start_placeholder: "Select a start date" + range_separator: " and " + range_end_placeholder: "Select an end date" + no_data: "No data available" + + build_lists: + build_started_title: "Build started" + success_title: "Build complete" + build_error_title: "Build error" + build_published_title: "Build has been published" + + total_build_started: "Total build started" + total_success: "Total build complete" + total_build_error: "Total build error" + total_build_published: "Total build has been published" + + helper: + period: + twenty_four_hours: "the last 24 hours" + last_7_days: "the last 7 days" + last_30_days: "the last 30 days" + last_60_days: "the last 60 days" + last_90_days: "the last 90 days" + last_180_days: "the last 6 months" + last_year: "the last year" + custom: "custom" \ No newline at end of file diff --git a/config/locales/models/statistics.ru.yml b/config/locales/models/statistics.ru.yml new file mode 100644 index 000000000..53233f3c7 --- /dev/null +++ b/config/locales/models/statistics.ru.yml @@ -0,0 +1,34 @@ +ru: + statistics: + index: + header: "Статистика" + + filter: + header: "Статистика" + range_label: "Показать данные за" + range_start_placeholder: "Выберите начальную дату" + range_separator: " и " + range_end_placeholder: "Выберите конечную дату" + no_data: "Нет данных" + + build_lists: + build_started_title: "Cобирается" + success_title: "Cобрано" + build_error_title: "Ошибка сборки" + build_published_title: "Опубликовано" + + total_build_started: "Всего собирается" + total_success: "Всего собрано" + total_build_error: "Всего ошибок сборки" + total_build_published: "Всего опубликовано" + + helper: + period: + twenty_four_hours: "последние 24 часа" + last_7_days: "последние 7 дней" + last_30_days: "последние 30 дней" + last_60_days: "последние 60 дней" + last_90_days: "последние 90 дней" + last_180_days: "последние 6 месяцев" + last_year: "последний год" + custom: "интервал" \ No newline at end of file diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 3d009684f..493a5663e 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -196,4 +196,5 @@ ru: reset: Сброс _on: с until: по + none: Нет close: Закрыть diff --git a/config/locales/users.en.yml b/config/locales/users.en.yml index 670b678d2..47e777b92 100644 --- a/config/locales/users.en.yml +++ b/config/locales/users.en.yml @@ -36,6 +36,7 @@ en: name: Name login: Nickname or Email email: Email + hide_email: Hide email uname: Nickname ssh_key: SSH key current_password: Current password diff --git a/config/locales/users.ru.yml b/config/locales/users.ru.yml index a79c1bf57..b07425e71 100644 --- a/config/locales/users.ru.yml +++ b/config/locales/users.ru.yml @@ -36,6 +36,7 @@ ru: name: Имя login: Псевдоним или Email email: Email + hide_email: Прятать email uname: Никнейм ssh_key: SSH ключ current_password: Текущий пароль diff --git a/config/resque_schedule.yml b/config/resque_schedule.yml index 79565cbb0..9b9ba8127 100644 --- a/config/resque_schedule.yml +++ b/config/resque_schedule.yml @@ -8,14 +8,14 @@ clean_rpm_build_nodes: build_lists_publish_task_manager: every: - '3m' - class: 'BuildListsPublishTaskManagerJob' + class: 'PublishTaskManagerJob' queue: middle description: 'Creates tasks for publishing' build_lists_queues_monitoring: every: - '1m' - class: 'BuildListsQueuesMonitoringJob' + class: 'BuildLists::QueuesMonitoringJob' queue: middle description: 'Monitoring for "user/mass-build" queues' @@ -26,6 +26,20 @@ clean_api_defender_statistics: queue: low description: 'Cleans ApiDefender statistics' +clean_build_list_buildroot: + every: + - '1h' + class: 'BuildLists::CleanBuildrootJob' + queue: middle + description: 'Cleans RPM buildroot from BuildList' + +run_extra_mass_builds: + every: + - '5m' + class: 'RunExtraMassBuildsJob' + queue: low + description: 'Run mass builds with relations' + restart_nodes: every: - '5m' diff --git a/config/routes.rb b/config/routes.rb index 5d4b48f30..a1dc1e8b7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -13,6 +13,7 @@ Rosa::Application.routes.draw do match '/sitemap.xml.gz' => 'sitemap#show', via: [:get, :post, :head], as: :sitemap match '/robots.txt' => 'sitemap#robots', via: [:get, :post, :head], as: :robots + resources :statistics, only: [:index] resource :contact, only: [:new, :create, :sended] do get '/' => 'contacts#new' get :sended @@ -182,11 +183,11 @@ Rosa::Application.routes.draw do end end - resources :mass_builds, only: [:create, :new, :index] do + resources :mass_builds, only: [:create, :new, :index, :show] do member do post :cancel post :publish - get '/:kind' => "mass_builds#get_list", as: :get_list, kind: /failed_builds_list|missed_projects_list|projects_list|tests_failed_builds_list/ + get '/:kind' => "mass_builds#get_list", as: :get_list, kind: /failed_builds_list|missed_projects_list|projects_list|tests_failed_builds_list|success_builds_list/ end end @@ -230,6 +231,7 @@ Rosa::Application.routes.draw do get :autocomplete_user_uname get :autocomplete_group_uname get :autocomplete_extra_build_list + get :autocomplete_extra_mass_build get :autocomplete_extra_repositories end end @@ -283,6 +285,8 @@ Rosa::Application.routes.draw do put :reject_publish put :publish_into_testing put :update_type + get :dependent_projects + post :dependent_projects end end diff --git a/db/migrate/20111012133633_create_event_logs.rb b/db/migrate/20111012133633_create_event_logs.rb index 2c81b40f9..0cca66f5e 100644 --- a/db/migrate/20111012133633_create_event_logs.rb +++ b/db/migrate/20111012133633_create_event_logs.rb @@ -1,9 +1,9 @@ class CreateEventLogs < ActiveRecord::Migration def self.up create_table :event_logs do |t| - t.references :user + t.references :user, foreign_key: false, index: true t.string :user_name - t.references :object, polymorphic: true + t.references :object, polymorphic: true, index: true t.string :object_name t.string :ip t.string :kind @@ -14,8 +14,6 @@ class CreateEventLogs < ActiveRecord::Migration t.timestamps end - # add_index :event_logs, :user_id - # add_index :event_logs, [:object_id, :object_type] end def self.down diff --git a/db/migrate/20111012223006_create_permissions.rb b/db/migrate/20111012223006_create_permissions.rb index 8001623a4..fb897bc90 100644 --- a/db/migrate/20111012223006_create_permissions.rb +++ b/db/migrate/20111012223006_create_permissions.rb @@ -1,15 +1,10 @@ class CreatePermissions < ActiveRecord::Migration - def self.up + def change create_table :permissions do |t| - t.integer :id - t.integer :right_id - t.integer :role_id + t.integer :right_id, references: nil + t.integer :role_id, references: nil t.timestamps end end - - def self.down - drop_table :permissions - end end diff --git a/db/migrate/20111012223306_create_roles.rb b/db/migrate/20111012223306_create_roles.rb index b5693219d..c31c4615a 100644 --- a/db/migrate/20111012223306_create_roles.rb +++ b/db/migrate/20111012223306_create_roles.rb @@ -1,14 +1,9 @@ class CreateRoles < ActiveRecord::Migration - def self.up + def change create_table :roles do |t| - t.integer :id t.string :name t.timestamps end end - - def self.down - drop_table :roles - end end diff --git a/db/migrate/20111012223521_create_relations.rb b/db/migrate/20111012223521_create_relations.rb index 8017f07f4..6a9f124ee 100644 --- a/db/migrate/20111012223521_create_relations.rb +++ b/db/migrate/20111012223521_create_relations.rb @@ -1,9 +1,9 @@ class CreateRelations < ActiveRecord::Migration def self.up create_table :relations do |t| - t.integer :object_id + t.integer :object_id, references: nil t.string :object_type - t.integer :target_id + t.integer :target_id, references: nil t.string :target_type t.timestamps diff --git a/db/migrate/20111013150125_create_project_to_repositories.rb b/db/migrate/20111013150125_create_project_to_repositories.rb index 148561c4c..46b5d3773 100644 --- a/db/migrate/20111013150125_create_project_to_repositories.rb +++ b/db/migrate/20111013150125_create_project_to_repositories.rb @@ -1,15 +1,10 @@ class CreateProjectToRepositories < ActiveRecord::Migration - def self.up + def change create_table :project_to_repositories do |t| - t.integer :id t.integer :project_id t.integer :repository_id t.timestamps end end - - def self.down - drop_table :project_to_repositories - end end diff --git a/db/migrate/20111020160644_add_new_fields_to_build_lists.rb b/db/migrate/20111020160644_add_new_fields_to_build_lists.rb index 35cc1772e..da0709826 100644 --- a/db/migrate/20111020160644_add_new_fields_to_build_lists.rb +++ b/db/migrate/20111020160644_add_new_fields_to_build_lists.rb @@ -2,8 +2,8 @@ class AddNewFieldsToBuildLists < ActiveRecord::Migration def self.up add_column :build_lists, :build_requires, :boolean add_column :build_lists, :update_type, :string - add_column :build_lists, :bpl_id, :integer - add_column :build_lists, :pl_id, :integer + add_column :build_lists, :bpl_id, :integer, references: nil + add_column :build_lists, :pl_id, :integer, references: nil rename_column :build_lists, :branch_name, :project_version end diff --git a/db/migrate/20111023154034_add_global_role_to_users.rb b/db/migrate/20111023154034_add_global_role_to_users.rb index 94ea1f1c4..54316bed6 100644 --- a/db/migrate/20111023154034_add_global_role_to_users.rb +++ b/db/migrate/20111023154034_add_global_role_to_users.rb @@ -1,6 +1,6 @@ class AddGlobalRoleToUsers < ActiveRecord::Migration def self.up - add_column :users, :global_role_id, :integer + add_column :users, :global_role_id, :integer, references: nil end def self.down diff --git a/db/migrate/20111023154130_add_global_role_to_groups.rb b/db/migrate/20111023154130_add_global_role_to_groups.rb index b1d5f38a2..f31257d52 100644 --- a/db/migrate/20111023154130_add_global_role_to_groups.rb +++ b/db/migrate/20111023154130_add_global_role_to_groups.rb @@ -1,6 +1,6 @@ class AddGlobalRoleToGroups < ActiveRecord::Migration def self.up - add_column :groups, :global_role_id, :integer + add_column :groups, :global_role_id, :integer, references: nil end def self.down diff --git a/db/migrate/20120111080234_change_commentable_id.rb b/db/migrate/20120111080234_change_commentable_id.rb index 60c3c3268..fbe62f69e 100644 --- a/db/migrate/20120111080234_change_commentable_id.rb +++ b/db/migrate/20120111080234_change_commentable_id.rb @@ -1,6 +1,6 @@ class ChangeCommentableId < ActiveRecord::Migration def self.up - change_column :comments, :commentable_id, :string + change_column :comments, :commentable_id, :string, references: nil end def self.down diff --git a/db/migrate/20120123161250_change_subscribeable_to_string.rb b/db/migrate/20120123161250_change_subscribeable_to_string.rb index fcee398de..a7bbe5083 100644 --- a/db/migrate/20120123161250_change_subscribeable_to_string.rb +++ b/db/migrate/20120123161250_change_subscribeable_to_string.rb @@ -1,6 +1,6 @@ class ChangeSubscribeableToString < ActiveRecord::Migration def self.up - change_column :subscribes, :subscribeable_id, :string + change_column :subscribes, :subscribeable_id, :string, references: nil end def self.down diff --git a/db/migrate/20120219161749_add_creator_to_issue.rb b/db/migrate/20120219161749_add_creator_to_issue.rb index 54a3ba492..1829bc16c 100644 --- a/db/migrate/20120219161749_add_creator_to_issue.rb +++ b/db/migrate/20120219161749_add_creator_to_issue.rb @@ -1,5 +1,5 @@ class AddCreatorToIssue < ActiveRecord::Migration def change - add_column :issues, :creator_id, :integer + add_column :issues, :creator_id, :integer, references: nil end end diff --git a/db/migrate/20120428053303_create_advisories.rb b/db/migrate/20120428053303_create_advisories.rb index 8886e2aea..dc18875ec 100644 --- a/db/migrate/20120428053303_create_advisories.rb +++ b/db/migrate/20120428053303_create_advisories.rb @@ -1,8 +1,8 @@ class CreateAdvisories < ActiveRecord::Migration def change create_table :advisories do |t| - t.string :advisory_id - t.integer :project_id + t.string :advisory_id, references: nil + t.integer :project_id, references: nil t.text :description, default: '' t.text :references, default: '' t.text :update_type, default: '' diff --git a/db/migrate/20120727141521_add_save_to_repository_to_build_lists.rb b/db/migrate/20120727141521_add_save_to_repository_to_build_lists.rb index 99768ba40..9d35d40df 100644 --- a/db/migrate/20120727141521_add_save_to_repository_to_build_lists.rb +++ b/db/migrate/20120727141521_add_save_to_repository_to_build_lists.rb @@ -1,6 +1,6 @@ class AddSaveToRepositoryToBuildLists < ActiveRecord::Migration def self.up - add_column :build_lists, :save_to_repository_id, :integer + add_column :build_lists, :save_to_repository_id, :integer, references: nil BuildList.includes(project: :repositories, save_to_platform: :repositories).find_in_batches do |batch| batch.each do |bl| diff --git a/db/migrate/20120730185119_set_string_type_for_key_pairs_keyid.rb b/db/migrate/20120730185119_set_string_type_for_key_pairs_keyid.rb index da6337e16..59b107693 100644 --- a/db/migrate/20120730185119_set_string_type_for_key_pairs_keyid.rb +++ b/db/migrate/20120730185119_set_string_type_for_key_pairs_keyid.rb @@ -1,6 +1,6 @@ class SetStringTypeForKeyPairsKeyid < ActiveRecord::Migration def up - change_column :key_pairs, :key_id, :string + change_column :key_pairs, :key_id, :string, references: nil end def down diff --git a/db/migrate/20120822130632_add_maintainer_id_to_projects.rb b/db/migrate/20120822130632_add_maintainer_id_to_projects.rb index 94614ef56..267ae2bf8 100644 --- a/db/migrate/20120822130632_add_maintainer_id_to_projects.rb +++ b/db/migrate/20120822130632_add_maintainer_id_to_projects.rb @@ -1,6 +1,6 @@ class AddMaintainerIdToProjects < ActiveRecord::Migration def self.up - add_column :projects, :maintainer_id, :integer + add_column :projects, :maintainer_id, :integer, references: nil end def self.down diff --git a/db/migrate/20121219122905_add_encrypted_secret_to_key_pairs.rb b/db/migrate/20121219122905_add_encrypted_secret_to_key_pairs.rb index 514a8f6ea..08ed74fda 100644 --- a/db/migrate/20121219122905_add_encrypted_secret_to_key_pairs.rb +++ b/db/migrate/20121219122905_add_encrypted_secret_to_key_pairs.rb @@ -1,12 +1,12 @@ class AddEncryptedSecretToKeyPairs < ActiveRecord::Migration def up rename_table :key_pairs, :key_pairs_backup - rename_index :key_pairs_backup, 'index_key_pairs_on_repository_id', 'index_key_pairs_backup_on_repository_id' + # rename_index :key_pairs_backup, 'index_key_pairs_on_repository_id', 'index_key_pairs_backup_on_repository_id' create_table :key_pairs do |t| t.text :public, null: false t.text :encrypted_secret, null: false - t.string :key_id, null: false + t.string :key_id, null: false, references: nil t.references :user, null: false t.references :repository, null: false t.timestamps @@ -17,6 +17,6 @@ class AddEncryptedSecretToKeyPairs < ActiveRecord::Migration def down drop_table :key_pairs rename_table :key_pairs_backup, :key_pairs - rename_index :key_pairs, 'index_key_pairs_backup_on_repository_id', 'index_key_pairs_on_repository_id' + # rename_index :key_pairs, 'index_key_pairs_backup_on_repository_id', 'index_key_pairs_on_repository_id' end end diff --git a/db/migrate/20130214101404_create_project_tags_table.rb b/db/migrate/20130214101404_create_project_tags_table.rb index d78365bd2..7ad38174a 100644 --- a/db/migrate/20130214101404_create_project_tags_table.rb +++ b/db/migrate/20130214101404_create_project_tags_table.rb @@ -2,7 +2,7 @@ class CreateProjectTagsTable < ActiveRecord::Migration def change create_table :project_tags do |t| t.integer :project_id - t.string :commit_id + t.string :commit_id, references: nil t.string :sha1 t.string :tag_name t.integer :format_id, references: nil diff --git a/db/migrate/20130326165628_add_add_info_to_comments.rb b/db/migrate/20130326165628_add_add_info_to_comments.rb index ca1a34041..a4e6289f8 100644 --- a/db/migrate/20130326165628_add_add_info_to_comments.rb +++ b/db/migrate/20130326165628_add_add_info_to_comments.rb @@ -1,7 +1,7 @@ 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_column :comments, :created_from_issue_id, :integer, references: nil add_index :comments, :created_from_issue_id add_index :comments, :created_from_commit_hash diff --git a/db/migrate/20130328112110_add_published_user_to_build_list.rb b/db/migrate/20130328112110_add_published_user_to_build_list.rb index b7d31b4e5..ff22d4392 100644 --- a/db/migrate/20130328112110_add_published_user_to_build_list.rb +++ b/db/migrate/20130328112110_add_published_user_to_build_list.rb @@ -1,5 +1,5 @@ class AddPublishedUserToBuildList < ActiveRecord::Migration def change - add_column :build_lists, :publisher_id, :integer + add_column :build_lists, :publisher_id, :integer, references: nil 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 index 7cddde1cc..7b244c1ec 100644 --- 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 @@ -3,9 +3,9 @@ class AddExtraReposAndBuildListsToMassBuild < ActiveRecord::Migration end def up - add_column :mass_builds, :save_to_platform_id, :integer + add_column :mass_builds, :save_to_platform_id, :integer, references: nil MassBuild.update_all('save_to_platform_id = platform_id') - change_column :mass_builds, :save_to_platform_id, :integer, null: false + change_column :mass_builds, :save_to_platform_id, :integer, null: false, references: nil 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 diff --git a/db/migrate/20131017171426_add_builder_into_build_list.rb b/db/migrate/20131017171426_add_builder_into_build_list.rb index 4d989bcf5..432f0b1f8 100644 --- a/db/migrate/20131017171426_add_builder_into_build_list.rb +++ b/db/migrate/20131017171426_add_builder_into_build_list.rb @@ -1,5 +1,5 @@ class AddBuilderIntoBuildList < ActiveRecord::Migration def change - add_column :build_lists, :builder_id, :integer + add_column :build_lists, :builder_id, :integer, references: nil end end diff --git a/db/migrate/20140612213342_remove_dependent_projects_from_build_list_packages.rb b/db/migrate/20140612213342_remove_dependent_projects_from_build_list_packages.rb new file mode 100644 index 000000000..04223ed8e --- /dev/null +++ b/db/migrate/20140612213342_remove_dependent_projects_from_build_list_packages.rb @@ -0,0 +1,9 @@ +class RemoveDependentProjectsFromBuildListPackages < ActiveRecord::Migration + def up + remove_column :build_list_packages, :dependent_projects + end + + def down + add_column :build_list_packages, :dependent_projects, :text + end +end diff --git a/db/migrate/20140625204136_add_hide_email_to_users.rb b/db/migrate/20140625204136_add_hide_email_to_users.rb new file mode 100644 index 000000000..0f8eac9da --- /dev/null +++ b/db/migrate/20140625204136_add_hide_email_to_users.rb @@ -0,0 +1,5 @@ +class AddHideEmailToUsers < ActiveRecord::Migration + def change + add_column :users, :hide_email, :boolean, default: true, null: false + end +end diff --git a/db/migrate/20140626195741_add_description_to_mass_builds.rb b/db/migrate/20140626195741_add_description_to_mass_builds.rb new file mode 100644 index 000000000..ba0cc5710 --- /dev/null +++ b/db/migrate/20140626195741_add_description_to_mass_builds.rb @@ -0,0 +1,5 @@ +class AddDescriptionToMassBuilds < ActiveRecord::Migration + def change + add_column :mass_builds, :description, :string + end +end diff --git a/db/migrate/20140627162053_add_auto_publish_status_to_mass_builds.rb b/db/migrate/20140627162053_add_auto_publish_status_to_mass_builds.rb new file mode 100644 index 000000000..733a05db0 --- /dev/null +++ b/db/migrate/20140627162053_add_auto_publish_status_to_mass_builds.rb @@ -0,0 +1,16 @@ +class AddAutoPublishStatusToMassBuilds < ActiveRecord::Migration + class MassBuild < ActiveRecord::Base + end + + def up + add_column :mass_builds, :auto_publish_status, :string, default: 'none', null: false + MassBuild.where(auto_publish: true).update_all(auto_publish_status: :default) + remove_column :mass_builds, :auto_publish + end + + def down + add_column :mass_builds, :auto_publish, :boolean, default: false + MassBuild.where(auto_publish_status: :default).update_all(auto_publish: true) + remove_column :mass_builds, :auto_publish_status + end +end diff --git a/db/migrate/20140627173727_add_extra_mass_builds_to_mass_builds.rb b/db/migrate/20140627173727_add_extra_mass_builds_to_mass_builds.rb new file mode 100644 index 000000000..ed374708d --- /dev/null +++ b/db/migrate/20140627173727_add_extra_mass_builds_to_mass_builds.rb @@ -0,0 +1,5 @@ +class AddExtraMassBuildsToMassBuilds < ActiveRecord::Migration + def change + add_column :mass_builds, :extra_mass_builds, :text + end +end diff --git a/db/migrate/20140630205045_add_include_testing_subrepository_to_mass_builds.rb b/db/migrate/20140630205045_add_include_testing_subrepository_to_mass_builds.rb new file mode 100644 index 000000000..9721ca0a0 --- /dev/null +++ b/db/migrate/20140630205045_add_include_testing_subrepository_to_mass_builds.rb @@ -0,0 +1,5 @@ +class AddIncludeTestingSubrepositoryToMassBuilds < ActiveRecord::Migration + def change + add_column :mass_builds, :include_testing_subrepository, :boolean, null: false, default: false + end +end diff --git a/db/migrate/20140701172739_add_auto_create_container_to_mass_builds.rb b/db/migrate/20140701172739_add_auto_create_container_to_mass_builds.rb new file mode 100644 index 000000000..d109d7f88 --- /dev/null +++ b/db/migrate/20140701172739_add_auto_create_container_to_mass_builds.rb @@ -0,0 +1,5 @@ +class AddAutoCreateContainerToMassBuilds < ActiveRecord::Migration + def change + add_column :mass_builds, :auto_create_container, :boolean, default: false, null: false + end +end diff --git a/db/migrate/20140709194335_add_status_to_mass_builds.rb b/db/migrate/20140709194335_add_status_to_mass_builds.rb new file mode 100644 index 000000000..89f7b89b2 --- /dev/null +++ b/db/migrate/20140709194335_add_status_to_mass_builds.rb @@ -0,0 +1,13 @@ +class AddStatusToMassBuilds < ActiveRecord::Migration + + def up + # Sets SUCCESS for all current builds + add_column :mass_builds, :status, :integer, null: false, default: 0 # SUCCESS + # Sets BUILD_PENDING for all new builds + change_column :mass_builds, :status, :integer, null: false, default: 2000 # BUILD_PENDING + end + + def down + remove_column :mass_builds, :status + end +end diff --git a/db/migrate/20140924203530_add_save_buildroot_to_build_lists.rb b/db/migrate/20140924203530_add_save_buildroot_to_build_lists.rb new file mode 100644 index 000000000..fe911c1be --- /dev/null +++ b/db/migrate/20140924203530_add_save_buildroot_to_build_lists.rb @@ -0,0 +1,5 @@ +class AddSaveBuildrootToBuildLists < ActiveRecord::Migration + def change + add_column :build_lists, :save_buildroot, :boolean, default: false, null: false + end +end diff --git a/db/migrate/20141006182907_create_statistics.rb b/db/migrate/20141006182907_create_statistics.rb new file mode 100644 index 000000000..cd134d78c --- /dev/null +++ b/db/migrate/20141006182907_create_statistics.rb @@ -0,0 +1,32 @@ +class CreateStatistics < ActiveRecord::Migration + def up + create_table :statistics do |t| + t.integer :user_id, null: false + t.string :email, null: false + t.integer :project_id, null: false + t.string :project_name_with_owner, null: false + t.string :key, null: false + t.integer :counter, null: false, default: 0 + t.datetime :activity_at, null: false + + t.timestamps + end + + + add_index :statistics, :user_id + add_index :statistics, :project_id + add_index :statistics, :key + add_index :statistics, [:user_id, :key, :activity_at] + add_index :statistics, [:project_id, :key, :activity_at] + add_index :statistics, [:key, :activity_at] + add_index :statistics, :activity_at + + add_index :statistics, [:user_id, :project_id, :key, :activity_at], unique: true, + name: 'index_statistics_on_all_keys' + end + + def down + drop_table :statistics + end + +end diff --git a/db/schema.rb b/db/schema.rb index 3bc0ac99d..ad6657b54 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20140606193047) do +ActiveRecord::Schema.define(version: 20141006182907) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -96,7 +96,6 @@ ActiveRecord::Schema.define(version: 20140606193047) do t.string "sha1" t.integer "epoch" t.text "dependent_packages" - t.text "dependent_projects" t.index ["actual", "platform_id"], :name => "index_build_list_packages_on_actual_and_platform_id" t.index ["build_list_id"], :name => "index_build_list_packages_on_build_list_id" t.index ["name", "project_id"], :name => "index_build_list_packages_on_name_and_project_id" @@ -144,6 +143,7 @@ ActiveRecord::Schema.define(version: 20140606193047) do t.string "auto_publish_status", default: "default", null: false t.boolean "use_cached_chroot", default: false, null: false t.boolean "use_extra_tests", default: true, null: false + t.boolean "save_buildroot", default: false, null: false t.index ["advisory_id"], :name => "index_build_lists_on_advisory_id" t.index ["arch_id"], :name => "index_build_lists_on_arch_id" t.index ["mass_build_id", "status"], :name => "index_build_lists_on_mass_build_id_and_status" @@ -309,25 +309,30 @@ ActiveRecord::Schema.define(version: 20140606193047) do end create_table "mass_builds", force: true do |t| - t.integer "build_for_platform_id", null: false + t.integer "build_for_platform_id", null: false t.string "name" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.string "arch_names" t.integer "user_id" - t.boolean "auto_publish", default: false, null: false - t.integer "build_lists_count", default: 0, null: false - t.boolean "stop_build", default: false, null: false + t.integer "build_lists_count", default: 0, null: false + t.boolean "stop_build", default: false, null: false t.text "projects_list" - t.integer "missed_projects_count", default: 0, null: false + t.integer "missed_projects_count", default: 0, null: false t.text "missed_projects_list" - t.boolean "new_core", default: true - t.integer "save_to_platform_id", null: false + t.boolean "new_core", default: true + 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 - t.boolean "use_cached_chroot", default: true, null: false - t.boolean "use_extra_tests", default: false, null: false + t.boolean "increase_release_tag", default: false, null: false + t.boolean "use_cached_chroot", default: true, null: false + t.boolean "use_extra_tests", default: false, null: false + t.string "description" + t.string "auto_publish_status", default: "none", null: false + t.text "extra_mass_builds" + t.boolean "include_testing_subrepository", default: false, null: false + t.boolean "auto_create_container", default: false, null: false + t.integer "status", default: 2000, null: false end create_table "users", force: true do |t| @@ -361,6 +366,7 @@ ActiveRecord::Schema.define(version: 20140606193047) do t.string "authentication_token" t.integer "build_priority", default: 50 t.boolean "sound_notifications", default: true + t.boolean "hide_email", default: true, null: false t.index ["authentication_token"], :name => "index_users_on_authentication_token" t.index ["confirmation_token"], :name => "index_users_on_confirmation_token", :unique => true t.index ["email"], :name => "index_users_on_email", :unique => true @@ -575,6 +581,26 @@ ActiveRecord::Schema.define(version: 20140606193047) do t.index ["user_id"], :name => "index_ssh_keys_on_user_id" end + create_table "statistics", force: true do |t| + t.integer "user_id", null: false + t.string "email", null: false + t.integer "project_id", null: false + t.string "project_name_with_owner", null: false + t.string "key", null: false + t.integer "counter", default: 0, null: false + t.datetime "activity_at", null: false + t.datetime "created_at" + t.datetime "updated_at" + t.index ["activity_at"], :name => "index_statistics_on_activity_at" + t.index ["key", "activity_at"], :name => "index_statistics_on_key_and_activity_at" + t.index ["key"], :name => "index_statistics_on_key" + t.index ["project_id", "key", "activity_at"], :name => "index_statistics_on_project_id_and_key_and_activity_at" + t.index ["project_id"], :name => "index_statistics_on_project_id" + t.index ["user_id", "key", "activity_at"], :name => "index_statistics_on_user_id_and_key_and_activity_at" + t.index ["user_id", "project_id", "key", "activity_at"], :name => "index_statistics_on_all_keys", :unique => true + t.index ["user_id"], :name => "index_statistics_on_user_id" + end + create_table "subscribes", force: true do |t| t.string "subscribeable_type" t.integer "user_id" diff --git a/lib/abf_worker/build_lists_publish_task_manager.rb b/lib/abf_worker/build_lists_publish_task_manager.rb deleted file mode 100644 index 95f4b1700..000000000 --- a/lib/abf_worker/build_lists_publish_task_manager.rb +++ /dev/null @@ -1,459 +0,0 @@ -module AbfWorker - class BuildListsPublishTaskManager - REDIS_MAIN_KEY = 'abf-worker::build-lists-publish-task-manager::' - - %w(PROJECTS_FOR_CLEANUP - LOCKED_PROJECTS_FOR_CLEANUP - LOCKED_BUILD_LISTS - PACKAGES_FOR_CLEANUP - REP_AND_PLS_OF_BUILD_LISTS_FOR_CLEANUP_FROM_TESTING - BUILD_LISTS_FOR_CLEANUP_FROM_TESTING).each do |kind| - const_set kind, "#{REDIS_MAIN_KEY}#{kind.downcase.gsub('_', '-')}" - end - - def initialize - @workers_count = APP_CONFIG['abf_worker']['publish_workers_count'] - 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| - key = "#{project.id}-#{repository.id}-#{main_platform.id}" - Redis.current.lpush PROJECTS_FOR_CLEANUP, key - gather_old_packages project.id, repository.id, main_platform.id - - Redis.current.lpush PROJECTS_FOR_CLEANUP, ('testing-' << key) - gather_old_packages project.id, repository.id, main_platform.id, true - end - else - key = "#{project.id}-#{repository.id}-#{repository.platform.id}" - Redis.current.lpush PROJECTS_FOR_CLEANUP, key - gather_old_packages project.id, repository.id, repository.platform.id - - Redis.current.lpush PROJECTS_FOR_CLEANUP, ('testing-' << key) - gather_old_packages project.id, repository.id, repository.platform.id, true - end - end - - def cleanup_completed(projects_for_cleanup) - projects_for_cleanup.each do |key| - Redis.current.lrem LOCKED_PROJECTS_FOR_CLEANUP, 0, key - Redis.current.hdel PACKAGES_FOR_CLEANUP, key - end - end - - def cleanup_failed(projects_for_cleanup) - projects_for_cleanup.each do |key| - Redis.current.lrem LOCKED_PROJECTS_FOR_CLEANUP, 0, key - Redis.current.lpush PROJECTS_FOR_CLEANUP, key - end - end - - def cleanup_packages_from_testing(platform_id, repository_id, *build_lists) - return if build_lists.blank? - rep_pl = "#{repository_id}-#{platform_id}" - key = "#{BUILD_LISTS_FOR_CLEANUP_FROM_TESTING}-#{rep_pl}" - Redis.current.sadd REP_AND_PLS_OF_BUILD_LISTS_FOR_CLEANUP_FROM_TESTING, rep_pl - Redis.current.sadd key, build_lists - end - - def unlock_build_list(build_list) - Redis.current.lrem LOCKED_BUILD_LISTS, 0, build_list.id - end - - def packages_structure - structure = {sources: [], binaries: {}} - Arch.pluck(:name).each{ |name| structure[:binaries][name.to_sym] = [] } - structure - end - - def create_container_for(build_list) - platform_path = "#{build_list.save_to_platform.path}/container/#{build_list.id}" - system "rm -rf #{platform_path} && mkdir -p #{platform_path}" - - 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, - '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', - 'class' => 'AbfWorker::PublishWorker', - 'args' => [{ - id: build_list.id, - cmd_params: cmd_params, - 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}, - time_living: 9600, # 160 min - packages: packages, - old_packages: packages_structure, - build_list_ids: [build_list.id], - projects_for_cleanup: [], - extra: {create_container: true} - }] - ) - end - - 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: status). - where(save_to_repository_id: repository_id). - where(build_for_platform_id: platform_id). - where(arch_id: arch_id). - order(:updated_at).first - build_lists_for_cleanup << bl if bl - end - - old_packages = packages_structure - build_lists_for_cleanup.each do |bl| - bl.last_published(testing).includes(:packages).limit(2).each{ |old_bl| - fill_packages(old_bl, old_packages, :fullname) - } - end - key = (testing ? 'testing-' : '') << "#{project_id}-#{repository_id}-#{platform_id}" - Redis.current.hset PACKAGES_FOR_CLEANUP, key, old_packages.to_json - end - - def fill_packages(bl, results_map, field = :sha1) - results_map[:sources] |= bl.packages.by_package_type('source').pluck(field).compact if field != :sha1 - - binaries = bl.packages.by_package_type('binary').pluck(field).compact - arch = bl.arch.name.to_sym - results_map[:binaries][arch] |= binaries - # Publish/remove i686 RHEL packages into/from x86_64 - if arch == :i586 && bl.build_for_platform.distrib_type == 'rhel' && bl.project.publish_i686_into_x86_64? - results_map[:binaries][:x86_64] |= binaries - end - end - - end - - private - - def create_tasks_for_resign_repositories - 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 = { - 'RELEASED' => r.platform.released, - 'REPOSITORY_NAME' => r.name, - 'TYPE' => distrib_type - }.map{ |k, v| "#{k}=#{v}" }.join(' ') - - Resque.push( - 'publish_worker_default', - 'class' => "AbfWorker::PublishWorkerDefault", - 'args' => [{ - id: r.id, - cmd_params: cmd_params, - 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}, - skip_feedback: true, - 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(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: (testing ? BuildList::BUILD_PUBLISH_INTO_TESTING : BuildList::BUILD_PUBLISH)). - group(:save_to_repository_id, :build_for_platform_id). - order('min_updated_at ASC'). - limit(@workers_count * 2) # because some repos may be locked - - 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? - - for_cleanup = Redis.current.lrange(PROJECTS_FOR_CLEANUP, 0, -1).map do |key| - 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 - - for_cleanup_from_testing = Redis.current.smembers(REP_AND_PLS_OF_BUILD_LISTS_FOR_CLEANUP_FROM_TESTING).map do |key| - next if Redis.current.scard("#{BUILD_LISTS_FOR_CLEANUP_FROM_TESTING}-#{key}") == 0 - rep, pl = *key.split('-') - locked_rep.present? && locked_rep.include?(rep.to_i) ? nil : [rep.to_i, pl.to_i] - end.compact if testing - for_cleanup_from_testing ||= [] - - counter = 1 - available_repos = available_repos.map{ |bl| [bl.save_to_repository_id, bl.build_for_platform_id] } | for_cleanup | for_cleanup_from_testing - available_repos.each do |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, testing) - end - end - - 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.current.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('-')[testing ? 1 : 0] }.uniq, - save_to_repository_id: save_to_repository_id, - status: [BuildList::BUILD_PUBLISH, BuildList::BUILD_PUBLISH_INTO_TESTING] - ).update_all(status: BuildList::FAILED_PUBLISH) - - build_lists = BuildList. - 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.current.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(150) - - old_packages = self.class.packages_structure - - projects_for_cleanup.each do |key| - Redis.current.lrem PROJECTS_FOR_CLEANUP, 0, key - packages = Redis.current.hget PACKAGES_FOR_CLEANUP, key - next unless packages - packages = JSON.parse packages - old_packages[:sources] |= packages['sources'] - Arch.pluck(:name).each do |arch| - old_packages[:binaries][arch.to_sym] |= packages['binaries'][arch] || [] - end - end - - if testing - build_lists_for_cleanup_from_testing = Redis.current.smembers("#{BUILD_LISTS_FOR_CLEANUP_FROM_TESTING}-#{save_to_repository_id}-#{build_for_platform_id}") - BuildList.where(id: build_lists_for_cleanup_from_testing).each do |b| - self.class.fill_packages(b, old_packages, :fullname) - end if build_lists_for_cleanup_from_testing.present? - end - build_lists_for_cleanup_from_testing ||= [] - - bl = build_lists.first - return false if !bl && old_packages[:sources].empty? && old_packages[:binaries].values.flatten.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" - if save_to_platform.personal? - platform_path << '/' << build_for_platform.name - system "mkdir -p #{platform_path}" - end - - distrib_type = build_for_platform.distrib_type - cmd_params = { - '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(' ') - - options = { - 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}, - time_living: 9600, # 160 min - extra: { - repository_status_id: repository_status.id, - build_lists_for_cleanup_from_testing: build_lists_for_cleanup_from_testing - } - } - - 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(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.current.lpush(LOCKED_BUILD_LISTS, bl.id) - end - packages[:sources] = new_sources.values.compact - - Resque.push( - 'publish_worker_default', - 'class' => 'AbfWorker::PublishWorkerDefault', - 'args' => [options.merge({ - packages: packages, - old_packages: old_packages, - build_list_ids: build_list_ids, - projects_for_cleanup: projects_for_cleanup - })] - ) - - projects_for_cleanup.each do |key| - Redis.current.lpush LOCKED_PROJECTS_FOR_CLEANUP, key - end - - rep_pl = "#{save_to_repository_id}-#{build_for_platform_id}" - r_key = "#{BUILD_LISTS_FOR_CLEANUP_FROM_TESTING}-#{rep_pl}" - build_lists_for_cleanup_from_testing.each do |key| - Redis.current.srem r_key, key - end - if Redis.current.scard(r_key) == 0 - Redis.current.srem REP_AND_PLS_OF_BUILD_LISTS_FOR_CLEANUP_FROM_TESTING, rep_pl - end - - 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 - 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? - - 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(' ') - - platform_path = "#{rep.platform.path}/repository" - if rep.platform.personal? - platform_path << '/' << build_for_platform.name - system "mkdir -p #{platform_path}" - end - - Resque.push( - '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' - }, - repository: {id: rep.id}, - # time_living: 9600, # 160 min - time_living: 14400, # 240 min - extra: {repository_status_id: repository_status.id, regenerate: true} - }] - ) if repository_status.start_regeneration - end - end - end -end diff --git a/lib/abf_worker/status_inspector.rb b/lib/abf_worker/status_inspector.rb deleted file mode 100644 index 0106f3df3..000000000 --- a/lib/abf_worker/status_inspector.rb +++ /dev/null @@ -1,60 +0,0 @@ -module AbfWorker - class StatusInspector - - class << self - def projects_status - 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 + count_of_tasks('user_build_') - - mass_build_tasks = count_of_tasks('mass_build_') - result[:rpm][:low_tasks] += mass_build_tasks - result[:rpm][:tasks] += external_bls + mass_build_tasks - result - end - end - - def count_of_tasks(regexp) - Redis.current.smembers('resque:queues'). - select{ |q| q =~ /#{regexp}/ }. - map{ |q| Redis.current.llen("resque:queue:#{q}") }.sum - end - - def products_status - get_status(:iso) { |w, worker| - str = w.to_s - str =~ /iso_worker/ && str !~ /observer/ - } - end - - protected - - def get_status(*queues) - status = {} - queues.each do |worker| - workers = Resque.workers.select{ |w| yield w, worker } - status[worker] = status_of_worker workers, worker - end - status - end - - def status_of_worker(workers, worker) - key = "resque:queue:#{worker}_worker" - default_tasks, tasks = Redis.current.llen("#{key}_default"), Redis.current.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 - end - end -end diff --git a/lib/ext/git/grit.rb b/lib/ext/git/grit.rb index e484ebeb5..5e67c2d26 100644 --- a/lib/ext/git/grit.rb +++ b/lib/ext/git/grit.rb @@ -49,7 +49,7 @@ module Grit if mime_type == 'text/rpm-spec' @raw_mime_type = 'text/x-rpm-spec' else - @raw_mime_type = Linguist::Language.detect(name, data).try(:lexer).try(:mimetypes).try(:first) + @raw_mime_type = Linguist::Language.detect(self).try(:lexer).try(:mimetypes).try(:first) @raw_mime_type ||= DEFAULT_MIME_TYPE @raw_mime_type.gsub!('application', 'text') @raw_mime_type diff --git a/lib/tasks/remove_branch.rake b/lib/tasks/remove_branch.rake index 5019c52cb..040a9dc8a 100644 --- a/lib/tasks/remove_branch.rake +++ b/lib/tasks/remove_branch.rake @@ -11,4 +11,20 @@ namespace :remove_branch do end say 'DONE' end + + desc "Remove branch for platform projects" + task platform: :environment do + branch = ENV['BRANCH'] + platform = ENV['PLATFORM'] + say "START remove branch #{branch} from #{platform} platform" + Platform.find_by(name: platform).repositories.each do |r| + say "=== Process #{r.name} repo" + r.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 + end + say 'DONE' + end end diff --git a/app/assets/images/emoji/bowtie.png b/public/images/emoji/bowtie.png similarity index 100% rename from app/assets/images/emoji/bowtie.png rename to public/images/emoji/bowtie.png diff --git a/app/assets/images/emoji/feelsgood.png b/public/images/emoji/feelsgood.png similarity index 100% rename from app/assets/images/emoji/feelsgood.png rename to public/images/emoji/feelsgood.png diff --git a/app/assets/images/emoji/finnadie.png b/public/images/emoji/finnadie.png similarity index 100% rename from app/assets/images/emoji/finnadie.png rename to public/images/emoji/finnadie.png diff --git a/public/images/emoji/fu.png b/public/images/emoji/fu.png new file mode 100644 index 000000000..61a3fee8d Binary files /dev/null and b/public/images/emoji/fu.png differ diff --git a/app/assets/images/emoji/goberserk.png b/public/images/emoji/goberserk.png similarity index 100% rename from app/assets/images/emoji/goberserk.png rename to public/images/emoji/goberserk.png diff --git a/app/assets/images/emoji/godmode.png b/public/images/emoji/godmode.png similarity index 100% rename from app/assets/images/emoji/godmode.png rename to public/images/emoji/godmode.png diff --git a/app/assets/images/emoji/hurtrealbad.png b/public/images/emoji/hurtrealbad.png similarity index 100% rename from app/assets/images/emoji/hurtrealbad.png rename to public/images/emoji/hurtrealbad.png diff --git a/app/assets/images/emoji/metal.png b/public/images/emoji/metal.png similarity index 100% rename from app/assets/images/emoji/metal.png rename to public/images/emoji/metal.png diff --git a/app/assets/images/emoji/neckbeard.png b/public/images/emoji/neckbeard.png similarity index 100% rename from app/assets/images/emoji/neckbeard.png rename to public/images/emoji/neckbeard.png diff --git a/app/assets/images/emoji/octocat.png b/public/images/emoji/octocat.png similarity index 100% rename from app/assets/images/emoji/octocat.png rename to public/images/emoji/octocat.png diff --git a/app/assets/images/emoji/rage1.png b/public/images/emoji/rage1.png similarity index 100% rename from app/assets/images/emoji/rage1.png rename to public/images/emoji/rage1.png diff --git a/app/assets/images/emoji/rage2.png b/public/images/emoji/rage2.png similarity index 100% rename from app/assets/images/emoji/rage2.png rename to public/images/emoji/rage2.png diff --git a/app/assets/images/emoji/rage3.png b/public/images/emoji/rage3.png similarity index 100% rename from app/assets/images/emoji/rage3.png rename to public/images/emoji/rage3.png diff --git a/app/assets/images/emoji/rage4.png b/public/images/emoji/rage4.png similarity index 100% rename from app/assets/images/emoji/rage4.png rename to public/images/emoji/rage4.png diff --git a/app/assets/images/emoji/shipit.png b/public/images/emoji/shipit.png similarity index 100% rename from app/assets/images/emoji/shipit.png rename to public/images/emoji/shipit.png diff --git a/app/assets/images/emoji/suspect.png b/public/images/emoji/suspect.png similarity index 100% rename from app/assets/images/emoji/suspect.png rename to public/images/emoji/suspect.png diff --git a/app/assets/images/emoji/trollface.png b/public/images/emoji/trollface.png similarity index 100% rename from app/assets/images/emoji/trollface.png rename to public/images/emoji/trollface.png diff --git a/app/assets/images/emoji/hash.png b/public/images/emoji/unicode/0023-20e3.png similarity index 100% rename from app/assets/images/emoji/hash.png rename to public/images/emoji/unicode/0023-20e3.png diff --git a/app/assets/images/emoji/unicode/0030.png b/public/images/emoji/unicode/0030-20e3.png similarity index 100% rename from app/assets/images/emoji/unicode/0030.png rename to public/images/emoji/unicode/0030-20e3.png diff --git a/app/assets/images/emoji/one.png b/public/images/emoji/unicode/0031-20e3.png similarity index 100% rename from app/assets/images/emoji/one.png rename to public/images/emoji/unicode/0031-20e3.png diff --git a/app/assets/images/emoji/two.png b/public/images/emoji/unicode/0032-20e3.png similarity index 100% rename from app/assets/images/emoji/two.png rename to public/images/emoji/unicode/0032-20e3.png diff --git a/app/assets/images/emoji/three.png b/public/images/emoji/unicode/0033-20e3.png similarity index 100% rename from app/assets/images/emoji/three.png rename to public/images/emoji/unicode/0033-20e3.png diff --git a/app/assets/images/emoji/four.png b/public/images/emoji/unicode/0034-20e3.png similarity index 100% rename from app/assets/images/emoji/four.png rename to public/images/emoji/unicode/0034-20e3.png diff --git a/app/assets/images/emoji/five.png b/public/images/emoji/unicode/0035-20e3.png similarity index 100% rename from app/assets/images/emoji/five.png rename to public/images/emoji/unicode/0035-20e3.png diff --git a/app/assets/images/emoji/six.png b/public/images/emoji/unicode/0036-20e3.png similarity index 100% rename from app/assets/images/emoji/six.png rename to public/images/emoji/unicode/0036-20e3.png diff --git a/app/assets/images/emoji/seven.png b/public/images/emoji/unicode/0037-20e3.png similarity index 100% rename from app/assets/images/emoji/seven.png rename to public/images/emoji/unicode/0037-20e3.png diff --git a/app/assets/images/emoji/eight.png b/public/images/emoji/unicode/0038-20e3.png similarity index 100% rename from app/assets/images/emoji/eight.png rename to public/images/emoji/unicode/0038-20e3.png diff --git a/app/assets/images/emoji/nine.png b/public/images/emoji/unicode/0039-20e3.png similarity index 100% rename from app/assets/images/emoji/nine.png rename to public/images/emoji/unicode/0039-20e3.png diff --git a/app/assets/images/emoji/unicode/00a9.png b/public/images/emoji/unicode/00a9.png similarity index 100% rename from app/assets/images/emoji/unicode/00a9.png rename to public/images/emoji/unicode/00a9.png diff --git a/app/assets/images/emoji/unicode/00ae.png b/public/images/emoji/unicode/00ae.png similarity index 100% rename from app/assets/images/emoji/unicode/00ae.png rename to public/images/emoji/unicode/00ae.png diff --git a/app/assets/images/emoji/unicode/1f004.png b/public/images/emoji/unicode/1f004.png similarity index 100% rename from app/assets/images/emoji/unicode/1f004.png rename to public/images/emoji/unicode/1f004.png diff --git a/app/assets/images/emoji/unicode/1f0cf.png b/public/images/emoji/unicode/1f0cf.png similarity index 100% rename from app/assets/images/emoji/unicode/1f0cf.png rename to public/images/emoji/unicode/1f0cf.png diff --git a/app/assets/images/emoji/unicode/1f170.png b/public/images/emoji/unicode/1f170.png similarity index 100% rename from app/assets/images/emoji/unicode/1f170.png rename to public/images/emoji/unicode/1f170.png diff --git a/app/assets/images/emoji/unicode/1f171.png b/public/images/emoji/unicode/1f171.png similarity index 100% rename from app/assets/images/emoji/unicode/1f171.png rename to public/images/emoji/unicode/1f171.png diff --git a/app/assets/images/emoji/unicode/1f17e.png b/public/images/emoji/unicode/1f17e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f17e.png rename to public/images/emoji/unicode/1f17e.png diff --git a/app/assets/images/emoji/unicode/1f17f.png b/public/images/emoji/unicode/1f17f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f17f.png rename to public/images/emoji/unicode/1f17f.png diff --git a/app/assets/images/emoji/unicode/1f18e.png b/public/images/emoji/unicode/1f18e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f18e.png rename to public/images/emoji/unicode/1f18e.png diff --git a/app/assets/images/emoji/unicode/1f191.png b/public/images/emoji/unicode/1f191.png similarity index 100% rename from app/assets/images/emoji/unicode/1f191.png rename to public/images/emoji/unicode/1f191.png diff --git a/app/assets/images/emoji/unicode/1f192.png b/public/images/emoji/unicode/1f192.png similarity index 100% rename from app/assets/images/emoji/unicode/1f192.png rename to public/images/emoji/unicode/1f192.png diff --git a/app/assets/images/emoji/unicode/1f193.png b/public/images/emoji/unicode/1f193.png similarity index 100% rename from app/assets/images/emoji/unicode/1f193.png rename to public/images/emoji/unicode/1f193.png diff --git a/app/assets/images/emoji/unicode/1f194.png b/public/images/emoji/unicode/1f194.png similarity index 100% rename from app/assets/images/emoji/unicode/1f194.png rename to public/images/emoji/unicode/1f194.png diff --git a/app/assets/images/emoji/unicode/1f195.png b/public/images/emoji/unicode/1f195.png similarity index 100% rename from app/assets/images/emoji/unicode/1f195.png rename to public/images/emoji/unicode/1f195.png diff --git a/app/assets/images/emoji/unicode/1f196.png b/public/images/emoji/unicode/1f196.png similarity index 100% rename from app/assets/images/emoji/unicode/1f196.png rename to public/images/emoji/unicode/1f196.png diff --git a/app/assets/images/emoji/unicode/1f197.png b/public/images/emoji/unicode/1f197.png similarity index 100% rename from app/assets/images/emoji/unicode/1f197.png rename to public/images/emoji/unicode/1f197.png diff --git a/app/assets/images/emoji/unicode/1f198.png b/public/images/emoji/unicode/1f198.png similarity index 100% rename from app/assets/images/emoji/unicode/1f198.png rename to public/images/emoji/unicode/1f198.png diff --git a/app/assets/images/emoji/unicode/1f199.png b/public/images/emoji/unicode/1f199.png similarity index 100% rename from app/assets/images/emoji/unicode/1f199.png rename to public/images/emoji/unicode/1f199.png diff --git a/app/assets/images/emoji/unicode/1f19a.png b/public/images/emoji/unicode/1f19a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f19a.png rename to public/images/emoji/unicode/1f19a.png diff --git a/app/assets/images/emoji/cn.png b/public/images/emoji/unicode/1f1e8-1f1f3.png similarity index 100% rename from app/assets/images/emoji/cn.png rename to public/images/emoji/unicode/1f1e8-1f1f3.png diff --git a/app/assets/images/emoji/de.png b/public/images/emoji/unicode/1f1e9-1f1ea.png similarity index 100% rename from app/assets/images/emoji/de.png rename to public/images/emoji/unicode/1f1e9-1f1ea.png diff --git a/app/assets/images/emoji/es.png b/public/images/emoji/unicode/1f1ea-1f1f8.png similarity index 100% rename from app/assets/images/emoji/es.png rename to public/images/emoji/unicode/1f1ea-1f1f8.png diff --git a/app/assets/images/emoji/fr.png b/public/images/emoji/unicode/1f1eb-1f1f7.png similarity index 100% rename from app/assets/images/emoji/fr.png rename to public/images/emoji/unicode/1f1eb-1f1f7.png diff --git a/app/assets/images/emoji/gb.png b/public/images/emoji/unicode/1f1ec-1f1e7.png similarity index 100% rename from app/assets/images/emoji/gb.png rename to public/images/emoji/unicode/1f1ec-1f1e7.png diff --git a/app/assets/images/emoji/it.png b/public/images/emoji/unicode/1f1ee-1f1f9.png similarity index 100% rename from app/assets/images/emoji/it.png rename to public/images/emoji/unicode/1f1ee-1f1f9.png diff --git a/app/assets/images/emoji/jp.png b/public/images/emoji/unicode/1f1ef-1f1f5.png similarity index 100% rename from app/assets/images/emoji/jp.png rename to public/images/emoji/unicode/1f1ef-1f1f5.png diff --git a/app/assets/images/emoji/kr.png b/public/images/emoji/unicode/1f1f0-1f1f7.png similarity index 100% rename from app/assets/images/emoji/kr.png rename to public/images/emoji/unicode/1f1f0-1f1f7.png diff --git a/app/assets/images/emoji/ru.png b/public/images/emoji/unicode/1f1f7-1f1fa.png similarity index 100% rename from app/assets/images/emoji/ru.png rename to public/images/emoji/unicode/1f1f7-1f1fa.png diff --git a/app/assets/images/emoji/unicode/1f1fa.png b/public/images/emoji/unicode/1f1fa-1f1f8.png similarity index 100% rename from app/assets/images/emoji/unicode/1f1fa.png rename to public/images/emoji/unicode/1f1fa-1f1f8.png diff --git a/app/assets/images/emoji/unicode/1f201.png b/public/images/emoji/unicode/1f201.png similarity index 100% rename from app/assets/images/emoji/unicode/1f201.png rename to public/images/emoji/unicode/1f201.png diff --git a/app/assets/images/emoji/unicode/1f202.png b/public/images/emoji/unicode/1f202.png similarity index 100% rename from app/assets/images/emoji/unicode/1f202.png rename to public/images/emoji/unicode/1f202.png diff --git a/app/assets/images/emoji/unicode/1f21a.png b/public/images/emoji/unicode/1f21a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f21a.png rename to public/images/emoji/unicode/1f21a.png diff --git a/app/assets/images/emoji/unicode/1f22f.png b/public/images/emoji/unicode/1f22f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f22f.png rename to public/images/emoji/unicode/1f22f.png diff --git a/app/assets/images/emoji/unicode/1f232.png b/public/images/emoji/unicode/1f232.png similarity index 100% rename from app/assets/images/emoji/unicode/1f232.png rename to public/images/emoji/unicode/1f232.png diff --git a/app/assets/images/emoji/unicode/1f233.png b/public/images/emoji/unicode/1f233.png similarity index 100% rename from app/assets/images/emoji/unicode/1f233.png rename to public/images/emoji/unicode/1f233.png diff --git a/app/assets/images/emoji/unicode/1f234.png b/public/images/emoji/unicode/1f234.png similarity index 100% rename from app/assets/images/emoji/unicode/1f234.png rename to public/images/emoji/unicode/1f234.png diff --git a/app/assets/images/emoji/unicode/1f235.png b/public/images/emoji/unicode/1f235.png similarity index 100% rename from app/assets/images/emoji/unicode/1f235.png rename to public/images/emoji/unicode/1f235.png diff --git a/app/assets/images/emoji/unicode/1f236.png b/public/images/emoji/unicode/1f236.png similarity index 100% rename from app/assets/images/emoji/unicode/1f236.png rename to public/images/emoji/unicode/1f236.png diff --git a/app/assets/images/emoji/unicode/1f237.png b/public/images/emoji/unicode/1f237.png similarity index 100% rename from app/assets/images/emoji/unicode/1f237.png rename to public/images/emoji/unicode/1f237.png diff --git a/app/assets/images/emoji/unicode/1f238.png b/public/images/emoji/unicode/1f238.png similarity index 100% rename from app/assets/images/emoji/unicode/1f238.png rename to public/images/emoji/unicode/1f238.png diff --git a/app/assets/images/emoji/unicode/1f239.png b/public/images/emoji/unicode/1f239.png similarity index 100% rename from app/assets/images/emoji/unicode/1f239.png rename to public/images/emoji/unicode/1f239.png diff --git a/app/assets/images/emoji/unicode/1f23a.png b/public/images/emoji/unicode/1f23a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f23a.png rename to public/images/emoji/unicode/1f23a.png diff --git a/app/assets/images/emoji/unicode/1f250.png b/public/images/emoji/unicode/1f250.png similarity index 100% rename from app/assets/images/emoji/unicode/1f250.png rename to public/images/emoji/unicode/1f250.png diff --git a/app/assets/images/emoji/unicode/1f251.png b/public/images/emoji/unicode/1f251.png similarity index 100% rename from app/assets/images/emoji/unicode/1f251.png rename to public/images/emoji/unicode/1f251.png diff --git a/app/assets/images/emoji/unicode/1f300.png b/public/images/emoji/unicode/1f300.png similarity index 100% rename from app/assets/images/emoji/unicode/1f300.png rename to public/images/emoji/unicode/1f300.png diff --git a/app/assets/images/emoji/unicode/1f301.png b/public/images/emoji/unicode/1f301.png similarity index 100% rename from app/assets/images/emoji/unicode/1f301.png rename to public/images/emoji/unicode/1f301.png diff --git a/app/assets/images/emoji/unicode/1f302.png b/public/images/emoji/unicode/1f302.png similarity index 100% rename from app/assets/images/emoji/unicode/1f302.png rename to public/images/emoji/unicode/1f302.png diff --git a/public/images/emoji/unicode/1f303.png b/public/images/emoji/unicode/1f303.png new file mode 100644 index 000000000..d4de585f6 Binary files /dev/null and b/public/images/emoji/unicode/1f303.png differ diff --git a/app/assets/images/emoji/unicode/1f304.png b/public/images/emoji/unicode/1f304.png similarity index 100% rename from app/assets/images/emoji/unicode/1f304.png rename to public/images/emoji/unicode/1f304.png diff --git a/app/assets/images/emoji/unicode/1f305.png b/public/images/emoji/unicode/1f305.png similarity index 100% rename from app/assets/images/emoji/unicode/1f305.png rename to public/images/emoji/unicode/1f305.png diff --git a/app/assets/images/emoji/unicode/1f306.png b/public/images/emoji/unicode/1f306.png similarity index 100% rename from app/assets/images/emoji/unicode/1f306.png rename to public/images/emoji/unicode/1f306.png diff --git a/app/assets/images/emoji/unicode/1f307.png b/public/images/emoji/unicode/1f307.png similarity index 100% rename from app/assets/images/emoji/unicode/1f307.png rename to public/images/emoji/unicode/1f307.png diff --git a/app/assets/images/emoji/unicode/1f308.png b/public/images/emoji/unicode/1f308.png similarity index 100% rename from app/assets/images/emoji/unicode/1f308.png rename to public/images/emoji/unicode/1f308.png diff --git a/app/assets/images/emoji/unicode/1f309.png b/public/images/emoji/unicode/1f309.png similarity index 100% rename from app/assets/images/emoji/unicode/1f309.png rename to public/images/emoji/unicode/1f309.png diff --git a/app/assets/images/emoji/unicode/1f30a.png b/public/images/emoji/unicode/1f30a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f30a.png rename to public/images/emoji/unicode/1f30a.png diff --git a/app/assets/images/emoji/unicode/1f30b.png b/public/images/emoji/unicode/1f30b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f30b.png rename to public/images/emoji/unicode/1f30b.png diff --git a/app/assets/images/emoji/unicode/1f30c.png b/public/images/emoji/unicode/1f30c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f30c.png rename to public/images/emoji/unicode/1f30c.png diff --git a/app/assets/images/emoji/unicode/1f30d.png b/public/images/emoji/unicode/1f30d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f30d.png rename to public/images/emoji/unicode/1f30d.png diff --git a/app/assets/images/emoji/unicode/1f30e.png b/public/images/emoji/unicode/1f30e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f30e.png rename to public/images/emoji/unicode/1f30e.png diff --git a/app/assets/images/emoji/unicode/1f30f.png b/public/images/emoji/unicode/1f30f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f30f.png rename to public/images/emoji/unicode/1f30f.png diff --git a/app/assets/images/emoji/unicode/1f310.png b/public/images/emoji/unicode/1f310.png similarity index 100% rename from app/assets/images/emoji/unicode/1f310.png rename to public/images/emoji/unicode/1f310.png diff --git a/app/assets/images/emoji/unicode/1f311.png b/public/images/emoji/unicode/1f311.png similarity index 100% rename from app/assets/images/emoji/unicode/1f311.png rename to public/images/emoji/unicode/1f311.png diff --git a/app/assets/images/emoji/unicode/1f312.png b/public/images/emoji/unicode/1f312.png similarity index 100% rename from app/assets/images/emoji/unicode/1f312.png rename to public/images/emoji/unicode/1f312.png diff --git a/app/assets/images/emoji/unicode/1f313.png b/public/images/emoji/unicode/1f313.png similarity index 100% rename from app/assets/images/emoji/unicode/1f313.png rename to public/images/emoji/unicode/1f313.png diff --git a/app/assets/images/emoji/unicode/1f314.png b/public/images/emoji/unicode/1f314.png similarity index 100% rename from app/assets/images/emoji/unicode/1f314.png rename to public/images/emoji/unicode/1f314.png diff --git a/app/assets/images/emoji/unicode/1f315.png b/public/images/emoji/unicode/1f315.png similarity index 100% rename from app/assets/images/emoji/unicode/1f315.png rename to public/images/emoji/unicode/1f315.png diff --git a/app/assets/images/emoji/unicode/1f316.png b/public/images/emoji/unicode/1f316.png similarity index 100% rename from app/assets/images/emoji/unicode/1f316.png rename to public/images/emoji/unicode/1f316.png diff --git a/app/assets/images/emoji/unicode/1f317.png b/public/images/emoji/unicode/1f317.png similarity index 100% rename from app/assets/images/emoji/unicode/1f317.png rename to public/images/emoji/unicode/1f317.png diff --git a/app/assets/images/emoji/unicode/1f318.png b/public/images/emoji/unicode/1f318.png similarity index 100% rename from app/assets/images/emoji/unicode/1f318.png rename to public/images/emoji/unicode/1f318.png diff --git a/app/assets/images/emoji/unicode/1f319.png b/public/images/emoji/unicode/1f319.png similarity index 100% rename from app/assets/images/emoji/unicode/1f319.png rename to public/images/emoji/unicode/1f319.png diff --git a/app/assets/images/emoji/unicode/1f31a.png b/public/images/emoji/unicode/1f31a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f31a.png rename to public/images/emoji/unicode/1f31a.png diff --git a/app/assets/images/emoji/unicode/1f31b.png b/public/images/emoji/unicode/1f31b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f31b.png rename to public/images/emoji/unicode/1f31b.png diff --git a/app/assets/images/emoji/unicode/1f31c.png b/public/images/emoji/unicode/1f31c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f31c.png rename to public/images/emoji/unicode/1f31c.png diff --git a/app/assets/images/emoji/unicode/1f31d.png b/public/images/emoji/unicode/1f31d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f31d.png rename to public/images/emoji/unicode/1f31d.png diff --git a/app/assets/images/emoji/unicode/1f31e.png b/public/images/emoji/unicode/1f31e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f31e.png rename to public/images/emoji/unicode/1f31e.png diff --git a/app/assets/images/emoji/unicode/1f31f.png b/public/images/emoji/unicode/1f31f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f31f.png rename to public/images/emoji/unicode/1f31f.png diff --git a/app/assets/images/emoji/stars.png b/public/images/emoji/unicode/1f320.png similarity index 100% rename from app/assets/images/emoji/stars.png rename to public/images/emoji/unicode/1f320.png diff --git a/app/assets/images/emoji/unicode/1f330.png b/public/images/emoji/unicode/1f330.png similarity index 100% rename from app/assets/images/emoji/unicode/1f330.png rename to public/images/emoji/unicode/1f330.png diff --git a/app/assets/images/emoji/unicode/1f331.png b/public/images/emoji/unicode/1f331.png similarity index 100% rename from app/assets/images/emoji/unicode/1f331.png rename to public/images/emoji/unicode/1f331.png diff --git a/app/assets/images/emoji/unicode/1f332.png b/public/images/emoji/unicode/1f332.png similarity index 100% rename from app/assets/images/emoji/unicode/1f332.png rename to public/images/emoji/unicode/1f332.png diff --git a/app/assets/images/emoji/unicode/1f333.png b/public/images/emoji/unicode/1f333.png similarity index 100% rename from app/assets/images/emoji/unicode/1f333.png rename to public/images/emoji/unicode/1f333.png diff --git a/app/assets/images/emoji/unicode/1f334.png b/public/images/emoji/unicode/1f334.png similarity index 100% rename from app/assets/images/emoji/unicode/1f334.png rename to public/images/emoji/unicode/1f334.png diff --git a/app/assets/images/emoji/unicode/1f335.png b/public/images/emoji/unicode/1f335.png similarity index 100% rename from app/assets/images/emoji/unicode/1f335.png rename to public/images/emoji/unicode/1f335.png diff --git a/app/assets/images/emoji/unicode/1f337.png b/public/images/emoji/unicode/1f337.png similarity index 100% rename from app/assets/images/emoji/unicode/1f337.png rename to public/images/emoji/unicode/1f337.png diff --git a/app/assets/images/emoji/unicode/1f338.png b/public/images/emoji/unicode/1f338.png similarity index 100% rename from app/assets/images/emoji/unicode/1f338.png rename to public/images/emoji/unicode/1f338.png diff --git a/app/assets/images/emoji/unicode/1f339.png b/public/images/emoji/unicode/1f339.png similarity index 100% rename from app/assets/images/emoji/unicode/1f339.png rename to public/images/emoji/unicode/1f339.png diff --git a/app/assets/images/emoji/unicode/1f33a.png b/public/images/emoji/unicode/1f33a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f33a.png rename to public/images/emoji/unicode/1f33a.png diff --git a/app/assets/images/emoji/unicode/1f33b.png b/public/images/emoji/unicode/1f33b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f33b.png rename to public/images/emoji/unicode/1f33b.png diff --git a/app/assets/images/emoji/unicode/1f33c.png b/public/images/emoji/unicode/1f33c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f33c.png rename to public/images/emoji/unicode/1f33c.png diff --git a/app/assets/images/emoji/unicode/1f33d.png b/public/images/emoji/unicode/1f33d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f33d.png rename to public/images/emoji/unicode/1f33d.png diff --git a/app/assets/images/emoji/unicode/1f33e.png b/public/images/emoji/unicode/1f33e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f33e.png rename to public/images/emoji/unicode/1f33e.png diff --git a/app/assets/images/emoji/unicode/1f33f.png b/public/images/emoji/unicode/1f33f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f33f.png rename to public/images/emoji/unicode/1f33f.png diff --git a/app/assets/images/emoji/unicode/1f340.png b/public/images/emoji/unicode/1f340.png similarity index 100% rename from app/assets/images/emoji/unicode/1f340.png rename to public/images/emoji/unicode/1f340.png diff --git a/app/assets/images/emoji/unicode/1f341.png b/public/images/emoji/unicode/1f341.png similarity index 100% rename from app/assets/images/emoji/unicode/1f341.png rename to public/images/emoji/unicode/1f341.png diff --git a/app/assets/images/emoji/unicode/1f342.png b/public/images/emoji/unicode/1f342.png similarity index 100% rename from app/assets/images/emoji/unicode/1f342.png rename to public/images/emoji/unicode/1f342.png diff --git a/app/assets/images/emoji/unicode/1f343.png b/public/images/emoji/unicode/1f343.png similarity index 100% rename from app/assets/images/emoji/unicode/1f343.png rename to public/images/emoji/unicode/1f343.png diff --git a/app/assets/images/emoji/unicode/1f344.png b/public/images/emoji/unicode/1f344.png similarity index 100% rename from app/assets/images/emoji/unicode/1f344.png rename to public/images/emoji/unicode/1f344.png diff --git a/app/assets/images/emoji/unicode/1f345.png b/public/images/emoji/unicode/1f345.png similarity index 100% rename from app/assets/images/emoji/unicode/1f345.png rename to public/images/emoji/unicode/1f345.png diff --git a/app/assets/images/emoji/unicode/1f346.png b/public/images/emoji/unicode/1f346.png similarity index 100% rename from app/assets/images/emoji/unicode/1f346.png rename to public/images/emoji/unicode/1f346.png diff --git a/app/assets/images/emoji/unicode/1f347.png b/public/images/emoji/unicode/1f347.png similarity index 100% rename from app/assets/images/emoji/unicode/1f347.png rename to public/images/emoji/unicode/1f347.png diff --git a/app/assets/images/emoji/unicode/1f348.png b/public/images/emoji/unicode/1f348.png similarity index 100% rename from app/assets/images/emoji/unicode/1f348.png rename to public/images/emoji/unicode/1f348.png diff --git a/app/assets/images/emoji/unicode/1f349.png b/public/images/emoji/unicode/1f349.png similarity index 100% rename from app/assets/images/emoji/unicode/1f349.png rename to public/images/emoji/unicode/1f349.png diff --git a/app/assets/images/emoji/unicode/1f34a.png b/public/images/emoji/unicode/1f34a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f34a.png rename to public/images/emoji/unicode/1f34a.png diff --git a/app/assets/images/emoji/unicode/1f34b.png b/public/images/emoji/unicode/1f34b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f34b.png rename to public/images/emoji/unicode/1f34b.png diff --git a/app/assets/images/emoji/unicode/1f34c.png b/public/images/emoji/unicode/1f34c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f34c.png rename to public/images/emoji/unicode/1f34c.png diff --git a/app/assets/images/emoji/unicode/1f34d.png b/public/images/emoji/unicode/1f34d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f34d.png rename to public/images/emoji/unicode/1f34d.png diff --git a/app/assets/images/emoji/unicode/1f34e.png b/public/images/emoji/unicode/1f34e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f34e.png rename to public/images/emoji/unicode/1f34e.png diff --git a/app/assets/images/emoji/unicode/1f34f.png b/public/images/emoji/unicode/1f34f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f34f.png rename to public/images/emoji/unicode/1f34f.png diff --git a/app/assets/images/emoji/unicode/1f350.png b/public/images/emoji/unicode/1f350.png similarity index 100% rename from app/assets/images/emoji/unicode/1f350.png rename to public/images/emoji/unicode/1f350.png diff --git a/app/assets/images/emoji/unicode/1f351.png b/public/images/emoji/unicode/1f351.png similarity index 100% rename from app/assets/images/emoji/unicode/1f351.png rename to public/images/emoji/unicode/1f351.png diff --git a/app/assets/images/emoji/unicode/1f352.png b/public/images/emoji/unicode/1f352.png similarity index 100% rename from app/assets/images/emoji/unicode/1f352.png rename to public/images/emoji/unicode/1f352.png diff --git a/app/assets/images/emoji/unicode/1f353.png b/public/images/emoji/unicode/1f353.png similarity index 100% rename from app/assets/images/emoji/unicode/1f353.png rename to public/images/emoji/unicode/1f353.png diff --git a/app/assets/images/emoji/unicode/1f354.png b/public/images/emoji/unicode/1f354.png similarity index 100% rename from app/assets/images/emoji/unicode/1f354.png rename to public/images/emoji/unicode/1f354.png diff --git a/app/assets/images/emoji/unicode/1f355.png b/public/images/emoji/unicode/1f355.png similarity index 100% rename from app/assets/images/emoji/unicode/1f355.png rename to public/images/emoji/unicode/1f355.png diff --git a/app/assets/images/emoji/unicode/1f356.png b/public/images/emoji/unicode/1f356.png similarity index 100% rename from app/assets/images/emoji/unicode/1f356.png rename to public/images/emoji/unicode/1f356.png diff --git a/app/assets/images/emoji/unicode/1f357.png b/public/images/emoji/unicode/1f357.png similarity index 100% rename from app/assets/images/emoji/unicode/1f357.png rename to public/images/emoji/unicode/1f357.png diff --git a/app/assets/images/emoji/unicode/1f358.png b/public/images/emoji/unicode/1f358.png similarity index 100% rename from app/assets/images/emoji/unicode/1f358.png rename to public/images/emoji/unicode/1f358.png diff --git a/app/assets/images/emoji/unicode/1f359.png b/public/images/emoji/unicode/1f359.png similarity index 100% rename from app/assets/images/emoji/unicode/1f359.png rename to public/images/emoji/unicode/1f359.png diff --git a/app/assets/images/emoji/unicode/1f35a.png b/public/images/emoji/unicode/1f35a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f35a.png rename to public/images/emoji/unicode/1f35a.png diff --git a/app/assets/images/emoji/unicode/1f35b.png b/public/images/emoji/unicode/1f35b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f35b.png rename to public/images/emoji/unicode/1f35b.png diff --git a/app/assets/images/emoji/unicode/1f35c.png b/public/images/emoji/unicode/1f35c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f35c.png rename to public/images/emoji/unicode/1f35c.png diff --git a/app/assets/images/emoji/unicode/1f35d.png b/public/images/emoji/unicode/1f35d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f35d.png rename to public/images/emoji/unicode/1f35d.png diff --git a/app/assets/images/emoji/unicode/1f35e.png b/public/images/emoji/unicode/1f35e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f35e.png rename to public/images/emoji/unicode/1f35e.png diff --git a/app/assets/images/emoji/unicode/1f35f.png b/public/images/emoji/unicode/1f35f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f35f.png rename to public/images/emoji/unicode/1f35f.png diff --git a/app/assets/images/emoji/unicode/1f360.png b/public/images/emoji/unicode/1f360.png similarity index 100% rename from app/assets/images/emoji/unicode/1f360.png rename to public/images/emoji/unicode/1f360.png diff --git a/app/assets/images/emoji/unicode/1f361.png b/public/images/emoji/unicode/1f361.png similarity index 100% rename from app/assets/images/emoji/unicode/1f361.png rename to public/images/emoji/unicode/1f361.png diff --git a/app/assets/images/emoji/unicode/1f362.png b/public/images/emoji/unicode/1f362.png similarity index 100% rename from app/assets/images/emoji/unicode/1f362.png rename to public/images/emoji/unicode/1f362.png diff --git a/app/assets/images/emoji/unicode/1f363.png b/public/images/emoji/unicode/1f363.png similarity index 100% rename from app/assets/images/emoji/unicode/1f363.png rename to public/images/emoji/unicode/1f363.png diff --git a/app/assets/images/emoji/unicode/1f364.png b/public/images/emoji/unicode/1f364.png similarity index 100% rename from app/assets/images/emoji/unicode/1f364.png rename to public/images/emoji/unicode/1f364.png diff --git a/app/assets/images/emoji/unicode/1f365.png b/public/images/emoji/unicode/1f365.png similarity index 100% rename from app/assets/images/emoji/unicode/1f365.png rename to public/images/emoji/unicode/1f365.png diff --git a/app/assets/images/emoji/unicode/1f366.png b/public/images/emoji/unicode/1f366.png similarity index 100% rename from app/assets/images/emoji/unicode/1f366.png rename to public/images/emoji/unicode/1f366.png diff --git a/app/assets/images/emoji/unicode/1f367.png b/public/images/emoji/unicode/1f367.png similarity index 100% rename from app/assets/images/emoji/unicode/1f367.png rename to public/images/emoji/unicode/1f367.png diff --git a/app/assets/images/emoji/unicode/1f368.png b/public/images/emoji/unicode/1f368.png similarity index 100% rename from app/assets/images/emoji/unicode/1f368.png rename to public/images/emoji/unicode/1f368.png diff --git a/app/assets/images/emoji/unicode/1f369.png b/public/images/emoji/unicode/1f369.png similarity index 100% rename from app/assets/images/emoji/unicode/1f369.png rename to public/images/emoji/unicode/1f369.png diff --git a/app/assets/images/emoji/unicode/1f36a.png b/public/images/emoji/unicode/1f36a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f36a.png rename to public/images/emoji/unicode/1f36a.png diff --git a/app/assets/images/emoji/unicode/1f36b.png b/public/images/emoji/unicode/1f36b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f36b.png rename to public/images/emoji/unicode/1f36b.png diff --git a/app/assets/images/emoji/unicode/1f36c.png b/public/images/emoji/unicode/1f36c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f36c.png rename to public/images/emoji/unicode/1f36c.png diff --git a/app/assets/images/emoji/unicode/1f36d.png b/public/images/emoji/unicode/1f36d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f36d.png rename to public/images/emoji/unicode/1f36d.png diff --git a/app/assets/images/emoji/unicode/1f36e.png b/public/images/emoji/unicode/1f36e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f36e.png rename to public/images/emoji/unicode/1f36e.png diff --git a/app/assets/images/emoji/unicode/1f36f.png b/public/images/emoji/unicode/1f36f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f36f.png rename to public/images/emoji/unicode/1f36f.png diff --git a/app/assets/images/emoji/unicode/1f370.png b/public/images/emoji/unicode/1f370.png similarity index 100% rename from app/assets/images/emoji/unicode/1f370.png rename to public/images/emoji/unicode/1f370.png diff --git a/app/assets/images/emoji/unicode/1f371.png b/public/images/emoji/unicode/1f371.png similarity index 100% rename from app/assets/images/emoji/unicode/1f371.png rename to public/images/emoji/unicode/1f371.png diff --git a/app/assets/images/emoji/unicode/1f372.png b/public/images/emoji/unicode/1f372.png similarity index 100% rename from app/assets/images/emoji/unicode/1f372.png rename to public/images/emoji/unicode/1f372.png diff --git a/app/assets/images/emoji/unicode/1f373.png b/public/images/emoji/unicode/1f373.png similarity index 100% rename from app/assets/images/emoji/unicode/1f373.png rename to public/images/emoji/unicode/1f373.png diff --git a/app/assets/images/emoji/unicode/1f374.png b/public/images/emoji/unicode/1f374.png similarity index 100% rename from app/assets/images/emoji/unicode/1f374.png rename to public/images/emoji/unicode/1f374.png diff --git a/app/assets/images/emoji/unicode/1f375.png b/public/images/emoji/unicode/1f375.png similarity index 100% rename from app/assets/images/emoji/unicode/1f375.png rename to public/images/emoji/unicode/1f375.png diff --git a/app/assets/images/emoji/unicode/1f376.png b/public/images/emoji/unicode/1f376.png similarity index 100% rename from app/assets/images/emoji/unicode/1f376.png rename to public/images/emoji/unicode/1f376.png diff --git a/app/assets/images/emoji/unicode/1f377.png b/public/images/emoji/unicode/1f377.png similarity index 100% rename from app/assets/images/emoji/unicode/1f377.png rename to public/images/emoji/unicode/1f377.png diff --git a/app/assets/images/emoji/unicode/1f378.png b/public/images/emoji/unicode/1f378.png similarity index 100% rename from app/assets/images/emoji/unicode/1f378.png rename to public/images/emoji/unicode/1f378.png diff --git a/app/assets/images/emoji/unicode/1f379.png b/public/images/emoji/unicode/1f379.png similarity index 100% rename from app/assets/images/emoji/unicode/1f379.png rename to public/images/emoji/unicode/1f379.png diff --git a/app/assets/images/emoji/unicode/1f37a.png b/public/images/emoji/unicode/1f37a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f37a.png rename to public/images/emoji/unicode/1f37a.png diff --git a/app/assets/images/emoji/unicode/1f37b.png b/public/images/emoji/unicode/1f37b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f37b.png rename to public/images/emoji/unicode/1f37b.png diff --git a/app/assets/images/emoji/unicode/1f37c.png b/public/images/emoji/unicode/1f37c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f37c.png rename to public/images/emoji/unicode/1f37c.png diff --git a/app/assets/images/emoji/unicode/1f380.png b/public/images/emoji/unicode/1f380.png similarity index 100% rename from app/assets/images/emoji/unicode/1f380.png rename to public/images/emoji/unicode/1f380.png diff --git a/app/assets/images/emoji/unicode/1f381.png b/public/images/emoji/unicode/1f381.png similarity index 100% rename from app/assets/images/emoji/unicode/1f381.png rename to public/images/emoji/unicode/1f381.png diff --git a/app/assets/images/emoji/unicode/1f382.png b/public/images/emoji/unicode/1f382.png similarity index 100% rename from app/assets/images/emoji/unicode/1f382.png rename to public/images/emoji/unicode/1f382.png diff --git a/app/assets/images/emoji/unicode/1f383.png b/public/images/emoji/unicode/1f383.png similarity index 100% rename from app/assets/images/emoji/unicode/1f383.png rename to public/images/emoji/unicode/1f383.png diff --git a/app/assets/images/emoji/unicode/1f384.png b/public/images/emoji/unicode/1f384.png similarity index 100% rename from app/assets/images/emoji/unicode/1f384.png rename to public/images/emoji/unicode/1f384.png diff --git a/app/assets/images/emoji/unicode/1f385.png b/public/images/emoji/unicode/1f385.png similarity index 100% rename from app/assets/images/emoji/unicode/1f385.png rename to public/images/emoji/unicode/1f385.png diff --git a/app/assets/images/emoji/unicode/1f386.png b/public/images/emoji/unicode/1f386.png similarity index 100% rename from app/assets/images/emoji/unicode/1f386.png rename to public/images/emoji/unicode/1f386.png diff --git a/app/assets/images/emoji/unicode/1f387.png b/public/images/emoji/unicode/1f387.png similarity index 100% rename from app/assets/images/emoji/unicode/1f387.png rename to public/images/emoji/unicode/1f387.png diff --git a/app/assets/images/emoji/unicode/1f388.png b/public/images/emoji/unicode/1f388.png similarity index 100% rename from app/assets/images/emoji/unicode/1f388.png rename to public/images/emoji/unicode/1f388.png diff --git a/app/assets/images/emoji/unicode/1f389.png b/public/images/emoji/unicode/1f389.png similarity index 100% rename from app/assets/images/emoji/unicode/1f389.png rename to public/images/emoji/unicode/1f389.png diff --git a/app/assets/images/emoji/unicode/1f38a.png b/public/images/emoji/unicode/1f38a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f38a.png rename to public/images/emoji/unicode/1f38a.png diff --git a/app/assets/images/emoji/unicode/1f38b.png b/public/images/emoji/unicode/1f38b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f38b.png rename to public/images/emoji/unicode/1f38b.png diff --git a/app/assets/images/emoji/unicode/1f38c.png b/public/images/emoji/unicode/1f38c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f38c.png rename to public/images/emoji/unicode/1f38c.png diff --git a/app/assets/images/emoji/unicode/1f38d.png b/public/images/emoji/unicode/1f38d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f38d.png rename to public/images/emoji/unicode/1f38d.png diff --git a/app/assets/images/emoji/unicode/1f38e.png b/public/images/emoji/unicode/1f38e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f38e.png rename to public/images/emoji/unicode/1f38e.png diff --git a/app/assets/images/emoji/unicode/1f38f.png b/public/images/emoji/unicode/1f38f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f38f.png rename to public/images/emoji/unicode/1f38f.png diff --git a/app/assets/images/emoji/unicode/1f390.png b/public/images/emoji/unicode/1f390.png similarity index 100% rename from app/assets/images/emoji/unicode/1f390.png rename to public/images/emoji/unicode/1f390.png diff --git a/app/assets/images/emoji/unicode/1f391.png b/public/images/emoji/unicode/1f391.png similarity index 100% rename from app/assets/images/emoji/unicode/1f391.png rename to public/images/emoji/unicode/1f391.png diff --git a/app/assets/images/emoji/unicode/1f392.png b/public/images/emoji/unicode/1f392.png similarity index 100% rename from app/assets/images/emoji/unicode/1f392.png rename to public/images/emoji/unicode/1f392.png diff --git a/app/assets/images/emoji/unicode/1f393.png b/public/images/emoji/unicode/1f393.png similarity index 100% rename from app/assets/images/emoji/unicode/1f393.png rename to public/images/emoji/unicode/1f393.png diff --git a/app/assets/images/emoji/unicode/1f3a0.png b/public/images/emoji/unicode/1f3a0.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3a0.png rename to public/images/emoji/unicode/1f3a0.png diff --git a/app/assets/images/emoji/unicode/1f3a1.png b/public/images/emoji/unicode/1f3a1.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3a1.png rename to public/images/emoji/unicode/1f3a1.png diff --git a/app/assets/images/emoji/unicode/1f3a2.png b/public/images/emoji/unicode/1f3a2.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3a2.png rename to public/images/emoji/unicode/1f3a2.png diff --git a/app/assets/images/emoji/unicode/1f3a3.png b/public/images/emoji/unicode/1f3a3.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3a3.png rename to public/images/emoji/unicode/1f3a3.png diff --git a/app/assets/images/emoji/unicode/1f3a4.png b/public/images/emoji/unicode/1f3a4.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3a4.png rename to public/images/emoji/unicode/1f3a4.png diff --git a/app/assets/images/emoji/unicode/1f3a5.png b/public/images/emoji/unicode/1f3a5.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3a5.png rename to public/images/emoji/unicode/1f3a5.png diff --git a/app/assets/images/emoji/unicode/1f3a6.png b/public/images/emoji/unicode/1f3a6.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3a6.png rename to public/images/emoji/unicode/1f3a6.png diff --git a/app/assets/images/emoji/unicode/1f3a7.png b/public/images/emoji/unicode/1f3a7.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3a7.png rename to public/images/emoji/unicode/1f3a7.png diff --git a/app/assets/images/emoji/unicode/1f3a8.png b/public/images/emoji/unicode/1f3a8.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3a8.png rename to public/images/emoji/unicode/1f3a8.png diff --git a/app/assets/images/emoji/unicode/1f3a9.png b/public/images/emoji/unicode/1f3a9.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3a9.png rename to public/images/emoji/unicode/1f3a9.png diff --git a/app/assets/images/emoji/unicode/1f3aa.png b/public/images/emoji/unicode/1f3aa.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3aa.png rename to public/images/emoji/unicode/1f3aa.png diff --git a/app/assets/images/emoji/unicode/1f3ab.png b/public/images/emoji/unicode/1f3ab.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3ab.png rename to public/images/emoji/unicode/1f3ab.png diff --git a/app/assets/images/emoji/unicode/1f3ac.png b/public/images/emoji/unicode/1f3ac.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3ac.png rename to public/images/emoji/unicode/1f3ac.png diff --git a/app/assets/images/emoji/unicode/1f3ad.png b/public/images/emoji/unicode/1f3ad.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3ad.png rename to public/images/emoji/unicode/1f3ad.png diff --git a/app/assets/images/emoji/unicode/1f3ae.png b/public/images/emoji/unicode/1f3ae.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3ae.png rename to public/images/emoji/unicode/1f3ae.png diff --git a/app/assets/images/emoji/unicode/1f3af.png b/public/images/emoji/unicode/1f3af.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3af.png rename to public/images/emoji/unicode/1f3af.png diff --git a/app/assets/images/emoji/unicode/1f3b0.png b/public/images/emoji/unicode/1f3b0.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3b0.png rename to public/images/emoji/unicode/1f3b0.png diff --git a/app/assets/images/emoji/unicode/1f3b1.png b/public/images/emoji/unicode/1f3b1.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3b1.png rename to public/images/emoji/unicode/1f3b1.png diff --git a/app/assets/images/emoji/unicode/1f3b2.png b/public/images/emoji/unicode/1f3b2.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3b2.png rename to public/images/emoji/unicode/1f3b2.png diff --git a/app/assets/images/emoji/unicode/1f3b3.png b/public/images/emoji/unicode/1f3b3.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3b3.png rename to public/images/emoji/unicode/1f3b3.png diff --git a/app/assets/images/emoji/unicode/1f3b4.png b/public/images/emoji/unicode/1f3b4.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3b4.png rename to public/images/emoji/unicode/1f3b4.png diff --git a/app/assets/images/emoji/unicode/1f3b5.png b/public/images/emoji/unicode/1f3b5.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3b5.png rename to public/images/emoji/unicode/1f3b5.png diff --git a/app/assets/images/emoji/unicode/1f3b6.png b/public/images/emoji/unicode/1f3b6.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3b6.png rename to public/images/emoji/unicode/1f3b6.png diff --git a/app/assets/images/emoji/unicode/1f3b7.png b/public/images/emoji/unicode/1f3b7.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3b7.png rename to public/images/emoji/unicode/1f3b7.png diff --git a/app/assets/images/emoji/unicode/1f3b8.png b/public/images/emoji/unicode/1f3b8.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3b8.png rename to public/images/emoji/unicode/1f3b8.png diff --git a/app/assets/images/emoji/unicode/1f3b9.png b/public/images/emoji/unicode/1f3b9.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3b9.png rename to public/images/emoji/unicode/1f3b9.png diff --git a/app/assets/images/emoji/unicode/1f3ba.png b/public/images/emoji/unicode/1f3ba.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3ba.png rename to public/images/emoji/unicode/1f3ba.png diff --git a/app/assets/images/emoji/unicode/1f3bb.png b/public/images/emoji/unicode/1f3bb.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3bb.png rename to public/images/emoji/unicode/1f3bb.png diff --git a/app/assets/images/emoji/unicode/1f3bc.png b/public/images/emoji/unicode/1f3bc.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3bc.png rename to public/images/emoji/unicode/1f3bc.png diff --git a/app/assets/images/emoji/unicode/1f3bd.png b/public/images/emoji/unicode/1f3bd.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3bd.png rename to public/images/emoji/unicode/1f3bd.png diff --git a/app/assets/images/emoji/unicode/1f3be.png b/public/images/emoji/unicode/1f3be.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3be.png rename to public/images/emoji/unicode/1f3be.png diff --git a/app/assets/images/emoji/unicode/1f3bf.png b/public/images/emoji/unicode/1f3bf.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3bf.png rename to public/images/emoji/unicode/1f3bf.png diff --git a/app/assets/images/emoji/unicode/1f3c0.png b/public/images/emoji/unicode/1f3c0.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3c0.png rename to public/images/emoji/unicode/1f3c0.png diff --git a/app/assets/images/emoji/unicode/1f3c1.png b/public/images/emoji/unicode/1f3c1.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3c1.png rename to public/images/emoji/unicode/1f3c1.png diff --git a/app/assets/images/emoji/unicode/1f3c2.png b/public/images/emoji/unicode/1f3c2.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3c2.png rename to public/images/emoji/unicode/1f3c2.png diff --git a/app/assets/images/emoji/unicode/1f3c3.png b/public/images/emoji/unicode/1f3c3.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3c3.png rename to public/images/emoji/unicode/1f3c3.png diff --git a/app/assets/images/emoji/unicode/1f3c4.png b/public/images/emoji/unicode/1f3c4.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3c4.png rename to public/images/emoji/unicode/1f3c4.png diff --git a/app/assets/images/emoji/unicode/1f3c6.png b/public/images/emoji/unicode/1f3c6.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3c6.png rename to public/images/emoji/unicode/1f3c6.png diff --git a/app/assets/images/emoji/unicode/1f3c7.png b/public/images/emoji/unicode/1f3c7.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3c7.png rename to public/images/emoji/unicode/1f3c7.png diff --git a/app/assets/images/emoji/unicode/1f3c8.png b/public/images/emoji/unicode/1f3c8.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3c8.png rename to public/images/emoji/unicode/1f3c8.png diff --git a/app/assets/images/emoji/unicode/1f3c9.png b/public/images/emoji/unicode/1f3c9.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3c9.png rename to public/images/emoji/unicode/1f3c9.png diff --git a/app/assets/images/emoji/unicode/1f3ca.png b/public/images/emoji/unicode/1f3ca.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3ca.png rename to public/images/emoji/unicode/1f3ca.png diff --git a/app/assets/images/emoji/unicode/1f3e0.png b/public/images/emoji/unicode/1f3e0.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3e0.png rename to public/images/emoji/unicode/1f3e0.png diff --git a/app/assets/images/emoji/unicode/1f3e1.png b/public/images/emoji/unicode/1f3e1.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3e1.png rename to public/images/emoji/unicode/1f3e1.png diff --git a/app/assets/images/emoji/unicode/1f3e2.png b/public/images/emoji/unicode/1f3e2.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3e2.png rename to public/images/emoji/unicode/1f3e2.png diff --git a/app/assets/images/emoji/unicode/1f3e3.png b/public/images/emoji/unicode/1f3e3.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3e3.png rename to public/images/emoji/unicode/1f3e3.png diff --git a/app/assets/images/emoji/unicode/1f3e4.png b/public/images/emoji/unicode/1f3e4.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3e4.png rename to public/images/emoji/unicode/1f3e4.png diff --git a/app/assets/images/emoji/unicode/1f3e5.png b/public/images/emoji/unicode/1f3e5.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3e5.png rename to public/images/emoji/unicode/1f3e5.png diff --git a/app/assets/images/emoji/unicode/1f3e6.png b/public/images/emoji/unicode/1f3e6.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3e6.png rename to public/images/emoji/unicode/1f3e6.png diff --git a/app/assets/images/emoji/unicode/1f3e7.png b/public/images/emoji/unicode/1f3e7.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3e7.png rename to public/images/emoji/unicode/1f3e7.png diff --git a/app/assets/images/emoji/unicode/1f3e8.png b/public/images/emoji/unicode/1f3e8.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3e8.png rename to public/images/emoji/unicode/1f3e8.png diff --git a/app/assets/images/emoji/unicode/1f3e9.png b/public/images/emoji/unicode/1f3e9.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3e9.png rename to public/images/emoji/unicode/1f3e9.png diff --git a/app/assets/images/emoji/unicode/1f3ea.png b/public/images/emoji/unicode/1f3ea.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3ea.png rename to public/images/emoji/unicode/1f3ea.png diff --git a/app/assets/images/emoji/unicode/1f3eb.png b/public/images/emoji/unicode/1f3eb.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3eb.png rename to public/images/emoji/unicode/1f3eb.png diff --git a/app/assets/images/emoji/unicode/1f3ec.png b/public/images/emoji/unicode/1f3ec.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3ec.png rename to public/images/emoji/unicode/1f3ec.png diff --git a/app/assets/images/emoji/unicode/1f3ed.png b/public/images/emoji/unicode/1f3ed.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3ed.png rename to public/images/emoji/unicode/1f3ed.png diff --git a/app/assets/images/emoji/unicode/1f3ee.png b/public/images/emoji/unicode/1f3ee.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3ee.png rename to public/images/emoji/unicode/1f3ee.png diff --git a/app/assets/images/emoji/unicode/1f3ef.png b/public/images/emoji/unicode/1f3ef.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3ef.png rename to public/images/emoji/unicode/1f3ef.png diff --git a/app/assets/images/emoji/unicode/1f3f0.png b/public/images/emoji/unicode/1f3f0.png similarity index 100% rename from app/assets/images/emoji/unicode/1f3f0.png rename to public/images/emoji/unicode/1f3f0.png diff --git a/app/assets/images/emoji/unicode/1f400.png b/public/images/emoji/unicode/1f400.png similarity index 100% rename from app/assets/images/emoji/unicode/1f400.png rename to public/images/emoji/unicode/1f400.png diff --git a/app/assets/images/emoji/unicode/1f401.png b/public/images/emoji/unicode/1f401.png similarity index 100% rename from app/assets/images/emoji/unicode/1f401.png rename to public/images/emoji/unicode/1f401.png diff --git a/app/assets/images/emoji/unicode/1f402.png b/public/images/emoji/unicode/1f402.png similarity index 100% rename from app/assets/images/emoji/unicode/1f402.png rename to public/images/emoji/unicode/1f402.png diff --git a/app/assets/images/emoji/unicode/1f403.png b/public/images/emoji/unicode/1f403.png similarity index 100% rename from app/assets/images/emoji/unicode/1f403.png rename to public/images/emoji/unicode/1f403.png diff --git a/app/assets/images/emoji/unicode/1f404.png b/public/images/emoji/unicode/1f404.png similarity index 100% rename from app/assets/images/emoji/unicode/1f404.png rename to public/images/emoji/unicode/1f404.png diff --git a/app/assets/images/emoji/unicode/1f405.png b/public/images/emoji/unicode/1f405.png similarity index 100% rename from app/assets/images/emoji/unicode/1f405.png rename to public/images/emoji/unicode/1f405.png diff --git a/app/assets/images/emoji/unicode/1f406.png b/public/images/emoji/unicode/1f406.png similarity index 100% rename from app/assets/images/emoji/unicode/1f406.png rename to public/images/emoji/unicode/1f406.png diff --git a/app/assets/images/emoji/unicode/1f407.png b/public/images/emoji/unicode/1f407.png similarity index 100% rename from app/assets/images/emoji/unicode/1f407.png rename to public/images/emoji/unicode/1f407.png diff --git a/app/assets/images/emoji/unicode/1f408.png b/public/images/emoji/unicode/1f408.png similarity index 100% rename from app/assets/images/emoji/unicode/1f408.png rename to public/images/emoji/unicode/1f408.png diff --git a/app/assets/images/emoji/unicode/1f409.png b/public/images/emoji/unicode/1f409.png similarity index 100% rename from app/assets/images/emoji/unicode/1f409.png rename to public/images/emoji/unicode/1f409.png diff --git a/app/assets/images/emoji/unicode/1f40a.png b/public/images/emoji/unicode/1f40a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f40a.png rename to public/images/emoji/unicode/1f40a.png diff --git a/app/assets/images/emoji/unicode/1f40b.png b/public/images/emoji/unicode/1f40b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f40b.png rename to public/images/emoji/unicode/1f40b.png diff --git a/app/assets/images/emoji/unicode/1f40c.png b/public/images/emoji/unicode/1f40c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f40c.png rename to public/images/emoji/unicode/1f40c.png diff --git a/app/assets/images/emoji/unicode/1f40d.png b/public/images/emoji/unicode/1f40d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f40d.png rename to public/images/emoji/unicode/1f40d.png diff --git a/app/assets/images/emoji/unicode/1f40e.png b/public/images/emoji/unicode/1f40e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f40e.png rename to public/images/emoji/unicode/1f40e.png diff --git a/app/assets/images/emoji/unicode/1f40f.png b/public/images/emoji/unicode/1f40f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f40f.png rename to public/images/emoji/unicode/1f40f.png diff --git a/app/assets/images/emoji/unicode/1f410.png b/public/images/emoji/unicode/1f410.png similarity index 100% rename from app/assets/images/emoji/unicode/1f410.png rename to public/images/emoji/unicode/1f410.png diff --git a/app/assets/images/emoji/unicode/1f411.png b/public/images/emoji/unicode/1f411.png similarity index 100% rename from app/assets/images/emoji/unicode/1f411.png rename to public/images/emoji/unicode/1f411.png diff --git a/app/assets/images/emoji/unicode/1f412.png b/public/images/emoji/unicode/1f412.png similarity index 100% rename from app/assets/images/emoji/unicode/1f412.png rename to public/images/emoji/unicode/1f412.png diff --git a/app/assets/images/emoji/unicode/1f413.png b/public/images/emoji/unicode/1f413.png similarity index 100% rename from app/assets/images/emoji/unicode/1f413.png rename to public/images/emoji/unicode/1f413.png diff --git a/app/assets/images/emoji/unicode/1f414.png b/public/images/emoji/unicode/1f414.png similarity index 100% rename from app/assets/images/emoji/unicode/1f414.png rename to public/images/emoji/unicode/1f414.png diff --git a/app/assets/images/emoji/unicode/1f415.png b/public/images/emoji/unicode/1f415.png similarity index 100% rename from app/assets/images/emoji/unicode/1f415.png rename to public/images/emoji/unicode/1f415.png diff --git a/app/assets/images/emoji/unicode/1f416.png b/public/images/emoji/unicode/1f416.png similarity index 100% rename from app/assets/images/emoji/unicode/1f416.png rename to public/images/emoji/unicode/1f416.png diff --git a/app/assets/images/emoji/unicode/1f417.png b/public/images/emoji/unicode/1f417.png similarity index 100% rename from app/assets/images/emoji/unicode/1f417.png rename to public/images/emoji/unicode/1f417.png diff --git a/app/assets/images/emoji/unicode/1f418.png b/public/images/emoji/unicode/1f418.png similarity index 100% rename from app/assets/images/emoji/unicode/1f418.png rename to public/images/emoji/unicode/1f418.png diff --git a/app/assets/images/emoji/unicode/1f419.png b/public/images/emoji/unicode/1f419.png similarity index 100% rename from app/assets/images/emoji/unicode/1f419.png rename to public/images/emoji/unicode/1f419.png diff --git a/app/assets/images/emoji/unicode/1f41a.png b/public/images/emoji/unicode/1f41a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f41a.png rename to public/images/emoji/unicode/1f41a.png diff --git a/app/assets/images/emoji/unicode/1f41b.png b/public/images/emoji/unicode/1f41b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f41b.png rename to public/images/emoji/unicode/1f41b.png diff --git a/app/assets/images/emoji/unicode/1f41c.png b/public/images/emoji/unicode/1f41c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f41c.png rename to public/images/emoji/unicode/1f41c.png diff --git a/app/assets/images/emoji/unicode/1f41d.png b/public/images/emoji/unicode/1f41d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f41d.png rename to public/images/emoji/unicode/1f41d.png diff --git a/app/assets/images/emoji/unicode/1f41e.png b/public/images/emoji/unicode/1f41e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f41e.png rename to public/images/emoji/unicode/1f41e.png diff --git a/app/assets/images/emoji/unicode/1f41f.png b/public/images/emoji/unicode/1f41f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f41f.png rename to public/images/emoji/unicode/1f41f.png diff --git a/app/assets/images/emoji/unicode/1f420.png b/public/images/emoji/unicode/1f420.png similarity index 100% rename from app/assets/images/emoji/unicode/1f420.png rename to public/images/emoji/unicode/1f420.png diff --git a/app/assets/images/emoji/unicode/1f421.png b/public/images/emoji/unicode/1f421.png similarity index 100% rename from app/assets/images/emoji/unicode/1f421.png rename to public/images/emoji/unicode/1f421.png diff --git a/app/assets/images/emoji/unicode/1f422.png b/public/images/emoji/unicode/1f422.png similarity index 100% rename from app/assets/images/emoji/unicode/1f422.png rename to public/images/emoji/unicode/1f422.png diff --git a/app/assets/images/emoji/unicode/1f423.png b/public/images/emoji/unicode/1f423.png similarity index 100% rename from app/assets/images/emoji/unicode/1f423.png rename to public/images/emoji/unicode/1f423.png diff --git a/app/assets/images/emoji/unicode/1f424.png b/public/images/emoji/unicode/1f424.png similarity index 100% rename from app/assets/images/emoji/unicode/1f424.png rename to public/images/emoji/unicode/1f424.png diff --git a/app/assets/images/emoji/unicode/1f425.png b/public/images/emoji/unicode/1f425.png similarity index 100% rename from app/assets/images/emoji/unicode/1f425.png rename to public/images/emoji/unicode/1f425.png diff --git a/app/assets/images/emoji/unicode/1f426.png b/public/images/emoji/unicode/1f426.png similarity index 100% rename from app/assets/images/emoji/unicode/1f426.png rename to public/images/emoji/unicode/1f426.png diff --git a/app/assets/images/emoji/unicode/1f427.png b/public/images/emoji/unicode/1f427.png similarity index 100% rename from app/assets/images/emoji/unicode/1f427.png rename to public/images/emoji/unicode/1f427.png diff --git a/app/assets/images/emoji/unicode/1f428.png b/public/images/emoji/unicode/1f428.png similarity index 100% rename from app/assets/images/emoji/unicode/1f428.png rename to public/images/emoji/unicode/1f428.png diff --git a/app/assets/images/emoji/unicode/1f429.png b/public/images/emoji/unicode/1f429.png similarity index 100% rename from app/assets/images/emoji/unicode/1f429.png rename to public/images/emoji/unicode/1f429.png diff --git a/app/assets/images/emoji/unicode/1f42a.png b/public/images/emoji/unicode/1f42a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f42a.png rename to public/images/emoji/unicode/1f42a.png diff --git a/app/assets/images/emoji/unicode/1f42b.png b/public/images/emoji/unicode/1f42b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f42b.png rename to public/images/emoji/unicode/1f42b.png diff --git a/app/assets/images/emoji/unicode/1f42c.png b/public/images/emoji/unicode/1f42c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f42c.png rename to public/images/emoji/unicode/1f42c.png diff --git a/app/assets/images/emoji/unicode/1f42d.png b/public/images/emoji/unicode/1f42d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f42d.png rename to public/images/emoji/unicode/1f42d.png diff --git a/app/assets/images/emoji/unicode/1f42e.png b/public/images/emoji/unicode/1f42e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f42e.png rename to public/images/emoji/unicode/1f42e.png diff --git a/app/assets/images/emoji/unicode/1f42f.png b/public/images/emoji/unicode/1f42f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f42f.png rename to public/images/emoji/unicode/1f42f.png diff --git a/app/assets/images/emoji/unicode/1f430.png b/public/images/emoji/unicode/1f430.png similarity index 100% rename from app/assets/images/emoji/unicode/1f430.png rename to public/images/emoji/unicode/1f430.png diff --git a/app/assets/images/emoji/unicode/1f431.png b/public/images/emoji/unicode/1f431.png similarity index 100% rename from app/assets/images/emoji/unicode/1f431.png rename to public/images/emoji/unicode/1f431.png diff --git a/app/assets/images/emoji/unicode/1f432.png b/public/images/emoji/unicode/1f432.png similarity index 100% rename from app/assets/images/emoji/unicode/1f432.png rename to public/images/emoji/unicode/1f432.png diff --git a/app/assets/images/emoji/unicode/1f433.png b/public/images/emoji/unicode/1f433.png similarity index 100% rename from app/assets/images/emoji/unicode/1f433.png rename to public/images/emoji/unicode/1f433.png diff --git a/app/assets/images/emoji/unicode/1f434.png b/public/images/emoji/unicode/1f434.png similarity index 100% rename from app/assets/images/emoji/unicode/1f434.png rename to public/images/emoji/unicode/1f434.png diff --git a/app/assets/images/emoji/unicode/1f435.png b/public/images/emoji/unicode/1f435.png similarity index 100% rename from app/assets/images/emoji/unicode/1f435.png rename to public/images/emoji/unicode/1f435.png diff --git a/app/assets/images/emoji/unicode/1f436.png b/public/images/emoji/unicode/1f436.png similarity index 100% rename from app/assets/images/emoji/unicode/1f436.png rename to public/images/emoji/unicode/1f436.png diff --git a/app/assets/images/emoji/unicode/1f437.png b/public/images/emoji/unicode/1f437.png similarity index 100% rename from app/assets/images/emoji/unicode/1f437.png rename to public/images/emoji/unicode/1f437.png diff --git a/app/assets/images/emoji/unicode/1f438.png b/public/images/emoji/unicode/1f438.png similarity index 100% rename from app/assets/images/emoji/unicode/1f438.png rename to public/images/emoji/unicode/1f438.png diff --git a/app/assets/images/emoji/unicode/1f439.png b/public/images/emoji/unicode/1f439.png similarity index 100% rename from app/assets/images/emoji/unicode/1f439.png rename to public/images/emoji/unicode/1f439.png diff --git a/app/assets/images/emoji/unicode/1f43a.png b/public/images/emoji/unicode/1f43a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f43a.png rename to public/images/emoji/unicode/1f43a.png diff --git a/app/assets/images/emoji/unicode/1f43b.png b/public/images/emoji/unicode/1f43b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f43b.png rename to public/images/emoji/unicode/1f43b.png diff --git a/app/assets/images/emoji/unicode/1f43c.png b/public/images/emoji/unicode/1f43c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f43c.png rename to public/images/emoji/unicode/1f43c.png diff --git a/app/assets/images/emoji/unicode/1f43d.png b/public/images/emoji/unicode/1f43d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f43d.png rename to public/images/emoji/unicode/1f43d.png diff --git a/app/assets/images/emoji/unicode/1f43e.png b/public/images/emoji/unicode/1f43e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f43e.png rename to public/images/emoji/unicode/1f43e.png diff --git a/app/assets/images/emoji/unicode/1f440.png b/public/images/emoji/unicode/1f440.png similarity index 100% rename from app/assets/images/emoji/unicode/1f440.png rename to public/images/emoji/unicode/1f440.png diff --git a/app/assets/images/emoji/unicode/1f442.png b/public/images/emoji/unicode/1f442.png similarity index 100% rename from app/assets/images/emoji/unicode/1f442.png rename to public/images/emoji/unicode/1f442.png diff --git a/app/assets/images/emoji/unicode/1f443.png b/public/images/emoji/unicode/1f443.png similarity index 100% rename from app/assets/images/emoji/unicode/1f443.png rename to public/images/emoji/unicode/1f443.png diff --git a/app/assets/images/emoji/unicode/1f444.png b/public/images/emoji/unicode/1f444.png similarity index 100% rename from app/assets/images/emoji/unicode/1f444.png rename to public/images/emoji/unicode/1f444.png diff --git a/app/assets/images/emoji/unicode/1f445.png b/public/images/emoji/unicode/1f445.png similarity index 100% rename from app/assets/images/emoji/unicode/1f445.png rename to public/images/emoji/unicode/1f445.png diff --git a/app/assets/images/emoji/unicode/1f446.png b/public/images/emoji/unicode/1f446.png similarity index 100% rename from app/assets/images/emoji/unicode/1f446.png rename to public/images/emoji/unicode/1f446.png diff --git a/app/assets/images/emoji/unicode/1f447.png b/public/images/emoji/unicode/1f447.png similarity index 100% rename from app/assets/images/emoji/unicode/1f447.png rename to public/images/emoji/unicode/1f447.png diff --git a/app/assets/images/emoji/unicode/1f448.png b/public/images/emoji/unicode/1f448.png similarity index 100% rename from app/assets/images/emoji/unicode/1f448.png rename to public/images/emoji/unicode/1f448.png diff --git a/app/assets/images/emoji/unicode/1f449.png b/public/images/emoji/unicode/1f449.png similarity index 100% rename from app/assets/images/emoji/unicode/1f449.png rename to public/images/emoji/unicode/1f449.png diff --git a/app/assets/images/emoji/unicode/1f44a.png b/public/images/emoji/unicode/1f44a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f44a.png rename to public/images/emoji/unicode/1f44a.png diff --git a/app/assets/images/emoji/unicode/1f44b.png b/public/images/emoji/unicode/1f44b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f44b.png rename to public/images/emoji/unicode/1f44b.png diff --git a/app/assets/images/emoji/unicode/1f44c.png b/public/images/emoji/unicode/1f44c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f44c.png rename to public/images/emoji/unicode/1f44c.png diff --git a/app/assets/images/emoji/unicode/1f44d.png b/public/images/emoji/unicode/1f44d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f44d.png rename to public/images/emoji/unicode/1f44d.png diff --git a/app/assets/images/emoji/unicode/1f44e.png b/public/images/emoji/unicode/1f44e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f44e.png rename to public/images/emoji/unicode/1f44e.png diff --git a/app/assets/images/emoji/unicode/1f44f.png b/public/images/emoji/unicode/1f44f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f44f.png rename to public/images/emoji/unicode/1f44f.png diff --git a/app/assets/images/emoji/unicode/1f450.png b/public/images/emoji/unicode/1f450.png similarity index 100% rename from app/assets/images/emoji/unicode/1f450.png rename to public/images/emoji/unicode/1f450.png diff --git a/app/assets/images/emoji/unicode/1f451.png b/public/images/emoji/unicode/1f451.png similarity index 100% rename from app/assets/images/emoji/unicode/1f451.png rename to public/images/emoji/unicode/1f451.png diff --git a/app/assets/images/emoji/unicode/1f452.png b/public/images/emoji/unicode/1f452.png similarity index 100% rename from app/assets/images/emoji/unicode/1f452.png rename to public/images/emoji/unicode/1f452.png diff --git a/app/assets/images/emoji/unicode/1f453.png b/public/images/emoji/unicode/1f453.png similarity index 100% rename from app/assets/images/emoji/unicode/1f453.png rename to public/images/emoji/unicode/1f453.png diff --git a/app/assets/images/emoji/unicode/1f454.png b/public/images/emoji/unicode/1f454.png similarity index 100% rename from app/assets/images/emoji/unicode/1f454.png rename to public/images/emoji/unicode/1f454.png diff --git a/app/assets/images/emoji/unicode/1f455.png b/public/images/emoji/unicode/1f455.png similarity index 100% rename from app/assets/images/emoji/unicode/1f455.png rename to public/images/emoji/unicode/1f455.png diff --git a/app/assets/images/emoji/unicode/1f456.png b/public/images/emoji/unicode/1f456.png similarity index 100% rename from app/assets/images/emoji/unicode/1f456.png rename to public/images/emoji/unicode/1f456.png diff --git a/app/assets/images/emoji/unicode/1f457.png b/public/images/emoji/unicode/1f457.png similarity index 100% rename from app/assets/images/emoji/unicode/1f457.png rename to public/images/emoji/unicode/1f457.png diff --git a/app/assets/images/emoji/unicode/1f458.png b/public/images/emoji/unicode/1f458.png similarity index 100% rename from app/assets/images/emoji/unicode/1f458.png rename to public/images/emoji/unicode/1f458.png diff --git a/app/assets/images/emoji/unicode/1f459.png b/public/images/emoji/unicode/1f459.png similarity index 100% rename from app/assets/images/emoji/unicode/1f459.png rename to public/images/emoji/unicode/1f459.png diff --git a/app/assets/images/emoji/unicode/1f45a.png b/public/images/emoji/unicode/1f45a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f45a.png rename to public/images/emoji/unicode/1f45a.png diff --git a/app/assets/images/emoji/unicode/1f45b.png b/public/images/emoji/unicode/1f45b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f45b.png rename to public/images/emoji/unicode/1f45b.png diff --git a/app/assets/images/emoji/unicode/1f45c.png b/public/images/emoji/unicode/1f45c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f45c.png rename to public/images/emoji/unicode/1f45c.png diff --git a/app/assets/images/emoji/unicode/1f45d.png b/public/images/emoji/unicode/1f45d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f45d.png rename to public/images/emoji/unicode/1f45d.png diff --git a/app/assets/images/emoji/unicode/1f45e.png b/public/images/emoji/unicode/1f45e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f45e.png rename to public/images/emoji/unicode/1f45e.png diff --git a/app/assets/images/emoji/unicode/1f45f.png b/public/images/emoji/unicode/1f45f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f45f.png rename to public/images/emoji/unicode/1f45f.png diff --git a/app/assets/images/emoji/unicode/1f460.png b/public/images/emoji/unicode/1f460.png similarity index 100% rename from app/assets/images/emoji/unicode/1f460.png rename to public/images/emoji/unicode/1f460.png diff --git a/app/assets/images/emoji/unicode/1f461.png b/public/images/emoji/unicode/1f461.png similarity index 100% rename from app/assets/images/emoji/unicode/1f461.png rename to public/images/emoji/unicode/1f461.png diff --git a/app/assets/images/emoji/unicode/1f462.png b/public/images/emoji/unicode/1f462.png similarity index 100% rename from app/assets/images/emoji/unicode/1f462.png rename to public/images/emoji/unicode/1f462.png diff --git a/app/assets/images/emoji/unicode/1f463.png b/public/images/emoji/unicode/1f463.png similarity index 100% rename from app/assets/images/emoji/unicode/1f463.png rename to public/images/emoji/unicode/1f463.png diff --git a/app/assets/images/emoji/unicode/1f464.png b/public/images/emoji/unicode/1f464.png similarity index 100% rename from app/assets/images/emoji/unicode/1f464.png rename to public/images/emoji/unicode/1f464.png diff --git a/app/assets/images/emoji/unicode/1f465.png b/public/images/emoji/unicode/1f465.png similarity index 100% rename from app/assets/images/emoji/unicode/1f465.png rename to public/images/emoji/unicode/1f465.png diff --git a/app/assets/images/emoji/unicode/1f466.png b/public/images/emoji/unicode/1f466.png similarity index 100% rename from app/assets/images/emoji/unicode/1f466.png rename to public/images/emoji/unicode/1f466.png diff --git a/app/assets/images/emoji/unicode/1f467.png b/public/images/emoji/unicode/1f467.png similarity index 100% rename from app/assets/images/emoji/unicode/1f467.png rename to public/images/emoji/unicode/1f467.png diff --git a/app/assets/images/emoji/unicode/1f468.png b/public/images/emoji/unicode/1f468.png similarity index 100% rename from app/assets/images/emoji/unicode/1f468.png rename to public/images/emoji/unicode/1f468.png diff --git a/app/assets/images/emoji/unicode/1f469.png b/public/images/emoji/unicode/1f469.png similarity index 100% rename from app/assets/images/emoji/unicode/1f469.png rename to public/images/emoji/unicode/1f469.png diff --git a/app/assets/images/emoji/unicode/1f46a.png b/public/images/emoji/unicode/1f46a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f46a.png rename to public/images/emoji/unicode/1f46a.png diff --git a/app/assets/images/emoji/unicode/1f46b.png b/public/images/emoji/unicode/1f46b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f46b.png rename to public/images/emoji/unicode/1f46b.png diff --git a/app/assets/images/emoji/unicode/1f46c.png b/public/images/emoji/unicode/1f46c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f46c.png rename to public/images/emoji/unicode/1f46c.png diff --git a/app/assets/images/emoji/unicode/1f46d.png b/public/images/emoji/unicode/1f46d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f46d.png rename to public/images/emoji/unicode/1f46d.png diff --git a/app/assets/images/emoji/unicode/1f46e.png b/public/images/emoji/unicode/1f46e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f46e.png rename to public/images/emoji/unicode/1f46e.png diff --git a/app/assets/images/emoji/unicode/1f46f.png b/public/images/emoji/unicode/1f46f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f46f.png rename to public/images/emoji/unicode/1f46f.png diff --git a/app/assets/images/emoji/unicode/1f470.png b/public/images/emoji/unicode/1f470.png similarity index 100% rename from app/assets/images/emoji/unicode/1f470.png rename to public/images/emoji/unicode/1f470.png diff --git a/app/assets/images/emoji/unicode/1f471.png b/public/images/emoji/unicode/1f471.png similarity index 100% rename from app/assets/images/emoji/unicode/1f471.png rename to public/images/emoji/unicode/1f471.png diff --git a/app/assets/images/emoji/unicode/1f472.png b/public/images/emoji/unicode/1f472.png similarity index 100% rename from app/assets/images/emoji/unicode/1f472.png rename to public/images/emoji/unicode/1f472.png diff --git a/app/assets/images/emoji/unicode/1f473.png b/public/images/emoji/unicode/1f473.png similarity index 100% rename from app/assets/images/emoji/unicode/1f473.png rename to public/images/emoji/unicode/1f473.png diff --git a/app/assets/images/emoji/unicode/1f474.png b/public/images/emoji/unicode/1f474.png similarity index 100% rename from app/assets/images/emoji/unicode/1f474.png rename to public/images/emoji/unicode/1f474.png diff --git a/app/assets/images/emoji/unicode/1f475.png b/public/images/emoji/unicode/1f475.png similarity index 100% rename from app/assets/images/emoji/unicode/1f475.png rename to public/images/emoji/unicode/1f475.png diff --git a/app/assets/images/emoji/unicode/1f476.png b/public/images/emoji/unicode/1f476.png similarity index 100% rename from app/assets/images/emoji/unicode/1f476.png rename to public/images/emoji/unicode/1f476.png diff --git a/app/assets/images/emoji/unicode/1f477.png b/public/images/emoji/unicode/1f477.png similarity index 100% rename from app/assets/images/emoji/unicode/1f477.png rename to public/images/emoji/unicode/1f477.png diff --git a/app/assets/images/emoji/unicode/1f478.png b/public/images/emoji/unicode/1f478.png similarity index 100% rename from app/assets/images/emoji/unicode/1f478.png rename to public/images/emoji/unicode/1f478.png diff --git a/app/assets/images/emoji/unicode/1f479.png b/public/images/emoji/unicode/1f479.png similarity index 100% rename from app/assets/images/emoji/unicode/1f479.png rename to public/images/emoji/unicode/1f479.png diff --git a/app/assets/images/emoji/unicode/1f47a.png b/public/images/emoji/unicode/1f47a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f47a.png rename to public/images/emoji/unicode/1f47a.png diff --git a/app/assets/images/emoji/unicode/1f47b.png b/public/images/emoji/unicode/1f47b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f47b.png rename to public/images/emoji/unicode/1f47b.png diff --git a/app/assets/images/emoji/unicode/1f47c.png b/public/images/emoji/unicode/1f47c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f47c.png rename to public/images/emoji/unicode/1f47c.png diff --git a/app/assets/images/emoji/unicode/1f47d.png b/public/images/emoji/unicode/1f47d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f47d.png rename to public/images/emoji/unicode/1f47d.png diff --git a/app/assets/images/emoji/unicode/1f47e.png b/public/images/emoji/unicode/1f47e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f47e.png rename to public/images/emoji/unicode/1f47e.png diff --git a/app/assets/images/emoji/unicode/1f47f.png b/public/images/emoji/unicode/1f47f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f47f.png rename to public/images/emoji/unicode/1f47f.png diff --git a/app/assets/images/emoji/unicode/1f480.png b/public/images/emoji/unicode/1f480.png similarity index 100% rename from app/assets/images/emoji/unicode/1f480.png rename to public/images/emoji/unicode/1f480.png diff --git a/app/assets/images/emoji/unicode/1f481.png b/public/images/emoji/unicode/1f481.png similarity index 100% rename from app/assets/images/emoji/unicode/1f481.png rename to public/images/emoji/unicode/1f481.png diff --git a/app/assets/images/emoji/unicode/1f482.png b/public/images/emoji/unicode/1f482.png similarity index 100% rename from app/assets/images/emoji/unicode/1f482.png rename to public/images/emoji/unicode/1f482.png diff --git a/app/assets/images/emoji/unicode/1f483.png b/public/images/emoji/unicode/1f483.png similarity index 100% rename from app/assets/images/emoji/unicode/1f483.png rename to public/images/emoji/unicode/1f483.png diff --git a/app/assets/images/emoji/unicode/1f484.png b/public/images/emoji/unicode/1f484.png similarity index 100% rename from app/assets/images/emoji/unicode/1f484.png rename to public/images/emoji/unicode/1f484.png diff --git a/app/assets/images/emoji/unicode/1f485.png b/public/images/emoji/unicode/1f485.png similarity index 100% rename from app/assets/images/emoji/unicode/1f485.png rename to public/images/emoji/unicode/1f485.png diff --git a/app/assets/images/emoji/unicode/1f486.png b/public/images/emoji/unicode/1f486.png similarity index 100% rename from app/assets/images/emoji/unicode/1f486.png rename to public/images/emoji/unicode/1f486.png diff --git a/app/assets/images/emoji/unicode/1f487.png b/public/images/emoji/unicode/1f487.png similarity index 100% rename from app/assets/images/emoji/unicode/1f487.png rename to public/images/emoji/unicode/1f487.png diff --git a/app/assets/images/emoji/unicode/1f488.png b/public/images/emoji/unicode/1f488.png similarity index 100% rename from app/assets/images/emoji/unicode/1f488.png rename to public/images/emoji/unicode/1f488.png diff --git a/app/assets/images/emoji/unicode/1f489.png b/public/images/emoji/unicode/1f489.png similarity index 100% rename from app/assets/images/emoji/unicode/1f489.png rename to public/images/emoji/unicode/1f489.png diff --git a/app/assets/images/emoji/unicode/1f48a.png b/public/images/emoji/unicode/1f48a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f48a.png rename to public/images/emoji/unicode/1f48a.png diff --git a/app/assets/images/emoji/unicode/1f48b.png b/public/images/emoji/unicode/1f48b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f48b.png rename to public/images/emoji/unicode/1f48b.png diff --git a/app/assets/images/emoji/unicode/1f48c.png b/public/images/emoji/unicode/1f48c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f48c.png rename to public/images/emoji/unicode/1f48c.png diff --git a/app/assets/images/emoji/unicode/1f48d.png b/public/images/emoji/unicode/1f48d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f48d.png rename to public/images/emoji/unicode/1f48d.png diff --git a/app/assets/images/emoji/unicode/1f48e.png b/public/images/emoji/unicode/1f48e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f48e.png rename to public/images/emoji/unicode/1f48e.png diff --git a/app/assets/images/emoji/unicode/1f48f.png b/public/images/emoji/unicode/1f48f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f48f.png rename to public/images/emoji/unicode/1f48f.png diff --git a/app/assets/images/emoji/unicode/1f490.png b/public/images/emoji/unicode/1f490.png similarity index 100% rename from app/assets/images/emoji/unicode/1f490.png rename to public/images/emoji/unicode/1f490.png diff --git a/app/assets/images/emoji/unicode/1f491.png b/public/images/emoji/unicode/1f491.png similarity index 100% rename from app/assets/images/emoji/unicode/1f491.png rename to public/images/emoji/unicode/1f491.png diff --git a/app/assets/images/emoji/unicode/1f492.png b/public/images/emoji/unicode/1f492.png similarity index 100% rename from app/assets/images/emoji/unicode/1f492.png rename to public/images/emoji/unicode/1f492.png diff --git a/app/assets/images/emoji/unicode/1f493.png b/public/images/emoji/unicode/1f493.png similarity index 100% rename from app/assets/images/emoji/unicode/1f493.png rename to public/images/emoji/unicode/1f493.png diff --git a/app/assets/images/emoji/unicode/1f494.png b/public/images/emoji/unicode/1f494.png similarity index 100% rename from app/assets/images/emoji/unicode/1f494.png rename to public/images/emoji/unicode/1f494.png diff --git a/app/assets/images/emoji/unicode/1f495.png b/public/images/emoji/unicode/1f495.png similarity index 100% rename from app/assets/images/emoji/unicode/1f495.png rename to public/images/emoji/unicode/1f495.png diff --git a/app/assets/images/emoji/unicode/1f496.png b/public/images/emoji/unicode/1f496.png similarity index 100% rename from app/assets/images/emoji/unicode/1f496.png rename to public/images/emoji/unicode/1f496.png diff --git a/app/assets/images/emoji/unicode/1f497.png b/public/images/emoji/unicode/1f497.png similarity index 100% rename from app/assets/images/emoji/unicode/1f497.png rename to public/images/emoji/unicode/1f497.png diff --git a/app/assets/images/emoji/unicode/1f498.png b/public/images/emoji/unicode/1f498.png similarity index 100% rename from app/assets/images/emoji/unicode/1f498.png rename to public/images/emoji/unicode/1f498.png diff --git a/app/assets/images/emoji/unicode/1f499.png b/public/images/emoji/unicode/1f499.png similarity index 100% rename from app/assets/images/emoji/unicode/1f499.png rename to public/images/emoji/unicode/1f499.png diff --git a/app/assets/images/emoji/unicode/1f49a.png b/public/images/emoji/unicode/1f49a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f49a.png rename to public/images/emoji/unicode/1f49a.png diff --git a/app/assets/images/emoji/unicode/1f49b.png b/public/images/emoji/unicode/1f49b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f49b.png rename to public/images/emoji/unicode/1f49b.png diff --git a/app/assets/images/emoji/unicode/1f49c.png b/public/images/emoji/unicode/1f49c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f49c.png rename to public/images/emoji/unicode/1f49c.png diff --git a/app/assets/images/emoji/unicode/1f49d.png b/public/images/emoji/unicode/1f49d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f49d.png rename to public/images/emoji/unicode/1f49d.png diff --git a/app/assets/images/emoji/unicode/1f49e.png b/public/images/emoji/unicode/1f49e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f49e.png rename to public/images/emoji/unicode/1f49e.png diff --git a/app/assets/images/emoji/unicode/1f49f.png b/public/images/emoji/unicode/1f49f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f49f.png rename to public/images/emoji/unicode/1f49f.png diff --git a/app/assets/images/emoji/unicode/1f4a0.png b/public/images/emoji/unicode/1f4a0.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4a0.png rename to public/images/emoji/unicode/1f4a0.png diff --git a/app/assets/images/emoji/unicode/1f4a1.png b/public/images/emoji/unicode/1f4a1.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4a1.png rename to public/images/emoji/unicode/1f4a1.png diff --git a/app/assets/images/emoji/unicode/1f4a2.png b/public/images/emoji/unicode/1f4a2.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4a2.png rename to public/images/emoji/unicode/1f4a2.png diff --git a/app/assets/images/emoji/unicode/1f4a3.png b/public/images/emoji/unicode/1f4a3.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4a3.png rename to public/images/emoji/unicode/1f4a3.png diff --git a/app/assets/images/emoji/unicode/1f4a4.png b/public/images/emoji/unicode/1f4a4.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4a4.png rename to public/images/emoji/unicode/1f4a4.png diff --git a/app/assets/images/emoji/unicode/1f4a5.png b/public/images/emoji/unicode/1f4a5.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4a5.png rename to public/images/emoji/unicode/1f4a5.png diff --git a/app/assets/images/emoji/unicode/1f4a6.png b/public/images/emoji/unicode/1f4a6.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4a6.png rename to public/images/emoji/unicode/1f4a6.png diff --git a/app/assets/images/emoji/unicode/1f4a7.png b/public/images/emoji/unicode/1f4a7.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4a7.png rename to public/images/emoji/unicode/1f4a7.png diff --git a/app/assets/images/emoji/unicode/1f4a8.png b/public/images/emoji/unicode/1f4a8.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4a8.png rename to public/images/emoji/unicode/1f4a8.png diff --git a/app/assets/images/emoji/unicode/1f4a9.png b/public/images/emoji/unicode/1f4a9.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4a9.png rename to public/images/emoji/unicode/1f4a9.png diff --git a/app/assets/images/emoji/unicode/1f4aa.png b/public/images/emoji/unicode/1f4aa.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4aa.png rename to public/images/emoji/unicode/1f4aa.png diff --git a/app/assets/images/emoji/unicode/1f4ab.png b/public/images/emoji/unicode/1f4ab.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4ab.png rename to public/images/emoji/unicode/1f4ab.png diff --git a/app/assets/images/emoji/unicode/1f4ac.png b/public/images/emoji/unicode/1f4ac.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4ac.png rename to public/images/emoji/unicode/1f4ac.png diff --git a/app/assets/images/emoji/unicode/1f4ad.png b/public/images/emoji/unicode/1f4ad.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4ad.png rename to public/images/emoji/unicode/1f4ad.png diff --git a/app/assets/images/emoji/unicode/1f4ae.png b/public/images/emoji/unicode/1f4ae.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4ae.png rename to public/images/emoji/unicode/1f4ae.png diff --git a/app/assets/images/emoji/unicode/1f4af.png b/public/images/emoji/unicode/1f4af.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4af.png rename to public/images/emoji/unicode/1f4af.png diff --git a/app/assets/images/emoji/unicode/1f4b0.png b/public/images/emoji/unicode/1f4b0.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4b0.png rename to public/images/emoji/unicode/1f4b0.png diff --git a/app/assets/images/emoji/unicode/1f4b1.png b/public/images/emoji/unicode/1f4b1.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4b1.png rename to public/images/emoji/unicode/1f4b1.png diff --git a/app/assets/images/emoji/unicode/1f4b2.png b/public/images/emoji/unicode/1f4b2.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4b2.png rename to public/images/emoji/unicode/1f4b2.png diff --git a/app/assets/images/emoji/unicode/1f4b3.png b/public/images/emoji/unicode/1f4b3.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4b3.png rename to public/images/emoji/unicode/1f4b3.png diff --git a/app/assets/images/emoji/unicode/1f4b4.png b/public/images/emoji/unicode/1f4b4.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4b4.png rename to public/images/emoji/unicode/1f4b4.png diff --git a/app/assets/images/emoji/unicode/1f4b5.png b/public/images/emoji/unicode/1f4b5.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4b5.png rename to public/images/emoji/unicode/1f4b5.png diff --git a/app/assets/images/emoji/unicode/1f4b6.png b/public/images/emoji/unicode/1f4b6.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4b6.png rename to public/images/emoji/unicode/1f4b6.png diff --git a/app/assets/images/emoji/unicode/1f4b7.png b/public/images/emoji/unicode/1f4b7.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4b7.png rename to public/images/emoji/unicode/1f4b7.png diff --git a/app/assets/images/emoji/unicode/1f4b8.png b/public/images/emoji/unicode/1f4b8.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4b8.png rename to public/images/emoji/unicode/1f4b8.png diff --git a/app/assets/images/emoji/unicode/1f4b9.png b/public/images/emoji/unicode/1f4b9.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4b9.png rename to public/images/emoji/unicode/1f4b9.png diff --git a/app/assets/images/emoji/unicode/1f4ba.png b/public/images/emoji/unicode/1f4ba.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4ba.png rename to public/images/emoji/unicode/1f4ba.png diff --git a/app/assets/images/emoji/unicode/1f4bb.png b/public/images/emoji/unicode/1f4bb.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4bb.png rename to public/images/emoji/unicode/1f4bb.png diff --git a/app/assets/images/emoji/unicode/1f4bc.png b/public/images/emoji/unicode/1f4bc.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4bc.png rename to public/images/emoji/unicode/1f4bc.png diff --git a/app/assets/images/emoji/unicode/1f4bd.png b/public/images/emoji/unicode/1f4bd.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4bd.png rename to public/images/emoji/unicode/1f4bd.png diff --git a/app/assets/images/emoji/unicode/1f4be.png b/public/images/emoji/unicode/1f4be.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4be.png rename to public/images/emoji/unicode/1f4be.png diff --git a/app/assets/images/emoji/unicode/1f4bf.png b/public/images/emoji/unicode/1f4bf.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4bf.png rename to public/images/emoji/unicode/1f4bf.png diff --git a/app/assets/images/emoji/unicode/1f4c0.png b/public/images/emoji/unicode/1f4c0.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4c0.png rename to public/images/emoji/unicode/1f4c0.png diff --git a/app/assets/images/emoji/unicode/1f4c1.png b/public/images/emoji/unicode/1f4c1.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4c1.png rename to public/images/emoji/unicode/1f4c1.png diff --git a/app/assets/images/emoji/unicode/1f4c2.png b/public/images/emoji/unicode/1f4c2.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4c2.png rename to public/images/emoji/unicode/1f4c2.png diff --git a/app/assets/images/emoji/unicode/1f4c3.png b/public/images/emoji/unicode/1f4c3.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4c3.png rename to public/images/emoji/unicode/1f4c3.png diff --git a/app/assets/images/emoji/unicode/1f4c4.png b/public/images/emoji/unicode/1f4c4.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4c4.png rename to public/images/emoji/unicode/1f4c4.png diff --git a/app/assets/images/emoji/unicode/1f4c5.png b/public/images/emoji/unicode/1f4c5.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4c5.png rename to public/images/emoji/unicode/1f4c5.png diff --git a/app/assets/images/emoji/unicode/1f4c6.png b/public/images/emoji/unicode/1f4c6.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4c6.png rename to public/images/emoji/unicode/1f4c6.png diff --git a/app/assets/images/emoji/unicode/1f4c7.png b/public/images/emoji/unicode/1f4c7.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4c7.png rename to public/images/emoji/unicode/1f4c7.png diff --git a/app/assets/images/emoji/unicode/1f4c8.png b/public/images/emoji/unicode/1f4c8.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4c8.png rename to public/images/emoji/unicode/1f4c8.png diff --git a/app/assets/images/emoji/unicode/1f4c9.png b/public/images/emoji/unicode/1f4c9.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4c9.png rename to public/images/emoji/unicode/1f4c9.png diff --git a/app/assets/images/emoji/unicode/1f4ca.png b/public/images/emoji/unicode/1f4ca.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4ca.png rename to public/images/emoji/unicode/1f4ca.png diff --git a/app/assets/images/emoji/unicode/1f4cb.png b/public/images/emoji/unicode/1f4cb.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4cb.png rename to public/images/emoji/unicode/1f4cb.png diff --git a/app/assets/images/emoji/unicode/1f4cc.png b/public/images/emoji/unicode/1f4cc.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4cc.png rename to public/images/emoji/unicode/1f4cc.png diff --git a/app/assets/images/emoji/unicode/1f4cd.png b/public/images/emoji/unicode/1f4cd.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4cd.png rename to public/images/emoji/unicode/1f4cd.png diff --git a/app/assets/images/emoji/unicode/1f4ce.png b/public/images/emoji/unicode/1f4ce.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4ce.png rename to public/images/emoji/unicode/1f4ce.png diff --git a/app/assets/images/emoji/unicode/1f4cf.png b/public/images/emoji/unicode/1f4cf.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4cf.png rename to public/images/emoji/unicode/1f4cf.png diff --git a/app/assets/images/emoji/unicode/1f4d0.png b/public/images/emoji/unicode/1f4d0.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4d0.png rename to public/images/emoji/unicode/1f4d0.png diff --git a/app/assets/images/emoji/unicode/1f4d1.png b/public/images/emoji/unicode/1f4d1.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4d1.png rename to public/images/emoji/unicode/1f4d1.png diff --git a/app/assets/images/emoji/unicode/1f4d2.png b/public/images/emoji/unicode/1f4d2.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4d2.png rename to public/images/emoji/unicode/1f4d2.png diff --git a/app/assets/images/emoji/unicode/1f4d3.png b/public/images/emoji/unicode/1f4d3.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4d3.png rename to public/images/emoji/unicode/1f4d3.png diff --git a/app/assets/images/emoji/unicode/1f4d4.png b/public/images/emoji/unicode/1f4d4.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4d4.png rename to public/images/emoji/unicode/1f4d4.png diff --git a/app/assets/images/emoji/unicode/1f4d5.png b/public/images/emoji/unicode/1f4d5.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4d5.png rename to public/images/emoji/unicode/1f4d5.png diff --git a/app/assets/images/emoji/unicode/1f4d6.png b/public/images/emoji/unicode/1f4d6.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4d6.png rename to public/images/emoji/unicode/1f4d6.png diff --git a/app/assets/images/emoji/unicode/1f4d7.png b/public/images/emoji/unicode/1f4d7.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4d7.png rename to public/images/emoji/unicode/1f4d7.png diff --git a/app/assets/images/emoji/unicode/1f4d8.png b/public/images/emoji/unicode/1f4d8.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4d8.png rename to public/images/emoji/unicode/1f4d8.png diff --git a/app/assets/images/emoji/unicode/1f4d9.png b/public/images/emoji/unicode/1f4d9.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4d9.png rename to public/images/emoji/unicode/1f4d9.png diff --git a/app/assets/images/emoji/unicode/1f4da.png b/public/images/emoji/unicode/1f4da.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4da.png rename to public/images/emoji/unicode/1f4da.png diff --git a/app/assets/images/emoji/unicode/1f4db.png b/public/images/emoji/unicode/1f4db.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4db.png rename to public/images/emoji/unicode/1f4db.png diff --git a/app/assets/images/emoji/unicode/1f4dc.png b/public/images/emoji/unicode/1f4dc.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4dc.png rename to public/images/emoji/unicode/1f4dc.png diff --git a/app/assets/images/emoji/unicode/1f4dd.png b/public/images/emoji/unicode/1f4dd.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4dd.png rename to public/images/emoji/unicode/1f4dd.png diff --git a/app/assets/images/emoji/unicode/1f4de.png b/public/images/emoji/unicode/1f4de.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4de.png rename to public/images/emoji/unicode/1f4de.png diff --git a/app/assets/images/emoji/unicode/1f4df.png b/public/images/emoji/unicode/1f4df.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4df.png rename to public/images/emoji/unicode/1f4df.png diff --git a/app/assets/images/emoji/unicode/1f4e0.png b/public/images/emoji/unicode/1f4e0.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4e0.png rename to public/images/emoji/unicode/1f4e0.png diff --git a/app/assets/images/emoji/unicode/1f4e1.png b/public/images/emoji/unicode/1f4e1.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4e1.png rename to public/images/emoji/unicode/1f4e1.png diff --git a/app/assets/images/emoji/unicode/1f4e2.png b/public/images/emoji/unicode/1f4e2.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4e2.png rename to public/images/emoji/unicode/1f4e2.png diff --git a/app/assets/images/emoji/unicode/1f4e3.png b/public/images/emoji/unicode/1f4e3.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4e3.png rename to public/images/emoji/unicode/1f4e3.png diff --git a/app/assets/images/emoji/unicode/1f4e4.png b/public/images/emoji/unicode/1f4e4.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4e4.png rename to public/images/emoji/unicode/1f4e4.png diff --git a/app/assets/images/emoji/unicode/1f4e5.png b/public/images/emoji/unicode/1f4e5.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4e5.png rename to public/images/emoji/unicode/1f4e5.png diff --git a/app/assets/images/emoji/unicode/1f4e6.png b/public/images/emoji/unicode/1f4e6.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4e6.png rename to public/images/emoji/unicode/1f4e6.png diff --git a/app/assets/images/emoji/unicode/1f4e7.png b/public/images/emoji/unicode/1f4e7.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4e7.png rename to public/images/emoji/unicode/1f4e7.png diff --git a/app/assets/images/emoji/unicode/1f4e8.png b/public/images/emoji/unicode/1f4e8.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4e8.png rename to public/images/emoji/unicode/1f4e8.png diff --git a/public/images/emoji/unicode/1f4e9.png b/public/images/emoji/unicode/1f4e9.png new file mode 100644 index 000000000..efaa1fa89 Binary files /dev/null and b/public/images/emoji/unicode/1f4e9.png differ diff --git a/app/assets/images/emoji/unicode/1f4ea.png b/public/images/emoji/unicode/1f4ea.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4ea.png rename to public/images/emoji/unicode/1f4ea.png diff --git a/app/assets/images/emoji/unicode/1f4eb.png b/public/images/emoji/unicode/1f4eb.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4eb.png rename to public/images/emoji/unicode/1f4eb.png diff --git a/app/assets/images/emoji/unicode/1f4ec.png b/public/images/emoji/unicode/1f4ec.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4ec.png rename to public/images/emoji/unicode/1f4ec.png diff --git a/app/assets/images/emoji/unicode/1f4ed.png b/public/images/emoji/unicode/1f4ed.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4ed.png rename to public/images/emoji/unicode/1f4ed.png diff --git a/app/assets/images/emoji/unicode/1f4ee.png b/public/images/emoji/unicode/1f4ee.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4ee.png rename to public/images/emoji/unicode/1f4ee.png diff --git a/app/assets/images/emoji/unicode/1f4ef.png b/public/images/emoji/unicode/1f4ef.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4ef.png rename to public/images/emoji/unicode/1f4ef.png diff --git a/app/assets/images/emoji/unicode/1f4f0.png b/public/images/emoji/unicode/1f4f0.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4f0.png rename to public/images/emoji/unicode/1f4f0.png diff --git a/app/assets/images/emoji/unicode/1f4f1.png b/public/images/emoji/unicode/1f4f1.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4f1.png rename to public/images/emoji/unicode/1f4f1.png diff --git a/app/assets/images/emoji/unicode/1f4f2.png b/public/images/emoji/unicode/1f4f2.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4f2.png rename to public/images/emoji/unicode/1f4f2.png diff --git a/app/assets/images/emoji/unicode/1f4f3.png b/public/images/emoji/unicode/1f4f3.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4f3.png rename to public/images/emoji/unicode/1f4f3.png diff --git a/app/assets/images/emoji/unicode/1f4f4.png b/public/images/emoji/unicode/1f4f4.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4f4.png rename to public/images/emoji/unicode/1f4f4.png diff --git a/app/assets/images/emoji/unicode/1f4f5.png b/public/images/emoji/unicode/1f4f5.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4f5.png rename to public/images/emoji/unicode/1f4f5.png diff --git a/app/assets/images/emoji/unicode/1f4f6.png b/public/images/emoji/unicode/1f4f6.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4f6.png rename to public/images/emoji/unicode/1f4f6.png diff --git a/app/assets/images/emoji/unicode/1f4f7.png b/public/images/emoji/unicode/1f4f7.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4f7.png rename to public/images/emoji/unicode/1f4f7.png diff --git a/app/assets/images/emoji/unicode/1f4f9.png b/public/images/emoji/unicode/1f4f9.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4f9.png rename to public/images/emoji/unicode/1f4f9.png diff --git a/app/assets/images/emoji/unicode/1f4fa.png b/public/images/emoji/unicode/1f4fa.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4fa.png rename to public/images/emoji/unicode/1f4fa.png diff --git a/app/assets/images/emoji/unicode/1f4fb.png b/public/images/emoji/unicode/1f4fb.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4fb.png rename to public/images/emoji/unicode/1f4fb.png diff --git a/app/assets/images/emoji/unicode/1f4fc.png b/public/images/emoji/unicode/1f4fc.png similarity index 100% rename from app/assets/images/emoji/unicode/1f4fc.png rename to public/images/emoji/unicode/1f4fc.png diff --git a/app/assets/images/emoji/unicode/1f500.png b/public/images/emoji/unicode/1f500.png similarity index 100% rename from app/assets/images/emoji/unicode/1f500.png rename to public/images/emoji/unicode/1f500.png diff --git a/app/assets/images/emoji/unicode/1f501.png b/public/images/emoji/unicode/1f501.png similarity index 100% rename from app/assets/images/emoji/unicode/1f501.png rename to public/images/emoji/unicode/1f501.png diff --git a/app/assets/images/emoji/unicode/1f502.png b/public/images/emoji/unicode/1f502.png similarity index 100% rename from app/assets/images/emoji/unicode/1f502.png rename to public/images/emoji/unicode/1f502.png diff --git a/app/assets/images/emoji/unicode/1f503.png b/public/images/emoji/unicode/1f503.png similarity index 100% rename from app/assets/images/emoji/unicode/1f503.png rename to public/images/emoji/unicode/1f503.png diff --git a/app/assets/images/emoji/unicode/1f504.png b/public/images/emoji/unicode/1f504.png similarity index 100% rename from app/assets/images/emoji/unicode/1f504.png rename to public/images/emoji/unicode/1f504.png diff --git a/app/assets/images/emoji/unicode/1f505.png b/public/images/emoji/unicode/1f505.png similarity index 100% rename from app/assets/images/emoji/unicode/1f505.png rename to public/images/emoji/unicode/1f505.png diff --git a/app/assets/images/emoji/unicode/1f506.png b/public/images/emoji/unicode/1f506.png similarity index 100% rename from app/assets/images/emoji/unicode/1f506.png rename to public/images/emoji/unicode/1f506.png diff --git a/app/assets/images/emoji/unicode/1f507.png b/public/images/emoji/unicode/1f507.png similarity index 100% rename from app/assets/images/emoji/unicode/1f507.png rename to public/images/emoji/unicode/1f507.png diff --git a/app/assets/images/emoji/speaker.png b/public/images/emoji/unicode/1f508.png similarity index 100% rename from app/assets/images/emoji/speaker.png rename to public/images/emoji/unicode/1f508.png diff --git a/app/assets/images/emoji/unicode/1f509.png b/public/images/emoji/unicode/1f509.png similarity index 100% rename from app/assets/images/emoji/unicode/1f509.png rename to public/images/emoji/unicode/1f509.png diff --git a/public/images/emoji/unicode/1f50a.png b/public/images/emoji/unicode/1f50a.png new file mode 100644 index 000000000..f63e81497 Binary files /dev/null and b/public/images/emoji/unicode/1f50a.png differ diff --git a/app/assets/images/emoji/unicode/1f50b.png b/public/images/emoji/unicode/1f50b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f50b.png rename to public/images/emoji/unicode/1f50b.png diff --git a/app/assets/images/emoji/unicode/1f50c.png b/public/images/emoji/unicode/1f50c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f50c.png rename to public/images/emoji/unicode/1f50c.png diff --git a/app/assets/images/emoji/unicode/1f50d.png b/public/images/emoji/unicode/1f50d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f50d.png rename to public/images/emoji/unicode/1f50d.png diff --git a/app/assets/images/emoji/unicode/1f50e.png b/public/images/emoji/unicode/1f50e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f50e.png rename to public/images/emoji/unicode/1f50e.png diff --git a/app/assets/images/emoji/unicode/1f50f.png b/public/images/emoji/unicode/1f50f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f50f.png rename to public/images/emoji/unicode/1f50f.png diff --git a/app/assets/images/emoji/unicode/1f510.png b/public/images/emoji/unicode/1f510.png similarity index 100% rename from app/assets/images/emoji/unicode/1f510.png rename to public/images/emoji/unicode/1f510.png diff --git a/app/assets/images/emoji/unicode/1f511.png b/public/images/emoji/unicode/1f511.png similarity index 100% rename from app/assets/images/emoji/unicode/1f511.png rename to public/images/emoji/unicode/1f511.png diff --git a/app/assets/images/emoji/unicode/1f512.png b/public/images/emoji/unicode/1f512.png similarity index 100% rename from app/assets/images/emoji/unicode/1f512.png rename to public/images/emoji/unicode/1f512.png diff --git a/app/assets/images/emoji/unicode/1f513.png b/public/images/emoji/unicode/1f513.png similarity index 100% rename from app/assets/images/emoji/unicode/1f513.png rename to public/images/emoji/unicode/1f513.png diff --git a/app/assets/images/emoji/unicode/1f514.png b/public/images/emoji/unicode/1f514.png similarity index 100% rename from app/assets/images/emoji/unicode/1f514.png rename to public/images/emoji/unicode/1f514.png diff --git a/app/assets/images/emoji/unicode/1f515.png b/public/images/emoji/unicode/1f515.png similarity index 100% rename from app/assets/images/emoji/unicode/1f515.png rename to public/images/emoji/unicode/1f515.png diff --git a/app/assets/images/emoji/unicode/1f516.png b/public/images/emoji/unicode/1f516.png similarity index 100% rename from app/assets/images/emoji/unicode/1f516.png rename to public/images/emoji/unicode/1f516.png diff --git a/app/assets/images/emoji/unicode/1f517.png b/public/images/emoji/unicode/1f517.png similarity index 100% rename from app/assets/images/emoji/unicode/1f517.png rename to public/images/emoji/unicode/1f517.png diff --git a/app/assets/images/emoji/unicode/1f518.png b/public/images/emoji/unicode/1f518.png similarity index 100% rename from app/assets/images/emoji/unicode/1f518.png rename to public/images/emoji/unicode/1f518.png diff --git a/app/assets/images/emoji/unicode/1f519.png b/public/images/emoji/unicode/1f519.png similarity index 100% rename from app/assets/images/emoji/unicode/1f519.png rename to public/images/emoji/unicode/1f519.png diff --git a/app/assets/images/emoji/unicode/1f51a.png b/public/images/emoji/unicode/1f51a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f51a.png rename to public/images/emoji/unicode/1f51a.png diff --git a/app/assets/images/emoji/unicode/1f51b.png b/public/images/emoji/unicode/1f51b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f51b.png rename to public/images/emoji/unicode/1f51b.png diff --git a/app/assets/images/emoji/unicode/1f51c.png b/public/images/emoji/unicode/1f51c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f51c.png rename to public/images/emoji/unicode/1f51c.png diff --git a/public/images/emoji/unicode/1f51d.png b/public/images/emoji/unicode/1f51d.png new file mode 100644 index 000000000..9d9557dd5 Binary files /dev/null and b/public/images/emoji/unicode/1f51d.png differ diff --git a/app/assets/images/emoji/unicode/1f51e.png b/public/images/emoji/unicode/1f51e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f51e.png rename to public/images/emoji/unicode/1f51e.png diff --git a/app/assets/images/emoji/unicode/1f51f.png b/public/images/emoji/unicode/1f51f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f51f.png rename to public/images/emoji/unicode/1f51f.png diff --git a/app/assets/images/emoji/unicode/1f520.png b/public/images/emoji/unicode/1f520.png similarity index 100% rename from app/assets/images/emoji/unicode/1f520.png rename to public/images/emoji/unicode/1f520.png diff --git a/app/assets/images/emoji/unicode/1f521.png b/public/images/emoji/unicode/1f521.png similarity index 100% rename from app/assets/images/emoji/unicode/1f521.png rename to public/images/emoji/unicode/1f521.png diff --git a/app/assets/images/emoji/unicode/1f522.png b/public/images/emoji/unicode/1f522.png similarity index 100% rename from app/assets/images/emoji/unicode/1f522.png rename to public/images/emoji/unicode/1f522.png diff --git a/app/assets/images/emoji/unicode/1f523.png b/public/images/emoji/unicode/1f523.png similarity index 100% rename from app/assets/images/emoji/unicode/1f523.png rename to public/images/emoji/unicode/1f523.png diff --git a/app/assets/images/emoji/unicode/1f524.png b/public/images/emoji/unicode/1f524.png similarity index 100% rename from app/assets/images/emoji/unicode/1f524.png rename to public/images/emoji/unicode/1f524.png diff --git a/app/assets/images/emoji/unicode/1f525.png b/public/images/emoji/unicode/1f525.png similarity index 100% rename from app/assets/images/emoji/unicode/1f525.png rename to public/images/emoji/unicode/1f525.png diff --git a/app/assets/images/emoji/unicode/1f526.png b/public/images/emoji/unicode/1f526.png similarity index 100% rename from app/assets/images/emoji/unicode/1f526.png rename to public/images/emoji/unicode/1f526.png diff --git a/app/assets/images/emoji/unicode/1f527.png b/public/images/emoji/unicode/1f527.png similarity index 100% rename from app/assets/images/emoji/unicode/1f527.png rename to public/images/emoji/unicode/1f527.png diff --git a/public/images/emoji/unicode/1f528.png b/public/images/emoji/unicode/1f528.png new file mode 100644 index 000000000..88e17f07b Binary files /dev/null and b/public/images/emoji/unicode/1f528.png differ diff --git a/app/assets/images/emoji/unicode/1f529.png b/public/images/emoji/unicode/1f529.png similarity index 100% rename from app/assets/images/emoji/unicode/1f529.png rename to public/images/emoji/unicode/1f529.png diff --git a/app/assets/images/emoji/unicode/1f52a.png b/public/images/emoji/unicode/1f52a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f52a.png rename to public/images/emoji/unicode/1f52a.png diff --git a/app/assets/images/emoji/unicode/1f52b.png b/public/images/emoji/unicode/1f52b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f52b.png rename to public/images/emoji/unicode/1f52b.png diff --git a/app/assets/images/emoji/unicode/1f52c.png b/public/images/emoji/unicode/1f52c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f52c.png rename to public/images/emoji/unicode/1f52c.png diff --git a/app/assets/images/emoji/unicode/1f52d.png b/public/images/emoji/unicode/1f52d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f52d.png rename to public/images/emoji/unicode/1f52d.png diff --git a/app/assets/images/emoji/unicode/1f52e.png b/public/images/emoji/unicode/1f52e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f52e.png rename to public/images/emoji/unicode/1f52e.png diff --git a/app/assets/images/emoji/unicode/1f52f.png b/public/images/emoji/unicode/1f52f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f52f.png rename to public/images/emoji/unicode/1f52f.png diff --git a/app/assets/images/emoji/unicode/1f530.png b/public/images/emoji/unicode/1f530.png similarity index 100% rename from app/assets/images/emoji/unicode/1f530.png rename to public/images/emoji/unicode/1f530.png diff --git a/app/assets/images/emoji/unicode/1f531.png b/public/images/emoji/unicode/1f531.png similarity index 100% rename from app/assets/images/emoji/unicode/1f531.png rename to public/images/emoji/unicode/1f531.png diff --git a/app/assets/images/emoji/unicode/1f532.png b/public/images/emoji/unicode/1f532.png similarity index 100% rename from app/assets/images/emoji/unicode/1f532.png rename to public/images/emoji/unicode/1f532.png diff --git a/app/assets/images/emoji/unicode/1f533.png b/public/images/emoji/unicode/1f533.png similarity index 100% rename from app/assets/images/emoji/unicode/1f533.png rename to public/images/emoji/unicode/1f533.png diff --git a/app/assets/images/emoji/unicode/1f534.png b/public/images/emoji/unicode/1f534.png similarity index 100% rename from app/assets/images/emoji/unicode/1f534.png rename to public/images/emoji/unicode/1f534.png diff --git a/app/assets/images/emoji/unicode/1f535.png b/public/images/emoji/unicode/1f535.png similarity index 100% rename from app/assets/images/emoji/unicode/1f535.png rename to public/images/emoji/unicode/1f535.png diff --git a/app/assets/images/emoji/unicode/1f536.png b/public/images/emoji/unicode/1f536.png similarity index 100% rename from app/assets/images/emoji/unicode/1f536.png rename to public/images/emoji/unicode/1f536.png diff --git a/app/assets/images/emoji/unicode/1f537.png b/public/images/emoji/unicode/1f537.png similarity index 100% rename from app/assets/images/emoji/unicode/1f537.png rename to public/images/emoji/unicode/1f537.png diff --git a/app/assets/images/emoji/unicode/1f538.png b/public/images/emoji/unicode/1f538.png similarity index 100% rename from app/assets/images/emoji/unicode/1f538.png rename to public/images/emoji/unicode/1f538.png diff --git a/app/assets/images/emoji/unicode/1f539.png b/public/images/emoji/unicode/1f539.png similarity index 100% rename from app/assets/images/emoji/unicode/1f539.png rename to public/images/emoji/unicode/1f539.png diff --git a/app/assets/images/emoji/unicode/1f53a.png b/public/images/emoji/unicode/1f53a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f53a.png rename to public/images/emoji/unicode/1f53a.png diff --git a/app/assets/images/emoji/unicode/1f53b.png b/public/images/emoji/unicode/1f53b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f53b.png rename to public/images/emoji/unicode/1f53b.png diff --git a/app/assets/images/emoji/unicode/1f53c.png b/public/images/emoji/unicode/1f53c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f53c.png rename to public/images/emoji/unicode/1f53c.png diff --git a/app/assets/images/emoji/unicode/1f53d.png b/public/images/emoji/unicode/1f53d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f53d.png rename to public/images/emoji/unicode/1f53d.png diff --git a/app/assets/images/emoji/unicode/1f550.png b/public/images/emoji/unicode/1f550.png similarity index 100% rename from app/assets/images/emoji/unicode/1f550.png rename to public/images/emoji/unicode/1f550.png diff --git a/app/assets/images/emoji/unicode/1f551.png b/public/images/emoji/unicode/1f551.png similarity index 100% rename from app/assets/images/emoji/unicode/1f551.png rename to public/images/emoji/unicode/1f551.png diff --git a/app/assets/images/emoji/unicode/1f552.png b/public/images/emoji/unicode/1f552.png similarity index 100% rename from app/assets/images/emoji/unicode/1f552.png rename to public/images/emoji/unicode/1f552.png diff --git a/app/assets/images/emoji/unicode/1f553.png b/public/images/emoji/unicode/1f553.png similarity index 100% rename from app/assets/images/emoji/unicode/1f553.png rename to public/images/emoji/unicode/1f553.png diff --git a/app/assets/images/emoji/unicode/1f554.png b/public/images/emoji/unicode/1f554.png similarity index 100% rename from app/assets/images/emoji/unicode/1f554.png rename to public/images/emoji/unicode/1f554.png diff --git a/app/assets/images/emoji/unicode/1f555.png b/public/images/emoji/unicode/1f555.png similarity index 100% rename from app/assets/images/emoji/unicode/1f555.png rename to public/images/emoji/unicode/1f555.png diff --git a/app/assets/images/emoji/unicode/1f556.png b/public/images/emoji/unicode/1f556.png similarity index 100% rename from app/assets/images/emoji/unicode/1f556.png rename to public/images/emoji/unicode/1f556.png diff --git a/app/assets/images/emoji/unicode/1f557.png b/public/images/emoji/unicode/1f557.png similarity index 100% rename from app/assets/images/emoji/unicode/1f557.png rename to public/images/emoji/unicode/1f557.png diff --git a/app/assets/images/emoji/unicode/1f558.png b/public/images/emoji/unicode/1f558.png similarity index 100% rename from app/assets/images/emoji/unicode/1f558.png rename to public/images/emoji/unicode/1f558.png diff --git a/app/assets/images/emoji/unicode/1f559.png b/public/images/emoji/unicode/1f559.png similarity index 100% rename from app/assets/images/emoji/unicode/1f559.png rename to public/images/emoji/unicode/1f559.png diff --git a/app/assets/images/emoji/unicode/1f55a.png b/public/images/emoji/unicode/1f55a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f55a.png rename to public/images/emoji/unicode/1f55a.png diff --git a/app/assets/images/emoji/unicode/1f55b.png b/public/images/emoji/unicode/1f55b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f55b.png rename to public/images/emoji/unicode/1f55b.png diff --git a/app/assets/images/emoji/unicode/1f55c.png b/public/images/emoji/unicode/1f55c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f55c.png rename to public/images/emoji/unicode/1f55c.png diff --git a/app/assets/images/emoji/unicode/1f55d.png b/public/images/emoji/unicode/1f55d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f55d.png rename to public/images/emoji/unicode/1f55d.png diff --git a/app/assets/images/emoji/unicode/1f55e.png b/public/images/emoji/unicode/1f55e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f55e.png rename to public/images/emoji/unicode/1f55e.png diff --git a/app/assets/images/emoji/unicode/1f55f.png b/public/images/emoji/unicode/1f55f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f55f.png rename to public/images/emoji/unicode/1f55f.png diff --git a/app/assets/images/emoji/unicode/1f560.png b/public/images/emoji/unicode/1f560.png similarity index 100% rename from app/assets/images/emoji/unicode/1f560.png rename to public/images/emoji/unicode/1f560.png diff --git a/app/assets/images/emoji/unicode/1f561.png b/public/images/emoji/unicode/1f561.png similarity index 100% rename from app/assets/images/emoji/unicode/1f561.png rename to public/images/emoji/unicode/1f561.png diff --git a/app/assets/images/emoji/unicode/1f562.png b/public/images/emoji/unicode/1f562.png similarity index 100% rename from app/assets/images/emoji/unicode/1f562.png rename to public/images/emoji/unicode/1f562.png diff --git a/app/assets/images/emoji/unicode/1f563.png b/public/images/emoji/unicode/1f563.png similarity index 100% rename from app/assets/images/emoji/unicode/1f563.png rename to public/images/emoji/unicode/1f563.png diff --git a/app/assets/images/emoji/unicode/1f564.png b/public/images/emoji/unicode/1f564.png similarity index 100% rename from app/assets/images/emoji/unicode/1f564.png rename to public/images/emoji/unicode/1f564.png diff --git a/app/assets/images/emoji/unicode/1f565.png b/public/images/emoji/unicode/1f565.png similarity index 100% rename from app/assets/images/emoji/unicode/1f565.png rename to public/images/emoji/unicode/1f565.png diff --git a/app/assets/images/emoji/unicode/1f566.png b/public/images/emoji/unicode/1f566.png similarity index 100% rename from app/assets/images/emoji/unicode/1f566.png rename to public/images/emoji/unicode/1f566.png diff --git a/app/assets/images/emoji/unicode/1f567.png b/public/images/emoji/unicode/1f567.png similarity index 100% rename from app/assets/images/emoji/unicode/1f567.png rename to public/images/emoji/unicode/1f567.png diff --git a/app/assets/images/emoji/unicode/1f5fb.png b/public/images/emoji/unicode/1f5fb.png similarity index 100% rename from app/assets/images/emoji/unicode/1f5fb.png rename to public/images/emoji/unicode/1f5fb.png diff --git a/app/assets/images/emoji/unicode/1f5fc.png b/public/images/emoji/unicode/1f5fc.png similarity index 100% rename from app/assets/images/emoji/unicode/1f5fc.png rename to public/images/emoji/unicode/1f5fc.png diff --git a/app/assets/images/emoji/unicode/1f5fd.png b/public/images/emoji/unicode/1f5fd.png similarity index 100% rename from app/assets/images/emoji/unicode/1f5fd.png rename to public/images/emoji/unicode/1f5fd.png diff --git a/app/assets/images/emoji/unicode/1f5fe.png b/public/images/emoji/unicode/1f5fe.png similarity index 100% rename from app/assets/images/emoji/unicode/1f5fe.png rename to public/images/emoji/unicode/1f5fe.png diff --git a/app/assets/images/emoji/unicode/1f5ff.png b/public/images/emoji/unicode/1f5ff.png similarity index 100% rename from app/assets/images/emoji/unicode/1f5ff.png rename to public/images/emoji/unicode/1f5ff.png diff --git a/app/assets/images/emoji/unicode/1f600.png b/public/images/emoji/unicode/1f600.png similarity index 100% rename from app/assets/images/emoji/unicode/1f600.png rename to public/images/emoji/unicode/1f600.png diff --git a/app/assets/images/emoji/unicode/1f601.png b/public/images/emoji/unicode/1f601.png similarity index 100% rename from app/assets/images/emoji/unicode/1f601.png rename to public/images/emoji/unicode/1f601.png diff --git a/app/assets/images/emoji/unicode/1f602.png b/public/images/emoji/unicode/1f602.png similarity index 100% rename from app/assets/images/emoji/unicode/1f602.png rename to public/images/emoji/unicode/1f602.png diff --git a/app/assets/images/emoji/unicode/1f603.png b/public/images/emoji/unicode/1f603.png similarity index 100% rename from app/assets/images/emoji/unicode/1f603.png rename to public/images/emoji/unicode/1f603.png diff --git a/app/assets/images/emoji/unicode/1f604.png b/public/images/emoji/unicode/1f604.png similarity index 100% rename from app/assets/images/emoji/unicode/1f604.png rename to public/images/emoji/unicode/1f604.png diff --git a/app/assets/images/emoji/unicode/1f605.png b/public/images/emoji/unicode/1f605.png similarity index 100% rename from app/assets/images/emoji/unicode/1f605.png rename to public/images/emoji/unicode/1f605.png diff --git a/app/assets/images/emoji/unicode/1f606.png b/public/images/emoji/unicode/1f606.png similarity index 100% rename from app/assets/images/emoji/unicode/1f606.png rename to public/images/emoji/unicode/1f606.png diff --git a/app/assets/images/emoji/unicode/1f607.png b/public/images/emoji/unicode/1f607.png similarity index 100% rename from app/assets/images/emoji/unicode/1f607.png rename to public/images/emoji/unicode/1f607.png diff --git a/app/assets/images/emoji/unicode/1f608.png b/public/images/emoji/unicode/1f608.png similarity index 100% rename from app/assets/images/emoji/unicode/1f608.png rename to public/images/emoji/unicode/1f608.png diff --git a/app/assets/images/emoji/unicode/1f609.png b/public/images/emoji/unicode/1f609.png similarity index 100% rename from app/assets/images/emoji/unicode/1f609.png rename to public/images/emoji/unicode/1f609.png diff --git a/app/assets/images/emoji/unicode/1f60a.png b/public/images/emoji/unicode/1f60a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f60a.png rename to public/images/emoji/unicode/1f60a.png diff --git a/app/assets/images/emoji/unicode/1f60b.png b/public/images/emoji/unicode/1f60b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f60b.png rename to public/images/emoji/unicode/1f60b.png diff --git a/app/assets/images/emoji/unicode/1f60c.png b/public/images/emoji/unicode/1f60c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f60c.png rename to public/images/emoji/unicode/1f60c.png diff --git a/app/assets/images/emoji/unicode/1f60d.png b/public/images/emoji/unicode/1f60d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f60d.png rename to public/images/emoji/unicode/1f60d.png diff --git a/app/assets/images/emoji/unicode/1f60e.png b/public/images/emoji/unicode/1f60e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f60e.png rename to public/images/emoji/unicode/1f60e.png diff --git a/app/assets/images/emoji/unicode/1f60f.png b/public/images/emoji/unicode/1f60f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f60f.png rename to public/images/emoji/unicode/1f60f.png diff --git a/app/assets/images/emoji/unicode/1f610.png b/public/images/emoji/unicode/1f610.png similarity index 100% rename from app/assets/images/emoji/unicode/1f610.png rename to public/images/emoji/unicode/1f610.png diff --git a/app/assets/images/emoji/unicode/1f611.png b/public/images/emoji/unicode/1f611.png similarity index 100% rename from app/assets/images/emoji/unicode/1f611.png rename to public/images/emoji/unicode/1f611.png diff --git a/app/assets/images/emoji/unicode/1f612.png b/public/images/emoji/unicode/1f612.png similarity index 100% rename from app/assets/images/emoji/unicode/1f612.png rename to public/images/emoji/unicode/1f612.png diff --git a/app/assets/images/emoji/unicode/1f613.png b/public/images/emoji/unicode/1f613.png similarity index 100% rename from app/assets/images/emoji/unicode/1f613.png rename to public/images/emoji/unicode/1f613.png diff --git a/app/assets/images/emoji/unicode/1f614.png b/public/images/emoji/unicode/1f614.png similarity index 100% rename from app/assets/images/emoji/unicode/1f614.png rename to public/images/emoji/unicode/1f614.png diff --git a/app/assets/images/emoji/unicode/1f615.png b/public/images/emoji/unicode/1f615.png similarity index 100% rename from app/assets/images/emoji/unicode/1f615.png rename to public/images/emoji/unicode/1f615.png diff --git a/app/assets/images/emoji/unicode/1f616.png b/public/images/emoji/unicode/1f616.png similarity index 100% rename from app/assets/images/emoji/unicode/1f616.png rename to public/images/emoji/unicode/1f616.png diff --git a/app/assets/images/emoji/unicode/1f617.png b/public/images/emoji/unicode/1f617.png similarity index 100% rename from app/assets/images/emoji/unicode/1f617.png rename to public/images/emoji/unicode/1f617.png diff --git a/app/assets/images/emoji/unicode/1f618.png b/public/images/emoji/unicode/1f618.png similarity index 100% rename from app/assets/images/emoji/unicode/1f618.png rename to public/images/emoji/unicode/1f618.png diff --git a/app/assets/images/emoji/unicode/1f619.png b/public/images/emoji/unicode/1f619.png similarity index 100% rename from app/assets/images/emoji/unicode/1f619.png rename to public/images/emoji/unicode/1f619.png diff --git a/app/assets/images/emoji/unicode/1f61a.png b/public/images/emoji/unicode/1f61a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f61a.png rename to public/images/emoji/unicode/1f61a.png diff --git a/app/assets/images/emoji/unicode/1f61b.png b/public/images/emoji/unicode/1f61b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f61b.png rename to public/images/emoji/unicode/1f61b.png diff --git a/app/assets/images/emoji/unicode/1f61c.png b/public/images/emoji/unicode/1f61c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f61c.png rename to public/images/emoji/unicode/1f61c.png diff --git a/app/assets/images/emoji/unicode/1f61d.png b/public/images/emoji/unicode/1f61d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f61d.png rename to public/images/emoji/unicode/1f61d.png diff --git a/app/assets/images/emoji/unicode/1f61e.png b/public/images/emoji/unicode/1f61e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f61e.png rename to public/images/emoji/unicode/1f61e.png diff --git a/app/assets/images/emoji/unicode/1f61f.png b/public/images/emoji/unicode/1f61f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f61f.png rename to public/images/emoji/unicode/1f61f.png diff --git a/app/assets/images/emoji/unicode/1f620.png b/public/images/emoji/unicode/1f620.png similarity index 100% rename from app/assets/images/emoji/unicode/1f620.png rename to public/images/emoji/unicode/1f620.png diff --git a/app/assets/images/emoji/unicode/1f621.png b/public/images/emoji/unicode/1f621.png similarity index 100% rename from app/assets/images/emoji/unicode/1f621.png rename to public/images/emoji/unicode/1f621.png diff --git a/app/assets/images/emoji/unicode/1f622.png b/public/images/emoji/unicode/1f622.png similarity index 100% rename from app/assets/images/emoji/unicode/1f622.png rename to public/images/emoji/unicode/1f622.png diff --git a/app/assets/images/emoji/unicode/1f623.png b/public/images/emoji/unicode/1f623.png similarity index 100% rename from app/assets/images/emoji/unicode/1f623.png rename to public/images/emoji/unicode/1f623.png diff --git a/app/assets/images/emoji/unicode/1f624.png b/public/images/emoji/unicode/1f624.png similarity index 100% rename from app/assets/images/emoji/unicode/1f624.png rename to public/images/emoji/unicode/1f624.png diff --git a/app/assets/images/emoji/unicode/1f625.png b/public/images/emoji/unicode/1f625.png similarity index 100% rename from app/assets/images/emoji/unicode/1f625.png rename to public/images/emoji/unicode/1f625.png diff --git a/app/assets/images/emoji/unicode/1f626.png b/public/images/emoji/unicode/1f626.png similarity index 100% rename from app/assets/images/emoji/unicode/1f626.png rename to public/images/emoji/unicode/1f626.png diff --git a/app/assets/images/emoji/unicode/1f627.png b/public/images/emoji/unicode/1f627.png similarity index 100% rename from app/assets/images/emoji/unicode/1f627.png rename to public/images/emoji/unicode/1f627.png diff --git a/app/assets/images/emoji/unicode/1f628.png b/public/images/emoji/unicode/1f628.png similarity index 100% rename from app/assets/images/emoji/unicode/1f628.png rename to public/images/emoji/unicode/1f628.png diff --git a/app/assets/images/emoji/unicode/1f629.png b/public/images/emoji/unicode/1f629.png similarity index 100% rename from app/assets/images/emoji/unicode/1f629.png rename to public/images/emoji/unicode/1f629.png diff --git a/app/assets/images/emoji/unicode/1f62a.png b/public/images/emoji/unicode/1f62a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f62a.png rename to public/images/emoji/unicode/1f62a.png diff --git a/app/assets/images/emoji/unicode/1f62b.png b/public/images/emoji/unicode/1f62b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f62b.png rename to public/images/emoji/unicode/1f62b.png diff --git a/app/assets/images/emoji/unicode/1f62c.png b/public/images/emoji/unicode/1f62c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f62c.png rename to public/images/emoji/unicode/1f62c.png diff --git a/app/assets/images/emoji/unicode/1f62d.png b/public/images/emoji/unicode/1f62d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f62d.png rename to public/images/emoji/unicode/1f62d.png diff --git a/app/assets/images/emoji/unicode/1f62e.png b/public/images/emoji/unicode/1f62e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f62e.png rename to public/images/emoji/unicode/1f62e.png diff --git a/app/assets/images/emoji/unicode/1f62f.png b/public/images/emoji/unicode/1f62f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f62f.png rename to public/images/emoji/unicode/1f62f.png diff --git a/app/assets/images/emoji/unicode/1f630.png b/public/images/emoji/unicode/1f630.png similarity index 100% rename from app/assets/images/emoji/unicode/1f630.png rename to public/images/emoji/unicode/1f630.png diff --git a/app/assets/images/emoji/unicode/1f631.png b/public/images/emoji/unicode/1f631.png similarity index 100% rename from app/assets/images/emoji/unicode/1f631.png rename to public/images/emoji/unicode/1f631.png diff --git a/app/assets/images/emoji/unicode/1f632.png b/public/images/emoji/unicode/1f632.png similarity index 100% rename from app/assets/images/emoji/unicode/1f632.png rename to public/images/emoji/unicode/1f632.png diff --git a/app/assets/images/emoji/unicode/1f633.png b/public/images/emoji/unicode/1f633.png similarity index 100% rename from app/assets/images/emoji/unicode/1f633.png rename to public/images/emoji/unicode/1f633.png diff --git a/app/assets/images/emoji/unicode/1f634.png b/public/images/emoji/unicode/1f634.png similarity index 100% rename from app/assets/images/emoji/unicode/1f634.png rename to public/images/emoji/unicode/1f634.png diff --git a/app/assets/images/emoji/unicode/1f635.png b/public/images/emoji/unicode/1f635.png similarity index 100% rename from app/assets/images/emoji/unicode/1f635.png rename to public/images/emoji/unicode/1f635.png diff --git a/app/assets/images/emoji/unicode/1f636.png b/public/images/emoji/unicode/1f636.png similarity index 100% rename from app/assets/images/emoji/unicode/1f636.png rename to public/images/emoji/unicode/1f636.png diff --git a/app/assets/images/emoji/unicode/1f637.png b/public/images/emoji/unicode/1f637.png similarity index 100% rename from app/assets/images/emoji/unicode/1f637.png rename to public/images/emoji/unicode/1f637.png diff --git a/app/assets/images/emoji/unicode/1f638.png b/public/images/emoji/unicode/1f638.png similarity index 100% rename from app/assets/images/emoji/unicode/1f638.png rename to public/images/emoji/unicode/1f638.png diff --git a/app/assets/images/emoji/unicode/1f639.png b/public/images/emoji/unicode/1f639.png similarity index 100% rename from app/assets/images/emoji/unicode/1f639.png rename to public/images/emoji/unicode/1f639.png diff --git a/app/assets/images/emoji/unicode/1f63a.png b/public/images/emoji/unicode/1f63a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f63a.png rename to public/images/emoji/unicode/1f63a.png diff --git a/app/assets/images/emoji/unicode/1f63b.png b/public/images/emoji/unicode/1f63b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f63b.png rename to public/images/emoji/unicode/1f63b.png diff --git a/app/assets/images/emoji/unicode/1f63c.png b/public/images/emoji/unicode/1f63c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f63c.png rename to public/images/emoji/unicode/1f63c.png diff --git a/app/assets/images/emoji/unicode/1f63d.png b/public/images/emoji/unicode/1f63d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f63d.png rename to public/images/emoji/unicode/1f63d.png diff --git a/app/assets/images/emoji/unicode/1f63e.png b/public/images/emoji/unicode/1f63e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f63e.png rename to public/images/emoji/unicode/1f63e.png diff --git a/app/assets/images/emoji/unicode/1f63f.png b/public/images/emoji/unicode/1f63f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f63f.png rename to public/images/emoji/unicode/1f63f.png diff --git a/app/assets/images/emoji/unicode/1f640.png b/public/images/emoji/unicode/1f640.png similarity index 100% rename from app/assets/images/emoji/unicode/1f640.png rename to public/images/emoji/unicode/1f640.png diff --git a/app/assets/images/emoji/unicode/1f645.png b/public/images/emoji/unicode/1f645.png similarity index 100% rename from app/assets/images/emoji/unicode/1f645.png rename to public/images/emoji/unicode/1f645.png diff --git a/app/assets/images/emoji/unicode/1f646.png b/public/images/emoji/unicode/1f646.png similarity index 100% rename from app/assets/images/emoji/unicode/1f646.png rename to public/images/emoji/unicode/1f646.png diff --git a/app/assets/images/emoji/unicode/1f647.png b/public/images/emoji/unicode/1f647.png similarity index 100% rename from app/assets/images/emoji/unicode/1f647.png rename to public/images/emoji/unicode/1f647.png diff --git a/app/assets/images/emoji/unicode/1f648.png b/public/images/emoji/unicode/1f648.png similarity index 100% rename from app/assets/images/emoji/unicode/1f648.png rename to public/images/emoji/unicode/1f648.png diff --git a/app/assets/images/emoji/unicode/1f649.png b/public/images/emoji/unicode/1f649.png similarity index 100% rename from app/assets/images/emoji/unicode/1f649.png rename to public/images/emoji/unicode/1f649.png diff --git a/app/assets/images/emoji/unicode/1f64a.png b/public/images/emoji/unicode/1f64a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f64a.png rename to public/images/emoji/unicode/1f64a.png diff --git a/app/assets/images/emoji/unicode/1f64b.png b/public/images/emoji/unicode/1f64b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f64b.png rename to public/images/emoji/unicode/1f64b.png diff --git a/app/assets/images/emoji/unicode/1f64c.png b/public/images/emoji/unicode/1f64c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f64c.png rename to public/images/emoji/unicode/1f64c.png diff --git a/app/assets/images/emoji/unicode/1f64d.png b/public/images/emoji/unicode/1f64d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f64d.png rename to public/images/emoji/unicode/1f64d.png diff --git a/app/assets/images/emoji/unicode/1f64e.png b/public/images/emoji/unicode/1f64e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f64e.png rename to public/images/emoji/unicode/1f64e.png diff --git a/app/assets/images/emoji/unicode/1f64f.png b/public/images/emoji/unicode/1f64f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f64f.png rename to public/images/emoji/unicode/1f64f.png diff --git a/app/assets/images/emoji/unicode/1f680.png b/public/images/emoji/unicode/1f680.png similarity index 100% rename from app/assets/images/emoji/unicode/1f680.png rename to public/images/emoji/unicode/1f680.png diff --git a/app/assets/images/emoji/unicode/1f681.png b/public/images/emoji/unicode/1f681.png similarity index 100% rename from app/assets/images/emoji/unicode/1f681.png rename to public/images/emoji/unicode/1f681.png diff --git a/app/assets/images/emoji/unicode/1f682.png b/public/images/emoji/unicode/1f682.png similarity index 100% rename from app/assets/images/emoji/unicode/1f682.png rename to public/images/emoji/unicode/1f682.png diff --git a/app/assets/images/emoji/unicode/1f683.png b/public/images/emoji/unicode/1f683.png similarity index 100% rename from app/assets/images/emoji/unicode/1f683.png rename to public/images/emoji/unicode/1f683.png diff --git a/public/images/emoji/unicode/1f684.png b/public/images/emoji/unicode/1f684.png new file mode 100644 index 000000000..49985a3b3 Binary files /dev/null and b/public/images/emoji/unicode/1f684.png differ diff --git a/public/images/emoji/unicode/1f685.png b/public/images/emoji/unicode/1f685.png new file mode 100644 index 000000000..61b494ae2 Binary files /dev/null and b/public/images/emoji/unicode/1f685.png differ diff --git a/app/assets/images/emoji/unicode/1f686.png b/public/images/emoji/unicode/1f686.png similarity index 100% rename from app/assets/images/emoji/unicode/1f686.png rename to public/images/emoji/unicode/1f686.png diff --git a/public/images/emoji/unicode/1f687.png b/public/images/emoji/unicode/1f687.png new file mode 100644 index 000000000..4890f9bce Binary files /dev/null and b/public/images/emoji/unicode/1f687.png differ diff --git a/app/assets/images/emoji/unicode/1f688.png b/public/images/emoji/unicode/1f688.png similarity index 100% rename from app/assets/images/emoji/unicode/1f688.png rename to public/images/emoji/unicode/1f688.png diff --git a/app/assets/images/emoji/unicode/1f689.png b/public/images/emoji/unicode/1f689.png similarity index 100% rename from app/assets/images/emoji/unicode/1f689.png rename to public/images/emoji/unicode/1f689.png diff --git a/app/assets/images/emoji/unicode/1f68a.png b/public/images/emoji/unicode/1f68a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f68a.png rename to public/images/emoji/unicode/1f68a.png diff --git a/public/images/emoji/unicode/1f68b.png b/public/images/emoji/unicode/1f68b.png new file mode 100644 index 000000000..0a8ea52b2 Binary files /dev/null and b/public/images/emoji/unicode/1f68b.png differ diff --git a/app/assets/images/emoji/unicode/1f68c.png b/public/images/emoji/unicode/1f68c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f68c.png rename to public/images/emoji/unicode/1f68c.png diff --git a/app/assets/images/emoji/unicode/1f68d.png b/public/images/emoji/unicode/1f68d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f68d.png rename to public/images/emoji/unicode/1f68d.png diff --git a/app/assets/images/emoji/unicode/1f68e.png b/public/images/emoji/unicode/1f68e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f68e.png rename to public/images/emoji/unicode/1f68e.png diff --git a/app/assets/images/emoji/unicode/1f68f.png b/public/images/emoji/unicode/1f68f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f68f.png rename to public/images/emoji/unicode/1f68f.png diff --git a/app/assets/images/emoji/unicode/1f690.png b/public/images/emoji/unicode/1f690.png similarity index 100% rename from app/assets/images/emoji/unicode/1f690.png rename to public/images/emoji/unicode/1f690.png diff --git a/app/assets/images/emoji/unicode/1f691.png b/public/images/emoji/unicode/1f691.png similarity index 100% rename from app/assets/images/emoji/unicode/1f691.png rename to public/images/emoji/unicode/1f691.png diff --git a/app/assets/images/emoji/unicode/1f692.png b/public/images/emoji/unicode/1f692.png similarity index 100% rename from app/assets/images/emoji/unicode/1f692.png rename to public/images/emoji/unicode/1f692.png diff --git a/app/assets/images/emoji/unicode/1f693.png b/public/images/emoji/unicode/1f693.png similarity index 100% rename from app/assets/images/emoji/unicode/1f693.png rename to public/images/emoji/unicode/1f693.png diff --git a/app/assets/images/emoji/unicode/1f694.png b/public/images/emoji/unicode/1f694.png similarity index 100% rename from app/assets/images/emoji/unicode/1f694.png rename to public/images/emoji/unicode/1f694.png diff --git a/app/assets/images/emoji/unicode/1f695.png b/public/images/emoji/unicode/1f695.png similarity index 100% rename from app/assets/images/emoji/unicode/1f695.png rename to public/images/emoji/unicode/1f695.png diff --git a/app/assets/images/emoji/unicode/1f696.png b/public/images/emoji/unicode/1f696.png similarity index 100% rename from app/assets/images/emoji/unicode/1f696.png rename to public/images/emoji/unicode/1f696.png diff --git a/app/assets/images/emoji/unicode/1f697.png b/public/images/emoji/unicode/1f697.png similarity index 100% rename from app/assets/images/emoji/unicode/1f697.png rename to public/images/emoji/unicode/1f697.png diff --git a/app/assets/images/emoji/unicode/1f698.png b/public/images/emoji/unicode/1f698.png similarity index 100% rename from app/assets/images/emoji/unicode/1f698.png rename to public/images/emoji/unicode/1f698.png diff --git a/app/assets/images/emoji/unicode/1f699.png b/public/images/emoji/unicode/1f699.png similarity index 100% rename from app/assets/images/emoji/unicode/1f699.png rename to public/images/emoji/unicode/1f699.png diff --git a/app/assets/images/emoji/unicode/1f69a.png b/public/images/emoji/unicode/1f69a.png similarity index 100% rename from app/assets/images/emoji/unicode/1f69a.png rename to public/images/emoji/unicode/1f69a.png diff --git a/app/assets/images/emoji/unicode/1f69b.png b/public/images/emoji/unicode/1f69b.png similarity index 100% rename from app/assets/images/emoji/unicode/1f69b.png rename to public/images/emoji/unicode/1f69b.png diff --git a/app/assets/images/emoji/unicode/1f69c.png b/public/images/emoji/unicode/1f69c.png similarity index 100% rename from app/assets/images/emoji/unicode/1f69c.png rename to public/images/emoji/unicode/1f69c.png diff --git a/app/assets/images/emoji/unicode/1f69d.png b/public/images/emoji/unicode/1f69d.png similarity index 100% rename from app/assets/images/emoji/unicode/1f69d.png rename to public/images/emoji/unicode/1f69d.png diff --git a/app/assets/images/emoji/unicode/1f69e.png b/public/images/emoji/unicode/1f69e.png similarity index 100% rename from app/assets/images/emoji/unicode/1f69e.png rename to public/images/emoji/unicode/1f69e.png diff --git a/app/assets/images/emoji/unicode/1f69f.png b/public/images/emoji/unicode/1f69f.png similarity index 100% rename from app/assets/images/emoji/unicode/1f69f.png rename to public/images/emoji/unicode/1f69f.png diff --git a/app/assets/images/emoji/unicode/1f6a0.png b/public/images/emoji/unicode/1f6a0.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6a0.png rename to public/images/emoji/unicode/1f6a0.png diff --git a/app/assets/images/emoji/unicode/1f6a1.png b/public/images/emoji/unicode/1f6a1.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6a1.png rename to public/images/emoji/unicode/1f6a1.png diff --git a/app/assets/images/emoji/unicode/1f6a2.png b/public/images/emoji/unicode/1f6a2.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6a2.png rename to public/images/emoji/unicode/1f6a2.png diff --git a/app/assets/images/emoji/unicode/1f6a3.png b/public/images/emoji/unicode/1f6a3.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6a3.png rename to public/images/emoji/unicode/1f6a3.png diff --git a/app/assets/images/emoji/unicode/1f6a4.png b/public/images/emoji/unicode/1f6a4.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6a4.png rename to public/images/emoji/unicode/1f6a4.png diff --git a/app/assets/images/emoji/unicode/1f6a5.png b/public/images/emoji/unicode/1f6a5.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6a5.png rename to public/images/emoji/unicode/1f6a5.png diff --git a/app/assets/images/emoji/unicode/1f6a6.png b/public/images/emoji/unicode/1f6a6.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6a6.png rename to public/images/emoji/unicode/1f6a6.png diff --git a/app/assets/images/emoji/unicode/1f6a7.png b/public/images/emoji/unicode/1f6a7.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6a7.png rename to public/images/emoji/unicode/1f6a7.png diff --git a/app/assets/images/emoji/unicode/1f6a8.png b/public/images/emoji/unicode/1f6a8.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6a8.png rename to public/images/emoji/unicode/1f6a8.png diff --git a/app/assets/images/emoji/unicode/1f6a9.png b/public/images/emoji/unicode/1f6a9.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6a9.png rename to public/images/emoji/unicode/1f6a9.png diff --git a/app/assets/images/emoji/unicode/1f6aa.png b/public/images/emoji/unicode/1f6aa.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6aa.png rename to public/images/emoji/unicode/1f6aa.png diff --git a/app/assets/images/emoji/unicode/1f6ab.png b/public/images/emoji/unicode/1f6ab.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6ab.png rename to public/images/emoji/unicode/1f6ab.png diff --git a/app/assets/images/emoji/unicode/1f6ac.png b/public/images/emoji/unicode/1f6ac.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6ac.png rename to public/images/emoji/unicode/1f6ac.png diff --git a/app/assets/images/emoji/unicode/1f6ad.png b/public/images/emoji/unicode/1f6ad.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6ad.png rename to public/images/emoji/unicode/1f6ad.png diff --git a/app/assets/images/emoji/unicode/1f6ae.png b/public/images/emoji/unicode/1f6ae.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6ae.png rename to public/images/emoji/unicode/1f6ae.png diff --git a/app/assets/images/emoji/unicode/1f6af.png b/public/images/emoji/unicode/1f6af.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6af.png rename to public/images/emoji/unicode/1f6af.png diff --git a/app/assets/images/emoji/unicode/1f6b0.png b/public/images/emoji/unicode/1f6b0.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6b0.png rename to public/images/emoji/unicode/1f6b0.png diff --git a/app/assets/images/emoji/unicode/1f6b1.png b/public/images/emoji/unicode/1f6b1.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6b1.png rename to public/images/emoji/unicode/1f6b1.png diff --git a/app/assets/images/emoji/unicode/1f6b2.png b/public/images/emoji/unicode/1f6b2.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6b2.png rename to public/images/emoji/unicode/1f6b2.png diff --git a/app/assets/images/emoji/unicode/1f6b3.png b/public/images/emoji/unicode/1f6b3.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6b3.png rename to public/images/emoji/unicode/1f6b3.png diff --git a/app/assets/images/emoji/unicode/1f6b4.png b/public/images/emoji/unicode/1f6b4.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6b4.png rename to public/images/emoji/unicode/1f6b4.png diff --git a/app/assets/images/emoji/unicode/1f6b5.png b/public/images/emoji/unicode/1f6b5.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6b5.png rename to public/images/emoji/unicode/1f6b5.png diff --git a/app/assets/images/emoji/unicode/1f6b6.png b/public/images/emoji/unicode/1f6b6.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6b6.png rename to public/images/emoji/unicode/1f6b6.png diff --git a/app/assets/images/emoji/unicode/1f6b7.png b/public/images/emoji/unicode/1f6b7.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6b7.png rename to public/images/emoji/unicode/1f6b7.png diff --git a/app/assets/images/emoji/unicode/1f6b8.png b/public/images/emoji/unicode/1f6b8.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6b8.png rename to public/images/emoji/unicode/1f6b8.png diff --git a/app/assets/images/emoji/unicode/1f6b9.png b/public/images/emoji/unicode/1f6b9.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6b9.png rename to public/images/emoji/unicode/1f6b9.png diff --git a/app/assets/images/emoji/unicode/1f6ba.png b/public/images/emoji/unicode/1f6ba.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6ba.png rename to public/images/emoji/unicode/1f6ba.png diff --git a/app/assets/images/emoji/unicode/1f6bb.png b/public/images/emoji/unicode/1f6bb.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6bb.png rename to public/images/emoji/unicode/1f6bb.png diff --git a/app/assets/images/emoji/unicode/1f6bc.png b/public/images/emoji/unicode/1f6bc.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6bc.png rename to public/images/emoji/unicode/1f6bc.png diff --git a/app/assets/images/emoji/unicode/1f6bd.png b/public/images/emoji/unicode/1f6bd.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6bd.png rename to public/images/emoji/unicode/1f6bd.png diff --git a/app/assets/images/emoji/unicode/1f6be.png b/public/images/emoji/unicode/1f6be.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6be.png rename to public/images/emoji/unicode/1f6be.png diff --git a/app/assets/images/emoji/unicode/1f6bf.png b/public/images/emoji/unicode/1f6bf.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6bf.png rename to public/images/emoji/unicode/1f6bf.png diff --git a/app/assets/images/emoji/unicode/1f6c0.png b/public/images/emoji/unicode/1f6c0.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6c0.png rename to public/images/emoji/unicode/1f6c0.png diff --git a/app/assets/images/emoji/unicode/1f6c1.png b/public/images/emoji/unicode/1f6c1.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6c1.png rename to public/images/emoji/unicode/1f6c1.png diff --git a/app/assets/images/emoji/unicode/1f6c2.png b/public/images/emoji/unicode/1f6c2.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6c2.png rename to public/images/emoji/unicode/1f6c2.png diff --git a/app/assets/images/emoji/unicode/1f6c3.png b/public/images/emoji/unicode/1f6c3.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6c3.png rename to public/images/emoji/unicode/1f6c3.png diff --git a/app/assets/images/emoji/unicode/1f6c4.png b/public/images/emoji/unicode/1f6c4.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6c4.png rename to public/images/emoji/unicode/1f6c4.png diff --git a/app/assets/images/emoji/unicode/1f6c5.png b/public/images/emoji/unicode/1f6c5.png similarity index 100% rename from app/assets/images/emoji/unicode/1f6c5.png rename to public/images/emoji/unicode/1f6c5.png diff --git a/app/assets/images/emoji/unicode/203c.png b/public/images/emoji/unicode/203c.png similarity index 100% rename from app/assets/images/emoji/unicode/203c.png rename to public/images/emoji/unicode/203c.png diff --git a/app/assets/images/emoji/unicode/2049.png b/public/images/emoji/unicode/2049.png similarity index 100% rename from app/assets/images/emoji/unicode/2049.png rename to public/images/emoji/unicode/2049.png diff --git a/app/assets/images/emoji/unicode/2122.png b/public/images/emoji/unicode/2122.png similarity index 100% rename from app/assets/images/emoji/unicode/2122.png rename to public/images/emoji/unicode/2122.png diff --git a/app/assets/images/emoji/unicode/2139.png b/public/images/emoji/unicode/2139.png similarity index 100% rename from app/assets/images/emoji/unicode/2139.png rename to public/images/emoji/unicode/2139.png diff --git a/app/assets/images/emoji/unicode/2194.png b/public/images/emoji/unicode/2194.png similarity index 100% rename from app/assets/images/emoji/unicode/2194.png rename to public/images/emoji/unicode/2194.png diff --git a/app/assets/images/emoji/unicode/2195.png b/public/images/emoji/unicode/2195.png similarity index 100% rename from app/assets/images/emoji/unicode/2195.png rename to public/images/emoji/unicode/2195.png diff --git a/app/assets/images/emoji/unicode/2196.png b/public/images/emoji/unicode/2196.png similarity index 100% rename from app/assets/images/emoji/unicode/2196.png rename to public/images/emoji/unicode/2196.png diff --git a/app/assets/images/emoji/unicode/2197.png b/public/images/emoji/unicode/2197.png similarity index 100% rename from app/assets/images/emoji/unicode/2197.png rename to public/images/emoji/unicode/2197.png diff --git a/app/assets/images/emoji/unicode/2198.png b/public/images/emoji/unicode/2198.png similarity index 100% rename from app/assets/images/emoji/unicode/2198.png rename to public/images/emoji/unicode/2198.png diff --git a/app/assets/images/emoji/unicode/2199.png b/public/images/emoji/unicode/2199.png similarity index 100% rename from app/assets/images/emoji/unicode/2199.png rename to public/images/emoji/unicode/2199.png diff --git a/app/assets/images/emoji/unicode/21a9.png b/public/images/emoji/unicode/21a9.png similarity index 100% rename from app/assets/images/emoji/unicode/21a9.png rename to public/images/emoji/unicode/21a9.png diff --git a/app/assets/images/emoji/unicode/21aa.png b/public/images/emoji/unicode/21aa.png similarity index 100% rename from app/assets/images/emoji/unicode/21aa.png rename to public/images/emoji/unicode/21aa.png diff --git a/app/assets/images/emoji/unicode/231a.png b/public/images/emoji/unicode/231a.png similarity index 100% rename from app/assets/images/emoji/unicode/231a.png rename to public/images/emoji/unicode/231a.png diff --git a/app/assets/images/emoji/unicode/231b.png b/public/images/emoji/unicode/231b.png similarity index 100% rename from app/assets/images/emoji/unicode/231b.png rename to public/images/emoji/unicode/231b.png diff --git a/app/assets/images/emoji/unicode/23e9.png b/public/images/emoji/unicode/23e9.png similarity index 100% rename from app/assets/images/emoji/unicode/23e9.png rename to public/images/emoji/unicode/23e9.png diff --git a/app/assets/images/emoji/unicode/23ea.png b/public/images/emoji/unicode/23ea.png similarity index 100% rename from app/assets/images/emoji/unicode/23ea.png rename to public/images/emoji/unicode/23ea.png diff --git a/app/assets/images/emoji/unicode/23eb.png b/public/images/emoji/unicode/23eb.png similarity index 100% rename from app/assets/images/emoji/unicode/23eb.png rename to public/images/emoji/unicode/23eb.png diff --git a/app/assets/images/emoji/unicode/23ec.png b/public/images/emoji/unicode/23ec.png similarity index 100% rename from app/assets/images/emoji/unicode/23ec.png rename to public/images/emoji/unicode/23ec.png diff --git a/app/assets/images/emoji/unicode/23f0.png b/public/images/emoji/unicode/23f0.png similarity index 100% rename from app/assets/images/emoji/unicode/23f0.png rename to public/images/emoji/unicode/23f0.png diff --git a/app/assets/images/emoji/unicode/23f3.png b/public/images/emoji/unicode/23f3.png similarity index 100% rename from app/assets/images/emoji/unicode/23f3.png rename to public/images/emoji/unicode/23f3.png diff --git a/app/assets/images/emoji/unicode/24c2.png b/public/images/emoji/unicode/24c2.png similarity index 100% rename from app/assets/images/emoji/unicode/24c2.png rename to public/images/emoji/unicode/24c2.png diff --git a/app/assets/images/emoji/unicode/25aa.png b/public/images/emoji/unicode/25aa.png similarity index 100% rename from app/assets/images/emoji/unicode/25aa.png rename to public/images/emoji/unicode/25aa.png diff --git a/app/assets/images/emoji/unicode/25ab.png b/public/images/emoji/unicode/25ab.png similarity index 100% rename from app/assets/images/emoji/unicode/25ab.png rename to public/images/emoji/unicode/25ab.png diff --git a/app/assets/images/emoji/unicode/25b6.png b/public/images/emoji/unicode/25b6.png similarity index 100% rename from app/assets/images/emoji/unicode/25b6.png rename to public/images/emoji/unicode/25b6.png diff --git a/app/assets/images/emoji/unicode/25c0.png b/public/images/emoji/unicode/25c0.png similarity index 100% rename from app/assets/images/emoji/unicode/25c0.png rename to public/images/emoji/unicode/25c0.png diff --git a/app/assets/images/emoji/unicode/25fb.png b/public/images/emoji/unicode/25fb.png similarity index 100% rename from app/assets/images/emoji/unicode/25fb.png rename to public/images/emoji/unicode/25fb.png diff --git a/app/assets/images/emoji/unicode/25fc.png b/public/images/emoji/unicode/25fc.png similarity index 100% rename from app/assets/images/emoji/unicode/25fc.png rename to public/images/emoji/unicode/25fc.png diff --git a/app/assets/images/emoji/unicode/25fd.png b/public/images/emoji/unicode/25fd.png similarity index 100% rename from app/assets/images/emoji/unicode/25fd.png rename to public/images/emoji/unicode/25fd.png diff --git a/app/assets/images/emoji/unicode/25fe.png b/public/images/emoji/unicode/25fe.png similarity index 100% rename from app/assets/images/emoji/unicode/25fe.png rename to public/images/emoji/unicode/25fe.png diff --git a/app/assets/images/emoji/unicode/2600.png b/public/images/emoji/unicode/2600.png similarity index 100% rename from app/assets/images/emoji/unicode/2600.png rename to public/images/emoji/unicode/2600.png diff --git a/app/assets/images/emoji/unicode/2601.png b/public/images/emoji/unicode/2601.png similarity index 100% rename from app/assets/images/emoji/unicode/2601.png rename to public/images/emoji/unicode/2601.png diff --git a/app/assets/images/emoji/unicode/260e.png b/public/images/emoji/unicode/260e.png similarity index 100% rename from app/assets/images/emoji/unicode/260e.png rename to public/images/emoji/unicode/260e.png diff --git a/app/assets/images/emoji/unicode/2611.png b/public/images/emoji/unicode/2611.png similarity index 100% rename from app/assets/images/emoji/unicode/2611.png rename to public/images/emoji/unicode/2611.png diff --git a/app/assets/images/emoji/unicode/2614.png b/public/images/emoji/unicode/2614.png similarity index 100% rename from app/assets/images/emoji/unicode/2614.png rename to public/images/emoji/unicode/2614.png diff --git a/app/assets/images/emoji/unicode/2615.png b/public/images/emoji/unicode/2615.png similarity index 100% rename from app/assets/images/emoji/unicode/2615.png rename to public/images/emoji/unicode/2615.png diff --git a/app/assets/images/emoji/unicode/261d.png b/public/images/emoji/unicode/261d.png similarity index 100% rename from app/assets/images/emoji/unicode/261d.png rename to public/images/emoji/unicode/261d.png diff --git a/app/assets/images/emoji/unicode/263a.png b/public/images/emoji/unicode/263a.png similarity index 100% rename from app/assets/images/emoji/unicode/263a.png rename to public/images/emoji/unicode/263a.png diff --git a/app/assets/images/emoji/unicode/2648.png b/public/images/emoji/unicode/2648.png similarity index 100% rename from app/assets/images/emoji/unicode/2648.png rename to public/images/emoji/unicode/2648.png diff --git a/app/assets/images/emoji/unicode/2649.png b/public/images/emoji/unicode/2649.png similarity index 100% rename from app/assets/images/emoji/unicode/2649.png rename to public/images/emoji/unicode/2649.png diff --git a/app/assets/images/emoji/unicode/264a.png b/public/images/emoji/unicode/264a.png similarity index 100% rename from app/assets/images/emoji/unicode/264a.png rename to public/images/emoji/unicode/264a.png diff --git a/app/assets/images/emoji/unicode/264b.png b/public/images/emoji/unicode/264b.png similarity index 100% rename from app/assets/images/emoji/unicode/264b.png rename to public/images/emoji/unicode/264b.png diff --git a/app/assets/images/emoji/unicode/264c.png b/public/images/emoji/unicode/264c.png similarity index 100% rename from app/assets/images/emoji/unicode/264c.png rename to public/images/emoji/unicode/264c.png diff --git a/app/assets/images/emoji/unicode/264d.png b/public/images/emoji/unicode/264d.png similarity index 100% rename from app/assets/images/emoji/unicode/264d.png rename to public/images/emoji/unicode/264d.png diff --git a/app/assets/images/emoji/unicode/264e.png b/public/images/emoji/unicode/264e.png similarity index 100% rename from app/assets/images/emoji/unicode/264e.png rename to public/images/emoji/unicode/264e.png diff --git a/app/assets/images/emoji/unicode/264f.png b/public/images/emoji/unicode/264f.png similarity index 100% rename from app/assets/images/emoji/unicode/264f.png rename to public/images/emoji/unicode/264f.png diff --git a/app/assets/images/emoji/unicode/2650.png b/public/images/emoji/unicode/2650.png similarity index 100% rename from app/assets/images/emoji/unicode/2650.png rename to public/images/emoji/unicode/2650.png diff --git a/app/assets/images/emoji/unicode/2651.png b/public/images/emoji/unicode/2651.png similarity index 100% rename from app/assets/images/emoji/unicode/2651.png rename to public/images/emoji/unicode/2651.png diff --git a/app/assets/images/emoji/unicode/2652.png b/public/images/emoji/unicode/2652.png similarity index 100% rename from app/assets/images/emoji/unicode/2652.png rename to public/images/emoji/unicode/2652.png diff --git a/app/assets/images/emoji/unicode/2653.png b/public/images/emoji/unicode/2653.png similarity index 100% rename from app/assets/images/emoji/unicode/2653.png rename to public/images/emoji/unicode/2653.png diff --git a/app/assets/images/emoji/unicode/2660.png b/public/images/emoji/unicode/2660.png similarity index 100% rename from app/assets/images/emoji/unicode/2660.png rename to public/images/emoji/unicode/2660.png diff --git a/app/assets/images/emoji/unicode/2663.png b/public/images/emoji/unicode/2663.png similarity index 100% rename from app/assets/images/emoji/unicode/2663.png rename to public/images/emoji/unicode/2663.png diff --git a/app/assets/images/emoji/unicode/2665.png b/public/images/emoji/unicode/2665.png similarity index 100% rename from app/assets/images/emoji/unicode/2665.png rename to public/images/emoji/unicode/2665.png diff --git a/app/assets/images/emoji/unicode/2666.png b/public/images/emoji/unicode/2666.png similarity index 100% rename from app/assets/images/emoji/unicode/2666.png rename to public/images/emoji/unicode/2666.png diff --git a/app/assets/images/emoji/unicode/2668.png b/public/images/emoji/unicode/2668.png similarity index 100% rename from app/assets/images/emoji/unicode/2668.png rename to public/images/emoji/unicode/2668.png diff --git a/app/assets/images/emoji/unicode/267b.png b/public/images/emoji/unicode/267b.png similarity index 100% rename from app/assets/images/emoji/unicode/267b.png rename to public/images/emoji/unicode/267b.png diff --git a/app/assets/images/emoji/unicode/267f.png b/public/images/emoji/unicode/267f.png similarity index 100% rename from app/assets/images/emoji/unicode/267f.png rename to public/images/emoji/unicode/267f.png diff --git a/app/assets/images/emoji/unicode/2693.png b/public/images/emoji/unicode/2693.png similarity index 100% rename from app/assets/images/emoji/unicode/2693.png rename to public/images/emoji/unicode/2693.png diff --git a/app/assets/images/emoji/unicode/26a0.png b/public/images/emoji/unicode/26a0.png similarity index 100% rename from app/assets/images/emoji/unicode/26a0.png rename to public/images/emoji/unicode/26a0.png diff --git a/app/assets/images/emoji/unicode/26a1.png b/public/images/emoji/unicode/26a1.png similarity index 100% rename from app/assets/images/emoji/unicode/26a1.png rename to public/images/emoji/unicode/26a1.png diff --git a/app/assets/images/emoji/unicode/26aa.png b/public/images/emoji/unicode/26aa.png similarity index 100% rename from app/assets/images/emoji/unicode/26aa.png rename to public/images/emoji/unicode/26aa.png diff --git a/app/assets/images/emoji/unicode/26ab.png b/public/images/emoji/unicode/26ab.png similarity index 100% rename from app/assets/images/emoji/unicode/26ab.png rename to public/images/emoji/unicode/26ab.png diff --git a/app/assets/images/emoji/unicode/26bd.png b/public/images/emoji/unicode/26bd.png similarity index 100% rename from app/assets/images/emoji/unicode/26bd.png rename to public/images/emoji/unicode/26bd.png diff --git a/app/assets/images/emoji/unicode/26be.png b/public/images/emoji/unicode/26be.png similarity index 100% rename from app/assets/images/emoji/unicode/26be.png rename to public/images/emoji/unicode/26be.png diff --git a/app/assets/images/emoji/unicode/26c4.png b/public/images/emoji/unicode/26c4.png similarity index 100% rename from app/assets/images/emoji/unicode/26c4.png rename to public/images/emoji/unicode/26c4.png diff --git a/app/assets/images/emoji/unicode/26c5.png b/public/images/emoji/unicode/26c5.png similarity index 100% rename from app/assets/images/emoji/unicode/26c5.png rename to public/images/emoji/unicode/26c5.png diff --git a/app/assets/images/emoji/unicode/26ce.png b/public/images/emoji/unicode/26ce.png similarity index 100% rename from app/assets/images/emoji/unicode/26ce.png rename to public/images/emoji/unicode/26ce.png diff --git a/app/assets/images/emoji/unicode/26d4.png b/public/images/emoji/unicode/26d4.png similarity index 100% rename from app/assets/images/emoji/unicode/26d4.png rename to public/images/emoji/unicode/26d4.png diff --git a/app/assets/images/emoji/unicode/26ea.png b/public/images/emoji/unicode/26ea.png similarity index 100% rename from app/assets/images/emoji/unicode/26ea.png rename to public/images/emoji/unicode/26ea.png diff --git a/app/assets/images/emoji/unicode/26f2.png b/public/images/emoji/unicode/26f2.png similarity index 100% rename from app/assets/images/emoji/unicode/26f2.png rename to public/images/emoji/unicode/26f2.png diff --git a/app/assets/images/emoji/unicode/26f3.png b/public/images/emoji/unicode/26f3.png similarity index 100% rename from app/assets/images/emoji/unicode/26f3.png rename to public/images/emoji/unicode/26f3.png diff --git a/app/assets/images/emoji/unicode/26f5.png b/public/images/emoji/unicode/26f5.png similarity index 100% rename from app/assets/images/emoji/unicode/26f5.png rename to public/images/emoji/unicode/26f5.png diff --git a/app/assets/images/emoji/unicode/26fa.png b/public/images/emoji/unicode/26fa.png similarity index 100% rename from app/assets/images/emoji/unicode/26fa.png rename to public/images/emoji/unicode/26fa.png diff --git a/app/assets/images/emoji/unicode/26fd.png b/public/images/emoji/unicode/26fd.png similarity index 100% rename from app/assets/images/emoji/unicode/26fd.png rename to public/images/emoji/unicode/26fd.png diff --git a/app/assets/images/emoji/unicode/2702.png b/public/images/emoji/unicode/2702.png similarity index 100% rename from app/assets/images/emoji/unicode/2702.png rename to public/images/emoji/unicode/2702.png diff --git a/app/assets/images/emoji/unicode/2705.png b/public/images/emoji/unicode/2705.png similarity index 100% rename from app/assets/images/emoji/unicode/2705.png rename to public/images/emoji/unicode/2705.png diff --git a/app/assets/images/emoji/unicode/2708.png b/public/images/emoji/unicode/2708.png similarity index 100% rename from app/assets/images/emoji/unicode/2708.png rename to public/images/emoji/unicode/2708.png diff --git a/app/assets/images/emoji/unicode/2709.png b/public/images/emoji/unicode/2709.png similarity index 100% rename from app/assets/images/emoji/unicode/2709.png rename to public/images/emoji/unicode/2709.png diff --git a/app/assets/images/emoji/unicode/270a.png b/public/images/emoji/unicode/270a.png similarity index 100% rename from app/assets/images/emoji/unicode/270a.png rename to public/images/emoji/unicode/270a.png diff --git a/app/assets/images/emoji/unicode/270b.png b/public/images/emoji/unicode/270b.png similarity index 100% rename from app/assets/images/emoji/unicode/270b.png rename to public/images/emoji/unicode/270b.png diff --git a/app/assets/images/emoji/unicode/270c.png b/public/images/emoji/unicode/270c.png similarity index 100% rename from app/assets/images/emoji/unicode/270c.png rename to public/images/emoji/unicode/270c.png diff --git a/app/assets/images/emoji/unicode/270f.png b/public/images/emoji/unicode/270f.png similarity index 100% rename from app/assets/images/emoji/unicode/270f.png rename to public/images/emoji/unicode/270f.png diff --git a/app/assets/images/emoji/unicode/2712.png b/public/images/emoji/unicode/2712.png similarity index 100% rename from app/assets/images/emoji/unicode/2712.png rename to public/images/emoji/unicode/2712.png diff --git a/app/assets/images/emoji/unicode/2714.png b/public/images/emoji/unicode/2714.png similarity index 100% rename from app/assets/images/emoji/unicode/2714.png rename to public/images/emoji/unicode/2714.png diff --git a/app/assets/images/emoji/unicode/2716.png b/public/images/emoji/unicode/2716.png similarity index 100% rename from app/assets/images/emoji/unicode/2716.png rename to public/images/emoji/unicode/2716.png diff --git a/app/assets/images/emoji/unicode/2728.png b/public/images/emoji/unicode/2728.png similarity index 100% rename from app/assets/images/emoji/unicode/2728.png rename to public/images/emoji/unicode/2728.png diff --git a/app/assets/images/emoji/unicode/2733.png b/public/images/emoji/unicode/2733.png similarity index 100% rename from app/assets/images/emoji/unicode/2733.png rename to public/images/emoji/unicode/2733.png diff --git a/app/assets/images/emoji/unicode/2734.png b/public/images/emoji/unicode/2734.png similarity index 100% rename from app/assets/images/emoji/unicode/2734.png rename to public/images/emoji/unicode/2734.png diff --git a/app/assets/images/emoji/unicode/2744.png b/public/images/emoji/unicode/2744.png similarity index 100% rename from app/assets/images/emoji/unicode/2744.png rename to public/images/emoji/unicode/2744.png diff --git a/app/assets/images/emoji/unicode/2747.png b/public/images/emoji/unicode/2747.png similarity index 100% rename from app/assets/images/emoji/unicode/2747.png rename to public/images/emoji/unicode/2747.png diff --git a/app/assets/images/emoji/unicode/274c.png b/public/images/emoji/unicode/274c.png similarity index 100% rename from app/assets/images/emoji/unicode/274c.png rename to public/images/emoji/unicode/274c.png diff --git a/app/assets/images/emoji/unicode/274e.png b/public/images/emoji/unicode/274e.png similarity index 100% rename from app/assets/images/emoji/unicode/274e.png rename to public/images/emoji/unicode/274e.png diff --git a/app/assets/images/emoji/unicode/2753.png b/public/images/emoji/unicode/2753.png similarity index 100% rename from app/assets/images/emoji/unicode/2753.png rename to public/images/emoji/unicode/2753.png diff --git a/app/assets/images/emoji/unicode/2754.png b/public/images/emoji/unicode/2754.png similarity index 100% rename from app/assets/images/emoji/unicode/2754.png rename to public/images/emoji/unicode/2754.png diff --git a/app/assets/images/emoji/unicode/2755.png b/public/images/emoji/unicode/2755.png similarity index 100% rename from app/assets/images/emoji/unicode/2755.png rename to public/images/emoji/unicode/2755.png diff --git a/app/assets/images/emoji/unicode/2757.png b/public/images/emoji/unicode/2757.png similarity index 100% rename from app/assets/images/emoji/unicode/2757.png rename to public/images/emoji/unicode/2757.png diff --git a/app/assets/images/emoji/unicode/2764.png b/public/images/emoji/unicode/2764.png similarity index 100% rename from app/assets/images/emoji/unicode/2764.png rename to public/images/emoji/unicode/2764.png diff --git a/app/assets/images/emoji/unicode/2795.png b/public/images/emoji/unicode/2795.png similarity index 100% rename from app/assets/images/emoji/unicode/2795.png rename to public/images/emoji/unicode/2795.png diff --git a/app/assets/images/emoji/unicode/2796.png b/public/images/emoji/unicode/2796.png similarity index 100% rename from app/assets/images/emoji/unicode/2796.png rename to public/images/emoji/unicode/2796.png diff --git a/app/assets/images/emoji/unicode/2797.png b/public/images/emoji/unicode/2797.png similarity index 100% rename from app/assets/images/emoji/unicode/2797.png rename to public/images/emoji/unicode/2797.png diff --git a/app/assets/images/emoji/unicode/27a1.png b/public/images/emoji/unicode/27a1.png similarity index 100% rename from app/assets/images/emoji/unicode/27a1.png rename to public/images/emoji/unicode/27a1.png diff --git a/app/assets/images/emoji/unicode/27b0.png b/public/images/emoji/unicode/27b0.png similarity index 100% rename from app/assets/images/emoji/unicode/27b0.png rename to public/images/emoji/unicode/27b0.png diff --git a/app/assets/images/emoji/unicode/27bf.png b/public/images/emoji/unicode/27bf.png similarity index 100% rename from app/assets/images/emoji/unicode/27bf.png rename to public/images/emoji/unicode/27bf.png diff --git a/app/assets/images/emoji/unicode/2934.png b/public/images/emoji/unicode/2934.png similarity index 100% rename from app/assets/images/emoji/unicode/2934.png rename to public/images/emoji/unicode/2934.png diff --git a/app/assets/images/emoji/unicode/2935.png b/public/images/emoji/unicode/2935.png similarity index 100% rename from app/assets/images/emoji/unicode/2935.png rename to public/images/emoji/unicode/2935.png diff --git a/app/assets/images/emoji/unicode/2b05.png b/public/images/emoji/unicode/2b05.png similarity index 100% rename from app/assets/images/emoji/unicode/2b05.png rename to public/images/emoji/unicode/2b05.png diff --git a/app/assets/images/emoji/unicode/2b06.png b/public/images/emoji/unicode/2b06.png similarity index 100% rename from app/assets/images/emoji/unicode/2b06.png rename to public/images/emoji/unicode/2b06.png diff --git a/app/assets/images/emoji/unicode/2b07.png b/public/images/emoji/unicode/2b07.png similarity index 100% rename from app/assets/images/emoji/unicode/2b07.png rename to public/images/emoji/unicode/2b07.png diff --git a/app/assets/images/emoji/unicode/2b1b.png b/public/images/emoji/unicode/2b1b.png similarity index 100% rename from app/assets/images/emoji/unicode/2b1b.png rename to public/images/emoji/unicode/2b1b.png diff --git a/app/assets/images/emoji/unicode/2b1c.png b/public/images/emoji/unicode/2b1c.png similarity index 100% rename from app/assets/images/emoji/unicode/2b1c.png rename to public/images/emoji/unicode/2b1c.png diff --git a/app/assets/images/emoji/unicode/2b50.png b/public/images/emoji/unicode/2b50.png similarity index 100% rename from app/assets/images/emoji/unicode/2b50.png rename to public/images/emoji/unicode/2b50.png diff --git a/app/assets/images/emoji/unicode/2b55.png b/public/images/emoji/unicode/2b55.png similarity index 100% rename from app/assets/images/emoji/unicode/2b55.png rename to public/images/emoji/unicode/2b55.png diff --git a/app/assets/images/emoji/unicode/3030.png b/public/images/emoji/unicode/3030.png similarity index 100% rename from app/assets/images/emoji/unicode/3030.png rename to public/images/emoji/unicode/3030.png diff --git a/app/assets/images/emoji/unicode/303d.png b/public/images/emoji/unicode/303d.png similarity index 100% rename from app/assets/images/emoji/unicode/303d.png rename to public/images/emoji/unicode/303d.png diff --git a/app/assets/images/emoji/unicode/3297.png b/public/images/emoji/unicode/3297.png similarity index 100% rename from app/assets/images/emoji/unicode/3297.png rename to public/images/emoji/unicode/3297.png diff --git a/app/assets/images/emoji/unicode/3299.png b/public/images/emoji/unicode/3299.png similarity index 100% rename from app/assets/images/emoji/unicode/3299.png rename to public/images/emoji/unicode/3299.png diff --git a/public/metadatas/mdv/MD5SUM b/public/metadatas/mdv/MD5SUM new file mode 100644 index 000000000..5945ea5b1 --- /dev/null +++ b/public/metadatas/mdv/MD5SUM @@ -0,0 +1 @@ +145c9f725bdcc3ba2f7955b102bce09b synthesis.hdlist.cz diff --git a/public/metadatas/mdv/synthesis.hdlist.cz b/public/metadatas/mdv/synthesis.hdlist.cz new file mode 100644 index 000000000..5b1c7aae8 Binary files /dev/null and b/public/metadatas/mdv/synthesis.hdlist.cz differ diff --git a/public/metadatas/rhel/31ad3f2fbe7608dea8c8c7952373ca917bfe00fc729e1f4ecb29fb43c2064f1d-filelists.xml.gz b/public/metadatas/rhel/31ad3f2fbe7608dea8c8c7952373ca917bfe00fc729e1f4ecb29fb43c2064f1d-filelists.xml.gz new file mode 100644 index 000000000..2c33f0733 Binary files /dev/null and b/public/metadatas/rhel/31ad3f2fbe7608dea8c8c7952373ca917bfe00fc729e1f4ecb29fb43c2064f1d-filelists.xml.gz differ diff --git a/public/metadatas/rhel/a44e7259aa2c562a139498175f1fc64f8e98d3fda7fa22444b4121cd6e246fb7-primary.xml.gz b/public/metadatas/rhel/a44e7259aa2c562a139498175f1fc64f8e98d3fda7fa22444b4121cd6e246fb7-primary.xml.gz new file mode 100644 index 000000000..2d39f07ee Binary files /dev/null and b/public/metadatas/rhel/a44e7259aa2c562a139498175f1fc64f8e98d3fda7fa22444b4121cd6e246fb7-primary.xml.gz differ diff --git a/public/metadatas/rhel/a9769a64dee317b6b67923a4f6c173419e43720cb60f6411d5525382df28605f-other.xml.gz b/public/metadatas/rhel/a9769a64dee317b6b67923a4f6c173419e43720cb60f6411d5525382df28605f-other.xml.gz new file mode 100644 index 000000000..2eb010868 Binary files /dev/null and b/public/metadatas/rhel/a9769a64dee317b6b67923a4f6c173419e43720cb60f6411d5525382df28605f-other.xml.gz differ diff --git a/public/metadatas/rhel/repomd.xml b/public/metadatas/rhel/repomd.xml new file mode 100644 index 000000000..96b5f894a --- /dev/null +++ b/public/metadatas/rhel/repomd.xml @@ -0,0 +1,28 @@ + + + 1412059094 + + 31ad3f2fbe7608dea8c8c7952373ca917bfe00fc729e1f4ecb29fb43c2064f1d + bf9808b81cb2dbc54b4b8e35adc584ddcaa73bd81f7088d73bf7dbbada961310 + + 1412059094 + 186 + 125 + + + a9769a64dee317b6b67923a4f6c173419e43720cb60f6411d5525382df28605f + e0ed5e0054194df036cf09c1a911e15bf2a4e7f26f2a788b6f47d53e80717ccc + + 1412059094 + 182 + 121 + + + a44e7259aa2c562a139498175f1fc64f8e98d3fda7fa22444b4121cd6e246fb7 + e1e2ffd2fb1ee76f87b70750d00ca5677a252b397ab6c2389137a0c33e7b359f + + 1412059094 + 195 + 167 + + diff --git a/spec/controllers/api/v1/issues_controller_spec.rb b/spec/controllers/api/v1/issues_controller_spec.rb index a70ff7873..ec2b5cd05 100644 --- a/spec/controllers/api/v1/issues_controller_spec.rb +++ b/spec/controllers/api/v1/issues_controller_spec.rb @@ -139,13 +139,13 @@ describe Api::V1::IssuesController do 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 + post :create, @create_params.deep_merge(project_id: @own_hidden_project.id, issue: {assignee_id: @issue.user.id}) + @own_hidden_project.issues.order(:id).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 + @open_project.issues.order(:id).last.assignee.should be_nil end end diff --git a/spec/controllers/platforms/contents_controller_spec.rb b/spec/controllers/platforms/contents_controller_spec.rb index e8b1c1454..07b740ba7 100644 --- a/spec/controllers/platforms/contents_controller_spec.rb +++ b/spec/controllers/platforms/contents_controller_spec.rb @@ -28,7 +28,7 @@ shared_examples_for 'content platform user with show rights' do end end -shared_examples_for 'content platform user without owner rights' do +shared_examples_for 'content platform user without member rights' do it 'should not be able to perform remove_file action for main platform' do get :remove_file, platform_id: @platform, path: '/test' response.should_not be_success @@ -40,7 +40,7 @@ shared_examples_for 'content platform user without owner rights' do end end -shared_examples_for 'content platform user with owner rights' do +shared_examples_for 'content platform user with member rights' do before do allow(PlatformContent).to receive(:remove_file) end @@ -80,7 +80,7 @@ describe Platforms::ContentsController do 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' - it_should_behave_like 'content platform user without owner rights' + it_should_behave_like 'content platform user without member rights' end context 'for global admin' do @@ -90,20 +90,7 @@ describe Platforms::ContentsController do it_should_behave_like 'content platform user with show rights' it_should_behave_like 'content platform user with show rights for hidden platform' - it_should_behave_like 'content platform user with owner rights' - end - - context 'for owner user' do - before do - http_login(@user) - allow(Platform).to receive(:find).and_return(@platform) - allow(@platform).to receive(:owner).and_return(@user) - create_relation(@platform, @user, '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' - it_should_behave_like 'content platform user with owner rights' + it_should_behave_like 'content platform user with member rights' end context 'for member of platform' do @@ -115,7 +102,7 @@ describe Platforms::ContentsController do it_should_behave_like 'content platform user with show rights' it_should_behave_like 'content platform user with show rights for hidden platform' - it_should_behave_like 'content platform user without owner rights' + it_should_behave_like 'content platform user with member rights' end context 'for simple user' do @@ -125,7 +112,7 @@ describe Platforms::ContentsController do it_should_behave_like 'content platform user with show rights' it_should_behave_like 'content platform user without show rights for hidden platform' - it_should_behave_like 'content platform user without owner rights' + it_should_behave_like 'content platform user without member rights' end end diff --git a/spec/controllers/platforms/mass_builds_controller_spec.rb b/spec/controllers/platforms/mass_builds_controller_spec.rb index ecd24e027..87786f69c 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 show action' do + get :show, platform_id: @platform, id: @mass_build + response.should render_template(:show) + end + it 'should be able to perform new action' do get :new, platform_id: @platform response.should render_template(:new) @@ -125,8 +130,7 @@ describe Platforms::MassBuildsController do @create_params = { mass_build: { :projects_list => @repository.projects.map(&:name).join("\n"), - :auto_publish => true, - :build_for_platform_id => @platform + :build_for_platform_id => @platform.id }, :platform_id => @platform, :arches => [Arch.first.id], diff --git a/spec/controllers/projects/pull_requests_controller_spec.rb b/spec/controllers/projects/pull_requests_controller_spec.rb index eef8119da..4a78dc106 100644 --- a/spec/controllers/projects/pull_requests_controller_spec.rb +++ b/spec/controllers/projects/pull_requests_controller_spec.rb @@ -70,8 +70,10 @@ shared_examples_for 'pull request user with project reader rights' do end it "should not create already up-to-date pull" do - post :create, @create_params.merge({pull_request: {issue_attributes: {title: 'already', body: 'creating'}, to_ref: 'master', from_ref: 'master'}, to_project_id: @project.id}) - PullRequest.joins(:issue).where(issues: {title: 'already', body: 'creating'}).count.should == 0 + lambda{ + post :create, @create_params.merge({pull_request: {issue_attributes: {title: 'already', body: 'creating'}, + to_ref: 'master', from_ref: 'master'}, to_project_id: @project.id}) }.should + change{ PullRequest.count }.by(0) end it "should create pull request to the same project" do @@ -151,7 +153,7 @@ shared_examples_for 'user without pull request update rights' do pull.issue.body.should_not =='updating' end - it 'should be able to perform merge action' do + it 'should not be able to perform merge action' do @pull.check put :merge, @update_params response.should_not be_success @@ -239,7 +241,7 @@ describe Projects::PullRequestsController do it_should_behave_like 'pull request user with project guest rights' 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 'user with pull request update rights' it_should_behave_like 'pull request when project with issues turned off' end diff --git a/spec/factories/mass_build.rb b/spec/factories/mass_build.rb index 8912a3330..579ae4cdb 100644 --- a/spec/factories/mass_build.rb +++ b/spec/factories/mass_build.rb @@ -4,7 +4,6 @@ FactoryGirl.define do association :user projects_list "first" arches { [ Arch.where(name: 'x86_64').first_or_create.id ] } - auto_publish true stop_build false end end diff --git a/spec/jobs/clean_buildroot_job_spec.rb b/spec/jobs/clean_buildroot_job_spec.rb new file mode 100644 index 000000000..a1be850a3 --- /dev/null +++ b/spec/jobs/clean_buildroot_job_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +describe BuildLists::CleanBuildrootJob do + + before { stub_symlink_methods } + subject { BuildLists::CleanBuildrootJob } + + it 'ensures that not raises error' do + FactoryGirl.create(:build_list, status: BuildList::BUILD_ERROR) + expect(FileStoreService::File).to_not receive(:new) + expect do + subject.perform + end.to_not raise_exception + end + + it 'cleans RPM buildroot' do + results = [ + { 'sha1' => 'sha1-1', 'file_name' => BuildLists::CleanBuildrootJob::FILENAME }, + { 'sha1' => 'sha1-2', 'file_name' => 'test.log' } + ] + FactoryGirl.create(:build_list, + results: results, + save_buildroot: true, + status: BuildList::BUILD_ERROR + ) + bl = FactoryGirl.create(:build_list, + results: results, + save_buildroot: true, + status: BuildList::BUILD_ERROR, + updated_at: Time.now - 2.hours + ) + file_store_service = double(:file_store_service, destroy: true) + + expect(FileStoreService::File).to receive(:new).with(sha1: 'sha1-1').and_return(file_store_service) + + subject.perform + expect(bl.reload.results).to eq [{ 'sha1' => 'sha1-2', 'file_name' => 'test.log' }] + end + +end diff --git a/spec/jobs/create_empty_metadata_job_spec.rb b/spec/jobs/create_empty_metadata_job_spec.rb new file mode 100644 index 000000000..8c63e45c1 --- /dev/null +++ b/spec/jobs/create_empty_metadata_job_spec.rb @@ -0,0 +1,97 @@ +require 'spec_helper' + +describe CreateEmptyMetadataJob do + + before { stub_symlink_methods } + subject { BuildLists::CleanBuildrootJob } + + context 'create_empty_metadata' do + let(:job) { CreateEmptyMetadataJob.new } + let(:platform) { FactoryGirl.build(:platform) } + let(:path) { platform.path } + + it 'creates metadata for rhel platform' do + platform.distrib_type = 'rhel' + job.send :create_empty_metadata, platform, path.dup + + expect(Dir["#{ path }/repodata/*"]).to be_present + expect(Dir["#{ path }/media_info/*"]).to be_empty + end + + it 'creates metadata for mdv platform' do + platform.distrib_type = 'mdv' + job.send :create_empty_metadata, platform, path.dup + + expect(Dir["#{ path }/media_info/*"]).to be_present + expect(Dir["#{ path }/repodata/*"]).to be_empty + end + + it 'does nothing for other platforms' do + platform.distrib_type = 'test' + job.send :create_empty_metadata, platform, path.dup + + expect(Dir["#{ path }/media_info/*"]).to be_empty + expect(Dir["#{ path }/repodata/*"]).to be_empty + end + end + + context 'create_empty_metadata_for_repository' do + let(:job) { CreateEmptyMetadataJob.new } + let(:repository) { FactoryGirl.build(:repository) } + let(:platform) { repository.platform } + + before do + allow(job).to receive(:arch_names).and_return(%w(i586 x86_64)) + end + + it 'repository of main platform' do + paths = <<-STR + #{ platform.path }/repository/i586/#{ repository.name }/release + #{ platform.path }/repository/i586/#{ repository.name }/updates + #{ platform.path }/repository/x86_64/#{ repository.name }/release + #{ platform.path }/repository/x86_64/#{ repository.name }/updates + STR + paths.split("\n").each do |path| + expect(job).to receive(:create_empty_metadata).with(platform, path.strip) + end + + job.send :create_empty_metadata_for_repository, repository + end + + it 'repository of personal platform' do + platform.platform_type = Platform::TYPE_PERSONAL + Platform.stub_chain(:main, :opened).and_return([platform]) + paths = <<-STR + #{ platform.path }/repository/#{ platform.name }/i586/#{ repository.name }/release + #{ platform.path }/repository/#{ platform.name }/i586/#{ repository.name }/updates + #{ platform.path }/repository/#{ platform.name }/x86_64/#{ repository.name }/release + #{ platform.path }/repository/#{ platform.name }/x86_64/#{ repository.name }/updates + STR + paths.split("\n").each do |path| + expect(job).to receive(:create_empty_metadata).with(platform, path.strip) + end + + job.send :create_empty_metadata_for_repository, repository + end + end + + context 'create_empty_metadata_for_platform' do + let(:platform) { FactoryGirl.build(:platform, id: 123) } + let(:repository1) { FactoryGirl.build(:personal_repository) } + let(:repository2) { FactoryGirl.build(:personal_repository) } + let(:job) { CreateEmptyMetadataJob.new('Platform', 123) } + + before do + Platform.stub_chain(:main, :opened, :find).and_return(platform) + Repository.stub_chain(:joins, :where, :find_each).and_yield(repository1).and_yield(repository2) + end + + it 'creates metadata for all personal repositories' do + expect(job).to receive(:create_empty_metadata_for_repository).with(repository1) + expect(job).to receive(:create_empty_metadata_for_repository).with(repository2) + + job.send :create_empty_metadata_for_platform + end + end + +end diff --git a/spec/jobs/dependent_packages_job_spec.rb b/spec/jobs/dependent_packages_job_spec.rb new file mode 100644 index 000000000..eed039cdc --- /dev/null +++ b/spec/jobs/dependent_packages_job_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +describe BuildLists::DependentPackagesJob do + let(:build_list) { FactoryGirl.build(:build_list, id: 123) } + let(:user) { build_list.user } + let(:project) { build_list.project } + let(:ability) { double(:ability) } + let(:project_ids) { [build_list.project_id] } + let(:arch_ids) { [build_list.arch_id] } + let(:options) { { + 'auto_publish_status' => 'none', + 'auto_create_container' => '0', + 'include_testing_subrepository' => '0', + 'use_cached_chroot' => '0', + 'use_extra_tests' => '0' + } } + + before do + stub_symlink_methods + allow(BuildList).to receive(:find).with(123).and_return(build_list) + # BuildList::Package.stub_chain(:joins, :where, :reorder, :uniq, :pluck).and_return([project.id]) + Project.stub_chain(:where, :to_a).and_return([project]) + Arch.stub_chain(:where, :to_a).and_return([build_list.arch]) + + allow(Ability).to receive(:new).and_return(ability) + allow(ability).to receive(:can?).with(:show, build_list).and_return(true) + allow(ability).to receive(:can?).with(:write, project).and_return(true) + allow(ability).to receive(:can?).with(:create, anything).and_return(true) + end + + subject { BuildLists::DependentPackagesJob } + + it 'ensures that not raises error' do + expect do + subject.perform build_list.id, user.id, project_ids, arch_ids, options + end.to_not raise_exception + end + + it 'ensures that creates build_list' do + expect do + subject.perform build_list.id, user.id, project_ids, arch_ids, options + end.to change(BuildList, :count).by(1) + end + + it 'ensures that do nothing if user has no access for show of build_list' do + allow(ability).to receive(:can?).with(:show, build_list).and_return(false) + expect do + subject.perform build_list.id, user.id, project_ids, arch_ids, options + end.to change(BuildList, :count).by(0) + end + + it 'ensures that do nothing if user has no access for write of project' do + allow(ability).to receive(:can?).with(:write, project).and_return(false) + expect do + subject.perform build_list.id, user.id, project_ids, arch_ids, options + end.to change(BuildList, :count).by(0) + end + + it 'ensures that do nothing if user has no access for create of build_list' do + allow(ability).to receive(:can?).with(:create, anything).and_return(false) + expect do + subject.perform build_list.id, user.id, project_ids, arch_ids, options + end.to change(BuildList, :count).by(0) + end + +end diff --git a/spec/jobs/destroy_project_from_repository_job_spec.rb b/spec/jobs/destroy_project_from_repository_job_spec.rb new file mode 100644 index 000000000..f6e9e5944 --- /dev/null +++ b/spec/jobs/destroy_project_from_repository_job_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe DestroyProjectFromRepositoryJob do + let(:project) { FactoryGirl.build(:project, id: 123) } + let(:repository) { FactoryGirl.build(:repository, id: 234) } + + before do + stub_symlink_methods + end + + subject { DestroyProjectFromRepositoryJob } + + it 'ensures that not raises error' do + expect do + subject.perform project, repository + end.to_not raise_exception + end + +end diff --git a/spec/jobs/publish_task_manager_job_spec.rb b/spec/jobs/publish_task_manager_job_spec.rb new file mode 100644 index 000000000..d46a85f72 --- /dev/null +++ b/spec/jobs/publish_task_manager_job_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +describe PublishTaskManagerJob do + + subject { PublishTaskManagerJob } + + it 'ensures that not raises error' do + expect do + subject.perform + end.to_not raise_exception + end +end diff --git a/spec/jobs/run_extra_mass_builds_job_spec.rb b/spec/jobs/run_extra_mass_builds_job_spec.rb new file mode 100644 index 000000000..38475f591 --- /dev/null +++ b/spec/jobs/run_extra_mass_builds_job_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +describe RunExtraMassBuildsJob do + let(:mass_build1) { FactoryGirl.build(:mass_build, id: 123) } + let(:mass_build2) { FactoryGirl.build(:mass_build, id: 234, extra_mass_builds: [mass_build1.id]) } + let(:build_list) { FactoryGirl.build(:build_list, id: 345, mass_build: mass_build) } + + let(:job) { RunExtraMassBuildsJob.new } + + before do + stub_symlink_methods + MassBuild.stub_chain(:where, :find_each).and_yield(mass_build2) + MassBuild.stub_chain(:where, :to_a).and_return([mass_build1]) + allow(job).to receive(:not_ready?).with(mass_build1).and_return(false) + allow(mass_build2).to receive(:build_all) + end + + it 'ensures that not raises error' do + expect do + job.perform + end.to_not raise_exception + end + + it 'ensures that calls #build_all' do + expect(mass_build2).to receive(:build_all) + job.perform + end + + it 'ensures that do nothing when no extra_mass_builds' do + mass_build2.extra_mass_builds = [] + expect(mass_build2).to_not receive(:build_all) + job.perform + end + + it 'ensures that do nothing when extra mass build not ready' do + allow(job).to receive(:not_ready?).with(mass_build1).and_return(true) + expect(mass_build2).to_not receive(:build_all) + job.perform + end + + it 'ensures that do nothing when some extra mass builds have no status SUCCESS' do + mass_build0 = FactoryGirl.build(:mass_build, id: 1) + mass_build2.extra_mass_builds = [mass_build0, mass_build1] + MassBuild.stub_chain(:where, :to_a).and_return([mass_build1]) + expect(mass_build2).to_not receive(:build_all) + job.perform + 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 deleted file mode 100644 index 7937c770c..000000000 --- a/spec/lib/abf-worker/build_lists_publish_task_manager_spec.rb +++ /dev/null @@ -1,272 +0,0 @@ -require 'spec_helper' - -describe AbfWorker::BuildListsPublishTaskManager do - before(:all) do - @publish_workers_count = APP_CONFIG['abf_worker']['publish_workers_count'] - APP_CONFIG['abf_worker']['publish_workers_count'] = 2 - end - - before do - stub_symlink_methods - FactoryGirl.create(:build_list) - end - - subject { AbfWorker::BuildListsPublishTaskManager } - let(:build_list) { FactoryGirl.create(:build_list) } - - context 'when no items for publishing' do - before { subject.new.run } - - %w(PROJECTS_FOR_CLEANUP - LOCKED_PROJECTS_FOR_CLEANUP - 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 - - %w(publish_worker_default publish_worker).each do |kind| - it "ensures that no tasks in '#{kind}' queue" do - @redis_instance.lrange(kind, 0, -1).should be_empty - end - end - - end - - context 'when one build_list for publishing' do - before do - build_list.update_column(:status, BuildList::BUILD_PUBLISH) - 2.times{ subject.new.run } - end - %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 "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 "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 "ensures that new task for publishing has been created" do - @redis_instance.lrange('resque:queue:publish_worker_default', 0, -1).should have(1).item - end - - end - - 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 - build_list.update_column(:status, BuildList::BUILD_PUBLISH) - build_list2.update_column(:status, BuildList::BUILD_PUBLISH) - 2.times{ subject.new.run } - end - - %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 "ensures that only one repository_status has status publish" do - RepositoryStatus.where(status: RepositoryStatus::PUBLISH).should have(1).item - end - - 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 "ensures that new task for publishing has been created" do - @redis_instance.lrange('resque:queue:publish_worker_default', 0, -1).should have(1).item - end - - end - - context 'creates not more than 4 tasks for publishing' do - before do - build_list.update_column(:status, BuildList::BUILD_PUBLISH) - 4.times { - bl = FactoryGirl.create(:build_list, new_core: true) - bl.update_column(:status, BuildList::BUILD_PUBLISH) - } - 2.times{ subject.new.run } - end - - it "ensures that 4 repository_statuses have status publish" do - RepositoryStatus.where(status: RepositoryStatus::PUBLISH).should have(4).items - end - - 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 "ensures that new tasks for publishing has been created" do - @redis_instance.lrange('resque:queue:publish_worker_default', 0, -1).should have(4).items - end - - end - - context 'creates task for removing project from repository' do - before do - 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(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 "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("testing-#{build_list.project_id}-#{build_list.save_to_repository_id}-#{build_list.build_for_platform_id}") - end - - 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 "ensures that new task for publishing has been created" do - @redis_instance.lrange('resque:queue:publish_worker_default', 0, -1).should have(1).item - end - - end - - 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, - 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 - build_list.update_column(:status, BuildList::BUILD_PUBLISH) - build_list2.update_column(:status, BuildList::BUILD_PUBLISHED) - build_list3.update_column(:status, BuildList::BUILD_PUBLISHED) - ProjectToRepository.where(project_id: build_list3.project_id, repository_id: build_list3.save_to_repository_id).destroy_all - 2.times{ subject.new.run } - end - - 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("testing-#{build_list3.project_id}-#{build_list3.save_to_repository_id}-#{build_list3.build_for_platform_id}") - end - - 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 "ensures that new task for publishing has been created" do - @redis_instance.lrange('resque:queue:publish_worker_default', 0, -1).should have(1).item - end - - 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 - - context 'resign packages in repository' do - before do - 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(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 "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 - @redis_instance.lrange('resque:queue:publish_worker_default', 0, -1).should have(1).item - end - - 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('resque: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('resque: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 - FileUtils.rm_rf(APP_CONFIG['root_path']) - end -end diff --git a/spec/models/build_list_observer_spec.rb b/spec/models/build_list_observer_spec.rb index 78ea54d44..dbedfe1b2 100644 --- a/spec/models/build_list_observer_spec.rb +++ b/spec/models/build_list_observer_spec.rb @@ -1,5 +1,15 @@ require 'spec_helper' describe BuildListObserver do - pending "add some examples to (or delete) #{__FILE__}" + + before { stub_symlink_methods } + + let(:build_list) { FactoryGirl.create(:build_list) } + + it 'updates started_at when build started' do + expect(build_list.started_at).to be_nil + build_list.start_build + expect(build_list.started_at).to_not be_nil + end + end diff --git a/spec/models/build_list_spec.rb b/spec/models/build_list_spec.rb index f45535063..36929f6f3 100644 --- a/spec/models/build_list_spec.rb +++ b/spec/models/build_list_spec.rb @@ -15,22 +15,60 @@ describe BuildList do end end - context "#notify_users" do - let!(:user) { FactoryGirl.create(:user) } - let!(:build_list) { FactoryGirl.create(:build_list, - user: user, - auto_publish_status: BuildList::AUTO_PUBLISH_STATUS_NONE) } - let!(:build_list_package) { FactoryGirl.create(:build_list_package, - build_list: build_list, - project: build_list.project) } + context '#next_build' do + let!(:build_list) { FactoryGirl.create(:build_list, updated_at: Time.now - 20.seconds) } - - before(:all) { ActionMailer::Base.deliveries = [] } - before do - build_list.update_attributes({commit_hash: build_list.project.repo.commits('master').last.id, - status: BuildList::BUILD_STARTED}, without_protection: true) + it 'returns build list' do + expect_any_instance_of(BuildList).to receive(:delayed_add_job_to_abf_worker_queue) + expect(BuildList.next_build([], [])).to eq build_list end - after { ActionMailer::Base.deliveries = [] } + + context 'filtering build lists by platform' do + it 'returns build list for correct platform' do + expect_any_instance_of(BuildList).to receive(:delayed_add_job_to_abf_worker_queue) + expect(BuildList.next_build([], [build_list.build_for_platform_id])).to eq build_list + end + + it 'returns nothing if build list does not in queue' do + expect_any_instance_of(BuildList).to receive(:destroy_from_resque_queue).and_return(0) + expect(BuildList.next_build([], [build_list.build_for_platform_id])).to be_nil + end + + it 'returns nothing for wrong platform' do + expect(BuildList.next_build([], [-1])).to be_nil + end + end + + context 'filtering build lists by arch' do + it 'returns build list for correct arch' do + expect_any_instance_of(BuildList).to receive(:delayed_add_job_to_abf_worker_queue) + expect(BuildList.next_build([build_list.arch_id], [])).to eq build_list + end + + it 'returns nothing if build list does not in queue' do + expect_any_instance_of(BuildList).to receive(:destroy_from_resque_queue).and_return(0) + expect(BuildList.next_build([build_list.arch_id], [])).to be_nil + end + + it 'returns nothing for wrong arch' do + expect(BuildList.next_build([-1], [])).to be_nil + end + end + + end + + context "#notify_users" do + let(:user) { FactoryGirl.build(:user) } + let!(:build_list) { + FactoryGirl.create(:build_list, + user: user, + auto_publish_status: BuildList::AUTO_PUBLISH_STATUS_NONE, + status: BuildList::BUILD_STARTED + ) + } + + before(:all) { ActionMailer::Base.deliveries = [] } + after { ActionMailer::Base.deliveries = [] } shared_examples_for 'build list notifications by email' do it "gets notification by email when status - Build complete" do @@ -43,50 +81,59 @@ describe BuildList do should have(1).item end + it "gets notification by email when status - Unpermitted architecture" do + build_list.unpermitted_arch + should have(1).item + end + it "gets notification by email when auto_publish and status - Build error" do - build_list.update_attributes(auto_publish_status: BuildList::AUTO_PUBLISH_STATUS_DEFAULT) + build_list.auto_publish_status = BuildList::AUTO_PUBLISH_STATUS_DEFAULT build_list.build_error should have(1).item end it "gets notification by email when status - Failed publish" do - build_list.update_attributes({status: BuildList::BUILD_PUBLISH}, without_protection: true) + build_list.status = BuildList::BUILD_PUBLISH build_list.fail_publish should have(1).item end it "gets notification by email when auto_publish and status - Failed publish" do - build_list.update_attributes({auto_publish_status: BuildList::AUTO_PUBLISH_STATUS_DEFAULT, status: BuildList::BUILD_PUBLISH}, without_protection: true) + build_list.auto_publish_status = BuildList::AUTO_PUBLISH_STATUS_DEFAULT + build_list.status = BuildList::BUILD_PUBLISH build_list.fail_publish should have(1).item end it "gets notification by email when status - Build published" do - build_list.update_attributes({status: BuildList::BUILD_PUBLISH}, without_protection: true) + build_list.status = BuildList::BUILD_PUBLISH build_list.published should have(1).item end it "gets notification by email when auto_publish and status - Build published" do - build_list.update_attributes({auto_publish_status: BuildList::AUTO_PUBLISH_STATUS_DEFAULT, status: BuildList::BUILD_PUBLISH}, without_protection: true) + build_list.auto_publish_status = BuildList::AUTO_PUBLISH_STATUS_DEFAULT + build_list.status = BuildList::BUILD_PUBLISH build_list.published should have(1).item end it "doesn't get notification by email when auto_publish and status - Build complete" do - build_list.update_attributes(auto_publish_status: BuildList::AUTO_PUBLISH_STATUS_DEFAULT) + build_list.auto_publish_status = BuildList::AUTO_PUBLISH_STATUS_DEFAULT build_list.build_success should have(:no).items end it "doesn't get notification by email when auto_publish_into_testing and status - Build complete" do - build_list.update_attributes(auto_publish_status: BuildList::AUTO_PUBLISH_STATUS_TESTING) + build_list.auto_publish_status = BuildList::AUTO_PUBLISH_STATUS_TESTING build_list.build_success should have(:no).items end it "doesn't get notification by email when mass build" do - build_list.update_attributes({mass_build_id: 1, status: BuildList::BUILD_PUBLISH}, without_protection: true) + mb = FactoryGirl.create(:mass_build) + build_list.mass_build_id = mb.id + build_list.status = BuildList::BUILD_PUBLISH build_list.published should have(:no).items end @@ -103,20 +150,21 @@ describe BuildList do context "user created build task" do let!(:notifier) { user.notifier } before do - notifier.update_attributes(new_associated_build: false) + allow(notifier).to receive(:new_associated_build?).and_return(false) build_list.project.owner.notifier.update_attributes(can_notify: false) end it_should_behave_like 'build list notifications by email' it "doesn't get notification by email when 'build list' notifications has been disabled" do - notifier.update_attributes(new_build: false) + allow(notifier).to receive(:new_build?).and_return(false) build_list.build_success should have(:no).items end it "doesn't get notification by email when 'build list' notifications - enabled, email notifications - disabled" do - notifier.update_attributes(can_notify: false, new_build: true) + allow(notifier).to receive(:can_notify?).and_return(false) + allow(notifier).to receive(:new_build?).and_return(true) build_list.build_success should have(:no).items end @@ -151,7 +199,6 @@ describe BuildList do auto_publish_status: BuildList::AUTO_PUBLISH_STATUS_DEFAULT, project: project ) - FactoryGirl.create(:build_list_package, build_list: bl, project: bl.project) bl.update_attributes({commit_hash: bl.project.repo.commits('master').last.id, status: BuildList::BUILD_PUBLISH}, without_protection: true) bl.published @@ -188,6 +235,11 @@ describe BuildList do build_list.has_new_packages?.should be_false end + it 'ensures that return true if version of packages are same and platform RHEL is released' do + build_list.save_to_platform.update_attributes(released: true, distrib_type: 'rhel') + build_list.has_new_packages?.should be_true + 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 diff --git a/spec/models/mass_build_spec.rb b/spec/models/mass_build_spec.rb index 171c1a174..7936db91b 100644 --- a/spec/models/mass_build_spec.rb +++ b/spec/models/mass_build_spec.rb @@ -23,6 +23,7 @@ describe MassBuild do it { should validate_presence_of(:projects_list)} it { should ensure_length_of(:projects_list).is_at_most(500_000) } + it { should ensure_length_of(:description).is_at_most(255) } it { should_not allow_mass_assignment_of(:name) } it { should_not allow_mass_assignment_of(:arch_names) } @@ -76,4 +77,31 @@ describe MassBuild do mb.send(:publish, user, []) end + it 'ensures that calls #build_all on create' do + mass_build = FactoryGirl.build(:mass_build) + expect(mass_build).to receive(:build_all) + mass_build.save + end + + it 'ensures that does not call #build_all on create if attached extra mass builds' do + mass_build = FactoryGirl.build(:mass_build, extra_mass_builds: [1]) + expect(mass_build).to_not receive(:build_all) + mass_build.save + end + + context '#build_all' do + let(:mass_build) { FactoryGirl.create(:mass_build, extra_mass_builds: [1]) } + + it 'ensures that do nothing when build has status build_started' do + mass_build.start + expect(mass_build).to_not receive(:projects_list) + mass_build.build_all + end + + it 'ensures that works when build has status build_pending' do + expect(mass_build).to receive(:projects_list).at_least(:once) + mass_build.build_all + end + end + end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index a3d21aa46..b3c2daf17 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -167,17 +167,23 @@ describe Project do 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' + context '#run_mass_import' do + before { WebMock.allow_net_connect! } + after { WebMock.disable_net_connect! } - 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) + it 'success' do + owner = FactoryGirl.create(:user) + repository = FactoryGirl.create(:repository) + url = 'http://abf-downloads.rosalinux.ru/abf_personal/repository/test-mass-import' + visibility = 'open' - Project.count.should == 2 - repository.projects.should have(2).items - owner.projects.should have(2).items + + 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 shared_examples_for 'autostart build_lists' do |once_a_12_hours, once_a_day, once_a_week| diff --git a/spec/models/project_to_repository_spec.rb b/spec/models/project_to_repository_spec.rb index d1d417b9b..3c2bb5112 100644 --- a/spec/models/project_to_repository_spec.rb +++ b/spec/models/project_to_repository_spec.rb @@ -1,26 +1,24 @@ require 'spec_helper' describe ProjectToRepository do - before(:each) do + let(:platform) { FactoryGirl.create(:platform) } + let(:first_repo) { FactoryGirl.create(:repository, platform: platform) } + let(:second_repo) { FactoryGirl.create(:repository, platform: platform) } + let(:project) { FactoryGirl.create(:project) } + + before do stub_symlink_methods - @platform = FactoryGirl.create(:platform) - @first_repo = FactoryGirl.create(:repository, platform_id: @platform.id) - @second_repo = FactoryGirl.create(:repository, platform_id: @platform.id) - @project = FactoryGirl.create(:project) - @first_repo.projects << @project - @first_repo.save + first_repo.projects << project + first_repo.save end it 'should not add the same project in different repositories of same platform' do - p2r = @second_repo.project_to_repositories.build project_id: @project.id - p2r.should_not be_valid + p2r = second_repo.project_to_repositories.build project: project + expect(p2r).to_not be_valid end it 'creates task for removing project from repository on destroy' do - @first_repo.project_to_repositories.destroy_all - queue = @redis_instance.lrange(AbfWorker::BuildListsPublishTaskManager::PROJECTS_FOR_CLEANUP, 0, -1) - queue.should have(2).item - key = "#{@project.id}-#{@first_repo.id}-#{@platform.id}" - queue.should include(key, "testing-#{key}") + expect(DestroyProjectFromRepositoryJob).to receive(:perform).with(project, first_repo) + first_repo.project_to_repositories.destroy_all end end diff --git a/spec/services/abf_worker_service/container_spec.rb b/spec/services/abf_worker_service/container_spec.rb new file mode 100644 index 000000000..8bf4a62b0 --- /dev/null +++ b/spec/services/abf_worker_service/container_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe AbfWorkerService::Container do + let(:build_list) { FactoryGirl.build(:build_list, id: 123) } + + before do + stub_symlink_methods + end + + subject { AbfWorkerService::Container.new(build_list) } + + context '#create!' do + + it 'creates task' do + expect(build_list).to_not receive(:fail_publish_container) + expect(Resque).to receive(:push) + subject.create! + end + + it 'fails when no packages on FS' do + expect(subject).to receive(:filter_build_lists_without_packages).and_return([]) + expect(build_list).to receive(:fail_publish_container) + expect(Resque).to_not receive(:push) + subject.create! + end + + end + +end diff --git a/spec/services/abf_worker_service/platform_metadata_spec.rb b/spec/services/abf_worker_service/platform_metadata_spec.rb new file mode 100644 index 000000000..bd589afc5 --- /dev/null +++ b/spec/services/abf_worker_service/platform_metadata_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe AbfWorkerService::PlatformMetadata do + let(:platform) { FactoryGirl.build(:platform, id: 123) } + + before do + stub_symlink_methods + end + + subject { AbfWorkerService::PlatformMetadata.new(platform) } + + context '#regenerate!' do + + it 'creates task' do + expect(platform).to receive(:start_regeneration).and_return(true) + expect(Resque).to receive(:push) + subject.regenerate! + end + + end + +end diff --git a/spec/services/abf_worker_service/repository_metadata_spec.rb b/spec/services/abf_worker_service/repository_metadata_spec.rb new file mode 100644 index 000000000..9587d3e75 --- /dev/null +++ b/spec/services/abf_worker_service/repository_metadata_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe AbfWorkerService::RepositoryMetadata do + let(:repository) { FactoryGirl.build(:repository, id: 123) } + let(:platform) { repository.platform } + let(:repository_status) { double(:repository_status, id: 234, repository: repository, platform: platform) } + + before do + stub_symlink_methods + allow(repository_status).to receive(:start_regeneration).and_return(true) + end + + subject { AbfWorkerService::RepositoryMetadata.new(repository_status) } + + context '#regenerate!' do + + context 'repository of main platform' do + it 'creates task' do + expect(subject).to_not receive(:system) + expect(Resque).to receive(:push) + subject.regenerate! + end + end + + context 'repository of personal platform' do + before do + allow(platform).to receive(:personal?).and_return(true) + end + + it 'creates task' do + expect(subject).to receive(:system) + expect(Resque).to receive(:push) + subject.regenerate! + end + end + + end + +end diff --git a/spec/services/abf_worker_service/repository_spec.rb b/spec/services/abf_worker_service/repository_spec.rb new file mode 100644 index 000000000..00c1e7a6f --- /dev/null +++ b/spec/services/abf_worker_service/repository_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper' + +describe AbfWorkerService::Repository do + let(:repository) { FactoryGirl.build(:repository, id: 123) } + + before do + stub_symlink_methods + end + + subject { AbfWorkerService::Repository.new(repository) } + + context '#destroy_project!' do + let(:project) { FactoryGirl.build(:project, id: 234) } + + context 'repository of main platform' do + let(:key) { "#{project.id}-#{repository.id}-#{repository.platform_id}" } + + it 'adds to PROJECTS_FOR_CLEANUP queue' do + expect(Redis.current).to receive(:lpush).with(AbfWorkerService::Base::PROJECTS_FOR_CLEANUP, key) + expect(Redis.current).to receive(:lpush).with(AbfWorkerService::Base::PROJECTS_FOR_CLEANUP, 'testing-' << key) + + subject.destroy_project!(project) + end + + end + + context 'repository of personal platform' do + let(:platform1) { FactoryGirl.build(:platform, id: 345) } + let(:platform2) { FactoryGirl.build(:platform, id: 456) } + + before do + allow(repository.platform).to receive(:personal?).and_return(true) + allow(Platform).to receive(:main).and_return([platform1, platform2]) + end + + it 'adds to PROJECTS_FOR_CLEANUP queue' do + [platform1, platform2].each do |platform| + key = "#{project.id}-#{repository.id}-#{platform.id}" + expect(Redis.current).to receive(:lpush).with(AbfWorkerService::Base::PROJECTS_FOR_CLEANUP, key) + expect(Redis.current).to receive(:lpush).with(AbfWorkerService::Base::PROJECTS_FOR_CLEANUP, 'testing-' << key) + end + + subject.destroy_project!(project) + end + + end + end + + context '#resign!' do + let(:repository_status) { double(:repository_status, id: 234) } + + it 'creates task' do + expect(repository_status).to receive(:start_resign).and_return(true) + expect(Resque).to receive(:push) + subject.resign!(repository_status) + end + + end + +end diff --git a/spec/services/abf_worker_service/rpm_spec.rb b/spec/services/abf_worker_service/rpm_spec.rb new file mode 100644 index 000000000..c9c25e743 --- /dev/null +++ b/spec/services/abf_worker_service/rpm_spec.rb @@ -0,0 +1,215 @@ +require 'spec_helper' + +describe AbfWorkerService::Rpm do + let(:build_list) { FactoryGirl.create(:build_list) } + + before(:all) do + @publish_workers_count = APP_CONFIG['abf_worker']['publish_workers_count'] + APP_CONFIG['abf_worker']['publish_workers_count'] = 2 + end + + after(:all) do + APP_CONFIG['abf_worker']['publish_workers_count'] = @publish_workers_count + end + + before do + stub_symlink_methods + end + + subject { AbfWorkerService::Rpm } + + context '#publish!' do + + before do + FactoryGirl.create(:build_list) + end + + context 'no items for publishing' do + + %w(PROJECTS_FOR_CLEANUP + LOCKED_PROJECTS_FOR_CLEANUP + LOCKED_BUILD_LISTS).each do |kind| + + it "ensures that no '#{kind.downcase.gsub('_', ' ')}'" do + subject.publish! + @redis_instance.lrange(subject.const_get(kind), 0, -1).should be_empty + end + end + + it 'ensures that no tasks' do + expect(Resque).to_not receive(:push) + subject.publish! + end + end + + context 'when one build_list for publishing' do + before do + build_list.update_column(:status, BuildList::BUILD_PUBLISH) + 2.times{ subject.publish! } + end + + %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 "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 "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 "ensures that new task for publishing has been created" do + @redis_instance.lrange('resque:queue:publish_worker_default', 0, -1).should have(1).item + end + + end + + context 'creates not more than 4 tasks for publishing' do + before do + build_list.update_column(:status, BuildList::BUILD_PUBLISH) + 4.times do + bl = FactoryGirl.build(:build_list) + bl.status = BuildList::BUILD_PUBLISH + bl.save! + end + end + + it "ensures that 4 repository_statuses have status publish" do + subject.publish! + subject.publish! + + RepositoryStatus.where(status: RepositoryStatus::PUBLISH).should have(4).items + end + + it "ensures that 'locked build lists' has 4 items" do + subject.publish! + subject.publish! + + @redis_instance.lrange(subject::LOCKED_BUILD_LISTS, 0, -1).should have(4).items + end + + it "ensures that new tasks for publishing has been created" do + expect(Resque).to receive(:push).exactly(4).times + + subject.publish! + subject.publish! + end + end + + context 'grouping build lists for publishing into same repository' do + let(:build_list2) { FactoryGirl.build(: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 + build_list.update_column(:status, BuildList::BUILD_PUBLISH) + build_list2.status = BuildList::BUILD_PUBLISH + build_list2.save! + end + + %w(PROJECTS_FOR_CLEANUP LOCKED_PROJECTS_FOR_CLEANUP).each do |kind| + it "ensures that no '#{kind.downcase.gsub('_', ' ')}'" do + subject.publish! + subject.publish! + @redis_instance.lrange(subject.const_get(kind), 0, -1).should be_empty + end + end + + it "ensures that only one repository_status has status publish" do + subject.publish! + subject.publish! + RepositoryStatus.where(status: RepositoryStatus::PUBLISH).should have(1).item + end + + it "ensures that 'locked build lists' has 2 items" do + subject.publish! + subject.publish! + 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 "ensures that new task for publishing has been created" do + expect(Resque).to receive(:push).once + + subject.publish! + subject.publish! + end + end + + context 'grouping build lists for publishing and tasks for removing project from repository' do + let(:build_list2) { FactoryGirl.build(: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.build(: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 + build_list.update_column(:status, BuildList::BUILD_PUBLISH) + build_list2.status = BuildList::BUILD_PUBLISHED; build_list2.save! + build_list3.status = BuildList::BUILD_PUBLISHED; build_list3.save! + ProjectToRepository.where(project_id: build_list3.project_id, repository_id: build_list3.save_to_repository_id).destroy_all + end + + it "ensures that no 'projects for cleanup' for main repo" do + subject.publish! + subject.publish! + + queue = @redis_instance.lrange(subject::PROJECTS_FOR_CLEANUP, 0, -1) + queue.should have(1).item + queue.should include("testing-#{build_list3.project_id}-#{build_list3.save_to_repository_id}-#{build_list3.build_for_platform_id}") + end + + it "ensures that only one repository_status has status publish" do + subject.publish! + subject.publish! + + RepositoryStatus.where(status: RepositoryStatus::PUBLISH).should have(1).item + end + + it "ensures that 'locked projects for cleanup' has only one item" do + subject.publish! + subject.publish! + + 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 "ensures that new task for publishing has been created" do + expect(Resque).to receive(:push).once + + subject.publish! + subject.publish! + end + + it "ensures that 'locked build lists' has only one item" do + subject.publish! + subject.publish! + + 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 + + end + +end diff --git a/spec/services/file_store_service_spec.rb b/spec/services/file_store_service_spec.rb new file mode 100644 index 000000000..d7f781926 --- /dev/null +++ b/spec/services/file_store_service_spec.rb @@ -0,0 +1,82 @@ +require 'spec_helper' + +describe FileStoreService::File do + + let(:sha1) { 'test-sha1' } + + context '#exist?' do + let(:response) { [ { file_name: 'test.log', sha1_hash: sha1 } ].to_json } + let(:url) { "http://file-store.rosalinux.ru/api/v1/file_stores.json?hash=#{sha1}" } + + subject { FileStoreService::File.new(sha1: sha1) } + + it 'returns true if file exists' do + stub_request(:get, url).to_return(body: response) + expect(subject.exist?).to be_true + end + + it 'returns false if file does not exist' do + stub_request(:get, url).to_return(body: '[]') + expect(subject.exist?).to be_false + end + + it 'returns false on error' do + stub_request(:get, url).to_raise(StandardError) + expect(subject.exist?).to be_false + end + end + + context '#save' do + let(:data) { { path: 'test-path', fullname: 'test-fullname' } } + let(:file) { double(:file) } + + before do + allow(Digest::SHA1).to receive(:hexdigest).and_return(sha1) + allow(File).to receive(:read).and_return(file) + end + + subject { FileStoreService::File.new(data: data) } + + it 'returns sha1 if file already exists on FS' do + allow(subject).to receive(:exist?).and_return(true) + expect(subject.save).to eq sha1 + end + + context 'file does not exist on FS' do + let(:url) { "http://test-token:@file-store.rosalinux.ru/api/v1/upload" } + let(:response) { { sha1_hash: sha1 }.to_json } + + before do + allow(subject).to receive(:exist?).and_return(false) + allow(File).to receive(:new).and_return(file) + allow(subject).to receive(:token).and_return('test-token') + end + + it 'returns sha1 if response code - 422' do + stub_request(:post, url).to_raise(RestClient::UnprocessableEntity) + expect(subject.save).to eq sha1 + end + + it 'returns nil on error' do + stub_request(:post, url).to_raise(StandardError) + expect(subject.save).to be_nil + end + + it 'returns sha1 on success' do + stub_request(:post, url).to_return(body: response) + expect(subject.save).to eq sha1 + end + end + end + + context '#destroy' do + let(:url) { "http://file-store.rosalinux.ru/api/v1/file_stores/#{sha1}.json" } + + it 'not raise errors' do + stub_request(:delete, url).to_raise(StandardError) + expect {subject.destroy }.to_not raise_error + end + + end + +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index dcae6ef9d..3f5b9d3fc 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,6 +2,7 @@ ENV["RAILS_ENV"] ||= 'test' require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' +require 'webmock/rspec' # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. @@ -47,6 +48,9 @@ end def stub_symlink_methods allow_any_instance_of(Platform).to receive(:symlink_directory).and_return(true) allow_any_instance_of(Platform).to receive(:remove_symlink_directory).and_return(true) + + allow_any_instance_of(Platform).to receive(:create_empty_metadata).and_return(true) + allow_any_instance_of(Repository).to receive(:create_empty_metadata).and_return(true) end Resque.inline = true diff --git a/travis.sh b/travis.sh index d54fd88b2..75f3dd158 100755 --- a/travis.sh +++ b/travis.sh @@ -13,9 +13,9 @@ then $rspec spec/controllers/api/ elif [ $SPEC_GROUP = 'models' ] then - $rspec spec/models/ + $rspec spec/models/ spec/jobs/ elif [ $SPEC_GROUP = 'others' ] then # $rspec spec/helpers/ spec/integration/ spec/lib/ spec/mailers/ spec/mailers/ spec/routing/ - $rspec spec/integration/ spec/lib/ spec/mailers/ spec/mailers/ spec/routing/ + $rspec spec/integration/ spec/services/ spec/mailers/ spec/mailers/ spec/routing/ fi diff --git a/vendor/assets/stylesheets/old-bootstrap.scss b/vendor/assets/stylesheets/old-bootstrap.scss index 2a171d492..4f1406046 100644 --- a/vendor/assets/stylesheets/old-bootstrap.scss +++ b/vendor/assets/stylesheets/old-bootstrap.scss @@ -822,7 +822,7 @@ a.badge:hover { margin-top: 1px; line-height: 14px; vertical-align: text-top; - background-image: url("glyphicons-halflings.png"); + background-image: image-url("glyphicons-halflings.png"); background-position: 14px 14px; background-repeat: no-repeat; }