Skip to content

Commit

Permalink
basic implementation of jQuery based tree for 1.4 (still to be improved
Browse files Browse the repository at this point in the history
to make it nicer)
  • Loading branch information
Guite committed Jul 3, 2014
1 parent 41c3060 commit 555e109
Show file tree
Hide file tree
Showing 5 changed files with 276 additions and 163 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -297,16 +297,22 @@ class TreeFunctions {
* Callback function for config.onSave. This function is called after each tree change.
*
* @param node - the node which is currently being moved
* @param params - array with insertion params, which are [relativenode, dir];
* - "dir" is a string with value "after', "before" or "bottom" and defines
* whether the affected node is inserted after, before or as last child of "relativenode"
* @param tree data - serialized to JSON tree data
«IF targets('1.3.5')»
«' '»* @param params - array with insertion params, which are [relativenode, dir];
«' '»* - "dir" is a string with value "after", "before" or "bottom" and defines
«' '»* whether the affected node is inserted after, before or as last child of "relativenode"
«' '»* @param tree data - serialized to JSON tree data
«ELSE»
«' '»* @param parentNode - the new parent node
«' '»* @param position - can be "after", "before" or "bottom" and defines
«' '»* whether the affected node is inserted after, before or as last child of "relativenode"
«ENDIF»
*
* @return true on success, otherwise the change will be reverted
*/
function «prefix()»TreeSave(node, params, data)
function «prefix()»TreeSave(node, «IF targets('1.3.5'params, data«ELSE»parentNode, position«ENDIF»)
{
var nodeParts, rootId, nodeId, destId, params«IF targets('1.3.5')», request«ENDIF»;
var nodeParts, rootId, nodeId, destId, requestParams«IF targets('1.3.5')», request«ENDIF»;

// do not allow inserts on root level
«IF targets('1.3.5'
Expand All @@ -328,12 +334,12 @@ class TreeFunctions {
nodeParts = node.attr('id').split('node_');
rootId = nodeParts[0].replace('tree', '');
nodeId = nodeParts[1];
destId = params[1].attr('id').replace('tree' + rootId + 'node_', '');
destId = parentNode.attr('id').replace('tree' + rootId + 'node_', '');
«ENDIF»

params = {
requestParams = {
'op': 'moveNodeTo',
'direction': params[0],
'direction': «IF targets('1.3.5'params[0]«ELSE»position«ENDIF»,
'root': rootId,
'id': nodeId,
'destid': destId
Expand All @@ -344,12 +350,13 @@ class TreeFunctions {
Zikula.Config.baseURL + 'ajax.php?module=«appName»&func=handleTreeOperation',
{
method: 'post',
parameters: params,
parameters: requestParams,
onComplete: function (req) {
if (!req.isSuccess()) {
var treeName = 'itemTree' + rootId;
Zikula.UI.Alert(req.getMessage(), Zikula.__('Error', 'module_«appName.formatForDB»_js'));

return Zikula.TreeSortable.categoriesTree.revertInsertion();
return Zikula.TreeSortable[treeName].revertInsertion();
}
return true;
}
Expand All @@ -361,13 +368,15 @@ class TreeFunctions {
$.ajax({
type: 'POST',
url: Routing.generate('«appName.formatForDB»_ajax_handleTreeOperation'),
data: params
data: requestParams
}).done(function(res) {
return true;
}).fail(function(jqXHR, textStatus) {
var treeName = 'itemTree' + rootId;
«prefix()»SimpleAlert($('.tree-container'), Zikula.__('Error', 'module_«appName.formatForDB»_js'), Zikula.__('Could not persist your change.', 'module_«appName.formatForDB»_js'), 'treeAjaxFailedAlert', 'danger');

return Zikula.TreeSortable.categoriesTree.revertInsertion();
window.location.reload();
return false;
});

return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import org.zikula.modulestudio.generator.cartridges.zclassic.view.plugin.ObjectS
import org.zikula.modulestudio.generator.cartridges.zclassic.view.plugin.ObjectTypeSelector
import org.zikula.modulestudio.generator.cartridges.zclassic.view.plugin.TemplateHeaders
import org.zikula.modulestudio.generator.cartridges.zclassic.view.plugin.TemplateSelector
import org.zikula.modulestudio.generator.cartridges.zclassic.view.plugin.TreeJS
import org.zikula.modulestudio.generator.cartridges.zclassic.view.plugin.TreeData
import org.zikula.modulestudio.generator.cartridges.zclassic.view.plugin.TreeSelection
import org.zikula.modulestudio.generator.cartridges.zclassic.view.plugin.ValidationError
import org.zikula.modulestudio.generator.cartridges.zclassic.view.plugin.form.AbstractObjectSelector
Expand Down Expand Up @@ -75,7 +75,7 @@ class Plugins {
new FormatGeoData().generate(it, fsa)
}
if (hasTrees) {
new TreeJS().generate(it, fsa)
new TreeData().generate(it, fsa)
new TreeSelection().generate(it, fsa)
}
if (generateModerationPanel && needsApproval) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,29 +131,87 @@ class ViewHierarchy {
{if isset($items) && (is_object($items) && $items->count() gt 0) || (is_array($items) && count($items) gt 0)}
{assign var='hasNodes' value=true}
{/if}
{assign var='idPrefix' value="«name.formatForCode.toFirstLower»Tree`$rootId`"}

<div id="«name.formatForCode.toFirstLower»Tree{$rootId}" class="«IF isLegacyApp»z-«ENDIF»tree-container">
<div id="«name.formatForCode.toFirstLower»TreeItems{$rootId}" class="«IF isLegacyApp»z-«ENDIF»tree-items">
{if $hasNodes}
{«appName.formatForDB»TreeJS objectType='«name.formatForCode»' tree=$items controller=$lct root=$rootId sortable=true}
{/if}
</div>
«IF !isLegacyApp»
<p>
<label for="{$idPrefix}SearchTerm">{gt text='Quick search'}:</label>
<input type="search" id="{$idPrefix}SearchTerm" value="" />
</p>
«ENDIF»
<div id="{$idPrefix}" class="«IF isLegacyApp»z-«ENDIF»tree-container">
«IF isLegacyApp»
<div id="«name.formatForCode.toFirstLower»TreeItems{$rootId}" class="«IF isLegacyApp»z-«ENDIF»tree-items">
{if $hasNodes}
{«appName.formatForDB»TreeData objectType='«name.formatForCode»' tree=$items controller=$lct root=$rootId sortable=true}
{/if}
</div>
«ELSE»
{if $hasNodes}
<ul>
{«appName.formatForDB»TreeData objectType='«name.formatForCode»' tree=$items controller=$lct root=$rootId}
</ul>
{/if}
«ENDIF»
</div>

{if $hasNodes}
{pageaddvar name='javascript' value='«container.application.rootFolder»/«IF isLegacyApp»«appName»/javascript/«ELSE»«container.application.getAppJsPath»«ENDIF»«appName»«IF isLegacyApp»_t«ELSE».T«ENDIF»ree.js'}
«IF !isLegacyApp»
{pageaddvar name='javascript' value='web/jstree/dist/jstree.min.js'}
{pageaddvar name='stylesheet' value='web/jstree/dist/themes/default/style.min.css'}
«ENDIF»
<script type="text/javascript">
/* <![CDATA[ */
«IF isLegacyApp»
document.observe('dom:loaded', function() {
«appPrefix»InitTreeNodes('«name.formatForCode»', '{{$rootId}}', «hasActions('display').displayBool», «(hasActions('edit') && !readOnly).displayBool»);
Zikula.TreeSortable.trees.itemtree{{$rootId}}.config.onSave = «appPrefix»TreeSave;
Zikula.TreeSortable.trees.itemTree{{$rootId}}.config.onSave = «appPrefix»TreeSave;
});
«ELSE»
( function($) {
$(document).ready(function() {
«appPrefix»InitTreeNodes('«name.formatForCode»', '{{$rootId}}', «hasActions('display').displayBool», «(hasActions('edit') && !readOnly).displayBool»);
Zikula.TreeSortable.trees.itemtree{{$rootId}}.config.onSave = «appPrefix»TreeSave;

var tree = $('#{{$idPrefix}}').jstree({
'core': {
'multiple': false,
'check_callback': true
},
'dnd': {
'copy': false,
'is_draggable': function(node) {
// disable drag and drop for root category
var inst = node.inst;
var level = inst.get_path().length;

return (level > 1) ? true : false;
}
},
'state': {
'key': '{{$idPrefix}}'
},
'plugins': [ 'dnd', 'search', 'state', 'wholerow' ]
});

tree.on('move_node.jstree', function (e, data) {
var node = data.node;
var parentId = data.parent;
var parentNode = $tree.jstree('get_node', parentId, false);

«appPrefix»TreeSave(node, parentNode, 'bottom');
});

var searchStartDelay = false;
$('#{{$idPrefix}}SearchTerm').keyup(function () {
if (searchStartDelay) {
clearTimeout(to);
}
searchStartDelay = setTimeout(function () {
var v = $('#{{$idPrefix}}SearchTerm').val();
$('#{{$idPrefix}}').jstree(true).search(v);
}, 250);
});
});
})(jQuery);
«ENDIF»
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package org.zikula.modulestudio.generator.cartridges.zclassic.view.plugin

import com.google.inject.Inject
import de.guite.modulestudio.metamodel.modulestudio.Application
import org.eclipse.xtext.generator.IFileSystemAccess
import org.zikula.modulestudio.generator.cartridges.zclassic.smallstuff.FileHelper
import org.zikula.modulestudio.generator.extensions.ControllerExtensions
import org.zikula.modulestudio.generator.extensions.FormattingExtensions
import org.zikula.modulestudio.generator.extensions.ModelExtensions
import org.zikula.modulestudio.generator.extensions.NamingExtensions
import org.zikula.modulestudio.generator.extensions.Utils

class TreeData {
@Inject extension ControllerExtensions = new ControllerExtensions
@Inject extension ModelExtensions = new ModelExtensions
@Inject extension FormattingExtensions = new FormattingExtensions
@Inject extension NamingExtensions = new NamingExtensions
@Inject extension Utils = new Utils

def generate(Application it, IFileSystemAccess fsa) {
val pluginFilePath = viewPluginFilePath('function', 'TreeJS')
if (!shouldBeSkipped(pluginFilePath)) {
fsa.generateFile(pluginFilePath, new FileHelper().phpFileContent(it, treeDataImpl))
}
}

def private treeDataImpl(Application it) '''
/**
* The «appName.formatForDB»TreeData plugin delivers the html output for a JS tree
* based on given tree entities.
*
* Available parameters:
* - objectType: Name of treated object type.
* - tree: Object collection with tree items.
* - controller: Optional name of controller, defaults to 'user'.
* - root: Optional id of root node, defaults to 1.
«IF targets('1.3.5')»
«' '»* - sortable: Whether tree nodes should be sortable or not, defaults to true.
«ENDIF»
* - assign: If set, the results are assigned to the corresponding variable instead of printed out.
*
* @param array $params All attributes passed to this function from the template.
* @param Zikula_View $view Reference to the view object.
*
* @return string The output of the plugin.
*/
function smarty_function_«appName.formatForDB»TreeData($params, $view)
{
if (!isset($params['objectType']) || empty($params['objectType'])) {
$view->trigger_error(__f('Error! in %1$s: the %2$s parameter must be specified.', array('«appName.formatForDB»TreeJS', 'objectType')));

return false;
}

if (!isset($params['tree']) || empty($params['tree'])) {
$view->trigger_error(__f('Error! in %1$s: the %2$s parameter must be specified.', array('«appName.formatForDB»TreeJS', 'tree')));

return false;
}

if (!isset($params['controller']) || empty($params['controller'])) {
$params['controller'] = 'user';
}
«IF !targets('1.3.5'

$params['lct'] = $params['controller'];
$params['controller'] = $params['objectType'];
«ENDIF»

if (!isset($params['root']) || empty($params['root'])) {
$params['root'] = 1;
}

// check whether an edit action is available
$controllerHasEditAction = false;
switch ($params['controller']) {
«controllerEditActionFlags»
}

«IF targets('1.3.5'
$entityClass = '«appName»_Entity_' . ucwords($params['objectType']);
«ENDIF»
$serviceManager = ServiceUtil::getManager();
«IF targets('1.3.5'
$entityManager = $serviceManager->get«IF targets('1.3.5'Service«ENDIF»('doctrine.entitymanager');
$repository = $entityManager->getRepository($entityClass);
«ELSE»
$repository = $serviceManager->get('«appName.formatForDB».«name.formatForCode»_factory')->getRepository();
«ENDIF»
$descriptionFieldName = $repository->getDescriptionFieldName();

$result = '';
«IF targets('1.3.5'
$treeData = array();

foreach ($params['tree'] as $item) {
$url = $controllerHasEditAction ? ModUtil::url('«appName»', $params['controller'], 'edit', $item->createUrlArgs()) : '';

$treeData[] = array('id' => $item->createCompositeIdentifier(),
'parent_id' => $item->getParent()->createCompositeIdentifier(),
'name' => $item->getTitleFromDisplayPattern(),
'title' => (($descriptionFieldName != '') ? strip_tags($item[$descriptionFieldName]) : ''),
//'icon' => '',
//'class' => '',
'active' => true,
//'expanded' => null,
'href' => $url);
}

// instantiate and initialise the output tree object
$tree = new Zikula_Tree();
$tree->setOption('id', 'itemTree' . $params['root']);
$tree->setOption('treeClass', 'z-nestedsetlist');
$tree->setOption('nodePrefix', 'tree' . $params['root'] . 'node_');
$tree->setOption('sortable', ((isset($params['sortable']) && $params['sortable']) ? true : false));
$tree->setOption('withWraper', true);

// disable drag and drop for root category
$tree->setOption('disabled', array(1));

// put data into output tree
$tree->loadArrayData($treeData);

// get output result
$result = $tree->getHTML();
«ELSE»
foreach ($params['tree'] as $item) {
$result .= processTreeItemWithChildren($item, $rootId, $descriptionFieldName, $controllerHasEditAction);
}
«ENDIF»

if (array_key_exists('assign', $params)) {
$view->assign($params['assign'], $result);

return;
}

return $result;
}
«IF !targets('1.3.5'

function processTreeItemWithChildren($node, $rootId, $descriptionFieldName, $controllerHasEditAction)
{
$output = '';
$title = (($descriptionFieldName != '') ? strip_tags($item[$descriptionFieldName]) : '');
$liTag = '<li id="tree' . $rootId . 'node_' . $item->createCompositeIdentifier() . '" title="' . str_replace('"', '', $title) . '" class="lvl' . $item->getLvl() . '">';

$liContent = $item->getTitleFromDisplayPattern();
if ($controllerHasEditAction) {
$urlArgs = $item->createUrlArgs();
$urlArgs['lct'] = $params['lct'];
$url = $serviceManager->get('router')->generate('«appName.formatForDB»_' . $params['objectType'] . '_edit', $urlArgs);

$liContent = '<a href="' . $url . '" title="' . str_replace('"', '', $title) . '">' . $liContent . '</a>';
}

$treeItem = $liTag . $liContent;

if (count($item->getChildren()) > 0) {
$treeItem .= '<ul>';
foreach ($item->getChildren() as $childNode) {
$treeItem .= processTreeItemWithChildren($childNode, $rootId, $descriptionFieldName, $controllerHasEditAction);
}
$treeItem .= '</ul>';
}

$treeItem .= '</li>';

$output .= $treeItem;

return $output;
}
«ENDIF»
'''
def private controllerEditActionFlags(Application it) '''
«FOR controller : getAllControllers.filter[hasActions('edit')]»
case '«controller.formattedName»': $controllerHasEditAction = true; break;
«ENDFOR»
«IF !targets('1.3.5'
«FOR entity : getAllEntities.filter[hasActions('edit')]»
case '«entity.name.formatForCode»': $controllerHasEditAction = true; break;
«ENDFOR»
«ENDIF»
'''
}
Loading

0 comments on commit 555e109

Please sign in to comment.