diff --git a/.gitignore b/.gitignore index 8434f39..aed3396 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ /config/oauth.yml bundle/ +public/images/emoji +vendor/assets/bower_components diff --git a/Bowerfile b/Bowerfile new file mode 100644 index 0000000..1c38b5d --- /dev/null +++ b/Bowerfile @@ -0,0 +1 @@ +asset 'jquery-textcomplete' diff --git a/Gemfile b/Gemfile index d07e45f..cf8b7e1 100644 --- a/Gemfile +++ b/Gemfile @@ -11,6 +11,8 @@ gem 'sass-rails', '~> 5.0' gem 'uglifier', '>= 1.3.0' gem 'coffee-rails', '~> 4.1.0' gem 'turbolinks' +gem 'bower-rails' +gem 'gon' gem 'jquery-rails' gem 'bourbon' @@ -26,6 +28,7 @@ gem 'jbuilder', '~> 2.2.12' gem 'yajl-ruby', '~> 1.2.1' gem 'github-markdown', '~> 0.6.8' gem 'redcarpet', '~> 3.3.1' +gem 'gemoji' gem 'omniauth', '~> 1.2.2' gem 'omniauth-slack', '~> 2.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index 314c7b4..7f0ef39 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -47,6 +47,7 @@ GEM bourbon (4.2.3) sass (~> 3.4) thor + bower-rails (0.10.0) builder (3.2.2) bullet (4.14.7) activesupport (>= 3.0.0) @@ -91,9 +92,15 @@ GEM multipart-post (>= 1.2, < 3) font-awesome-sass (4.3.2.1) sass (~> 3.2) + gemoji (2.1.0) github-markdown (0.6.8) globalid (0.3.5) activesupport (>= 4.1.0) + gon (6.0.1) + actionpack (>= 3.0) + json + multi_json + request_store (>= 1.0) haml (4.0.6) tilt haml-rails (0.9.0) @@ -205,6 +212,7 @@ GEM rake (10.4.2) redcarpet (3.3.1) ref (1.0.5) + request_store (1.2.0) rsolr (1.0.12) builder (>= 2.1.2) rspec-core (3.2.3) @@ -281,6 +289,7 @@ DEPENDENCIES better_errors (= 2.1.1) binding_of_caller (= 0.7.2) bourbon + bower-rails bullet byebug capistrano (= 3.4.0) @@ -291,7 +300,9 @@ DEPENDENCIES coffee-rails (~> 4.1.0) factory_girl_rails (~> 4.5.0) font-awesome-sass (~> 4.3.0) + gemoji github-markdown (~> 0.6.8) + gon haml-rails (~> 0.9.0) jbuilder (~> 2.2.12) jquery-rails @@ -320,3 +331,6 @@ DEPENDENCIES unicorn (= 4.8.3) web-console (~> 2.0) yajl-ruby (~> 1.2.1) + +BUNDLED WITH + 1.10.6 diff --git a/README.md b/README.md index 34a8485..beb6e77 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ RG Portal * Ruby 2.2.1 * Bundler +* Bower ### Recommends @@ -23,7 +24,7 @@ $ cp config/oauth.yml.sample config/oauth.yml * Do not create a new authentication yourself. The number of integrations has a limit. -### Library installation +### Gem installation ``` $ bundle install --path=vendor/bundle @@ -36,6 +37,18 @@ $ bundle config build.libv8 --with-system-v8 $ bundle config build.therubyracer --with-v8-dir ``` +### Copy emoji files to public directory + +``` +$ rake emoji +``` + +### JavaScript library installation + +``` +rake bower:install +``` + ### Database creation ``` diff --git a/Rakefile b/Rakefile index ba6b733..6a66787 100644 --- a/Rakefile +++ b/Rakefile @@ -4,3 +4,5 @@ require File.expand_path('../config/application', __FILE__) Rails.application.load_tasks + +load 'tasks/emoji.rake' diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 788c662..c6f2d0b 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -14,4 +14,5 @@ //= require jquery_ujs //= require jquery_nested_form //= require turbolinks +//= require jquery-textcomplete/dist/jquery.textcomplete //= require_tree . diff --git a/app/assets/javascripts/emoji_complete.js.coffee b/app/assets/javascripts/emoji_complete.js.coffee new file mode 100644 index 0000000..988b5a5 --- /dev/null +++ b/app/assets/javascripts/emoji_complete.js.coffee @@ -0,0 +1,16 @@ +ready = -> + emojiCompletions = $('textarea.emoji-complete') + if emojiCompletions.length > 0 + emojiAliases = Object.keys(gon.emojis) + emojiCompletions.textcomplete([ + match: /(^|\s):([\w+-]*)$/, + search: (term, callback) -> + callback(emojiAliases.filter (e) -> e.indexOf(term) != -1) + template: (value) -> + " #{value}" + replace: (value) -> + "$1:#{value}:" + ], maxCount: 5) + +$(ready) +$(document).on('page:load', ready) diff --git a/app/assets/stylesheets/_style.css.scss b/app/assets/stylesheets/_style.css.scss index e361495..4ce5f25 100644 --- a/app/assets/stylesheets/_style.css.scss +++ b/app/assets/stylesheets/_style.css.scss @@ -109,3 +109,10 @@ nav { width: 15%; } } + +.emoji { + width: 1em; + height: 1em; + margin: 0; + padding: 0; +} diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 32db6da..26ff702 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -1,6 +1,9 @@ // Reset CSS @import "reset"; +// Autocomplete +@import "jquery-textcomplete/dist/jquery.textcomplete.css"; + // Bootstrap @import "bourbon"; @import "neat"; diff --git a/app/controllers/concerns/emoji_complete.rb b/app/controllers/concerns/emoji_complete.rb new file mode 100644 index 0000000..b45320a --- /dev/null +++ b/app/controllers/concerns/emoji_complete.rb @@ -0,0 +1,5 @@ +module EmojiComplete + def set_emoji_completion + gon.emojis = Emoji.send(:names_index).map { |name, emoji| [name, emoji.image_filename] }.to_h + end +end diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index 614a452..8822323 100644 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -1,7 +1,9 @@ class PagesController < ApplicationController + include EmojiComplete before_action :require_active_current_user before_action :set_page, only: [:show, :edit, :update] before_action :set_new_comment, only: :show + before_action :set_emoji_completion, only: [:show, :edit] def index @pages = Page.all diff --git a/app/models/comment.rb b/app/models/comment.rb index 555743b..bd15833 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,4 +1,7 @@ class Comment < ActiveRecord::Base + include Emojifier + include MarkdownRender + belongs_to :user belongs_to :page @@ -8,10 +11,6 @@ class Comment < ActiveRecord::Base MENTION_USER_REGEX = /@[A-z0-9]+/ - def render_content - Redcarpet::Markdown.new(Redcarpet::Render::HTML).render(self.content) - end - private def notify_mentions diff --git a/app/models/concerns/emojifier.rb b/app/models/concerns/emojifier.rb new file mode 100644 index 0000000..36c53c5 --- /dev/null +++ b/app/models/concerns/emojifier.rb @@ -0,0 +1,13 @@ +module Emojifier + def emojify + self.content.gsub(/:([\w+-]+):/) do |match| + name = Regexp.last_match(1) + emoji = Emoji.find_by_alias(name) + return match unless emoji + ActionController::Base.helpers.image_tag( + ActionController::Base.helpers.image_path("emoji/#{emoji.image_filename}"), + alt: name, class: 'emoji' + ) + end + end +end diff --git a/app/models/concerns/markdown_render.rb b/app/models/concerns/markdown_render.rb index 4f764ca..fef260f 100644 --- a/app/models/concerns/markdown_render.rb +++ b/app/models/concerns/markdown_render.rb @@ -3,6 +3,7 @@ module MarkdownRender HTML_RENDER_OPTIONS = { hard_wrap: true } def render_content - Redcarpet::Markdown.new(Redcarpet::Render::HTML.new(HTML_RENDER_OPTIONS), MARKDOWN_RENDER_OPTIONS).render(self.content) + content = self.class.include?(Emojifier) ? emojify : self.content + Redcarpet::Markdown.new(Redcarpet::Render::HTML.new(HTML_RENDER_OPTIONS), MARKDOWN_RENDER_OPTIONS).render(content) end end diff --git a/app/models/page.rb b/app/models/page.rb index b6ef30b..077a7a1 100644 --- a/app/models/page.rb +++ b/app/models/page.rb @@ -1,4 +1,5 @@ class Page < ActiveRecord::Base + include Emojifier include MarkdownRender has_many :comments diff --git a/app/views/comments/_list.html.haml b/app/views/comments/_list.html.haml index 11a5164..4c82b03 100644 --- a/app/views/comments/_list.html.haml +++ b/app/views/comments/_list.html.haml @@ -16,5 +16,5 @@ .user .icon= image_tag @current_user.icon_url .box - = f.text_area :content, rows: 4 + = f.text_area :content, rows: 4, class: 'emoji-complete' = f.submit 'Comment' diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 82085a9..7ea23b6 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -6,6 +6,7 @@ = content_for(:css) = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true = content_for(:javascript) + = include_gon = javascript_include_tag 'application', 'data-turbolinks-track' => true = csrf_meta_tags %body diff --git a/app/views/pages/edit.html.haml b/app/views/pages/edit.html.haml index 8db5297..cef6385 100644 --- a/app/views/pages/edit.html.haml +++ b/app/views/pages/edit.html.haml @@ -3,7 +3,7 @@ = form_for @page, url: update_page_path, method: :patch do |f| = f.hidden_field :path = f.text_field :title - = f.text_area :content, rows: 30 + = f.text_area :content, rows: 30, class: 'emoji-complete' = f.submit 'Save' = f.submit 'Draft Save', name: 'draft' = link_to 'Cancel', page_path(@page.path) diff --git a/config/deploy.rb b/config/deploy.rb index b2ef2ab..a335eff 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -20,3 +20,25 @@ invoke 'unicorn:restart' end end + +before 'deploy:compile_assets', 'bower:install' +namespace :bower do + task :install do + on roles(:web) do + within release_path do + execute :rake, 'bower:install CI=true' + end + end + end +end + +before 'deploy:compile_assets', 'gemoji:install' +namespace :gemoji do + task :install do + on roles(:web) do + within release_path do + execute :rake, 'gemoji' + end + end + end +end diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 1ae31fd..e54f596 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -4,8 +4,11 @@ Rails.application.config.assets.version = '1.0' # Add additional assets to the asset load path -# Rails.application.config.assets.paths << Emoji.images_path +Rails.application.config.assets.paths << Emoji.images_path +Rails.application.config.assets.paths << Rails.root.join('vendor', 'assets', 'bower_components') # Precompile additional assets. # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. # Rails.application.config.assets.precompile += %w(*.css *.scss) +Rails.application.config.assets.precompile += %w(emoji/*.png) +Rails.application.config.assets.precompile += %w(emoji/**/*.png) diff --git a/config/initializers/bower_rails.rb b/config/initializers/bower_rails.rb new file mode 100644 index 0000000..8b5d67f --- /dev/null +++ b/config/initializers/bower_rails.rb @@ -0,0 +1,19 @@ +BowerRails.configure do |bower_rails| + # Tell bower-rails what path should be considered as root. Defaults to Dir.pwd + # bower_rails.root_path = Dir.pwd + + # Invokes rake bower:install before precompilation. Defaults to false + bower_rails.install_before_precompile = true + + # Invokes rake bower:resolve before precompilation. Defaults to false + # bower_rails.resolve_before_precompile = true + + # Invokes rake bower:clean before precompilation. Defaults to false + bower_rails.clean_before_precompile = true + + # Invokes rake bower:install:deployment instead rake bower:install. Defaults to false + # bower_rails.use_bower_install_deployment = true + # + # Invokes rake bower:install and rake bower:install:deployment with -F (force) flag. Defaults to false + # bower_rails.force_install = true +end diff --git a/spec/factories/comments.rb b/spec/factories/comments.rb index 2c5c395..d9064c4 100644 --- a/spec/factories/comments.rb +++ b/spec/factories/comments.rb @@ -1,8 +1,7 @@ FactoryGirl.define do factory :comment do - user nil -post nil -content "MyText" + content 'This is a comment.' + association :user + association :page end - end diff --git a/spec/factories/pages.rb b/spec/factories/pages.rb new file mode 100644 index 0000000..03d422a --- /dev/null +++ b/spec/factories/pages.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do + factory :page do + path 'sample' + title 'sample' + content 'This is a page.' + end +end diff --git a/spec/factories/slack_credentials.rb b/spec/factories/slack_credentials.rb index 28faa92..ca50f7d 100644 --- a/spec/factories/slack_credentials.rb +++ b/spec/factories/slack_credentials.rb @@ -1,5 +1,5 @@ FactoryGirl.define do factory :slack_credential do - user_id 'U03AE1H0U' + sequence(:slack_user_id) { |n| "U03AE1H0U#{n}" } end end diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index c10688d..7151c3e 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -1,5 +1,5 @@ require 'rails_helper' RSpec.describe Comment, type: :model do - pending "add some examples to (or delete) #{__FILE__}" + it_behaves_like 'emojifier' end diff --git a/spec/models/concerns/emojifier.rb b/spec/models/concerns/emojifier.rb new file mode 100644 index 0000000..ab1cae9 --- /dev/null +++ b/spec/models/concerns/emojifier.rb @@ -0,0 +1,20 @@ +require 'rails_helper' + +shared_examples_for 'emojifier' do + let(:model) { described_class } + let(:factory) { model.to_s.underscore.to_sym } + + subject { object.emojify } + + context 'contents do not include any emojis' do + let(:object) { FactoryGirl.create(factory, content: 'Example content') } + it { should eq(object.content) } + end + + context 'contents include some emojis' do + let(:object) { FactoryGirl.create(factory, content: 'Example content :smile: :+1:') } + + it { should_not match(/:[\w+-]+:/) } + it { should match(/