WebDrawer
Open WebDrawer using Google Chrome
find a record with a PDF document
View it (using the Trapeze viewer
- editing Views\Search.cshhtml
- enable 'Name' retrieval for the search form
- add a filter field based on the name.
- each file uploaded in a seperate request,
- checks file size before upload, and
- update RecordDateCreated with file last modified date.
- add Json to serviceFeatures
- add the following route (in routeDefaults)
- copy the DLL to the web client bin folder,
- edit your web.config modules section to look like the below, and
- ensure that the identifier in your ADFS relying party trust is the same as your web client base URL (e.g. https://myserver.com/HPRMWebClient/)
- edit hprmServiceAPI.config in the web client folder,
- find the setup element, then
- change the webClientClassicUrl to include a trailing slash.
- a first look at event processor add-ins,
- creating a ServiceAPI plugin
- there are many posts discussing WebDrawer customisation, here is one.
edit the file Views\WDSearchClauseDefJS.cshtml, then
in the FieldDefinition 'foreach' loop I edit the search clause to only return the field definitions I am interested in.
- it will degrade performance, and
- it writes potentially sensitive information (such as a user's session id) to the log file.
- create a Google Client Id
- add the appropriate settings to the WebDrawer config files, and
- copy the required DLLs.
- GoogleOAuth DLLs, and
- source code.
- A beginners Guide
- Http Caching (at Google Developers)
- Caching when you least expect it
- create two new images the same size as the existing images,
- place them in your own folder (e.g. images\myimages)
- change the SRC elements in _Layout.cshtml.
- preview a document,
- select the Links / Actions button, then
- find yourself back at the main search page.
- embed the preview HTML via an iframe so that the 'base' element is no longer required, or
- fix the links in the HTML so that the 'base' element is no longer required.
- height from the window heigh less the top position of the iframe (plus a 5 pixel buffer), and
- width to the window width less the iframe margin (10 pixels) plus the menu width (if the left menu is on the left).
Object Notifier
The Object Notifier is an event based system to notify you when objects in Content Manager have changed, it can be used in a ServiceAPI application to proactively update your UI to refleect changes in the data, this sample implements the Object Notifier in the WebDrawer Record search results.
WebDrawer Infinite Scrolling
This sample helps you convert the WebDrawer paging to an infinite scroll so your users never have to click nexy page again.
Displaying the properties of a Schedule in WebDrawer
At some point since CM 8.3 the method used to determine which properties to display on a page was changed to allow the hptrim.config file to determine which properties are displayed. One omission in this change was the default hptrim.config was not updated to specify which properties to display for a retention schedule. This post describes how to remedy that omission.
WebDrawer - Group results by owner
While grouping search results is not something that CM does out of the box it can be achieved with little work. These samples demonstrate how you might group Record search results by owner Location.
WebDrawer - Electronic document 'Not found'
It might be the case that some CM users have permission to view a Record metadata but not the electronic document. In this case they will get a message like the one below when they open a document link.
WebDrawer Viewing error
The scenario:
Webdrawer - playing audio files
Webdrawer does not support the playing of audio files, instead you will get an error message when you preview a Record with an audio file attached. This can be remedied by adding some HTML to the preview template. To do this, in your Webdrawer install folder:
Open the file '\Views\WDRecordPreview.cshtml' in a text editor
Add the following HTML
if (new string[] { "MP3", "M4A", "WAV", "OGG" }.Any(ext => ext == record.Extension.Value)) { <audio preload="auto" autobuffer controls> <source src="~/Record/@record.Uri/file/document?inline=true" /> <p>Audio playback not supported in this browser.</p> </audio> } else
Your HTML should end up looking like this:
WebDrawer - group search clauses
Just added a new sample in the GitHub repo to allow for brackets in the standard WebDrawer search form. The readme in the repo should be enough to install the sample but if you want to have a look without installing it yourself the video below demonstrates how to install and what it looks like.
Recursive searching in WebDrawer and ServiceAPI
The CM search syntax allows for recursive searching, for example returning results from folders contained within folders. The operator that specifies recursion is the plus sign (+), the problem is that in a URL the plus sign is reserved to indicate white space.
To overcome this the ServiceAPI (and WebDrawer) use the tilde (~) instead of the plus sign, so instead of this:
container:[title:asia]+
Do this:
container:[title:asia]~
If you would rather use a character (or sequence of characters) other than the tilde you can customise this in the hptrim.config. The Web Client, for example, uses _$_ instead of tilde, which is set in the 'searching' element in its hprmServiceAPI.config.
<searching pageSize="30" searchRecursiveOption="_$_"/>
Using a search query instead of a Uri in a URL
Recently I wanted to link to a Record using the external ID, not the Uri or the number. This was from a customised WeDrawer page so I could have done a search to find the Uri and then used that, but I didn't want that extra step. A little reflection (and poking around in the code) and I realised that I could use the external id in the URL after all.
If it was a search
A search by external id would look like this, problem was I want to open the actual record page, not a list of one record.
/WebDrawer/record?q=recExternal:abc123
As a URL
As long as a search returns only one result then you can substitute the query for the Uri in the URL, as long as you can compose the query without using a colon (colons are reserved characters in URLs). So below I use equals instead of colon to either open the details page or go directly to the document via the external id.
http://localhost/WebDrawer/record/recExternal=abc123 http://localhost/WebDrawer/record/recExternal=abc123/file/document
Note
As is my habit I use the internal name for the search clause (recExternal) not the name (external) to minimise the possibility of breakages due to either custom captions or use of different languages.
ServiceAPI - Glitch in 9.1.1 and 9.2 upgrade
If you are upgrading the ServiceAPI or WebDrawer to 9.1.1 or 9.2 you may get an error like the one below when you open via a web browser. This is due to a glitch in the way we upgrade the config files.
WebDrawer multi tenancy in 92
WebDrawer has not changed a lot in 92, one of the enhancements is the ability to run multiple instances of WebDrawer out of the same installation. Prior to this it was required to copy the WeDrawer program folder and create a new IIS Application. The new IIS Application is still required but the copied WebDrawer binaries can be avoided,
Where to store config, web.config or not...
For those who customise WebDrawer on one machine and then push it to a production machine this may prove handy. I wanted to change the setup of IIS on my dev machine to not update the web.config when I change the authentication settings. Here is what I did...
1. Go to Feature delegation in IIS Manager
Custom Authentication
A couple of years I wrote a post describing Google authentication in the ServiceAPI. This is an update of that. The details are in the samples repo and here is a video of me applying the sample from the repo to a WebDrawer and Web Client instance.
Other authentication providers
Remember the repo contains a link the ServiceStack documentation which provides assistance for using (and creating) other authentication providers.
Filter WebDrawer Search Forms
Lets say you have designed multiple search forms which are used via WebDrawer. These forms provide different fields with which to search. In addition to this you may also want to filter the results for each form to make them appropriate for the target audience.
Embedding the filter
One approach is to embed a filter based on the search form name. I did this by:
Name retrieval
Near the top of Search.cshtml you will see code that looks like this
searchForm = TrimHelper.Search<SearchForm>( BaseObjectTypes.SearchForm, "all", properties: new string[] { "ObjectType", "DefinitionForm" }).FirstOrDefault();
Add the property 'Name' in the propert list, so that it now looks like this:
searchForm = TrimHelper.Search<SearchForm>( BaseObjectTypes.SearchForm, sfName, properties: new string[] { "ObjectType", "DefinitionForm", "Name" }).FirstOrDefault();<p>Hello, World!
Add the filter
Further down the page is found the search form itself, starting with this HTML:
<form id="search-form-form" class="form-horizontal search-form" action="FormSearch" method="GET">
After this line add some code to insert a filter INPUT element, potentially different for each search form. The code below add a filter for the search form named 'Test Form'
@if (searchForm.Name == "Test Form") { <input type="hidden" name="filter" value="recType:Document" /> }
Warning
This is a convenience, not a security measure. For example a user could still modify the URL resulting from this search to avoid the filtering.
WebDrawer - Show contained Records
How would we show a list of all contained Records within the details page of a container in WebDrawer? I can think of at least two ways.
Embed contained Records
Edit detailsView.cshtml and include the code below somewhere near (or at) the bottom. This will embed up to 500 contained Records in this page.
@if (Model.TrimModel.TrimType == BaseObjectTypes.Record) { Record record = trimObject as Record; if (record.IsContainer) { <h2>Contained Records</h2> <table class="table"> <thead> <tr> <th>Title</th> <th>Number</th> </tr> </thead> <tbody> @foreach(Record containedRec in this.TrimHelper.Search<Record>( BaseObjectTypes.Record, string.Format("recContainer:{0}", record.Uri), properties: new string[] { "RecordTitle", "RecordNumber" }, pageSize: 500)) { <tr> <td><a href="~/Record/@containedRec.Uri">@containedRec.Title</a></td> <td>@containedRec.Number</td> </tr> } </tbody> </table> } }
Embed contained Records within an IFrame
It is also possible to embed the contained Records as an iframe. The advantage of this is that you can page through a large result set inside the iframe, the cost is that you will need to set up clean view without the menu and banner and link that up using the routeDefaults (in hprtrim.config).
I can provide examples of the clean view and routeDefaults on request.
<h2>Contained Records</h2> <iframe src="~/Record?q=@string.Format("recContainer:{0}", record.Uri)" width="100%" height="200"></iframe>
The result
The result of the first option above will look something like this.
Sunshine Coast Council
Sunshine Coast council use WebDrawer to deliver property related documents from Content Manager direct to the public. This replaces a four hour process to publish each document with a simple status change on a Record.
WebDrawer download file from PDF/TIF preview
WebDrawer uses the Trapeze viewer for PDF and TIF files, it was pointed out to me that when in this view the user does not have a menu link to download the original document. What the end users wanted was something like this.
Add a count button to WebDrawer
It is possible to speed up the search results in WebDrawer by switching to simpler page navigation. This post shows how to add a 'Get Count' button to allow users to determine the total number of search results.
Follow the steps in the video and use the code snippets below.
The Code
Views\Shared\searchResults.cshtml
<ul id="trim-pagination"> <li class="@(start < 2 ? "disabled" : null)"> @if (start > 1) { queryString["start"] = (start > pageSize ? start - pageSize : 0).ToString(); <a href="?@queryString.ToFormUrlEncoded()">«</a> } else { <span> «</span> } </li> <li> @{ var t = ((start - 1) / pageSize) + 1; } <span>@t</span> </li>
WebDrawer Error Message
In WebDrawer 8.2 and 8.3 you might see the error message below...
Grand Junction
Another customer who has customised a WebDrawer instance to provide public access to a sub set of their records is the city of Grand Junction Colorado. A good example of taking the default templates, removing the unnecessary bits and adding your own branding.
ECU
I love to see examples of customised WebDrawer and I have seen a couple recently, the first of which is a simple and effective policy portal from Edit Cowan University.
WebDrawer - Hide And/Or button in search form
When you create a search form you may choose to only give it one field, in which case the And/Or button is redundant (see below).
Create a new Record from WebDrawer
There are many samples in the ServiceAPI documentation for creating forms to create a new Record, you can adapt any of these to work in WebDrawer if you want to add a record creation form in your WebDrawer instance. Here is a very simple first form...
Steps
1. Create file
In the WebDrawer install folder create a new folder, give it any name (I called mine 'My'). Inside that folder create a text file (with a CSHTML extension) with any name (I called mine 'Page.cshtml').
2. Create a Form
Paste the code below into your new file:
@{ ViewBag.Title = "Create and Upload a Document";
WebDrawer search form lookup items
As you can see in the image below the lookup items displayed for an Additional Field in a search form are not sorted by name. This is not a problem when there are only four items but is when there are many. The good news is that, with a little bit of code, we can fix this.
Displaying Notes in WebDrawer
Someone just pointed out that Record Notes do not display in the WebDrawer details page. We hope to fix that in an upcoming 9.x release but if you want to patch it manually here is how.
First
Before you fix WebDrawer to allow it to display Notes make sure you have added Notes to the routeDefaults, for example:
<add name="Record" model="RecordFind" template="WDRecordDetails" properties="NameString,RecordIsElectronic,enabledcommandids,RecordRelatedRecs,RecordIsContainer,RecordIsInPartSeries,RecordRecordType,RecordPrimaryContact,Notes" propertySets="Identification,Locations,Electronic,Action,ChildLists,ChildDetails,Parts,Archiving,Container,Other" />
The Update
Edit string resources
Add the label 'Notes' to the string resources by editing App_GlobalResources\lang.resx and adding the followg XML before the end tag </root>.
<data name="field_groups_notes" xml:space="preserve"> <value>Notes</value> </data>
Update detailsView.cshtml
Follow these steps to update the file Views\Shared\detailsView.cshtml.
Add Notes variable
Add this below the line that reads 'string jr_other = 'jr_other'
string notes = "notes";
Add field group
Inside the initialisation for the fieldGroups array add this line:
new { Caption = Translations.lang.field_groups_notes, Id = notes, forTrimType = BaseObjectTypes.Record},
Add property category
After the line that reads 'var propertyCategories = new Dictionary<PropertyIds, string>();' add this:
propertyCategories.Add(PropertyIds.RecordNotes, notes);
Done
Once you have done the above Record Notes should display in a Notes
Including a Location Type field in WebDrawer
The problem
To include an Additional Field in either the search results or details page in WebDrawer all you need to do is add the field 'Search Clause Id' to the appropriate routeDefault in hptrim.config. Unfortunately, by default, Additional Fields of type Location will display the Uri, rather than the name.
Recap - hptrim.config settings
Below you can see the two route defaults with two Additional Fields included (PoliceOfficer and DateOfIssue)
<add name="Record" model="Records" template="WDRecordList" propertyValue="Both" properties="RecordRecordType,RecordExtension,RecordTitle,RecordNumber,RecordIsElectronic,PoliceOfficer,DateOfIssue" />
WebDrawer date range search picker
By default we keep WebDrawer as simple as possible. Here is show how to add a 3rd party date range picker to date search fields. For the full source code go to our samples repository.
WebDrawer (and Web Client) auto filter search
While all users may configure default filters and have ACLs restricting access to certain records it can be desirable to configure an instance of WebDrawer to only return a subset of Records. Maybe this is the planning portal and you only want it to return planning permission related Records?
Filter WebDrawer
The hptrim.config file contains a section called routeDefaults, there are two of these elements you will need to modify. The first has the model 'Records', add a filter attribute as seen below. This example filters out all .doc files.
<add name="Record" model="Records" template="WDRecordList" filter="not extension:doc" properties="RecordRecordType,RecordExtension,RecordTitle,RecordNumber,RecordIsElectronic" />
If you are using custom search forms you will also need to filter the FormSearch route.
<add name="FormSearch" model="FormSearch" filter="not extension:doc" properties="RecordRecordType,RecordExtension,RecordTitle,RecordNumber,RecordIsElectronic" />
Filter Web Client
The Web Client may be similarly filtered, edit hptrimServiceAPI.config and edit the Records route, as seen here:
<add name="Record" model="Records" filter="not extension:doc" sortBy="recRegisteredOn- unkUri" includePropertyDefs="true" />
Saved Searches
A slightly more elegant twist on the above approach is to not use the actual search string but to use a saved search. The example below filters using the Saved Search named 'NotDocuments'.
<add name="Record" model="Records" template="WDRecordList" filter="saved:NotDocuments" properties="RecordRecordType,RecordExtension,RecordTitle,RecordNumber,RecordIsElectronic" />
Be Careful
It is best to not copy the XML snippets directly to your config file as the other attributes may be different in your version. Just add the filter attribute.
Use 'local' when running on the Workgroup Server
One tip that is not as well known as it should be is the use of the reserved work 'local' to refer to a local Workgroup Server. Any time a .Net SDK application is talking to a Workgroup Server on the same machine you should use 'local' for the Workgroup Server name, rather than using the machine name. Why is this? Because 'local' will use a named pipes connection to the Workgroup Server rather than going through the network layer.
Web Client / ServiceAPI Users read this!
Of course the Web Client, ServiceaAPI and WebDrawer are .Net SDK applications, so of you are connecting to a WorkGroup Server on the same machine use 'local'. Note the MSI will probably not suggest this to you so you wil have to edit the hptrim.config manually. Make it look something like this:
<workgroupServer port="1137" workPath="C:\HP Records Manager\ServiceAPIWorkpath" name="local" alternateName="MY_ALTERNATE_WGS" alternatePort="1137" />
Hello, World!
.Net SDK Developers
If you are writing an application to run on the same machine as the Workgroup Server do something like this:
using (Database database = new Database()) { database.Id = "J1"; database.WorkgroupServerName = "local"; database.Connect();
WebDrawer - excluding records
WebDrawer (and the ServiceAPI) allow you to exclude Records (and other object types) from search results. The trick is to find (or add) a routeDefault in hptrim.config. In WebDrawer the record search already has defaults set in hptrim.config so you simply need to go to the routeDefaults elements, find the ADD element matching the one below and add the 'q' attribute.
In the samples below the 'q' parameter will be added to every Record search.
In this example I only want to include electronic documents.
<add name="Record" model="Records" template="WDRecordList" q="electronic" properties="RecordRecordType,RecordExtension,RecordTitle,RecordNumber,RecordIsElectronic" />
In this sample I only want non-electronic documents.
<add name="Record" model="Records" template="WDRecordList" q="not electronic" properties="RecordRecordType,RecordExtension,RecordTitle,RecordNumber,RecordIsElectronic" />
NOTE: The 'model' in the routeDefault above indicates that the elelement applies to record search results. A model of 'Locations' would apply to location search results.
More help: Route defaults are discussed in the online help shipped with the ServiceAPI under /help/routing.
WebDrawer sorting
Intro
Chatting with one of our customers I mentioned that implementing sorting in WebDrawer would be pretty simple to implement, probably time to demonstrate that. Life has been fairly full lately coming up to the pointy end of a release cycle at work and also at home with the imminent arrival of son number 3. Anyway, took a break from scheduled work this morning...
The scenario
By default the only sort-able property in the record search results is the record number. You can, however, easily add other properties via the route defaults in hptrim.config, this is documented in the ServiceAPI help and in a number of previous posts on this site. If you have sortable columns in the search results this code will allow you to convert them to links to sort your search results.
The Code (step 1)
In 83 you will be editing resultsList.cshtml, in prior versions edit searchResults.cshtml.
Add this code to the top, immediately below any 'using' statements.
@functions {
Browse Records by User Label
Browsing by Classifications in RM can be a helpful way to get at Records, how about if the Classifications are not familiar to you? Maybe the Records Managers have a different idea of how Records should be organised than than people in particular departments? In this video explore a sample label browser in WebDrawer.
There is one catch...
Making a group location the owner of a user label requires just the right level of access. You need both to have the 'Modify Internal Locations' permission and also to be a member of the group. The challenge is that, by default, only Records Managers and up have 'Modify Internal Locations'. So to use group labels as an alternate navigation method you will either need a Records Manager to join groups to create labels for them, or have users in various groups who have the required permission. I am hoping for a more flexible model in an upcoming release.
WebDrawer - go to end of search results
Background
I had an enhancement request for the WebDrawer search results to include links in the paging to allow the user to go to the first or last page. I thought I might as well post the code for earlier versions for those who may want to use it without waiting for the next major release.
The code
Simply replace the file Views\Shared\searchResults.cshtml with the fixed version for either 82 or 83.
Manual Merge
If you have modified searchResults.cshtml you might prefer to merge my changes into your CSHTML. Look for these changes:
Reduce number of items in page list from 7 to 4:
int pagingLength = 4;
Display a got to start link at the beginning:
if ((progressiveCount - 1) > Model.Results.Count) { queryString["pageSize"] = pageSize.ToString(); queryString["start"] = "1"; <li> <a href="?@queryString.ToFormUrlEncoded()">«</a> </li> queryString["start"] = (progressiveCount - (pageSize * 2)).ToString(); <li> <a href="?@queryString.ToFormUrlEncoded()"><</a> </li>
Portland Oregon
Brian from The City of Portland showed me around their public WebDrawer the other day, if you are interested in customising WebDrawer this might give you some ideas.
WebDrawer custom search results page
If you create one of more Search Form objects in RM these will be incorporated into the WebDrawer search page, as a drop down list of custom search pages. It is possible to customise the properties that are displayed in the search results for these pages, the trick is that different versions of WebDrawer require different customisations. This video details customising in 82 and earlier and also in 83 and later.
Javascript multi file upload
Overview
There are a number of ways to do a multi file upload, this is a simple one that has these features:
Tutorial
The setup
In hptrim.config:
<add name="Record" model="Record" />
The code
Copy this file to your WebDrawer root file (or a subfolder if you wish).
Favourites in anonymous WebDrawer
Overview
A common usage of WebDrawer is to allow anonymous access to part or all of an RM dataset. How then to allow a user mark records as favourites? One way is to store a list of record uris in the browser local storage.
This sample injects a star icon into the search results and allows you to display and print a list of all your favourite records.
Video
Code
The code is here, if you are using RM 83 use this code instead.
Potential Issues
URL Limits
IIS imposes limits on URL length, so if a user selects lots of records (I tested up to 70 without problem) they might get an error when loading the starred view. This is fixed in the 83 sample by using a post rather than a get to open the starred view.
Locations
I have not explicitly excluded Locations, although they are excluded almost by accident due to the fact that Location search results only have one column. If you display more than one column in tour Location search results you will see a star and this could confuse everything. Some refinement of the code will be required.
New in 83 - use IDOL from the WebDrawer quick search
If you choose you may do an IDOL search from the WebDrawer quick search, rather than doing a simple search. This will do a broader search (including content as well as other metadata) and will also allow you to use IDOL syntax of you want.
ADFS and the trailing slash
The problem
Those who have experimented with ADFS authentication for the web client (or Web Drawer) may have noticed that if the trailing slash is not included in the URL then the authentication fails.
The solution
The solution is to include a trailing slash.
The details
What is actually going on here is not, technically a bug, but a feature. The slashes in the URL separate out the realms. So when the trailing slash is ommitted ADFS considers that rather than authenticating with the HPRMWebClient realm you are actually opening the page HPRMWebClient in the www.myserver.com realm.
A 'better' solution?
I have experimented with this a fair bit, one solution would be for us to re-design the Web Client so that, for example, the base URL was /HPRMWebClient/home. This may be a solution for the future but what about the present. IIS URL re-write had some promise but from what I can see the ADFS authentication module kicks in before the rewrite.
So...
This post on stackoverflow provided a handy shortcut to a solution, override the authentication module to append the trailing slash if it is missing. I have added the code, or you can download the DLL. To use this:
<modules> <add name="FixedWSFederationAuthenticationModule" type="FixedWSFederationAuthenticationModule.FixedWSFederationAuthenticationModule, FixedWSFederationAuthenticationModule" preCondition="managedHandler" /> <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" /> </modules>
An update on the Web Client and Web Client classic
If you deal with the trailing slash using the technique above in both the web client and web client classic then you will have one more thing to do. The web client enables linking to the web client classic, if the base address does not include the trailing slash this will fail. To fix this:
For example:
<setup databaseId="T1" webClientClassicUrl="/HPRMWebClientClassic/" searchAhead="false" advancedSearch="false" workpath="C:\HP Records Manager\ServiceAPIWorkpath\Uploads"/>
Thumbnail view (WebDrawer)
In which I have a look at the new thumbnail view in WebDrawer.
New in 83 - PDF and TIF viewing in WebDrawer
New in RM 83
So starts my series on new features in RM 83. As per usual this is my opinion not an officially sponsored list of 83 features. As my opinion the choices lean well toward the web side of the playground. Come back every day or two (or subscribe to the RSS feed) for more.
PDF / TIF Viewing
This video show-cases the new TIF and PDF viewer used within WebDrawer.
ADFS claim rule mapping
Map User-Principal-Name to Name
This is a for those of you who notice a gap in the current RM Web Client ADFS configuration documentation. The documentation for the RM native client specifies the Relying Party claim rule should have the LDAP attribute User-Principle-Name mapped to the outgoing claim UPN. The Web Client documentation contains a simple custom rule but does not specify exactly which claims are are required. Unlike the native client the User-Principal-Name must be mapped to the outgoing claim Name, like this:
Thumbnails
Intro
This post pulls together techniques discussed in previous posts to examine how to auto generate thumbnails for images, store them as rendtitions, expose them via the ServiceAPI and display them in WebDrawer.
Warning: ServiceAPI plugin feature featured in this post requires HPRM 8.1.1 or later.
The talking
Links
Code available here:
Related Posts
Some related posts to get started:
WebDrawer custom_scripts
I am off to IGF Melbourne today so just a short post. The other day I introduced custom_head, which allows us to inject code into the WebDrawer HTML head element. I omitted to mention that there is also custom_scripts.cshtml. This allows you to inject code after the WebDrawer scripts at the bottom of the page. The top of the page is typically the place to load styles and the bottom the place to load scripts.
Remove page numbers to make WebDrawer faster
Intro
I have previously discussed ExcludeCount and search result page numbers and how we can improve WebDrawer search performance, especially for large databases. In HPRM 82 this is all much simpler.
How to
WebDrawer 82 will adjust its paging model to use back and next, rather than numbered pages, if the search routes are set to show results only. To do this you need to add resultsOnly="true" to the two record search routes in hptrim.config. For example:
<add name="FormSearch" model="FormSearch" resultsOnly="true"/>
WebDrawer 82 - Menu at top
Background
The WebDrawer HTML is structured in a way that allows you to re-organise the page simply by applying some CSS. In a previous post I looked at the custom_head.cshtml file which allows you to inject code into the head of the WebDrawer HTML pages. In this post I demonstrate a technique to display the WebDrawer menu at the top of each page, rather than at the left.
Show me
The code
This is the code used in the video above
custom_head.cshtml
<link href="~/css/hide_menu.css" media="(min-width: 480px)" type="text/css" rel="stylesheet" /> <link href="~/css/top_menu.css" media="(min-width: 480px)" type="text/css" rel="stylesheet" />
top_menu.css
#quick-search-forms > form:first-child + hr, #quick-search-forms > form:first-child + hr + form, ul.left-menu > li:first-child + li, ul.left-menu > li:first-child + li + li { display:none; }
Fun with 82 - Low impact CSS WebDrawer customisation
Custom_Head
In HPRM 82 we introduce a simple new feature to allow you to inject your own code into the head portion of every WebDrawer page without changing any of the core templates. This is done simply by adding the file custom_head.cshtml anywhere below your Views folder.
Watch it happen
The Code
This is the CSS I use to hide the Location quick search.
#quick-search-forms > form:first-child + hr, #quick-search-forms > form:first-child + hr + form { display:none; }
Refine the fields in the WebDrawer search form
Refining the list of fields
The standard search form in WebDrawer contains 3 lists each containing a list of search clauses and additional fields. Maybe you only want a selection of your additional fields included in these lists? This video shows one way to achieve this.
In short
In the video above I:
Refining Search Clauses
Search clauses are managed in the same CSHTML file (help\WDSearchClauseDefJS.cshtml). To refine the list of search clauses remove the IF clause from within the foreach and add a WHERE on the foreach.
bool start = true; foreach (SearchClauseDef searchClause in searchClauses.Where(sc => !new SearchClauseIds[] { SearchClauseIds.RecordTitle,SearchClauseIds.RecordNumber }.Contains(sc.Id))) { if (!start) {<text>,</text>} <text>{</text><text>InternalName: "</text>@searchClause.InternalName<text>",</text> <text>Name: "</text>@searchClause.Name<text>",</text> <text>MethodGroup: "</text>@searchClause.MethodGroup<text>",</text> <text>ObjectTypeParameter: "</text>@searchClause.ObjectTypeParameter<text>",</text> <text>ParameterFormat: "</text>@searchClause.ParameterFormat<text>",</text> <text>CanSort: </text>@searchClause.CanSort.ToJson()<text>,</text> <text>Caption: "</text>@searchClause.Caption.AsRaw()<text>"</text> <text>}</text> start = false; }
Note for version 9.0 and later
In 9.0 the file WDSearchClauseDefJS.cshtml was moved to the 'Help' folder. Unfortunately it is also installed in the 'Views' folder. Ignore the file in the 'Views' folder and modify the one in the 'Help' folder.
Fun with 82 - Tracing errors
In HPRM 82 we have a new feature that allows you to write a trace of the request that caused an error. This feature should only be enabled if you are getting errors, for two reasons:
Fun with 82 - WebDrawer, search forms and IDOL
Fun with HPRM 82
Having spent quite a few hours reviewing, testing and playing with HPRM 82 I thought I would share some of my favourite things. These are mostly technical things so you are unlikely to find me expounding on some great new records management feature, talk to support or sales for that. This is the first in the series.
Searching in WebDrawer
A combination of some new features in HPRM 82 allow you to add something a little bit like a Google search to WebDrawer, all without writing any code.
Thumbnail Views (a response for Brian)
A comment
Someone called Brian posted a comment on one of my posts a month ago, unfortunately I never noticed this comment. I find the best place for actual conversations to be the various HP RM Discussion boards, I monitor them daily but rarely look at comments on this blog. Just in case Brian is reading I will answer his question here.
The question
Do you think it would be possible, and effective, to leverage the preview capability to display a thumbnail grid of search results?
My answer
I have thought about this on a number of occasions and my conclusion is no. The preview is slow and does not have a facility to generate a preview of a single page. I do not see it as a solution for a thumbnails view.
Another answer
If what you are interested in is thumbnails of images (PNG, GIF, JPG, etc but not other document types) and you are happy to do your search in WebDrawer (not the Web Client) then there is a potential solution. I have previously supplied a sample EventProcessor add-in to auto create a thumbnail rendition of each image document as it is created/modified. It is quite simple then to have a custom search view in WebDrawer to display this thumbnail rendition.
So...
I will come back and check this post in a week or so, if you really want the samples I describe above add a comment. Or put in a request on one of the discussion boards. I cannot make any promises as compiling samples has to fit in around other work but I will do my best.
Update - display native PDF in WebDrawer
Second Chances
Some time ago I wrote this post detailing how to edit the preview page in WebDrawer, turns out I did not put enough time into testing my solution. In Chrome the PDF preview does not always resize correctly and in IE the Links/Actions menu displays under the PDF preview. This new approach should work better.
Instructions
All modifications are made in WDRecordPreview.cshtml, ensure you have a backup before commencing. The full page is available here and can simply be copied into your Views folder.
The details
Page settings
At the top of the page the following variables hide the left menu (to allow more screen real estate for the preview) and set a browser detection variable for later use.
ViewBag.HideMenu = true; bool isIE = Request.UserAgent.Contains("MSIE") || Request.UserAgent.Contains("Trident");
Body
The body of the page embeds an iframe to display the native PDF or the HTML preview, depending on document type.
<iframe id="preview-frame" src="~/@("Record/" + @Model.Results[0].Uri + "/file/" + (record.Extension == "PDF" ? "document?inline" : "html"))" width='100%' height='100%' style="border:none; margin-left:10px; visibility:hidden;"> </iframe>
HTML head
The customhead section injects some CSS to tidy up the page layout.
@section customhead { <style> h2 { white-space: normal; }
Google authentication
Introductory thoughts
The ServiceAPI (upon which WebDrawer and the Web Client are based) has a pluggable authentication module. This allows for the support of a variety of identity providers (IdP). For those considering Google as IdP this is the first step in that direction.
Breaking News!
While doing some testing in 8.2 I found a regression that means plugins will not work in the 82 Web Client (WebDrawer and ServiceAPI or OK). This should be fixed in the first 82 patch. So be warned, 811 should be fine but not 82, skip straight to 8.2 Patch 1 if you want to use Google authentication in the Web Client.
Looks simple to me
In which I demonstrate the process to:
Secrets
You may note I do not make an effort to obscure the various bits of secret information in my Google Developer console. Don't worry, I have re-set the sensitive bits.
Files
Available to download are the:
HPRM 82
The code remains the same but in HPRM 82 some interfaces have changed, use these for 82 instances:
Source of the source
For those interested the source for the above plugin all comes from ServiceStack framework (V3), look in the ServiceStack.Authentication.OAuth2 project and ServiceStack.ServiceInterface/Auth.
Steps
The steps I follow in the video above include:
Create a Google Client Id
Go to console.developers.google.com/project to create a project then create a client id for a web application
Edit the config files
In the WebDrawer web.config I add this in the appSettings element, making sure to use the correct WebDrawer URL of course:
<add key="oauth.RedirectUrl" value="https://811x.hprm.info/GWebDrawer/"/> <add key="oauth.CallbackUrl" value="https://811x.hprm.info/GWebDrawer/auth/{0}"/> <add key="oauth.GoogleOAuth.ConsumerKey" value="YOUR_CLIENT_ID"/> <add key="oauth.GoogleOAuth.ConsumerSecret" value="YOUR_SECRET"/>
In hptrim.config I add this:
<pluginAssemblies> <add name="GoogleAuthPlugin" /> </pluginAssemblies>
Copy the DLLs
Lastly I copy the DLLs into the WebDrawer\bin folder.
For the Web Client only
The Web Client embeds the ServiceAPI slightly differently to WebDrawer, for this reason you will need to add a login element to appSettings (in web.config), just like this one.
<add key="oauth.login" value="~/HPRMServiceAPI/auth/GoogleOAuth" />
Simple WebDrawer ADFS
Background
Setting up a connection between WebDrawer and one of our lab ADFS servers is something I only do occasionally. Now I am documenting it for the next time I do it. This is a very simple setup so I do not promise that it will work the same in your environment.
Watch me
The config
Here I detail the various changes made to the WebDrawer web.config in the previous video.
configSections
We identify the two sections we will be configuring later in web.config in the configSections element (usually at the top of web.config)
<configSections> ... <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" /> <section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" /> </configSections>
appSettings
In appSettings we provide the location of the ADFS metadata to allow WebDrawer to interrogate this later.
<appSettings file="user.config"> ... <add key="ida:FederationMetadataLocation" value="https://adfs1.testteam.local/FederationMetadata/2007-06/FederationMetadata.xml" /> </appSettings>
authorization
Here we deny access to anonymous users (thus requiring authentication and disable standard windows authentication.
<authorization> <deny users="?" /> </authorization> <authentication mode="None" />
system.IdentityModel
I usually add the system.IdentityModel section just before system.webServer, although I believe it may go anywhere.
<system.identityModel> <identityConfiguration> <audienceUris> <add value="[Your WebDrawer URL]" /> </audienceUris> <securityTokenHandlers> <add type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> </securityTokenHandlers> <certificateValidation certificateValidationMode="None" /> <issuerNameRegistry type="System.IdentityModel.Tokens.ValidatingIssuerNameRegistry, System.IdentityModel.Tokens.ValidatingIssuerNameRegistry"> <authority name="http://[Your ADFS Server]/adfs/services/trust"> <keys> </keys> <validIssuers> <add name="http://[Your ADFS Server]/adfs/services/trust" /> </validIssuers> </authority> </issuerNameRegistry> </identityConfiguration> </system.identityModel>
modules
The modules go inside system.webServer, I usually place them just after the handlers.
<modules> <add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" /> <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" /> </modules>
WIFHandler
I add this location to avoid a validation error triggered by the re-direct back from the ADFS login page.
<location path="WIFHandler"> <system.web> <httpRuntime requestValidationMode="2.0" /> </system.web> </location>
system.identityModel.services
<system.identityModel.services> <federationConfiguration> <cookieHandler requireSsl="true" /> <wsFederation passiveRedirectEnabled="true" issuer="https://[Your ADFS Server]/adfs/ls" realm="[Your Realm]" reply="https://[Your WebDrawer URL/WIFHandler" requireHttps="true" /> </federationConfiguration> </system.identityModel.services>
WebDrawer and Cache-Control
Background
I occasionally get asked about setting the Cache-Control headers on WebDrawer responses. By default WebDrawer sets the Cache-Control header to 'private', which means that the only place the content should be cached is in the end user's browser, not on any intermediate servers.
No-Cache
It seems that there are various certification tools that require cache-control headers other than 'private' be sent, for example No-Cache or No-Store. While it is not possible to stop WebDrawer from sending private we can send these other directives in addition.
How To
Adding the customHeaders element within system.WebServer/httpProtocol (in web.config) will allow you to add options to cache-control. The example below will result in the Cache-Control response header being set to 'private,No-Cache'. Given that 'No-Cache' is more restrictive than 'private' this is equivalent to setting the Cache-Control header to 'No-Cache'.
<system.webServer> <httpProtocol> <customHeaders> <clear /> <add name="Cache-Control" value="No-Cache"/> </customHeaders> </httpProtocol> ... </system.webServer>
What about things I do want to cache?
The setting above will set the Cache-Control header on all responses, including CSS, JavaScript and images, you may not want to do this. We can switch these off by adding something similar to the above in the location element for the paths in which these static files are found. Each of the paths 'scripts', 'css' and 'images' already has a location element in the WebDrawer web.config, we can modify each of these to look like the 'scripts' one below, this will remove any cache-control header set above.
<location path="scripts" inheritInChildApplications="false"> <system.web> <authorization> <allow users="*" /> </authorization> </system.web> <system.webServer> <httpProtocol> <customHeaders> <clear /> <remove name="Cache-Control" /> </customHeaders> </httpProtocol> </system.webServer> </location>
Does this matter?
The fact that non-static WebDrawer pages (e.g. any search, record details etc) do not contain cache validation headers (such as Last-Modified or ETag) means that it is very unlikely that they will be cached on intermediate servers (as discussed here), however you may still wish to explicitly forbid this. In addition, even though a browser is most likely to reload a dynamic page it is still likely to cache it on the user's local machine (in Google Chrome use chrome://cache to view cached pages). Security concerns may mean you wish to prevent pages being cached locally, in which case you would set the header 'No-Store'.
More Information
Here are some posts I have found useful in discussing Cache-Control:
Change the logo in WebDrawer
Background
The logo in the banner of WebDrawer is the HP logo and the text says 'HP Records Manager'. Maybe you want different branding? Changing this is quite simple.
The Razor
The logo is actually two images which are embedded using IMG elements and shown/hidden using CSS. This is done to allow us to show a different image when WebDrawer is re-sized down to mobile phone size. You can see this HTML in Views\Shared\_Layout.cshtml, it looks like this:
<div class="innertube"> <img id="banner-img-large" src="~/images/top-banner-logo.png" /> <img id="banner-img-small" src="~/images/top-banner-logo-title-SM.png" /> </div>
The process
I recommend you:
Upgrading
On WebDrawer upgrade don't forget to take a backup of _layout.cshtml (preferrably all files under the WebDrawer folder) so that you can re-apply any changes you made.
Location objects in Razor
The problem
Many record properties are represented by Location objects, for example the owner location. This post examines the requirements for displaying them in a razor template.
Location as additional field
Most additional fields are simple values, such as strings or numbers. The location type additional field is slightly more complex. To display properties of a location you need the correct settings in hptrim.config as well as the correct razor.
Config
Use the defaultProperties config element to specify the location properties you wish to return always, whether they are requested in a particular route or not. The example below will ensure that FormattedName and FullFormatted name are available. In fact that specifies that we want FormattedName and FullFormatted for every single location object.
<defaultProperties> <clear /> <add name="Location" properties="FormattedName,FullFormattedName" /> ... </defaultProperties>
Razor
Location objects when used in additional fields use the reference version of the Location object, LocationRef. This example demonstrates displaying the FormattedName.
LocationRef policeOfficer = trimObject.GetPropertyOrFieldValue("PoliceOfficer") as LocationRef; if (policeOfficer != null) { <h1>@policeOfficer.FormattedName</h1> }
Location as a property
Of course a location property will work in a similar fashion to the additional field. First make sure the properties you require are set in defaultProperties then insert them as show here:
<h1>@trimObject.OwnerLocation.FormattedName</h1>
A pre-requisite
The above all assumes that you have pre-requested the additional field or property via the 'properties' request parameter. For razor applications this is often done in the route, for example:
<add name="MyRoute" model="RecordFind" template="MyTemplate" properties="NameString,RecordOwnerLocation,PoliceOfficer" />
Simplified paging
Why?
In a previous post I used the ExcludeCount option to improve search performance on large datasets, one cost of this is that it breaks the WebDrawer paging widget. This post looks at re-factoring that widget.
Lets do it
The code
This is the code that I use to replace the standard paging widget in the above video. Tp note in this code is that all the logic is based around the starting position of the search (found in the 'start' query parameter) and the HasMoreItems (found in the response from the search). If HasMoreItems == true then the next button is enabled, if start > 1 then the previous button is enabled. The layout (e.g. pagination CSS) is all handled by twitter bootstrap.
{ int start = 0; Int32.TryParse(HttpContext.Current.Request.QueryString["start"], out start);
ExcludeCount and performance
Background
I am doing some load testing on an HPRM dataset containing 16 million records. Response times from our web clients were not great, depending on which search query I used of course. I really wanted to improve this.
ExcludeCount
A ServiceAPI parameter not often mentioned is ExcludeCount (otherwise known as resultsOnly). This has the effect of committing the SearchTitle and TotalResults properties from the response. Total results in particular can degrade performance on a large dataset as it calculates the total number of records matched by the query.
ServiceAPI Example
http://[myserver]/HPRMServiceAPI/Record?q=uri:2013778,1208882,1380177,1542060,1378119&format=json&Excludecount=true
WebDrawer Example
Setting ExcludeCount as the default in WebDrawer will result in the search results page no longer displaying the search title. To do this add the 'resultsOnly' attribute in the Record route defaults, as seen below. Ensure you set this on the route named Record with the model named Records.
<add name="Record" model="Records" template="WDRecordList" properties="RecordRecordType,RecordExtension,RecordTitle,RecordNumber" pageSize="15" resultsOnly="true"/>
The outcome
On my large dataset ExcludeCount more than halved the response time on many requests.
The Cost
In WebDrawer the cost is that the page number links no longer work. This post examines a solution to the broken paging problem.
ServiceAPI Plugin Example
Why?
The 811 ServiceAPI documentation contains a very simple example of a ServiceAPI plugin. This example takes it one step forward and demonstrates hows to participate in the ServiceAPI pipeline to get a database connection for the current user.
Your Service
The service below inherits from the class TrimServiceBase which provides you with the property called 'Database'. This is the best way to get a database connection for the current user as it uses the same mechanism to acquire (and release) a connection as used by every other service in the ServiceAPI.
namespace ServiceAPIPlugin {
WebDrawer preview page Links / Actions button
The problem
In some versions of WebDrawer you might have this experience:
The cause
As is often the way a fix to one thing led to a breakage somewhere else. Some documents (such as email items) contain links to attached files. These links were breaking. To 'fix' this the following code was added to WDRecordPreview.cshtml.
@section customhead { @* This is so links in VMBX files do not link to /RecordHTML*@ <base href="~/Record/" /> }
Unfortunately the 'base' element broke the link in the Links / Actions menu.
Resolution
Later releases of WebDrawer fix this issue in one of two ways:
Fixing the HTML
WDRecordPreview.cshtml contains this line of code:
@record.Html.Replace("src=\"", "src=\"../Record/")
Replace it with this:
@record.Html.Replace("src=\"", "src=\"../Record/").Replace(string.Format("href=\"{0}", record.Uri), string.Format("href=\"../Record/{0}", record.Uri)).AsRaw()
Also remove the customhead section containing the base element.
Customise the WebDrawer HTML preview page
Update!
The solution I provide to display the native PDF in the preview page is flawed. A better solution is available here.
The problem
What if the HTML preview of a document is not displaying as you think it should? Depending on the cause this may be out of our hands, however some problems can be avoided even if they cannot be completely resolved.
An Example
This particular post was spurred by one customer who was experiencing strange black boxes overlaying the HTML rendition of some PDFs. It turns our that this was a problem in the rendered HTML being exaggerated by the way the HTML was displayed in WebDrawer.
A Fix
In this video I spend the first half improving the viewing experience by displaying the HTML preview within an iframe, at about the 13 minute mark I look at how you might simply embed the native PDF in the preview page rather than rely on the HTML rendition of the PDF.
Sorry about the audio levels in this, you may have to turn your volume up to full and listen carefully...
Scripts
This is the scripts section as seen in the above video. It sets the iframe:
@section scripts { <script> var resizePreview = function () { $('#preview-frame').show(); $('#preview-frame').css("visibility", "visible");
Fetch a list of record revisions and display them in WebDrawer
Intro
This video goes start to finish fetching a list of record revisions and creating a list in WebDrawer. Along the way I show where in the ServiceAPI help I found all the information I needed to achieve this.
Sample Code
In this video I call the ServiceAPI with a URL like the one below to fetch a list of record revisions.
http://localhost/HPRMServiceAPI/Record/REC_15?properties=ChildRevisions
I write this Razor code:
<ul> @foreach (RecordRevision revision in this.Model.Results[0].ChildRevisions) { <li><a href="~/Record/@this.Model.Results[0].Uri/RecordRevision/@revision.Uri">@revision.Description</a></li> } </ul>
Modify a routeDefault to look like this:
<add name="Record" model="RecordFind" template="MyRecordDetails" properties="NameString,RecordIsElectronic,enabledcommandids,RecordRelatedRecs,RecordIsContainer,RecordIsInPartSeries,RecordRecordType,RecordPrimaryContact,RecordExtension,ChildRevisions" propertySets="Identification,ChildDetails"/>
Add this custom property Set:
<add name="RecordRevisionChildDetails" propertySets="All" properties="RecordRevisionDescription,NameString"/>
Notes
The custom property set I added above has redundant information. I specify the propertySet 'all' which means that I do not need to specify the individual properties at all.
Also, you might notice a record revision property in the help that has a numeric ID (in fact I comment on this). This was the result of me working with a partially re-built instance of HPRM.
And lastly, if you see some odd looking things in WebDrawer (such as the 'Create' button) just ignore them. They are a part of some other sample code I am currently part way through building.
Download a document directly from a list in WebDrawer
Show Me
The default WebDrawer behaviour requires the user to open the Record details page before they can preview or download the electronic document. This allows for a simple UI design that is easily made responsive to different screen sizes. If you would prefer to be able to access the document without first opening the details page this post details some template customisations that will get you there.
Source
Here is the source as demonstrated in the video. You may have noted that I overlooked something, when I added the RecordIsElectronic property to the 'properties' in hptrim.config this had the effect of also adding it to my view. In MySearchResults.cshtml I have added some code to exclude that property. It looks something lke this...
The exclusion list
string[] columnsToIgnore = new string[] { "RecordExtension", "RecordIsElectronic" };
The if statement
if (!columnsToIgnore.Any(id => id == pfdef.Id)) { ...
Simplification
If the code in MySearchResults.cshtml is a little too complex for your liking, for example we loop through Model.PropertiesAndFields[this.TrimHelper.TrimType] to get the list of properties to display based on the properties specified in hptrim.config. You might choose to replace this block with something more like the simple table in this example.
Access properties of object properties in a WebDrawer template
Intro
Some things in the Razor templates of the ServiceAPI and WebDrawer are not all that obvious, one is how to display the name of the record author. In an earlier post I created a custom search form, this video shows a custom search results template and how to display the name (or any other property) of the record author.
A Future Release...
In a future release accessing properties of property objects should become a little more straightforward.
Optimising the performance of the WebDrawer details page
Intro
WebDrawer is designed to show more rather than less of what a user may wish to see. Being a simple HTML application it does not aim to do anything as sophisticated as on demand loading of data. The cost of this approach is that a Record details page contains a lot of information and can be quite slow to load. If your users typically do not need to see all of this information then you can remove it and improve the load time of this page significantly.
Try It Out
This video demonstrates removing the bulk of the properties displayed on a Record details page and improves load of the page from around 4 seconds to just over 1 second. I hope you enjoy the soothing sounds of children playing in the background on this video.
Make it more maintainable
While the above will improve the performance of the Record details page it will make it more difficult to upgrade WebDrawer. One way to improve this situation is to not edit the Razor files that ship with WebDrawer but to copy them and use your own. This way your changes will not be overwritten by an upgrade. In the picture below I have created a folder called 'MyViews' within which I have made a copy of WDRecordDetails.cshtml (renamed MyRecordDetails.cshtml) and a shared folder in which I have a copy of detailsView.cshtml (renamed MyDetailsView.cshtml). To use the MyRecordDetails View I had to specify it in the 'template' property of the routeDefault element in hptrim.config.
A simple HTML search form in WebDrawer or ServiceAPI
Background
WebDrawer contains a fairly generic search form that allows you to choose up to three search clauses. It si possible to refine which search clauses are displayed in those fields but you may want to create your own entirely custom form. This post talks about the basics of doing that.
File Location
In the video I place my template in the Views folder. It is probably better to place customer templates in a folder separate to the standard WebDrawer files. This folder must be a child of Views as seen below.