redmine-org-chart/app/controllers/departments_controller.rb
ioresponse b50ab25083 Initial commit: Redmine Organization Chart Plugin
🤖 Generated with Claude Code
2025-12-23 00:21:49 +09:00

270 lines
7.2 KiB
Ruby

class DepartmentsController < ApplicationController
layout 'admin'
before_action :require_admin
before_action :find_department, only: [:show, :edit, :update, :destroy, :add_member, :remove_member, :set_leader, :set_acting_leader, :clear_leader]
def index
@departments = Department.roots.sorted.includes(:children, :leader, :acting_leader, :users)
end
def show
@members = @department.department_members.includes(:user).order(:position)
end
def new
@department = Department.new
@department.parent_id = params[:parent_id] if params[:parent_id]
@department.department_type = params[:type] || 'team'
load_parent_options
end
def create
@department = Department.new(department_params)
if @department.save
flash[:notice] = l(:notice_successful_create)
redirect_to '/org_chart'
else
load_parent_options
render :new
end
end
def edit
load_parent_options
end
def update
if @department.update(department_params)
flash[:notice] = l(:notice_successful_update)
redirect_to '/org_chart'
else
load_parent_options
render :edit
end
end
def destroy
@department.destroy
flash[:notice] = l(:notice_successful_delete)
redirect_to '/org_chart'
end
def add_member
user = find_or_create_user_from_ldap(params[:user_id], params[:ldap_uid], params[:ldap_id])
if user
member = @department.department_members.find_or_initialize_by(user: user)
member.role = 'member'
if member.save
flash[:notice] = "#{user.name} added to #{@department.name}"
else
flash[:error] = member.errors.full_messages.join(', ')
end
else
flash[:error] = 'User not found'
end
redirect_to department_path(@department)
end
def remove_member
member = @department.department_members.find_by(user_id: params[:user_id])
if member
@department.update(leader_id: nil) if @department.leader_id == member.user_id
@department.update(acting_leader_id: nil) if @department.acting_leader_id == member.user_id
member.destroy
flash[:notice] = 'Member removed'
end
redirect_to department_path(@department)
end
def set_leader
user = User.find(params[:user_id])
@department.department_members.find_or_create_by(user: user)
@department.update(leader_id: user.id, acting_leader_id: nil)
flash[:notice] = "#{user.name} is now the leader"
redirect_to department_path(@department)
end
def set_acting_leader
user = User.find(params[:user_id])
@department.department_members.find_or_create_by(user: user)
@department.update(acting_leader_id: user.id)
flash[:notice] = "#{user.name} is now the acting leader"
redirect_to department_path(@department)
end
def clear_leader
@department.update(leader_id: nil, acting_leader_id: nil)
flash[:notice] = 'Leader cleared'
redirect_to department_path(@department)
end
def org_chart
@ceo = Department.ceo.first
@departments = Department.roots.sorted.includes(:children, :leader, :acting_leader, :users)
render layout: 'base'
end
def search_ldap
query = params[:q].to_s.strip
if query.length < 2
render json: []
return
end
results = []
AuthSourceLdap.all.each do |ldap|
begin
ldap_users = search_ldap_users(ldap, query)
ldap_users.each do |entry|
uid = entry[ldap.attr_login]&.first
next unless uid
existing_user = User.find_by(login: uid)
results << {
uid: uid,
name: "#{entry[ldap.attr_firstname]&.first} #{entry[ldap.attr_lastname]&.first}".strip,
email: entry[ldap.attr_mail]&.first,
ldap_id: ldap.id,
exists: existing_user.present?,
user_id: existing_user&.id
}
end
rescue => e
Rails.logger.error "LDAP search error: #{e.message}"
end
end
render json: results.uniq { |r| r[:uid] }.first(20)
end
private
def find_department
@department = Department.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
def department_params
params.require(:department).permit(:name, :parent_id, :position, :description, :department_type, :leader_id, :acting_leader_id)
end
def load_parent_options
if @department.new_record?
@parents = @department.available_parents.sorted
else
@parents = @department.available_parents.where.not(id: @department.self_and_descendants.map(&:id)).sorted
end
end
def search_ldap_users(ldap, query)
options = {
host: ldap.host,
port: ldap.port,
auth: {
method: :simple,
username: ldap.account,
password: ldap.account_password
}
}
if ldap.tls
if ldap.verify_peer
options[:encryption] = { method: :simple_tls }
else
options[:encryption] = { method: :simple_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE } }
end
end
conn = Net::LDAP.new(options)
filter = Net::LDAP::Filter.begins(ldap.attr_login, query) |
Net::LDAP::Filter.begins(ldap.attr_firstname, query) |
Net::LDAP::Filter.begins(ldap.attr_lastname, query)
if ldap.filter.present?
base_filter = Net::LDAP::Filter.construct(ldap.filter)
filter = base_filter & filter
end
conn.search(
base: ldap.base_dn,
filter: filter,
attributes: [ldap.attr_login, ldap.attr_firstname, ldap.attr_lastname, ldap.attr_mail],
size: 20
) || []
end
def find_or_create_user_from_ldap(user_id, ldap_uid, ldap_id)
return User.find(user_id) if user_id.present?
return nil if ldap_uid.blank?
user = User.find_by(login: ldap_uid)
return user if user
ldap = AuthSourceLdap.find_by(id: ldap_id)
return nil unless ldap
user_info = get_ldap_user_info(ldap, ldap_uid)
return nil unless user_info
user = User.new(
login: ldap_uid,
firstname: user_info[:firstname] || ldap_uid,
lastname: user_info[:lastname] || '-',
mail: user_info[:mail] || "#{ldap_uid}@example.com",
auth_source_id: ldap.id,
status: User::STATUS_ACTIVE
)
user.random_password
user.save ? user : nil
end
def get_ldap_user_info(ldap, uid)
options = {
host: ldap.host,
port: ldap.port,
auth: {
method: :simple,
username: ldap.account,
password: ldap.account_password
}
}
if ldap.tls
if ldap.verify_peer
options[:encryption] = { method: :simple_tls }
else
options[:encryption] = { method: :simple_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE } }
end
end
conn = Net::LDAP.new(options)
filter = Net::LDAP::Filter.eq(ldap.attr_login, uid)
if ldap.filter.present?
base_filter = Net::LDAP::Filter.construct(ldap.filter)
filter = base_filter & filter
end
result = conn.search(
base: ldap.base_dn,
filter: filter,
attributes: [ldap.attr_login, ldap.attr_firstname, ldap.attr_lastname, ldap.attr_mail],
size: 1
)&.first
return nil unless result
{
firstname: result[ldap.attr_firstname]&.first,
lastname: result[ldap.attr_lastname]&.first,
mail: result[ldap.attr_mail]&.first
}
end
end