Buy me a coffee »

Filtering files by name in CKEditor’s file browser dialog in Liferay

After I reach the point where I think that my topics covering Liferay are finally done I encounter another task at work which I should solve.

Now it is the filtering of file names in the CKEditor file browser. This goes hand in hand with my previous article where I introduced an ext-plugin which can be used to sort files by their modification date.

The current extension is for those users who do not want to use an ext plugin for sorting by file modification date but want to filter by name.

Describing the task more properly

So we have a folder with a lot of documents and we want to create downloads on different sites — sometimes using the same file we have uploaded previously.

Using the sort by date is not an option because installing an ext-plugin is defined as too risky by the project owner and customer so we need an alternative to make selecting files easier.

Sorting by name is the first step (the current hook already sorts by filename if no date is provided in the callback-result) but we want an option to filter by names.

For this we add a text input field to the CKEditor browser dialog and a button where the user can enter a text (part of a filename) and when clicking the button this filter is applied. When the user clears the input field and hits the button the filter is disabled.

As you can see this is a simple, easy going solution which does not have any AJAX calls — to make our life simpler. Naturally with some tinkering it could be made more dynamic but for now I let is stay as it is.

The solution

I have created a simple solution where I placed the search field into the top left corner, where you can select the document type to load from Liferay (Document or Page on my installation). Alternatively you can place the search field in any other display frame. I will show in this article another way where you include the search field below the file-display area.

Filtering the files

Here again I do some minor implementation because this functionality is only a workaround. To filter the result list based on the input string I extend the already known GetFoldersAndFilesCallBack function in the frmresourceslist.html file.

// Add the Files.
oNodes = fckXml.SelectNodes('Connector/Files/File');
var sortableNodes = {};
var ascending = true;
for (var j = 0; j < oNodes.length; j++) {
    var oNode = oNodes[j];
    var sFileName = oNode.attributes.getNamedItem('name').value;
    if(nameFilter && sFileName.toLowerCase().indexOf(nameFilter) < 0) {
        continue;
    }
...

Where the files are added to the file-browser display we simply filter by file name: if the pattern is not empty and the lowercase of the file does match the filter pattern we simply skip to add the file to the list.

To have this work I included the nameFilter variable at the beginning of the file’s JavaScript block and defined a function which sets this value and refreshes the frame:

function SetResourceNameFilter (nameFilter) {
    nameFilter = nameFilter.toLowerCase();
    Refresh();
}

var nameFilter = ""

The function converta the value of the parameter to lowerCase so we do not have to check it again later and refreshes the frame. When calling Refresh() the files are gathered again out of the database and the GetFoldersAndFilesCallBack is executed again.

This makes the execution of the filter a bit slow: every time you enter a term all contents are reloaded. With a dynamic solution this could be a big performance bottleneck.

Setting the filter

To set the filter we need an input filed to let the user enter the filter term and a button to submit this term to the filter function SetResourceNameFilter described above.

In my eyes there are three possible positions for the filter field:

Filter in the top-left corner

Filter field in the top-left corner

 

 

Filter above the file display

Filter field above the file display

 

Filter below the file display

Filter field below the file display

 

Naturally you can combine all these versions together but in this case you have to be aware of the changes in one field and delete the input from the other or update the other fields to contain the same value. However in my eyes three times the same input is a bit too much.

So let’s add the input field. As I told you in the introduction I will place the field with the button into the top-left corner.

The first step is to find the right HTML file and add it to the hook. The frame of the top-left corner is in the frmresourcetype.html file. The entry is quite simple:

<tr>
    <td nowrap>
        Filter by filename<br />
        <input id="filterText" type="text" size="10" / >
        <input type="button" value="Filter" onclick="SetFilterText(filterText.value)" / >
    </td>
</tr>

This table row goes into the table in the frmresourcetype.html file. You can decide if it is the first other the second row.

As you can see in the onclick function we call the SetFilterText function with the input of the text field:

function SetFilterText(filterText) {
    window.parent.parent.frames[3].SetResourceNameFilter( filterText ) ;
}

This script goes into the block of the same HTML file. Did you notice how I access the target frame? This is because of the structure of the site: there are two framesets, the first contains the left side (with the modified HTML) and the second contains the main part where the files are listed, the user can upload a new file and so on.

If you make and try the changes you will see that the frame stays with its fixed size which is quite ugly. To fix this, we need to import the browser.html file too and change the row count in the first frameset because this contains the top-left corner entry:

<frameset rows="100,*" framespacing="0">
	<frame src="frmresourcetype.html" scrolling="no" frameborder="0">
	<frame name="frmFolders" src="frmfolders.html" scrolling="auto" frameborder="1">
</frameset>

Alternative ways

As I promised I will show alternative ways where and how to include the filter.

The input filed stays the same and the filtering in frmresourceslist.html too. The only thing you have to do is move the input field and the JavaScript function to the right HTML file.

To add the filter button to the top, edit the frmactualfolder.html. However in this case you can slightly change the JavaScript function:

function SetFilterText(filterText) {
    window.parent.frames["frmResourcesList"].SetResourceNameFilter( filterText ) ;
}

This is because we are in the same parent frameset than the target and the internal frames have a name — and we can access them by their name. This makes the change more tolerant for future changes in the frames of the browser (reordering or maybe adding a new frame to the main part).

The same goes for the third variation where the filename filter is below the file list because it is in the same frameset too.

Conclusion and sources

As you could see above, this article contains a static solution for filtering names. The dynamic filtering requires a bit more work. Maybe is I have time I will take a look at how to filter the contents without reloading the site and make the submit button gone and filter as you type.

The sources are available with the complete CKEditor Hook from the previous article. I am thinking about adding it to the marketplace, however I have some issues with the process… Perhaps in the near future.

About the author

GHajba

Senior developer, consultant, author, mentor, apprentice. I love to share my knowledge and insights what I achieve through my daily work which is not trivial -- at least not for me.

Comments are closed