Merge remote-tracking branch 'origin/master' into dependabot/bundler/jquery-ui-rails-6.0.1

This commit is contained in:
Jyri-Petteri Paloposki 2020-08-18 15:46:47 +03:00
commit 90653568ef
55 changed files with 594 additions and 227 deletions

3
.gitignore vendored
View file

@ -21,8 +21,9 @@
/tmp
config/deploy.rb
config/site.yml
config/database.yml
db/data.yml
nbproject
rerun.txt
tags
.skip-docker
.use-docker

View file

@ -17,9 +17,10 @@ COPY . /app/
COPY config/database.docker.yml /app/config/database.yml
COPY config/site.docker.yml /app/config/site.yml
COPY docker-startserver.sh /
RUN RAILS_ENV=production bundle exec rake assets:precompile
ENTRYPOINT ["/app/docker-entrypoint.sh"]
EXPOSE 3000
#CMD ["rails", "server", "-e", "production", "-b", "0.0.0.0"]
CMD ["./docker-startserver.sh"]
CMD ["rails", "server", "-b", "0.0.0.0"]

View file

@ -29,14 +29,14 @@ gem "RedCloth"
gem "sanitize", "~> 5.2"
gem "will_paginate"
gem "acts_as_list"
gem "aasm", '~> 3.4.0'
gem "aasm", '~> 4.12.3'
gem "htmlentities"
gem "rails_autolink"
gem 'puma', '~> 4.3'
gem 'paperclip'
# To use ActiveModel has_secure_password
gem 'bcrypt', '~> 3.1.13'
gem 'bcrypt', '~> 3.1.15'
gem 'chartjs-ror', :git => 'git://github.com/ZeiP/chartjs-ror.git'
@ -53,7 +53,7 @@ group :development do
gem "spring"
gem "yard"
gem 'tolk', '~> 3.2.1'
gem 'tolk', '~> 4.0.0'
gem "bullet"
gem "rack-mini-profiler"
@ -63,7 +63,7 @@ end
group :development, :test do
gem 'byebug'
gem 'listen'
gem "rubocop", "~> 0.88", require: false
gem "rubocop", "~> 0.89", require: false
end
group :test do

View file

@ -9,7 +9,8 @@ GEM
remote: https://rubygems.org/
specs:
RedCloth (4.3.2)
aasm (3.4.0)
aasm (4.12.3)
concurrent-ruby (~> 1.0)
actioncable (5.2.4.3)
actionpack (= 5.2.4.3)
nio4r (~> 2.0)
@ -65,7 +66,7 @@ GEM
autoprefixer-rails (9.4.7)
execjs
backport (1.1.2)
bcrypt (3.1.13)
bcrypt (3.1.15)
benchmark (0.1.0)
bootstrap-sass (3.4.1)
autoprefixer-rails (>= 5.2.1)
@ -105,7 +106,7 @@ GEM
globalid (0.4.2)
activesupport (>= 4.2.0)
htmlentities (4.3.4)
i18n (1.8.3)
i18n (1.8.4)
concurrent-ruby (~> 1.0)
jaro_winkler (1.5.4)
jquery-rails (4.4.0)
@ -154,7 +155,7 @@ GEM
puma (4.3.5)
nio4r (~> 2.0)
rack (2.2.3)
rack-mini-profiler (2.0.1)
rack-mini-profiler (2.0.4)
rack (>= 1.2.0)
rack-test (1.1.0)
rack (>= 1.0, < 3)
@ -190,7 +191,7 @@ GEM
thor (>= 0.19.0, < 2.0)
rainbow (3.0.0)
rake (13.0.1)
rb-fsevent (0.10.3)
rb-fsevent (0.10.4)
rb-inotify (0.10.1)
ffi (~> 1.0)
ref (2.0.0)
@ -202,17 +203,17 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-support (3.9.3)
rubocop (0.88.0)
rubocop (0.89.1)
parallel (~> 1.10)
parser (>= 2.7.1.1)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.7)
rexml
rubocop-ast (>= 0.1.0, < 1.0)
rubocop-ast (>= 0.3.0, < 1.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 2.0)
rubocop-ast (0.1.0)
parser (>= 2.7.0.1)
rubocop-ast (0.3.0)
parser (>= 2.7.1.4)
ruby-progressbar (1.10.1)
rubyzip (2.0.0)
safe_yaml (1.0.5)
@ -237,11 +238,11 @@ GEM
selenium-webdriver (3.142.7)
childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2)
simplecov (0.18.5)
simplecov (0.19.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov-html (0.12.2)
solargraph (0.39.11)
solargraph (0.39.14)
backport (~> 1.1)
benchmark
bundler (>= 1.17.2)
@ -272,17 +273,17 @@ GEM
thor (1.0.1)
thread_safe (0.3.6)
tilt (2.0.10)
tolk (3.2.1)
tolk (4.0.0)
rails (>= 5.0)
safe_yaml (>= 0.8.6)
sass
sassc
tzinfo (1.2.7)
thread_safe (~> 0.1)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
unicode-display_width (1.7.0)
uniform_notifier (1.13.0)
websocket-driver (0.7.2)
websocket-driver (0.7.3)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
will_paginate (3.3.0)
@ -293,11 +294,11 @@ PLATFORMS
DEPENDENCIES
RedCloth
aasm (~> 3.4.0)
aasm (~> 4.12.3)
actionpack-xml_parser (~> 2.0)
activemodel-serializers-xml (~> 1.0.1)
acts_as_list
bcrypt (~> 3.1.13)
bcrypt (~> 3.1.15)
bootstrap-sass (= 3.4.1)
bullet
byebug
@ -322,7 +323,7 @@ DEPENDENCIES
rails-dom-testing (~> 2.0.0)
rails_autolink
rspec-expectations
rubocop (~> 0.88)
rubocop (~> 0.89)
sanitize (~> 5.2)
sass-rails (~> 5.0)
selenium-webdriver (~> 3.142)
@ -331,7 +332,7 @@ DEPENDENCIES
spring
sqlite3
therubyracer
tolk (~> 3.2.1)
tolk (~> 4.0.0)
uglifier (>= 1.3.0)
will_paginate
yard

View file

@ -53,3 +53,7 @@ body {
color: $text-color;
background-color: $body-bg;
}
.ui-datepicker {
z-index: 999999 !important;
}

View file

@ -1349,10 +1349,6 @@ div.auto_complete {
}
}
.ui-datepicker {
z-index: 1000 !important;
}
.ui-autocomplete-loading {
background: white image-url('ui-anim_basic_16x16.gif') right center no-repeat;
}

View file

@ -11,6 +11,9 @@ body {
.login-wrapper {
@include make-row();
.footer {
z-index: 1;
}
}
.login-box {
@ -19,11 +22,21 @@ body {
@include make-sm-column-offset(3);
margin: 2em auto 1em;
background-color: rgba(0, 0, 0, 0.75);
color: #eaeaea;
padding: 0 0 1em 0;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5), 0 2px 3px rgba(0, 0, 0, 0.3);
color: #eaeaea;
a {
color: #eaeaea;
text-decoration: underline;
}
.signup-prompt {
text-align: right;
}
}
@media(min-width: $screen-sm-min) {
.login-box {
border-radius: 5px;
@ -34,7 +47,7 @@ body {
padding: 1em 0;
}
.login-form {
.login-form, .signup-form {
@include make-xs-column(12);
@include make-sm-column(8);
@include make-sm-column-offset(2)

View file

@ -154,6 +154,13 @@ class ApplicationController < ActionController::Base
end
end
def admin_or_self_login_required
unless User.find(session['user_id']).is_admin || session['user_id'] == params[:id].to_i
render :body => t('errors.user_unauthorized'), :status => 401
return false
end
end
def redirect_back_or_home
respond_to do |format|
format.html { redirect_back_or_default root_url }

View file

@ -12,6 +12,10 @@ class IntegrationsController < ApplicationController
@page_title = 'TRACKS::REST API Documentation'
end
def help
@page_title = 'TRACKS::Help'
end
def search_plugin
@icon_data = [File.open(File.join(Rails.root, 'app', 'assets', 'images', 'done.png')).read].
pack('m').gsub(/\n/, '')

View file

@ -14,6 +14,7 @@ class LoginController < ApplicationController
case request.method
when 'POST'
if @user = User.authenticate(params['user_login'], params['user_password'])
@user.update_attribute(:last_login_at, Time.now)
return handle_post_success
else
handle_post_failure

View file

@ -44,7 +44,7 @@ private
end
def user_params
params.require(:user).permit(:login, :first_name, :last_name, :password_confirmation, :password, :auth_type, :open_id_url)
params.require(:user).permit(:login, :first_name, :last_name, :email, :password_confirmation, :password, :auth_type, :open_id_url)
end
# Display notification if preferences are successful updated

View file

@ -1,6 +1,7 @@
class UsersController < ApplicationController
before_action :admin_login_required, :only => [ :index, :show, :destroy ]
before_action :admin_login_required, :only => [ :index, :show ]
before_action :admin_or_self_login_required, :only => [ :destroy ]
skip_before_action :login_required, :only => [ :new, :create ]
prepend_before_action :login_optional, :only => [ :new, :create ]
@ -76,10 +77,14 @@ class UsersController < ApplicationController
return
end
unless params['approve_tos'] == 'on' || SITE_CONFIG['tos_link'].blank?
render_failure "You have to accept the terms of service to sign up!"
return
end
user = User.new(user_params)
unless user.valid?
session['new_user'] = user
redirect_to signup_path
return
end
@ -99,13 +104,18 @@ class UsersController < ApplicationController
end
format.xml do
unless current_user && current_user.is_admin
render :body => "401 Unauthorized: Only admin users are allowed access to this function.", :status => 401
render :body => t('errors.user_unauthorized'), :status => 401
return
end
unless check_create_user_params
render_failure "Expected post format is valid xml like so: <user><login>username</login><password>abc123</password></user>.", 400
return
end
unless user_params['approve_tos'] == 'on' || SITE_CONFIG['tos_link'].blank?
render_failure "You have to accept the terms of service to sign up!"
return
end
user = User.new(user_params)
user.password_confirmation = user_params[:password]
saved = user.save
@ -122,8 +132,14 @@ class UsersController < ApplicationController
# DELETE /users/id DELETE /users/id.xml
def destroy
@deleted_user = User.find(params[:id])
# Remove the user
@saved = @deleted_user.destroy
@total_users = User.count
# Log out the user if they've deleted their own user and it succeeded.
if @saved && current_user == @deleted_user
logout_user
end
respond_to do |format|
format.html do
@ -132,10 +148,18 @@ class UsersController < ApplicationController
else
notify :error, t('users.failed_to_delete_user', :username => @deleted_user.login)
end
if current_user == @deleted_user
redirect_to login
else
redirect_to users_url
end
format.js
format.xml { head :ok }
end
format.js do
@total_users = User.count
end
format.xml do
head :ok
end
end
end
@ -178,7 +202,7 @@ class UsersController < ApplicationController
private
def user_params
params.require(:user).permit(:login, :first_name, :last_name, :password_confirmation, :password, :auth_type, :open_id_url)
params.require(:user).permit(:login, :first_name, :last_name, :email, :password_confirmation, :password, :auth_type, :open_id_url)
end
def get_new_user

View file

@ -18,4 +18,14 @@ module PreferencesHelper
pref(model, pref_name) { text_field(model, pref_name, class: "form-control") }
end
def profile_delete_user(user)
return link_to(
t('users.destroy_user'),
url_for({:controller => 'users', :action => 'destroy', :id => user.id}),
{:id => "delete_user_#{user.id}",
:class => "delete_user_button btn btn-danger",
:title => t('users.destroy_user'),
:x_confirm_message => t('users.destroy_confirmation', :login => user.login)
})
end
end

View file

@ -7,7 +7,7 @@ class User < ApplicationRecord
#for will_paginate plugin
cattr_accessor :per_page
@@per_page = 5
@@per_page = 10
has_many(:contexts, -> { order 'position ASC' }, dependent: :delete_all) do
def find_by_params(params)
@ -107,6 +107,7 @@ class User < ApplicationRecord
validates_length_of :login, within: 3..80
validates_uniqueness_of :login, on: :create
validate :validate_auth_type
validates :email, :allow_blank => true, format: { with: URI::MailTo::EMAIL_REGEXP }
before_create :crypt_password, :generate_token
before_update :crypt_password

View file

@ -0,0 +1,13 @@
<h1>Help</h1>
<p>You can find information on the usage in the <a href="https://github.com/TracksApp/tracks/wiki/User-manual">User manual</a> in the project GitHub wiki.</p>
<p>If you encounter a bug or have a feature request, please report it in the <a href="https://github.com/TracksApp/tracks/issues">issue queue</a>.</p>
<p>We gladly welcome all contributions to Tracks. Check the <a href="https://www.getontracks.org/contribute/">project website</a> for further information. You can also come discuss with the community:</p>
<ul>
<li><a href="http://groups.google.com/group/TracksApp">Mailing list</a></li>
<li><a href="https://gitter.im/TracksApp/tracks">Gitter chat</a></li>
<li><a href="https://webchat.freenode.net/#Tracks">IRC channel #Tracks@FreeNode</li>
</ul>

View file

@ -29,6 +29,11 @@
</div>
<%= submit_tag t("login.sign_in"), class: "btn btn-default" %>
<% end %>
<% if SITE_CONFIG['open_signups'] -%>
<div class="signup-prompt">
<%= link_to t('login.signup_prompt'), url_for(:action => 'new', :controller => 'users') %>
</div>
<% end -%>
</div>
</div>
</div>

View file

@ -4,6 +4,9 @@
<div class="form-group">
<%= pref_with_text_field 'user', 'last_name' %>
</div>
<div class="form-group">
<%= pref_with_text_field 'user', 'email' %>
</div>
<div class="form-group">
<%= pref_with_select_field('prefs', 'locale', I18n.available_locales.map {|l| l.to_s}) %>
</div>

View file

@ -0,0 +1,4 @@
<p><%= t 'preferences.remove_introduction' %></p>
<div class="form-group">
<%= profile_delete_user(@user) %>
</div>

View file

@ -21,16 +21,20 @@
<li role="presentation">
<%= link_to t('preferences.tabs.tracks_behavior'), "#behavior", data: { toggle: "tab" } %>
</li>
<li role="presentation">
<%= link_to t('preferences.tabs.remove_account'), "#remove_account", data: { toggle: "tab" } %>
</li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="profile"><%= render :partial => 'profile'%></div>
<div role="tabpanel" class="tab-pane" id="authentication"><%= render :partial => 'authentication'%></div>
<div role="tabpanel" class="tab-pane" id="date_and_time"><%= render :partial => 'date_and_time'%></div>
<div role="tabpanel" class="tab-pane" id="behavior"><%= render :partial => 'tracks_behavior'%></div>
<div role="tabpanel" class="tab-pane" id="remove_account"><%= render :partial => 'remove_account'%></div>
</div>
<div class="btn-group" role="group" aria-label="Basic example">
<button type="submit" id="prefs_submit" class="btn btn-default"><%= t('common.update') %></button>
</div>
<% end %>
</div>
</div>

View file

@ -51,6 +51,7 @@
<%= t('layouts.navigation.help') %> <span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><%= link_to t('layouts.navigation.help_page'), help_path %></li>
<li><%= link_to t('layouts.navigation.integrations_'), integrations_path %></li>
<li><%= link_to t('layouts.navigation.api_docs'), rest_api_docs_path %></li>
</ul>

View file

@ -6,24 +6,30 @@
<tr>
<th><%= User.human_attribute_name('login') %></th>
<th><%= User.human_attribute_name('display_name') %></th>
<th><%= User.human_attribute_name('email') %></th>
<th><%= User.human_attribute_name('auth_type') %></th>
<th><%= User.human_attribute_name('open_id_url') %></th>
<th><%= t('users.total_actions') %></th>
<th><%= t('users.total_contexts') %></th>
<th><%= t('users.total_projects') %></th>
<th><%= t('users.total_notes') %></th>
<th><%= User.human_attribute_name('created_at') %></th>
<th><%= User.human_attribute_name('last_login_at') %></th>
<th>&nbsp;</th>
</tr>
<% for user in @users %>
<tr <%= "class=\"highlight\"" if user.is_admin? %> id="user-<%= user.id %>">
<td><%=h user.login %></td>
<td><%=h user.display_name %></td>
<td><%=h user.email %></td>
<td><%= h user.auth_type %></td>
<td><%= h user.open_id_url || '-' %></td>
<td><%= h user.todos.size %></td>
<td><%= h user.contexts.size %></td>
<td><%= h user.projects.size %></td>
<td><%= h user.notes.size %></td>
<td><%= format_date(user.created_at) %></td>
<td><%= format_date(user.last_login_at) %></td>
<td><%= !user.is_admin? ? remote_delete_user(user) : "&nbsp;".html_safe %></td>
</tr>
<% end %>

View file

@ -1,5 +1,16 @@
<div title="<%= t('users.account_signup') %>" id="signupform" class="form">
<%= form_tag :action=> "create" do %>
<div class="container-fluid">
<div class="row">
<div class="login-flash">
<%= bootstrap_flash :close_button => false %>
</div>
</div>
<div class="login-wrapper">
<div class="login-box">
<div class="icon-box">
<div class="tracks-icon"><div class="tracks-icon-text">10</div>
</div>
<div title="<%= t('users.account_signup') %>" id="signupform" class="signup-form" class="form">
<%= form_tag :action=> "create" do %>
<%= get_list_of_error_messages_for @user %><br/>
@ -7,25 +18,38 @@
<h3><%= @heading -%></h3>
<table>
<tr>
<td><label for="user_login"><%= t('users.desired_login') %>:</label></td>
<td> <%= text_field "user", "login", :size => 20 %></td>
</tr>
<tr>
<td><label for="user_password"><%= t('users.choose_password') %>:</label></td>
<td><%= password_field "user", "password", :size => 20 %></td>
</tr>
<tr>
<td><label for="user_password_confirmation"><%= t('users.confirm_password') %>:</label></td>
<td><%= password_field "user", "password_confirmation", :size => 20 %></td>
</tr>
<tr>
<td></td>
<td><input type="submit" id="signup" value="<%= t('users.signup') %> &#187;" class="primary" /></td>
</tr>
</table>
<%end-%>
<div class="label-element-combo">
<%= label_tag "user_login", t('users.desired_login') %>
<%= text_field "user", "login", class: "form-control" %>
</div>
<div class="label-element-combo">
<%= label_tag "user_email", t('users.email_address') %>
<%= text_field "user", "email", class: "form-control" %>
</div>
<div class="label-element-combo">
<%= label_tag "user_password", t('users.choose_password') %>
<%= password_field "user", "password", class: "form-control" %>
</div>
<div class="label-element-combo">
<%= label_tag "user_password_confirmation", t('users.confirm_password') %>
<%= password_field "user", "password_confirmation", class: "form-control" %>
</div>
<% unless SITE_CONFIG['tos_link'].blank? -%>
<div class="label-element-combo">
<div class="checkbox">
<input name="approve_tos" id="approve_tos" type="checkbox" />
<label for="approve_tos"><%= t("users.approve_tos") %></label>
(<a href="<%= SITE_CONFIG['tos_link'] %>"><%= t('users.tos_link') %></a>)
</div>
</div>
<% end -%>
<%= submit_tag t("users.signup"), class: "btn btn-default" %>
<% end %>
</div>
</div>
</div>
<div class="footer">
<%= render :partial => "shared/footer" %>
</div>
</div>

View file

@ -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__)

View file

@ -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

View file

@ -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'

View file

@ -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

View file

@ -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') %>

View file

@ -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:

View file

@ -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."

View file

@ -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'

View file

@ -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.

View file

@ -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

View file

@ -0,0 +1,5 @@
class AddEmailToUser < ActiveRecord::Migration[5.2]
def change
add_column :users, :email, :string
end
end

View file

@ -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

View file

@ -0,0 +1,5 @@
class AddLastloginToUser < ActiveRecord::Migration[5.2]
def change
add_column :users, :last_login_at, :datetime
end
end

View file

@ -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

View file

@ -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, youll 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!

View file

@ -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

View file

@ -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:

20
docker-entrypoint.sh Executable file
View file

@ -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 "$@"

View file

@ -1,4 +0,0 @@
#!/bin/sh
rails db:migrate
rails server -b 0.0.0.0

View file

@ -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

14
script/bootstrap Executable file
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

18
script/setup Executable file
View file

@ -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!"

47
script/test Executable file
View file

@ -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

13
script/update Executable file
View file

@ -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

View file

@ -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,6 +44,14 @@ 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 }
# should fail because no login

View file

@ -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

View file

@ -11,6 +11,7 @@ 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
@ -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
@ -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

View file

@ -3,7 +3,9 @@ require 'test_helper'
class UsersXmlApiTest < ActionDispatch::IntegrationTest
@@foobar_postdata = "<user><login>foo</login><password>bar</password></user>"
@@barfoo_postdata = "<user><login>bar</login><email>barfoo@example.org</email><password>foo</password></user>"
@@johnny_postdata = "<user><login>johnny</login><password>barracuda</password></user>"
@@barracuda_postdata = "<user><login>barracuda</login><email>barracuda@example.org</email><password>johnny</password></user>"
def test_fails_with_401_if_not_authorized_user
authenticated_post_xml_to_user_create @@foobar_postdata, 'nobody', 'nohow'
@ -62,6 +64,17 @@ class UsersXmlApiTest < ActionDispatch::IntegrationTest
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
@ -70,7 +83,7 @@ class UsersXmlApiTest < ActionDispatch::IntegrationTest
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
@ -82,6 +95,14 @@ class UsersXmlApiTest < ActionDispatch::IntegrationTest
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

View file

@ -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"

View file

@ -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)