s...@ull.at
unread,Jun 25, 2014, 2:39:51 AM6/25/14Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
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);
+}
+
+
+
+