diff --git a/Gemfile b/Gemfile index 457d99b7ff2497364fadc4e85f659ea1f4361e96..1446d5dc47fbf3871fa43c68ff8358a1c796c197 100644 --- a/Gemfile +++ b/Gemfile @@ -154,6 +154,9 @@ gem 'truncato', '~> 0.7.9' gem 'bootstrap_form', '~> 2.7.0' gem 'nokogiri', '~> 1.8.2' +# Calendar rendering +gem 'icalendar' + # Diffs gem 'diffy', '~> 3.1.0' diff --git a/Gemfile.lock b/Gemfile.lock index 47ca6ffd7aabcce8a7ccc2a9ff271df693281d7e..a2e620905e1784690c3d99954eafcdd322727270 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -437,6 +437,7 @@ GEM httpclient (2.8.3) i18n (0.9.5) concurrent-ruby (~> 1.0) + icalendar (2.4.1) ice_nine (0.11.2) influxdb (0.2.3) cause @@ -1096,6 +1097,7 @@ DEPENDENCIES html-pipeline (~> 2.7.1) html2text httparty (~> 0.13.3) + icalendar influxdb (~> 0.2) jira-ruby (~> 1.4) jquery-atwho-rails (~> 1.3.2) diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb index 3b11a373368819310bdbd8819138de69aa90c3cd..b6eb7d292fcbe51e989d0766ebd74649f886a08f 100644 --- a/app/controllers/concerns/issues_action.rb +++ b/app/controllers/concerns/issues_action.rb @@ -17,10 +17,23 @@ def issues end # rubocop:enable Gitlab/ModuleWithInstanceVariables + # rubocop:disable Gitlab/ModuleWithInstanceVariables + def issues_calendar + @issues = issuables_collection + .non_archived + .with_due_date + .limit(100) + + respond_to do |format| + format.ics { response.headers['Content-Disposition'] = 'inline' } + end + end + # rubocop:enable Gitlab/ModuleWithInstanceVariables + private def finder_type (super if defined?(super)) || - (IssuesFinder if action_name == 'issues') + (IssuesFinder if %w(issues issues_calendar).include?(action_name)) end end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 9f5ad23a20f9fd6b525fd132f82987bd32f3ac7f..0fdbd2f6a35a5cb7598bdce6fc7bb180333da57f 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -34,12 +34,12 @@ def reset_incoming_email_token redirect_to profile_personal_access_tokens_path end - def reset_rss_token + def reset_feed_token Users::UpdateService.new(current_user, user: @user).execute! do |user| - user.reset_rss_token! + user.reset_feed_token! end - flash[:notice] = "RSS token was successfully reset" + flash[:notice] = "Feed token was successfully reset" redirect_to profile_personal_access_tokens_path end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 1de9d684d2e938793f313d23b62833920c7e4063..0c05673b79ada285bfdd77b667ff5bf6505d0cda 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -11,9 +11,9 @@ class Projects::IssuesController < Projects::ApplicationController before_action :whitelist_query_limiting_ee, only: [:update] before_action :whitelist_query_limiting, only: [:create, :create_merge_request, :move, :bulk_update] before_action :check_issues_available! - before_action :issue, except: [:index, :new, :create, :bulk_update, :export_csv] + before_action :issue, except: [:index, :calendar, :new, :create, :bulk_update, :export_csv] - before_action :set_issuables_index, only: [:index] + before_action :set_issuables_index, only: [:index, :calendar] # Allow write(create) issue before_action :authorize_create_issue!, only: [:new, :create] @@ -43,6 +43,17 @@ def index end end + def calendar + @issues = @issuables + .non_archived + .with_due_date + .limit(100) + + respond_to do |format| + format.ics { response.headers['Content-Disposition'] = 'inline' } + end + end + def new params[:issue] ||= ActionController::Parameters.new( assignee_ids: "" diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb index 24817fd4692ba02ea0f6e51322ffbe4e22b58e22..616cc241550af5baf55019a098ea90c244c050c4 100644 --- a/app/finders/issues_finder.rb +++ b/app/finders/issues_finder.rb @@ -77,6 +77,8 @@ def by_due_date(items) items = items.due_between(Date.today.beginning_of_week, Date.today.end_of_week) elsif filter_by_due_this_month? items = items.due_between(Date.today.beginning_of_month, Date.today.end_of_month) + elsif filter_by_due_next_month_and_previous_two_weeks? + items = items.due_between(Date.today - 2.weeks, (Date.today + 1.month).end_of_month) end end @@ -99,6 +101,10 @@ def filter_by_due_this_month? due_date? && params[:due_date] == Issue::DueThisMonth.name end + def filter_by_due_next_month_and_previous_two_weeks? + due_date? && params[:due_date] == Issue::DueNextMonthAndPreviousTwoWeeks.name + end + def due_date? params[:due_date].present? end diff --git a/app/helpers/calendar_helper.rb b/app/helpers/calendar_helper.rb new file mode 100644 index 0000000000000000000000000000000000000000..c54b91b0ce5a229996f8b78dbb4e18bf5259ac90 --- /dev/null +++ b/app/helpers/calendar_helper.rb @@ -0,0 +1,8 @@ +module CalendarHelper + def calendar_url_options + { format: :ics, + feed_token: current_user.try(:feed_token), + due_date: Issue::DueNextMonthAndPreviousTwoWeeks.name, + sort: 'closest_future_date' } + end +end diff --git a/app/helpers/rss_helper.rb b/app/helpers/rss_helper.rb index 9ac4df88dc3e10e8c99aa8dfa7b3fe79ca99300a..7d4fa83a67a3619fa6e2bd7d7d86b0acd83a1993 100644 --- a/app/helpers/rss_helper.rb +++ b/app/helpers/rss_helper.rb @@ -1,5 +1,5 @@ module RssHelper def rss_url_options - { format: :atom, rss_token: current_user.try(:rss_token) } + { format: :atom, feed_token: current_user.try(:feed_token) } end end diff --git a/app/models/issue.rb b/app/models/issue.rb index 19a4f2e7bcc956eed6e16c7f633113d253749034..ffaf6b8b3f65279df95ced52100fd3d746bca5de 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -18,12 +18,13 @@ class Issue < ActiveRecord::Base ignore_column :assignee_id, :branch_name, :deleted_at - DueDateStruct = Struct.new(:title, :name).freeze - NoDueDate = DueDateStruct.new('No Due Date', '0').freeze - AnyDueDate = DueDateStruct.new('Any Due Date', '').freeze - Overdue = DueDateStruct.new('Overdue', 'overdue').freeze - DueThisWeek = DueDateStruct.new('Due This Week', 'week').freeze - DueThisMonth = DueDateStruct.new('Due This Month', 'month').freeze + DueDateStruct = Struct.new(:title, :name).freeze + NoDueDate = DueDateStruct.new('No Due Date', '0').freeze + AnyDueDate = DueDateStruct.new('Any Due Date', '').freeze + Overdue = DueDateStruct.new('Overdue', 'overdue').freeze + DueThisWeek = DueDateStruct.new('Due This Week', 'week').freeze + DueThisMonth = DueDateStruct.new('Due This Month', 'month').freeze + DueNextMonthAndPreviousTwoWeeks = DueDateStruct.new('Due Next Month And Previous Two Weeks', 'next_month_and_previous_two_weeks').freeze belongs_to :project belongs_to :moved_to, class_name: 'Issue' @@ -53,6 +54,7 @@ class Issue < ActiveRecord::Base scope :unassigned, -> { where('NOT EXISTS (SELECT TRUE FROM issue_assignees WHERE issue_id = issues.id)') } scope :assigned_to, ->(u) { where('EXISTS (SELECT TRUE FROM issue_assignees WHERE user_id = ? AND issue_id = issues.id)', u.id)} + scope :with_due_date, -> { where('due_date IS NOT NULL') } scope :without_due_date, -> { where(due_date: nil) } scope :due_before, ->(date) { where('issues.due_date < ?', date) } scope :due_between, ->(from_date, to_date) { where('issues.due_date >= ?', from_date).where('issues.due_date <= ?', to_date) } @@ -60,6 +62,7 @@ class Issue < ActiveRecord::Base scope :order_due_date_asc, -> { reorder('issues.due_date IS NULL, issues.due_date ASC') } scope :order_due_date_desc, -> { reorder('issues.due_date IS NULL, issues.due_date DESC') } + scope :order_closest_future_date, -> { reorder('CASE WHEN due_date >= CURRENT_DATE THEN 0 ELSE 1 END ASC, ABS(CURRENT_DATE - due_date) ASC') } scope :preload_associations, -> { preload(:labels, project: :namespace) } @@ -126,6 +129,7 @@ def self.project_foreign_key def self.sort_by_attribute(method, excluded_labels: []) case method.to_s + when 'closest_future_date' then order_closest_future_date when 'due_date' then order_due_date_asc when 'due_date_asc' then order_due_date_asc when 'due_date_desc' then order_due_date_desc diff --git a/app/models/user.rb b/app/models/user.rb index 596f61331966372635db726f67e5097a1e2d3a2d..fc624009d6951cc36e58b8c44a30d580203de1c2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -28,7 +28,7 @@ class User < ActiveRecord::Base ignore_column :authentication_token add_authentication_token_field :incoming_email_token - add_authentication_token_field :rss_token + add_authentication_token_field :feed_token default_value_for :admin, false default_value_for(:external) { Gitlab::CurrentSettings.user_default_external } @@ -1189,11 +1189,11 @@ def update_two_factor_requirement save end - # each existing user needs to have an `rss_token`. + # each existing user needs to have an `feed_token`. # we do this on read since migrating all existing users is not a feasible # solution. - def rss_token - ensure_rss_token! + def feed_token + ensure_feed_token! end def sync_attribute?(attribute) diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml index 4bf04dadf01a016512a5862342ce768b946166bb..86a21e24ac95eaa201c5d78ffa0f7da127811c15 100644 --- a/app/views/dashboard/issues.html.haml +++ b/app/views/dashboard/issues.html.haml @@ -7,8 +7,7 @@ .top-area = render 'shared/issuable/nav', type: :issues, display_count: !@no_filters_set .nav-controls - = link_to safe_params.merge(rss_url_options), class: 'btn has-tooltip', data: { container: 'body' }, title: 'Subscribe' do - = icon('rss') + = render 'shared/issuable/feed_buttons' = render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", with_feature_enabled: 'issues', type: :issues = render 'shared/issuable/filter', type: :issues diff --git a/app/views/dashboard/issues_calendar.ics.haml b/app/views/dashboard/issues_calendar.ics.haml new file mode 100644 index 0000000000000000000000000000000000000000..59573e5fecf00c56243e3bd9c2a4d10aaf29b01d --- /dev/null +++ b/app/views/dashboard/issues_calendar.ics.haml @@ -0,0 +1 @@ += render 'issues/issues_calendar', issues: @issues diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index 662db18cf86613c17e07ed666c513fc0c899992c..8037cf4b69d54eeb89caf6a17422b99cafc94bbd 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -8,10 +8,7 @@ .top-area = render 'shared/issuable/nav', type: :issues .nav-controls - = link_to safe_params.merge(rss_url_options), class: 'btn' do - = icon('rss') - %span.icon-label - Subscribe + = render 'shared/issuable/feed_buttons' = render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", type: :issues = render 'shared/issuable/search_bar', type: :issues diff --git a/app/views/groups/issues_calendar.ics.haml b/app/views/groups/issues_calendar.ics.haml new file mode 100644 index 0000000000000000000000000000000000000000..59573e5fecf00c56243e3bd9c2a4d10aaf29b01d --- /dev/null +++ b/app/views/groups/issues_calendar.ics.haml @@ -0,0 +1 @@ += render 'issues/issues_calendar', issues: @issues diff --git a/app/views/issues/_issues_calendar.ics.ruby b/app/views/issues/_issues_calendar.ics.ruby new file mode 100644 index 0000000000000000000000000000000000000000..3563635d33db2b823ebe702170a987ca4b54979d --- /dev/null +++ b/app/views/issues/_issues_calendar.ics.ruby @@ -0,0 +1,15 @@ +cal = Icalendar::Calendar.new +cal.prodid = '-//GitLab//NONSGML GitLab//EN' +cal.x_wr_calname = 'GitLab Issues' + +@issues.includes(project: :namespace).each do |issue| + cal.event do |event| + event.dtstart = Icalendar::Values::Date.new(issue.due_date) + event.summary = "#{issue.title} (in #{issue.project.full_path})" + event.description = "Find out more at #{issue_url(issue)}" + event.url = issue_url(issue) + event.transp = 'TRANSPARENT' + end +end + +cal.to_ical diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml index d253e8e456e4c7ae9871198895e432905992dfbc..d111113c646531e9402283357d288f240843d3f7 100644 --- a/app/views/profiles/personal_access_tokens/index.html.haml +++ b/app/views/profiles/personal_access_tokens/index.html.haml @@ -34,18 +34,18 @@ .row.prepend-top-default .col-lg-4.profile-settings-sidebar %h4.prepend-top-0 - RSS token + Feed token %p - Your RSS token is used to authenticate you when your RSS reader loads a personalized RSS feed, and is included in your personal RSS feed URLs. + Your feed token is used to authenticate you when your RSS reader loads a personalized RSS feed or when when your calendar application loads a personalized calendar, and is included in those feed URLs. %p It cannot be used to access any other data. - .col-lg-8.rss-token-reset - = label_tag :rss_token, 'RSS token', class: "label-light" - = text_field_tag :rss_token, current_user.rss_token, class: 'form-control', readonly: true, onclick: 'this.select()' + .col-lg-8.feed-token-reset + = label_tag :feed_token, 'Feed token', class: "label-light" + = text_field_tag :feed_token, current_user.feed_token, class: 'form-control', readonly: true, onclick: 'this.select()' %p.form-text.text-muted - Keep this token secret. Anyone who gets ahold of it can read activity and issue RSS feeds as if they were you. + Keep this token secret. Anyone who gets ahold of it can read activity and issue RSS feeds or your calendar feed as if they were you. You should - = link_to 'reset it', [:reset, :rss_token, :profile], method: :put, data: { confirm: 'Are you sure? Any RSS URLs currently in use will stop working.' } + = link_to 'reset it', [:reset, :feed_token, :profile], method: :put, data: { confirm: 'Are you sure? Any RSS or calendar URLs currently in use will stop working.' } if that ever happens. - if incoming_email_token_enabled? diff --git a/app/views/projects/issues/_nav_btns.html.haml b/app/views/projects/issues/_nav_btns.html.haml index 40a373cb8d9c3920ae64618ea1033bdea9b825c5..3a6e52fd44610c4c4a599e7eebce4ca8e62656d4 100644 --- a/app/views/projects/issues/_nav_btns.html.haml +++ b/app/views/projects/issues/_nav_btns.html.haml @@ -2,8 +2,10 @@ - show_export_button = local_assigns.fetch(:show_export_button, true) - if show_rss_button - = link_to safe_params.merge(rss_url_options), class: 'btn btn-default append-right-10 has-tooltip', title: 'Subscribe' do + = link_to safe_params.merge(rss_url_options), class: 'btn btn-default append-right-10 has-tooltip', title: 'Subscribe to RSS feed' do = icon('rss') + = link_to safe_params.merge(calendar_url_options), class: 'btn btn-default append-right-10 has-tooltip', title: 'Subscribe to calendar' do + = custom_icon('icon_calendar') - if show_export_button = render 'projects/issues/export_issues/button' diff --git a/app/views/projects/issues/calendar.ics.haml b/app/views/projects/issues/calendar.ics.haml new file mode 100644 index 0000000000000000000000000000000000000000..59573e5fecf00c56243e3bd9c2a4d10aaf29b01d --- /dev/null +++ b/app/views/projects/issues/calendar.ics.haml @@ -0,0 +1 @@ += render 'issues/issues_calendar', issues: @issues diff --git a/app/views/shared/icons/_icon_calendar.svg b/app/views/shared/icons/_icon_calendar.svg new file mode 100644 index 0000000000000000000000000000000000000000..4d0a703f9a00adde23b88adc7e4e38c15b7e262e --- /dev/null +++ b/app/views/shared/icons/_icon_calendar.svg @@ -0,0 +1 @@ +<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M15 5v7a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V5a2 2 0 0 1 2-2h1V2a1 1 0 1 1 2 0v1h4V2a1 1 0 1 1 2 0v1h1a2 2 0 0 1 2 2zM3 6v6a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V6H3zm2 2h2a1 1 0 1 1 0 2H5a1 1 0 1 1 0-2z" fill="#000" fill-rule="evenodd"/></svg> \ No newline at end of file diff --git a/app/views/shared/issuable/_feed_buttons.html.haml b/app/views/shared/issuable/_feed_buttons.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..d4834090413dd17b577a186c13249e34ee61e7ca --- /dev/null +++ b/app/views/shared/issuable/_feed_buttons.html.haml @@ -0,0 +1,4 @@ += link_to safe_params.merge(rss_url_options), class: 'btn has-tooltip', data: { container: 'body' }, title: 'Subscribe to RSS feed' do + = icon('rss') += link_to safe_params.merge(calendar_url_options), class: 'btn has-tooltip', data: { container: 'body' }, title: 'Subscribe to calendar' do + = custom_icon('icon_calendar') diff --git a/changelogs/unreleased/44184-issues_ical_feed.yml b/changelogs/unreleased/44184-issues_ical_feed.yml new file mode 100644 index 0000000000000000000000000000000000000000..8151d82625a92dc91494e405ce14f4907464a75b --- /dev/null +++ b/changelogs/unreleased/44184-issues_ical_feed.yml @@ -0,0 +1,5 @@ +--- +title: Export assigned issues in iCalendar feed +merge_request: 17783 +author: Imre Farkas +type: added diff --git a/config/routes/dashboard.rb b/config/routes/dashboard.rb index d2437285cdf142ce0d3dec107fa67556ab744f72..f1e8c2b9d8251f9f242d649520d85ab75e46b469 100644 --- a/config/routes/dashboard.rb +++ b/config/routes/dashboard.rb @@ -1,4 +1,5 @@ resource :dashboard, controller: 'dashboard', only: [] do + get :issues, action: :issues_calendar, constraints: lambda { |req| req.format == :ics } get :issues get :merge_requests get :activity diff --git a/config/routes/group.rb b/config/routes/group.rb index a3b68c5fa554ddcc8fe225ff1eb07ca057e027b9..67817f59ebd72042f8daa48637f18ee0ef7ff3b2 100644 --- a/config/routes/group.rb +++ b/config/routes/group.rb @@ -5,9 +5,10 @@ constraints(::Constraints::GroupUrlConstrainer.new) do scope(path: 'groups/*id', controller: :groups, - constraints: { id: Gitlab::PathRegex.full_namespace_route_regex, format: /(html|json|atom)/ }) do + constraints: { id: Gitlab::PathRegex.full_namespace_route_regex, format: /(html|json|atom|ics)/ }) do scope(path: '-') do get :edit, as: :edit_group + get :issues, as: :issues_group_calendar, action: :issues_calendar, constraints: lambda { |req| req.format == :ics } get :issues, as: :issues_group get :merge_requests, as: :merge_requests_group get :projects, as: :projects_group diff --git a/config/routes/profile.rb b/config/routes/profile.rb index f49e61646fd8824dc42f33c09c15a021df3d633b..6f2b8664e3b0f95bf6cf9645ae224a415f4756fd 100644 --- a/config/routes/profile.rb +++ b/config/routes/profile.rb @@ -7,7 +7,7 @@ get :applications, to: 'oauth/applications#index' put :reset_incoming_email_token - put :reset_rss_token + put :reset_feed_token put :update_username end diff --git a/config/routes/project.rb b/config/routes/project.rb index 374ad4f3703aae093f63c314e0b9df8ae6f5935d..a24cde5c13d6b020ddda7ffa26d435fbdf5bdfda 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -389,6 +389,7 @@ ## EE-specific resources :vulnerability_feedback, only: [:index, :create, :destroy], constraints: { id: /\d+/ } + get :issues, to: 'issues#calendar', constraints: lambda { |req| req.format == :ics } resources :issues, concerns: :awardable, constraints: { id: /\d+/ } do member do post :toggle_subscription diff --git a/db/migrate/20180408143354_rename_users_rss_token_to_feed_token.rb b/db/migrate/20180408143354_rename_users_rss_token_to_feed_token.rb new file mode 100644 index 0000000000000000000000000000000000000000..007cbebaf1bc3992dff536e7eb087649ae95e185 --- /dev/null +++ b/db/migrate/20180408143354_rename_users_rss_token_to_feed_token.rb @@ -0,0 +1,15 @@ +class RenameUsersRssTokenToFeedToken < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + rename_column_concurrently :users, :rss_token, :feed_token + end + + def down + cleanup_concurrent_column_rename :users, :feed_token, :rss_token + end +end diff --git a/db/post_migrate/20180408143355_cleanup_users_rss_token_rename.rb b/db/post_migrate/20180408143355_cleanup_users_rss_token_rename.rb new file mode 100644 index 0000000000000000000000000000000000000000..bff83379087cd39d5288c6f25839e78010b68142 --- /dev/null +++ b/db/post_migrate/20180408143355_cleanup_users_rss_token_rename.rb @@ -0,0 +1,13 @@ +class CleanupUsersRssTokenRename < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + disable_ddl_transaction! + + def up + cleanup_concurrent_column_rename :users, :rss_token, :feed_token + end + + def down + rename_column_concurrently :users, :feed_token, :rss_token + end +end diff --git a/db/schema.rb b/db/schema.rb index a2cc7929ed20e453ce5fe96a1f606003e0d909cd..78e572650bf09c031e9ea978bb1f1b7ebd779ae5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2657,13 +2657,13 @@ t.boolean "notified_of_own_activity" t.boolean "support_bot" t.string "preferred_language" - t.string "rss_token" t.boolean "email_opted_in" t.string "email_opted_in_ip" t.integer "email_opted_in_source_id" t.datetime "email_opted_in_at" t.integer "theme_id", limit: 2 t.integer "accepted_term_id" + t.string "feed_token" end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree @@ -2671,12 +2671,12 @@ add_index "users", ["created_at"], name: "index_users_on_created_at", using: :btree add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree add_index "users", ["email"], name: "index_users_on_email_trigram", using: :gin, opclasses: {"email"=>"gin_trgm_ops"} + add_index "users", ["feed_token"], name: "index_users_on_feed_token", using: :btree add_index "users", ["ghost"], name: "index_users_on_ghost", using: :btree add_index "users", ["incoming_email_token"], name: "index_users_on_incoming_email_token", using: :btree add_index "users", ["name"], name: "index_users_on_name", using: :btree add_index "users", ["name"], name: "index_users_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree - add_index "users", ["rss_token"], name: "index_users_on_rss_token", using: :btree add_index "users", ["state"], name: "index_users_on_state", using: :btree add_index "users", ["support_bot"], name: "index_users_on_support_bot", using: :btree add_index "users", ["username"], name: "index_users_on_username", using: :btree diff --git a/doc/user/project/issues/due_dates.md b/doc/user/project/issues/due_dates.md index 1bf8b776c2e200b006a10834132fab164d0bbf46..93306437c6ce6cf40e9c8509fc1835908864ea0d 100644 --- a/doc/user/project/issues/due_dates.md +++ b/doc/user/project/issues/due_dates.md @@ -39,5 +39,13 @@ The day before an open issue is due, an email will be sent to all participants of the issue. Both the due date and the day before are calculated using the server's timezone. +Issues with due dates can also be exported as an iCalendar feed. The URL of the +feed can be added to calendar applications. The feed is accessible by clicking +on the _Subscribe to calendar_ button on the following pages: +- on the **Assigned Issues** page that is linked on the right-hand side of the + GitLab header +- on the **Project Issues** page +- on the **Group Issues** page + [ce-3614]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3614 [permissions]: ../../permissions.md#project diff --git a/ee/app/controllers/ee/projects/issues_controller.rb b/ee/app/controllers/ee/projects/issues_controller.rb index 5047adfd5020ca73e9e28fa31e472439a5b97362..fef141e2e0dddd02601651deb150ed0d8a0147e2 100644 --- a/ee/app/controllers/ee/projects/issues_controller.rb +++ b/ee/app/controllers/ee/projects/issues_controller.rb @@ -6,7 +6,7 @@ module IssuesController prepended do before_action :check_export_issues_available!, only: [:export_csv] before_action :check_service_desk_available!, only: [:service_desk] - before_action :set_issuables_index, only: [:index, :service_desk] + before_action :set_issuables_index, only: [:index, :calendar, :service_desk] skip_before_action :issue, only: [:service_desk] end diff --git a/lib/gitlab/auth/request_authenticator.rb b/lib/gitlab/auth/request_authenticator.rb index a0b5cd868c3b7556db1baf48e18da97a650717fa..66de52506ce873cc3681e403c1a703b4babcbc04 100644 --- a/lib/gitlab/auth/request_authenticator.rb +++ b/lib/gitlab/auth/request_authenticator.rb @@ -16,7 +16,7 @@ def user end def find_sessionless_user - find_user_from_access_token || find_user_from_rss_token + find_user_from_access_token || find_user_from_feed_token rescue Gitlab::Auth::AuthenticationError nil end diff --git a/lib/gitlab/auth/user_auth_finders.rb b/lib/gitlab/auth/user_auth_finders.rb index 9d38872d7613cc9fc1c61190900653403a075463..7d8d33d035dbab4a6e939abddec03c4657a0a2ce 100644 --- a/lib/gitlab/auth/user_auth_finders.rb +++ b/lib/gitlab/auth/user_auth_finders.rb @@ -27,13 +27,15 @@ def find_user_from_warden current_request.env['warden']&.authenticate if verified_request? end - def find_user_from_rss_token - return unless current_request.path.ends_with?('.atom') || current_request.format.atom? + def find_user_from_feed_token + return unless rss_request? || ics_request? - token = current_request.params[:rss_token].presence + # NOTE: feed_token was renamed from rss_token but both needs to be supported because + # users might have already added the feed to their RSS reader before the rename + token = current_request.params[:feed_token].presence || current_request.params[:rss_token].presence return unless token - User.find_by_rss_token(token) || raise(UnauthorizedError) + User.find_by_feed_token(token) || raise(UnauthorizedError) end def find_user_from_access_token @@ -106,6 +108,14 @@ def ensure_action_dispatch_request(request) def current_request @current_request ||= ensure_action_dispatch_request(request) end + + def rss_request? + current_request.path.ends_with?('.atom') || current_request.format.atom? + end + + def ics_request? + current_request.path.ends_with?('.ics') || current_request.format.ics? + end end end end diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 0e27a28ea6e36b7ce1ea27fc9f9c8128fb392f70..72eb8adcce246d70ba14485dfe451de9f8c76661 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -31,27 +31,27 @@ map $http_upgrade $connection_upgrade_gitlab { log_format gitlab_access $remote_addr - $remote_user [$time_local] "$request_method $gitlab_filtered_request_uri $server_protocol" $status $body_bytes_sent "$gitlab_filtered_http_referer" "$http_user_agent"; ## Remove private_token from the request URI -# In: /foo?private_token=unfiltered&authenticity_token=unfiltered&rss_token=unfiltered&... -# Out: /foo?private_token=[FILTERED]&authenticity_token=unfiltered&rss_token=unfiltered&... +# In: /foo?private_token=unfiltered&authenticity_token=unfiltered&feed_token=unfiltered&... +# Out: /foo?private_token=[FILTERED]&authenticity_token=unfiltered&feed_token=unfiltered&... map $request_uri $gitlab_temp_request_uri_1 { default $request_uri; ~(?i)^(?<start>.*)(?<temp>[\?&]private[\-_]token)=[^&]*(?<rest>.*)$ "$start$temp=[FILTERED]$rest"; } ## Remove authenticity_token from the request URI -# In: /foo?private_token=[FILTERED]&authenticity_token=unfiltered&rss_token=unfiltered&... -# Out: /foo?private_token=[FILTERED]&authenticity_token=[FILTERED]&rss_token=unfiltered&... +# In: /foo?private_token=[FILTERED]&authenticity_token=unfiltered&feed_token=unfiltered&... +# Out: /foo?private_token=[FILTERED]&authenticity_token=[FILTERED]&feed_token=unfiltered&... map $gitlab_temp_request_uri_1 $gitlab_temp_request_uri_2 { default $gitlab_temp_request_uri_1; ~(?i)^(?<start>.*)(?<temp>[\?&]authenticity[\-_]token)=[^&]*(?<rest>.*)$ "$start$temp=[FILTERED]$rest"; } -## Remove rss_token from the request URI -# In: /foo?private_token=[FILTERED]&authenticity_token=[FILTERED]&rss_token=unfiltered&... -# Out: /foo?private_token=[FILTERED]&authenticity_token=[FILTERED]&rss_token=[FILTERED]&... +## Remove feed_token from the request URI +# In: /foo?private_token=[FILTERED]&authenticity_token=[FILTERED]&feed_token=unfiltered&... +# Out: /foo?private_token=[FILTERED]&authenticity_token=[FILTERED]&feed_token=[FILTERED]&... map $gitlab_temp_request_uri_2 $gitlab_filtered_request_uri { default $gitlab_temp_request_uri_2; - ~(?i)^(?<start>.*)(?<temp>[\?&]rss[\-_]token)=[^&]*(?<rest>.*)$ "$start$temp=[FILTERED]$rest"; + ~(?i)^(?<start>.*)(?<temp>[\?&]feed[\-_]token)=[^&]*(?<rest>.*)$ "$start$temp=[FILTERED]$rest"; } ## A version of the referer without the query string diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 8218d68f9ba80c92578c7c040a61bfab1ba71cde..2e3799d5e1bb08bc333a02029e167818372dc7df 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -36,27 +36,27 @@ map $http_upgrade $connection_upgrade_gitlab_ssl { log_format gitlab_ssl_access $remote_addr - $remote_user [$time_local] "$request_method $gitlab_ssl_filtered_request_uri $server_protocol" $status $body_bytes_sent "$gitlab_ssl_filtered_http_referer" "$http_user_agent"; ## Remove private_token from the request URI -# In: /foo?private_token=unfiltered&authenticity_token=unfiltered&rss_token=unfiltered&... -# Out: /foo?private_token=[FILTERED]&authenticity_token=unfiltered&rss_token=unfiltered&... +# In: /foo?private_token=unfiltered&authenticity_token=unfiltered&feed_token=unfiltered&... +# Out: /foo?private_token=[FILTERED]&authenticity_token=unfiltered&feed_token=unfiltered&... map $request_uri $gitlab_ssl_temp_request_uri_1 { default $request_uri; ~(?i)^(?<start>.*)(?<temp>[\?&]private[\-_]token)=[^&]*(?<rest>.*)$ "$start$temp=[FILTERED]$rest"; } ## Remove authenticity_token from the request URI -# In: /foo?private_token=[FILTERED]&authenticity_token=unfiltered&rss_token=unfiltered&... -# Out: /foo?private_token=[FILTERED]&authenticity_token=[FILTERED]&rss_token=unfiltered&... +# In: /foo?private_token=[FILTERED]&authenticity_token=unfiltered&feed_token=unfiltered&... +# Out: /foo?private_token=[FILTERED]&authenticity_token=[FILTERED]&feed_token=unfiltered&... map $gitlab_ssl_temp_request_uri_1 $gitlab_ssl_temp_request_uri_2 { default $gitlab_ssl_temp_request_uri_1; ~(?i)^(?<start>.*)(?<temp>[\?&]authenticity[\-_]token)=[^&]*(?<rest>.*)$ "$start$temp=[FILTERED]$rest"; } -## Remove rss_token from the request URI -# In: /foo?private_token=[FILTERED]&authenticity_token=[FILTERED]&rss_token=unfiltered&... -# Out: /foo?private_token=[FILTERED]&authenticity_token=[FILTERED]&rss_token=[FILTERED]&... +## Remove feed_token from the request URI +# In: /foo?private_token=[FILTERED]&authenticity_token=[FILTERED]&feed_token=unfiltered&... +# Out: /foo?private_token=[FILTERED]&authenticity_token=[FILTERED]&feed_token=[FILTERED]&... map $gitlab_ssl_temp_request_uri_2 $gitlab_ssl_filtered_request_uri { default $gitlab_ssl_temp_request_uri_2; - ~(?i)^(?<start>.*)(?<temp>[\?&]rss[\-_]token)=[^&]*(?<rest>.*)$ "$start$temp=[FILTERED]$rest"; + ~(?i)^(?<start>.*)(?<temp>[\?&]feed[\-_]token)=[^&]*(?<rest>.*)$ "$start$temp=[FILTERED]$rest"; } ## A version of the referer without the query string diff --git a/lib/tasks/tokens.rake b/lib/tasks/tokens.rake index 693597afdf8e135e77ce8758f5d975912f7fb7ed..81829668de8fd6b1fee155ad4d2f521f289e8c19 100644 --- a/lib/tasks/tokens.rake +++ b/lib/tasks/tokens.rake @@ -6,9 +6,9 @@ namespace :tokens do reset_all_users_token(:reset_incoming_email_token!) end - desc "Reset all GitLab RSS tokens" - task reset_all_rss: :environment do - reset_all_users_token(:reset_rss_token!) + desc "Reset all GitLab feed tokens" + task reset_all_feed: :environment do + reset_all_users_token(:reset_feed_token!) end def reset_all_users_token(reset_token_method) @@ -31,8 +31,8 @@ class TmpUser < ActiveRecord::Base save!(validate: false) end - def reset_rss_token! - write_new_token(:rss_token) + def reset_feed_token! + write_new_token(:feed_token) save!(validate: false) end end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index f0caac40afd1ccf197b07d23a52483fe023bdcdf..9d1f234ae83eb17ea68aaa0ed38553a8b8f93ca4 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -146,35 +146,43 @@ def index end end - describe '#authenticate_user_from_rss_token' do - describe "authenticating a user from an RSS token" do + describe '#authenticate_sessionless_user!' do + describe "authenticating a user from a feed token" do controller(described_class) do def index render text: 'authenticated' end end - context "when the 'rss_token' param is populated with the RSS token" do + context "when the 'feed_token' param is populated with the feed token" do context 'when the request format is atom' do it "logs the user in" do - get :index, rss_token: user.rss_token, format: :atom + get :index, feed_token: user.feed_token, format: :atom expect(response).to have_gitlab_http_status 200 expect(response.body).to eq 'authenticated' end end - context 'when the request format is not atom' do + context 'when the request format is ics' do + it "logs the user in" do + get :index, feed_token: user.feed_token, format: :ics + expect(response).to have_gitlab_http_status 200 + expect(response.body).to eq 'authenticated' + end + end + + context 'when the request format is neither atom nor ics' do it "doesn't log the user in" do - get :index, rss_token: user.rss_token + get :index, feed_token: user.feed_token expect(response.status).not_to have_gitlab_http_status 200 expect(response.body).not_to eq 'authenticated' end end end - context "when the 'rss_token' param is populated with an invalid RSS token" do + context "when the 'feed_token' param is populated with an invalid feed token" do it "doesn't log the user" do - get :index, rss_token: "token" + get :index, feed_token: "token", format: :atom expect(response.status).not_to eq 200 expect(response.body).not_to eq 'authenticated' end @@ -454,7 +462,7 @@ def index end it 'renders a 403 when the sessionless user did not accept the terms' do - get :index, rss_token: user.rss_token, format: :atom + get :index, feed_token: user.feed_token, format: :atom expect(response).to have_gitlab_http_status(403) end @@ -462,7 +470,7 @@ def index it 'renders a 200 when the sessionless user accepted the terms' do accept_terms(user) - get :index, rss_token: user.rss_token, format: :atom + get :index, feed_token: user.feed_token, format: :atom expect(response).to have_gitlab_http_status(200) end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index d3c9d6535341017405cb0f3a083bae37c60d43b9..150025dfa326cdef068f1a10ec1ea80b36c69f29 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -12,10 +12,6 @@ user.notification_email = user.email end - before(:create) do |user| - user.ensure_rss_token - end - trait :admin do admin true end diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb index fb6c71ce9976e562222ba03b9dc876ae52a35203..da7749b42d259e127fc1b65469c13c8991327a72 100644 --- a/spec/features/atom/dashboard_issues_spec.rb +++ b/spec/features/atom/dashboard_issues_spec.rb @@ -31,20 +31,20 @@ expect(body).to have_selector('title', text: "#{user.name} issues") end - it "renders atom feed via RSS token" do - visit issues_dashboard_path(:atom, rss_token: user.rss_token, assignee_id: user.id) + it "renders atom feed via feed token" do + visit issues_dashboard_path(:atom, feed_token: user.feed_token, assignee_id: user.id) expect(response_headers['Content-Type']).to have_content('application/atom+xml') expect(body).to have_selector('title', text: "#{user.name} issues") end it "renders atom feed with url parameters" do - visit issues_dashboard_path(:atom, rss_token: user.rss_token, state: 'opened', assignee_id: user.id) + visit issues_dashboard_path(:atom, feed_token: user.feed_token, state: 'opened', assignee_id: user.id) link = find('link[type="application/atom+xml"]') params = CGI.parse(URI.parse(link[:href]).query) - expect(params).to include('rss_token' => [user.rss_token]) + expect(params).to include('feed_token' => [user.feed_token]) expect(params).to include('state' => ['opened']) expect(params).to include('assignee_id' => [user.id.to_s]) end @@ -53,7 +53,7 @@ let!(:issue2) { create(:issue, author: user, assignees: [assignee], project: project2, description: 'test desc') } it "renders issue fields" do - visit issues_dashboard_path(:atom, rss_token: user.rss_token, assignee_id: assignee.id) + visit issues_dashboard_path(:atom, feed_token: user.feed_token, assignee_id: assignee.id) entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue2.title}')]") @@ -76,7 +76,7 @@ end it "renders issue label and milestone info" do - visit issues_dashboard_path(:atom, rss_token: user.rss_token, assignee_id: assignee.id) + visit issues_dashboard_path(:atom, feed_token: user.feed_token, assignee_id: assignee.id) entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue1.title}')]") diff --git a/spec/features/atom/dashboard_spec.rb b/spec/features/atom/dashboard_spec.rb index c6683bb3bc9b8225a0431ccc2935602105701069..462eab07a75479c842c32be0d10325fc73169a8a 100644 --- a/spec/features/atom/dashboard_spec.rb +++ b/spec/features/atom/dashboard_spec.rb @@ -13,9 +13,9 @@ end end - context "projects atom feed via RSS token" do + context "projects atom feed via feed token" do it "renders projects atom feed" do - visit dashboard_projects_path(:atom, rss_token: user.rss_token) + visit dashboard_projects_path(:atom, feed_token: user.feed_token) expect(body).to have_selector('feed title') end end @@ -29,7 +29,7 @@ project.add_master(user) issue_event(issue, user) note_event(note, user) - visit dashboard_projects_path(:atom, rss_token: user.rss_token) + visit dashboard_projects_path(:atom, feed_token: user.feed_token) end it "has issue opened event" do diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb index 525ce23aa56a00ee2a42014fbe6ebd452e66c528..ee3570a5b2be4d33721ccfeaeacd90a9c1243da0 100644 --- a/spec/features/atom/issues_spec.rb +++ b/spec/features/atom/issues_spec.rb @@ -45,10 +45,10 @@ end end - context 'when authenticated via RSS token' do + context 'when authenticated via feed token' do it 'renders atom feed' do visit project_issues_path(project, :atom, - rss_token: user.rss_token) + feed_token: user.feed_token) expect(response_headers['Content-Type']) .to have_content('application/atom+xml') @@ -61,24 +61,23 @@ end it "renders atom feed with url parameters for project issues" do - visit project_issues_path(project, - :atom, rss_token: user.rss_token, state: 'opened', assignee_id: user.id) + visit project_issues_path(project, :atom, feed_token: user.feed_token, state: 'opened', assignee_id: user.id) link = find('link[type="application/atom+xml"]') params = CGI.parse(URI.parse(link[:href]).query) - expect(params).to include('rss_token' => [user.rss_token]) + expect(params).to include('feed_token' => [user.feed_token]) expect(params).to include('state' => ['opened']) expect(params).to include('assignee_id' => [user.id.to_s]) end it "renders atom feed with url parameters for group issues" do - visit issues_group_path(group, :atom, rss_token: user.rss_token, state: 'opened', assignee_id: user.id) + visit issues_group_path(group, :atom, feed_token: user.feed_token, state: 'opened', assignee_id: user.id) link = find('link[type="application/atom+xml"]') params = CGI.parse(URI.parse(link[:href]).query) - expect(params).to include('rss_token' => [user.rss_token]) + expect(params).to include('feed_token' => [user.feed_token]) expect(params).to include('state' => ['opened']) expect(params).to include('assignee_id' => [user.id.to_s]) end diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb index 2d074c115ddca2f8f199a34d139d4a0424cb90d5..eeaaa40fe2175325d6801aae172b4947a221e816 100644 --- a/spec/features/atom/users_spec.rb +++ b/spec/features/atom/users_spec.rb @@ -13,9 +13,9 @@ end end - context 'user atom feed via RSS token' do + context 'user atom feed via feed token' do it "renders user atom feed" do - visit user_path(user, :atom, rss_token: user.rss_token) + visit user_path(user, :atom, feed_token: user.feed_token) expect(body).to have_selector('feed title') end end @@ -51,7 +51,7 @@ issue_event(issue, user) note_event(note, user) merge_request_event(merge_request, user) - visit user_path(user, :atom, rss_token: user.rss_token) + visit user_path(user, :atom, feed_token: user.feed_token) end it 'has issue opened event' do diff --git a/spec/features/dashboard/activity_spec.rb b/spec/features/dashboard/activity_spec.rb index a74a8aac2b2dbad35e5469e4251820ee5931ec20..941208fa2448ee1d48e56a477d7830607cd1d97f 100644 --- a/spec/features/dashboard/activity_spec.rb +++ b/spec/features/dashboard/activity_spec.rb @@ -12,8 +12,8 @@ visit activity_dashboard_path end - it_behaves_like "it has an RSS button with current_user's RSS token" - it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token" + it_behaves_like "it has an RSS button with current_user's feed token" + it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" end context 'event filters', :js do diff --git a/spec/features/dashboard/issues_filter_spec.rb b/spec/features/dashboard/issues_filter_spec.rb index bab34ac9346be84f286fc056dde9ead3015cfe39..8d0b0be1bd42fee318e288f157db7afe342bf4cb 100644 --- a/spec/features/dashboard/issues_filter_spec.rb +++ b/spec/features/dashboard/issues_filter_spec.rb @@ -47,15 +47,15 @@ it 'updates atom feed link' do visit_issues(milestone_title: '', assignee_id: user.id) - link = find('.nav-controls a[title="Subscribe"]') + link = find('.nav-controls a[title="Subscribe to RSS feed"]') params = CGI.parse(URI.parse(link[:href]).query) auto_discovery_link = find('link[type="application/atom+xml"]', visible: false) auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query) - expect(params).to include('rss_token' => [user.rss_token]) + expect(params).to include('feed_token' => [user.feed_token]) expect(params).to include('milestone_title' => ['']) expect(params).to include('assignee_id' => [user.id.to_s]) - expect(auto_discovery_params).to include('rss_token' => [user.rss_token]) + expect(auto_discovery_params).to include('feed_token' => [user.feed_token]) expect(auto_discovery_params).to include('milestone_title' => ['']) expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s]) end diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb index e41a2e4ce091c5052d0c494946d542b1e9eaf56a..3cc7b38550d2fa8e0c64ee7ce3b0bf4213d9c57a 100644 --- a/spec/features/dashboard/issues_spec.rb +++ b/spec/features/dashboard/issues_spec.rb @@ -56,8 +56,8 @@ expect(page).to have_current_path(issues_dashboard_url(assignee_id: current_user.id, state: 'closed'), url: true) end - it_behaves_like "it has an RSS button with current_user's RSS token" - it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token" + it_behaves_like "it has an RSS button with current_user's feed token" + it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" end describe 'new issue dropdown' do diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb index 257a38225037af2b549b54ac423cb5dc5a04032c..ef2f0b5b31a323567d526400ff00cf5bde425a26 100644 --- a/spec/features/dashboard/projects_spec.rb +++ b/spec/features/dashboard/projects_spec.rb @@ -10,7 +10,7 @@ sign_in(user) end - it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token" do + it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" do before do visit dashboard_projects_path end diff --git a/spec/features/groups/activity_spec.rb b/spec/features/groups/activity_spec.rb index 7bc809b310409511c6eda6c8c9eecba5f74eb409..0d7d37710716165102cdad5319ecd2d04d01003d 100644 --- a/spec/features/groups/activity_spec.rb +++ b/spec/features/groups/activity_spec.rb @@ -15,8 +15,8 @@ visit path end - it_behaves_like "it has an RSS button with current_user's RSS token" - it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token" + it_behaves_like "it has an RSS button with current_user's feed token" + it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" end context 'when project is in the group', :js do @@ -39,7 +39,7 @@ visit path end - it_behaves_like "it has an RSS button without an RSS token" - it_behaves_like "an autodiscoverable RSS feed without an RSS token" + it_behaves_like "it has an RSS button without a feed token" + it_behaves_like "an autodiscoverable RSS feed without a feed token" end end diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb index 90bf7ba49f6eda39c733726d58421b81d33f5524..111a24c0d94711582156977b23aa86f53635b95e 100644 --- a/spec/features/groups/issues_spec.rb +++ b/spec/features/groups/issues_spec.rb @@ -16,17 +16,21 @@ let(:access_level) { ProjectFeature::ENABLED } context 'when signed in' do - let(:user) { user_in_group } - - it_behaves_like "it has an RSS button with current_user's RSS token" - it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token" + let(:user) do + user_in_group.ensure_feed_token + user_in_group.save! + user_in_group + end + + it_behaves_like "it has an RSS button with current_user's feed token" + it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" end context 'when signed out' do let(:user) { nil } - it_behaves_like "it has an RSS button without an RSS token" - it_behaves_like "an autodiscoverable RSS feed without an RSS token" + it_behaves_like "it has an RSS button without a feed token" + it_behaves_like "an autodiscoverable RSS feed without a feed token" end end diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 3a0424d60f8a55f58b93dfd6fc30ded40507f606..b7a7aa0e1740020a336fbbec77f9c87aab3206df 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -14,7 +14,7 @@ visit path end - it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token" + it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" context 'when group does not exist' do let(:path) { group_path('not-exist') } @@ -29,7 +29,7 @@ visit path end - it_behaves_like "an autodiscoverable RSS feed without an RSS token" + it_behaves_like "an autodiscoverable RSS feed without a feed token" end context 'when group has a public project', :js do diff --git a/spec/features/ics/dashboard_issues_spec.rb b/spec/features/ics/dashboard_issues_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..5d6cd44ad1cca8e1ff1f1f449b0a735d5de1e883 --- /dev/null +++ b/spec/features/ics/dashboard_issues_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +describe 'Dashboard Issues Calendar Feed' do + describe 'GET /issues' do + let!(:user) { create(:user, email: 'private1@example.com', public_email: 'public1@example.com') } + let!(:assignee) { create(:user, email: 'private2@example.com', public_email: 'public2@example.com') } + let!(:project) { create(:project) } + + before do + project.add_master(user) + end + + context 'when authenticated' do + it 'renders calendar feed' do + sign_in user + visit issues_dashboard_path(:ics) + + expect(response_headers['Content-Type']).to have_content('text/calendar') + expect(response_headers['Content-Disposition']).to have_content('inline') + expect(body).to have_text('BEGIN:VCALENDAR') + end + end + + context 'when authenticated via personal access token' do + it 'renders calendar feed' do + personal_access_token = create(:personal_access_token, user: user) + + visit issues_dashboard_path(:ics, private_token: personal_access_token.token) + + expect(response_headers['Content-Type']).to have_content('text/calendar') + expect(response_headers['Content-Disposition']).to have_content('inline') + expect(body).to have_text('BEGIN:VCALENDAR') + end + end + + context 'when authenticated via feed token' do + it 'renders calendar feed' do + visit issues_dashboard_path(:ics, feed_token: user.feed_token) + + expect(response_headers['Content-Type']).to have_content('text/calendar') + expect(response_headers['Content-Disposition']).to have_content('inline') + expect(body).to have_text('BEGIN:VCALENDAR') + end + end + + context 'issue with due date' do + let!(:issue) do + create(:issue, author: user, assignees: [assignee], project: project, title: 'test title', + description: 'test desc', due_date: Date.tomorrow) + end + + it 'renders issue fields' do + visit issues_dashboard_path(:ics, feed_token: user.feed_token) + + expect(body).to have_text("SUMMARY:test title (in #{project.full_path})") + # line length for ics is 75 chars + expected_description = "DESCRIPTION:Find out more at #{issue_url(issue)}".insert(75, "\r\n") + expect(body).to have_text(expected_description) + expect(body).to have_text("DTSTART;VALUE=DATE:#{Date.tomorrow.strftime('%Y%m%d')}") + expect(body).to have_text("URL:#{issue_url(issue)}") + expect(body).to have_text('TRANSP:TRANSPARENT') + end + end + end +end diff --git a/spec/features/ics/group_issues_spec.rb b/spec/features/ics/group_issues_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..0a049be2ffe7615d3453a0ab8947311f8325440e --- /dev/null +++ b/spec/features/ics/group_issues_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' + +describe 'Group Issues Calendar Feed' do + describe 'GET /issues' do + let!(:user) { create(:user, email: 'private1@example.com', public_email: 'public1@example.com') } + let!(:assignee) { create(:user, email: 'private2@example.com', public_email: 'public2@example.com') } + let!(:group) { create(:group) } + let!(:project) { create(:project, group: group) } + + before do + project.add_developer(user) + group.add_developer(user) + end + + context 'when authenticated' do + it 'renders calendar feed' do + sign_in user + visit issues_group_path(group, :ics) + + expect(response_headers['Content-Type']).to have_content('text/calendar') + expect(response_headers['Content-Disposition']).to have_content('inline') + expect(body).to have_text('BEGIN:VCALENDAR') + end + end + + context 'when authenticated via personal access token' do + it 'renders calendar feed' do + personal_access_token = create(:personal_access_token, user: user) + + visit issues_group_path(group, :ics, private_token: personal_access_token.token) + + expect(response_headers['Content-Type']).to have_content('text/calendar') + expect(response_headers['Content-Disposition']).to have_content('inline') + expect(body).to have_text('BEGIN:VCALENDAR') + end + end + + context 'when authenticated via feed token' do + it 'renders calendar feed' do + visit issues_group_path(group, :ics, feed_token: user.feed_token) + + expect(response_headers['Content-Type']).to have_content('text/calendar') + expect(response_headers['Content-Disposition']).to have_content('inline') + expect(body).to have_text('BEGIN:VCALENDAR') + end + end + + context 'issue with due date' do + let!(:issue) do + create(:issue, author: user, assignees: [assignee], project: project, title: 'test title', + description: 'test desc', due_date: Date.tomorrow) + end + + it 'renders issue fields' do + visit issues_group_path(group, :ics, feed_token: user.feed_token) + + expect(body).to have_text("SUMMARY:test title (in #{project.full_path})") + # line length for ics is 75 chars + expected_description = "DESCRIPTION:Find out more at #{issue_url(issue)}".insert(75, "\r\n") + expect(body).to have_text(expected_description) + expect(body).to have_text("DTSTART;VALUE=DATE:#{Date.tomorrow.strftime('%Y%m%d')}") + expect(body).to have_text("URL:#{issue_url(issue)}") + expect(body).to have_text('TRANSP:TRANSPARENT') + end + end + end +end diff --git a/spec/features/ics/project_issues_spec.rb b/spec/features/ics/project_issues_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..b99e9607f1d2379179534560ce251ce054c5a191 --- /dev/null +++ b/spec/features/ics/project_issues_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +describe 'Project Issues Calendar Feed' do + describe 'GET /issues' do + let!(:user) { create(:user, email: 'private1@example.com', public_email: 'public1@example.com') } + let!(:assignee) { create(:user, email: 'private2@example.com', public_email: 'public2@example.com') } + let!(:project) { create(:project) } + let!(:issue) { create(:issue, author: user, assignees: [assignee], project: project) } + + before do + project.add_developer(user) + end + + context 'when authenticated' do + it 'renders calendar feed' do + sign_in user + visit project_issues_path(project, :ics) + + expect(response_headers['Content-Type']).to have_content('text/calendar') + expect(response_headers['Content-Disposition']).to have_content('inline') + expect(body).to have_text('BEGIN:VCALENDAR') + end + end + + context 'when authenticated via personal access token' do + it 'renders calendar feed' do + personal_access_token = create(:personal_access_token, user: user) + + visit project_issues_path(project, :ics, private_token: personal_access_token.token) + + expect(response_headers['Content-Type']).to have_content('text/calendar') + expect(response_headers['Content-Disposition']).to have_content('inline') + expect(body).to have_text('BEGIN:VCALENDAR') + end + end + + context 'when authenticated via feed token' do + it 'renders calendar feed' do + visit project_issues_path(project, :ics, feed_token: user.feed_token) + + expect(response_headers['Content-Type']).to have_content('text/calendar') + expect(response_headers['Content-Disposition']).to have_content('inline') + expect(body).to have_text('BEGIN:VCALENDAR') + end + end + + context 'issue with due date' do + let!(:issue) do + create(:issue, author: user, assignees: [assignee], project: project, title: 'test title', + description: 'test desc', due_date: Date.tomorrow) + end + + it 'renders issue fields' do + visit project_issues_path(project, :ics, feed_token: user.feed_token) + + expect(body).to have_text("SUMMARY:test title (in #{project.full_path})") + # line length for ics is 75 chars + expected_description = "DESCRIPTION:Find out more at #{issue_url(issue)}".insert(75, "\r\n") + expect(body).to have_text(expected_description) + expect(body).to have_text("DTSTART;VALUE=DATE:#{Date.tomorrow.strftime('%Y%m%d')}") + expect(body).to have_text("URL:#{issue_url(issue)}") + expect(body).to have_text('TRANSP:TRANSPARENT') + end + end + end +end diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb index 6b4dd875ab372f519a4238f3f3a16205a186d455..8dca81a8627842766ad2d6d64496fb320c71ccc2 100644 --- a/spec/features/issues/filtered_search/filter_issues_spec.rb +++ b/spec/features/issues/filtered_search/filter_issues_spec.rb @@ -485,13 +485,13 @@ def expect_no_issues_list it "for #{type}" do visit path - link = find_link('Subscribe') + link = find_link('Subscribe to RSS feed') params = CGI.parse(URI.parse(link[:href]).query) auto_discovery_link = find('link[type="application/atom+xml"]', visible: false) auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query) expected = { - 'rss_token' => [user.rss_token], + 'feed_token' => [user.feed_token], 'milestone_title' => [milestone.title], 'assignee_id' => [user.id.to_s] } @@ -511,15 +511,15 @@ def expect_no_issues_list it 'updates atom feed link for group issues' do visit issues_group_path(group, milestone_title: milestone.title, assignee_id: user.id) - link = find('.nav-controls a', text: 'Subscribe', visible: false) + link = find('.nav-controls a[title="Subscribe to RSS feed"]', visible: false) params = CGI.parse(URI.parse(link[:href]).query) auto_discovery_link = find('link[type="application/atom+xml"]', visible: false) auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query) - expect(params).to include('rss_token' => [user.rss_token]) + expect(params).to include('feed_token' => [user.feed_token]) expect(params).to include('milestone_title' => [milestone.title]) expect(params).to include('assignee_id' => [user.id.to_s]) - expect(auto_discovery_params).to include('rss_token' => [user.rss_token]) + expect(auto_discovery_params).to include('feed_token' => [user.feed_token]) expect(auto_discovery_params).to include('milestone_title' => [milestone.title]) expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s]) end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 4170e2c42687fd5567e5cbef860e99eb648cecaf..2290ae8ad231566a0b4b45d36b1b315b6fb4ab00 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -351,6 +351,20 @@ expect(page).to have_content('baz') end end + + it 'filters by due next month and previous two weeks' do + foo.update(due_date: Date.today - 4.weeks) + bar.update(due_date: (Date.today + 2.months).beginning_of_month) + baz.update(due_date: Date.yesterday) + + visit project_issues_path(project, due_date: Issue::DueNextMonthAndPreviousTwoWeeks.name) + + page.within '.issues-holder' do + expect(page).not_to have_content('foo') + expect(page).not_to have_content('bar') + expect(page).to have_content('baz') + end + end end describe 'sorting by milestone' do diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb index 15dcb30cbddc8816b81e38af16382e42ccca9784..2e0753c3bfbc942c1fb9b0d807aace82d8afa6cf 100644 --- a/spec/features/profile_spec.rb +++ b/spec/features/profile_spec.rb @@ -56,21 +56,21 @@ end end - describe 'when I reset RSS token' do + describe 'when I reset feed token' do before do visit profile_personal_access_tokens_path end - it 'resets RSS token' do - within('.rss-token-reset') do - previous_token = find("#rss_token").value + it 'resets feed token' do + within('.feed-token-reset') do + previous_token = find("#feed_token").value accept_confirm { click_link('reset it') } - expect(find('#rss_token').value).not_to eq(previous_token) + expect(find('#feed_token').value).not_to eq(previous_token) end - expect(page).to have_content 'RSS token was successfully reset' + expect(page).to have_content 'Feed token was successfully reset' end end diff --git a/spec/features/projects/activity/rss_spec.rb b/spec/features/projects/activity/rss_spec.rb index cd1cfe0799864efe274c0e9a100eb9bbe448af28..4ac34adde0edea262beeeb8a27411a5e0dc583b2 100644 --- a/spec/features/projects/activity/rss_spec.rb +++ b/spec/features/projects/activity/rss_spec.rb @@ -15,7 +15,7 @@ visit path end - it_behaves_like "it has an RSS button with current_user's RSS token" + it_behaves_like "it has an RSS button with current_user's feed token" end context 'when signed out' do @@ -23,6 +23,6 @@ visit path end - it_behaves_like "it has an RSS button without an RSS token" + it_behaves_like "it has an RSS button without a feed token" end end diff --git a/spec/features/projects/commits/rss_spec.rb b/spec/features/projects/commits/rss_spec.rb index 0d9c7355ddda36211917e931c0895def969bebf6..0bc207da9703cb85034fb4486a899e151dd55888 100644 --- a/spec/features/projects/commits/rss_spec.rb +++ b/spec/features/projects/commits/rss_spec.rb @@ -12,8 +12,8 @@ visit path end - it_behaves_like "it has an RSS button with current_user's RSS token" - it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token" + it_behaves_like "it has an RSS button with current_user's feed token" + it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" end context 'when signed out' do @@ -21,7 +21,7 @@ visit path end - it_behaves_like "it has an RSS button without an RSS token" - it_behaves_like "an autodiscoverable RSS feed without an RSS token" + it_behaves_like "it has an RSS button without a feed token" + it_behaves_like "an autodiscoverable RSS feed without a feed token" end end diff --git a/spec/features/projects/issues/rss_spec.rb b/spec/features/projects/issues/rss_spec.rb index ff91aabc311e8c8629667701c1ba66182cd9d954..8b1f7d432ee5c900440d44ff29c50736c692ebaf 100644 --- a/spec/features/projects/issues/rss_spec.rb +++ b/spec/features/projects/issues/rss_spec.rb @@ -17,8 +17,8 @@ visit path end - it_behaves_like "it has an RSS button with current_user's RSS token" - it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token" + it_behaves_like "it has an RSS button with current_user's feed token" + it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" end context 'when signed out' do @@ -26,7 +26,7 @@ visit path end - it_behaves_like "it has an RSS button without an RSS token" - it_behaves_like "an autodiscoverable RSS feed without an RSS token" + it_behaves_like "it has an RSS button without a feed token" + it_behaves_like "an autodiscoverable RSS feed without a feed token" end end diff --git a/spec/features/projects/show/rss_spec.rb b/spec/features/projects/show/rss_spec.rb index d02eaf3453357896b88c3e43b235abc9862f867f..52164d30c40347a1d8b277b855a229578d2e0a2b 100644 --- a/spec/features/projects/show/rss_spec.rb +++ b/spec/features/projects/show/rss_spec.rb @@ -12,7 +12,7 @@ visit path end - it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token" + it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" end context 'when signed out' do @@ -20,6 +20,6 @@ visit path end - it_behaves_like "an autodiscoverable RSS feed without an RSS token" + it_behaves_like "an autodiscoverable RSS feed without a feed token" end end diff --git a/spec/features/projects/tree/rss_spec.rb b/spec/features/projects/tree/rss_spec.rb index 6407370ac0dc1b219fdd5a20702443353679cd20..f52b3cc1d86c4d0fc77b376d7a75eb4bdd6d6dd2 100644 --- a/spec/features/projects/tree/rss_spec.rb +++ b/spec/features/projects/tree/rss_spec.rb @@ -12,7 +12,7 @@ visit path end - it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token" + it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" end context 'when signed out' do @@ -20,6 +20,6 @@ visit path end - it_behaves_like "an autodiscoverable RSS feed without an RSS token" + it_behaves_like "an autodiscoverable RSS feed without a feed token" end end diff --git a/spec/features/users/rss_spec.rb b/spec/features/users/rss_spec.rb index 7c5abe54d560a9530718fc4ee83433adf6d171f2..c3734b5c808442599a57693019e7f2f15e02452a 100644 --- a/spec/features/users/rss_spec.rb +++ b/spec/features/users/rss_spec.rb @@ -10,7 +10,7 @@ visit path end - it_behaves_like "it has an RSS button with current_user's RSS token" + it_behaves_like "it has an RSS button with current_user's feed token" end context 'when signed out' do @@ -18,6 +18,6 @@ visit path end - it_behaves_like "it has an RSS button without an RSS token" + it_behaves_like "it has an RSS button without a feed token" end end diff --git a/spec/helpers/calendar_helper_spec.rb b/spec/helpers/calendar_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..828a9d9fea09842eaef97b68b058921107a00243 --- /dev/null +++ b/spec/helpers/calendar_helper_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe CalendarHelper do + describe '#calendar_url_options' do + context 'when signed in' do + it "includes the current_user's feed_token" do + current_user = create(:user) + allow(helper).to receive(:current_user).and_return(current_user) + expect(helper.calendar_url_options).to include feed_token: current_user.feed_token + end + end + + context 'when signed out' do + it "does not have a feed_token" do + allow(helper).to receive(:current_user).and_return(nil) + expect(helper.calendar_url_options[:feed_token]).to be_nil + end + end + end +end diff --git a/spec/helpers/rss_helper_spec.rb b/spec/helpers/rss_helper_spec.rb index 269e1057e8d1da3e672c0ba1b0ec537bf35dbe16..a7f9bdf07e4f6c2a8e9210d2c1fe33dabeeecf6d 100644 --- a/spec/helpers/rss_helper_spec.rb +++ b/spec/helpers/rss_helper_spec.rb @@ -3,17 +3,17 @@ describe RssHelper do describe '#rss_url_options' do context 'when signed in' do - it "includes the current_user's rss_token" do + it "includes the current_user's feed_token" do current_user = create(:user) allow(helper).to receive(:current_user).and_return(current_user) - expect(helper.rss_url_options).to include rss_token: current_user.rss_token + expect(helper.rss_url_options).to include feed_token: current_user.feed_token end end context 'when signed out' do - it "does not have an rss_token" do + it "does not have a feed_token" do allow(helper).to receive(:current_user).and_return(nil) - expect(helper.rss_url_options[:rss_token]).to be_nil + expect(helper.rss_url_options[:feed_token]).to be_nil end end end diff --git a/spec/lib/gitlab/auth/request_authenticator_spec.rb b/spec/lib/gitlab/auth/request_authenticator_spec.rb index ffcd90b9fcb1e49318d85fc9e83407971cc9f71e..242ab4a91dd83c03ff6ba0e8631119112a53d98d 100644 --- a/spec/lib/gitlab/auth/request_authenticator_spec.rb +++ b/spec/lib/gitlab/auth/request_authenticator_spec.rb @@ -39,19 +39,19 @@ describe '#find_sessionless_user' do let!(:access_token_user) { build(:user) } - let!(:rss_token_user) { build(:user) } + let!(:feed_token_user) { build(:user) } it 'returns access_token user first' do allow_any_instance_of(described_class).to receive(:find_user_from_access_token).and_return(access_token_user) - allow_any_instance_of(described_class).to receive(:find_user_from_rss_token).and_return(rss_token_user) + allow_any_instance_of(described_class).to receive(:find_user_from_feed_token).and_return(feed_token_user) expect(subject.find_sessionless_user).to eq access_token_user end - it 'returns rss_token user if no access_token user found' do - allow_any_instance_of(described_class).to receive(:find_user_from_rss_token).and_return(rss_token_user) + it 'returns feed_token user if no access_token user found' do + allow_any_instance_of(described_class).to receive(:find_user_from_feed_token).and_return(feed_token_user) - expect(subject.find_sessionless_user).to eq rss_token_user + expect(subject.find_sessionless_user).to eq feed_token_user end it 'returns nil if no user found' do diff --git a/spec/lib/gitlab/auth/user_auth_finders_spec.rb b/spec/lib/gitlab/auth/user_auth_finders_spec.rb index 2733eef66117a68bb79d8fa08595ecda193c3387..136646bd4ee54ded3e041f2e43b0505847cc5a1d 100644 --- a/spec/lib/gitlab/auth/user_auth_finders_spec.rb +++ b/spec/lib/gitlab/auth/user_auth_finders_spec.rb @@ -46,34 +46,54 @@ def set_param(key, value) end end - describe '#find_user_from_rss_token' do + describe '#find_user_from_feed_token' do context 'when the request format is atom' do before do env['HTTP_ACCEPT'] = 'application/atom+xml' end - it 'returns user if valid rss_token' do - set_param(:rss_token, user.rss_token) + context 'when feed_token param is provided' do + it 'returns user if valid feed_token' do + set_param(:feed_token, user.feed_token) - expect(find_user_from_rss_token).to eq user - end + expect(find_user_from_feed_token).to eq user + end + + it 'returns nil if feed_token is blank' do + expect(find_user_from_feed_token).to be_nil + end + + it 'returns exception if invalid feed_token' do + set_param(:feed_token, 'invalid_token') - it 'returns nil if rss_token is blank' do - expect(find_user_from_rss_token).to be_nil + expect { find_user_from_feed_token }.to raise_error(Gitlab::Auth::UnauthorizedError) + end end - it 'returns exception if invalid rss_token' do - set_param(:rss_token, 'invalid_token') + context 'when rss_token param is provided' do + it 'returns user if valid rssd_token' do + set_param(:rss_token, user.feed_token) - expect { find_user_from_rss_token }.to raise_error(Gitlab::Auth::UnauthorizedError) + expect(find_user_from_feed_token).to eq user + end + + it 'returns nil if rss_token is blank' do + expect(find_user_from_feed_token).to be_nil + end + + it 'returns exception if invalid rss_token' do + set_param(:rss_token, 'invalid_token') + + expect { find_user_from_feed_token }.to raise_error(Gitlab::Auth::UnauthorizedError) + end end end context 'when the request format is not atom' do it 'returns nil' do - set_param(:rss_token, user.rss_token) + set_param(:feed_token, user.feed_token) - expect(find_user_from_rss_token).to be_nil + expect(find_user_from_feed_token).to be_nil end end @@ -81,7 +101,7 @@ def set_param(key, value) it 'the method call does not modify the original value' do env['action_dispatch.request.formats'] = nil - find_user_from_rss_token + find_user_from_feed_token expect(env['action_dispatch.request.formats']).to be_nil end diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb index dfb83578fce4f6a75a99082e7db976ff2131c5e6..9b80442913811f0007704f4a63757455b7fee522 100644 --- a/spec/models/concerns/token_authenticatable_spec.rb +++ b/spec/models/concerns/token_authenticatable_spec.rb @@ -12,7 +12,7 @@ end describe User, 'TokenAuthenticatable' do - let(:token_field) { :rss_token } + let(:token_field) { :feed_token } it_behaves_like 'TokenAuthenticatable' describe 'ensures authentication token' do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 45967fdb64ad6a613745485fa2e55caa0a4a23b1..8e2ea5537f9dc76be23e1e1ffe6b2028e9258c84 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -672,13 +672,13 @@ end end - describe 'rss token' do - it 'ensures an rss token on read' do - user = create(:user, rss_token: nil) - rss_token = user.rss_token + describe 'feed token' do + it 'ensures a feed token on read' do + user = create(:user, feed_token: nil) + feed_token = user.feed_token - expect(rss_token).not_to be_blank - expect(user.reload.rss_token).to eq rss_token + expect(feed_token).not_to be_blank + expect(user.reload.feed_token).to eq feed_token end end diff --git a/spec/requests/rack_attack_global_spec.rb b/spec/requests/rack_attack_global_spec.rb index b18e922b0630d33e1eb295284ed2420795b03908..c0a3ea397df6af00a48a5ed08223384cbc37e2e9 100644 --- a/spec/requests/rack_attack_global_spec.rb +++ b/spec/requests/rack_attack_global_spec.rb @@ -349,7 +349,7 @@ def api_get_args_with_token_headers(partial_url, token_headers) end def rss_url(user) - "/dashboard/projects.atom?rss_token=#{user.rss_token}" + "/dashboard/projects.atom?feed_token=#{user.feed_token}" end def private_token_headers(user) diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb index 9345671a1a7fe4c4755093f990a1b2e935aaf63b..dd8f6239587b75780ac9ff4558dac929cffffe20 100644 --- a/spec/routing/routing_spec.rb +++ b/spec/routing/routing_spec.rb @@ -162,8 +162,8 @@ expect(get("/profile/audit_log")).to route_to('profiles#audit_log') end - it "to #reset_rss_token" do - expect(put("/profile/reset_rss_token")).to route_to('profiles#reset_rss_token') + it "to #reset_feed_token" do + expect(put("/profile/reset_feed_token")).to route_to('profiles#reset_feed_token') end it "to #show" do @@ -249,7 +249,11 @@ end it "to #issues" do - expect(get("/dashboard/issues")).to route_to('dashboard#issues') + expect(get("/dashboard/issues.html")).to route_to('dashboard#issues', format: 'html') + end + + it "to #calendar_issues" do + expect(get("/dashboard/issues.ics")).to route_to('dashboard#issues_calendar', format: 'ics') end it "to #merge_requests" do diff --git a/spec/support/features/rss_shared_examples.rb b/spec/support/features/rss_shared_examples.rb index 50fbbc7f55b569ec5e328d73c2515782d2d44134..0de92aedba51273af26c2058ba8c53b5b2d77659 100644 --- a/spec/support/features/rss_shared_examples.rb +++ b/spec/support/features/rss_shared_examples.rb @@ -1,23 +1,23 @@ -shared_examples "an autodiscoverable RSS feed with current_user's RSS token" do - it "has an RSS autodiscovery link tag with current_user's RSS token" do - expect(page).to have_css("link[type*='atom+xml'][href*='rss_token=#{user.rss_token}']", visible: false) +shared_examples "an autodiscoverable RSS feed with current_user's feed token" do + it "has an RSS autodiscovery link tag with current_user's feed token" do + expect(page).to have_css("link[type*='atom+xml'][href*='feed_token=#{user.feed_token}']", visible: false) end end -shared_examples "it has an RSS button with current_user's RSS token" do - it "shows the RSS button with current_user's RSS token" do - expect(page).to have_css("a:has(.fa-rss)[href*='rss_token=#{user.rss_token}']") +shared_examples "it has an RSS button with current_user's feed token" do + it "shows the RSS button with current_user's feed token" do + expect(page).to have_css("a:has(.fa-rss)[href*='feed_token=#{user.feed_token}']") end end -shared_examples "an autodiscoverable RSS feed without an RSS token" do - it "has an RSS autodiscovery link tag without an RSS token" do - expect(page).to have_css("link[type*='atom+xml']:not([href*='rss_token'])", visible: false) +shared_examples "an autodiscoverable RSS feed without a feed token" do + it "has an RSS autodiscovery link tag without a feed token" do + expect(page).to have_css("link[type*='atom+xml']:not([href*='feed_token'])", visible: false) end end -shared_examples "it has an RSS button without an RSS token" do - it "shows the RSS button without an RSS token" do - expect(page).to have_css("a:has(.fa-rss):not([href*='rss_token'])") +shared_examples "it has an RSS button without a feed token" do + it "shows the RSS button without a feed token" do + expect(page).to have_css("a:has(.fa-rss):not([href*='feed_token'])") end end diff --git a/spec/tasks/tokens_spec.rb b/spec/tasks/tokens_spec.rb index 51f7a536cbb98c9bfab9b831a3f9e9f9eb257c6b..555a58e9aa1f5765141598cb3a13e9b1e91d9b0a 100644 --- a/spec/tasks/tokens_spec.rb +++ b/spec/tasks/tokens_spec.rb @@ -13,9 +13,9 @@ end end - describe 'reset_all_rss task' do + describe 'reset_all_feed task' do it 'invokes create_hooks task' do - expect { run_rake_task('tokens:reset_all_rss') }.to change { user.reload.rss_token } + expect { run_rake_task('tokens:reset_all_feed') }.to change { user.reload.feed_token } end end end