Results 1 to 10 of 10

Thread: Multi-dates Selection

  1. #1
    Experienced user
    Join Date
    06-20-09.
    Posts
    1,311

    Default Multi-dates Selection

    Not sure really how to ask this one :?
    I'm working on a way to input multiple dates in the backend for use in a "realestate" module, so that someone can enter say 50 dates and associate them with a price.

    I couldn't find a way to select multiple dates into a field using the default Contao calendar, so I've made a Backend class that allows it - using the calendar from here http://keith-wood.name/datepick.html, and adapting Helmut Schottmüller's multitextWizard http://www.contao.org/extension-list...010009.en.html

    Each Wizard row consists of a multiple-dates-picker popup calendar and one (or more) text fields.


    This works great except that when you click the little "+" icon to make a new row the calendar won't appear on the field UNTIL the record is saved.
    I guess this is because the #id that the calendar needs to instantiate from is not in the sourcecode until save, as javascript is responsible for creating the new rows.

    Is it possible to make the calendar "bind" to the field in a new row without saving?
    I'm not sure what code I need to post here to show anyone, but heres the main class as it stands

    Code:
    <?php if (!defined('TL_ROOT')) die('You can not access this file directly!');
     
    class RealestatePriceWizard extends Widget
    {
    	/**
    	 * Number of text fields
    	 * @var int
    	 */
    	protected $intColumns = 1;
    
    	/**
    	 * Labels for the text fields (count must match $intColumns)
    	 * @var array
    	 */
    	protected $arrLabels = array();
    
    	/**
    	 * Submit user input
    	 * @var boolean
    	 */
    	protected $blnSubmitInput = true;
    
    	/**
    	 * Template
    	 * @var string
    	 */
    	protected $strTemplate = 'be_widget';
    
    
    	/**
    	 * Add specific attributes
    	 * @param string
    	 * @param mixed
    	 */
    	public function __set($strKey, $varValue)
    	{
    		switch ($strKey)
    		{
    			case 'labels':
    				if (is_array($varValue))
    				{
    					$this->arrLabels = array_values($varValue);
    				}
    				break;
    				
    			case 'columns':
    				$this->intColumns = $varValue;
    				break;
    
    			case 'value':
    				$this->varValue = deserialize($varValue);
    				break;
    
    			case 'mandatory':
    				$this->arrConfiguration['mandatory'] = $varValue ? true : false;
    				break;
    
    			default:
    				parent::__set($strKey, $varValue);
    				break;
    		}
    	}
    
    
    	/**
    	 * Generate the widget and return it as string
    	 * @return string
    	 */
    
    	public function generate()
    	{
    
    // JQUERY DATEPICKER V2 FROM http://keith-wood.name/datepick.html
    //ADD NESESSARY JAVASCRIPT TO BACKEND HEAD
    		if (is_array($GLOBALS['TL_JAVASCRIPT']))
    		{
    		array_insert($GLOBALS['TL_JAVASCRIPT'], 1, array('system/modules/realestate/html/js/realestateprice.js','http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js','system/modules/realestate/html/js/jquery.datepick.js'));
    			
    		}else
    		{
    			$GLOBALS['TL_JAVASCRIPT'] = array('system/modules/realestate/html/js/realestateprice.js','http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js','system/modules/realestate/html/js/jquery.datepick.js');
    		}
    		
    //ADD NESESSARY CSS TO BACKEND HEAD		
    		if (is_array($GLOBALS['TL_CSS']))
    		{
    			array_insert($GLOBALS['TL_CSS'], 1, 'system/modules/realestate/html/jquery.datepick.css');
    		}else
    		{
    			$GLOBALS['TL_CSS'] = array('system/modules/realestate/html/jquery.datepick.css');
    		}		
    
    		$arrButtons = array('rnew','rcopy', 'rup', 'rdown', 'rdelete');
    
    		$strCommand = 'cmd_' . $this->strField;
    		$emptyarray = array();
    		for ($i = 0; $i < $this->intColumns; $i++) array_push($emptyarray, '');
    		// Change the order
    		if ($this->Input->get($strCommand) && is_numeric($this->Input->get('cid')) && $this->Input->get('id') == $this->currentRecord)
    		{
    			$this->import('Database');
    
    			switch ($this->Input->get($strCommand))
    			{
    				case 'rnew':
    					array_insert($this->varValue, $this->Input->get('cid') + 1, array($emptyarray));
    					break;
    
    				case 'rcopy':
    					$this->varValue = array_duplicate($this->varValue, $this->Input->get('cid'));
    					break;
    
    				case 'rup':
    					$this->varValue = array_move_up($this->varValue, $this->Input->get('cid'));
    					break;
    
    				case 'rdown':
    					$this->varValue = array_move_down($this->varValue, $this->Input->get('cid'));
    					break;
    
    				case 'rdelete':
    					$this->varValue = array_delete($this->varValue, $this->Input->get('cid'));
    					break;
    			}
    
    			$this->Database->prepare("UPDATE " . $this->strTable . " SET " . $this->strField . "=? WHERE id=?")
    						   ->execute(serialize($this->varValue), $this->currentRecord);
    
    			$this->redirect(preg_replace('/&(amp;)?cid=[^&]*/i', '', preg_replace('/&(amp;)?' . preg_quote($strCommand, '/') . '=[^&]*/i', '', $this->Environment->request)));
    		}
    
    		// Make sure there is at least an empty array
    		if (!is_array($this->varValue) || !$this->varValue[0])
    		{
    			$this->varValue = array($emptyarray);
    		}
    
    		// put  JQUERY DATEPICKER into no-conflict mode  and make available to fields of id #multiPrice_0 etc http://keith-wood.name/datepick.html
    		$return .='
    <script type="text/javascript">
    $j = jQuery.noConflict();
    </script>';
    		
    		// Begin table		
    		$return .= '<div class="tl_realestatepricewizard">
      <table cellspacing="0" cellpadding="0" class="tl_modulewizard" id="ctrl_'.$this->strId.'" summary="RealestatePrice wizard" ' . $this->getAttributes() . '>';
    	$return .= '<thead>';
    	$return .= '
      <tr>';
    
    		// Add labels
    		for ($h=0; $h<$this->intColumns; $h++)
    		{
    			$return .= '<th style="padding-right: 7px;">' . ((array_key_exists($h, $this->arrLabels)) ? $this->arrLabels[$h] : "") . '</th>';
    		}
    
    		$return .= '<th style="white-space:nowrap;"></th>
      </tr></thead>';
    
      $return .= '<tbody>';
    
    		// Add rows
    		for ($i=0; $i<count($this->varValue); $i++)
    		{
    			$return .= '
        <tr>';
    
    //***********************************************************
    			// Add cells  dates,amount,class
    			for ($k=0; $k<$this->intColumns; $k++)
    			{
    
    			$return .= '<td style="padding-right: 7px;">';
    //class="tl_text" style="width: 150px; padding-right: 2px;"			
    					if($k == 0)
    					{
    					$return .= '<input type="hidden" name="'.$this->strId.'['.$i.']['.$k.']" id="multiPrice_'.$i.'" value="'.specialchars($this->varValue[$i][$k]).'"' . ' /><span>[img]system/modules/realestate/html/calendar-icon.gif[/img]</span>
    <script type="text/javascript">
    <![CDATA[//><!--
      
    $j(\'#multiPrice_'.$i.'\').datepick({
    	dateFormat: \'@\', 
        multiSelect: 999, monthsToShow: 3, 
        showTrigger: \'#multiPriceButton_'.$i.'\'});
    
      
    //--><!]]>
    </script>';			
    					}else
    					{
    					$return .= '<input type="text" name="'.$this->strId.'['.$i.']['.$k.']" class="tl_text" style="width: 150px; padding-right: 2px;" value="'.specialchars($this->varValue[$i][$k]).'"' . ' />';			
    					}
    			
    			$return .= '</td>';			
    
    			}
    
    			$return .= '
          <td style="white-space:nowrap; width: 110px;">';
    	  
    //***************************************************************	  
    
    			// Add row buttons
    			foreach ($arrButtons as $button)
    			{
    				$return .= 'strTable][$button][0]).'" onclick="RealestatePrice.realestatepriceWizard(this, \''.$button.'\', \'ctrl_'.$this->strId.'\'); return false;">'.$this->generateImage(substr($button, 1).'.gif', $GLOBALS['TL_LANG'][$this->strTable][$button][0], 'class="tl_realestatepricewizard_img"').' ';
    			}
    
    			$return .= '</td>
        </tr>';
    		}
    
    		return $return.'
      </tbody>
    
     </table>
      </div>';
    
    	}
    	
    }
    
    ?>
    There are 2 javascript files that came from multitextWizard as well.... the same except for name changes.

    [attachment=0:3opqbjn0]prices.jpg[/attachment:3opqbjn0]
    The attachment demonstrates selecting all Fri/Sat over 3 months into a hidden field via an image.... all good except no calendar appears on new rows until after "save".
    I also can't figure why my calendar-button image appears twice (left works, right doesn't!)

  2. #2
    Experienced user
    Join Date
    06-10-09.
    Location
    Cape Town, South Africa
    Posts
    1,387

    Default Re: Multi-dates Selection

    I would show the date, not only do you have to show the field for the calendar pop-up (without having to hack it), but also you should display all fields entered, and you're not showing the date to the user, so he can't see it visually now. Otherwise he can't relate this. I would also sort the field by date in the Wizard after save (similar to when I developed the checkboxWizard, upon save, all the selected items move to the top first).

    The wizard isn't close to using the maximum width anyway, so it would be easy to just add your fields to the left of each calendar button. Then you can also use the standard calendar view as well

    PS. And Contao creates the ID and then displays the new item, so the ID is always available, even if not saved. Look at the NEW url, and also at the top it always says: "Edit record ID 7" and if you were talking about the ID of the sub-control you insert dynamically with JS, then that is part of the JS code you have to write, to clone the correct element and adjust its sub-elements to have unique ids to link to.

  3. #3
    Experienced user
    Join Date
    06-20-09.
    Posts
    1,311

    Default Re: Multi-dates Selection

    I only want the dates graphically represented in the calendar, as the field may contain 200 0r more dates as timestamps seperated by a comma.

    <input type="hidden" name="individual_prices[0][0]" id="multiPrice_0" value="1286362800,1286967600,1287572400,1287486000 ,1286881200,1286276400,1286449200,1287054000,12876 58800,1287399600,1286794800,1286190000,1286103600, 1286708400,1287313200,1288868400,1288954800,128947 3200,1289559600,1290078000,1290164400,1290682800,1 290769200,1291287600,1291374000,1291892400,1291978 800,1292497200,1292583600,1293102000,1293188400,12 93793200,1294311600,1294398000,1295002800,12949164 00,1295521200,1295607600,1296212400,1296126000,129 3706800,1296730800,1296817200,1297335600,129742200 0,1297940400,1298026800,1298545200,1298631600,1299 236400,1299754800,1299841200,1300446000,1301050800 ,1300964400,1300359600,1301569200,1301655600,13022 64000,1302177600,1302782400,1303387200,1303473600, 1304078400,1303992000,1302868800,1296644400,129915 0000,1304596800,1304683200,1305201600,1305288000,1 305806400,1305892800,1306411200,1306497600,1307016 000,1307102400" />
    Its easy enough to see what you entered, you click on the button and the saved values show as dark colour in the calendar. You can quickly scroll through and select or deselect more, and resave.

    The idea is (for instance) those 200 dates are input for accommodation rented out at x price.
    A new row might be 300 dates at y price.

    Everything is stored serialized in one database textfield which is good. At this stage I'm only concerned with data in.

    if you were talking about the ID of the sub-control you insert dynamically with JS, then that is part of the JS code you have to write, to clone the correct element and adjust its sub-elements to have unique ids to link to
    yes, this is what i need to figure....

  4. #4
    Experienced user
    Join Date
    06-10-09.
    Location
    Cape Town, South Africa
    Posts
    1,387

    Default Re: Multi-dates Selection

    If I wanted to adjust the dates for a price that is all the same, e.g.

    (# is the date icon)

    # # 500.00 class1 (+) +
    # # 500.00 class1 (+) +
    # # 500.00 class1 (+) +
    # # 500.00 class1 (+) +
    # # 450.00 class2 (+) +
    # # 650.00 class3 (+) +


    Then how do I quickly know which date's price to change. I'll have to go look. That's 4 looks to know which date it is.
    In fact, you should probably make the class a drop-down as well

    2010-10-01 # 2010-10-31 # 500.00 class1 (+)
    2010-11-01 # 2010-11-30 # 500.00 class1 (+)
    2010-12-01 # 2010-12-31 # 500.00 class1 (+)
    2011-01-01 # 2011-01-31 # 500.00 class1 (+)

    I think a sub-table with the properties price dates (start and top) would have been a much simpler solution. Then you can also query the items individually and build queries using start stop which you can't do now, because it's serialised inside the data element.

  5. #5
    Experienced user
    Join Date
    06-20-09.
    Posts
    1,311

    Default Re: Multi-dates Selection

    hmmmm, i'll ponder this .....but its bedtime now.

  6. #6
    Experienced user
    Join Date
    06-20-09.
    Posts
    1,311

    Default Re: Multi-dates Selection

    ok ....pondered, and for my purposes i disagee.

    The wizard is in each record of a property listing, and the property listing already has a default price.
    "Sunshine Villa" - $500 night.
    The wizards purpose for my use anyway is as a Price Override, that is the owner wants Sunshine Villa to be $400 on a Thursday for the next three years, and $100 on every full moon, etc.
    So he create a new wizard row, enters 400 and selects all the Thursdays he wants. Its fast.
    Then create another new row, enter 100, and select the dates.
    The end result is he has 2 rows, which he can easily change.

    This wouldn't happen
    # # 500.00 class1 (+) +
    # # 500.00 class1 (+) +
    # # 500.00 class1 (+) +
    # # 500.00 class1 (+) +

    The class is for styling the frontend output, it probably will be a dropdown of colours instead, not sure yet.


    Doing it by date individually would take ages, 1 save for each date, 1 new row for each date.... plus I would have a phenomenal amount of record rows. Its not that easy to scan listed dates, seeing them on a calendar is much easier in my opinion. Plus if I want to put my price up across the board I just change 400 to 450 and save.

    Then you can also query the items individually and build queries using start stop which you can't do now, because it's serialised inside the data element.
    Serialized is not a problem, it doesn't need to be queried as such ... the stored dates will just be compared when I draw it out within the loop that draws the frontend booking calendar for each listing, and the class/colour printed out, and the price if he has elected it.

    Theres a bit to do, but I'm happier with this method than I would be with a list of dates.
    I'm hoping I can prevent the same date being entered in more than one calendar instance (not sure yet... it was possible in the first multi-date calendar I tried, but other things were no good), but I've only started looking at this one and it looks really powerful.
    Unfortunately I'm crap at javascript, so my original question still stands....
    Can anyone see how I would make the calendar instantiate on a new Wizard row????

  7. #7
    Experienced user
    Join Date
    06-10-09.
    Location
    Cape Town, South Africa
    Posts
    1,387

    Default Re: Multi-dates Selection

    Ok. Don't say i didn't warn you... ;-)

  8. #8
    Experienced user
    Join Date
    06-20-09.
    Posts
    1,311

    Default Re: Multi-dates Selection

    i'd never say that Thyon :roll:

  9. #9
    User
    Join Date
    07-26-09.
    Posts
    175

    Default Re: Multi-dates Selection

    Well, I will be developing similar tool in the nearest future, so if I invent something cool I will share it with you.

  10. #10
    Experienced user
    Join Date
    06-20-09.
    Posts
    1,311

    Default Re: Multi-dates Selection

    The newly cloned field looks like this in Firebug (its not visible in the page source)
    Code:
    <td style="padding-right: 7px;">
    <input type="text" value="1286881200,1286967600,1287054000" style="width: 150px; padding-right: 2px;" class="tl_text hasDatepick" name="individual_prices[3][0]" id="individual_prices3_0">
    
    <script type="text/javascript">
    &lt;!--//--&gt;&lt;![CDATA[//&gt;&lt;!--
      
    $j('#individual_prices0_0').datepick({
    	dateFormat: '@', 
        multiSelect: 999, monthsToShow: 3});
    
      
    //--&gt;&lt;!]]&gt;
    </script>
    </td>
    I've got it to produce the id i need for the calendar
    id="individual_prices3_0"
    by adding into the javascript file this line
    if (j==0)
    {
    first.id = first.name.replace(/\[[0-9]+\][[0-9]+\]/ig, i + '_' + j);
    }
    I needed to alter the id to be different from the name because it didn't work when it looked like an array.

    The javascript file:
    Code:
    var RealestatePrice =
    {
    	/**
    	 * RealestatePrice wizard
    	 * @param object
    	 * @param string
    	 * @param string
    	 */
    	realestatepriceWizard: function(el, command, id)
    	{
    		var table = $(id);
    		var thead = table.getFirst();
    		var tbody = thead.getNext();
    		var rows = tbody.getChildren();
    		var parentTd = $(el).getParent();
    		var parentTr = parentTd.getParent();
    		var cols = parentTr.getChildren();
    		var index = 0;
    		var selectElement = null;
    		for (var i=0; i<cols.length; i++)
    		{
    			if (cols[i] == parentTd)
    			{
    				break;
    			}
    
    			index++;
    		}
    
    		Backend.getScrollOffset();
    
    		switch (command)
    		{
    			case 'rnew':
    				var tr = new Element('tr');
    				var childs = parentTr.getChildren();
    
    				for (var i=0; i<childs.length; i++)
    				{
    					var next = childs[i].clone(true).injectInside(tr);
    					if (!selectElement) selectElement = next.getFirst();
    					next.getFirst().value = '';
    				}
    				tr.injectAfter(parentTr);
    				break;
    
    			case 'rcopy':
    				var tr = new Element('tr');
    				var childs = parentTr.getChildren();
    
    				for (var i=0; i<childs.length; i++)
    				{
    					var next = childs[i].clone(true).injectInside(tr);
    					if (!selectElement) selectElement = next.getFirst();
    					next.getFirst().value = childs[i].getFirst().value;
    				}
    				tr.injectAfter(parentTr);
    				break;
    
    			case 'rup':
    				parentTr.getPrevious() ? parentTr.injectBefore(parentTr.getPrevious()) : parentTr.injectInside(tbody);
    				break;
    
    			case 'rdown':
    				parentTr.getNext() ? parentTr.injectAfter(parentTr.getNext()) : parentTr.injectBefore(tbody.getFirst().getNext());
    				break;
    
    			case 'rdelete':
    				(rows.length > 1) ? parentTr.dispose() : null;
    				break;
    		}
    
    		rows = tbody.getChildren();
    
    		for (var i=0; i<rows.length; i++)
    		{
    			var childs = rows[i].getChildren();
    
    			for (var j=0; j<childs.length; j++)
    			{
    				var first = childs[j].getFirst();
    				if (first && first.type.toLowerCase() == 'text')
    				{
    					first.name = first.name.replace(/\[[0-9]+\][[0-9]+\]/ig, '[' + i + '][' + j + ']');
    					if (j==0)
    					{
    					first.id = first.name.replace(/\[[0-9]+\][[0-9]+\]/ig, i + '_' + j);
    					}
    				}
    				
    			}
    		}
    		if (selectElement)
    		{
    			selectElement.select();
    		}
    	}
    };
    but there is still something I need to do -
    alter the cloned javascript to reflect the new id- it still reflects the id of the parent it was cloned from
    $j('#individual_prices0_0').datepick({
    Any insight anyone?

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •