diff --git a/app/controllers/batch_actions_controller.rb b/app/controllers/batch_actions_controller.rb index 76d09cb..dc624b9 100644 --- a/app/controllers/batch_actions_controller.rb +++ b/app/controllers/batch_actions_controller.rb @@ -107,11 +107,11 @@ def process_asset_updates(assets, updates_applied) # Update Assigned User if params[:assigned_user_id].present? if params[:assigned_user_id] == "unassigned" - updates[:assigned_to] = nil + updates[:assigned_to_id] = nil updates_applied << "assigned user to unassigned" else user = User.find(params[:assigned_user_id]) - updates[:assigned_to] = user.id + updates[:assigned_to_id] = user.id updates_applied << "assigned user to #{user.title}" end end @@ -160,7 +160,7 @@ def process_folder_updates(descendant_folder_ids, updates_applied) user = User.find(params[:assigned_user_id]) end - updates[:assigned_to] = user&.id + updates[:assigned_to_id] = user&.id add_update_message(updates_applied, "assigned folders to #{user ? user.title : 'unassigned'}") end diff --git a/app/controllers/volumes_controller.rb b/app/controllers/volumes_controller.rb index 0909c92..e574345 100644 --- a/app/controllers/volumes_controller.rb +++ b/app/controllers/volumes_controller.rb @@ -51,7 +51,7 @@ def file_tree_assets_search # Column filters scope = scope.where(migration_status: params[:migration_status]) if params[:migration_status].present? - scope = scope.where(assigned_to: params[:assigned_to]) if params[:assigned_to].present? && params[:assigned_to] != "unassigned" + scope = scope.where(assigned_to_id: params[:assigned_to]) if params[:assigned_to].present? && params[:assigned_to] != "unassigned" scope = scope.where(contentdm_collection_id: params[:contentdm_collection_id]) if params[:contentdm_collection_id].present? scope = scope.where(aspace_collection_id: params[:aspace_collection_id]) if params[:aspace_collection_id].present? scope = scope.where(aspace_linking_status: ActiveModel::Type::Boolean.new.cast(params[:aspace_linking_status])) if params.key?(:aspace_linking_status) @@ -59,7 +59,7 @@ def file_tree_assets_search # Handle unassigned users (assigned_to = nil) if params[:assigned_to] == "unassigned" - scope = scope.where(assigned_to: [ nil, "" ]) + scope = scope.where(assigned_to_id: nil) end assets = scope.includes(parent_folder: :parent_folder).limit(500) @@ -91,21 +91,21 @@ def file_tree_updates end field_map = { - "migration_status" => "migration_status_id" + "migration_status" => "migration_status_id", + "assigned_to" => "assigned_to_id" } db_field = field_map[params[:field]] || params[:field] value = params[:value] - # Handle assigned_to specially - it's an association, not a direct field - if db_field == "assigned_to" + if db_field == "assigned_to_id" if value.present? && value != "unassigned" user = User.find_by(id: value.to_i) unless user return render json: { status: "error", errors: [ "User not found" ] }, status: :unprocessable_entity end - value = user + value = user.id else value = nil # unassigned end @@ -120,7 +120,7 @@ def file_tree_updates aspace_collection_id preservica_reference_id aspace_linking_status - assigned_to + assigned_to_id notes ] @@ -139,6 +139,7 @@ def file_tree_updates case db_field when "contentdm_collection_id" then record.contentdm_collection&.name when "aspace_collection_id" then record.aspace_collection&.name + when "assigned_to_id" then record.assigned_to&.name else record[db_field] end diff --git a/app/javascript/controllers/wunderbaum_controller.js b/app/javascript/controllers/wunderbaum_controller.js index d769c9e..34a434f 100644 --- a/app/javascript/controllers/wunderbaum_controller.js +++ b/app/javascript/controllers/wunderbaum_controller.js @@ -99,7 +99,11 @@ export default class extends Controller { filterable: true, title: "Assigned To", width: "175px", - sortValue: (node) => (node?.data?.assigned_to || "").toString().toLowerCase() + sortValue: (node) => { + const label = node?.data?.assigned_to || + this._optionLabelFor("assigned_to", node?.data?.assigned_to_id); + return (label || "").toString().toLowerCase(); + } }, { id: "is_duplicate", @@ -200,7 +204,10 @@ export default class extends Controller { for (const colInfo of Object.values(e.renderColInfosById)) { const colId = colInfo.id; - let rawValue = this._normalizeValue(colId, node.data[colId]); + let rawValue = this._normalizeValue( + colId, + colId === "assigned_to" ? node.data.assigned_to_id : node.data[colId] + ); if (isFolder && colId !== "assigned_to" && this.selectLikeColumns.has(colId)) { colInfo.elem.replaceChildren(); @@ -228,7 +235,11 @@ export default class extends Controller { let displayValue = rawValue ?? ""; if (this.selectLikeColumns.has(colId)) { - displayValue = this._optionLabelFor(colId, rawValue); + if (colId === "assigned_to" && node.data?.assigned_to) { + displayValue = node.data.assigned_to; + } else { + displayValue = this._optionLabelFor(colId, rawValue); + } } colInfo.elem.dataset.colid = colId; @@ -288,7 +299,13 @@ export default class extends Controller { const colId = e.info.colId; const value = util.getValueFromElem(e.inputElem, true); - e.node.data[colId] = value; + if (colId === "assigned_to") { + const label = this._optionLabelFor("assigned_to", value); + e.node.data.assigned_to_id = value === "unassigned" ? null : value; + e.node.data.assigned_to = label; + } else { + e.node.data[colId] = value; + } this._saveCellChange(e.node, colId, value); }, @@ -881,7 +898,7 @@ export default class extends Controller { // Normalizes column values for predicate comparisons. _filterValueFor(colId, data = {}) { if (colId === "assigned_to") { - const id = data.assigned_to_id ?? data.assigned_to; + const id = data.assigned_to_id; return (id == null || id === "") ? "unassigned" : String(id); } diff --git a/app/models/isilon_asset.rb b/app/models/isilon_asset.rb index d615424..0258231 100644 --- a/app/models/isilon_asset.rb +++ b/app/models/isilon_asset.rb @@ -4,7 +4,7 @@ class IsilonAsset < ApplicationRecord belongs_to :migration_status, optional: true # optional: true if some records are still NULL belongs_to :aspace_collection, optional: true belongs_to :contentdm_collection, optional: true - belongs_to :assigned_to, class_name: "User", foreign_key: "assigned_to", optional: true + belongs_to :assigned_to, class_name: "User", optional: true has_many :duplicate_group_memberships, dependent: :delete_all has_many :duplicate_groups, through: :duplicate_group_memberships @@ -22,15 +22,6 @@ def full_path_with_volume "/#{volume_name}#{path}".gsub(%r{//+}, "/") end - def assigned_to_id - assigned_to&.id - end - - def assigned_to_id=(value) - self.assigned_to = value.present? ? User.find(value) : nil - end - - private def set_default_migration_status diff --git a/app/models/isilon_folder.rb b/app/models/isilon_folder.rb index ed22e5f..3bab5e3 100644 --- a/app/models/isilon_folder.rb +++ b/app/models/isilon_folder.rb @@ -3,7 +3,7 @@ class IsilonFolder < ApplicationRecord belongs_to :parent_folder, class_name: "IsilonFolder", foreign_key: "parent_folder_id", optional: true has_many :child_folders, class_name: "IsilonFolder", foreign_key: "parent_folder_id" has_many :isilon_assets, foreign_key: "parent_folder_id" - belongs_to :assigned_to, class_name: "User", foreign_key: "assigned_to", optional: true + belongs_to :assigned_to, class_name: "User", foreign_key: "assigned_to_id", optional: true def ancestors current = self diff --git a/app/models/user.rb b/app/models/user.rb index 8495825..61fce96 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -6,7 +6,7 @@ class User < ApplicationRecord :timeoutable, :omniauthable, omniauth_providers: [ :google_oauth2 ] - has_many :assigned_assets, class_name: "IsilonAsset", foreign_key: "assigned_to" + has_many :assigned_assets, class_name: "IsilonAsset", foreign_key: "assigned_to_id" enum :status, { inactive: "inactive", active: "active" }, suffix: true before_validation :assign_names_from_name_field before_validation :ensure_random_password, if: :new_record? diff --git a/app/serializers/file_tree_search_result_serializer.rb b/app/serializers/file_tree_search_result_serializer.rb index 2558f88..fdec66f 100644 --- a/app/serializers/file_tree_search_result_serializer.rb +++ b/app/serializers/file_tree_search_result_serializer.rb @@ -1,6 +1,6 @@ class FileTreeSearchResultSerializer < ActiveModel::Serializer attributes :id, :folder, :parent_folder_id, :path, - :migration_status, :assigned_to + :migration_status, :assigned_to_id, :assigned_to def folder folder? @@ -24,6 +24,11 @@ def migration_status object.migration_status&.name.to_s end + def assigned_to_id + return nil if folder? + object.assigned_to&.id + end + def assigned_to return nil if folder? object.assigned_to&.name.to_s diff --git a/app/serializers/isilon_asset_serializer.rb b/app/serializers/isilon_asset_serializer.rb index 942d94e..4acd212 100644 --- a/app/serializers/isilon_asset_serializer.rb +++ b/app/serializers/isilon_asset_serializer.rb @@ -62,6 +62,7 @@ def assigned_to object.assigned_to&.name.to_s.presence || "Unassigned" end + def contentdm_collection object.contentdm_collection&.id.to_s end diff --git a/app/serializers/isilon_folder_serializer.rb b/app/serializers/isilon_folder_serializer.rb index 0f27d1e..b66672d 100644 --- a/app/serializers/isilon_folder_serializer.rb +++ b/app/serializers/isilon_folder_serializer.rb @@ -28,6 +28,7 @@ def assigned_to object.assigned_to&.name.to_s.presence || "Unassigned" end + def path return [] if object.respond_to?(:parent_folder_id) && object.parent_folder_id.nil? diff --git a/db/migrate/20260213140000_rename_isilon_assets_assigned_to_id.rb b/db/migrate/20260213140000_rename_isilon_assets_assigned_to_id.rb new file mode 100644 index 0000000..a625db8 --- /dev/null +++ b/db/migrate/20260213140000_rename_isilon_assets_assigned_to_id.rb @@ -0,0 +1,31 @@ +class RenameIsilonAssetsAssignedToId < ActiveRecord::Migration[7.2] + def up + if index_name_exists?(:isilon_assets, "index_isilon_assets_on_assigned_to") + remove_index :isilon_assets, name: "index_isilon_assets_on_assigned_to" + end + if index_name_exists?(:isilon_assets, "index_isilon_assets_on_assigned_to_and_migration_status") + remove_index :isilon_assets, name: "index_isilon_assets_on_assigned_to_and_migration_status" + end + + rename_column :isilon_assets, :assigned_to, :assigned_to_id + + add_index :isilon_assets, :assigned_to_id + add_index :isilon_assets, [ :assigned_to_id, :migration_status_id ], + name: "index_isilon_assets_on_assigned_to_id_and_migration_status" + end + + def down + if index_name_exists?(:isilon_assets, "index_isilon_assets_on_assigned_to_id") + remove_index :isilon_assets, name: "index_isilon_assets_on_assigned_to_id" + end + if index_name_exists?(:isilon_assets, "index_isilon_assets_on_assigned_to_id_and_migration_status") + remove_index :isilon_assets, name: "index_isilon_assets_on_assigned_to_id_and_migration_status" + end + + rename_column :isilon_assets, :assigned_to_id, :assigned_to + + add_index :isilon_assets, :assigned_to + add_index :isilon_assets, [ :assigned_to, :migration_status_id ], + name: "index_isilon_assets_on_assigned_to_and_migration_status" + end +end diff --git a/db/migrate/20260213143000_rename_isilon_folders_assigned_to_id.rb b/db/migrate/20260213143000_rename_isilon_folders_assigned_to_id.rb new file mode 100644 index 0000000..84c1854 --- /dev/null +++ b/db/migrate/20260213143000_rename_isilon_folders_assigned_to_id.rb @@ -0,0 +1,31 @@ +class RenameIsilonFoldersAssignedToId < ActiveRecord::Migration[7.2] + def up + if foreign_key_exists?(:isilon_folders, :users, column: :assigned_to) + remove_foreign_key :isilon_folders, :users, column: :assigned_to + end + + if index_exists?(:isilon_folders, :assigned_to, name: "index_isilon_folders_on_assigned_to") + remove_index :isilon_folders, name: "index_isilon_folders_on_assigned_to" + end + + rename_column :isilon_folders, :assigned_to, :assigned_to_id + + add_index :isilon_folders, :assigned_to_id + add_foreign_key :isilon_folders, :users, column: :assigned_to_id + end + + def down + if foreign_key_exists?(:isilon_folders, :users, column: :assigned_to_id) + remove_foreign_key :isilon_folders, :users, column: :assigned_to_id + end + + if index_exists?(:isilon_folders, :assigned_to_id, name: "index_isilon_folders_on_assigned_to_id") + remove_index :isilon_folders, name: "index_isilon_folders_on_assigned_to_id" + end + + rename_column :isilon_folders, :assigned_to_id, :assigned_to + + add_index :isilon_folders, :assigned_to + add_foreign_key :isilon_folders, :users, column: :assigned_to + end +end diff --git a/db/schema.rb b/db/schema.rb index c19b46f..a7a39df 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2026_02_11_100000) do +ActiveRecord::Schema[7.2].define(version: 2026_02_13_143000) do create_table "admins", force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false @@ -74,12 +74,12 @@ t.integer "migration_status_id" t.integer "aspace_collection_id" t.integer "contentdm_collection_id" - t.integer "assigned_to" + t.integer "assigned_to_id" t.boolean "has_duplicates", default: false, null: false t.integer "volume_id" t.index ["aspace_collection_id"], name: "index_isilon_assets_on_aspace_collection_id" - t.index ["assigned_to", "migration_status_id"], name: "index_isilon_assets_on_assigned_to_and_migration_status" - t.index ["assigned_to"], name: "index_isilon_assets_on_assigned_to" + t.index ["assigned_to_id", "migration_status_id"], name: "index_isilon_assets_on_assigned_to_id_and_migration_status" + t.index ["assigned_to_id"], name: "index_isilon_assets_on_assigned_to_id" t.index ["contentdm_collection_id"], name: "index_isilon_assets_on_contentdm_collection_id" t.index ["file_checksum"], name: "index_isilon_assets_on_file_checksum" t.index ["has_duplicates"], name: "index_isilon_assets_on_has_duplicates" @@ -95,9 +95,9 @@ t.integer "parent_folder_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "assigned_to" + t.integer "assigned_to_id" t.text "notes" - t.index ["assigned_to"], name: "index_isilon_folders_on_assigned_to" + t.index ["assigned_to_id"], name: "index_isilon_folders_on_assigned_to_id" t.index ["parent_folder_id"], name: "index_isilon_folders_on_parent_folder_id" t.index ["volume_id", "full_path"], name: "index_isilon_folders_on_volume_id_and_full_path", unique: true t.index ["volume_id"], name: "index_isilon_folders_on_volume_id" @@ -143,9 +143,9 @@ add_foreign_key "isilon_assets", "contentdm_collections" add_foreign_key "isilon_assets", "isilon_folders", column: "parent_folder_id" add_foreign_key "isilon_assets", "migration_statuses" - add_foreign_key "isilon_assets", "users", column: "assigned_to" + add_foreign_key "isilon_assets", "users", column: "assigned_to_id" add_foreign_key "isilon_assets", "volumes" add_foreign_key "isilon_folders", "isilon_folders", column: "parent_folder_id" - add_foreign_key "isilon_folders", "users", column: "assigned_to" + add_foreign_key "isilon_folders", "users", column: "assigned_to_id" add_foreign_key "isilon_folders", "volumes" end diff --git a/spec/requests/volumes/file_tree_updates_spec.rb b/spec/requests/volumes/file_tree_updates_spec.rb index 4dcaf4b..0e1bc77 100644 --- a/spec/requests/volumes/file_tree_updates_spec.rb +++ b/spec/requests/volumes/file_tree_updates_spec.rb @@ -35,6 +35,24 @@ expect(response).to have_http_status(:ok) expect(folder.reload.notes).to eq("Folder note") end + + it "updates and persists assigned_to for a folder" do + assignee = create(:user, name: "Other User") + + patch file_tree_updates_volume_path(volume), params: { + node_id: folder.id, + node_type: "folder", + field: "assigned_to", + value: assignee.id + }, as: :json + + expect(response).to have_http_status(:ok) + expect(folder.reload.assigned_to).to eq(assignee) + + json = JSON.parse(response.body) + expect(json["field"]).to eq("assigned_to_id") + expect(json["label"]).to eq("Other User") + end end context "with an invalid node_id" do