111 |
112 |
115 |
--------------------------------------------------------------------------------
/app/views/commontator/threads/_show.js.erb:
--------------------------------------------------------------------------------
1 | <%#
2 | Views that use this partial must supply the following variables:
3 | user
4 | thread
5 | page
6 | show_all
7 | %>
8 |
9 |
10 | $("#commontator-thread-<%= thread.id %>").html("<%= escape_javascript(
11 | render partial: 'commontator/threads/show', formats: [ :html ], locals: {
12 | user: user, thread: thread, page: page, show_all: show_all
13 | }
14 | ) %>");
15 |
--------------------------------------------------------------------------------
/app/views/commontator/threads/show.js.erb:
--------------------------------------------------------------------------------
1 | <%=
2 | render partial: 'show', locals: {
3 | user: @commontator_user,
4 | thread: @commontator_thread,
5 | page: @commontator_page,
6 | show_all: @commontator_show_all
7 | }
8 | %>
9 |
10 | $("#commontator-thread-<%= @commontator_thread.id %>-comment-list").hide().fadeIn();
11 |
12 | <%= javascript_proc %>
13 |
--------------------------------------------------------------------------------
/arrow.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lml/commontator/729fc34968d131bcb5d31325f3e5536e4ee491d4/arrow.xcf
--------------------------------------------------------------------------------
/commontator.gemspec:
--------------------------------------------------------------------------------
1 | $:.push File.expand_path('lib', __dir__)
2 |
3 | # Maintain your gem's version:
4 | require 'commontator/version'
5 |
6 | # Describe your gem and declare its dependencies:
7 | Gem::Specification.new do |gem|
8 | gem.name = 'commontator'
9 | gem.version = COMMONTATOR_VERSION
10 | gem.authors = [ 'Dante Soares' ]
11 | gem.email = [ 'dante.m.soares@rice.edu' ]
12 | gem.homepage = 'https://github.com/lml/commontator'
13 | gem.license = 'MIT'
14 | gem.summary = 'Allows users to comment on any model in your application.'
15 | gem.description = 'A Rails engine for comments.'
16 |
17 | gem.files = Dir['{app,config,db,lib,vendor}/**/*'] + [ 'MIT-LICENSE', 'README.md' ]
18 |
19 | gem.add_dependency 'rails', '>= 6.0'
20 | gem.add_dependency 'sprockets-rails'
21 | gem.add_dependency 'will_paginate'
22 |
23 | gem.add_development_dependency 'jquery-rails'
24 | gem.add_development_dependency 'sassc-rails'
25 | gem.add_development_dependency 'rails-i18n'
26 | gem.add_development_dependency 'rspec-rails'
27 | gem.add_development_dependency 'rails-controller-testing'
28 | gem.add_development_dependency 'listen'
29 | gem.add_development_dependency 'acts_as_votable'
30 | gem.add_development_dependency 'mailgun_rails'
31 | end
32 |
--------------------------------------------------------------------------------
/config/locales/de.yml:
--------------------------------------------------------------------------------
1 | ---
2 | de:
3 | activerecord:
4 | attributes:
5 | commontator/comment:
6 | body: Kommentar
7 | creator: Ersteller
8 | editor: Bearbeiter
9 | thread: Diskussion
10 | parent: Elternkommentar
11 | commontator/subscription:
12 | subscriber: Subscriber
13 | thread: Diskussion
14 | commontator/thread:
15 | commontable: Commontable
16 | errors:
17 | models:
18 | commontator/comment:
19 | attributes:
20 | body:
21 | double_posted: ist ein Duplikat.
22 | models:
23 | commontator/comment:
24 | one: Kommentar
25 | other: Kommentare
26 | commontator/subscription:
27 | one: Subscription
28 | other: Subscriptions
29 | commontator/thread:
30 | one: Diskussion
31 | other: Diskussionen
32 | commontator:
33 | anonymous: Anonymous
34 | comment:
35 | actions:
36 | cancel: Abbrechen
37 | confirm_delete: Sind Sie sicher, dass sie diesen Kommentar löschen wollen?
38 | create: Kommentar speichern
39 | delete: Löschen
40 | edit: Bearbeiten
41 | new: Neuer Kommentar
42 | undelete: Löschen rückgängig
43 | update: Kommentar bearbeiten
44 | reply: Auf Kommentar antworten.
45 | errors:
46 | already_deleted: Dieser Kommentar wurde bereits gelöscht.
47 | create: "Kommentar kann nicht veröffentlicht werden:"
48 | not_deleted: Dieser Kommentar wurde nicht gelöscht..
49 | update: "Kommentar kann nicht bearbeitet werden:"
50 | status:
51 | created_at: Veröffentlich am %{created_at}.
52 | deleted_by: Kommentar gelöscht von %{deleter_name}.
53 | updated_at: Bearbeitet von %{editor_name} am %{updated_at}.
54 | replying: "%{creator_name} Antworten"
55 | reply_pages: "Antwortseiten:"
56 | email:
57 | comment_created:
58 | body: "%{creator_name} kommentierte zu %{commontable_name}:"
59 | subject: "%{creator_name} veröffentlichte einen Kommentar zu %{commontable_name}"
60 | thread_link_html: Hier klicken um alle Kommentare zu %{commontable_name} zu sehen.
61 | require_login: Sie müssen sich anmelden bevor sie einen Kommentar veröffentlichen können.
62 | subscription:
63 | actions:
64 | subscribe: Subscribe
65 | unsubscribe: Unsubscribe
66 | errors:
67 | already_subscribed: Sie erhalten zu der Diskussion bereits Aktualisierungen
68 | not_subscribed: Sie erhalten zu der Diskussion keine Nachrichten.
69 | thread:
70 | actions:
71 | show_all: Alle Kommentare anzeigen
72 | filter: Kommentare filtern
73 | close: Diskussion beenden
74 | confirm_close: Sind Sie sicher, dass sie die Diskussion schliessen wollen?
75 | reopen: Diskussion wieder eröffnen
76 | show: Kommentare anzeigen
77 | hide: Kommentare ausblenden
78 | errors:
79 | already_closed: Diese Diskussion ist bereits geschlossen.
80 | not_closed: Diese Diskussion ist nicht geschlossen.
81 | status:
82 | cannot_post: Zurzeit können keine neuen Kommentare verfasst werden.
83 | closed: Kommentare (geschlossen von %{closer_name})
84 | open: Kommentare
85 | pages: "Kommentarseiten:"
86 | time:
87 | formats:
88 | commontator: "%d. %B %Y um %H:%M %Z"
89 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # 'true': 'foo'
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at https://guides.rubyonrails.org/i18n.html.
31 |
32 | ---
33 | en:
34 | activerecord:
35 | attributes:
36 | commontator/comment:
37 | body: Comment
38 | creator: Creator
39 | editor: Editor
40 | thread: Discussion
41 | parent: Parent Comment
42 | commontator/subscription:
43 | subscriber: Subscriber
44 | thread: Discussion
45 | commontator/thread:
46 | commontable: Commontable
47 | errors:
48 | models:
49 | commontator/comment:
50 | attributes:
51 | body:
52 | double_posted: is a duplicate of another comment.
53 | models:
54 | commontator/comment:
55 | one: comment
56 | other: comments
57 | commontator/subscription:
58 | one: subscription
59 | other: subscriptions
60 | commontator/thread:
61 | one: thread
62 | other: threads
63 | commontator:
64 | anonymous: Anonymous
65 | comment:
66 | actions:
67 | cancel: Cancel
68 | confirm_delete: Are you sure you want to delete this comment?
69 | create: Post Comment
70 | delete: Delete
71 | edit: Edit
72 | new: New Comment
73 | undelete: Undelete
74 | update: Modify Comment
75 | reply: Reply to Comment.
76 | errors:
77 | already_deleted: This comment has already been deleted.
78 | create: "This comment could not be posted because:"
79 | not_deleted: This comment is not deleted.
80 | update: "This comment could not be modified because:"
81 | status:
82 | created_at: Posted on %{created_at}.
83 | deleted_by: Comment deleted by %{deleter_name}.
84 | updated_at: Last modified by %{editor_name} on %{updated_at}.
85 | replying: Replying to %{creator_name}
86 | reply_pages: "Reply pages:"
87 | email:
88 | comment_created:
89 | body: "%{creator_name} commented on %{commontable_name}:"
90 | subject: "%{creator_name} posted a comment on %{commontable_name}"
91 | thread_link_html: Click here to view all comments on %{commontable_name}.
92 | require_login: You must login before you can post a comment.
93 | subscription:
94 | actions:
95 | subscribe: Subscribe
96 | unsubscribe: Unsubscribe
97 | errors:
98 | already_subscribed: You are already subscribed to this discussion.
99 | not_subscribed: You are not subscribed to this discussion.
100 | thread:
101 | actions:
102 | show_all: Show All Comments
103 | filter: Filter Comments
104 | close: Close Discussion
105 | confirm_close: Are you sure you want to close this discussion?
106 | reopen: Reopen Discussion
107 | show: Show Comments
108 | hide: Hide Comments
109 | errors:
110 | already_closed: This discussion has already been closed.
111 | not_closed: This discussion is not closed.
112 | status:
113 | cannot_post: New comments cannot be posted at this time.
114 | closed: Comments (Closed by %{closer_name})
115 | open: Comments
116 | pages: "Comment pages:"
117 | time:
118 | formats:
119 | commontator: "%b %d %Y at %I:%M%p %Z"
120 |
--------------------------------------------------------------------------------
/config/locales/pt-BR.yml:
--------------------------------------------------------------------------------
1 | ---
2 | pt-BR:
3 | activerecord:
4 | attributes:
5 | commontator/comment:
6 | body: Comentário
7 | creator: Criador
8 | editor: Editor
9 | thread: Discussão
10 | parent: Comentário pai
11 | commontator/subscription:
12 | subscriber: Assinante
13 | thread: Discussão
14 | commontator/thread:
15 | commontable: Comentável
16 | errors:
17 | models:
18 | commontator/comment:
19 | attributes:
20 | body:
21 | double_posted: é um comentário duplicado.
22 | models:
23 | commontator/comment:
24 | one: comentário
25 | other: comentários
26 | commontator/subscription:
27 | one: inscrição
28 | other: incrições
29 | commontator/thread:
30 | one: tópico
31 | other: tópicos
32 | commontator:
33 | anonymous: Anônimo
34 | comment:
35 | actions:
36 | cancel: Cancelar
37 | confirm_delete: Tem certeza que deseja remover este comentário?
38 | create: Postar comentário
39 | delete: Deletar
40 | edit: Editar
41 | new: Novo comentário
42 | undelete: Desfazer
43 | update: Editar comentário
44 | reply: Responder ao comentário.
45 | errors:
46 | already_deleted: Este comentário já foi removido.
47 | create: "Este comentário não pôde ser postado porque:"
48 | not_deleted: Este comentário não foi removido.
49 | update: "Este comentário não pôde ser editado porque:"
50 | status:
51 | created_at: Postado em %{created_at}.
52 | deleted_by: Comentário removido por %{deleter_name}.
53 | updated_at: Editado por %{editor_name} em %{updated_at}.
54 | replying: Respondendo a %{creator_name}
55 | reply_pages: "Páginas de respostas:"
56 | email:
57 | comment_created:
58 | body: "%{creator_name} comentou em %{commontable_name}:"
59 | subject: "%{creator_name} postou um comentário em %{commontable_name}"
60 | thread_link_html: Clique aqui para ver todos os comentários em %{commontable_name}.
61 | require_login: Você deve fazer login para postar um comentário.
62 | subscription:
63 | actions:
64 | subscribe: Inscrever-se
65 | unsubscribe: Cancelar inscrição
66 | errors:
67 | already_subscribed: Você já está inscrito nessa discussão.
68 | not_subscribed: Você não está inscrito nessa discussão.
69 | thread:
70 | actions:
71 | show_all: Mostrar todos os comentários
72 | filter: Filtrar comentários
73 | close: Fechar discussão
74 | confirm_close: Tem certeza que deseja fechar a discussão?
75 | reopen: Reabrir discussão
76 | show: Mostrar comentários
77 | hide: Esconder comentários
78 | errors:
79 | already_closed: Essa discussão já foi fechada.
80 | not_closed: Essa discussão não está fechada.
81 | status:
82 | cannot_post: Não é possível postar novos comentários.
83 | closed: Comentários (Fechado por %{closer_name})
84 | open: Comentários
85 | pages: "Páginas de comentários:"
86 | time:
87 | formats:
88 | commontator: "%d de %B de %Y às %H:%M %Z"
89 |
--------------------------------------------------------------------------------
/config/locales/ru.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ru:
3 | activerecord:
4 | attributes:
5 | commontator/comment:
6 | body: Комментарий
7 | creator: Создал
8 | editor: Редактировал
9 | thread: Обсуждение
10 | parent: Родительский Комментарий
11 | commontator/subscription:
12 | subscriber: Подписчик
13 | thread: Обсуждение
14 | commontator/thread:
15 | commontable: Комментируем
16 | errors:
17 | models:
18 | commontator/comment:
19 | attributes:
20 | body:
21 | double_posted: этот комментарий уже есть.
22 | models:
23 | commontator/comment:
24 | one: комментарий
25 | many: комментарии
26 | other: комментарии
27 | commontator/subscription:
28 | one: подписка
29 | many: подписки
30 | other: подписки
31 | commontator/thread:
32 | one: обсуждение
33 | many: обсуждения
34 | other: обсуждения
35 | commontator:
36 | anonymous: Аноним
37 | comment:
38 | actions:
39 | cancel: Отменить
40 | confirm_delete: Вы уверены, что хотите удалить комментарий?
41 | create: Оставить Комментарий
42 | delete: Удалить
43 | edit: Редактировать
44 | new: Новый Комментарий
45 | undelete: Восстановить
46 | update: Изменить Комментарий
47 | reply: Ответить на Комментарий.
48 | errors:
49 | already_deleted: Этот комментарий был удален.
50 | create: Комментарий нельзя оставить потому что
51 | not_deleted: Этот комментарий не удален.
52 | update: Этот комментарий нельзя изменить потому что
53 | status:
54 | created_at: Создан %{created_at}.
55 | deleted_by: Комментарий был удален %{deleter_name}.
56 | updated_at: Редактировал %{editor_name}, %{updated_at}.
57 | replying: Отвечая %{creator_name}
58 | reply_pages: "Страницы ответов:"
59 | email:
60 | comment_created:
61 | body: "%{creator_name} комментировал %{commontable_name}:"
62 | subject: "%{creator_name} оставил комментарий к %{commontable_name}"
63 | thread_link_html: Нажмите здесь чтобы посмотреть все комментарии к %{commontable_name}.
64 | require_login: Вы должны залогиниться прежде чем оставить комментарий.
65 | subscription:
66 | actions:
67 | subscribe: Подписаться
68 | unsubscribe: Отписаться
69 | errors:
70 | already_subscribed: Вы уже подписаны на это обсуждение.
71 | not_subscribed: Вы не подписаны на это обсуждение.
72 | thread:
73 | actions:
74 | show_all: Показать все комментарии
75 | filter: Фильтровать комментарии
76 | close: Закрыть обсуждение
77 | confirm_close: Вы уверены, что хотите закрыть обсуждение?
78 | reopen: Открыть заново обсуждение
79 | show: Показать комментарии
80 | hide: скрыть комментарии
81 | errors:
82 | already_closed: Это обсуждение было закрыто.
83 | not_closed: Это обсуждение не закрыто.
84 | status:
85 | cannot_post: Новый комментарии нельзя разместить в данный момент.
86 | closed: Комментарии (Закрыты %{closer_name})
87 | open: Комментарии
88 | pages: "Страницы комментариев:"
89 | time:
90 | formats:
91 | commontator: "%d %b %Y в %H:%M %Z"
92 |
--------------------------------------------------------------------------------
/config/locales/zh.yml:
--------------------------------------------------------------------------------
1 | ---
2 | zh: &zh
3 | activerecord:
4 | attributes:
5 | commontator/comment:
6 | body: 留言
7 | creator: 留言者
8 | editor: 編輯
9 | thread: 討論
10 | parent: 家长留言
11 | commontator/subscription:
12 | subscriber: 訂閱者
13 | thread: 討論
14 | commontator/thread:
15 | commontable: 可以留言
16 | errors:
17 | models:
18 | commontator/comment:
19 | attributes:
20 | body:
21 | double_posted: 已經有一筆相同的留言。
22 | models:
23 | commontator/comment:
24 | one: 留言
25 | other: 留言
26 | commontator/subscription:
27 | one: 訂閱
28 | other: 訂閱
29 | commontator/thread:
30 | one: 討論
31 | other: 討論
32 | commontator:
33 | anonymous: 匿名
34 | comment:
35 | actions:
36 | cancel: 取消
37 | confirm_delete: 確定要刪除這筆留言嗎?
38 | create: 發佈
39 | delete: 刪除
40 | edit: 編輯
41 | new: 新留言
42 | undelete: 未刪除
43 | update: 修改留言
44 | reply: 回复评论
45 | errors:
46 | already_deleted: 此留言已經被刪除了
47 | create: 此留言無法發佈因為:
48 | not_deleted: 此留言未被刪除
49 | update: 此留言無法被修改因為:
50 | status:
51 | created_at: 於%{created_at}發佈
52 | deleted_by: 被%{deleter_name}刪除
53 | updated_at: 最在於%{updated_at}被%{editor_name}修改
54 | replying: 回复%{creator_name}
55 | reply_pages: 回复页面:
56 | email:
57 | comment_created:
58 | body: "%{creator_name}留言於%{commontable_name}"
59 | subject: "%{creator_name}於%{commontable_name}發佈了一則留言"
60 | thread_link_html: 點此處看%{commontable_name}的所有留言
61 | require_login: 你需要先登入
62 | subscription:
63 | actions:
64 | subscribe: 訂閱
65 | unsubscribe: 取消訂閱
66 | errors:
67 | already_subscribed: 你已經訂閱過了
68 | not_subscribed: 你沒有訂閱過
69 | thread:
70 | actions:
71 | show_all: 列出所有留言
72 | filter: 過濾留言
73 | close: 關閉討論
74 | confirm_close: 你確定要關閉此討論嗎?
75 | reopen: 回復討論
76 | show: 打開留言
77 | hide: 关闭留言
78 | errors:
79 | already_closed: 此討論已經被關閉
80 | not_closed: 此討論尚未被關閉
81 | status:
82 | cannot_post: 現在還不能發佈
83 | closed: 留言已經被%{closer_name}關閉
84 | open: 留言
85 | pages: 留言页面:
86 | time:
87 | formats:
88 | commontator: "%Y年%m月%d日 於 %H:%M %Z"
89 |
90 | zh-CN:
91 | <<: *zh
92 |
93 | zh-HK:
94 | <<: *zh
95 |
96 | zh-TW:
97 | <<: *zh
98 |
99 | zh-YUE:
100 | <<: *zh
101 | time:
102 | formats:
103 | commontator: "%Y年%m月%d號 於 %H:%M %Z"
104 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | Commontator::Engine.routes.draw do
2 | resources :threads, only: [ :show ] do
3 | resources :comments, except: [ :index, :destroy ], shallow: true do
4 | member do
5 | put 'upvote'
6 | put 'downvote'
7 | put 'unvote'
8 |
9 | put 'delete'
10 | put 'undelete'
11 | end
12 | end
13 |
14 | member do
15 | get 'mentions' if Commontator.mentions_enabled
16 |
17 | put 'subscribe', to: 'subscriptions#subscribe'
18 | put 'unsubscribe', to: 'subscriptions#unsubscribe'
19 |
20 | put 'close'
21 | put 'reopen'
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/db/migrate/10_install_commontator.rb:
--------------------------------------------------------------------------------
1 | class InstallCommontator < ActiveRecord::Migration[6.0]
2 | def change
3 | create_table :commontator_threads do |t|
4 | t.references :commontable,
5 | polymorphic: true,
6 | index: { unique: true, name: 'index_commontator_threads_on_c_id_and_c_type' }
7 | t.references :closer, polymorphic: true
8 |
9 | t.datetime :closed_at
10 |
11 | t.timestamps
12 | end
13 |
14 | create_table :commontator_comments do |t|
15 | t.references :thread, null: false, index: false, foreign_key: {
16 | to_table: :commontator_threads, on_update: :cascade, on_delete: :cascade
17 | }
18 | t.references :creator, polymorphic: true, null: false, index: false
19 | t.references :editor, polymorphic: true
20 |
21 | t.text :body, null: false
22 | t.datetime :deleted_at
23 |
24 | t.integer :cached_votes_up, default: 0, index: true
25 | t.integer :cached_votes_down, default: 0, index: true
26 |
27 | t.timestamps
28 | end
29 |
30 | add_index :commontator_comments, [ :creator_id, :creator_type, :thread_id ],
31 | name: 'index_commontator_comments_on_c_id_and_c_type_and_t_id'
32 | add_index :commontator_comments, [ :thread_id, :created_at ]
33 |
34 | create_table :commontator_subscriptions do |t|
35 | t.references :thread, null: false, foreign_key: {
36 | to_table: :commontator_threads, on_update: :cascade, on_delete: :cascade
37 | }
38 | t.references :subscriber, polymorphic: true, null: false, index: false
39 |
40 | t.timestamps
41 | end
42 |
43 | add_index :commontator_subscriptions, [ :subscriber_id, :subscriber_type, :thread_id ],
44 | unique: true,
45 | name: 'index_commontator_subscriptions_on_s_id_and_s_type_and_t_id'
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/db/migrate/11_add_replying_to_comments.rb:
--------------------------------------------------------------------------------
1 | class AddReplyingToComments < ActiveRecord::Migration[6.0]
2 | def change
3 | add_reference :commontator_comments, :parent, foreign_key: {
4 | to_table: :commontator_comments, on_update: :restrict, on_delete: :cascade
5 | }
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/lib/commontator.rb:
--------------------------------------------------------------------------------
1 | require 'will_paginate'
2 | require 'commontator/version'
3 |
4 | module Commontator
5 | # These requires need the Commontator module to function properly
6 | require 'commontator/engine'
7 | require 'commontator/config'
8 | require 'commontator/controllers'
9 | require 'commontator/acts_as_commontator'
10 | require 'commontator/acts_as_commontable'
11 |
12 | VERSION = COMMONTATOR_VERSION
13 |
14 | include Commontator::Config
15 | end
16 |
--------------------------------------------------------------------------------
/lib/commontator/acts_as_commontable.rb:
--------------------------------------------------------------------------------
1 | require_relative 'commontable_config'
2 | require_relative 'build_thread'
3 |
4 | module Commontator::ActsAsCommontable
5 | def self.included(base)
6 | base.class_attribute :is_commontable
7 | base.is_commontable = false
8 | base.extend(ClassMethods)
9 | end
10 |
11 | module ClassMethods
12 | def acts_as_commontable(options = {})
13 | class_exec do
14 | association_options = options.extract!(:dependent)
15 | association_options[:dependent] ||= :nullify
16 |
17 | cattr_accessor :commontable_config
18 | self.commontable_config = Commontator::CommontableConfig.new(options)
19 |
20 | has_one :commontator_thread, **association_options.merge(
21 | as: :commontable, class_name: 'Commontator::Thread'
22 | )
23 |
24 | prepend Commontator::BuildThread
25 |
26 | # Support creating acts_as_commontable records without a commontator_thread when migrating
27 | validates :commontator_thread, presence: true, if: -> { Commontator::Thread.table_exists? }
28 |
29 | self.is_commontable = true
30 | end
31 | end
32 |
33 | alias_method :acts_as_commentable, :acts_as_commontable
34 | end
35 | end
36 |
37 | ActiveSupport.on_load :active_record do
38 | include Commontator::ActsAsCommontable
39 | end
40 |
--------------------------------------------------------------------------------
/lib/commontator/acts_as_commontator.rb:
--------------------------------------------------------------------------------
1 | require_relative 'commontator_config'
2 |
3 | module Commontator::ActsAsCommontator
4 | def self.included(base)
5 | base.class_attribute :is_commontator
6 | base.is_commontator = false
7 | base.extend(ClassMethods)
8 | end
9 |
10 | module ClassMethods
11 | def acts_as_commontator(options = {})
12 | class_exec do
13 | association_options = options.extract!(:dependent)
14 | association_options[:dependent] ||= :destroy
15 |
16 | cattr_accessor :commontator_config
17 | self.commontator_config = Commontator::CommontatorConfig.new(options)
18 |
19 | has_many :commontator_comments, **association_options.merge(
20 | as: :creator, class_name: 'Commontator::Comment'
21 | )
22 | has_many :commontator_subscriptions, **association_options.merge(
23 | as: :subscriber, class_name: 'Commontator::Subscription'
24 | )
25 |
26 | self.is_commontator = true
27 | end
28 | end
29 |
30 | alias_method :acts_as_commonter, :acts_as_commontator
31 | alias_method :acts_as_commentator, :acts_as_commontator
32 | alias_method :acts_as_commenter, :acts_as_commontator
33 | end
34 | end
35 |
36 | ActiveSupport.on_load :active_record do
37 | include Commontator::ActsAsCommontator
38 | end
39 |
--------------------------------------------------------------------------------
/lib/commontator/build_thread.rb:
--------------------------------------------------------------------------------
1 | module Commontator::BuildThread
2 | def commontator_thread
3 | @commontator_thread ||= super
4 | return @commontator_thread unless @commontator_thread.nil?
5 |
6 | @commontator_thread = build_commontator_thread.tap do |thread|
7 | thread.save if persisted?
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/lib/commontator/commontable_config.rb:
--------------------------------------------------------------------------------
1 | require_relative 'config'
2 |
3 | class Commontator::CommontableConfig
4 | Commontator::Config::COMMONTABLE_ATTRIBUTES.each do |attribute|
5 | attr_accessor attribute
6 | end
7 |
8 | # For backwards-compatibility with Integer comments_per_page
9 | def comments_per_page=(cpp)
10 | @comments_per_page = [ cpp ].flatten
11 | end
12 |
13 | def initialize(options = {})
14 | Commontator::Config::COMMONTABLE_ATTRIBUTES.each do |attribute|
15 | send "#{attribute}=", options[attribute] || Commontator.send(attribute)
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/lib/commontator/commontator_config.rb:
--------------------------------------------------------------------------------
1 | require_relative 'config'
2 |
3 | class Commontator::CommontatorConfig
4 | Commontator::Config::COMMONTATOR_ATTRIBUTES.each do |attribute|
5 | attr_accessor attribute
6 | end
7 |
8 | def initialize(options = {})
9 | Commontator::Config::COMMONTATOR_ATTRIBUTES.each do |attribute|
10 | send "#{attribute}=", options[attribute] || Commontator.send(attribute)
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/commontator/config.rb:
--------------------------------------------------------------------------------
1 | module Commontator::Config
2 | # Attributes
3 |
4 | # Can be set in initializer only
5 | ENGINE_ATTRIBUTES = [
6 | :current_user_proc,
7 | :javascript_proc
8 | ]
9 |
10 | # Can be set in initializer or passed as an option to acts_as_commontator
11 | COMMONTATOR_ATTRIBUTES = [
12 | :user_name_proc,
13 | :user_link_proc,
14 | :user_avatar_proc,
15 | :user_email_proc,
16 | :user_mentions_proc
17 | ]
18 |
19 | # Can be set in initializer or passed as an option to acts_as_commontable
20 | COMMONTABLE_ATTRIBUTES = [
21 | :comment_filter,
22 | :thread_read_proc,
23 | :thread_moderator_proc,
24 | :comment_editing,
25 | :comment_deletion,
26 | :moderator_permissions,
27 | :comment_voting,
28 | :vote_count_proc,
29 | :comment_order,
30 | :thread_subscription,
31 | :email_from_proc,
32 | :commontable_name_proc,
33 | :comment_url_proc,
34 | :new_comment_style,
35 | :comment_reply_style,
36 | :comments_per_page,
37 | :mentions_enabled
38 | ]
39 |
40 | DEPRECATED_ATTRIBUTES = [
41 | [:moderators_can_edit_comments, :moderator_permissions],
42 | [:hide_deleted_comments, :comment_filter],
43 | [:hide_closed_threads, :thread_read_proc],
44 | [:wp_link_renderer_proc],
45 | [:voting_text_proc, :vote_count_proc],
46 | [:user_name_clickable, :user_link_proc],
47 | [:user_admin_proc, :thread_moderator_proc],
48 | [:auto_subscribe_on_comment, :thread_subscription],
49 | [:can_edit_own_comments, :comment_editing],
50 | [:can_edit_old_comments, :comment_editing],
51 | [:can_delete_own_comments, :comment_deletion],
52 | [:can_delete_old_comments, :comment_deletion],
53 | [:can_subscribe_to_thread, :thread_subscription],
54 | [:can_vote_on_comments, :comment_voting],
55 | [:combine_upvotes_and_downvotes, :vote_count_proc],
56 | [:comments_order, :comment_order],
57 | [:closed_threads_are_readable, :thread_read_proc],
58 | [:deleted_comments_are_visible, :comment_filter],
59 | [:can_read_thread_proc, :thread_read_proc],
60 | [:can_edit_thread_proc, :thread_moderator_proc],
61 | [:admin_can_edit_comments, :moderator_permissions],
62 | [:subscription_email_enable_proc, :user_email_proc],
63 | [:comment_name, 'config/locales'],
64 | [:comment_create_verb_present, 'config/locales'],
65 | [:comment_create_verb_past, 'config/locales'],
66 | [:comment_edit_verb_present, 'config/locales'],
67 | [:comment_edit_verb_past, 'config/locales'],
68 | [:timestamp_format, 'config/locales'],
69 | [:subscription_email_to_proc, 'config/locales'],
70 | [:subscription_email_from_proc, :email_from_proc],
71 | [:subscription_email_subject_proc, 'config/locales'],
72 | [:comments_ordered_by_votes, :comment_order],
73 | [:current_user_method, :current_user_proc],
74 | [:user_missing_name, 'config/locales'],
75 | [:user_email_method, :user_email_proc],
76 | [:user_name_method, :user_name_proc],
77 | [:commontable_id_method],
78 | [:commontable_url_proc, :comment_url_proc]
79 | ]
80 |
81 | def self.included(base)
82 | (ENGINE_ATTRIBUTES + COMMONTATOR_ATTRIBUTES + COMMONTABLE_ATTRIBUTES).each do |attribute|
83 | base.mattr_accessor attribute
84 | end
85 |
86 | base.mattr_accessor :show_deprecation_warning
87 | DEPRECATED_ATTRIBUTES.each do |deprecated, replacement|
88 | base.define_singleton_method(deprecated) do
89 | base.show_deprecation_warning = true
90 | replacement_string = (replacement.nil? ? 'No replacement is available. You can safely remove it from your configuration file.' : "Use `#{replacement.to_s}` instead.")
91 | warn "\n[COMMONTATOR] Deprecation: `config.#{deprecated.to_s}` is deprecated and has been disabled. #{replacement_string}\n"
92 | end
93 |
94 | base.define_singleton_method("#{deprecated.to_s}=") { |obj| base.send(deprecated) }
95 | end
96 |
97 | base.extend ClassMethods
98 | end
99 |
100 | module ClassMethods
101 | def configure
102 | self.show_deprecation_warning = false
103 |
104 | yield self
105 |
106 | warn("\n[COMMONTATOR] We recommend that you backup the config/initializers/commontator.rb file, rename or remove it, run rake commontator:install:initializers to copy the new default one, then configure it to your liking.\n") if show_deprecation_warning
107 | end
108 |
109 | def commontator_config(user)
110 | (user && user.is_commontator) ? user.commontator_config : self
111 | end
112 |
113 | def commontable_config(obj)
114 | (obj && obj.is_commontable) ? obj.commontable_config : self
115 | end
116 |
117 | def commontator_name(user)
118 | commontator_config(user).user_name_proc.call(user)
119 | end
120 |
121 | def commontator_link(user, routing_proxy)
122 | commontator_config(user).user_link_proc.call(user, routing_proxy)
123 | end
124 |
125 | def commontator_email(user, mailer = nil)
126 | commontator_config(user).user_email_proc.call(user, mailer)
127 | end
128 |
129 | def commontator_avatar(user, view)
130 | commontator_config(user).user_avatar_proc.call(user, view)
131 | end
132 |
133 | def commontator_mentions(user, thread, search_phrase)
134 | commontator_config(user).user_mentions_proc.call(user, thread, search_phrase)
135 | end
136 |
137 | def commontable_name(commontable)
138 | commontable_config(commontable).commontable_name_proc.call(commontable)
139 | end
140 |
141 | def comment_url(comment, routing_proxy)
142 | commontable_config(comment.thread.commontable).comment_url_proc.call(comment, routing_proxy)
143 | end
144 | end
145 | end
146 |
--------------------------------------------------------------------------------
/lib/commontator/controllers.rb:
--------------------------------------------------------------------------------
1 | require_relative 'shared_helper'
2 |
3 | module Commontator::Controllers
4 | def commontator_set_thread_variables
5 | return if @commontator_thread.nil? || !@commontator_thread.can_be_read_by?(@commontator_user)
6 |
7 | @commontator_page = [params[:page].to_i, 1].max
8 | @commontator_show_all = !params[:show_all].blank? &&
9 | @commontator_thread.can_be_edited_by?(@commontator_user)
10 | end
11 |
12 | def commontator_set_new_comment
13 | return unless @commontator_thread.config.new_comment_style == :t
14 |
15 | new_comment = Commontator::Comment.new(creator: @commontator_user, thread: @commontator_thread)
16 | @commontator_new_comment = new_comment if new_comment.can_be_created_by?(@commontator_user)
17 | end
18 |
19 | def commontator_thread_show(commontable)
20 | commontator_set_user
21 | commontator_set_thread(commontable)
22 | commontator_set_thread_variables
23 | commontator_set_new_comment
24 |
25 | @commontator_thread_show = true
26 | @commontator_thread.mark_as_read_for(@commontator_user)
27 | end
28 | end
29 |
30 | ActiveSupport.on_load :action_controller do
31 | include Commontator::Controllers
32 | end
33 |
--------------------------------------------------------------------------------
/lib/commontator/engine.rb:
--------------------------------------------------------------------------------
1 | require 'commontator'
2 | require 'sprockets/railtie'
3 |
4 | class Commontator::Engine < ::Rails::Engine
5 | isolate_namespace Commontator
6 |
7 | # Files in installed gems don't change during development,
8 | # but still cause issues in Rails 7 if autoloaded in an initializer
9 | # To fix this, make sure they are autoloaded only once
10 | config.autoload_once_paths = config.autoload_paths + config.eager_load_paths
11 |
12 | config.assets.precompile += [ 'commontator/*.png' ]
13 | end
14 |
--------------------------------------------------------------------------------
/lib/commontator/shared_helper.rb:
--------------------------------------------------------------------------------
1 | module Commontator::SharedHelper
2 | def commontator_set_user
3 | @commontator_user = Commontator.current_user_proc.call(self)
4 | end
5 |
6 | def commontator_set_thread(commontable)
7 | @commontator_thread = commontable.commontator_thread
8 | end
9 |
10 | def commontator_thread(commontable)
11 | commontator_set_user
12 | commontator_set_thread(commontable)
13 |
14 | render(
15 | partial: 'commontator/shared/thread', locals: {
16 | user: @commontator_user,
17 | thread: @commontator_thread,
18 | page: @commontator_page,
19 | show_all: @commontator_show_all
20 | }
21 | ).html_safe
22 | end
23 |
24 | def commontator_gravatar_image_tag(user, border = 1, options = {})
25 | email = Commontator.commontator_email(user) || ''
26 | name = Commontator.commontator_name(user) || ''
27 |
28 | url = "https://secure.gravatar.com/avatar/#{Digest::MD5.hexdigest(email)}?#{options.to_query}"
29 |
30 | image_tag(url, alt: name, title: name, border: border)
31 | end
32 |
33 | # Unlike the Rails versions of split_paragraphs and simple_format, Commontator's:
34 | # - Split paragraphs on any number of newlines optionally adjacent to spaces
35 | # - Create all
tags (no tags)
36 | # - Do not add
tags between other html tags
37 | def commontator_split_paragraphs(text)
38 | return [] if text.blank?
39 |
40 | text.to_str.gsub(/\r\n?/, "\n").gsub(/>\s*, ">\n<").split(/\s*\n\s*/).reject(&:blank?)
41 | end
42 |
43 | def commontator_simple_format(text, html_options = {}, options = {})
44 | wrapper_tag = options.fetch(:wrapper_tag, :p)
45 |
46 | text = sanitize(text) if options.fetch(:sanitize, true)
47 | paragraphs = commontator_split_paragraphs(text)
48 |
49 | if paragraphs.empty?
50 | content_tag(wrapper_tag, nil, html_options)
51 | else
52 | paragraphs.map! do |paragraph|
53 | paragraph.starts_with?('<') && paragraph.ends_with?('>') ?
54 | raw(paragraph) : content_tag(wrapper_tag, raw(paragraph), html_options)
55 | end.join("\n").html_safe
56 | end
57 | end
58 | end
59 |
60 | ActiveSupport.on_load :action_controller do
61 | include Commontator::SharedHelper
62 | end
63 |
64 | ActiveSupport.on_load :action_controller_base do
65 | helper Commontator::SharedHelper
66 | end
67 |
--------------------------------------------------------------------------------
/lib/commontator/version.rb:
--------------------------------------------------------------------------------
1 | COMMONTATOR_VERSION = '7.0.1'
2 |
--------------------------------------------------------------------------------
/lib/tasks/commontator_tasks.rake:
--------------------------------------------------------------------------------
1 | COMMONTATOR_COPY_TASKS = ['config/locales', 'app/assets/images',
2 | 'app/assets/stylesheets', 'app/views', 'app/mailers',
3 | 'app/helpers', 'app/controllers', 'app/models']
4 |
5 | namespace :commontator do
6 | namespace :install do
7 | desc "Copy initializers from commontator to application"
8 | task :initializers do
9 | Dir.glob(File.expand_path('../../config/initializers/*.rb', __dir__)) do |file|
10 | if File.exist?(File.expand_path(File.basename(file), 'config/initializers'))
11 | print "NOTE: Initializer #{File.basename(file)} from commontator has been skipped. Initializer with the same name already exists.\n"
12 | else
13 | cp file, 'config/initializers', verbose: false
14 | print "Copied initializer #{File.basename(file)} from commontator\n"
15 | end
16 | end
17 | end
18 | end
19 |
20 | namespace :copy do
21 | COMMONTATOR_COPY_TASKS.each do |path|
22 | name = File.basename(path)
23 | desc "Copy #{name} from commontator to application"
24 | task name.to_sym do
25 | namespace = path.start_with?('app') ? '/commontator' : ''
26 | cp_r File.expand_path("../../#{path}#{namespace}", __dir__), path, verbose: false
27 | print "Copied #{name} from commontator\n"
28 | end
29 | end
30 | end
31 |
32 | desc "Copy initializers and migrations from commontator to application"
33 | task :install do
34 | Rake::Task["commontator:install:initializers"].invoke
35 | Rake::Task["commontator:install:migrations"].invoke
36 | end
37 |
38 | desc "Copy assets, views, mailers, helpers, controllers and models from commontator to application"
39 | task :copy do
40 | COMMONTATOR_COPY_TASKS.each do |path|
41 | Rake::Task["commontator:copy:#{File.basename(path)}"].invoke
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/script/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # This command will automatically be run when you run "rails" from the root of your application.
3 |
4 | ENGINE_ROOT = File.expand_path('..', __dir__)
5 | ENGINE_PATH = File.expand_path('../lib/commontator/engine', __dir__)
6 |
7 | require 'rails/all'
8 |
9 | APP_PATH = File.expand_path('spec/dummy/config/application', ENGINE_ROOT)
10 |
11 | require 'rails/commands'
12 |
--------------------------------------------------------------------------------
/spec/controllers/commontator/subscriptions_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe Commontator::SubscriptionsController, type: :controller do
4 | routes { Commontator::Engine.routes }
5 |
6 | before { setup_controller_spec }
7 |
8 | context 'authorized' do
9 | before do
10 | @user.can_read = true
11 | Thread.current[:user] = @user
12 | end
13 |
14 | context 'PUT #subscribe' do
15 | it 'works' do
16 | put :subscribe, params: { id: @thread.id }
17 | expect(response).to redirect_to(@commontable_path)
18 | expect(assigns(:commontator_thread).errors).to be_empty
19 | expect(@thread.subscription_for(@user)).not_to be_nil
20 |
21 | @thread.unsubscribe(@user)
22 | @user.can_read = false
23 | @user.can_edit = true
24 | put :subscribe, params: { id: @thread.id }
25 | expect(response).to redirect_to(@commontable_path)
26 | expect(assigns(:commontator_thread).errors).to be_empty
27 | expect(@thread.subscription_for(@user)).not_to be_nil
28 |
29 | @thread.unsubscribe(@user)
30 | @user.can_edit = false
31 | @user.is_admin = true
32 | put :subscribe, params: { id: @thread.id }
33 | expect(response).to redirect_to(@commontable_path)
34 | expect(assigns(:commontator_thread).errors).to be_empty
35 | expect(@thread.subscription_for(@user)).not_to be_nil
36 | end
37 | end
38 |
39 | context 'PUT #unsubscribe' do
40 | it 'works' do
41 | @thread.subscribe(@user)
42 | put :unsubscribe, params: { id: @thread.id }
43 | expect(response).to redirect_to(@commontable_path)
44 | expect(assigns(:commontator_thread).errors).to be_empty
45 | expect(@thread.subscription_for(@user)).to be_nil
46 |
47 | @thread.subscribe(@user)
48 | @user.can_read = false
49 | @user.can_edit = true
50 | put :unsubscribe, params: { id: @thread.id }
51 | expect(response).to redirect_to(@commontable_path)
52 | expect(assigns(:commontator_thread).errors).to be_empty
53 | expect(@thread.subscription_for(@user)).to be_nil
54 |
55 | @thread.subscribe(@user)
56 | @user.can_edit = false
57 | @user.is_admin = true
58 | put :unsubscribe, params: { id: @thread.id }
59 | expect(response).to redirect_to(@commontable_path)
60 | expect(assigns(:commontator_thread).errors).to be_empty
61 | expect(@thread.subscription_for(@user)).to be_nil
62 | end
63 | end
64 | end
65 |
66 | context 'unauthorized' do
67 | context 'PUT #subscribe' do
68 | it 'returns 403 Forbidden' do
69 | put :subscribe, params: { id: @thread.id }
70 | expect(response).to have_http_status(:forbidden)
71 | expect(@thread.subscription_for(nil)).to be_nil
72 | expect(@thread.subscription_for(@user)).to be_nil
73 |
74 | Thread.current[:user] = @user
75 | put :subscribe, params: { id: @thread.id }
76 | expect(response).to have_http_status(:forbidden)
77 | expect(@thread.subscription_for(@user)).to be_nil
78 |
79 | @thread.subscribe(@user)
80 | @user.can_read = true
81 | put :subscribe, params: { id: @thread.id }
82 | expect(response).to redirect_to(@commontable_path)
83 | expect(assigns(:commontator_thread).errors).not_to be_empty
84 | end
85 | end
86 |
87 | context 'PUT #unsubscribe' do
88 | it 'returns 403 Forbidden' do
89 | @thread.subscribe(@user)
90 | put :unsubscribe, params: { id: @thread.id }
91 | expect(response).to have_http_status(:forbidden)
92 | expect(@thread.subscription_for(nil)).to be_nil
93 | expect(@thread.subscription_for(@user)).not_to be_nil
94 |
95 | Thread.current[:user] = @user
96 | put :unsubscribe, params: { id: @thread.id }
97 | expect(response).to have_http_status(:forbidden)
98 | expect(@thread.subscription_for(@user)).not_to be_nil
99 |
100 | @thread.unsubscribe(@user)
101 | @user.can_read = true
102 | put :unsubscribe, params: { id: @thread.id }
103 | expect(response).to redirect_to(@commontable_path)
104 | expect(assigns(:commontator_thread).errors).not_to be_empty
105 | end
106 | end
107 | end
108 | end
109 |
--------------------------------------------------------------------------------
/spec/controllers/commontator/threads_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe Commontator::ThreadsController, type: :controller do
4 | routes { Commontator::Engine.routes }
5 |
6 | before { setup_controller_spec }
7 |
8 | context 'authorized' do
9 | before do
10 | @user.can_read = true
11 | Thread.current[:user] = @user
12 | end
13 |
14 | context 'GET #show' do
15 | it 'works' do
16 | get :show, params: { id: @thread.id }
17 | expect(response).to redirect_to(@commontable_path)
18 |
19 | @user.can_read = false
20 | @user.can_edit = true
21 | get :show, params: { id: @thread.id }
22 | expect(response).to redirect_to(@commontable_path)
23 |
24 | @user.can_edit = false
25 | @user.is_admin = true
26 | get :show, params: { id: @thread.id }
27 | expect(response).to redirect_to(@commontable_path)
28 | end
29 | end
30 |
31 | context 'open' do
32 | context 'PUT #close' do
33 | it 'works' do
34 | @user.can_edit = true
35 | put :close, params: { id: @thread.id }
36 | expect(response).to redirect_to(@commontable_path)
37 | expect(assigns(:commontator_thread).errors).to be_empty
38 | expect(assigns(:commontator_thread).is_closed?).to eq true
39 | expect(assigns(:commontator_thread).closer).to eq @user
40 |
41 | expect(assigns(:commontator_thread).reopen).to eq true
42 | @user.can_edit = false
43 | @user.is_admin = true
44 | put :close, params: { id: @thread.id }
45 | expect(response).to redirect_to(@commontable_path)
46 | expect(assigns(:commontator_thread).errors).to be_empty
47 | expect(assigns(:commontator_thread).is_closed?).to eq true
48 | expect(assigns(:commontator_thread).closer).to eq @user
49 | end
50 | end
51 |
52 | context 'PUT #reopen' do
53 | it 'redirects to the thread and returns an error message' do
54 | @user.can_edit = true
55 | put :reopen, params: { id: @thread.id }
56 | expect(response).to redirect_to(@commontable_path)
57 | expect(assigns(:commontator_thread).errors).not_to be_empty
58 | expect(assigns(:commontator_thread).is_closed?).to eq false
59 | expect(assigns(:commontator_thread).closer).to be_nil
60 |
61 | @user.can_edit = false
62 | @user.is_admin = true
63 | put :reopen, params: { id: @thread.id }
64 | expect(response).to redirect_to(@commontable_path)
65 | expect(assigns(:commontator_thread).errors).not_to be_empty
66 | expect(assigns(:commontator_thread).is_closed?).to eq false
67 | expect(assigns(:commontator_thread).closer).to be_nil
68 | end
69 | end
70 | end
71 |
72 | context 'closed' do
73 | before { expect(@thread.close).to eq true }
74 |
75 | context 'PUT #reopen' do
76 | it 'works' do
77 | @user.can_edit = true
78 | put :reopen, params: { id: @thread.id }
79 | expect(response).to redirect_to(@commontable_path)
80 | expect(assigns(:commontator_thread).errors).to be_empty
81 | expect(assigns(:commontator_thread).is_closed?).to eq false
82 |
83 | expect(assigns(:commontator_thread).close).to eq true
84 | @user.can_edit = false
85 | @user.is_admin = true
86 | put :reopen, params: { id: @thread.id }
87 | expect(response).to redirect_to(@commontable_path)
88 | expect(assigns(:commontator_thread).errors).to be_empty
89 | expect(assigns(:commontator_thread).is_closed?).to eq false
90 | end
91 | end
92 |
93 | context 'PUT #close' do
94 | it 'redirects to the thread and returns an error message' do
95 | @user.can_edit = true
96 | put :close, params: { id: @thread.id }
97 | expect(response).to redirect_to(@commontable_path)
98 | expect(assigns(:commontator_thread).errors).not_to be_empty
99 | expect(assigns(:commontator_thread).is_closed?).to eq true
100 | expect(assigns(:commontator_thread).closer).to be_nil
101 |
102 | @user.can_edit = false
103 | @user.is_admin = true
104 | put :close, params: { id: @thread.id }
105 | expect(response).to redirect_to(@commontable_path)
106 | expect(assigns(:commontator_thread).errors).not_to be_empty
107 | expect(assigns(:commontator_thread).is_closed?).to eq true
108 | expect(assigns(:commontator_thread).closer).to be_nil
109 | end
110 | end
111 | end
112 |
113 | context 'GET #mentions' do
114 | let(:search_phrase) { nil }
115 | let(:call_request) do
116 | get :mentions, params: { id: @thread.id, format: :json, q: search_phrase }
117 | end
118 |
119 | let!(:other_user) { DummyUser.create }
120 |
121 | context 'mentions enabled' do
122 | context 'query is blank' do
123 | it 'returns a JSON error message' do
124 | call_request
125 | expect(response).to have_http_status(:unprocessable_entity)
126 | expect(JSON.parse(response.body)['errors']).to(
127 | include('Query string is too short (minimum 3 characters)')
128 | )
129 | end
130 | end
131 |
132 | context 'query is too short' do
133 | let(:search_phrase) { 'Us' }
134 |
135 | it 'returns a JSON error message' do
136 | call_request
137 | expect(response).to have_http_status(:unprocessable_entity)
138 | expect(JSON.parse(response.body)['errors']).to(
139 | include('Query string is too short (minimum 3 characters)')
140 | )
141 | end
142 | end
143 |
144 | context 'query is 3 characters or more' do
145 | let(:search_phrase) { 'User' }
146 |
147 | let(:valid_result) { [@user] }
148 | let(:valid_response) do
149 | {
150 | 'mentions' => valid_result.map do |user|
151 | { 'id' => user.id, 'name' => user.name, 'type' => 'user' }
152 | end
153 | }
154 | end
155 |
156 | it 'calls the user_mentions_proc and returns the result' do
157 | expect(Commontator.user_mentions_proc).to(
158 | receive(:call).with(@user, @thread, search_phrase).and_return(valid_result)
159 | )
160 |
161 | call_request
162 | expect(response).to have_http_status(:success)
163 |
164 | response_body = JSON.parse(response.body)
165 | expect(response_body['errors']).to be_nil
166 | expect(response_body).to eq valid_response
167 | end
168 | end
169 | end
170 |
171 | context 'mentions disabled' do
172 | before { @thread.config.mentions_enabled = false }
173 | after { @thread.config.mentions_enabled = true }
174 |
175 | it 'returns 403 Forbidden' do
176 | call_request
177 | expect(response).to have_http_status(:forbidden)
178 | end
179 | end
180 | end
181 | end
182 |
183 | context 'unauthorized' do
184 | context 'GET #show' do
185 | it 'returns 403 Forbidden' do
186 | get :show, params: { id: @thread.id }
187 | expect(response).to have_http_status(:forbidden)
188 |
189 | Thread.current[:user] = @user
190 | get :show, params: { id: @thread.id }
191 | expect(response).to have_http_status(:forbidden)
192 | end
193 | end
194 |
195 | context 'PUT #close' do
196 | it 'returns 403 Forbidden' do
197 | put :close, params: { id: @thread.id }
198 | expect(response).to have_http_status(:forbidden)
199 | @thread.reload
200 | expect(@thread.is_closed?).to eq false
201 |
202 | Thread.current[:user] = @user
203 | put :close, params: { id: @thread.id }
204 | expect(response).to have_http_status(:forbidden)
205 | @thread.reload
206 | expect(@thread.is_closed?).to eq false
207 |
208 | @user.can_read = true
209 | put :close, params: { id: @thread.id }
210 | expect(response).to have_http_status(:forbidden)
211 | @thread.reload
212 | expect(@thread.is_closed?).to eq false
213 |
214 | @user.can_edit = true
215 | expect(@thread.close).to eq true
216 | put :close, params: { id: @thread.id }
217 | expect(response).to redirect_to(@commontable_path)
218 | expect(assigns(:commontator_thread).errors).not_to be_empty
219 | end
220 | end
221 |
222 | context 'PUT #reopen' do
223 | it 'returns 403 Forbidden' do
224 | expect(@thread.close).to eq true
225 | put :reopen, params: { id: @thread.id }
226 | expect(response).to have_http_status(:forbidden)
227 | @thread.reload
228 | expect(@thread.is_closed?).to eq true
229 |
230 | Thread.current[:user] = @user
231 | put :reopen, params: { id: @thread.id }
232 | expect(response).to have_http_status(:forbidden)
233 | @thread.reload
234 | expect(@thread.is_closed?).to eq true
235 |
236 | @user.can_read = true
237 | put :reopen, params: { id: @thread.id }
238 | expect(response).to have_http_status(:forbidden)
239 | @thread.reload
240 | expect(@thread.is_closed?).to eq true
241 |
242 | expect(@thread.reopen).to eq true
243 | @user.can_edit = true
244 | put :reopen, params: { id: @thread.id }
245 | expect(response).to redirect_to(@commontable_path)
246 | expect(assigns(:commontator_thread).errors).not_to be_empty
247 | end
248 | end
249 |
250 | context 'GET #mentions' do
251 | it 'returns 403 Forbidden' do
252 | get :mentions, params: { id: @thread.id, format: :json, q: 'User' }
253 | expect(response).to have_http_status(:forbidden)
254 | end
255 | end
256 | end
257 | end
258 |
--------------------------------------------------------------------------------
/spec/dummy/README.md:
--------------------------------------------------------------------------------
1 | ## Dummy
2 |
3 | A dummy application used to test the Commontator engine.
4 |
5 |
--------------------------------------------------------------------------------
/spec/dummy/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require_relative 'config/application'
5 |
6 | Dummy::Application.load_tasks
7 |
--------------------------------------------------------------------------------
/spec/dummy/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link commontator/manifest.js
2 | //= link_directory ../javascripts .js
3 | //= link_directory ../stylesheets .css
4 |
--------------------------------------------------------------------------------
/spec/dummy/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // the compiled file.
9 | //
10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11 | // GO AFTER THE REQUIRES BELOW.
12 | //
13 | //= require jquery
14 | //= require rails-ujs
15 | //= require commontator/application
16 | //= require_tree .
17 |
--------------------------------------------------------------------------------
/spec/dummy/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the top of the
9 | * compiled file, but it's generally better to create a new file per style scope.
10 | *
11 | *= require_self
12 | *= require commontator/application
13 | *= require_tree .
14 | */
15 |
--------------------------------------------------------------------------------
/spec/dummy/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | end
3 |
--------------------------------------------------------------------------------
/spec/dummy/app/controllers/dummy_api_controller.rb:
--------------------------------------------------------------------------------
1 | class DummyApiController < ActionController::API
2 | before_action :get_dummy
3 |
4 | def show
5 | commontator_thread_show(@dummy_model)
6 | end
7 |
8 | def url_options
9 | return Hash.new if request.nil?
10 | super
11 | end
12 |
13 | protected
14 |
15 | def get_dummy
16 | @dummy_model = DummyModel.find_by(id: params[:id]) || DummyModel.first
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/spec/dummy/app/controllers/dummy_models_controller.rb:
--------------------------------------------------------------------------------
1 | class DummyModelsController < ApplicationController
2 | before_action :get_dummy
3 |
4 | def show
5 | commontator_thread_show(@dummy_model)
6 | end
7 |
8 | def url_options
9 | return Hash.new if request.nil?
10 | super
11 | end
12 |
13 | protected
14 |
15 | def get_dummy
16 | @dummy_model = DummyModel.find_by(id: params[:id]) || DummyModel.first
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/spec/dummy/app/models/dummy_dependent_model.rb:
--------------------------------------------------------------------------------
1 | class DummyDependentModel < ActiveRecord::Base
2 | acts_as_commontable dependent: :destroy
3 | end
4 |
5 |
--------------------------------------------------------------------------------
/spec/dummy/app/models/dummy_model.rb:
--------------------------------------------------------------------------------
1 | class DummyModel < ActiveRecord::Base
2 | acts_as_commontable
3 | end
4 |
5 |
--------------------------------------------------------------------------------
/spec/dummy/app/models/dummy_user.rb:
--------------------------------------------------------------------------------
1 | class DummyUser < ActiveRecord::Base
2 | acts_as_commontator
3 |
4 | attr_accessor :is_admin, :can_edit, :can_read
5 |
6 | def email
7 | "dummy_user#{id}@example.com"
8 | end
9 |
10 | def name
11 | "Dummy User ##{id}"
12 | end
13 | end
14 |
15 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/dummy_models/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= commontator_thread(@dummy_model) %>
2 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Dummy
5 | <%= stylesheet_link_tag "application", media: "all" %>
6 | <%= javascript_include_tag "application" %>
7 | <%= csrf_meta_tags %>
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/spec/dummy/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path('../config/application', __dir__)
3 | require_relative '../config/boot'
4 | require 'rails/commands'
5 |
--------------------------------------------------------------------------------
/spec/dummy/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/spec/dummy/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 |
4 | # path to your application root.
5 | APP_ROOT = File.expand_path('..', __dir__)
6 |
7 | def system!(*args)
8 | system(*args) || abort("\n== Command #{args} failed ==")
9 | end
10 |
11 | FileUtils.chdir APP_ROOT do
12 | # This script is a way to setup or update your development environment automatically.
13 | # This script is idempotent, so that you can run it at anytime and get an expectable outcome.
14 | # Add necessary setup steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # puts "\n== Copying sample files =="
21 | # unless File.exist?('config/database.yml')
22 | # FileUtils.cp 'config/database.yml.sample', 'config/database.yml'
23 | # end
24 |
25 | puts "\n== Preparing database =="
26 | system! 'bin/rails db:prepare'
27 |
28 | puts "\n== Removing old logs and tempfiles =="
29 | system! 'bin/rails log:clear tmp:clear'
30 |
31 | puts "\n== Restarting application server =="
32 | system! 'bin/rails restart'
33 | end
34 |
--------------------------------------------------------------------------------
/spec/dummy/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require_relative 'config/environment'
4 | run Rails.application
5 |
--------------------------------------------------------------------------------
/spec/dummy/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative 'boot'
2 |
3 | require 'rails/all'
4 |
5 | # Require the gems listed in Gemfile, including any gems
6 | # you've limited to :test, :development, or :production.
7 | Bundler.require(*Rails.groups)
8 |
9 | module Dummy
10 | class Application < Rails::Application
11 | # Initialize configuration defaults for originally generated Rails version.
12 | config.load_defaults 6.1
13 |
14 | # Settings in config/environments/* take precedence over those specified here.
15 | # Application configuration can go into files in config/initializers
16 | # -- all .rb files in that directory are automatically loaded after loading
17 | # the framework and any gems in your application.
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/spec/dummy/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
2 |
3 | require 'bundler/setup' # Set up gems listed in the Gemfile.
4 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations.
5 |
--------------------------------------------------------------------------------
/spec/dummy/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: test
6 |
7 | production:
8 | adapter: redis
9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
10 | channel_prefix: dummy_production
11 |
--------------------------------------------------------------------------------
/spec/dummy/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | <% commontator_database_adapter = ENV.fetch('COMMONTATOR_DATABASE_ADAPTER', 'sqlite3') %>
7 |
8 | default: &default
9 | adapter: <%= commontator_database_adapter %>
10 | pool: <%= ENV.fetch('RAILS_MAX_THREADS') { 5 } %>
11 | host: localhost
12 | username: <%= ENV.fetch('COMMONTATOR_DATABASE_USERNAME', 'commontator') %>
13 | password: <%= ENV.fetch('COMMONTATOR_DATABASE_PASSWORD', 'commontator') %>
14 |
15 | development:
16 | <<: *default
17 | database: <%= ENV.fetch(
18 | 'COMMONTATOR_DEV_DATABASE',
19 | commontator_database_adapter == 'sqlite3' ? 'db/development.sqlite3' : 'commontator_dev'
20 | ) %>
21 |
22 | # Warning: The database defined as "test" will be erased and
23 | # re-generated from your development database when you run "rake".
24 | # Do not set this db to the same as development or production.
25 | test:
26 | <<: *default
27 | database: <%= ENV.fetch(
28 | 'COMMONTATOR_TEST_DATABASE',
29 | commontator_database_adapter == 'sqlite3' ? 'db/test.sqlite3' : 'commontator_test'
30 | ) %>
31 |
--------------------------------------------------------------------------------
/spec/dummy/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative 'application'
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/spec/dummy/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports.
13 | config.consider_all_requests_local = true
14 |
15 | # Enable/disable caching. By default caching is disabled.
16 | # Run rails dev:cache to toggle caching.
17 | if Rails.root.join('tmp', 'caching-dev.txt').exist?
18 | config.action_controller.perform_caching = true
19 | config.action_controller.enable_fragment_cache_logging = true
20 |
21 | config.cache_store = :memory_store
22 | config.public_file_server.headers = {
23 | 'Cache-Control' => "public, max-age=#{2.days.to_i}"
24 | }
25 | else
26 | config.action_controller.perform_caching = false
27 |
28 | config.cache_store = :null_store
29 | end
30 |
31 | # Store uploaded files on the local file system (see config/storage.yml for options).
32 | config.active_storage.service = :local
33 |
34 | # Don't care if the mailer can't send.
35 | config.action_mailer.raise_delivery_errors = false
36 |
37 | config.action_mailer.perform_caching = false
38 |
39 | # Print deprecation notices to the Rails logger.
40 | config.active_support.deprecation = :log
41 |
42 | # Raise an error on page load if there are pending migrations.
43 | config.active_record.migration_error = :page_load
44 |
45 | # Highlight code that triggered database queries in logs.
46 | config.active_record.verbose_query_logs = true
47 |
48 | # Debug mode disables concatenation and preprocessing of assets.
49 | # This option may cause significant delays in view rendering with a large
50 | # number of complex assets.
51 | config.assets.debug = true
52 |
53 | # Suppress logger output for asset requests.
54 | config.assets.quiet = true
55 |
56 | # Raises error for missing translations.
57 | # config.action_view.raise_on_missing_translations = true
58 |
59 | # Use an evented file watcher to asynchronously detect changes in source code,
60 | # routes, locales, etc. This feature depends on the listen gem.
61 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker
62 | end
63 |
--------------------------------------------------------------------------------
/spec/dummy/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both threaded web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
18 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
19 | # config.require_master_key = true
20 |
21 | # Disable serving static files from the `/public` folder by default since
22 | # Apache or NGINX already handles this.
23 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
24 |
25 | # Compress CSS using a preprocessor.
26 | # config.assets.css_compressor = :sass
27 |
28 | # Do not fallback to assets pipeline if a precompiled asset is missed.
29 | config.assets.compile = false
30 |
31 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
32 | # config.action_controller.asset_host = 'http://assets.example.com'
33 |
34 | # Specifies the header that your server uses for sending files.
35 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
36 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
37 |
38 | # Store uploaded files on the local file system (see config/storage.yml for options).
39 | config.active_storage.service = :local
40 |
41 | # Mount Action Cable outside main process or domain.
42 | # config.action_cable.mount_path = nil
43 | # config.action_cable.url = 'wss://example.com/cable'
44 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
45 |
46 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
47 | # config.force_ssl = true
48 |
49 | # Use the lowest log level to ensure availability of diagnostic information
50 | # when problems arise.
51 | config.log_level = :debug
52 |
53 | # Prepend all log lines with the following tags.
54 | config.log_tags = [ :request_id ]
55 |
56 | # Use a different cache store in production.
57 | # config.cache_store = :mem_cache_store
58 |
59 | # Use a real queuing backend for Active Job (and separate queues per environment).
60 | # config.active_job.queue_adapter = :resque
61 | # config.active_job.queue_name_prefix = "dummy_production"
62 |
63 | config.action_mailer.perform_caching = false
64 |
65 | # Ignore bad email addresses and do not raise email delivery errors.
66 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
67 | # config.action_mailer.raise_delivery_errors = false
68 |
69 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
70 | # the I18n.default_locale when a translation cannot be found).
71 | config.i18n.fallbacks = true
72 |
73 | # Send deprecation notices to registered listeners.
74 | config.active_support.deprecation = :notify
75 |
76 | # Use default logging formatter so that PID and timestamp are not suppressed.
77 | config.log_formatter = ::Logger::Formatter.new
78 |
79 | # Use a different logger for distributed setups.
80 | # require 'syslog/logger'
81 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
82 |
83 | if ENV["RAILS_LOG_TO_STDOUT"].present?
84 | logger = ActiveSupport::Logger.new(STDOUT)
85 | logger.formatter = config.log_formatter
86 | config.logger = ActiveSupport::TaggedLogging.new(logger)
87 | end
88 |
89 | # Do not dump schema after migrations.
90 | config.active_record.dump_schema_after_migration = false
91 |
92 | # Inserts middleware to perform automatic connection switching.
93 | # The `database_selector` hash is used to pass options to the DatabaseSelector
94 | # middleware. The `delay` is used to determine how long to wait after a write
95 | # to send a subsequent read to the primary.
96 | #
97 | # The `database_resolver` class is used by the middleware to determine which
98 | # database is appropriate to use based on the time delay.
99 | #
100 | # The `database_resolver_context` class is used by the middleware to set
101 | # timestamps for the last write to the primary. The resolver uses the context
102 | # class timestamps to determine how long to wait before reading from the
103 | # replica.
104 | #
105 | # By default Rails will store a last write timestamp in the session. The
106 | # DatabaseSelector middleware is designed as such you can define your own
107 | # strategy for connection switching and pass that into the middleware through
108 | # these configuration options.
109 | # config.active_record.database_selector = { delay: 2.seconds }
110 | # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
111 | # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
112 | end
113 |
--------------------------------------------------------------------------------
/spec/dummy/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | # The test environment is used exclusively to run your application's
2 | # test suite. You never need to work with it otherwise. Remember that
3 | # your test database is "scratch space" for the test suite and is wiped
4 | # and recreated between test runs. Don't rely on the data there!
5 |
6 | Rails.application.configure do
7 | # Settings specified here will take precedence over those in config/application.rb.
8 |
9 | config.cache_classes = true
10 |
11 | # Do not eager load code on boot. This avoids loading your whole application
12 | # just for the purpose of running a single test. If you are using a tool that
13 | # preloads Rails for running tests, you may have to set it to true.
14 | config.eager_load = false
15 |
16 | # Configure public file server for tests with Cache-Control for performance.
17 | config.public_file_server.enabled = true
18 | config.public_file_server.headers = {
19 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}"
20 | }
21 |
22 | # Show full error reports and disable caching.
23 | config.consider_all_requests_local = true
24 | config.action_controller.perform_caching = false
25 | config.cache_store = :null_store
26 |
27 | # Raise exceptions instead of rendering exception templates.
28 | config.action_dispatch.show_exceptions = false
29 |
30 | # Disable request forgery protection in test environment.
31 | config.action_controller.allow_forgery_protection = false
32 |
33 | # Store uploaded files on the local file system in a temporary directory.
34 | config.active_storage.service = :test
35 |
36 | config.action_mailer.perform_caching = false
37 |
38 | # Tell Action Mailer not to deliver emails to the real world.
39 | # The :test delivery method accumulates sent emails in the
40 | # ActionMailer::Base.deliveries array.
41 | config.action_mailer.delivery_method = :test
42 |
43 | # Print deprecation notices to the stderr.
44 | config.active_support.deprecation = :stderr
45 |
46 | # Raises error for missing translations.
47 | # config.action_view.raise_on_missing_translations = true
48 | end
49 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # ActiveSupport::Reloader.to_prepare do
4 | # ApplicationController.renderer.defaults.merge!(
5 | # http_host: 'example.org',
6 | # https: false
7 | # )
8 | # end
9 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = '1.0'
5 |
6 | # Add additional assets to the asset load path.
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 |
9 | # Precompile additional assets.
10 | # application.js, application.css, and all non-JS/CSS in the app/assets
11 | # folder are already added.
12 | # Rails.application.config.assets.precompile += %w( admin.js admin.css )
13 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/commontator.rb:
--------------------------------------------------------------------------------
1 | # Dummy application configuration file
2 | Commontator.configure do |config|
3 | config.javascript_proc = ->(view) { '// Some javascript' }
4 |
5 | config.current_user_proc = ->(context) do
6 | user = Thread.current[:user]
7 | return user unless user.nil? && Rails.env.development?
8 |
9 | DummyUser.order(:created_at).last.tap do |user|
10 | user.can_read = true
11 | user.can_edit = true
12 | user.is_admin = true
13 | end
14 | end
15 |
16 | config.user_name_proc = ->(user) { user.try(:name) || 'Anonymous' }
17 |
18 | config.user_avatar_proc = ->(user, view) do
19 | view.commontator_gravatar_image_tag(user, 1, s: 60, d: 'mm')
20 | end
21 |
22 | config.thread_read_proc = ->(thread, user) { user && user.can_read }
23 |
24 | config.thread_moderator_proc = ->(thread, user) { user.is_admin || user.can_edit }
25 |
26 | config.comment_voting = :ld
27 |
28 | config.comment_order = :e
29 |
30 | config.new_comment_style = :l
31 |
32 | config.comment_reply_style = :b
33 |
34 | config.comments_per_page = [ 5, 3, 2 ]
35 |
36 | config.thread_subscription = :b
37 |
38 | config.mentions_enabled = true
39 |
40 | config.user_mentions_proc = ->(current_user, thread, query) do
41 | 'DummyUser'.include?(query) ? DummyUser.all : DummyUser.none
42 | end
43 |
44 | config.comment_filter = Commontator::Comment.arel_table[:body].does_not_match('%hidden%')
45 | end
46 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Define an application-wide content security policy
4 | # For further information see the following documentation
5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
6 |
7 | # Rails.application.config.content_security_policy do |policy|
8 | # policy.default_src :self, :https
9 | # policy.font_src :self, :https, :data
10 | # policy.img_src :self, :https, :data
11 | # policy.object_src :none
12 | # policy.script_src :self, :https
13 | # policy.style_src :self, :https
14 |
15 | # # Specify URI for violation reports
16 | # # policy.report_uri "/csp-violation-report-endpoint"
17 | # end
18 |
19 | # If you are using UJS then enable automatic nonce generation
20 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
21 |
22 | # Set the nonce only to specific directives
23 | # Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
24 |
25 | # Report CSP violations to a specified URI
26 | # For further information see the following documentation:
27 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
28 | # Rails.application.config.content_security_policy_report_only = true
29 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Specify a serializer for the signed and encrypted cookie jars.
4 | # Valid options are :json, :marshal, and :hybrid.
5 | Rails.application.config.action_dispatch.cookies_serializer = :json
6 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/default_url_options.rb:
--------------------------------------------------------------------------------
1 | Rails.application.config.action_mailer.default_url_options = { host: 'example.com' }
2 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password]
5 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/secret_token.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Your secret key is used for verifying the integrity of signed cookies.
4 | # If you change this key, all old signed cookies will become invalid!
5 |
6 | # Make sure the secret is at least 30 characters and all random,
7 | # no regular words or you'll be exposed to dictionary attacks.
8 | # You can use `rake secret` to generate a secure secret key.
9 |
10 | # Make sure your secret_key_base is kept private
11 | # if you're sharing your code publicly.
12 | Dummy::Application.config.secret_key_base = 'dummy'
13 |
14 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Dummy::Application.config.session_store :cookie_store, key: '_dummy_session'
4 |
5 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/spec/dummy/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # 'true': 'foo'
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at https://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/spec/dummy/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
3 | root controller: :dummy_models, action: :show
4 |
5 | resources :dummy_models, only: :show
6 |
7 | mount Commontator::Engine => :commontator
8 | end
9 |
--------------------------------------------------------------------------------
/spec/dummy/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10 | # amazon:
11 | # service: S3
12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14 | # region: us-east-1
15 | # bucket: your_own_bucket
16 |
17 | # Remember not to checkin your GCS keyfile to a repository
18 | # google:
19 | # service: GCS
20 | # project: your_project
21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22 | # bucket: your_own_bucket
23 |
24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25 | # microsoft:
26 | # service: AzureStorage
27 | # storage_account_name: your_account_name
28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29 | # container: your_container_name
30 |
31 | # mirror:
32 | # service: Mirror
33 | # primary: local
34 | # mirrors: [ amazon, google, microsoft ]
35 |
--------------------------------------------------------------------------------
/spec/dummy/db/migrate/00_create_dummy_models.rb:
--------------------------------------------------------------------------------
1 | class CreateDummyModels < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :dummy_models do |t|
4 | t.timestamps null: false
5 | end
6 |
7 | reversible do |dir|
8 | dir.up do
9 | DummyModel.create
10 | DummyModel.delete_all
11 | end
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/spec/dummy/db/migrate/01_create_dummy_users.rb:
--------------------------------------------------------------------------------
1 | class CreateDummyUsers < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :dummy_users do |t|
4 | t.timestamps null: false
5 | end
6 |
7 | reversible do |dir|
8 | dir.up do
9 | DummyUser.create
10 | DummyUser.delete_all
11 | end
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/spec/dummy/db/migrate/02_create_dummy_dependent_models.rb:
--------------------------------------------------------------------------------
1 | class CreateDummyDependentModels < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :dummy_dependent_models do |t|
4 | t.timestamps null: false
5 | end
6 |
7 | reversible do |dir|
8 | dir.up do
9 | DummyDependentModel.create
10 | DummyDependentModel.delete_all
11 | end
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/spec/dummy/db/migrate/03_acts_as_votable_migration.rb:
--------------------------------------------------------------------------------
1 | class ActsAsVotableMigration < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :votes do |t|
4 | t.references :votable, polymorphic: true
5 | t.references :voter, polymorphic: true
6 |
7 | t.boolean :vote_flag
8 | t.string :vote_scope
9 | t.integer :vote_weight
10 |
11 | t.timestamps null: false
12 | end
13 |
14 | add_index :votes, [:voter_id, :voter_type, :vote_scope]
15 | add_index :votes, [:votable_id, :votable_type, :vote_scope]
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/dummy/db/schema.rb:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from the current state of the database. Instead
2 | # of editing this file, please use the migrations feature of Active Record to
3 | # incrementally modify your database, and then regenerate this schema definition.
4 | #
5 | # This file is the source Rails uses to define your schema when running `bin/rails
6 | # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
7 | # be faster and is potentially less error prone than running all of your
8 | # migrations from scratch. Old migrations may fail to apply correctly if those
9 | # migrations use external dependencies or application code.
10 | #
11 | # It's strongly recommended that you check this file into your version control system.
12 |
13 | ActiveRecord::Schema.define(version: 11) do
14 |
15 | create_table "commontator_comments", force: :cascade do |t|
16 | t.integer "thread_id", null: false
17 | t.string "creator_type", null: false
18 | t.integer "creator_id", null: false
19 | t.string "editor_type"
20 | t.integer "editor_id"
21 | t.text "body", null: false
22 | t.datetime "deleted_at"
23 | t.integer "cached_votes_up", default: 0
24 | t.integer "cached_votes_down", default: 0
25 | t.datetime "created_at", precision: 6, null: false
26 | t.datetime "updated_at", precision: 6, null: false
27 | t.integer "parent_id"
28 | t.index ["cached_votes_down"], name: "index_commontator_comments_on_cached_votes_down"
29 | t.index ["cached_votes_up"], name: "index_commontator_comments_on_cached_votes_up"
30 | t.index ["creator_id", "creator_type", "thread_id"], name: "index_commontator_comments_on_c_id_and_c_type_and_t_id"
31 | t.index ["editor_type", "editor_id"], name: "index_commontator_comments_on_editor_type_and_editor_id"
32 | t.index ["parent_id"], name: "index_commontator_comments_on_parent_id"
33 | t.index ["thread_id", "created_at"], name: "index_commontator_comments_on_thread_id_and_created_at"
34 | end
35 |
36 | create_table "commontator_subscriptions", force: :cascade do |t|
37 | t.bigint "thread_id", null: false
38 | t.string "subscriber_type", null: false
39 | t.bigint "subscriber_id", null: false
40 | t.datetime "created_at", precision: 6, null: false
41 | t.datetime "updated_at", precision: 6, null: false
42 | t.index ["subscriber_id", "subscriber_type", "thread_id"], name: "index_commontator_subscriptions_on_s_id_and_s_type_and_t_id", unique: true
43 | t.index ["thread_id"], name: "index_commontator_subscriptions_on_thread_id"
44 | end
45 |
46 | create_table "commontator_threads", force: :cascade do |t|
47 | t.string "commontable_type"
48 | t.bigint "commontable_id"
49 | t.string "closer_type"
50 | t.bigint "closer_id"
51 | t.datetime "closed_at"
52 | t.datetime "created_at", precision: 6, null: false
53 | t.datetime "updated_at", precision: 6, null: false
54 | t.index ["closer_type", "closer_id"], name: "index_commontator_threads_on_closer_type_and_closer_id"
55 | t.index ["commontable_type", "commontable_id"], name: "index_commontator_threads_on_c_id_and_c_type", unique: true
56 | end
57 |
58 | create_table "dummy_dependent_models", force: :cascade do |t|
59 | t.datetime "created_at", null: false
60 | t.datetime "updated_at", null: false
61 | end
62 |
63 | create_table "dummy_models", force: :cascade do |t|
64 | t.datetime "created_at", null: false
65 | t.datetime "updated_at", null: false
66 | end
67 |
68 | create_table "dummy_users", force: :cascade do |t|
69 | t.datetime "created_at", null: false
70 | t.datetime "updated_at", null: false
71 | end
72 |
73 | create_table "votes", force: :cascade do |t|
74 | t.string "votable_type"
75 | t.integer "votable_id"
76 | t.string "voter_type"
77 | t.integer "voter_id"
78 | t.boolean "vote_flag"
79 | t.string "vote_scope"
80 | t.integer "vote_weight"
81 | t.datetime "created_at", null: false
82 | t.datetime "updated_at", null: false
83 | t.index ["votable_id", "votable_type", "vote_scope"], name: "index_votes_on_votable_id_and_votable_type_and_vote_scope"
84 | t.index ["votable_type", "votable_id"], name: "index_votes_on_votable"
85 | t.index ["voter_id", "voter_type", "vote_scope"], name: "index_votes_on_voter_id_and_voter_type_and_vote_scope"
86 | t.index ["voter_type", "voter_id"], name: "index_votes_on_voter"
87 | end
88 |
89 | add_foreign_key "commontator_comments", "commontator_comments", column: "parent_id", on_update: :restrict, on_delete: :cascade
90 | add_foreign_key "commontator_comments", "commontator_threads", column: "thread_id", on_update: :cascade, on_delete: :cascade
91 | add_foreign_key "commontator_subscriptions", "commontator_threads", column: "thread_id", on_update: :cascade, on_delete: :cascade
92 | end
93 |
--------------------------------------------------------------------------------
/spec/dummy/db/seeds.rb:
--------------------------------------------------------------------------------
1 | DummyUser.create!
2 | DummyModel.create!
3 |
--------------------------------------------------------------------------------
/spec/dummy/lib/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lml/commontator/729fc34968d131bcb5d31325f3e5536e4ee491d4/spec/dummy/lib/.keep
--------------------------------------------------------------------------------
/spec/dummy/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
The page you were looking for doesn't exist.
23 |
You may have mistyped the address or the page may have moved.