app/controllers/projects_controller.rb
class ProjectsController < ApplicationController
before_action :set_project, only: %i[show edit update destroy import import_upload
reports ownership archive unarchive
change_archived projects_unjoined]
before_action :prepare_session, only: %i[import import_upload]
before_action -> { define_sidebar :project_settings }, only: %i[import edit]
before_action :set_story_flow, only: %i[show]
before_action :fluid_layout, only: %i[show edit import]
# GET /projects
# GET /projects.xml
def index
@projects = {}
projects_joined = policy_scope(Project).preload(:tag_group)
@projects = {
joined: serialize_from_collection(projects_joined)
}
unless current_user.guest?
@projects[:unjoined] = serialize_from_collection(projects_unjoined.order(:updated_at))
end
@activities_group = Activity.grouped_activities(projects_joined, 1.week.ago)
end
# GET /projects/1
# GET /projects/1.xml
def show
@story = @project.stories.build
update_current_team
respond_to do |format|
format.html # show.html.erb
format.js { render json: @project }
format.xml { render xml: @project }
end
end
# GET /projects/new
# GET /projects/new.xml
def new
@project = policy_scope(Project).new
authorize @project
respond_to do |format|
format.html # new.html.erb
format.xml { render xml: @project }
end
end
# GET /projects/1/edit
def edit
@project.users.build
authorize @project
end
# POST /projects
# POST /projects.xml
def create
@project = policy_scope(Project).new(allowed_params)
authorize @project
@project.users << current_user
result = ProjectOperations::Create.call(project: @project, current_user: current_user,
current_team: current_team)
respond_to do |format|
match_result(result) do |on|
on.success do |project|
# TODO: move from here
format.html do
redirect_to(project, notice: t('projects.project was successfully created'))
end
format.xml { render xml: project, status: :created, location: project }
end
on.failure do |project|
format.html { render action: 'new' }
format.xml { render xml: project.errors, status: :unprocessable_entity }
end
end
end
end
# PUT /projects/1
# PUT /projects/1.xml
def update
result = ProjectOperations::Update.call(
project: @project,
project_attrs: allowed_params,
current_user: current_user
)
respond_to do |format|
match_result(result) do |on|
on.success do |project|
format.html do
redirect_to(project, notice: t('projects.project was successfully updated'))
end
format.xml { head :ok }
end
on.failure do |project|
format.html { render action: 'edit' }
format.xml { render xml: project.errors, status: :unprocessable_entity }
end
end
end
end
# DELETE /projects/1
# DELETE /projects/1.xml
def destroy
if valid_name_confirmation?
ProjectOperations::Destroy.call(project: @project, current_user: current_user)
redirect_to(projects_url)
else
redirect_to(edit_project_path, alert: t('projects.confirmation_invalid'))
end
end
def join
project = projects_unjoined.find_by!(slug: params[:id])
authorize project
project.users << current_user
respond_to do |format|
format.html do
redirect_to(
project,
notice: I18n.t('was added to this project', scope: 'users', email: current_user.email)
)
end
format.xml { render xml: project, status: :created, location: project }
end
end
# CSV import form
def import
@import_job = session[:import_job]
return if @import_job.blank?
job_result = Rails.cache.read(@import_job[:id])
if job_result
session[:import_job] = nil
if job_result[:errors]
flash[:alert] = "Unable to import CSV: #{job_result[:errors]}"
else
@valid_stories = @project.stories
@invalid_stories = job_result[:invalid_stories]
flash[:notice] = I18n.t(
'imported n stories', count: @valid_stories.count
)
unless @invalid_stories.empty?
flash[:alert] = I18n.t(
'n stories failed to import', count: @invalid_stories.count
)
end
end
else
minutes_ago = (Time.current - @import_job[:created_at].to_datetime) / 1.minute
session[:import_job] = nil if minutes_ago > 60
end
end
# CSV import
def import_upload
if params[:project].blank?
flash[:alert] = I18n.t('projects.uploads.select_file')
else
session[:import_job] = { id: ImportWorker.new_job_id, created_at: Time.current }
@project.update(allowed_params)
ImportWorker.perform_async(session[:import_job][:id], params[:id])
flash[:notice] = I18n.t('projects.uploads.being_processed')
end
redirect_to [:import, @project]
end
def reports
since = params[:since].nil? ? nil : params[:since].to_i.months.ago
@service = IterationService.new(@project, since: since)
end
def ownership
team = Team.not_archived.friendly.find(params.dig(:project, :slug))
manager = ProjectOwnership.new(@project, team, current_team, params.dig(:ownership_action))
if manager.perform
flash[:notice] = manager.performed_action_message
redirect_to(manager.transfer? ? root_path : edit_project_path(@project))
else
flash[:alert] = I18n.t('projects.invalid_action')
render 'edit'
end
end
def archived
@projects = policy_scope(Project).archived
authorize @projects
end
def archive
change_archived true
end
def unarchive
change_archived false
end
protected
def change_archived(archive = true)
@project = policy_scope(Project).archived.friendly.find(params[:id]) unless @project
authorize @project
result = ProjectOperations::Update.call(
project: @project,
project_attrs: { archived: archive ? Time.current : '0' },
current_user: current_user
)
respond_to do |format|
match_result(result) do |on|
on.success do |project|
format.html do
redirect_to(
project.archived ? projects_path : project,
notice: t("projects.project was successfully #{archive ? 'archived' : 'unarchived'}")
)
end
format.xml { head :ok }
end
on.failure do |project|
format.html { render action: 'edit' }
format.xml { render xml: project.errors, status: :unprocessable_entity }
end
end
end
end
def set_story_flow
@story_flow = {
current: cookies[:current_flow],
default: Fulcrum::Application.config.fulcrum.column_order
}
end
def allowed_params
params
.fetch(:project)
.permit(:name, :point_scale, :default_velocity, :tag_group_id, :start_date,
:iteration_start_day, :iteration_length, :import, :archived,
:disallow_join, :mail_reports, :velocity_strategy)
end
def fluid_layout
@layout_settings[:fluid] = true
end
def set_project
@project = current_user.projects.friendly.find(params[:id])
authorize @project
end
def prepare_session
session[:import_job] = (session[:import_job] || {}).with_indifferent_access
end
private
def valid_name_confirmation?
params[:name_confirmation] == @project.name
end
def projects_unjoined
current_team.projects.not_archived.joinable_except(policy_scope(Project))
end
def serialize_from_collection(collection)
ProjectSerializer.from_collection(ProjectPresenter.from_collection(collection))
end
end