Jump to content

Recommended Posts

Select Images

An inputfield that allows the visual selection and sorting of images, intended for use with the FieldtypeDynamicOptions module. Together these modules can be used to create a kind of "image reference" field.

select-images

Integration with FieldtypeDynamicOptions

InputfieldSelectImages was developed to be used together with FieldtypeDynamicOptions (v0.1.3 or newer):

  1. Create a Dynamic Options field.
  2. Choose "Select Images" as the "Inputfield type". Select Images appears in the "Multiple item selection" category but you can set "Maximum number of items" to 1 if you want to use Select Images for single image selections.
  3. Define selectable options for the field via a FieldtypeDynamicOptions::getSelectableOptions hook. See some examples below.

FieldtypeDynamicOptions is recommended but is not a strict requirement for installing InputfieldSelectImages in case you want to use an alternative way to store the field data.

Selection of Pageimages

In this example the field allows selection of Pageimages that are in the "images" field of the home page.

The field will store URLs to the Pageimages so it works as a kind of "image reference" field. You can use the "Format as Pagefile/Pageimage object(s)" option for the Dynamic Options field to have the formatted value of the field be automatically converted from the stored Pageimage URLs to Pageimage objects.

$wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) {
    // The page being edited
    $page = $event->arguments(0);
    // The Dynamic Options field
    $field = $event->arguments(1);

    // For a field named "select_images"
    if($field->name === 'select_images') {
        $options = [];
        // Get Pageimages within the "images" field on the home page
        foreach($event->wire()->pages(1)->images as $image) {
            // Add an option for each Pageimage
            // When the key is a Pageimage URL the inputfield will automatically create a thumbnail
            // In this example the label includes the basename and the filesize
            /** @var Pageimage $image */
            $options[$image->url] = "{$image->basename}<br>{$image->filesizeStr}";
        }
        $event->return = $options;
    }
});

Selection of image files not associated with a Page

When not working with Pageimages you must add a "data-thumb" attribute for each selectable option which contains a URL to a thumbnail/image.

In this example the field allows selection of image files in a "/pics/" folder which is in the site root.

$wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) {
    // The page being edited
    $page = $event->arguments(0);
    // The Dynamic Options field
    $field = $event->arguments(1);

    // For a field named "select_images"
    if($field->name === 'select_images') {
        $options = [];
        // Get files that are in the /pics/ folder
        $root = $event->wire()->config->paths->root;
        $path = $root . 'pics/';
        $files = $event->wire()->files->find($path);
        // Add an option for each file
        foreach($files as $file) {
            $basename = str_replace($path, '', $file);
            $url = str_replace($root, '/', $file);
            // The value must be an array with the following structure...
            $options[$url] = [
                // The label for the image
                'label' => $basename,
                'attributes' => [
                    // An image URL in the "data-thumb" attribute
                    'data-thumb' => $url,
                ],
            ];
        }
        $event->return = $options;
    }
});

The field values don't have to be image URLs

The values stored by the Dynamic Options field don't have to be image URLs. For example, you could use the images to represent different layout options for a page, or to represent widgets that will be inserted on the page.

Also, you can use external URLs for the thumbnails. In the example below the options "calm" and "crazy" are represented by thumbnails from placecage.com.

$wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) {
    // The page being edited
    $page = $event->arguments(0);
    // The Dynamic Options field
    $field = $event->arguments(1);

    // For a field named "calm_or_crazy"
    if($field->name === 'calm_or_crazy') {
        $options = [];
        // Add options that are illustrated with thumbnails from placecage.com
        $options['calm'] = [
            // The label for the option
            'label' => 'Nicolas Cage is a calm man',
            'attributes' => [
                // An image URL in the "data-thumb" attribute
                'data-thumb' => 'https://www.placecage.com/260/260',
            ]
        ];
        $options['crazy'] = [
            // The label for the option
            'label' => 'Nicolas Cage is a crazy man',
            'attributes' => [
                // An image URL in the "data-thumb" attribute
                'data-thumb' => 'https://www.placecage.com/c/260/260',
            ]
        ];
        $event->return = $options;
    }
});

Field configuration

If the inputfield values are Pageimage URLs then you can optionally include a button for each selected image to open the containing page for editing in a modal window. Note that if the selected image is contained in a Repeater item then it is the Repeater page that will be opened for editing.

You can define labels for the button, notices, etc, that are used within the inputfield if the defaults don't suit.

labels

 

https://github.com/Toutouwai/InputfieldSelectImages
https://processwire.com/modules/inputfield-select-images/

  • Like 8
  • Thanks 1
Link to comment
Share on other sites

Hey @Robin S this looks interesting!

Could you please add an example of the output of such a field? What data is the field's value? An array of url strings? Can we use the PageImage sizing functions? What is the use case you developed this for?

Thx ? 

PS: Ok read in the other thread that it seems to return Pagefile/Pageimage object(s). Follow up question: How do you handle deletion of referenced images on the storage page?

  • Like 1
Link to comment
Share on other sites

8 hours ago, bernhard said:

What data is the field's value? An array of url strings? Can we use the PageImage sizing functions?

In most cases I expect people to use the Select Image inputfield in conjunction with the Dynamic Options fieldtype. And regarding the value of a Dynamic Options field:

Quote

If a Dynamic Options field uses a "single" input type then its formatted value is a string, and if it uses a "multiple" input type then its formatted value is an array. The unformatted value of a Dynamic Options field is always an array.

The Select Images input type is a bit of a special case because it can be a "multiple" or a "single" input type:

Quote

Select Images appears in the "Multiple item selection" category but you can set "Maximum number of items" to 1 if you want to use Select Images for single image selections.

And beyond that there is a config option for Dynamic Options that can be used if the values that are saved are paths or URLs to Pagefiles or Pageimages, and this could be a good option when you're using Select Images as the inputfield:

Quote

If the field will store paths/URLs to Pagefiles/Pageimages then you can enable this option to have the formatted value be a Pagefile/Pageimage object for "single" fields or an array of Pagefile/Pageimage objects for "multiple" fields.

When that option is enabled you could use the Pageimage sizing methods on the field value. Note that for multiple images the value is an array of Pageimage objects and not a Pageimages object. That's because these modules are intended to be flexible enough to allow selection of images from more than one PW page but a Pageimages object only supports Pageimage objects associated with a single page (see the class constructor).

The readme for Select Images says:

Quote

The values stored by the Dynamic Options field don't have to be image URLs. For example, you could use the images to represent different layout options for a page, or to represent widgets that will be inserted on the page.

So you can have the value for each option (thumbnail) be any string you like and output that in a template file or use it in a conditional to affect the page markup in some way. But I think my most common use case will be as an "image reference" field to select URLs to Pageimages and have the formatted value be a Pageimage or array of Pageimage objects.

 

8 hours ago, bernhard said:

What is the use case you developed this for?

People have suggested different use cases for an image reference field in the GitHub request linked to at the start of the readme: https://github.com/processwire/processwire-requests/issues/207

Personally I have used image references in these scenarios:

  • To allow an editor to select a social media "share" image from among all the uploads to Images fields on a page.
  • In cases where an editor may only select from a collection of existing images and is not allowed to upload new images.
  • Similar to the previous example, but where the editor needs to choose images that have been prepared in advance to fit in spaces with particular aspect ratios. So for a landscape space they can only select landscape images, square space can only select square images, etc.

 

8 hours ago, bernhard said:

How do you handle deletion of referenced images on the storage page?

The allowed options for a Dynamic Options field are determined at runtime according to the FieldtypeDynamicOptions::getSelectableOptions hook you are using. These allowed options are used to validate any stored value when it is accessed. So if an image that was referenced is deleted then it will no longer be in the value you get from $page->your_dynamic_options_field

  • Like 3
  • Thanks 1
Link to comment
Share on other sites

  • 1 month later...

Hello, @Robin S!

Did you try this inputfield with Combo pro field? It seems like it could be a great fit in this scenario. I am trying to make it work but without a success. FieldtypeDynamicOptions::getSelectableOptions hook seems to not be triggered in that context. But I might be doing some silly mistakes not knowing how it should work, so decided to ask)

Edit: Silly me! This is FieldtypeDynamicOptions method, which surely is not called there! So I rephrase my question - is there a way to populate this inputfield options somehow to make it work with Combo, where there is no fieldtypes but inputfields only?

Link to comment
Share on other sites

On 9/15/2021 at 4:46 AM, Ivan Gretsky said:

So I rephrase my question - is there a way to populate this inputfield options somehow to make it work with Combo, where there is no fieldtypes but inputfields only?

Well it's not intended for use with Combo but rather with FieldtypeDynamicOptions. But I tested and if you enter paths/URLs to already uploaded Pageimage files as the values of the selectable options then it does work.

2021-09-16_133756.thumb.png.de94a1954c33436541e6f63b39da7580.png

2021-09-16_134011.png.1a24fd2e4601e724bfa82119d6a45508.png

But I can't see why that would be a better thing to do than using a FieldtypeDynamicOptions field where you can define the selectable options dynamically.

Link to comment
Share on other sites

I need to have at least 4 Image select inputfields (and the number will probably grow), so I want to put them in Combo not to create too many fields. Actual storage (Image field) for now has to be outside of Combo. Do I was hoping to use Select images inputfield to choose image urls from that Image field storage, and store them inside Combo subfield.

Link to comment
Share on other sites

  • 4 months later...

@Robin S thank you for fixing the issue I reported so quickly on Github - I've not looked at new modules in a while and several of yours are going to be real time-savers for me on a current project.

I have a feature request but I'm not sure how difficult it would be. When you have the field limited to only one item, I'd like to be able to just click on the image rather than have to delete it via trash icon to select a different image. I think in terms of UI, instead of seeing the "drag handle" icon on hover for a single image it could show some other symbol and a tooltip perhaps stating that it would replace it?

My use case is for a website where they are selecting an image to use in search results and categories on the site and it's just a nice easy visual field to do that with, but if they want to play around selecting the best one they need two clicks to delete and select a different photo instead of one, and there are >100 pages where they might want to do this.

Link to comment
Share on other sites

15 hours ago, Pete said:

I think in terms of UI, instead of seeing the "drag handle" icon on hover for a single image it could show some other symbol and a tooltip perhaps stating that it would replace it?

I agree that the drag cursor isn't needed here.

15 hours ago, Pete said:

When you have the field limited to only one item, I'd like to be able to just click on the image rather than have to delete it via trash icon to select a different image.

I don't like the idea that any click on the image would delete it, but what I've done in v0.2.0 is that when the max items is set to one and an image is already selected in the inputfield then the button is labelled "Replace image..." so that the next selected image will replace the existing image with a minimum of clicks.

replace-select-images.gif.025fb0779068d514614dd447fbd1d6dc.gif

  • Like 2
Link to comment
Share on other sites

That's great, thank you ? The only tiny thing missing is being able to change the "replace image" label, but only because I changed the other labels to say "photo".

I feel like I'm being picky now.

Link to comment
Share on other sites

On 1/27/2022 at 8:35 PM, Pete said:

The only tiny thing missing is being able to change the "replace image" label, but only because I changed the other labels to say "photo".

I feel like I'm being picky now.

Fair enough, I've added support for more custom labels in v0.2.1.

  • Thanks 1
Link to comment
Share on other sites

  • 4 months later...

Hi @Robin S,

Thank you very much for your modules!

With this I managed to create a simple conditional visual page selector. For anyone interested:

$wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) {
	/** @var Page $page */
	$page = $event->arguments(0);
	/** @var Field $field */
	$field = $event->arguments(1);
	if($field->name === 'artworks') {
		$artists = $page->artists; // Page-reference field
		$inputfield = modules("InputfieldImage");
		$options = [];
		foreach($artists as $artist) {
			foreach($artist->children() as $artwork) {
				if(!$artwork->gallery->count()) continue;
				$thumb = $inputfield->getAdminThumb($artwork->gallery->first());
				$options[$artwork->id] = [
					"label" => "$artwork->title, $artwork->year",
					"attributes" => [
						"data-thumb" => $thumb["thumb"]->url
					]
				];
			}
		}
		$event->return = $options;
	}
});

+ with the added bonus of being able to customize labels it works great.

A question and a suggestion:

  • Based on some condition (eg: if no artist is selected), it'd be nice to be able to display a specific message other than the "Unavailable" label using the hook (for the Dynamic Options module in general)
  • A teeny-tiny css edit: put the clear button at top: 0 and left: 0, and add a padding: 6px to the label <div>

1766820858_Screenshot2022-06-26at23_43_23.png.8a99f0f8f19f6d3fceeb8d63548a3271.png135748147_Screenshot2022-06-26at23_44_07.png.6151208db57bf9b459d297a65577c33d.png

  • Thanks 1
Link to comment
Share on other sites

10 hours ago, monollonom said:

Based on some condition (eg: if no artist is selected), it'd be nice to be able to display a specific message other than the "Unavailable" label using the hook (for the Dynamic Options module in general)

The labels are settings/properties of InputfieldSelectImages so you would need to customise them there rather than via FieldtypeDynamicOptions. You'd use a hook like this:

$wire->addHookAfter('InputfieldSelectImages::renderReadyHook', function(HookEvent $event) {
	$inputfield = $event->object;
	$field = $inputfield->hasField;
	$page = $inputfield->hasPage;
	// Some test here using $inputfield/$field/$page
	$inputfield->label_unavailable = 'This is my custom label.';
});

 

10 hours ago, monollonom said:

A teeny-tiny css edit: put the clear button at top: 0 and left: 0

The styling is based on the core InputfieldImage, and rightly or wrongly the equivalent icon there is centered within the button, excluding the thumbnail border. So I don't want to change from that generally.

2022-06-27_203209.png.58ffb30a5e148b3574947890bf6ef29b.png

But now that the icon has been changed to a cross instead of the earlier trash icon it does look like it could come up a couple of pixels so I've done that. Of course you can always tweak the styling to your liking with custom CSS as you're doing.

10 hours ago, monollonom said:

add a padding: 6px to the label <div>

Done.

  • Thanks 1
Link to comment
Share on other sites

  • 4 weeks later...

Hi @Robin S,

I had an issue and just found a solution, but I can't explain why it's behaving this way:

Given the setup mentionned in my previous post, when saving after selecting images it worked fine. But then if I saved the page without changes it cleared my selection. Trying to debug I checked if it was an issue with my image, my loops, had a look at your code but in the end what mattered was the artwork's id used as the option's value. Apparently if the option's key resolved to an int it got cleared ("1" was working though and "0" as well but then no image was shown in the selected part, weird).

My solution was to add a prefix (e.g. "id1024") and then it worked as expected.

I have to point out this is only happening with the SelectImages inputfield type, if I use AsmSelect, then it works as expected.

Do you have an idea why it's behaving this way ?

Thanks !

  • Like 1
Link to comment
Share on other sites

  • 2 years later...

Hi,
Thanks for this solution, that I was looking for. Are you aware if it have a limit of number of pictures? Or maybe it's my code which is not optimal but strictly talking the code is good. I just add a loop to get all images on all pages with template meetings. However I got fatal error (30s execution time). The original code searching on only 1 page (having 50 pictures) is fine. But on total, I have for now no more than 55 pictures for all my PageImages field. Is it a way to make it more efficient? Or I should just have many fields for the different meetings?
Thanks
Mel

$wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) {
  // The page being edited
  $page = $event->arguments(0);
  // The Dynamic Options field
  $field = $event->arguments(1);

  // For a field named "select_images"
  if($field->name === 'image_selection') {
      $options = [];
      // Get Pageimages within the "images" field on the home page
      foreach($event->wire()->pages->find("template=meetings") as $p) {
        foreach($p->images as $image) {
          // Add an option for each Pageimage
          // When the key is a Pageimage URL the inputfield will automatically create a thumbnail
          // In this example the label includes the basename and the filesize
          /** @var Pageimage $image */
          $options[$image->url] = "{$image->basename}<br>{$image->filesizeStr}";
      }
    }
      $event->return = $options;
  }
});

 

Link to comment
Share on other sites

@monollonom, the edit button sounds like a generally useful feature so I've built an option for this into v0.3.0. There is also a hookable InputfieldSelectImages::getImageButtons() method if you want to add to or replace the button markup.

@mel47, the module itself doesn't impose any limit on how many selectable images you can have, but I think it would be poor usability to have more than about 100 images to wade through.

I don't see anything wrong in your hook code, so if it's timing out perhaps you are returning a very large number of images. The inputfield uses the standard admin image thumbnail, so if you have added images to a page using the API rather than in Page Edit then it can take a while to generate the admin thumbnails the first time they are rendered.

Probably you should work out a way to present fewer images for selection. For example, you could have a Page Reference field on the page to select a particular meeting page, and then the Select Images field would only show the images on that meeting page.

$wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) {
	// The page being edited
	$page = $event->arguments(0);
	// The Dynamic Options field
	$field = $event->arguments(1);
	
	if($field->name === 'image_selection') {
		if(!$page->selected_meeting->id) return;
		$options = [];
		foreach($page->selected_meeting->images as $image) {
			$options[$image->url] = "{$image->basename}<br>{$image->filesizeStr}";
		}
		$event->return = $options;
	}
});

 

  • Thanks 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...