Skip to content

Commit

Permalink
Merge pull request #2 from AtlasSystems/feature/23448/FixSearchUrlBui…
Browse files Browse the repository at this point in the history
…lding

Feature/23448/fix search url building
  • Loading branch information
amorgan-atlas-sys authored Jul 11, 2023
2 parents 69ec408 + c940903 commit 8aba00e
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 47 deletions.
101 changes: 70 additions & 31 deletions Catalog.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ catalogSearchForm.Form = nil;
catalogSearchForm.Browser = nil;
catalogSearchForm.RibbonPage = nil;
catalogSearchForm.ItemsButton = nil;
catalogSearchForm.ImportButton = nil;
catalogSearchForm.ImportButtons = {};
catalogSearchForm.SearchButtons = {};

local mmsIdsCache = {};
Expand Down Expand Up @@ -59,7 +59,7 @@ local watcherEnabled = false;
local recordsLastRetrievedFrom = "";
local layoutMode = "browse";
local browserType = nil;
if AddonInfo.Browsers ~= nil and AddonInfo.Browsers.WebView2 ~= nil and AddonInfo.Browsers.WebView2 == true then
if AddonInfo.Browsers and AddonInfo.Browsers.WebView2 then
browserType = "WebView2";
else
browserType = "Chromium";
Expand All @@ -82,26 +82,41 @@ function Init()
catalogSearchForm.SearchButtons["Home"] = catalogSearchForm.RibbonPage:CreateButton("New Search", GetClientImage(DataMapping.Icons[product]["Web"]), "ShowCatalogHome", "Search Options");

log:Info("Creating buttons for available search types.");
for _, searchType in ipairs(settings.AvailableSearchTypes) do
local searchStyle = DataMapping.SearchTypes[searchType].SearchStyle;
local success, err = pcall(function()
for _, searchType in ipairs(settings.AvailableSearchTypes) do
local searchStyle = DataMapping.SearchTypes[searchType].SearchStyle;

log:DebugFormat("Creating button for search type {0} with search style {1}", searchType, searchStyle);
log:DebugFormat("Creating button for search type {0} with search style {1}", searchType, searchStyle);

catalogSearchForm.SearchButtons[searchType] = catalogSearchForm.RibbonPage:CreateButton(DataMapping.SearchTypes[searchType].ButtonText, GetClientImage(DataMapping.SearchTypes[searchType][product .. "Icon"]), "Placeholder", "Search Options");
catalogSearchForm.SearchButtons[searchType] = catalogSearchForm.RibbonPage:CreateButton(DataMapping.SearchTypes[searchType].ButtonText, GetClientImage(DataMapping.SearchTypes[searchType][product .. "Icon"]), "Placeholder", "Search Options");

catalogSearchForm.SearchButtons[searchType].BarButton:add_ItemClick(ButtonSearch);
catalogSearchForm.SearchButtons[searchType].BarButton.Tag = {SearchType = searchType, SearchStyle = searchStyle};
catalogSearchForm.SearchButtons[searchType].BarButton:add_ItemClick(ButtonSearch);
catalogSearchForm.SearchButtons[searchType].BarButton.Tag = {SearchType = searchType, SearchStyle = searchStyle};
end
end);
if not success then
log:ErrorFormat("{0}. Search types may be configured incorrectly. Please ensure SearchTypes exist in DataMapping.lua for each search type in the AvailableSearchTypes setting.", TraverseError(err));
interfaceMngr:ShowMessage("Search types may be configured incorrectly. Please ensure SearchTypes exist in DataMapping.lua for each search type in the AvailableSearchTypes setting. See client log for details.", "Configuration Error");
end

log:Info("Creating buttons for import profiles.");
for importProfileName, importProfile in pairs(DataMapping.ImportProfiles) do
if importProfile.Product == product then
log:DebugFormat("Creating button for import profile {0}", importProfileName);

catalogSearchForm.ImportButtons[importProfileName] = catalogSearchForm.RibbonPage:CreateButton(DataMapping.ImportProfiles[importProfileName].ButtonText, GetClientImage(DataMapping.ImportProfiles[importProfileName].Icon), "Placeholder", "Process");

catalogSearchForm.ImportButtons[importProfileName].BarButton:add_ItemClick(DoItemImport);
catalogSearchForm.ImportButtons[importProfileName].BarButton.Tag = {ImportProfileName = importProfileName};
catalogSearchForm.ImportButtons[importProfileName].BarButton.Enabled = false;
end
end

if (not settings.AutoRetrieveItems) then
catalogSearchForm.ItemsButton = catalogSearchForm.RibbonPage:CreateButton("Retrieve Items", GetClientImage(DataMapping.Icons[product]["Retrieve Items"]), "RetrieveItems", "Process");
catalogSearchForm.ItemsButton.BarButton.ItemShortcut = types["DevExpress.XtraBars.BarShortcut"](types["System.Windows.Forms.Shortcut"].CtrlR);
end

catalogSearchForm.ImportButton = catalogSearchForm.RibbonPage:CreateButton("Import", GetClientImage(DataMapping.Icons[product]["Import"]), "DoItemImport", "Process");
catalogSearchForm.ImportButton.BarButton.ItemShortcut = types["DevExpress.XtraBars.BarShortcut"](types["System.Windows.Forms.Shortcut"].CtrlI);
catalogSearchForm.ImportButton.BarButton.Enabled = false;

BuildItemsGrid();
catalogSearchForm.Form:LoadLayout("CatalogLayout_Browse_" .. browserType .. ".xml");

Expand Down Expand Up @@ -213,9 +228,15 @@ function PerformSearch(searchInfo)
end

local searchUrl = "";
local encodedSiteCode = Utility.URLEncode(settings.PrimoSiteCode):gsub("%%", "%%%%");
local encodedSearchType = Utility.URLEncode(DataMapping.SearchTypes[searchInfo[1]]["PrimoField"]):gsub("%%", "%%%%");
local encodedSearchTerm = Utility.URLEncode(searchTerm):gsub("%%", "%%%%");

--Construct the search url based on the base catalog url and search style.
searchUrl = settings.CatalogUrl .. DataMapping.SearchStyleUrls[searchInfo[2]]:gsub("{PrimoSiteCode}", settings.PrimoSiteCode):gsub("{SearchType}", DataMapping.SearchTypes[searchInfo[1]]["PrimoField"]):gsub("{SearchTerm}", Utility.URLEncode(searchTerm));
searchUrl = settings.CatalogUrl .. DataMapping.SearchStyleUrls[searchInfo[2]]
:gsub("{PrimoSiteCode}", encodedSiteCode)
:gsub("{SearchType}", encodedSearchType)
:gsub("{SearchTerm}", encodedSearchTerm);

log:InfoFormat("Navigating to {0}", searchUrl);
catalogSearchForm.Browser:Navigate(searchUrl);
Expand Down Expand Up @@ -251,7 +272,10 @@ function ExtractIds(itemDetails)
local idMatches = {};
local urlId = (catalogSearchForm.Browser.Address):match("%d+" .. settings.IdSuffix);
-- Easy way to prevent duplicates regardless of order since the keys get overwritten.
idMatches[urlId] = true;
-- In rare cases the URL won't contain an ID.
if urlId then
idMatches[urlId] = true;
end

-- MMS Ids (and presumably IE IDs) all have the same last four digits specific to the institution.
-- 99 is the prefix for MMS IDs, and IE IDs always have 1, 2, or 5 as their first digit and 1 as the second digit.
Expand Down Expand Up @@ -366,15 +390,19 @@ function ToggleItemsUIElements(enabled)
-- If there's an item in the Item Grid
if(catalogSearchForm.Grid.GridControl.MainView.FocusedRowHandle > -1) then
catalogSearchForm.Grid.GridControl.Enabled = true;
catalogSearchForm.ImportButton.BarButton.Enabled = true;
for buttonName, _ in pairs(catalogSearchForm.ImportButtons) do
catalogSearchForm.ImportButtons[buttonName].BarButton.Enabled = true;
end
end
end
else
log:Debug("Disabling UI.");
ClearItems();
recordsLastRetrievedFrom = "";
catalogSearchForm.Grid.GridControl.Enabled = false;
catalogSearchForm.ImportButton.BarButton.Enabled = false;
for buttonName, _ in pairs(catalogSearchForm.ImportButtons) do
catalogSearchForm.ImportButtons[buttonName].BarButton.Enabled = false;
end

if (not settings.AutoRetrieveItems) then
catalogSearchForm.ItemsButton.BarButton.Enabled = false;
Expand Down Expand Up @@ -472,10 +500,14 @@ end

function ItemsGridFocusedRowChanged(sender, args)
if (args.FocusedRowHandle > -1) then
catalogSearchForm.ImportButton.BarButton.Enabled = true;
for buttonName, _ in pairs(catalogSearchForm.ImportButtons) do
catalogSearchForm.ImportButtons[buttonName].BarButton.Enabled = true;
end
catalogSearchForm.Grid.GridControl.Enabled = true;
else
catalogSearchForm.ImportButton.BarButton.Enabled = false;
for buttonName, _ in pairs(catalogSearchForm.ImportButtons) do
catalogSearchForm.ImportButtons[buttonName].BarButton.Enabled = false;
end
end;
end

Expand Down Expand Up @@ -647,9 +679,11 @@ function PopulateItemsDataSources( response, itemsDataTable )
catalogSearchForm.Grid.GridControl:EndUpdate();
end

function DoItemImport()
function DoItemImport(sender, args)
cursor.Current = cursors.WaitCursor;

local importProfileName = args.Item.Tag.ImportProfileName;

log:Debug("Retrieving import row.");
local importRow = catalogSearchForm.Grid.GridControl.MainView:GetFocusedRow();

Expand All @@ -659,18 +693,24 @@ function DoItemImport()
end;

log:Info("Importing item values.");
for _, target in ipairs(DataMapping.ImportFields.Item[product]) do
local importValue = importRow:get_Item(target.Value);
local success, err = pcall(function()
for _, target in ipairs(DataMapping.ImportFields.Item[importProfileName]) do
local importValue = importRow:get_Item(target.Value);

log:DebugFormat("Importing value '{0}' to {1}", importValue, target.Field);
ImportField(target.Table, target.Field, importValue, target.MaxSize);
log:DebugFormat("Importing value '{0}' to {1}", importValue, target.Field);
ImportField(target.Table, target.Field, importValue, target.MaxSize);
end
end);
if not success then
log:ErrorFormat("{0}. Import profiles may not be configured correctly. Please ensure that each import profile in DataMapping.lua corresponds to a set of item import fields.", TraverseError(err));
interfaceMngr:ShowMessage("Import profiles may not be configured correctly. Please ensure that each import profile in DataMapping.lua corresponds to a set of item import fields. See client log for details.", "Configuration Error");
end

local mmsId = importRow:get_Item("ReferenceNumber");
local holdingId = importRow:get_Item("HoldingId");

local holdingInformation = GetMarcInformation(mmsId, holdingId);
local bibliographicInformation = GetMarcInformation(mmsId);
local holdingInformation = GetMarcInformation(importProfileName, mmsId, holdingId);
local bibliographicInformation = GetMarcInformation(importProfileName, mmsId);

log:Info("Importing bib values.");
for _, target in ipairs(bibliographicInformation) do
Expand All @@ -688,7 +728,7 @@ function DoItemImport()
ExecuteCommand("SwitchTab", "Detail");
end

function GetMarcInformation(mmsId, holdingId)
function GetMarcInformation(importProfileName, mmsId, holdingId)
local marcInformation = {};

local marcXmlDoc = nil;
Expand All @@ -713,9 +753,9 @@ function GetMarcInformation(mmsId, holdingId)
-- Loops through each import mapping
local mappingTable = {};
if holdingId then
mappingTable = DataMapping.ImportFields.Holding[product];
mappingTable = DataMapping.ImportFields.Holding[importProfileName];
else
mappingTable = DataMapping.ImportFields.Bibliographic[product];
mappingTable = DataMapping.ImportFields.Bibliographic[importProfileName];
end

for _, target in ipairs(mappingTable) do
Expand Down Expand Up @@ -770,12 +810,11 @@ end
function TraverseError(e)
if not e.GetType then
-- Not a .NET type
return nil;
return e;
else
if not e.Message then
-- Not a .NET exception
log:Debug(e:ToString());
return nil;
return e;
end
end

Expand Down
2 changes: 1 addition & 1 deletion Config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Alma Primo Definitive Catalog Search</Name>
<Author>Atlas Systems</Author>
<Version>1.0.0</Version>
<Version>1.0.1</Version>
<Active>True</Active>
<Type>Addon</Type>
<Description>Catalog Search and Import Addon that uses Alma as the catalog and Primo or Primo VE as the discovery layer.</Description>
Expand Down
27 changes: 21 additions & 6 deletions DataMapping.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ DataMapping.Icons = {};
DataMapping.SearchTypes = {};
DataMapping.SearchStyleUrls = {};
DataMapping.SourceFields = {};
DataMapping.ImportProfiles = {};
DataMapping.ImportFields = {};
DataMapping.ImportFields.Bibliographic = {};
DataMapping.ImportFields.Holding = {};
Expand All @@ -18,7 +19,6 @@ DataMapping.Icons["Aeon"] = {};
DataMapping.Icons["Aeon"]["Home"] = "home_32x32";
DataMapping.Icons["Aeon"]["Web"] = "web_32x32";
DataMapping.Icons["Aeon"]["Retrieve Items"] = "record_32x32";
DataMapping.Icons["Aeon"]["Import"] = "impt_32x32";

--[[
SearchTypes
Expand Down Expand Up @@ -87,10 +87,25 @@ DataMapping.SearchStyleUrls["Browse"] = "browse?vid={PrimoSiteCode}&browseQuery=
DataMapping.SourceFields["Aeon"] = {};
DataMapping.SourceFields["Aeon"]["TransactionNumber"] = { Table = "Transaction", Field = "TransactionNumber" };

--[[
Import Profiles
Each import profile defines a set of fields to be imported. A button will be generated for each
profile with a Product property matching the product you're using. The key for the import profile
must have a corresponding set of import fields with matching keys in the next section.
- ButtonText: The text that appears on the ribbon button for the import.
- Product: The product the import profile is for.
- Icon: The name of the icon file to use as the button's image.
--]]

DataMapping.ImportProfiles["Default"] = {
ButtonText = "Import",
Product = "Aeon",
Icon = "impt_32x32"
}

-- Import Fields: The table for each ImportFields section must be defined for the product (Aeon, ILLiad, or Ares)
-- Import Fields: The table for each ImportFields section must be defined for each import profile above.
-- running the addon to work properly, even if it is empty. Example of an empty ImportFields table:
-- DataMapping.ImportFields.Bibliographic["Aeon"] = { };
-- DataMapping.ImportFields.Bibliographic["Default"] = { };

--[[
Bib-level import fields.
Expand All @@ -101,7 +116,7 @@ DataMapping.SourceFields["Aeon"]["TransactionNumber"] = { Table = "Transaction",
in parentheses next to the field type.
- Value must be an XPath expression.
--]]
DataMapping.ImportFields.Bibliographic["Aeon"] = {
DataMapping.ImportFields.Bibliographic["Default"] = {
{
Table = "Transaction",
Field = "ItemTitle", MaxSize = 255,
Expand Down Expand Up @@ -140,12 +155,12 @@ DataMapping.ImportFields.Bibliographic["Aeon"] = {
};

-- Holding-level import fields. Value must be an XPath expression.
DataMapping.ImportFields.Holding["Aeon"] = {
DataMapping.ImportFields.Holding["Default"] = {

}

-- Item-level import fields. Value should not be changed.
DataMapping.ImportFields.Item["Aeon"] = {
DataMapping.ImportFields.Item["Default"] = {
{
Table = "Transaction",
Field = "ReferenceNumber", MaxSize = 50,
Expand Down
24 changes: 15 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,6 @@ DataMapping.SearchTypes["Author"] = {
AeonIcon = "srch_32x32",
AeonSourceField = { Table = "Transaction", Field = "ItemAuthor" }
};
DataMapping.SearchTypes["Call Number"] = {
ButtonText = "Call Number",
PrimoField = "lsr01",
SearchStyle = "Query",
AeonIcon = "srch_32x32",
AeonSourceField = { Table = "Transaction", Field = "CallNumber" }
};
DataMapping.SearchTypes["ISBN"] = {
ButtonText = "ISBN",
PrimoField = "isbn",
Expand All @@ -119,7 +112,7 @@ DataMapping.SearchTypes["Catalog Number"] = {
};
```

>**Note:** The *Catalog Number* search type performs an `any` search because Primo does not have a search type for MMS ID.
>**Note:** The *Catalog Number* search type performs an `any` search because Primo does not have a search type for MMS ID by default.
### Source Fields
The field that the addon reads from for values used by the addon that are not used in searches.
Expand All @@ -133,6 +126,19 @@ DataMapping.SourceFields["Aeon"] = {};
DataMapping.SourceFields["Aeon"]["TransactionNumber"] = { Table = "Transaction", Field = "TransactionNumber" };
```

### Import Profiles
Similar to SearchTypes, custom import profiles can be configured. Each import profile in DataMapping.lua will generate an import button with the ButtonText as its label. Each import profile must correspond to a set of bibliographic, holding, and item import fields with matching keys.

*Default Configuration:*

```lua
DataMapping.ImportProfiles["Default"] = {
ButtonText = "Import",
Product = "Aeon",
Icon = "impt_32x32"
}
```

### Bibliographic Import
The information within this data mapping is used to perform the bibliographic api call. The `Field` is the product field that the data will be imported into, `MaxSize` is the maximum character size the data going into the product field can be, and `Value` is the XPath query to the information.

Expand All @@ -150,7 +156,7 @@ The information within this data mapping is used import the correct information
*Default Configuration:*
```lua
DataMapping.ImportFields.Item["Aeon"] = {
DataMapping.ImportFields.Item["Default"] = {
{
Table = "Transaction",
Field = "ReferenceNumber", MaxSize = 50,
Expand Down

0 comments on commit 8aba00e

Please sign in to comment.