diff --git a/lib/WebGUI/Content/PassiveAnalytics.pm b/lib/WebGUI/Content/PassiveAnalytics.pm
new file mode 100644
index 000000000..7de3b86a8
--- /dev/null
+++ b/lib/WebGUI/Content/PassiveAnalytics.pm
@@ -0,0 +1,49 @@
+package WebGUI::Content::PassiveAnalytics;
+
+use strict;
+use WebGUI::AdminConsole;
+use WebGUI::Exception;
+use WebGUI::PassiveAnalytics::Flow;
+
+=head1 NAME
+
+Package WebGUI::Content::PassiveAnalytics
+
+=head1 DESCRIPTION
+
+Handle all requests for building and editing Passive Analytic flows.
+
+=head1 SYNOPSIS
+
+ use WebGUI::Content::PassiveAnalytics;
+ my $output = WebGUI::Content::PassiveAnalytics::handler($session);
+
+=head1 SUBROUTINES
+
+These subroutines are available from this package:
+
+=cut
+
+#-------------------------------------------------------------------
+
+=head2 handler ( session )
+
+The content handler for this package.
+
+=cut
+
+sub handler {
+ my ($session) = @_;
+ my $output = undef;
+ return undef unless $session->form->get('op') eq 'passiveAnalytics';
+ my $function = "www_".$session->form->get('func');
+ if ($function ne "www_" && (my $sub = WebGUI::PassiveAnalytics::Flow->can($function))) {
+ $output = $sub->($session);
+ }
+ else {
+ WebGUI::Error::MethodNotFound->throw(error=>"Couldn't call non-existant method $function inside PassiveAnalytics", method=>$function);
+ }
+ return $output;
+}
+
+1;
diff --git a/lib/WebGUI/PassiveAnalytics/Flow.pm b/lib/WebGUI/PassiveAnalytics/Flow.pm
new file mode 100644
index 000000000..143a24b66
--- /dev/null
+++ b/lib/WebGUI/PassiveAnalytics/Flow.pm
@@ -0,0 +1,254 @@
+package WebGUI::PassiveAnalytics::Flow;
+
+use strict;
+use Tie::IxHash;
+use WebGUI::AdminConsole;
+use WebGUI::HTMLForm;
+use WebGUI::International;
+use WebGUI::Pluggable;
+use WebGUI::PassiveAnalytics::Rule;
+use WebGUI::Utility;
+use WebGUI::HTMLForm;
+use WebGUI::Workflow;
+use WebGUI::Workflow::Instance;
+
+=head1 NAME
+
+Package WebGUI::PassiveAnalytics::Flow
+
+=head1 DESCRIPTION
+
+Web interface for making sets of rules for doing passive analytics, and
+running them.
+
+=cut
+
+#----------------------------------------------------------------------------
+
+=head2 canView ( session [, user] )
+
+Returns true if the user can administrate this operation. user defaults to
+the current user.
+
+=cut
+
+sub canView {
+ my $session = shift;
+ my $user = shift || $session->user;
+ return $user->isInGroup( 3 );
+}
+
+#-------------------------------------------------------------------
+
+=head2 www_deleteRule ( )
+
+Deletes an activity from a workflow.
+
+=cut
+
+sub www_deleteRule {
+ my $session = shift;
+ return $session->privilege->insufficient() unless canView($session);
+ my $rule = WebGUI::PassiveAnalytics::Rule->new($session, $session->form->get("ruleId"));
+ if (defined $rule) {
+ $rule->delete;
+ }
+ return www_editRuleflow($session);
+}
+
+#------------------------------------------------------------------
+
+=head2 www_demoteRule ( session )
+
+Moves a Rule down one position in the execution order.
+
+=head3 session
+
+A reference to the current session.
+
+=cut
+
+sub www_demoteRule {
+ my $session = shift;
+ return $session->privilege->insufficient() unless canView($session);
+ my $rule = WebGUI::PassiveAnalytics::Rule->new($session, $session->form->get("ruleId"));
+ if (defined $rule) {
+ $rule->demote;
+ }
+ return www_editRuleflow($session);
+}
+
+#-------------------------------------------------------------------
+
+=head2 www_editRuleflow ( session )
+
+Configure a set of analyses to run on the passive logs. The analysis is destructive.
+
+=cut
+
+sub www_editRuleflow {
+ my $session = shift;
+ my $error = shift;
+ return $session->privilege->insufficient() unless canView($session);
+ if ($error) {
+ $error = qq|
$error
\n|;
+ }
+ my $i18n = WebGUI::International->new($session, "PassiveAnalytics");
+ my $addmenu = '';
+ $addmenu .= sprintf '
%s',
+ $session->url->page('op=passiveAnalytics;func=editRule'),
+ $i18n->get('Add a bucket');
+ $addmenu .= '
';
+ my $f = WebGUI::HTMLForm->new($session);
+ $f->hidden(
+ name=>'op',
+ value=>'passiveAnalytics'
+ );
+ $f->hidden(
+ name=>'func',
+ value=>'editRuleflowSave'
+ );
+ $f->integer(
+ name => 'pauseInterval',
+ value => 300,
+ label => $i18n->get('pause interval'),
+ hoverHelp => $i18n->get('pause interval help'),
+ );
+ $f->submit(value => $i18n->get('Begin analysis'));
+ my $steps = '';
+ my $getARule = WebGUI::PassiveAnalytics::Rule->getAllIterator($session);
+ my $icon = $session->icon;
+ while (my $rule = $getARule->()) {
+ my $id = $rule->getId;
+ my $bucket = $rule->get('bucketName');
+ $steps .= '| '
+ . $icon->delete( 'op=passiveAnalytics;func=deleteRule;ruleId='.$id, undef, $i18n->get('confirm delete rule'))
+ . $icon->edit( 'op=passiveAnalytics;func=editRule;ruleId='.$id)
+ . $icon->moveDown('op=passiveAnalytics;func=demoteRule;ruleId='.$id)
+ . $icon->moveUp( 'op=passiveAnalytics;func=promoteRule;ruleId='.$id)
+ . ' | '.$bucket.' |
';
+
+ }
+ $steps .= '| | Other |
';
+ my $ac = WebGUI::AdminConsole->new($session,'passiveAnalytics');
+ return $ac->render($error.$f->print.$addmenu.$steps, 'Passive Analytics');
+}
+
+#-------------------------------------------------------------------
+
+=head2 www_editRuleflowSave ( )
+
+Saves the results of www_editRuleflow()
+
+=cut
+
+sub www_editRuleflowSave {
+ my $session = shift;
+ return $session->privilege->insufficient() unless canView($session);
+ my $workflow = WebGUI::Workflow->new($session, 'PassiveAnalytics000001');
+ return www_editRuleflow($session, "The Passive Analytics workflow has been deleted. Please contact an Administrator immediately.") unless defined $workflow;
+ my $delta = $session->form->process('pauseInterval','integer');
+ my $activities = $workflow->getActivities();
+ ##Note, they're in order, and the order is known.
+ $activities->[0]->set('deltaInterval', $delta);
+ $activities->[1]->set('userId', $session->user->userId);
+ my $instance = WebGUI::Workflow::Instance->create($session, {
+ workflowId => $workflow->getId,
+ priority => 1,
+ });
+ if (!defined $instance) {
+ return www_editRuleflow($session, "A Passive Analytics analysis is currently running.") if $session->stow->get('singletonWorkflowClash');
+ return www_editRuleflow($session, "Error creating the workflow instance.");
+ }
+ $instance->start('skipRealtime');
+ return www_editRuleflow($session, "Passive Analytics session started");
+}
+
+
+#-------------------------------------------------------------------
+
+=head2 www_editRule ( )
+
+Displays a form to edit the properties rule.
+
+=cut
+
+sub www_editRule {
+ my $session = shift;
+ return $session->privilege->insufficient() unless canView($session);
+
+ ##Make a PassiveAnalytics rule to use to populate the form.
+ my $ruleId = $session->form->get('ruleId');
+ my $rule;
+ if ($ruleId) {
+ $rule = WebGUI::PassiveAnalytics::Rule->new($session, $ruleId);
+ }
+ else {
+ ##We need a temporary rule so that we can call dynamicForm, below
+ $ruleId = 'new';
+ $rule = WebGUI::PassiveAnalytics::Rule->create($session, {});
+ }
+
+ ##Build the form
+ my $form = WebGUI::HTMLForm->new($session);
+ $form->hidden( name=>"op", value=>"passiveAnalytics");
+ $form->hidden( name=>"func", value=>"editRuleSave");
+ $form->hidden( name=>"ruleId", value=>$ruleId);
+ $form->dynamicForm([WebGUI::PassiveAnalytics::Rule->crud_definition($session)], 'properties', $rule);
+ $form->submit;
+
+ my $i18n = WebGUI::International->new($session, 'PassiveAnalytics');
+ my $ac = WebGUI::AdminConsole->new($session,'passiveAnalytics');
+ $ac->addSubmenuItem($session->url->page("op=passiveAnalytics;func=editRuleflow"), $i18n->get("manage ruleset"));
+ if ($ruleId eq 'new') {
+ $rule->delete;
+ }
+ return $ac->render($form->print,$i18n->get('Edit Rule'));
+}
+
+#-------------------------------------------------------------------
+
+=head2 www_editRuleSave ( )
+
+Saves the results of www_editRule().
+
+=cut
+
+sub www_editRuleSave {
+ my $session = shift;
+ return $session->privilege->insufficient() unless canView($session);
+ my $ruleId = $session->form->get('ruleId');
+ my $rule;
+ if ($ruleId eq 'new') {
+ $rule = WebGUI::PassiveAnalytics::Rule->create($session, {});
+ }
+ else {
+ $rule = WebGUI::PassiveAnalytics::Rule->new($session, $ruleId);
+ }
+ $rule->updateFromFormPost if $rule;
+ return www_editRuleflow($session);
+}
+
+#------------------------------------------------------------------
+
+=head2 www_promoteRule ( session )
+
+Moves a rule up one position in the execution order.
+
+=head3 session
+
+A reference to the current session.
+
+=cut
+
+sub www_promoteRule {
+ my $session = shift;
+ return $session->privilege->insufficient() unless canView($session);
+ my $rule = WebGUI::PassiveAnalytics::Rule->new($session, $session->form->get("ruleId"));
+ if (defined $rule) {
+ $rule->promote;
+ }
+ return www_editRuleflow($session);
+}
+
+1;
diff --git a/lib/WebGUI/PassiveAnalytics/Rule.pm b/lib/WebGUI/PassiveAnalytics/Rule.pm
new file mode 100644
index 000000000..5d052c8da
--- /dev/null
+++ b/lib/WebGUI/PassiveAnalytics/Rule.pm
@@ -0,0 +1,92 @@
+package WebGUI::PassiveAnalytics::Rule;
+
+use base qw/WebGUI::Crud/;
+use WebGUI::International;
+
+=head1 NAME
+
+Package WebGUI::PassiveAnalytics::Rule;
+
+=head1 DESCRIPTION
+
+Base class for rules that are used to analyze the Passive Analytics log.
+
+=head1 METHODS
+
+These methods are available from this class:
+
+=cut
+
+#-------------------------------------------------------------------
+
+=head2 crud_definition ( )
+
+WebGUI::Crud definition for this class.
+
+=head3 tableName
+
+analyticRule.
+
+=head3 tableKey
+
+ruleId
+
+=head3 sequenceKey
+
+None. There is only 1 sequence of rules for a site.
+
+=head3 properties
+
+=head4 bucketName
+
+The name of a bucket to hold results for this rule.
+
+=head4 rules
+
+JSON blob with configuration data for the individual rules.
+
+=cut
+
+sub crud_definition {
+ my ($class, $session) = @_;
+ my $definition = $class->SUPER::crud_definition($session);
+ $definition->{tableName} = 'analyticRule';
+ $definition->{tableKey} = 'ruleId';
+ $definition->{sequenceKey} = '';
+ my $properties = $definition->{properties};
+ my $i18n = WebGUI::International->new($session);
+ $properties->{bucketName} = {
+ fieldType => 'text',
+ label => $i18n->get('Bucket Name','PassiveAnalytics'),
+ hoverHelp => $i18n->get('Bucket Name help','PassiveAnalytics'),
+ defaultValue => '',
+ };
+ $properties->{regexp} = {
+ fieldType => 'text',
+ label => $i18n->get('regexp','PassiveAnalytics'),
+ hoverHelp => $i18n->get('regexp help','PassiveAnalytics'),
+ defaultValue => '.+',
+ };
+ return $definition;
+}
+
+#-------------------------------------------------------------------
+
+=head2 matchesBucket ( $logLine )
+
+Executes the rule to determine if a log file entry matches the rule.
+
+=head3 $logLine
+
+A hashref of information from 1 line of the logs.
+
+=cut
+
+sub matchesBucket {
+ my ($self, $logLine) = @_;
+ my $regexp = $self->get('regexp');
+ return $logLine->{url} =~ m/$regexp/;
+}
+
+1;
+#vim:ft=perl
diff --git a/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm b/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm
new file mode 100644
index 000000000..170d50dfb
--- /dev/null
+++ b/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm
@@ -0,0 +1,138 @@
+package WebGUI::Workflow::Activity::BucketPassiveAnalytics;
+
+
+=head1 LEGAL
+
+ -------------------------------------------------------------------
+ Copyright 2001-2008 SDH Corporation
+ -------------------------------------------------------------------
+
+=cut
+
+use strict;
+use base 'WebGUI::Workflow::Activity';
+use WebGUI::PassiveAnalytics::Rule;
+use WebGUI::Inbox;
+
+=head1 NAME
+
+Package WebGUI::Workflow::Activity::BucketPassiveAnalytics
+
+=head1 DESCRIPTION
+
+Run through a set of rules to figure out how to classify log file entries.
+
+=head1 SYNOPSIS
+
+See WebGUI::Workflow::Activity for details on how to use any activity.
+
+=head1 METHODS
+
+These methods are available from this class:
+
+=cut
+
+
+#-------------------------------------------------------------------
+
+=head2 definition ( session, definition )
+
+See WebGUI::Workflow::Activity::defintion() for details.
+
+=cut
+
+sub definition {
+ my $class = shift;
+ my $session = shift;
+ my $definition = shift;
+ my $i18n = WebGUI::International->new($session, "PassiveAnalytics");
+ push( @{$definition}, {
+ name=>$i18n->get("Bucket Passive Analytics"),
+ properties=> {
+ notifyUser => {
+ fieldType => 'user',
+ label => $i18n->get('User'),
+ hoverHelp => $i18n->get('User help'),
+ defaultValue => $session->user->userId,
+ },
+ },
+ });
+ return $class->SUPER::definition($session,$definition);
+}
+
+
+#-------------------------------------------------------------------
+
+=head2 execute ( [ object ] )
+
+Analyze the deltaLog table, and generate the bucketLog table.
+
+=head3 notes
+
+=cut
+
+sub execute {
+ my ($self, undef, $instance) = @_;
+ my $session = $self->session;
+ my $endTime = time() + $self->getTTL;
+ my $expired = 0;
+
+ ##Load all the rules into an array
+ my @rules = ();
+ my $getARule = WebGUI::PassiveAnalytics::Rule->getAllIterator($session);
+ while (my $rule = $getARule->()) {
+ push @rules, $rule;
+ }
+
+ ##Get the index stored from the last invocation of the Activity. If this is
+ ##the first run, then clear out the table.
+ my $logIndex = $instance->getScratch('lastPassiveLogIndex') || 0;
+ if ($logIndex == 0) {
+ $session->db->write('delete from bucketLog');
+ }
+
+ ##Configure all the SQL
+ my $deltaSql = <<"EOSQL1";
+select userId, assetId, url, delta, from_unixtime(timeStamp) as stamp
+ from deltaLog order by timestamp limit $logIndex, 1234567890
+EOSQL1
+ my $deltaSth = $session->db->read($deltaSql);
+ my $bucketSth = $session->db->prepare('insert into bucketLog (userId, Bucket, duration, timeStamp) VALUES (?,?,?,?)');
+
+ ##Walk through the log file entries, one by one. Run each entry against
+ ##all the rules until 1 matches. If it doesn't match any rule, then bin it
+ ##into the "Other" bucket.
+ DELTA_ENTRY: while (my $entry = $deltaSth->hashRef()) {
+ ++$logIndex;
+ my $bucketFound = 0;
+ RULE: foreach my $rule (@rules) {
+ next RULE unless $rule->matchesBucket($entry);
+ $bucketSth->execute([$entry->{userId}, $rule->get('bucketName'), $entry->{delta}, $entry->{stamp}]);
+ }
+ if (!$bucketFound) {
+ $bucketSth->execute([$entry->{userId}, 'Other', $entry->{delta}, $entry->{stamp}]);
+ }
+ if (time() > $endTime) {
+ $expired = 1;
+ last DELTA_ENTRY;
+ }
+ }
+
+ if ($expired) {
+ $instance->setScratch('logIndex', $logIndex);
+ return $self->WAITING(1);
+ }
+ my $inbox = WebGUI::Inbox->new($self->session);
+ $inbox->addMessage({
+ status => 'unread',
+ subject => 'Passive analytics is done',
+ userId => $self->get('userId'),
+ message => 'Passive analytics is done',
+ });
+
+ return $self->COMPLETE;
+}
+
+1;
+
+#vim:ft=perl
diff --git a/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm b/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm
new file mode 100644
index 000000000..7d5e52f49
--- /dev/null
+++ b/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm
@@ -0,0 +1,158 @@
+package WebGUI::Workflow::Activity::SummarizePassiveAnalytics;
+
+
+=head1 LEGAL
+
+ -------------------------------------------------------------------
+ Copyright 2001-2008 SDH Corporation
+ -------------------------------------------------------------------
+
+=cut
+
+use strict;
+use base 'WebGUI::Workflow::Activity';
+
+=head1 NAME
+
+Package WebGUI::Workflow::Activity::SummarizePassiveAnalytics
+
+=head1 DESCRIPTION
+
+Summarize how long a user stayed on a page, using a user supplied interval.
+
+=head1 SYNOPSIS
+
+See WebGUI::Workflow::Activity for details on how to use any activity.
+
+=head1 METHODS
+
+These methods are available from this class:
+
+=cut
+
+
+#-------------------------------------------------------------------
+
+=head2 definition ( session, definition )
+
+See WebGUI::Workflow::Activity::defintion() for details.
+
+=cut
+
+sub definition {
+ my $class = shift;
+ my $session = shift;
+ my $definition = shift;
+ my $i18n = WebGUI::International->new($session, 'PassiveAnalytics');
+ push(@{$definition}, {
+ name=>$i18n->get('activityName'),
+ properties=> {
+ deltaInterval => {
+ fieldType => 'interval',
+ label => $i18n->get('pause interval'),
+ defaultValue => 15,
+ hoverHelp => $i18n->get('pause interval help'),
+ },
+ }
+ });
+ return $class->SUPER::definition($session,$definition);
+}
+
+
+#-------------------------------------------------------------------
+
+=head2 execute ( [ object ] )
+
+Analyze the passiveLog table, and generate the deltaLog table.
+
+=head3 notes
+
+If there is only 1 line in the table for a particular sessionId or
+userId, no conclusions as to how long the user viewed a page can be
+drawn from that. Similarly, the last entry in their browsing log
+yields no data, since we require another entry in the passiveLog to
+determine a delta.
+
+=cut
+
+sub execute {
+ my ($self, undef, $instance) = @_;
+ my $session = $self->session;
+ my $endTime = time() + $self->getTTL;
+ my $deltaInterval = $self->get('deltaInterval');
+
+ my $passive = 'select * from passiveLog where userId <> 1 order by userId, sessionId, timeStamp';
+ my $sth;
+ my $lastUserId;
+ my $lastSessionId;
+ my $lastTimeStamp;
+ my $lastAssetId;
+ my $lastUrl;
+ my $counter = $instance->getScratch('counter');
+ if ($counter) {
+ $passive .= ' limit '. $counter .', 1234567890';
+ $sth = $session->db->read($passive);
+ $lastUserId = $instance->getScratch('lastUserId');
+ $lastSessionId = $instance->getScratch('lastSessionId');
+ $lastTimeStamp = $instance->getScratch('lastTimeStamp');
+ $lastAssetId = $instance->getScratch('lastAssetId');
+ $lastUrl = $instance->getScratch('lastUrl');
+ }
+ else {
+ $sth = $session->db->read($passive);
+ my $logLine = $sth->hashRef();
+ $logLine = $sth->hashRef();
+ $lastUserId = $logLine->{userId};
+ $lastSessionId = $logLine->{sessionId};
+ $lastTimeStamp = $logLine->{timeStamp};
+ $lastAssetId = $logLine->{assetId};
+ $lastUrl = $logLine->{url};
+ }
+
+ $session->db->write('delete from deltaLog'); ##Only if we're starting out
+ my $deltaLog = $session->db->prepare('insert into deltaLog (userId, assetId, delta, timeStamp, url) VALUES (?,?,?,?,?)');
+
+ my $expired = 0;
+ LOG_ENTRY: while (my $logLine = $sth->hashRef()) {
+ $counter++;
+ my $delta = $logLine->{timeStamp} - $lastTimeStamp;
+ if ( $logLine->{userId} eq $lastUserId
+ && $logLine->{sessionId} eq $lastSessionId
+ && $delta < $deltaInterval ) {
+ $deltaLog->execute([$lastUserId, $lastAssetId, $delta, $lastTimeStamp, $lastUrl]);
+ }
+ $lastUserId = $logLine->{userId};
+ $lastSessionId = $logLine->{sessionId};
+ $lastTimeStamp = $logLine->{timeStamp};
+ $lastAssetId = $logLine->{assetId};
+ $lastUrl = $logLine->{url};
+ if (time() > $endTime) {
+ $instance->setScratch('lastUserId', $lastUserId);
+ $instance->setScratch('lastSessionId', $lastSessionId);
+ $instance->setScratch('lastTimeStamp', $lastTimeStamp);
+ $instance->setScratch('lastAssetId', $lastAssetId);
+ $instance->setScratch('lastUrl', $lastUrl);
+ $instance->setScratch('counter', $counter);
+ $expired = 1;
+ last LOG_ENTRY;
+ }
+ }
+
+ if ($expired) {
+ return $self->WAITING(1);
+ }
+
+ $instance->deleteScratch('lastUserId');
+ $instance->deleteScratch('lastSessionId');
+ $instance->deleteScratch('lastTimeStamp');
+ $instance->deleteScratch('lastAssetId');
+ $instance->deleteScratch('lastUrl');
+ $instance->deleteScratch('counter');
+ return $self->COMPLETE;
+}
+
+
+
+1;
+
+#vim:ft=perl
diff --git a/lib/WebGUI/i18n/English/PassiveAnalytics.pm b/lib/WebGUI/i18n/English/PassiveAnalytics.pm
new file mode 100644
index 000000000..b46a12eca
--- /dev/null
+++ b/lib/WebGUI/i18n/English/PassiveAnalytics.pm
@@ -0,0 +1,99 @@
+package WebGUI::i18n::English::PassiveAnalytics;
+
+use strict;
+
+our $I18N = {
+
+ 'Summarize Passive Analytics' => {
+ message => q|Summarize Passive Analytics|,
+ context => q|The name of this workflow activity.|,
+ lastUpdated => 0,
+ },
+
+ 'pause interval' => {
+ message => q|Pause threshold|,
+ lastUpdated => 0,
+ },
+
+ 'pause interval help' => {
+ message => q|Set the time between clicks that is interpreted as the user reading the page, as opposed to beginning a new browsing session, or leaving the site.|,
+ lastUpdated => 0,
+ },
+
+ 'other' => {
+ message => q|Other|,
+ lastUpdated => 0,
+ context => q|Meaning not like anything in a set. This, that and the other one. Also, a catch all.|
+ },
+
+ 'Bucket Name' => {
+ message => q|Bucket Name|,
+ lastUpdated => 0,
+ context => q|To name a container, or bucket.|
+ },
+
+ 'Bucket Name help' => {
+ message => q|Pick a unique, descriptive short name for this bucket.|,
+ lastUpdated => 0,
+ context => q||
+ },
+
+ 'regexp' => {
+ message => q|Regular expression|,
+ lastUpdated => 0,
+ context => q||
+ },
+
+ 'regexp help' => {
+ message => q|Define a regular expression to pick log entries for this bucket.
+^ = beginning of url
+$ = end of url
+. = any character
+* = any amount
++ = 1 or more
+? = 0 or 1
+|,
+ lastUpdated => 0,
+ context => q||
+ },
+
+ 'Passive Analytics' => {
+ message => q|Passive Analytics|,
+ lastUpdated => 0,
+ context => q||
+ },
+
+ 'Edit Rule' => {
+ message => q|Edit Rule|,
+ lastUpdated => 0,
+ context => q||
+ },
+
+ 'Add a bucket' => {
+ message => q|Add a bucket|,
+ lastUpdated => 0,
+ context => q||
+ },
+
+ 'User' => {
+ message => q|User|,
+ lastUpdated => 0,
+ context => q||
+ },
+
+ 'User help' => {
+ message => q|The user who will recieve an email when bucket processing is done.|,
+ lastUpdated => 0,
+ context => q||
+ },
+
+ 'Begin analysis' => {
+ message => q|Begin analysis|,
+ lastUpdated => 0,
+ context => q|Button label to begin analyzing the logs.|
+ },
+
+};
+
+1;
+#vim:ft=perl