diff --git a/api/js/etemplate/et2_widget_entry.js b/api/js/etemplate/et2_widget_entry.js index 8f5545e71d..d486efda46 100644 --- a/api/js/etemplate/et2_widget_entry.js +++ b/api/js/etemplate/et2_widget_entry.js @@ -138,7 +138,12 @@ var et2_entry = (function(){ "use strict"; return et2_valueWidget.extend( // If value was set, find the record explicitly. if(typeof this.options.value == 'string') { - widget.options.value = this.getRoot().getArrayMgr('content').getEntry(this.prefix+this.options.value + '['+this.options.field+']'); + widget.options.value = this.getArrayMgr('content').getEntry(this.id+'['+this.options.field+']') || + this.getRoot().getArrayMgr('content').getEntry(this.prefix+this.options.value + '['+this.options.field+']'); + } + else if (this.options.field && this.options.value && this.options.value[this.options.field]) + { + widget.options.value = this.options.value[this.options.field]; } if(this.options.compare) { diff --git a/api/src/Etemplate.php b/api/src/Etemplate.php index 83c301f8a7..9a519608a6 100644 --- a/api/src/Etemplate.php +++ b/api/src/Etemplate.php @@ -531,6 +531,7 @@ class Etemplate extends Etemplate\Widget\Template public static function reset_request() { self::$request = Etemplate\Request::read(); + self::$cache = array(); } /** * Get template data as array diff --git a/api/src/Etemplate/Widget/Entry.php b/api/src/Etemplate/Widget/Entry.php index 1bac26f21a..b8b92d839c 100644 --- a/api/src/Etemplate/Widget/Entry.php +++ b/api/src/Etemplate/Widget/Entry.php @@ -70,7 +70,8 @@ abstract class Entry extends Transformer $attrs['id'] = $this->id; $form_name = self::form_name($cname, $this->id); - $data_id = $attrs['value'] ? self::form_name($cname, $attrs['value']) : self::form_name($cname, self::ID_PREFIX . $this->id); + $prefixed_id = (substr($this->id, 0, 1) == self::ID_PREFIX ? $this->id : self::ID_PREFIX . $this->id); + $data_id = $attrs['value'] ? self::form_name($cname, $attrs['value']) : self::form_name($cname, $prefixed_id); // No need to proceed if(!$data_id) return; @@ -94,8 +95,8 @@ abstract class Entry extends Transformer } // Set the new value so transformer can find it. Use prefix to avoid changing the original value - $new_value =& self::get_array(self::$request->content, self::ID_PREFIX .$this->id, true, false); - if (true) $new_value = $data; + $new_value =& self::get_array(self::$request->content, $prefixed_id, true, false); + if ($data) $new_value = $data; // Check for missing field if(!$attrs['field'] && !$attrs['alternate_fields']) @@ -109,10 +110,15 @@ abstract class Entry extends Transformer $this->regex($attrs, $new_value); + // Change this before parent so client gets what it needs + // Client is expecting to find data with the prefix + if(substr($this->id, 0, 1) !== self::ID_PREFIX) + { + $this->id = self::ID_PREFIX . $this->id . "[{$attrs['field']}]"; + } + parent::beforeSendToClient($cname, $expand); - // Change this after parent::beforeSendToClient or it adds another layer - $this->id = self::ID_PREFIX . $this->id . "[{$attrs['field']}]"; } @@ -173,7 +179,8 @@ abstract class Entry extends Transformer protected function customfield($attrs, &$data) { list($app, $type) = explode('-',$attrs['type']); - $id = is_array($data) ? static::get_array($data, $this->id) : $data; + $data_id = $attrs['value'] ?: $attrs['id']; + $id = is_array($data) ? static::get_array($data, $data_id) : $data; if(!$app || !$type || !$GLOBALS['egw_info']['apps'][$app] || !$id || // Simple CF, already there $data[$attrs['field']] @@ -207,9 +214,10 @@ abstract class Entry extends Transformer */ protected function regex($attrs, &$data) { - $id = is_array($data) ? static::get_array($data, $this->id) : $data; + $data_id = $attrs['value'] ?: $attrs['id']; + $id = is_array($data) ? static::get_array($data, $data_id) : $data; $value =& $this->get_data_field($attrs, $data); - if(!$attrs['regex'] || !$id || !$value) + if(!$attrs['regex'] || !$value) { return; } @@ -221,6 +229,6 @@ abstract class Entry extends Transformer $replace = array_pop($regex); $regex = implode(',', $regex); } - $value = preg_replace($regex, $replace, $value); + $data[$attrs['field']] = preg_replace($regex, $replace, $value); } } \ No newline at end of file diff --git a/api/templates/test/entry_test.xet b/api/templates/test/entry_test.xet new file mode 100644 index 0000000000..c52bf96db3 --- /dev/null +++ b/api/templates/test/entry_test.xet @@ -0,0 +1,25 @@ + + + + + + \ No newline at end of file diff --git a/api/templates/test/entry_test_contact.xet b/api/templates/test/entry_test_contact.xet new file mode 100644 index 0000000000..d4967324cb --- /dev/null +++ b/api/templates/test/entry_test_contact.xet @@ -0,0 +1,27 @@ + + + + + + \ No newline at end of file diff --git a/api/tests/Etemplate/Widget/ContactEntryTest.php b/api/tests/Etemplate/Widget/ContactEntryTest.php new file mode 100644 index 0000000000..db65bb3f79 --- /dev/null +++ b/api/tests/Etemplate/Widget/ContactEntryTest.php @@ -0,0 +1,139 @@ +elements as $id) + { + list($app, $id) = explode(':',$id); + + $bo_class = "{$app}_bo"; + $bo = new $bo_class(); + $bo->delete($id, true, false, true); + } + + parent::tearDown(); + + } + + /** + * Test that the correct value is extracted, based on the value attribute, + * or if it is missing try the ID. This goes to contact, rather than + * abstract test class. + */ + public function testContact() + { + // Create a test contact + $test_contact = $this->make_contact(); + + // Instanciate the template + $etemplate = new Etemplate(); + $etemplate->read(static::TEST_TEMPLATE, 'test'); + + // Content - IDs are important, they should match valid contacts + $content = array( + 'entry_id' => $GLOBALS['egw_info']['user']['person_id'], + 'info_contact' => $test_contact + ); + + $result = $this->mockedExec($etemplate, $content); + $data = array(); + foreach($result as $response) + { + if($response['type'] == 'et2_load') + { + $data = $response['data']['data']; + break; + } + } + + // Check that the entry was found + $this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'widget']); + $this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'entry_id']); + $this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'info_contact_email']); + $this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'info_contact']); + $this->assertEmpty($data['content'][Entry::ID_PREFIX.'no_value']); + + // Check that the value is present - exact value is pulled client side + $this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'widget']['email']); + $this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'entry_id']['n_fn']); + + $this->assertEquals($GLOBALS['egw_info']['user']['account_email'], $data['content'][Entry::ID_PREFIX.'entry_id']['email']); + $this->assertEquals($GLOBALS['egw_info']['user']['account_fullname'], $data['content'][Entry::ID_PREFIX.'entry_id']['n_fn']); + + // Values are to be done as labels + $this->assertEquals('label', $data['modifications'][Entry::ID_PREFIX.'widget[email]']['type'], 'Unexpected widget type'); + $this->assertEquals('label', $data['modifications'][Entry::ID_PREFIX.'entry_id[n_fn]']['type'], 'Unexpected widget type'); + $this->assertEquals('label', $data['modifications'][Entry::ID_PREFIX.'info_contact_email[email]']['type'], 'Unexpected widget type'); + $this->assertEquals('label', $data['modifications'][Entry::ID_PREFIX.'info_contact[email]']['type'], 'Unexpected widget type'); + $this->assertEquals('label', $data['modifications'][Entry::ID_PREFIX.'no_value[email]']['type'], 'Unexpected widget type'); + + // No errors + $this->assertEmpty($data['validation_errors']); + } + + /** + * Make a contact so we can test + */ + protected function make_contact() + { + $bo = new \addressbook_bo(); + $element = array( + 'n_fn' => "Mr Test Guy", + 'n_family' => 'Guy', + 'n_fileas' => 'Test Organisation: Guy, Test', + 'n_given' => 'Test', + 'n_prefix' => 'Mr', + 'org_name' => 'Test Organisation', + 'tel_cell' => '555-4321', + 'tel_home' => '66 12 34 56', + 'bday' => "1995-07-28T10:52:54Z" + ); + $element_id = $bo->save($element, true, true, true, true); + $this->elements[] = 'addressbook:'.$element_id; + return $element_id; + } +} + +/** + * Testable entry widget + */ +class EntryTestWidget extends \EGroupware\Api\Etemplate\Widget\Entry +{ + public function get_entry($value, array $attrs) + { + $entry = EntryTest::getEntry($value, $attrs); + + return $entry; + } +} \ No newline at end of file diff --git a/api/tests/Etemplate/Widget/EntryTest.php b/api/tests/Etemplate/Widget/EntryTest.php new file mode 100644 index 0000000000..3d8bcddc7f --- /dev/null +++ b/api/tests/Etemplate/Widget/EntryTest.php @@ -0,0 +1,138 @@ + 7, + 'entry_field_1' => 'Field 1', + 'entry_field_2' => 'Field 2', + 'entry_field_3' => 'Field 3', + 'entry_num_1' => 1, + 'entry_num_2' => 2, + 'entry_num_3' => 3, + 'entry_date' => '2018-11-12' + ); + return $entry[$value]; + } + /** + * Test that the correct value is extracted, based on the value attribute, + * or if it is missing try the ID + */ + public function testValueAttr() + { + // Instanciate the template + $etemplate = new Etemplate(); + $etemplate->read(static::TEST_TEMPLATE, 'test'); + + // Content - entry ID is important, it should match what getEntry() gives + $content = array( + 'entry_id' => '7', + 'not_entry_id' => '123' + ); + + $result = $this->mockedExec($etemplate, $content); + $data = array(); + foreach($result as $response) + { + if($response['type'] == 'et2_load') + { + $data = $response['data']['data']; + break; + } + } + + // Check that the entry was found + $this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'widget']); + $this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'entry_id']); + + // Check that the entry was not found + $this->assertEmpty($data['content'][Entry::ID_PREFIX.'no_value']); + + // Check that the value is present - exact value is pulled client side + $this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'widget']['entry_field_1']); + $this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'entry_id']['entry_field_2']); + + // No errors + $this->assertEmpty($data['validation_errors']); + } + + /** + * Check on compare attribute. + * Actual comparison is done client side, but here we check that the value is found. + */ + public function testCompareAttr() + { + // Instanciate the template + $etemplate = new Etemplate(); + $etemplate->read(static::TEST_TEMPLATE, 'test'); + + // Content - entry ID is important, it should match what getEntry() gives + $content = array( + 'entry_id' => '7', + ); + + $result = $this->mockedExec($etemplate, $content); + $data = array(); + foreach($result as $response) + { + if($response['type'] == 'et2_load') + { + $data = $response['data']['data']; + break; + } + } + + // Check that the value is present - exact value is pulled client side + $this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'compare']['entry_num_1']); + } +} + +/** + * Testable entry widget + */ +class EntryTestWidget extends \EGroupware\Api\Etemplate\Widget\Entry +{ + public function get_entry($value, array $attrs) + { + $entry = EntryTest::getEntry($value, $attrs); + + return $entry; + } +} \ No newline at end of file