Part 0: Introduction
Part 1: Write and Test the PHP Script
Part 2: Create the Snippet
So far we have created the raw PHP script and tested the code outside of MODx. We’ve started the process of transforming this script into the rssfeedFetcher snippet and we have it working on the Learn MODx website. We’ve learned how to add optional parameters with default values to our call and everything seems to be working as expected. At the close of the last segment, I mentioned that our snippet is still relatively inflexible because the user has no control over how the output looks. We’re going to rectify this today by tapping a little into the MODx API to create placeholders and utilize chunks to allow for some templating and customization of the output.
As before, I want to reiterate one important thing. MODx is EXTREMELY flexible, and that flexibility means that there are numerous ways to accomplish the same task, so keep in mind that this is just one approach. I encourage you to keep your mind open as you read these tutorials and think of different ways, more optimal ways, etc. that you could do this. Read the forums, read the docs, ask questions, make suggestions 🙂
OK, that said, let’s dive in. Our snippet is working fine, we could improve on it, and we may, but for now it will do. Just as a reminder, this is what our snippet code looks like:
<?php //default parameter values $limit = (isset($limit)) ? $limit : 10; if (!function_exists(fetchFeed)) { function fetchFeed($feed_url, $limit){ //create a variable to hold the output $output = ''; //retrieve file and return as string</pre> $content = file_get_contents($feed_url); try { //all is good, we parse the feed $feeditems = new SimpleXMLElement($content); //iterate over item in the channel and get the title of each item foreach($feeditems->channel->item as $entry){ //set up a counter to determine how many items to be displayed if ($i < $limit) { $output .= "<a href='$entry->link' title='$entry->title'>" . $entry->title . '</a><br />'; } $i++; } } catch (Exception $e) { //some error occured, we output an error message and a description of the error $output .= 'An error occurred. The feed ' . $feed_url . ' could not be read: ' . $e->getMessage(); } return $output; } } //call the function return fetchFeed($feed_url, $limit); ?>
The MODx API
As I mentioned, we’re going to tap a little into the MODx API to accomplish our goals today. I have to start out by saying that there is documentation of the API in the Wiki and in the SVN where the most current official docs sit. Some of the methods and stuff don’t seem to be documented yet but you can learn a lot by studying the code of snippets in the repository and prowling the forums. I have some links at the end of this post that will hopefully be instructive in helping you start to get a grasp on using the API.
Styling the Snippet Output
The first thing we want to create is the ability for our user to optionally style the output of the snippet with some CSS. There are probably different ways to do this, but here’s one solution for this snippet. The user will be able to create a chunk with the CSS to style the chunk output. I will then use the regClientCSS() method to inject this code into the HEAD of the document using the getChunk() function to call the chunk into the snippet. Additionally, I want this to be optional, so I need to have some default styling in place. So you can already see from what we learned last time that we need to have something that looks like this:
... $cssChunk = (isset($cssChunk)) ? $cssChunk : 'our default chunk'; $modx->regClientCSS($modx->getChunk($cssChunk)); ...
We have created a variable called $cssChunk and then said that if the user creates their own chunk, assign that chunk name as the value of $cssChunk, if not, then assign our own default chunk name. The next line calls this chunk and injects its contents into the head section of the document.
I would like to mention that using a default chunk like I’m doing here is not optimal because it complicates the snippet installation process. The user has to actually create this chunk and paste the code in as part of installing the snippet. We could alternatively just have a default CSS file instead of a default chunk but for now let’s do it this way and then we can change it down the road as we refine our snippet.
Let’s think about this a little more. Since regClientCSS() injects the code into the head of the document, any styling we put in there can override the styling of our template. So if I style the anchor tag of my output that styling may override the styling for all the other anchor tags on my site. The way to resolve this is to very specifically target the outputted elements in our snippet with class names, and then use those in our CSS. Make sense? Let’s work on it then!
I will start by adding a class feedfetcher to the anchor tag in my snippet like so:
... $output .= "<a class='feedfetcher' href='$entry->link' title='$entry->title'>" . $entry->title . '</a><br />'; ...
So now that we have that set up, let’s create our chunk, which I’m going to call rssfeedFetcherCSS and place it in the same category as the snippet. In that chunk, I will place the following CSS code:
<style media="screen">* a.feedfetcher { text-decoration: none; color: #292929; display: block; } a.feedfetcher:hover { text-decoration: underline; color: #fff; background: #000; } </style>
So now I need to modify my snippet to pull this chunk in, and add the argument in my function, like so:
//default parameter values $limit = (isset($limit)) ? $limit : 10; $cssChunk = (isset($cssChunk)) ? $cssChunk : 'rssfeedFetcherCSS'; //Inject the CSS code into the head section $modx->regClientCSS($modx->getChunk($cssChunk)); if (!function_exists(fetchFeed)) { function fetchFeed($feed_url, $limit, $cssChunk){ //create a variable to hold the output $output = ''; .... //call the function return fetchFeed($feed_url, $limit, $cssChunk);
Let’s test this to make sure first that our default styling is going to be applied.
You can see now that our output looks significally different than it did before, and the other links on the site have not been affected by our custom styling because we targeted our snippet output by adding a class to it. Looks good.
Let’s now imagine our user decides they don’t like our styling and want to do something different on the sidebar output, and so they create their own custom chunk which we’ll call mycssChunk with this code:
<style media="screen">* a.feedfetcher { text-decoration: none; color: blue; } a.feedfetcher:hover { text-decoration: underline; color: #000; background: #fff; } </style>
If we now change the sidebar snippet call and add our custom css styling:
[!rssfeedFetcher? &feed_url=`http://modxcms.com/extras/rss/latestadded.xml` &cssChunk=`mycssChunk` !]
You’ll notice something interesting. If you reload the Test Feed page, nothing’s changed. It all looks the same. BUT, if you load the home page or any other page, the sidebar now shows our new custom styling:
This is happening because on the Test Feed page, we already have the default CSS added targeting the same class. The way I’m going to avoid this kind of thing is to give the user the ability to add their own CSS classes to the output and target these classes in their custom CSS. This will allow more flexibility and prevents inconsistent and unpredictable results. Again, there may be easier ways to do this.
Placeholders and Templating Snippet Output
At the moment, with the exception of the styling, our output is being controlled completely from within the snippet. How about if we change this and have the snippet just spit out placeholders that we can organize and place however we want using our own template?
In this example, this is a trivial thing because really, we only have two things to spit out, the title of the item and it’s link. In a more complex snippet you may be outputting more values than that.
The section of the code that is the core of our output is this:
$output .= "<a class='feedfetcher' href='$entry->link' title='$entry->title'>" . $entry->title . '</a><br />';
Ideally, we want to completely remove this from the snippet and pass the output formatting to a chunk. To do this, I will use the setPlaceholder() function of the MODx API Document Parser. The syntax in our case would look something like this:
$modx->setPlaceholder("placeholder name", $somevariable)
Let’s do this. First, let’s create the placeholders that we’re going to use inside the function like so:
$modx->setPlaceholder("fftitle", $entry->title); $modx->setPlaceholder("fflink", $entry->link);
To be able to use these placeholders we need to use the MODx parseChunk() function to assign them to the chunk. The generic syntax for this looks something like:
$modx->parseChunk('some chunk', $modx->placeholders, '[+', '+]')
We’re going to create a variable called $tplChunk to hold the name of our chunk, pass this to the function, and as before, we’re going to create a default chunk with a fallback template (called rssfeedFetchertpl) in case the user doesn’t create their own. Therefore in our snippet code we will replace
.... if ($i < $limit) { $output .= "<a class='feedfetcher' href='$entry->link' title='$entry->title'>" . $entry->title . '</a><br />'; } ....
with
if ($i < $limit) { $modx->setPlaceholder("fftitle", $entry->title); $modx->setPlaceholder("fflink", $entry->link); $output .= (!isset($tplChunk)) ? $modx->parseChunk('rssfeedFetchertpl', $modx->placeholders, '[+', '+]') : $modx->parseChunk($tplChunk, $modx->placeholders, '[+', '+]'); }
Look at that for some time and make sure it makes sense to you. The only new concept here is the use of the parseChunk() function.
We now need to pass this variable $tplChunk as an argument to our function, and then create our default chunk rssfeedFetchertpl and place this code in it:
<a class='feedfetcher' href="[+fflink+]" title='[+fftitle+]'>[+fftitle+]</a><br />
BUT! – We need global $modx;
As a last step before we test this to make sure it works, we need to do one more thing. Because we’re doing all this stuff inside the function, it’s not going to recognize the MODx API functions and methods like setPlaceholder() function. To resolve this, we need to declare $modx as a global variable simply by adding the following statement inside our function:
global modx;
Once we reload our website, if we haven’t made any syntax errors, everything should load just fine, exactly the same as before, since the default template is being loaded.
Testing it All
Let’s now assume our user wants to have the Test Feed page items appear as an unordered list. All they have to do is create a template chunk to effect this and then add that parameter to their call. Let’s create a custom chunk which we’ll call mypagefeedtpl and type this code in it.
<ul> <li class="feedfetcher><a class='feedfetcher' href="[+fflink+]" title='[+fftitle+]'>[+fftitle+]</a></li> </ul>
Let’s now call our page snippet with this custom template chunk:
[!rssfeedFetcher? &feed_url=`https://codingpad.maryspad.com/feed/` &limit=`5`&tplChunk=`mypagefeedtpl` !]
Additionally, let’s solve our little problem from before with the sidebar styling by having our user create a separate template for the sidebar output (mysidebarfeedtpl) and giving the anchor tag a different class name that I can then target in the custom CSS chunk. Below is the code for all that:
mysidebarfeedtpl Chunk
<a class='sidebarfeed' href="[+fflink+]" title='[+fftitle+]'>[+fftitle+]</a>
The modified mycssChunk
<style media="screen">* a.sidebarfeed { text-decoration: none; color: blue; style: inline; } a.sidebarfeed:hover { text-decoration: underline; color: #000; background: #fff; } </style>
And finally the sidebar snippet call:
[!rssfeedFetcher? &feed_url=`http://modxcms.com/extras/rss/latestadded.xml` &cssChunk=`mycssChunk` &tplChunk=`mysidebarfeedtpl` !]
And now our page looks like this, with the separate templating and styling appearing in both snippet calls and with the user having full control on how the output is displayed.
You can see that what we have now handed control of formatting and styling the snippet output to chunks and taken it out of the snippet. By using placeholders and chunks, the user can now present this information whichever odd way (s)he wants.
For your reference, here’s our snippet code so far.
<?php //default parameter values $limit = (isset($limit)) ? $limit : 10; $cssChunk = (isset($cssChunk)) ? $cssChunk : 'rssfeedFetcherCSS'; //Inject the CSS code into the head section $modx->regClientCSS($modx->getChunk($cssChunk)); if (!function_exists(fetchFeed)) { function fetchFeed($feed_url, $limit, $cssChunk, $tplChunk){ global $modx; //create a variable to hold the output $output = ''; //retrieve file and return as string $content = file_get_contents($feed_url); try { //all is good, we parse the feed $feeditems = new SimpleXMLElement($content); //iterate over item in the channel and get the title of each item foreach($feeditems->channel->item as $entry){ //set up a counter to determine how many items to be displayed if ($i < $limit) { $modx->setPlaceholder("fftitle", $entry->title); $modx->setPlaceholder("fflink", $entry->link); $output .= (!isset($tplChunk)) ? $modx->parseChunk('rssfeedFetchertpl', $modx->placeholders, '[+', '+]') : $modx->parseChunk($tplChunk, $modx->placeholders, '[+', '+]'); } $i++; } } catch (Exception $e) { //some error occured, we output an error message and a description of the error $output .= 'An error occurred. The feed ' . $feed_url . ' could not be read: ' . $e->getMessage(); } return $output; } } //call the function return fetchFeed($feed_url, $limit, $cssChunk, $tplChunk);
Wow, what a workout! 🙂 We’ve done quite a bit today, looked a little bit at the MODx API and used some of its methods, and transformed our snippet into something more flexible and customizable by an end user by creating and using placeholders, and enabling the use of chunks for templating and styling.
The snippet so far is quite functional, even though it’s not optimal for an actual live site. We really should be caching things, but that’s a whole other story. But it’s been a great exercise in learning some of the ins and outs of creating a snippet. We could make this a lot more interesting by using SimplePie, and I’ll probably delve into that later.
Remember what I said before, there are many ways to do things with MODx, so do some more reading and get those gears turning and think up different ways you could accomplish what we did here. I will be one very excited MODxer if you share those ideas and any feedback in the comments! 🙂
More Reading
MODx Evolution API Documentation (in SVN)
MODx 0963 API Documentation (the Wiki)
Introduction to the MODx API (0.9.6.x)
Snippet call parameters and default values
Forum Gems on Writing Snippets – http://modxcms.com/forums/index.php/topic,43393.msg259126.html#msg259126
Nice, but ooops, you left out a critical “$” under “BUT! – We need global modx;”
Should be “global $modx;”
Thanks Bruce! Don’t know how I missed that 🙂
Wow what can I say, thank you for another brilliant set of tutorials, I find them very easy to follow and have learned more about MODx in the past few days than ever before, I will definitely use MODx for my current project, I hadn’t realised just how flexible it could be.
Thanks again Mary for all of your hard work and I look forward to going through more of your tutorials, I really like your writing style.
Thanks for the feedback Jon!
mary
Hi Mary! thanks for this!
This chunk is repeated for each item in the list.
including and
for unordered lists itś no big deal if you don’t setup a different margin top and margin bottom for
I guess but I am not sure how to do this that we need to use two separate chunks, an outer tpl and a rowTpl, like wayfinder does
Do you know how it could be done?
I meant including <ul> and </ul>
I’m not entirely sure I understand your question since it seems like some things got cut out of your comment.
From what I gather though, I can say that yes, you can add as many templating chunks as you want. These chunks should come out of the HTML you’re using to set up the formatting of your output. Does this make sense? If you have a specific example of what you’re trying to do I can try to help you further
mary
hI Mary, thanks for your prompt reply!
Seeing how wayfinder behaves I guess I need to use an outer template which isparsed only once, containing a wrapper placeholder…
I’m not sure but I guess I know how to do it. If I success, I’ll come back to you with my solution if you like…
Abner
Hi Mary! I found a solution and as I told you,here it goes in case you would like to look at it:
Chunk: myrssChunk
<li class=”feedfetcher”><a class=’feedfetcher’
href=”[+fflink+]”
title='[+fftitle+]’>[+fftitle+]</a></li>
Chunk: rssOutTpl
<ol>
[+rows+]
</ol>
Snippets
[!RSSFetchFeed!]
<?php
$RSSFetchFeed_base =
$modx->config[‘base_path’].’assets/snippets/RSSFetchFeed/’;
include_once $RSSFetchFeed_base . ‘RSSFetchFeed.inc.php’;
if (class_exists(‘RSSFeed’))
$rss = new RSSFeed;
else
return ‘La clase no existe’;
// limit existe?
$limit = (isset($limit))? $limit : 10;
// Se definió chunk CSS o uso el default?
//default parameter values
$cssStyle = (isset($cssChunk)) ? $modx->getChunk($cssChunk) :
file_get_contents($RSSFetchFeed_base.’Defaultstyle.css’);
$tplChunk = (!isset($tplChunk)) ? ‘rssfeedFetchertpl’: $tplChunk ;
$outTpl = isset($outTpl) ? $outTpl : ”;
//Inject the CSS code into the head section
$modx->regClientCSS($cssStyle);
// Argumento URL?
if (isset($URL))
{
$URL=
str_replace(array(‘|xq|’,’|xe|’,’|xa|’), array(‘?’,’=’,’&’), $URL);
$output=$rss->FetchFeed($URL,
$limit, $tplChunk,$outTpl);
}
else
$output = ‘y la URL?’;
return $output;
?>
RSSFetchFeed.inc.php
<?php
class RSSFeed {
function fetchFeed($feed_url,$limit,$Chunk,$outTpl)
{
global $modx;
//create a variable to hold the
output
$output = ”;
//retrieve file and return as
string</pre>
$content =
file_get_contents($feed_url);
try {
//all is good,
we parse the feed
$feeditems =
new SimpleXMLElement($content);//</pre>
//iterate over
item in the channel and get the title of each item
$i=0;
$rowOutput =
”;
foreach($feeditems->channel->item as $entry){
if($i<$limit){
$modx->setPlaceholder(“fftitle”, $entry->title);
$modx->setPlaceholder(“fflink”, $entry->link);
$rowOutput .=
$modx->parseChunk($Chunk, $modx->placeholders, ‘[+’, ‘+]’);
}
$i++;
}
if(!$outTpl)
$output = $rowOutput;
else
{
$modx->setPlaceholder(‘rows’,$rowOutput);
$output =
$modx->parseChunk($outTpl,$modx->placeholders,'[+’,’+]’);
}
}
catch (Exception $e) {
//some error
occured, we output an error message and a description of the error
$output .= ‘An
error occurred. The feed ‘ . $feed_url . ‘ could not be read: ‘ .
$e->getMessage();
}
return $output;
}
}
?>
I have been asked to create a webstore-like website and would like to use mod for it
and thought to ask you to either give me some points on how to start it.
I also have a friend whom I used to creae webstores, websites but he does not know much about modx but is good with php
I have been showing him modx this weekend the client is asking for a very complex web store where he would rent out the site to other sub stores to sell their goods like etsy.com
the problem is in this case we would need to create an admin page for the sub-store owners to manage their goods and I do not know if it should be a modx manager that they have access to or should we create custom pages for them where they log in etc.
I would apreciate any tips, help.
many thanks, Csaba
I have done only very simple webstores with MODX, mostly either using PayPal directly or integrating with third party carts like FoxyCart, LemonStand, etc.
I would recommend that you use MODX Revolution rather than Evolution going forwards as Revolution is the branch that is being actively developed and supported by the core dev team.
I have never developed anything with that level of complexity (in terms of the sub-stores). My immediate thought would be to give each sub-store its own context within MODX, and then you can restrict it so that each sub-store client sees only their context when they log in. I believe that should work, but you’ll need to experiment.
Hope this helps a little bit
mary