uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ env.ruby }}
+ rubygems: 3.4.10
bundler-cache: true
- name: Run rubocop
run: bundle exec rubocop --format fuubar
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ env.ruby }}
+ rubygems: 3.4.10
bundler-cache: true
- name: Run erblint
run: bundle exec erblint .
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ env.ruby }}
+ rubygems: 3.4.10
bundler-cache: true
- name: Cache node modules
uses: actions/cache@v3
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ env.ruby }}
+ rubygems: 3.4.10
bundler-cache: true
- name: Run brakeman
run: bundle exec brakeman -q
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ env.ruby }}
+ rubygems: 3.4.10
bundler-cache: true
- name: Setup database
run: |
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
+ rubygems: 3.4.10
bundler-cache: true
- name: Cache node modules
uses: actions/cache@v3
# Offense count: 26
# Configuration parameters: CountComments, CountAsOne.
Metrics/ClassLength:
- Max: 286
+ Max: 297
# Offense count: 59
# Configuration parameters: AllowedMethods, AllowedPatterns.
# Reduces boot times through caching; required in config/boot.rb
gem "bootsnap", ">= 1.4.2", :require => false
-# Use R2 for RTL conversion
-gem "r2", "~> 0.2.7"
+# Use rtlcss for RTL conversion
+gem "rtlcss"
# Use autoprefixer to generate CSS prefixes
gem "autoprefixer-rails"
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)
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)
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)
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)
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)
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)
kramdown (2.4.0)
rexml
language_server-protocol (3.17.0.3)
+ libv8-node (18.16.0.0)
libxml-ruby (4.1.2)
listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3)
mini_magick (4.12.0)
mini_mime (1.1.5)
mini_portile2 (2.8.5)
+ mini_racer (0.8.0)
+ libv8-node (~> 18.16.0.0)
minitest (5.20.0)
msgpack (1.7.2)
multi_json (1.15.0)
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)
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)
- r2 (0.2.8)
racc (1.7.3)
rack (2.2.8)
rack-cors (2.0.1)
rinku (2.0.6)
rotp (6.3.0)
rouge (4.2.0)
+ rtlcss (0.2.1)
+ mini_racer (>= 0.6.3)
rubocop (1.57.2)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
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)
pg
puma (~> 5.6)
quad_tile (~> 1.0.1)
- r2 (~> 0.2.7)
rack-cors
rack-uri_sanitizer
rails (~> 7.1.0)
rails-i18n (~> 7.0.0)
rinku (>= 2.0.6)
rotp
+ rtlcss
rubocop
rubocop-capybara
rubocop-factory_bot
@import "bootstrap";
@import "rails_bootstrap_forms";
-/* Bootstrap + r2 fixes */
-
-:root[dir=rtl] {
- .bs-tooltip-auto[data-popper-placement^="right"] .tooltip-arrow {
- /* no-r2 */
- right: unset !important;
- left: calc(-1 * var(--bs-tooltip-arrow-height)) !important;
-
- &::before {
- /* no-r2 */
- left: unset !important;
- right: -1px !important;
- }
- }
-
- .bs-tooltip-auto[data-popper-placement^="left"] .tooltip-arrow {
- /* no-r2 */
- left: unset !important;
- right: calc(-1 * var(--bs-tooltip-arrow-height)) !important;
-
- &::before {
- /* no-r2 */
- right: unset !important;
- left: -1px !important;
- }
- }
-}
-
/* Styles common to large and small screens */
/* Default rules for the body of every page */
overflow: hidden;
}
-.icon.search { /* no-r2 */ background-position: 0 0; }
-.icon.donate { /* no-r2 */ background-position: -20px 0; }
-.icon.zoomin { /* no-r2 */ background-position: -40px 0; }
-.icon.zoomout { /* no-r2 */ background-position: -60px 0; }
-.icon.geolocate { /* no-r2 */ background-position: -80px 0; }
-.active .icon.geolocate { /* no-r2 */ background-position: -80px -20px; }
-.icon.layers { /* no-r2 */ background-position: -100px 0; }
-.icon.key { /* no-r2 */ background-position: -120px 0; }
-.icon.share { /* no-r2 */ background-position: -140px 0; }
-.icon.clipboard { /* no-r2 */ background-position: -160px 0; }
-.icon.link { /* no-r2 */ background-position: -180px 0; }
-.icon.close { /* no-r2 */ background-position: -200px 0; }
-.icon.close:hover { /* no-r2 */ background-position: -200px -20px; }
-.icon.check { /* no-r2 */ background-position: -220px 0; }
-.icon.note { /* no-r2 */ background-position: -240px 0; }
-.icon.note.grey { /* no-r2 */ background-position: -240px -20px; }
-.icon.query { /* no-r2 */ background-position: -260px 0; }
+.icon.search { /*rtl:ignore*/ background-position: 0 0; }
+.icon.donate { /*rtl:ignore*/ background-position: -20px 0; }
+.icon.zoomin { /*rtl:ignore*/ background-position: -40px 0; }
+.icon.zoomout { /*rtl:ignore*/ background-position: -60px 0; }
+.icon.geolocate { /*rtl:ignore*/ background-position: -80px 0; }
+.active .icon.geolocate { /*rtl:ignore*/ background-position: -80px -20px; }
+.icon.layers { /*rtl:ignore*/ background-position: -100px 0; }
+.icon.key { /*rtl:ignore*/ background-position: -120px 0; }
+.icon.share { /*rtl:ignore*/ background-position: -140px 0; }
+.icon.clipboard { /*rtl:ignore*/ background-position: -160px 0; }
+.icon.link { /*rtl:ignore*/ background-position: -180px 0; }
+.icon.close { /*rtl:ignore*/ background-position: -200px 0; }
+.icon.close:hover { /*rtl:ignore*/ background-position: -200px -20px; }
+.icon.check { /*rtl:ignore*/ background-position: -220px 0; }
+.icon.note { /*rtl:ignore*/ background-position: -240px 0; }
+.icon.note.grey { /*rtl:ignore*/ background-position: -240px -20px; }
+.icon.query { /*rtl:ignore*/ background-position: -260px 0; }
/* Utility for de-emphasizing content */
}
}
+/* Force LTR/RTL alignment for placeholder text */
+
+.form-control::placeholder {
+ text-align: left;
+}
+
/* Rules for export sidebar */
.export_form {
#maxlat { margin-top: -1px; }
#minlon {
float: left;
- /* no-r2 */ margin-left: -1px;
+ /*rtl:ignore*/ margin-left: -1px;
}
#maxlon {
float: right;
- /* no-r2 */ margin-right: -1px;
+ /*rtl:ignore*/ margin-right: -1px;
}
#minlat { margin-bottom: -1px; }
}
}
.sprite.x {
- /* no-r2 */ background-position: -50px 0;
+ /*rtl:ignore*/ background-position: -50px 0;
}
.sprite.term {
}
.sprite.node {
- /* no-r2 */ background-position: -100px 0;
+ /*rtl:ignore*/ background-position: -100px 0;
}
.sprite.way {
- /* no-r2 */ background-position: -150px 0;
+ /*rtl:ignore*/ background-position: -150px 0;
}
.sprite.tag {
- /* no-r2 */ background-position: -200px 0;
+ /*rtl:ignore*/ background-position: -200px 0;
}
.sprite.editor {
- /* no-r2 */ background-position: -250px 0;
+ /*rtl:ignore*/ background-position: -250px 0;
}
.sprite.question {
- /* no-r2 */ background-position: -300px 0;
+ /*rtl:ignore*/ background-position: -300px 0;
}
.sprite.rules {
- /* no-r2 */ background-position: -350px 0;
+ /*rtl:ignore*/ background-position: -350px 0;
}
.icon.note {
background: 40px 40px image-url('about/sprite.png') no-repeat;
&.local {
- /* no-r2 */
+ /*rtl:ignore*/
background-position: 0px 0px;
}
&.community {
- /* no-r2 */
+ /*rtl:ignore*/
background-position: 0px -40px;
}
&.open {
- /* no-r2 */
+ /*rtl:ignore*/
background-position: 0px -80px;
}
&.partners {
- /* no-r2 */
+ /*rtl:ignore*/
background-position: 0px -120px;
}
&.infringement {
- /* no-r2 */
+ /*rtl:ignore*/
background-position: 0px -160px;
}
&.legal {
- /* no-r2 */
+ /*rtl:ignore*/
background-position: -45px -160px;
}
}
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
##
# 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
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
##
# 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
#
# 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
#
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
<li><%= t ".retain_email" %></li>
</ul>
-<%= 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 %>
+ <div class="alert alert-warning">
+ <%= t ".recent_editing_html", :time => friendly_date(current_user.deletion_allowed_at) %>
+ </div>
+ <button class="btn btn-secondary" disabled><%= t(".delete_account") %></button>
+<% end %>
+
<%= link_to t(".cancel"), edit_account_path, :class => "btn btn-link" %>
xml.instruct! :xml, :version => "1.0"
-# basic attributes
-
xml.osm(OSM::API.new.xml_root_attributes) do |osm|
osm << render(@changeset)
end
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)
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?)
+++ /dev/null
-require "r2"
-
-class R2ScssProcessor < SassC::Rails::ScssTemplate
- def self.call(input)
- output = super(input)
- data = R2.r2(output[:data])
- output.delete(:map)
- output.merge(:data => data)
- end
-end
-
-Rails.application.config.assets.configure do |env|
- env.register_mime_type "text/r2+scss", :extensions => [".r2.scss"]
- env.register_transformer "text/r2+scss", "text/css", R2ScssProcessor
- env.register_preprocessor "text/r2+scss", Sprockets::DirectiveProcessor.new(:comments => ["//", ["/*", "*/"]])
-end
--- /dev/null
+require "rtlcss"
+
+class RtlcssSCSSProcessor < SassC::Rails::ScssTemplate
+ def self.call(input)
+ output = super(input)
+ data = Rtlcss.flip_css(output[:data])
+ output.delete(:map)
+ output.merge(:data => data)
+ end
+end
+
+Rails.application.config.assets.configure do |env|
+ env.register_mime_type "text/rtlcss+scss", :extensions => [".rtlcss.scss"]
+ env.register_transformer "text/rtlcss+scss", "text/css", RtlcssSCSSProcessor
+ env.register_preprocessor "text/rtlcss+scss", Sprockets::DirectiveProcessor.new(:comments => ["//", ["/*", "*/"]])
+end
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
roof: Toenn
ruins: Savadur dismantret
school: Savadur skol
+ semidetached_house: Ti stag
service: Savadur servij
shed: Lochenn
stable: Marchosi
warehouse: Sanailh
"yes": Savadur
club:
+ scout: Diazlec'h ur strollad skout
sport: Kleub sport
"yes": Klub
craft:
confectionery: Koñfizerezh
dressmaker: Kemener
electrician: Tredanour
+ electronics_repair: Dreser dafar tredanek
gardener: Liorzhour
glaziery: Gweraerezh
handicraft: Artizanerezh
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:
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:
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
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:
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
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: 버려진 고속도로
cycleway: 자전거 전용도로
elevator: 엘리베이터
emergency_access_point: 긴급 액세스 포인트
+ emergency_bay: 비상주차대
footway: 보도
ford: 여울
give_way: 양보 표지
track: 오솔길
traffic_mirror: 도로반사경
traffic_signals: 교통 신호
+ trailhead: 산책로 기점
trunk: 간선 도로
trunk_link: 간선 도로
turning_loop: 방향전환 운전용 루프선
"yes": 산길
natural:
atoll: 환초
+ bare_rock: 노출 암반
bay: 만
beach: 해변
cape: 곶
hill: 언덕
hot_spring: 온천
island: 섬
+ isthmus: 지협
land: 토지
marsh: 습지
moor: 습지
locality: 지역
municipality: 지방자치체
neighbourhood: 마을
+ plot: 대지
postcode: 우편 번호
quarter: 구역
region: 지역
"yes": 장소
railway:
abandoned: 폐선된 철도
+ buffer_stop: 철도 차막이
construction: 건설 중인 철도
disused: 폐선된 철도
funicular: 케이블 카
bakery: 제과점
bathroom_furnishing: 욕실 설치 업체
beauty: 미용실
+ bed: 침구점
beverages: 음료 가게
bicycle: 자전거 가게
bookmaker: 마권업자
attraction: 관광 명소
bed_and_breakfast: 민박
cabin: 여행자 오두막
+ camp_pitch: 캠프장 피치
camp_site: 캠프장
caravan_site: 캐러밴 사이트
chalet: 샬렛
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의 등록 상표
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}는 오픈스트리트맵 재단에서 제공하는 웹사이트 및 기타 인프라 사용을 관리합니다.
링크를 클릭하여 텍스트를 읽고 동의하십시오.
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: 거부
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: 이 사용자를 숨기기 취소
# Author: Malafaya
# Author: Mansil
# Author: Mansil alfalb
+# Author: Matheusgomesms
# Author: McDutchie
# Author: MokaAkashiyaPT
# Author: Nemo bis
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
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
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}.'
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
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
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
cyclosm: CyclOSM
cycle_map: Mapa de Ciclismo
transport_map: Transportes Públicos
+ tracestracktop_topo: Tracestrack Topo
hot: Humanitário
opnvkarte: ÖPNVKarte
layers:
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:
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
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
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}.'
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
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
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)
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'
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}
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
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:'
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.
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
college: Здание колледжа
commercial: Офисное здание
construction: Строящееся здание
- detached: Ð\9eÑ\81обнÑ\8fк
+ detached: Ð\9eÑ\82делÑ\8cноÑ\81Ñ\82оÑ\8fÑ\89ий жилой дом
dormitory: Общежитие
duplex: Дуплекс
farm: Ферма
office: Офисное здание
public: Общественное здание
residential: Жилой дом
- retail: Ð\97дание на пÑ\80одажÑ\83
+ retail: ТоÑ\80говое здание
roof: Крыша
ruins: Разрушенное здание
school: Здание школы
stable: Конюшня
static_caravan: Передвижной дом
temple: Здание храма
- terrace: Ð\97дание Ñ\81 Ñ\82еÑ\80Ñ\80аÑ\81ой
+ terrace: Ð Ñ\8fд домов
train_station: Железнодорожный вокзал
university: Университет
warehouse: Склад
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
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
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:'
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
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
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:'
editor: redigerare
node: nod
way: sträcka
+ tag: tagg
rules:
title: Regler!
para_1_html: |-
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
role:
administrator: 這個使用者是一個管理員
moderator: 這個使用者是一個仲裁員
+ importer: 此名使用者是匯入者
grant:
administrator: 授予管理員權限
moderator: 授予仲裁員權限
+ importer: 授予匯入權限
revoke:
administrator: 撤銷管理員權限
moderator: 撤銷仲裁員權限
+ importer: 撤銷匯入權限
block_history: 已封鎖
moderator_history: 給予封鎖
comments: 評論
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
--- /dev/null
+class AddClosedAtIndexToChangesets < ActiveRecord::Migration[7.1]
+ disable_ddl_transaction!
+
+ def change
+ add_index :changesets, [:user_id, :closed_at], :algorithm => :concurrently
+ end
+end
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: -
--
('23'),
('22'),
('21'),
+('20231117170422'),
('20231101222146'),
('20231029151516'),
('20231010194809'),
# 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
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
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
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