├── .dockerignore ├── .envrc ├── .github ├── dependabot.yml └── workflows │ └── docker.yml ├── .gitignore ├── .ruby-version ├── .tx ├── config └── config.yml ├── CLOUD-9-SETUP.md ├── DEMODALISATION-NOTES.md ├── DESIGN.md ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── MIGRATE.md ├── Makefile ├── Procfile ├── README.md ├── Rakefile ├── TROUBLESHOOTING.md ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── images │ │ ├── .keep │ │ ├── atlases-large.gif │ │ ├── failed.gif │ │ ├── field-large.gif │ │ ├── image-make-map-notes.png │ │ ├── image-make-maps-notes-2up.png │ │ ├── image-make-maps-only.png │ │ ├── logo-header.png │ │ ├── snapshots-large.gif │ │ └── ui │ │ │ ├── button-move-atlas-off.png │ │ │ ├── button-move-atlas-on.png │ │ │ ├── button-scale-atlas-off.png │ │ │ └── button-scale-atlas-on.png │ ├── javascripts │ │ ├── application.js │ │ ├── atlases.js │ │ ├── compose.js │ │ ├── fp │ │ │ ├── compose-menu.js │ │ │ ├── map │ │ │ │ ├── atlas-map.js │ │ │ │ ├── bound-boxes.js │ │ │ │ ├── compose.select.js │ │ │ │ ├── locator-map.js │ │ │ │ ├── map-options.js │ │ │ │ └── snapshot-map.js │ │ │ └── nav.js │ │ ├── leaflet │ │ │ ├── leaflet-draggable-any.js │ │ │ ├── leaflet-page-composer.js │ │ │ └── leaflet-zoomextras.js │ │ ├── pages.js │ │ ├── snapshots.js │ │ └── vendor │ │ │ └── leaflet-hash.js │ └── stylesheets │ │ ├── application.scss │ │ ├── atlases.scss │ │ ├── home.scss │ │ ├── pages.scss │ │ ├── partials │ │ ├── atlases.scss │ │ ├── base.scss │ │ ├── cards.scss │ │ ├── common.scss │ │ ├── compose.scss │ │ ├── helpers.scss │ │ ├── map-cards.scss │ │ ├── map.scss │ │ ├── nav.scss │ │ ├── pagination.scss │ │ └── snapshots.scss │ │ ├── snapshots.scss │ │ └── vendor │ │ └── skeleton.scss ├── controllers │ ├── application_controller.rb │ ├── atlases_controller.rb │ ├── compose_controller.rb │ ├── concerns │ │ └── .keep │ ├── home_controller.rb │ ├── pages_controller.rb │ └── snapshots_controller.rb ├── helpers │ ├── application_helper.rb │ ├── atlases_helper.rb │ ├── compose_helper.rb │ ├── devise_helper.rb │ ├── home_helper.rb │ ├── pages_helper.rb │ └── snapshots_helper.rb ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── atlas.rb │ ├── concerns │ │ └── .keep │ ├── page.rb │ ├── snapshot.rb │ └── user.rb └── views │ ├── atlases │ ├── _atlas.html.erb │ ├── _atlas_activity.html.erb │ ├── _atlas_failed.html.erb │ ├── _atlas_incomplete.html.erb │ ├── _atlas_snapshot.html.erb │ ├── index.html.erb │ ├── show.csv.erb │ ├── show.fpxml.builder │ ├── show.geojson.erb │ └── show.html.erb │ ├── compose │ └── new.html.erb │ ├── devise │ ├── confirmations │ │ └── new.html.erb │ ├── mailer │ │ ├── confirmation_instructions.html.erb │ │ ├── reset_password_instructions.html.erb │ │ └── unlock_instructions.html.erb │ ├── passwords │ │ ├── edit.html.erb │ │ └── new.html.erb │ ├── registrations │ │ ├── edit.html.erb │ │ └── new.html.erb │ ├── sessions │ │ └── new.html.erb │ ├── shared │ │ └── _links.html.erb │ └── unlocks │ │ └── new.html.erb │ ├── home │ ├── about.html.erb │ ├── advanced.fr.html.erb │ ├── advanced.html.erb │ ├── index.html.erb │ ├── make-canned-atlas-template.html.erb │ └── make-geojson-atlas-form.html.erb │ ├── layouts │ ├── application.html.erb │ └── big_map.html.erb │ ├── pages │ ├── show.fpxml.builder │ └── show.html.erb │ ├── shared │ ├── _footer.html.erb │ └── _navbar.html.erb │ └── snapshots │ ├── _failed.html.erb │ ├── _incomplete.html.erb │ ├── _snapshot.html.erb │ ├── create.js.erb │ ├── index.csv.erb │ ├── index.html.erb │ ├── new.html.erb │ ├── show.fpxml.builder │ ├── show.html.erb │ └── show.json.jbuilder ├── bin ├── _guard-core ├── annotate ├── bundle ├── foreman ├── guard ├── pry ├── rails ├── rake └── setup ├── config.ru ├── config ├── application.rb ├── boot.rb ├── capitals.dat ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── assets.rb │ ├── aws.rb │ ├── backtrace_silencers.rb │ ├── cookies_serializer.rb │ ├── devise.rb │ ├── fast_gettext.rb │ ├── fieldpapers.rb │ ├── filter_parameter_logging.rb │ ├── friendly_id.rb │ ├── i18n.rb │ ├── inflections.rb │ ├── kaminari_config.rb │ ├── mailer_defaults.rb │ ├── mime_types.rb │ ├── paperclip.rb │ ├── s3_direct_upload.rb │ ├── session_store.rb │ └── wrap_parameters.rb ├── locales │ ├── devise.en.yml │ └── en.yml ├── providers.json ├── puma.rb ├── routes.rb ├── secrets.yml └── storage.yml ├── db ├── migrate │ ├── 20150213010510_add_devise_to_users.rb │ ├── 20150214015436_clean_atlases.rb │ ├── 20150214015444_clean_pages.rb │ ├── 20150214015451_clean_snapshots.rb │ ├── 20150216203144_add_dimensions_to_atlases.rb │ ├── 20150219233932_rename_created_in_users.rb │ ├── 20150220070538_drop_logs.rb │ ├── 20150220070654_drop_forms.rb │ ├── 20150220071148_drop_messages.rb │ ├── 20150220071555_assign_ids_to_atlases.rb │ ├── 20150220071605_assign_ids_to_pages.rb │ ├── 20150220071615_assign_ids_to_snapshots.rb │ ├── 20150220071620_assign_ids_to_users.rb │ ├── 20150220071630_assign_ids_to_mb_tiles.rb │ ├── 20150220071639_assign_ids_to_notes.rb │ ├── 20150220071900_reverse_propagate_atlas_metadata.rb │ ├── 20150220074110_update_foreign_keys.rb │ ├── 20150221002228_add_unique_indexes_for_slugs.rb │ ├── 20150223220737_add_attachment_scene_to_snapshots.rb │ ├── 20150224193646_drop_text_from_pages.rb │ ├── 20150317220513_correct_atlas_provider.rb │ ├── 20150422001133_add_failed_at_to_atlases.rb │ ├── 20150430230647_add_bounds_to_snapshots.rb │ ├── 20150430231207_populate_snapshot_bounds.rb │ ├── 20150430232850_drop_geo_jpeg_bounds_from_snapshots.rb │ ├── 20150430233132_drop_decoding_json_from_snapshots.rb │ ├── 20150504234531_add_geo_tiff_url_to_snapshots.rb │ ├── 20150505010118_convert_snapshot_failed_to_timestamp.rb │ ├── 20150518231916_add_workflow_state_to_atlas.rb │ ├── 20150519044330_add_pdf_url_to_page.rb │ ├── 20150519224555_rename_print_href_to_page_url.rb │ ├── 20150519230420_add_workflow_state_to_snapshot.rb │ └── 20150528055308_update_atlas_pdf_urls.rb ├── mysql_fdw.sql ├── schema.rb └── seeds.rb ├── docker-compose.yml ├── lib ├── assets │ └── .keep ├── geocoder.rb ├── paper.rb ├── providers.rb └── tasks │ └── .keep ├── locale ├── app.pot ├── ar │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── cs │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── da │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── de │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── en │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── es │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── es_MX │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── fr │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── hu │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── id │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── it │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── ja │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── ku │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── nl │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── pl │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── pt │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── pt_BR │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── ru │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── sw │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── tl │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── uk │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── vi │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── zh_CN │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp └── zh_TW │ ├── app.edit.po │ ├── app.po │ └── app.po.time_stamp ├── log └── .keep ├── public ├── 404.html ├── 422.html ├── 500.html ├── favicon.ico └── robots.txt ├── requirements.txt ├── sample-production.env ├── sample.env ├── test ├── controllers │ ├── .keep │ ├── atlases_controller_test.rb │ ├── compose_controller_test.rb │ ├── home_controller_test.rb │ ├── pages_controller_test.rb │ └── snapshots_controller_test.rb ├── fixtures │ ├── .keep │ ├── atlases.yml │ ├── pages.yml │ ├── snapshots.yml │ └── users.yml ├── helpers │ └── .keep ├── integration │ └── .keep ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── atlas_test.rb │ ├── page_test.rb │ ├── snapshot_test.rb │ └── user_test.rb └── test_helper.rb └── vendor └── assets ├── javascripts └── .keep └── stylesheets └── .keep /.dockerignore: -------------------------------------------------------------------------------- 1 | .* 2 | docker-compose.yml 3 | log 4 | tmp 5 | vendor/bundle 6 | venv 7 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | PATH_add bin 2 | test -f .env && dotenv 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "bundler" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" 11 | - package-ecosystem: "docker" 12 | directory: "/" 13 | schedule: 14 | interval: "daily" 15 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: "Docker" 2 | on: 3 | push: 4 | branches: [ "main" ] 5 | pull_request: 6 | branches: [ "main" ] 7 | jobs: 8 | build: 9 | # disable workflow for now 10 | if: false 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: "finnp/create-file-action@2.0.0" 15 | env: 16 | FILE_NAME: ".env" 17 | FILE_DATA: "" 18 | - name: Run docker compose build 19 | run: docker compose build 20 | - name: Run docker compose up 21 | run: docker compose up --detach 22 | # - name: Set up database schema 23 | # run: docker-compose run web rake db:schema:load 24 | # - name: Run migrations 25 | # run: docker-compose run web rake db:migrate 26 | - name: Run docker compose down 27 | run: docker compose down 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all logfiles and tempfiles. 11 | /log/* 12 | !/log/.keep 13 | /tmp 14 | 15 | # Misc 16 | .bash_history 17 | vendor/bundle 18 | .env 19 | venv 20 | .tx/fieldpapers.www/ 21 | public/assets/ 22 | public/snapshots/ 23 | 24 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.1.3 2 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://app.transifex.com 3 | 4 | [o:fieldpapers:p:fieldpapers:r:en_app_po] 5 | source_file = locale/en/app.po 6 | source_lang = en 7 | file_filter = locale//app.po 8 | type = PO 9 | resource_name = fieldpapers.org -------------------------------------------------------------------------------- /.tx/config.yml: -------------------------------------------------------------------------------- 1 | git: 2 | filters: 3 | - filter_type: file 4 | file_format: PO 5 | source_file: locale/en/app.po 6 | source_language: en 7 | translation_files_expression: 'locale//app.po' -------------------------------------------------------------------------------- /CLOUD-9-SETUP.md: -------------------------------------------------------------------------------- 1 | 1. Make a new workspace (call it `fp-web`), cloning from the 2 | `Cadasta/fp-web` repo. 3 | 4 | 2. In the Cloud9 terminal, do: 5 | 6 | ``` 7 | rvm install ruby-2.2.3 8 | gem install bundle 9 | bundle install 10 | mysql-ctl start 11 | ``` 12 | 13 | 3. Then replace `config/database.yml` with: 14 | 15 | ``` 16 | development: 17 | adapter: mysql2 18 | encoding: utf8 19 | database: c9 20 | username: <%=ENV['C9_USER']%> 21 | host: <%=ENV['IP']%> 22 | ``` 23 | 24 | 4. Next, in the terminal, do: 25 | 26 | ``` 27 | bin/rake db:schema:load RAILS_ENV=development 28 | rails server -b $IP -p PORT 29 | ``` 30 | 31 | The Field Papers web app should then be visible at 32 | http://fp-web-ian-ross.c9users.io (based on using my GitHub user name, 33 | `ian-ross`). 34 | -------------------------------------------------------------------------------- /DESIGN.md: -------------------------------------------------------------------------------- 1 | # Design (Philosophy) 2 | 3 | Field Papers is built around a single [social 4 | object](http://gapingvoid.com/so/), the _Atlas_. 5 | 6 | Atlases consist of individual (typically printed) pages, including an index 7 | page which serves as a table of contents. Atlases may also be associated with 8 | _Snapshot_s, which are images of a particular page after it has interacted with 9 | the real world. 10 | 11 | CRUD Rails apps are typically comprised of (often nested) resources that map to 12 | conceptual objects such as atlases, prints, snapshots, and users. Each object 13 | is modeled by a Ruby object (`app/models`) and controlled both as a collection 14 | and as individual entities (`app/controllers`) and viewed by users or other 15 | applications (`app/views`). 16 | 17 | Actions that can be taken on a model or collection thereof are grouped into 18 | a single logical controller and assigned verbs according to use. 19 | 20 | Thus, an atlas can be _show_n, _update_d, _print_ed, etc. Individual pages can 21 | be _show_n. Snapshots can be _upload_ed (_create_d, really) or otherwise 22 | manipulated. 23 | 24 | Corralling actions into controllers that manipulate individual classes of model 25 | allows us to reduce the number of controllers necessary and to assist in the 26 | process of navigating an unfamiliar codebase. 27 | 28 | More complex workflows may not map as cleanly, hence the creation of such 29 | things as the `ComposeController` (`CompositionController`, to fit into the 30 | kingdom of nouns?) which aggregate stepwise functionality such as the 31 | multi-step process involved in composing an atlas. 32 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:3.1.3 2 | 3 | RUN \ 4 | apt-get update -qq && \ 5 | apt-get install -y build-essential nodejs zlib1g-dev default-libmysqlclient-dev libssl-dev && \ 6 | apt-get clean 7 | 8 | WORKDIR /app 9 | 10 | ADD Gemfile /app/Gemfile 11 | ADD Gemfile.lock /app/Gemfile.lock 12 | 13 | RUN bundle install -j4 14 | 15 | ADD . /app/ 16 | 17 | ENV PATH /app/bin:$PATH 18 | VOLUME /app/app /app/config /app/db /app/lib /app/locale /app/public /app/test 19 | 20 | CMD rm -f tmp/pids/server.pid && bundle exec rails server -b 0.0.0.0 21 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | ruby "3.1.3" 4 | 5 | ## standard dependencies 6 | 7 | # Bundle edge Rails instead: gem "rails", github: "rails/rails" 8 | gem "rails", "7.0.4.3" 9 | # Use SCSS for stylesheets 10 | gem "sass-rails", "~> 6.0.0" 11 | gem 'font-awesome-sass' 12 | # Use terser-ruby as compressor for JavaScript assets 13 | gem "terser", ">= 1.1.14" 14 | # Use jquery as the JavaScript library 15 | gem "jquery-rails" 16 | gem "selectize-rails" 17 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 18 | gem "jbuilder", "~> 2.11.5" 19 | # bundle exec rake doc:rails generates the API under doc/api. 20 | gem "sdoc", "~> 2.6.1", group: :doc 21 | 22 | ## explicit choices 23 | 24 | gem "aws-sdk-core", "~> 3" 25 | gem "aws-sdk-s3", '~> 1.120' 26 | gem 'aws-sdk-rails', '~> 3.7', '>= 3.7.1' 27 | gem "bootstrap-sass", "~> 3.4.1" 28 | gem "composite_primary_keys", "~> 14.0.6" 29 | gem "devise", "~> 4.9.2" # authentication 30 | gem "devise-i18n" # Devise translations 31 | gem "devise-i18n-views" # internationalized views for Devise 32 | gem "faraday" # http client 33 | gem "faraday_middleware" # response parsing, etc. 34 | gem "friendly_id", "~> 5.5.0" # alphanumeric slugs 35 | gem "gettext_i18n_rails", "~> 1.13.0" # gettext-style i18n 36 | gem "has_scope" # automatic filter generation 37 | gem "http_accept_language" 38 | gem "kaminari" # pagination 39 | gem "kaminari-i18n" 40 | gem "leaflet-rails", git: 'https://github.com/stamen/leaflet-rails' 41 | gem "paperclip", "~> 6.1.0" # file attachments 42 | gem "puma", "~> 6.6.0" # app server 43 | gem "rack-contrib" 44 | gem "rack-rewrite" # URL rewriting middleware 45 | gem "rails-i18n", "~> 7.0.6" 46 | gem "s3_direct_upload", git: 'https://github.com/waynehoover/s3_direct_upload' 47 | gem "mysql2", "~> 0.5.6" 48 | gem "workflow", "~> 3.0.0" 49 | gem "json", "~> 2.6.3" 50 | gem 'geo', git: 'https://github.com/ollie/geo-mercator.git' 51 | gem 'actionview-encoded_mail_to' 52 | gem 'mapbox-rails', git: 'https://github.com/aai/mapbox-rails' 53 | 54 | ## production-only dependencies 55 | 56 | group :production do 57 | gem "rails_12factor" # Heroku compatibility 58 | gem "sentry-ruby" # exception logging 59 | gem "sentry-rails" 60 | end 61 | 62 | ## development-only dependencies 63 | 64 | group :development do 65 | gem "annotate", "~> 3.2.0" # model annotation 66 | 67 | gem "gettext", ">= 3.4.3", require: false 68 | gem "guard" 69 | gem "guard-bundler", require: false 70 | gem "guard-livereload", "~> 2.5.2", require: false 71 | gem "guard-minitest" 72 | gem "foreman" 73 | 74 | # Access an IRB console on exception pages or by using <%= console %> in views 75 | gem "web-console", "~> 4.2.0" 76 | end 77 | 78 | group :development, :test do 79 | # Call "byebug" anywhere in the code to stop execution and get a debugger console 80 | gem "byebug" 81 | 82 | gem "meta_request" # to support https://github.com/dejan/rails_panel 83 | gem "minitest-reporters" 84 | 85 | gem "rake" 86 | end 87 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # A sample Guardfile 2 | # More info at https://github.com/guard/guard#readme 3 | 4 | ## Uncomment and set this to only include directories you want to watch 5 | # directories %w(app lib config test spec features) 6 | 7 | ## Uncomment to clear the screen before every task 8 | clearing :on 9 | 10 | ## Guard internally checks for changes in the Guardfile and exits. 11 | ## If you want Guard to automatically start up again, run guard in a 12 | ## shell loop, e.g.: 13 | ## 14 | ## $ while bundle exec guard; do echo "Restarting Guard..."; done 15 | ## 16 | ## Note: if you are using the `directories` clause above and you are not 17 | ## watching the project directory ('.'), then you will want to move 18 | ## the Guardfile to a watched dir and symlink it back, e.g. 19 | # 20 | # $ mkdir config 21 | # $ mv Guardfile config/ 22 | # $ ln -s config/Guardfile . 23 | # 24 | # and, you'll have to watch "config/Guardfile" instead of "Guardfile" 25 | 26 | guard 'annotate' do 27 | watch( 'db/schema.rb' ) 28 | 29 | # Uncomment the following line if you also want to run annotate anytime 30 | # a model file changes 31 | #watch( 'app/models/**/*.rb' ) 32 | 33 | # Uncomment the following line if you are running routes annotation 34 | # with the ":routes => true" option 35 | #watch( 'config/routes.rb' ) 36 | end 37 | 38 | guard :bundler do 39 | require 'guard/bundler' 40 | require 'guard/bundler/verify' 41 | helper = Guard::Bundler::Verify.new 42 | 43 | files = ['Gemfile'] 44 | files += Dir['*.gemspec'] if files.any? { |f| helper.uses_gemspec?(f) } 45 | 46 | # Assume files are symlinked from somewhere 47 | files.each { |file| watch(helper.real_path(file)) } 48 | end 49 | 50 | guard :minitest, spring: true do 51 | # with Minitest::Unit 52 | # watch(%r{^test/(.*)\/?test_(.*)\.rb$}) 53 | # watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" } 54 | # watch(%r{^test/test_helper\.rb$}) { 'test' } 55 | 56 | # with Minitest::Spec 57 | # watch(%r{^spec/(.*)_spec\.rb$}) 58 | # watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } 59 | # watch(%r{^spec/spec_helper\.rb$}) { 'spec' } 60 | 61 | # Rails 4 62 | watch(%r{^app/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" } 63 | watch(%r{^app/controllers/application_controller\.rb$}) { 'test/controllers' } 64 | watch(%r{^app/controllers/(.+)_controller\.rb$}) { |m| "test/integration/#{m[1]}_test.rb" } 65 | watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/#{m[1]}_mailer_test.rb" } 66 | watch(%r{^lib/(.+)\.rb$}) { |m| "test/lib/#{m[1]}_test.rb" } 67 | watch(%r{^test/.+_test\.rb$}) 68 | watch(%r{^test/test_helper\.rb$}) { 'test' } 69 | end 70 | 71 | guard 'livereload' do 72 | watch(%r{app/views/.+\.(erb|haml|slim)$}) 73 | watch(%r{app/helpers/.+\.rb}) 74 | watch(%r{public/.+\.(css|js|html)}) 75 | watch(%r{config/locales/.+\.yml}) 76 | # Rails Assets Pipeline 77 | watch(%r{(app|vendor)(/assets/\w+/(.+\.(css|js|html|png|jpg))).*}) { |m| "/assets/#{m[3]}" } 78 | end 79 | -------------------------------------------------------------------------------- /MIGRATE.md: -------------------------------------------------------------------------------- 1 | ```bash 2 | export AWS_ACCESS_KEY_ID= 3 | export AWS_SECRET_ACCESS_KEY= 4 | export PYTHONPATH=/usr/local/lib/python2.6/dist-packages/ 5 | 6 | cd /usr/local/fieldpapers/site/www/files/mbtiles 7 | aws s3 sync . s3://mbtiles.fieldpapers.org/ --exclude \* --include \*.mbtiles --acl public-read 8 | 9 | cd /usr/local/fieldpapers/site/www/files/scans 10 | aws s3 sync . s3://snapshots.fieldpapers.org/ --acl public-read 11 | 12 | cd /usr/local/fieldpapers/site/www/files/prints 13 | aws s3 sync . s3://atlases.fieldpapers.org/ --acl public-read 14 | ``` 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME = fieldpapers/web 2 | VERSION ?= latest 3 | 4 | default: 5 | docker run --rm \ 6 | -p 3000:3000 \ 7 | -v $$(pwd):/app \ 8 | --env-file .env \ 9 | $(NAME):$(VERSION) 10 | 11 | image: 12 | docker build --rm -t $(NAME):$(VERSION) . 13 | 14 | publish-image: 15 | docker push $(NAME):$(VERSION) 16 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec rails server -p $PORT 2 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /TROUBLESHOOTING.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | ## Error: client and server don't have same version (client : 1.17, server: 1.14) 4 | 5 | Your `docker` client and server versions differ (use `docker version` to see 6 | what versions are in use). This will upgrade both on OS X: 7 | 8 | ```bash 9 | brew update && \ 10 | brew upgrade docker boot2docker && \ 11 | boot2docker upgrade && \ 12 | boot2docker init 13 | ``` 14 | 15 | ## Post http:///var/run/docker.sock/v1.18/containers/create: dial unix /var/run/docker.sock: no such file or directory. Are you trying to connect to a TLS-enabled daemon without TLS? 16 | 17 | The `docker` client can't connect to an appropriate server. `$(boot2docker 18 | shellinit` will set relevant environment variables. If these (`DOCKER_HOST` 19 | etc.) are present, the `boot2docker` VM may not be running. 20 | 21 | ## The snapshot processor can't access the API 22 | 23 | First, check to see that the IP/hostname present in the PDF corresponds to your 24 | development instance (i.e. isn't `fieldpapers.org`; this can be configured by 25 | setting `BASE_URL`). Second, ensure that your docker containers can access 26 | it using that name. Using `.local` is a convenient way to do so and 27 | to survive DHCP renewals. 28 | 29 | ## I have a `.env` but my environment doesn’t reflect it 30 | 31 | Assuming you're also using `direnv`, the easiest way to do this is to exit the 32 | directory and reenter: `cd ..; cd -`. 33 | 34 | # ActiveRecord::AdapterNotSpecified: database configuration does not specify adapter 35 | 36 | `DATABASE_URL` probably isn't set. It should look like 37 | `mysql2://root@localhost/fieldpapers_development`. 38 | 39 | # `rails` won't start after complaining about missing gems 40 | 41 | First, make sure your gems are up-to-date by running `bundle`. If they are (and 42 | are installed globally), try removing `.bundle/config`, as it's likely 43 | preventing system gems from being loaded correctly. This is most likely to 44 | happen when running Field Papers with Docker. 45 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/images/atlases-large.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/app/assets/images/atlases-large.gif -------------------------------------------------------------------------------- /app/assets/images/failed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/app/assets/images/failed.gif -------------------------------------------------------------------------------- /app/assets/images/field-large.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/app/assets/images/field-large.gif -------------------------------------------------------------------------------- /app/assets/images/image-make-map-notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/app/assets/images/image-make-map-notes.png -------------------------------------------------------------------------------- /app/assets/images/image-make-maps-notes-2up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/app/assets/images/image-make-maps-notes-2up.png -------------------------------------------------------------------------------- /app/assets/images/image-make-maps-only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/app/assets/images/image-make-maps-only.png -------------------------------------------------------------------------------- /app/assets/images/logo-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/app/assets/images/logo-header.png -------------------------------------------------------------------------------- /app/assets/images/snapshots-large.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/app/assets/images/snapshots-large.gif -------------------------------------------------------------------------------- /app/assets/images/ui/button-move-atlas-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/app/assets/images/ui/button-move-atlas-off.png -------------------------------------------------------------------------------- /app/assets/images/ui/button-move-atlas-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/app/assets/images/ui/button-move-atlas-on.png -------------------------------------------------------------------------------- /app/assets/images/ui/button-scale-atlas-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/app/assets/images/ui/button-scale-atlas-off.png -------------------------------------------------------------------------------- /app/assets/images/ui/button-scale-atlas-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/app/assets/images/ui/button-scale-atlas-on.png -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. 9 | // 10 | // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require selectize 16 | //= require leaflet 17 | //= require s3_direct_upload 18 | //= requite mapbox.js 19 | //= require_directory ./fp 20 | //= require_tree . 21 | 22 | (function(exports){ 23 | "use strict"; 24 | 25 | var FP = exports.FP || (exports.FP = {}); 26 | 27 | // required both by atlases and snapshots; 28 | // therefore, this logic is in application.js 29 | FP.setUpJOSMClickHandler = function (errorMsg) { 30 | 31 | // handle JOSM link click with all required HTTP requests 32 | var josmLink = document.querySelector('#josm_link'); 33 | josmLink.addEventListener('click', function (event) { 34 | var dataRequestURL = event.currentTarget.dataset.dataRequest, 35 | tileRequestURL = event.currentTarget.dataset.tileRequest; 36 | 37 | if (!dataRequestURL) { return; } 38 | 39 | var errorHandler = function (errorEvent) { 40 | window.alert(errorMsg); 41 | }; 42 | 43 | // fire XHRs in sequence: 44 | // first the data request, 45 | // then the tile request. 46 | var dataRequest = new XMLHttpRequest(); 47 | dataRequest.addEventListener("error", errorHandler); 48 | dataRequest.addEventListener("load", function (loadEvent) { 49 | var tileRequest = new XMLHttpRequest(); 50 | tileRequest.addEventListener("error", errorHandler); 51 | tileRequest.open("GET", tileRequestURL); 52 | tileRequest.send(); 53 | }); 54 | 55 | dataRequest.open("GET", dataRequestURL); 56 | dataRequest.send(); 57 | }); 58 | 59 | }; 60 | 61 | })(this); 62 | -------------------------------------------------------------------------------- /app/assets/javascripts/atlases.js: -------------------------------------------------------------------------------- 1 | // Place all the behaviors and hooks related to the matching controller here. 2 | // All this logic will automatically be available in application.js. 3 | -------------------------------------------------------------------------------- /app/assets/javascripts/compose.js: -------------------------------------------------------------------------------- 1 | // Place all the behaviors and hooks related to the matching controller here. 2 | // All this logic will automatically be available in application.js. 3 | -------------------------------------------------------------------------------- /app/assets/javascripts/fp/compose-menu.js: -------------------------------------------------------------------------------- 1 | /* 2 | Javascript for accordion menu on /compose 3 | */ 4 | 5 | $(document).on('page:change', function(){ 6 | $(function(){ 7 | $('select#atlas_provider').selectize({ 8 | create: true, 9 | closeAfterSelect: true 10 | }); 11 | }); 12 | 13 | $('#atlas_layout').change(function(){ 14 | if ($('#atlas_layout').prop('checked')) { 15 | $('.notes-textarea').addClass('notes-textarea-visible'); 16 | } else { 17 | $('.notes-textarea').removeClass('notes-textarea-visible'); 18 | } 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /app/assets/javascripts/fp/map/bound-boxes.js: -------------------------------------------------------------------------------- 1 | (function(exports){ 2 | "use strict"; 3 | 4 | var FP = exports.FP || (exports.FP = {}); 5 | var map = FP.map || (FP.map = {}); 6 | 7 | // make a map showing bounding boxes of atlases as 8 | // squares 9 | map.boundingBoxes = function( selector, settings ) { 10 | var __ = {}, 11 | data = [], 12 | markers = []; 13 | 14 | var map = L.map(selector, FP.map.options).setView([0,0], 5); 15 | 16 | // TODO: factor FP.map.atlas.getAttribution into mixin or reusable util 17 | L.tileLayer(FP.map.utils.conformTemplate(settings.provider), { 18 | attribution: settings.providerSettings[0].options.attribution, 19 | maxZoom: 18 20 | }).addTo(map); 21 | 22 | function resizeToMarkers() { 23 | var bds; 24 | markers.forEach(function(marker){ 25 | 26 | if (!bds) { 27 | bds = marker.getBounds(); 28 | } else { 29 | bds.extend(marker.getBounds()); 30 | } 31 | 32 | }); 33 | 34 | if (bds.isValid()) map.fitBounds(bds); 35 | } 36 | 37 | function clearMarkers() { 38 | markers.forEach(function(marker){ 39 | map.removeLayer(marker); 40 | }); 41 | 42 | markers.length = 0; 43 | } 44 | 45 | function refresh() { 46 | clearMarkers(); 47 | 48 | data.forEach(function(item){ 49 | var m = L.polygon([ 50 | [item.north, item.west], 51 | [item.north, item.east], 52 | [item.south, item.east], 53 | [item.south, item.west] 54 | ], 55 | { 56 | className: 'atlas-boundingbox' 57 | }).addTo(map); 58 | 59 | markers.push(m); 60 | }); 61 | 62 | resizeToMarkers(); 63 | } 64 | 65 | __.data = function(_) { 66 | if (!_) return data; 67 | data = _; 68 | __.update(); 69 | return __; 70 | }; 71 | 72 | __.update = function(_) { 73 | refresh(); 74 | }; 75 | 76 | __.onresize = function(_) { 77 | __.update(); 78 | }; 79 | 80 | return __; 81 | }; 82 | 83 | 84 | 85 | })(this); -------------------------------------------------------------------------------- /app/assets/javascripts/fp/map/locator-map.js: -------------------------------------------------------------------------------- 1 | (function(exports){ 2 | "use strict"; 3 | 4 | var FP = exports.FP || (exports.FP = {}); 5 | var Map = FP.map || (FP.map = {}); 6 | 7 | // TODO: write something helpful 8 | function deriveCenter(settings) { 9 | if (settings.latlng && settings.latlng instanceof Array && settings.latlng.length === 2) return settings.latlng; 10 | 11 | // bbox = [west, south, east, north] 12 | if (settings.bbox && settings.bbox instanceof Array && settings.bbox.length === 4) { 13 | var sw = L.latLng(settings.bbox[1], settings.bbox[0]), 14 | ne = L.latLng(settings.bbox[3], settings.bbox[2]), 15 | bds = L.latLngBounds(sw, ne); 16 | if (bds.isValid()) return bds.getCenter(); 17 | } 18 | 19 | return [0,0]; 20 | } 21 | var locatorMapOptions = { 22 | dragging: false, 23 | touchZoom: false, 24 | scrollWheelZoom: false, 25 | doubleClickZoom: false, 26 | boxZoom: false, 27 | tap: false, 28 | keyboard: false, 29 | zoomControl: false, 30 | attributionControl: false 31 | }; 32 | 33 | Map.locator = function(selector, settings) { 34 | var __ = {}; 35 | var mapOptions = L.Util.extend({}, locatorMapOptions, FP.map.options); 36 | var center = deriveCenter(settings); 37 | var zoom = settings.zoom || 8; 38 | 39 | var map = L.map(selector, mapOptions).setView(center, zoom); 40 | 41 | /* TODO: do we want to show overlays? 42 | var providers = FP.map.utils.parseProvider(settings.provider); 43 | providers.forEach(function(provider){ 44 | L.tileLayer(provider.toLowerCase(), { 45 | attribution: '', 46 | maxZoom: 18 47 | }).addTo(map); 48 | }); 49 | */ 50 | 51 | L.tileLayer(FP.map.utils.conformTemplate(settings.provider), { 52 | attribution: '' 53 | }).addTo(map); 54 | 55 | // show a marker at center 56 | if (settings.showMarker) { 57 | var marker = L.circleMarker(center,{ 58 | stroke: true, 59 | color: 'black', 60 | weight: 2, 61 | opacity: 1.0, 62 | fill: true, 63 | fillColor: 'yellow', 64 | fillOpacity: 1.0, 65 | clickable: false, 66 | className: 'fp-locatormap-marker', 67 | radius: 4 68 | }).addTo(map); 69 | } 70 | 71 | return __; 72 | }; 73 | 74 | 75 | })(this); -------------------------------------------------------------------------------- /app/assets/javascripts/fp/map/map-options.js: -------------------------------------------------------------------------------- 1 | (function(exports){ 2 | "use strict"; 3 | 4 | var FP = exports.FP || (exports.FP = {}); 5 | var map = FP.map || (FP.map = {}); 6 | 7 | 8 | map.options = { 9 | scrollWheelZoom: true, 10 | attributionControl: true 11 | }; 12 | 13 | var utils = map.utils = {}; 14 | 15 | // Break apart a provider string from 16 | // an atlas that may contain multiple templates 17 | utils.parseProvider = function(str) { 18 | var re = /https?:\/\/.+?\.png|https?:\/\/.+?\.jpg/ig; 19 | var matches = str.match(re); 20 | return matches; 21 | }; 22 | 23 | // Simple check to see if string 24 | // fits Leaflet's template requirements 25 | // http://leafletjs.com/reference.html#tilelayer 26 | utils.isTemplateString = function(str) { 27 | return true; 28 | // var re = /^https?:\/\/(\{[s]\})?[\/\w\.\-\?\+\*_\|~:\[\]@#!\$'\(\),=&]*\{[zxy]\}\/\{[zxy]\}\/\{[zxy]\}[\/\w\.\-\?\+\*_\|~:\[\]@#!\$'\(\),=&]*(jpg|png)([\/\w\.\-\?\+\*_\|~:\[\]@#!\$'\(\),=&]*)?$/gi; 29 | // return re.test(str); 30 | }; 31 | 32 | utils.conformTemplate = function(template) { 33 | return template.replace('{S}','{s}').replace('{X}','{x}').replace('{Y}','{y}').replace('{Z}','{z}'); 34 | }; 35 | })(this); 36 | -------------------------------------------------------------------------------- /app/assets/javascripts/fp/map/snapshot-map.js: -------------------------------------------------------------------------------- 1 | (function(exports){ 2 | "use strict"; 3 | 4 | var FP = exports.FP || (exports.FP = {}); 5 | var Map = FP.map || (FP.map = {}); 6 | 7 | Map.snapshot = function(selector, url) { 8 | var map = L.map(selector, FP.map.options); 9 | 10 | $.getJSON(url, function(data) { 11 | // provide a default location in the hash, not the L.map (since that will 12 | // load before L.Hash takes control) 13 | if (!window.location.hash) { 14 | if (data.center) { 15 | window.location.hash = "#" + data.center.reverse().join("/"); 16 | } else { 17 | window.location.hash = "#13/37.8/-122.4"; 18 | } 19 | } 20 | 21 | new L.Hash(map); 22 | 23 | var options = { 24 | minZoom: data.minzoom || 0, 25 | maxZoom: data.maxzoom || 20 26 | }; 27 | 28 | var provider = data.tiles.pop(); 29 | 30 | var mediaQuery = "(-webkit-min-device-pixel-ratio: 1.5),\ 31 | (min--moz-device-pixel-ratio: 1.5),\ 32 | (-o-min-device-pixel-ratio: 3/2),\ 33 | (min-resolution: 1.5dppx)"; 34 | 35 | if (window.devicePixelRatio > 1 || 36 | (window.matchMedia && window.matchMedia(mediaQuery).matches)) { 37 | // replace the last "." with "@2x." 38 | provider = provider.replace(/\.(?!.*\.)/, "@2x.") 39 | } 40 | 41 | L.tileLayer(provider, options).addTo(map); 42 | }); 43 | }; 44 | 45 | 46 | })(this); 47 | -------------------------------------------------------------------------------- /app/assets/javascripts/fp/nav.js: -------------------------------------------------------------------------------- 1 | /* 2 | Javascript for nav menu 3 | */ 4 | 5 | (function(exports){ 6 | "use strict"; 7 | 8 | var FP = exports.FP || (exports.FP = {}); 9 | 10 | FP.nav = function() { 11 | var navBar = document.getElementById('nav'), 12 | menuToggle = document.getElementById('nav-toggle'); 13 | 14 | if (!navBar || !menuToggle) return; 15 | 16 | menuToggle.onclick = function(evt) { 17 | if (navBar) { 18 | navBar.className = (navBar.className === 'open') ? '' : 'open'; 19 | } 20 | }; 21 | }; 22 | 23 | 24 | })(this); -------------------------------------------------------------------------------- /app/assets/javascripts/leaflet/leaflet-zoomextras.js: -------------------------------------------------------------------------------- 1 | L.Control.ZoomExtras = L.Control.Zoom.extend({ 2 | options: { 3 | position: 'topleft', 4 | zoomInText: '+', 5 | zoomInTitle: 'Zoom in', 6 | zoomOutText: '-', 7 | zoomOutTitle: 'Zoom out', 8 | extras: [], 9 | extraMapDisabledEvents:[] 10 | }, 11 | 12 | onAdd: function (map) { 13 | var zoomName = 'leaflet-control-zoom', 14 | container = L.DomUtil.create('div', zoomName + ' leaflet-bar'), 15 | that = this; 16 | 17 | this._map = map; 18 | 19 | this._zoomInButton = this._createButton( 20 | this.options.zoomInText, this.options.zoomInTitle, 21 | zoomName + '-in', container, this._zoomIn, this); 22 | 23 | this._zoomOutButton = this._createButton( 24 | this.options.zoomOutText, this.options.zoomOutTitle, 25 | zoomName + '-out', container, this._zoomOut, this); 26 | 27 | this.options.extras.forEach(function(btn) { 28 | btn.instance = that._createButton(btn.text, btn.title, btn.klass, container, function() {return btn.onClick.call(that)}, that); 29 | }); 30 | 31 | this._updateDisabled(); 32 | 33 | map.on('zoomend zoomlevelschange', this._updateDisabled, this); 34 | 35 | this.options.extraMapDisabledEvents.forEach(function(evt){ 36 | map.on(evt, that._updateDisabled, that); 37 | }); 38 | 39 | 40 | return container; 41 | }, 42 | onRemove: function (map) { 43 | map.off('zoomend zoomlevelschange', this._updateDisabled, this); 44 | 45 | var that = this; 46 | this.options.extraMapDisabledEvents.forEach(function(evt){ 47 | map.off(evt, that._updateDisabled, that); 48 | }); 49 | }, 50 | 51 | _updateDisabled: function () { 52 | var map = this._map, 53 | className = 'leaflet-disabled', 54 | that = this; 55 | 56 | L.DomUtil.removeClass(this._zoomInButton, className); 57 | L.DomUtil.removeClass(this._zoomOutButton, className); 58 | 59 | if (map._zoom === map.getMinZoom()) { 60 | L.DomUtil.addClass(this._zoomOutButton, className); 61 | } 62 | if (map._zoom === map.getMaxZoom()) { 63 | L.DomUtil.addClass(this._zoomInButton, className); 64 | } 65 | 66 | this.options.extras.forEach(function(btn) { 67 | btn.onDisabled.call(that, btn.instance, className); 68 | }); 69 | } 70 | }); 71 | -------------------------------------------------------------------------------- /app/assets/javascripts/pages.js: -------------------------------------------------------------------------------- 1 | // Place all the behaviors and hooks related to the matching controller here. 2 | // All this logic will automatically be available in application.js. 3 | -------------------------------------------------------------------------------- /app/assets/javascripts/snapshots.js: -------------------------------------------------------------------------------- 1 | // Place all the behaviors and hooks related to the matching controller here. 2 | // All this logic will automatically be available in application.js. -------------------------------------------------------------------------------- /app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | //= require leaflet 2 | //= require s3_direct_upload_progress_bars 3 | //= require mapbox 4 | //= require selectize 5 | 6 | @import "partials/base"; 7 | @import "partials/nav"; 8 | @import "partials/pagination"; 9 | @import "partials/common"; 10 | @import "partials/cards"; 11 | @import "partials/map-cards"; 12 | @import "partials/map"; 13 | @import "partials/compose.scss"; 14 | @import "partials/atlases.scss"; 15 | @import "partials/snapshots.scss"; 16 | @import "partials/helpers"; 17 | @import "font-awesome"; 18 | -------------------------------------------------------------------------------- /app/assets/stylesheets/atlases.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the atlases controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/home.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the home controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | 5 | -------------------------------------------------------------------------------- /app/assets/stylesheets/pages.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Pages controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/partials/atlases.scss: -------------------------------------------------------------------------------- 1 | /* 2 | All styles under 'atlases' path 3 | */ 4 | 5 | .atlases-activity ul { 6 | margin-left: 40px; 7 | } 8 | .atlases-activity li p { 9 | margin: 0; 10 | } 11 | .atlases-activity .details { 12 | color: #aaa; 13 | margin-left: 1.5rem; 14 | 15 | -ms-word-break: break-all; 16 | word-break: break-word; 17 | word-wrap: break-word; 18 | -webkit-hyphens: auto; 19 | -moz-hyphens: auto; 20 | hyphens: auto; 21 | } 22 | 23 | .leaflet-pagelayout-container { 24 | position: absolute; 25 | height: 100%; 26 | width: 100%; 27 | z-index: 1; 28 | background: transparent; 29 | } 30 | 31 | .leaflet-pagelayout-container .page { 32 | position: absolute; 33 | border: 1px solid black; 34 | } 35 | .leaflet-pagelayout-container .page.outer-top { 36 | border-top-width: 3px; 37 | } 38 | .leaflet-pagelayout-container .page.outer-bottom { 39 | border-bottom-width: 3px; 40 | } 41 | .leaflet-pagelayout-container .page.outer-left { 42 | border-left-width: 3px; 43 | } 44 | .leaflet-pagelayout-container .page.outer-right { 45 | border-right-width: 3px; 46 | } 47 | 48 | .leaflet-pagelayout-container .page.no-top { 49 | border-top: 0; 50 | } 51 | .leaflet-pagelayout-container .page.no-bottom { 52 | border-bottom: 0; 53 | } 54 | .leaflet-pagelayout-container .page.no-left { 55 | border-left: 0; 56 | } 57 | .leaflet-pagelayout-container .page.no-right { 58 | border-right: 0; 59 | } 60 | 61 | .leaflet-pagelayout-container .page-link { 62 | display: block; 63 | position: relative; 64 | text-decoration: none; 65 | width: 100%; 66 | height: 100%; 67 | color: #000 !important; 68 | } 69 | 70 | .leaflet-pagelayout-container .page-link:hover { 71 | background: transparent; 72 | background: rgba(255,255,255,0.3); 73 | } 74 | 75 | .leaflet-pagelayout-container .page-label{ 76 | display: inline; 77 | position: absolute; 78 | padding: 2px 4px; 79 | left: 50%; 80 | top: 50%; 81 | background: white; 82 | background: rgba(255,255,255,0.7); 83 | font-weight: bold; 84 | transform: translateX(-50%) translateY(-50%); 85 | } -------------------------------------------------------------------------------- /app/assets/stylesheets/partials/base.scss: -------------------------------------------------------------------------------- 1 | // Define global variables here 2 | 3 | 4 | // overide skeleton variables here 5 | $link-color: #09F; 6 | $link-visited-color: #339; 7 | 8 | $success-color: #dff0d8; 9 | $info-color: #d9edf7; 10 | $warning-color: #fcf8e3; 11 | $error-color: #f2dede; 12 | $hover-color: #fafafa; 13 | 14 | // bump up min-width to test in browser 15 | $bp-larger-than-mobile : "min-width: 401px" !default; 16 | 17 | 18 | // import skeleton 19 | @import "vendor/skeleton"; 20 | -------------------------------------------------------------------------------- /app/assets/stylesheets/partials/cards.scss: -------------------------------------------------------------------------------- 1 | /* 2 | Home page cards 3 | */ 4 | 5 | .cards .four.columns { 6 | width: 100%; 7 | margin-left: 0; 8 | 9 | @media (#{$bp-larger-than-tablet}) { 10 | width: grid-column-width(4); 11 | margin-left: $column-margin; 12 | } 13 | } 14 | 15 | .cards .four.columns:first-child { 16 | margin-left: 0; 17 | } 18 | 19 | .card { 20 | margin-bottom: 20px; 21 | text-align: left; 22 | 23 | @media (#{$bp-larger-than-tablet}) { 24 | margin-bottom: 0; 25 | text-align: center; 26 | } 27 | } 28 | 29 | .card-details { 30 | display: block; 31 | text-align: center; 32 | 33 | @media (#{$bp-larger-than-phablet}) { 34 | display: inline-block; 35 | vertical-align: middle; 36 | text-align: left; 37 | } 38 | 39 | @media (#{$bp-larger-than-tablet}) { 40 | display: block; 41 | text-align: center; 42 | } 43 | } 44 | 45 | .card-image { 46 | display: block; 47 | 48 | @media (#{$bp-larger-than-phablet}) { 49 | display: inline-block; 50 | vertical-align: middle; 51 | } 52 | 53 | @media (#{$bp-larger-than-tablet}) { 54 | display: block; 55 | } 56 | } 57 | 58 | .card-image img { 59 | border: none; 60 | width: 100%; 61 | height: auto; 62 | max-height: initial; 63 | 64 | @media (#{$bp-larger-than-phablet}) { 65 | max-height: 150px; 66 | width: auto; 67 | } 68 | 69 | @media (#{$bp-larger-than-tablet}) { 70 | width: 100%; 71 | height: auto; 72 | max-height: initial; 73 | } 74 | 75 | } 76 | 77 | .card-title { 78 | margin: 0 0 10px 0; 79 | font-size: 2rem; 80 | font-weight: 600; 81 | } 82 | 83 | .card-desc { 84 | margin: 0; 85 | } -------------------------------------------------------------------------------- /app/assets/stylesheets/partials/helpers.scss: -------------------------------------------------------------------------------- 1 | /* 2 | Misc classes that provide some sort of minor embellishment 3 | */ 4 | 5 | .relative { 6 | position: relative; 7 | } 8 | 9 | .embolden { 10 | font-weight: 800; 11 | } 12 | 13 | .align-center { 14 | text-align: center; 15 | } 16 | 17 | .align-center.small-screen { 18 | text-align: left; 19 | } 20 | 21 | .pad-bottom { 22 | margin-bottom: 20px; 23 | } 24 | 25 | .pad-bottom.double { 26 | margin-bottom: 40px; 27 | } 28 | 29 | .auto-height { 30 | height: auto !important; 31 | } 32 | 33 | 34 | @media (#{$bp-larger-than-phablet}) { 35 | .align-center.small-screen { 36 | text-align: center; 37 | } 38 | } 39 | 40 | .align-left { 41 | text-align: left; 42 | } 43 | 44 | .align-right { 45 | text-align: right; 46 | } 47 | 48 | .small { 49 | font-size: 1.3rem; 50 | line-height: 1.5; 51 | letter-spacing: 0; 52 | } 53 | 54 | .no-wrap { 55 | white-space: nowrap; 56 | /*overflow: hidden; */ 57 | } 58 | 59 | .no-select { 60 | -webkit-user-select: none; /* Chrome all / Safari all */ 61 | -moz-user-select: none; /* Firefox all */ 62 | -ms-user-select: none; /* IE 10+ */ 63 | 64 | /* No support for these yet, use at own risk */ 65 | -o-user-select: none; 66 | user-select: none; 67 | } 68 | 69 | .indent { 70 | margin-left: 4rem; 71 | } 72 | 73 | .status-text.msg { 74 | padding: 10px; 75 | } 76 | 77 | .status-text.success.msg { 78 | background: $success-color; 79 | } 80 | 81 | .status-text.info.msg { 82 | background: $info-color; 83 | } 84 | 85 | .status-text.warning.msg { 86 | background: $warning-color; 87 | } 88 | 89 | .status-text.error.msg { 90 | background: $error-color; 91 | } 92 | 93 | .no-margin-left {margin-left: 0 !important;} 94 | .col-width-only { width: 100%; } 95 | 96 | 97 | @media (#{$bp-larger-than-phablet}) { 98 | .one.col-width-only { width: grid-column-width(1); } 99 | .two.col-width-only { width: grid-column-width(2); } 100 | .three.col-width-only { width: grid-column-width(3); } 101 | .four.col-width-only { width: grid-column-width(4); } 102 | .five.col-width-only { width: grid-column-width(5); } 103 | .six.col-width-only { width: grid-column-width(6); } 104 | .seven.col-width-only { width: grid-column-width(7); } 105 | .eight.col-width-only { width: grid-column-width(8); } 106 | .nine.col-width-only { width: grid-column-width(9); } 107 | .ten.col-width-only { width: grid-column-width(10); } 108 | .eleven.col-width-only { width: grid-column-width(11); } 109 | .twelve.col-width-only { width: 100%; margin-left: 0; } 110 | 111 | .one-third.col-width-only { width: grid-column-width(4); } 112 | .two-thirds.col-width-only { width: grid-column-width(8); } 113 | .one-half.col-width-only { width: grid-column-width(6); } 114 | } 115 | 116 | .table {display: table;} 117 | .align-center .table {margin: 0 auto;} 118 | .table .tr {display: table-row;} 119 | .table .tc {display: table-cell;} -------------------------------------------------------------------------------- /app/assets/stylesheets/partials/map-cards.scss: -------------------------------------------------------------------------------- 1 | $column-counts: 3; 2 | 3 | .map-cards { 4 | margin-top: 2rem; 5 | list-style: none; 6 | @media (#{$bp-larger-than-tablet}) {} 7 | } 8 | 9 | .map-card { 10 | display: block; 11 | margin-bottom:15px; 12 | padding: 15px; 13 | border: 2px solid #FAFAFA; 14 | box-shadow: 0 1px 2px rgba(34, 25, 25, 0.4); 15 | background: #FEFEFE; 16 | background-image: linear-gradient(45deg, #FFF, #F9F9F9); 17 | color: #666; 18 | vertical-align: top; 19 | 20 | @media (#{$bp-larger-than-tablet}) {} 21 | } 22 | 23 | .map-card:first-child, 24 | .map-card:nth-child(3n+1) { 25 | margin-left: 0 !important; 26 | } 27 | 28 | .map-card .no-wrap { 29 | overflow: hidden !important; 30 | } 31 | 32 | .map-card-preview { 33 | display: block; 34 | width: auto; 35 | height: auto; 36 | margin: 0 0 10px 0; 37 | padding: 0; 38 | text-decoration: none !important; 39 | font-size: 0; 40 | } 41 | 42 | .map-card-preview img { 43 | display: block; 44 | margin: 0 auto 0 auto; 45 | padding: 0; 46 | width: auto; 47 | max-width: 100%; 48 | height: 240px; 49 | border: 0; 50 | 51 | @media (#{$bp-larger-than-tablet}) { 52 | //height: 148px; 53 | } 54 | } 55 | 56 | .map-card-preview.static-map > div { 57 | height: 240px; 58 | } 59 | .map-card-preview .leaflet-container { 60 | cursor: pointer; 61 | } 62 | 63 | 64 | .map-card-pages, 65 | .map-card-title { 66 | margin-right: 2px; 67 | } 68 | 69 | .atlas-status { 70 | display: inline-block; 71 | width: 8px; 72 | height: 8px; 73 | margin-right: 3px; 74 | background: green; 75 | border-radius: 8px; 76 | } 77 | 78 | .atlas-status.failed { 79 | background: red; 80 | } 81 | 82 | .atlas-status.incomplete { 83 | background: yellow; 84 | } 85 | -------------------------------------------------------------------------------- /app/assets/stylesheets/partials/nav.scss: -------------------------------------------------------------------------------- 1 | /* 2 | Navbar 3 | */ 4 | 5 | .logo { 6 | text-align: left; 7 | padding-top: 5px; 8 | border: none; 9 | } 10 | 11 | #nav { 12 | margin-bottom: 2.5rem; 13 | } 14 | 15 | #nav-toggle { 16 | display: block; 17 | margin-top: 10px; 18 | 19 | @media (#{$bp-larger-than-tablet}) { 20 | display: none; 21 | } 22 | } 23 | 24 | .navbar-container { 25 | position: relative; 26 | } 27 | 28 | .navbar { 29 | display: none; 30 | list-style: none; 31 | margin-top: 10px; 32 | margin-bottom: 0; 33 | 34 | position: absolute; 35 | background: white; 36 | width: 100%; 37 | top: 50px; 38 | left: 0; 39 | z-index: 1; 40 | 41 | @media (#{$bp-larger-than-tablet}) { 42 | position: relative; 43 | display: table; 44 | width: auto; 45 | top: 0; 46 | left: 0; 47 | } 48 | } 49 | 50 | .open .navbar { 51 | display: table !important; 52 | } 53 | 54 | .nav-item { 55 | display: block; 56 | margin-right: 0; 57 | float: none; 58 | width: 100%; 59 | line-height: 20px; 60 | border-bottom: 2px solid #fff; 61 | padding-bottom: 2px; 62 | 63 | @media (#{$bp-larger-than-tablet}) { 64 | margin-right: 30px; 65 | float: left; 66 | width: auto; 67 | } 68 | } 69 | .nav-item.active { 70 | border-bottom-color: #000; 71 | } 72 | 73 | .nav-item:last-child { 74 | margin-right: 0; 75 | } 76 | 77 | .nav-item.in { 78 | border-bottom: 2px solid black; 79 | } 80 | 81 | .nav-item a, 82 | .nav-item a:link, 83 | .nav-item a:visited { 84 | color: #000; 85 | text-decoration: none; 86 | } 87 | 88 | .nav-item a:hover { 89 | text-decoration: none; 90 | color: $link-color; 91 | } 92 | 93 | .nav-item .section { 94 | font-weight: bold; 95 | font-style:italic; 96 | font-size: 14px; 97 | } 98 | 99 | .nav-item .desc { 100 | margin-top: 0; 101 | font-size: 11px; 102 | color: #666; 103 | } 104 | -------------------------------------------------------------------------------- /app/assets/stylesheets/partials/pagination.scss: -------------------------------------------------------------------------------- 1 | .pagination { 2 | margin-top: 2rem; 3 | 4 | .first { 5 | 6 | } 7 | 8 | .next { 9 | 10 | } 11 | 12 | .last { 13 | 14 | } 15 | 16 | .page { 17 | 18 | } 19 | 20 | .page.current { 21 | 22 | } 23 | 24 | .page.gap { 25 | 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/assets/stylesheets/partials/snapshots.scss: -------------------------------------------------------------------------------- 1 | .status-text, 2 | .upload, 3 | .uploading .status-text.default, 4 | .uploading .upload-form, 5 | .uploading .status-text.error, 6 | .error .status-text.default, 7 | .error .upload { 8 | display: none; 9 | } 10 | 11 | .status-text.default, 12 | .error .status-text.error, 13 | .uploading .status-text.uploading, 14 | .uploading .upload { 15 | display: block; 16 | } 17 | 18 | .upload { 19 | border: 0; 20 | margin-bottom: 2.5rem; 21 | padding-top: 0; 22 | margin-top: 0; 23 | } 24 | 25 | .error.msg { 26 | margin-top: -2rem; 27 | } -------------------------------------------------------------------------------- /app/assets/stylesheets/snapshots.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the snapshots controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | before_action :redirect_www 3 | 4 | before_action :set_locale 5 | 6 | # Prevent CSRF attacks by raising an exception. 7 | # For APIs, you may want to use :null_session instead. 8 | protect_from_forgery with: :exception 9 | 10 | # per https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-sign-in-using-their-username-or-email-address 11 | before_action :configure_permitted_parameters, if: :devise_controller? 12 | 13 | protected 14 | 15 | def configure_permitted_parameters 16 | devise_parameter_sanitizer.permit(:sign_up) { |u| u.permit(:username, :email, :password, :password_confirmation, :remember_me) } 17 | devise_parameter_sanitizer.permit(:sign_in) { |u| u.permit(:login, :username, :email, :password, :remember_me) } 18 | devise_parameter_sanitizer.permit(:account_update) { |u| u.permit(:username, :email, :password, :password_confirmation, :current_password) } 19 | end 20 | 21 | def set_locale 22 | cookies[:locale] = params[:locale] if params[:locale] 23 | 24 | http_accept_language.user_preferred_languages.unshift cookies[:locale] if cookies[:locale] 25 | 26 | I18n.locale = http_accept_language.compatible_language_from(I18n.available_locales) 27 | end 28 | 29 | def redirect_www 30 | if request.host.starts_with?('www.') 31 | redirect_to "https://#{request.host.sub('www.', '')}#{request.fullpath}", status: 301 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /app/controllers/atlases_controller.rb: -------------------------------------------------------------------------------- 1 | require "csv" 2 | require "providers" 3 | require "sentry-ruby" 4 | 5 | class AtlasesController < ApplicationController 6 | # filters 7 | 8 | has_scope :date, only: :index 9 | has_scope :month, only: :index 10 | has_scope :place, only: :index 11 | has_scope :user, only: :index 12 | has_scope :username, only: :index 13 | 14 | # allow API usage 15 | skip_before_action :verify_authenticity_token, only: :update 16 | 17 | def index 18 | @atlases = apply_scopes(Atlas.unscoped).default.by_creator(current_user).page(params[:page]) 19 | @counts = apply_scopes(Atlas.unscoped).default.by_creator(current_user).count('id') 20 | end 21 | 22 | def show 23 | @atlas = Atlas.unscoped.friendly.find(params[:id]) 24 | 25 | respond_to do |format| 26 | format.html 27 | format.pdf { 28 | # convenience redirect if "pdf" was provided as an extension 29 | return redirect_to @atlas.pdf_url if @atlas.pdf_url 30 | raise ActionController::RoutingError.new("Not Found") 31 | } 32 | 33 | format.csv do 34 | headers["Content-Type"] ||= "text/csv" 35 | end 36 | 37 | format.geojson do 38 | headers["Content-Type"] ||= "application/geo+json; charset=UTF-8" 39 | end 40 | end 41 | end 42 | 43 | def update 44 | atlas = Atlas.unscoped.find_by_slug(params[:id]) 45 | 46 | if params[:task] && params[:error] 47 | logger.warn(params[:error][:message]) 48 | logger.warn(params[:error][:stack]) 49 | Sentry.capture_message(params[:error][:message], extra: { 50 | stack: params[:error][:stack], 51 | atlas: atlas.slug, 52 | }) 53 | 54 | atlas.fail! 55 | atlas.save! 56 | elsif params[:task] == "merge_pages" 57 | # this is a callback from our renderer 58 | atlas.update!(atlas_params) 59 | atlas.merged! 60 | atlas.save! 61 | else 62 | atlas.update!(atlas_params) 63 | end 64 | 65 | respond_to do |format| 66 | format.html { 67 | redirect_to atlas_url(atlas) 68 | } 69 | 70 | format.json { 71 | render status: 201, json: true 72 | } 73 | end 74 | end 75 | 76 | private 77 | 78 | def atlas_params 79 | params.require(:atlas).permit(:pdf_url, :slug) 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | class HomeController < ApplicationController 2 | end 3 | -------------------------------------------------------------------------------- /app/controllers/pages_controller.rb: -------------------------------------------------------------------------------- 1 | require "sentry-ruby" 2 | 3 | class PagesController < ApplicationController 4 | # allow API usage 5 | skip_before_action :verify_authenticity_token, only: :update 6 | 7 | def show 8 | @atlas = Atlas.unscoped.friendly.find(params[:id]) 9 | @page = @atlas.pages.find_by_page_number(params[:page_number]) 10 | end 11 | 12 | def update 13 | atlas = Atlas.unscoped.friendly.find(params[:id]) 14 | page = atlas.pages.find_by_page_number(params[:page_number]) 15 | 16 | if params[:task] && params[:error] 17 | logger.warn(params[:error][:message]) 18 | logger.warn(params[:error][:stack]) 19 | Sentry.capture_message(params[:error][:message], extra: { 20 | stack: params[:error][:stack], 21 | atlas: atlas.slug, 22 | page: page.page_number, 23 | }) 24 | 25 | page.atlas.fail! 26 | page.atlas.save! 27 | elsif ["render_page", "render_index"].include? params[:task] 28 | # this is a callback from our renderer 29 | page.update!(page_params.merge(composed_at: Time.now)) 30 | page.atlas.rendered! 31 | page.atlas.save! 32 | else 33 | page.update!(page_params) 34 | end 35 | 36 | respond_to do |format| 37 | format.html { 38 | redirect_to atlas_page_atlas_url(page.atlas, page.page_number) 39 | } 40 | 41 | format.json { 42 | render status: 201, json: {} 43 | } 44 | end 45 | end 46 | 47 | private 48 | 49 | def page_params 50 | # :atlas param was causing some issues and doesn't seem to be needed so just delete it 51 | params[:page].delete :atlas 52 | params.require(:page).permit(:page_number, :pdf_url, :geotiff_url) 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /app/controllers/snapshots_controller.rb: -------------------------------------------------------------------------------- 1 | require "sentry-ruby" 2 | 3 | class SnapshotsController < ApplicationController 4 | has_scope :date, only: :index 5 | has_scope :month, only: :index 6 | has_scope :place, only: :index 7 | has_scope :user, only: :index 8 | has_scope :username, only: :index 9 | 10 | # allow API usage 11 | skip_before_action :verify_authenticity_token, only: [:create, :update] 12 | 13 | def new 14 | @snapshot = Snapshot.new 15 | end 16 | 17 | # @http_method XHR POST 18 | # @url /snapshots 19 | def create 20 | @snapshot = Snapshot.create!(snapshot_upload_params) 21 | @snapshot.process! 22 | 23 | redirect_to snapshot_url(@snapshot) unless request.xhr? 24 | end 25 | 26 | def index 27 | @snapshots = apply_scopes(Snapshot.unscoped).default.by_creator(current_user).page(params[:page]) 28 | @counts = apply_scopes(Snapshot.unscoped).default.by_creator(current_user).count('id') 29 | 30 | respond_to do |format| 31 | format.html 32 | 33 | # the grid CSV only makes sense if this is scoped beneath an atlas 34 | if params[:atlas_id] 35 | @atlas = Atlas.unscoped.friendly.find(params[:atlas_id]) 36 | format.csv do 37 | headers["Content-Type"] ||= "text/csv" 38 | end 39 | end 40 | end 41 | end 42 | 43 | def show 44 | @snapshot = Snapshot.unscoped.friendly.find(params[:id]) 45 | end 46 | 47 | def update 48 | snapshot = Snapshot.unscoped.friendly.find(params[:id]) 49 | 50 | if params[:task] && params[:error] 51 | logger.warn(params[:error][:message]) 52 | logger.warn(params[:error][:stack]) 53 | Sentry.capture_message(params[:error][:message], extra: { 54 | stack: params[:error][:stack], 55 | snapshot: snapshot.slug, 56 | }) 57 | 58 | snapshot.fail! 59 | snapshot.save! 60 | elsif params[:task] == "process_snapshot" 61 | # this is a callback from our renderer 62 | snapshot.update!(snapshot_update_params) 63 | snapshot.processed! 64 | snapshot.save! 65 | elsif params[:task] == "fetch_snapshot_metadata" 66 | # this is a callback from our renderer 67 | snapshot.update!(snapshot_update_params) 68 | snapshot.metadata_fetched! 69 | snapshot.save! 70 | else 71 | snapshot.update!(snapshot_update_params) 72 | end 73 | 74 | respond_to do |format| 75 | format.html { 76 | redirect_to snapshot_url(snapshot) 77 | } 78 | 79 | format.json { 80 | render status: 201, json: true 81 | } 82 | end 83 | end 84 | 85 | private 86 | 87 | def snapshot_upload_params 88 | case FieldPapers::PERSIST 89 | when "local" 90 | params.require(:snapshot) 91 | .permit(:scene) 92 | .merge(uploader: current_user) 93 | when "s3" 94 | params.require(:snapshot) 95 | .permit(:s3_scene_url) 96 | .merge(scene_file_name: params.permit(:filepath)[:filepath], 97 | scene_content_type: params.permit(:filetype)[:filetype], 98 | scene_file_size: params.permit(:filesize)[:filesize]) 99 | .merge(uploader: current_user) 100 | end 101 | end 102 | 103 | def snapshot_update_params 104 | params.require(:snapshot) 105 | .permit(:geotiff_url, :page_url, :private, :zoom, bbox: []) 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | 3 | # Used to set the active navbar item 4 | def current_nav_target 5 | target = "" 6 | if controller_name == "compose" 7 | target = "compose" 8 | elsif controller_name == 'snapshots' && action_name == 'new' 9 | target = 'upload' 10 | elsif ["snapshots", "atlases"].include? controller_name 11 | target = 'watch' 12 | elsif controller_name == 'home' && action_name == 'advanced' 13 | target = 'extend' 14 | elsif ["sessions","registrations","confirmations"].include? controller_name 15 | target = 'sessions' 16 | end 17 | return target 18 | end 19 | 20 | def id_link(zoom, lon, lat, slug = nil) 21 | if slug 22 | "#{FieldPapers::OSM_BASE_URL}/edit#background=custom:#{FieldPapers::TILE_BASE_URL}/snapshots/#{slug}/{z}/{x}/{y}.png&map=#{zoom}/#{lat}/#{lon}" 23 | else 24 | "#{FieldPapers::OSM_BASE_URL}/edit#map=#{zoom}/#{lat}/#{lon}" 25 | end 26 | end 27 | 28 | def potlatch_link(zoom, lon, lat, slug = nil) 29 | if slug 30 | "#{FieldPapers::OSM_BASE_URL}/edit?lat=#{lat}&lon=#{lon}&zoom=#{zoom}&tileurl=#{FieldPapers::TILE_BASE_URL}/snapshots/#{slug}/$z/$x/$y.png" 31 | else 32 | "#{FieldPapers::OSM_BASE_URL}/edit?lat=#{lat}&lon=#{lon}&zoom=#{zoom}" 33 | end 34 | end 35 | 36 | # derives URLs for HTTP GET requests needed for JOSM to load data and tiles. 37 | # Actual XHRs exist in 50 | <% end %> 51 | -------------------------------------------------------------------------------- /app/views/atlases/show.csv.erb: -------------------------------------------------------------------------------- 1 | <%# TODO: populate empty fields and other activity -%> 2 | <%= CSV.generate_line(['type', 'href', 'title', 'created', 'person_href', 'geometry', 'atlas_page_href', 'snapshot_href', 'note']).html_safe -%> 3 | <%= CSV.generate_line(['atlas', 4 | atlas_url(@atlas), 5 | @atlas.title, 6 | @atlas.created_at.strftime('%a, %e %b %Y %H:%M:%S %z'), 7 | atlas_person_href(@atlas), 8 | @atlas.geometry_string, 9 | "", 10 | "", 11 | "" 12 | ]).html_safe -%> 13 | <% if @atlas.snapshots -%> 14 | <%- @atlas.snapshots.each do |snapshot| -%> 15 | 16 | <%= CSV.generate_line(['snapshot', 17 | snapshot_url(snapshot), 18 | snapshot.title, 19 | snapshot.created_at.strftime('%a, %e %b %Y %H:%M:%S %z'), 20 | snapshot_person_href(snapshot), 21 | snapshot.geometry_string, 22 | atlas_url(@atlas) + "/" + snapshot.page.page_number, 23 | "", 24 | "" 25 | ]).html_safe -%> 26 | <%- end -%> 27 | <% end %> 28 | <%# Notes should go here, if we keep them -%> 29 | -------------------------------------------------------------------------------- /app/views/atlases/show.fpxml.builder: -------------------------------------------------------------------------------- 1 | xml.instruct! 2 | xml.print id: @atlas.slug, user: @atlas.creator.try(:slug), href: atlas_url(@atlas) do 3 | xml.paper size: @atlas.paper_size, orientation: @atlas.orientation, layout: @atlas.layout 4 | xml.provider @atlas.provider 5 | xml.pdf href: @atlas.pdf_url 6 | xml.private @page.private 7 | 8 | xml.bounds do 9 | xml.north @atlas.north 10 | xml.south @atlas.south 11 | xml.east @atlas.east 12 | xml.west @atlas.west 13 | end 14 | 15 | xml.center do 16 | xml.latitude @atlas.latitude 17 | xml.longitude @atlas.longitude 18 | xml.zoom @atlas.zoom 19 | end 20 | 21 | xml.country @atlas.country_name, woeid: @atlas.country_woeid 22 | xml.region @atlas.region_name, woeid: @atlas.region_woeid 23 | xml.place @atlas.place_name, woeid: @atlas.place_woeid 24 | end 25 | -------------------------------------------------------------------------------- /app/views/atlases/show.geojson.erb: -------------------------------------------------------------------------------- 1 | <%= @atlas.to_json(geojson: true).html_safe %> -------------------------------------------------------------------------------- /app/views/devise/confirmations/new.html.erb: -------------------------------------------------------------------------------- 1 |

<%= t('.resend_confirmation_instructions', :default => 'Resend confirmation instructions') %>

2 | 3 | <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 |
8 | <%= f.label :email %> 9 | <%= f.email_field :email, autofocus: true, class: 'u-full-width' %> 10 |
11 |
12 | 13 |
14 | <%= f.submit t('.resend_confirmation_instructions', :default => 'Resend confirmation instructions') %> 15 |
16 | <% end %> 17 | 18 | <%= render "devise/shared/links" %> 19 | -------------------------------------------------------------------------------- /app/views/devise/mailer/confirmation_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

<%= t('.greeting', recipient: @email, default: "Welcome #{@email}!") %>

2 | 3 |

<%= t('.instruction', default: "You can confirm your account email through the link below:") %>

4 |

<%= link_to t('.action', default: "Confirm my account"), 5 | confirmation_url(@resource, confirmation_token: @token) %>

6 | -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

<%= t('.greeting', recipient: @resource.email, default: "Hello #{@resource.email}!") %>

2 | 3 |

<%= t('.instruction', default: "Someone has requested a link to change your password, and you can do this through the link below.") %>

4 | 5 |

<%= link_to t('.action', default: "Change my password"), edit_password_url(@resource, reset_password_token: @token) %>

6 | 7 |

<%= t('.instruction_2', default: "If you didn't request this, please ignore this email.") %>

8 |

<%= t('.instruction_3', default: "Your password won't change until you access the link above and create a new one.") %>

9 | -------------------------------------------------------------------------------- /app/views/devise/mailer/unlock_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

<%= t('.greeting', recipient: @resource.email, default: "Hello #{@resource.email}!") %>

2 | 3 |

<%= t('.message', default: "Your account has been locked due to an excessive amount of unsuccessful sign in attempts.") %>

4 | 5 |

<%= t('.instruction', default: "Click the link below to unlock your account:") %>

6 | 7 |

<%= link_to t('.action', default:"Unlock my account"), unlock_url(@resource, unlock_token: @token) %>

8 | -------------------------------------------------------------------------------- /app/views/devise/passwords/edit.html.erb: -------------------------------------------------------------------------------- 1 |

<%= t('.change_your_password', default: 'Change your password') %>

2 | 3 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %> 4 | <%= devise_error_messages! %> 5 | <%= f.hidden_field :reset_password_token %> 6 | 7 |
8 |
9 | <%= f.label :password, t('.new_password', default: 'New password') %> 10 | <%= f.password_field :password, autofocus: true, autocomplete: "off", class: 'u-full-width' %> 11 |
12 |
13 | 14 |
15 |
16 | <%= f.label :password_confirmation, t('.confirm_new_password', default: 'Confirm new password') %> 17 | <%= f.password_field :password_confirmation, autocomplete: "off", class: 'u-full-width' %> 18 |
19 |
20 | 21 | <%= f.submit t('.change_my_password', default: 'Change my password') %> 22 | 23 | <% end %> 24 | 25 | <%= render "devise/shared/links" %> 26 | -------------------------------------------------------------------------------- /app/views/devise/passwords/new.html.erb: -------------------------------------------------------------------------------- 1 |

<%= t('.forgot_your_password', default: 'Forgot your password?') %>

2 | 3 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 |
8 | <%= f.label :email %> 9 | <%= f.email_field :email, autofocus: true, class: 'u-full-width' %> 10 |
11 |
12 | 13 |
14 | <%= f.submit t('.send_me_reset_password_instructions', default: "Send me reset password instructions") %> 15 |
16 | <% end %> 17 | 18 | <%= render "devise/shared/links" %> 19 | -------------------------------------------------------------------------------- /app/views/devise/registrations/edit.html.erb: -------------------------------------------------------------------------------- 1 |

<%= t('.title', resource: resource_class.model_name.human, default: "Edit #{resource_name.to_s.humanize}") %>

2 | 3 | <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 |
8 | <%= f.label :username %> 9 | <%= f.text_field :username, autofocus: true, class: 'u-full-width' %> 10 |
11 |
12 | 13 |
14 |
15 | <%= f.label :email %> 16 | <%= f.email_field :email, class: 'u-full-width' %> 17 |
18 |
19 | 20 | <%- if devise_mapping.confirmable? && resource.pending_reconfirmation? -%> 21 |

22 | <%= t('.currently_waiting_confirmation_for_email', :email => resource.unconfirmed_email, :default => "Currently waiting confirmation for: %{email}") %> 23 |

24 | <%- end -%> 25 | 26 |
27 |
28 | <%= f.label :password %> (<%= t('.leave_blank_if_you_don_t_want_to_change_it', :default => "leave blank if you don't want to change it") %> 29 | <%= f.password_field :password, autocomplete: "off", class: 'u-full-width' %> 30 |
31 |
32 | 33 |
34 |
35 | <%= f.label :password_confirmation %> 36 | <%= f.password_field :password_confirmation, autocomplete: "off", class: 'u-full-width' %> 37 |
38 |
39 | 40 |
41 |
42 | <%= f.label :current_password %> (<%= t('.we_need_your_current_password_to_confirm_your_changes', :default => 'we need your current password to confirm your changes') %>) 43 | <%= f.password_field :current_password, autocomplete: "off", class: 'u-full-width' %> 44 |
45 |
46 | 47 |
48 | <%= f.submit t('.update', default: "Update") %> 49 |
50 | <% end %> 51 | 52 |

<%= t('.cancel_my_account', default: 'Cancel my account') %>

53 | 54 |

<%= t('.unhappy', default: 'Unhappy') %>? <%= link_to t('.cancel_my_account', default: "Cancel my account"), registration_path(resource_name), data: { confirm: t('.are_you_sure', default: "Are you sure?") }, method: :delete %>.

55 | 56 | <%= link_to t('devise.shared.links.back', default: "Back"), :back %> 57 | -------------------------------------------------------------------------------- /app/views/devise/registrations/new.html.erb: -------------------------------------------------------------------------------- 1 |

<%= t('.sign_up', default: "Sign up") %>

2 | 3 | <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 |
8 | <%= f.label :username %> 9 | <%= f.text_field :username, autofocus: true, class: 'u-full-width' %> 10 |
11 |
12 | 13 |
14 |
15 | <%= f.label :email %> 16 | <%= f.email_field :email, class: 'u-full-width' %> 17 |
18 |
19 | 20 |
21 |
22 | <%= f.label :password %> 23 | <% if @validatable %> 24 | (<%= @minimum_password_length %> characters minimum) 25 | <% end %>
26 | <%= f.password_field :password, autocomplete: "off", class: 'u-full-width' %> 27 |
28 |
29 | 30 |
31 |
32 | <%= f.label :password_confirmation %> 33 | <%= f.password_field :password_confirmation, autocomplete: "off", class: 'u-full-width' %> 34 |
35 |
36 | 37 |
38 | <%= f.submit t('.sign_up', default: "Sign up") %> 39 |
40 | <% end %> 41 | 42 | <%= render "devise/shared/links" %> 43 | -------------------------------------------------------------------------------- /app/views/devise/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 |

<%= t('.sign_in', default: "Sign in") %>

2 | 3 | <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> 4 |
5 |
6 | <%= f.label :login %> 7 | <%= f.text_field :login, autofocus: true, required: true, placeholder: "...", class: 'u-full-width' %> 8 |
9 |
10 | 11 |
12 |
13 | <%= f.label :password %> 14 | <%= f.password_field :password, autocomplete: "off", required: true, class: 'u-full-width' %> 15 |
16 |
17 | 18 |
19 | <% if devise_mapping.rememberable? -%> 20 |
21 | <%= f.label :remember_me do %> 22 | <%= f.check_box :remember_me %> 23 | <%= t(".remember_me", default: "Remember Me") %> 24 | <% end %> 25 |
26 | <% end -%> 27 | 28 |
29 | <%= f.submit t('.sign_in', :default => "Sign in") %> 30 |
31 |
32 | <% end %> 33 | 34 | <%= render "devise/shared/links" %> 35 | -------------------------------------------------------------------------------- /app/views/devise/shared/_links.html.erb: -------------------------------------------------------------------------------- 1 | <%- if controller_name != 'sessions' %> 2 | <%= link_to t(".sign_in", default: "Sign in"), new_session_path(resource_name) %>
3 | <% end -%> 4 | 5 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %> 6 | <%= link_to t(".sign_up", default: "Sign up"), new_registration_path(resource_name) %>
7 | <% end -%> 8 | 9 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' %> 10 | <%= link_to t(".forgot_your_password", default: "Forgot your password?"), new_password_path(resource_name) %>
11 | <% end -%> 12 | 13 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> 14 | <%= link_to t('.didn_t_receive_confirmation_instructions', default: "Didn't receive confirmation instructions?"), new_confirmation_path(resource_name) %>
15 | <% end -%> 16 | 17 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> 18 | <%= link_to t('.didn_t_receive_unlock_instructions', default: "Didn't receive unlock instructions?"), new_unlock_path(resource_name) %>
19 | <% end -%> 20 | 21 | <%- if devise_mapping.omniauthable? %> 22 | <%- resource_class.omniauth_providers.each do |provider| %> 23 | <%= link_to t('.sign_in_with_provider', provider: provider.to_s.titleize, default: "Sign in with #{provider.to_s.titleize}"), omniauth_authorize_path(resource_name, provider) %>
24 | <% end -%> 25 | <% end -%> 26 | -------------------------------------------------------------------------------- /app/views/devise/unlocks/new.html.erb: -------------------------------------------------------------------------------- 1 |

<%= t('.resend_unlock_instructions', default: "Resend unlock instructions") %>

2 | 3 | <%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 |
8 | <%= f.label :email %>
9 | <%= f.email_field :email, autofocus: true, class: 'u-full-width' %> 10 |
11 |
12 | 13 |
14 | <%= f.submit t('.resend_unlock_instructions', default: "Resend unlock instructions") %> 15 |
16 | <% end %> 17 | 18 | <%= render "devise/shared/links" %> 19 | -------------------------------------------------------------------------------- /app/views/home/advanced.fr.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :title, _("Advanced - Field Papers") %> 2 | 3 |
4 |
5 |

Outils avancés

6 |

7 | Field Papers propose plusieurs outils d'automatisation et de personnalisation des cartes. Ces outils et processus sont mis à disposition pour des utilisateurs d'un bon niveau technique et aucun support (ou limité) n'est fourni. 8 |

9 |
    10 |
  1. 11 |

    12 | Outil de Modèle d'Atlas - Field Papers incorpore une modèle d'API sous la forme d'un formulaire HTML que vous pouvez héberger où vous le souhaitez et le remplir avec des valeurs prédéfinies pour chaque cas d'utilisation. Le formulaire publie ces paramètres aux Field Papers et renseigne chaque étape de la création de l'atlas avec ces valeurs, tout en permettant à l'utilisateur de modifier la zone d'intérêt et le fond d'imagerie utilisé pour l'atlas. 13 |

    14 |
  2. 15 |
  3. 16 |

    17 | Cartes d'incidents - Besoin de contrôler sur le terrain une liste de d'objets localisés ? Transférez une liste de lieux au format GeoJSON et une page d'atlas sera centrée autour de chaque incident pour vous permettre de vous passer de l'atlas classique sous forme de grille continue. 18 |

    19 |
  4. 20 |
  5. 21 |

    22 | Personnaliser des styles de carte - TMS spécifiques - Vous avez déjà publié votre fond de carte en ligne en tant que tms (tiled map service) ? Utilisez cet outil pour indiquer le chemin vers ce tms spécifique. Exemple : https://tile.stamen.com/watercolor/{Z}/{X}/{Y}.jpg. 23 |

    24 |
  6. 25 |
  7. 26 |

    27 | Editer la base OSM - Si vous transférez un aperçu de votre carte vers Walking Papers, vous pouvez utiliser les outils qui s'y trouvent pour ;editer la base OpenStreetMap dans Potlatch ou JOSM en tuilisant votre aperçu comme couche de référence. Les éditions les plus fréquentes concernent les rues, les bâtiments, les équipements de toutes sortes, les parcs, les adresses, les commerces, etc. 28 |

    29 |
  8. 30 |
  9. 31 |

    32 | Créer une fork sur GitHub - Field Papers est un projet open source hébergé sur GitHub. 33 |

  10. 34 | 35 |
36 |
37 |
38 | -------------------------------------------------------------------------------- /app/views/home/advanced.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :title, _("Advanced - Field Papers") %> 2 | 3 |
4 |
5 |

Advanced Tools

6 |

7 | Field Papers offers several automation and map customization tools. These tools and workflows are provided for technical users and limited to no support is provided. 8 |

9 |
    10 |
  1. 11 |

    12 | Atlas Template Tool - Field Papers includes a HTML form based template API that you can host anywhere and populate with preset values for each use case. The form posts those parameters to Field Papers and populates each phase of the make atlas process with those values, while allowing the user to modify the area of interest, etc. 13 |

    14 |
  2. 15 |
  3. 16 |

    17 | Incident Maps - Field checking a list of feature locations? Upload a list of locations in GeoJSON format and we'll center an atlas page on each incident to give you a head start and liberate you from the atlas page grid. 18 |

    19 |
  4. 20 |
  5. 21 |

    22 | Custom Map Styles - TMS endpoints - Already published your basemap online as a tiled map service? Use our template tool detailed above to point to the TMS endpoint. Example: https://tile.stamen.com/watercolor/{Z}/{X}/{Y}.jpg. 23 |

    24 |
  6. 25 |
  7. 26 |

    27 | Edit in OSM - Field Papers plays well with others. If you upload a snapshot of your map at Field Papers, you can use the tools there to edit OpenStreetMap in Potlatch and JOSM using your snapshot as a reference layer. Common tasks include adding streets, parks, building outlines, addresses, business names, and more. 28 |

    29 |
  8. 30 |
  9. 31 |

    32 | Fork us on GitHub - Field Papers in an open source project hosted at GitHub. 33 |

  10. 34 | 35 |
36 |
37 |
38 | -------------------------------------------------------------------------------- /app/views/home/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

<%= _("Welcome to Field Papers") %>

3 |
4 | 5 |
6 |
7 | <%= _("> 8 |
9 |
<%= link_to _("Make yourself an atlas"), compose_path %>
10 |

<%= _("Print out anywhere in the world.") %>

11 |
12 |
13 | 14 |
15 | <%= _("> 16 |
17 |
<%= link_to _("Take it into the field"), compose_path %>
18 |

<%= _("Make your notes and observations.") %>

19 |
20 |
21 | 22 |
23 | <%= _("> 24 |
25 |
<%= link_to _("Capture your notes"), new_snapshot_path %>
26 |

<%= _("%{Upload} pages you've photographed.") % {Upload: content_tag(:b, link_to(_("Upload"), new_snapshot_path)) } %>

27 |
28 |
29 |
30 | -------------------------------------------------------------------------------- /app/views/home/make-canned-atlas-template.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |

<%= _("Making Canned Atlases") %>

4 | 5 |

<%= _("It’s easy to create “canned” Field Papers atlases by providing extra details up-front that are used as default values throughout the Make process.") % compose_path %>

6 |

<%= _("Each setting below can be posted via plain HTML form to fieldpapers.org. All are optional.") % compose_path %>

7 | 8 | <%= form_tag compose_path, method: :get do %> 9 |
10 |
11 | 12 | <%= text_field_tag :atlas_title, nil, placeholder: _("Atlas title"), size: "40" %> 13 |
14 | 15 |
16 | 17 | <%= text_area_tag :atlas_text, nil, placeholder: _("Atlas description"), rows: "4", cols: "40" %> 18 |
19 | 20 |
21 | 22 |

<%= _("Latitude, longitude, and optional zoom. Use dbsgeo.com/latlon/ to make new ones.") %>

23 | <%= text_field_tag :atlas_location, nil, placeholder: _("Location and zoom"), size: "40" %> 24 |
25 |

<%= _("Examples:") %>

26 |
    27 |
  • 37.8045, -122.2712 15
  • 28 |
  • 37.8045 -122.2712, 15
  • 29 |
  • 37.8045,-122.2712
  • 30 |
  • 37.8045 -122.2712
  • 31 |
32 |
33 |
34 | 35 |
36 | 37 | <%= text_field_tag :atlas_provider, nil, placeholder: _("Tile URL template"), size: "40" %> 38 |
39 |

<%= _("Examples:") %>

40 |
    41 |
  • https://tile.openstreetmap.org/{Z}/{X}/{Y}.png (OpenStreetMap)
  • 42 |
43 |
44 |
45 |

<%= button_tag _("Submit") %>

46 |
47 | <% end %> 48 |
49 |
50 | -------------------------------------------------------------------------------- /app/views/home/make-geojson-atlas-form.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |

<%= _("Making Atlases From GeoJSON") %>

4 |

<%= _("It’s possible to generate atlases from raw GeoJSON files. This can be done in one of two ways:") %>

5 |
    6 |
  1. 7 |

    <%= _("A GeoJSON file (similar to the below) can be POSTed as an attached file to %{link}, named geojson_file:") % { link: compose_url } %>

    8 | 9 | <%= form_tag compose_path, method: :post, multipart: true do %> 10 |
    11 |
    12 | 13 | <%= file_field_tag :geojson_file, accept: "application/vnd.geo+json,application/json" %> 14 |
    15 |
    16 |
    17 |
    18 | <%= button_tag _("Submit") %> 19 |
    20 |
    21 | <% end %> 22 |
  2. 23 | 24 |
  3. 25 |

    <%= _("Alternatively, raw GeoJSON content can be POSTed to %{link} as a form value, named geojson_data:") % { link: compose_url } %>

    26 | 27 | <%= form_tag compose_path, method: :post, multipart: true do %> 28 |
    29 |
    30 | 31 | <%= text_area_tag :geojson_data, '{ 32 | "properties": { 33 | "paper_size": "letter", 34 | "orientation": "portrait" 35 | }, 36 | "type": "FeatureCollection", 37 | "features": [ 38 | { 39 | "properties": { 40 | "provider": "https://tile.openstreetmap.org/{Z}/{X}/{Y}.png", 41 | "zoom": 17 42 | }, 43 | "type": "Feature", 44 | "geometry": { 45 | "type": "Polygon", 46 | "coordinates": [[ [-122.293647, 37.817211], [-122.293647, 37.801750], [-122.278198, 37.801750], [-122.278198, 37.817211], [-122.293647, 37.817211] ]] 47 | } 48 | }, 49 | { 50 | "properties": { 51 | "provider": "https://tile.openstreetmap.org/{Z}/{X}/{Y}.png", 52 | "zoom": 17 53 | }, 54 | "type": "Feature", 55 | "geometry": { 56 | "type": "Point", 57 | "coordinates": [-122.281694, 37.822091] 58 | } 59 | } 60 | ] 61 | }', rows: "10", class: "u-full-width auto-height" %> 62 | 63 |
    64 |
    65 |
    66 |
    67 | <%= button_tag _("Submit") %> 68 |
    69 |
    70 | <% end %> 71 |
  4. 72 |
73 |
74 |
75 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% if Rails.env.production? %> 5 | 6 | 7 | 13 | <% end %> 14 | 15 | 16 | 17 | <%= yield(:title).presence || _("Field Papers") %> 18 | <%= stylesheet_link_tag 'application', media: 'all' %> 19 | <%= csrf_meta_tags %> 20 | 21 | <%= javascript_include_tag 'application' %> 22 | <%= yield :head %> 23 | 24 | 25 | 26 |
27 | 28 | <%= render partial: "shared/navbar" %> 29 | <% if notice || alert %> 30 |
31 |
32 |

<%= notice %>

33 |

<%= alert %>

34 |
35 |
36 | <% end %> 37 | 38 | <%= yield %> 39 | 40 | <%= render partial: "shared/footer" %> 41 | 42 |
43 | 44 | <%= yield :foot %> 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/views/layouts/big_map.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% if Rails.env.production? %> 5 | 6 | 7 | 13 | <% end %> 14 | 15 | 16 | 17 | <%= yield(:title).presence || _("Field Papers") %> 18 | <%= stylesheet_link_tag 'application', media: 'all' %> 19 | <%= csrf_meta_tags %> 20 | 21 | <%= javascript_include_tag 'application' %> 22 | <%= yield :head %> 23 | 24 | 25 |
26 | <%= yield %> 27 |
28 | 29 | <%= yield :foot %> 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/views/pages/show.fpxml.builder: -------------------------------------------------------------------------------- 1 | xml.instruct! 2 | xml.print id: "#{@page.atlas.slug}/#{@page.page_number}", user: @page.atlas.creator.try(:slug), href: atlas_page_atlas_url(@page.atlas, @page.page_number) do 3 | xml.paper size: @page.atlas.paper_size, orientation: @page.atlas.orientation, layout: @page.atlas.layout 4 | xml.provider @page.provider 5 | xml.pdf href: @page.atlas.pdf_url 6 | xml.private @page.atlas.private 7 | 8 | xml.bounds do 9 | xml.north @page.north 10 | xml.south @page.south 11 | xml.east @page.east 12 | xml.west @page.west 13 | end 14 | 15 | xml.center do 16 | xml.latitude @page.latitude 17 | xml.longitude @page.longitude 18 | xml.zoom @page.zoom 19 | end 20 | 21 | xml.country @page.country_name, woeid: @page.country_woeid 22 | xml.region @page.region_name, woeid: @page.region_woeid 23 | xml.place @page.place_name, woeid: @page.place_woeid 24 | end 25 | -------------------------------------------------------------------------------- /app/views/pages/show.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :title, _("Atlas page - Field Papers") %> 2 | 3 | <% if !defined? @page.atlas.id %> 4 |

No atlas

5 | <% elsif @atlas.incomplete? %> 6 | <%= render partial: "atlases/atlas_incomplete", locals: {atlas: @page.atlas} %> 7 | <% elsif @atlas.failed?%> 8 | <%= render partial: "atlases/atlas_failed", locals: {atlas: @page.atlas} %> 9 | <% else %> 10 |
11 |
12 |
13 | 14 |
15 |
<%= _("From") %> <%= link_to @page.atlas.title, atlas_path(@page.atlas) %><% if @page.atlas.private? %><%= _("private") %><% end %>
16 |

<%=h @page.page_number %>

17 |
18 | 19 |
20 | <%= form_for @page.atlas, url: compose_path, method: :put do |f| %> 21 | <%= f.hidden_field :north, :value=> @page.atlas.north %> 22 | <%= f.hidden_field :south, :value=> @page.atlas.south %> 23 | <%= f.hidden_field :east, :value=> @page.atlas.east %> 24 | <%= f.hidden_field :west, :value=> @page.atlas.west %> 25 | <%= f.hidden_field :zoom, :value=> @page.atlas.zoom %> 26 | <%= f.hidden_field :rows, :value=> @page.atlas.get_rows %> 27 | <%= f.hidden_field :cols, :value=> @page.atlas.get_cols %> 28 | <%= f.hidden_field :orientation, :value=> @page.atlas.orientation %> 29 | <%= f.hidden_field :provider, :value=> @page.atlas.provider %> 30 | <%= f.hidden_field :title, :value=> @page.atlas.title %> 31 | <%= f.hidden_field :text, :value=> @page.atlas.text %> 32 | <%= f.hidden_field :private, :value=> @page.atlas.private %> 33 | <%= f.hidden_field :cloned_from, :value=> @page.atlas.id %> 34 | <%= f.button _("Copy this atlas") %> 35 | <% end %> 36 | 37 | <% if @page.pdf_url %><%= _("Download PDF") %><% end %> 38 |
39 | 40 |
41 |
42 |
43 | 44 |
45 | <%= render partial: "atlases/atlas_activity", locals: {atlas: @page.atlas} %> 46 |
47 | 48 | 49 | <% content_for :foot do %> 50 | 73 | <% end %> 74 | <% end %> 75 | -------------------------------------------------------------------------------- /app/views/shared/_navbar.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 49 | 50 | <% content_for :foot do %> 51 | 56 | <% end %> 57 | -------------------------------------------------------------------------------- /app/views/snapshots/_failed.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

<%= _("Snapshot Processing Failed (%{image_filename})") % {image_filename: snapshot.image_filename} %>

6 |
7 |
8 |
9 |
10 |

<%= _("Unfortunately, your snapshot for image %{image_filename} failed to be processed. It's possible that it was an intermittent error, in which case you should try re-uploading. If the problem persists, please %{open_issue} or email %{email_url} with details.") % { open_issue: link_to(_("open a GitHub issue"), "https://github.com/fieldpapers/fieldpapers/issues/"), email_url: mail_to("help@fieldpapers.org", nil, replace_at: "[at]", class: "email", encode: "hex", subject: "Snapshot #{snapshot.slug} failed to process for image #{snapshot.image_filename}"), image_filename: snapshot.image_filename } %>

11 |
12 |
13 |
14 |
15 | -------------------------------------------------------------------------------- /app/views/snapshots/_incomplete.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :head do %> 2 | 3 | 4 | <% end %> 5 | 6 |
7 |
8 |
9 |
10 |

<%= _("Snapshot Processing (%{image_filename})") % {image_filename: snapshot.image_filename} %>

11 |
12 |
13 |
14 |
15 | 16 |
17 | 18 |
19 |

<%= _("This process has been running for %{time}") % {time: time_ago_in_words(snapshot.created_at)} %>

20 |
21 |
22 |

<%= _("This may take a while, generally a few minutes. You don't need to keep this window open; you can bookmark this page and come back later.") % {url: snapshot_path(snapshot)} %>

23 |

<%= _("If it takes more than an hour, check %{twitter_link} for system status updates, and email us at %{email_url} if your atlas is stuck.") % 24 | { 25 | email_url: mail_to("help@fieldpapers.org", nil, replace_at: "[at]", class: "email", encode: "hex", subject: "Snapshot #{snapshot.slug} stuck for image #{snapshot.image_filename}"), 26 | twitter_link: link_to(_("@fieldpapers on Twitter"), "https://twitter.com/fieldpapers") 27 | } %>

28 | 29 | <% if snapshot.private? %> 30 |

<%= _("Since this snapshot is private, you probably should bookmark it.") % {url: snapshot_path(snapshot)} %>

31 | <% end %> 32 |
33 |
34 |
35 |
36 | -------------------------------------------------------------------------------- /app/views/snapshots/_snapshot.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | 3 | <%= map( 4 | :container_id => "static-map-#{snapshot.slug}", 5 | :center => { 6 | :latlng => [snapshot.latitude, snapshot.longitude], 7 | :zoom => snapshot.zoom - 3 8 | }, 9 | :map_options => { 10 | :attribution_control => false, 11 | :zoom_control => false, 12 | :dragging => false, 13 | :touchZoom => false, 14 | :scrollWheelZoom => false, 15 | :doubleClickZoom => false, 16 | :boxZoom => false, 17 | :tap => false, 18 | :keyboard => false 19 | }, 20 | 21 | :tile_layer => "#{FieldPapers::TILE_BASE_URL}/snapshots/#{snapshot.slug}/{z}/{x}/{y}.png", 22 | :attribution => nil, 23 | :max_zoom => 18, 24 | :subdomains => ["a", "b", "c", "d"] 25 | ) %> 26 | 27 | 28 |
    29 | <%= link_to snapshot.title, snapshot_path(snapshot), {:class=>"map-card-title embolden"} %>
    30 | 31 | <%= link_to snapshot.uploader_name, snapshots_path(username: snapshot.uploader.username) if snapshot.uploader %> 32 | <%= _("anonymous") unless snapshot.uploader %> 33 | 34 |
    35 | 36 |
    37 | <%= link_to _("%{time_period} ago") % { time_period: time_ago_in_words(snapshot.created_at) }, snapshots_path(month: snapshot.created_at.strftime("%Y-%m")) %> 38 |
    39 |
  • 40 | -------------------------------------------------------------------------------- /app/views/snapshots/create.js.erb: -------------------------------------------------------------------------------- 1 | <% if @snapshot.persisted? %> 2 | // redirect to snapshot page 3 | window.location.href = "<%= snapshot_url(@snapshot) %>"; 4 | <% else %> 5 | console.info("not persisted. validation errors presumably present."); 6 | 7 | <% @snapshot.errors.full_messages.each do |msg| %> 8 | console.error("<%= msg %>"); 9 | <% end %> 10 | 11 | $('body').addClass('error').removeClass('uploading'); 12 | <% end %> 13 | -------------------------------------------------------------------------------- /app/views/snapshots/index.csv.erb: -------------------------------------------------------------------------------- 1 | <% 2 | row_names = [""] + ("A".."Z").to_a 3 | 4 | grid = "" 5 | 6 | if @atlas.cols == 0 && @atlas.rows == 0 7 | slugs = @atlas.snapshots.pluck(:slug) 8 | 9 | grid << "\"" + slugs.map { |slug| snapshot_url(slug) }.join(",") + "\"" if slugs 10 | else 11 | (@atlas.cols + 1).times do |y| 12 | (@atlas.rows + 1).times do |x| 13 | grid << row_names[x] if y == 0 14 | grid << y.to_s if x == 0 && y > 0 15 | 16 | slugs = if y > 0 && x > 0 17 | page_number = "#{row_names[x]}#{y}" 18 | @atlas.pages.find_by_page_number(page_number).snapshots.pluck(:slug) 19 | elsif y == 0 && x == 0 20 | @atlas.pages.find_by_page_number("i").snapshots.pluck(:slug) 21 | end 22 | 23 | grid << "\"" + slugs.map { |slug| snapshot_url(slug) }.join(",") + "\"" if slugs 24 | 25 | grid << "," unless x == @atlas.cols - 1 26 | end 27 | 28 | grid << "\n" 29 | end 30 | end 31 | -%> 32 | <%= grid.html_safe -%> 33 | -------------------------------------------------------------------------------- /app/views/snapshots/index.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :title, _("Snapshots - Field Papers") %> 2 | 3 |
    4 |
    5 | 13 |
    14 |
    15 | 16 |
    17 |
    18 | <% if @snapshots.empty? %> 19 |

    <%= _("No snapshots.") %>

    20 | <% else %> 21 | <%= paginate @snapshots %> 22 |
    23 |
    24 | 25 |
      26 | <%= render partial: "snapshot", collection: @snapshots %> 27 |
    28 | 29 |
    30 |
    31 | <%= paginate @snapshots %> 32 |
    33 |
    34 | 35 | <% end %> 36 | -------------------------------------------------------------------------------- /app/views/snapshots/new.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :title, _("Snapshot upload - Field Papers") %> 2 | 3 |
    4 |
    5 |

    <%= _("Upload") %>

    6 |

    <%= _("Uploading") %>

    7 |

    <%= _("Upload Failed") %>

    8 |

    <%= _("Choose an atlas page to upload. We'll work out where it goes (using the QR code).") %>

    9 |
    10 |
    11 |

    <%= _("Something went wrong! Please review the rules below and try again.") %>

    12 | 13 | <% case FieldPapers::PERSIST %> 14 | <% when "local" %> 15 | <%= form_for @snapshot, url: snapshots_path, html: { multipart: true } do |f| %> 16 | <%= f.file_field :scene %>
    17 | <%= f.submit "Upload" %> 18 | <% end %> 19 | <% when "s3" %> 20 | <%= s3_uploader_form id: "s3_uploader", 21 | callback_url: snapshots_url, 22 | callback_param: "snapshot[s3_scene_url]", 23 | expiration: 24.hours.from_now.utc.iso8601, 24 | acl: "private", 25 | class: "upload-form", 26 | max_file_size: 25.megabytes do %> 27 | <%= file_field_tag :file, multiple: true, data: { url: s3_uploader_url } %> 28 | <% end %> 29 | 30 |
    31 |
    32 |
    33 |
    34 |
    35 | <% end %> 36 |
    37 |
    38 | 39 |
    40 |
    41 |

    <%= _("Rules") %>

    42 |
      43 |
    • <%= _("Make sure the scan/photo/image is at least 200dpi.") %>
    • 44 |
    • <%= _("Make sure you're uploading a JPG, PNG, TIF, or GIF. (PDFs won't work.)") %>
    • 45 |
    • <%= _("Don't upload things that aren't Field Papers maps, please.") %>
    • 46 |
    47 |
    48 |
    49 | 50 | <% if FieldPapers::PERSIST == "s3" %> 51 | <% content_for :foot do %> 52 | 80 | <% end %> 81 | <% end %> 82 | -------------------------------------------------------------------------------- /app/views/snapshots/show.fpxml.builder: -------------------------------------------------------------------------------- 1 | xml.instruct! 2 | xml.scan id: @snapshot.slug, user: @snapshot.uploader.try(:slug), href: snapshot_url(@snapshot) do 3 | xml.provider href: @snapshot.page.provider 4 | xml.private @snapshot.private 5 | xml.print id: @snapshot.atlas.slug, user: @snapshot.atlas.creator.try(:slug), href: atlas_url(@snapshot.atlas) 6 | 7 | xml.paper size: @snapshot.atlas.paper_size, orientation: @snapshot.atlas.orientation, layout: @snapshot.atlas.layout 8 | xml.provider @snapshot.atlas.provider 9 | xml.pdf href: @snapshot.atlas.pdf_url 10 | 11 | xml.bounds do 12 | xml.north @snapshot.atlas.north 13 | xml.south @snapshot.atlas.south 14 | xml.east @snapshot.atlas.east 15 | xml.west @snapshot.atlas.west 16 | end 17 | 18 | xml.center do 19 | xml.latitude @snapshot.atlas.latitude 20 | xml.longitude @snapshot.atlas.longitude 21 | xml.zoom @snapshot.atlas.zoom 22 | end 23 | 24 | xml.country @snapshot.atlas.country_name, woeid: @snapshot.atlas.country_woeid 25 | xml.region @snapshot.atlas.region_name, woeid: @snapshot.atlas.region_woeid 26 | xml.place @snapshot.atlas.place_name, woeid: @snapshot.atlas.place_woeid 27 | end 28 | -------------------------------------------------------------------------------- /app/views/snapshots/show.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :title, _("Snapshot - Field Papers") %> 2 | 3 | <% if @snapshot.incomplete? %> 4 | <%= render partial: "incomplete", locals: { snapshot: @snapshot } %> 5 | <% elsif @snapshot.failed? %> 6 | <%= render partial: "failed", locals: { snapshot: @snapshot } %> 7 | <% else %> 8 |
    9 | <% if @snapshot.bbox && @snapshot.provider %> 10 |
    11 | <% end %> 12 |
    13 |
    14 | <% if @snapshot.atlas %> 15 |
    <%= link_to(@snapshot.atlas.title, atlas_path(@snapshot.atlas.slug)) %><% if @snapshot.private? %><%= _("private") %><% end %>
    16 |

    Page <%= @snapshot.page.page_number %> (of <%= @snapshot.atlas.pages.size %>)

    17 | <% else %> 18 |
    Unknown<% if @snapshot.private? %>private<% end %>
    19 | <% end %> 20 |
    21 | 22 |
    23 |

    File <%= @snapshot.image_filename %>

    24 |
    25 | 26 | 29 | 30 |
    31 |
    32 |
    33 | 34 |
    35 | 45 | 46 |
    47 | 48 | <% content_for :foot do %> 49 | 66 | <% end %> 67 | <% end %> 68 | -------------------------------------------------------------------------------- /app/views/snapshots/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.id @snapshot.slug 2 | 3 | json.geotiff do 4 | json.url @snapshot.geotiff_url 5 | end 6 | 7 | json.bbox @snapshot.bbox 8 | json.tiles ["#{FieldPapers::TILE_BASE_URL}/snapshots/#{@snapshot.slug}/{z}/{x}/{y}.png"] 9 | json.tilejson_url tilejson_url(@snapshot) 10 | -------------------------------------------------------------------------------- /bin/_guard-core: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application '_guard-core' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('guard', '_guard-core') 17 | -------------------------------------------------------------------------------- /bin/annotate: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'annotate' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('annotate', 'annotate') 17 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/foreman: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'foreman' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require "pathname" 10 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require "rubygems" 14 | require "bundler/setup" 15 | 16 | load Gem.bin_path("foreman", "foreman") 17 | -------------------------------------------------------------------------------- /bin/guard: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'guard' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('guard', 'guard') 17 | -------------------------------------------------------------------------------- /bin/pry: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'pry' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('pry', 'pry') 17 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../../config/application', __FILE__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | # path to your application root. 5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 6 | 7 | Dir.chdir APP_ROOT do 8 | # This script is a starting point to setup your application. 9 | # Add necessary setup steps to this file: 10 | 11 | puts "== Installing dependencies ==" 12 | system "gem install bundler --conservative" 13 | system "bundle check || bundle install" 14 | 15 | # puts "\n== Copying sample files ==" 16 | # unless File.exist?("config/database.yml") 17 | # system "cp config/database.yml.sample config/database.yml" 18 | # end 19 | 20 | puts "\n== Preparing database ==" 21 | system "bin/rake db:setup" 22 | 23 | puts "\n== Removing old logs and tempfiles ==" 24 | system "rm -f log/*" 25 | system "rm -rf tmp/cache" 26 | 27 | puts "\n== Restarting application server ==" 28 | system "touch tmp/restart.txt" 29 | end 30 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | 5 | map ENV['RAILS_RELATIVE_URL_ROOT'] || "/" do 6 | run Rails.application 7 | end 8 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module App 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 16 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 17 | # config.time_zone = 'Central Time (US & Canada)' 18 | 19 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 20 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 21 | # config.i18n.default_locale = :de 22 | 23 | # Do not swallow errors in after_commit/after_rollback callbacks. 24 | # config.active_record.raise_in_transactional_callbacks = true 25 | 26 | config.active_record.legacy_connection_handling = false 27 | 28 | # rewrite URLs for backward-compatibility 29 | config.middleware.insert_before(Rack::Runtime, Rack::Rewrite) do 30 | # handle both encoded and non-encoded /s 31 | r301 %r{^/atlas.php(\?id=(.*)%2F(.*))?$}, "/atlases/$2/$3" 32 | r301 %r{^/atlas.php(\?id=(.*))?$}, "/atlases/$2" 33 | r301 %r{^/atlases.php(.*)$}, "/atlases$1" 34 | r301 %r{^/snapshot.php(\?id=(.*))?$}, "/snapshots/$2" 35 | r301 %r{^/snapshots.php(.*)$}, "/snapshots$1" 36 | 37 | # this S3 bucket is hard-coded here because these requests are purely 38 | # legacy requests 39 | r301 %r{^/files/prints/(.*)}, "http://s3.amazonaws.com/files.fieldpapers.org/atlases/$1" 40 | r301 %r{^/files/scans/(.*)}, "http://s3.amazonaws.com/files.fieldpapers.org/snapshots/$1" 41 | end 42 | 43 | # this allows us to use url_helpers in models without passing all the args 44 | config.after_initialize do 45 | Rails.application.routes.default_url_options[:host] = FieldPapers::BASE_URL 46 | end 47 | 48 | # remove the Devise :confirmable module based on env variable 49 | config.disable_login_confirmations = ENV['DISABLE_LOGIN_CONFIRMATIONS'] ? ENV['DISABLE_LOGIN_CONFIRMATIONS'] == 'true' : false 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | adapter: mysql2 3 | encoding: utf8 4 | pool: 5 5 | 6 | development: 7 | <<: *default 8 | 9 | # Warning: The database defined as "test" will be erased and 10 | # re-generated from your development database when you run "rake". 11 | # Do not set this db to the same as development or production. 12 | test: 13 | <<: *default 14 | 15 | # As with config/secrets.yml, you never want to store sensitive information, 16 | # like your database password, in your source code. If your source code is 17 | # ever seen by anyone, they now have access to your database. 18 | # 19 | # Instead, provide the password as a unix environment variable when you boot 20 | # the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database 21 | # for a full rundown on how to provide these environment variables in a 22 | # production deployment. 23 | # 24 | # On Heroku and other platform providers, you may have a full connection URL 25 | # available as an environment variable. For example: 26 | # 27 | # DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase" 28 | # 29 | # You can use this database configuration with: 30 | # 31 | # production: 32 | # url: <%= ENV['DATABASE_URL'] %> 33 | # 34 | 35 | # This "production" environment is specialised for deployment on 36 | # Amazon Web Services using the AWS Elastic Beanstalk service, with 37 | # resources set up by the aws-quick-start.py script in the 38 | # fieldpapers/fieldpapers repository. The RDS_* environment variables 39 | # used here are provided with values by the AWS environment on the EC2 40 | # instance where the web app runs, i.e. they are not explicitly 41 | # assigned values anywhere the web app code, but are set up 42 | # automatically by AWS. 43 | # If DATABASE_URL is provided, that will also work 44 | 45 | production: 46 | adapter: mysql2 47 | database: <%= ENV['RDS_DB_NAME'] %> 48 | username: <%= ENV['RDS_USERNAME'] %> 49 | password: <%= ENV['RDS_PASSWORD'] %> 50 | host: <%= ENV['RDS_HOSTNAME'] %> 51 | port: <%= ENV['RDS_PORT'] || 3306 %> 52 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | 7 | GettextI18nRails.translations_are_html_safe = true 8 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports and disable caching. 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | # Don't care if the mailer can't send. 17 | config.action_mailer.raise_delivery_errors = false 18 | config.action_mailer.delivery_method = :ses 19 | 20 | config.action_mailer.default_url_options = { 21 | host: 'localhost', 22 | port: 3000 23 | } 24 | 25 | # Print deprecation notices to the Rails logger. 26 | config.active_support.deprecation = :log 27 | 28 | # Raise an error on page load if there are pending migrations. 29 | config.active_record.migration_error = :page_load 30 | 31 | # Debug mode disables concatenation and preprocessing of assets. 32 | # This option may cause significant delays in view rendering with a large 33 | # number of complex assets. 34 | config.assets.debug = true 35 | 36 | # Use default logging formatter so that PID and timestamp are not suppressed. 37 | config.logger = Logger.new(STDOUT) 38 | config.log_formatter = ::Logger::Formatter.new 39 | config.log_level = :debug 40 | 41 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 42 | # yet still be able to expire them through the digest params. 43 | config.assets.digest = true 44 | 45 | # Adds additional error checking when serving assets at runtime. 46 | # Checks for improperly declared sprockets dependencies. 47 | # Raises helpful error messages. 48 | config.assets.raise_runtime_errors = true 49 | 50 | # Raises error for missing translations 51 | # config.action_view.raise_on_missing_translations = true 52 | end 53 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure static file server for tests with Cache-Control for performance. 16 | config.serve_static_files = true 17 | config.static_cache_control = 'public, max-age=3600' 18 | 19 | # Show full error reports and disable caching. 20 | config.consider_all_requests_local = true 21 | config.action_controller.perform_caching = false 22 | 23 | # Raise exceptions instead of rendering exception templates. 24 | config.action_dispatch.show_exceptions = false 25 | 26 | # Disable request forgery protection in test environment. 27 | config.action_controller.allow_forgery_protection = false 28 | 29 | # Tell Action Mailer not to deliver emails to the real world. 30 | # The :test delivery method accumulates sent emails in the 31 | # ActionMailer::Base.deliveries array. 32 | config.action_mailer.delivery_method = :test 33 | 34 | # Randomize the order test cases are executed. 35 | config.active_support.test_order = :random 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | 43 | config.active_job.queue_adapter = :test 44 | end 45 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 11 | -------------------------------------------------------------------------------- /config/initializers/aws.rb: -------------------------------------------------------------------------------- 1 | creds = Aws::Credentials.new( 2 | Rails.application.secrets[:aws][:access_key_id], 3 | Rails.application.secrets[:aws][:secret_access_key] 4 | ) 5 | 6 | Aws.config.update( 7 | region: Rails.application.secrets[:aws][:s3_bucket_region], 8 | credentials: creds 9 | ) 10 | 11 | Aws::Rails.add_action_mailer_delivery_method( 12 | :ses, 13 | credentials: creds, 14 | region: Rails.application.secrets[:aws][:s3_bucket_region], 15 | ) -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.action_dispatch.cookies_serializer = :json 4 | -------------------------------------------------------------------------------- /config/initializers/fast_gettext.rb: -------------------------------------------------------------------------------- 1 | require 'gettext_i18n_rails/string_interpolate_fix' 2 | 3 | FastGettext.add_text_domain('app', path: 'locale', type: :po) 4 | FastGettext.default_available_locales = %w(ar cs da de en es es_MX fr hu id it ja ku nl pl pt pt_BR ru sw tl uk vi zh_CN zh_TW) 5 | FastGettext.default_text_domain = 'app' 6 | FastGettext.default_locale = 'en' 7 | -------------------------------------------------------------------------------- /config/initializers/fieldpapers.rb: -------------------------------------------------------------------------------- 1 | require 'socket' 2 | 3 | module FieldPapers 4 | BASE_URL = ENV["BASE_URL"] || "https://fieldpapers.org" 5 | STATIC_URI_PREFIX = ENV["STATIC_URI_PREFIX"] || BASE_URL 6 | STATIC_PATH = ENV["STATIC_PATH"] || "./public" 7 | TASK_BASE_URL = ENV["TASK_BASE_URL"] || "https://tasks.fieldpapers.org" 8 | TILE_BASE_URL = ENV["TILE_BASE_URL"] || "https://tiles.fieldpapers.org" 9 | PERSIST = ENV["PERSIST"] || "s3" 10 | OSM_BASE_URL = ENV["OSM_BASE_URL"] || "https://www.openstreetmap.org" 11 | ATLAS_COMPLETE_WEBHOOKS = ENV["ATLAS_COMPLETE_WEBHOOKS"] || "" 12 | ATLAS_INDEX_HEADER_TILELAYER = ENV['ATLAS_INDEX_HEADER_TILELAYER'] || "https://{S}.tile.openstreetmap.org/{Z}/{X}/{Y}.png" 13 | 14 | if ENV["DEFAULT_CENTER"].present? 15 | DEFAULT_CENTER = ENV["DEFAULT_CENTER"] 16 | zoom, DEFAULT_LATITUDE, DEFAULT_LONGITUDE = ENV["DEFAULT_CENTER"].split("/").map(&:to_f) 17 | DEFAULT_ZOOM = zoom.to_i 18 | else 19 | DEFAULT_CENTER = nil 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /config/initializers/i18n.rb: -------------------------------------------------------------------------------- 1 | require "i18n/backend/fallbacks" 2 | 3 | # Don't complain if locales aren't available in support packages (e.g. 4 | # rails_i18n, etc.) 5 | I18n.config.enforce_available_locales = false 6 | 7 | # configure fallbacks, so zh_CN falls back to zh 8 | I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks) 9 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | inflect.plural /^(atlas)$/i, '\1es' 8 | inflect.singular /^(atlas)es/i, '\1' 9 | 10 | # inflect.plural /^(ox)$/i, '\1en' 11 | # inflect.singular /^(ox)en/i, '\1' 12 | # inflect.irregular 'person', 'people' 13 | # inflect.uncountable %w( fish sheep ) 14 | end 15 | 16 | # These inflection rules are supported but not enabled by default: 17 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 18 | # inflect.acronym 'RESTful' 19 | # end 20 | -------------------------------------------------------------------------------- /config/initializers/kaminari_config.rb: -------------------------------------------------------------------------------- 1 | Kaminari.configure do |config| 2 | # config.default_per_page = 25 3 | # config.max_per_page = nil 4 | # config.window = 4 5 | # config.outer_window = 0 6 | # config.left = 0 7 | # config.right = 0 8 | # config.page_method_name = :page 9 | # config.param_name = :page 10 | end 11 | -------------------------------------------------------------------------------- /config/initializers/mailer_defaults.rb: -------------------------------------------------------------------------------- 1 | class ActionMailer::Base 2 | def defaults 3 | headers['X-SES-FROM-ARN'] = ENV["MAIL_SOURCE_ARN"] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | Mime::Type.register "application/geo+json", :geojson 5 | Mime::Type.register "application/paperwalking+xml", :fpxml 6 | -------------------------------------------------------------------------------- /config/initializers/paperclip.rb: -------------------------------------------------------------------------------- 1 | case FieldPapers::PERSIST 2 | when "local" 3 | Paperclip::Attachment.default_options.merge!( 4 | storage: :filesystem, 5 | path: "#{FieldPapers::STATIC_PATH}:url", 6 | ) 7 | when "s3" 8 | Paperclip::Attachment.default_options.merge!( 9 | storage: :s3, 10 | s3_credentials: { 11 | access_key_id: Rails.application.secrets[:aws][:access_key_id], 12 | secret_access_key: Rails.application.secrets[:aws][:secret_access_key], 13 | bucket: Rails.application.secrets[:aws][:s3_bucket_name] 14 | }, 15 | s3_region: Rails.application.secrets[:aws][:s3_bucket_region], 16 | s3_permissions: :private, 17 | s3_protocol: 'https' 18 | ) 19 | end 20 | -------------------------------------------------------------------------------- /config/initializers/s3_direct_upload.rb: -------------------------------------------------------------------------------- 1 | S3DirectUpload.config do |c| 2 | c.access_key_id = Rails.application.secrets[:aws][:access_key_id] 3 | c.secret_access_key = Rails.application.secrets[:aws][:secret_access_key] 4 | c.bucket = Rails.application.secrets[:aws][:s3_bucket_name] 5 | c.region = Rails.application.secrets[:aws][:s3_bucket_region] 6 | c.url = "https://s3.#{c.region}.amazonaws.com/#{c.bucket}/" 7 | end 8 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_app_session' 4 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters) 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /config/providers.json: -------------------------------------------------------------------------------- 1 | { 2 | "openstreetmap": { 3 | "label": "OpenStreetMap", 4 | "template": "https://{S}.tile.openstreetmap.org/{Z}/{X}/{Y}.png", 5 | "options": { 6 | "attribution": "© OpenStreetMap" 7 | }, 8 | "minzoom": 0, 9 | "maxzoom": 19 10 | }, 11 | "humanitarian": { 12 | "label": "Humanitarian", 13 | "template": "https://{S}.tile.openstreetmap.fr/hot/{Z}/{X}/{Y}.png", 14 | "options": { 15 | "attribution": "© OpenStreetMap" 16 | }, 17 | "minzoom": 0, 18 | "maxzoom": 20 19 | }, 20 | "EsriImagery": { 21 | "label": "Esri World Imagery", 22 | "template": "https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{Z}/{Y}/{X}.png", 23 | "options": { 24 | "attribution": "Terms & Feedback" 25 | }, 26 | "minzoom": 0, 27 | "maxzoom": 20 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | # From https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server 2 | 3 | workers Integer(ENV['WEB_CONCURRENCY'] || 2) 4 | threads_count = Integer(ENV['MAX_THREADS'] || 5) 5 | threads threads_count, threads_count 6 | 7 | preload_app! 8 | 9 | port ENV['PORT'] || 3000 10 | environment ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development' 11 | 12 | on_worker_boot do 13 | # Worker specific setup for Rails 4.1+ 14 | # See: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#on-worker-boot 15 | ActiveRecord::Base.establish_connection 16 | end 17 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | devise_for :users 3 | 4 | # The priority is based upon order of creation: first created -> highest priority. 5 | # See how all your routes lay out with "rake routes". 6 | 7 | # site root 8 | root 'home#index' 9 | 10 | get '/about' => 'home#about', as: :about 11 | get '/advanced' => 'home#advanced', as: :advanced 12 | get '/make-canned-atlas-template' => 'home#make-canned-atlas-template', as: :make_canned_atlas 13 | get '/make-geojson-atlas-form' => 'home#make-geojson-atlas-form', as: :make_geojson_atlas 14 | 15 | concern :pageable do 16 | get '(page/:page)' => :index, on: :collection, as: '' 17 | end 18 | 19 | resources :atlases, :concerns => :pageable do 20 | resources :snapshots, only: [:index] 21 | member do 22 | get ':page_number' => 'pages#show', 23 | as: :atlas_page, 24 | constraints: { 25 | id: /(?:(?!page).)+/ # use negative lookaheads to match anything 26 | # *except* page (necessary because concerns 27 | # are prioritized lower) 28 | } 29 | patch ':page_number' => 'pages#update', 30 | constraints: { 31 | id: /(?:(?!page).)+/ # use negative lookaheads to match anything 32 | # *except* page (necessary because concerns 33 | # are prioritized lower) 34 | } 35 | end 36 | end 37 | 38 | get '/compose' => 'compose#new', as: :compose 39 | post '/compose' => 'compose#create' 40 | put '/compose' => 'compose#update' 41 | 42 | resources :snapshots, :concerns => :pageable 43 | 44 | mount Rack::NotFound.new("public/404.html") => "activity.php" 45 | end 46 | -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | aws_defaults: &aws_defaults 14 | access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %> 15 | secret_access_key: <%= ENV["AWS_SECRET_ACCESS_KEY"] %> 16 | s3_bucket_name: <%= ENV["S3_BUCKET_NAME"] || "files.fieldpapers.org" %> 17 | s3_bucket_region: <%= ENV["AWS_REGION"] || "us-east-1" %> 18 | 19 | development: 20 | secret_key_base: 43a5df1c68d5c8ff9763cf434fc4ae8e3328918fc6d7081940d8cc268fbea7b48c070a3595b5310cb01301687601c98c2a4df994f20b6034f9575926ed9d6915 21 | aws: 22 | <<: *aws_defaults 23 | #s3_bucket_name: "dev.files.fieldpapers.org" 24 | #s3_bucket_region: "us-east-1" 25 | 26 | test: 27 | secret_key_base: 8250116f6fd746e5fc9258277e308b711c8e66e565cadb132a9911c7386e2253debd088c9c8d7b6c3455b205fad050e80cfb9a7c02c971bdf8439c654773fdcc 28 | aws: 29 | <<: *aws_defaults 30 | #s3_bucket_name: "test.files.fieldpapers.org" 31 | #s3_bucket_region: "us-east-1" 32 | 33 | # Do not keep production secrets in the repository, instead read 34 | # values from the environment. NOTE: no AWS credentials here! 35 | # Temporary EC2 credentials are used in production. 36 | production: 37 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 38 | aws: 39 | <<: *aws_defaults 40 | s3_bucket_name: <%= ENV["S3_BUCKET_NAME"] || "files.fieldpapers.org" %> 41 | s3_bucket_region: <%= ENV["AWS_REGION"] || "us-east-1" %> 42 | -------------------------------------------------------------------------------- /config/storage.yml: -------------------------------------------------------------------------------- 1 | # this file is required by heroku 2 | 3 | test: 4 | service: Disk 5 | root: <%= Rails.root.join("tmp/storage") %> 6 | 7 | local: 8 | service: Disk 9 | root: <%= Rails.root.join("storage") %> -------------------------------------------------------------------------------- /db/migrate/20150213010510_add_devise_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddDeviseToUsers < ActiveRecord::Migration[4.2] 2 | def self.up 3 | change_table(:users) do |t| 4 | ## Database authenticatable 5 | t.string :encrypted_password, null: false, default: "" 6 | 7 | ## Recoverable 8 | t.string :reset_password_token 9 | t.datetime :reset_password_sent_at 10 | 11 | ## Rememberable 12 | t.datetime :remember_created_at 13 | 14 | ## Confirmable 15 | t.string :confirmation_token 16 | t.datetime :confirmed_at 17 | t.datetime :confirmation_sent_at 18 | t.string :unconfirmed_email # Only if using reconfirmable 19 | 20 | t.datetime :updated_at 21 | 22 | # change the schema 23 | 24 | t.rename :name, :username 25 | t.rename :password, :legacy_password 26 | t.remove :hash, :activated 27 | end 28 | 29 | # mark existing users (those with passwords, anyway) as confirmed 30 | execute("UPDATE users SET confirmed_at = NOW() WHERE legacy_password IS NOT NULL") 31 | 32 | # indexes 33 | 34 | add_index :users, :email, unique: true 35 | add_index :users, :reset_password_token, unique: true 36 | add_index :users, :confirmation_token, unique: true 37 | end 38 | 39 | def self.down 40 | # By default, we don't want to make any assumption about how to roll back a migration when your 41 | # model already existed. Please edit below which fields you would like to remove in this migration. 42 | raise ActiveRecord::IrreversibleMigration 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /db/migrate/20150214015436_clean_atlases.rb: -------------------------------------------------------------------------------- 1 | class CleanAtlases < ActiveRecord::Migration[4.2] 2 | def change 3 | rename_table :prints, :atlases 4 | 5 | execute("ALTER TABLE atlases CHANGE created created_at TIMESTAMP") 6 | execute("ALTER TABLE atlases CHANGE composed composed_at TIMESTAMP") 7 | 8 | change_table(:atlases) do |t| 9 | t.datetime :updated_at 10 | end 11 | 12 | execute("DROP VIEW IF EXISTS new_atlases") 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20150214015444_clean_pages.rb: -------------------------------------------------------------------------------- 1 | class CleanPages < ActiveRecord::Migration[4.2] 2 | def change 3 | execute("ALTER TABLE pages CHANGE created created_at TIMESTAMP") 4 | execute("ALTER TABLE pages CHANGE composed composed_at TIMESTAMP") 5 | 6 | change_table(:pages) do |t| 7 | t.datetime :updated_at 8 | end 9 | 10 | execute("DROP VIEW IF EXISTS new_pages") 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20150214015451_clean_snapshots.rb: -------------------------------------------------------------------------------- 1 | class CleanSnapshots < ActiveRecord::Migration[4.2] 2 | def change 3 | rename_table :scans, :snapshots 4 | 5 | execute("ALTER TABLE snapshots CHANGE created created_at TIMESTAMP") 6 | execute("ALTER TABLE snapshots CHANGE decoded decoded_at TIMESTAMP") 7 | 8 | change_table(:snapshots) do |t| 9 | t.datetime :updated_at 10 | end 11 | 12 | execute("DROP VIEW IF EXISTS new_snapshots") 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20150216203144_add_dimensions_to_atlases.rb: -------------------------------------------------------------------------------- 1 | class AddDimensionsToAtlases < ActiveRecord::Migration[4.2] 2 | def change 3 | change_table(:atlases) do |t| 4 | t.integer :rows, null: false 5 | t.integer :cols, null: false 6 | 7 | t.remove :atlas_pages 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20150219233932_rename_created_in_users.rb: -------------------------------------------------------------------------------- 1 | class RenameCreatedInUsers < ActiveRecord::Migration[4.2] 2 | def change 3 | change_table(:users) do |t| 4 | t.datetime :created_at 5 | end 6 | 7 | execute "UPDATE users SET created_at = created" 8 | 9 | change_table(:users) do |t| 10 | t.remove :created 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20150220070538_drop_logs.rb: -------------------------------------------------------------------------------- 1 | class DropLogs < ActiveRecord::Migration[4.2] 2 | def up 3 | drop_table :logs 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20150220070654_drop_forms.rb: -------------------------------------------------------------------------------- 1 | class DropForms < ActiveRecord::Migration[4.2] 2 | def up 3 | drop_table :forms 4 | drop_table :form_fields 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20150220071148_drop_messages.rb: -------------------------------------------------------------------------------- 1 | class DropMessages < ActiveRecord::Migration[4.2] 2 | def up 3 | drop_table :messages 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20150220071555_assign_ids_to_atlases.rb: -------------------------------------------------------------------------------- 1 | class AssignIdsToAtlases < ActiveRecord::Migration[4.2] 2 | def up 3 | # clear out titles containing invalid characters (manually identified with 4 | # the help of 5 | # https://www.blueboxcloud.com/insight/blog-article/getting-out-of-mysql-character-set-hell 6 | execute <<-EOQ 7 | UPDATE atlases 8 | SET title = NULL 9 | WHERE id IN ('38lmc9f8', '79vdl2b2', 'c65f7wx8', 'mcrtnkns', 'mzdw4g49', 'v4cx7kl9', 'vqhskvtk', 'wznc8s69', 'xm6gkzpx') 10 | EOQ 11 | 12 | execute <<-EOQ 13 | CREATE TABLE new_atlases ( 14 | id INT NOT NULL AUTO_INCREMENT, 15 | user_id INT, 16 | west DOUBLE NOT NULL, 17 | south DOUBLE NOT NULL, 18 | east DOUBLE NOT NULL, 19 | north DOUBLE NOT NULL, 20 | zoom TINYINT, 21 | rows TINYINT NOT NULL, 22 | cols TINYINT NOT NULL, 23 | provider VARCHAR(255), 24 | paper_size ENUM('letter', 'a4', 'a3') NOT NULL DEFAULT 'letter', 25 | orientation ENUM('portrait', 'landscape') NOT NULL DEFAULT 'portrait', 26 | layout ENUM('half-page', 'full-page') NOT NULL DEFAULT 'full-page', 27 | private BOOL NOT NULL DEFAULT false, 28 | PRIMARY KEY(id), 29 | KEY(slug), 30 | KEY(user_slug), 31 | KEY(private) 32 | ) 33 | SELECT 34 | id AS slug, 35 | user_id AS user_slug, 36 | CONVERT(CAST(CONVERT(title USING latin1) AS BINARY) USING utf8) title, -- fix encoding issues 37 | CONVERT(CAST(CONVERT(text USING latin1) AS BINARY) USING utf8) text, -- fix encoding issues 38 | west, 39 | south, 40 | east, 41 | north, 42 | zoom, 43 | rows, 44 | cols, 45 | provider, 46 | paper_size, 47 | orientation, 48 | layout, 49 | pdf_url, 50 | preview_url, 51 | country_name, 52 | country_woeid, 53 | region_name, 54 | region_woeid, 55 | place_name, 56 | place_woeid, 57 | progress, 58 | private, 59 | cloned AS cloned_from_slug, 60 | refreshed AS refreshed_from_slug, 61 | CAST(created_at AS datetime) AS created_at, 62 | updated_at, 63 | CAST(composed_at AS datetime) AS composed_at 64 | FROM atlases 65 | WHERE west IS NOT NULL 66 | AND south IS NOT NULL 67 | AND east IS NOT NULL 68 | AND north IS NOT NULL 69 | EOQ 70 | 71 | drop_table :atlases 72 | rename_table :new_atlases, :atlases 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /db/migrate/20150220071605_assign_ids_to_pages.rb: -------------------------------------------------------------------------------- 1 | class AssignIdsToPages < ActiveRecord::Migration[4.2] 2 | def change 3 | execute <<-EOQ 4 | CREATE TABLE new_pages ( 5 | id INT NOT NULL AUTO_INCREMENT, 6 | print_id INT NOT NULL, 7 | west DOUBLE NOT NULL, 8 | south DOUBLE NOT NULL, 9 | east DOUBLE NOT NULL, 10 | north DOUBLE NOT NULL, 11 | zoom TINYINT, 12 | provider VARCHAR(255), 13 | PRIMARY KEY(id), 14 | KEY(print_id) 15 | ) 16 | SELECT 17 | -1 AS print_id, 18 | print_id AS print_slug, 19 | user_id AS user_slug, 20 | page_number, 21 | text, 22 | west, 23 | south, 24 | east, 25 | north, 26 | zoom, 27 | provider, 28 | preview_url, 29 | country_name, 30 | country_woeid, 31 | region_name, 32 | place_name, 33 | place_woeid, 34 | CAST(created_at AS datetime) AS created_at, 35 | updated_at, 36 | CAST(composed_at AS datetime) AS composed_at 37 | FROM pages 38 | WHERE west IS NOT NULL 39 | AND south IS NOT NULL 40 | AND east IS NOT NULL 41 | AND north IS NOT NULL 42 | EOQ 43 | 44 | drop_table :pages 45 | rename_table :new_pages, :pages 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /db/migrate/20150220071615_assign_ids_to_snapshots.rb: -------------------------------------------------------------------------------- 1 | class AssignIdsToSnapshots < ActiveRecord::Migration[4.2] 2 | def up 3 | execute <<-EOQ 4 | CREATE TABLE new_snapshots ( 5 | id INT NOT NULL AUTO_INCREMENT, 6 | user_id INT, 7 | page_id INT, 8 | private BOOLEAN NOT NULL DEFAULT false, 9 | PRIMARY KEY(id), 10 | KEY(user_id) 11 | ) 12 | SELECT 13 | id AS slug, 14 | NULL AS user_id, 15 | NULL AS page_id, 16 | print_id AS print_slug, 17 | user_id AS user_slug, 18 | print_page_number, 19 | print_href, 20 | min_row, 21 | max_row, 22 | min_column, 23 | max_column, 24 | min_zoom, 25 | max_zoom, 26 | CONVERT(CAST(CONVERT(description USING latin1) AS BINARY) USING utf8) description, -- fix encoding issues 27 | false AS private, 28 | has_geotiff, 29 | has_geojpeg, 30 | base_url, 31 | uploaded_file, 32 | geojpeg_bounds, 33 | decoding_json, 34 | country_name, 35 | country_woeid, 36 | region_name, 37 | region_woeid, 38 | place_name, 39 | place_woeid, 40 | failed, 41 | progress, 42 | CAST(created_at AS datetime) AS created_at, 43 | updated_at, 44 | CAST(decoded_at AS datetime) AS decoded_at 45 | FROM snapshots 46 | EOQ 47 | 48 | drop_table :snapshots 49 | rename_table :new_snapshots, :snapshots 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /db/migrate/20150220071620_assign_ids_to_users.rb: -------------------------------------------------------------------------------- 1 | class AssignIdsToUsers < ActiveRecord::Migration[4.2] 2 | def up 3 | execute <<-EOQ 4 | CREATE TABLE new_users ( 5 | id INT NOT NULL AUTO_INCREMENT, 6 | PRIMARY KEY(id), 7 | UNIQUE(username), 8 | UNIQUE(email), 9 | UNIQUE(reset_password_token), 10 | UNIQUE(confirmation_token) 11 | ) 12 | SELECT 13 | id AS slug, 14 | CONVERT(CAST(CONVERT(username USING latin1) AS BINARY) USING utf8) username, -- fix encoding issues 15 | legacy_password, 16 | email, 17 | encrypted_password, 18 | reset_password_token, 19 | reset_password_sent_at, 20 | remember_created_at, 21 | confirmation_token, 22 | confirmed_at, 23 | confirmation_sent_at, 24 | unconfirmed_email, 25 | updated_at, 26 | created_at 27 | FROM users 28 | EOQ 29 | 30 | drop_table :users 31 | rename_table :new_users, :users 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /db/migrate/20150220071630_assign_ids_to_mb_tiles.rb: -------------------------------------------------------------------------------- 1 | class AssignIdsToMbTiles < ActiveRecord::Migration[4.2] 2 | def up 3 | execute <<-EOQ 4 | CREATE TABLE new_mbtiles ( 5 | id INT NOT NULL AUTO_INCREMENT, 6 | user_id INT NOT NULL, 7 | private BOOLEAN NOT NULL DEFAULT FALSE, 8 | PRIMARY KEY(id), 9 | KEY(user_id) 10 | ) 11 | SELECT 12 | id AS slug, 13 | -1 AS user_id, 14 | user_id AS user_slug, 15 | false AS private, 16 | url, 17 | uploaded_file, 18 | min_zoom, 19 | max_zoom, 20 | center_zoom, 21 | center_x_coord, 22 | center_y_coord, 23 | CAST(created AS datetime) AS created_at, 24 | CAST(created AS datetime) AS updated_at 25 | FROM mbtiles 26 | EOQ 27 | 28 | drop_table :mbtiles 29 | rename_table :new_mbtiles, :mbtiles 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /db/migrate/20150220071639_assign_ids_to_notes.rb: -------------------------------------------------------------------------------- 1 | class AssignIdsToNotes < ActiveRecord::Migration[4.2] 2 | def up 3 | execute <<-EOQ 4 | CREATE TABLE notes ( 5 | id INT NOT NULL AUTO_INCREMENT, 6 | snapshot_id INT NOT NULL, 7 | user_id INT, 8 | PRIMARY KEY(id) 9 | ) 10 | SELECT 11 | scan_id AS scan_slug, 12 | user_id AS user_slug, 13 | -1 AS snapshot_id, 14 | NULL AS user_id, 15 | note_number, 16 | CONVERT(CAST(CONVERT(note USING latin1) AS BINARY) USING utf8) note, -- fix encoding issues 17 | latitude, 18 | longitude, 19 | geometry, 20 | CAST(created AS datetime) AS created_at, 21 | CAST(created AS datetime) AS updated_at 22 | FROM scan_notes 23 | EOQ 24 | 25 | drop_table :scan_notes 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /db/migrate/20150220071900_reverse_propagate_atlas_metadata.rb: -------------------------------------------------------------------------------- 1 | class ReversePropagateAtlasMetadata < ActiveRecord::Migration[4.2] 2 | def change 3 | execute <<-EOQ 4 | CREATE TEMPORARY TABLE atlas_metadata 5 | SELECT 6 | print_slug, 7 | zoom, 8 | provider 9 | FROM pages 10 | GROUP BY print_slug 11 | EOQ 12 | 13 | execute <<-EOQ 14 | UPDATE atlas_metadata 15 | LEFT JOIN atlases ON atlases.slug = atlas_metadata.print_slug 16 | SET atlases.zoom = atlas_metadata.zoom, 17 | atlases.provider = atlas_metadata.provider 18 | EOQ 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /db/migrate/20150221002228_add_unique_indexes_for_slugs.rb: -------------------------------------------------------------------------------- 1 | class AddUniqueIndexesForSlugs < ActiveRecord::Migration[4.2] 2 | def up 3 | remove_index :atlases, :slug 4 | remove_index :snapshots, :slug 5 | 6 | add_index :atlases, :slug, unique: true 7 | add_index :snapshots, :slug, unique: true 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20150223220737_add_attachment_scene_to_snapshots.rb: -------------------------------------------------------------------------------- 1 | class AddAttachmentSceneToSnapshots < ActiveRecord::Migration[4.2] 2 | def self.up 3 | change_table :snapshots do |t| 4 | t.attachment :scene 5 | t.string :s3_scene_url 6 | end 7 | end 8 | 9 | def self.down 10 | remove_column :snapshots, :s3_scene_url 11 | remove_attachment :snapshots, :scene 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20150224193646_drop_text_from_pages.rb: -------------------------------------------------------------------------------- 1 | class DropTextFromPages < ActiveRecord::Migration[4.2] 2 | def up 3 | remove_column :pages, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20150317220513_correct_atlas_provider.rb: -------------------------------------------------------------------------------- 1 | class CorrectAtlasProvider < ActiveRecord::Migration[4.2] 2 | def change 3 | execute <<-EOQ 4 | CREATE TEMPORARY TABLE atlas_metadata2 5 | SELECT 6 | atlas_id, 7 | zoom, 8 | provider 9 | FROM pages 10 | GROUP BY atlas_id 11 | EOQ 12 | 13 | execute <<-EOQ 14 | UPDATE atlas_metadata2 15 | LEFT JOIN atlases ON atlases.id = atlas_metadata2.atlas_id 16 | SET atlases.zoom = atlas_metadata2.zoom, 17 | atlases.provider = atlas_metadata2.provider 18 | EOQ 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /db/migrate/20150422001133_add_failed_at_to_atlases.rb: -------------------------------------------------------------------------------- 1 | class AddFailedAtToAtlases < ActiveRecord::Migration[4.2] 2 | def change 3 | change_table(:atlases) do |t| 4 | t.datetime :failed_at 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20150430230647_add_bounds_to_snapshots.rb: -------------------------------------------------------------------------------- 1 | class AddBoundsToSnapshots < ActiveRecord::Migration[4.2] 2 | def change 3 | change_table(:snapshots) do |t| 4 | t.float :west 5 | t.float :south 6 | t.float :east 7 | t.float :north 8 | t.integer :zoom 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20150430231207_populate_snapshot_bounds.rb: -------------------------------------------------------------------------------- 1 | class PopulateSnapshotBounds < ActiveRecord::Migration[4.2] 2 | def up 3 | execute <<-EOQ 4 | UPDATE snapshots 5 | SET south=SUBSTRING_INDEX(geojpeg_bounds, ',', 1), 6 | west=SUBSTRING_INDEX(SUBSTRING_INDEX(geojpeg_bounds, ',', 2), ',', -1), 7 | north=SUBSTRING_INDEX(SUBSTRING_INDEX(geojpeg_bounds, ',', -2), ',', 1), 8 | east=SUBSTRING_INDEX(geojpeg_bounds, ',', -1), 9 | zoom=min_zoom + 3 10 | EOQ 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20150430232850_drop_geo_jpeg_bounds_from_snapshots.rb: -------------------------------------------------------------------------------- 1 | class DropGeoJpegBoundsFromSnapshots < ActiveRecord::Migration[4.2] 2 | def change 3 | remove_column :snapshots, :geojpeg_bounds 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20150430233132_drop_decoding_json_from_snapshots.rb: -------------------------------------------------------------------------------- 1 | class DropDecodingJsonFromSnapshots < ActiveRecord::Migration[4.2] 2 | def change 3 | remove_column :snapshots, :decoding_json 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20150504234531_add_geo_tiff_url_to_snapshots.rb: -------------------------------------------------------------------------------- 1 | class AddGeoTiffUrlToSnapshots < ActiveRecord::Migration[4.2] 2 | def up 3 | change_table(:snapshots) do |t| 4 | t.string :geotiff_url 5 | end 6 | 7 | execute <<-EOQ 8 | DELETE FROM snapshots 9 | WHERE base_url IS NULL 10 | EOQ 11 | 12 | execute <<-EOQ 13 | UPDATE snapshots 14 | SET geotiff_url = CONCAT('http://s3.amazonaws.com/files.fieldpapers.org/snapshots/', slug, '/walking-paper-', slug, '.tif') 15 | EOQ 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20150505010118_convert_snapshot_failed_to_timestamp.rb: -------------------------------------------------------------------------------- 1 | class ConvertSnapshotFailedToTimestamp < ActiveRecord::Migration[4.2] 2 | def up 3 | change_table(:snapshots) do |t| 4 | t.datetime :failed_at 5 | end 6 | 7 | execute <<-EOQ 8 | UPDATE snapshots 9 | SET failed_at=decoded_at 10 | WHERE failed=true 11 | EOQ 12 | 13 | remove_column :snapshots, :failed 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20150518231916_add_workflow_state_to_atlas.rb: -------------------------------------------------------------------------------- 1 | class AddWorkflowStateToAtlas < ActiveRecord::Migration[4.2] 2 | def change 3 | change_table(:atlases) do |t| 4 | t.string :workflow_state 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20150519044330_add_pdf_url_to_page.rb: -------------------------------------------------------------------------------- 1 | class AddPdfUrlToPage < ActiveRecord::Migration[4.2] 2 | def change 3 | change_table(:pages) do |t| 4 | t.string :pdf_url 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20150519224555_rename_print_href_to_page_url.rb: -------------------------------------------------------------------------------- 1 | class RenamePrintHrefToPageUrl < ActiveRecord::Migration[4.2] 2 | def change 3 | rename_column :snapshots, :print_href, :page_url 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20150519230420_add_workflow_state_to_snapshot.rb: -------------------------------------------------------------------------------- 1 | class AddWorkflowStateToSnapshot < ActiveRecord::Migration[4.2] 2 | def change 3 | change_table(:snapshots) do |t| 4 | t.string :workflow_state 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20150528055308_update_atlas_pdf_urls.rb: -------------------------------------------------------------------------------- 1 | class UpdateAtlasPdfUrls < ActiveRecord::Migration[4.2] 2 | def up 3 | execute <<-EOQ 4 | UPDATE atlases 5 | SET pdf_url=CONCAT('http://s3.amazonaws.com/files.fieldpapers.org/atlases/', slug, '/walking-paper-', slug, '.pdf') 6 | WHERE pdf_url REGEXP 'fieldpapers.org/files/prints/.+/walking' 7 | EOQ 8 | execute <<-EOQ 9 | UPDATE atlases 10 | SET pdf_url=CONCAT('http://s3.amazonaws.com/files.fieldpapers.org/atlases/', slug, '/field-paper-', slug, '.pdf') 11 | WHERE pdf_url REGEXP 'fieldpapers.org/files/prints/.+/field' 12 | EOQ 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | db: 3 | image: mysql:latest 4 | environment: 5 | - MYSQL_ROOT_PASSWORD=fp 6 | - MYSQL_DATABASE=fieldpapers_development 7 | - MYSQL_USER=fieldpapers 8 | - MYSQL_PASSWORD=fieldpapers 9 | ports: 10 | - 3306:3306 11 | web: 12 | build: . 13 | env_file: .env 14 | environment: 15 | - RAILS_ENV=development 16 | - RAILS_DEVELOPMENT_HOSTS=web 17 | - DATABASE_URL=mysql2://fieldpapers:fieldpapers@db/fieldpapers_development 18 | - TEST_DATABASE_URL=mysql2://fieldpapers:fieldpapers@db/fieldpapers_test 19 | - BASE_URL=http://web:3000 20 | # needs to be publicly addressable 21 | - TILE_BASE_URL=http://tiler:8080 22 | - TASK_BASE_URL=http://tasks:8081 23 | hostname: fieldpapers 24 | links: 25 | - db 26 | ports: 27 | - 3000:3000 28 | volumes: 29 | - ./app:/app/app 30 | - ./config:/app/config 31 | - ./db:/app/db 32 | - ./lib:/app/lib 33 | - ./locale:/app/locale 34 | - ./public:/app/public 35 | - ./test:/app/test 36 | tasks: 37 | image: quay.io/fieldpapers/tasks:v0.12.0 38 | env_file: .env 39 | environment: 40 | # this will cause generated URLs in the PDFs to look suspicious, but it will work 41 | - API_BASE_URL=http://web:3000/ 42 | - NODE_ENV=production 43 | - PORT=8081 44 | ports: 45 | - 8081:8081 46 | tiler: 47 | image: quay.io/fieldpapers/tiler:v0.2.0 48 | env_file: .env 49 | environment: 50 | - API_BASE_URL=http://web:3000/ 51 | - NODE_ENV=production 52 | - PORT=8080 53 | ports: 54 | - 8080:8080 55 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/lib/assets/.keep -------------------------------------------------------------------------------- /lib/geocoder.rb: -------------------------------------------------------------------------------- 1 | require "faraday" 2 | require "faraday_middleware" 3 | 4 | class Geocoder 5 | class PlaceNotFoundException < Exception 6 | attr_reader :place 7 | 8 | def initialize(message, place) 9 | super(message) 10 | @place = place 11 | end 12 | end 13 | 14 | def self.query(q) 15 | client = Faraday.new(url: "https://search.mapzen.com/v1/search") do |faraday| 16 | faraday.response :json, content_type: /\bjson$/ 17 | 18 | faraday.adapter Faraday.default_adapter 19 | end 20 | 21 | rsp = client.get("", { 22 | api_key: ENV["MAPZEN_SEARCH_KEY"], 23 | text: q, 24 | size: 1, 25 | }) 26 | 27 | case rsp.status 28 | when 200 29 | results = rsp.body["features"] 30 | 31 | raise PlaceNotFoundException.new("'#{q}' could not be found", q) if results.empty? 32 | 33 | result = results.first 34 | 35 | zoom = case result["properties"]["layer"] 36 | when "neighborhood" 37 | 14 38 | when "locality" 39 | 12 40 | when "region" 41 | 8 42 | when "country" 43 | 6 44 | else 45 | 10 46 | end 47 | 48 | return zoom, result["geometry"]["coordinates"][0], result["geometry"]["coordinates"][1] 49 | else 50 | raise rsp.body 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/paper.rb: -------------------------------------------------------------------------------- 1 | class Paper 2 | # from http://www.papersizes.org/ 3 | PAPER_SIZES = { 4 | "4a0" => [66.2, 93.6], 5 | "2a0" => [46.8, 66.2], 6 | "a0" => [33.1, 46.8], 7 | "a1" => [23.4, 33.1], 8 | "a2" => [16.5, 23.4], 9 | "a3" => [11.7, 16.5], 10 | "a4" => [8.3, 11.6], 11 | "a5" => [5.8, 8.3], 12 | "a6" => [4.1, 5.8], 13 | "a7" => [2.9, 4.1], 14 | "a8" => [2.0, 2.9], 15 | "a9" => [1.5, 2.0], 16 | "a10" => [1.0, 1.5], 17 | "letter" => [8.5, 11], 18 | "legal" => [8.5, 14], 19 | "junior legal" => [5, 8], 20 | "tabloid" => [11, 17], 21 | } 22 | 23 | # paper sizes (in inches) 24 | def self.size(format) 25 | PAPER_SIZES[format.downcase] 26 | end 27 | 28 | def self.canvas_size(format, orientation) 29 | paper_size = self.size(format) 30 | 31 | paper_size = paper_size.reverse if orientation == "landscape" 32 | 33 | # TODO extract margins 34 | [paper_size[0] - 1, paper_size[1] - 1.5] 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/providers.rb: -------------------------------------------------------------------------------- 1 | # Base maps available for Fieldpapers 2 | # 3 | require "json" 4 | 5 | class Providers 6 | # Use layers 7 | def self.default 8 | self.layers.keys.first 9 | end 10 | 11 | def self.options() 12 | self.layers.map do |k, v| 13 | [v['label'], v['template']] 14 | end 15 | end 16 | 17 | # Get the ID of the provider that corresponds to the 18 | # given URL (which may be saved in the database). 19 | # TODO: is there a better way? 20 | def self.derive(url) 21 | if url.include? 'openstreetmap.org' 22 | 'openstreetmap' 23 | elsif url.include? 'toner-lite' 24 | 'toner' 25 | elsif url.include? 'boner' 26 | 'satellite-labels' 27 | elsif url.include? 'bing-lite' 28 | 'satellite-only' 29 | elsif url.include? 'openstreetmap.fr/hot' 30 | 'humanitarian' 31 | elsif url.include? 'kll.ptthjjor' 32 | 'humanitarian-nepal' 33 | elsif url.include? 'stamen.i808gmk6' 34 | 'mapbox-satellite' 35 | elsif url.include? 'opencyclemap.org' 36 | 'opencyclemap' 37 | elsif url.include? 'sputnik.ru' 38 | 'sputnik.ru' 39 | else 40 | url 41 | end 42 | end 43 | 44 | def self.get_layer_from_url(url) 45 | layer_key = self.derive(url) 46 | obj = self.layers.select do |k,v| 47 | k.to_s == layer_key 48 | end.values 49 | end 50 | 51 | def self.get_template_from_url(url) 52 | provider_layer = self.get_layer_from_url(url) 53 | if provider_layer && !provider_layer[0].nil? 54 | return provider_layer[0]['template'] 55 | end 56 | url 57 | end 58 | 59 | def self.layers 60 | # For layer options see: http://leafletjs.com/reference.html#tilelayer 61 | # templates are (currently) expected to be ModestMaps-formatted, which means 62 | # capital letters for placeholders 63 | @layers ||= JSON.parse(File.read("config/providers.json")) 64 | end 65 | 66 | end 67 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/lib/tasks/.keep -------------------------------------------------------------------------------- /locale/ar/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/ar/app.po.time_stamp -------------------------------------------------------------------------------- /locale/cs/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/cs/app.po.time_stamp -------------------------------------------------------------------------------- /locale/da/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/da/app.po.time_stamp -------------------------------------------------------------------------------- /locale/de/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/de/app.po.time_stamp -------------------------------------------------------------------------------- /locale/en/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/en/app.po.time_stamp -------------------------------------------------------------------------------- /locale/es/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/es/app.po.time_stamp -------------------------------------------------------------------------------- /locale/es_MX/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/es_MX/app.po.time_stamp -------------------------------------------------------------------------------- /locale/fr/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/fr/app.po.time_stamp -------------------------------------------------------------------------------- /locale/hu/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/hu/app.po.time_stamp -------------------------------------------------------------------------------- /locale/id/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/id/app.po.time_stamp -------------------------------------------------------------------------------- /locale/it/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/it/app.po.time_stamp -------------------------------------------------------------------------------- /locale/ja/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/ja/app.po.time_stamp -------------------------------------------------------------------------------- /locale/ku/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/ku/app.po.time_stamp -------------------------------------------------------------------------------- /locale/nl/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/nl/app.po.time_stamp -------------------------------------------------------------------------------- /locale/pl/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/pl/app.po.time_stamp -------------------------------------------------------------------------------- /locale/pt/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/pt/app.po.time_stamp -------------------------------------------------------------------------------- /locale/pt_BR/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/pt_BR/app.po.time_stamp -------------------------------------------------------------------------------- /locale/ru/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/ru/app.po.time_stamp -------------------------------------------------------------------------------- /locale/sw/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/sw/app.po.time_stamp -------------------------------------------------------------------------------- /locale/tl/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/tl/app.po.time_stamp -------------------------------------------------------------------------------- /locale/uk/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/uk/app.po.time_stamp -------------------------------------------------------------------------------- /locale/vi/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/vi/app.po.time_stamp -------------------------------------------------------------------------------- /locale/zh_CN/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/zh_CN/app.po.time_stamp -------------------------------------------------------------------------------- /locale/zh_TW/app.po.time_stamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/locale/zh_TW/app.po.time_stamp -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/log/.keep -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | The page you were looking for doesn't exist (404) 13 | 14 | 63 | 64 | 65 | 66 | 67 |
    68 |
    69 |

    The page you were looking for doesn't exist.

    70 |

    You may have mistyped the address or the page may have moved.

    71 |
    72 |

    If you are the application owner check the logs for more information.

    73 |
    74 | 75 | 76 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | The change you wanted was rejected (422) 13 | 14 | 63 | 64 | 65 | 66 | 67 |
    68 |
    69 |

    The change you wanted was rejected.

    70 |

    Maybe you tried to change something you didn't have access to.

    71 |
    72 |

    If you are the application owner check the logs for more information.

    73 |
    74 | 75 | 76 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | We're sorry, but something went wrong (500) 13 | 14 | 63 | 64 | 65 | 66 | 67 |
    68 |
    69 |

    We're sorry, but something went wrong.

    70 |
    71 |

    If you are the application owner check the logs for more information.

    72 |
    73 | 74 | 75 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | transifex-client 2 | -------------------------------------------------------------------------------- /sample-production.env: -------------------------------------------------------------------------------- 1 | AWS_ACCESS_KEY_ID= 2 | AWS_REGION=us-east-1 3 | AWS_SECRET_ACCESS_KEY= 4 | BASE_URL=https://fieldpapers.org 5 | MAIL_ORIGIN=help@fieldpapers.org 6 | MAIL_SOURCE_ARN=arn:aws:ses:us-east-1:585138094471:identity/help@fieldpapers.org 7 | DATABASE_URL=mysql2://:@:3306/fieldpapers_production 8 | S3_BUCKET_NAME=files.fieldpapers.org 9 | SECRET_KEY_BASE= 10 | SENTRY_DSN= 11 | TASK_BASE_URL=https://tasks.fieldpapers.org 12 | TILE_BASE_URL=https://tiles.fieldpapers.org 13 | URL_HOST=fieldpapers.org 14 | MAPZEN_SEARCH_KEY=search-xxxxxxx 15 | 16 | # automatically set by Heroku 17 | RACK_ENV=production 18 | RAILS_ENV=production 19 | RAILS_SERVE_STATIC_FILES=enabled 20 | -------------------------------------------------------------------------------- /sample.env: -------------------------------------------------------------------------------- 1 | DATABASE_URL=mysql2://root@localhost/fieldpapers_development 2 | AWS_ACCESS_KEY_ID= 3 | AWS_SECRET_ACCESS_KEY= 4 | S3_BUCKET_NAME= 5 | AWS_REGION=us-east-1 6 | MAPZEN_SEARCH_KEY=search-xxxxxxx 7 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/test/controllers/.keep -------------------------------------------------------------------------------- /test/controllers/atlases_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class AtlasesControllerTest < ActionController::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/compose_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ComposeControllerTest < ActionController::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/home_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class HomeControllerTest < ActionController::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/pages_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PagesControllerTest < ActionController::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/snapshots_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SnapshotsControllerTest < ActionController::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/test/fixtures/.keep -------------------------------------------------------------------------------- /test/fixtures/atlases.yml: -------------------------------------------------------------------------------- 1 | 2 | # == Schema Information 3 | # 4 | # Table name: atlases 5 | # 6 | # id :integer not null, primary key 7 | # user_id :integer 8 | # slug :string(8) not null 9 | # title :text(4294967295) 10 | # text :text(4294967295) 11 | # west :float(53) not null 12 | # south :float(53) not null 13 | # east :float(53) not null 14 | # north :float(53) not null 15 | # zoom :integer 16 | # rows :integer not null 17 | # cols :integer not null 18 | # provider :string(255) 19 | # paper_size :string(6) default("letter"), not null 20 | # orientation :string(9) default("portrait"), not null 21 | # layout :string(9) default("full-page"), not null 22 | # pdf_url :string(255) 23 | # preview_url :string(255) 24 | # country_name :string(64) 25 | # country_woeid :integer 26 | # region_name :string(64) 27 | # region_woeid :integer 28 | # place_name :string(128) 29 | # place_woeid :integer 30 | # progress :float(24) 31 | # private :boolean default(FALSE), not null 32 | # cloned_from :integer 33 | # refreshed_from :integer 34 | # created_at :datetime 35 | # updated_at :datetime 36 | # composed_at :datetime 37 | # failed_at :datetime 38 | # workflow_state :string(255) 39 | # 40 | -------------------------------------------------------------------------------- /test/fixtures/pages.yml: -------------------------------------------------------------------------------- 1 | 2 | # == Schema Information 3 | # 4 | # Table name: pages 5 | # 6 | # id :integer not null, primary key 7 | # atlas_id :integer not null 8 | # page_number :string(5) not null 9 | # west :float(53) not null 10 | # south :float(53) not null 11 | # east :float(53) not null 12 | # north :float(53) not null 13 | # zoom :integer 14 | # provider :string(255) 15 | # preview_url :string(255) 16 | # country_name :string(64) 17 | # country_woeid :integer 18 | # region_name :string(64) 19 | # place_name :string(128) 20 | # place_woeid :integer 21 | # created_at :datetime 22 | # updated_at :datetime 23 | # composed_at :datetime 24 | # pdf_url :string(255) 25 | # 26 | -------------------------------------------------------------------------------- /test/fixtures/snapshots.yml: -------------------------------------------------------------------------------- 1 | 2 | # == Schema Information 3 | # 4 | # Table name: snapshots 5 | # 6 | # id :integer not null, primary key 7 | # slug :string(8) not null 8 | # user_id :integer 9 | # page_id :integer 10 | # print_href :text(65535) 11 | # min_row :float(24) 12 | # max_row :float(24) 13 | # min_column :float(24) 14 | # max_column :float(24) 15 | # min_zoom :integer 16 | # max_zoom :integer 17 | # description :text(4294967295) 18 | # private :boolean default(FALSE), not null 19 | # has_geotiff :string(3) default("no") 20 | # has_geojpeg :string(3) default("no") 21 | # base_url :string(255) 22 | # uploaded_file :string(255) 23 | # country_name :string(64) 24 | # country_woeid :integer 25 | # region_name :string(64) 26 | # region_woeid :integer 27 | # place_name :string(128) 28 | # place_woeid :integer 29 | # progress :float(24) 30 | # created_at :datetime 31 | # updated_at :datetime 32 | # decoded_at :datetime 33 | # scene_file_name :string(255) 34 | # scene_content_type :string(255) 35 | # scene_file_size :integer 36 | # scene_updated_at :datetime 37 | # s3_scene_url :string(255) 38 | # west :float(24) 39 | # south :float(24) 40 | # east :float(24) 41 | # north :float(24) 42 | # zoom :integer 43 | # geotiff_url :string(255) 44 | # failed_at :datetime 45 | # 46 | -------------------------------------------------------------------------------- /test/fixtures/users.yml: -------------------------------------------------------------------------------- 1 | 2 | # == Schema Information 3 | # 4 | # Table name: users 5 | # 6 | # id :integer not null, primary key 7 | # username :string(32) 8 | # legacy_password :string(40) 9 | # email :string(255) 10 | # encrypted_password :string(255) default(""), not null 11 | # reset_password_token :string(255) 12 | # reset_password_sent_at :datetime 13 | # remember_created_at :datetime 14 | # confirmation_token :string(255) 15 | # confirmed_at :datetime 16 | # confirmation_sent_at :datetime 17 | # unconfirmed_email :string(255) 18 | # updated_at :datetime 19 | # created_at :datetime 20 | # 21 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/test/helpers/.keep -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/test/integration/.keep -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/test/mailers/.keep -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/test/models/.keep -------------------------------------------------------------------------------- /test/models/atlas_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: atlases 4 | # 5 | # id :integer not null, primary key 6 | # user_id :integer 7 | # slug :string(8) not null 8 | # title :text(4294967295) 9 | # text :text(4294967295) 10 | # west :float(53) not null 11 | # south :float(53) not null 12 | # east :float(53) not null 13 | # north :float(53) not null 14 | # zoom :integer 15 | # rows :integer not null 16 | # cols :integer not null 17 | # provider :string(255) 18 | # paper_size :string(6) default("letter"), not null 19 | # orientation :string(9) default("portrait"), not null 20 | # layout :string(9) default("full-page"), not null 21 | # pdf_url :string(255) 22 | # preview_url :string(255) 23 | # country_name :string(64) 24 | # country_woeid :integer 25 | # region_name :string(64) 26 | # region_woeid :integer 27 | # place_name :string(128) 28 | # place_woeid :integer 29 | # progress :float(24) 30 | # private :boolean default(FALSE), not null 31 | # cloned_from :integer 32 | # refreshed_from :integer 33 | # created_at :datetime 34 | # updated_at :datetime 35 | # composed_at :datetime 36 | # failed_at :datetime 37 | # workflow_state :string(255) 38 | # 39 | 40 | require 'test_helper' 41 | 42 | class AtlasTest < ActiveSupport::TestCase 43 | def assert_bounds(page, west, south, east, north) 44 | assert_equal west, page.west 45 | assert_equal south, page.south 46 | assert_equal east, page.east 47 | assert_equal north, page.north 48 | end 49 | 50 | test "#create creates associated pages" do 51 | west = -122.3789 52 | south = 47.5771 53 | east = -122.2931 54 | north = 47.6349 55 | 56 | atlas = Atlas.create! \ 57 | cols: 2, 58 | rows: 2, 59 | west: west, 60 | south: south, 61 | east: east, 62 | north: north, 63 | zoom: 13, 64 | provider: "https://{S}.tile.openstreetmap.org/{Z}/{X}/{Y}.png" 65 | 66 | assert_equal 5, atlas.pages.size 67 | 68 | assert_equal "i", atlas.pages[0].page_number 69 | assert_equal "A1", atlas.pages[1].page_number 70 | assert_equal "A2", atlas.pages[2].page_number 71 | assert_equal "B1", atlas.pages[3].page_number 72 | assert_equal "B2", atlas.pages[4].page_number 73 | 74 | assert_bounds atlas.pages[1], west, (north + south) / 2, (west + east) / 2, north 75 | assert_bounds atlas.pages[2], (west + east) / 2, (north + south) / 2, east, north 76 | assert_bounds atlas.pages[3], west, south, (west + east) / 2, (north + south) / 2 77 | assert_bounds atlas.pages[4], (west + east) / 2, south, east, (north + south) / 2 78 | 79 | buffered_west = west - ((east - west) * 0.1).abs 80 | buffered_south = south - ((north - south) * 0.1).abs 81 | buffered_east = east + ((east - west) * 0.1).abs 82 | buffered_north = north + ((north - south) * 0.1).abs 83 | 84 | assert_bounds atlas.pages[0], buffered_west, buffered_south, buffered_east, buffered_north 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /test/models/page_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: pages 4 | # 5 | # id :integer not null, primary key 6 | # atlas_id :integer not null 7 | # page_number :string(5) not null 8 | # west :float(53) not null 9 | # south :float(53) not null 10 | # east :float(53) not null 11 | # north :float(53) not null 12 | # zoom :integer 13 | # provider :string(255) 14 | # preview_url :string(255) 15 | # country_name :string(64) 16 | # country_woeid :integer 17 | # region_name :string(64) 18 | # place_name :string(128) 19 | # place_woeid :integer 20 | # created_at :datetime 21 | # updated_at :datetime 22 | # composed_at :datetime 23 | # pdf_url :string(255) 24 | # 25 | 26 | require 'test_helper' 27 | 28 | class PageTest < ActiveSupport::TestCase 29 | # test "the truth" do 30 | # assert true 31 | # end 32 | end 33 | -------------------------------------------------------------------------------- /test/models/snapshot_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: snapshots 4 | # 5 | # id :integer not null, primary key 6 | # slug :string(8) not null 7 | # user_id :integer 8 | # page_id :integer 9 | # print_href :text(65535) 10 | # min_row :float(24) 11 | # max_row :float(24) 12 | # min_column :float(24) 13 | # max_column :float(24) 14 | # min_zoom :integer 15 | # max_zoom :integer 16 | # description :text(4294967295) 17 | # private :boolean default(FALSE), not null 18 | # has_geotiff :string(3) default("no") 19 | # has_geojpeg :string(3) default("no") 20 | # base_url :string(255) 21 | # uploaded_file :string(255) 22 | # country_name :string(64) 23 | # country_woeid :integer 24 | # region_name :string(64) 25 | # region_woeid :integer 26 | # place_name :string(128) 27 | # place_woeid :integer 28 | # progress :float(24) 29 | # created_at :datetime 30 | # updated_at :datetime 31 | # decoded_at :datetime 32 | # scene_file_name :string(255) 33 | # scene_content_type :string(255) 34 | # scene_file_size :integer 35 | # scene_updated_at :datetime 36 | # s3_scene_url :string(255) 37 | # west :float(24) 38 | # south :float(24) 39 | # east :float(24) 40 | # north :float(24) 41 | # zoom :integer 42 | # geotiff_url :string(255) 43 | # failed_at :datetime 44 | # 45 | 46 | require 'test_helper' 47 | 48 | class SnapshotTest < ActiveSupport::TestCase 49 | # test "the truth" do 50 | # assert true 51 | # end 52 | end 53 | -------------------------------------------------------------------------------- /test/models/user_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: users 4 | # 5 | # id :integer not null, primary key 6 | # username :string(32) 7 | # legacy_password :string(40) 8 | # email :string(255) 9 | # encrypted_password :string(255) default(""), not null 10 | # reset_password_token :string(255) 11 | # reset_password_sent_at :datetime 12 | # remember_created_at :datetime 13 | # confirmation_token :string(255) 14 | # confirmed_at :datetime 15 | # confirmation_sent_at :datetime 16 | # unconfirmed_email :string(255) 17 | # updated_at :datetime 18 | # created_at :datetime 19 | # 20 | 21 | require 'test_helper' 22 | 23 | class UserTest < ActiveSupport::TestCase 24 | # test "the truth" do 25 | # assert true 26 | # end 27 | end 28 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | 5 | require 'minitest/reporters' 6 | Minitest::Reporters.use!( 7 | Minitest::Reporters::SpecReporter.new, 8 | ENV, 9 | Minitest.backtrace_filter 10 | ) 11 | 12 | class ActiveSupport::TestCase 13 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 14 | fixtures :all 15 | 16 | # Add more helper methods to be used by all tests here... 17 | end 18 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/vendor/assets/javascripts/.keep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fieldpapers/fp-web/6cdeb875bcd8242f9ec2671d2a26c61a5388c60b/vendor/assets/stylesheets/.keep --------------------------------------------------------------------------------