ruby-version: 3.1
rubygems: 3.4.10
bundler-cache: true
+ - name: Create base branch
+ run: |
+ git fetch ${{ github.event.pull_request.base.repo.clone_url }} ${{ github.event.pull_request.base.ref }}:danger_base
+ - name: Create head branch
+ run: |
+ git fetch ${{ github.event.pull_request.head.repo.clone_url }} ${{ github.event.pull_request.head.ref }}:danger_head
- name: Danger
env:
DANGER_GITHUB_BEARER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
An example excerpt from settings.local.yml:
-```
+```yaml
# Default editor
default_editor: "id"
# OAuth 2 Client ID for iD
An example excerpt from settings.local.yml:
-```
+```yaml
# OAuth 2 Client ID for the web site
oauth_application: "SGm8QJ6tmoPXEaUPIZzLUmm1iujltYZVWCp9hvGsqXg"
# OAuth 2 Client Secret for the web site
+# Contributing
+
* https://www.ruby-lang.org/ - The homepage of Ruby which has more links and some great tutorials.
* https://rubyonrails.org/ - The homepage of Rails, also has links and tutorials.
+## Assigning Issues
+
+We don't assign issues to individual contributors. You are welcome to work on any
+issue, and there's no need to ask first.
+
+For more details see [our FAQ](FAQ.md)]
+
## Coding style
We use [Rubocop](https://github.com/rubocop-hq/rubocop) (for ruby files)
The first step is to fork/clone the repo to your local machine:
- git clone https://github.com/openstreetmap/openstreetmap-website.git
+```
+git clone https://github.com/openstreetmap/openstreetmap-website.git
+```
Now change working directory to the `openstreetmap-website`:
- cd openstreetmap-website
+```
+cd openstreetmap-website
+```
## Initial Setup
### Storage
- cp config/example.storage.yml config/storage.yml
+```
+cp config/example.storage.yml config/storage.yml
+```
### Database
- cp config/docker.database.yml config/database.yml
+```
+cp config/docker.database.yml config/database.yml
+```
## Prepare local settings file
This is a workaround. [See issues/2185 for details](https://github.com/openstreetmap/openstreetmap-website/issues/2185#issuecomment-508676026).
- touch config/settings.local.yml
+```
+touch config/settings.local.yml
+```
**Windows users:** `touch` is not an availible command in Windows so just create a `settings.local.yml` file in the `config` directory, or if you have WSL you can run `wsl touch config/settings.local.yml`.
To build local Docker images run from the root directory of the repository:
- docker compose build
+```
+docker compose build
+```
If this is your first time running or you have removed cache this will take some time to complete. Once the Docker images have finished building you can launch the images as containers.
To launch the app run:
- docker compose up -d
+```
+docker compose up -d
+```
This will launch one Docker container for each 'service' specified in `docker-compose.yml` and run them in the background. There are two options for inspecting the logs of these running containers:
Run the Rails database migrations:
- docker compose run --rm web bundle exec rails db:migrate
+```
+docker compose run --rm web bundle exec rails db:migrate
+```
### Tests
Prepare the test database:
- docker compose run --rm web bundle exec rails db:test:prepare
+```
+docker compose run --rm web bundle exec rails db:test:prepare
+```
Run the test suite:
- docker compose run --rm web bundle exec rails test:all
+```
+docker compose run --rm web bundle exec rails test:all
+```
If you encounter errors about missing assets, precompile the assets:
- docker compose run --rm web bundle exec rake assets:precompile
+```
+docker compose run --rm web bundle exec rake assets:precompile
+```
### Loading an OSM extract
For example, let's download the District of Columbia from Geofabrik or [any other region](https://download.geofabrik.de):
- wget https://download.geofabrik.de/north-america/us/district-of-columbia-latest.osm.pbf
+```
+wget https://download.geofabrik.de/north-america/us/district-of-columbia-latest.osm.pbf
+```
You can now use Docker to load this extract into your local Docker-based OSM instance:
- docker compose run --rm web osmosis \
- -verbose \
- --read-pbf district-of-columbia-latest.osm.pbf \
- --log-progress \
- --write-apidb \
- host="db" \
- database="openstreetmap" \
- user="openstreetmap" \
- validateSchemaVersion="no"
+```
+docker compose run --rm web osmosis \
+ -verbose \
+ --read-pbf district-of-columbia-latest.osm.pbf \
+ --log-progress \
+ --write-apidb \
+ host="db" \
+ database="openstreetmap" \
+ user="openstreetmap" \
+ validateSchemaVersion="no"
+```
**Windows users:** Powershell uses `` ` `` and CMD uses `^` at the end of each line, e.g.:
- docker compose run --rm web osmosis `
- -verbose `
- --read-pbf district-of-columbia-latest.osm.pbf `
- --log-progress `
- --write-apidb `
- host="db" `
- database="openstreetmap" `
- user="openstreetmap" `
- validateSchemaVersion="no"
+```
+docker compose run --rm web osmosis `
+ -verbose `
+ --read-pbf district-of-columbia-latest.osm.pbf `
+ --log-progress `
+ --write-apidb `
+ host="db" `
+ database="openstreetmap" `
+ user="openstreetmap" `
+ validateSchemaVersion="no"
+```
Once you have data loaded for Washington, DC you should be able to navigate to [`http://localhost:3000/#map=12/38.8938/-77.0146`](http://localhost:3000/#map=12/38.8938/-77.0146) to begin working with your local instance.
If you want to get into a web container and run specific commands you can fire up a throwaway container to run bash in via:
- docker compose run --rm web bash
+```
+docker compose run --rm web bash
+```
Alternatively, if you want to use the already-running `web` container then you can `exec` into it via:
- docker compose exec web bash
+```
+docker compose exec web bash
+```
Similarly, if you want to `exec` in the db container use:
- docker compose exec db bash
+```
+docker compose exec db bash
+```
+# Frequently Asked Questions
+
## How do I create a banner to promote my OpenStreetMap event?
We occasionally display banner images on the main page of [openstreetmap.org](https://www.openstreetmap.org/) to
See [PR #1296](https://github.com/openstreetmap/openstreetmap-website/pull/1296)
as an example.
+
+## Why don't you assign issues?
+
+We don't assign issues to volunteers for several reasons. The main reasons are that it discourages other volunteers from working on the issue, and the process turns into an unproductive administrative overhead for our team.
+
+There's no need to ask for an issue to be assigned before anyone starts working on it. Everyone is welcome to work on any issue at any time.
+
+In our experience, most people who ask for an issue to be assigned to them never create a pull request. So we would need to keep track of the assigned issues, and remember to unassign them a week or two into the future, when it is likely that they will not be making a PR. Assigned developers might feel bad if they perceive that we're unhappy with their progress, further discouraging them from contributing. Or we will get drawn into discussions about needing more time, or re-assigning them again, or so on. So it is best not to assign in the first place.
+
+The risk that two people are both genuinely working on the same task in the same hour or two is vanishingly remote, and doesn't outweigh the downsides described above. A better approach is to encourage people to simply work on the task and create a pull request, at which point everyone knows that they are actually working on the issue and not just planning/hoping/wishing to do so.
group :development do
gem "better_errors"
gem "binding_of_caller"
- gem "danger", :github => "tomhughes/danger", :ref => "pull-request-target"
+ gem "danger"
gem "danger-auto_label"
gem "debug_inspector"
gem "i18n-tasks"
-GIT
- remote: https://github.com/tomhughes/danger.git
- revision: a265cf74d2f464a25796b48d95697f5eed553454
- ref: pull-request-target
- specs:
- danger (9.5.1)
- base64 (~> 0.2)
- claide (~> 1.0)
- claide-plugins (>= 0.9.2)
- colored2 (~> 3.1)
- cork (~> 0.1)
- faraday (>= 0.9.0, < 3.0)
- faraday-http-cache (~> 2.0)
- git (~> 1.13)
- kramdown (~> 2.3)
- kramdown-parser-gfm (~> 1.0)
- octokit (>= 4.0)
- pstore (~> 0.1)
- terminal-table (>= 1, < 4)
-
GEM
remote: https://rubygems.org/
specs:
autoprefixer-rails (10.4.19.0)
execjs (~> 2)
aws-eventstream (1.3.0)
- aws-partitions (1.1001.0)
- aws-sdk-core (3.211.0)
+ aws-partitions (1.1004.0)
+ aws-sdk-core (3.212.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
aws-sdk-kms (1.95.0)
aws-sdk-core (~> 3, >= 3.210.0)
aws-sigv4 (~> 1.5)
- aws-sdk-s3 (1.169.0)
+ aws-sdk-s3 (1.170.1)
aws-sdk-core (~> 3, >= 3.210.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
aws-sigv4 (1.10.1)
aws-eventstream (~> 1, >= 1.0.2)
base64 (0.2.0)
- benchmark (0.3.0)
+ benchmark (0.4.0)
better_errors (2.10.1)
erubi (>= 1.0.0)
rack (>= 0.9.0)
rexml
crass (1.0.6)
dalli (3.2.8)
+ danger (9.5.1)
+ base64 (~> 0.2)
+ claide (~> 1.0)
+ claide-plugins (>= 0.9.2)
+ colored2 (~> 3.1)
+ cork (~> 0.1)
+ faraday (>= 0.9.0, < 3.0)
+ faraday-http-cache (~> 2.0)
+ git (~> 1.13)
+ kramdown (~> 2.3)
+ kramdown-parser-gfm (~> 1.0)
+ octokit (>= 4.0)
+ pstore (~> 0.1)
+ terminal-table (>= 1, < 4)
danger-auto_label (1.3.1)
danger-plugin-api (~> 1.0)
danger-plugin-api (1.0.0)
reline (>= 0.3.8)
debug_inspector (1.2.0)
deep_merge (1.2.2)
- delayed_job (4.1.12)
- activesupport (>= 3.0, < 8.0)
- delayed_job_active_record (4.1.10)
- activerecord (>= 3.0, < 8.0)
+ delayed_job (4.1.13)
+ activesupport (>= 3.0, < 9.0)
+ delayed_job_active_record (4.1.11)
+ activerecord (>= 3.0, < 9.0)
delayed_job (>= 3.0, < 5)
docile (1.4.1)
doorkeeper (5.7.1)
globalid (1.2.1)
activesupport (>= 6.1)
google-protobuf (3.25.5)
- hashdiff (1.1.1)
+ hashdiff (1.1.2)
hashie (5.0.0)
highline (3.1.1)
reline
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
- json (2.7.6)
+ json (2.8.1)
jwt (2.9.3)
base64
kgio (2.11.4)
minitest (5.25.1)
minitest-focus (1.4.0)
minitest (>= 4, < 6)
- msgpack (1.7.3)
+ msgpack (1.7.5)
multi_json (1.15.0)
multi_xml (0.7.1)
bigdecimal (~> 3.1)
nap (1.1.0)
- net-http (0.4.1)
+ net-http (0.5.0)
uri
- net-imap (0.5.0)
+ net-imap (0.5.1)
date
net-protocol
net-pop (0.1.2)
omniauth (~> 2.0)
open4 (1.3.4)
openstreetmap-deadlock_retry (1.3.1)
- ostruct (0.6.0)
+ ostruct (0.6.1)
overcommit (0.64.1)
childprocess (>= 0.6.3, < 6)
iniparse (~> 1.4)
popper_js (2.11.8)
progress (3.6.0)
pstore (0.1.3)
- psych (5.1.2)
+ psych (5.2.0)
stringio
public_suffix (6.0.1)
puma (5.6.9)
rdoc (6.7.0)
psych (>= 4.0.0)
regexp_parser (2.9.2)
- reline (0.5.10)
+ reline (0.5.11)
io-console (~> 0.5)
request_store (1.7.0)
rack (>= 1.4)
rexml (3.3.9)
rinku (2.0.6)
rotp (6.3.0)
- rouge (4.4.0)
+ rouge (4.5.1)
rtlcss (0.2.1)
mini_racer (>= 0.6.3)
rubocop (1.68.0)
rubocop-ast (>= 1.32.2, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
- rubocop-ast (1.34.0)
+ rubocop-ast (1.35.0)
parser (>= 3.3.1.0)
rubocop-capybara (2.21.0)
rubocop (~> 1.41)
sawyer (0.9.2)
addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3)
- securerandom (0.3.1)
+ securerandom (0.3.2)
selenium-webdriver (4.23.0)
base64 (~> 0.2)
logger (~> 1.4)
execjs (>= 0.3.0, < 3)
thor (1.3.2)
tilt (2.4.0)
- timeout (0.4.1)
+ timeout (0.4.2)
turbo-rails (2.0.11)
actionpack (>= 6.0.0)
railties (>= 6.0.0)
config
connection_pool
dalli
- danger!
+ danger
danger-auto_label
dartsass-sprockets
debug
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
- # use official ubuntu image for virtualbox
+ # use official debian image
+ config.vm.box = "debian/bookworm64"
+
+ # configure virtualbox provider
config.vm.provider "virtualbox" do |vb, override|
- override.vm.box = "ubuntu/noble64"
override.vm.synced_folder ".", "/srv/openstreetmap-website"
vb.customize ["modifyvm", :id, "--memory", "4096"]
vb.customize ["modifyvm", :id, "--cpus", "2"]
# Use sshfs sharing if available, otherwise NFS sharing
sharing_type = Vagrant.has_plugin?("vagrant-sshfs") ? "sshfs" : "nfs"
- # use third party image and sshfs or NFS sharing for lxc
+ # configure lxc provider
config.vm.provider "lxc" do |_, override|
- override.vm.box = "generic/ubuntu2404"
override.vm.synced_folder ".", "/srv/openstreetmap-website", :type => sharing_type
end
- # use third party image and sshfs or NFS sharing for libvirt
- config.vm.provider "libvirt" do |_, override|
- override.vm.box = "generic/ubuntu2404"
+ # configure libvirt provider
+ config.vm.provider "libvirt" do |libvirt, override|
override.vm.synced_folder ".", "/srv/openstreetmap-website", :type => sharing_type
+ libvirt.memory = 4096
+ libvirt.cpus = 2
end
# configure shared package cache if possible
//= require qs/dist/qs
$(document).ready(function () {
- var loaderTimeout;
-
var map = new L.OSM.Map("map", {
zoomControl: false,
layerControl: false,
map.setSidebarOverlaid(false);
- clearTimeout(loaderTimeout);
-
- loaderTimeout = setTimeout(function () {
- $("#sidebar_loader").show();
- }, 200);
+ $("#sidebar_loader").show().addClass("delayed-fade-in");
// IE<10 doesn't respect Vary: X-Requested-With header, so
// prevent caching the XHR response as a full-page URL.
url: content_path,
dataType: "html",
complete: function (xhr) {
- clearTimeout(loaderTimeout);
$("#flash").empty();
- $("#sidebar_loader").hide();
+ $("#sidebar_loader").removeClass("delayed-fade-in").hide();
var content = $(xhr.responseText);
newNote.on("remove", function () {
addNoteButton.removeClass("active");
- }).on("dragstart", function () {
- $(newNote).stopTime("removenote");
}).on("dragend", function () {
content.find("textarea").focus();
});
.hide();
if (marker) map.removeLayer(marker);
- marker = L.circle(latlng, radius, featureStyle).addTo(map);
-
- $(document).everyTime(75, "fadeQueryMarker", function (i) {
- if (i === 10) {
- map.removeLayer(marker);
- } else {
- marker.setStyle({
- opacity: 1 - (i * 0.1),
- fillOpacity: 0.5 - (i * 0.05)
- });
- }
- }, 10);
+ marker = L.circle(latlng, Object.assign({
+ radius: radius,
+ className: "query-marker"
+ }, featureStyle)).addTo(map);
runQuery(latlng, radius, nearby, $("#query-nearby"), false);
runQuery(latlng, radius, isin, $("#query-isin"), true, compareSize);
*/
$(document).on("change", ".richtext_container textarea", function () {
var container = $(this).closest(".richtext_container");
+ var preview = container.find(".tab-pane[id$='_preview']");
- container.find(".tab-pane[id$='_preview']").empty();
+ preview.children(".richtext_placeholder").attr("hidden", true).removeClass("delayed-fade-in");
+ preview.children(".richtext").empty();
});
/*
var editor = container.find("textarea");
var preview = container.find(".tab-pane[id$='_preview']");
- if (preview.contents().length === 0) {
- preview.oneTime(500, "loading", function () {
- preview.addClass("loading");
- });
+ if (preview.children(".richtext").contents().length === 0) {
+ preview.children(".richtext_placeholder").removeAttr("hidden").addClass("delayed-fade-in");
- preview.load(editor.data("previewUrl"), { text: editor.val() }, function () {
- preview.stopTime("loading");
- preview.removeClass("loading");
+ preview.children(".richtext").load(editor.data("previewUrl"), { text: editor.val() }, function () {
+ preview.children(".richtext_placeholder").attr("hidden", true).removeClass("delayed-fade-in");
});
}
});
}
}
+/* Utility for delayed loading spinner */
+
+.delayed-fade-in {
+ animation: 300ms linear forwards delayed-fade-in;
+}
+
+@keyframes delayed-fade-in {
+ 0% { opacity: 0 }
+ 66% { opacity: 0 }
+ 100% { opacity: 1 }
+}
+
/* Rules for the header */
#menu-icon {
.leaflet-marker-draggable {
cursor: move;
}
+
+ .query-marker {
+ animation: 1500ms forwards query-marker-fade;
+
+ @keyframes query-marker-fade {
+ to { opacity: 0 }
+ }
+ }
}
#map-ui {
comment = note.comments.create!(attributes)
- note.comments.map(&:author).uniq.each do |user|
- UserMailer.note_comment_notification(comment, user).deliver_later if notify && user && user != current_user && user.visible?
+ if notify
+ note.subscribers.visible.each do |user|
+ UserMailer.note_comment_notification(comment, user).deliver_later if current_user != user
+ end
end
NoteSubscription.find_or_create_by(:note => note, :user => current_user) if current_user
end
def create
- session[:remember_me] ||= params[:remember_me]
+ session[:remember_me] = params[:remember_me] == "yes"
referer = safe_referer(params[:referer]) if params[:referer]
end
def show
- if current_user && current_user == @user_block.user
+ if current_user && current_user == @user_block.user && !@user_block.deactivates_at
@user_block.needs_view = false
@user_block.deactivates_at = [@user_block.ends_at, Time.now.utc].max
@user_block.save!
if moderator?
Settings.moderator_changeset_comments_per_hour
else
- previous_comments = changeset_comments.limit(200).count
- max_comments = previous_comments / 200.0 * Settings.max_changeset_comments_per_hour
+ previous_comments = changeset_comments.limit(Settings.comments_to_max_changeset_comments).count
+ max_comments = previous_comments / Settings.comments_to_max_changeset_comments.to_f * Settings.max_changeset_comments_per_hour
max_comments = max_comments.floor.clamp(Settings.initial_changeset_comments_per_hour, Settings.max_changeset_comments_per_hour)
max_comments /= 2**active_reports
max_comments.floor.clamp(Settings.min_changeset_comments_per_hour, Settings.max_changeset_comments_per_hour)
<%= f.password_field :password, :autocomplete => "on", :tabindex => 2, :value => "", :skip_label => true %>
<%= f.form_group do %>
- <%= f.check_box :remember_me, { :label => t(".remember"), :tabindex => 3, :checked => (params[:remember_me] == "yes") }, "yes" %>
+ <%= f.check_box :remember_me, { :label => t(".remember"), :tabindex => 3, :checked => (params[:remember_me] == "true") }, "yes" %>
<% end %>
<div class="mb-3">
<div id="<%= id %>_edit" class="tab-pane show active">
<%= builder.text_area(attribute, options.merge(:wrapper => false, "data-preview-url" => preview_url(:type => type))) %>
</div>
- <div id="<%= id %>_preview" class="tab-pane richtext text-break"></div>
+ <div id="<%= id %>_preview" class="tab-pane">
+ <div class="richtext_placeholder text-center py-5" hidden>
+ <div class="spinner-border" role="status">
+ <span class="visually-hidden"><%= t("browse.start_rjs.loading") %></span>
+ </div>
+ </div>
+ <div class="richtext text-break"></div>
+ </div>
<div id="<%= id %>_help" class="tab-pane">
<div class="card bg-body-tertiary h-100">
<div class="card-body">
# Author: Vugar 1981
# Author: Wertuose
# Author: Şeyx Şamil
+# Author: Əkrəm Cəfər
---
az:
time:
other: təxminən %{count} saat əvvəl
half_a_minute: yarım dəqiqə əvvəl
editor:
- default: Susmaya görə (hal-hazırda %{name})
+ default: Defolt (hal-hazırda %{name})
id:
name: iD
description: iD (brauzerdaxili redaktə)
create: বাধা তৈরি করুন
update: বাধা হালনাগাদ করুন
activerecord:
+ errors:
+ messages:
+ display_name_is_user_n: n আপনার ব্যবহারকারী আইডি না হলে user_n হবে না
+ models:
+ user_mute:
+ is_already_muted: ইতিমধ্যেই মিউট করা হয়েছে
models:
acl: প্রবেশাধিকার নিয়ন্ত্রণ তালিকা
changeset: পরিবর্তনসমূহ
reopened_at_by_html: '%{when} %{user} দ্বারা পুনর্সক্রিয়'
rss:
title: ওপেনস্ট্রিটম্যাপ টীকা
+ description_all: রিপোর্ট করা, মন্তব্য করা বা বন্ধ করা নোটের তালিকা
description_area: নোটের তালিকা, রিপোর্ট করা, মন্তব্য করা বা আপনার এলাকায়
বন্ধ করা [(%{min_lat}|%{min_lon}) -- (%{max_lat}|%{max_lon})]
description_item: নোট %{id}-এর জন্য একটি আরএসএস চারণ
retain_changeset_discussions: আপনার পরিবর্তন ধার্য আলোচনাগুলি, যদি করে থাকেন,
রয়ে যাবে।
retain_email: আপমার ইমেইল ঠিকানা রয়ে যাবে।
+ recent_editing_html: আপনি সম্প্রতি সম্পাদনা করেছেন বলে আপনার অ্যাকাউন্টটি
+ বর্তমানে মুছে ফেলা যাবে না। %{time} পরে মুছে ফেলা সম্ভব হবে।
confirm_delete: আপনি কি নিশ্চিত?
cancel: বাতিল করুন
accounts:
deleted_ago_by_html: '%{user} কর্তৃক %{time_ago} অপসারিত'
edited_ago_by_html: '%{user} কর্তৃক %{time_ago} সম্পাদিত'
version: সংস্করণ
+ redacted_version: সংশোধিত সংস্করণ
in_changeset: পরিবর্তনসমূহ
anonymous: নামহীন
no_comment: (কোনো মন্তব্য নেই)
other: '%{count}টি রাস্তা'
download_xml: XML ডাউনলোড করুন
view_history: ইতিহাস দেখুন
+ view_unredacted_history: অসংশোধিত ইতিহাস দেখুন
view_details: বিস্তারিত দেখুন
location: 'অবস্থান:'
node:
index:
title: পরিবর্তনধার্য
title_user: '%{user} কর্তৃক পরিবর্তন ধার্য'
+ title_user_link_html: '%{user_link}-এর দ্বারা পরিবর্তনগুলি'
title_friend: আমার বন্ধুদের দ্বারা পরিবর্তনসেট
title_nearby: কাছকাছি ব্যবহারকারীর পরিবর্তনধার্য
empty: কোনো পরিবর্তনধার্য পাওয়া যায়নি।
created: তৈরি হয়েছে
closed: বন্ধ হয়েছে
belongs_to: লেখক
+ subscribe:
+ button: আলোচনায় সাবস্ক্রাইব করুন
+ unsubscribe:
+ button: আলোচনায় আনসাবস্ক্রাইব করুন
+ no_such_entry:
+ heading: '%{id} আইডির কোনো ভুক্তি নেই'
show:
title: 'পরিবর্তনধার্য: %{id}'
+ created: 'তৈরি করা হয়েছে: %{when}'
+ closed: 'বন্ধ করা হয়েছে: %{when}'
created_ago_html: '%{time_ago} তৈরি'
closed_ago_html: '%{time_ago} বন্ধকৃত'
created_ago_by_html: '%{user} কর্তৃক %{time_ago} তৈরিকৃত'
join_discussion: আলোচনায় যোগ দিতে প্রবেশ করুন
still_open: চেঞ্জসেট এখনও খোলা - চেঞ্জসেট বন্ধ হয়ে গেলে আলোচনা খোলা হবে।
hidden_comment_by_html: '%{user} %{time_ago} মন্তব্যটি লুকিয়েছেন'
+ hide_comment: লুকান
+ unhide_comment: দৃশ্যমান করুন
comment: মন্তব্য
changesetxml: পরিবর্তনধার্য এক্সএমএল
osmchangexml: osmChange এক্সএমএল
create:
notice: অ্যাপ্লিকেশন নিবন্ধিত।
scopes:
+ address: আপনার আসল ঠিকানা দেখুন
email: আপনার ইমেইল ঠিকানা দেখুন
+ phone: আপনার ফোন নম্বর দেখুন
+ profile: আপনার প্রোফাইলের তথ্য দেখুন
errors:
contact:
contact: যোগাযোগ
fashion: ফ্যাশন সামগ্রীর দোকান
florist: ফুলওয়ালা
food: খাবারের দোকান
+ frame: ফ্রেমের দোকান
funeral_directors: অন্ত্যেষ্টিক্রিয়া পরিচালকবৃন্দ
furniture: আসবাবপত্র
garden_centre: বাগান কেন্দ্র
+ gas: গ্যাসের দোকান
general: সাধারণ দোকান
gift: উপহারের দোকান
greengrocer: সবজিওয়ালা
grocery: মুদি দোকান
hairdresser: নাপিত
hardware: যন্ত্রাংশের দোকান
+ health_food: স্বাস্থ্যকর খাবারের দোকান
hearing_aids: শ্রবণসহায়ক যন্ত্র
+ herbalist: ভেষজ বিশেষজ্ঞ
hifi: হাই-ফাই দোকান
houseware: ঘরোয়া জিনিসের দোকান
ice_cream: আইসক্রিমের দোকান
interior_decoration: ভিতরের সজ্জা
jewelry: গহনার দোকান
+ kiosk: কিয়স্কের দোকান
kitchen: রান্নাঘরের দোকান
laundry: ধোপার দোকান
locksmith: চাবিওয়ালা
lottery: লটারি
mall: বিপণী বিতান
massage: ম্যাসেজ
+ medical_supply: চিকিৎসা সামগ্রীর দোকান
mobile_phone: মোবাইল ফোনের দোকান
+ money_lender: ঋণদাতা
motorcycle: মোটোরসইকেলের দোকান
motorcycle_repair: মোটরসাইকেল মেরামতের দোকান
music: সঙ্গীতের দোকান
nutrition_supplements: পুষ্টি সম্পূরক
optician: চশমা বিক্রেতা
organic: জৈব খাদ্যের দোকান
+ outdoor: আউটডোর দোকান
paint: রঙের দোকান
+ pastry: পেস্ট্রির দোকান
perfumery: সুগন্ধিশালা
+ pet: পোষা প্রাণীর দোকান
photo: ছবির দোকান
seafood: সামুদ্রিক খাবার
second_hand: পুরনো-সামগ্রীর দোকান
wine: মদের দোকান
"yes": দোকান
tourism:
+ artwork: শিল্পকর্ম
attraction: আকর্ষণ
cabin: পর্যটক কেবিন
camp_site: ক্যাম্পের স্থল
"yes": জলপথ
admin_levels:
level2: রাষ্ট্রের সীমানা
+ level3: অঞ্চলের সীমানা
level4: রাজ্যের সীমানা
level5: অঞ্চলের সীমানা
level6: প্রদেশের সীমানা
select_type: প্রকার নির্বাচন করুন
search: অনুসন্ধান
states:
+ ignored: উপেক্ষিত
open: খুলুন
resolved: মীমাংসিত
page:
user_not_found: ব্যবহারকারীর অস্তিত্ব নেই
+ issues_not_found: এই ধরনের কোনও ইস্যু পাওয়া যায়নি
status: স্থিতি
reports: অভিযোগ
last_updated: সর্বশেষ হালনাগাদ
reports_count:
one: '%{count}টি প্রতিবেদন'
other: '%{count}টি প্রতিবেদন'
+ states:
+ open: খুলুন
+ resolved: মীমাংসিত
show:
resolve: মীমাংসা করুন
ignore: উপেক্ষা করুন
reports_of_this_issue: এই সমস্যা প্রতিবেদন করুন
read_reports: প্রতিবেদন পড়ুন
new_reports: নতুন প্রতিবেদন
+ comments_on_this_issue: এই বিষয়ে মন্তব্যসমূহ
helper:
reportable_title:
note: 'টীকা #%{note_id}'
delete image: বর্তমান ছবিটি অপসারণ করুন
replace image: বর্তমান ছবি বদল করুন
home location: বাড়ির অবস্থান
+ show: দেখাও
+ delete: অপসারণ
update:
success: প্রোফাইল পরিবর্তিত হয়েছে।
sessions:
new:
- tab_title: প্রবেশ
+ tab_title: প্রবেশ করুন
email or username: ইমেইল ঠিকানা অথবা ব্যবহারকারী নাম
password: পাসওয়ার্ড
remember: আমাকে মনে রাখো
lost password link: পাসওয়ার্ড ভুলে গেছেন?
login_button: প্রবেশ করুন
+ or: অথবা
auth failure: দুঃখিত, এই তথ্য দিয়ে প্রবেশ করানো যাচ্ছে না।
destroy:
title: প্রস্থান
+ heading: ওপেনস্ট্রিটম্যাপ থেকে লগআউট করুন
logout_button: প্রস্থান
+ suspended_flash:
+ suspended: দুঃখিত, সন্দেহজনক কার্যকলাপের কারণে আপনার অ্যাকাউন্ট স্থগিত করা হয়েছে।
+ contact_support_html: আপনি যদি এই বিষয়ে আলোচনা করতে চান তাহলে অনুগ্রহ করে %{support_link}-এর
+ সাথে যোগাযোগ করুন।
+ support: সহায়তা
shared:
markdown_help:
heading_html: '%{kramdown_link} দিয়ে পার্স করা হয়েছে'
richtext_field:
edit: সম্পাদনা
preview: প্রাকদর্শন
+ help: সাহায্য
pagination:
diary_comments:
older: পুরাতন মন্তব্য
closed: Caëwyd
belongs_to: Awdur
subscribe:
+ heading: Tanysgrifio i'r drafodaeth grŵp newid ganlynol?
button: Tanysgrifio i drafodaeth
unsubscribe:
+ heading: Dad-danysgrifio i'r drafodaeth grŵp newid ganlynol?
button: Dad-danysgrifio o'r drafodaeth
heading:
title: Grŵp newid %{id}
created_by_html: Crëwyd gan %{link_user} ar %{created}.
no_such_entry:
heading: 'Dim cofnod gyda''r id: %{id}'
+ body: Mae'n ddrwg gennym, nid oes grŵp newid gyda'r id %{id}. Gwiriwch eich
+ sillafu, neu efallai bod y ddolen rydych chi wedi ei chlicio arni'n anghywir.
show:
title: 'Grŵp newid: %{id}'
created: 'Crëwyd: %{when}'
title: Cofnodion dyddiadur OpenStreetMap
description: Cofnodion dyddiadur diweddar gan ddefnyddwyr OpenStreetMap
subscribe:
+ heading: Tanysgrifio i'r drafodaeth cofnod dyddiadur ganlynol?
button: Tanysgrifio i drafodaeth
unsubscribe:
heading: Dad-danysgrifio o'r drafodaeth cofnod dyddiadur ganlynol?
educational_institution: Sefydliad Addysgol
employment_agency: Asiantaeth Cyflogi
energy_supplier: Swyddfa Gyflenwr Ynni
- estate_agent: Gwerthwr Tai
+ estate_agent: Asiant Eiddo
financial: Swyddfa Gyllid
government: Swyddfa Llywodraeth
insurance: Swyddfa Yswiriant
e-cigarette: Siop E-Sigaréts
electronics: Siop Electroneg
erotic: Siop Erotig
- estate_agent: Gwerthwr Tai
+ estate_agent: Asiant Eiddo
fabric: Siop Ddeunydd
farm: Siop Fferm
fashion: Siop Ffasiwn
partial_changeset_without_comment: dim sylw
details: 'Ateb neu ddysgu mwy am y grŵp newid: %{url}.'
details_html: 'Ateb neu ddysgu mwy am y grŵp newid: %{url}.'
+ unsubscribe: Gallwch ddad-danysgrifio o ddiweddariadau i'r grŵp newid hwn yn
+ %{url}.
+ unsubscribe_html: Gallwch ddad-danysgrifio o ddiweddariadau i'r grŵp newid hwn
+ yn %{url}.
confirmations:
confirm:
heading: Gwiriwch eich e-byst!
application: Ap
permissions: Caniatadau
last_authorized: Awdurdodwyd Ddiweddaf
+ no_applications_html: Nid ydych wedi awdurdodi unrhyw apiau %{oauth2} eto.
application:
revoke: Dirymu Mynediad
confirm_revoke: Dirymu mynediad ar gyfer yr ap hwn?
show:
title: Viser spor %{name}
heading: Viser spor %{name}
- pending: VENTENDE
+ pending: AFVENTENDE
filename: 'Filnavn:'
download: hent
uploaded: 'Overført:'
visibility: 'Synlighed:'
confirm_delete: Slet dette spor?
trace:
- pending: VENTENDE
+ pending: AFVENTENDE
count_points:
one: '%{count} punkt'
other: '%{count} punkter'
longitude: קו אורך
public: ציבורי
description: תיאור
- gpx_file: ×\91×\97×\99רת קובץ מסלול GPS
+ gpx_file: × ×\90 ×\9c×\91×\97×\95ר קובץ מסלול GPS
visibility: נִראוּת
tagstring: תגים
message:
befriend_them: באפשרותך לסמנו כחבר בכתובת %{befriendurl}.
befriend_them_html: באפשרותך לסמנו כחבר בכתובת %{befriendurl}.
gpx_description:
- description_with_tags: '×\96×\94 × ×¨×\90×\94 ×\9b×\9e×\95 הקובץ שלך %{trace_name} עם התיאור %{trace_description}
+ description_with_tags: '× ×¨×\90×\94 שהקובץ שלך %{trace_name} עם התיאור %{trace_description}
ועם התגים הבאים: %{tags}'
- description_with_tags_html: '×\96×\94 × ×¨×\90×\94 ×\9b×\9e×\95 הקובץ שלך %{trace_name} עם התיאור %{trace_description}
+ description_with_tags_html: '× ×¨×\90×\94 שהקובץ שלך %{trace_name} עם התיאור %{trace_description}
ועם התגים הבאים: %{tags}'
- description_with_no_tags: זה נראה כמו הקובץ שלך %{trace_name} עם התיאור %{trace_description}
+ description_with_no_tags: נראה שהקובץ שלך %{trace_name} עם התיאור %{trace_description}
+ וללא תגים
+ description_with_no_tags_html: נראה שהקובץ שלך %{trace_name} עם התיאור %{trace_description}
וללא תגים
- description_with_no_tags_html: זה נראה כמו הקובץ שלך %{trace_name} עם התיאור
- %{trace_description} וללא תגים
gpx_failure:
hi: שלום %{to_user},
failed_to_import: 'הייבוא כקובץ מסלול GPS נכשל. נא לוודא שהקובץ שלך הוא קובץ
# Export driver: phpyaml
# Author: Dr Lotus Black
# Author: Ninjastrikers
+# Author: Zyh333222
# Author: ခွန်ပညာႏ(တောင်ႏကီꩻ)
# Author: သူထွန်း
---
my:
+ time:
+ formats:
+ friendly: |-
+ %e %B %Y
+ at %H:%M
helpers:
file:
prompt: ဖိုင်ရွေးပါ
cycleway_local: Pista tziclàbile locale
footway: Caminu pro pedones
rail: Ferrovia
+ train: Trenu
subway: Metropolitana
+ light_rail: Trenu lèbiu/metropolitana lèbia
+ tram: Tram
+ bus: Postale
cable_car: Funivia
chair_lift: ascensore carrotzina
runway: Pista de aeroportu
taxiway: carrera de furriada
apron: Àrea de parchègiu de sos aèreos
admin: Làcana amministrativa
+ capital: Capitale
+ city: Tzitade
+ vineyard: Bìngia
forest: Litu
wood: Buscu
+ farmland: Terras de coltivu
+ grass: Erba
+ meadow: Pradu
+ bare_rock: Roca nuda
+ sand: Arena
golf: Campu de golf
park: Parcu
common: Comunu
+ built_up: Zona fraigada
resident: Àrea de residèntzia
retail: Àrea cummertziale
industrial: Àrea industriale
commercial: Àrea cummertziale
heathland: Istruvina
lake: Lagu
- reservoir: riserva de abba
+ reservoir: Riserva de abba
farm: Fatoria
brownfield: Terrinu industriale abbandonadu
cemetery: Campusantu
allotments: Giardinos familiares
pitch: Campu isportivu
centre: Tzentru isportivu
+ beach: Marina
reserve: Reserva naturale
military: Zona militare
school: Iscola
- university: universidade
+ university: Universidade
+ hospital: Ispidale
building: Edifìtziu significativu
station: Istatzione ferroviària
+ tram_stop: Firmada de su tram
summit: Cùcuru de monte
- peak: cùcuru
+ peak: Cùcuru
tunnel: Lìnia trategiada = galleria
bridge: Oros nieddos = ponte
private: Atzessu privadu
destination: Atzessu pro sa destinatzione
construction: Caminos in costrutzione
+ bus_stop: Firmada de su postale
bicycle_shop: Butega de bitzicletas
bicycle_parking: Parchègiu pro bitzicletas
toilets: Còmodu
oauth2_applications: Aplicatziones OAuth 2
oauth2_authorizations: Autorizatziones OAuth 2
auth_providers:
+ openid_url: URL OpenID
+ openid_login_button: Sighi
openid:
title: Intra cun OpenID
- alt: Intra cun un'URL de OpenID
+ alt: Logotipu de OpenID
google:
title: Intra cun Google
- alt: Intra cun un'OpenID de Google
+ alt: Logotipu de Google
facebook:
title: Intra cun Facebook
- alt: Intra cun unu contu de Facebook
+ alt: Logotipu de Facebook
microsoft:
title: Intra cun Microsoft
- alt: Intra cun unu contu de Microsoft
+ alt: Logotipu de Microsoft
github:
title: Intra cun GitHub
- alt: Intra cun unu contu de GitHub
+ alt: Logotipu de GitHub
wikipedia:
title: Intra cun Wikipedia
- alt: Intra cun unu contu de Wikipedia
+ alt: Logotipu de Wikipedia
oauth:
permissions:
missing: No as cuntzèdidu s'atzessu a custa caraterìstica a s'aplicatzione
title: Sas aplicatziones autorizadas meas
application: Aplicatzione
permissions: Permissos
+ last_authorized: Ùrtima autorizatzione
no_applications_html: No as galu autorizadu peruna aplicatzione %{oauth2}.
application:
revoke: Rèvoca s'atzessu
users:
new:
title: Iscrie·ti
+ tab_title: Registrati·ti
+ signup_to_authorize_html: Registra·ti cun OpenStreetMap pro atzèdere a %{client_app_name}.
no_auto_account_create: A dolu mannu como non semus in gradu de creare unu contu
pro tene in automàticu.
please_contact_support_html: Cuntata %{support_link} pro fàghere in manera chi
su prus in presse chi podimus.
support: assistèntzia
about:
- header: Lìbera e modificàbile
+ header: Lìbera e modificàbile.
paragraph_1: A diferèntzia de àteras mapas, OpenStreetMap est totu realizada
dae persones che a tie e chie si siat la podet currègere, agiornare, iscarrigare
o impreare in manera lìbera.
- paragraph_2: Registra·ti pro incumintzare a contribuire. T'amus a imbiare
- una lìtera eletrònica pro cunfirmare su contu tuo.
+ paragraph_2: Registra·ti pro incumintzare a contribuire.
+ welcome: Bene bènnidu in OpenStreetMap
+ duplicate_social_email: Si tenes giai unu contu de OpenStreetMap e boles impreare
+ unu frunidore de identidade esternu, intra impreende sa crae tua e muda sas
+ impostaduras de su contu tuo.
display name description: Su nùmene de utente chi s'at a mustrare in manera
pùblica. Lu podes cambiare prus a tardu in sas preferèntzias.
by_signing_up:
reason: Resone de su blocu
status: Istadu
revoker_name: Revocadu dae
+ navigation:
+ all_blocks: Totu sos blocos
+ blocks_on_me: Blocos subra a mene
+ blocks_by_me: Blocos fatos dae mene
+ block: 'Blocu #%{id}'
+ new_block: Blocu nou
notes:
index:
title: Notas insertadas o cummentadas dae %{user}
intro: As agatadu una faddina o carchi cosa chi mancat? Informa sos àteros mapadores
a manera chi lu potzant acontzare. Move su marcadore a sa positzione curreta
e iscrie una nota pro descrìere su problema.
+ anonymous_warning_log_in: intra
+ anonymous_warning_sign_up: registra·ti
advice: Sa nota tua est pùblica e si podet impreare pro atualizare sa mapa,
duncas non nch'insertes peruna informatzione personale o informatzione de
mapas cun deretu de autore o elencos.
add: Annanghe una nota
notes_paging_nav:
showing_page: Pàgina %{page}
+ next: Imbeniente
+ previous: Antepostu
javascripts:
close: Serra
share:
destroy_button: Обриши
back: Назад
wrong_user: Пријављени сте као '%{user}', али порука коју сте желели да прочитате
- ниÑ\98е поÑ\81лаÑ\82а Ñ\82ом коÑ\80иÑ\81никÑ\83. Ð\9fÑ\80иÑ\98авиÑ\82е Ñ\81е као иÑ\81пÑ\80аван коÑ\80иÑ\81ник да биÑ\81Ñ\82е Ñ\98е
- прочитали.
+ ниÑ\81Ñ\82е поÑ\81лали ви или Ñ\82ом коÑ\80иÑ\81никÑ\83. Ð\9fÑ\80иÑ\98авиÑ\82е Ñ\81е као иÑ\81пÑ\80аван коÑ\80иÑ\81ник да
+ биÑ\81Ñ\82е Ñ\98е пÑ\80оÑ\87иÑ\82али.
sent_message_summary:
destroy_button: Обриши
heading:
min_changeset_comments_per_hour: 1
initial_changeset_comments_per_hour: 6
max_changeset_comments_per_hour: 60
+comments_to_max_changeset_comments: 200
moderator_changeset_comments_per_hour: 36000
# Rate limit for changes
min_changes_per_hour: 100
trace_icon_storage: "test"
# Lower some rate limits for testing
max_changeset_comments_per_hour: 10
+comments_to_max_changeset_comments: 20
moderator_changeset_comments_per_hour: 15
# Private key for signing id_tokens
doorkeeper_signing_key: |
--- /dev/null
+class BackfillNoteSubscriptions < ActiveRecord::Migration[7.2]
+ class NoteComment < ApplicationRecord; end
+ class NoteSubscription < ApplicationRecord; end
+
+ disable_ddl_transaction!
+
+ def up
+ attrs = %w[user_id note_id]
+
+ NoteComment.in_batches(:of => 1000) do |note_comments|
+ rows = note_comments.distinct.where.not(:author_id => nil).pluck(:author_id, :note_id)
+ NoteSubscription.upsert_all(rows.map { |r| attrs.zip(r).to_h })
+ end
+ end
+end
('23'),
('22'),
('21'),
+('20241023004427'),
('20241022141247'),
('20240913171951'),
('20240912181413'),
# abort on error
set -e
-# set locale to UTF-8 compatible. apologies to non-english speakers...
-locale-gen en_GB.utf8
-update-locale LANG=en_GB.utf8 LC_ALL=en_GB.utf8
-export LANG=en_GB.utf8
-export LC_ALL=en_GB.utf8
-
# make sure we have up-to-date packages
apt-get update
# install packages as explained in INSTALL.md
apt-get install -y ruby ruby-dev ruby-bundler \
libxml2-dev libxslt1-dev nodejs npm \
- build-essential git-core \
+ build-essential git-core firefox-esr \
postgresql postgresql-contrib libpq-dev libvips-dev libyaml-dev \
libsasl2-dev libffi-dev libgd-dev libarchive-dev libbz2-dev
npm install --global yarn
def test_create_comment_experienced_user_rate_limit
changeset = create(:changeset, :closed)
user = create(:user)
- create_list(:changeset_comment, 200, :author_id => user.id, :created_at => Time.now.utc - 1.day)
+ create_list(:changeset_comment, Settings.comments_to_max_changeset_comments, :author_id => user.id, :created_at => Time.now.utc - 1.day)
auth_header = bearer_authorization_header user
assert_equal user.display_name, js["properties"]["comments"].last["user"]
end
+ def test_comment_without_notifications_success
+ # Ensure that emails are sent to users
+ first_user = create(:user)
+ second_user = create(:user)
+ third_user = create(:user)
+
+ note_with_comments_by_users = create(:note) do |note|
+ create(:note_comment, :note => note, :author => first_user)
+ create(:note_comment, :note => note, :author => second_user)
+ end
+
+ auth_header = bearer_authorization_header third_user
+
+ assert_difference "NoteComment.count", 1 do
+ assert_difference "NoteSubscription.count", 1 do
+ assert_no_difference "ActionMailer::Base.deliveries.size" do
+ perform_enqueued_jobs do
+ post comment_api_note_path(note_with_comments_by_users, :text => "This is an additional comment", :format => "json"), :headers => auth_header
+ end
+ end
+ end
+ end
+ assert_response :success
+ js = ActiveSupport::JSON.decode(@response.body)
+ assert_not_nil js
+ assert_equal "Feature", js["type"]
+ assert_equal note_with_comments_by_users.id, js["properties"]["id"]
+ assert_equal "open", js["properties"]["status"]
+ assert_equal 3, js["properties"]["comments"].count
+ assert_equal "commented", js["properties"]["comments"].last["action"]
+ assert_equal "This is an additional comment", js["properties"]["comments"].last["text"]
+ assert_equal third_user.display_name, js["properties"]["comments"].last["user"]
+
+ subscription = NoteSubscription.last
+ assert_equal third_user, subscription.user
+ assert_equal note_with_comments_by_users, subscription.note
+
+ get api_note_path(note_with_comments_by_users, :format => "json")
+ assert_response :success
+ js = ActiveSupport::JSON.decode(@response.body)
+ assert_not_nil js
+ assert_equal "Feature", js["type"]
+ assert_equal note_with_comments_by_users.id, js["properties"]["id"]
+ assert_equal "open", js["properties"]["status"]
+ assert_equal 3, js["properties"]["comments"].count
+ assert_equal "commented", js["properties"]["comments"].last["action"]
+ assert_equal "This is an additional comment", js["properties"]["comments"].last["text"]
+ assert_equal third_user.display_name, js["properties"]["comments"].last["user"]
+
+ ActionMailer::Base.deliveries.clear
+ end
+
def test_comment_with_notifications_success
# Ensure that emails are sent to users
first_user = create(:user)
create(:note_comment, :note => note, :author => first_user)
create(:note_comment, :note => note, :author => second_user)
end
+ create(:note_subscription, :note => note_with_comments_by_users, :user => first_user)
+ create(:note_subscription, :note => note_with_comments_by_users, :user => second_user)
auth_header = bearer_authorization_header third_user
assert_redirected_to root_path
end
+ def test_login_remembered
+ user = create(:user)
+
+ post login_path, :params => { :username => user.display_name, :password => "test", :remember_me => "yes" }
+ assert_redirected_to root_path
+
+ assert_equal 28 * 86400, session[:_remember_for]
+ end
+
+ def test_login_not_remembered
+ user = create(:user)
+
+ post login_path, :params => { :username => user.display_name, :password => "test", :remember_me => "0" }
+ assert_redirected_to root_path
+
+ assert_nil session[:_remember_for]
+ end
+
def test_logout_without_referer
post logout_path
assert_redirected_to root_path
assert_select "h1 a[href='#{user_path active_block.user}']", :text => active_block.user.display_name
assert_select "h1 a[href='#{user_path active_block.creator}']", :text => active_block.creator.display_name
assert UserBlock.find(active_block.id).needs_view
+ end
- # Login as the blocked user
- session_for(active_block.user)
+ ##
+ # test clearing needs_view by showing a zero-hour block to the blocked user
+ def test_show_sets_deactivates_at_for_zero_hour_block
+ user = create(:user)
+ session_for(user)
- # Now viewing it should mark it as seen
- get user_block_path(:id => active_block)
- assert_response :success
- assert_not UserBlock.find(active_block.id).needs_view
+ freeze_time do
+ block = create(:user_block, :needs_view, :zero_hour, :user => user)
+ assert block.needs_view
+ assert_nil block.deactivates_at
+
+ travel 1.hour
+
+ get user_block_path(block)
+ assert_response :success
+ block.reload
+ assert_not block.needs_view
+ assert_equal Time.now.utc, block.deactivates_at
+
+ travel 1.hour
+
+ get user_block_path(block)
+ assert_response :success
+ block.reload
+ assert_not block.needs_view
+ assert_equal Time.now.utc - 1.hour, block.deactivates_at
+ end
+ end
+
+ ##
+ # test clearing needs_view by showing a timed block to the blocked user
+ def test_show_sets_deactivates_at_for_timed_block
+ user = create(:user)
+ session_for(user)
+
+ freeze_time do
+ block = create(:user_block, :needs_view, :created_at => Time.now.utc, :ends_at => Time.now.utc + 24.hours, :user => user)
+ assert block.needs_view
+ assert_nil block.deactivates_at
+
+ travel 1.hour
+
+ get user_block_path(block)
+ assert_response :success
+ block.reload
+ assert_not block.needs_view
+ assert_equal Time.now.utc + 23.hours, block.deactivates_at
+
+ travel 1.hour
+
+ get user_block_path(block)
+ assert_response :success
+ block.reload
+ assert_not block.needs_view
+ assert_equal Time.now.utc + 22.hours, block.deactivates_at
+
+ travel 24.hours
+
+ get user_block_path(block)
+ assert_response :success
+ block.reload
+ assert_not block.needs_view
+ assert_equal Time.now.utc - 2.hours, block.deactivates_at
+ end
end
##
--- /dev/null
+FactoryBot.define do
+ factory :note_subscription
+end