diff --git a/app/assets/fonts/Sanchez.eot b/app/assets/fonts/Sanchez.eot new file mode 100644 index 0000000..243248e Binary files /dev/null and b/app/assets/fonts/Sanchez.eot differ diff --git a/app/assets/fonts/Sanchez.svg b/app/assets/fonts/Sanchez.svg new file mode 100644 index 0000000..894a1b0 --- /dev/null +++ b/app/assets/fonts/Sanchez.svg @@ -0,0 +1,894 @@ + + +This is a Webfont from MyFonts. Full information about this font: +http://www.myfonts.com/fonts/latinotype/sanchez/ +Copyright (c) 2011 by Daniel Hern·ndez. All rights reserved.Sanchez Black is a trademark of Daniel Hern·ndez. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/assets/fonts/Sanchez.ttf b/app/assets/fonts/Sanchez.ttf new file mode 100644 index 0000000..0e97895 Binary files /dev/null and b/app/assets/fonts/Sanchez.ttf differ diff --git a/app/assets/fonts/Sanchez.woff b/app/assets/fonts/Sanchez.woff new file mode 100644 index 0000000..05723b7 Binary files /dev/null and b/app/assets/fonts/Sanchez.woff differ diff --git a/app/assets/fonts/Sanchez.woff2 b/app/assets/fonts/Sanchez.woff2 new file mode 100644 index 0000000..ba6e98a Binary files /dev/null and b/app/assets/fonts/Sanchez.woff2 differ diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 99175b4..19ef922 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -184,6 +184,22 @@ }, false); } }, node || document); + forEachElement('[data-opens]', function(control) { + control.addEventListener('click', function(event) { + var opens = document.querySelector(control.getAttribute('data-opens')); + if (!opens.classList.contains('open')){ + event.preventDefault(); + opens.className += ' open'; + if (control.getAttribute('data-focus')) { + var input = opens.querySelector(control.getAttribute('data-focus')); + if (input) { + input.focus(); + } + } + return false; + } + }); + }); } ]; window.initNode = function(node) { forEach(initNodeFunctions, function(fn) { diff --git a/app/assets/stylesheets/_application.scss b/app/assets/stylesheets/_application.scss index f6113a4..3a0ad4b 100644 --- a/app/assets/stylesheets/_application.scss +++ b/app/assets/stylesheets/_application.scss @@ -794,6 +794,93 @@ fieldset { margin-bottom: 3em; } +.comments { + list-style: none; + + .comment { + position: relative; + border: 0.1rem solid #EEE; + border-radius: 0.25rem; + padding: 1em; + margin-top: 2em; + } + + .comment-body { + font-size: 0.9em; + } + + .comment-title { + position: absolute; + top: -0.75em; + left: 0.5em; + margin: 0; + padding: 0 0.5em; + background-color: #FFF; + } + + time { + position: absolute; + top: -0.75em; + right: 0.75em; + background-color: #FFF; + padding: 0 0.5em; + } + + .sub-comment { + font-size: 0.8em; + + .comment-title, time { + top: -1em; + } + } + + .text-area-field { + overflow: hidden; + max-height: 0; + padding: 0.3em; + @include _(transition, max-height 150ms ease-in-out); + } + + #main .columns & form { + margin-top: 0; + button { + @include _(transition, background-color 150ms ease-in-out); + background-color: #888; + } + + &.open { + @include _(opacity, 1); + + .text-area-field { + max-height: 12em; + } + + button { + background-color: $colour-5; + } + } + } + + button { + margin: 0; + } +} + +#comments { + textarea { + font-size: 1.25em; + min-height: 7.5em; + } + + .actions { + margin: 0; + + button { + margin: 0; + } + } +} + .flex-form, .flex-column { button, .button { width: 100%; @@ -3502,7 +3589,8 @@ html[data-ontop] { @include _(flex, none); @include _(flex-basis, auto); @include _(flex-grow, 1); - margin: 0; + margin: 0 0 0.25em 1em; + text-align: left; } } diff --git a/app/assets/stylesheets/bumbleberry-settings.json b/app/assets/stylesheets/bumbleberry-settings.json index 8cec740..6782c53 100644 --- a/app/assets/stylesheets/bumbleberry-settings.json +++ b/app/assets/stylesheets/bumbleberry-settings.json @@ -63,9 +63,9 @@ "font-loading-method": "http2", "fonts": { "primary": { - "name": "queulat", - "location": "queulat", - "svg_id": "queulat", + "name": "Sanchez Light", + "location": "Sanchez", + "svg_id": "wf", "ttf_type": "ttf", "fallback": ["Helvetica Neue", "Helvetica", "Arial", "Lucida Grande", "sans-serif"] }, diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb index 6557991..3364a8b 100644 --- a/app/controllers/conferences_controller.rb +++ b/app/controllers/conferences_controller.rb @@ -1793,6 +1793,35 @@ class ConferencesController < ApplicationController return redirect_to view_workshop_url(@this_conference.slug, params[:workshop_id]) end + def add_comment + set_conference + set_conference_registration + workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id) + + return do_404 unless workshop && current_user + + if params[:button] == 'reply' + comment = Comment.find_by!(id: params[:comment_id].to_i, model_type: :workshops, model_id: workshop.id) + new_comment = comment.add_comment(current_user, params[:reply]) + + UserMailer.send_mail :workshop_comment do + [ workshop, new_comment, comment.user ] + end + elsif params[:button] = 'add_comment' + new_comment = workshop.add_comment(current_user, params[:comment]) + + workshop.active_facilitators.each do | u | + UserMailer.send_mail :workshop_comment do + [ workshop, new_comment, u ] + end + end + else + return do_404 + end + + return redirect_to view_workshop_url(@this_conference.slug, workshop.id, anchor: "comment-#{new_comment.id}") + end + def schedule set_conference return do_404 unless @this_conference.workshop_schedule_published || @this_conference.host?(current_user) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index d592a74..9eab7e7 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -972,17 +972,20 @@ module ApplicationHelper strip_tags(text.gsub('>', '> ')).gsub(/^(.{40,60})\s.*$/m, '\1…').html_safe end - def textarea(name, value, options = {}) + def textarea(name, value = nil, options = {}) id = name.to_s.gsub('[', '_').gsub(']', '') label_id = "#{id}-label" description_id = nil + html = '' - if options[:label].present? - html = label_tag(id, nil, id: label_id) do + if options[:label] == false + label_id = options[:labelledby] + elsif options[:label].present? + html += label_tag(id, nil, id: label_id) do _(options[:label], :t, vars: options[:vars] || {}) end else - html = label_tag(id, nil, id: label_id) + html += label_tag(id, nil, id: label_id) end if options[:help].present? @@ -995,22 +998,32 @@ module ApplicationHelper html += content_tag(:div, _(options[:warning], :s, 2), id: description_id, class: 'warning-info') end - html += content_tag(:div, value.present? ? value.html_safe : '', + aria = {} + aria[:labeledby] = label_id if label_id.present? + aria[:describedby] = description_id if description_id.present? + if options[:plain] + html += (text_area_tag name, value, id: id, - class: 'textarea', - data: { name: name, 'edit-on': options[:edit_on] || :load }, lang: options[:lang], - aria: { labeledby: label_id, describedby: description_id }, - tabindex: 0 - ) - - html = content_tag(:div, html, class: ['text-area-field', 'input-field']).html_safe - html += _original_content(options[:original_value], options[:original_lang]) if options[:original_value].present? + aria: aria) + else + html += content_tag(:div, value.present? ? value.html_safe : '', + id: id, + class: 'textarea', + data: { name: name, 'edit-on': options[:edit_on] || :load }, + lang: options[:lang], + aria: aria, + tabindex: 0 + ) + + add_stylesheet :editor + add_inline_script :pen + add_inline_script :markdown + add_inline_script :editor + end - add_stylesheet :editor - add_inline_script :pen - add_inline_script :markdown - add_inline_script :editor + html = content_tag(:div, html.html_safe, class: ['text-area-field', 'input-field']).html_safe + html += _original_content(options[:original_value], options[:original_lang]) if options[:original_value].present? return html.html_safe end @@ -1237,6 +1250,16 @@ module ApplicationHelper return html.html_safe end + def comment(comment) + content_tag(:div, class: 'comment-body') do + content_tag(:h4, comment.user.name, class: 'comment-title') + + content_tag(:time, time(comment.created_at, :default), datetime: comment.created_at.to_s) + + content_tag(:div, class: 'comment-text') do + markdown comment.comment + end + end + end + private def _original_content(value, lang) content_tag(:div, ( diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 75e03e8..1eabbad 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -159,6 +159,20 @@ class UserMailer < ActionMailer::Base mail to: user.named_email, subject: @subject end + def workshop_comment(workshop, comment, user) + @workshop = workshop + @comment = comment + @user = user + + if comment.reply? + @subject = (_'email.subject.workshop_comment.reply', vars: { user_name: comment.user.name }) + else + @subject = (_'email.subject.workshop_comment.comment', vars: { user_name: comment.user.name, workshop_title: workshop.title }) + end + + mail to: user.named_email, subject: @subject + end + def error_report(subject, message, report, exception, request, params, user) @subject = subject @message = message diff --git a/app/models/comment.rb b/app/models/comment.rb new file mode 100644 index 0000000..1c3ddc6 --- /dev/null +++ b/app/models/comment.rb @@ -0,0 +1,37 @@ +class Comment < ActiveRecord::Base + belongs_to :user + + def comment_object + model_type.classify.constantize.find(model_id) + end + + def set_model(model) + model_type = model.class.name.tableize + model_id = model.id + end + + def self.for(model) + where(model_type: model.class.name.tableize, model_id: model.id).order(created_at: :asc) + end + + def self.create_for(model, user, comment) + create( + model_type: model.class.name.tableize, + model_id: model.id, + user_id: user.id, + comment: comment + ) + end + + def add_comment(user, comment) + Comment.create_for(self, user, comment) + end + + def comments + Comment.for(self) + end + + def reply? + model_type == 'comments' + end +end diff --git a/app/models/workshop.rb b/app/models/workshop.rb index 7318a71..c9527c4 100644 --- a/app/models/workshop.rb +++ b/app/models/workshop.rb @@ -146,6 +146,14 @@ class Workshop < ActiveRecord::Base end return notify_list end + + def comments + Comment.for(self) + end + + def add_comment(user, comment) + Comment.create_for(self, user, comment) + end private def make_slug diff --git a/app/views/user_mailer/workshop_comment.html.haml b/app/views/user_mailer/workshop_comment.html.haml new file mode 100644 index 0000000..f3a2cdb --- /dev/null +++ b/app/views/user_mailer/workshop_comment.html.haml @@ -0,0 +1,15 @@ +%p=_('email.workshop_comment.paragraph.user_said', vars: {user_name: @comment.user.name}) +%blockquote + =_!@comment.comment + + - if @comment.reply? + - original_comment = @comment.comment_object + %p=_('email.workshop_comment.paragraph.user_said', vars: {user_name: original_comment.user.name}) + %blockquote + =_!original_comment.comment + + +%p + =_'email.workshop.paragraph.view_workshop' + - workshop_link = @host + view_workshop_path(@workshop.conference.slug, @workshop.id) + %a{href: workshop_link}=workshop_link diff --git a/app/views/workshops/_show.html.haml b/app/views/workshops/_show.html.haml index f780f41..ccd315e 100644 --- a/app/views/workshops/_show.html.haml +++ b/app/views/workshops/_show.html.haml @@ -67,3 +67,24 @@ = columns(medium: 12, class: 'workshop-notes') do %h3=_'articles.workshops.headings.notes','Notes' = richtext workshop.notes, 3 + = columns(medium: 12, id: :comments) do + %h3=_'articles.workshops.headings.Comments' + %ul.comments + - workshop.comments.each do | comment | + %li.comment{id: "comment-#{comment.id}"} + = comment(comment) + - sub_comments = comment.comments + - if sub_comments.present? + %ul.sub-comments.comments + - sub_comments.each do | sub_comment | + %li.sub-comment.comment{id: "comment-#{sub_comment.id}"} + = comment(sub_comment) + = form_tag workshop_comment_path(workshop.conference.slug, workshop.id) do + = hidden_field_tag :comment_id, comment.id + = textarea :reply, nil, plain: true, required: true, label: false, labelledby: "replyto-#{comment.id}" + .actions.right + = button_tag :reply, value: :reply, data: {opens: "#comment-#{comment.id} form", focus: :textarea}, class: :small, id: "replyto-#{comment.id}" + = form_tag workshop_comment_path(workshop.conference.slug, workshop.id) do + = textarea :comment, nil, plain: true, required: true, label: false, labelledby: :add_comment + .actions.right + = button_tag :add_comment, value: :add_comment, id: :add_comment diff --git a/config/locales/en.yml b/config/locales/en.yml index c27f311..4185ecf 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -234,7 +234,7 @@ en: time: am: am formats: - default: "%a, %d %b %Y %H:%M:%S %z" + default: "%a, %d %b %Y %H:%M" long: "%l%P, %B %e, %Y" short: "%l:%M%P" pm: pm @@ -5542,6 +5542,7 @@ en: facilitate: Request to Facilitate ‘%{workshop_title}’ add_facilitator: Add a facilitator needs_facilitators: Looking for help? + Comments: Comments paragraphs: Proposed_Workshops: Would you like to facilitate your own workshop? Simply register and visit the workshops page. If you have already registered you @@ -5657,6 +5658,8 @@ en: delete: Delete place_guest: Place Guest set_host: Set Host + add_comment: Add Comment + reply: Reply aria: remove_interest: Click if you are no longer interested in this workshop show_interest: Click if you are interested in this workshop @@ -5806,6 +5809,9 @@ en: denied workshop_translated: The %{language} translation for %{workshop_title} has been modified workshop_original_content_changed: Original content for %{workshop_title} has been modified + workshop_comment: + comment: '%{user_name} commented on %{workshop_title}' + reply: '%{user_name} replied to your comment' general: paragraph: see_you: See you in %{conference_location}! @@ -5825,6 +5831,9 @@ en: paragraph: workshop_translated: '%{user_name} has modified the %{language} translation for %{workshop_title}.' workshop_original_content_changed: '%{user_name} has modified the original content for %{workshop_title}. Translations may need to be updated.' + workshop_comment: + paragraph: + user_said: '%{user_name} said: ' workshop: paragraph: request_approved: You have been added as a facilitator of %{workshop_title}. @@ -5836,3 +5845,4 @@ en: request_message: "%{user_name} has requested to help facilitate %{workshop_title}:" request_reply_instructions: You can also reply directly to this email to ask follow-up questions. + view_workshop: 'View the workshop here: ' diff --git a/config/locales/es.yml b/config/locales/es.yml index 340cab7..4f7052e 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -199,7 +199,7 @@ es: time: am: am formats: - default: '%A, %d de %B de %Y %H:%M:%S %z' + default: '%A, %d de %B de %Y %H:%M' long: '%d de %B de %Y %H:%M' short: '%H:%M' pm: pm @@ -838,7 +838,7 @@ es: term: education: Los talleres tienen un enfoque en la educación, en enseñar a otros cómo arreglar bicicletas - export_bikes: '' + export_bikes: Organizaciones que se incluyen bicicletas para las comunidades de otros países. low_cost: Hay organizaciones que envían bicicletas donadas a comunidades en otros países no_money: Hay organizaciones que reciclan bicicletas y algunas de sus partes @@ -1068,8 +1068,6 @@ es: no_value: Indefinido pages: 'Páginas:' pluraliztion_same_as_other: Igual que el otro - test: - one: '' Enabled_Locales: Idiomas habilitados History: Historia Locale_Translation: Traducción en %%{language} diff --git a/config/routes.rb b/config/routes.rb index d8931a0..d165c74 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -35,6 +35,7 @@ BikeBike::Application.routes.draw do match '/conferences/:slug/workshops/:workshop_id/edit' => 'conferences#edit_workshop', :as => :edit_workshop, via: [:get, :post] match '/conferences/:slug/workshops/:workshop_id/translate/:locale' => 'conferences#translate_workshop', :as => :translate_workshop, via: [:get, :post] match '/conferences/:slug/workshops/:workshop_id/delete' => 'conferences#delete_workshop', :as => :delete_workshop, via: [:get, :post] + post '/conferences/:slug/workshops/:workshop_id/comment' => 'conferences#add_comment', :as => :workshop_comment get '/conferences/:slug/workshops/:workshop_id/facilitate' => 'conferences#facilitate_workshop', :as => :facilitate_workshop post '/conferences/:slug/workshops/:workshop_id/facilitate_request' => 'conferences#facilitate_request', :as => :facilitate_workshop_request get '/conferences/:slug/workshops/:workshop_id/facilitate_request/:user_id/:approve_or_deny' => 'conferences#approve_facilitate_request', :as => :approve_facilitate_workshop_request diff --git a/db/migrate/20160708042302_create_comments.rb b/db/migrate/20160708042302_create_comments.rb new file mode 100644 index 0000000..56421cc --- /dev/null +++ b/db/migrate/20160708042302_create_comments.rb @@ -0,0 +1,11 @@ +class CreateComments < ActiveRecord::Migration + def change + create_table :comments do |t| + t.string :model_type + t.integer :model_id + t.text :comment + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20160708042511_add_user_id_to_comments.rb b/db/migrate/20160708042511_add_user_id_to_comments.rb new file mode 100644 index 0000000..2d1f0c8 --- /dev/null +++ b/db/migrate/20160708042511_add_user_id_to_comments.rb @@ -0,0 +1,5 @@ +class AddUserIdToComments < ActiveRecord::Migration + def change + add_column :comments, :user_id, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 08510f2..bd7caa5 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: 20160707034050) do +ActiveRecord::Schema.define(version: 20160708042511) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -24,6 +24,15 @@ ActiveRecord::Schema.define(version: 20160707034050) do t.datetime "updated_at" end + create_table "comments", force: :cascade do |t| + t.string "model_type" + t.integer "model_id" + t.text "comment" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "user_id" + end + create_table "conference_admins", force: :cascade do |t| t.integer "conference_id" t.integer "user_id"