Creating your own custom blocktype consists of 2 steps: first you create the back-end (the block add/edit interface that your users will see when they add the block to a page or edit an existing block), then you create the front-end (the block's view.php file, and related JS/CSS assets).
Navigate to Dashboard > Stacks & Blocks > Designer Content Pro, and click the "Create New Block Type" button. Enter the blocktype handle and name, then add the fields you want this block to have. Hover your mouse over any field to see an explanation.
Now comes the fun part! After you've created the blocktype via the dashboard interface in step 1, click the "Sample Code" button next to the new blocktype. Copy the contents of the textbox, and make note of the path it tells you where your block's view.php file is located.
Now locate this view.php file on your computer or server, and edit it with a text editor. You'll notice that this file is fairly empty. Replace the comments inside the php "foreach" loop with the sample code you copied from the dashboard. This gives you the most basic display of the block data, but you can customize it to your heart's content by wrapping various fields in your markup.
You will usually want more fine-grained control of the data output (for example, you may want to wrap an image in a link, so you only want the "href" of the chosen link, not the entire <a> tag). See the Front-End API section below for details on all the options available to you for each field type.
Note that as with any Concrete5 block, you can add a view.css file and/or a view.js file if you want to include a stylesheet or custom javascript code (or you can add a /css/ and/or /js/ folder and put any number of files inside them for inclusion with the block markup).
Not right now -- this addon is specifically for people who understand HTML and CSS (and optionally Javascript). It doesn't create the actual code to display the block data -- it just creates the editing interface and provides the data in a clean and consistent way. HOWEVER, we will soon be releasing an update that allows for the importing of custom blocktypes from other sites, and when we have this feature ready we will also be providing a LOT of free front-ends that you can easily drop into your site. Sign up for our mailing list if you'd like to be notified of product updates.
As of version 1, the two addons serve slightly different purposes. While both let you create custom blocktypes, the free Designer Content addon only allows users to enter one set of information per block ("non-repeating" data), and the Designer Content Pro addon only allows users to enter "repeating" data. We are already working on version 2, however, which will bring in the "non-repeating data" feature of the free Designer Content addon into Designer Content Pro.
Other differences between the two addons:
It will keep getting better! We are planning an update to the free Designer Content addon that will bring in some of the improvements to the field types that you can already see in Designer Content Pro (for example, the "combo" link field type). By charging a fee for the Pro version, it allows us more time to devote to the free version as well. In addition, we have a strong financial motivation to continue developing and maintaining the free version: it is the best way to promote the paid version because it gives people a good feel for the kinds of things they'll be able to do with "Pro".
No. The two addons, while conceptually similar, have a completely different codebase underneath. So you can have both the free Designer Content and Designer Content Pro installed on the same site, but they are 100% separate entities that have no effect on each other.
No. Unlike the free Designer Content addon, the Designer Content Pro addon must remain installed in order for the custom blocktypes to work. This is because our guiding philosophy behind Designer Content Pro is to keep ALL of the back-end code out of your way and make it as easy as possible to integrate your front-end markup -- and in order to achieve this we stuffed all of the back-end functionality into the addon package code.
You can use permissions to restrict access to the Designer Content Pro dashboard page so only the administrators group can use it, or you could set the "Exclude Nav" attribute on the dashboard page so it doesn't show up in the dashboard menu at all.
Not at this time, but we are working on an update that will allow for importing/exporting of custom blocktypes so you can re-use them across multiple sites (each of which must have a separate license and copy of Designer Content Pro installed). Sign up for our mailing list if you'd like to be notified of product updates.
Just drop a 16x16 PNG called "icon.png" into the SITEROOT/blocks/your_blocktype_handle/ directory (next to your view.php file)
Not really. We recommend adding a global area to your site and putting a block in there instead -- or just hardcoding the markup of your block front-end into the theme template (since you don't get the block editing interface anyway with hardcoded blocks).
There is no technical limit to the number of items that can be added to a single block. But note that Designer Content Pro is optimized for user experience (for both you the designer who has to build the custom blocktype, as well as the user who edits block content), and as such it might not be the best solution for situations where you have hundreds or thousands of items per block. Not only is the editing interface not designed for it (there is no way to bulk add/edit/delete items), but all of the repeating item data for each block instance is serialized and stored in a single database field, which will probably not play well with the search index when you're dealing with hundreds of items per block instance.
Very well! All blocktype definitions are stored in files (not in the database), so your custom blocktypes can be versioned just like any other blocktype or package you create for your sites. Note that as with all Concrete5 blocks, this only applies to the definition of the blocktype -- not the content that users enter.
In a similar fashion as you would any other Concrete5 block (only difference is that you'll call the getRepeatingItems() function on the block instance to retrieve the repeating items, just like you would in the view.php template). For example:
//Assume you have a $page variable, either from a page_list loop, or from Page::getByPath() or Page::getByID()
$blocks = $page->getBlocks('Main');
foreach ($blocks as $block) {
if ($block->getBlockTypeHandle() == 'my_block_handle') {
$items = $block->getInstance()->getRepeatingItems();
foreach ($items as $item) {
$item->my_image->display();
}
}
}
This section is a reference for the code you can use in the view.php file of your custom blocktype (AFTER you've created it via the dashboard interface).
This is how you retrieve the repeating item data. The field_handle
s are whatever you defined in the dashboard interface when creating the blocktype.
foreach ($controller->getRepeatingItems() as $item) {
$item->field_handle_1->display(option1, option2, etc.);
$item->field_handle_2->display(option1, option2, etc.);
}
All of the sample code that follows is assumed to be inside the repeating items loop...
In these examples, the field handle is my_file
...
Output "force download" link to file (clicking link will download the file even if browser is capable of displaying it)
Link text is the same as the getTitle() function (see below)
$item->my_file->display();
Outputs empty string if user leaves field blank.
Output "view inline" link to file (clicking link will display file in browser if possible)
$item->my_file->display(false);
Outputs empty string if user leaves field blank.
Get the file title:
$title = $item->my_file->getTitle();
Returns file's title
attribute if user leaves editable title text blank or if "Editable Title Text" option is disabled.
Get the "raw" (unescaped) file title:
$raw_title $item->my_file->getTitle(false);
Returns file's title
attribute if user leaves editable title text blank or if "Editable Title Text" option is disabled.
Get the file URL ("force download" behavior):
$download_url = echo $item->my_file->getHref();
Returns empty string if user leaves field blank.
Get the file URL ("view inline" behavior):
$view_url = $item->my_file->getHref(false);
Returns empty string if user leaves field blank.
Get the C5 File Object (on which you call ->getDescription()
, ->getAttribute()
, etc.)
$file = $item->my_file->getFileObj();
if ($file) {
//do something
}
Returns null
if user leaves field blank.
Note about file permissions: If you have "Advanced Permissions" enabled for your site and you restrict the "view" permissions of a file, then the file will only be outputted if the user viewing the site has the appropriate permissions. If the user viewing the site doesn't have access to a chosen file, then the various functions above will behave as if the file was not provided (e.g. the display()
function will output an empty string, the getFileObj()
function will return null
, etc.).
In these examples, the field handle is photo
...
Output <img>
tag for the original (non-resized) image
This image tag will include width
and height
attributes
Alt text is the same as the getAltText()
function (see below)
$item->photo->display(); //results in <img src="path/to/image.jpg" width="xxx" height="yyy" alt="alt text" />
Outputs empty string if user leaves field blank.
The display()
function for image fields accepts the same arguments as the C5 image helper's getThumbnail()
function:
$item->photo->display(200, 100); //resize proportionally: max 200px wide, max 100px tall
$item->photo->display(200); //resize down to 200px wide, and whatever height matches that proportionally
$item->photo->display(0, 100); //resize down to 100px tall, and whatever width matches that proportionally
$item->photo->display(150, 150, true); //cropped to 150px square
Outputs empty string if user leaves field blank.
Responsive Web Design note: Images can be "responsive" even with the width/height attributes! Just add this to your CSS:
img { width: auto; max-width: 100%; height: auto; }
Get image alt text:
$alt = $item->photo->getAltText();
Returns image file's description
attribute (or title
attribute if description
is empty) if user leaves editable alt text blank or if "Editable Alt Text" option is disabled.
Get the "raw" (unescaped) image alt text:
$raw_alt = $item->photo->getAltText(false);
Returns image file's description
attribute (or title
attribute if description
is empty) if user leaves editable alt text blank or if "Editable Alt Text" option is disabled.
Get an "image object" (similar to the C5 image helper's getThumbnail()
function, except it could be for the original image as well as a thumbnail)
Note that this function accepts the same width/height/crop arguments as the display()
function above
original (not resized) image:
$img = $item->photo->getImageObj();
if ($img) {
echo $img->src; //path to original (unresized) image file
echo $img->width; //original image width
echo $img->height; //original image height
}
proportionally resized thumbnail:
$img = $item->photo->getImageObj(200, 100);
if ($img) {
echo $img->src; //path to thumbnail
echo $img->width; //width of resized image
echo $img->height; //height of resized image
}
cropped thumbnail:
$img = $item->photo->getImageObj(150, 150, true);
if ($img) {
echo $img->src; //path to cropped thumbnail
echo $img->width; //width of cropped image
echo $img->height; //height of cropped image
}
Returns null
if user leaves field blank.
Get the C5 File Object for the image (on which you can call ->getDescription()
, ->getAttribute()
, etc.)
Note that unlike getImageObj()
, this function has no arguments (because it doesn't do any resizing)
$file = $item->photo->getFileObj();
if ($file) {
//do something
}
Returns null
if user leaves field blank.
Note: If for some reason you want to call the C5 image helper's getThumbnail()
function yourself, you'll need to pass it the result of the getFileObj()
function (NOT getImageObj()
).
Note about file permissions: Images follow the same permissions rules as files -- see note at the end of the "File Fields" section above for details.
In these examples, the field handle is content
...
Output the html from the rich text editor:
$item->content->display();
Outputs empty string if user leaves field blank.
Get the content (without outputting it):
$content = $item->content->getContent();
Returns empty string if user leaves field blank.
Note that unlike most other field types, there is no concept of "raw" versus "escaped" data for wysiwyg fields (because the whole point of the rich text editor is to generate actual HTML).
In these examples, the field handle is title
...
Output the text:
$item->title->display();
Outputs empty string if user leaves field blank.
Get the text (without outputting it):
$text = $item->title->getText();
Returns empty string if user leaves field blank.
Get the "raw" (unescaped) text:
$raw_text = $item->title->getText(false);
Returns empty string if user leaves field blank.
In these examples, the field handle is description
...
Output the text:
$item->description->display();
Outputs empty string if user leaves field blank.
Get the text (without outputting it):
$text = $item->description->getText();
Returns empty string if user leaves field blank.
Get the "raw" (unescaped AND without being passed through nl2br()
) text:
$raw_text = $item->description->getText(false);
Returns empty string if user leaves field blank.
In these examples, the field handle is some_link
...
Output <a>
tag to the linked page or url.
Link title is the same as the getText() function (see below)
$item->some_link->display(); //includes `target="_blank"` for external urls, but not for site pages
$item->some_link->display(true); //ALWAYS includes `target="_blank"`
$item->some_link->display(false); //NEVER includes `target="_blank"`
Outputs empty string if user leaves field blank.
Get the link href:
$href = $item->some_link->getHref();
Returns empty string if user leaves field blank.
Get the link text:
$text = $item->some_link->getText();
Returns page name (for site pages) or the url (for external urls) if user leaves editable link text blank or if "Editable Link Text" option is disabled.
Get "raw" (unescaped) link text:
$raw_text = $item->some_link->getText(false);
Returns page name (for site pages) or the url (for external urls) if user leaves editable link text blank or if "Editable Link Text" option is disabled.
Determine if user entered a site page or an "external url":
$is_page_link = $item->some_link->isPageLink();
if ($is_page_link) {
//user chose a site page from page chooser control
} else {
//user entered an "external url" (or they left both controls blank)
}
Returns false
if user leaves field blank.
[Added in version 1.2]: Get the C5 Page/Collection Object (on which you call ->getCollectionName()
, ->getCollectionDatePublic()
, ->getAttribute()
, etc.)
$page = $item->some_link->getPageObj();
if ($page) {
$some_attribute = $page->getAttribute('some_custom_attribute_handle');
//do something...
}
Returns null
if user leaves field blank or if the link is an external link.
This field type is similar to the "Link" type -- but they differ in that the purpose of a "Link" field is to get a url that can be placed into an <a>
tag, whereas the purpose of the "Page" field is to get a Concrete5 Page Object.
Another way to think about this is like the difference between the autonav
and page_list
blocks. Autonav is for nav menus where you just need the page titles and links, but Page List templates often display a lot more information about the page (publish date, image attributes, descriptions/teasers, etc.).
In these examples, the field handle is the_page
...
Output <a>
tag to the chosen page. Link text will be the page name. Note that this function exists to maintain consistency with other field types -- you will probably never need to use it in actual code.
$item->the_page->display();
Outputs empty string if user leaves field blank.
Get the C5 Page/Collection Object (on which you call ->getCollectionName()
, ->getCollectionDatePublic()
, ->getAttribute()
, etc.)
$page = $item->the_page->getPageObj();
if ($page) {
$title = $page->getCollectionName();
$url = Loader::helper('navigation')->getLinkToCollection($page);
$img_attr = $page->getAttribute('some_custom_attribute_handle');
$thumb = $img_attr ? Loader::helper('image')->getThumbnail($img_attr, 300, 200) : false;
echo '<h2><a href="' . $url . '">' . $title . '</a></h2>';
if ($thumb) {
echo '<img src="' . $thumb->src . '" width="' . $thumb->width . '" height="' . $thumb->height . '" alt="" />';
}
}
Returns null
if user leaves field blank.
[Date fields were added in version 1.2]
In these examples, the field handle is start_day
...
Output the date in the C5 default format:
$item->start_day->display();
Outputs empty string if user leaves field blank.
Output the date in a different format:
$item->start_day->display('F jS, Y');
See https://php.net/date for date formatting options.
Outputs empty string if user leaves field blank.
Get the date in the C5 default format:
$start_date = $item->start_day->getDate();
Returns empty string if user leaves field blank.
Get the date in a different format:
$start_date = $item->start_day->getDate('F jS, Y');
See https://php.net/date for date formatting options.
Returns empty string if user leaves field blank.
NOTE about using alternate date formats: In order to make the date picker work for non-U.S. date formats, you need to define the following 2 settings in your config/site.php file:
define('DATE_APP_GENERIC_MDY', 'j/n/Y'); //as per https://php.net/date
define('DATE_APP_DATE_PICKER', 'd/m/yy'); //as per https://api.jqueryui.com/datepicker/#utility-formatDate
The two definitions must equate to the same format, although they each use different codes!
DATE_APP_GENERIC_MDY is used in PHP code and hence must be a valid format as per the date() function (see https://php.net/date).
DATE_APP_DATE_PICKER is used in Javascript code for the JQueryUI date picker widget, and hence must be in a different format (see https://api.jqueryui.com/datepicker/#utility-formatDate).
[Checkbox fields were added in version 1.2]
In these examples, the field handle is is_special
...
Output "Yes!" if the box was checked, or "No." if it wasn't checked:
$item->is_special->display('Yes!', 'No.');
If you don't pass in a 2nd argument to the display
function, an empty string will be outputted when the box is unchecked.
Determine if the box was checked:
$is_checked = $item->is_special->isChecked();
if ($is_checked) {
//do something...
}
[Select List fields were added in version 1.2]
Select List fields come in two different types: Dropdown List (which only allow a single selection) and Checkbox Lists (which allow multiple selections).
When you are creating your custom blocktype via the Designer Content Pro dashboard page, you enter all of the options that will populate the dropdown or checkbox list via the "Select Options" text area. Enter one option per line, and each option must contain a "handle" and a "label", separated by a colon. For example:
1: First Choice
2: Second Choice
3: Third Choice
In the above example, "1", "2", and "3" are the handles
(which become the dropdown or checkbox values), and "First Choice", "Second Choice", and "Third Choice" are the labels
(which become the dropdown or checkbox labels).
Handles can only contain letters (lowercase and uppercase), numbers, and underscores. Labels can contain any characters. For example, the following handles are all valid:
first_choice: First Choice
2nd_choice: Second Choice
Third_Choice: Third Choice
...but none of the following handles are valid:
first choice: First Choice
second.choice: Second Choice
third_choice!: Third Choice
Also note that every handle must be unique within a field
Last but not least, you can specify a "header option" for dropdowns by leaving the handle blank:
: --Choose One--
first: First Choice
second: Second Choice
third: Third Choice
This "header option" is especially useful when you wish to make a dropdown field "required" without defaulting to the first item in the list
In these examples, the field handle is your_choice
:
Output the chosen label (for dropdown lists) or a comma-separated list of chosen labels (for checkbox lists):
$item->your_choice->display();
Outputs empty string if nothing was selected/checked by user.
Determine if a particular option handle was selected/checked:
$first_choice_is_selected = $item->your_choice->isChecked('first_choice');
if ($first_choice_is_selected) {
//do something...
}
Get the handle of the selected option:
$selection = $item->your_choice->getSelectedHandle();
if ($selection == 'first_choice') {
//do something...
} else if ($selection == 'second_choice') {
//do something else...
} else {
//etc.
}
This function is intended for use with dropdown lists. If you call it on a checkbox list, only the first checked option handle will be returned.
Returns empty string if nothing was selected/checked by user.
Get the handles of all checked options:
$selections = $item->your_choice->getSelectedHandles();
foreach ($selections as $selected_handles) {
//do something...
}
This function is intended for use with checkbox lists. If you call it on a dropdown list, an array with 1 element will be returned.
Returns empty array if nothing was selected/checked by user.
Get the label of the selected option:
$label = $item->your_choice->getSelectedLabel();
echo $label;
This function is intended for use with dropdown lists. If you call it on a checkbox list, only the first checked option label will be returned.
Returns empty string if nothing was selected/checked by user.
Get the labels of all checked options:
$labels = $item->your_choice->getSelectedLabels();
echo '<ul>';
foreach ($labels as $label) {
echo '<li>' . $label . '</li>';
}
echo '</ul>';
This function is intended for use with checkbox lists. If you call it on a dropdown list, an array with 1 element will be returned.
Returns empty array if nothing was selected/checked by user.
Get an associative array of all selected options (handles as array keys, labels as array values):
$selections = $item->your_choice->getSelections();
echo '<ul>';
foreach ($selections as $handle => $label) {
echo '<li>' . $handle . ': ' . $label . '</li>';
}
echo '</ul>';
Returns empty array if nothing was selected/checked by user.
Get an associative array of all select options defined for the field (regardless of whether or not they were selected/checked by the user):
$all_options = $item->your_choice->getAllOptions();
echo '<ul>';
foreach ($all_options as $handle => $label) {
echo '<li>' . $handle . ': ' . $label . '</li>';
}
echo '</ul>';