From 62865dece92efd6222604f67b4d1ff4745faece5 Mon Sep 17 00:00:00 2001
From: Kyle M Hall <kyle@bywatersolutions.com>
Date: Mon, 22 Dec 2014 05:32:05 -0500
Subject: [PATCH] Bug 11431 [8] - Add audio alerts editor

---
 Koha/AudioAlert.pm                                 |   79 ++++++++++
 Koha/AudioAlerts.pm                                |  157 ++++++++++++++++++++
 Koha/Object.pm                                     |    6 +-
 Koha/Objects.pm                                    |    6 +-
 Koha/Template/Plugin/Koha.pm                       |    7 +
 admin/audio_alerts.pl                              |   60 ++++++++
 .../intranet-tmpl/prog/en/includes/admin-menu.inc  |    1 +
 .../prog/en/includes/doc-head-close.inc            |   33 +----
 koha-tmpl/intranet-tmpl/prog/en/js/staff-global.js |   12 --
 .../prog/en/modules/admin/admin-home.tt            |    2 +
 .../prog/en/modules/admin/audio_alerts.tt          |  131 ++++++++++++++++
 11 files changed, 452 insertions(+), 42 deletions(-)
 create mode 100644 Koha/AudioAlert.pm
 create mode 100644 Koha/AudioAlerts.pm
 create mode 100755 admin/audio_alerts.pl
 create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/admin/audio_alerts.tt

diff --git a/Koha/AudioAlert.pm b/Koha/AudioAlert.pm
new file mode 100644
index 0000000..4bedbb9
--- /dev/null
+++ b/Koha/AudioAlert.pm
@@ -0,0 +1,79 @@
+package Koha::AudioAlert;
+
+# Copyright ByWater Solutions 2014
+#
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use Modern::Perl;
+
+use Carp;
+
+use Koha::Database;
+
+use base qw(Koha::Object);
+
+=head1 NAME
+
+Koha::AudioAlert - Koha Borrower Object class
+
+=head1 API
+
+=head2 Class Methods
+
+=head3 store
+
+Override base store to set default precedence
+if there is not one set already.
+
+=cut
+
+sub store {
+    my ($self) = @_;
+
+    $self->precedence( Koha::AudioAlerts->get_next_precedence() ) unless defined $self->precedence();
+
+    return $self->SUPER::store();
+}
+
+=head3 move
+
+$alert->move('up');
+
+Changes the alert's precedence up, down, top, or bottom
+
+=cut
+
+sub move {
+    my ( $self, $where ) = @_;
+
+    return Koha::AudioAlerts->move( { audio_alert => $self, where => $where } );
+}
+
+=head3 type
+
+=cut
+
+sub type {
+    return 'AudioAlert';
+}
+
+=head1 AUTHOR
+
+Kyle M Hall <kyle@bywatersolutions.com>
+
+=cut
+
+1;
diff --git a/Koha/AudioAlerts.pm b/Koha/AudioAlerts.pm
new file mode 100644
index 0000000..1127297
--- /dev/null
+++ b/Koha/AudioAlerts.pm
@@ -0,0 +1,157 @@
+package Koha::AudioAlerts;
+
+# Copyright ByWater Solutions 2014
+#
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use Modern::Perl;
+
+use Carp;
+
+use Koha::Database;
+
+use Koha::AudioAlert;
+
+use base qw(Koha::Objects);
+
+=head1 NAME
+
+Koha::AudioAlert - Koha Borrower Object class
+
+=head1 API
+
+=head2 Class Methods
+
+=head3 search
+
+Overrides default search such that
+the default ordering is by precedence
+
+=cut
+
+sub search {
+    my ( $self, $params, $attributes ) = @_;
+
+    $attributes->{order_by} ||= 'precedence';
+
+    return $self->SUPER::search( $params, $attributes );
+}
+
+=head3 get_next_precedence
+
+Gets the next precedence value for audio alerts
+
+=cut
+
+sub get_next_precedence {
+    my ($self) = @_;
+
+    return $self->get_last_precedence() + 1;
+}
+
+=head3 get_last_precedence
+
+Gets the last precedence value for audio alerts
+
+=cut
+
+sub get_last_precedence {
+    my ($self) = @_;
+
+    return $self->_resultset()->get_column('precedence')->max();
+}
+
+=head3 move
+
+Koha::AudioAlerts->move( { audio_alert => $audio_alert, where => $where } );
+
+Moves the given alert precedence 'up', 'down', 'top' or 'bottom'
+
+=cut
+
+sub move {
+    my ( $self, $params ) = @_;
+
+    my $alert = $params->{audio_alert};
+    my $where = $params->{where};
+
+    return unless ( $alert && $where );
+
+    if ( $where eq 'up' ) {
+        unless ( $alert->precedence() == 1 ) {
+            my ($other) = $self->search( { precedence => $alert->precedence() - 1 } );
+            $other->precedence( $alert->precedence() )->store();
+            $alert->precedence( $alert->precedence() - 1 )->store();
+        }
+    }
+    elsif ( $where eq 'down' ) {
+        unless ( $alert->precedence() == $self->get_last_precedence() ) {
+            my ($other) = $self->search( { precedence => $alert->precedence() + 1 } );
+            $other->precedence( $alert->precedence() )->store();
+            $alert->precedence( $alert->precedence() + 1 )->store();
+        }
+    }
+    elsif ( $where eq 'top' ) {
+        $alert->precedence(0)->store();
+        $self->fix_precedences();
+    }
+    elsif ( $where eq 'bottom' ) {
+        $alert->precedence( $self->get_next_precedence() )->store();
+        $self->fix_precedences();
+    }
+}
+
+=head3 fix_precedences
+
+Koha::AudioAlerts->fix_precedences();
+
+Updates precedence numbers to start with 1
+and to have no gaps
+
+=cut
+
+sub fix_precedences {
+    my ($self) = @_;
+
+    my @alerts = $self->search();
+
+    my $i = 1;
+    map { $_->precedence( $i++ )->store() } @alerts;
+}
+
+=head3 type
+
+=cut
+
+sub type {
+    return 'AudioAlert';
+}
+
+=head3 object_class
+
+=cut
+
+sub object_class {
+    return 'Koha::AudioAlert';
+}
+
+=head1 AUTHOR
+
+Kyle M Hall <kyle@bywatersolutions.com>
+
+=cut
+
+1;
diff --git a/Koha/Object.pm b/Koha/Object.pm
index bc2746f..1be2cf5 100644
--- a/Koha/Object.pm
+++ b/Koha/Object.pm
@@ -255,7 +255,11 @@ sub AUTOLOAD {
     # Using direct setter/getter like $item->barcode() or $item->barcode($barcode);
     if ( grep {/^$method$/} @columns ) {
         if ( @_ ) {
-            return $self->_result()->set_column( $method, @_ );
+            warn "METHOD: $method";
+            warn "VAL: " . $_[0];
+            carp "TEST";
+            $self->_result()->set_column( $method, @_ );
+            return $self;
         } else {
             my $value = $self->_result()->get_column( $method );
             return $value;
diff --git a/Koha/Objects.pm b/Koha/Objects.pm
index 5413fb6..6223d89 100644
--- a/Koha/Objects.pm
+++ b/Koha/Objects.pm
@@ -94,17 +94,17 @@ my @objects = Koha::Objects->search($params);
 =cut
 
 sub search {
-    my ( $self, $params ) = @_;
+    my ( $self, $params, $attributes ) = @_;
 
     if (wantarray) {
-        my @dbic_rows = $self->_resultset()->search($params);
+        my @dbic_rows = $self->_resultset()->search($params, $attributes);
 
         return $self->_wrap(@dbic_rows);
 
     }
     else {
         my $class = ref($self) ? ref($self) : $self;
-        my $rs = $self->_resultset()->search($params);
+        my $rs = $self->_resultset()->search($params, $attributes);
 
         return $class->_new_from_dbic($rs);
     }
diff --git a/Koha/Template/Plugin/Koha.pm b/Koha/Template/Plugin/Koha.pm
index 6b95e3b..3aebee3 100644
--- a/Koha/Template/Plugin/Koha.pm
+++ b/Koha/Template/Plugin/Koha.pm
@@ -19,6 +19,7 @@ package Koha::Template::Plugin::Koha;
 
 use Modern::Perl;
 use Encode qw( encode );
+use JSON;
 
 use base qw( Template::Plugin );
 
@@ -45,4 +46,10 @@ sub Preference {
     return encode('UTF-8', C4::Context->preference( $pref ) );
 }
 
+sub AudioAlerts {
+    my $dbh = C4::Context->dbh;
+    my $audio_alerts = $dbh->selectall_arrayref( 'SELECT * FROM audio_alerts ORDER BY precedence', { Slice => {} } );
+    return encode_json($audio_alerts);
+}
+
 1;
diff --git a/admin/audio_alerts.pl b/admin/audio_alerts.pl
new file mode 100755
index 0000000..f62bacd
--- /dev/null
+++ b/admin/audio_alerts.pl
@@ -0,0 +1,60 @@
+#!/usr/bin/perl
+
+# Copyright 2014 ByWater Solutions
+#
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use Modern::Perl;
+
+use CGI;
+use C4::Auth;
+use C4::Output;
+use Koha::AudioAlert;
+use Koha::AudioAlerts;
+
+my $cgi = new CGI;
+
+my $selector = $cgi->param('selector');
+my $sound    = $cgi->param('sound');
+my $id       = $cgi->param('id');
+my $action     = $cgi->param('action');
+my $where    = $cgi->param('where');
+my @delete   = $cgi->param('delete');
+
+my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+    {
+        template_name   => "admin/audio_alerts.tt",
+        query           => $cgi,
+        type            => "intranet",
+        authnotrequired => 0,
+        flagsrequired   => { parameters => 'parameters_remaining_permissions' },
+        debug           => 1,
+    }
+);
+
+if ( $selector && $sound ) {
+    Koha::AudioAlert->new( { selector => $selector, sound => $sound } )->store();
+}
+
+map { Koha::AudioAlerts->find($_)->delete() } @delete;
+
+if ( $id && $action && $where && $action eq 'move' ) {
+    Koha::AudioAlerts->find($id)->move($where);
+}
+
+$template->param( audio_alerts => scalar Koha::AudioAlerts->search() );
+
+output_html_with_http_headers $cgi, $cookie, $template->output;
diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc
index 281bb54..e38b0b4 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc
+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc
@@ -64,6 +64,7 @@
     <li><a href="/cgi-bin/koha/admin/z3950servers.pl">Z39.50/SRU servers</a></li>
     <li><a href="/cgi-bin/koha/admin/didyoumean.pl">Did you mean?</a></li>
     <li><a href="/cgi-bin/koha/admin/columns_settings.pl">Columns settings</a></li>
+    <li><a href="/cgi-bin/koha/admin/audio_alerts.pl">Audio alerts</a></li>
 </ul>
 </div>
 </div>
diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/doc-head-close.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/doc-head-close.inc
index 77b6381..cfa8254 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/includes/doc-head-close.inc
+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/doc-head-close.inc
@@ -64,42 +64,23 @@
     </script>
 [% END %]
 
-[% IF AudioAlerts %]
+[% IF Koha.Preference('AudioAlerts') %]
     <script type="text/javascript">
         //<![CDATA[
             var AUDIO_ALERT_PATH = '[% interface %]/[% theme %]/sound/';
-            var AUDIO_ALERT_ACTION  = '[% Koha.Preference("OverrideAudioAlertAction")  || "opening.ogg"  %]';
-            var AUDIO_ALERT_WARNING = '[% Koha.Preference("OverrideAudioAlertWarning") || "critical.ogg" %]';
-            var AUDIO_ALERT_SUCCESS = '[% Koha.Preference("OverrideAudioAlertSuccess") || "beep.ogg"     %]';
-            var AUDIO_ALERT_SELECTOR_ACTION  = '[% Koha.Preference("AudioAlertSelectorAction")  | replace( "'", "\\'" ) %]';
-            var AUDIO_ALERT_SELECTOR_WARNING = '[% Koha.Preference("AudioAlertSelectorWarning") | replace( "'", "\\'" ) %]';
-            var AUDIO_ALERT_SELECTOR_SUCCESS = '[% Koha.Preference("AudioAlertSelectorSuccess") | replace( "'", "\\'" ) %]';
-            var AUDIO_ALERT_SELECTOR_CUSTOM = JSON.parse( '{ [% String.new( Koha.Preference("AudioAlertsCustom") ).collapse | replace( "'", "\\'" ) %] }' );
+            var AUDIO_ALERTS = JSON.parse( '[% Koha.AudioAlerts | replace( "'", "\\'" ) %]' );
         //]]>
 
         $( document ).ready(function() {
-            var no_sound_played = true;
-            if ( Object.keys(AUDIO_ALERT_SELECTOR_CUSTOM) ) {
-                for ( var k in AUDIO_ALERT_SELECTOR_CUSTOM ) {
-                    if ( $(k).length ) {
-                        playSound( AUDIO_ALERT_SELECTOR_CUSTOM[k] );
-                        no_sound_played = false;
+            if ( AUDIO_ALERTS ) {
+                for ( var k in AUDIO_ALERTS ) {
+                    var alert = AUDIO_ALERTS[k];
+                    if ( $( alert.selector ).length ) {
+                        playSound( alert.sound );
                         break;
                     }
                 }
             }
-
-            if ( no_sound_played ) {
-                if ( $( AUDIO_ALERT_SELECTOR_ACTION ).length ) {
-                    playSoundAction();
-                }
-                else if ( $( AUDIO_ALERT_SELECTOR_WARNING ).length ) {
-                    playSoundWarning();
-                }
-                else if ( $( AUDIO_ALERT_SELECTOR_SUCCESS ).length ) {
-                    playSoundSuccess();
-                }
-            }
         });
     </script>
 [% END %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/js/staff-global.js b/koha-tmpl/intranet-tmpl/prog/en/js/staff-global.js
index e6f03a2..fd0f33e 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/js/staff-global.js
+++ b/koha-tmpl/intranet-tmpl/prog/en/js/staff-global.js
@@ -128,15 +128,3 @@ function playSound( sound ) {
     }
     document.getElementById("audio-alert").innerHTML = '<audio src="' + sound + '" autoplay="autoplay" autobuffer="autobuffer"></audio>';
 }
-
-function playSoundWarning() {
-    playSound( AUDIO_ALERT_WARNING );
-}
-
-function playSoundAction() {
-    playSound( AUDIO_ALERT_ACTION );
-}
-
-function playSoundSuccess() {
-    playSound( AUDIO_ALERT_SUCCESS );
-}
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt
index 02bc60a..27068ae 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt
@@ -104,6 +104,8 @@
     <dd>Choose which plugins to use to suggest searches to patrons and staff.</dd>
     <dt><a href="/cgi-bin/koha/admin/columns_settings.pl">Configure columns</a></dt>
     <dd>Hide or show columns for tables.</dd>
+    <dt><a href="/cgi-bin/koha/admin/audio_alerts.pl">Audio alerts</a></dt>
+    <dd>Define which events trigger which sounds</dd>
 </dl>
 </div>
 
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/audio_alerts.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/audio_alerts.tt
new file mode 100644
index 0000000..a64f9b6
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/audio_alerts.tt
@@ -0,0 +1,131 @@
+[% INCLUDE 'doc-head-open.inc' %]
+<title>Koha &rsaquo; Administration &rsaquo; Audio alerts</title>
+[% INCLUDE 'doc-head-close.inc' %]
+
+<script type="text/javascript">
+$( document ).ready(function() {
+    $.ajax({
+        //This will retrieve the contents of the folder if the folder is configured as 'browsable'
+        url: AUDIO_ALERT_PATH,
+        success: function (data) {
+            $("#fileNames").html('<ul>');
+            //List all png or jpg or gif file names in the page
+            $(data).find('a:contains("ogg")').each(function () {
+                var filename = this.href.split('/').pop();
+                $('#koha-sounds').append($('<option>', { value : filename }).text(filename));
+            });
+        }
+    });
+
+    $('#koha-sounds').on('change', function() {
+        $('#sound').val( this.value );
+    });
+
+    $('#koha-sounds').on('change', function() {
+        $('#sound').val( this.value );
+    });
+
+    $('#play-sound').on('click', function() {
+        playSound( $('#sound').val() );
+        return false;
+    });
+
+    $('#new-alert-form').on('submit', function() {
+        if ( ! $('#selector').val() ) {
+            alert(_("You must enter a selector!"));
+            return false;
+        } else if ( ! $('#sound').val() ) {
+            alert(_("You must choose a sound!"));
+            return false;
+        } else {
+            return true;
+        }
+    });
+
+    $('#delete-alert-form').on('submit', function() {
+        return confirm(_("Are you sure you want to delete the selected audio alerts?"));
+    });
+});
+</script>
+
+</head>
+<body>
+[% INCLUDE 'header.inc' %]
+[% INCLUDE 'patrons-admin-search.inc' %]
+
+<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a> &rsaquo; Audio alerts</div>
+
+<div id="doc3" class="yui-t2">
+    <div id="bd">
+        <div id="yui-main">
+            <div class="yui-b">
+                <form id="new-alert-form" action="audio_alerts.pl" method="post">
+                    <fieldset class="form-inline">
+                        <legend>Add new alert</legend>
+
+                        <input id="selector" name="selector" type="text" class="input-large" placeholder="selector" />
+                        <input id="sound" name="sound" type="text" class="input-large" placeholder="sound" />
+
+                        <button id="play-sound" class="btn"><i class="icon-play"></i> Play sound</button>
+
+                        <br/>
+
+                        <select id="koha-sounds">
+                            <option value="">Select built-in sound</option>
+                        </select>
+
+                        <button id="save-alert" type="submit" class="btn"><i class="icon-hdd"></i> Save alert</button>
+                    </fieldset>
+                </form>
+
+                <form id="delete-alert-form" action="audio_alerts.pl" method="post">
+                    <table>
+                        <thead>
+                            <tr>
+                                <th>&nbsp;</th>
+                                <th>Precedence</th>
+                                <th>&nbsp;</th>
+                                <th>Selector</th>
+                                <th>Sound</th>
+                            </tr>
+                        </thead>
+
+                        <tbody>
+                            [% FOREACH a IN audio_alerts %]
+                                <tr>
+                                    <td><input type="checkbox" name="delete" value="[% a.id %]" /></td>
+                                    <td>[% a.precedence %]</td>
+                                    <td style="white-space:nowrap;">
+                                        <a title="Move alert up" href="audio_alerts.pl?action=move&amp;where=up&amp;id=[% a.id %]">
+                                            <img src="[% interface %]/[% theme %]/img/go-up.png" border="0" alt="Go up" />
+                                        </a>
+
+                                        <a title="Move alert to top" href="audio_alerts.pl?action=move&amp;where=top&amp;id=[% a.id %]">
+                                            <img src="[% interface %]/[% theme %]/img/go-top.png" border="0" alt="Go top" />
+                                        </a>
+
+                                        <a title="Move alert to bottom" href="audio_alerts.pl?action=move&amp;where=bottom&amp;id=[% a.id %]">
+                                            <img src="[% interface %]/[% theme %]/img/go-bottom.png" border="0" alt="Go bottom" />
+                                        </a>
+
+                                        <a title="Move alert down" href="audio_alerts.pl?action=move&amp;where=down&amp;id=[% a.id %]">
+                                            <img src="[% interface %]/[% theme %]/img/go-down.png" border="0" alt="Go down" />
+                                        </a>
+                                    </td>
+                                    <td>[% a.selector %]</td>
+                                    <td>[% a.sound %]</td>
+                                </tr>
+                            [% END %]
+                        </tbody>
+                    </table>
+
+                    <p/>
+                    <button id="delete-alerts" type="submit" class="btn"><i class="icon-trash"></i> Delete selected alerts</button>
+                </form>
+            </div>
+        </div>
+    <div class="yui-b">
+[% INCLUDE 'admin-menu.inc' %]
+</div>
+</div>
+[% INCLUDE 'intranet-bottom.inc' %]
-- 
1.7.2.5