アップロードの管理機能を追加 その1

前回考えたアップロード管理の仕組みに合わせて、コードを追記してみた。

マイグレーションでデータベースの修正

  • csvsテーブルに、以下のフィールドを追加した。
management_section_id アップロードしたcsvファイルの管理部門
user_id アップロードしたcsvファイルの担当者
editable アップロードしたcsvファイルの内容を編集可能かどうか
# ---------- db/migrate/004_add_column_managed_section_uploader_readonly.rb ----------

class AddColumnManagedSectionUploaderReadonly < ActiveRecord::Migration
  def self.up
    add_column :csvs, :management_section_id, :integer
    add_column :csvs, :user_id,            :integer
    add_column :csvs, :editable,           :boolean    
  end

  def self.down
    remove_column :csvs, :management_section_id
    remove_column :csvs, :user_id
    remove_column :csvs, :editable
  end
end
  • management_sectionsテーブルと、usersとの多:多関連のため中間テーブルを追加した。
# ---------- db/migrate/005_create_management_sections.rb ----------

class CreateManagementSections < ActiveRecord::Migration
  def self.up
    create_table :management_sections do |t|
      t.column :name, :string
    end
    
    # id列が不要な場合は、:id => false とオプション指定する。
    create_table :management_sections_users, :id => false do |t|
      t.column :management_section_id, :integer
      t.column :user_id,               :integer
    end
  end

  def self.down
    drop_table :management_sections
    drop_table :management_sections_users
  end
end

モデル間の関連を設定

  • app/models/フォルダのファイル
class Csv < ActiveRecord::Base
  belongs_to :user
  belongs_to :management_section

class ManagementSection < ActiveRecord::Base
  has_and_belongs_to_many :users
  has_many :csvs

class Path < ActiveRecord::Base
  has_and_belongs_to_many :users

class User < ActiveRecord::Base
  has_and_belongs_to_many :paths
  has_and_belongs_to_many :management_sections
  has_many :csvs

関連を図にしてみた。

      • 多:多関連の中間テーブルは省略した。
      • login_engine、user_engineの属性も省略した。

new、editアクションに関係する変更

コントローラー
  • @sections = session[:user].management_sectionsで、ユーザーが所有するmanagement_sectionの配列が代入される。collection_selectでプルダウンリストを表示する時に利用する。
# ---------- app/controllers/csvs_controller.rb ----------

...(途中省略)...
  def new
    # ログインユーザーをアップロード担当者にする。new(:user_id => session[:user].id)
    @csv = Csv.new(:user_id => session[:user].id)
    @sections = session[:user].management_sections
  end

  def create
    @csv = Csv.new(params[:csv])
    @sections = session[:user].management_sections
    if @csv.save
...(途中省略)...
  end

  def edit
    @csv = Csv.find(params[:id])
    @sections = session[:user].management_sections
  end

  def update
    @csv = Csv.find(params[:id])
    @sections = session[:user].management_sections
    if @csv.update_attributes(params[:csv])
...(途中省略)...
  end
ビュー
  • CSVファイルをアップロードする時(new、createアクションの時)は、ファイル選択フィールドを表示する。
  • CSVファイルの属性を編集する時(edit、updateアクションの時)は、テキストフィールドでファイル名だけ表示する。
<%# ---------- app/views/csvs/_form.rhtml ---------- %>

<%= error_messages_for 'csv' %>

<table>
<!--[form:csv]-->
<tr>
<% if ['new', 'create'].include?(controller.action_name) %>
  <th align="right"><label for="csv_tempfile">ファイル:</label></th>
  <td><%= file_field 'csv', 'tempfile' %></td>
<% else %>
  <th align="right"><label for="csv_file_name"><b>ファイル:</label></th>
  <td><%= text_field 'csv', 'file_name', :disabled => true %></td>
<% end %>
</tr>

<tr>
  <th></th>
  <td><%= check_box 'csv', 'editable'  %>編集可能</td>
</tr>

<tr>
  <th align="right"><label for="csv_file_comment">コメント:</label></th>
  <td><%= text_field 'csv', 'file_comment'  %></td>
</tr>

<tr>
  <th align="right"><label for="csv_management_section_id"><b>管理部門:</label></th>
  <td><%= collection_select :csv, :management_section_id, @sections, :id, :name %></td>
</tr>

<tr>
  <th align="right"><label for="csv_user_id">アップロード担当者:</label></th>
  <td><%= text_field_tag 'csv_user_id', @csv.user.fullname, :disabled => true  %>
      <%#= :disabled => true を指定すると、パラメーターも送信されないため、hidden_fieldを利用 %>
      <%= hidden_field 'csv', 'user_id'  %></td>
</tr>

上記ビューの修正で、見た目はこのようになる。

listアクションに関係する変更

ビュー
  • ユーザーが所有する権限によって、アクションのリンクを表示、非表示、リンクなし、の状態に変化するようにした。
  • admin?、uploader?、management_section?は、app/helpers/フォルダ内に定義したメソッド。application_helper.rbに定義しておいたので、csv_serverプロジェクトのビューであれば、どこからでも呼び出すことが出来る。
<%# ---------- app/views/csvs/_listd.rhtml ---------- %>

<!--[:]-->
<tr>
  <%# リストデータの表示 %>
  <!--<td><%=h listd.id %></td>-->
  <td><%= link_to h(listd.file_name), :controller => 'displays', :action=>'list', 
                  :table => listd.csv_table %></td>
  <td><%=h listd.file_comment %></td>
  <td><%= listd.editable ? "編集可能" : "表示のみ" %></td>
  <td align="right"><%=h listd.file_size.to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, '\1,') %> B</td>
  <td><%=h listd.created_at.strftime('%Y/%m/%d %H:%M:%S') %></td>
  <td><%=h listd.file_updated_at.strftime('%Y/%m/%d %H:%M:%S') if listd.file_updated_at %></td>
  <td><%=h listd.management_section.name if listd.management_section_id > 0 %></td>
  <td><%=h listd.user.fullname if listd.user_id > 0 %></td>

  <%# 詳細 編集 削除のアクションを表示 %>
  <%# ユーザーが所有する権限によって、表示 非表示 リンク設定なし に状態を変更する。 %>
  <% if admin? %>
    <td class="action" align="center">
      <%= link_to '詳細', :action => 'show', :id => listd %></td>
  <% end %>
  <% if admin? || uploader? %>
    <td class="action" align="center">
      <%= link_to_if management_section?(listd.management_section), '編集', 
                     :action => 'edit', :id => listd %></td>
    <td class="action" align="center">
      <%= link_to_if management_section?(listd.management_section), '削除', 
                     { :action => 'destroy', :id => listd }, 
                     :confirm => _('Are you sure?'), :post => true %></td>
  <% end %>
</tr>
<!--[:]-->
ヘルパ
  • ここで定義したメソッドは、ビューから関数を利用するように呼び出すことが出来る。
  • モデル名と同じ名前_helper.rb のファイルに定義すると、そのモデルのビュー限定のメソッドになる。
  • application_helper.rb に定義すると、全てのビューから呼び出すことが出来る。
# ---------- app/helpers/application_helper.rb ----------

# Methods added to this helper will be available to all templates in the application.
module ApplicationHelper
...(途中省略)...
  # ログインユーザーが、admin権限ならtrueを返す。
  def admin?
    session[:user].admin? if session[:user]
  end

  # ログインユーザーが、Uploader権限ならtrueを返す。  
  def uploader?
    session[:user].roles.each { |r| return true if r.name == 'Uploader' }
    false
  end
  
  # ログインユーザーが、csv_sectionで渡された、管理部門に所属していればtrueを返す。
  def management_section?(csv_section)
    return true if session[:user].management_sections.include?(csv_section) || csv_section.nil? || admin?
    false
  end

リスト表示は、このようになった。(Uplodaer権限の 鈴木 一郎 でログインしている場合)