diff --git a/app/authorizers/timeline_authorizer.rb b/app/authorizers/timeline_authorizer.rb new file mode 100644 index 00000000..1a4e3a0d --- /dev/null +++ b/app/authorizers/timeline_authorizer.rb @@ -0,0 +1,31 @@ +class TimelineAuthorizer < ContentAuthorizer + def self.creatable_by?(user) + return false unless user.present? + return false if ENV.key?('CONTENT_BLACKLIST') && ENV['CONTENT_BLACKLIST'].split(',').include?(user.email) + + return true if user.on_premium_plan? + end + + def readable_by?(user) + return true if resource.privacy == 'public' + return true if user && resource.user_id == user.id + return true if resource.universe.present? && resource.universe.privacy == 'public' + return true if user && resource.universe.present? && resource.universe.user_id == user.id + return true if user && resource.universe.present? && resource.universe.contributors.pluck(:user_id).include?(user.id) + return true if user && user.site_administrator? + + return false + end + + def updatable_by?(user) + [ + user && resource.user_id == user.id + ].any? + end + + def deletable_by?(user) + [ + user && resource.user_id == user.id + ].any? + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 4f578efc..efb66824 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -16,6 +16,12 @@ class ApplicationController < ActionController::Base private + def require_premium_plan + unless user_signed_in? && current_user.on_premium_plan? + return redirect_back(fallback_location: root_path, notice: "Doing that requires Premium access.") + end + end + def set_metadata @page_title ||= '' @page_keywords ||= %w[writing author nanowrimo novel character fiction fantasy universe creative dnd roleplay game design] diff --git a/app/controllers/page_tags_controller.rb b/app/controllers/page_tags_controller.rb index 55653d39..ee41cc8a 100644 --- a/app/controllers/page_tags_controller.rb +++ b/app/controllers/page_tags_controller.rb @@ -1,4 +1,19 @@ class PageTagsController < ApplicationController + def update + raise "placeholder" + end + + def rename + old_tag_name = params[:tag].to_s + new_tag_name = params.dig(old_tag_name, :label) + + if new_tag_name.blank? + new_tag_name = 'Untitled Tag' + end + + current_user.page_tags.where(tag: old_tag_name).update_all(tag: new_tag_name) + end + # Remove a tag and all of its links to a page def remove # Params diff --git a/app/controllers/timelines_controller.rb b/app/controllers/timelines_controller.rb index 3e243cce..2c67807d 100644 --- a/app/controllers/timelines_controller.rb +++ b/app/controllers/timelines_controller.rb @@ -5,15 +5,28 @@ class TimelinesController < ApplicationController before_action :set_navbar_color before_action :set_sidenav_expansion + before_action :require_premium_plan, only: [:new, :create] + before_action :require_timeline_read_permission, only: [:show] + before_action :require_timeline_edit_permission, only: [:edit, :update] + # GET /timelines def index cache_linkable_content_for_each_content_type + # TODO: We SHOULD be just doing the below, but since it returns ContentPage stand-ins instead + # of actual Timeline models, it's a bit wonky to get all the Timeline-specific logic in place + # without reworking most of the views. For now, we're just grabbing timelines and contributable + # timelines manually. + # @timelines = @linkables_raw.fetch('Timeline', []) @timelines = current_user.timelines + @page_title = "My timelines" if @universe_scope - @timelines = @timelines.where(universe: @universe_scope) + @timelines = Timeline.where(universe: @universe_scope) + else + # Add in all timelines from shared universes also + @timelines += Timeline.where(universe_id: current_user.contributable_universe_ids) end @page_tags = PageTag.where( @@ -26,17 +39,12 @@ class TimelinesController < ApplicationController end # if params.key?(:favorite_only) - # @content.select!(&:favorite?) + # @timelines.select!(&:favorite?) # end - end def show @page_title = @timeline.name - - unless @timeline.privacy == 'public' || (user_signed_in? && current_user == @timeline.user) - return redirect_back(fallback_location: root_path, notice: "You don't have permission to view that timeline!") - end end # GET /timelines/new @@ -47,11 +55,8 @@ class TimelinesController < ApplicationController # GET /timelines/1/edit def edit - @page_title = "Editing " + @timeline.name - + @page_title = "Editing #{@timeline.name}" @suggested_page_tags = [] - - raise "No Access" unless user_signed_in? && current_user == @timeline.user end # POST /timelines @@ -70,8 +75,6 @@ class TimelinesController < ApplicationController # PATCH/PUT /timelines/1 def update - return unless user_signed_in? && current_user == @timeline.user - if @timeline.update(timeline_params) update_page_tags @@ -89,6 +92,14 @@ class TimelinesController < ApplicationController private + def require_timeline_read_permission + return user_signed_in? && @timeline.readable_by?(current_user) + end + + def require_timeline_edit_permission + return user_signed_in? && @timeline.updatable_by?(current_user) + end + # TODO: move this (and the copy in ContentController) into the has_page_tags concern? def update_page_tags tag_list = page_tag_params.split(PageTag::SUBMISSION_DELIMITER) diff --git a/app/models/timelines/timeline.rb b/app/models/timelines/timeline.rb index 8efa143f..e12d6449 100644 --- a/app/models/timelines/timeline.rb +++ b/app/models/timelines/timeline.rb @@ -8,7 +8,7 @@ class Timeline < ApplicationRecord include BelongsToUniverse include Authority::Abilities - self.authorizer_name = 'ExtendedContentAuthorizer' + self.authorizer_name = 'TimelineAuthorizer' validates :user_id, presence: true belongs_to :user diff --git a/app/views/data/tags.html.erb b/app/views/data/tags.html.erb index ee09258f..8d761639 100644 --- a/app/views/data/tags.html.erb +++ b/app/views/data/tags.html.erb @@ -35,21 +35,35 @@ <% end %> -
- Used by <%= pluralize page_list.length, 'page' %> -
+
+ <%= form_for tag, url: rename_tag_path(tag: tag) do |f| %> +
+
+ <%= f.text_field :label, class: '', value: tag %> + <%= f.label :label, 'Rename tag' %> +
+
+ <%= f.submit 'Mass-rename', class: 'btn white blue-text', onclick: "javascript: M.toast({ html: 'Saving changes...' });" %> +
+
+ <% end %> +
+
<%= link_to 'Delete this tag', tag_remove_path( page_type: content_type.name, slug: PageTagService.slug_for(tag) ), data: { confirm: "Are you sure? This will delete this tag and remove it from all pages." - }, class: 'red-text' + }, class: 'red-text btn red white-text' %>
+
+ Used by <%= pluralize page_list.length, 'page' %> +
<% page_list.each do |page_tag| %>
<%= link_to send("#{page_tag.page_type.downcase}_path", page_tag.page_id) do %> diff --git a/app/views/timelines/index.html.erb b/app/views/timelines/index.html.erb index 8a685a4a..8389bbd9 100644 --- a/app/views/timelines/index.html.erb +++ b/app/views/timelines/index.html.erb @@ -59,13 +59,17 @@
- <%= link_to timeline_path(timeline), class: 'blue-text left' do %> - <%= Timeline.icon %> - View + <% if timeline.readable_by?(current_user) %> + <%= link_to timeline_path(timeline), class: 'blue-text left' do %> + <%= Timeline.icon %> + View + <% end %> <% end %> - <%= link_to edit_timeline_path(timeline), class: 'green-text right' do %> - edit - Edit + <% if timeline.updatable_by?(current_user) %> + <%= link_to edit_timeline_path(timeline), class: 'green-text right' do %> + edit + Edit + <% end %> <% end %>
diff --git a/config/attributes/school.yml b/config/attributes/school.yml index 71476cdb..da8f83b3 100644 --- a/config/attributes/school.yml +++ b/config/attributes/school.yml @@ -89,6 +89,8 @@ :label: Past students - :name: famous_students :label: Famous students + - :name: headcount + :label: Headcount :extracurriculars: :label: Extracurriculars :icon: rowing diff --git a/config/routes.rb b/config/routes.rb index 0becf658..e2ddef15 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -98,7 +98,9 @@ Rails.application.routes.draw do get '/scratchpad', to: 'main#notes', as: :notes - get 'tag/remove', to: 'page_tags#remove' + get 'tag/remove', to: 'page_tags#remove' + # post 'tag/:slug/update', to: 'page_tags#update', as: :update_tag + post '/tag/:tag/rename', to: 'page_tags#rename', as: :rename_tag delete 'tag/:id/destroy', to: 'page_tags#destroy', as: :destroy_specific_tag # Legacy route: left intact so /my/documents/X URLs continue to work for everyone's bookmarks