From: Andy Allan Date: Wed, 22 Nov 2023 17:13:35 +0000 (+0000) Subject: Merge pull request #4353 from tomhughes/rtlcss X-Git-Tag: live~486 X-Git-Url: https://git.openstreetmap.org/rails.git/commitdiff_plain/5e90b569db1b0f09063d1eed0aada7a513b60dcb?hp=6cd9caa4e1c1279f06e5aae2c19426af5dbef3df Merge pull request #4353 from tomhughes/rtlcss Improve handling of RTL styling --- diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index e3407c6dc..bd2f92309 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -61,7 +61,7 @@ Metrics/BlockNesting: # Offense count: 26 # Configuration parameters: CountComments, CountAsOne. Metrics/ClassLength: - Max: 286 + Max: 297 # Offense count: 59 # Configuration parameters: AllowedMethods, AllowedPatterns. diff --git a/Gemfile.lock b/Gemfile.lock index fc67f9790..885de22b4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -65,7 +65,7 @@ GEM activemodel (= 7.1.2) activesupport (= 7.1.2) timeout (>= 0.4.0) - activerecord-import (1.5.0) + activerecord-import (1.5.1) activerecord (>= 4.2) activestorage (7.1.2) actionpack (= 7.1.2) @@ -95,8 +95,8 @@ GEM autoprefixer-rails (10.4.15.0) execjs (~> 2) aws-eventstream (1.2.0) - aws-partitions (1.849.0) - aws-sdk-core (3.186.0) + aws-partitions (1.854.0) + aws-sdk-core (3.187.1) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.5) @@ -104,7 +104,7 @@ GEM aws-sdk-kms (1.72.0) aws-sdk-core (~> 3, >= 3.184.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.136.0) + aws-sdk-s3 (1.137.0) aws-sdk-core (~> 3, >= 3.181.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.6) @@ -233,7 +233,7 @@ GEM factory_bot_rails (6.2.0) factory_bot (~> 6.2.0) railties (>= 5.0.0) - faraday (2.7.11) + faraday (2.7.12) base64 faraday-net_http (>= 2.0, < 3.1) ruby2_keywords (>= 0.0.4) @@ -251,7 +251,7 @@ GEM ffi (>= 1.0.0) globalid (1.2.1) activesupport (>= 6.1) - google-protobuf (3.25.0) + google-protobuf (3.25.1) hashdiff (1.0.1) hashie (5.0.0) highline (2.1.0) @@ -288,7 +288,7 @@ GEM image_size (3.3.0) in_threads (1.6.0) io-console (0.6.0) - irb (1.9.0) + irb (1.9.1) rdoc reline (>= 0.3.8) jbuilder (2.11.5) @@ -343,8 +343,8 @@ GEM timeout net-smtp (0.4.0) net-protocol - nio4r (2.5.9) - nokogiri (1.15.4) + nio4r (2.6.1) + nokogiri (1.15.5) mini_portile2 (~> 2.8.2) racc (~> 1.4) oauth (0.4.7) @@ -402,7 +402,7 @@ GEM progress (3.6.0) psych (5.1.1.1) stringio - public_suffix (5.0.3) + public_suffix (5.0.4) puma (5.6.7) nio4r (~> 2.0) quad_tile (1.0.1) @@ -499,10 +499,11 @@ GEM rubocop-performance (1.19.1) rubocop (>= 1.7.0, < 2.0) rubocop-ast (>= 0.4.0) - rubocop-rails (2.22.1) + rubocop-rails (2.22.2) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.33.0, < 2.0) + rubocop-ast (>= 1.30.0, < 2.0) rubocop-rake (0.6.0) rubocop (~> 1.0) ruby-openid (2.9.2) diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index 63da1293f..db9721010 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -53,12 +53,16 @@ class AccountsController < ApplicationController end def destroy - current_user.soft_destroy! + if current_user.deletion_allowed? + current_user.soft_destroy! - session.delete(:user) - session_expires_automatically + session.delete(:user) + session_expires_automatically - flash[:notice] = t ".success" - redirect_to root_path + flash[:notice] = t ".success" + redirect_to root_path + else + head :bad_request + end end end diff --git a/app/controllers/api/changesets_controller.rb b/app/controllers/api/changesets_controller.rb index 9bdf0f2bd..c9c806de6 100644 --- a/app/controllers/api/changesets_controller.rb +++ b/app/controllers/api/changesets_controller.rb @@ -283,7 +283,6 @@ module Api ## # if a bounding box was specified do some sanity checks. # restrict changesets to those enclosed by a bounding box - # we need to return both the changesets and the bounding box def conditions_bbox(changesets, bbox) if bbox bbox.check_boundaries diff --git a/app/controllers/api/notes_controller.rb b/app/controllers/api/notes_controller.rb index 95466781f..e28c0a622 100644 --- a/app/controllers/api/notes_controller.rb +++ b/app/controllers/api/notes_controller.rb @@ -389,8 +389,14 @@ module Api def add_comment(note, text, event, notify: true) attributes = { :visible => true, :event => event, :body => text } - if current_user - attributes[:author_id] = current_user.id + if doorkeeper_token || current_token + author = current_user if scope_enabled?(:write_notes) + else + author = current_user + end + + if author + attributes[:author_id] = author.id else attributes[:author_ip] = request.remote_ip end diff --git a/app/controllers/changesets_controller.rb b/app/controllers/changesets_controller.rb index fef4d85eb..859242b60 100644 --- a/app/controllers/changesets_controller.rb +++ b/app/controllers/changesets_controller.rb @@ -83,7 +83,6 @@ class ChangesetsController < ApplicationController ## # if a bounding box was specified do some sanity checks. # restrict changesets to those enclosed by a bounding box - # we need to return both the changesets and the bounding box def conditions_bbox(changesets, bbox) if bbox bbox.check_boundaries diff --git a/app/models/changeset.rb b/app/models/changeset.rb index ce0943824..137de18fd 100644 --- a/app/models/changeset.rb +++ b/app/models/changeset.rb @@ -14,11 +14,12 @@ # # Indexes # -# changesets_bbox_idx (min_lat,max_lat,min_lon,max_lon) USING gist -# changesets_closed_at_idx (closed_at) -# changesets_created_at_idx (created_at) -# changesets_user_id_created_at_idx (user_id,created_at) -# changesets_user_id_id_idx (user_id,id) +# changesets_bbox_idx (min_lat,max_lat,min_lon,max_lon) USING gist +# changesets_closed_at_idx (closed_at) +# changesets_created_at_idx (created_at) +# changesets_user_id_created_at_idx (user_id,created_at) +# changesets_user_id_id_idx (user_id,id) +# index_changesets_on_user_id_and_closed_at (user_id,closed_at) # # Foreign Keys # diff --git a/app/models/user.rb b/app/models/user.rb index 7571dd9dc..1942a25cc 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -419,6 +419,18 @@ class User < ApplicationRecord end end + def deletion_allowed_at + unless Settings.user_account_deletion_delay.nil? + last_changeset = changesets.reorder(:closed_at => :desc).first + return last_changeset.closed_at.utc + Settings.user_account_deletion_delay.hours if last_changeset + end + creation_time.utc + end + + def deletion_allowed? + deletion_allowed_at <= Time.now.utc + end + private def encrypt_password diff --git a/app/views/account/deletions/show.html.erb b/app/views/account/deletions/show.html.erb index ddc821677..0ed4d663f 100644 --- a/app/views/account/deletions/show.html.erb +++ b/app/views/account/deletions/show.html.erb @@ -31,5 +31,13 @@
  • <%= t ".retain_email" %>
  • -<%= link_to t(".delete_account"), account_path, { :method => :delete, :class => "btn btn-danger", :data => { :confirm => t(".confirm_delete") } } %> +<% if current_user.deletion_allowed? %> + <%= link_to t(".delete_account"), account_path, { :method => :delete, :class => "btn btn-danger", :data => { :confirm => t(".confirm_delete") } } %> +<% else %> +
    + <%= t ".recent_editing_html", :time => friendly_date(current_user.deletion_allowed_at) %> +
    + +<% end %> + <%= link_to t(".cancel"), edit_account_path, :class => "btn btn-link" %> diff --git a/app/views/api/changesets/changeset.xml.builder b/app/views/api/changesets/changeset.xml.builder index cedfc81ea..b53061d4c 100644 --- a/app/views/api/changesets/changeset.xml.builder +++ b/app/views/api/changesets/changeset.xml.builder @@ -1,7 +1,5 @@ xml.instruct! :xml, :version => "1.0" -# basic attributes - xml.osm(OSM::API.new.xml_root_attributes) do |osm| osm << render(@changeset) end diff --git a/app/views/api/changesets/changesets.xml.builder b/app/views/api/changesets/changesets.xml.builder index c68a76da3..5ff7e2620 100644 --- a/app/views/api/changesets/changesets.xml.builder +++ b/app/views/api/changesets/changesets.xml.builder @@ -1,7 +1,5 @@ xml.instruct! :xml, :version => "1.0" -# basic attributes - xml.osm(OSM::API.new.xml_root_attributes) do |osm| @changesets.each do |changeset| osm << render(changeset) diff --git a/config/initializers/config.rb b/config/initializers/config.rb index c1cc522a5..f36e6ac1a 100644 --- a/config/initializers/config.rb +++ b/config/initializers/config.rb @@ -79,6 +79,7 @@ Config.setup do |config| required(:max_number_of_relation_members).filled(:int?) required(:max_issues_count).filled(:int?) required(:api_timeout).filled(:int?) + required(:user_account_deletion_delay).maybe(:number?) required(:imagery_blacklist).maybe(:array?) required(:status).filled(:str?, :included_in? => ALLOWED_STATUS) required(:avatar_storage).filled(:str?) diff --git a/config/locales/br.yml b/config/locales/br.yml index c3c4a5b49..f7ce20268 100644 --- a/config/locales/br.yml +++ b/config/locales/br.yml @@ -776,6 +776,7 @@ br: ranger_station: Post gward-koad recycling: Lec'h adaozañ restaurant: Preti + sanitary_dump_station: Savlec'h pompañ an dourioù lous school: Skol shelter: Gwasked shower: Strinkadenn @@ -852,6 +853,7 @@ br: roof: Toenn ruins: Savadur dismantret school: Savadur skol + semidetached_house: Ti stag service: Savadur servij shed: Lochenn stable: Marchosi @@ -863,6 +865,7 @@ br: warehouse: Sanailh "yes": Savadur club: + scout: Diazlec'h ur strollad skout sport: Kleub sport "yes": Klub craft: @@ -874,6 +877,7 @@ br: confectionery: Koñfizerezh dressmaker: Kemener electrician: Tredanour + electronics_repair: Dreser dafar tredanek gardener: Liorzhour glaziery: Gweraerezh handicraft: Artizanerezh @@ -1975,6 +1979,9 @@ br: legal_1_1_terms_of_use: Termenoù implij legal_1_1_privacy_policy: Politikerezh prevezded legal_2_1_contact_the_osmf: mont e darempred gant diazezadur OSM + legal_2_2_html: OpenStreetMap, al logo gant al loupenn hag ar State of the Map + a zo %{registered_trademarks_link}. + legal_2_2_registered_trademarks: merkoù marilhet an OSMF partners_title: Kevelerien copyright: foreign: diff --git a/config/locales/en.yml b/config/locales/en.yml index 1a41dcce8..56d722f43 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -256,6 +256,7 @@ en: retain_notes: Your map notes and note comments, if any, will be retained but hidden from view. retain_changeset_discussions: Your changeset discussions, if any, will be retained. retain_email: Your email address will be retained. + recent_editing_html: "As you have edited recently your account cannot currently be deleted. Deletion will be possible in %{time}." confirm_delete: Are you sure? cancel: Cancel accounts: diff --git a/config/locales/eo.yml b/config/locales/eo.yml index c2434fdde..259a64a7f 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -2778,12 +2778,15 @@ eo: role: administrator: Ĉi tiu uzanto estas administranto moderator: Ĉi tiu uzanto estas kontrolanto + importer: Tiu ĉi uzanto estas enportisto grant: administrator: Permesi aliron de administranto moderator: Permesi aliron de kontrolanto + importer: Permesi aliron de enportisto revoke: administrator: Nuligi aliron de administranto moderator: Nuligi aliron de kontrolanto + importer: Nuligi aliron de enportisto block_history: Blokadoj aktivaj moderator_history: Blokadoj eldonitaj comments: Komentoj diff --git a/config/locales/fr.yml b/config/locales/fr.yml index c3ff91afd..d3f258a5b 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -2109,7 +2109,7 @@ fr: si vous avez des questions sur les conditions de licence, les droits d’auteur ou d’autres questions juridiques. legal_2_1_contact_the_osmf: contacter l’OSMF legal_2_2_html: OpenStreetMap, le logo avec la loupe, ainsi que State of the - Map are %{registered_trademarks_link}. + Map sont des %{registered_trademarks_link}. legal_2_2_registered_trademarks: marques commerciales enregistrées de l’OSMF partners_title: Partenaires copyright: diff --git a/config/locales/ia.yml b/config/locales/ia.yml index 7aa59b5a1..12e502545 100644 --- a/config/locales/ia.yml +++ b/config/locales/ia.yml @@ -2810,12 +2810,15 @@ ia: role: administrator: Iste usator es un administrator moderator: Iste usator es un moderator + importer: Iste usator es un importator grant: administrator: Conceder accesso de administrator moderator: Conceder accesso de moderator + importer: Conceder le accesso de importator revoke: administrator: Revocar accesso de administrator moderator: Revocar accesso de moderator + importer: Revocar le accesso de importator block_history: Blocadas active moderator_history: Blocadas imponite comments: Commentos diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 6ae22c636..62ccce407 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -834,23 +834,30 @@ ko: gardener: 정원사 glaziery: 유리 공장 handicraft: 수공업체 + metal_construction: 금속 시공업자 painter: 화가 photographer: 사진 작가 plumber: 배관공 + roofer: 지붕 시공업자 sawmill: 제재소 shoemaker: 구두공 stonemason: 석공 tailor: 재단사 + window_construction: 창문 시공업자 winery: 포도주 양조장 "yes": 공예품점 emergency: + access_point: 비상시 구조 지점 ambulance_station: 구급 의료 센터 assembly_point: 집합 장소 defibrillator: 제세동기 + fire_extinguisher: 소화기 fire_water_pond: 방화용 연못 landing_site: 비상 착륙지 + life_ring: 구명부표 phone: 긴급 전화 siren: 비상 사이렌 + suction_point: 소방용 수원지 water_tank: 긴급 물탱크 highway: abandoned: 버려진 고속도로 @@ -863,6 +870,7 @@ ko: cycleway: 자전거 전용도로 elevator: 엘리베이터 emergency_access_point: 긴급 액세스 포인트 + emergency_bay: 비상주차대 footway: 보도 ford: 여울 give_way: 양보 표지 @@ -895,6 +903,7 @@ ko: track: 오솔길 traffic_mirror: 도로반사경 traffic_signals: 교통 신호 + trailhead: 산책로 기점 trunk: 간선 도로 trunk_link: 간선 도로 turning_loop: 방향전환 운전용 루프선 @@ -1060,6 +1069,7 @@ ko: "yes": 산길 natural: atoll: 환초 + bare_rock: 노출 암반 bay: 만 beach: 해변 cape: 곶 @@ -1078,6 +1088,7 @@ ko: hill: 언덕 hot_spring: 온천 island: 섬 + isthmus: 지협 land: 토지 marsh: 습지 moor: 습지 @@ -1145,6 +1156,7 @@ ko: locality: 지역 municipality: 지방자치체 neighbourhood: 마을 + plot: 대지 postcode: 우편 번호 quarter: 구역 region: 지역 @@ -1158,6 +1170,7 @@ ko: "yes": 장소 railway: abandoned: 폐선된 철도 + buffer_stop: 철도 차막이 construction: 건설 중인 철도 disused: 폐선된 철도 funicular: 케이블 카 @@ -1193,6 +1206,7 @@ ko: bakery: 제과점 bathroom_furnishing: 욕실 설치 업체 beauty: 미용실 + bed: 침구점 beverages: 음료 가게 bicycle: 자전거 가게 bookmaker: 마권업자 @@ -1306,6 +1320,7 @@ ko: attraction: 관광 명소 bed_and_breakfast: 민박 cabin: 여행자 오두막 + camp_pitch: 캠프장 피치 camp_site: 캠프장 caravan_site: 캐러밴 사이트 chalet: 샬렛 @@ -1846,14 +1861,12 @@ ko: legal_title: 법률 legal_1_1_html: |- 본 사이트와 기타 관련 서비스의 운영은 커뮤니티의 위탁으로 - %{openstreetmap_foundation_link}(OSMF)가 공식적으로 맡고 있습니다. OSMF가 운영하는 모든 서비스의 이용 시 %{terms_of_use_link}, %{aup_link}, %{privacy_policy_link}가 적용됩니다. + %{openstreetmap_foundation_link}(OSMF)가 공식적으로 맡고 있습니다. OSMF가 운영하는 모든 서비스의 이용 시 %{terms_of_use_link}, %{aup_link}, %{privacy_policy_link}이 적용됩니다. legal_1_1_openstreetmap_foundation: 오픈스트리트맵 재단 legal_1_1_terms_of_use: 이용 약관 legal_1_1_aup: 사용 범위 정책 legal_1_1_privacy_policy: 개인정보처리방침 - legal_2_1_html: |- - 라이선스, 저작권 또는 그 밖의 법무 관련 질문이 있는 경우 %{contact_the_osmf_link} - 으로 문의하세요. + legal_2_1_html: 라이선스, 저작권 또는 그 밖의 법무 관련 질문이 있는 경우 %{contact_the_osmf_link}. legal_2_1_contact_the_osmf: OSMF에 문의하세요 legal_2_2_html: 오픈스트리트맵 (OpenStreetMap)과 지도 위 돋보기 로고는 %{registered_trademarks_link}입니다. legal_2_2_registered_trademarks: OSMF의 등록 상표 @@ -2512,12 +2525,13 @@ ko: email_confirmation_help_html: 당신의 주소는 공개적으로 노출되지 않습니다. 자세한 내용은 %{privacy_policy_link}를 참조하세요. privacy_policy: 개인정보처리방침 + privacy_policy_title: 이메일 주소 섹션을 포함한 OSMF 개인 정보 보호 정책 terms: title: 약관 heading: 약관 heading_ct: 기여자 약관 read and accept with tou: 기여자 동의 및 사용 약관을 읽고 두 개의 체크 상자에 체크한 다음 계속 버튼을 눌러 주십시오. - contributor_terms_explain: 이 동의는 기존 기여와 앞으로의 기여자의 약관에 적용됩니다. + contributor_terms_explain: 본 동의는 기존 기여분과 추후 기여분의 이용 약관에 적용됩니다. read_ct: 상기의 기여자 약관을 읽었고 동의합니다 tou_explain_html: 이러한 %{tou_link}는 오픈스트리트맵 재단에서 제공하는 웹사이트 및 기타 인프라 사용을 관리합니다. 링크를 클릭하여 텍스트를 읽고 동의하십시오. @@ -2525,6 +2539,10 @@ ko: consider_pd: 위의 내용 외에도 내 기여가 퍼블릭 도메인에 있다고 간주합니다 consider_pd_why: 무엇인가요? consider_pd_why_url: https://wiki.osmfoundation.org/wiki/Licence_and_Legal_FAQ/Why_would_I_want_my_contributions_to_be_public_domain + guidance_info_html: '이러한 용어를 이해하는 데 도움이 되는 정보: %{readable_summary_link} / 일부 + %{informal_translations_link}' + readable_summary: 해석 요약본 + informal_translations: 비공식 번역 continue: 계속 declined: https://wiki.openstreetmap.org/wiki/Contributor_Terms_Declined decline: 거부 @@ -2577,18 +2595,22 @@ ko: role: administrator: 이 사용자는 관리자입니다 moderator: 이 사용자는 운영자입니다 + importer: 이 사용자는 임포터입니다 grant: administrator: 관리자 권한 부여 moderator: 운영자 권한 부여 + importer: 임포터 접근 권한 부여 revoke: administrator: 관리자 권한 해제 moderator: 운영자 권한 해제 + importer: 임포터 접근 권한 취소 block_history: 활성화된 차단 moderator_history: 실행된 차단 comments: 의견 create_block: 이 사용자를 차단 activate_user: 이 사용자 활성화 confirm_user: 이 사용자 확인 + unconfirm_user: 이 사용자 확인 취소 unsuspend_user: 이 사용자 정지 해제 hide_user: 이 사용자를 숨기기 unhide_user: 이 사용자를 숨기기 취소 diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml index fdb3db46c..d9534088a 100644 --- a/config/locales/pt-PT.yml +++ b/config/locales/pt-PT.yml @@ -29,6 +29,7 @@ # Author: Malafaya # Author: Mansil # Author: Mansil alfalb +# Author: Matheusgomesms # Author: McDutchie # Author: MokaAkashiyaPT # Author: Nemo bis @@ -268,6 +269,7 @@ pt-PT: reopened_at_by_html: Reaberto %{when} por %{user} rss: title: Notas OpenStreetMap + description_all: Uma lista de notas denunciadas, comentadas ou fechadas. description_area: Lista de notas reportadas, comentadas ou resolvidas na tua área [(%{min_lat}|%{min_lon}) -- (%{max_lat}|%{max_lon})] description_item: Um feed RSS para a nota %{id} reportada @@ -624,10 +626,39 @@ pt-PT: newer_comments: Comentários mais recentes older_comments: Comentários mais antigos doorkeeper: + errors: + messages: + account_selection_required: O servidor de autorização requer seleção de conta + de utilizador final + consent_required: O servidor de autorização requer o consentimento do utilizador + final + interaction_required: O servidor de autorização requer interação com o utilizador + final + login_required: O servidor de autorização requer autenticação do utilizador + final flash: applications: create: notice: Aplicação registada. + openid_connect: + errors: + messages: + auth_time_from_resource_owner_not_configured: Falha devido a ausência de + configuração de Doorkeeper::OpenidConnect.configure.auth_time_from_resource_owner. + reauthenticate_resource_owner_not_configured: Falha devido a ausência de + configuração de Doorkeeper::OpenidConnect.configure.reauthenticate_resource_owner. + resource_owner_from_access_token_not_configured: Falha devido a ausência + de configuração de Doorkeeper::OpenidConnect.configure.resource_owner_from_access_token. + select_account_for_resource_owner_not_configured: Falha devido a ausência + de configuração de Doorkeeper::OpenidConnect.configure.select_account_for_resource_owner. + subject_not_configured: Falha na geração de token ID devido a ausência de + configuração de Doorkeeper::OpenidConnect.configure.subject. + scopes: + address: Ver o teu endereço físico + email: Ver o teu endereço eletrónico + openid: Autenticar a tua conta + phone: Ver o teu número de telefone + profile: Ver a tua informação de perfil errors: contact: contact_url_title: Vários canais de contacto explicados @@ -2078,6 +2109,11 @@ pt-PT: de Recursos Naturais do Canadá) e StatCan (Divisão de Geografia, Estatísticas do Canadá).' contributors_ca_canada: Canadá + contributors_cz_credit_html: '%{czechia}: contém dados da Administração Estatal + de Topografia e Cadastro sob licença %{cc_licence_link}' + contributors_cz_czechia: Chéquia + contributors_cz_cc_licence: Atribuição Creative Commons 4.0 Licença Internacional + (CC BY 4.0) contributors_fi_credit_html: '%{finland}: Contém dados do Serviço Topográfico Nacional da Base de Dados Topográfica da Finlândia e outros conjuntos de dados, sob a %{nlsfi_license_link}.' @@ -2597,6 +2633,7 @@ pt-PT: permissions: missing: Não deste permissão à aplicação para aceder a este serviço scopes: + openid: Iniciar sessão com OpenStreetMap read_prefs: Ler preferências de utilizador write_prefs: Modificar preferências de utilizador write_diary: Criar entradas de diário, comentar e fazer amigos @@ -2791,6 +2828,7 @@ pt-PT: remove as friend: Remover amigo add as friend: Adicionar aos amigos mapper since: 'A mapear desde:' + uid: 'Id de utilizador:' ct status: 'Termos do Contribuidor:' ct undecided: Por decidir ct declined: Rejeitado @@ -2801,12 +2839,15 @@ pt-PT: role: administrator: Este utilizador é um administrador moderator: Este utilizador é um moderador + importer: Este utilizador é importador grant: administrator: Dar acesso de administrador moderator: Dar acesso de moderador + importer: Conceder acesso de importador revoke: administrator: Retirar acesso de administrador moderator: Retirar acesso de moderador + importer: Revogar acesso de importador block_history: Bloqueios ativos moderator_history: Bloqueios aplicados comments: Comentários @@ -3075,6 +3116,7 @@ pt-PT: cyclosm: CyclOSM cycle_map: Mapa de Ciclismo transport_map: Transportes Públicos + tracestracktop_topo: Tracestrack Topo hot: Humanitário opnvkarte: ÖPNVKarte layers: @@ -3093,6 +3135,7 @@ pt-PT: andy_allan: Andy Allan opnvkarte_credit: Mosaicos cortesia de %{memomaps_link} memomaps: MeMoMaps + tracestrack_credit: Mosaicos cortesia de %{tracestrack_link} hotosm_credit: Estilo de mosaicos por %{hotosm_link} hospedado por %{osm_france_link} hotosm_name: Equipa Humanitária do OpenStreetMap site: @@ -3122,7 +3165,7 @@ pt-PT: graphhopper_car: Carro (GraphHopper) graphhopper_foot: A pé (GraphHopper) fossgis_valhalla_bicycle: Bicicleta (Valhalla) - fossgis_valhalla_car: Automóvel (Valhalla) + fossgis_valhalla_car: Carro (Valhalla) fossgis_valhalla_foot: Pé (Valhalla) descend: Descida directions: Direções diff --git a/config/locales/pt.yml b/config/locales/pt.yml index 5a46056f7..cc27269ba 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -636,11 +636,35 @@ pt: newer_comments: Comentários mais recentes older_comments: Comentários mais antigos doorkeeper: + errors: + messages: + account_selection_required: O servidor de autorização requer a seleção da + conta do usuário final + consent_required: O servidor de autorização requer o consentimento do usuário + final + interaction_required: O servidor de autorização requer interação do usuário + final + login_required: O servidor de autorização requer a autenticação do usuário + final flash: applications: create: notice: Aplicação registada. + openid_connect: + errors: + messages: + auth_time_from_resource_owner_not_configured: Falha devido a falta de configuração + de Doorkeeper::OpenidConnect.configure.auth_time_from_resource_owner. + reauthenticate_resource_owner_not_configured: Falha devido a falta de configuração + de Doorkeeper::OpenidConnect.configure.reauthenticate_resource_owner. + resource_owner_from_access_token_not_configured: Falha devido a falta de + configuração de Doorkeeper::OpenidConnect.configure.resource_owner_from_access_token. + select_account_for_resource_owner_not_configured: Falha devido a falta de + configuração de Doorkeeper::OpenidConnect.configure.select_account_for_resource_owner. + subject_not_configured: A geração de tokens de identificação falhou devido + a falta de configuração de Doorkeeper::OpenidConnect.configure.subject. scopes: + address: Ver seu endereço físico email: Ver seu endereço de e-mail openid: Autenticar sua conta phone: Ver seu número de telefone @@ -2102,6 +2126,12 @@ pt: de Recursos Naturais do Canadá) e StatCan (Divisão de Geografia, Estatísticas do Canadá).' contributors_ca_canada: Canadá + contributors_cz_credit_html: |- + %{czechia}: Contém dados da Administração Estatal de Agrimensura + e Cadastro licenciados sob %{cc_licence_link} + contributors_cz_czechia: Tcheca + contributors_cz_cc_licence: Atribuição Creative Commons 4.0 Licença Internacional + (CC BY 4.0) contributors_fi_credit_html: '%{finland}: Contém dados do Serviço Topográfico Nacional da Base de Dados Topográfica da Finlândia e outros conjuntos de dados, sob a %{nlsfi_license_link}.' @@ -2626,6 +2656,7 @@ pt: permissions: missing: Você não permitiu o acesso da aplicação a esta facilidade scopes: + openid: Iniciar sessão usando OpenStreetMap read_prefs: Ler preferências de usuário write_prefs: Modificar preferências de usuário write_diary: Criar entradas de diário, comentar e fazer amigos @@ -2827,12 +2858,15 @@ pt: role: administrator: Este usuário é um administrador moderator: Este usuário é um moderador + importer: Este usuário é importador grant: administrator: Conceder acesso de administrador moderator: Conceder acesso de moderador + importer: Conceder o acesso de importador revoke: administrator: Revogar acesso de administrador moderator: Revogar acesso de moderador + importer: Revogar o acesso de importador block_history: Bloqueios ativos moderator_history: Bloqueios aplicados comments: Comentários @@ -3139,7 +3173,7 @@ pt: edit_help: Mover o mapa e ampliar uma localização que pretende editar e clique aqui. directions: - ascend: Ascender + ascend: Ascenção engines: fossgis_osrm_bike: Bicicleta (OSRM) fossgis_osrm_car: Carro (OSRM) @@ -3149,8 +3183,8 @@ pt: graphhopper_foot: A pé (GraphHopper) fossgis_valhalla_bicycle: Bicicleta (Valhalla) fossgis_valhalla_car: Carro (Valhalla) - fossgis_valhalla_foot: Pé (Valhalla) - descend: Descender + fossgis_valhalla_foot: A pé (Valhalla) + descend: Descida directions: Itinerário distance: Distância distance_m: '%{distance}m' @@ -3159,63 +3193,63 @@ pt: no_route: Rota entre esses dois lugares não encontrada. no_place: Desculpe - não foi possível encontrar '%{place}'. instructions: - continue_without_exit: Continuar em %{name} + continue_without_exit: Continue em %{name} slight_right_without_exit: Curva suave à direita para %{name} - offramp_right: Conduza até a rampa do lado direito - offramp_right_with_exit: Pegue a saída %{exit} a direita - offramp_right_with_exit_name: Pegue a saída %{exit} à direita na %{name} - offramp_right_with_exit_directions: Pegue a saída %{exit} à direita para %{directions} - offramp_right_with_exit_name_directions: Pegue a saída %{exit} à direita na - %{name}, em direção %{directions} - offramp_right_with_name: Pegue a via de acesso à direita na %{name} - offramp_right_with_directions: Conduza até a rampa do lado direito em direção + offramp_right: Pegue a rampa à direita + offramp_right_with_exit: Pegue a saída %{exit} à direita + offramp_right_with_exit_name: Pegue a saída %{exit} à direita para %{name} + offramp_right_with_exit_directions: Pegue a saída %{exit} à direita em direção a %{directions} - offramp_right_with_name_directions: Conduza até a rampa do lado direito para + offramp_right_with_exit_name_directions: Pegue a saída %{exit} à direita para %{name}, em direção a %{directions} - onramp_right_without_exit: Vire à direita, na via de acesso, na %{name} + offramp_right_with_name: Pegue a rampa à direita para %{name} + offramp_right_with_directions: Pegue a rampa à direita em direção a %{directions} + offramp_right_with_name_directions: Pegue a rampa à direita para %{name}, + em direção a %{directions} + onramp_right_without_exit: Vire à direita na rampa para %{name} onramp_right_with_directions: Vire à direita na rampa em direção a %{directions} onramp_right_with_name_directions: Vire à direita na rampa para %{name}, em direção a %{directions} - onramp_right_without_directions: Vire à direita na rampa + onramp_right_without_directions: Vire à direita para a rampa onramp_right: Vire à direita para a rampa - endofroad_right_without_exit: No fim da estrada, vire à direita na %{name} - merge_right_without_exit: Entre à direita na %{name} - fork_right_without_exit: Na bifurcação, vire à direita na %{name} + endofroad_right_without_exit: No fim da estrada, vire à direita para %{name} + merge_right_without_exit: Entre à direita para %{name} + fork_right_without_exit: Na bifurcação, vire à direita para %{name} turn_right_without_exit: Vire à direita para %{name} sharp_right_without_exit: Curva acentuada à direita para %{name} uturn_without_exit: Retorno em %{name} sharp_left_without_exit: Curva acentuada à esquerda para %{name} turn_left_without_exit: Vire à esquerda para %{name} - offramp_left: Conduza até a rampa do lado esquerdo + offramp_left: Pegue a rampa à esquerda offramp_left_with_exit: Pegue a saída %{exit} à esquerda - offramp_left_with_exit_name: Pegue a saída %{exit} à esquerda na %{name} - offramp_left_with_exit_directions: Pegue a saída %{exit} à esquerda para %{directions} - offramp_left_with_exit_name_directions: Pegue a saída %{exit} à esquerda na - %{name}, em direção %{directions} - offramp_left_with_name: Pegue a via de acesso à esquerda na %{name} - offramp_left_with_directions: Conduza até a rampa do lado esquerdo em direção + offramp_left_with_exit_name: Pegue a saída %{exit} à esquerda para %{name} + offramp_left_with_exit_directions: Pegue a saída %{exit} à esquerda em direção a %{directions} - offramp_left_with_name_directions: Conduza até a tampa do lado esquerdo para + offramp_left_with_exit_name_directions: Pegue a saída %{exit} à esquerda para %{name}, em direção a %{directions} - onramp_left_without_exit: Vire à esquerda, na via de acesso, na %{name} + offramp_left_with_name: Pegue a rampa à esquerda para %{name} + offramp_left_with_directions: Pegue a rampa à esquerda em direção a %{directions} + offramp_left_with_name_directions: Pegue a rampa à esquerda para %{name}, + em direção a %{directions} + onramp_left_without_exit: Vire à esquerda na rampa para %{name} onramp_left_with_directions: Vire à esquerda na rampa em direção a %{directions} onramp_left_with_name_directions: Vire à esquerda na rampa para %{name}, em direção a %{directions} - onramp_left_without_directions: Vire à esquerda na rampa + onramp_left_without_directions: Vire à esquerda para a rampa onramp_left: Vire à esquerda para a rampa - endofroad_left_without_exit: No fim da estrada, vire à esquerda na %{name} - merge_left_without_exit: Entre à esquerda na %{name} - fork_left_without_exit: Na bifurcação, vire à esquerda na %{name} - slight_left_without_exit: Esquerda suave para %{name} - via_point_without_exit: (ponto de passagem) + endofroad_left_without_exit: No fim da estrada, vire à esquerda para %{name} + merge_left_without_exit: Entre à esquerda para %{name} + fork_left_without_exit: Na bifurcação, vire à esquerda para %{name} + slight_left_without_exit: Curva suave à esquerda para %{name} + via_point_without_exit: (ponto intermediário) follow_without_exit: Siga %{name} roundabout_without_exit: Na rotatória, pegue a saída para %{name} leave_roundabout_without_exit: Saia da rotatória - %{name} stay_roundabout_without_exit: Mantenha-se na rotatória - %{name} start_without_exit: Comece em %{name} - destination_without_exit: Chegue ao destino - against_oneway_without_exit: Vá contra o sentido da mão única em %{name} - end_oneway_without_exit: Final de mão única em %{name} + destination_without_exit: Chegada ao destino + against_oneway_without_exit: Vá na contra-mão em %{name} + end_oneway_without_exit: Fim de mão única em %{name} roundabout_with_exit: Na rotatória, pegue a saída %{exit} para %{name} roundabout_with_exit_ordinal: Na rotatória, pegue %{exit} saída para %{name} exit_roundabout: Saia da rotatória para %{name} @@ -3241,8 +3275,8 @@ pt: error: 'Erro ao contatar %{server}: %{error}' timeout: Tempo esgotado com %{server} context: - directions_from: Início da rota - directions_to: Destino da rota + directions_from: Início de rota a partir daqui + directions_to: Fim de rota até daqui add_note: Adicionar uma nota aqui show_address: Mostrar endereço query_features: Consultar elementos @@ -3254,9 +3288,9 @@ pt: index: empty: Nenhuma anulação para mostrar. heading: Lista de anulações - title: Lista de redações + title: Lista de anulações new: - heading: Digite informações para a nova anulação + heading: Introduza a informação da nova anulação title: Criando uma nova anulação show: description: 'Descrição:' @@ -3264,7 +3298,7 @@ pt: title: Exibindo anulação user: 'Criador:' edit: Editar esta anulação - destroy: Remover esta redação + destroy: Remover esta anulação confirm: Tem certeza? create: flash: Anulação criada. @@ -3273,8 +3307,8 @@ pt: destroy: not_empty: A anulação não está vazia. Desanule todas as versões pertencentes a esta anulação antes de destruí-la. - flash: Redação destruída. - error: Houve um erro ao destruir esta anulação. + flash: Anulação eliminada. + error: Houve um erro ao tentar eliminar esta anulação. validations: leading_whitespace: tem espaço em branco principal trailing_whitespace: tem espaço em branco à direita diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 00db6a8de..55c84d533 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -901,7 +901,7 @@ ru: college: Здание колледжа commercial: Офисное здание construction: Строящееся здание - detached: Особняк + detached: Отдельностоящий жилой дом dormitory: Общежитие duplex: Дуплекс farm: Ферма @@ -921,7 +921,7 @@ ru: office: Офисное здание public: Общественное здание residential: Жилой дом - retail: Здание на продажу + retail: Торговое здание roof: Крыша ruins: Разрушенное здание school: Здание школы @@ -931,7 +931,7 @@ ru: stable: Конюшня static_caravan: Передвижной дом temple: Здание храма - terrace: Здание с террасой + terrace: Ряд домов train_station: Железнодорожный вокзал university: Университет warehouse: Склад diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 04f5d2797..791d63e93 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -346,7 +346,12 @@ sv: kan inte skicka meddelanden till dig eller se din plats. För att visa vad du redigerade och tillåta andra att kontakta dig via webbplatsen, klicka på knappen nedan. + only_public_can_edit: Sedan övergången till 0.6-API:n kan bara offentliga användare + redigera kartdata. find_out_why: ta reda på varför + email_not_revealed: Din e-postadress kommer inte avslöjas när du blir offentlig. + not_reversible: Denna åtgärd kan inte ångras och alla nya användare är nu offentliga + som standard. make_edits_public_button: Gör alla mina redigeringar offentliga update: success_confirm_needed: Användarinformation uppdaterades. Kontrollera din e-post @@ -654,6 +659,8 @@ sv: contact: contact_url_title: Olika kontaktkanaler förklaras contact: kontakta + contact_the_community_html: Gå gärna till %{contact_link} OpenStreetMap-gemenskapen + om du har hittat en trasig länk/bugg. Anteckna din förfrågas exakta webbadress. forbidden: title: Förbjudet description: Åtgärden du begärde på OpenStreetMap-servern är endast tillgänglig @@ -2031,10 +2038,18 @@ sv: OpenStreetMap%{registered_trademark_link} är %{open_data}, licensierad under %{odc_odbl_link} (ODbL) av %{osm_foundation_link} (OSMF). introduction_1_open_data: öppna data + introduction_1_odc_odbl: Open Data Commons Open Database License introduction_1_osm_foundation: OpenStreetMap-stiftelsen + introduction_2_html: |- + Du är fri att kopiera, distribuera, överföra och anpassa vår data, + så länge du anger OpenStreetMap och dess bidragsgivare som källa. + Om du ändrar eller bygger vidare på vår data kan du + endast distribuera resultatet under samma licens. Den + fullständiga %{legal_code_link} förklarar dina rättigheter och skyldigheter. introduction_2_legal_code: juridiska texten introduction_3_html: Vår dokumentation är licensierad under licensen %{creative_commons_link} (CC BY-SA 2.0). + introduction_3_creative_commons: Creative Commons Erkännande-DelaLika 2.0 credit_title_html: Hur du anger OpenStreetMap som källa credit_1_html: 'När du använder OpenStreetMap-data, måste du göra dessa två saker:' @@ -2045,6 +2060,7 @@ sv: regler för hur upphovsrättsmeddelandet ska visas beroende på om du har skapat en bläddringsbar karta, en utskriven karta eller en statisk bild. Fullständig information om -kraven finns i %{attribution_guidelines_link}. + credit_3_attribution_guidelines: Riktlinjer för tillskrivning credit_4_1_html: "För att tydliggöra att data är tillgänglig under Öppen\nDatabase Licensen kan du länka till %{this_copyright_page_link}.\nAlternativt, och som ett krav om du distribuerar OSM i en\ndataform kan du namn och länk @@ -2076,7 +2092,9 @@ sv: contributors_at_stadt_wien: Staden Wien contributors_at_cc_by: CC BY contributors_at_land_vorarlberg: Förbundsland Vorarlberg + contributors_at_cc_by_at_with_amendments: CC BY AT med tillägg contributors_au_australia: Australien + contributors_au_geoscape_australia: Geoscape Australien contributors_ca_canada: Kanada contributors_cz_czechia: Tjeckien contributors_fi_finland: Finland @@ -2166,6 +2184,7 @@ sv: embeddable_html: Inbäddad HTML licence: Licens licence_details_html: OpenStreetMap-data licensieras under %{odbl_link} (ODbL). + odbl: Open Data Commons Open Database License too_large: advice: 'Om ovanstående export misslyckas, vänligen överväg att använda en av de källor som anges nedan:' @@ -2389,6 +2408,7 @@ sv: editor: redigerare node: nod way: sträcka + tag: tagg rules: title: Regler! para_1_html: |- @@ -2785,12 +2805,15 @@ sv: role: administrator: Den här användaren är en administratör moderator: Den här användaren är en moderator + importer: Denna användare är en importör grant: administrator: Tilldela administratörsrättigheter moderator: Tilldela moderatorrättigheter + importer: Ge importåtkomst revoke: administrator: Återkalla administratörsrättigheter moderator: Återkalla moderatorrättigheter + importer: Återkalla importåtkomst block_history: Aktiva blockeringar moderator_history: Utdelade blockeringar comments: Kommentarer diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index ee0960e94..e6be76e3d 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -2602,12 +2602,15 @@ zh-TW: role: administrator: 這個使用者是一個管理員 moderator: 這個使用者是一個仲裁員 + importer: 此名使用者是匯入者 grant: administrator: 授予管理員權限 moderator: 授予仲裁員權限 + importer: 授予匯入權限 revoke: administrator: 撤銷管理員權限 moderator: 撤銷仲裁員權限 + importer: 撤銷匯入權限 block_history: 已封鎖 moderator_history: 給予封鎖 comments: 評論 diff --git a/config/settings.yml b/config/settings.yml index 87c467c88..1c9c7e0a1 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -53,6 +53,8 @@ api_timeout: 300 web_timeout: 30 # Periods (in hours) which are allowed for user blocks user_block_periods: [0, 1, 3, 6, 12, 24, 48, 96, 168, 336, 731, 4383, 8766, 87660] +# Account deletion cooldown period (in hours) since last changeset close; null to disable, 0 to make sure there aren't any open changesets when the deletion happens +user_account_deletion_delay: null # Rate limit for message sending max_messages_per_hour: 60 # Rate limit for friending diff --git a/db/migrate/20231117170422_add_closed_at_index_to_changesets.rb b/db/migrate/20231117170422_add_closed_at_index_to_changesets.rb new file mode 100644 index 000000000..e9d7e62f7 --- /dev/null +++ b/db/migrate/20231117170422_add_closed_at_index_to_changesets.rb @@ -0,0 +1,7 @@ +class AddClosedAtIndexToChangesets < ActiveRecord::Migration[7.1] + disable_ddl_transaction! + + def change + add_index :changesets, [:user_id, :closed_at], :algorithm => :concurrently + end +end diff --git a/db/structure.sql b/db/structure.sql index 56e778523..f74d4d571 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -2499,6 +2499,13 @@ CREATE INDEX index_changeset_comments_on_changeset_id_and_created_at ON public.c CREATE INDEX index_changeset_comments_on_created_at ON public.changeset_comments USING btree (created_at); +-- +-- Name: index_changesets_on_user_id_and_closed_at; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_changesets_on_user_id_and_closed_at ON public.changesets USING btree (user_id, closed_at); + + -- -- Name: index_changesets_subscribers_on_changeset_id; Type: INDEX; Schema: public; Owner: - -- @@ -3499,6 +3506,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('23'), ('22'), ('21'), +('20231117170422'), ('20231101222146'), ('20231029151516'), ('20231010194809'), diff --git a/test/controllers/accounts_controller_test.rb b/test/controllers/accounts_controller_test.rb index 7546c3797..131292f41 100644 --- a/test/controllers/accounts_controller_test.rb +++ b/test/controllers/accounts_controller_test.rb @@ -152,4 +152,23 @@ class AccountsControllerTest < ActionDispatch::IntegrationTest # Make sure we have a button to "go public" assert_select "form.button_to[action='/user/go_public']", true end + + def test_destroy_allowed + user = create(:user) + session_for(user) + + delete account_path + assert_response :redirect + end + + def test_destroy_not_allowed + with_user_account_deletion_delay(24) do + user = create(:user) + create(:changeset, :user => user, :created_at => Time.now.utc) + session_for(user) + + delete account_path + assert_response :bad_request + end + end end diff --git a/test/models/user_test.rb b/test/models/user_test.rb index a4ed07e09..5c48bb969 100644 --- a/test/models/user_test.rb +++ b/test/models/user_test.rb @@ -282,4 +282,62 @@ class UserTest < ActiveSupport::TestCase oauth_access_token.reload assert_predicate oauth_access_token, :revoked? end + + def test_deletion_allowed_when_no_changesets + with_user_account_deletion_delay(10000) do + user = create(:user) + assert_predicate user, :deletion_allowed? + end + end + + def test_deletion_allowed_without_delay + with_user_account_deletion_delay(nil) do + user = create(:user) + create(:changeset, :user => user) + user.reload + assert_predicate user, :deletion_allowed? + end + end + + def test_deletion_allowed_past_delay + with_user_account_deletion_delay(10) do + user = create(:user) + create(:changeset, :user => user, :created_at => Time.now.utc - 12.hours, :closed_at => Time.now.utc - 10.hours) + user.reload + assert_predicate user, :deletion_allowed? + end + end + + def test_deletion_allowed_during_delay + with_user_account_deletion_delay(10) do + user = create(:user) + create(:changeset, :user => user, :created_at => Time.now.utc - 11.hours, :closed_at => Time.now.utc - 9.hours) + user.reload + assert_not_predicate user, :deletion_allowed? + assert_equal Time.now.utc + 1.hour, user.deletion_allowed_at + end + end + + def test_deletion_allowed_past_zero_delay + with_user_account_deletion_delay(0) do + user = create(:user) + create(:changeset, :user => user, :created_at => Time.now.utc, :closed_at => Time.now.utc + 1.hour) + travel 90.minutes do + user.reload + assert_predicate user, :deletion_allowed? + end + end + end + + def test_deletion_allowed_during_zero_delay + with_user_account_deletion_delay(0) do + user = create(:user) + create(:changeset, :user => user, :created_at => Time.now.utc, :closed_at => Time.now.utc + 1.hour) + travel 30.minutes do + user.reload + assert_not_predicate user, :deletion_allowed? + assert_equal Time.now.utc + 30.minutes, user.deletion_allowed_at + end + end + end end diff --git a/test/system/account_deletion_test.rb b/test/system/account_deletion_test.rb index 87e981c64..e6517dccc 100644 --- a/test/system/account_deletion_test.rb +++ b/test/system/account_deletion_test.rb @@ -41,4 +41,59 @@ class AccountDeletionTest < ApplicationSystemTestCase assert_content "Account Deleted" end + + test "can delete with any delay setting value if the user has no changesets" do + with_user_account_deletion_delay(10000) do + travel 1.hour do + visit edit_account_path + + click_link "Delete Account..." + + assert_no_content "cannot currently be deleted" + end + end + end + + test "can delete with delay disabled" do + with_user_account_deletion_delay(nil) do + create(:changeset, :user => @user) + + travel 1.hour do + visit edit_account_path + + click_link "Delete Account..." + + assert_no_content "cannot currently be deleted" + end + end + end + + test "can delete when last changeset is old enough" do + with_user_account_deletion_delay(10) do + create(:changeset, :user => @user, :created_at => Time.now.utc, :closed_at => Time.now.utc + 1.hour) + + travel 12.hours do + visit edit_account_path + + click_link "Delete Account..." + + assert_no_content "cannot currently be deleted" + end + end + end + + test "can't delete when last changeset isn't old enough" do + with_user_account_deletion_delay(10) do + create(:changeset, :user => @user, :created_at => Time.now.utc, :closed_at => Time.now.utc + 1.hour) + + travel 10.hours do + visit edit_account_path + + click_link "Delete Account..." + + assert_content "cannot currently be deleted" + assert_content "in about 1 hour" + end + end + end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 68749c0f7..19e1a2784 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -371,5 +371,16 @@ module ActiveSupport el << tag_el end end + + def with_user_account_deletion_delay(value) + freeze_time + default_value = Settings.user_account_deletion_delay + Settings.user_account_deletion_delay = value + + yield + + Settings.user_account_deletion_delay = default_value + unfreeze_time + end end end