Jump to content

Robin S

  • Content Count

  • Joined

  • Last visited

  • Days Won


Posts posted by Robin S

  1. @Marco Ro, you can use InputfieldWrapper::setMarkup() to customise the markup of inputfields.

    See the defaultMarkup property as a starting point for what can be customised:

     * Markup used during the render() method - customize with InputfieldWrapper::setMarkup($array)
    static protected $defaultMarkup = array(
    	'list' => "<ul {attrs}>{out}</ul>",
    	'item' => "<li {attrs}>{out}</li>", 
    	'item_label' => "<label class='InputfieldHeader ui-widget-header{class}' for='{for}'>{out}</label>",
    	'item_label_hidden' => "<label class='InputfieldHeader InputfieldHeaderHidden ui-widget-header{class}'><span>{out}</span></label>",
    	'item_content' => "<div class='InputfieldContent ui-widget-content{class}'>{out}</div>", 
    	'item_error' => "<p class='InputfieldError ui-state-error'><i class='fa fa-fw fa-flash'></i><span>{out}</span></p>",
    	'item_description' => "<p class='description'>{out}</p>", 
    	'item_head' => "<h2>{out}</h2>", 
    	'item_notes' => "<p class='notes'>{out}</p>",
    	'item_detail' => "<p class='detail'>{out}</p>", 
    	'item_icon' => "<i class='fa fa-fw fa-{name}'></i> ",
    	'item_toggle' => "<i class='toggle-icon fa fa-fw fa-angle-down' data-to='fa-angle-down fa-angle-right'></i>", 
    	// ALSO: 
    	// InputfieldAnything => array( any of the properties above to override on a per-Inputifeld basis)

    There are still some limitations (e.g. item_label is always rendered before item_content) but you can get closer to what you want like so:

    	'list' => "<div {attrs}>{out}</div>",
    	'item' => "<div {attrs}><div class='InputfieldContent'>{out}</div></div>",
    	'item_label' => "<label class='{class}' for='{for}'>{out}</label>",
    	'item_content' => "{out}",
    	'item_toggle' => "",
    $form = $modules->InputfieldForm;
    $f = $modules->InputfieldText;
    $f->name = 'greeting';
    $f->label = 'Greeting';
    $f = $modules->InputfieldSubmit;
    echo $form->render();



    11 hours ago, Marco Ro said:

    I need move the text label inside the input, when write inside the input the label text will be smaller and move across the InputfieldContent box. 

    If I understand right you don't really need to change the markup to do this. The label element can never literally be inside the input element, but you can position it overlaying the input using CSS with the default markup.

    .Inputfield { position:relative; }
    .InputfieldHeader { position:absolute; left:5px; top:3px; font-size:12px; text-transform:uppercase; }
    input[type=text] { padding:17px 5px 5px; border:1px solid #ccc; }
    .InputfieldSubmit { margin-top:20px; }


    If you want to change the label styling when the input is focused you can use some JS to add a class to the parent .Inputfield element when its child input is focused.

  2. 47 minutes ago, MarkE said:

    I don't seem to be able to set that directly

    Oh right, I was thinking about InputfieldSelector in the context of Lister.

    You just set the value of the inputfield:

    $f = $this->wire('modules')->InputfieldSelector;
    $f->name = 'test_selector';
    $f->label = 'Test InputfieldSelector';
    $f->value = "template=news_item, colours.title=Blue";


    • Like 1

  3. 2 hours ago, SebastianP said:

    I want to register an event handler on a page reference field inside the repeater item.

    Is the event handler specific to the page reference field inside only new repeater items, or should it handle events of the page reference field inside all repeater items?

    Because if it's the latter you probably don't need to do anything in particular when a new repeater item is added - rather you can attach the handler to "document" and then use a selector to filter descendants. So supposing your handler is for the "change" event of selects named "foo" inside repeater items...

    $(document).on('change', 'select[name^=foo_repeater]', function() {
    	console.log('repeater select changed');

    Edit: and if only inside new repeater items then this seems to do the job:

    $(document).on('change', '.InputfieldRepeaterItemRequested select[name^=foo_repeater]', function() {
    	console.log('new repeater item select changed');


    • Like 3

  4. 8 hours ago, jsantari said:

    I want for example someone with a basic-member role to only be allowed 2 images, silver-member 5 etc.

    To do this you would set the "maxFiles" property of the inputfield dynamically. In the field settings set "0" for "Maximum files allowed" (i.e. no limit).

    In /site/ready.php:

    $wire->addHookBefore('InputfieldImage::processInput', function(HookEvent $event) {
    	/* @var InputfieldImage $inputfield */
    	$inputfield = $event->object;
    	$process = $event->wire('process');
    	$user = $event->wire('user');
    	// Only for a specific field
    	if($inputfield->hasField != 'images') return;
    	// Only in ProcessPageEdit
    	if($process != 'ProcessPageEdit') return;
    	$page = $process->getPage();
    	// Only for a specific template
    	if($page->template != 'basic-page') return;
    	$limit = null;
    	if($user->hasRole('basic-member')) $limit = 2;
    	if($user->hasRole('silver-member')) $limit = 5;
    	// Only if a limit applies to this role
    	if(!$limit) return;
    	$inputfield->maxFiles = $limit;


    • Like 2

  5. 1 hour ago, teppo said:

    In some cases the distinction may not matter, but in others it will — i.e. when something really shouldn't be viewable before a predefined date/time

    For 90% of these kinds of cases I do it like @Jens Martsch - dotnetic suggested, and in the template of the page (event, news item, etc) I have:

    // Throw 404 if item is future-dated
    if(!$user->isLoggedin() && $page->getUnformatted('date_1') > time()) throw new Wire404Exception();

    But on a complex site where the pages might be queried from many selectors in many different places then unpublishing is the way to go.

    • Like 6

  6. @FlorianA, you can probably achieve what you want by using hooks. ProcessForgotPassword::renderEmailBody is hookable and includes the link URL as an argument, to which you could add the verification code (which is also an argument) as a GET parameter.

    Then you could hook before InputfieldText::render and if the field name is "verify" (you could do additional checks if needed) you could check $input and set the value from the GET parameter.

    • Like 4

  7. 1 hour ago, montero4 said:

    if I try to limit it to only looking at the blog_body field it doesn't work, i.e. $page->links($field='blog_body');

    The $page->links() method takes two optional arguments:

    selector (optional) string, bool

    Optional selector to filter by or boolean true for “include=all”. (default='')

    field (optional) string, Field

    Optionally limit results to specified field. (default=all applicable Textarea fields)

    You are passing only one argument, so this is interpreted as the first argument. Passing "$field='blog_body'" doesn't make this become the second argument - your code is saying "pass the string 'blog_body' as the first argument and also assign the string 'blog_body' to a variable named $field".

    If you want to specify the second argument (field) but not the first (selector) you need to pass a value for the first argument, and in this case you would pass the default value which is an empty string. So you would do this:

    $items = $page->links('', 'blog_body');


    1 hour ago, montero4 said:

    Also, is there a way to sort the $items result in descending order? Something like sort=-blog_date so that the blog_posts are shown in descending order?

    It would be reasonable to think that you could pass a sort as part of the selector argument, but actually this doesn't work because behind the scenes this method is ultimately just getting some page IDs and then loading data from the database using those IDs without any ORDER_BY clause.

    So you'll need to sort the links after you get them:

    $items = $page->links('', 'blog_body');


    • Like 1
    • Thanks 1

  8. On 2/5/2020 at 4:05 AM, BitPoet said:

    The hook probably should check against preg_match('^/crondate(_repeater\\d+)?$/', $field->name) instead of a straight comparison to work both inside and outside a repeater/fsp.

    The way that I find easiest is to get the Field object that is associated with the Inputfield via hasField and check its name, because the Inputfield object's name changes inside a Repeater but the Field object's name doesn't. So something like this:

    $wire->addHookAfter('InputfieldDatetime::processInput', function(HookEvent $event) {
    	$inputfield = $event->object;
    	$field = $inputfield->hasField;
    	if(!$field || $field->name !== 'your_field_name') return;
    	// ...


    • Like 1

  9. On 2/5/2020 at 1:45 AM, saschapi said:

    Is this because dependencies in repeaters do not work stable and pagefieldset works like a repeater?


    Some possible solutions, in order from easiest to hardest or least recommended:

    1. Use FieldsetGroup instead of FieldsetPage if you have ProFields.

    2. Use a normal Fieldset instead of FieldsetPage, and just bite the bullet if you have to spend some time setting up the fields in multiple templates.

    3. Write some custom JS for the admin that adds/removes the HTML required attribute to the adjacent field depending on the state of the checkbox.

    4. Use a hook to a processInput method to do custom validation for the required fields. So if your required field is a text field then you would hook InputfieldText::processInput. Here is an example for a text field named "text_1" that is required if a field named "checkbox_1" is checked inside the same Repeater item (or FieldsetPage item):

    $wire->addHookBefore('InputfieldText::processInput', function(HookEvent $event) {
    	/* @var InputfieldText $inputfield */
    	$inputfield = $event->object;
    	// Get field
    	$field = $inputfield->hasField;
    	// Only for field "text_1"
    	if(!$field || $field->name !== 'text_1') return;
    	// Get page
    	$page = $inputfield->hasPage;
    	// Only for fields in a Repeater page
    	if(!$page instanceof RepeaterPage) return;
    	// Repeater suffix
    	$r_suffix = '_repeater' . $page->id;
    	// Get checkbox field
    	$wrapper = $inputfield->parent;
    	$checkbox = $wrapper->getChildByName('checkbox_1' . $r_suffix);
    	if(!$checkbox) return;
    	// If checkbox is checked then field is required
    	if($checkbox->value) {
    		$inputfield->required = 1;
    	} else {
    		$inputfield->required = 0;

    If you have multiple required fields you'll need to adjust this to suit.

    5. I posted some suggested changes to the core to support required-if inside Repeaters here: https://github.com/processwire/processwire-requests/issues/262
    But making custom changes to the core is not recommended because they will be lost when you upgrade.

    • Thanks 1

  10. Hey @adrian,

    What do you think about the idea of adding a link to the "Restore" page somewhere within the main interface? I know it's accessible via the flyout menu but it's easy (for me at least) to overlook or forget this and there have been a few times where I've started to worry that I don't have the ability to restore a backup when an action fails due to an error or I've navigated away from the success screen which includes the restore link. Then after a moment I remember where the link is and breathe a sigh of relief. 😅

    • Like 1

  11. Inputfield Selector Select ID

    Uses the Page List Select inputfield for user-friendly input of page IDs into Inputfield Selector.


    This module adds a feature to Inputfield Selector, which is most commonly seen in Lister (Find) and Lister Pro.

    When adding a filter row for "Parent", "Has parent/ancestor" or "ID" the user is expected to enter a page ID to match against. But this is not as user-friendly as it could be because the user may be able to identify the desired page by its title or location in the tree but not know its ID. This is particularly the case for site editors who may not even understand the concept of a page ID.

    So this module adds a thunderbolt icon to relevant rows in Inputfield Selector. When the icon is clicked a Page List Select inputfield opens in a modal window, allowing the user to visually select a page from the tree. When the modal is closed with "OK" the ID of the selected page is inserted into the filter row.



    After a page selection has been made in the modal window the "OK" button will automatically receive focus so if you prefer you can close the modal by hitting the Enter key rather than mousing to the OK button.


    Install the Inputfield Selector Select ID module.



    • Like 8
    • Thanks 1

  12. You need to create a database and a user for the database before you complete that part of the PW installation process. Then you will know the database name, user name, and password for the user.

    I'm not a XAMPP user but I'm pretty sure that it comes bundled with phpMyAdmin. So you open phpMyAdmin in your browser (probably at http://localhost/phpmyadmin) and then the quickest way is to create the user and database in one step at User accounts > Add user account


    After you have created the user/database then you will know the user name, database name (which will be the same as the user name) and the password so you can complete the PW installation process.

    • Like 2

  13. Very helpful, thanks!

    I see the module gives a warning if a tag is created that has the same name as an existing template. Tags added to a template via the core way on the Advanced tab don't give this warning. Is there a catch to watch out for if a tag name is also a template name?

    Also, it's only a small thing but maybe this module only needs to autoload in the PW admin ("autoload" => "template=admin") rather than for the front-end also.

    • Like 1

  14. Hi @bernhard,

    I gave this module a try to see if it might be a good way to get data for a CSV download (on the front-end). But I'm having trouble getting off the ground.

    I installed the module, put some demo code in my template file...

    $rf = new RockFinder2();
    $rf->find("id>2, limit=5");

    ...and I get this error:


    The same code seems to execute in the Tracy Console in the back-end without an error. Is this module only for use in the PW admin?

    I had trouble understanding the GitHub readme - are those just notes to yourself? It wasn't clear to me how the SQL examples at the top of the readme relate to the module, and several of the code examples use "new RockFinder()" when this module class is RockFinder2. 


    I just discovered that the uninstall routine in v0.2.0 can inadvertently delete directories that it shouldn't depending on the module configuration. If you have v0.2.0 installed please DO NOT UNINSTALL but instead update the module to the recently released v0.2.1 as soon as possible. The module can be safely uninstalled from v0.2.1 or higher.

    To update the module, visit the module config page, expand the Module Information section and click "check for updates".


    I apologise for this error.


    • Like 4

  16. 1 hour ago, sww said:

    is there a way pages will be added at the top of the list?

    Not really. Could you explain why you want to do this?

    Rather than modifying asmSelect.js and needing to make changes to the Connect Page Fields module I think it might be better to adapt to how PW handles the sorting of newly added pages by default and reverse the sort order of the Page Reference field when you get it via the API.




  17. 2 hours ago, dragan said:

    However, if you want to stick to the DRY principle (don't repeat yourself), and choose a maincat<-->subcat relationship that I have chosen, this won't work.

    There's nothing in the core dependent selects feature that dictates how you organise your pages. A parent/child relationship to define categories and subcategories is just one that suits many scenarios, and if I understand right it's the way that was specified in the original question.

    The thing that determines how it works is this:

    On 1/19/2020 at 12:41 PM, Robin S said:

    "page.category" will be replaced with the ID of the page selected in the Category inputfield in Page Edit, whenever that field changes.

    So you can use "page.page_reference_field_name" in the selector string setting for the dependent field in any way that suits you. You could have all your subcategories under one parent and connect them with one or more categories using a "category" page reference field in the subcategory template. Then the selector string for the subcategory field could include "category=page.category" to limit the subcategories according to what is selected in the category field on the currently edited page.

    2 hours ago, dragan said:

    From a UX/GUI perspective, when you need multiple subcats to attach to a maincat, your options are rather poor.

    AsmSelect works with the core dependent selects, but it certainly would be nice if the range of compatible inputfield types could be expanded.

  • Create New...