diff --git a/data/System/MultiSearchPlugin.txt b/data/System/MultiSearchPlugin.txt index 15d16d9..5019beb 100644 --- a/data/System/MultiSearchPlugin.txt +++ b/data/System/MultiSearchPlugin.txt @@ -124,6 +124,7 @@ The plugin requires the CPAN library Time::ParseDate. Redhat/Centos users can ru ---++ Plugin Info | Change History: |   | +| 1.1 (18 Aug 2015) | Better handling when using month as relative time (same date in next month instead of fixed number of seconds).
More checks on input values to avoid infinite loops
Support of the delay parameter when using MULTISEARCH inside a nested SEARCH or FORMAT | | 1.0 (17 Aug 2015) | Initial release | | Dependencies: | %$DEPENDENCIES% | diff --git a/data/System/VarMULTISEARCH.txt b/data/System/VarMULTISEARCH.txt index 5452aaa..676789c 100644 --- a/data/System/VarMULTISEARCH.txt +++ b/data/System/VarMULTISEARCH.txt @@ -22,6 +22,7 @@ The =%MULTISEARCH%= variable is handled by the MultiSearchPlugin | =listformat#= | The format that is used for each found topic. These formatted lists are then joined together using the listseparator and output with the $list# (# - the search number) token inside the format parameter | '' | | =listseparator#= | The number # must also be used in a search# option. This is the format used for the $list# token used in the =format= string. Typically this is set to ', ' (comma space) to create a list separated by commas. | '' | | =indexfield#= | The name of the form field used for search number #. | '' | + | =delay="#"= | Number of times the MULTISEARCH will delay its own expansion by changing the first and last %-sign to $percnt and all double quotes (") to $quot. This is used when you use MULTISAVE inside the format parameter of a SEARCH or FORMAT macro and you do not want to expand MULTISEARCH until the outer SEARCH/FORMAT is completed. Set the delay to 1 if it is a single level SEARCH/FORMAT. If you have nested SEARCHes you can set delay to the level of nesting. | =0= | * Note1: The parameters formats, headers, and footers do not by default put a new line at the end. You must always specify this with a $n if you need a new line (you will need this in most cases) * Note2: Dates given to indexstart and indexend can be entered in two different formats diff --git a/lib/Foswiki/Plugins/MultiSearchPlugin.pm b/lib/Foswiki/Plugins/MultiSearchPlugin.pm index b8b4eee..d4e7d91 100644 --- a/lib/Foswiki/Plugins/MultiSearchPlugin.pm +++ b/lib/Foswiki/Plugins/MultiSearchPlugin.pm @@ -37,7 +37,7 @@ use Time::ParseDate (); # For relative dates # v1.2.1_001 -> v1.2.2 -> v1.2.2_001 -> v1.2.3 # 1.21_001 -> 1.22 -> 1.22_001 -> 1.23 # -our $VERSION = '1.00'; +our $VERSION = '1.1'; # $RELEASE is used in the "Find More Extensions" automation in configure. # It is a manually maintained string used to identify functionality steps. @@ -52,7 +52,7 @@ our $VERSION = '1.00'; # It is preferred to keep this compatible with $VERSION. At some future # date, Foswiki will deprecate RELEASE and use the VERSION string. # -our $RELEASE = '17 Aug 2015'; +our $RELEASE = '18 Aug 2015'; # One line description of the module our $SHORTDESCRIPTION = @@ -169,6 +169,26 @@ sub _convertStringToDate { return $date; } +# input value, step size, type +# If type is date the input is a day and the step a relative time +# The function returns undef if the relative time cannot be parsed +sub _incrementByInterval { + my ( $input, $step, $type ) = @_; + + return $input + $step unless ($type eq 'date'); + + my $result = Time::ParseDate::parsedate( $step, + NO_RELATIVE => 0, + DATE_REQUIRED => 1, + UK => 1, + NOW => $input, + PREFER_FUTURE => 1, + GMT => 1 + ); + return $result; + +} + # _MULTISEARCH # # $session= - a reference to the Foswiki session object @@ -231,6 +251,31 @@ sub _MULTISEARCH { my $indexEnd = $params->{indexend}; my $indexStep = $params->{indexstep} || "1"; + my $resultString = ''; + + # Delay means we escape with $percnt and $quot + # We cannot replace inside _RAW because that also replace quotes in values + my $delay = $params->{delay}; + if ( $delay && ( $delay =~ /\d+/ ) && $delay-- > 0 ) { + $resultString = "\$percntMULTISEARCH{"; + $resultString .= "\$quot$ignored\$quot "; + + foreach my $key ( keys %$params ) { + next if ($key eq '_RAW' || $key eq '_DEFAULT'); + + if ( $key eq 'delay' ) { + $resultString .= "delay=\$quot$delay\$quot "; + next; + } + + $resultString .= "$key=\$quot" . $params->{$key} . "\$quot "; + } + $resultString .= "}\$percnt"; + return $resultString + } + + + my @multiSearchStrings; my @listFormats; my @listSeparators; @@ -301,8 +346,6 @@ sub _MULTISEARCH { } - my $resultString = ''; - my @totalFound; for ( my $i = 1 ; $i <= $searchCounter ; $i++ ) { $totalFound[$i] = 0; @@ -349,7 +392,8 @@ sub _MULTISEARCH { # Convert a date to seconds from epoch. # Set the value to 0 if this fails $tempValue = Foswiki::Time::parseTime( $tempValue ) || 0 ; - } else { + } + else { # We dig any digits out of any strings present if the field is not a number # We remove none digits and none . + and -. # Not idiot proof but will normally work @@ -364,7 +408,8 @@ sub _MULTISEARCH { } for ( my $i = 1 ; $i <= $searchCounter ; $i++ ) { - @{$sortedIndexes[$i]} = sort { $a->{indexvalue} <=> $b->{indexvalue} } @{$sortedIndexes[$i]}; + @{$sortedIndexes[$i]} = sort { $a->{indexvalue} <=> $b->{indexvalue} } + @{$sortedIndexes[$i]}; } my $cnt = 0; @@ -373,23 +418,35 @@ sub _MULTISEARCH { # Let us calculate all time into epoch $indexStart = _convertStringToDate($indexStart, undef); $indexEnd = _convertStringToDate($indexEnd, undef); - # We get relative by passing epoch as reference date - $indexStep = _convertStringToDate($indexStep, 0); - } + + # Check that indexStep can be interpreted + return "Invalid indexstep value" unless defined _incrementByInterval( 0, $indexStep, $indexType ); + + # Limit intervals to max 1000 + if ( ( $indexEnd - $indexStart) / _incrementByInterval( 0, $indexStep, $indexType ) > 1000 ) { + return "Number of intervals should be less than 1000"; + } + } + else { + # Limit intervals to max 1000 in the none date case + return "Number of intervals should be less than 1000" if ( abs(($indexEnd-$indexStart)/$indexStep) > 1000 ) + } return "End value must be larger or later than start value" if ($indexStart >= $indexEnd); return "Cannot have an index step of 0 or negative" if ($indexStep <= 0); # This loops each interval - for ( my $ix = $indexStart; $ix < $indexEnd; $ix += $indexStep ) { + for ( my $ix = $indexStart; $ix < $indexEnd; $ix = _incrementByInterval( $ix, $indexStep, $indexType ) ) { my $result = $lineFormat; - + my $intervalEnd = _incrementByInterval( $ix, $indexStep, $indexType); + # Within each interval this loops each search for ( my $i = 1 ; $i <= $searchCounter ; $i++ ) { # First we get rid of all that are before the start interval # but we still include them in the total as this will normally # be what the user wants + while ( exists $sortedIndexes[$i][0]{indexvalue} && $sortedIndexes[$i][0]{indexvalue}< $ix ) { $totalFound[$i]++; @@ -402,7 +459,7 @@ sub _MULTISEARCH { while ( exists $sortedIndexes[$i][0]{indexvalue} && $sortedIndexes[$i][0]{indexvalue} >= $ix && - $sortedIndexes[$i][0]{indexvalue} < $ix + $indexStep ) { + $sortedIndexes[$i][0]{indexvalue} < $intervalEnd ) { $topicCount++; push @formatLists, $sortedIndexes[$i][0]{formatlist}; @@ -430,7 +487,8 @@ sub _MULTISEARCH { $resultString .= $result; } - } else { + } + else { return "Unknown index mode"; }