r4114 - in trunk/plugins/ullCorePlugin: lib/form/widget web/js

0 views
Skip to first unread message

s...@ull.at

unread,
Jun 25, 2014, 2:39:51 AM6/25/14
to ullrigh...@googlegroups.com
Author: klemens
Date: 2014-06-25 08:39:49 +0200 (Wed, 25 Jun 2014)
New Revision: 4114

Added:
trunk/plugins/ullCorePlugin/web/js/ullWidgetManyToManySortable.js
Modified:
trunk/plugins/ullCorePlugin/lib/form/widget/ullMetaWidgetManyToMany.class.php
trunk/plugins/ullCorePlugin/lib/form/widget/ullWidgetManyToManyWrite.class.php
Log:
Enhanced many to many widget with sortable capability. Form handling will follow shortly...

Modified: trunk/plugins/ullCorePlugin/lib/form/widget/ullMetaWidgetManyToMany.class.php
===================================================================
--- trunk/plugins/ullCorePlugin/lib/form/widget/ullMetaWidgetManyToMany.class.php 2014-06-24 10:18:37 UTC (rev 4113)
+++ trunk/plugins/ullCorePlugin/lib/form/widget/ullMetaWidgetManyToMany.class.php 2014-06-25 06:39:49 UTC (rev 4114)
@@ -4,7 +4,9 @@
* This meta widget provides support for many to many relationships.
*
* This widget implies the use of ullTableToolGeneratorForm.
- * The form's value handling is handled there by doSave() / saveManyToMany()
+ * The form's value handling is handled there by
+ * ullTableToolGeneratorForm::doSave() and furthermore
+ * ullTableToolGeneratorForm::saveManyToMany()
*
* Both widget and validator need the 'model' option set.
*/

Modified: trunk/plugins/ullCorePlugin/lib/form/widget/ullWidgetManyToManyWrite.class.php
===================================================================
--- trunk/plugins/ullCorePlugin/lib/form/widget/ullWidgetManyToManyWrite.class.php 2014-06-24 10:18:37 UTC (rev 4113)
+++ trunk/plugins/ullCorePlugin/lib/form/widget/ullWidgetManyToManyWrite.class.php 2014-06-25 06:39:49 UTC (rev 4114)
@@ -20,7 +20,8 @@
class ullWidgetManyToManyWrite extends ullWidgetFormDoctrineChoice
{
protected
- $cachedChoices = null
+ $cachedChoices = null,
+ $baseObjectId = null
;

protected static
@@ -30,12 +31,14 @@
'/ullCorePlugin/js/jq/jquery.tools.min.js', // for overlay
'/ullCorePlugin/js/jq/jquery.multiselect-min.js',
'/ullCorePlugin/js/jq/jquery.multiselect.filter.js',
- '/ullCorePlugin/js/ullWidgetManyToMany.js'
+ '/ullCorePlugin/js/ullWidgetManyToMany.js',
+ '/ullCorePlugin/js/ullWidgetManyToManySortable.js',
+
),
$stylesheets = array(
'/ullCorePlugin/css/jqui/jquery-ui.css' => 'all',
'/ullCorePlugin/css/jquery.multiselect.css' => 'all',
- '/ullCorePlugin/css/jquery.multiselect.filter.css' => 'all'
+ '/ullCorePlugin/css/jquery.multiselect.filter.css' => 'all',
)
;

@@ -45,6 +48,9 @@
*/
protected function configure($options = array(), $attributes = array())
{
+ $this->addRequiredOption('owner_model');
+ $this->addRequiredOption('owner_relation_name');
+
$this->addOption('config',
"{ minWidth : 400,
header : ' ',
@@ -53,19 +59,21 @@
$this->addOption('filter_config',
"{ label : '" . __('Search', null, 'common') . ":' }");
$this->addOption('filter_results');
- $this->addRequiredOption('owner_model');
- $this->addRequiredOption('owner_relation_name');
+ $this->addOption('is_sortable', false);

+
parent::configure($options, $attributes);

//we should remove the table_method option if we do not support it, but how?
}

+
/**
* Get choices
*/
public function getChoices()
{
+
if ($this->cachedChoices !== null)
{
return $this->cachedChoices;
@@ -83,6 +91,8 @@

$query = null === $this->getOption('query') ? Doctrine_Core::getTable($this->getOption('model'))->createQuery() : $this->getOption('query');

+
+
if ($order = $this->getOption('order_by'))
{
$query->addOrderBy($order[0] . ' ' . $order[1]);
@@ -100,7 +110,47 @@
$query->limit(100);
}

+ // HANDLING FOR SORTABLE
+ // Move the selected options to the top in the correct order
+ // Therefore the order is preserved during post request
+ if ($this->getOption('is_sortable') && $this->baseObjectId)
+ {
+// if (!$this->baseObjectId)
+// {
+// throw new InvalidArgumentException('Base object id not set (Inject identifier)');
+// }
+
+// var_dump($this->baseObjectId);
+// var_dump($this->getOption('model'));
+// var_dump($this->getOption('owner_model'));
+// var_dump($this->getOption('owner_relation_name'));
+
+ //
+
+
+ $relation = Doctrine::getTable($this->getOption('owner_model'))
+ ->getRelation($this->getOption('owner_relation_name'));
+
+ $associationModel = $relation->getAssociationTable()->getComponentName();
+
+ // Add the position of the many to many table entries
+ $query->getDoctrineQuery()->leftJoin(
+ 'x.' . $associationModel . ' assoc ' .
+ 'WITH assoc.' . $relation->getLocalRefColumnName() . ' = ?',
+ $this->baseObjectId
+ );
+
+ // convert NULL to a high integer. therefore NULL entries are ordered last
+ $query->getDoctrineQuery()->addSelect(
+ 'IFNULL(assoc.position, 999999999) as postition_int');
+ $query->addOrderByPrefix('postition_int');
+ }
+
+// printQuery($query);
+
$results = $query->execute();
+
+// var_dump($results);

//was array hydration used?
if (is_array($results))
@@ -133,13 +183,18 @@
}
}

- natcasesort($choices);
+ // Don't destroy the order for sortable
+ if (!$this->getOption('is_sortable'))
+ {
+ natcasesort($choices);
+ }

$this->cachedChoices = $choices;

return $choices;
}

+
/**
* Typical widget rendering code, uses the rendering of the
* ullWidgetFormDoctrineChoice parent class but adds some
@@ -147,15 +202,28 @@
*/
public function render($name, $value = null, $attributes = array(), $errors = array())
{
- // an array of selected ids are passed as $value
+ // Create html id
+ $this->setAttribute('name', $name);
+ $this->setAttributes($this->fixFormId($this->getAttributes()));
+ $htmlId = $this->getAttribute('id');

- $id = $this->generateId($name);
+ // Parse value with injected base object id
+ // $value format:
+ // id => 1234 // base object id
+ // value => an array of selected option ids
+
+ if (is_array($value))
+ {
+ $this->baseObjectId = $value['id'];
+ $value = isset($value['value']) ? $value['value'] : null;
+ }

$choicesCount = count($this->getChoices());
$threshold = sfConfig::get('app_ull_widget_many_to_many_write_ajax_mode_threshold', 1000);

- //enable AJAX mode if choice count is high
- if ($choicesCount > $threshold)
+ // enable AJAX mode if choice count is high
+ // currently not supported for sortable
+ if ($choicesCount > $threshold && !$this->getOption('is_sortable'))
{
$ownerModel = $this->getOption('owner_model');
$ownerRelationName = $this->getOption('owner_relation_name');
@@ -166,9 +234,9 @@

$return .= sprintf(<<<EOF
<script type="text/javascript">
- var widget_$id = {
+ var widget_$htmlId = {
selectedOptions: null, selectedValues: null,
- selectBox: $('#$id'), xhr: null,
+ selectBox: $('#$htmlId'), xhr: null,
timeoutId: null, oldFilterValue: '',
ownerModel: '$ownerModel',
ownerRelationName: '$ownerRelationName',
@@ -177,7 +245,7 @@
};
jQuery(document).ready(function()
{
- manyToMany_setup(widget_$id, %s);
+ manyToMany_setup(widget_$htmlId, %s);
});

</script>
@@ -188,6 +256,28 @@

return $return;
}
+
+ // Sortable mode
+ elseif ($this->getOption('is_sortable'))
+ {
+ $return = parent::render($name, $value, $attributes, $errors);
+
+ $return .= sprintf(<<<EOF
+ <script type="text/javascript">
+ jQuery(document).ready(function() {
+ manyToManySortableSetup("%s");
+ });
+ </script>
+EOF
+ ,
+ $htmlId
+ );
+
+
+ return $return;
+ }
+
+ // Normal multiselect mode
else
{
$return = parent::render($name, $value, $attributes, $errors);
@@ -202,7 +292,7 @@
</script>
EOF
,
- $id,
+ $htmlId,
$this->getOption('config'),
$this->getOption('filter_config')
);
@@ -214,16 +304,16 @@
/**
* Render javascript code to be executed after the widget has been reloaded
*
- * @param string $id
+ * @param string $htmlId
* @param string $name
*/
- public function renderPostWidgetReload($id, $name)
+ public function renderPostWidgetReload($htmlId, $name)
{
$return = '
// select the new entry
-var selector = "#'. $id . ' > option[value=\'" + window.overlayId + "\']";
+var selector = "#'. $htmlId . ' > option[value=\'" + window.overlayId + "\']";
$(selector).attr("selected", true);
-$("#' . $id . '").multiselect("refresh");
+$("#' . $htmlId . '").multiselect("refresh");
';

return $return;

Added: trunk/plugins/ullCorePlugin/web/js/ullWidgetManyToManySortable.js
===================================================================
--- trunk/plugins/ullCorePlugin/web/js/ullWidgetManyToManySortable.js (rev 0)
+++ trunk/plugins/ullCorePlugin/web/js/ullWidgetManyToManySortable.js 2014-06-25 06:39:49 UTC (rev 4114)
@@ -0,0 +1,253 @@
+/**
+ * ullMetaWidgetManyToMany js for is_sortable support
+ *
+ * Allows to select multiple options in a particular order
+ *
+ * @param id form field html id
+ * @returns
+ */
+function manyToManySortable(id)
+{
+ // PROPERTIES
+ var id = id;
+ var multiselect = $('#' + id);
+
+ var selectedOptions = [];
+ var selectedValues = [];
+ var unselectedOptions = [];
+ var unselectedValues = [];
+
+
+ // METHODS
+ /**
+ * Store the original selected options
+ */
+ var saveOriginalSelectedOptions = function () {
+ var i = 0;
+ multiselect.children('option:selected').each(function(i, selected) {
+ selectedOptions[i] = selected.text;
+ selectedValues[i] = selected.value;
+ i++;
+ });
+ };
+
+
+ /**
+ * Store the original unselected options
+ */
+ var saveOriginalUnselectedOptions = function () {
+ var i = 0;
+ multiselect.children('option:not(:selected)').each(function(i, selected) {
+ unselectedOptions[i] = selected.text;
+ unselectedValues[i] = selected.value;
+ i++;
+ });
+ };
+
+
+ /**
+ * Create list of selected options from the main data array
+ */
+ var updateSelectedList = function () {
+
+ var selectedHtml = '<ul ' +
+ 'id="' + id + '_selected" ' +
+ 'class="many_to_many_box many_to_many_sortable_selected"' +
+ '>';
+ $(selectedOptions).each(function(i, option) {
+ selectedHtml += '<li rel="' + selectedValues[i] + '">'
+ + selectedOptions[i] + '</li>';
+ });
+ selectedHtml += '</ul>';
+
+ // Check if the selected box already exists
+ if ($('#' + id + '_selected').length == 0) {
+ $(multiselect).before(selectedHtml);
+ }
+ else {
+ $('#' + id + '_selected').replaceWith(selectedHtml);
+ }
+
+ // Bind click behaviour
+ var selected = $('#' + id + '_selected');
+ $(selected).children('li').each(function() {
+
+ $(this).click(function() {
+ // Prevent firing the click event upon dragging
+ if ($(selected).hasClass('m2ms_prevent_click')) {
+ $(selected).removeClass('m2ms_prevent_click');
+ }
+ else {
+ doUnselect($(this));
+ }
+ });
+
+ });
+
+ // Drag'n'drop sortable
+ $(selected).sortable({
+ axis: "y",
+ start: function(event, ui) {
+ // Prevent firing the click event upon dragging
+ $(this).addClass('m2ms_prevent_click');
+ },
+ stop: function(event, ui) {
+ updateArrays();
+ updateMultiselect();
+ },
+ });
+
+ }
+
+
+ /**
+ * Create list of unselected options from the main data array
+ */
+ var updateUnselectedList = function () {
+
+ var unselectedHtml = '<ul ' +
+ 'id="' + id + '_unselected" ' +
+ 'class="many_to_many_box many_to_many_sortable_unselected"' +
+ '>';
+ $(unselectedOptions).each(function(i, option) {
+ unselectedHtml += '<li rel="' + unselectedValues[i] + '">'
+ + unselectedOptions[i] + '</li>';
+ });
+ unselectedHtml += '</ul>';
+
+ // Check if the unselected box already exists
+ if ($('#' + id + '_unselected').length == 0) {
+ $(multiselect).before(unselectedHtml);
+ }
+ else {
+ $('#' + id + '_unselected').replaceWith(unselectedHtml);
+ }
+
+ // Bind click behaviour
+ var unselected = $('#' + id + '_unselected');
+ $(unselected).children('li').each(function() {
+ $(this).click(function() {
+ doSelect($(this));
+ });
+ });
+
+ }
+
+ /**
+ * Sort the unselected list alphabetically
+ */
+ var sortUnselectedList = function () {
+ var list = $('#' + id + '_unselected');
+ var listitems = list.children('li').get();
+ listitems.sort(function(a, b) {
+ return $(a).text().toUpperCase().localeCompare($(b).text().toUpperCase());
+ })
+ $.each(listitems, function(idx, itm) { list.append(itm); });
+ }
+
+
+ /**
+ * Click action for selection
+ */
+ var doSelect = function (item) {
+ // move li
+ item.detach().appendTo('#' + id + '_selected');
+
+ updateArrays();
+ updateMultiselect();
+ updateSelectedList();
+ }
+
+
+ /**
+ * Click action for unselection
+ */
+ var doUnselect = function (item) {
+ // move li
+ item.detach().appendTo('#' + id + '_unselected');
+
+ sortUnselectedList();
+ updateArrays();
+ updateMultiselect();
+ updateUnselectedList();
+ }
+
+
+ /**
+ * Update the data arrays from the lists
+ */
+ var updateArrays = function () {
+
+ //selected list
+ selectedOptions = [];
+ selectedValues = [];
+
+ var selected = $('#' + id + '_selected');
+ $(selected).children('li').each(function(i) {
+
+ selectedOptions[i] = $(this).html();
+ selectedValues[i] = $(this).attr('rel');
+ });
+
+
+ //unselected list
+ unselectedOptions = [];
+ unselectedValues = [];
+
+ var unselected = $('#' + id + '_unselected');
+ $(unselected).children('li').each(function(i) {
+
+ unselectedOptions[i] = $(this).html();
+ unselectedValues[i] = $(this).attr('rel');
+ });
+
+ }
+
+
+ /**
+ * Update the multiselect form field from the data arrays
+ */
+ var updateMultiselect = function () {
+
+ var html = '';
+
+ $(selectedOptions).each(function(i) {
+ html += '<option selected="selected" value="' + selectedValues[i] + '">'
+ + selectedOptions[i] + '</option>';
+ });
+
+ $(unselectedOptions).each(function(i) {
+ html += '<option value="' + unselectedValues[i] + '">'
+ + unselectedOptions[i] + '</option>';
+ });
+
+ multiselect.html(html);
+ }
+
+
+ // CONSTRUCTOR
+ saveOriginalSelectedOptions();
+ saveOriginalUnselectedOptions();
+
+ updateUnselectedList();
+ updateSelectedList();
+
+ $(multiselect).addClass('ull_widget_many_to_many_sortable');
+
+
+}
+
+
+/**
+ * Factory
+ *
+ * @param id form field html id
+ */
+function manyToManySortableSetup(id)
+{
+ var multisort = new manyToManySortable(id);
+}
+
+
+
+

Reply all
Reply to author
Forward
0 new messages