SilverStripe: setting up flexible images

If you use the WYSYWYG Tiny MCE field, it's really easy to add images to your main text.

 

Mixing up your images with your text has it's limits. Say you want to create a list of images for a gallery. It quickly becomes very difficult to manage that in such a MS Word-like interface.

It's also impossible to create relationships between your data and your images. What if you have a profile page for an employee:

 

Example of an employee page.

Maybe you want to use the same image on the overview page that lists all the employees:

 

Example of an employee overview page.

 

If you try to do this with the WYSYWYG, this will soon become a great mess. And it will also require a lot of work, keeping everything up to date in different locations.

Install SilverStripe and download the theme

Make sure you have a recent version of SilverStripe. You can download the latest version from the SilverStripe Site.

I created a simple theme based on the default Black Candy theme that has templates for an Employee Page and an Employee Overview Page. You can download it here.

Drag the theme to your templates folder and replace the current theme name in mysite/_config.php with the name of the new theme, 'flexibleImages'

SSViewer::set_theme('flexibleImages');

Setting up the image for different sizes

On most installs, I extend the image class with functions that can generate the uploaded images at every size I will be needing it in my templates. Add a new file called CustomImage.php in your mysite/code folder:

<?php
class CustomImage extends Image {
	
	function generate470_Width($gd)
	{
		return $gd->resizeByWidth(470);
	}
	
	function generate200_Width($gd)
	{
		return $gd->resizeByWidth(200);
	}
}

Each generate function you add will be able to create resized version of your image in your assets/Uploads/_resampled/folder

 

The _resampled folder

 

The resampled files will only be generated once they are requested in the templates. You'll be able to get to the resampled versions through template code like this:

<img src="$ProfileImage.470_Width.URL" />

As you can see, the part after generate in the PHP function becomes the name though which you can retrieve the resampled image. I like to use the dimensions in the name, but you can just as easily call them mainimage, smallthumbnail or whatever you like.

Setting up the Employee Page

First, let's create an Employee Overview Page class. Add a file called EmployeeOverviewPage.php to your mysite/code folder and put this code in it:

<?php
class EmployeeOverviewPage extends Page
{

}

class EmployeeOverviewPage_Controller extends Page_controller {
	
}

Add another file called EmployeePage.php to your mysite/code folder. We will be adding a CustomImage to this page type:

<?php
class EmployeePage extends Page
{
	
	static $has_one = array (
		'ProfileImage' => 'CustomImage'
	);
	
	public function getCMSFields()
	{
		$f = parent::getCMSFields();
		$f->addFieldToTab('Root.Content.Main', new FileIFrameField('ProfileImage', 'Profile Image'));
		return $f;
	}
}

class EmployeePage_Controller extends Page_controller {
	
}

 

The first (has_one) part sets up the relationship in the database. The function getCMSFields first gets the CMS fields as they appear on a standard page (Page.php). It then adds a field that lets us upload our CustomImage.

Once you have added this to the code folder, don't forget to run www.yoursite.com/dev/build, this way SilverStripe will update the database. It will also become aware of the new classes we created.

Once you have everything set up, first add an Employee Overview Page, then add a few Employee Pages below that:

 

 

You should now be able to add an image to your employee page:

 

 

Now, let's open up the template for EmployeePage.ss Look for a div with the id employeeImage. Add the following code:

<div id="employeeImage">
<!-- add the following code -->
<img src="$ProfileImage.470_Width.URL" />
</div>

You should now be able to see the image in the template. If not, you might have to add ?flush=1 to your URL to refresh the template.

Using the same image in the overview page

It's now really easy to use (a smaller version of) the image on the overview page. Open the EmployeeOverviewPage.ss file and look for the <div> with id employees. Add the following code:

<div id="employees">
	<!-- Add the following code: -->
	<% if Children %>
	<ul>
		<% control Children %>
		<li>
		<a href="$Link">
		<img src="$ProfileImage.200_Width.URL" />
		</a>
		<p><a href="$link">$Title</a></p>
		</li>
		<% end_control %>
	</ul>
	<% else %>
	<p>No employees yet.</p>
	<% end_if %>
	<!-- Add the code above this line -->
</div>

Uploadify

Now let's see how we can make things a bit easier for the people editing these pages. Uploadify is a great extra fieldtype from Uncle Cheese, a major contributor to SilverStripe. You can download it from his site or clone it from GitHub.

Uploadify also needs the DataObjectManager module. We will use that module later on in the tutorial. Get DataObjectManager from from Uncle Cheese's Site or from Github (if you choose Github, make sure you rename the folder to dataobject_manager).

Drag both modules to your site folder and run dev/build.

With Uploadify, you get an interface that is a lot nicer. It's easier to browse files that are already uploaded, you get a progress bar and it's even possible to upload more than one image at once.

 

Example of the Uploadify field.

 

To use this field type, open up the EmployeePage.php in mysite/code and change the field type in the addFieldToTab line:

<?php
class EmployeePage extends Page
{
	
	static $has_one = array (
		'ProfileImage' => 'CustomImage'
	);
	
	public function getCMSFields()
	{
		$f = parent::getCMSFields();
		// CHANGE THE LINE BELOW THIS ONE
		$f->addFieldToTab('Root.Content.Main', new FileUploadField('ProfileImage', 'Upload a profile image'));
		return $f;
	}
}

class EmployeePage_Controller extends Page_controller {
	
}

 

That's all there is to it, really. I always use Uploadify, as the user experience is a lot smoother. You can also set up a lot more options.

DataObjectManager

With SilverStripe 3 coming somewhere at the end of 2011, the DataObjectManager module will probably be replaced by the DataGrid. In the mean time, the DOM is one of the most important modules for SilverStripe.

In SilverStripe, every entry in the database is a DataObject. You create a class for a new type of data by extending the DataObject class (sapphire/core/model/DataObject.php). This class handles almost all database interactions in SilverStripe.

How will we use it? Let's say you want to be able to add pictures of your employees' holidays. There's no predefined number of pictures and you want to be able to add a small caption for every picture.

 

Holiday Pictures on an Employee page.

 

Let's set up a DataObject that will hold our picture and its caption. Create a file called HolidayPicture in you mysite/code folder. Add the following code to it:

<?php
class HolidayPicture extends DataObject
{
	static $db = array (
		'Caption' => 'Text'
	);
	
	static $has_one = array (
		'Attachment' => 'CustomImage',
		'EmployeePage' => 'EmployeePage'
	);
	
	public function getCMSFields_forPopup()
	{
		return new FieldSet(
			new FileUploadField('Attachment', 'Add a holiday picture'),
			new TextAreaField('Caption', 'Add a caption')
		);
	}
}

The DataObject gets an extra database field (Caption) which will hold the text for our caption. It get also linked to a CustomImage and an EmployeePage.

The getCMSFields_forPopup function defines the fields that will appear in the DataObjectManager.

The next step is to link the HolidayPicture in our EmployeePage and add a DataObjectManager field to that class.

Open up EmployeePage.php and add the following code:

<?php
class EmployeePage extends Page
{
	
	static $has_one = array (
		'ProfileImage' => 'CustomImage'
	);
	
	/* Add the code below this line */
	// Add this to set up the DB link to the HolidayPicture DO
	static $has_many = array (
		'HolidayPictures' => 'HolidayPicture'
	);
	/* Add the code above this line */
	
	public function getCMSFields()
	{
		$f = parent::getCMSFields();
		$f->addFieldToTab('Root.Content.Main', new FileUploadField('ProfileImage', 'Upload a profile image'));
		
		/* Add the code below this line */
		// This adds a DataObjectManager to a new tab
		$manager = new ImageDataObjectManager(
			$this,
			'HolidayPictures',
			'HolidayPicture',
			'Attachment',
			array(
				'Caption' => 'Caption'
			),
			'getCMSFields_forPopup'
		);
		$f->addFieldToTab("Root.Content.HolidayPictures", $manager);
		/* Add the code above this line */
		
		return $f;
	}
}

 

As you can see, we used an ImageDataObjectManager. That is a subclass of the regular DataObjectManager. Now is the time to run dev/build again to finish setting everything up.

You should now have an extra tab on your employee pages where you can add the holiday pictures:

 

Example of the DataObjectmanager

 

Add a few pictures so we can show them in the template. Open EmployeePage.ss and look for the div with the holidayPictures ID. Add the following code:

<div id="holidayPictures">
	<h3>Holiday Pictures</h3>
	<!-- Add the following code -->
	<% if HolidayPictures %>
		<ul>
		<% control HolidayPictures %>
		<li>
		<img src="$Attachment.200_Width.URL" />
		<p>$Caption</p>
		</li>
		<% end_control %>
		</ul>
	<% else %>
	<p>This employee has no holiday pictures.</p>
	<% end_if %>
	<!-- Add the code above this line -->
</div>

Because Attachment in HolidayPicture uses CustomImage, you get all the functions from CustomImage, including 200_Width.

Making HolidayPictures sortable

It's easy to make our holiday pictures sortable. Open _config.php in mysite/code and add this line:

SortableDataObject::add_sortable_class('HolidayPicture');

You will need to run dev/build to add a field to the database. That will make a checkbox available at the bottom of your DataObjectManager:

 

Sortable checkbox

 

When it's checked, you can change the order of the holiday pictures:

 

Resorting the holiday pictures

 

SilverStripe is really flexible and this is just one way you can set this up. I use it a lot, because it makes it easy to change functions on my CustomImage class that trickle down to all the other classes.

See an error in the tutorial? Have a better way of handling things? Be sure to let me know! 

Post your comment

Comments

  • PS: From here:
    http://api.silverstripe.org/2.4/sapphire/filesystem/Image.html

    You can use any of the generate* methods from a template by simply leaving off the 'generate' part.

    For example (in your .ss template):

    $Image.CroppedImage(320,200)

    $Image.PaddedImage(320,200)

    $Image.ResizedImage(320,200)

    etc.

    Posted by Lucas, 31/10/2012 4:31pm (1 year ago)

  • This is a very nice guide, thanks!

    The only comment I have is that your methods 'generate470_Width' etc. aren't really needed as you can directly access builtin methods from the templates as below:

    $Image.CroppedImage(320,200)
    $Image.SetWidth(360)

    Posted by Lucas, 31/10/2012 4:28pm (1 year ago)

RSS feed for comments on this page | RSS feed for all comments