biovision-comment 0.1.170914 → 0.7.190905.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/README.md +68 -14
- data/app/assets/javascripts/biovision/comment/biovision-comments.js +118 -0
- data/app/assets/stylesheets/biovision/comment/comments.scss +111 -38
- data/app/controllers/admin/comments_controller.rb +18 -5
- data/app/controllers/comments_controller.rb +45 -32
- data/app/helpers/comments_helper.rb +20 -0
- data/app/models/comment.rb +88 -19
- data/app/models/concerns/commentable_item.rb +15 -0
- data/app/services/biovision/components/comments_component.rb +39 -0
- data/app/services/comment_handler.rb +49 -0
- data/app/services/comments_manager.rb +24 -0
- data/app/views/admin/comments/entity/_in_list.html.erb +8 -5
- data/app/views/admin/comments/index.html.erb +2 -1
- data/app/views/admin/comments/show.html.erb +25 -10
- data/app/views/admin/components/links/_comments.html.erb +3 -0
- data/app/views/comment_mailer/entry_reply.text.erb +2 -6
- data/app/views/comments/_comment.html.erb +66 -16
- data/app/views/comments/_form.html.erb +92 -19
- data/app/views/comments/_list.html.erb +17 -19
- data/app/views/comments/_reply_container.html.erb +15 -0
- data/app/views/comments/_section.html.erb +34 -0
- data/app/views/comments/edit.html.erb +6 -5
- data/app/views/comments/new.html.erb +2 -2
- data/config/locales/comments-ru.yml +32 -2
- data/config/routes.rb +18 -10
- data/db/migrate/20170914000001_create_comments.rb +48 -23
- data/db/migrate/20190203090909_add_fields_to_comments.rb +14 -0
- data/db/migrate/20190428212121_add_approved_to_comments.rb +14 -0
- data/db/migrate/20190428212122_create_comments_component.rb +21 -0
- data/db/migrate/20190628101010_add_data_to_comments.rb +15 -0
- data/lib/biovision/comment/engine.rb +7 -2
- data/lib/biovision/comment/version.rb +3 -1
- metadata +19 -8
- data/app/views/comments/show.html.erb +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a602c3288d543c9a10860099700cb6657f206b3b6dce1b88a82c5ad1c239b5c8
|
4
|
+
data.tar.gz: 552231814a6ad6ed6b43c1edbc12b195192090e2ef821d73b962c1c6cd1850b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba351318a8b74404e890374ae9c9a1cfab90924d25c10dc0374660fba6b014005e7ce4105ca3f6ce4805939935cbd52fd4517d4cda960057e1e2c23bbbc6badb
|
7
|
+
data.tar.gz: 5272919b6715ef8641f458257d9d35f2638b5c4cb6b593163c1fe44e0e3c5d8da2a265477a205bc338b7bd5625282d9330b7db042de7e1b3556fd04a14ea3a1e
|
data/README.md
CHANGED
@@ -1,28 +1,82 @@
|
|
1
1
|
# Biovision::Comment
|
2
|
-
Short description and motivation.
|
3
2
|
|
4
|
-
|
5
|
-
How to use my plugin.
|
3
|
+
Компонент с комментариями для проектов на базе `Biovision`.
|
6
4
|
|
7
|
-
|
8
|
-
|
5
|
+
Находится в стадии разработки. Используйте на свой страх и риск.
|
6
|
+
|
7
|
+
## Использование
|
8
|
+
|
9
|
+
### Подготовка моделей
|
10
|
+
|
11
|
+
Для моделей, которым нужны комментарии, следует в классе добавить это:
|
9
12
|
|
10
13
|
```ruby
|
11
|
-
|
14
|
+
has_many :comments, as: :commentable
|
12
15
|
```
|
13
16
|
|
14
|
-
|
17
|
+
Тамже всем моделям с комментариями нужно поле `comments_count` для счётчика
|
18
|
+
комментарием:
|
19
|
+
|
15
20
|
```bash
|
16
|
-
|
21
|
+
rails g migration add_comments_count_to_sample comments_count:integer
|
22
|
+
```
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
# frozen_string_literal: true
|
26
|
+
|
27
|
+
# Add counter for comments to Sample model
|
28
|
+
class AddCommentsCountToSample < ActiveRecord::Migration[5.2]
|
29
|
+
def change
|
30
|
+
add_column :samples, :comments_count, :integer, default: 0, null: false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
### В представлениях
|
36
|
+
|
37
|
+
Для вывода комментариев используется код такого вида:
|
38
|
+
|
39
|
+
```erb
|
40
|
+
<%= render(partial: 'comments/section', locals: { entity: entity }) %>
|
41
|
+
```
|
42
|
+
|
43
|
+
Здесь `entity` — это объект модели с комментариями.
|
44
|
+
|
45
|
+
### Стили и сценарии
|
46
|
+
|
47
|
+
Для работы переноса формы ответа нужно включить в сценарии JS код компонента
|
48
|
+
в `application.js` после добавления `biovision/base/biovision`:
|
49
|
+
|
50
|
+
```
|
51
|
+
//= require biovision/comment/biovision-comments
|
17
52
|
```
|
18
53
|
|
19
|
-
|
54
|
+
Стили по умолчанию описаны в `biovision/comment/comments.scss`.
|
55
|
+
|
56
|
+
## Установка
|
57
|
+
|
58
|
+
Нужно добавить компонент в `Gemfile`:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
gem 'biovision-comment', git: 'https://github.com/Biovision/biovision-comment'
|
62
|
+
# gem 'biovision-comment', path: '/Users/maxim/Projects/Biovision/gems/biovision-comment'
|
63
|
+
```
|
64
|
+
|
65
|
+
После этого:
|
66
|
+
|
20
67
|
```bash
|
21
|
-
$
|
68
|
+
$ bundle
|
69
|
+
$ rails railties:install:migrations
|
70
|
+
$ rails db:migrate
|
22
71
|
```
|
23
72
|
|
24
|
-
##
|
25
|
-
|
73
|
+
## Вклад в разработку
|
74
|
+
|
75
|
+
Особых инструкций нет. Fork/update/PR, как обычно. Или просто опишите свои идеи
|
76
|
+
в разделе `issues`.
|
77
|
+
|
78
|
+
## Лицензия
|
79
|
+
|
80
|
+
[MIT License](http://opensource.org/licenses/MIT).
|
26
81
|
|
27
|
-
|
28
|
-
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
82
|
+
Продукт предоставляется «как есть». Используйте на свой страх и риск.
|
@@ -0,0 +1,118 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Biovision Comments component
|
5
|
+
*
|
6
|
+
* @type {Object}
|
7
|
+
*/
|
8
|
+
const Comments = {
|
9
|
+
initialized: false,
|
10
|
+
autoInitComponents: true,
|
11
|
+
components: {}
|
12
|
+
};
|
13
|
+
|
14
|
+
/**
|
15
|
+
* Move new comment form to different parent comment
|
16
|
+
*
|
17
|
+
* Handles click on "Reply" button and moves form under parent comment.
|
18
|
+
* Click on "Cancel" button moves form back to top-level reply.
|
19
|
+
*
|
20
|
+
* @type {Object}
|
21
|
+
*/
|
22
|
+
Comments.components.replyFormMover = {
|
23
|
+
initialized: false,
|
24
|
+
/**
|
25
|
+
* Container for list of comments
|
26
|
+
*
|
27
|
+
* @type {HTMLElement}
|
28
|
+
*/
|
29
|
+
listContainer: undefined,
|
30
|
+
/**
|
31
|
+
* Container for reply form
|
32
|
+
*
|
33
|
+
* @type {HTMLElement}
|
34
|
+
*/
|
35
|
+
mainContainer: undefined,
|
36
|
+
form: undefined,
|
37
|
+
/**
|
38
|
+
* @type {HTMLElement}
|
39
|
+
*/
|
40
|
+
parentId: undefined,
|
41
|
+
replyButtons: [],
|
42
|
+
cancelButtons: [],
|
43
|
+
replySelector: ".comment-reply-button button",
|
44
|
+
cancelSelector: ".container button.cancel",
|
45
|
+
/**
|
46
|
+
* Initialize
|
47
|
+
*/
|
48
|
+
init: function () {
|
49
|
+
this.listContainer = document.getElementById("comments");
|
50
|
+
this.form = document.getElementById("comment-form");
|
51
|
+
|
52
|
+
if (this.listContainer && this.form) {
|
53
|
+
this.parentId = document.getElementById("comment_parent_id");
|
54
|
+
this.mainContainer = this.listContainer.querySelector(".reply-container .container");
|
55
|
+
this.listContainer.querySelectorAll(this.replySelector).forEach(this.applyToReplyButton);
|
56
|
+
this.listContainer.querySelectorAll(this.cancelSelector).forEach(this.applyToCancelButton);
|
57
|
+
|
58
|
+
this.initialized = true;
|
59
|
+
}
|
60
|
+
},
|
61
|
+
/**
|
62
|
+
* Apply handler for pressing "Reply" button
|
63
|
+
*
|
64
|
+
* @param {HTMLElement} button
|
65
|
+
* @type {Function}
|
66
|
+
*/
|
67
|
+
applyToReplyButton: function (button) {
|
68
|
+
const component = Comments.components.replyFormMover;
|
69
|
+
|
70
|
+
component.replyButtons.push(button);
|
71
|
+
button.addEventListener("click", component.move);
|
72
|
+
},
|
73
|
+
/**
|
74
|
+
* Apply handler for pressing "Cancel" button
|
75
|
+
*
|
76
|
+
* @param {HTMLElement} button
|
77
|
+
* @type {Function}
|
78
|
+
*/
|
79
|
+
applyToCancelButton: function (button) {
|
80
|
+
const component = Comments.components.replyFormMover;
|
81
|
+
|
82
|
+
component.cancelButtons.push(button);
|
83
|
+
button.addEventListener("click", component.cancel);
|
84
|
+
},
|
85
|
+
/**
|
86
|
+
* Move reply form to parent comment
|
87
|
+
*
|
88
|
+
* @param {Event} event
|
89
|
+
* @type {Function}
|
90
|
+
*/
|
91
|
+
move: function (event) {
|
92
|
+
const component = Comments.components.replyFormMover;
|
93
|
+
const button = event.target;
|
94
|
+
const container = button.closest(".comment-reply-block").querySelector(".container");
|
95
|
+
|
96
|
+
if (container) {
|
97
|
+
component.parentId.value = button.closest(".comment-item").getAttribute("data-id");
|
98
|
+
container.appendChild(component.form);
|
99
|
+
container.classList.remove("hidden");
|
100
|
+
}
|
101
|
+
},
|
102
|
+
/**
|
103
|
+
* Handler for pressing "Cancel" button
|
104
|
+
*
|
105
|
+
* @param {Event} event
|
106
|
+
* @type {Function}
|
107
|
+
*/
|
108
|
+
cancel: function (event) {
|
109
|
+
const component = Comments.components.replyFormMover;
|
110
|
+
const button = event.target;
|
111
|
+
button.parentNode.classList.add("hidden");
|
112
|
+
|
113
|
+
component.parentId.value = "";
|
114
|
+
component.mainContainer.appendChild(component.form);
|
115
|
+
}
|
116
|
+
};
|
117
|
+
|
118
|
+
Biovision.components.comments = Comments;
|
@@ -1,35 +1,37 @@
|
|
1
|
-
$border-
|
2
|
-
$
|
3
|
-
$font-size-small: 1rem !default;
|
1
|
+
$border-secondary: .1rem solid #aaa !default;
|
2
|
+
$text-color-secondary: #777 !default;
|
4
3
|
$row-background-even: #f0f0f0 !default;
|
5
4
|
$row-background-odd: #fafafa !default;
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
border-top: $border-primary;
|
11
|
-
|
12
|
-
> h2 {
|
13
|
-
margin: 0.8rem 0;
|
6
|
+
.comments {
|
7
|
+
h3::first-letter {
|
8
|
+
text-transform: uppercase;
|
14
9
|
}
|
10
|
+
}
|
15
11
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
&:nth-of-type(even) {
|
21
|
-
background-color: $row-background-even;
|
22
|
-
}
|
23
|
-
}
|
12
|
+
.comments-list {
|
13
|
+
border-bottom: $border-secondary;
|
14
|
+
margin: var(--spacer-s, 1.6rem) 0;
|
15
|
+
padding: 0 0 var(--spacer-s, 1.6rem);
|
24
16
|
}
|
25
17
|
|
26
|
-
|
18
|
+
.comment-item {
|
27
19
|
position: relative;
|
20
|
+
background-color: $row-background-odd;
|
21
|
+
padding: var(--spacer-xxs, .4rem);
|
22
|
+
|
23
|
+
&:first-of-type {
|
24
|
+
margin-top: var(--spacer-s, 1.6rem);
|
25
|
+
}
|
26
|
+
|
27
|
+
&:nth-of-type(even) {
|
28
|
+
background-color: $row-background-even;
|
29
|
+
}
|
28
30
|
|
29
31
|
.deleted {
|
30
|
-
font-size:
|
32
|
+
font-size: var(--font-size-decreased, 1.4rem);
|
31
33
|
font-style: italic;
|
32
|
-
padding: 0 .8rem;
|
34
|
+
padding: 0 var(--spacer-xs, .8rem);
|
33
35
|
|
34
36
|
&::before {
|
35
37
|
content: '[';
|
@@ -41,9 +43,9 @@ div.comment {
|
|
41
43
|
}
|
42
44
|
|
43
45
|
.title {
|
44
|
-
|
45
|
-
|
46
|
-
|
46
|
+
border-bottom: dotted .1rem;
|
47
|
+
margin: var(--spacer-xs, .8rem) 0;
|
48
|
+
padding: 0 0 var(--spacer-xxs, .4rem) 0;
|
47
49
|
|
48
50
|
cite {
|
49
51
|
&:before {
|
@@ -56,39 +58,84 @@ div.comment {
|
|
56
58
|
}
|
57
59
|
}
|
58
60
|
|
61
|
+
.meta {
|
62
|
+
display: flex;
|
63
|
+
flex-wrap: wrap;
|
64
|
+
}
|
65
|
+
|
66
|
+
.avatar {
|
67
|
+
height: 4.8rem;
|
68
|
+
margin: 0 var(--spacer-xs, .8rem) 0 0;
|
69
|
+
width: 4.8rem;
|
70
|
+
}
|
71
|
+
|
72
|
+
.info {
|
73
|
+
display: flex;
|
74
|
+
flex-direction: column;
|
75
|
+
|
76
|
+
time {
|
77
|
+
color: $text-color-secondary;
|
78
|
+
font-size: var(--font-size-decreased, 1.4rem);
|
79
|
+
margin-top: auto;
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
.comment-wrapper {
|
84
|
+
display: flex;
|
85
|
+
}
|
86
|
+
|
87
|
+
.vote-block {
|
88
|
+
align-items: center;
|
89
|
+
display: flex;
|
90
|
+
flex-direction: column;
|
91
|
+
flex: none;
|
92
|
+
justify-content: center;
|
93
|
+
width: 3.2rem;
|
94
|
+
|
95
|
+
.result {
|
96
|
+
color: $text-color-secondary;
|
97
|
+
font-size: var(--font-size-decreased, 1.4rem);
|
98
|
+
margin: var(--spacer-xxs, .4rem) 0;
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
.vote {
|
103
|
+
height: 1.6rem;
|
104
|
+
width: .8rem;
|
105
|
+
}
|
106
|
+
|
59
107
|
.body {
|
60
108
|
background: #ffffff;
|
61
|
-
|
62
|
-
|
109
|
+
flex: 1;
|
110
|
+
margin: var(--spacer-xs, .8rem);
|
111
|
+
padding: var(--spacer-xs, .8rem);
|
63
112
|
position: relative;
|
64
113
|
}
|
65
114
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
padding: 0 0.8rem;
|
115
|
+
p {
|
116
|
+
&:first-of-type {
|
117
|
+
margin-top: 0;
|
118
|
+
}
|
71
119
|
|
72
|
-
|
73
|
-
|
74
|
-
font-style: italic;
|
120
|
+
&:last-of-type {
|
121
|
+
margin-bottom: 0;
|
75
122
|
}
|
76
123
|
}
|
77
124
|
|
78
125
|
.footer {
|
79
|
-
padding:
|
126
|
+
padding: var(--spacer-xs, .8rem) var(--spacer-s, 1.6rem);
|
80
127
|
|
81
128
|
> ul {
|
82
129
|
display: flex;
|
130
|
+
justify-content: flex-end;
|
83
131
|
margin: 0;
|
84
132
|
padding: 0;
|
85
|
-
justify-content: flex-end;
|
86
133
|
|
87
134
|
> li {
|
88
135
|
list-style: none;
|
89
|
-
margin: 0
|
136
|
+
margin: 0 var(--spacer-xxs, .4rem);
|
90
137
|
padding: 0;
|
91
|
-
font-size:
|
138
|
+
font-size: var(--font-size-small, 1.2rem);
|
92
139
|
text-align: right;
|
93
140
|
|
94
141
|
&:before {
|
@@ -102,3 +149,29 @@ div.comment {
|
|
102
149
|
}
|
103
150
|
}
|
104
151
|
}
|
152
|
+
|
153
|
+
.comment-form {
|
154
|
+
.fields {
|
155
|
+
padding: var(--spacer-s, 1.6rem) 0;
|
156
|
+
|
157
|
+
label {
|
158
|
+
display: block;
|
159
|
+
}
|
160
|
+
|
161
|
+
textarea {
|
162
|
+
margin: 0;
|
163
|
+
padding: var(--spacer-xs, .8rem);
|
164
|
+
width: 100%;
|
165
|
+
}
|
166
|
+
}
|
167
|
+
|
168
|
+
.required {
|
169
|
+
label {
|
170
|
+
&::before {
|
171
|
+
color: rgb(255, 63, 0);
|
172
|
+
content: '*';
|
173
|
+
margin: 0 var(--spacer-xxs, .4rem) 0 0;
|
174
|
+
}
|
175
|
+
}
|
176
|
+
}
|
177
|
+
}
|
@@ -1,8 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Administrative handling comments
|
1
4
|
class Admin::CommentsController < AdminController
|
2
5
|
include ToggleableEntity
|
3
6
|
include LockableEntity
|
4
7
|
|
5
|
-
before_action :set_entity, except:
|
8
|
+
before_action :set_entity, except: :index
|
6
9
|
|
7
10
|
# get /admin/comments
|
8
11
|
def index
|
@@ -13,16 +16,26 @@ class Admin::CommentsController < AdminController
|
|
13
16
|
def show
|
14
17
|
end
|
15
18
|
|
19
|
+
# put /admin/comments/:id/approve
|
20
|
+
def approve
|
21
|
+
@entity.update(approved: true)
|
22
|
+
|
23
|
+
head :no_content
|
24
|
+
end
|
25
|
+
|
16
26
|
protected
|
17
27
|
|
28
|
+
def component_slug
|
29
|
+
Biovision::Components::CommentsComponent::SLUG
|
30
|
+
end
|
31
|
+
|
18
32
|
def restrict_access
|
19
|
-
|
33
|
+
error = 'Managing comments is not allowed'
|
34
|
+
handle_http_401(error) unless component_handler.allow?('moderator')
|
20
35
|
end
|
21
36
|
|
22
37
|
def set_entity
|
23
38
|
@entity = Comment.find_by(id: params[:id])
|
24
|
-
if @entity.nil?
|
25
|
-
handle_http_404('Cannot find comment')
|
26
|
-
end
|
39
|
+
handle_http_404('Cannot find comment') if @entity.nil?
|
27
40
|
end
|
28
41
|
end
|