-
Posts
1,306 -
Joined
-
Last visited
-
Days Won
13
Everything posted by Juergen
-
Hello @ all ! Today I want to share another new inputfield with the community! It is called Fieldtype OpeningHours and it is designed to enter one or multiple times per day (especially for company opening times). I know that there is another great fieldtype in the repository (https://modules.processwire.com/modules/fieldtype-business-hours/), but I wanted to create my own with a different UI than the other one. Here is a screencast of what it looks like in action: OpeningHours.mp4 A lot of things going on behind the scenes and I dont want to write it all down here, because you can find the whole information on my Github account. https://github.com/juergenweb/FieldtypeOpeningHours Requirements: PHP >= 8.0 (because it uses union types, I have also tested it with new PHP 8.2) ProcessWire >=3.0.181 If you may find any bugs, have any ideas to improve this fieldtype please report it in my Github repository. Greetings from Austria and have a nice day! CHANGELOG: 21.7.20 Add new option to show (true) or hide (false) days with no opening hours on various methods (please be aware that setting options has been changed - it is recommended to deinstall old version and install this inputfield again) . Take a look at the READ.ME for further instructions. 1.1 Add multilang support for timeformat and add 2 additional Schema.org markup methods UPDATE: 09-06-2023: The module has been added to the module directory and can be downloaded from there after it has been published.
- 21 replies
-
- 18
-
Hello @ all, I have created an inputfield with a configuration field in the backend where you can set a time format for every language (see screenshot below). As you can see both language values (default and German) have the default value(%R), but I have set different values, which were correctly stored in the DB (see screenshot below): As you can see the values are '%R' and '%r'. I have created the configuration inputfield like this: /** @var InputfieldText $f */ $languages = $this->wire('languages'); $f = $this->wire('modules')->get('InputfieldText'); $f->attr('name+id', 'timeformat'); $f->label = $this->_('Timeformat on frontend'); $f->initValue = '%R';//default value $f->attr('value', $this->timeformat ? $this->timeformat : '%R'); $this->message($this->get('timesformat')); if($languages) { $f->useLanguages = true; foreach($languages as $language) { if($language->isDefault()) continue; $f->set("value$language", (string) $this->get("timeformat$language->id")); } } $f->inputType = 'text'; $f->description = $this->_('Please enter the time format that the times should appear on the frontend in strftime format.'); $f->notes = sprintf($this->_('For example shows the time as 08:00, as 08:00 AM. You can find more examples at %s.'), '<a href="https://www.php.net/manual/de/function.strftime.php">https://www.php.net/manual/de/function.strftime.php</a>'); $f->columnWidth = 100; The important part here is: if($languages) { $f->useLanguages = true; foreach($languages as $language) { if($language->isDefault()) continue; $f->set("value$language", (string) $this->get("timeformat$language->id")); } } I have borrowed the code from the DateTimeInputfield (https://github.com/processwire/processwire/blob/master/wire/modules/Inputfield/InputfieldDatetime/InputfieldDatetime.module), but the field values will be always populated with the default value ('%R'). Are I am missing something? Does anyone has experience with multilanguage fields and could help me out? Thanks in advance.
-
OK, I figured it out. I transformed the values of the inputfields inside the sleepValue function to a json array and now the values will be stored in the database. public function sleepValue(Page $page, Field $field, $value) { // throw error if value is not of the right type if (!$value instanceof OpeningHours) { throw new \Exception($this->_('Expecting an instance of OpeningHours')); } $content = json_encode($value->data['hours']); $sleepValue = ['hours' => $content]; return $sleepValue; } So the responsible lines are $content = json_encode($value->data['hours']); $sleepValue = ['hours' => $content]; The problem of the storage was that the column 'hours' was not defined in the sleepValue method. This was the important part because without it the system doesnt know where to store the value.
- 2 replies
-
- 2
-
- fieldtype
- inputfield
-
(and 1 more)
Tagged with:
-
OK, I am 1 step forward :-) Adding $this->value['hours'] = $time_values; to the processInput() method adds the values to the object Here is the complete code of this method: public function ___processInput(WireInputData $input): self { $name = $this->attr('name'); $value = $this->attr('value'); //input object includes always every input on the page, so lets filter out only inputs from this field //we need to do this, because the number of values is variable - so extract only values that starts with $name.'_' $nameAttributes = []; foreach($input as $key=>$value){ if(substr($key, 0, strlen($name.'_')) === $name.'_'){ $nameAttributes[$key] = $value; } } // loop through all inputfields and set the value (if changed or not) back $time_values = []; foreach($nameAttributes as $nameAttr => $value) { $time_values[$nameAttr] = $value; } //add it to the object $this->value['hours'] = $time_values; return $this; } Now the values are also reachable in sleepValue() method and the sanitizeValue() method. ProcessWire\OpeningHours Object ( [data] => Array ( [hours] => Array ( [openinghours_mo-0-start] => 09:00 [openinghours_mo-0-finish] => 13:00 [openinghours_mo-1-start] => 14:00 [openinghours_mo-1-finish] => 18:00 [openinghours_mo-2-start] => 21:00 [openinghours_mo-2-finish] => 23:00 [openinghours_tu-0-start] => 09:00 [openinghours_tu-0-finish] => 13:00 [openinghours_tu-1-start] => 14:00 [openinghours_tu-1-finish] => 18:00 [openinghours_we-0-start] => 09:00 [openinghours_we-0-finish] => 13:00 [openinghours_we-1-start] => 14:00 [openinghours_we-1-finish] => 18:00 [openinghours_th-0-start] => 09:00 [openinghours_th-0-finish] => 13:00 [openinghours_th-1-start] => 14:00 [openinghours_th-1-finish] => 18:00 [openinghours_fr-0-start] => 09:00 [openinghours_fr-0-finish] => 13:00 [openinghours_fr-1-start] => 14:00 [openinghours_fr-1-finish] => 18:00 [openinghours_sa-0-start] => [openinghours_sa-0-finish] => [openinghours_so-0-start] => [openinghours_so-0-finish] => [openinghours_ho-0-start] => [openinghours_ho-0-finish] => ) ) ) Nevertheless the value in the DB is NULL :-(
- 2 replies
-
- fieldtype
- inputfield
-
(and 1 more)
Tagged with:
-
Hello @ all, I am creating a new inputfield/fieldtype to store opening hours, but I am struggeling to save values from multiple dynamic created inputfields in 1 column of the database. Scenario: The user can enter one or more opening times per day in a UI. Fe: Monday open from 08:00 to 12:00 and from 14:00 to 17:00 Tuesday open from 08:00 to 12:00 and from 14:00 to 19:00 and so on Via a little JavaScript you can add as much opening times as you need per day - the additional inputfield will be created dynamically. After form submission all the values are in the POST array -> this works (see example below): ProcessWire\WireInputData Object ( [openinghours_mo-0-start] => 09:00 [openinghours_mo-0-finish] => 13:00 [openinghours_mo-1-start] => 14:00 [openinghours_mo-1-finish] => 18:00 [openinghours_mo-2-start] => 21:00 [openinghours_mo-2-finish] => 23:00 [openinghours_tu-0-start] => 09:00 [openinghours_tu-0-finish] => 13:00 [openinghours_tu-1-start] => 14:00 [openinghours_tu-1-finish] => 18:00 [openinghours_we-0-start] => 09:00 [openinghours_we-0-finish] => 13:00 [openinghours_we-1-start] => 14:00 [openinghours_we-1-finish] => 18:00 [openinghours_th-0-start] => 09:00 [openinghours_th-0-finish] => 13:00 [openinghours_th-1-start] => 14:00 [openinghours_th-1-finish] => 18:00 [openinghours_fr-0-start] => 09:00 [openinghours_fr-0-finish] => 13:00 [openinghours_fr-1-start] => 14:00 [openinghours_fr-1-finish] => 18:00 [openinghours_sa-0-start] => [openinghours_sa-0-finish] => [openinghours_so-0-start] => [openinghours_so-0-finish] => ) The property name is always the name attribute of the field ? . If the property is empty means closed on that day. Now I need to combine all those values into 1 array (or json array) and store it in the database in 1 column called 'hours' in my case (see screenshot below): In my ___processInput(WireInputData $input) method I have tried to make it work like this: public function ___processInput(WireInputData $input): self { $name = $this->attr('name'); $value = $this->attr('value'); //input object includes always every input on the page, so lets filter out only inputs from this field //we need to do this, because the number of values is variable - so extract only values that starts with $name.'_' $nameAttributes = []; foreach($input as $key=>$value){ if(substr($key, 0, strlen($name.'_')) === $name.'_'){ $nameAttributes[$key] = $value; } } // loop through all inputfields of this fieldtype $time_values = []; foreach($nameAttributes as $nameAttr => $value) { $time_values[$nameAttr] = $value; } } //save it in the database $input->set('hours', serialize($time_values)); return $this; } The only important part of this code is the last part with the serialize function. After saving it will create a record in the database, but the value is always NULL (default value) (see below). Checking $time_values returns all the values, but printing out "$this" shows me that the property "hours" inside the Openinghours object is empty (see below) - so the mistake must be there, but I dont know where?!?!?!? [title] => Home [openinghours] => ProcessWire\OpeningHours Object ( [data] => Array ( [hours] => ) ) If I check the sleepValue() method or the sanitizeValue() - they are also empty. So it seems that the values will not reach these methods. I havent found a clear documentation of whats going on behind the saving process of an inputfield. As far as I know the saving process starts with the form submission. The values are in the POST array and will be processed by the processInput() method. Before they will be saved in the database they will be sanitized by the sanitizeValue() mehtod and afterwards they will be prepared for storage in the sleepValue() method. The last step is the storage itself. Has someone an idea what is missing by storing values from multiple fields into 1 database column or has someone a working example of such a scenario on github to help me out. A clear explanation of the storage process will be also helpful. Thanks and best regards
- 2 replies
-
- fieldtype
- inputfield
-
(and 1 more)
Tagged with:
-
Hello @ all Today I want to share an inputfield/fieldtype to store 2 or 3 dimensions of an object. This fieldtype was inspired by the amazing fieldtype "Fieldtype Dimensions" from SOMA. This fieldtype was introduced in 2013 - so its time for a relaunch. This new fieldtype offers more possibilities than the old one. This inputfield/fieldtype let you enter max. 3 dimensions (width/height/depth) of an object (fe a product), but you can select if you want to display inputs for 2 or 3 dimensions. 2 dimension can be used fe for wallpapers or photos, 3 dimensions fe for furnitures or other objects. There are several configuration options for this fieldtype in the backend. set type (2 or 3 dimensional) set size unit as suffix after each inputfield (default is cm) set max number of decimals (default is 2) show/hide a hint to the user how much decimals are allowed If the number of decimals will be changed, the database schema for each dimension column will also change after saving the field in the backend. For example: If the schema for each dimension field in the DB is f.e. decimal(65,2) and you will set the number of decimals in the configuration to 3, then the schema in the DB will also change to decimal(65,3) after saving the inputfield. You can download this inputfield at https://github.com/juergenweb/FieldtypeObjectDimensions There you will find more detailed information and explanation too. If you find any bugs or you have an idea to improve it (also code improvements) please report it on Github. Have a nice day! UPDATE 14.06.23 / VERSION 1.2.2 I have re-written the complete fieldtype to be compatible with PHP 8.2 and refactored some of the methods and the database scheme. If you have downloaded the fieldtype before, please deinstall the old one and make a new install. I have added this fieldtype to the Processwire module directory. After it has been published, please download it from there.
- 2 replies
-
- 15
-
Hello @ all! I want to share a simple fieldtype and inputfield to store address data with you. I have created this inputfield for learning purposes and it has no fancy functionality. It is simply for storing address data such as street, number, postalcode and so on in one table. As an addition you can store latitude and longitude too, so you can use them in maps. Here is a screenshot of what it looks like: You can select which fields are mandatory and you can choose if the inputs for longitude and latitude should be displayed. These settings can be configured in the field configuration. If you find this inputfield useful you can download it at https://github.com/juergenweb/FieldtypeSimpleAddress There you will find a detailed explanation. If you have an idea of an usefull feature that can be added or you have detected a bug, please report it in my github account.
-
- 11
-
- inputfield
- address field
-
(and 1 more)
Tagged with:
-
I found a solution to keep multiple checkboxes checked after form submission. Here is the complete code of this multiple checkbox list field: // checkbox field to make specific fields required $f = $this->modules->get('InputfieldCheckboxes'); $f->label = _x('Required fields', 'simpleaddress'); $f->attr('name', 'input_required'); $options = [ 'street' => _x('Street', 'simpleaddress'), 'number' => _x('Number', 'simpleaddress'), 'postalcode' => _x('Postalcode', 'simpleaddress'), 'city' => _x('City', 'simpleaddress'), 'state' => _x('State', 'simpleaddress'), 'country' => _x('Country', 'simpleaddress'), ]; $values = (isset($this->hasField->data['input_required'])) ? $this->hasField->data['input_required'] : []; foreach($options as $value => $label) { $checked = (in_array($value,$values)) ? 'checked' : ''; $f->addOption($value, $label, ['checked' => $checked]); } $f->description = _x('If checked the input is required.', 'simpleaddress'); $inputfields->append($f); Lets take a closer look: $values = (isset($this->hasField->data['input_required'])) ? $this->hasField->data['input_required'] : []; This is how we get all checked checkboxes after form submission. "input_required" is the name attribute of my multiple checkbox field. If one or more checkboxes were checked, we get the values in an array, otherwise we will get an empty array. So $values could be an empty array or an array with values like ['street, postalcode']. To make the checkboxes checked after form submission we include a simple check inside the foreach loop. $checked = (in_array($value,$values)) ? 'checked' : ''; We check if the value from the foreach loop is in the array too. If yes, then add the checked attribute as third parameter to this checkbox input. $f->addOption($value, $label, ['checked' => $checked]); Thats all! Maybe someone has a nicer solution, please post it here. Best regards
-
Thanks for your help @Robin S Your code: Unfortunately $this->foo is always empty after form submission (independent if a checkbox was marked or not). Array ( [0] => ) By taking a look at the $this object you can see that you cannot reach the foo property via $this->foo, because foo is part of the field object. ProcessWire\InputfieldSimpleAddress Object ( [data] => Array ( [input_street] => [input_number] => [input_postalcode] => [input_city] => [input_state] => [input_country] => test [label] => Address [description] => [icon] => [notes] => [detail] => [head] => [required] => 0 [requiredIf] => [collapsed] => 0 [showIf] => [columnWidth] => [skipLabel] => [wrapClass] => [headerClass] => [contentClass] => [textFormat] => 4 [renderValueFlags] => 0 [prependMarkup] => [appendMarkup] => [hasFieldtype] => ProcessWire\FieldtypeSimpleAddress Object ( ) [hasField] => ProcessWire\Field Object ( [id] => 116 [name] => address [label] => Address [flags] => [type] => ProcessWire\FieldtypeSimpleAddress Object ( ) [data] => Array ( [label1021] => Adresse [collapsed] => 0 [foo] => Array ( [0] => red [1] => green [2] => blue ) ) ) [hasPage] => ProcessWire\Page Object ( [id] => 1 [name] => home [parent] => [status] => systemID [template] => home [numChildren] => 3 [title] => Home [data] => Array ( [title] => Home ) ) ) ) I have checked all 3 checkboxes and you get the values (red, green and blue) in the object as part of the data property (see code below): [data] => Array ( [label1021] => Adresse [collapsed] => 0 [foo] => Array ( [0] => red [1] => green [2] => blue ) ) Therefore you can get the values (array) only by using $this->hasField->data['foo'] instead of $this->foo My goal: I want to make multiple checkboxes stay checked after form submission if they were marked by an user. I havent found a way to achive this at the moment and this was the reason for this post.
-
Hello @ all, On a custom inputfield I want to use a multiple checkbox field (InputfieldCheckboxes) in the inputfield configuration. The user can select which fields of this inputfield are required. I have created these checkboxes as followed: As you can see, I am not using "InputfieldCheckbox" but "InputfieldCheckboxes" (the multi-checkbox version) $f = $this->modules->get('InputfieldCheckboxes'); $f->label = _x('Required fields', 'simpleaddress'); $options = [ 'street' => _x('Street', 'simpleaddress'), 'number' => _x('Number', 'simpleaddress'), 'postalcode' => _x('Postalcode', 'simpleaddress'), 'city' => _x('City', 'simpleaddress'), 'state' => _x('State', 'simpleaddress'), 'country' => _x('Country', 'simpleaddress'), ]; foreach($options as $value => $label) { $f->attr('name', 'input_required[]'); $checked = ($this->input_required == '1') ? 'checked' : '';//How to check if a checkbox was checked??????? $f->addOption($value, $label, ['checked' => $checked]); } $inputfields->append($f); Problem: I am struggeling to find out how to determine if a checkbox was checked or not . On a single checkbox field I can use something like this to set the checked attribute. $f->attr('checked', $this->myInputfield == '1' ? 'checked' : ''); How can I achive the same for a multiple checkbox field? I haven´t found a working example. Thanks in advance!
-
Put this code inside your ready.php which you will find inside your site/templates/ folder. If there is no such a file then create one (site/templates/ready.php)
-
@tires This is the hook to remove the unwanted generator meta tag. Best regards
-
Only to mention: Using $page->children() will also show all pages independent of release status.
-
Hello @LostKobrakai I have written a class for creating tables. The first class is the table class itself. The second class is a class for creating table-cells (the content of the table of course). Both class extends from the same parent class (a wrapper class). So both use the same methods from the wrapper class (in this case methods for setting attributes like class, id,...) My aim was to chain methods from the table-cell class inside the table class. My OOP-code of creating a table looks like this in this case: $table = ( new \UikitClass\Table(3))->setCaption('Beschreibung'); //thead $table->addHeaderCell()->setModifier('expand')->setText('Spalte 1')->setClass('thclass'); $table->addHeaderCell()->setText('Spalte 2')->setClass('custom'); $table->addHeaderCell()->setText('Spalte 3')->setClass('custom'); //tbody $table->addCell()->setText('Text 1'); $table->addCell()->setText('Text 1')->setClass('custom'); $table->addCell()->setText('Text 1')->setClass('custom'); $table->addCell()->setText('Text 1')->setClass('custom'); $table->addCell()->setText('')->setClass('custom'); //tfoot $table->addFooterCell()->setText('Footer')->setClass('uk-text-center')->setAttribute(['colspan' => '3']); echo $table->render(); So every chaining starts from $table for easier writing. Therefore I wanted to include the class of the table-cells inside the table class. In this case addHeaderCell(), addCell() and addFooterCell holding an object of the table-cell class and all methods after this affect the table-cell object. So I start with $table (table-class object), switch to the table-cell object (fe addHeaderCell() method), use methods to the table-cell object and add this to the table class. Maybe a little bit difficult to explain, but the idea behind was to make writing easier for the developer.
-
Thanks @Edison so I was very close to the solution ?. This works !! Have a nice weekend too!
-
Hello @all, I know how to use chaining with pipe within one class, but I want to know if it is possible to use chaining of methods of 2 different classes. Here is a simple code example: class A() { protected $text = ''; public function setText(string $value = null) { $this->text = trim($value); return $this; } } class B() { public function addText() { //instantiate class A return new class A(); return $this; ?????????? } } Use it: $test = new B(); $test->addText->setText(); addText is method of class A setText is method of class B As you can see there are 2 different classes (A and B). A has the method setText() and B has the method addText(). My goal is to create a new instance of class B, then I want to use the method addText(), which is a method of class B and this method should instantiate an object of class A. After that I want to go on with methods of class A in the chaining. So it will be a mix of class A and B. Is this possible? I have tried to find useful information via Google but without success. Can someone give me a hint, how this can be implemented. Thanks for your help!
-
Here is another snippet that I use to get rid of unwanted table properties: // Remove unwanted attributes from tables CKEDITOR.on('dialogDefinition', function(ev) { var dialogName = ev.data.name; var dialogDefinition = ev.data.definition; if (dialogName == 'table') { var info = dialogDefinition.getContents('info'); info.remove('txtWidth'); info.remove('txtHeight'); info.remove('txtBorder'); info.remove('txtCellPad'); info.remove('txtSummary'); info.remove('txtCellSpace'); info.remove('cmbAlign'); var advanced = dialogDefinition.getContents('advanced'); advanced.remove('advStyles'); advanced.remove('advId'); //Id attribute advanced.remove('advLangDir'); // writing direction advanced.get('advCSSClasses')['default'] = 'uk-table'; //set default class for table } Put this code inside your custom config.js Best regards
- 15 replies
-
- 10
-
CKEditor Inputfield and multiple content stylesheets
Juergen replied to Juergen's topic in General Support
I have opened a new issue on Github: https://github.com/processwire/processwire-issues/issues/929 -
Thanks for this!! I think this is one thing that is much more important today because you have to add image credits to the site. So this should be part of the core.
-
Now this works: $module = wire('modules')->getModule('ProcessPageAdd'); $test = ($module->executeNavJSON()); return $test; This gives me the following json-output: {"url":"\/processwire\/processwire\/page\/add\/","label":"Modules","icon":"plus-circle","add":null,"list":[{"url":"?parent_id=1016&template_id=57","label":"Eine neue Leistung erstellen","icon":"plus-circle","parent_id":1016,"template_id":57},{"url":"?parent_id=1048&template_id=51","label":"Neuigkeit erstellen","icon":"plus-circle","parent_id":1048,"template_id":51},{"url":"bookmarks\/?role=0","label":"Lesezeichen","icon":"bookmark-o","className":"separator separator"}]} And these are the pages I was looking for: list":[ {"url":"?parent_id=1016&template_id=57","label":"Eine neue Leistung erstellen","icon":"plus-circle","parent_id":1016,"template_id":57},/n{"url":"?parent_id=1048&template_id=51","label":"Neuigkeit erstellen","icon":"plus-circle","parent_id":1048,"template_id":51}........ So I get: parent_id: 1016, template_id:57 and parent_id:1048, template_id:51 These are the 2 pages that I have in the shortcut menu and the IDs that I need? So for the moment my new dashboard has the same functionality as the add-new navigation of PW, but a little more userfriendly than the small navigation in the left corner. New shortcut items can be added via the bookmark button, so every user can add his own favorites shortcuts to make his dashboard individually (if he has the rights of course ;-).
-
Really complicated, but I will give this a try. I thought that it must be really simple to get the IDs of the pages in the shortcut navigation, but it isnt. Thanks for the tipp!!!
-
CKEditor Inputfield and multiple content stylesheets
Juergen replied to Juergen's topic in General Support
Yes the folder is under site and if I look into the source code I can see that the correct config.js is there. "stylesSet": "customstyles:/processwire/site/templates/scripts/customstyles.js?nc=1563189734", "customConfig": "/processwire/site/modules/InputfieldCKEditor/config-body.js?nc=1563175984" Inside the config-body.js I have the following code: CKEDITOR.editorConfig = function( config ) { config.contentsCss = 'https://cdnjs.cloudflare.com/ajax/libs/uikit/3.1.6/css/uikit.min.css'; }; But the CSS from the CDN will not be loaded. Instead the one from the wire-folder will be loaded. "baseHref": "/processwire/", "contentsCss": "/processwire/wire/modules/Inputfield/InputfieldCKEditor/contents.css", "extraPlugins": "pwimage,pwlink,sourcedialog", ? -
Same result, but I have checked the database (table 'modules') and the data column is empty. So I am wondering where the shortcuts pages are stored? Inside the module file of this module you can find following lines: $configData = $this->wire('modules')->getModuleConfigData($this); // because admin theme calls with noInit option $shortcutSort = isset($configData['shortcutSort']) ? $configData['shortcutSort'] : array(); So there must be an index 'shortcutSort' inside the configuration array. Very strange?!?