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 %>
-
+
+
+ <%= image_tag "osm_logo.svg", :alt => t("layouts.logo.alt_text") %>
+
+
+ <%= 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
}
}