? .homebox.module.swp
Index: homebox-block.tpl.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/homebox/homebox-block.tpl.php,v
retrieving revision 1.1.4.9
diff -u -F '^f' -r1.1.4.9 homebox-block.tpl.php
--- homebox-block.tpl.php	2 Jul 2010 19:38:19 -0000	1.1.4.9
+++ homebox-block.tpl.php	16 Sep 2010 00:35:29 -0000
@@ -9,23 +9,30 @@
 ?>
 <div id="homebox-block-<?php print $block->module .'-'. $block->delta; ?>" class="<?php print $block->homebox_classes ?> clear-block block block-<?php print $block->module ?>">
   <div class="homebox-portlet-inner">
-    <h3 class="portlet-header"><span class="portlet-title"><?php print $block->subject ?></span></h3>
+    <h3 class="portlet-header">
+      <?php if ($block->closable): ?>
+        <a class="portlet-icon portlet-close"></a>
+      <?php endif; ?>
+      <a class="portlet-icon portlet-maximize"></a>
+      <a class="portlet-icon portlet-minus"></a>
+      <?php if ($page->settings['color'] || isset($block->edit_form)): ?>
+        <a class="portlet-icon portlet-settings"></a>
+      <?php endif; ?>
+      <span class="portlet-title"><?php print $block->subject ?></span>
+    </h3>
     <div class="portlet-config">
       <?php if ($page->settings['color']): ?>
-        <div class="homebox-colors">
+        <div class="clear-block"><div class="homebox-colors">
           <span class="homebox-color-message"><?php print t('Select a color') . ':'; ?></span>
           <?php for ($i=0; $i < HOMEBOX_NUMBER_OF_COLOURS; $i++): ?>
             <span class="homebox-color-selector" style="background-color: <?php print $page->settings['colors'][$i] ?>;">&nbsp;</span>
           <?php endfor ?>
-        </div>
+        </div></div>
       <?php endif; ?>
       <?php if ($block->module == 'homebox'): ?>
         <button id="delete-<?php print $block->module . '_' . $block->delta; ?>" class="homebox-delete-custom-link"><?php print t('Delete'); ?></button>
-        <button id="edit-<?php print $block->module . '_' . $block->delta; ?>" class="homebox-edit-custom-link"><?php print t('Edit'); ?></button>
       <?php endif; ?>
-      <?php if ($page->settings['color'] || $block->module == 'views' && !is_null($filters)): ?>
-        <div class="clear-block"></div>
-      <?php endif ?>
+      <?php if (isset($block->edit_form)): print $block->edit_form; endif; ?>
     </div>
     <div class="portlet-content content"><?php print $block->content; ?></div>
     <?php print $block->hidden; ?>
Index: homebox.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/homebox/homebox.css,v
retrieving revision 1.1.4.25
diff -u -F '^f' -r1.1.4.25 homebox.css
--- homebox.css	2 Jul 2010 19:49:39 -0000	1.1.4.25
+++ homebox.css	16 Sep 2010 00:35:29 -0000
@@ -134,7 +134,7 @@
 /*
 ** Closed box/portlet class
 */
-.homebox-portlet-closed {
+#homebox .homebox-portlet-closed {
   display: none;
 }
 
Index: homebox.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/homebox/homebox.js,v
retrieving revision 1.1.4.36
diff -u -F '^f' -r1.1.4.36 homebox.js
--- homebox.js	30 Jul 2010 02:54:53 -0000	1.1.4.36
+++ homebox.js	16 Sep 2010 00:35:29 -0000
@@ -3,111 +3,29 @@
 Drupal.behaviors.homebox = function(context) {
   $homebox = $('#homebox:not(.homebox-processed)', context).addClass('homebox-processed');
   
-  // Prevent double-clicks from causing a selection
-  $(".portlet-header").disableSelection();
-  
   if ($homebox.length > 0) {
     // Find all columns
-    $columns = $homebox.find('div.homebox-column');
+    Drupal.homebox.$columns = $homebox.find('div.homebox-column');
     
     // Equilize columns height
-    $columns = Drupal.homebox.equalizeColumnsHeights($columns);
+    Drupal.homebox.equalizeColumnsHeights();
     
     // Make columns sortable
-    $columns.sortable({
+    Drupal.homebox.$columns.sortable({
       items: '.homebox-portlet.homebox-draggable',
       handle: '.portlet-header',
-      connectWith: $columns,
+      connectWith: Drupal.homebox.$columns,
       placeholder: 'homebox-placeholder',
       forcePlaceholderSize: true,
       over: function() {
-        Drupal.homebox.equalizeColumnsHeights($columns);
+        Drupal.homebox.equalizeColumnsHeights();
       },
       stop: function() {
-        Drupal.homebox.equalizeColumnsHeights($columns);
+        Drupal.homebox.equalizeColumnsHeights();
         $('#homebox-changes-made').show();
       }
     });
     
-    // Add tools links
-    $boxes = $homebox.find('.homebox-portlet');
-    $boxes.find('.portlet-config').each(function() {
-      if (jQuery.trim($(this).html()) != '') {
-        $(this).prev('.portlet-header').prepend('<div class="portlet-icon portlet-settings"></div>').end();
-      };
-    });
-    $boxes.find('.portlet-header').prepend('<div class="portlet-icon portlet-minus"></div>')
-        .prepend('<div class="portlet-icon portlet-close"></div>')
-        .end();
-        
-    // Remove close tool for unclosable blocks
-    $homebox.find('.homebox-unclosable div.portlet-close').remove();
-    
-    // Add maximize link to every portlet
-    $boxes.find('.portlet-header .portlet-minus').before('<div class="portlet-icon portlet-maximize"></div>');
-    
-    // Add region to place maximized portlets
-    $homebox.find('.homebox-column-wrapper:first').before('<div class="homebox-maximized"></div>');
-    
-    // Attach click event to maximize icon
-    $boxes.find('.portlet-header .portlet-maximize').click(function() {
-      $(this).toggleClass("portlet-maximize");
-      $(this).toggleClass("portlet-minimize");
-      Drupal.homebox.maximizeBox(this);
-      Drupal.homebox.equalizeColumnsHeights($columns);
-    });  
-    
-    // Attach click event on minus
-    $boxes.find('.portlet-header .portlet-minus').click(function() {
-      $(this).toggleClass("portlet-minus");
-      $(this).toggleClass("portlet-plus");
-      $(this).parents(".homebox-portlet:first").find(".portlet-content").toggle();
-      Drupal.homebox.equalizeColumnsHeights($columns);
-      $('#homebox-changes-made').show();
-    });
-    
-    // Attach click event on minus
-    $boxes.find('.portlet-header .portlet-minus').each(function() {
-      if (!$(this).parents(".homebox-portlet:first").find(".portlet-content").is(':visible')) {
-        $(this).toggleClass("portlet-minus");
-        $(this).toggleClass("portlet-plus");
-        Drupal.homebox.equalizeColumnsHeights($columns);
-      };
-    });
-    
-    // Attach double click event on portlet header
-    $boxes.find('.portlet-title').dblclick(function() {
-      if ($(this).parents(".homebox-portlet:first").find(".portlet-content").is(':visible')) {
-        $(this).parent('.portlet-header').find('.portlet-minus').toggleClass("portlet-plus");  
-        $(this).parent('.portlet-header').find('.portlet-minus').toggleClass("portlet-minus");
-      }
-      else {
-        $(this).parent('.portlet-header').find('.portlet-plus').toggleClass("portlet-minus");
-        $(this).parent('.portlet-header').find('.portlet-plus').toggleClass("portlet-plus"); 
-      }
-      
-      $(this).parents(".homebox-portlet:first").find(".portlet-content").toggle();
-      
-      Drupal.homebox.equalizeColumnsHeights($columns);
-      $('#homebox-changes-made').show();
-    });
-    
-    // Attach click event on settings icon
-    $boxes.find('.portlet-header .portlet-settings').click(function() {
-      $(this).parents(".homebox-portlet:first").find(".portlet-config").toggle();
-      Drupal.homebox.equalizeColumnsHeights($columns);
-    });
-    
-    // Attach click event on close
-    $boxes.find('.portlet-header .portlet-close').click(function() {
-      $(this).parents(".homebox-portlet:first").hide();
-      // Uncheck input settings
-      dom_id = $(this).parents(".homebox-portlet:first").attr('id');
-      $('#homebox_toggle_' + dom_id).attr('checked', false);
-      Drupal.homebox.equalizeColumnsHeights($columns);
-      $('#homebox-changes-made').show();
-    });
-    
     // Add click behaviour to checkboxes that enable/disable blocks
     $togglers = $homebox.find('#homebox-settings input.homebox_toggle_box');
     $togglers.click(function() {
@@ -119,43 +37,12 @@
         el_id = $(this).attr('id').replace('homebox_toggle_', '');
         $('#' + el_id).hide();
       };
-      Drupal.homebox.equalizeColumnsHeights($columns);
+      Drupal.homebox.equalizeColumnsHeights();
       $('#homebox-changes-made').show();
     });
     
-    // Apply custom colors to blocks
-    $boxes.each(function() {
-      var attributes = $(this).attr('class').split(' ');
-      for (a in attributes) {
-        if (attributes[a].substr(0, 14) == 'homebox-color-') {
-          $(this).find('.portlet-header').attr("style", "background: #" + attributes[a].substr(14));
-          $(this).find('.homebox-portlet-inner').attr("style", "border: 1px solid #" + attributes[a].substr(14));
-        }
-      }
-    });
-    
-    // Add click behaviour to color buttons
-    $boxes.find('.homebox-color-selector').click(function() {
-      color = $(this).css('background-color');
-      classes = $(this).parents(".homebox-portlet:first").attr('class').split(" ");
-      jQuery.each(classes, function(key, value) {
-        if (value.indexOf('homebox-color-') == 0) {
-          classes[key] = "";
-        };
-      });
-      classes = classes.join(" ");
-      
-      // Add color classes to blocks
-      // This is used when we save so we know what color it is
-      $(this).parents(".homebox-portlet:first").attr('class', classes);
-      $(this).parents(".homebox-portlet:first").addClass("homebox-color-" + Drupal.homebox.convertRgbToHex(color).replace("#", ''));
-      
-      // Apply the colors via style attributes
-      // This avoid dynamic CSS
-      $(this).parents(".homebox-portlet:first").find('.portlet-header').attr("style", "background: " + Drupal.homebox.convertRgbToHex(color));
-      $(this).parents(".homebox-portlet:first").find('.homebox-portlet-inner').attr("style", "border: 1px solid " + Drupal.homebox.convertRgbToHex(color));
-      $('#homebox-changes-made').show();
-    });
+    // Add region to place maximized portlets
+    $homebox.find('.homebox-column-wrapper:first').before('<div class="homebox-maximized"></div>');
     
     // Initialize popup dialogs
     Drupal.homebox.initDialogs();
@@ -169,33 +56,7 @@
     
     // Equalize column heights after AJAX calls
     $homebox.ajaxStop(function(){
-      Drupal.homebox.equalizeColumnsHeights($columns);
-    });
-    
-    // Add tooltips to icons
-    $('.portlet-icon').tipsy({
-      gravity: 's',
-      title: function() {
-        switch ($(this).attr('class').replace('portlet-icon portlet-', '')) {
-          case 'close':
-            return Drupal.t('Close');
-          case 'maximize':
-            return Drupal.t('Maximize');
-          case 'minimize':
-            return Drupal.t('Minimize');
-          case 'minus':
-            return Drupal.t('Collapse');
-          case 'plus':
-            return Drupal.t('Expand');
-          case 'settings':
-            return Drupal.t('Settings');
-        }
-      }
-    });
-    
-    // Remove tooltips on header clicks
-    $boxes.find('.portlet-header').click(function() {
-      $('.tipsy').remove();
+      Drupal.homebox.equalizeColumnsHeights();
     });
   }
 };
@@ -254,27 +115,6 @@
     }
   });
   
-  // Edit item dialog
-  $('#homebox-edit-form').dialog({
-    autoOpen: false,
-	  modal: true,
-    zIndex: 500,
-    width: 500,
-    height: 350,
-    buttons: {
-		  'Submit': function() {
-        Drupal.homebox.editItem($(this).find('input:hidden').val());
-        $('#homebox-changes-made').show();
-      },
-      Cancel: function() {
-        $('#homebox-edit-form-status').hide();
-        $('#homebox-edit-form-title').val('');
-        $('#homebox-edit-form-content').val('');
-				$(this).dialog('close');
-			}
-    }
-  });
-  
   // Restore to default in-progress dialog
   $('#homebox-restore-inprogress').dialog({
     autoOpen: false,
@@ -333,28 +173,14 @@
     );
     $('#homebox-delete-custom-message').dialog('open'); 
   });
-  
-  // Edit custom item link
-  $('.homebox-edit-custom-link').click(function() {
-    // Place the block ID into the dialog
-    $('#homebox-edit-form input:hidden').val(
-      $(this).attr('id').replace('edit-', '')
-    );
-    // Populate the title field
-    $('#homebox-edit-form-title').val($(this).parents('.homebox-portlet').find('.portlet-title').html());
-    // Populate the content field
-    $('#homebox-edit-form-content').val($(this).parents('.homebox-portlet').find('.portlet-content').html().HTMLtoNewline());
-    // Open the dialog
-    $('#homebox-edit-form').dialog('open'); 
-  });
 };
 
 /*
  * Set all column heights equal
  */
-Drupal.homebox.equalizeColumnsHeights = function(columns) {
+Drupal.homebox.equalizeColumnsHeights = function() {
   maxHeight = 0;
-  $columns.each(function() {
+  Drupal.homebox.$columns.each(function() {
     if ($(this).parent('.homebox-column-wrapper').attr('style') != 'width: 100%;') {
       $(this).height('auto');
       currentHeight = $(this).height();
@@ -367,8 +193,6 @@
       $(this).height(maxHeight);
     }
   });
-  
-  return $columns;
 };
 
 /*
@@ -384,9 +208,9 @@
   
   $.ajax({
     url: Drupal.settings.basePath + '?q=homebox/js/restore',
-    type: "POST",
-    cache: "false",
-    dataType: "json",
+    type: 'POST',
+    cache: 'false',
+    dataType: 'json',
     data: {name: name},
     success: function() {
       location.reload(); // Reload page to show defaults
@@ -499,9 +323,9 @@
 Drupal.homebox.addItemAjax = function(name, block) {
   $.ajax({
     url: Drupal.settings.basePath + '?q=homebox/js/add',
-    type: "POST",
-    cache: "false",
-    dataType: "json",
+    type: 'POST',
+    cache: 'false',
+    dataType: 'json',
     data: {name: name, block: block},
     success: function() {
       $('#homebox-add-form').html(Drupal.t('Refreshing page') + '...');
@@ -523,9 +347,9 @@
   
   $.ajax({
     url: Drupal.settings.basePath + '?q=homebox/js/delete',
-    type: "POST",
-    cache: "false",
-    dataType: "json",
+    type: 'POST',
+    cache: 'false',
+    dataType: 'json',
     data: {name: name, block: block},
     success: function() {
       $('#homebox-delete-custom-message').html(Drupal.t('Refreshing page') + '...');
@@ -556,11 +380,11 @@
   // Show progress dialog
   $('#homebox-save-message').dialog('open');
 
-  $columns = Drupal.homebox.equalizeColumnsHeights($columns);
-  $columns.each(function(colIndex) {
+  Drupal.homebox.equalizeColumnsHeights();
+  Drupal.homebox.$columns.each(function(colIndex) {
     // Determine region
     var colIndex = colIndex + 1;
-    $(this).find('>.homebox-portlet').each(function(boxIndex) {
+    $(this).find('.homebox-portlet').each(function(boxIndex) {
       // Determine block name
       block = $(this).find('input:hidden.homebox').val();
       
@@ -580,7 +404,7 @@
       }
       
       // Determine state (open/closed)
-      open = $(this).find(".portlet-content").is(':visible');
+      open = $(this).find('.portlet-content').is(':visible');
 
       // Build blocks object
       if (block.search('homebox_') != -1) {
@@ -593,7 +417,9 @@
           title: $(this).find('.portlet-title').html().stripTags(),
           content: $(this).find('.portlet-content').html(),
           module: 'homebox',
-          delta: block.replace('homebox_', '')
+          delta: block.replace('homebox_', ''),
+          closable: 1,
+          movable: 1
         }
       }
       else {
@@ -612,9 +438,9 @@
   
   $.ajax({
     url: Drupal.settings.basePath + '?q=homebox/js/save',
-    type: "POST",
-    cache: "false",
-    dataType: "json",
+    type: 'POST',
+    cache: 'false',
+    dataType: 'json',
     data: {name: name, blocks: blocks},
     success: function() {
       $('#homebox-save-message').dialog('close');
@@ -632,43 +458,6 @@
   });
 };
 
-/*
- * Edit a custom item
- * 
- * @param block
- *   The block ID being edited
- */
-Drupal.homebox.editItem = function(block) {
-  var title = $('#homebox-edit-form-title').val().stripTags();
-  var content = $('#homebox-edit-form-content').val();
-  
-  // Make sure both fields are supplied
-  if (!title || !content) {
-    $('#homebox-edit-form-status').show();
-    $('#homebox-edit-form-status').html(Drupal.t('All fields are required.'));
-    return;
-  }
-  
-  // Alter block ID to match block class
-  block = block.replace('_', '-');
-  
-  // Convert newlines to HTML
-  content = content.newlineToHTML();
-  
-  // Replace block title with input
-  $('#homebox-block-' + block).find('.portlet-title').html(title);
-  $('#homebox-block-' + block).find('.portlet-content').html(content);
-  
-  // Clear form and close dialog
-  $('#homebox-edit-form-status').hide();
-  $('#homebox-edit-form-title').val('');
-  $('#homebox-edit-form-content').val('');
-	$('#homebox-edit-form').dialog('close');
-  
-  // Equalize columns
-  Drupal.homebox.equalizeColumnsHeights($columns);
-};
-
 Drupal.homebox.convertRgbToHex = function(rgb) {
   if (!jQuery.browser.msie) {
     // Script taken from
@@ -680,7 +469,7 @@
       parts[i] = parseInt(parts[i]).toString(16);
       if (parts[i].length == 1) parts[i] = '0' + parts[i];
     }
-    return "#" + parts.join(''); // "0070ff"
+    return '#' + parts.join(''); // '0070ff'
   } else {
     return rgb;
   };
@@ -693,10 +482,130 @@
 
 // Replace newline characters with HTML breakrules
 String.prototype.newlineToHTML = function() {
-  return this.replace(/\r?\n|\r/g, "<br />");
+  return this.replace(/\r?\n|\r/g, '<br />');
 };
 
-// Replace HTML breakfules with newline characters
-String.prototype.HTMLtoNewline = function() {
-  return this.replace(/<br>|<br\/>|<br \/>/gi, "\n");
-};
+Drupal.behaviors.homeboxPortlet = function (context) {
+  $('.homebox-portlet:not(.homebox-processed)', context).addClass('homebox-processed').each(function () {
+    var $portlet = $(this),
+      $portletHeader = $portlet.find('.portlet-header');
+
+    // Prevent double-clicks from causing a selection
+    $portletHeader.disableSelection();
+
+    // Attach click event to maximize icon
+    $portletHeader.find('.portlet-maximize').click(function() {
+      $(this).toggleClass('portlet-maximize');
+      $(this).toggleClass('portlet-minimize');
+      Drupal.homebox.maximizeBox(this);
+      Drupal.homebox.equalizeColumnsHeights();
+    });  
+    
+    // Attach click event on minus
+    $portletHeader.find('.portlet-minus').click(function() {
+      $(this).toggleClass('portlet-minus');
+      $(this).toggleClass('portlet-plus');
+      $portlet.find('.portlet-content').toggle();
+      Drupal.homebox.equalizeColumnsHeights();
+      $('#homebox-changes-made').show();
+    });
+    
+    // Attach click event on minus
+    $portletHeader.find('.portlet-minus').each(function() {
+      if (!$portlet.find('.portlet-content').is(':visible')) {
+        $(this).toggleClass('portlet-minus');
+        $(this).toggleClass('portlet-plus');
+        Drupal.homebox.equalizeColumnsHeights();
+      };
+    });
+    
+    // Attach double click event on portlet header
+    $portlet.find('.portlet-title').dblclick(function() {
+      if ($portlet.find('.portlet-content').is(':visible')) {
+        $portletHeader.find('.portlet-minus').toggleClass('portlet-plus');  
+        $portletHeader.find('.portlet-minus').toggleClass('portlet-minus');
+      }
+      else {
+        $portletHeader.find('.portlet-plus').toggleClass('portlet-minus');
+        $portletHeader.find('.portlet-plus').toggleClass('portlet-plus'); 
+      }
+      
+      $portlet.find('.portlet-content').toggle();
+      
+      Drupal.homebox.equalizeColumnsHeights();
+      $('#homebox-changes-made').show();
+    });
+    
+    // Attach click event on settings icon
+    $portletHeader.find('.portlet-settings').click(function() {
+      $portlet.find('.portlet-config').toggle();
+      Drupal.homebox.equalizeColumnsHeights();
+    });
+    
+    // Attach click event on close
+    $portletHeader.find('.portlet-close').click(function() {
+      $portlet.hide();
+      // Uncheck input settings
+      $('#homebox_toggle_' + $portlet.attr('id')).attr('checked', false);
+      Drupal.homebox.equalizeColumnsHeights();
+      $('#homebox-changes-made').show();
+    });
+
+    var attributes = $portlet.attr('class').split(' ');
+    for (a in attributes) {
+      if (attributes[a].substr(0, 14) == 'homebox-color-') {
+        $portletHeader.attr('style', 'background: #' + attributes[a].substr(14));
+        $portlet.find('.homebox-portlet-inner').attr('style', 'border: 1px solid #' + attributes[a].substr(14));
+      }
+    }
+    
+    // Add click behaviour to color buttons
+    $portlet.find('.homebox-color-selector').click(function() {
+      color = $(this).css('background-color');
+      classes = $portlet.attr('class').split(' ');
+      jQuery.each(classes, function(key, value) {
+        if (value.indexOf('homebox-color-') == 0) {
+          classes[key] = '';
+        };
+      });
+      classes = classes.join(' ');
+      
+      // Add color classes to blocks
+      // This is used when we save so we know what color it is
+      $portlet.attr('class', classes);
+      $portlet.addClass('homebox-color-' + Drupal.homebox.convertRgbToHex(color).replace('#', ''));
+      
+      // Apply the colors via style attributes
+      // This avoid dynamic CSS
+      $portletHeader.attr('style', 'background: ' + Drupal.homebox.convertRgbToHex(color));
+      $portlet.find('.homebox-portlet-inner').attr('style', 'border: 1px solid ' + Drupal.homebox.convertRgbToHex(color));
+      $('#homebox-changes-made').show();
+    });
+    
+    // Add tooltips to icons
+    $portlet.find('.portlet-icon').tipsy({
+      gravity: 's',
+      title: function() {
+        switch ($(this).attr('class').replace('portlet-icon portlet-', '')) {
+          case 'close':
+            return Drupal.t('Close');
+          case 'maximize':
+            return Drupal.t('Maximize');
+          case 'minimize':
+            return Drupal.t('Minimize');
+          case 'minus':
+            return Drupal.t('Collapse');
+          case 'plus':
+            return Drupal.t('Expand');
+          case 'settings':
+            return Drupal.t('Settings');
+        }
+      }
+    });
+    
+    // Remove tooltips on header clicks
+    $portletHeader.click(function() {
+      $('.tipsy').remove();
+    });
+  });
+}
Index: homebox.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/homebox/homebox.module,v
retrieving revision 1.2.2.41
diff -u -F '^f' -r1.2.2.41 homebox.module
--- homebox.module	11 Aug 2010 00:12:34 -0000	1.2.2.41
+++ homebox.module	16 Sep 2010 00:35:29 -0000
@@ -165,9 +165,14 @@ function homebox_load($name) {
 /**
  * Implementation of hook_forms().
  */
-function homebox_forms() {
-  $forms['homebox_admin_new_page']['callback'] = 'homebox_admin_page';
-  return $forms;
+function homebox_forms($form_id, $args) {
+  switch ($form_id) {
+    case 'homebox_admin_new_page':
+      return array('homebox_admin_new_page' => array('callback' => 'homebox_admin_page'));
+
+    case isset($args[1]) && is_object($args[1]) ? 'homebox_block_edit_' . $args[1]->module . '_' . $args[1]->delta . '_form' : NULL:
+      return array($form_id => array('callback' => 'homebox_block_edit_form_builder'));
+  }
 }
 
 /**
@@ -176,19 +181,14 @@ function homebox_forms() {
 function homebox_help($path, $arg) {
   switch ($path) {
     case 'admin/build/homebox':
-      $output = '<p>'. theme('advanced_help_topic', 'homebox', 'introduction') .'&nbsp;';
-      $output .= t("Homebox pages are listed below. Each page is accessible from a single url on your site that you specify during page creation. You can create as many pages as you need. Be sure to review all available layout options and settings.</p>");
-      break;
+      return '<p>'. theme('advanced_help_topic', 'homebox', 'introduction') .'&nbsp;' . t("Homebox pages are listed below. Each page is accessible from a single url on your site that you specify during page creation. You can create as many pages as you need. Be sure to review all available layout options and settings.</p>");
+
     case 'admin/build/homebox/layout/%':
-      $output = '<p>'. theme('advanced_help_topic', 'homebox', 'default-layout') .'&nbsp;';
-      $output .= t("This page behave the same way as Drupal block administration page. Drag blocks to whatever column you want to enable it for your users. Note that you can change the number of columns in the <a href='!settings_url'>settings page</a>", array('!settings_url' => url('admin/build/homebox/settings/'. arg(4)))) .'.</p>';
-      break;
+      return '<p>'. theme('advanced_help_topic', 'homebox', 'default-layout') .'&nbsp;' . t("This page behave the same way as Drupal block administration page. Drag blocks to whatever column you want to enable it for your users. Note that you can change the number of columns in the <a href='!settings_url'>settings page</a>", array('!settings_url' => url('admin/build/homebox/settings/'. arg(4)))) .'.</p>';
+
     case 'admin/build/homebox/settings/%':
-      $output = '<p>'. theme('advanced_help_topic', 'homebox', 'settings') .'&nbsp;';
-      $output .= t('Homebox configuration page.');
-      break;
+      return '<p>'. theme('advanced_help_topic', 'homebox', 'settings') .'&nbsp;' . t('Homebox configuration page.');
   }
-  return $output;
 }
 
 /**
@@ -205,11 +205,11 @@ function homebox_theme($blocks) {
   return array(
     // Set hook name: see template_preprocess_homebox()
     'homebox' => array(
-      'arguments' => array('regions' => $regions, 'available_blocks' => $available_blocks, 'column_count' => $column_count, 'page' => $page),
+      'arguments' => array('regions' => NULL, 'available_blocks' => NULL, 'column_count' => NULL, 'page' => NULL),
       'template' => 'homebox', // Set template to homebox.tpl.php
     ),
     'homebox_block' => array(
-      'arguments' => array('block' => $block, 'page' => $page),
+      'arguments' => array('block' => NULL, 'page' => NULL),
       'template' => 'homebox-block', // Set template to homebox-block.tpl.php
     ),
     'homebox_admin_display_form' => array(
@@ -315,123 +315,200 @@ function homebox_pre_build_user($page, $
  *  An array of regions/columns containing blocks ordered by region / weight (see home-box.tpl.php)
  */
 function homebox_load_blocks_in_regions($page) {
-  global $user;
-
-  $regions = array();
   $column_count = $page->settings['regions'];
+  $regions = array_fill(1, $column_count, array());
   $blocks = $page->settings['blocks'];
-  $is_cache_enabled = (bool) $page->settings['cache'];
 
   // Apply customization from user, if any
   $blocks = _homebox_apply_user_settings($page, $blocks);
 
   // Preparing blocks object for theming
   foreach ($blocks as $key => $block_settings) {
-    // Build block
-    $block = new stdClass;
-    $block->subject = $block_settings['title'];
-    $block->content = $block_settings['content'];
-    $block->module = $block_settings['module'];
-    $block->delta = $block_settings['delta'];
-    $block->region = (int) $block_settings['region'];
-    $block->weight = (int) $block_settings['weight'];
-    $block->status = $block_settings['status'] ? TRUE : FALSE;
-    $block->open = $block_settings['open'] ? TRUE : FALSE;
-    $block->closable = ($block_settings['closable'] === 0) ? FALSE : TRUE;
-    $block->homebox_classes = _homebox_get_css_classes_for_block($block_settings);
-
-    // Check block permissions
-    if(!_homebox_can_view_block($block, $user)) {
-      // Permission denied, skip to the next block
-      continue; 
+    // Adds block to its regions
+    $block = homebox_prepare_block($key, $block_settings, $page);
+    if (!is_null($block)) {
+      // If user defined region is greater than real column count put block in
+      // the last column/region.
+      $regions[min($block->region, $column_count)][$block->weight][] = $block;
     }
+  }
 
-    // Attempt to find a block in the cache table
-    // No reason to render block content if the module is homebox
-    // because it's a custom item
-    if ($block->module != 'homebox') {
-      if ($is_cache_enabled == TRUE && !count(module_implements('node_grants')) && $_SERVER['REQUEST_METHOD'] == 'GET' && ($cid = _block_get_cache_id($block)) && ($cache = cache_get($cid, 'cache_block'))) {
-        $array = $cache->data;
-      }
-      else {
-        // No cache, fetch the blocks from modules
-        $array = module_invoke($block->module, 'block', 'view', $block->delta);
-        
-        // Block.module will return 'n/a' if a custom block has been deleted
-        if ($array['content'] == 'n/a') {
-          // If this is the case, skip this block
-          continue; 
-        }
-        if (isset($cid)) {
-          cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY);
-        }
-      }
+  // Sort each region/column based on key value
+  for ($i = 1; $i <= count($regions); $i++) {
+    ksort($regions[$i]);
+  }
 
-      // Render block content
-      if (isset($array) && is_array($array)) {
-        foreach ($array as $k => $v) {
-          // If block has custom title, leave it
-          if($k == 'subject' && !empty($block->subject)) {
-            continue;
-          }
-          $block->$k = $v;
-        } 
-      }
+  return $regions;
+}
+
+function homebox_prepare_block($block_key, $block_settings, $page) {
+  global $user;
+
+  // Build block
+  $block = new stdClass;
+  $block->subject = $block_settings['title'];
+  $block->content = isset($block_settings['content'])? $block_settings['content'] : '';
+  $block->module = $block_settings['module'];
+  $block->delta = $block_settings['delta'];
+  $block->region = (int) $block_settings['region'];
+  $block->weight = (int) $block_settings['weight'];
+  $block->status = (bool) $block_settings['status'];
+  $block->open = (bool) $block_settings['open'];
+  $block->closable = (bool) $block_settings['closable'];
+  $block->homebox_classes = _homebox_get_css_classes_for_block($block_settings);
+  if (module_hook($block->module, 'homebox_block_keys')) {
+    foreach (module_invoke($block->module, 'homebox_block_keys', $block) as $key) {
+      $block->$key = isset($block_settings[$key]) ? $block_settings[$key] : NULL;
     }
-    
-    // We don't continue to assign this block
-    // since Drupal didn't returned any content
-    // which could be permissions rules applied by any module
-    if ($block->content && trim($block->content) != '' || $can_access_view) {
-      // If no title provided we try to get one from blocks table
-      if (!$block->subject) {
-        $block->subject = db_result(db_query("SELECT title FROM {blocks} WHERE bid = %d", $block->bid));
-      }
-      if (!$block->subject && $block->module == 'views') {
-        $block->subject = _homebox_get_view_name($block);
-      }
-      if (!$block->subject) {
-        $module_blocks = module_invoke($block->module, 'block', 'list');
-        $block->subject = $module_blocks[$block->delta]['info'];
-      }
-      // Fail safe
-      if (!$block->subject) {
-        // Is this still necessary?
-        $block->subject = t('<em>No title defined</em>');
-      }
+  }
 
-      // Mostly ugly for now, but we need this for performance issue when saving blocks ordering this prevent many queries!
-      $block->hidden .= '<input type="hidden" class="homebox" value="' . $key . '" />';
+  // Check block permissions
+  if(!_homebox_can_view_block($block, $user)) {
+    // Permission denied, skip to the next block
+    return NULL; 
+  }
+
+  // Attempt to find a block in the cache table No reason to render block
+  // content if the module is homebox because it's a custom item.
+  if ($block->module != 'homebox') {
+    if ((bool) $page->settings['cache'] && !count(module_implements('node_grants')) && $_SERVER['REQUEST_METHOD'] == 'GET' && ($cid = _block_get_cache_id($block)) && ($cache = cache_get($cid, 'cache_block'))) {
+      $array = $cache->data;
+    }
+    else {
+      // No cache, fetch the blocks from modules
+      $array = module_invoke($block->module, 'block', 'view', $block->delta, array('homebox' => $block));
 
-      // If user defined region is greater than
-      // real column count put block in the last column/region
-      if ($block->region > $column_count) {
-        $block->region = $column_count;
+      // Block.module will return 'n/a' if a custom block has been deleted
+      if ($array['content'] == 'n/a') {
+        // If this is the case, skip this block
+        return NULL; 
+      }
+      if (isset($cid)) {
+        cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY);
       }
+    }
 
-      // Adds block to its regions
-      $regions[$block->region][$block->weight][] = $block;
+    // Render block content
+    if (isset($array) && is_array($array)) {
+      foreach ($array as $k => $v) {
+        // If block has custom title, leave it
+        if($k == 'subject' && !empty($block->subject)) {
+          return NULL;
+        }
+        $block->$k = $v;
+      } 
     }
   }
 
-  // Sort each region/column based on key value
-  for ($i = 1; $i <= count($regions); $i++) {
-    if (is_array($regions[$i]) && count($regions[$i]) > 0) {
-      ksort($regions[$i]);
-    }
+  // We don't continue to assign this block since Drupal didn't returned any
+  // content which could be permissions rules applied by any module.
+  if (!isset($block->content) || trim($block->content) === '') {
+    return NULL;
   }
 
-  // Fill region array to match defined column count
-  for ($i=1; $i <= $column_count; $i++) {
-    if (!isset($regions[$i])) {
-      $regions[$i] = array();
-    }
+  // If no title provided we try to get one from blocks table
+  if (!$block->subject) {
+    $block->subject = db_result(db_query("SELECT title FROM {blocks} WHERE bid = %d", $block->bid));
+  }
+  if (!$block->subject && $block->module == 'views') {
+    $block->subject = _homebox_get_view_name($block);
+  }
+  if (!$block->subject) {
+    $module_blocks = module_invoke($block->module, 'block', 'list');
+    $block->subject = $module_blocks[$block->delta]['info'];
+  }
+  // Fail safe
+  if (!$block->subject) {
+    // Is this still necessary?
+    $block->subject = t('<em>No title defined</em>');
   }
-  
-  // Sorts region/column
-  ksort($regions);
 
-  return $regions;
+  // Mostly ugly for now, but we need this for performance issue when saving blocks ordering this prevent many queries!
+  $block->hidden .= '<input type="hidden" class="homebox" value="' . $block_key . '" />';
+
+  if (module_hook($block->module, 'homebox_block_edit_form')) {
+    $block->edit_form = drupal_get_form('homebox_block_edit_' . $block->module . '_' . $block->delta . '_form', $page, $block);
+  }
+
+  return $block;
+}
+
+/**
+ * Get an edit form from the implementing module and add the standard buttons
+ * and submit handling.
+ */
+function homebox_block_edit_form_builder(&$form_state, $page, $block) {
+  $form = module_invoke($block->module, 'homebox_block_edit_form', $block);
+
+  $form['#attributes']['class'] = 'clear-block';
+  $form['save'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+    '#ahah' => array(
+      'path' => $_GET['q'],
+      'event' => 'click',
+      'wrapper' => 'homebox-block-' . $block->module .'-'. $block->delta,
+      'method' => 'replaceWith',
+    ),
+  );
+  $form['#submit'][] = 'homebox_block_edit_form_builder_submit';
+
+  return $form;
+}
+
+function homebox_block_edit_form_builder_validate($form, &$form_state) {
+  $block = $form['#parameters'][3];
+  module_invoke($block->module, 'homebox_block_edit_form_validate', $form, $form_state);
+}
+
+/**
+ * Save settings for the block and render a replacement with the updated settings.
+ */
+function homebox_block_edit_form_builder_submit($form, $form_state) {
+  $page = $form['#parameters'][2];
+  $block = $form['#parameters'][3];
+  $user_blocks = _homebox_get_user_settings($page);
+  if ($user_blocks === FALSE) {
+    // This is the first customization, save everything to get everything
+    // initially populated.
+    _homebox_save_user_settings($page, $page->settings['blocks']);
+    $user_blocks = _homebox_get_user_settings($page);
+  }
+  $key = $block->module . '_' . $block->delta;
+
+  // Make sure needed keys exist.
+  $user_blocks[$key] = array_merge(array_flip(module_invoke($block->module, 'homebox_block_keys', $block)), $user_blocks[$key]);
+
+  // Replace user settings with form values, when keys match.
+  $user_blocks[$key] = array_merge($user_blocks[$key], array_intersect_key($form_state['values'], $user_blocks[$key]));
+  _homebox_save_user_settings($page, $user_blocks);
+
+  // Do not process the submitted form again, it would infinite loop.
+  $_POST['form_id'] = NULL;
+  print drupal_json(array(
+    'status' => TRUE,
+    'data' => theme('homebox_block', homebox_prepare_block($key, homebox_merge_settings($page->settings['blocks'][$key], $user_blocks[$key]), $page), $page),
+  ));
+  exit;
+}
+
+function homebox_homebox_block_edit_form($block) {
+  $form = array();
+
+  $form['title'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Title'),
+    '#size' => 25,
+    '#default_value' => $block->subject,
+  );
+  $form['content'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Content'),
+    '#default_value' => $block->content,
+    '#required' => TRUE,
+  );
+
+  return $form;
 }
 
 /*
@@ -1172,7 +1249,7 @@ function _homebox_get_css_classes_for_bl
   }
 
   // Adds color css class
-  if ($block['color'] && $block['color'] != 'default') {
+  if (isset($block['color']) && $block['color'] != 'default') {
     $classes[] = 'homebox-color-' . drupal_strtolower($block['color']);
   }
   
@@ -1243,7 +1320,7 @@ function homebox_pages($api = TRUE) {
  *   TRUE if custom, otherwise FALSE
  */
 function homebox_block_is_custom($key) {
-  return (substr($key, 0, 8) == 'homebox_') ? "TRUE" : "FALSE";
+  return (substr($key, 0, 8) === 'homebox_');
 }
 
 /*
@@ -1278,18 +1355,21 @@ function _homebox_save_user_settings($pa
   // This function is only called after user status
   // and perms were checked
   global $user;
+  $user_blocks = _homebox_get_user_settings($page);
   
   $i = 0;
   foreach ($blocks as $key => $value) {
     // Add weights to blocks
     $blocks[$key]['weight'] = ++$i;
-    
+
     // Prevent crafty users from closing unclosable blocks
-    if (!$page->settings['blocks'][$key]['closable']) {
-      // Only matters if it isn't a custom block
-      if (!homebox_block_is_custom($key)) {
-        $blocks[$key]['status'] = 1;
-      }
+    if (!homebox_block_is_custom($key) && !$page->settings['blocks'][$key]['closable']) {
+      $blocks[$key]['status'] = 1;
+    }
+
+    // Carry over existing settings
+    if ($user_blocks !== FALSE) {
+      $blocks[$key] = homebox_merge_settings($user_blocks[$key], $blocks[$key]);
     }
   }
   
@@ -1337,7 +1417,7 @@ function _homebox_user_access_save_homeb
   global $user;
   
   // Only authenticated users can save
-  if ($user->uid) {
+  if ($user->uid && isset($_POST['name'])) {
     // Get current page
     $page = homebox_get_page(filter_xss(trim($_POST['name'])));
     // Make sure the page exists
Index: homebox.tpl.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/homebox/homebox.tpl.php,v
retrieving revision 1.1.4.18
diff -u -F '^f' -r1.1.4.18 homebox.tpl.php
--- homebox.tpl.php	2 Jul 2010 19:49:39 -0000	1.1.4.18
+++ homebox.tpl.php	16 Sep 2010 00:35:29 -0000
@@ -66,18 +66,6 @@
       </div>
     </form>
   </div>
-  <div id="homebox-edit-form" title="<?php print t('Edit item'); ?>">
-    <form class="form-item" action="">
-      <div id="homebox-edit-form-status"></div>
-      <div>
-        <label><?php print t('Title'); ?></label>
-        <input type="text" name="title" id="homebox-edit-form-title" class="" />
-        <label><?php print t('Content'); ?></label>
-        <textarea name="content" id="homebox-edit-form-content" class="" rows="5" cols="5"></textarea>
-        <input type="hidden" value="" />
-      </div>
-    </form>
-  </div>
   <div id="homebox-delete-custom-message" title="<?php print t('Deletion confirmation'); ?>">
     <?php print t('Are you sure you want to delete this item? This action cannot be undone.'); ?>
     <input type="hidden" value="" />
