This was done on a uPortal 3.0.1 quickstart.
Upgrade the jQuery library
The later release of jQuery makes everything work better. Change the jquery reference in the list of linked javacsript in TEMPLATE: PAGE JAVASCRIPT in universality.xsl (the main theme file).
Change from this:
<script type="text/javascript" src="{$SCRIPT_PATH}/jquery/jquery-1.2.3.min.js"></script>
To this:
<script type="text/javascript" src="{$SCRIPT_PATH}/jquery/jquery-1.2.6.min.js"></script>
In this template:
<!-- ========== TEMPLATE: PAGE JAVASCRIPT ========== --> <!-- =============================================== --> <!-- | YELLOW | This template renders the Javascript links in the page <head>. | Javascript provides AJAX and enhanced client-side interaction to the portal. | Javascript files are located in the uPortal skins directory: | /media/skins/[theme_name]/common/javascript/ | Template contents can be any valid XSL or XHTML. --> <xsl:template name="page.js"> <xsl:if test="$USE_AJAX='true' or $USE_FLYOUT_MENUS='true'"> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/jquery-1.2.3.min.js"></script> </xsl:if> <xsl:if test="$USE_AJAX='true' and $AUTHENTICATED='true'"> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/jquery.dimensions.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/ui.dialog.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/ui.resizable.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/ui.mouse.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/ui.draggable.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/ui.droppable.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/ui.sortable.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/ui.tabs.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/interface.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/ajax-preferences-jquery.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/up-channel-browser.js"></script> </xsl:if> </xsl:template>
Theme files are located at:
- \WEB-INF\classes\layout\theme\universality (deployed)
- \uportal-war\src\main\resources\layout\theme\universality (source)
Disable the uPortal default drag and drop
Comment out the script that initializes the default drag and drop from ajax-preferences-jquery.js:
// initialize portlet drag and drop // ajaxPref('div[@id*=inner-column_]').each(function(i){ // ajaxPref(this).Sortable({ // accept : 'movable', // helperclass : 'dropborder', // opacity : 0.5, // handle : 'div.portlet-toolbar', // onStop : movePortlet // }); // });
Javascript files are located at:
- \media\skins\universality\common\javascript (deployed)
- \uportal-war\src\main\webapp\media\skins\universality\common\javascript (source)
Add the Fluid Layout Customizer component
Add Fluid-all.js to the theme javascript directory.
Javascript files are located at:
- \media\skins\universality\common\javascript (deployed)
- \uportal-war\src\main\webapp\media\skins\universality\common\javascript (source)
Link to the Fluid component
Add this link to the Fluid-all.js file at the bottom of the list of linked javacsript in TEMPLATE: PAGE JAVASCRIPT in universality.xsl (the main theme file).
<!-- ========== TEMPLATE: PAGE JAVASCRIPT ========== --> <!-- =============================================== --> <!-- | YELLOW | This template renders the Javascript links in the page <head>. | Javascript provides AJAX and enhanced client-side interaction to the portal. | Javascript files are located in the uPortal skins directory: | /media/skins/[theme_name]/common/javascript/ | Template contents can be any valid XSL or XHTML. --> <xsl:template name="page.js"> <xsl:if test="$USE_AJAX='true' or $USE_FLYOUT_MENUS='true'"> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/jquery-1.2.6.min.js"></script> </xsl:if> <xsl:if test="$USE_AJAX='true' and $AUTHENTICATED='true'"> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/jquery.dimensions.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/ui.dialog.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/ui.resizable.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/ui.mouse.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/ui.draggable.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/ui.droppable.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/ui.sortable.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/ui.tabs.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/jquery/interface.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/ajax-preferences-jquery.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/up-channel-browser.js"></script> <script type="text/javascript" src="{$SCRIPT_PATH}/Fluid-all.js"></script> </xsl:if> </xsl:template>
Theme files are located at:
- \WEB-INF\classes\layout\theme\universality (deployed)
- \uportal-war\src\main\resources\layout\theme\universality (source)
Add drop warning for locked content
Add the following html for the drop warning message in TEMPLATE: FOOTER BLOCK in universality.xsl (the main theme file):
<!-- ========== TEMPLATE: FOOTER BLOCK ========== --> <!-- ============================================ --> <!-- | GREEN | This template renders custom content into the page footer. | Template contents can be any valid XSL or XHTML. --> <xsl:template name="footer.block"> ... <div id="portalDropWarning"> <p>The box cannot be moved here. The box currently located here is locked.</p> </div> </xsl:template> <!-- ============================================ -->
Theme files are located at:
- \WEB-INF\classes\layout\theme\universality (deployed)
- \uportal-war\src\main\resources\layout\theme\universality (source)
Add initialization script
Add the following initialization script in TEMPLATE: FOOTER BLOCK in universality.xsl (the main theme file):
<xsl:if test="$USE_AJAX='true' and $AUTHENTICATED='true'"> <script type="text/javascript"> var grabHandle = function (item) { // the handle is the toolbar. The toolbar id is the same as the portlet id, with the // "portlet_" prefix replaced by "toolbar_". return jQuery ("[id=toolbar_" + item.id.split ("_")[1] + "]"); }; var options = { cssClassNames: { mouseDrag: "orderable-dragging-mouse" }, grabHandle: grabHandle, dropWarningId: "portalDropWarning" }; fluid.reorderLayout ("#portalPageBodyColumns", { columns: ".portal-page-column-inner", modules: ".portlet-container ", lockedModules: ".locked" }, movePortlet, options); </script> </xsl:if>
Note that this script must come after the drop warning html in order for the drop warning message to work:
<!-- ========== TEMPLATE: FOOTER BLOCK ========== --> <!-- ============================================ --> <!-- | GREEN | This template renders custom content into the page footer. | Template contents can be any valid XSL or XHTML. --> <xsl:template name="footer.block"> ... <div id="portalDropWarning"> <p>The box cannot be moved here. The box currently located here is locked.</p> </div> <xsl:if test="$USE_AJAX='true' and $AUTHENTICATED='true'"> <script type="text/javascript"> ... </script> </xsl:if> </xsl:template> <!-- ============================================ -->
The xsl:if statement ensures that this script does not load improperly and cause a javascript error in the browser. This script:
- sets the drag handle to just the portlet title bar
- sets an optional css class for the mouse drag state (to avoid a error in the keyboard reordering)
- sets use of the grabHandle option
- sets use of the dropWarning option
- specifies the uPortal html ids and classes to map as drag items and drop areas
Theme files are located at:
- \WEB-INF\classes\layout\theme\universality (deployed)
- \uportal-war\src\main\resources\layout\theme\universality (source)
Add CSS
Add the following CSS to uportal3.css (the main css file).
/* Locked portlet styles */ .portal-page-column-inner .locked .portlet-top {display:none;} .portal-page-column-inner .locked .portlet-bottom {display:none;} .portal-page-column-inner .locked .portlet-toolbar {background-color:#ddd;border:1px solid #999;} .portal-page-column-inner .locked .portlet-toolbar a {color:#333;} .portal-page-column-inner .locked .portlet-toolbar h2 {background:transparent url("../../icons/lock.png") 0 3px no-repeat;} .portal-page-column-inner .locked .portlet-controls {background-color:transparent;} .dashboard .portal-page-column-inner .locked .portlet-content {border:1px solid #999;} /* Fluid Layout Customizer styles */ .orderable-hover{cursor:move;} .orderable-dragging{filter:alpha(opacity=50); opacity:.5;} .orderable-dragging-mouse{display:none;} .orderable-selected{} .orderable-avatar {width:auto; filter:alpha(opacity=50); opacity:.5;} div .orderable-drop-marker{height:200px !important; background-color: #99FF99; border:1px dashed #00CC00; margin:1em 0em;} .orderable-avatar-clone {height:200px; width:400px; filter:alpha(opacity=60); opacity:.6;} .drop-warning {position:absolute; top:50px; left:10px; border:2px solid red; background-color:#ffd7d7; display:none; padding:10px; margin:5px; z-index:65535;} .orderable-drop-marker-box {height:200px !important; width:100%; border:2px dashed red; margin-bottom:1em; background-color:#ffd7d7;} .orderable-mouse-drag {display: none;} #portalDropWarning{background-color:#FFCCCC; border:2px solid #FF0000; color:#FF0000; display:none; position:absolute; top:50px; left:10px;} #portalDropWarning p{margin:0em; padding:0.5em 1em;}
Skin files are located at:
- \webapps\uPortal\media\skins\universality\uportal3 (deployed)
- \uportal-war\src\main\webapp\media\skins\universality\uportal3 (source)
Add AJAX callback
AJAX callback for persisting layout changes is added to ajax-preferences-jquery.js. Replace the existing movePortlet function with this one:
function movePortlet(movedNode) { var method = 'insertBefore'; var target = null; if (ajaxPref(movedNode).nextAll('div[@id*=portlet_]').size() > 0) { target = ajaxPref(movedNode).nextAll('div[@id*=portlet_]').get(0); } else if (ajaxPref(movedNode).prevAll('div[@id*=portlet_]').size() > 0) { target = ajaxPref(movedNode).prevAll('div[@id*=portlet_]').get(0); method = 'appendAfter'; } else { target = ajaxPref(movedNode).parent(); } var columns = ajaxPref('#portalPageBodyColumns > td[@id*=column_]'); for (var i = 0; i < columns.length; i++) { ajaxPref(columns[i]).attr("width", ajaxPref(columns[i]).attr("width")); } ajaxPref.post(preferencesUrl, {action: 'movePortletHere', method: method, elementID: ajaxPref(target).attr('id').split('_')[1], sourceID: ajaxPref(movedNode).attr('id').split('_')[1]}, function(xml) { }); }
Javascript files are located at:
- \media\skins\universality\common\javascript (deployed)
- \uportal-war\src\main\webapp\media\skins\universality\common\javascript (source)
Tune the environment
Integration to this point enabled the Layout Customizer to work, but broke other uPortal AJAX functionality. Better namespacing of the uPortal Javascript (using jQuery.noConflict) fixed everything but the AJAX Add Content, which for the demo environment was simply commented out like so (contentAddingDialog):
// Initialization tasks for non-focused mode function initportal() { // initialize dialog menus // ajaxPref("#contentAddingDialog").channelbrowser({handles: new Array("#contentDialogLink")}); ajaxPref("#layoutDialogLink").click(initializeLayoutMenu); ajaxPref("#skinDialogLink").click(initializeSkinMenu); ... }