Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
gdpelican committed Nov 22, 2015
0 parents commit 1bbf27b
Show file tree
Hide file tree
Showing 8 changed files with 305 additions and 0 deletions.
8 changes: 8 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# The MIT License
Copyright (c) 2009-2014 Stuart Knightley, David Duponchel, Franz Buchinger, António Afonso

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Empty file added README.md
Empty file.
31 changes: 31 additions & 0 deletions assets/javascripts/discourse/initializers/retort-button.js.es6
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import PostMenuComponent from 'discourse/components/post-menu';
import { Button } from 'discourse/components/post-menu';
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
import { popupAjaxError } from 'discourse/lib/ajax-error';
import { showSelector } from "discourse/lib/emoji/emoji-toolbar";

export default {
name: 'retort-button',
initialize: function() {
PostMenuComponent.registerButton(function(visibleButtons) {
if (!Discourse.User.current() || !this.siteSettings.retort_enabled) { return }
return visibleButtons.splice(0, 0, new Button('retort', 'retort.title', 'smile-o'))
})

PostMenuComponent.reopen({
clickRetort: function(post) {
const self = this
showSelector({
container: self.container,
onSelect: function(retort) {
Discourse.ajax('/retorts/' + self.get('post.id') + '.json', {
type: 'POST',
data: { retort: retort }
})
return false
}
})
}
})
}
}
Empty file added assets/stylesheets/retort.scss
Empty file.
4 changes: 4 additions & 0 deletions config/settings.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
plugins:
retort_enabled:
default: true
client: true
123 changes: 123 additions & 0 deletions plugin.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# name: babble
# about: Reactions plugin for Discourse
# version: 0.0.1
# authors: James Kiesel (gdpelican)
# url: https://github.com/gdpelican/retort

register_asset "stylesheets/retort.scss"

RETORT_PLUGIN_NAME ||= "retort".freeze

enabled_site_setting :retort_enabled

after_initialize do
module ::Retort
class Engine < ::Rails::Engine
engine_name RETORT_PLUGIN_NAME
isolate_namespace Retort
end
end

::Retort::Engine.routes.draw do
post "/:post_id" => "retorts#update"
delete "/:post_id" => "retorts#destroy"
get "/index" => "retorts#index"
end

Discourse::Application.routes.append do
mount ::Retort::Engine, at: "/retorts"
end

class ::Retort::RetortsController < ApplicationController
before_filter :verify_post_and_user, only: [:update, :destroy]

def index
render json: serialized_retorts
end

def update
@retort = Retort::Retort.new(post, current_user, params[:retort]).save
respond_with_retort
end

def destroy
@retort = Retort::Retort.new(post, current_user).save
respond_with_retort
end

private

def post
@post ||= Post.find_by(id: params[:post_id]) if params[:post_id]
end

def topic
@topic ||= Topic.find_by(id: params[:topic_id]) if params[:topic_id]
end

def retorts
@retorts ||= Retort::Retort.where(post: post, topic: topic)
end

def verify_post_and_user
respond_with_unprocessable("Unable to find post #{params[:post_id]}") unless post
respond_with_unprocessable("You are not permitted to modify this") unless current_user
end

def respond_with_retort
if @retort && @retort.valid?
MessageBus.publish("/topic/#{params[:topic_id] || post.topic_id}", serialized_retorts)
render json: { success: :ok }
else
respond_with_unprocessable("Unable to save that retort. Please try again")
end
end

def serialized_retorts
ActiveModel::ArraySerializer.new(retorts, each_serializer: ::Retort::RetortSerializer)
end

def respond_with_unprocessable(error)
render json: { errors: error }, status: :unprocessable_entity
end
end

class ::Retort::RetortSerializer < ActiveModel::Serializer
attributes :user_id, :post_id, :emoji
define_method :post_id, -> { object.post.id }
define_method :user_id, -> { object.key.match(/\d+/).to_s.to_i }
define_method :emoji, -> { object.extra }
end

::Retort::Retort = Struct.new(:post, :user, :retort) do

def self.where(post: nil, topic: nil, user: nil)
where_params = { value: RETORT_PLUGIN_NAME }
where_params.merge!(key: :"retort_#{user.id}") if user

if post
PostDetail.where(where_params.merge(post: post))
elsif topic
PostDetail.joins(:post).where(where_params).where('posts.topic_id = ?', topic.id)
else
PostDetail.none
end
end

def save
if existing = PostDetail.find_by(detail_params)
retort.present? ? existing.update(extra: retort) : existing.destroy
existing
else
PostDetail.create(detail_params.merge(extra: retort))
end
end

private

def detail_params
{ post: post, key: :"retort_#{user.id}", value: RETORT_PLUGIN_NAME }
end
end

end
38 changes: 38 additions & 0 deletions spec/controllers/retorts_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require "spec_helper"

path = "./plugins/retort/plugin.rb"
source = File.read(path)
plugin = Plugin::Instance.new(Plugin::Metadata.parse(source), path)
plugin.activate!
plugin.initializers.first.call

describe ::Retort::RetortsController do
routes { ::Retort::Engine.routes }

before do
SiteSetting.load_settings(File.join(Rails.root, 'plugins', 'retort', 'config', 'settings.yml'))
end

describe "index" do
it "returns a list retorts for a post" do
xhr :get, :index
end

it "returns a list of retorts for a topic" do
end
end

describe "update" do
it "updates a retort" do
end
end

describe "destroy" do
it "destroys a retort"
end

def response_json
JSON.parse(response.body)
end

end
101 changes: 101 additions & 0 deletions spec/models/retort_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
require "spec_helper"

path = "./plugins/retort/plugin.rb"
source = File.read(path)
plugin = Plugin::Instance.new(Plugin::Metadata.parse(source), path)
plugin.activate!
plugin.initializers.first.call

describe ::Retort::Retort do
before do
SiteSetting.load_settings(File.join(Rails.root, 'plugins', 'retort', 'config', 'settings.yml'))
end

let(:user) { Fabricate :user }
let(:topic) { Fabricate :topic }
let(:post) { Fabricate :post, topic: topic }
let(:another_post) { Fabricate :post, topic: topic }
let(:another_topic_post) { Fabricate :post }
let(:emoji) { 'kickbutt' }
let(:altermoji) { 'puntrear' }
let(:retort) { Retort::Retort.new(post, user, emoji) }

describe 'initialize' do
it 'stores the post' do
expect(retort.post).to eq post
end
it 'stores the user' do
expect(retort.user).to eq user
end
it 'stores the retort' do
expect(retort.retort).to eq emoji
end
end

describe 'where' do
let(:another_user) { Fabricate :user }
let(:a_third_user) { Fabricate :user }
let!(:existing) { Fabricate :post_detail, post: post, key: "retort_#{user.id}", value: 'retort', extra: emoji }
let!(:another_existing) { Fabricate :post_detail, post: post, key: "retort_#{another_user.id}", value: :retort, extra: emoji }
let!(:another_post_retort) { Fabricate :post_detail, post: another_post, key: "retort_#{user.id}", value: :retort, extra: emoji }
let!(:another_topic_retort) { Fabricate :post_detail, post: another_topic_post, key: "retort_#{user.id}", value: :retort, extra: emoji }

it 'finds an existing retort by post / user' do
found = Retort::Retort.where(post: post, user: user)
expect(found).to include existing
expect(found).to_not include another_existing
end

it 'finds existing retorts by topic / user' do
found = Retort::Retort.where(topic: topic)
end

it 'finds all retorts for a post if no user is specified' do
found = Retort::Retort.where(post: post)
expect(found).to include existing
expect(found).to include another_existing
expect(found).to_not include another_post_retort
expect(found).to_not include another_topic_retort
end

it 'finds all retorts for a topic if no user is specified' do
found = Retort::Retort.where(topic: topic)
expect(found).to include existing
expect(found).to include another_existing
expect(found).to include another_post_retort
expect(found).to_not include another_topic_retort
end

it 'returns empty when no post or topic is specified' do
found = Retort::Retort.where(user: user)
expect(found).to be_empty
end

it 'returns empty when no retort exists' do
expect(Retort::Retort.where(post: post, user: a_third_user)).to be_empty
end
end

describe 'save' do
it 'saves the retort to the posts details' do
expect { retort.save }.to change { PostDetail.count }.by(1)
expect(PostDetail.find_by(post: post, key: "retort_#{user.id}", value: :retort, extra: emoji)).to be_present
end

it 'overwrites an existing retort' do
retort.save
retort.retort = altermoji
expect { retort.save }.not_to change { PostDetail.count }
expect(PostDetail.find_by(post: post, key: "retort_#{user.id}", value: :retort, extra: altermoji)).to be_present
expect(PostDetail.find_by(post: post, key: "retort_#{user.id}", value: :retort, extra: emoji)).not_to be_present
end

it 'deletes an empty retort' do
retort.save
retort.retort = ''
expect { retort.save }.to change { PostDetail.count }.by(-1)
expect(PostDetail.find_by(post: post, key: "retort_#{user.id}", value: :retort, extra: emoji)).not_to be_present
end
end

end

0 comments on commit 1bbf27b

Please sign in to comment.