Search code examples
jquerydatesearchplonezpt

How to search content using a date range on Plone


Plone gives you the possibility of filtering search results using a date range. Normal date range is set up by using only the minimum date so the range is between today and some date on the past.

I need to add the option of setting a date range with starting and ending dates.

I was thinking on adding an extra option that, when selected, will show a couple of date pickers, but I don't know how to set the created.query:record:list:date variable to use this 2 values.

I have tried passing a list on the query variable but then I get:

Error Value: Invalid DateTime "[DateTime('2000/07/14 00:00:00 GMT-3'), DateTime('2014/07/14 00:00:00 GMT-3')]"

Has anybody else has found with a requirement like this before? How did you solved it? Should this be ported to the core?


Solution

  • I spent some time investigating this and I have a working solution.

    First we need to understand how Zope and ZPT work; when you name a form element with something like created.query:record:list:date you are telling Zope to do some parameter conversions:

    • record, converts the variable to a record attribute
    • list, converts the variable to a Python list
    • date, converts a string to a DateTime object

    For more information on this you can take a look at Advanced Zope Scripting.

    So, the parameter is then automatically converted to a dictionary record like this in Python:

    'created': {'query': [DateTime('2014/07/09 00:00:00 GMT-3')], 'range': 'min'}
    

    My mistake was assuming that I had to pass all list elements on one request variable.

    The code I added at the end of the form now looks like this:

    <input type="radio"
           id="query-date-range"
           name="created.query:record:list:date:ignore_empty"
           tal:attributes="value python:'';
                           checked python:checked=='' and 'checked' or '';"
           />
    <label for="query-date-range" i18n:translate="time_range">Time range</label>
    <input type="text"
           id="range-start"
           name="created.query:record:list:date:ignore_empty">
    <input type="text"
           id="range-end"
           name="created.query:record:list:date:ignore_empty">
    <script>
    var today = new Date();
    var yesterday = new Date();
    yesterday.setDate(yesterday.getDate() - 1);
    var start = yesterday.toLocaleDateString().slice(0, 10);
    var end = today.toLocaleDateString().slice(0, 10);
    $(document).ready(function() {
        $("#range-start").datepicker();
        $("#range-end").datepicker();
        $('#range-start').hide();
        $('#range-end').hide();
    });
    
    $('input:radio').change(function() {
        var option = $('input:radio:checked', '#searchform').val();
        if (option === '') {
            $('#range-start').val(start);
            $('#range-start').show();
            $('#range-end').val(end);
            $('#range-end').show();
            $('#range').val('min:max');
        }
        else {
            $('#range-start').val('');
            $('#range-start').hide();
            $('#range-end').val('');
            $('#range-end').hide();
            $('#range').val('min');
        };
    });
    </script>
    

    Take special attention to the following:

    • the start and end dates are input elements associated with a jQueryUI datepicker widget.

    • both input elements share the same name (created.query:record:list:date:ignore_empty) as the other and here is where the Zope magic takes place: the values of all input elements with the same name are converted on a unique list record like this:

    'created': {'query': [DateTime('2014/07/15 00:00:00 GMT-3'), DateTime('2014/07/16 00:00:00 GMT-3')], 'range': 'min:max'}
    
    • we set an additional parameter converter (ignore_empty), which just avoid adding to the list the elements that have no value set (I added this converter to the other input elements too).

    • when we click on the query-date-range element we have to initialize the date selectors; when we click on any of the other elements we need to clear these values to avoid adding them to the list.

    • we also change the value of the range element according to the query type as mentioned before by @sdupton.

    The JavaScript part of the code could be enhanced for sure; my skills are really limited.