From: Tom Hughes Date: Sat, 15 Feb 2025 17:35:49 +0000 (+0000) Subject: Merge remote-tracking branch 'upstream/pull/5680' X-Git-Tag: live~151 X-Git-Url: https://git.openstreetmap.org./rails.git/commitdiff_plain/b0348093f8877d2f59f153bf35e87ba411f4004b?hp=2edceb4b613c572ca330e5bb1fcd9974f23119d5 Merge remote-tracking branch 'upstream/pull/5680' --- diff --git a/app/abilities/ability.rb b/app/abilities/ability.rb index adedce543..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, :account_pd_declaration] + can :read, [:deletion, :account_terms, :account_pd_declaration, :account_home] if Settings.status != "database_offline" can [:read, :create, :destroy], :changeset_subscription 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/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/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/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/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..1f5856908 100644 --- a/app/views/layouts/_header.html.erb +++ b/app/views/layouts/_header.html.erb @@ -27,59 +27,43 @@
-