diff --git a/Gemfile b/Gemfile index 3f1fc374e2..72718f3856 100644 --- a/Gemfile +++ b/Gemfile @@ -49,10 +49,10 @@ gem 'rails-dom-testing' gem 'rails-i18n', '~> 5.1.3' gem 'rails_autolink' gem 'rb-readline' -gem 'rdiscount', '~> 2.2', '>= 2.2.0.1' +gem 'rdiscount', '~> 2.2' gem "recaptcha", require: "recaptcha/rails" gem 'responders', '~> 3.0' -gem 'rubocop', '~> 0.88.0', require: false +gem 'rubocop', '~> 0.89.0', require: false gem "ruby-openid", :require => "openid" gem 'sanitize' gem 'sentry-raven' diff --git a/Gemfile.lock b/Gemfile.lock index 94656d687f..3439fc7451 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -82,7 +82,7 @@ GEM ci_reporter (~> 2.0) test-unit (>= 2.5.5, < 4.0) climate_control (0.2.0) - codecov (0.2.3) + codecov (0.2.5) colorize json simplecov @@ -421,7 +421,7 @@ GEM rb-inotify (0.10.1) ffi (~> 1.0) rb-readline (0.5.5) - rdiscount (2.2.0.1) + rdiscount (2.2.0.2) recaptcha (4.14.0) json redis (4.1.4) @@ -458,7 +458,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) rspec-support (3.9.0) - rubocop (0.88.0) + rubocop (0.89.0) parallel (~> 1.10) parser (>= 2.7.1.1) rainbow (>= 2.2.2, < 4.0) @@ -467,8 +467,8 @@ GEM rubocop-ast (>= 0.1.0, < 1.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 2.0) - rubocop-ast (0.1.0) - parser (>= 2.7.0.1) + rubocop-ast (0.3.0) + parser (>= 2.7.1.4) ruby-openid (2.9.2) ruby-progressbar (1.10.1) ruby2_keywords (0.0.2) @@ -656,13 +656,13 @@ DEPENDENCIES rails_autolink rake (~> 13.0.1) rb-readline - rdiscount (~> 2.2, >= 2.2.0.1) + rdiscount (~> 2.2) recaptcha responders (~> 3.0) rest-client reverse_markdown rspec - rubocop (~> 0.88.0) + rubocop (~> 0.89.0) ruby-openid sanitize sassc-rails diff --git a/app/assets/javascripts/spam2.js b/app/assets/javascripts/spam2.js index c3258824c1..5853697836 100644 --- a/app/assets/javascripts/spam2.js +++ b/app/assets/javascripts/spam2.js @@ -50,7 +50,8 @@ function pagination(id, url) { function search_table(filter, url) { localStorage.setItem('filter', filter); localStorage.setItem('page-select', pageselect); - window.location = url + filter + "/" + $("#pageselect").val(); + window.location = url + filter + "/30"; + localStorage.setItem('page-select', "30"); } function batch_nav(bulk) { diff --git a/app/assets/stylesheets/spam2.css b/app/assets/stylesheets/spam2.css index 704b602431..fbce44aa2b 100644 --- a/app/assets/stylesheets/spam2.css +++ b/app/assets/stylesheets/spam2.css @@ -49,10 +49,6 @@ width: 100%; } -#card-top { - margin-top: 2vh; -} - #bulk-nav { margin-top: 2vh; margin-bottom: 1vh; @@ -68,6 +64,7 @@ text-align: center; line-height: 1.6; } + .border-curve { border-radius: 40px; } @@ -76,24 +73,14 @@ color: white !important; } -#col-search { - cursor: text; - text-align: left !important; - margin-top: 2px; -} - -#col-select { - margin-top: 2px; -} - .page-table { width: 100%; margin-top: 10px; } .card .navbar .nav .nav-item .active { - color: blue; - border-bottom: 2px blue solid; + color: #343a40; + border-bottom: 2px #343a40 solid; } .spam-alert { @@ -106,8 +93,8 @@ #table-card, #bulk-nav, .nav_top, - .page-table, - #card-top { + .page-table + { margin-left: 5vw; width: 90vw; } @@ -115,9 +102,6 @@ font-size: 18px; margin-bottom: 10px; } - #card-top .card-header { - font-size: 18px; - } #info-btn { margin-left: 5vw; } diff --git a/app/controllers/batch_controller.rb b/app/controllers/batch_controller.rb new file mode 100644 index 0000000000..aaf9dec52a --- /dev/null +++ b/app/controllers/batch_controller.rb @@ -0,0 +1,149 @@ +class BatchController < ApplicationController + before_action :require_user + + def batch_spam + if logged_in_as(%w(moderator admin)) + user_spamed = [] + node_spamed = 0 + params[:ids].split(',').uniq.each do |nid| + node = Node.find nid + node_spamed += 1 + node.spam + user = node.author + user_spamed << user.id + user.ban + end + flash[:notice] = node_spamed.to_s + ' nodes spammed and ' + user_spamed.length.to_s + ' users banned.' + redirect_to '/spam2' + else + flash[:error] = 'Only admins and moderators can mark a batch spam.' + redirect_to '/dashboard' + end + end + + def batch_publish + if logged_in_as(%w(moderator admin)) + node_published = 0 + user_published = [] + params[:ids].split(',').uniq.each do |nid| + node = Node.find nid + node_published += 1 + node.publish + user = node.author + user.unban + user_published << user.id + end + flash[:notice] = node_published.to_s + ' nodes published and ' + user_published.length.to_s + ' users unbanned.' + redirect_to '/spam2' + else + flash[:error] = 'Only admins and moderators can batch publish.' + redirect_to '/dashboard' + end + end + + def batch_delete + if logged_in_as(%w(moderator admin)) + node_delete = 0 + params[:ids].split(',').uniq.each do |nid| + node = Node.find nid + node_delete += 1 + node.delete + end + flash[:notice] = node_delete.to_s + ' nodes deleted' + redirect_back fallback_location: root_path + else + flash[:error] = 'Only admins and moderators can batch delete.' + redirect_to '/dashboard' + end + end + + def batch_ban + if logged_in_as(%w(admin moderator)) + user_ban = [] + params[:ids].split(',').uniq.each do |nid| + node = Node.find nid + user = node.author + user_ban << user.id + user.ban + end + flash[:notice] = user_ban.length.to_s + ' users banned.' + redirect_back fallback_location: root_path + else + flash[:error] = 'Only admins and moderators can ban users.' + redirect_to '/dashboard' + end + end + + def batch_unban + unbanned_users = [] + if logged_in_as(%w(moderator admin)) + params[:ids].split(',').uniq.each do |node_id| + node = Node.find node_id + user_unban = node.author + unbanned_users << user_unban.id + user_unban.unban + end + flash[:notice] = unbanned_users.length.to_s + ' users unbanned.' + redirect_back fallback_location: root_path + else + flash[:error] = 'Only admins and moderators can unban users.' + redirect_to '/dashboard' + end + end + + def batch_ban_user + users = [] + if logged_in_as(%w(admin moderator)) + params[:ids].split(',').uniq.each do |user_id| + user_ban = User.find user_id + users << user_ban.id + user_ban.ban + end + flash[:notice] = users.length.to_s + ' users banned.' + redirect_back fallback_location: root_path + else + flash[:error] = 'Only moderators can moderate users.' + redirect_to '/dashboard' + end + end + + def batch_unban_user + if logged_in_as(%w(moderator admin)) + params[:ids].split(',').uniq.each do |id| + unban_user = User.find id + unban_user.unban + end + flash[:notice] = 'Success! users unbanned.' + redirect_back fallback_location: root_path + else + flash[:error] = 'Only admins and moderators can moderate users.' + redirect_to '/dashboard' + end + end + + def batch_comment + if logged_in_as(%w(moderator admin)) + comment_total = 0 + params[:ids].split(',').uniq.each do |cid| + comment = Comment.find cid + comment_total += 1 + case params[:type] + when 'publish' + comment.publish + when 'spam' + comment.spam + user.ban + when 'delete' + comment.delete + else + flash[:notice] = 'Invalid Url' + end + end + flash[:notice] = comment_total.to_s + ' comment moderated' + redirect_back fallback_location: root_path + else + flash[:error] = 'Only admins and moderators can moderate comments.' + redirect_to '/dashboard' + end + end +end diff --git a/app/controllers/spam2_controller.rb b/app/controllers/spam2_controller.rb index fdf86fee2a..94d7f29509 100644 --- a/app/controllers/spam2_controller.rb +++ b/app/controllers/spam2_controller.rb @@ -16,8 +16,6 @@ def _spam else @nodes.where(status: [0, 4]).order('changed DESC') end - @node_unmoderated_count = Node.where(status: 4).size - @node_flag_count = Node.where('flag > ?', 0).size else flash[:error] = 'Only moderators can moderate posts.' redirect_to '/dashboard' @@ -48,6 +46,32 @@ def _spam_flags end end + def _spam_queue + if logged_in_as(%w(moderator admin)) + @tags_followed = TagSelection.where(following: true, user_id: current_user.id) + @tag_queue = case params[:tag] + when 'everything' + TagSelection.where(following: true, user_id: current_user.id) + else + tids = Tag.where(name: params[:tag]).collect(&:tid) + TagSelection.where(following: true, tid: tids, user_id: current_user.id) + end + nodes = [] + @tag_queue.each do |tag_queue_name| + nodes += NodeTag.where(tid: tag_queue_name.tid).collect(&:nid) + end + @queue = Node.where(status: [0, 4]).or(Node.where('flag > ?', 0)) + .where(nid: nodes, type: %w(note page)) + .paginate(page: params[:page], per_page: 30) + .order('changed DESC') + .distinct + render template: 'spam2/_spam' + else + flash[:error] = 'Only moderators can moderate posts.' + redirect_to '/dashboard' + end + end + def _spam_users if logged_in_as(%w(moderator admin)) @users = User.paginate(page: params[:page], per_page: params[:pagination]) @@ -61,8 +85,6 @@ def _spam_users else @users.where('rusers.status = 1') end - @user_active_count = User.where('rusers.status = 1').size - @user_ban_count = User.where('rusers.status = 0').size render template: 'spam2/_spam' else flash[:error] = 'Only moderators can moderate other users.' @@ -73,7 +95,7 @@ def _spam_users def _spam_revisions if logged_in_as(%w(admin moderator)) @revisions = Revision.where(status: 0) - .paginate(page: params[:page], per_page: 50) + .paginate(page: params[:page], per_page: 30) .order('timestamp DESC') render template: 'spam2/_spam' else @@ -97,8 +119,6 @@ def _spam_comments .or(@comments.where('flag > ?', 0)) .order('timestamp DESC') end - @comment_unmoderated_count = Comment.where(status: 4).size - @comment_flag_count = Comment.where('flag > ?', 0).size render template: 'spam2/_spam' else flash[:error] = 'Only moderators can moderate comments.' @@ -106,126 +126,6 @@ def _spam_comments end end - def batch_spam - if logged_in_as(%w(moderator admin)) - user_spamed = [] - node_spamed = 0 - params[:ids].split(',').uniq.each do |nid| - node = Node.find nid - node_spamed += 1 - node.spam - user = node.author - user_spamed << user.id - user.ban - end - flash[:notice] = node_spamed.to_s + ' nodes spammed and ' + user_spamed.size.to_s + ' users banned.' - redirect_to '/spam2' - else - flash[:error] = 'Only admins and moderators can mark a batch spam.' - redirect_to '/dashboard' - end - end - - def batch_publish - if logged_in_as(%w(moderator admin)) - node_published = 0 - user_published = [] - params[:ids].split(',').uniq.each do |nid| - node = Node.find nid - node_published += 1 - node.publish - user = node.author - user.unban - user_published << user.id - end - flash[:notice] = node_published.to_s + ' nodes published and ' + user_published.size.to_s + ' users unbanned.' - redirect_to '/spam2' - else - flash[:error] = 'Only admins and moderators can batch publish.' - redirect_to '/dashboard' - end - end - - def batch_delete - if logged_in_as(%w(moderator admin)) - node_delete = 0 - params[:ids].split(',').uniq.each do |nid| - node = Node.find nid - node_delete += 1 - node.delete - end - flash[:notice] = node_delete.to_s + ' nodes deleted' - redirect_back fallback_location: root_path - else - flash[:error] = 'Only admins and moderators can batch delete.' - redirect_to '/dashboard' - end - end - - def batch_ban - if logged_in_as(%w(admin moderator)) - user_ban = [] - params[:ids].split(',').uniq.each do |nid| - node = Node.find nid - user = node.author - user_ban << user.id - user.ban - end - flash[:notice] = user_ban.size.to_s + ' users banned.' - redirect_back fallback_location: root_path - else - flash[:error] = 'Only admins and moderators can ban users.' - redirect_to '/dashboard' - end - end - - def batch_unban - unbanned_users = [] - if logged_in_as(%w(moderator admin)) - params[:ids].split(',').uniq.each do |node_id| - node = Node.find node_id - user_unban = node.author - unbanned_users << user_unban.id - user_unban.unban - end - flash[:notice] = unbanned_users.size.to_s + ' users unbanned.' - redirect_back fallback_location: root_path - else - flash[:error] = 'Only admins and moderators can unban users.' - redirect_to '/dashboard' - end - end - - def batch_ban_user - users = [] - if logged_in_as(%w(admin moderator)) - params[:ids].split(',').uniq.each do |user_id| - user_ban = User.find user_id - users << user_ban.id - user_ban.ban - end - flash[:notice] = users.size.to_s + ' users banned.' - redirect_back fallback_location: root_path - else - flash[:error] = 'Only moderators can moderate users.' - redirect_to '/dashboard' - end - end - - def batch_unban_user - if logged_in_as(%w(moderator admin)) - params[:ids].split(',').uniq.each do |id| - unban_user = User.find id - unban_user.unban - end - flash[:notice] = 'Success! users unbanned.' - redirect_back fallback_location: root_path - else - flash[:error] = 'Only admins and moderators can moderate users.' - redirect_to '/dashboard' - end - end - def flag_node @node = Node.find params[:id] @node.flag_node diff --git a/app/controllers/tag_controller.rb b/app/controllers/tag_controller.rb index d4aa5041b4..1d0f10f2ef 100644 --- a/app/controllers/tag_controller.rb +++ b/app/controllers/tag_controller.rb @@ -118,6 +118,7 @@ def show end nodes = nodes.paginate(page: params[:page], per_page: 24).order(order_by) + @paginated = true if @start && @end nodes = nodes.where(created: @start.to_i..@end.to_i) diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index 9c98273393..579db50905 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -102,6 +102,10 @@ def edit if @node.has_tag('locked') && !current_user.can_moderate? flash[:warning] = "This page is locked, and only moderators can edit it." redirect_to @node.path + elsif current_user &.first_time_poster + flash[:notice] = "Please post a question or other content before editing the wiki. Click here to learn why." + redirect_to Node.find_wiki(params[:id]).path + return end if ((Time.now.to_i - @node.latest.timestamp) < 5.minutes.to_i) && @node.latest.author.uid != current_user.uid flash.now[:warning] = I18n.t('wiki_controller.someone_clicked_edit_5_minutes_ago') diff --git a/app/models/tag.rb b/app/models/tag.rb index c570f3565f..c533bbfd09 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -37,7 +37,7 @@ def path end def run_count - self.count = NodeTag.where(tid: tid).count + self.count = NodeTag.joins(:node).where(tid: tid).where('node.status = 1').count save end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index bbe2691d47..55295cfd49 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -50,6 +50,9 @@ <% if @node.main_image %> + <% elsif @image %> + + <% end %> @@ -59,16 +75,16 @@ $(document).ready(function () { <% unless comment.node.nil? %> - <%= comment.node&.title.truncate(15) %>
- <%= comment.node&.type.capitalize %>(<%= time_ago_in_words(comment.node&.created_at) %> ago) + <% if comment.status == 1 %> <% elsif comment.status == 0 %> <% elsif comment.status == 4 %><% end %> + <%= comment.node&.title.truncate(15) %>
+ <%= comment.node&.type.capitalize %> | <%= time_ago_in_words(comment.node&.created_at) %> ago <% end %> <%= comment.body.truncate(20) %> - <%= comment.author&.name %>
- <% if comment.status == 1 %> Published<% elsif comment.status == 0 %> Spammed<% elsif comment.status == 4 %> Unmoderated<% end %> <% if comment.flag > 0%> <%= comment.flag %><% end %> + <%= comment.author&.name.truncate(15) %> <%= comment.flag%> <% unless comment.node.nil? %> @@ -80,7 +96,7 @@ $(document).ready(function () { Spam style="display:none;"<% end %> data-remote="true" href="/ban/<%= comment.author.id %>">Ban user style="display:none;"<% end %> data-remote="true" href="/unban/<%= comment.author.id %>">Unban user - Unflag + Unflag <%= link_to "/comment/delete/#{comment.cid}", data: { confirm: "Are you sure you want to delete this comment?" }, :remote => true, :class => "btn border-curve btn-sm font-weight-bold btn-light delete" do %> <% end %> diff --git a/app/views/spam2/_flags.html.erb b/app/views/spam2/_flags.html.erb index 14cf0f5706..36c0d7f640 100644 --- a/app/views/spam2/_flags.html.erb +++ b/app/views/spam2/_flags.html.erb @@ -49,22 +49,22 @@ $(document).ready(function () { @@ -88,7 +88,7 @@ $(document).ready(function () { <%= flag.title.truncate(15) %>
- <%= flag.type.capitalize %>( <%= time_ago_in_words(flag.created_at) %> ago) + <%= flag.type.capitalize %> | <%= time_ago_in_words(flag.created_at) %> ago <%= flag.author&.name %> <%= flag.author&.new_contributor%>
@@ -101,9 +101,9 @@ $(document).ready(function () { <%= time_ago_in_words(flag.updated_at) %> ago - Publish post - Spam post - Unflag + Publish post + Spam post + Unflag <%= link_to "/notes/delete/#{flag.id}", data: { confirm: "Are you sure you want to delete #{flag.path}?" }, :remote => true, :class => "btn border-curve btn-sm font-weight-bold btn-light delete" do %> <% end %> diff --git a/app/views/spam2/_nodes.html.erb b/app/views/spam2/_nodes.html.erb index 64aa5376f7..aa117aa270 100644 --- a/app/views/spam2/_nodes.html.erb +++ b/app/views/spam2/_nodes.html.erb @@ -42,31 +42,32 @@ $(document).ready(function () { }); }); -
-
<%= page_entries_info(@nodes) %>
+ <% unless @nodes.empty? %> +
<%= page_entries_info(@nodes) %>s
+ <% end %> @@ -74,7 +75,7 @@ $(document).ready(function () { - + @@ -85,7 +86,7 @@ $(document).ready(function () {
Title Author Status<% if params[:type] == "created" %>Created at <% else %> Updated at <% end %><% if params[:type] == "created"%>Created at <% else %> Updated at <% end %> Action
<%= node.title.truncate(15) %>
- <%= node.type.capitalize %>(<% if params[:type] == "created"%><%= time_ago_in_words(node.updated_at) %><% else %><%= time_ago_in_words(node.created_at) %><% end %> ago) + <%= node.type.capitalize %> | <% if params[:type] == "created"%><%= time_ago_in_words(node.updated_at) %><% else %><%= time_ago_in_words(node.created_at) %><% end %> ago
<%= node.author&.name.truncate(15) %> <%= node.author&.new_contributor%>
@@ -95,7 +96,7 @@ $(document).ready(function () { <% if node.status == 1 %> Published<% elsif node.status == 0 %> Spammed<% elsif node.status == 4 %> Unmoderated<% end %>
- <% if params[:type] == "created"%><%= time_ago_in_words(node.created_at) %> <% else %><%= time_ago_in_words(node.updated_at) %> <% end %> ago + <% if params[:type] == "created" %><%= time_ago_in_words(node.created_at) %> <% else %><%= time_ago_in_words(node.updated_at) %> <% end %> ago Publish post @@ -142,6 +143,13 @@ $(document).ready(function () {
- <%= will_paginate @nodes, :renderer => WillPaginate::ActionView::BootstrapLinkRenderer unless @unpaginated || @nodes.empty?%> + <%= will_paginate @nodes, :renderer => WillPaginate::ActionView::BootstrapLinkRenderer unless @unpaginated || @nodes.empty? %>
-
- Page number <%= @nodes.current_page%> of <%= @nodes.total_pages %> total Pages +
+ <% unless @nodes.empty? %> Page number <%= @nodes.current_page %> of <%= @nodes.total_pages %> total Pages <% end %>
diff --git a/app/views/spam2/_queue.html.erb b/app/views/spam2/_queue.html.erb new file mode 100644 index 0000000000..2b2275e242 --- /dev/null +++ b/app/views/spam2/_queue.html.erb @@ -0,0 +1,152 @@ + + +
+ <% if @tags_followed.length == 20 %> <% end %> + <% @tags_followed.limit(20).each do |tag| %> + <%= tag.tagname %> <%= Tag.followers(tag.tagname).where('rusers.role = ?', 'moderator').or(Tag.followers(tag.tagname).where('rusers.role = ?', 'admin')).size %> + <% end %> +
+
+ +
+ <% unless @queue.empty? %> +
<%= page_entries_info(@queue) %>s
+ <% end %> + + + + + + + + + + + + <% @queue.each do |node| %> + + + + + + + + + <% end %> + +
TitleAuthorUpdated atAction
+ <% if node.status == 1 %> <% elsif node.status == 0 %> <% elsif node.status == 4 %> <% end %> <% if params[:type] == 'flagged'%> <% end %> + <%= node.title.truncate(25) %>
+ <%= node.type.capitalize %> | <%= time_ago_in_words(node.created_at) %> ago +
+ <%= node.author&.name.truncate(15) %> <%= node.author&.new_contributor%>
+ <%= Node.where(uid: node.author&.id).count %> Nodes +
+ <%= time_ago_in_words(node.updated_at) %> ago + + Publish post + Spam post + style="display:none;"<% end %> data-remote="true" href="/ban/<%= node.author.id %>">Ban user + style="display:none;"<% end %> data-remote="true" href="/unban/<%= node.author.id %>">Unban user + Unflag + <%= link_to "/notes/delete/#{node.id}", data: { confirm: "Are you sure you want to delete #{node.path}?" }, :remote => true, :class => "btn border-curve btn-sm font-weight-bold btn-light delete" do %> + + <% end %> + +
+
+
+
+
+
+ <%= will_paginate @queue, :renderer => WillPaginate::ActionView::BootstrapLinkRenderer unless @unpaginated || @queue.empty? %> +
+
+ <% unless @queue.empty? %> Page number <%= @queue.current_page %> of <%= @queue.total_pages %> total Pages <% end %> +
+
diff --git a/app/views/spam2/_revisions.html.erb b/app/views/spam2/_revisions.html.erb index 7d8875a72b..66149205ae 100644 --- a/app/views/spam2/_revisions.html.erb +++ b/app/views/spam2/_revisions.html.erb @@ -5,32 +5,39 @@ $(document).ready(function () {
-
+
+ <% unless @revisions.empty? %> +
<%= page_entries_info(@revisions) %>s
+ <% end %> - + - <% @revisions.each do |revision| %>
Title AuthorTimeUpdated at Action
- <%= revision.title.truncate(20) %> + <%= revision.title.truncate(20) %>
- <%= revision.author&.name %> + <%= revision.author&.name %> - <%= time_ago_in_words(revision.timestamp.seconds.ago) %> ago + <%= time_ago_in_words(revision.updated_at) %> ago Publish post @@ -65,7 +72,7 @@ $(document).ready(function () {
<%= will_paginate @revisions, :renderer => WillPaginate::ActionView::BootstrapLinkRenderer unless @unpaginated || @revisions.empty?%>
-
- Batch <%= @revisions.current_page%> of <%= @revisions.total_pages %> total Batches +
+ Batch <%= @revisions.current_page%> of <%= @revisions.total_pages %> total Pages
diff --git a/app/views/spam2/_spam.html.erb b/app/views/spam2/_spam.html.erb index 55e03cfbbf..31eb7ee837 100644 --- a/app/views/spam2/_spam.html.erb +++ b/app/views/spam2/_spam.html.erb @@ -7,37 +7,40 @@ -