diff --git a/Gemfile b/Gemfile index f1f6f11..4239218 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source 'https://rubygems.org' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' -gem 'rails', '4.0.4' +gem 'rails', '~> 4.1.0' # Use SCSS for stylesheets gem 'sass-rails', '~> 4.0.2' @@ -53,6 +53,9 @@ gem 'rolify' # for heroku gem 'rails_12factor', group: :production +# dates validation +gem 'validates_timeliness', '~> 3.0.14' + group :development, :test do gem 'pry-rails' @@ -69,6 +72,7 @@ group :test do gem 'rspec-rails' gem 'spork-rails' gem 'database_cleaner' + gem 'minitest' gem 'shoulda-matchers' gem 'webmock' gem 'psych' diff --git a/Gemfile.lock b/Gemfile.lock index 3f64773..583a438 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,36 +1,38 @@ GEM remote: https://rubygems.org/ specs: - actionmailer (4.0.4) - actionpack (= 4.0.4) + actionmailer (4.1.0) + actionpack (= 4.1.0) + actionview (= 4.1.0) mail (~> 2.5.4) - actionpack (4.0.4) - activesupport (= 4.0.4) - builder (~> 3.1.0) - erubis (~> 2.7.0) + actionpack (4.1.0) + actionview (= 4.1.0) + activesupport (= 4.1.0) rack (~> 1.5.2) rack-test (~> 0.6.2) - activemodel (4.0.4) - activesupport (= 4.0.4) - builder (~> 3.1.0) - activerecord (4.0.4) - activemodel (= 4.0.4) - activerecord-deprecated_finders (~> 1.0.2) - activesupport (= 4.0.4) - arel (~> 4.0.0) - activerecord-deprecated_finders (1.0.3) - activesupport (4.0.4) + actionview (4.1.0) + activesupport (= 4.1.0) + builder (~> 3.1) + erubis (~> 2.7.0) + activemodel (4.1.0) + activesupport (= 4.1.0) + builder (~> 3.1) + activerecord (4.1.0) + activemodel (= 4.1.0) + activesupport (= 4.1.0) + arel (~> 5.0.0) + activesupport (4.1.0) i18n (~> 0.6, >= 0.6.9) - minitest (~> 4.2) - multi_json (~> 1.3) + json (~> 1.7, >= 1.7.7) + minitest (~> 5.1) thread_safe (~> 0.1) - tzinfo (~> 0.3.37) + tzinfo (~> 1.1) addressable (2.3.6) - arel (4.0.2) + arel (5.0.0) bcrypt (3.1.7) bootstrap-sass (3.1.1.0) sass (~> 3.2) - builder (3.1.4) + builder (3.2.2) cancancan (1.7.1) capybara (2.2.1) mime-types (>= 1.16) @@ -84,7 +86,7 @@ GEM method_source (0.8.2) mime-types (1.25.1) mini_portile (0.5.3) - minitest (4.7.5) + minitest (5.3.2) multi_json (1.9.2) mysql2 (0.3.15) nokogiri (1.6.1) @@ -107,22 +109,24 @@ GEM rack (1.5.2) rack-test (0.6.2) rack (>= 1.0) - rails (4.0.4) - actionmailer (= 4.0.4) - actionpack (= 4.0.4) - activerecord (= 4.0.4) - activesupport (= 4.0.4) + rails (4.1.0) + actionmailer (= 4.1.0) + actionpack (= 4.1.0) + actionview (= 4.1.0) + activemodel (= 4.1.0) + activerecord (= 4.1.0) + activesupport (= 4.1.0) bundler (>= 1.3.0, < 2.0) - railties (= 4.0.4) - sprockets-rails (~> 2.0.0) + railties (= 4.1.0) + sprockets-rails (~> 2.0) rails_12factor (0.0.2) rails_serve_static_assets rails_stdout_logging rails_serve_static_assets (0.0.2) rails_stdout_logging (0.0.3) - railties (4.0.4) - actionpack (= 4.0.4) - activesupport (= 4.0.4) + railties (4.1.0) + actionpack (= 4.1.0) + activesupport (= 4.1.0) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rake (10.2.2) @@ -152,7 +156,7 @@ GEM sdoc (0.4.0) json (~> 1.8) rdoc (~> 4.0, < 5.0) - shoulda-matchers (2.5.0) + shoulda-matchers (2.6.0) activesupport (>= 3.0.0) simplecov (0.8.2) docile (~> 1.1.0) @@ -169,7 +173,7 @@ GEM multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - sprockets-rails (2.0.1) + sprockets-rails (2.1.3) actionpack (>= 3.0) activesupport (>= 3.0) sprockets (~> 2.8) @@ -178,17 +182,21 @@ GEM libv8 (~> 3.16.14.0) ref thor (0.19.1) - thread_safe (0.3.2) + thread_safe (0.3.3) tilt (1.4.1) + timeliness (0.3.7) treetop (1.4.15) polyglot polyglot (>= 0.3.1) - turbolinks (2.2.1) + turbolinks (2.2.2) coffee-rails - tzinfo (0.3.39) + tzinfo (1.1.0) + thread_safe (~> 0.1) uglifier (2.5.0) execjs (>= 0.3.0) json (>= 1.8.0) + validates_timeliness (3.0.14) + timeliness (~> 0.3.6) warden (1.2.3) rack (>= 1.0) webmock (1.17.4) @@ -213,6 +221,7 @@ DEPENDENCIES faker jbuilder (~> 1.2) jquery-rails + minitest mysql2 pg pry @@ -220,7 +229,7 @@ DEPENDENCIES pry-rails pry-remote psych - rails (= 4.0.4) + rails (~> 4.1.0) rails_12factor rolify rspec-rails @@ -232,4 +241,5 @@ DEPENDENCIES therubyracer turbolinks uglifier (>= 1.3.0) + validates_timeliness (~> 3.0.14) webmock diff --git a/README.md b/README.md index a552c92..e3a7295 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ -# Taskoholic [![Build Status](https://travis-ci.org/RailsMafia/taskoholic.svg?branch=master)](https://travis-ci.org/RailsMafia/taskoholic) [![Code Climate](https://codeclimate.com/github/RailsMafia/taskoholic.png)](https://codeclimate.com/github/RailsMafia/taskoholic) +# Taskoholic +[![Build Status](https://travis-ci.org/RailsMafia/taskoholic.svg?branch=master)](https://travis-ci.org/RailsMafia/taskoholic) +[![Code Climate](https://codeclimate.com/github/RailsMafia/taskoholic.png)](https://codeclimate.com/github/RailsMafia/taskoholic) +[![Code Climate](https://codeclimate.com/github/RailsMafia/taskoholic/coverage.png)](https://codeclimate.com/github/RailsMafia/taskoholic) -The best ever system for managing tasks, projects, branches and whatever you could imagine +The best ever system for managing tasks, projects, branches and whatever you could imagine -## [Demo](http://taskoholic.herokuapp.com/) +## [Demo](http://taskoholic.herokuapp.com/) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 6b90bfa..5577d4e 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -3,7 +3,18 @@ class ApplicationController < ActionController::Base # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception + before_filter :configure_permitted_parameters, if: :devise_controller? + rescue_from CanCan::AccessDenied do |exception| render file: "#{Rails.root}/public/404", formats: [:html], status: 404, layout: false end + + protected + + def configure_permitted_parameters + devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :email, :password, :password_confirmation, :remember_me) } + devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:name, :email, :password, :remember_me) } + devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:name, :email, :password, :password_confirmation, :current_password) } + end + end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index eb0a69a..f2a31e7 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -25,7 +25,7 @@ def edit def create @project = Project.new(project_params) @project.owner = current_user - @project.users << current_user + @project.assign_user(current_user) if @project.save current_user.add_role :owner, @project redirect_to projects_url, notice: 'Project was successfully created.' @@ -49,6 +49,12 @@ def destroy redirect_to projects_url end + def assign + user = User.find(params[:user]['user_id']) + @project.assign_user(user) + redirect_to @project + end + private # Never trust parameters from the scary internet, only allow the white list through. def project_params diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb index 8d40bcf..32ec3d8 100644 --- a/app/controllers/tasks_controller.rb +++ b/app/controllers/tasks_controller.rb @@ -1,7 +1,8 @@ class TasksController < ApplicationController before_action :authenticate_user! before_action :load_project - load_and_authorize_resource param_method: :task_params + load_and_authorize_resource :project + load_and_authorize_resource :task, :through => :project def index @tasks = Task.where(project_id: params[:project_id]) diff --git a/app/models/ability.rb b/app/models/ability.rb index ded1b97..3d7750a 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -9,7 +9,7 @@ def initialize(user) can :read, Project do |project| project.users.find user.id rescue false end - can [:read, :update, :destroy], Project, owner: user + can [:read, :update, :destroy, :assign], Project, owner: user can :create, Task can [:read, :update, :destroy], Task do |task| task.project.owner = user diff --git a/app/models/project.rb b/app/models/project.rb index cc874cf..226739d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -12,5 +12,8 @@ class Project < ActiveRecord::Base validates :name, presence: true, length: {minimum: 2, maximum: 50} validates :owner, presence: true validates :description, length: {minimum: 10, maximum: 1000} - + + def assign_user(user) + self.users << user + end end diff --git a/app/models/task.rb b/app/models/task.rb index 7fe711d..7866518 100644 --- a/app/models/task.rb +++ b/app/models/task.rb @@ -8,7 +8,9 @@ class Task < ActiveRecord::Base validates :name, presence: true, length: {minimum: 2, maximum: 50} validates :description, length: {maximum: 1000} + validates_datetime :due_date, allow_nil: true + def late? - !self.due_date.nil? && self.due_date < Time.now + self.due_date.present? && self.due_date < Time.now end end diff --git a/app/models/user.rb b/app/models/user.rb index 066d467..fddc93c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -9,5 +9,8 @@ class User < ActiveRecord::Base has_many :own_projects, class_name: "Project", foreign_key: 'owner_id' has_many :tasks validates :email, uniqueness: true - + def self.has_no_project(project_id) + join = "LEFT OUTER JOIN projects_users AS pu ON pu.user_id = users.id AND pu.project_id = " + project_id.to_s + self.joins(join).where('pu.project_id' => nil) + end end diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb index 8730968..27ea4af 100644 --- a/app/views/devise/registrations/edit.html.erb +++ b/app/views/devise/registrations/edit.html.erb @@ -1,11 +1,20 @@
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, class: 'form-user', role: 'form' }) do |f| %> <%= devise_error_messages! %> + +

Account information

- <%= f.label :email %> - <%= f.email_field :email, class: 'form-control', autofocus: true %> + <%= f.label :name %> + <%= f.text_field :name, class: 'form-control', autofocus: true %> +
+ +
+ <%= f.label :email %> (will be used for your Gravatar.com picture also) + <%= f.email_field :email, class: 'form-control' %>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb index ab774d4..acdb3a1 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/devise/registrations/new.html.erb @@ -3,7 +3,9 @@ <%= devise_error_messages! %>

Sign up

- <%= f.email_field :email, class: 'form-control', placeholder: 'Your Email', autofocus: true %> + <%= f.text_field :name, class: 'form-control', placeholder: 'Your Name', autofocus: true %> + + <%= f.email_field :email, class: 'form-control', placeholder: 'Your Email' %> <%= f.password_field :password, class: 'form-control no-radius', placeholder: 'Password', autocomplete: "off" %> diff --git a/app/views/projects/_participants.erb b/app/views/projects/_participants.erb index ef9aafc..d290d5a 100644 --- a/app/views/projects/_participants.erb +++ b/app/views/projects/_participants.erb @@ -16,4 +16,11 @@ <% end %> - \ No newline at end of file + +

Add new participant

+<%= form_tag(assign_project_path, method: "patch", role: 'form', class: 'form-inline text-center') do %> + + <%= collection_select(:user, :user_id, User.has_no_project(@project.id), :id, :email, {}, class: 'form-control') %> + + <%= submit_tag("Assign", class: 'btn btn-primary') %> +<% end %> \ No newline at end of file diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb index 35ce60a..575bd43 100644 --- a/app/views/projects/show.html.erb +++ b/app/views/projects/show.html.erb @@ -14,11 +14,11 @@ <%= @project.description %>
-
+
<%= render partial: "participants", locals: {users: @users} if @users.any? %>
-
+

<%= link_to 'Tasks', project_tasks_path(@project) %>

<% if @project.tasks.empty? %>

Seems you don't have any. It's time to <%= link_to "create new", new_project_task_path(@project) %>

diff --git a/app/views/tasks/_form.html.erb b/app/views/tasks/_form.html.erb index ac43b2a..1afcadf 100644 --- a/app/views/tasks/_form.html.erb +++ b/app/views/tasks/_form.html.erb @@ -1,4 +1,15 @@ -<%= form_for(@task, html: {method: 'POST'}, url: project_tasks_path) do |f| %> +<%= form_for(@task, html: {method: 'POST', class: 'clearfix'}, url: project_tasks_path) do |f| %> + <% if @task.errors.any? %> +
+

<%= pluralize(@task.errors.count, "error") %> prohibited this task from being saved:

+
    + <% @task.errors.full_messages.each do |msg| %> +
  • <%= msg %>
  • + <% end %> +
+
+ <% end %> +
<%= f.label :name %> <%= f.text_field :name, class: 'form-control' %> @@ -20,7 +31,7 @@
- <%= f.submit class: "btn btn-primary" %> + <%= f.submit "Save", class: "btn btn-primary" %>
<% end %> diff --git a/app/views/tasks/show.html.erb b/app/views/tasks/show.html.erb index 7b1d3c7..b05584a 100644 --- a/app/views/tasks/show.html.erb +++ b/app/views/tasks/show.html.erb @@ -8,7 +8,7 @@

<%= @project.owner.email %>


Assigned to:

-

<%= @task.user.nil? ? 'nobody' : @taks.user.email %>

+

<%= @task.user.nil? ? 'nobody' : @task.user.email %>


<% unless @task.due_date.nil? %>

Due date:

diff --git a/config/locales/en.yml b/config/locales/en.yml index 0653957..53965b4 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -21,3 +21,10 @@ en: hello: "Hello world" + activerecord: + errors: + models: + task: + attributes: + due_date: + invalid_datetime: "is not valid date" \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index a800203..736c246 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -17,6 +17,9 @@ resources :projects do resources :tasks + member do + patch 'assign' + end end # The priority is based upon order of creation: first created -> highest priority.