From: Andy Allan Date: Sat, 15 Feb 2025 17:37:48 +0000 (+0000) Subject: Merge pull request #5677 from tomhughes/user-list-enhancements X-Git-Tag: live~36 X-Git-Url: https://git.openstreetmap.org./rails.git/commitdiff_plain/90e787eed845a850eab63a22831c9cfa47f4dd3d?hp=e91f4d82ea9b2496e19304beeb1e06624c55405f Merge pull request #5677 from tomhughes/user-list-enhancements User list enhancements --- diff --git a/app/abilities/ability.rb b/app/abilities/ability.rb index 3116bc5cd..dd377a727 100644 --- a/app/abilities/ability.rb +++ b/app/abilities/ability.rb @@ -29,7 +29,7 @@ class Ability if user&.active? can :welcome, :site - can :read, [:deletion, :account_terms] + can :read, [:deletion, :account_terms, :account_pd_declaration, :account_home] if Settings.status != "database_offline" can [:read, :create, :destroy], :changeset_subscription @@ -38,6 +38,7 @@ class Ability can [:read, :create, :destroy], :oauth2_authorization can [:update, :destroy], :account can :update, :account_terms + can :create, :account_pd_declaration can :read, :dashboard can [:create, :subscribe, :unsubscribe], DiaryEntry can :update, DiaryEntry, :user => user diff --git a/app/abilities/api_ability.rb b/app/abilities/api_ability.rb index 7bbd9889a..ec4de8f8e 100644 --- a/app/abilities/api_ability.rb +++ b/app/abilities/api_ability.rb @@ -43,7 +43,9 @@ class ApiAbility can :destroy, Note if scopes.include?("write_notes") - can :redact, [OldNode, OldWay, OldRelation] if user&.terms_agreed? && scopes.include?("write_redactions") + can :redact, [OldNode, OldWay, OldRelation] if user.terms_agreed? && scopes.include?("write_redactions") + + can :create, UserBlock if scopes.include?("write_blocks") end end end diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 2e13f6967..464bbecee 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -90,21 +90,76 @@ $(document).ready(function () { // See https://turbo.hotwired.dev/reference/drive#turbo.session.drive Turbo.session.drive = false; - var headerWidth = 0, - compactWidth = 0; + const $expandedSecondaryMenu = $("header nav.secondary > ul"), + $collapsedSecondaryMenu = $("#compact-secondary-nav > ul"), + secondaryMenuItems = [], + breakpointWidth = 768; + let moreItemWidth = 0; function updateHeader() { var windowWidth = $(window).width(); - if (windowWidth < compactWidth) { - $("body").removeClass("compact-nav").addClass("small-nav"); - } else if (windowWidth < headerWidth) { - $("body").addClass("compact-nav").removeClass("small-nav"); + if (windowWidth < breakpointWidth) { + $("body").addClass("small-nav"); + expandAllSecondaryMenuItems(); } else { - $("body").removeClass("compact-nav").removeClass("small-nav"); + $("body").removeClass("small-nav"); + const availableWidth = $expandedSecondaryMenu.width(); + secondaryMenuItems.forEach(function (item) { + $(item[0]).remove(); + }); + let runningWidth = 0, + i = 0, + requiredWidth; + for (; i < secondaryMenuItems.length; i++) { + runningWidth += secondaryMenuItems[i][1]; + if (i < secondaryMenuItems.length - 1) { + requiredWidth = runningWidth + moreItemWidth; + } else { + requiredWidth = runningWidth; + } + if (requiredWidth > availableWidth) { + break; + } + expandSecondaryMenuItem($(secondaryMenuItems[i][0])); + } + for (; i < secondaryMenuItems.length; i++) { + collapseSecondaryMenuItem($(secondaryMenuItems[i][0])); + } } } + function expandAllSecondaryMenuItems() { + secondaryMenuItems.forEach(function (item) { + expandSecondaryMenuItem($(item[0])); + }); + } + + function expandSecondaryMenuItem($item) { + $item.children("a") + .removeClass("dropdown-item") + .addClass("nav-link") + .addClass(function () { + return $(this).hasClass("active") ? "text-secondary-emphasis" : "text-secondary"; + }); + $item.addClass("nav-item").insertBefore("#compact-secondary-nav"); + toggleCompactSecondaryNav(); + } + + function collapseSecondaryMenuItem($item) { + $item.children("a") + .addClass("dropdown-item") + .removeClass("nav-link text-secondary text-secondary-emphasis"); + $item.removeClass("nav-item").appendTo($collapsedSecondaryMenu); + toggleCompactSecondaryNav(); + } + + function toggleCompactSecondaryNav() { + $("#compact-secondary-nav").toggle( + $collapsedSecondaryMenu.find("li").length > 0 + ); + } + /* * Chrome 60 and later seem to fire the "ready" callback * before the DOM is fully ready causing us to measure the @@ -112,20 +167,10 @@ $(document).ready(function () { * to defer the measurement slightly as a workaround. */ setTimeout(function () { - $("header").children(":visible").each(function (i, e) { - headerWidth += $(e).outerWidth(); + $expandedSecondaryMenu.find("li:not(#compact-secondary-nav)").each(function () { + secondaryMenuItems.push([this, $(this).width()]); }); - - $("body").addClass("compact-nav"); - - $("header").children(":visible").each(function (i, e) { - compactWidth += $(e).outerWidth(); - }); - - $("body").removeClass("compact-nav"); - - $("header").removeClass("text-nowrap"); - $("header nav.secondary > ul").removeClass("flex-nowrap"); + moreItemWidth = $("#compact-secondary-nav").width(); updateHeader(); diff --git a/app/assets/javascripts/index.js b/app/assets/javascripts/index.js index c3ee1e3bc..810327e3f 100644 --- a/app/assets/javascripts/index.js +++ b/app/assets/javascripts/index.js @@ -21,6 +21,7 @@ //= require index/directions //= require index/changeset //= require index/query +//= require index/home //= require router $(document).ready(function () { @@ -38,6 +39,14 @@ $(document).ready(function () { $("#sidebar_loader").show().addClass("delayed-fade-in"); + // Prevent caching the XHR response as a full-page URL + // https://github.com/openstreetmap/openstreetmap-website/issues/5663 + if (content_path.indexOf("?") >= 0) { + content_path += "&xhr=1"; + } else { + content_path += "?xhr=1"; + } + $("#sidebar_content") .empty(); @@ -213,16 +222,6 @@ $(document).ready(function () { L.marker([params.mlat, params.mlon]).addTo(map); } - $("#homeanchor").on("click", function (e) { - e.preventDefault(); - - var data = $(this).data(), - center = L.latLng(data.lat, data.lon); - - map.setView(center, data.zoom); - L.marker(center, { icon: OSM.getUserIcon() }).addTo(map); - }); - function remoteEditHandler(bbox, object) { var remoteEditHost = "http://127.0.0.1:8111", osmHost = location.protocol + "//" + location.host, @@ -309,8 +308,9 @@ $(document).ready(function () { }; function addObject(type, id, center) { + var hashParams = OSM.parseHash(window.location.hash); map.addObject({ type: type, id: parseInt(id, 10) }, function (bounds) { - if (!window.location.hash && bounds.isValid() && + if (!hashParams.center && bounds.isValid() && (center || !map.getBounds().contains(bounds))) { OSM.router.withoutMoveListener(function () { map.fitBounds(bounds); @@ -356,7 +356,8 @@ $(document).ready(function () { "/relation/:id(/history)": OSM.Browse(map, "relation"), "/relation/:id/history/:version": OSM.OldBrowse(), "/changeset/:id": OSM.Changeset(map), - "/query": OSM.Query(map) + "/query": OSM.Query(map), + "/account/home": OSM.Home(map) }); if (OSM.preferred_editor === "remote" && document.location.pathname === "/edit") { diff --git a/app/assets/javascripts/index/changeset.js b/app/assets/javascripts/index/changeset.js index 39b4abde1..d3e61270b 100644 --- a/app/assets/javascripts/index/changeset.js +++ b/app/assets/javascripts/index/changeset.js @@ -12,9 +12,10 @@ OSM.Changeset = function (map) { const changesetData = content.find("[data-changeset]").data("changeset"); changesetData.type = "changeset"; + var hashParams = OSM.parseHash(window.location.hash); initialize(); map.addObject(changesetData, function (bounds) { - if (!window.location.hash && bounds.isValid()) { + if (!hashParams.center && bounds.isValid()) { OSM.router.withoutMoveListener(function () { map.fitBounds(bounds); }); diff --git a/app/assets/javascripts/index/home.js b/app/assets/javascripts/index/home.js new file mode 100644 index 000000000..7e297b724 --- /dev/null +++ b/app/assets/javascripts/index/home.js @@ -0,0 +1,38 @@ +OSM.Home = function (map) { + let marker; + + function clearMarker() { + if (marker) map.removeLayer(marker); + marker = null; + } + + const page = {}; + + page.pushstate = page.popstate = page.load = function () { + map.setSidebarOverlaid(true); + clearMarker(); + + if (OSM.home) { + OSM.router.withoutMoveListener(function () { + map.setView(OSM.home, 15, { reset: true }); + }); + marker = L.marker(OSM.home, { + icon: OSM.getUserIcon(), + title: I18n.t("javascripts.home.marker_title") + }).addTo(map); + } else { + $("#browse_status").html( + $("
").text( + I18n.t("javascripts.home.not_set") + ) + ); + } + }; + + page.unload = function () { + clearMarker(); + $("#browse_status").empty(); + }; + + return page; +}; diff --git a/app/assets/javascripts/index/note.js b/app/assets/javascripts/index/note.js index e9c51f9bf..6a0487aaa 100644 --- a/app/assets/javascripts/index/note.js +++ b/app/assets/javascripts/index/note.js @@ -27,13 +27,16 @@ OSM.Note = function (map) { var data = $(".details").data(); if (!data) return; var latLng = L.latLng(data.coordinates.split(",")); - if (!map.getBounds().contains(latLng)) moveToNote(); + if (!map.getBounds().contains(latLng)) { + OSM.router.withoutMoveListener(function () { + map.setView(latLng, 15, { reset: true }); + }); + } }); }; page.load = function (path, id) { initialize(path, id); - moveToNote(); }; function initialize(path, id) { @@ -48,7 +51,6 @@ OSM.Note = function (map) { success: () => { OSM.loadSidebarContent(path, () => { initialize(path, id); - moveToNote(); }); }, error: (xhr) => { @@ -77,11 +79,19 @@ OSM.Note = function (map) { var data = $(".details").data(); if (data) { + var hashParams = OSM.parseHash(window.location.hash); map.addObject({ type: "note", id: parseInt(id, 10), latLng: L.latLng(data.coordinates.split(",")), icon: noteIcons[data.status] + }, function () { + if (!hashParams.center) { + var latLng = L.latLng(data.coordinates.split(",")); + OSM.router.withoutMoveListener(function () { + map.setView(latLng, 15, { reset: true }); + }); + } }); } } @@ -99,18 +109,6 @@ OSM.Note = function (map) { } } - function moveToNote() { - var data = $(".details").data(); - if (!data) return; - var latLng = L.latLng(data.coordinates.split(",")); - - if (!window.location.hash || window.location.hash.match(/^#?c[0-9]+$/)) { - OSM.router.withoutMoveListener(function () { - map.setView(latLng, 15, { reset: true }); - }); - } - } - page.unload = function () { map.removeObject(); }; diff --git a/app/assets/javascripts/leaflet.map.js b/app/assets/javascripts/leaflet.map.js index b10101721..1b92edf99 100644 --- a/app/assets/javascripts/leaflet.map.js +++ b/app/assets/javascripts/leaflet.map.js @@ -347,15 +347,22 @@ L.OSM.Map = L.Map.extend({ }, setSidebarOverlaid: function (overlaid) { - var sidebarWidth = 350; + var mediumDeviceWidth = window.getComputedStyle(document.documentElement).getPropertyValue("--bs-breakpoint-md"); + var isMediumDevice = window.matchMedia(`(max-width: ${mediumDeviceWidth})`).matches; + var sidebarWidth = $("#sidebar").width(); + var sidebarHeight = $("#sidebar").height(); if (overlaid && !$("#content").hasClass("overlay-sidebar")) { $("#content").addClass("overlay-sidebar"); this.invalidateSize({ pan: false }); - if ($("html").attr("dir") !== "rtl") { + if (isMediumDevice) { + this.panBy([0, -sidebarHeight], { animate: false }); + } else if ($("html").attr("dir") !== "rtl") { this.panBy([-sidebarWidth, 0], { animate: false }); } } else if (!overlaid && $("#content").hasClass("overlay-sidebar")) { - if ($("html").attr("dir") !== "rtl") { + if (isMediumDevice) { + this.panBy([0, $("#map").height() / 2], { animate: false }); + } else if ($("html").attr("dir") !== "rtl") { this.panBy([sidebarWidth, 0], { animate: false }); } $("#content").removeClass("overlay-sidebar"); diff --git a/app/assets/javascripts/leaflet.sidebar.js b/app/assets/javascripts/leaflet.sidebar.js index 9573c839d..1c911a961 100644 --- a/app/assets/javascripts/leaflet.sidebar.js +++ b/app/assets/javascripts/leaflet.sidebar.js @@ -17,6 +17,8 @@ L.OSM.sidebar = function (selector) { }; control.togglePane = function (pane, button) { + var mediumDeviceWidth = window.getComputedStyle(document.documentElement).getPropertyValue("--bs-breakpoint-md"); + var isMediumDevice = window.matchMedia(`(max-width: ${mediumDeviceWidth})`).matches; var paneWidth = 250; current @@ -27,18 +29,22 @@ L.OSM.sidebar = function (selector) { .removeClass("active"); if (current === pane) { - if ($("html").attr("dir") === "rtl") { - map.panBy([-paneWidth, 0], { animate: false }); - } $(sidebar).hide(); $("#content").addClass("overlay-right-sidebar"); current = currentButton = $(); + if (isMediumDevice) { + map.panBy([0, -$("#map").height() / 2], { animate: false }); + } else if ($("html").attr("dir") === "rtl") { + map.panBy([-paneWidth, 0], { animate: false }); + } } else { $(sidebar).show(); $("#content").removeClass("overlay-right-sidebar"); current = pane; currentButton = button || $(); - if ($("html").attr("dir") === "rtl") { + if (isMediumDevice) { + map.panBy([0, $("#map").height()], { animate: false }); + } else if ($("html").attr("dir") === "rtl") { map.panBy([paneWidth, 0], { animate: false }); } } diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss index fc9af9803..a27976854 100644 --- a/app/assets/stylesheets/common.scss +++ b/app/assets/stylesheets/common.scss @@ -131,10 +131,6 @@ header { font-size: 14px; } - nav.primary { - margin-right: auto; - } - .username { max-width: 12em; } @@ -174,7 +170,11 @@ nav.primary { nav.secondary { .nav-link { - padding: 0.3rem; + padding: 0 0.3rem; + } + + > ul { + height: 1.5em; } } @@ -191,15 +191,6 @@ nav.primary, nav.secondary { display: none; } -body.compact-nav { - #compact-secondary-nav { - display: inline-block; - } - .compact-hide { - display: none; - } -} - body.small-nav { #menu-icon { display: block; @@ -240,6 +231,10 @@ body.small-nav { nav.secondary { flex-direction: column; + > ul { + height: auto; + } + .user-menu, .login-menu { width: 100%; } diff --git a/app/assets/stylesheets/errors.scss b/app/assets/stylesheets/errors.scss index fd1400232..77b440a88 100644 --- a/app/assets/stylesheets/errors.scss +++ b/app/assets/stylesheets/errors.scss @@ -1,8 +1,43 @@ -.logo { - float: left; - margin: 10px; +body { + margin: 1rem; + margin-top: 2rem; + font-family: system-ui; } -.details { - float: left; +main { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem 2rem; + max-width: 960px; + + .logo { + flex-shrink: 0; + + img { + display: block; + max-width: 100%; + height: auto; + } + } + + .details { + h1 { + margin-top: 0; + } + } +} + +@media (min-width: 640px) { + body { + margin: 2rem; + } + + main { + flex-direction: row; + + .logo { + align-self: start; + } + } } diff --git a/app/controllers/accounts/homes_controller.rb b/app/controllers/accounts/homes_controller.rb new file mode 100644 index 000000000..e31cce746 --- /dev/null +++ b/app/controllers/accounts/homes_controller.rb @@ -0,0 +1,13 @@ +module Accounts + class HomesController < ApplicationController + layout :map_layout + + before_action :authorize_web + before_action :set_locale + before_action :require_oauth + + authorize_resource :class => :account_home + + def show; end + end +end diff --git a/app/controllers/accounts/pd_declarations_controller.rb b/app/controllers/accounts/pd_declarations_controller.rb new file mode 100644 index 000000000..2d2569d62 --- /dev/null +++ b/app/controllers/accounts/pd_declarations_controller.rb @@ -0,0 +1,28 @@ +module Accounts + class PdDeclarationsController < ApplicationController + layout "site" + + before_action :authorize_web + before_action :set_locale + + authorize_resource :class => :account_pd_declaration + + def show; end + + def create + if current_user.consider_pd + flash[:warning] = t(".already_declared") + else + current_user.consider_pd = params[:consider_pd] + + if current_user.consider_pd + flash[:notice] = t(".successfully_declared") if current_user.save + else + flash[:warning] = t(".did_not_confirm") + end + end + + redirect_to edit_account_path + end + end +end diff --git a/app/controllers/accounts/terms_controller.rb b/app/controllers/accounts/terms_controller.rb index 13e9de890..03007a532 100644 --- a/app/controllers/accounts/terms_controller.rb +++ b/app/controllers/accounts/terms_controller.rb @@ -33,7 +33,6 @@ module Accounts flash[:notice] = { :partial => "accounts/terms/terms_declined_flash" } if current_user.save else unless current_user.terms_agreed? - current_user.consider_pd = params[:user][:consider_pd] current_user.tou_agreed = Time.now.utc current_user.terms_agreed = Time.now.utc current_user.terms_seen = true diff --git a/app/controllers/api/user_blocks_controller.rb b/app/controllers/api/user_blocks_controller.rb index 51f0d26d3..e1fb70a65 100644 --- a/app/controllers/api/user_blocks_controller.rb +++ b/app/controllers/api/user_blocks_controller.rb @@ -1,5 +1,8 @@ module Api class UserBlocksController < ApiController + before_action :check_api_writable, :only => :create + before_action :authorize, :only => :create + authorize_resource before_action :set_request_formats @@ -11,5 +14,33 @@ module Api rescue ActiveRecord::RecordNotFound raise OSM::APINotFoundError end + + def create + raise OSM::APIBadUserInput, "No user was given" unless params[:user] + + user = User.visible.find_by(:id => params[:user]) + raise OSM::APINotFoundError unless user + raise OSM::APIBadUserInput, "No reason was given" unless params[:reason] + raise OSM::APIBadUserInput, "No period was given" unless params[:period] + + period = Integer(params[:period], :exception => false) + raise OSM::APIBadUserInput, "Period should be a number of hours" unless period + + max_period = UserBlock::PERIODS.max + raise OSM::APIBadUserInput, "Period must be between 0 and #{max_period}" if period.negative? || period > max_period + raise OSM::APIBadUserInput, "Needs_view must be true if provided" unless params[:needs_view].nil? || params[:needs_view] == "true" + + ends_at = Time.now.utc + period.hours + needs_view = params[:needs_view] == "true" + @user_block = UserBlock.create( + :user => user, + :creator => current_user, + :reason => params[:reason], + :ends_at => ends_at, + :deactivates_at => (ends_at unless needs_view), + :needs_view => needs_view + ) + render :show + end end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index a0be87bdc..0df971bd4 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -222,8 +222,7 @@ class UsersController < ApplicationController def user_params params.require(:user).permit(:email, :display_name, :auth_provider, :auth_uid, - :pass_crypt, :pass_crypt_confirmation, - :consider_pd) + :pass_crypt, :pass_crypt_confirmation) end ## diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index ff6dcd2ff..86a3d56cd 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -43,7 +43,7 @@ module ApplicationHelper end def header_nav_link_class(path) - ["nav-link", current_page?(path) ? "text-secondary-emphasis" : "text-secondary"] + ["nav-link", current_page?(path) ? "active text-secondary-emphasis" : "text-secondary"] end def application_data diff --git a/app/views/accounts/edit.html.erb b/app/views/accounts/edit.html.erb index 7a10b12e3..5c626fc9f 100644 --- a/app/views/accounts/edit.html.erb +++ b/app/views/accounts/edit.html.erb @@ -3,7 +3,7 @@ <% end %> <% content_for :heading do %> -

<%= t ".my settings" %>

+

<%= t ".my_account" %>

<% end %> <%= render :partial => "settings_menu" %> @@ -29,14 +29,18 @@ (" target="_new"><%= t ".openid.link text" %>) -
- +
+ <% if current_user.terms_agreed? %> <%= t ".contributor terms.agreed" %> (" target="_new"><%= t ".contributor terms.link text" %>) +
<% if current_user.consider_pd? %> <%= t ".contributor terms.agreed_with_pd" %> + <% else %> + <%= t ".contributor terms.not_agreed_with_pd" %> + (<%= link_to t(".contributor terms.pd_link_text"), account_pd_declaration_path %>) <% end %> <% else %> <%= t ".contributor terms.not yet agreed" %> diff --git a/app/views/accounts/homes/show.html.erb b/app/views/accounts/homes/show.html.erb new file mode 100644 index 000000000..ea6ee7088 --- /dev/null +++ b/app/views/accounts/homes/show.html.erb @@ -0,0 +1 @@ +<% content_for(:content_class) { "overlay-sidebar" } %> diff --git a/app/views/accounts/pd_declarations/show.html.erb b/app/views/accounts/pd_declarations/show.html.erb new file mode 100644 index 000000000..ad314feaa --- /dev/null +++ b/app/views/accounts/pd_declarations/show.html.erb @@ -0,0 +1,14 @@ +<% content_for :heading do %> +

<%= t ".title" %>

+<% end %> + +<%= bootstrap_form_tag do |f| %> + <%= f.form_group :help => link_to(t(".consider_pd_why"), t(".consider_pd_why_url"), :target => :new) do %> + <%= f.check_box :consider_pd, + :label => t(".consider_pd"), + :autocomplete => :off, + :checked => current_user.consider_pd, + :disabled => current_user.consider_pd %> + <% end %> + <%= f.primary t(".confirm"), :disabled => current_user.consider_pd %> +<% end %> diff --git a/app/views/accounts/terms/show.html.erb b/app/views/accounts/terms/show.html.erb index 3cc52302f..c1c0e0a89 100644 --- a/app/views/accounts/terms/show.html.erb +++ b/app/views/accounts/terms/show.html.erb @@ -72,13 +72,4 @@ <%= submit_tag(t(".continue"), :name => "continue", :id => "continue", :disabled => true, :class => "btn btn-primary") %> <%= submit_tag(t(".cancel"), :name => "decline", :id => "decline", :class => "btn btn-outline-secondary") %>
- -
-
- <%= check_box("user", "consider_pd", :class => "form-check-input") %> - - (<%= link_to(t(".consider_pd_why"), t(".consider_pd_why_url"), :target => :new) %>) -
<% end %> diff --git a/app/views/layouts/_head.html.erb b/app/views/layouts/_head.html.erb index e6d709b27..37d830ef6 100644 --- a/app/views/layouts/_head.html.erb +++ b/app/views/layouts/_head.html.erb @@ -1,6 +1,5 @@ <%= tag.head :data => application_data do %> - - + <%= render :partial => "layouts/meta" %> <%= javascript_include_tag "turbo", :type => "module" %> <%= javascript_include_tag "application" %> <%= javascript_include_tag "i18n/#{I18n.locale}" %> @@ -11,7 +10,6 @@ <% end %> <%= stylesheet_link_tag "print-#{dir}", :media => "print" %> <%= stylesheet_link_tag "leaflet-all", :media => "screen, print" %> - <%= render :partial => "layouts/meta" %> <%= yield :head %> <%= yield :auto_discovery_link_tag %> <%= csrf_meta_tag %> diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb index 45e23fc2b..f09812972 100644 --- a/app/views/layouts/_header.html.erb +++ b/app/views/layouts/_header.html.erb @@ -27,59 +27,43 @@
-
diff --git a/app/views/layouts/_meta.html.erb b/app/views/layouts/_meta.html.erb index 4c88887f9..48be6e0aa 100644 --- a/app/views/layouts/_meta.html.erb +++ b/app/views/layouts/_meta.html.erb @@ -1,3 +1,5 @@ + + <% [57, 60, 72, 76, 114, 120, 144, 152, 180].each do |size| -%> <%= favicon_link_tag "apple-touch-icon-#{size}x#{size}.png", :rel => "apple-touch-icon", :sizes => "#{size}x#{size}", :type => "image/png" %> <% end -%> diff --git a/app/views/layouts/error.html.erb b/app/views/layouts/error.html.erb index dfcb3cb91..eab764aa3 100644 --- a/app/views/layouts/error.html.erb +++ b/app/views/layouts/error.html.erb @@ -1,5 +1,5 @@ - + OpenStreetMap @@ -7,11 +7,13 @@ <%= render :partial => "layouts/meta" %> - - <%= image_tag "osm_logo.svg", :alt => t("layouts.logo.alt_text"), :class => "logo" %> - -
- <%= yield %> -
+
+ +
+ <%= yield %> +
+
diff --git a/app/views/layouts/map.html.erb b/app/views/layouts/map.html.erb index e17ea4ed8..72f6076b4 100644 --- a/app/views/layouts/map.html.erb +++ b/app/views/layouts/map.html.erb @@ -4,18 +4,6 @@ <% content_for(:body_class) { "map-layout" } %> -<% if current_user&.home_location? %> - <% content_for :greeting do %> - <%= link_to t("layouts.home"), - "#", - :id => "homeanchor", - :class => "set_position dropdown-item", - :data => { :lat => current_user.home_lat, - :lon => current_user.home_lon, - :zoom => 15 } %> - <% end %> -<% end %> - <% content_for :header do %> <%= render :partial => "layouts/search", :locals => { :autofocus => false } %> <% end %> diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb index 37493418a..22db279d9 100644 --- a/app/views/users/new.html.erb +++ b/app/views/users/new.html.erb @@ -78,17 +78,9 @@ :contributor_terms_link => link_to(t(".by_signing_up.contributor_terms"), t(".by_signing_up.contributor_terms_url"), :target => :new)) %>

- <%= f.form_group do %> - <%= f.check_box :consider_pd, - :tabindex => 5, - :label => t(".consider_pd_html", - :consider_pd_link => link_to(t(".consider_pd"), - t(".consider_pd_url"), - :target => :new)) %> - <% end %>
- <%= submit_tag(t(".continue"), :name => "continue", :id => "continue", :class => "btn btn-primary", :tabindex => 6) %> + <%= submit_tag(t(".continue"), :name => "continue", :id => "continue", :class => "btn btn-primary", :tabindex => 5) %>
<% end %> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 8ef7b5644..a32f5fae1 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -30,7 +30,7 @@ <%= number_with_delimiter(current_user.diary_comments.size) %>
  • - <%= link_to t(".my settings"), edit_account_path %> + <%= link_to t(".my_account"), edit_account_path %>
  • <% if current_user.blocks.exists? %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 03458b00d..42669ca9e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -246,7 +246,7 @@ en: accounts: edit: title: "Edit account" - my settings: My Settings + my_account: My Account current email address: "Current Email Address" external auth: "External Authentication" openid: @@ -260,6 +260,8 @@ en: agreed_with_pd: "You have also declared that you consider your edits to be in the Public Domain." link: "https://osmfoundation.org/wiki/Licence/Contributor_Terms" link text: "what is this?" + not_agreed_with_pd: "You haven't declared that you consider your edits to be in the Public Domain." + pd_link_text: "declare" save changes button: Save Changes delete_account: Delete Account... go_public: @@ -305,9 +307,6 @@ en: read_ct: "I have read and agree to the above contributor terms" tou_explain_html: "These %{tou_link} govern the use of the website and other infrastructure provided by the OSMF. Please click on the link, read and agree to the text." read_tou: "I have read and agree to the Terms of Use" - consider_pd: "In addition to the above, I consider my contributions to be in the Public Domain" - consider_pd_why: "what's this?" - consider_pd_why_url: https://osmfoundation.org/wiki/Licence_and_Legal_FAQ/Why_would_I_want_my_contributions_to_be_public_domain guidance_info_html: "Information to help understand these terms: a %{readable_summary_link} and some %{informal_translations_link}" readable_summary: human readable summary informal_translations: informal translations @@ -325,6 +324,17 @@ en: terms_declined_html: We are sorry that you have decided to not accept the new Contributor Terms. For more information, please see %{terms_declined_link}. terms_declined_link: this wiki page terms_declined_url: https://wiki.openstreetmap.org/wiki/Contributor_Terms_Declined + pd_declarations: + show: + title: Consider my contributions to be in the Public Domain + consider_pd: "I consider my contributions to be in the Public Domain" + consider_pd_why: "Why would I want my contributions to be Public Domain?" + consider_pd_why_url: https://osmfoundation.org/wiki/Licence_and_Legal_FAQ/Why_would_I_want_my_contributions_to_be_public_domain + confirm: Confirm + create: + successfully_declared: "You have successfully declared that you consider your edits to be in the Public Domain." + already_declared: "You have already declared that you consider your edits to be in the Public Domain." + did_not_confirm: "You didn't confirm that you consider your edits to be in the Public Domain." browse: deleted_ago_by_html: "Deleted %{time_ago} by %{user}" edited_ago_by_html: "Edited %{time_ago} by %{user}" @@ -1912,7 +1922,7 @@ en: failure: Couldn't update profile. sessions: new: - tab_title: "Log in" + tab_title: "Log In" login_to_authorize_html: "Log in to OpenStreetMap to access %{client_app_name}." email or username: "Email Address or Username" password: "Password" @@ -2643,8 +2653,8 @@ en: need_to_see_terms: "Your access to the API is temporarily suspended. Please log-in to the web interface to view the Contributor Terms. You do not need to agree, but you must view them." settings_menu: account_settings: Account Settings - oauth2_applications: OAuth 2 applications - oauth2_authorizations: OAuth 2 authorizations + oauth2_applications: OAuth 2 Applications + oauth2_authorizations: OAuth 2 Authorizations muted_users: Muted Users auth_providers: openid_url: "OpenID URL" @@ -2703,6 +2713,7 @@ en: write_gpx: Upload GPS traces write_notes: Modify notes write_redactions: Redact map data + write_blocks: Create and revoke user blocks read_email: Read user email address consume_messages: Read, update status and delete user messages send_messages: Send private messages to other users @@ -2760,7 +2771,7 @@ en: users: new: title: "Sign Up" - tab_title: "Sign up" + tab_title: "Sign Up" signup_to_authorize_html: "Sign up with OpenStreetMap to access %{client_app_name}." no_auto_account_create: "Unfortunately we are not currently able to create an account for you automatically." please_contact_support_html: 'Please contact %{support_link} to arrange for an account to be created - we will try and deal with the request as quickly as possible.' @@ -2785,9 +2796,6 @@ en: privacy_policy_url: https://osmfoundation.org/wiki/Privacy_Policy privacy_policy_title: OSMF privacy policy including section on email addresses html: 'Your address is not displayed publicly, see our %{privacy_policy_link} for more information.' - consider_pd_html: "I consider my contributions to be in the %{consider_pd_link}." - consider_pd: "public domain" - consider_pd_url: https://osmfoundation.org/wiki/Licence_and_Legal_FAQ/Why_would_I_want_my_contributions_to_be_public_domain or: "or" use external auth: "or sign up with a third party" no_such_user: @@ -2802,7 +2810,7 @@ en: my notes: My Notes my messages: My Messages my profile: My Profile - my settings: My Settings + my_account: My Account my comments: My Comments my_preferences: My Preferences my_dashboard: My Dashboard @@ -2905,7 +2913,7 @@ en: index: heading_html: "%{user}'s Comments" changesets: "Changesets" - diary_entries: "Diary entries" + diary_entries: "Diary Entries" no_comments: "No comments" changeset_comments: index: @@ -3318,6 +3326,9 @@ en: show_address: Show address query_features: Query features centre_map: Centre map here + home: + marker_title: My home location + not_set: Home location is not set for your account redactions: edit: heading: "Edit Redaction" diff --git a/config/routes.rb b/config/routes.rb index 45fc19f2c..b3fc5af15 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -121,7 +121,7 @@ OpenStreetMap::Application.routes.draw do resource :subscription, :only => [:create, :destroy], :controller => "note_subscriptions" end - resources :user_blocks, :only => :show, :id => /\d+/, :controller => "user_blocks" + resources :user_blocks, :only => [:show, :create], :id => /\d+/, :controller => "user_blocks" namespace :user_blocks, :path => "user/blocks" do resource :active_list, :path => "active", :only => :show end @@ -298,7 +298,9 @@ OpenStreetMap::Application.routes.draw do resource :account, :only => [:edit, :update, :destroy] do scope :module => :accounts do resource :terms, :only => [:show, :update] + resource :pd_declaration, :only => [:show, :create] resource :deletion, :only => :show + resource :home, :only => :show end end diff --git a/config/settings/test.yml b/config/settings/test.yml index b0e2f4613..b7cffcc27 100644 --- a/config/settings/test.yml +++ b/config/settings/test.yml @@ -53,3 +53,5 @@ doorkeeper_signing_key: | cK1+/2V+OkM/0nXjxPwPj7LiOediUyZNUn48r29uGOL1S83PSUdyST207CP6mZjc K8aJmnGsVEAcWPzbpNh14q/c -----END PRIVATE KEY----- +# Override Firefox binary used in system tests +#system_test_firefox_binary: diff --git a/lib/gpx.rb b/lib/gpx.rb index 45a4dcf5f..921dce12c 100644 --- a/lib/gpx.rb +++ b/lib/gpx.rb @@ -31,6 +31,8 @@ module GPX point.altitude ||= 0 yield point @actual_points += 1 + @lats << point.latitude + @lons << point.longitude elsif reader.name == "trkseg" @tracksegs += 1 end @@ -44,6 +46,8 @@ module GPX @possible_points = 0 @actual_points = 0 @tracksegs = 0 + @lats = [] + @lons = [] begin Archive::Reader.open_filename(@file).each_entry_with_data do |entry, data| @@ -94,9 +98,9 @@ module GPX first = true - points.each_with_index do |p, pt| - px = proj.x(p.longitude) - py = proj.y(p.latitude) + @actual_points.times do |pt| + px = proj.x @lons[pt] + py = proj.y @lats[pt] if (pt >= (points_per_frame * n)) && (pt <= (points_per_frame * (n + 1))) pen.thickness = 3 @@ -151,9 +155,9 @@ module GPX first = true - points do |p| - px = proj.x(p.longitude) - py = proj.y(p.latitude) + @actual_points.times do |pt| + px = proj.x @lons[pt] + py = proj.y @lats[pt] pen.line(px, py, oldpx, oldpy) unless first diff --git a/lib/oauth.rb b/lib/oauth.rb index dfa3a8028..47edba500 100644 --- a/lib/oauth.rb +++ b/lib/oauth.rb @@ -1,11 +1,11 @@ module Oauth SCOPES = %w[ read_prefs write_prefs write_diary - write_api write_changeset_comments read_gpx write_gpx write_notes write_redactions + write_api write_changeset_comments read_gpx write_gpx write_notes write_redactions write_blocks consume_messages send_messages openid ].freeze PRIVILEGED_SCOPES = %w[read_email skip_authorization].freeze - MODERATOR_SCOPES = %w[write_redactions].freeze + MODERATOR_SCOPES = %w[write_redactions write_blocks].freeze class Scope attr_reader :name diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb index 0ddb8a87a..852f270b6 100644 --- a/test/application_system_test_case.rb +++ b/test/application_system_test_case.rb @@ -9,6 +9,7 @@ end class ApplicationSystemTestCase < ActionDispatch::SystemTestCase driven_by :selenium, :using => :headless_firefox do |options| options.add_preference("intl.accept_languages", "en") + options.binary = Settings.system_test_firefox_binary if Settings.system_test_firefox_binary end def before_setup @@ -45,4 +46,8 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase def within_content_body(&) within("#content > .content-body", &) end + + def within_content_heading(&) + within("#content > .content-heading", &) + end end diff --git a/test/controllers/accounts/pd_declarations_controller_test.rb b/test/controllers/accounts/pd_declarations_controller_test.rb new file mode 100644 index 000000000..be0d46f1e --- /dev/null +++ b/test/controllers/accounts/pd_declarations_controller_test.rb @@ -0,0 +1,92 @@ +require "test_helper" + +module Accounts + class PdDeclarationsControllerTest < ActionDispatch::IntegrationTest + ## + # test all routes which lead to this controller + def test_routes + assert_routing( + { :path => "/account/pd_declaration", :method => :get }, + { :controller => "accounts/pd_declarations", :action => "show" } + ) + assert_routing( + { :path => "/account/pd_declaration", :method => :post }, + { :controller => "accounts/pd_declarations", :action => "create" } + ) + end + + def test_show_not_logged_in + get account_pd_declaration_path + + assert_redirected_to login_path(:referer => account_pd_declaration_path) + end + + def test_show_agreed + user = create(:user) + session_for(user) + + get account_pd_declaration_path + + assert_response :success + end + + def test_create_not_logged_in + post account_pd_declaration_path + + assert_response :forbidden + end + + def test_create_unconfirmed + user = create(:user) + session_for(user) + + post account_pd_declaration_path + + assert_redirected_to edit_account_path + assert_nil flash[:notice] + assert_equal "You didn't confirm that you consider your edits to be in the Public Domain.", flash[:warning] + + user.reload + assert_not_predicate user, :consider_pd + end + + def test_create_confirmed + user = create(:user) + session_for(user) + + post account_pd_declaration_path, :params => { :consider_pd => true } + + assert_equal "You have successfully declared that you consider your edits to be in the Public Domain.", flash[:notice] + assert_nil flash[:warning] + + user.reload + assert_predicate user, :consider_pd + end + + def test_create_already_declared_unconfirmed + user = create(:user, :consider_pd => true) + session_for(user) + + post account_pd_declaration_path + + assert_nil flash[:notice] + assert_equal "You have already declared that you consider your edits to be in the Public Domain.", flash[:warning] + + user.reload + assert_predicate user, :consider_pd + end + + def test_create_already_declared_confirmed + user = create(:user, :consider_pd => true) + session_for(user) + + post account_pd_declaration_path, :params => { :consider_pd => true } + + assert_nil flash[:notice] + assert_equal "You have already declared that you consider your edits to be in the Public Domain.", flash[:warning] + + user.reload + assert_predicate user, :consider_pd + end + end +end diff --git a/test/controllers/accounts/terms_controller_test.rb b/test/controllers/accounts/terms_controller_test.rb index 768884666..55b30506b 100644 --- a/test/controllers/accounts/terms_controller_test.rb +++ b/test/controllers/accounts/terms_controller_test.rb @@ -52,13 +52,12 @@ module Accounts user = create(:user, :terms_seen => false, :terms_agreed => nil) session_for(user) - put account_terms_path, :params => { :user => { :consider_pd => true }, :read_ct => 1, :read_tou => 1 } + put account_terms_path, :params => { :read_ct => 1, :read_tou => 1 } assert_redirected_to edit_account_path assert_equal "Thanks for accepting the new contributor terms!", flash[:notice] user.reload - assert user.consider_pd assert_not_nil user.terms_agreed assert user.terms_seen end @@ -67,13 +66,12 @@ module Accounts user = create(:user, :terms_seen => false, :terms_agreed => nil) session_for(user) - put account_terms_path, :params => { :user => { :consider_pd => true }, :referer => "/test", :read_ct => 1, :read_tou => 1 } + put account_terms_path, :params => { :referer => "/test", :read_ct => 1, :read_tou => 1 } assert_redirected_to "/test" assert_equal "Thanks for accepting the new contributor terms!", flash[:notice] user.reload - assert user.consider_pd assert_not_nil user.terms_agreed assert user.terms_seen end diff --git a/test/controllers/api/user_blocks_controller_test.rb b/test/controllers/api/user_blocks_controller_test.rb index 169338811..2705e332d 100644 --- a/test/controllers/api/user_blocks_controller_test.rb +++ b/test/controllers/api/user_blocks_controller_test.rb @@ -3,6 +3,10 @@ require "test_helper" module Api class UserBlocksControllerTest < ActionDispatch::IntegrationTest def test_routes + assert_routing( + { :path => "/api/0.6/user_blocks", :method => :post }, + { :controller => "api/user_blocks", :action => "create" } + ) assert_routing( { :path => "/api/0.6/user_blocks/1", :method => :get }, { :controller => "api/user_blocks", :action => "show", :id => "1" } @@ -14,11 +18,22 @@ module Api end def test_show - block = create(:user_block) + blocked_user = create(:user) + creator_user = create(:moderator_user) + block = create(:user_block, :user => blocked_user, :creator => creator_user, :reason => "because running tests") get api_user_block_path(block) assert_response :success - assert_select "user_block[id='#{block.id}']", 1 + assert_select "osm>user_block", 1 do + assert_select ">@id", block.id.to_s + assert_select ">user", 1 + assert_select ">user>@uid", blocked_user.id.to_s + assert_select ">creator", 1 + assert_select ">creator>@uid", creator_user.id.to_s + assert_select ">revoker", 0 + assert_select ">reason", 1 + assert_select ">reason", "because running tests" + end get api_user_block_path(block, :format => "json") assert_response :success @@ -32,5 +47,165 @@ module Api assert_response :not_found assert_equal "text/plain", @response.media_type end + + def test_create_no_permission + blocked_user = create(:user) + assert_empty blocked_user.blocks + + post api_user_blocks_path(:user => blocked_user.id, :reason => "because", :period => 1) + assert_response :unauthorized + assert_empty blocked_user.blocks + + regular_creator_user = create(:user) + auth_header = bearer_authorization_header(regular_creator_user, :scopes => %w[read_prefs]) + post api_user_blocks_path(:user => blocked_user.id, :reason => "because", :period => 1), :headers => auth_header + assert_response :forbidden + assert_empty blocked_user.blocks + + auth_header = bearer_authorization_header(regular_creator_user, :scopes => %w[read_prefs write_blocks]) + post api_user_blocks_path(:user => blocked_user.id, :reason => "because", :period => 1), :headers => auth_header + assert_response :forbidden + assert_empty blocked_user.blocks + + moderator_creator_user = create(:moderator_user) + auth_header = bearer_authorization_header(moderator_creator_user, :scopes => %w[read_prefs]) + post api_user_blocks_path(:user => blocked_user.id, :reason => "because", :period => 1), :headers => auth_header + assert_response :forbidden + assert_empty blocked_user.blocks + end + + def test_create_invalid_because_no_user + blocked_user = create(:user, :deleted) + assert_empty blocked_user.blocks + + creator_user = create(:moderator_user) + auth_header = bearer_authorization_header(creator_user, :scopes => %w[read_prefs write_blocks]) + post api_user_blocks_path(:reason => "because", :period => 1), :headers => auth_header + assert_response :bad_request + assert_equal "text/plain", @response.media_type + assert_equal "No user was given", @response.body + + assert_empty blocked_user.blocks + end + + def test_create_invalid_because_user_is_unknown + creator_user = create(:moderator_user) + auth_header = bearer_authorization_header(creator_user, :scopes => %w[read_prefs write_blocks]) + post api_user_blocks_path(:user => 0, :reason => "because", :period => 1), :headers => auth_header + assert_response :not_found + assert_equal "text/plain", @response.media_type + end + + def test_create_invalid_because_user_is_deleted + blocked_user = create(:user, :deleted) + assert_empty blocked_user.blocks + + creator_user = create(:moderator_user) + auth_header = bearer_authorization_header(creator_user, :scopes => %w[read_prefs write_blocks]) + post api_user_blocks_path(:user => blocked_user.id, :reason => "because", :period => 1), :headers => auth_header + assert_response :not_found + assert_equal "text/plain", @response.media_type + + assert_empty blocked_user.blocks + end + + def test_create_invalid_because_missing_reason + create_with_params_and_assert_bad_request("No reason was given", :period => "10") + end + + def test_create_invalid_because_missing_period + create_with_params_and_assert_bad_request("No period was given", :reason => "because") + end + + def test_create_invalid_because_non_numeric_period + create_with_params_and_assert_bad_request("Period should be a number of hours", :reason => "because", :period => "one hour") + end + + def test_create_invalid_because_negative_period + create_with_params_and_assert_bad_request("Period must be between 0 and #{UserBlock::PERIODS.max}", :reason => "go away", :period => "-1") + end + + def test_create_invalid_because_excessive_period + create_with_params_and_assert_bad_request("Period must be between 0 and #{UserBlock::PERIODS.max}", :reason => "go away", :period => "10000000") + end + + def test_create_invalid_because_unknown_needs_view + create_with_params_and_assert_bad_request("Needs_view must be true if provided", :reason => "because", :period => "1", :needs_view => "maybe") + end + + def test_create_success + blocked_user = create(:user) + creator_user = create(:moderator_user) + + assert_empty blocked_user.blocks + auth_header = bearer_authorization_header(creator_user, :scopes => %w[read_prefs write_blocks]) + post api_user_blocks_path(:user => blocked_user.id, :reason => "because", :period => 1), :headers => auth_header + assert_response :success + assert_equal 1, blocked_user.blocks.length + + block = blocked_user.blocks.take + assert_predicate block, :active? + assert_equal "because", block.reason + assert_equal creator_user, block.creator + + assert_equal "application/xml", @response.media_type + assert_select "osm>user_block", 1 do + assert_select ">@id", block.id.to_s + assert_select ">@needs_view", "false" + assert_select ">user", 1 + assert_select ">user>@uid", blocked_user.id.to_s + assert_select ">creator", 1 + assert_select ">creator>@uid", creator_user.id.to_s + assert_select ">revoker", 0 + assert_select ">reason", 1 + assert_select ">reason", "because" + end + end + + def test_create_success_with_needs_view + blocked_user = create(:user) + creator_user = create(:moderator_user) + + assert_empty blocked_user.blocks + auth_header = bearer_authorization_header(creator_user, :scopes => %w[read_prefs write_blocks]) + post api_user_blocks_path(:user => blocked_user.id, :reason => "because", :period => "1", :needs_view => "true"), :headers => auth_header + assert_response :success + assert_equal 1, blocked_user.blocks.length + + block = blocked_user.blocks.take + assert_predicate block, :active? + assert_equal "because", block.reason + assert_equal creator_user, block.creator + + assert_equal "application/xml", @response.media_type + assert_select "osm>user_block", 1 do + assert_select ">@id", block.id.to_s + assert_select ">@needs_view", "true" + assert_select ">user", 1 + assert_select ">user>@uid", blocked_user.id.to_s + assert_select ">creator", 1 + assert_select ">creator>@uid", creator_user.id.to_s + assert_select ">revoker", 0 + assert_select ">reason", 1 + assert_select ">reason", "because" + end + end + + private + + def create_with_params_and_assert_bad_request(message, **params) + blocked_user = create(:user) + assert_empty blocked_user.blocks + + moderator_creator_user = create(:moderator_user) + auth_header = bearer_authorization_header(moderator_creator_user, :scopes => %w[read_prefs write_blocks]) + + post api_user_blocks_path({ :user => blocked_user.id }.merge(params)), :headers => auth_header + assert_response :bad_request + assert_equal "text/plain", @response.media_type + assert_equal message, @response.body + + assert_empty blocked_user.blocks + end end end diff --git a/test/integration/user_creation_test.rb b/test/integration/user_creation_test.rb index 1a53f62da..5d75c508d 100644 --- a/test/integration/user_creation_test.rb +++ b/test/integration/user_creation_test.rb @@ -34,8 +34,7 @@ class UserCreationTest < ActionDispatch::IntegrationTest :params => { :user => { :email => dup_email, :display_name => display_name, :pass_crypt => "testtest", - :pass_crypt_confirmation => "testtest", - :consider_pd => "1" } } + :pass_crypt_confirmation => "testtest" } } end end end @@ -57,8 +56,7 @@ class UserCreationTest < ActionDispatch::IntegrationTest :pass_crypt => "testtest", :pass_crypt_confirmation => "testtest", :auth_provider => "google", - :auth_uid => "123454321", - :consider_pd => "1" } } + :auth_uid => "123454321" } } end end end @@ -97,8 +95,7 @@ class UserCreationTest < ActionDispatch::IntegrationTest :params => { :user => { :email => email, :display_name => display_name, :pass_crypt => "testtest", - :pass_crypt_confirmation => "blahblah", - :consider_pd => "1" } } + :pass_crypt_confirmation => "blahblah" } } end end end @@ -117,8 +114,7 @@ class UserCreationTest < ActionDispatch::IntegrationTest :params => { :user => { :email => email, :display_name => dup_display_name, :auth_provider => "google", - :auth_uid => "123454321", - :consider_pd => "1" } } + :auth_uid => "123454321" } } end end end @@ -138,8 +134,7 @@ class UserCreationTest < ActionDispatch::IntegrationTest :params => { :user => { :email => new_email, :display_name => display_name, :pass_crypt => "testtest", - :pass_crypt_confirmation => "testtest", - :consider_pd => "1" } } + :pass_crypt_confirmation => "testtest" } } assert_redirected_to :controller => :confirmations, :action => :confirm, :display_name => display_name follow_redirect! end @@ -192,8 +187,7 @@ class UserCreationTest < ActionDispatch::IntegrationTest :params => { :user => { :email => new_email, :display_name => display_name, :pass_crypt => password, - :pass_crypt_confirmation => password, - :consider_pd => "1" }, + :pass_crypt_confirmation => password }, :referer => referer } assert_response(:redirect) assert_redirected_to :controller => :confirmations, :action => :confirm, :display_name => display_name @@ -254,8 +248,7 @@ class UserCreationTest < ActionDispatch::IntegrationTest :params => { :user => { :email => new_email, :display_name => display_name, :auth_provider => "openid", - :auth_uid => auth_uid, - :consider_pd => "1" } } + :auth_uid => auth_uid } } end end end @@ -330,8 +323,7 @@ class UserCreationTest < ActionDispatch::IntegrationTest :params => { :user => { :email => new_email, :display_name => display_name, :auth_provider => "openid", - :auth_uid => auth_uid, - :consider_pd => "1" } } + :auth_uid => auth_uid } } follow_redirect! end end @@ -392,8 +384,7 @@ class UserCreationTest < ActionDispatch::IntegrationTest :params => { :user => { :email => new_email, :display_name => display_name, :auth_provider => "google", - :auth_uid => auth_uid, - :consider_pd => "1" }, + :auth_uid => auth_uid }, :email_hmac => email_hmac } assert_redirected_to welcome_path follow_redirect! @@ -479,8 +470,7 @@ class UserCreationTest < ActionDispatch::IntegrationTest :email_hmac => email_hmac, :display_name => display_name, :auth_provider => "google", - :auth_uid => auth_uid, - :consider_pd => "1" } } + :auth_uid => auth_uid } } assert_response :redirect follow_redirect! end @@ -541,8 +531,7 @@ class UserCreationTest < ActionDispatch::IntegrationTest :params => { :user => { :email => new_email, :display_name => display_name, :auth_provider => "facebook", - :auth_uid => auth_uid, - :consider_pd => "1" }, + :auth_uid => auth_uid }, :email_hmac => email_hmac } assert_redirected_to welcome_path follow_redirect! @@ -628,8 +617,7 @@ class UserCreationTest < ActionDispatch::IntegrationTest :email_hmac => email_hmac, :display_name => display_name, :auth_provider => "facebook", - :auth_uid => auth_uid, - :consider_pd => "1" } } + :auth_uid => auth_uid } } assert_response :redirect follow_redirect! end @@ -689,8 +677,7 @@ class UserCreationTest < ActionDispatch::IntegrationTest :params => { :user => { :email => new_email, :display_name => display_name, :auth_provider => "microsoft", - :auth_uid => auth_uid, - :consider_pd => "1" }, + :auth_uid => auth_uid }, :email_hmac => email_hmac } assert_redirected_to welcome_path follow_redirect! @@ -775,8 +762,7 @@ class UserCreationTest < ActionDispatch::IntegrationTest :email_hmac => email_hmac, :display_name => display_name, :auth_provider => "microsoft", - :auth_uid => auth_uid, - :consider_pd => "1" } } + :auth_uid => auth_uid } } assert_response :redirect follow_redirect! end @@ -926,8 +912,7 @@ class UserCreationTest < ActionDispatch::IntegrationTest :email_hmac => email_hmac, :display_name => display_name, :auth_provider => "github", - :auth_uid => auth_uid, - :consider_pd => "1" } } + :auth_uid => auth_uid } } assert_response :redirect follow_redirect! end @@ -1076,8 +1061,7 @@ class UserCreationTest < ActionDispatch::IntegrationTest :email_hmac => email_hmac, :display_name => display_name, :auth_provider => "wikipedia", - :auth_uid => auth_uid, - :consider_pd => "1" } } + :auth_uid => auth_uid } } assert_response :redirect follow_redirect! end diff --git a/test/system/account_home_test.rb b/test/system/account_home_test.rb new file mode 100644 index 000000000..813c45ec8 --- /dev/null +++ b/test/system/account_home_test.rb @@ -0,0 +1,57 @@ +require "application_system_test_case" + +class AccountHomeTest < ApplicationSystemTestCase + test "Go to Home Location works on map layout pages" do + user = create(:user, :display_name => "test user", :home_lat => 60, :home_lon => 30) + sign_in_as(user) + + visit root_path + assert_no_selector "img.leaflet-marker-icon" + + click_on "test user" + click_on "Go to Home Location" + all "img.leaflet-marker-icon", :count => 1 do |marker| + assert_equal "My home location", marker["title"] + end + + click_on "OpenStreetMap logo" + assert_no_selector "img.leaflet-marker-icon" + end + + test "Go to Home Location works on non-map layout pages" do + user = create(:user, :display_name => "test user", :home_lat => 60, :home_lon => 30) + sign_in_as(user) + + visit about_path + assert_no_selector "img.leaflet-marker-icon" + + click_on "test user" + click_on "Go to Home Location" + all "img.leaflet-marker-icon", :count => 1 do |marker| + assert_equal "My home location", marker["title"] + end + + click_on "OpenStreetMap logo" + assert_no_selector "img.leaflet-marker-icon" + end + + test "Go to Home Location is not available for users without home location" do + user = create(:user, :display_name => "test user") + sign_in_as(user) + + visit root_path + assert_no_selector "img.leaflet-marker-icon" + + click_on "test user" + assert_no_link "Go to Home Location" + end + + test "account home page shows a warning when visited by users without home location" do + user = create(:user, :display_name => "test user") + sign_in_as(user) + + visit account_home_path + assert_no_selector "img.leaflet-marker-icon" + assert_text "Home location is not set" + end +end diff --git a/test/system/account_pd_declaration_test.rb b/test/system/account_pd_declaration_test.rb new file mode 100644 index 000000000..d58484c8c --- /dev/null +++ b/test/system/account_pd_declaration_test.rb @@ -0,0 +1,46 @@ +require "application_system_test_case" + +class AccountPdDeclarationTest < ApplicationSystemTestCase + def setup + @user = create(:user, :display_name => "test user") + sign_in_as(@user) + end + + test "can decline declaration if no declaration was made" do + visit account_pd_declaration_path + + within_content_body do + assert_unchecked_field "I consider my contributions to be in the Public Domain" + assert_button "Confirm" + + click_on "Confirm" + + assert_no_text "You have also declared that you consider your edits to be in the Public Domain." + end + end + + test "can confirm declaration if no declaration was made" do + visit account_pd_declaration_path + + within_content_body do + assert_unchecked_field "I consider my contributions to be in the Public Domain" + assert_button "Confirm" + + check "I consider my contributions to be in the Public Domain" + click_on "Confirm" + + assert_text "You have also declared that you consider your edits to be in the Public Domain." + end + end + + test "show disabled checkbox if declaration was made" do + @user.update(:consider_pd => true) + + visit account_pd_declaration_path + + within_content_body do + assert_checked_field "I consider my contributions to be in the Public Domain", :disabled => true + assert_button "Confirm", :disabled => true + end + end +end diff --git a/test/system/issues_test.rb b/test/system/issues_test.rb index e26ae89ac..ae5e114c3 100644 --- a/test/system/issues_test.rb +++ b/test/system/issues_test.rb @@ -5,7 +5,10 @@ class IssuesTest < ApplicationSystemTestCase def test_view_issues_not_logged_in visit issues_path - assert_content "Log in" + + within_content_heading do + assert_content "Log In" + end end def test_view_issues_normal_user diff --git a/test/system/user_signup_test.rb b/test/system/user_signup_test.rb index 2fb90fc3a..2d05447a6 100644 --- a/test/system/user_signup_test.rb +++ b/test/system/user_signup_test.rb @@ -79,7 +79,9 @@ class UserSignupTest < ApplicationSystemTestCase test "Sign up from login page" do visit login_path - click_on "Sign up" + within_content_heading do + click_on "Sign Up" + end within_content_body do assert_content "Confirm Password" diff --git a/test/system/user_suspension_test.rb b/test/system/user_suspension_test.rb index a72512357..d6368b68b 100644 --- a/test/system/user_suspension_test.rb +++ b/test/system/user_suspension_test.rb @@ -5,7 +5,7 @@ class UserSuspensionTest < ApplicationSystemTestCase user = create(:user) sign_in_as(user) visit edit_account_path - assert_content "My Settings" + assert_content "My Account" user.suspend! diff --git a/test/teaspoon_env.rb b/test/teaspoon_env.rb index 8a9dc001f..c14045b1c 100644 --- a/test/teaspoon_env.rb +++ b/test/teaspoon_env.rb @@ -100,10 +100,12 @@ Teaspoon.configure do |config| # Capybara Webkit: https://github.com/jejacks0n/teaspoon/wiki/Using-Capybara-Webkit require "selenium-webdriver" config.driver = :selenium + firefox_options = Selenium::WebDriver::Firefox::Options.new(:args => ["-headless"]) + firefox_options.binary = Settings.system_test_firefox_binary if Settings.system_test_firefox_binary config.driver_options = { :client_driver => :firefox, :selenium_options => { - :options => Selenium::WebDriver::Firefox::Options.new(:args => ["-headless"]) + :options => firefox_options } }