From 9b20e633c0934efd037dfb50f6674c343a0b9e82 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 10 Mar 2016 17:37:58 -0500 Subject: [PATCH] Basic Functionality --- .eslintrc | 102 ++++++++++++++++++ .../discourse/controllers/staff-notes.js.es6 | 28 +++++ .../initializers/enable-staff-notes.js.es6 | 30 ++++++ .../discourse/templates/modal/staff-notes.hbs | 16 +++ assets/stylesheets/staff_notes.scss | 23 ++++ config/locales/client.en.yml | 5 + config/locales/server.en.yml | 3 + config/settings.yml | 4 + plugin.rb | 99 +++++++++++++++++ 9 files changed, 310 insertions(+) create mode 100644 .eslintrc create mode 100644 assets/javascripts/discourse/controllers/staff-notes.js.es6 create mode 100644 assets/javascripts/discourse/initializers/enable-staff-notes.js.es6 create mode 100644 assets/javascripts/discourse/templates/modal/staff-notes.hbs create mode 100644 assets/stylesheets/staff_notes.scss create mode 100644 config/locales/client.en.yml create mode 100644 config/locales/server.en.yml create mode 100644 config/settings.yml create mode 100644 plugin.rb diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..d2a13ae --- /dev/null +++ b/.eslintrc @@ -0,0 +1,102 @@ +{ + "env": { + "jasmine": true, + "node": true, + "mocha": true, + "browser": true, + "builtin": true + }, + ecmaVersion: 7, + "globals": + {"Ember":true, + "jQuery":true, + "$":true, + "RSVP":true, + "Discourse":true, + "Em":true, + "PreloadStore":true, + "Handlebars":true, + "I18n":true, + "bootbox":true, + "module":true, + "moduleFor":true, + "moduleForComponent":true, + "Pretender":true, + "sandbox":true, + "controllerFor":true, + "test":true, + "ok":true, + "not":true, + "expect":true, + "equal":true, + "visit":true, + "andThen":true, + "click":true, + "currentPath":true, + "currentRouteName":true, + "currentURL":true, + "fillIn":true, + "keyEvent":true, + "triggerEvent":true, + "count":true, + "exists":true, + "visible":true, + "invisible":true, + "asyncRender":true, + "selectDropdown":true, + "asyncTestDiscourse":true, + "fixture":true, + "find":true, + "sinon":true, + "moment":true, + "start":true, + "_":true, + "alert":true, + "containsInstance":true, + "deepEqual":true, + "notEqual":true, + "define":true, + "require":true, + "requirejs":true, + "hasModule":true, + "Blob":true, + "File":true}, + "rules": { + "block-scoped-var": 2, + "dot-notation": 0, + "eqeqeq": [ + 2, + "allow-null" + ], + "guard-for-in": 2, + "no-bitwise": 2, + "no-caller": 2, + "no-cond-assign": 0, + "no-debugger": 2, + "no-empty": 0, + "no-eval": 2, + "no-extend-native": 2, + "no-extra-parens": 0, + "no-irregular-whitespace": 2, + "no-iterator": 2, + "no-loop-func": 2, + "no-multi-str": 2, + "no-new": 2, + "no-plusplus": 0, + "no-proto": 2, + "no-script-url": 2, + "no-sequences": 2, + "no-shadow": 2, + "no-undef": 2, + "no-unused-vars": 2, + "no-with": 2, + "semi": 2, + "strict": 0, + "valid-typeof": 2, + "wrap-iife": [ + 2, + "inside" + ] + }, + "parser": "babel-eslint" +} diff --git a/assets/javascripts/discourse/controllers/staff-notes.js.es6 b/assets/javascripts/discourse/controllers/staff-notes.js.es6 new file mode 100644 index 0000000..80cd594 --- /dev/null +++ b/assets/javascripts/discourse/controllers/staff-notes.js.es6 @@ -0,0 +1,28 @@ +import { default as computed, on } from 'ember-addons/ember-computed-decorators'; +import { popupAjaxError } from 'discourse/lib/ajax-error'; + +export default Ember.Controller.extend({ + newNote: null, + saving: false, + + @on('init') + reset() { + this.setProperties({ newNote: null, saving: false }); + }, + + @computed('newNote', 'saving') + attachDisabled(newNote, saving) { + return saving || !newNote || (newNote.length === 0); + }, + + actions: { + attachNote() { + const note = this.store.createRecord('staff-note'); + this.set('saving', true); + note.save({ raw: this.get('newNote'), user_id: this.get('userId') }).then(() => { + this.set('newNote', ''); + this.get('model').pushObject(note); + }).catch(popupAjaxError).finally(() => this.set('saving', false)); + } + } +}); diff --git a/assets/javascripts/discourse/initializers/enable-staff-notes.js.es6 b/assets/javascripts/discourse/initializers/enable-staff-notes.js.es6 new file mode 100644 index 0000000..d81bcf4 --- /dev/null +++ b/assets/javascripts/discourse/initializers/enable-staff-notes.js.es6 @@ -0,0 +1,30 @@ +import { withPluginApi } from 'discourse/lib/plugin-api'; +import showModal from 'discourse/lib/show-modal'; + +export default { + name: 'enable-staff-notes', + initialize(container) { + const siteSettings = container.lookup('site-settings:main'); + if (!siteSettings.staff_notes_enabled) { return; } + + withPluginApi('0.2', api => { + api.decorateWidget('poster-name:after', dec => dec.attach('staff-notes-icon')); + + api.createWidget('staff-notes-icon', { + tagName: 'span.staff-notes-icon', + + html() { + return this.attach('emoji', { name: 'pencil' }); + }, + + click() { + return this.store.find('staff-note', { user_id: this.attrs.user_id }).then(model => { + const controller = showModal('staff-notes', { model, title: 'staff_notes.title' }); + controller.reset(); + controller.set('userId', this.attrs.user_id); + }); + } + }); + }); + }, +}; diff --git a/assets/javascripts/discourse/templates/modal/staff-notes.hbs b/assets/javascripts/discourse/templates/modal/staff-notes.hbs new file mode 100644 index 0000000..946b40b --- /dev/null +++ b/assets/javascripts/discourse/templates/modal/staff-notes.hbs @@ -0,0 +1,16 @@ + diff --git a/assets/stylesheets/staff_notes.scss b/assets/stylesheets/staff_notes.scss new file mode 100644 index 0000000..8391fe5 --- /dev/null +++ b/assets/stylesheets/staff_notes.scss @@ -0,0 +1,23 @@ +.staff-notes-modal { + height: 300px; + + .posted-by { + width: 30px; + float: left; + } + .cooked { + float: left; + + p { + margin: 0 0 1em 0; + } + } + + .staff-note { + margin-bottom: 1em; + } +} + +.staff-notes-icon { + cursor: pointer; +} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml new file mode 100644 index 0000000..0f128b7 --- /dev/null +++ b/config/locales/client.en.yml @@ -0,0 +1,5 @@ +en: + js: + staff_notes: + title: "Staff Notes" + attach: "Attach Note" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml new file mode 100644 index 0000000..092ad31 --- /dev/null +++ b/config/locales/server.en.yml @@ -0,0 +1,3 @@ +en: + site_settings: + staff_notes_enabled: "Allow staff users to attach notes to users" diff --git a/config/settings.yml b/config/settings.yml new file mode 100644 index 0000000..d2603f0 --- /dev/null +++ b/config/settings.yml @@ -0,0 +1,4 @@ +plugins: + staff_notes_enabled: + default: false + client: true diff --git a/plugin.rb b/plugin.rb new file mode 100644 index 0000000..ce7bbca --- /dev/null +++ b/plugin.rb @@ -0,0 +1,99 @@ +# name: staff-notes +# about: Gives the ability for staff members to attach notes to users +# version: 0.0.1 +# authors: Robin Ward + +enabled_site_setting :staff_notes_enabled + +register_asset 'stylesheets/staff_notes.scss' + +after_initialize do + + require_dependency 'user' + + module ::DiscourseStaffNotes + class Engine < ::Rails::Engine + engine_name "discourse_staff_notes" + isolate_namespace DiscourseStaffNotes + end + + def self.key_for(user_id) + "notes:#{user_id}" + end + + def self.notes_for(user_id) + PluginStore.get('staff_notes', key_for(user_id)) || [] + end + + def self.add_note(user_id, raw, created_by) + notes = notes_for(user_id) + record = { id: SecureRandom.hex(16), raw: raw, created_by: created_by, created_at: Time.now } + notes << record + ::PluginStore.set("staff_notes", key_for(user_id), notes) + + record + end + + end + + require_dependency 'application_serializer' + class ::StaffNoteSerializer < ApplicationSerializer + attributes :id, :raw, :created_by, :created_at + + def id + object[:id] + end + + def raw + object[:raw] + end + + def created_by + user = User.where(id: object[:created_by]).first + return nil if user.blank? + + BasicUserSerializer.new(user, scope: scope, root: false) + end + + def created_at + object[:created_at] + end + end + + require_dependency 'application_controller' + class DiscourseStaffNotes::StaffNotesController < ::ApplicationController + before_filter :ensure_logged_in + before_filter :ensure_staff + + def index + user = User.where(id: params[:user_id]).first + raise Discourse::NotFound if user.blank? + + notes = ::DiscourseStaffNotes.notes_for(params[:user_id]) + render json: { + extras: { username: user.username }, + staff_notes: serialize_data(notes, ::StaffNoteSerializer) + } + end + + def create + user_id = params[:staff_note][:user_id] + + user = User.where(id: user_id).first + raise Discourse::NotFound if user.blank? + staff_note = ::DiscourseStaffNotes.add_note(user.id, params[:staff_note][:raw], current_user.id) + + render json: serialize_data(staff_note, ::StaffNoteSerializer) + end + end + + DiscourseStaffNotes::Engine.routes.draw do + get '/' => 'staff_notes#index' + post '/' => 'staff_notes#create' + end + + Discourse::Application.routes.append do + mount ::DiscourseStaffNotes::Engine, at: "/staff_notes" + end + +end