In the first section of this article I'll demonstrate how to create a drag/drop portal in a few lines of JavaScript code, using the excellent Prototype and Scriptaculous JavaScript libraries. In the second section, I'll explain how to integrate this code into Drupal as a server backend for storing user settings. You may check the frontend here (tested with Firefox 1.5, IE6, and Opera 8.5), and download a reusable JavaScript Portal class and Drupal module for the backend at the bottom of this post.
Portal Frontend
Let's start with the XHTML structure of our portal, the portal will provide three columns for users to arrange their portlets, a list of available portlets to choose from, and the actual portlets. Each column has .portal-column as its class, and each portlet's class is .block (I'll refer to portlets as blocks from now on). The block list is going to be a special portal column identified by #portal-column-block-list as an id.
<div id="portal"> <div class="portal-column" id="portal-column-0"> <h2>Column 0</h2> </div> <div class="portal-column" id="portal-column-1"> <h2>Column 1</h2> </div> <div class="portal-column" id="portal-column-2"> <h2>Column 2</h2> </div> <div class="portal-column" id="portal-column-block-list" style="display: none;"> <h2 class="block-list-handle">Block List</h2> <!-- Blocks go here --> </div> </div>
The block list is not displayed until the user clicks on "Add content". Now let's take a look at each block's structure:
<div class="block"> <h3 class="handle"> <a class="block-toggle"><span>toggle</span></a> <!-- Title --> </h3> <div class="content"> <!-- Content --> </div> </div>
Toggle is a link to hide and show block's content.
Let's take a quick look at the relevant parts of the CSS code that styles our portal elements (I'll omit margin, padding, background and such):
#portal .portal-column { float: left; width: 30%; } #portal-column-block-list { position: absolute; width: 200px; top: 180px; left: 10px; z-index: 10; } #portal .block .block-toggle { background-image: url(block-slide.png); float: right; cursor: pointer; } #portal .block .block-toggle span { display: none; } #portal .block-list-handle, #portal .handle { cursor: move; }
We will use a three-column fluid layout. The block list is absolutely positioned (and will become draggable as we will see shortly). The block toggle button is styled as an image of a triangle, and mouse cursor on block handles is set to move, providing visual feedback that blocks are draggable.
Now for the fun part, making our portal interactive! I'll use the Sortable component of the Script.aculo.us library. This component represents a container with a list of draggable elements inside it, which makes it ideal for our portal interface. The JavaScript code starts with iterating over all elements with .portal-column class in the #portal div, making them sortables:
var sortables = document.getElementsByClassName( 'portal-column', 'portal' ); sortables.each(function (sortable) { Sortable.create(sortable, { containment: sortables, constraint: false, tag: 'div', only: 'block', dropOnEmpty: true, handle: 'handle', hoverclass: 'block-hover', }); });
Sortable.create makes a sortable out of the element passed to it. It takes a list of options. tag and only specify the tag and class of draggable elements inside the sortable. Explanation of other options is available at Sortable.create documentation page.
Next step is activating the toggle button on blocks. To do this, we will iterate over elements with the .block class, and assign an event listener to each toggle:
var blocks = document.getElementsByClassName( 'block', 'portal' ); blocks.each( function (block) { var content = Element.childrenWithClassName( block, 'content', true ); var toggle = Element.childrenWithClassName( block, 'block-toggle', true ); Event.observe( toggle, 'click', function (e) { Effect.toggle(content, 'Slide'); }, false ); } );
For each block, we retrieve the content and toggle elements inside it using Element.childrenWithClassName, then create an event listener that hides/shows content whenever toggle is clicked. We pass 'Slide' to Effect.toggle for a nice drawer effect.
The final step in creating our portal frontend is creating the "Add content" button (#portal-block-list-link) that shows the block list, and making the block list draggable:
Event.observe( 'portal-block-list-link', 'click', displayBlockList, false ); new Draggable('portal-block-list', { handle: 'block-list-handle' } ); function displayBlockList(e) { Effect.toggle('portal-column-block-list'); Event.stop(e); }
And that's it! This is all what we need to make a portal with draggable portlets. You may see a the portal frontend with some sample portlets here. The page is extracted from Drupal and newsportal theme.
At the bottom of this post, there is a packaged version of this portal prototype. portal.js contains a reusable class for creating such portal interfaces. It has a constructor tht takes Prototype-like options object. The following options are available:
| Option | Default | Description |
|---|---|---|
| portal | portal | Id of the containing div for the portal. |
| column | portal-column | Class of portal columns. |
| block | block | Class of portal blocks (portlets). |
| content | content | Class of block's content div. |
| handle | handle | Class of block's handle. |
| hoverclass | block-hover | Hover class for blocks. |
| toggle | block-toggle | Class of block's toggle button. |
| blocklist | portal-column-block-list | Id of block list. |
| blocklistlink | portal-block-list-link | Id of block list toggle button. |
| blocklisthandle | block-list-handle | Class of block list handle. |
| saveurl | (none) | URL to submit column state to. |
In the next section, I'll outline how to save user settings to database by doing Ajax requests. Drupal will be the backend that handles data storage, integrating the code into any well-structured CMS that features the concept of blocks should be quite similar.
Portal Backend
Saving Settings
To monitor and submit user changes to server, we will use Sortable's onUpdate event handler. This function is called whenever a sortable's state is changed (by receiving or losing a block). This callback is defined in the options object passed to Sortable.create:
Sortable.create(sortable, { /* Previously mentioned options here */ onUpdate: function (container) { if (container.id == 'portal-column-block-list') { return; // no need to save block list state. } var url = 'http://www.example.com/portal/save'; var postBody = container.id + ':'; var blocks = document.getElementsByClassName( 'block', container ); postBody += blocks.pluck('id').join(','); postBody = 'value=' + escape(postBody); new Ajax.Request(url, { method: 'post', postBody: postBody } ); } });
The function creates a query of the form: value=container:block1,block2,block3 (...) and submits it to the server. This is done by iterating over the blocks of the changed container. It's worth noting that Sortable has a serialize method with similar functionality. However, it requires special naming convention for element ids. I decided to write my own serialization function to make the code more flexible.
On the server side, Drupal will receive a POST request with the container state. Here is one way to handle it:
<?php $query = split(':', $_POST['container']); $container = $query[0]; $blocks = array(); if (!in_array($container, _portal_column_list())) { return; } $available_blocks = _portal_block_list('list'); foreach (split(',', $query[1]) as $block) { if (array_key_exists($block, $available_blocks)) { $blocks[] = $block; } } db_query("DELETE_FROM {portal_user_settings} WHERE user = '%s' AND container = '%s'", $uid, $container); if ($blocks) { db_query("INSERT_INTO {portal_user_settings} (user, container, blocks) VALUES ('%s', '%s', '%s')", $uid, $container, join(',', $blocks)); }
The code extracts container and blocks ids into $container and $blocks, then verifies that submitted ids are actually available to portal users (to prevent malicious queries from submitting random strings into the database). _portal_column_list() and _portal_block_list('list') return arrays of available column and block ids respectively. If everything is correct, data is saved to the database.
Loading Settings
To load the settings on the client side, Drupal generates a JavaScript associative array that encompasses the settings and embeds it into the page:
<?php $result = db_query("SELECT container, blocks FROM {portal_user_settings} WHERE user = '%s'", $uid); $settings = array(); while ($row = db_fetch_object($result)) { $blocks = array(); foreach (split(',', $row->blocks) as $block) { $blocks[] = "\"$block\""; } $blocks = join(', ', $blocks); $settings[] = "\"$row->container\": [$blocks]"; } $settings = join(', ', $settings); $output = ''; $output .= "\n<script type=""><!--\n"; $output .= "var settings = \{$settings};\n"; /* More JavaScript code added here */ $output .= "--></script>\n"; return $output;
The resulting array will look something like:
var settings = { container1: [block1, block2], container2: [block3], ... };
settings variable is now available for our JavaScript code to rearrange blocks when the portal loads according to user preference:
for (var container in settings) { settings[container].each(function (block) { $(container).appendChild($(block)); });
As you see, the code is pretty self-explanatory. It iterates over settings, and appends each block to its container.
This should cover the server side section. The code demonstrated here covers saving and loading user's portal settings. Attached below is the Drupal module from which code excerpts were taken. It's designed to nicely integrate with Drupal blocks. Any block can be used as a portlet, and developers need only implement hook_block to add portlets. Admins may specify what blocks are available as portlets, and both anonymous and registered users may maintain their own portals. The code is designed to gracefully degrade if JavaScript is disabled.
- Download the portal frontend.
- Download the Drupal module for the portal backend.












Comments
DaveNotik
Thrilled. Been wanting to do this for quite some time. Just today, revisited this need, and hours later I see your post.
I'd like to get this working for my Workspace service (http://www.wovenlabs.com/workspace), allowing every user to have a personalized Workspace home page. Using Views, I'd like to provide all kinds of blocks that the user can place -- latest issues, all issues assigned to me, latest posts within this particular project, favorite pages, and so on. Really an excellent use case.
I'm also building a service called MyCommunity.org -- an uber Yahoo Groups, if you will, and can imagine many uses there.
Can this be written to take advantage of jQuery, the JavaScript library that's now in Drupal core, or will these third-party libraries be needed?
Maybe I missed this: does your module handle keeping track of the placement of the blocks server-side -- so the user can log in elsewhere and see the page they created?
Thanks for your work. I'll keep following.
Posted at 1:40 a.m. on September 4, 2006
Jeff Eaton
Dave, if you're looking to implement something like this in jQuery you should probably check out http://interface.eyecon.ro/ -- it's a library of interface plugins for jQuery designed to duplicated prototype's functionality.
Posted at 3:28 a.m. on September 4, 2006
Mark
This is excellent Ayman, I really hope you do make a drupal project/module for this. And it makes a lot of sense that the workspace module uses this as a base, as suggested by Dave. Thanks for a Great article sharing this.
Posted at 8:02 a.m. on September 4, 2006
Ayman Hourieh
Cool :) Glad you found this post interesting. Regarding your questions: I'm more familiar with Prototype so I chose it to implement this. Porting to jQuery is a tempting option now that it's part of Core. However, it will still require a 3rd-party plugin for drag/drop (the one Jeff linked to).
The module does actually save user settings. I edited the fist paragraph to clearly mention this.
Thanks for your feedback.
Posted at 1:04 p.m. on September 4, 2006
Ayman Hourieh
Thanks for your reply, and indeed Workspace and this module can greatly benefit from each other. I'll research porting the JavaScript code to jQuery and creating a Drupal project for this.
Posted at 1:06 p.m. on September 4, 2006
Blue Cobalt
Is the Drupal module supposed to automatically save user settings?
It isn't doing so for me. I come back to the portal page, and there are no blocks saved.
Drupal 4.7.3, Firefox 1.5.0.6 & Safari 2.0.4, Mac OS X.
This is an incredible module. Thanks so much!
Posted at 6:03 p.m. on September 4, 2006
Ayman Hourieh
I investigated a bit and found an issue that prevented saving from working when clean URLs were disabled. Fixed it now. Would you please try again?
Thanks for the feedback.
Posted at 6:34 p.m. on September 4, 2006
Blue Cobalt
Thanks Ayman. It works great now!
Posted at 6:50 p.m. on September 4, 2006
Joe Moraca
Great demo. I am going to try it out. Thanks for sharing your work on this project.
Posted at 7:50 p.m. on September 4, 2006
FiReaNG3L
I can't wait to see your jQuery version... I was interested in building such a thing (but didn't have a clue of how-to). Very helpful!
Posted at 12:42 a.m. on September 5, 2006
Sami Khan
I think one should use cookies to store the settings for users not logged in, I believe that's what Google and Netvibes does. Using this on the blocks would be a prudent step as well. Most of these types of applications however are completely based in javascript, since the whole purpose is simply to aggregate content from many different sources on the user's browser all you need is the XMLHTTPRequest object. It looks pretty but in terms of functionality and purpose, it's rather limited. Also you have a problem in terms of being able to actively update the content in those blocks as it's not ajax, if Drupal had a more powerful AJAX framework to support the backend and then you could make AJAX calls to get that content, then such a portal solution might be cool and useful -- barring that it's rather limited and clunky to implement for an actual web application. Would be cool however if your users could reposition your blocks and get rid of the ones that they didn't want using this method as is the case with Wordpress Canvas. Thanks for sharing though, it might be useful to some people not all that familiar with the domain that just want some additional cool functionality on their site.
Posted at 5:17 p.m. on September 5, 2006
alf
This seems great, I'll be looking forward to a JQuery version as well.
Posted at 1 p.m. on September 6, 2006
Dries
In one word: hot. Rock on, Ayman. Oh, and aim for core!
Posted at 7:29 a.m. on September 7, 2006
Ayman Hourieh
Thanks. I did some work on porting the code to jQuery today. Will post to drupal.org once I have something working.
Posted at 5:55 p.m. on September 7, 2006
Jerry
I've been working on implementing a similiar CMS component just over the past week, and your demo just gave me a whole bunch of new ideas. Thanks!
Kudos for sharing your work, too :)
Posted at 4:50 a.m. on September 9, 2006
Mark
I just discovered earl miles (merlin of choas) wonderful panels module, and it struck me that your UI would be just great for panels module. I may be wrong but it may be fairly easy to integrate your interface for use with his panels module. looking forward to news on your jquery version..
Posted at 12:08 p.m. on September 18, 2006
Ayman Hourieh
I'm sorry for the delay. I have something almost working in jQuery. I'm just trying to find time to polish and release it. Hopefully, I'll do so in the next few days.
I've just checked the panels module and indeed my work is a logical extension to it. Thanks for pointing this out. We can discuss how to integrate them once I have the jQuery port ready.
Posted at 1:38 p.m. on September 18, 2006
Christoph Cemper
Wow... this is an amazin post... and the sample rock too... thank you very much for sharing... you are now on my rss reader :-)
christoph
Posted at 9:21 a.m. on September 23, 2006
bugz_nz
Hey Ayman,
This is a great module, I look forward to customising it and using it soon. There seems to be a script error in the portal.module file on line 143.
it reads:
When I think it should read:
Posted at 9:16 p.m. on September 24, 2006
Ayman Hourieh
Hi,
Thanks everyone for the feedback. The last two weeks were very busy for me and I haven't had time to finish the jQuery port yet. :(
bugz_nz, I double-checked and it looks like you are right. the syntax I used seems to work with versions prior to PHP 5.1(?), but for 5.1 it prints the backslash which is undesirable.
Posted at 3:06 p.m. on September 25, 2006
Ella
Hey Ayman
Thanks for the great tutorial. I am having a bit of trouble setting the default blocks that I want to appear in the page when it first loads. I am using the following syntax but the line below:
var settings = { portal-column-0: [block1, block2], portal-column-1: [block4], portal-column-2: [block5] };keeps throwing a javascript error:
Error: Expected ':'
Am I missing a colon somewhere?
Many thanks,
Ella
Posted at 5:32 p.m. on September 25, 2006
Ella
The error was due to the hypens in the column names, I renamed my div id's and the following code now works:
var settings = {portalcolumn0: [block1, block2], portalcolumn1: [block4], portalcolumn2: [block5]};Posted at 7:14 p.m. on September 25, 2006
inanc
but, how to make a block not removed from block-list after dragged upon one of the three columns.
drag a to the middle column and drag a to rightmost column so we have got two a's, cloning and never disappears from the leftmost block list.
Posted at 2:30 a.m. on September 28, 2006
inanc
and... how to make a block removed from one of the columns (droppables) after clicked on a button on'em just like that toggle guy.
Posted at 2:31 a.m. on September 28, 2006
Hasan
Hi Ayman
This is the great module, thanks for that. I am having a bit problem. When I move a block within the same column then it does not post AJAX request. I am stuck on this, pls help.
Thanks,
Hasan
Posted at 7:32 a.m. on October 5, 2006
Anonymous
Hi Hasan,
you can fix our problem by disabling the test of observer element in onEnd function : line 575 of dragdrop.js. For me it works fine now.
Thanks a lot for Ayman to this example.
Pat.
Posted at 9:27 a.m. on November 5, 2006
vik
Hi Ayman, the tutorial is undoubtedly very good and i have been looking for something on these lines for quite a long time...many many thanks.
just 1 small query, instead of using DIV, can i use Table just by giving the appropriate elements the right id, class name.
i think that should be possible by changing the tag attribute in "sortables.create", i tried it but it did not work and throws an error 'addEventListener' is null or not an object. would be great if you can help.
Thanks vik
Posted at 11:22 p.m. on November 6, 2006
Jason
Hi Ayman, this is a really great demo I have a question: 1) add two content to 'column0', the order is 'content0','content1' 2) if drag 'content0' to 'column1', the onUpdate is invoked, but while draging 'content0' in 'column0', the onUpdate will not be invoked
I don't know whether it is a bug, can you help me about this, ddrok@163.com Thanks
Posted at 11:59 a.m. on November 15, 2006
Anonymous
Ayman,
I uploaded the portal module in my drupal modules directory and got it enabled. I could see the three columns in the portal, but when I chose "add content" it said that the portal - block list is not implemented. I just couldn't seem to figure out how to get that implemented.
Thanks Nic
Posted at 12:36 a.m. on November 16, 2006
Jason
I think Pat's solution will cause a performance issue, it will post all column's info to server
Posted at 10:05 a.m. on November 16, 2006
Ayman Hourieh
1) Do you have JavaScript enabled in your browser?
2) What browser are you using?
3) Does the demo hosted here work?
Posted at 10:33 p.m. on November 17, 2006
Jason
Can you give me some help or suggestions on this issue, with my great thanks
Posted at 2:57 p.m. on November 26, 2006
Anonymous
Hi!
This is great! :) :)
Could you tell me though, how you are parsing RSS Feeds? Are you doing it yourself or is there some ready-made code online... if yes, i'd be grateful if you could share it..!(especially if it is in Javascript...) Many Thanks!
S
Posted at 6:47 a.m. on December 5, 2006
Timmy
This: http://www.geocities.com/timmy33479//demo.html is what I am trying to do. What is the easiest way to save the positions of the yellow squares on the server and without a database? I am just starting to mess with Scriptaculous, but I have to have a demo of this ready in 3 days. Help please!
Posted at 9:31 p.m. on December 5, 2006
Ram
Hey Ayman,
I would like to know when are you planning to update this module to work in Drupal5.
Posted at 5:21 a.m. on December 7, 2006
Ram
Hi Ayman..,
The portal module is great.. Iam trying to make columns of different widths.. I want to place portlets with large content in one column and portlets with small content into different column.. I tried to change the CSS but could not achieve that.. Is it possible to have columns of different widths?? Please Help me out. Thanks in Advance
Posted at 7:18 a.m. on December 12, 2006
Anonymous
Hi Ayman,
first many thanks for this great tutorial.
I updated script.aculo.us to v.1.6.5 und now i'm just getting an Error: "Element.childrenWithClassName is not a function"
Can you fix this, please?
I thik, a Fix may be necessary, v1.7 (still beta) will coming up with nice features.
Best Regards
ps. sorry for my english, i'm not a native speaker. :)
Posted at 4:41 a.m. on January 2, 2007
graham
Is there an easy/natural way to join this with the mysite module?
Fantastic work, btw.
Posted at 12:57 p.m. on January 4, 2007
Jon
Awesome post.
Had it working but then upgraded scriptaculous and now I get the same error as a post above "Element.childrenWithClassName is not a function"
Any ideas?
-Jon
Posted at 3:05 p.m. on January 16, 2007
Jon
I fixed the problem, kind of.
I downloaded the prototype.js / scriptaculous.js / dragdrop.js / effects.js file you use on your demo and it works fine now - so there is something with the latest version that is a little incompatible with your toggle and the block list appearing code.
I have a real question actually 2 -
Is there a way to get the div ID of the block you just dropped? I also noticed that the onUpdate doesn't get called when I change the order of blocks in the same column / container. Is this correct as well?
Again, This is fantastic ! Thanks for posting this.
-Jon
Posted at 3:39 p.m. on January 16, 2007
Tom
Hi I was wondering if there was a way to fix that jittering effect you get while your trying to swap panels. I noticed that when I try to change the order of the panels in the Block List the panels swap back and forth very fast while your draging over them. Is there anyway to fix this so that it has a smooth transition? It is a little jarring to the user.
I noticed that it does that sometimes when your trying to swap panels in the columns as well. They flicker back and forth until you drop it into place. When you are dragging something over another panel, they switch places very fast while moving the mouse. It looks amateuristic and would like to know if you can fix that or is it something done behind the scenes in the lib?
Thanks.
Posted at 10:04 p.m. on January 26, 2007
Anonymous
How do I use the Module>?>
Posted at 6:02 p.m. on January 29, 2007
Vader
First of all, thanks for the great article!
I'm having problems with the call Element.childrenWithClassName, FireFox just returns an error. Is there anyway around this?
Posted at 9:49 a.m. on February 2, 2007
Vader
As an answer to my self: var content = document.getElementsByClassName('content', block)[0]; this will do the same trick.
Posted at 10:25 a.m. on February 2, 2007
Jimmy Page
I have the same problem too. Tried it both in IE and Firefox .
Posted at 4:18 p.m. on February 7, 2007
bo
hi,
i managed to make the portlets in my jetspeed 2 portal movable by drag with the help of this article. now i would like to know how to save the position of the portlets. any idea? would be great if anyone could help me....
regards, bo
Posted at 6:41 p.m. on February 18, 2007
redwall_hp
I'm trying to create a backend for the portal without using Drupal or anything like that. Okay, the portal.js sends an AJAX query over to save.php. How do i process the query so it's in a form that can be stored in a MySQL database? The code in your blog post didn't work. It gives errors about nonexistant functions. I can't figure out what to do on my own either. I'd appreciate hep if anyone knows what to do. I basically need some php source to put in save.php that will process the query and insert it into a database table (same fields as example in blog post: user,countainer,blocks I beleive).
Posted at 5:18 p.m. on February 19, 2007
Quote Maniac
I've been thinking of migrating to drupal for my site for a long time. It's currently using Wordpress but after looking at the awesome and smooth effect you create, I'm now seriously going to consider migrating.
Posted at 4:58 a.m. on March 21, 2007
kirill
Hi, great post, thank you for sharing!
However I wonder if anyone noticed a bug in IE that happens in the following scenario.
1) drag Calendar into "Column 1" 2) drag deli.cio.us into "Column 2" 3) drag Google News into "Column 0". This causes "Column 2" with its contents to jump underneath "Column 1".
Thoughts?
Thanks, kirill
Posted at 6:24 p.m. on March 21, 2007
dustin
Some people mentioned a problem with saving rearranged columns and also saving empty columns.
I integrated this app with ruby on rails and had to make sure to write a blank array [] when the column was empty... without doing that, it wouldn't work... After I did that, it worked fine.
Thanks Ayman for such a wonderful app! Dustin
Posted at 6:40 p.m. on April 3, 2007
Geoff
Hi,
I am have some trouble loading the users setting from the cookie. I am storing the column and box order in a cookie and then on page load I'm writing the divs to the page and then getting the content for the boxes via an AJAX call. Everything is working fine, but the divs that are writing to the page are not sortable. The hide/show toggle works, and all the same classes are applied to the divs as are applied to the divs in the block-list div so I'm confused. I'd love some help with this.
Cheers, Geoff
Posted at 3:49 a.m. on April 27, 2007
Tiziano
Hi!
Above all thank you for such an interesting article! I'm trying to build a customized page with widgets in Drupal and I found it very useful. Now, I have a question for you: it seems that the onUpdate event is fired only if a draggable element is moved from a sortable container to another one. Is it possible to fire it also when the user changes the order of the elements inside the same container? I can't manage to save elements order if the user changes it (within the same container).
Thank you again. Tiziano
Posted at 1:45 p.m. on May 14, 2007
Kevin
This is exactly what I was looking for for one of my projects! The only problem is that the performance seems to be lacking as you drag more and more content into the portal. I have tested with about 80 items and there is some serious lag in dragging. I tested out the iGoogle personalized homepage after adding about 80 items to the page and the dragging worked without any lag. Unfortunately I need to support around 80 items on my site and won't be able to use this because of the drag lag. Keep in mind that I am using a brand new Intel Core2 Duo based computer, so the hardware is not holding it back any. Other than the speed, this is a really nice portal interface! Any help with speeding it up would be great. Kevin
Posted at 11:22 p.m. on May 16, 2007
Alfred
Same problem here...
Posted at 2:05 p.m. on June 4, 2007
Andy Mortland
Ayman - this is awesome - exactly what we need for our application. However, we are using the 1.7 version of scriptaculous, in which the 'childrenWithClassName' method has been removed. As this is a crucial component of your script, I wondered if you had ever reworked the script to work with the 1.7 libraries.
I've been trying to make do by altering portal.js, and by changing the method calls to use the getElementsByClassName, but have had little success.
Any pointers would be much appreciated. Thanks!
Posted at 5:01 p.m. on June 5, 2007
Doug
Hello, I am using the YUI Grid template to structure layouts for a portal framework. The goal is to create a portal in which users can define his/her own layout. An example for such portal could be iGoogle. But iGoogle or most available portal out there have very limited layout structures. For example iGoogle only allows you to create 2 or 3 columns portal layout. What if I want something like a one-column layout on top of 3 -column layout?
Inspired your article, I have the idea of combining what proposed in this article with the YUI grid framework to create a very flexible portal layout.
Please see it for yourself at
http://www2.cs.uh.edu/~dmly/portal/layoutBuilder.html
After creating your own portal layout click on "get This Layout" button to go to the next screen with your layout built and a set of demo portlet for you to drag and drop to the layout to see how it work.
I test this code with Firefox 2.0 and the latest version of IE (7.0) Please let me know if you have any suggestion. I would like to make it an extensible framework, probably in Ruby on Rails first as a proof of concept.
Regards,
Doug
Posted at 1:28 a.m. on June 23, 2007
Barker
I've recently been playing around with your example, and having upgraded to the latest version of prototype (v1.5.1), which also requires you to upgrade script.aculo.us (to v1.7.1 b3).
I see that Element.childrenWithClassName has been removed from the library and you'll need to replace the two lines that use it with Element.getElementsByClassName.
Excellent tutorial, it's been very useful, Cheers
Posted at 10:22 a.m. on July 6, 2007
Tom Jenkins
Hi there
I have used and implemented the above code and it works perfectly. I've had to change the database delete / insert / select into coldfusion and a few other bits and bobs but it still works really nicely. One problem I have though is saving the state of change within a single column.
For example:
If you drag the calendar into column0 then drag who's online into column0 BELOW the calendar, this work perfectly. If i then place the who's online ABOVE the calendar the onUpdate is not triggered meaning this ordering change is not saved to the database.
How would i make it so you can re order items in the same column?
Thank you for help in advance
Tom Jenkins
Posted at 1:39 p.m. on July 9, 2007
Tom Jenkins
Hi there
I posted here yesterday but nothing has come of it as yet. I really need help with the question that i submitted. Please help.
Kind Regards
Tom Jenkins
Posted at 8:27 a.m. on July 10, 2007
Adam
I wrote a solution to the childrenWithClassName issue. Im currently using scriptaculous 1.7.1_beta3 with prototype 1.5.1.1 and everything works great.
In the portal.js file replace:
var content = Element.childrenWithClassName( block, this.options.content, true );
WITH:
var content = block.getElementsByClassName(this.options.content)[0]; var toggle = block.getElementsByClassName(this.options.toggle)[0];
Posted at 4:38 p.m. on July 10, 2007
Anonymous
Ella,
After removing the hyphens from all of the div IDs, I'm now receving block1 is not defined. I too was getting the Error: Expected ':' before. How did you precent the not defined issue?
Posted at 1:15 p.m. on October 10, 2007
Devender Dagar
Hi Sami
Did you find some solution for cookie save ? I am also looking for same functionality and working on this for the last one week. Do you have some idea for doing this ?
I am very good in coding as well as in analysis but really I have no idea for doing this. I am trying some options. Let see how much time it takes?
If you have some logic please let me know so that I can implement it.
Posted at 11:47 a.m. on June 18, 2008
Anonymous
Hi Ayman,
Thanks for this great module. I have got a bit problem. I want to use this module to make a similar system like drag and drop shopping cart. I mean i want one column to be fixed with certain contents which will always be draggable only and another column which is always droppable. I want the content from the draggable area to be dropped to the droppable area keeping the content of the draggable area as it is along with scrolling the page as draggable is dragged.
In Scriptaculous module, for this there is an option of revert: true and for scroll: scroll: window. I am tryig to use it in this module, but isn't getting any idea how to use it.
I tried by adding revert and scroll in the following part:
new Draggable(this.options.blocklist, { handle: this.options.blocklisthandle,
revert: true, scroll: window,
in portal.js file, but it doesn't works,
Can you give any help or suggestions regarding this problem.
Thanks
Posted at 12:32 p.m. on July 7, 2008
Chanchal Gandhwani
Hi Ayman,
Thanks for this great stuff. I have a question can you please suggest how can i add a ondrop event handler
Thanks
Posted at 5:56 p.m. on July 15, 2008
dflorence
the code $_POST['container']); should be $_POST['value]);, as the .js script sets the name of the data posted to "value."'
Posted at 8:13 p.m. on September 4, 2008
Cabada
Where should i put the loading code?
Posted at 8:48 a.m. on October 5, 2008
Cabada
Can i see that source?
Posted at 7:14 p.m. on October 5, 2008
Cabada
How can i make that one window go just to the 2nd and 3rd column, and not letting it going to the first column?
HELP PLEASE!
Posted at noon on October 6, 2008
Mr. Low
This code is not working with Internet explorer; is when the user loads the interface from the DataBase
for (var container in settings) { settings[container].each(function (block) { $(container).appendChild($(block)); });
Please, answer me.
Posted at 7:14 a.m. on October 7, 2008
Bronson
Great stuff y've designed.
My question is how can'i limit the number of item in backend.
Ie, if front end carry 10 items backend is not eligible for more than 5 items, i can't figure out how to set this upright (in meantime trigger alert popup window to advise the visitor of that fact when trying to exceed this limit.)
Help would vrey meuch appreciated..
Rgds
Posted at 8:50 a.m. on October 29, 2008
anon
i like your tutorial many thanks but i am using this script outside drupal, and i was wondering if you could help me write a cookie to remember the block position? many thanks
Posted at 11:21 p.m. on November 22, 2008
RJ
When I tried to get this running with the latest Scriptaculous 1.8.2 and Prototype 1.6.0.3, I found that different methods of Class, Element, Sortable, Draggable, and so on, require the classes represented in different ways. (IDs of elements don't even require a '#' to be identified, which is also inconsistent in my opinion, but it's off-topic in this context.) What I mean here is that, at some places I need to prefix the class name from this.options with '.', and for others I don't need to. Otherwise, the $$(...) and $(...) return null or nothing at all.
I'd hope in the future versions IDs would be represented with prefix '#', Classes with a prefix '.', and HTML tags with no prefixes.
Posted at 7:30 p.m. on January 8, 2009
RJ
Oh by the way, great tutorial and thank you so much! :)
Posted at 7:31 p.m. on January 8, 2009
Rich
Thanks for the tutorial Ayman, I've got most of this all working fine after converting to coldfusion but I'm having some issues with the load part of it. When I pull back the data from my query I get the following error message:
block1 is not defined
I was having some issues with the container being referenced as well but saw that after dropping the hyphens and renaming columns to portalcolumn0 etc that worked but would appreciate any help from someone who can put me on the right track for getting this last part up and running.
Posted at 2:07 p.m. on February 6, 2009
Rich
Put "" characters around each block element and it works! Fantastic.
Posted at 10:05 a.m. on February 10, 2009
Matty B
This is a great script, however what if you wanted a block to span all columns? how would this be achieved.
Posted at 1:35 p.m. on March 6, 2009
Michel
Hi,
I noticed the script was not working with the newest prototype, but it was exactly what i needed so i kindof recoded your script to the newest prototype. And i added some extra features to it like a 'config' option for the block and delete button.
For those interested:
Its not optimal, stil working on it to get the data input to load the blocks dynamicly
Posted at 8:46 a.m. on March 10, 2009
Stella
Nice tutorial Ayman! Thanks for posting it here. However, I have a query before I switch some of my sites to drupal. is it possible to shift or transfer wordpress blogs (self-hosted) to drupal without losing any data?
Waiting for your reply!
Stella.
Posted at 1:49 a.m. on March 19, 2009
Art
Will it work with drupal 6? Are you planning in having a theme like that for drupal, or wordpress that would be great, we will be able to make sites lik the bbcs one I think they copied your idea http://www.bbc.co.uk/
Posted at 12:36 a.m. on April 3, 2009
Flora
i like your tutorial many thanks but i am using this script outside drupal, and i was wondering if you could help me write a cookie to remember the block position? many thanks Flora
Posted at 1:50 p.m. on April 27, 2009
Paul
Great sample. is there a way where one could save the settings so that if i login back i can get the settings?
Thanks Paul
Posted at 7:25 p.m. on May 27, 2009
Paul
Hi
I am not using Drupal. I am using a JSP running on Apache . Is there a way to store/restore the user settings?
Thanks P
Posted at 6:44 p.m. on May 28, 2009
remiglobal
i think, somebody else has written earlier that you have to disable some test @ some lines. but somehow this method is slow.
another method is to detect when you have stopped dragging. upon sensing that you have stopped dragging, you can detect the container and use ayman's method of serializing the widget. If you can code carefully, you will only need to send the data back once.
happy hacking, and thanks to ayman for this great stuff. It took me one whole day to digest the above problem :-)
Posted at 1:55 a.m. on June 1, 2009
Anonymous
I am wondering the same thing... Will this work with the recent version of Drupal?
Posted at 4:37 p.m. on June 17, 2009
Paul
I am trying to display the page with a pre-defined settings. For the sample code i am trying to set
var settings = { portal-column-0: [block-archive-0, block-aggregator-feed-2], portal-column-2: [block-user-3] };
but i am not getting the desired results?
Any help?
Posted at 5:06 p.m. on June 17, 2009
Paul
Would it be possible for you to post the source?
Posted at 5:32 p.m. on June 22, 2009
Nirav
i have got your Example. but i have create this t ype of Application using Asp.net Web Part. Plz help me
Posted at 10:54 a.m. on August 13, 2009
nuwan
When the block is moved within same column The OnUpdate event is not fired ? any suggestions ?
Posted at 11:54 a.m. on August 27, 2009
Les Dunaway
Thanks for some insight into Drupal. I'm using WordPress for my personal development blog and I've been curious about Drupal.
Posted at 3:33 p.m. on May 2, 2010
Crinch
Hi, It is a great post, i am familiar with drupal's basic module structure, i have installed drupal 6.2 and have placed theme in theme directory and modules folder in sites/all/modules but neither module nor theme is being seen in theme listing and module listing to make them active. Is there anymore I am to do. Thanks
Posted at 1:48 p.m. on February 8, 2011
Mayank Sharma
Anyone got the solution of the problem
"the block moved within same column The OnUpdate event is not fired"
Please help
Thanks
Posted at 8:17 a.m. on March 2, 2011
Kyle Faucett
To save the movement of blocks in the same container, try commenting out this line in the onEnd function (by prepending //):
if(this.lastValue != Sortable.serialize(this.element))
Posted at 8:04 a.m. on April 2, 2011
George
Is there a way to get the div to redraw when moved from column to column.
I want the same item to look different in column 1 than when its in column 2
Great code. Thanks
Posted at 7:51 p.m. on April 3, 2011
ashiwebi
Can we add such funcntionaloty in panel front end, so that user can drag and drop the blocks from front page without going to admin interface.
Posted at 10:04 a.m. on June 6, 2011
vivek
Does this support in all the browsers?? I am having problem with IE, the column3 is coming down the column 2 and the widgets are not properly aligned in the IE
Posted at 11:28 a.m. on October 5, 2011