Feature #17436 » 0005899_v7.patch

Administrator Admin, 2008-01-15 11:46

View differences:

t3lib/class.t3lib_tceforms.php (Arbeitskopie)
292 292
	var $requiredFields=array();				// Used to register input-field names, which are required. (Done during rendering of the fields). This information is then used later when the JavaScript is made.
293 293
	var $requiredAdditional=array();			// Used to register input-field names, which are required an have additional requirements (e.g. like a date/time must be positive integer). The information of this array is merged with $this->requiredFields later.
294 294
	var $requiredElements=array();				// Used to register the min and max number of elements for selectorboxes where that apply (in the "group" type for instance)
295
	var $requiredNested=array();				// Used to determine where $requiredFields or $requiredElements are nested (in Tabs or IRRE)
295 296
	var $renderDepth=0;							// Keeps track of the rendering depth of nested records.
296 297
	var $savedSchemes=array();					// Color scheme buffer.
297 298
	var $dynNestedStack = array();				// holds the path an element is nested in (e.g. required for RTEhtmlarea)
......
1068 1069
		foreach ($evalList as $func) {
1069 1070
			switch ($func) {
1070 1071
				case 'required':
1071
					$this->requiredFields[$table.'_'.$row['uid'].'_'.$field]=$PA['itemFormElName'];
1072
					$this->registerRequiredProperty('field', $table.'_'.$row['uid'].'_'.$field, $PA['itemFormElName']);
1072 1073
						// Mark this field for date/time disposal:
1073 1074
					if (array_intersect($evalList, array('date', 'datetime', 'time'))) {
1074 1075
						 $this->requiredAdditional[$PA['itemFormElName']]['isPositiveNumber'] = true;
......
1844 1845
		$minitems = t3lib_div::intInRange($config['minitems'],0);
1845 1846

  
1846 1847
			// Register the required number of elements:
1847
		$this->requiredElements[$PA['itemFormElName']] = array($minitems,$maxitems,'imgName'=>$table.'_'.$row['uid'].'_'.$field);
1848
		$this->registerRequiredProperty('range', $PA['itemFormElName'], array($minitems,$maxitems,'imgName'=>$table.'_'.$row['uid'].'_'.$field));
1848 1849

  
1849 1850
			// Get "removeItems":
1850 1851
		$removeItems = t3lib_div::trimExplode(',',$PA['fieldTSConfig']['removeItems'],1);
......
1948 1949
		}
1949 1950

  
1950 1951
		$item.= '<input type="hidden" name="'.$PA['itemFormElName'].'_mul" value="'.($config['multiple']?1:0).'"'.$disabled.' />';
1951
		$this->requiredElements[$PA['itemFormElName']] = array($minitems,$maxitems,'imgName'=>$table.'_'.$row['uid'].'_'.$field);
1952
		$this->registerRequiredProperty('range', $PA['itemFormElName'], array($minitems,$maxitems,'imgName'=>$table.'_'.$row['uid'].'_'.$field));
1952 1953
		$info='';
1953 1954

  
1954 1955
			// "Extra" configuration; Returns configuration for the field based on settings found in the "types" fieldlist. See http://typo3.org/documentation/document-library/doc_core_api/Wizards_Configuratio/.
......
5049 5050
			inline.addToDataArray('.t3lib_div::array2json($this->inline->inlineData).');
5050 5051
			';
5051 5052
		}
5053
			// Registered nested elements for tabs or inline levels:
5054
		if (count($this->requiredNested)) {
5055
			$out .= '
5056
			TBE_EDITOR.addNested('.t3lib_div::array2json($this->requiredNested).');
5057
			';
5058
		}
5052 5059
			// elements which are required or have a range definition:
5053 5060
		if (count($elements)) {
5054 5061
			$out .= '
......
5056 5063
			TBE_EDITOR.initRequired()
5057 5064
			';
5058 5065
		}
5059
			// $this->additionalJS_post:
5066
			// $this->additionalJS_submit:
5060 5067
		if ($this->additionalJS_submit) {
5061 5068
			$additionalJS_submit = implode('', $this->additionalJS_submit);
5062 5069
			$additionalJS_submit = str_replace("\r", '', $additionalJS_submit);
......
5814 5821
		}
5815 5822
		return ($json ? t3lib_div::array2json($result) : $result);
5816 5823
	}
5824

  
5825
	/**
5826
	 * Takes care of registering properties in requiredFields and requiredElements.
5827
	 * The current hierarchy of IRRE and/or Tabs is stored. Thus, it is possible to determine,
5828
	 * which required field/element was filled incorrectly and show it, even if the Tab or IRRE
5829
	 * level is hidden.
5830
	 *
5831
	 * @param	string		$type: Type of requirement ('field' or 'range')
5832
	 * @param	string		$name: The name of the form field
5833
	 * @param	mixed		$value: For type 'field' string, for type 'range' array
5834
	 * @return	void
5835
	 */
5836
	protected function registerRequiredProperty($type, $name, $value) {
5837
		if ($type == 'field' && is_string($value)) {
5838
			$this->requiredFields[$name] = $value;
5839
				// requiredFields have name/value swapped! For backward compatibility we keep this:
5840
			$itemName = $value;
5841
		} elseif ($type == 'range' && is_array($value)) {
5842
			$this->requiredElements[$name] = $value;
5843
			$itemName = $name;
5844
		}
5845
			// Set the situation of nesting for the current field:
5846
		$this->registerNestedElement($itemName);
5847
	}
5848

  
5849
	/**
5850
	 * Sets the current situation of nested tabs and inline levels for a given element.
5851
	 *
5852
	 * @param	string		$itemName: The element the nesting should be stored for
5853
	 * @param	boolean		$setLevel: Set the reverse level lookup - default: true
5854
	 * @return	void
5855
	 */
5856
	protected function registerNestedElement($itemName, $setLevel=true) {
5857
		$dynNestedStack = $this->getDynNestedStack();
5858
		if (count($dynNestedStack) && preg_match('/^(.+\])\[(\w+)\]$/', $itemName, $match)) {
5859
			array_shift($match);
5860
			$this->requiredNested[$itemName] = array(
5861
				'parts' => $match,
5862
				'level' => $dynNestedStack,
5863
			);
5864
		}
5865
	}
5817 5866
}
5818 5867

  
5819 5868

  
t3lib/jsfunc.inline.js (Arbeitskopie)
659 659

  
660 660
			// Remove from TBE_EDITOR (required fields, required range, etc.):
661 661
		if (TBE_EDITOR && TBE_EDITOR.removeElement) {
662
			var removeStack = [];
662 663
			inlineRecords = Element.getElementsByClassName(objectId+'_div', 'inlineRecord');
663 664
				// Remove nested child records from TBE_EDITOR required/range checks:
664 665
			for (i=inlineRecords.length-1; i>=0; i--) {
......
667 668
					childObjectId = this.data.map[inlineRecords[i].name];
668 669
					childTable = this.data.config[childObjectId].table;
669 670
					for (j=records.length-1; j>=0; j--) {
670
						TBE_EDITOR.removeElement(this.prependFormFieldNames+'['+childTable+']['+records[j]+']');
671
						removeStack.push(this.prependFormFieldNames+'['+childTable+']['+records[j]+']');
671 672
					}
672 673
				}
673 674
			}
674
			TBE_EDITOR.removeElement(this.prependFormFieldNames+shortName);
675
			removeStack.push(this.prependFormFieldNames+shortName);
676
			TBE_EDITOR.removeElementArray(removeStack);
675 677
		}
676 678

  
677 679
			// If the record is new and was never saved before, just remove it from DOM:
t3lib/class.t3lib_tceforms_inline.php (Arbeitskopie)
489 489
	 */
490 490
	function renderForeignRecordHeaderControl($parentUid, $foreign_table, $rec, $config = array()) {
491 491
			// Initialize:
492
		$cells=array();
492
		$cells = array();
493 493
		$isNewItem = substr($rec['uid'], 0, 3) == 'NEW';
494 494

  
495 495
		$tcaTableCtrl =& $GLOBALS['TCA'][$foreign_table]['ctrl'];
......
515 515
			// This expresses the edit permissions for this particular element:
516 516
		$permsEdit = ($isPagesTable && ($localCalcPerms&2)) || (!$isPagesTable && ($calcPerms&16));
517 517

  
518
			// Icon to visualize that a required field is nested in this inline level:
519
		$cells[] = '<img id="'.$nameObjectFtId.'_req" src="clear.gif" width="10" height="10" hspace="4" vspace="3" alt="" />';
520

  
518 521
			// "Info": (All records)
519 522
		if (!$isNewItem)
520 523
			$cells[]='<a href="#" onclick="'.htmlspecialchars('top.launchView(\''.$foreign_table.'\', \''.$rec['uid'].'\'); return false;').'">'.
typo3/jsfunc.tbe_editor.js (Arbeitskopie)
48 48
	*/
49 49

  
50 50
	elements: {},
51
	nested: {'field':{}, 'level':{}},
52
	ignoreElements: [],
51 53
	recentUpdatedElements: {},
52 54
	actionChecks: { submit:	[] },
53 55

  
......
78 80
		TBE_EDITOR.recentUpdatedElements = elements;
79 81
		TBE_EDITOR.elements = $H(TBE_EDITOR.elements).merge(elements).toObject();
80 82
	},
83
	addNested: function(elements) {
84
		var name, nested, levelMax, i, currentLevel, subLevel;
85
		// Merge data structures:
86
		if (elements) {
87
			for (name in elements) {
88
				nested = elements[name];
89
				if (nested.level && nested.level.length) {
90
						// If the first level is of type 'inline', it could be created by a AJAX request to IRRE.
91
						// So, try to get the upper levels this dynamic level is nested in:
92
					if (typeof inline!='undefined' && nested.level[0][0]=='inline') {
93
						nested.level = inline.findContinuedNestedLevel(nested.level, nested.level[0][1]);
94
					}
95
					levelMax = nested.level.length-1;
96
					for (i=0; i<=levelMax; i++) {
97
						currentLevel = TBE_EDITOR.getNestedLevelIdent(nested.level[i]);
98
						if (typeof TBE_EDITOR.nested.level[currentLevel] == 'undefined') {
99
							TBE_EDITOR.nested.level[currentLevel] = { 'clean': true, 'item': {}, 'sub': {} };
100
						}
101
							// Add next sub level to the current level:
102
						if (i<levelMax) {
103
							subLevel = TBE_EDITOR.getNestedLevelIdent(nested.level[i+1]);
104
							TBE_EDITOR.nested.level[currentLevel].sub[subLevel] = true;
105
							// Add the current item to the last level in nesting:
106
						} else {
107
							TBE_EDITOR.nested.level[currentLevel].item[name] = nested.parts;
108
						}
109
					}
110
				}
111
			}
112
				// Merge the nested fields:
113
			TBE_EDITOR.nested.field = $H(TBE_EDITOR.nested.field).merge(elements).toObject();
114
		}
115
	},
81 116
	removeElement: function(record) {
82 117
		if (TBE_EDITOR.elements && TBE_EDITOR.elements[record]) {
118
				// Inform envolved levels the this record is removed and the missing requirements are resolved:
119
			$H(TBE_EDITOR.elements[record]).each(
120
				function(pair) {
121
					TBE_EDITOR.notifyNested(record+'['+pair.key+']', true);
122
				}
123
			);
83 124
			delete(TBE_EDITOR.elements[record]);
84 125
		}
85 126
	},
127
	removeElementArray: function(removeStack) {
128
		if (removeStack && removeStack.length) {
129
			TBE_EDITOR.ignoreElements = removeStack;
130
			for (var i=removeStack.length; i>=0; i--) {
131
				TBE_EDITOR.removeElement(removeStack[i]);
132
			}
133
			TBE_EDITOR.ignoreElements = [];
134
		}
135
	},
86 136
	getElement: function(record, field, type) {
87 137
		var result = null;
88 138
		var element;
......
101 151
	checkElements: function(type, recentUpdated, record, field) {
102 152
		var result = 1;
103 153
		var elementName, elementData, elementRecord, elementField;
104
		var source = recentUpdated ? TBE_EDITOR.recentUpdatedElements : TBE_EDITOR.elements;
154
		var source = (recentUpdated ? TBE_EDITOR.recentUpdatedElements : TBE_EDITOR.elements);
105 155

  
156
		if (TBE_EDITOR.ignoreElements.length && TBE_EDITOR.ignoreElements.indexOf(record)!=-1) {
157
			return result;
158
		}
159

  
106 160
		if (type) {
107 161
			if (record && field) {
108 162
				elementName = record+'['+field+']';
109 163
				elementData = TBE_EDITOR.getElement(record, field, type);
110 164
				if (elementData) {
111
					if (!TBE_EDITOR.checkElementByType(type, elementName, elementData)) result = 0;
165
					if (!TBE_EDITOR.checkElementByType(type, elementName, elementData, recentUpdated)) {
166
						result = 0;
167
					}
112 168
				}
113 169

  
114 170
			} else {
......
122 178
						elementData = TBE_EDITOR.getElement(elementRecord, elementField, type);
123 179
						if (elementData) {
124 180
							elementName = elementRecord+'['+elementField+']';
125
							if (!TBE_EDITOR.checkElementByType(type, elementName, elementData)) result = 0;
181
							if (!TBE_EDITOR.checkElementByType(type, elementName, elementData, recentUpdated)) {
182
								result = 0;
183
							}
126 184
						}
127 185
					}
128 186
				}
......
131 189

  
132 190
		return result;
133 191
	},
134
	checkElementByType: function(type, elementName, elementData) {
192
	checkElementByType: function(type, elementName, elementData, autoNotify) {
135 193
		var result = 1;
136 194

  
137 195
		if (type) {
......
139 197
				var value = document[TBE_EDITOR.formname][elementName].value;
140 198
				if (!value || elementData.additional && elementData.additional.isPositiveNumber && (isNaN(value) || Number(value) <= 0)) {
141 199
					result = 0;
142
					TBE_EDITOR.setImage('req_'+elementData.requiredImg, TBE_EDITOR.images.req);
200
					if (autoNotify) {
201
						TBE_EDITOR.setImage('req_'+elementData.requiredImg, TBE_EDITOR.images.req);
202
						TBE_EDITOR.notifyNested(elementName, false);
203
					}
143 204
				}
144 205
			} else if (type == 'range' && elementData.range) {
145 206
				var formObj = document[TBE_EDITOR.formname][elementName+'_list'];
......
152 213
				}
153 214
				if (!TBE_EDITOR.checkRange(formObj, elementData.range[0], elementData.range[1])) {
154 215
					result = 0;
155
					TBE_EDITOR.setImage('req_'+elementData.rangeImg, TBE_EDITOR.images.req);
216
					if (autoNotify) {
217
						TBE_EDITOR.setImage('req_'+elementData.rangeImg, TBE_EDITOR.images.req);
218
						TBE_EDITOR.notifyNested(elementName, false);
219
					}
156 220
				}
157 221
			}
158 222
		}
159 223

  
160 224
		return result;
161 225
	},
226
	// Notify tabs and inline levels with nested requiredFields/requiredElements:
227
	notifyNested: function(elementName, resolved) {
228
		if (TBE_EDITOR.nested.field[elementName]) {
229
			var i, nested, element, fieldLevels, fieldLevelIdent, nestedLevelType, nestedLevelName;
230
			fieldLevels = TBE_EDITOR.nested.field[elementName].level;
231
			TBE_EDITOR.nestedCache = {};
232

  
233
			for (i=fieldLevels.length-1; i>=0; i--) {
234
				nestedLevelType = fieldLevels[i][0];
235
				nestedLevelName = fieldLevels[i][1];
236
				fieldLevelIdent = TBE_EDITOR.getNestedLevelIdent(fieldLevels[i]);
237
					// Construct the CSS id strings of the image/icon tags showing the notification:
238
				if (nestedLevelType == 'tab') {
239
					element = nestedLevelName+'-REQ';
240
				} else if (nestedLevelType == 'inline') {
241
					element = nestedLevelName+'_req';
242
				} else {
243
					continue;
244
				}
245
					// Set the icons:
246
				if (resolved) {
247
					if (TBE_EDITOR.checkNested(fieldLevelIdent)) {
248
						TBE_EDITOR.setImage(element, TBE_EDITOR.images.clear);
249
					} else {
250
						break;
251
					}
252
				} else {
253
					if (TBE_EDITOR.nested.level && TBE_EDITOR.nested.level[fieldLevelIdent]) {
254
						TBE_EDITOR.nested.level[fieldLevelIdent].clean = false;
255
					}
256
					TBE_EDITOR.setImage(element, TBE_EDITOR.images.req);
257
				}
258
			}
259
		}
260
	},
261
	// Check all the input fields on a given level of nesting - if only on is unfilled, the whole level is marked as required:
262
	checkNested: function(nestedLevelIdent) {
263
		var nestedLevel, isClean;
264
		if (nestedLevelIdent && TBE_EDITOR.nested.level && TBE_EDITOR.nested.level[nestedLevelIdent]) {
265
			nestedLevel = TBE_EDITOR.nested.level[nestedLevelIdent];
266
			if (!nestedLevel.clean) {
267
				if (typeof nestedLevel.item == 'object') {
268
					$H(nestedLevel.item).each(
269
						function(pair) {
270
							if (isClean || typeof isClean == 'undefined') {
271
								isClean = (
272
									TBE_EDITOR.checkElements('required', false, pair.value[0], pair.value[1]) &&
273
									TBE_EDITOR.checkElements('range', false, pair.value[0], pair.value[1])
274
								);
275
							}
276
						}
277
					);
278
					if (typeof isClean != 'undefined' && !isClean) {
279
						return false;
280
					}
281
				}
282
				if (typeof nestedLevel.sub == 'object') {
283
					$H(nestedLevel.sub).each(
284
						function(pair) {
285
							if (isClean || typeof isClean == 'undefined') {
286
								isClean = TBE_EDITOR.checkNested(pair.key);
287
							}
288
						}
289
					);
290
					if (typeof isClean != 'undefined' && !isClean) {
291
						return false;
292
					}
293
				}
294
					// Store the result, that this level (the fields on this and the sub levels) are clean:
295
				nestedLevel.clean = true;
296
			}
297
		}
298
		return true;
299
	},
300
	getNestedLevelIdent: function(level) {
301
		return level.join('::');
302
	},
162 303
	addActionChecks: function(type, checks) {
163 304
		TBE_EDITOR.actionChecks[type].push(checks);
164 305
	},
......
213 354
		if (TBE_EDITOR.getElement(theRecord,field,'required') && document[TBE_EDITOR.formname][theField])	{
214 355
			if (TBE_EDITOR.checkElements('required', false, theRecord, field)) {
215 356
				TBE_EDITOR.setImage(imgReqObjName,TBE_EDITOR.images.clear);
357
				TBE_EDITOR.notifyNested(theField, true);
216 358
			} else {
217 359
				TBE_EDITOR.setImage(imgReqObjName,TBE_EDITOR.images.req);
360
				TBE_EDITOR.notifyNested(theField, false);
218 361
			}
219 362
		}
220 363
		if (TBE_EDITOR.getElement(theRecord,field,'range') && document[TBE_EDITOR.formname][theField]) {
221 364
			if (TBE_EDITOR.checkElements('range', false, theRecord, field))	{
222 365
				TBE_EDITOR.setImage(imgReqObjName,TBE_EDITOR.images.clear);
366
				TBE_EDITOR.notifyNested(theField, true);
223 367
			} else {
224 368
				TBE_EDITOR.setImage(imgReqObjName,TBE_EDITOR.images.req);
369
				TBE_EDITOR.notifyNested(theField, false);
225 370
			}
226 371
		}
227 372

  
......
297 442
		TBE_EDITOR.checkElements('range', true);
298 443
	},
299 444
	setImage: function(name,image) {
445
		var object;
300 446
		if (document[name]) {
447
			object = document[name];
448
		} else if (document.getElementById(name)) {
449
			object = document.getElementById(name);
450
		}
451
		if (object) {
301 452
			if (typeof image == 'object') {
302 453
				document[name].src = image.src;
303 454
			} else {
typo3/template.php (Arbeitskopie)
1457 1457
				}
1458 1458

  
1459 1459
				$mouseOverOut = ' onmouseover="DTM_mouseOver(this);" onmouseout="DTM_mouseOut(this);"';
1460
				$requiredIcon = '<img id="'.$id.'-'.$index.'-REQ" src="clear.gif" width="10" height="10" hspace="4" alt="" />';
1460 1461

  
1461 1462
				if (!$foldout)	{
1462 1463
						// Create TAB cell:
......
1465 1466
							($isNotEmpty ? '<a href="#" onclick="'.htmlspecialchars($onclick).'"'.($def['linkTitle'] ? ' title="'.htmlspecialchars($def['linkTitle']).'"':'').'>' : '').
1466 1467
							$def['icon'].
1467 1468
							($def['label'] ? htmlspecialchars($def['label']) : '&nbsp;').
1469
							$requiredIcon.
1468 1470
							$this->icons($def['stateIcon'],'margin-left: 10px;').
1469 1471
							($isNotEmpty ? '</a>' :'').
1470 1472
							'</td>';
......
1476 1478
							($isNotEmpty ? '<a href="#" onclick="'.htmlspecialchars($onclick).'"'.($def['linkTitle'] ? ' title="'.htmlspecialchars($def['linkTitle']).'"':'').'>' : '').
1477 1479
							$def['icon'].
1478 1480
							($def['label'] ? htmlspecialchars($def['label']) : '&nbsp;').
1481
							$requiredIcon .
1479 1482
							($isNotEmpty ? '</a>' : '').
1480 1483
							'</div>';
1481 1484
				}
(3-3/4)