+
+
+ <%= bootstrap_flash :close_button => false %>
+
+
+
-
diff --git a/bin/bundle b/bin/bundle
index d63e647b..f7b07eed 100755
--- a/bin/bundle
+++ b/bin/bundle
@@ -1,5 +1,5 @@
#!/usr/bin/env ruby
-unless ENV["RAILS_ENV"] == "production" || File.exist?("#{__dir__}/../.skip-docker")
+if File.exist?("#{__dir__}/../.use-docker")
exec("#{__dir__}/../script/docker-environment", $PROGRAM_NAME, *ARGV) unless File.exist?("/etc/app-env")
end
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
diff --git a/bin/rails b/bin/rails
index e8199c68..e2afa025 100755
--- a/bin/rails
+++ b/bin/rails
@@ -1,5 +1,5 @@
#!/usr/bin/env ruby
-unless ENV["RAILS_ENV"] == "production" || File.exist?("#{__dir__}/../.skip-docker")
+if File.exist?("#{__dir__}/../.use-docker")
exec("#{__dir__}/../script/docker-environment", $PROGRAM_NAME, *ARGV) unless File.exist?("/etc/app-env")
end
diff --git a/bin/rake b/bin/rake
index f0ccc9e2..c9e1ee4c 100755
--- a/bin/rake
+++ b/bin/rake
@@ -1,5 +1,5 @@
#!/usr/bin/env ruby
-unless ENV["RAILS_ENV"] == "production" || File.exist?("#{__dir__}/../.skip-docker")
+if File.exist?("#{__dir__}/../.use-docker")
exec("#{__dir__}/../script/docker-environment", $PROGRAM_NAME, *ARGV) unless File.exist?("/etc/app-env")
end
require_relative '../config/boot'
diff --git a/bin/spring b/bin/spring
index 1e047727..4feabfe4 100755
--- a/bin/spring
+++ b/bin/spring
@@ -1,5 +1,5 @@
#!/usr/bin/env ruby
-unless ENV["RAILS_ENV"] == "production" || File.exist?("#{__dir__}/../.skip-docker")
+if File.exist?("#{__dir__}/../.use-docker")
exec("#{__dir__}/../script/docker-environment", $PROGRAM_NAME, *ARGV) unless File.exist?("/etc/app-env")
end
diff --git a/config/database.docker.yml b/config/database.docker.yml
index 790d8f77..cabb221f 100644
--- a/config/database.docker.yml
+++ b/config/database.docker.yml
@@ -1,44 +1,24 @@
-#development:
-# adapter: mysql2
-# database: tracks_dev
-# # set this if you are storing utf8 in your mysql database to handle strings
-# # like "Réné". Not needed for sqlite. For PostgreSQL use encoding: unicode
-# # encoding: utf8
-# host: docker
-# port: 3307
-# username: tracks_dev
-# password: FqUKMWPz5mh8UPhypZvq
-
-#development:
-# adapter: postgresql
-# database: tracks_dev
-# # set this if you are storing utf8 in your mysql database to handle strings
-# # like "Réné". Not needed for sqlite. For PostgreSQL use encoding: unicode
-# # encoding: utf8
-# host: docker
-# port: 5432
-# username: tracks_dev
-# password: password
-
-#development:
-# adapter: sqlite3
-# database: db.sqlite
-
-#test:
-# adapter: mysql2
-# database: tracks_test
-# # set this if you are storing utf8 in your mysql database to handle strings
-# # like "Réné". Not needed for sqlite. For PostgreSQL use encoding: unicode
-# # encoding: utf8
-# host: docker
-# port: 3307
-# username: tracks_tst
-# password: 9rMNV4y6RVcqmJTo2QoR
-
-# Production config is disabled by default
-#
-development:
+test:
adapter: <%= ENV.fetch('DATABASE_TYPE') %>
+ encoding: <%= ENV.fetch('DATABASE_ENCODING') %>
+ database: <%= ENV.fetch('DATABASE_NAME') %>
+ host: <%= ENV.fetch('DATABASE_HOST') %>
+ port: <%= ENV.fetch('DATABASE_PORT') %>
+ username: <%= ENV.fetch('DATABASE_USERNAME') %>
+ password: <%= ENV.fetch('DATABASE_PASSWORD') %>
+
+development:
+ adapter: <%= ENV.fetch('DATABASE_TYPE') %>
+ encoding: <%= ENV.fetch('DATABASE_ENCODING') %>
+ database: <%= ENV.fetch('DATABASE_NAME') %>
+ host: <%= ENV.fetch('DATABASE_HOST') %>
+ port: <%= ENV.fetch('DATABASE_PORT') %>
+ username: <%= ENV.fetch('DATABASE_USERNAME') %>
+ password: <%= ENV.fetch('DATABASE_PASSWORD') %>
+
+production:
+ adapter: <%= ENV.fetch('DATABASE_TYPE') %>
+ encoding: <%= ENV.fetch('DATABASE_ENCODING') %>
database: <%= ENV.fetch('DATABASE_NAME') %>
host: <%= ENV.fetch('DATABASE_HOST') %>
port: <%= ENV.fetch('DATABASE_PORT') %>
diff --git a/config/database.yml b/config/database.yml
deleted file mode 100644
index 757be5fe..00000000
--- a/config/database.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-development:
- adapter: mysql2
- database: tracks
- # set this if you are storing utf8 in your mysql database to handle strings
- # like "Réné". Not needed for sqlite. For PostgreSQL use encoding: unicode
- # encoding: utf8
- host: db
- username: root
- password:
-
-test:
- adapter: mysql2
- database: tracks_test
- # set this if you are storing utf8 in your mysql database to handle strings
- # like "Réné". Not needed for sqlite. For PostgreSQL use encoding: unicode
- # encoding: utf8
- host: db
- username: root
- password:
-
-# Production config is disabled by default
-#
-# production:
-# adapter: mysql2
-# database: tracks
-# # set this if you are storing utf8 in your mysql database to handle strings
-# # like "Réné".Not needed for sqlite. For PostgreSQL use encoding: unicode
-# # encoding: utf8
-# host: localhost
-# username: root
-# password:
-
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 1a545da1..663cbea6 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -51,6 +51,7 @@ en:
user:
auth_type: Auth type
display_name: Display name
+ email: Email address
first_name: First name
last_name: Last name
login: Login
@@ -87,7 +88,7 @@ en:
confirmation: doesn't match confirmation
less_than_or_equal_to: must be less than or equal to %{count}
blank: can't be blank
- invalid: "cannot contain the comma (',') character"
+ invalid: "is not valid"
exclusion: is reserved
odd: must be odd
even: must be even
@@ -400,6 +401,7 @@ en:
import_title: Import data
preferences: Preferences
integrations_: Integrate Tracks
+ help_page: Help
feeds_title: See a list of available feeds
calendar_title: Calendar of due actions
completed_tasks: Done
@@ -819,11 +821,13 @@ en:
authentication_header: Your authentication
current_authentication_type: Your authentication type is %{auth_type}
change_authentication_type: Change your authentication type
+ remove_introduction: You can remove your user account here. Note that this is irreversible and will remove all your data! After removal you will be logged out.
tabs:
authentication: Authentication
tracks_behavior: Tracks behavior
profile: Profile
date_and_time: Date and time
+ remove_account: Remove account
generate_new_token_confirm: Are you sure? Generating a new token will replace the existing one and break any external usages of this token.
data:
import_successful: Import was successful.
@@ -961,6 +965,7 @@ en:
change_password_prompt: Enter your new password in the fields below and click 'Change password' to replace your current password with your new one.
password_confirmation_label: Confirm password
destroy_error: There was an error deleting the user %{login}
+ email_address: Email address
choose_password: Choose password
register_with_cas: With your CAS username
label_auth_type: Authentication type
@@ -978,6 +983,8 @@ en:
change_authentication_type: Change authentication type
total_notes: Total notes
select_authentication_type: Select your new authentication type and click 'Change authentication type' to replace your current settings.
+ approve_tos: I approve the Terms of Service
+ tos_link: read the Terms of Service
feedlist:
choose_context: Choose the context you want a feed of
actions_due_today: Actions due today or earlier
@@ -1005,6 +1012,7 @@ en:
login:
login_cas: go to the CAS
sign_in: Sign in
+ signup_prompt: Want to create an account?
openid_identity_url_not_found: "Sorry, no user by that identity URL exists (%{identity_url})"
user_no_expiry: Stay logged in
cas_no_user_found: "Hello, %{username}! You do not have an account on Tracks."
diff --git a/config/routes.rb b/config/routes.rb
index c35f7991..6650560a 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -25,6 +25,7 @@ Rails.application.routes.draw do
get 'data/xml_export' => 'data#xml_export'
get 'data/csv_actions' => 'data#csv_actions'
+ get 'help' => "integrations#help"
get 'integrations' => "integrations#index"
get 'integrations/rest_api' => "integrations#rest_api", :as => 'rest_api_docs'
post 'integrations/cloudmailin' => 'integrations#cloudmailin'
@@ -105,7 +106,7 @@ Rails.application.routes.draw do
# This means the controller action needs to parse the extension and set format/content type
# Needed for /todos/tag/first.last.m to work
get 'todos/tag/:name' => 'todos#tag', :as => :tag, :format => false, :name => /.*/
-
+
get 'attachments/:id/:filename' => "todos#attachment"
get 'tags.autocomplete' => "todos#tags", :format => 'autocomplete'
get 'todos/done/tag/:name' => "todos#done_tag", :as => :done_tag
diff --git a/config/site.docker.yml b/config/site.docker.yml
index 02c8b373..4b9b2eb5 100644
--- a/config/site.docker.yml
+++ b/config/site.docker.yml
@@ -25,12 +25,6 @@ secret_token: "secret"
# Set to true when your application is running with https
force_ssl: false
-# Configure how static assets (images, stylesheets, etc.) will be served.
-# The best practice is to have a proxying web server such as Apache or Nginx
-# serve static assets (images, stylesheets, javascript) for you. Change
-# this to 'true' if you want Rails to be responsible for serving the static assets
-# serve_static_assets: false
-
# Uncomment if you want to dispatch todos that come from email based on the To:
# address rather than the From: address.
# email_dispatch: 'to'
@@ -48,6 +42,9 @@ force_ssl: false
# Set to true to allow anyone to sign up for a username.
open_signups: false
+# Set to require TOS approval on signup.
+#tos_link: "https://www.example.com"
+
# When integrating your tracks instance with http://cloudmailin.com/ by using
# the /integrations/cloudmailin URL, this value is the cloudmailin-secret for
# verifying the authenticity of the request.
diff --git a/config/site.yml.tmpl b/config/site.yml.tmpl
index 362c8ef6..1d837be0 100644
--- a/config/site.yml.tmpl
+++ b/config/site.yml.tmpl
@@ -28,12 +28,6 @@ secret_token: "change-me"
# Set to true when your application is running with https
force_ssl: false
-# Configure how static assets (images, stylesheets, etc.) will be served.
-# The best practice is to have a proxying web server such as Apache or Nginx
-# serve static assets (images, stylesheets, javascript) for you. Change
-# this to 'true' if you want Rails to be responsible for serving the static assets
-# serve_static_assets: false
-
# Uncomment if you want to dispatch todos that come from email based on the To:
# address rather than the From: address.
# email_dispatch: 'to'
@@ -54,6 +48,8 @@ force_ssl: false
# Set to true to allow anyone to sign up for a username.
open_signups: false
+# Set to require TOS approval on signup.
+#tos_link: "https://www.example.com"
# When integrating your tracks instance with http://cloudmailin.com/ by using
# the /integrations/cloudmailin URL, this value is the cloudmailin-secret for
diff --git a/db/migrate/20200720151220_add_email_to_user.rb b/db/migrate/20200720151220_add_email_to_user.rb
new file mode 100644
index 00000000..0f5627ee
--- /dev/null
+++ b/db/migrate/20200720151220_add_email_to_user.rb
@@ -0,0 +1,5 @@
+class AddEmailToUser < ActiveRecord::Migration[5.2]
+ def change
+ add_column :users, :email, :string
+ end
+end
diff --git a/db/migrate/20200807175610_add_dates_to_user.rb b/db/migrate/20200807175610_add_dates_to_user.rb
new file mode 100644
index 00000000..9f58dfd2
--- /dev/null
+++ b/db/migrate/20200807175610_add_dates_to_user.rb
@@ -0,0 +1,6 @@
+class AddDatesToUser < ActiveRecord::Migration[5.2]
+ def change
+ add_column :users, :created_at, :datetime
+ add_column :users, :updated_at, :datetime
+ end
+end
diff --git a/db/migrate/20200810123316_add_lastlogin_to_user.rb b/db/migrate/20200810123316_add_lastlogin_to_user.rb
new file mode 100644
index 00000000..c7ec36d9
--- /dev/null
+++ b/db/migrate/20200810123316_add_lastlogin_to_user.rb
@@ -0,0 +1,5 @@
+class AddLastloginToUser < ActiveRecord::Migration[5.2]
+ def change
+ add_column :users, :last_login_at, :datetime
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 539e80c0..b0f92d33 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2020_01_09_231555) do
+ActiveRecord::Schema.define(version: 2020_08_10_123316) do
create_table "attachments", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.integer "todo_id"
@@ -220,6 +220,10 @@ ActiveRecord::Schema.define(version: 2020_01_09_231555) do
t.string "open_id_url"
t.string "remember_token"
t.datetime "remember_token_expires_at"
+ t.string "email"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.datetime "last_login_at"
t.index ["login"], name: "index_users_on_login"
end
diff --git a/doc/installation.md b/doc/installation.md
index 23204be2..d138c70b 100644
--- a/doc/installation.md
+++ b/doc/installation.md
@@ -1,20 +1,108 @@
# Installing Tracks
-The following instructions will guide you through the installation of Tracks from source.
+Tracks can be installed several ways: You can run it through Docker, which is recommended because all requirements have already been taken care of for you, or you can install it on a custom server from source.
+
+Community-provided instructions for other options and tips for specific environments are available in the Tracks wiki: https://github.com/TracksApp/tracks/wiki/Installation.
+
+## Docker installation using Docker Compose
+
+You can easily run Tracks using Docker Compose. This option mounts the repository directly inside the Docker container, making it an optimal solution for developing Tracks. It does, however, also work for normal users.
+
+1. Make sure you have Docker Compose and Docker properly installed.
+2. Get the Tracks code base by either downloading the archive file for the latest releast or cloning it from GitHub.
+3. Create a file to enable the Docker Compose support in Tracks. *Note*: This is not needed or useful for the separate container, only Docker Composer!
+```
+ $ touch .use-docker
+```
+4. On the installation run the following command:
+```
+ $ ./script/setup
+```
+5. Run the server using the following command:
+```
+ $ ./script/server
+```
+6. You should now be able to access Tracks in http://localhost:3000
+
+## Docker installation using a separate container
+
+You can also install Tracks without Docker Compose. This allows you to use the official Docker containers and you can use your existing database server instead of having a separate one for Tracks.
+
+*Note*: For now, you have to first build the image manually before starting the
+Tracks container. In future there should be an official image in Docker Hub. You can build the image by:
+
+1. Get the Tracks code base by either downloading the archive file for the latest releast or cloning it from GitHub.
+2. Run the following command in the Tracks directory to build the image:
+```
+ $ docker build -t="tracks" .
+```
+
+1. Make sure you have Docker properly installed.
+2. Start a database container with either MySQL or PostgreSQL:
+```
+ $ docker run -p 5432:5432 --name postgres -e POSTGRES_PASSWORD=password -d postgres
+ $ docker run -p 3306:3306 --name mariadb -e MYSQL_ROOT_PASSWORD=password -d mariadb
+```
+3. Create a database and a user for Tracks in the database:
+```
+ # MySQL
+ $ mysql -u root -p
+ mysql> CREATE DATABASE tracks;
+ mysql> GRANT ALL PRIVILEGES ON tracks.* TO yourmysqluser@localhost IDENTIFIED BY 'password-goes-here' WITH GRANT OPTION;
+
+ # PostgreSQL
+ $ sudo -u postgres psql
+ postgres=# CREATE USER tracks WITH ENCRYPTED PASSWORD 'password-goes-here';
+ postgres=# CREATE DATABASE tracks OWNER=tracks;
+```
+4. Install the schema
+```
+ $ docker run --link mariadb:db --rm -t tracks bin/rake db:reset
+ $ docker run --link postgres:db --rm -t tracks bin/rake db:reset
+```
+5. Start the Tracks server:
+```
+ $ docker run -p 3000:3000 --name tracks --link mariadb:db -t tracks
+ $ docker run -p 3000:3000 --name tracks --link postgres:db -t tracks
+```
+6. You should now be able to access Tracks in http://localhost:3000
+
+## Environmental variables in the Docker image
+
+You can override database connection details by defining the environment variables in the run command, for example ”-e DATABASE_USERNAME=tracks_dev” or docker-compose.yml, if using Docker Compose.
+
+|Name |Default |For PostgreSQL |
+|------------------|-----------|---------------|
+|DATABASE_NAME |tracks | |
+|DATABASE_HOST |db | |
+|DATABASE_PORT |3306 |5432 |
+|DATABASE_USERNAME |tracks | |
+|DATABASE_PASSWORD |password | |
+|DATABASE_TYPE |mysql2 |postgresql |
+|DATABASE_ENCODING |utf8 |unicode |
+|RAILS_ENV |production |
+
+### Override files
+
+You can override files in the Docker image by using the --volume argument or docker-compose.yml, if using Docker Compose.
+
+|File |Argument |Replace for production?|
+|-------------------|---------------------------------------------------------|-----------------------|
+|config/site.yml |--volume /app/config/site.yml:/home/user/site.yml |Yes |
+
+## Custom server installation
This description is intended for people installing Tracks from scratch. If you would like to upgrade an existing installation, please see the [upgrade documentation](upgrading.md).
-For alternative installation options and tips for specific environments, please see [Installation](https://github.com/TracksApp/tracks/wiki/Installation) on the wiki.
-
-## Prerequisites
+### Prerequisites
Tracks has a few software requirements that must be satisfied before installation:
-1. **Ruby**. Tracks requires Ruby 2.4 or greater, but is not tested with 2.7.
+1. **Ruby**. Tracks requires Ruby 2.5 or greater. Most of the testing is done with 2.6.
2. **Bundler**. Tracks requires a recent version of [Bundler](http://bundler.io) to handle the installation of dependencies. Bundler is typically installed by running `gem install bundler`.
-3. **Database**. Tracks is tested on [MySQL](http://www.mysql.com/) and [SQLite](http://www.sqlite.org/), but [PostgreSQL](http://www.postgresql.org/) can also be used. Of the three, SQLite requires the least configuration. Whatever your choice, the appropriate database software must be installed.
+3. **Database**. Tracks is tested on [MySQL](http://www.mysql.com/) and [SQLite](http://www.sqlite.org/), but [PostgreSQL](http://www.postgresql.org/) can also be used. Of the three, SQLite requires the least configuration but is also the least performant and may make it difficult to operate in the future. We recommend either MySQL or PostgreSQL. Whatever your choice, the appropriate database software must be installed.
-## Get Tracks
+### Get Tracks
There are two methods of downloading Tracks:
@@ -27,18 +115,26 @@ There are two methods of downloading Tracks:
git clone https://github.com/TracksApp/tracks.git
cd tracks
-## Set up the database
+### Set up the database
-*This section only applies if you will be using Tracks with a MySQL database.*
+*This section doesn't apply if using SQLite.*
-You need to create a database and database-user to use with Tracks. For this, you can use MySQL Administrator or go into a terminal and issue the following commands:
+You need to create a database and database-user to use with Tracks. For this, you can use an GUI tool or go into a terminal and issue the following commands:
- mysql -u root -p
+#### MySQL
+
+ $ mysql -u root -p
mysql> CREATE DATABASE tracks;
mysql> GRANT ALL PRIVILEGES ON tracks.* TO yourmysqluser@localhost \
IDENTIFIED BY 'password-goes-here' WITH GRANT OPTION;
-## Install dependencies
+#### PostgreSQL
+
+ $ sudo -u postgres psql
+ postgres=# CREATE USER tracks WITH ENCRYPTED PASSWORD 'password-goes-here';
+ postgres=# CREATE DATABASE tracks OWNER=tracks;
+
+### Install dependencies
Tracks is built upon a number of Ruby libraries (known as ‘gems’). The Bundler tool makes it easy to install all the gems that Tracks needs, and ensures that they are all the correct versions.
@@ -64,10 +160,9 @@ Tracks is built upon a number of Ruby libraries (known as ‘gems’). The Bundl
2. Open the file `config/database.yml` and edit the `production:` section with the details of your database. If you are using MySQL the `adapter:` line should read `adapter: mysql2`, `host: localhost` (in the majority of cases), and your username and password should match those you assigned when you created the database. If you are using SQLite3, you should have only two lines under the production section: `adapter: sqlite3` and `database: db/tracks.db`.
3. Open the file `config/site.yml`, and read through the settings to make sure that they suit your setup. In most cases, all you need to change are the `secret_token`, the administrator email address (`admin_email`), and the time zone setting. For the time zone setting you can use the command `bundle exec rake time:zones:local` to see all available timezones on your machine
4. If you are using Windows, you may need to check the ‘shebang’ lines (`#!/usr/bin/env ruby`) of the `/public/dispatch.*` files and all the files in the `/script` directory. They are set to `#!/usr/bin/env ruby` by default. This should work for all Unix based setups (Linux or Mac OS X), but Windows users will probably have to change it to something like `#c:/ruby/bin/ruby` to point to the Ruby binary on your system.
-5. If you intend to deploy Tracks using its included web server, you’ll need to uncomment and change the `serve_static_assets` configuration option to `true` in `config/site.yml` in order for the images, stylesheets, and javascript files to be served correctly.
-6. If you intend to use Tracks behind a web server or reverse proxy with https enabled, ensure to set `force_ssl` option to `true`.
+5. If you intend to use Tracks behind a web server or reverse proxy with https enabled, ensure to set `force_ssl` option to `true`.
-## Populate your database with the Tracks schema
+### Populate your database with the Tracks schema
Open a terminal and change into the root of your Tracks directory. Enter the following command:
@@ -75,24 +170,26 @@ Open a terminal and change into the root of your Tracks directory. Enter the fol
This will set up your database with the required structure to hold Tracks’ data.
-## Precompile assets
+### Precompile assets
Static assets (images, stylesheets, and javascript) need to be compiled in order for them to work correctly with the new asset pipeline feature in Rails. Precompiling your assets is as simple as running the following command while inside the Tracks root directory:
bundle exec rake assets:precompile RAILS_ENV=production
-## Start the server
+### Start the server
While still in the Terminal inside the Tracks root directory, issue the following command:
- bundle exec rails server -e production
+ RAILS_SERVE_STATIC_FILES=TRUE bundle exec rails server -e production
If all goes well, you should see some text informing you that the server is running: `=> Rails application starting in production on http://localhost:3000`. If you are already running other services on port 3000, you need to select a different port when running the server, using the `-p` option.
-## Visit Tracks in a browser
+Optimally you should serve static files using Nginx or Apache, especially in larger production instances. If you do this, you can omit the RAILS_SERVE_STATIC_FILES=TRUE from the start of the command.
+
+### Visit Tracks in a browser
Visit `http://localhost:3000/signup` in a browser (or whatever URL and port was reported when you started the server in the step above) and chose a user name and password for admin user. Once logged in as admin, you can add other (ordinary level) users. If you need to access Tracks from a mobile/cellular phone browser, visit `http://yourdomain.com/mobile/`. This mobile version is a special, lightweight version of Tracks, designed to use on a mobile browser.
-## Customise Tracks
+### Customise Tracks
Once logged in, add some Contexts and Projects, and then go ahead and add your actions. You might also want to visit the Preferences page to edit various settings to your liking. Have fun!
diff --git a/doc/upgrading.md b/doc/upgrading.md
index b0d86911..8e481ddf 100644
--- a/doc/upgrading.md
+++ b/doc/upgrading.md
@@ -1,4 +1,13 @@
# Upgrading Tracks
+## Upgrading from Tracks 2.4.2 to 2.5
+
+* If you're using the Docker Compose environment and want to run the commands in
+ the bin/ directory inside the container from the host system, add a .use-docker
+ file to the root directory. This replaces the old .skip-docker file requirement
+ to favor the more common setup and avoid placing unexpected requirements.
+
+* The Docker environment has been changed quite a bit. However, it should work
+ at least as before for the usual needs.
## Upgrading from Tracks 2.3 to 2.4.2
diff --git a/docker-compose.yml b/docker-compose.yml
index dc2b55cf..00872463 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -9,8 +9,16 @@ services:
- db-data:/var/lib/mysql
web:
build: .
+ environment:
+ # These are set in script/ci-build, so we need to pass-thru them.
+ RAILS_ENV: $RAILS_ENV
+ DATABASE_NAME: $DATABASE_NAME
+ DATABASE_USERNAME: root
+ DATABASE_PASSWORD_EMPTY: 1
volumes:
- - ${VOLUME:-.:/app}
+ - ${VOLUME:-.}:/app:Z
+ - ${VOLUME:-.}/config/database.docker.yml:/app/config/database.yml:Z
+ - ${VOLUME:-.}/config/site.docker.yml:/app/config/site.yml:Z
ports:
- 3000:3000
depends_on:
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
new file mode 100755
index 00000000..bc38d35b
--- /dev/null
+++ b/docker-entrypoint.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+export RAILS_ENV=${RAILS_ENV:-production}
+export DATABASE_NAME=${DATABASE_NAME:-tracks}
+export DATABASE_HOST=${DATABASE_HOST:-db}
+export DATABASE_PORT=${DATABASE_PORT:-3306}
+export DATABASE_USERNAME=${DATABASE_USERNAME:-tracks}
+if [ "$DATABASE_PASSWORD_EMPTY" != 1 ];
+then
+ export DATABASE_PASSWORD=${DATABASE_PASSWORD:-password}
+else
+ export DATABASE_PASSWORD=""
+fi
+export DATABASE_TYPE=${DATABASE_TYPE:-mysql2}
+export DATABASE_ENCODING=${DATABASE_ENCODING:-utf8}
+
+export RAILS_SERVE_STATIC_FILES=TRUE
+export RAILS_LOG_TO_STDOUT=TRUE
+
+exec "$@"
diff --git a/docker-startserver.sh b/docker-startserver.sh
deleted file mode 100755
index dfe1e6e5..00000000
--- a/docker-startserver.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-
-rails db:migrate
-rails server -b 0.0.0.0
diff --git a/lib/login_system.rb b/lib/login_system.rb
index ce2e2614..85d1d3e0 100644
--- a/lib/login_system.rb
+++ b/lib/login_system.rb
@@ -222,6 +222,7 @@ private
def redirect_to_login
respond_to do |format|
format.html { redirect_to login_path }
+ format.js { render js: "redirect_to('" + login_path + "')" }
format.m { redirect_to login_path(:format => 'm') }
end
end
diff --git a/script/bootstrap b/script/bootstrap
new file mode 100755
index 00000000..ef8fc435
--- /dev/null
+++ b/script/bootstrap
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# script/bootstrap: Resolve all dependencies that the application requires to
+# run.
+
+set -e
+
+docker_compose="docker-compose --file docker-compose.yml"
+
+echo "==> Building Docker image…"
+$docker_compose build
+
+echo "==> Building assets…"
+bin/rake assets:precompile
diff --git a/script/cibuild b/script/cibuild
index af04b27a..783a1833 100755
--- a/script/cibuild
+++ b/script/cibuild
@@ -1,4 +1,5 @@
#!/bin/bash
+
set -e
docker_compose="docker-compose --file docker-compose.yml"
@@ -12,14 +13,10 @@ function die() {
exit 1
}
-
trap cleanup EXIT
export RAILS_ENV=test
-export TRACKS_DB=tracks_test
-
-# Put a config/site.yml file in place since it's needed for operation
-cp config/site.yml.tmpl config/site.yml
+export DATABASE_NAME=tracks_test
$docker_compose build
$docker_compose up -d
diff --git a/script/console b/script/console
index ff671891..69f8d3ab 100755
--- a/script/console
+++ b/script/console
@@ -1,7 +1,4 @@
#!/bin/sh
-appdir=$(cd $(dirname "$0")/.. && pwd)
-[ -f /etc/app-env ] || exec "$appdir/script/docker-environment" $0 $@
-
export RAILS_ENV='development'
bin/rails console
diff --git a/script/docker-environment b/script/docker-environment
index 423122d6..1d51c8b1 100755
--- a/script/docker-environment
+++ b/script/docker-environment
@@ -1,17 +1,16 @@
#!/bin/sh
-# Run a command in the app's environment
set -e
-# Find our app dir and just run the command in we're in the container since the
-# container is built with an /etc/app-env file inside of it.
-appdir=$(cd $(dirname "$0")/.. && pwd)
-[ -f /etc/app-env ] && exec "$@"
+docker_compose="docker-compose --file docker-compose.yml"
-# Otherwise, run docker compose to run our command in the container
+# Find our app dir
+appdir=$(cd $(dirname "$0")/.. && pwd)
+
+# Check if we've been told to run the command in Docker Composer.
cmd="$@"; [ "$#" -eq 0 ] && cmd=bash
-export VOLUME="$appdir:/app"
+export VOLUME="$appdir"
image=${DOCKER_IMAGE:=web}
port_publish=""; [ "${BIND_DOCKER_SERVICE_PORTS:-}" = 1 ] && port_publish="--service-ports"
-exec docker-compose run $port_publish --rm $image $cmd
+exec $docker_compose run $port_publish --rm $image $cmd
diff --git a/script/setup b/script/setup
new file mode 100755
index 00000000..c4bc29a9
--- /dev/null
+++ b/script/setup
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# script/setup: Set up application for the first time after cloning, or set it
+# back to the initial first unused state.
+
+set -e
+
+docker_compose="docker-compose --file docker-compose.yml"
+
+script/bootstrap
+
+script/poll-for-db
+
+echo "==> Setting up DB…"
+# reset database to a fresh state.
+bin/rake db:reset
+
+echo "==> App is now ready to go!"
diff --git a/script/test b/script/test
new file mode 100755
index 00000000..b7dc31af
--- /dev/null
+++ b/script/test
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+# script/test: Run test suite for application. Optionally pass in a path to an
+# individual test file to run a single test.
+
+set -e
+
+docker_compose="docker-compose --file docker-compose.yml"
+
+function cleanup() {
+ $docker_compose down
+}
+
+function die() {
+ echo $@
+ exit 1
+}
+
+trap cleanup EXIT
+
+script/poll-for-db
+
+if [ "$RAILS_ENV" = "test" ] || [ "$RACK_ENV" = "test" ]; then
+ # if executed and the environment is already set to `test`, then we want a
+ # clean from scratch application. This almost always means a ci environment,
+ # since we set the environment to `test` directly in `script/cibuild`.
+ script/setup
+else
+ # if the environment isn't set to `test`, set it to `test` and update the
+ # application to ensure all dependencies are met as well as any other things
+ # that need to be up to date, like db migrations. The environment not having
+ # already been set to `test` almost always means this is being called on its
+ # own from a `development` environment.
+ export RAILS_ENV="test" RACK_ENV="test"
+
+ script/update
+fi
+
+echo "==> Running tests…"
+
+if [ -n "$1" ]; then
+ # pass arguments to test call. This is useful for calling a single test.
+ $docker_compose run web bin/rake test "$1"
+else
+ $docker_compose run web bin/rake test
+fi
+
diff --git a/script/update b/script/update
new file mode 100755
index 00000000..56dc4429
--- /dev/null
+++ b/script/update
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+# script/update: Update application to run for its current checkout.
+
+set -e
+
+docker_compose="docker-compose --file docker-compose.yml"
+
+script/bootstrap
+
+echo "==> Updating db…"
+# run all database migrations to ensure everything is up to date.
+$docker_compose run web bin/rake db:migrate
diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb
index 04c6a99e..13ee8db6 100644
--- a/test/controllers/users_controller_test.rb
+++ b/test/controllers/users_controller_test.rb
@@ -18,7 +18,7 @@ class UsersControllerTest < ActionController::TestCase
get :index
assert_response :success
assert_equal "TRACKS::Manage Users", assigns['page_title']
- assert_equal 4, assigns['total_users']
+ assert_equal 5, assigns['total_users']
assert_equal users_url, session['return-to']
end
@@ -36,7 +36,7 @@ class UsersControllerTest < ActionController::TestCase
assert_equal assigns['users'],[User.where(:login => 'jane').first]
end
- def test_destroy_user
+ def test_destroy_user_as_admin
login_as :admin_user
@no_users_before = User.count
user_id = users(:ldap_user).id
@@ -44,8 +44,16 @@ class UsersControllerTest < ActionController::TestCase
assert_equal @no_users_before-1, User.count
end
+ def test_destroy_user_as_user
+ login_as :other_user
+ @no_users_before = User.count
+ user_id = users(:other_user).id
+ post :destroy, xhr: true, params: { :id => user_id.to_param }
+ assert_equal @no_users_before-1, User.count
+ end
+
def test_update_password_successful
- get :change_password, params: { :id => users(:admin_user).id }
+ get :change_password, params: { :id => users(:admin_user).id }
# should fail because no login
assert_redirected_to login_path
login_as :admin_user
diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml
index f43b9602..8df180d6 100644
--- a/test/fixtures/users.yml
+++ b/test/fixtures/users.yml
@@ -48,3 +48,15 @@ ldap_user:
first_name: International
last_name: Harvester
auth_type: CAS
+
+other_user_email:
+ id: 6
+ login: joe
+ crypted_password: <%= BCrypt::Password.create("open") %>
+ token: <%= Digest::SHA1.hexdigest("joeSun Feb 19 14:42:45 GMT 20060.408173979260027") %>
+ is_admin: false
+ first_name: Jane
+ last_name: Doe
+ email: joe@example.org
+ auth_type: database
+
diff --git a/test/integration/stories_test.rb b/test/integration/stories_test.rb
index 8e0f56a4..512a9b38 100644
--- a/test/integration/stories_test.rb
+++ b/test/integration/stories_test.rb
@@ -3,7 +3,7 @@ require 'support/stub_site_config_helper'
class StoriesTest < ActionDispatch::IntegrationTest
include StubSiteConfigHelper
-
+
# ####################################################
# Testing login and signup by different kinds of users
# ####################################################
@@ -11,10 +11,11 @@ class StoriesTest < ActionDispatch::IntegrationTest
admin = new_session_as(:admin_user,"abracadabra")
admin.goes_to_signup
admin.signs_up_with(:user => {:login => "newbie",
+ :email => "test.person@example.org",
:password => "newbiepass",
:password_confirmation => "newbiepass"})
end
-
+
def test_signup_new_user_by_nonadmin
stub_site_config do
SITE_CONFIG['open_signups'] = false
@@ -22,7 +23,7 @@ class StoriesTest < ActionDispatch::IntegrationTest
other_user.goes_to_signup_as_nonadmin
end
end
-
+
def test_open_signup_new_user
stub_site_config do
SITE_CONFIG['open_signups'] = true
@@ -30,6 +31,7 @@ class StoriesTest < ActionDispatch::IntegrationTest
assert_response :success
assert_template "users/new"
post "/users", params: { :user => {:login => "newbie",
+ :email => "test.person@example.org",
:password => "newbiepass",
:password_confirmation => "newbiepass"} }
assert_response :redirect
@@ -37,8 +39,8 @@ class StoriesTest < ActionDispatch::IntegrationTest
assert_response :success
assert_template "todos/index"
end
- end
-
+ end
+
private
module CustomAssertions
@@ -67,7 +69,7 @@ class StoriesTest < ActionDispatch::IntegrationTest
assert_response :success
assert_template "users/new"
end
-
+
def goes_to_signup_as_nonadmin
get "/signup"
assert_response :success
@@ -81,7 +83,6 @@ class StoriesTest < ActionDispatch::IntegrationTest
assert_response :success
assert_template "todos/index"
end
-
end
def new_session_as(user,plainpass)
@@ -92,5 +93,4 @@ class StoriesTest < ActionDispatch::IntegrationTest
yield sess if block_given?
end
end
-
end
diff --git a/test/integration/users_xml_api_test.rb b/test/integration/users_xml_api_test.rb
index cca726d9..f3d2125d 100644
--- a/test/integration/users_xml_api_test.rb
+++ b/test/integration/users_xml_api_test.rb
@@ -1,20 +1,22 @@
require 'test_helper'
class UsersXmlApiTest < ActionDispatch::IntegrationTest
-
+
@@foobar_postdata = "
foobar"
+ @@barfoo_postdata = "
barbarfoo@example.orgfoo"
@@johnny_postdata = "
johnnybarracuda"
-
+ @@barracuda_postdata = "
barracudabarracuda@example.orgjohnny"
+
def test_fails_with_401_if_not_authorized_user
authenticated_post_xml_to_user_create @@foobar_postdata, 'nobody', 'nohow'
assert_401_unauthorized_admin
end
-
+
def test_fails_with_401_if_not_admin_user
authenticated_post_xml_to_user_create @@foobar_postdata, users(:other_user).login, 'sesame'
assert_401_unauthorized_admin
end
-
+
def test_content_type_must_be_xml
authenticated_post_xml_to_user_create @@foobar_postdata, users(:admin_user).login, 'abracadabra', {'CONTENT_TYPE' => "application/x-www-form-urlencoded"}
assert_response 400, "Expected response 400"
@@ -25,12 +27,12 @@ class UsersXmlApiTest < ActionDispatch::IntegrationTest
# authenticated_post_xml_to_user_create "
"
# assert_equal 500, @integration_session.status
# end
-
+
def test_fails_with_invalid_xml_format2
authenticated_post_xml_to_user_create "foo"
assert_response_and_body 400, "Expected post format is valid xml like so: usernameabc123."
end
-
+
def test_xml_simple_param_parsing
authenticated_post_xml_to_user_create
assert @controller.params.has_key?(:user)
@@ -39,18 +41,18 @@ class UsersXmlApiTest < ActionDispatch::IntegrationTest
assert_equal 'foo', @controller.params['user'][:login]
assert_equal 'bar', @controller.params['user'][:password]
end
-
+
def test_fails_with_too_short_password
authenticated_post_xml_to_user_create
assert_responses_with_error "Password is too short (minimum is 5 characters"
end
-
+
def test_fails_with_nonunique_login
existing_login = users(:other_user).login
authenticated_post_xml_to_user_create "#{existing_login}barracuda"
assert_responses_with_error "Login has already been taken"
end
-
+
def test_creates_new_user
assert_difference 'User.count' do
authenticated_post_xml_to_user_create @@johnny_postdata
@@ -61,16 +63,27 @@ class UsersXmlApiTest < ActionDispatch::IntegrationTest
johnny2 = User.authenticate('johnny','barracuda')
assert_not_nil johnny2, "expected user johnny to be authenticated"
end
-
+
+ def test_creates_new_user
+ assert_difference 'User.count' do
+ authenticated_post_xml_to_user_create @@barracuda_postdata
+ assert_response_and_body 200, "User created."
+ end
+ barracuda1 = User.where(:login => 'barracuda').first
+ assert_not_nil barracuda1, "expected user barracuda to be created"
+ barracuda2 = User.authenticate('barracuda','johnny')
+ assert_not_nil barracuda2, "expected user barracuda to be authenticated"
+ end
+
def test_fails_with_get_verb
authenticated_get_xml "/users.xml", users(:admin_user).login, 'abracadabra', {}
end
-
+
def test_get_users_as_xml
get '/users.xml', params: {}, headers: basic_auth_headers()
assert_response :success
assert_select 'users' do
- assert_select 'user', count: 4
+ assert_select 'user', count: 5
end
assert_select 'password', false
end
@@ -81,7 +94,15 @@ class UsersXmlApiTest < ActionDispatch::IntegrationTest
assert_select 'user'
assert_select 'password', false
end
-
+
+ def test_get_email_user_as_xml
+ get "/users/#{users(:other_user_email).id}.xml", params: {}, headers: basic_auth_headers()
+ assert_response :success
+ assert_select 'user'
+ assert_select 'email'
+ assert_select 'password', false
+ end
+
private
def basic_auth_headers(username = users(:admin_user).login, password = 'abracadabra')
@@ -91,4 +112,8 @@ class UsersXmlApiTest < ActionDispatch::IntegrationTest
def authenticated_post_xml_to_user_create(postdata = @@foobar_postdata, user = users(:admin_user).login, password = 'abracadabra', headers = {})
authenticated_post_xml "/users.xml", user, password, postdata, headers
end
+
+ def authenticated_post_xml_to_user_create_with_email(postdata = @@barfoo_postdata, user = users(:admin_user).login, password = 'abracadabra', headers = {})
+ authenticated_post_xml "/users.xml", user, password, postdata, headers
+ end
end
diff --git a/test/models/user_test.rb b/test/models/user_test.rb
index 6e20d6a4..66d5c801 100644
--- a/test/models/user_test.rb
+++ b/test/models/user_test.rb
@@ -89,6 +89,19 @@ class UserTest < ActiveSupport::TestCase
end
end
+ def test_validate_correct_email
+ assert_difference 'User.count' do
+ create_user :email=> 'testi@example.org'
+ end
+ end
+
+ def test_validate_email_format
+ assert_no_difference 'User.count' do
+ u = create_user :email=> 'test'
+ assert_equal ["is not valid"], u.errors[:email]
+ end
+ end
+
def test_display_name_with_first_and_last_name_set
@other_user.first_name = "Jane"
@other_user.last_name = "Doe"
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 94bdf410..70c26275 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -140,7 +140,7 @@ class ActionDispatch::IntegrationTest
end
def assert_401_unauthorized_admin
- assert_response_and_body 401, "401 Unauthorized: Only admin users are allowed access to this function."
+ assert_response_and_body 401, "401 Unauthorized: Only administrative users are allowed access to this function."
end
def assert_responses_with_error(error_msg)