Fix addressbook preferences management in IMP.
authorMichael M Slusarz <slusarz@curecanti.org>
Tue, 18 Aug 2009 04:33:20 +0000 (22:33 -0600)
committerMichael M Slusarz <slusarz@curecanti.org>
Tue, 18 Aug 2009 05:03:36 +0000 (23:03 -0600)
Also, breakout javascript to static file.

imp/js/addressbooksmanagement.js [new file with mode: 0644]
imp/lib/Application.php
imp/templates/prefs/sourceselect.inc

diff --git a/imp/js/addressbooksmanagement.js b/imp/js/addressbooksmanagement.js
new file mode 100644 (file)
index 0000000..e3237f1
--- /dev/null
@@ -0,0 +1,166 @@
+/**
+ * Provides the javascript for managing addressbooks.
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ */
+
+var ImpAddressbooksmanagement = {
+    // Variables set by other code: fields
+
+    deselectHeaders: function()
+    {
+        $F('unselected_search_sources').selectedIndex = $F('selected_search_sources').selectedIndex = -1;
+    },
+
+    resetHidden: function()
+    {
+        $('search_sources').setValue($F('selected_search_sources').join("\t"));
+    },
+
+    addSource: function()
+    {
+        this._sourceAction($('unselected_search_sources'), $('selected_search_sources'));
+    },
+
+    removeSource: function()
+    {
+        this._sourceAction($('selected_search_sources'), $('unselected_search_sources'));
+    },
+
+    _sourceAction: function(from, to)
+    {
+        var i = 1;
+
+        $A(from.options).slice(1).each(function(s) {
+            if (s.selected) {
+                to[i] = s.cloneNode(false);
+                s.remove();
+            }
+            ++i;
+        });
+
+        this.resetHidden();
+    },
+
+    moveSourceUp: function()
+    {
+        var sss = $('selected_search_sources'),
+            sel = sss.selectedIndex;
+
+        if (sel == -1 || sss.length <= 2) {
+            return;
+        }
+
+        // Deselect everything but the first selected item
+        sss.selectedIndex = sel;
+
+        sss.options[sel].previous().insert({ before: sss.options[sel].cloneNode(false) });
+        sss.options[sel].remove();
+
+        this.resetHidden();
+    },
+
+    moveSourceDown: function()
+    {
+        var i,
+            sss = $('selected_search_sources'),
+            sel = sss.selectedIndex,
+            l = sss.length,
+            tmp = [];
+
+        if (sel == -1 || l <= 2) {
+            return;
+        }
+
+        // deselect everything but the first selected item
+        sss.selectedIndex = sel;
+
+        sss.options[sel].next().insert({ after: sss.options[sel].cloneNode(false) });
+        sss.options[sel].remove();
+
+        this.resetHidden();
+    },
+
+    updateSearchFields: function()
+    {
+        var sv = this._getSelectedValue(false),
+            sf = $('search_fields');
+
+        sf.update('');
+
+        this.fields.each(function(f) {
+            if (f[0] == sv) {
+                f.slice(1).each(function(o) {
+                    var tmp = new Option(o[1], o[0]);
+                    if (o[2]) {
+                        tmp.selected = true;
+                    }
+                    sf.insert(tmp);
+                });
+            }
+        });
+
+        this.changeSearchFields();
+    },
+
+    _getSelectedValue: function(index)
+    {
+        var sss = $('selected_search_sources');
+
+        if (sss) {
+            return index ? sss.selectedIndex : sss.options[sss.selectedIndex].value;
+        } else {
+            return index ? 0 : this.fields[0][0];
+        }
+    },
+
+    changeSearchFields: function()
+    {
+        var data = [],
+            i = 0,
+            sf = $('search_fields'),
+            sv = this._getSelectedValue(true);
+
+        $A(sf.options).each(function(o) {
+            this.fields[sv][i + 1][2] = o.selected;
+            ++i;
+        }.bind(this));
+
+        this.fields.each(function(f) {
+            var tmp = [ f[0] ];
+            f.slice(1).each(function(o) {
+                if (o[2]) {
+                    tmp.push(o[0]);
+                }
+            });
+            data.push(tmp.join("\t"));
+        });
+
+        $('search_fields_string').setValue(data.join("\n"));
+    },
+
+    onDomLoad: function()
+    {
+        this.updateSearchFields();
+
+        if ($('search_fields')) {
+            $('search_fields').observe('change', this.changeSearchFields.bind(this));
+        }
+
+        if ($('unselected_search_sources')) {
+            $('unselected_search_sources').observe('change', this.deselectHeaders.bind(this));
+            $('selected_search_sources').observe('change', function() {
+                this.deselectHeaders();
+                this.updateSearchFields();
+            }.bind(this));
+            $('addsource').observe('click', this.addSource.bind(this));
+            $('removesource').observe('click', this.removeSource.bind(this));
+            $('moveup').observe('click', this.moveSourceUp.bind(this));
+            $('movedown').observe('click', this.moveSourceDown.bind(this));
+        }
+    }
+
+};
+
+document.observe('dom:loaded', ImpAddressbooksmanagement.onDomLoad.bind(ImpAddressbooksmanagement));
index b7b253f..2be58dc 100644 (file)
@@ -63,6 +63,13 @@ class IMP_Application extends Horde_Registry_Application
     static public $noCompress = false;
 
     /**
+     * Cached data for prefs pages.
+     *
+     * @var array
+     */
+    static public $prefsCache = array();
+
+    /**
      * Constructor.
      *
      * @param array $args  The following entries:
@@ -418,6 +425,10 @@ class IMP_Application extends Horde_Registry_Application
             ));
             break;
 
+        case 'addressbooks':
+            $this->_prefsPrepareSourceselect();
+            break;
+
         case 'flags':
             Horde::addScriptFile('colorpicker.js', 'horde', true);
             Horde::addScriptFile('flagmanagement.js', 'imp', true);
@@ -783,6 +794,84 @@ class IMP_Application extends Horde_Registry_Application
         }
     }
 
+    /**
+     * TODO
+     */
+    protected function _prefsPrepareSourceselect()
+    {
+        self::$prefsCache['sourceselect'] = array();
+
+        $registry = Horde_Registry::singleton();
+        if (!$registry->hasMethod('contacts/sources') ||
+            $GLOBALS['prefs']->isLocked('search_sources')) {
+            return;
+        }
+
+        $readable = $search_fields = $prefSelect = $writeable = $writeSelect = array();
+
+        try {
+            $readable = $registry->call('contacts/sources');
+        } catch (Horde_Exception $e) {}
+
+        try {
+            $writeable = $registry->call('contacts/sources', true);
+        } catch (Horde_Exception $e) {}
+
+        $search = IMP_Compose::getAddressSearchParams();
+
+        if (count($readable) == 1) {
+            // Only one source, no need to display the selection widget
+            $search['sources'] = array_keys($readable);
+        }
+
+        foreach ($search['sources'] as $source) {
+            if (!empty($readable[$source])) {
+                $prefSelect[$source] = $readable[$source];
+            }
+        }
+
+        $readSelect = array_diff(array_keys($readable), $search['sources']);
+
+        if (!$GLOBALS['prefs']->isLocked('add_source')) {
+            foreach ($writeable as $source => $name) {
+                $writeSelect[] = array(
+                    'val' => $source,
+                    'sel' => ($GLOBALS['prefs']->getValue('add_source') == $source),
+                    'name' => $name
+                );
+            }
+        }
+
+        $source_count = 0;
+
+        foreach (array_keys($readable) as $source) {
+            $search_fields[$source_count][] = $source;
+
+            try {
+                foreach ($registry->call('contacts/fields', $source) as $field) {
+                    if ($field['search']) {
+                        $search_fields[$source_count][] = array($field['name'], $field['label'], isset($search['fields'][$source]) && in_array($field['name'], $search['fields'][$source]));
+                    }
+                }
+            } catch (Horde_Exception $e) {}
+
+            ++$source_count;
+        }
+
+        Horde::addScriptFile('addressbooksmanagement.js', 'imp', true);
+        Horde::addInlineScript(array(
+            'ImpAddressbooksmanagement.fields = ' . Horde_Serialize::serialize($search_fields, Horde_Serialize::JSON, Horde_Nls::getCharset())
+        ));
+        self::$prefsCache['sourceselect'] = array(
+            'prefSelect' => $prefSelect,
+            'readable' => $readable,
+            'readSelect' => $readSelect,
+            'search' => $search,
+            'writeable' => $writeable,
+            'writeSelect' => $writeSelect
+        );
+    }
+
     /* horde/services/cache.php methods. */
 
     /**
index 8133497..2a8e1ea 100644 (file)
 <?php
-if ($registry->hasMethod('contacts/sources')) {
-    $search = IMP_Compose::getAddressSearchParams();
-    try {
-        $readable = $registry->call('contacts/sources');
-    } catch (Horde_Exception $e) {
-        $readable = null;
-    }
-    try {
-        $writeable = $registry->call('contacts/sources', true);
-    } catch (Horde_Exception $e) {
-        $writeable = null;
-    }
-
-    $nbReadSources = count(array_keys($readable));
-    $nbWriteSources = count(array_keys($writeable));
-
-    if ($nbReadSources == 1) {
-        // Only one source, no need to display the selection widget
-        $search['sources'] = array_keys($readable);
-        $readSelect = '';
-    }
-
-    $prefSelect = '';
-    foreach ($search['sources'] as $source) {
-        if (!empty($readable[$source])) {
-            $prefSelect .= '<option value="' . $source . '">' . $readable[$source] . "</option>\n";
-        }
-    }
-
-    $readSelect = '';
-    if (is_array($readable)) {
-        foreach (array_diff(array_keys($readable), $search['sources']) as $source) {
-            $readSelect .= '<option value="' . $source . '">' . $readable[$source] . "</option>\n";
-        }
-    }
-
-    if (is_array($writeable)) {
-        $writeSelect = '<option value="">' . _("None") . '</option>' . "\n";
-        $writeSource = '';
-        foreach ($writeable as $source => $name) {
-            $sel = $prefs->getValue('add_source') == $source ? ' selected="selected"' : '';
-            $writeSelect .= '<option value="' . $source . '"' . "$sel>" . $name . "</option>\n";
-            $writeSource = $source;
-        }
-    }
-
-    $search_fields = array();
-    if (is_array($readable)) {
-        foreach (array_keys($readable) as $source) {
-            try {
-                $search_fields[$source] = $registry->call('contacts/fields', $source);
-            } catch (Horde_Exception $e) {}
-        }
-    }
-
-    $js = "var searchFields = [];\n";
-    $source_count = 0;
-    foreach ($search_fields as $source => $fields) {
-        $js .= "searchFields[$source_count] = [];\n";
-        $js .= "searchFields[$source_count][0] = '$source';\n";
-
-        $field_count = 1;
-        foreach ($fields as $field) {
-            if ($field['search']) {
-                $marked = isset($search['fields'][$source]) && in_array($field['name'], $search['fields'][$source]) ? 'true' : 'false';
-                $js .= "searchFields[$source_count][$field_count] = ['" . $field['name'] . "', '" . $field['label'] . "', $marked];\n";
-                $field_count++;
-            }
-        }
-
-        $source_count++;
-    }
-}
+$cache = IMP_Application::$prefsCache['sourceselect'];
+if (!empty($cache['readSelect']) || !empty($cache['prefSelect'])):
+$imagedir = $registry->getImageDir('horde');
 ?>
-
-<?php if (!$prefs->isLocked('search_sources') && (!empty($readSelect) || !empty($prefSelect))): ?>
-<script type="text/javascript">
-
-function deselectHeaders()
-{
-    var p = document.prefs;
-    p.unselected_search_sources[0].selected = p.selected_search_sources[0].selected = false;
-}
-
-function resetHidden()
-{
-    var i, l,
-        p = document.prefs,
-        tmp = '';
-    for (i = 1, l = p.selected_search_sources.length; i < l; ++i) {
-        tmp += p.selected_search_sources[i].value;
-        if (i < l - 1) {
-            tmp += "\t";
-        }
-    }
-
-    document.prefs.search_sources.value = tmp;
-}
-
-function addSource()
-{
-    var i, l,
-        p = document.prefs,
-        uss = p.unselected_search_sources,
-        sss = p.selected_search_sources;
-    for (i = 1, l = uss.length; i < l; ++i) {
-        if (uss[i].selected) {
-            sss[sss.length] = new Option(uss[i].text, uss[i].value);
-            uss[i] = null;
-            --i;
-        }
-    }
-
-    resetHidden();
-}
-
-function removeSource()
-{
-    var i, l,
-        p = document.prefs,
-        sss = p.selected_search_sources,
-        uss = p.unselected_search_sources;
-    for (i = 1, l = sss.length; i < l; ++i) {
-        if (sss[i].selected) {
-            uss[uss.length] = new Option(sss[i].text, sss[i].value)
-            sss[i] = null;
-            --i;
-        }
-    }
-
-    resetHidden();
-}
-
-function moveSourceUp()
-{
-    var i, l, tmp,
-        sss = document.prefs.selected_search_sources,
-        sel = sss.selectedIndex;
-
-    if (sel == -1 || sss.length <= 2) {
-        return;
-    }
-
-    // deselect everything but the first selected item
-    sss.selectedIndex = sel;
-
-    if (sel == 1) {
-        tmp = sss[sel];
-        sss[sel] = null;
-        sss[sss.length] = tmp;
-        sss.selectedIndex = sss.length - 1;
-    } else {
-        tmp = [];
-        for (i = 1, l = sss.length; i < l; ++i) {
-            tmp[i - 1] = new Option(sss[i].text, sss[i].value);
-        }
-
-        for (i = 0, l = tmp.length; i < l; ++i) {
-            if (i + 1 == sel - 1) {
-                sss[i + 1] = tmp[i + 1];
-            } else if (i + 1 == sel) {
-                sss[i + 1] = tmp[i - 1];
-            } else {
-                sss[i + 1] = tmp[i];
-            }
-        }
-
-        sss.selectedIndex = sel - 1;
-    }
-
-    resetHidden();
-}
-
-function moveSourceDown()
-{
-    var i,
-        sss = document.prefs.selected_search_sources,
-        sel = sss.selectedIndex,
-        l = sss.length,
-        tmp = [];
-
-    if (sel == -1 || l <= 2) {
-        return;
-    }
-
-    // deselect everything but the first selected item
-    sss.selectedIndex = sel;
-
-    if (sel == l - 1) {
-        for (i = 1; i < l; ++i) {
-            tmp[i - 1] = new Option(sss[i].text, sss[i].value);
-        }
-
-        sss[1] = tmp[tmp.length - 1];
-        for (i = 0, l = tmp.length - 1; i < l; ++i) {
-            sss[i + 2] = tmp[i];
-        }
-
-        sss.selectedIndex = 1;
-    } else {
-        for (i = 1; i < l; ++i) {
-            tmp[i - 1] = new Option(sss[i].text, sss[i].value)
-        }
-
-        for (i = 0, l = tmp.length; i < l; ++i) {
-            if (i + 1 == sel) {
-                sss[i + 1] = tmp[i + 1];
-            } else if (i + 1 == sel + 1) {
-                sss[i + 1] = tmp[i - 1];
-            } else {
-                sss[i + 1] = tmp[i];
-            }
-        }
-
-        sss.selectedIndex = sel + 1;
-    }
-
-    resetHidden();
-}
-
-<?php echo $js ?>
-
-var selectedIndex = false,
-    selectedValue = false,
-    nbSources = <?php echo $nbReadSources ?>;
-<?php if ($nbReadSources == 1): ?>
-selectedIndex = 1;
-selectedValue = "<?php echo $search['sources'][0] ?>";
-<?php endif; ?>
-
-function updateSearchFields()
-{
-    var i, j, l, l2,
-        f = document.prefs,
-        sf = f.search_fields,
-        fieldString = '';
-
-    <?php if ($nbReadSources > 1): ?>
-    var sss = f.selected_search_sources;
-    selectedIndex = sss.selectedIndex;
-    <?php endif; ?>
-
-    while (sf.length > 0) {
-        sf.options[sf.length - 1] = null;
-    }
-
-    if (selectedIndex < 1) {
-        return;
-    }
-
-    <?php if ($nbReadSources > 1): ?>
-    selectedValue = sss.options[selectedIndex].value;
-    <?php endif; ?>
-
-    for (i = 0, l = searchFields.length; i < l; ++i) {
-        if (i > 0) {
-            fieldString += "\n";
-        }
-        fieldString += searchFields[i][0];
-        for (j = 1, l2 = searchFields[i].length; j < l2; ++j) {
-            if (searchFields[i][j][2]) {
-                fieldString += "\t" + searchFields[i][j][0];
-            }
-
-            if (searchFields[i][0] == selectedValue) {
-                sf.options[sf.length] = new Option(searchFields[i][j][1], searchFields[i][j][0]);
-                if (searchFields[i][j][2]) {
-                    sf.options[sf.length - 1].selected = true;
-                }
-            }
-        }
-    }
-
-    f.search_fields_string.value = fieldString;
-}
-
-function changeSearchFields()
-{
-    var i, j, l, l2,
-        f = document.prefs;
-    <?php if ($nbReadSources > 1): ?>
-    var sss = f.selected_search_sources;
-    selectedIndex = sss.selectedIndex;
-    selectedValue = sss.options[selectedIndex].value;
-    <?php endif; ?>
-
-    for (i = 0, l = searchFields.length; i < l; ++i) {
-        if (searchFields[i][0] == selectedValue) {
-            for (j = 1, l2 = searchFields[i].length; j < l2; ++j) {
-                searchFields[i][j][2] = f.search_fields.options[j - 1].selected;
-            }
-            updateSearchFields();
-            return;
-        }
-    }
-}
-
-</script>
-
 <br />
-<input type="hidden" name="search_sources" value="<?php echo implode("\t", $search['sources']) ?>" />
-<?php if ($nbReadSources > 1): ?>
+
+<input type="hidden" name="search_sources" id="search_sources" value="<?php echo implode("\t", $cache['search']['sources']) ?>" />
+<?php if (count($cache['readable']) > 1): ?>
 <?php echo _("Choose the order of address books to search when expanding addresses.") ?><br />
 <table>
  <tr>
   <td>
    <label for="unselected_search_sources" class="hidden"><?php echo _("Available Address books:") ?></label>
-   <select id="unselected_search_sources" name="unselected_search_sources" multiple="multiple" size="5" style="width:20em" onchange="deselectHeaders()">
+   <select id="unselected_search_sources" name="unselected_search_sources" multiple="multiple" size="5" style="width:20em">
     <option value=""><?php echo _("Available Address books:") ?></option>
-    <?php echo $readSelect ?>
+<?php foreach ($cache['readSelect'] as $val): ?>
+    <option value="<?php echo $val ?>"><?php echo $cache['readable'][$val] ?></option>
+<?php endforeach; ?>
    </select>
   </td>
   <td>
-   <a href="#" onclick="addSource(); return false;"><?php echo Horde::img(isset($GLOBALS['nls']['rtl'][$GLOBALS['language']]) ? 'lhand.png' : 'rhand.png', _("Add source"), null, $registry->getImageDir('horde')) ?></a>
+   <a href="#" id="addsource"><?php echo Horde::img(isset($GLOBALS['nls']['rtl'][$GLOBALS['language']]) ? 'lhand.png' : 'rhand.png', _("Add source"), null, $imagedir) ?></a>
    <br />
-   <a href="#" onclick="removeSource(); return false;"><?php echo Horde::img(isset($GLOBALS['nls']['rtl'][$GLOBALS['language']]) ? 'rhand.png' : 'lhand.png', _("Remove source"), null, $registry->getImageDir('horde')) ?></a>
+   <a href="#" id="removesource"><?php echo Horde::img(isset($GLOBALS['nls']['rtl'][$GLOBALS['language']]) ? 'rhand.png' : 'lhand.png', _("Remove source"), null, $imagedir) ?></a>
   </td>
   <td>
    <label for="selected_search_sources" class="hidden"><?php echo _("Selected Address books:") ?></label>
-   <select name="selected_search_sources" multiple="multiple" size="5" style="width:20em" onchange="deselectHeaders();updateSearchFields();">
+   <select name="selected_search_sources" multiple="multiple" size="5" style="width:20em">
     <option value=""><?php echo _("Selected Address books:") ?></option>
-    <?php echo $prefSelect ?>
+<?php foreach ($cache['prefSelect'] as $key => $val): ?>
+    <option value="<?php echo $key ?>"><?php echo $val ?></option>
+<?php endforeach; ?>
    </select>
   </td>
   <td>
-   <a href="#" onclick="moveSourceUp(); return false;"><?php echo Horde::img('nav/up.png', _("Move up"), null, $registry->getImageDir('horde')) ?></a>
+   <a href="#" id="moveup"><?php echo Horde::img('nav/up.png', _("Move up"), null, $imagedir) ?></a>
    <br />
-   <a href="#" onclick="moveSourceDown(); return false;"><?php echo Horde::img('nav/down.png', _("Move down"), null, $registry->getImageDir('horde')) ?></a>
+   <a href="#" id="movedown"><?php echo Horde::img('nav/down.png', _("Move down"), null, $imagedir) ?></a>
   </td>
  </tr>
 </table>
@@ -337,26 +47,27 @@ function changeSearchFields()
 <?php endif; ?>
 
 <?php echo _("To select multiple fields, hold down the Control (PC) or Command (Mac) while clicking.") ?><br />
-<input type="hidden" name="search_fields_string" />
+<input type="hidden" name="search_fields_string" id="search_fields_string" />
 <table>
  <tr>
   <td>
    <label for="search_fields" class="hidden"><?php echo _("Fields to search") ?></label>
-   <select id="search_fields" name="search_fields" multiple="multiple" size="5" style="width:20em" onchange="changeSearchFields()">
+   <select id="search_fields" name="search_fields" multiple="multiple" size="5" style="width:20em">
     <option><?php echo str_repeat('&nbsp;', 50) ?></option>
    </select>
   </td>
  </tr>
 </table>
-
-<script type="text/javascript">
-updateSearchFields();
-</script>
 <?php endif; ?>
 
-<?php if (!$prefs->isLocked('add_source') && !empty($writeSelect)): ?>
- <?php echo _("Choose the address book to use when adding addresses.") ?><br />
- <br />
- <label for="add_source" class="hidden"><?php echo _("Address book to add addresses to") ?></label>
- <select id="add_source" name="add_source"><?php echo $writeSelect ?></select>
+<?php if (!empty($cache['writeSelect'])): ?>
+<?php echo _("Choose the address book to use when adding addresses.") ?><br />
+<br />
+<label for="add_source" class="hidden"><?php echo _("Address book to add addresses to") ?></label>
+<select id="add_source" name="add_source">
+ <option value=""><?php echo _("None") ?></option>
+<?php foreach ($cache['writeSelect'] as $val): ?>
+ <option value="<?php echo $val['val'] ?>"<?php if ($val['sel']) echo ' selected="selected"'; ?>><?php echo $val['name'] ?></option>
+<?php endforeach; ?>
+</select>
 <?php endif; ?>