?

Log in

Smartsite version: iXperion 1.4, maybe older too.

Challenge


To notify the web page visitor that the e-mail message was offered to the SMTP server succesfully.

Problem


When Sendmail works perfectly, it offers no way of seeing that, except for the omission of an error.

Solution


Put the Sendmail macro inside a Region macro. Then set the Sendmail's error attribute to throw its error. The Region macro will capture it. Tell the Region macro's error attribute to output its errors to screen. After the Sendmail macro, still inside the Region macro, you write up the success notification.

Now, when an error happens in the Sendmail macro, the remainder of the Region's execution will stop. Instead, the Region will show the error on screen.

However, when no error happens and sending is a success, the Region's execution will continue. Thus, the success notification is shown and the user is happy.

Technical implementation


<se:region
rem="this region rem text shows up in the error stack trace"
error="{string.concat(html.tag(p, 'Error while sending e-mail:', html.break(), this.error.message())}"
>

<se:sendmail
rem="this sendmail rem text shows up in the error stack trace"
error="{this.error.throw()}"
>
Implement your own parameters
</se:sendmail>

<p class="successHeader">Success!</p>

</se:region>




Happy coding!
 
 
Current Mood: accomplishedaccomplished
 
 

Smartsite version: 5.2, 5.3.

Problem

Smartsite's built-in macros for generating menus also generate too many AIM relations.

Cause

Smartsite offers no built-in macro that creates a menu the way most clients like to see it. XLinks is too sparse, Sitemap is too broad. We want something in-between. Usually we use XLinks or recursing XLinks to get what we want. Unfortunately every XLinked hyperlink is added to the AIM relations, causing a slow-down whilst saving articles.

Solution

A possible solution would be some granularity: when do we want to add the AIM relation, and when not? Well, we want the AIM relation only when the hyperlink needs to be visible. The easiest way to do that, is to avoid rendering unneeded hyperlinks and unneeded XLinks. We need to limit the XLink recursation: only recurse on a folder when it is part of the parent hierarchy. Thus, we avoid recursing on uncles and siblings.

Technical implementation

What we need to know is the parent hierarchy. Luckily, Smartsite offers the Parents macro. Then, we need to answer the question whether an item in the hierarchy needs recursing: no when it reflects an uncle or sibling of the current article, and yes when it reflects the article itself or any of its ancestors, all the way to an arbitrary top.

We can have Smartsite answer that question by executing a simple bit of VBScript:
returnValue = (InStr(strParents, strCurrent) > 0)

We catch the returnValue in a macro buffer and check that in the recursing XLink.

Technical considerations

The VBScript InStr function will find the item number 3 inside the item number 131. That would lead to a recursion of article 131, while we were hoping to avoid that. Therefore, we must make VBScript understand that we want 3, and nothing else. We achieve that by adding delimiters. Pipes come to mind, as they are not used in numbers. Therefore, when we build the parents list, we tell the Parens macro to use a pipe as its separator. And we also adorn the current item number with pipes. Thus, we ask whether |3| exists in |131|, which is false, subsequently avoiding recursion.

Full sample code

<se rem="1st parameter: the start item number; 2nd parameter: a pipe separated list of parent numbers, with the active item number as the last one" />
<se type="script" language="vbscript" save="doRecurse" rem="recurse only when the evaluated item lies within the parents path">
Dim strParents, strCurrent
strCurrent = "|{translationParam(1)}|"
strParents = "|{translationParam(2)}|"
returnValue = (InStr(strParents, strCurrent) > 0)
</se>

And of course we apply this to our XLink Recursion discussed earlier.

Happy coding!

 
 
Current Mood: cheerfulcheerful
 
 
Remember those MS VB6 components we created for the 5.x series of Smartsite? Remember how to debug them?

Last time we debugged a component and needed to step through the source code, was 3 years ago. And we forgot how to do it.

So, here's how:
1. Install MS VB6 on the web server.
2. Turn off the IIS and turn on the Smartserver.
3. Replace your macro.type attribute with the macro.object attribute.
4. Set MS VB6 to run the component, and place a break on the class *_execute() event.
5. Invoke the page.

Simple, yes?

Yes. Once you know how.
 
 
Current Mood: grumpygrumpy
 
 
26 May 2010 @ 13:01
One of Smartsite iXperion's greater achievements is the cache macro, which gives us a great degree of control over what page part is cached when, why, and for how long.

Unfortunately, the documentation about that macro fails to provide examples of its keydependencies attribute.

We use this attribute to create different versions of the cache based on differing parametrized input, like the values submitted by a search form.

We set keydependencies as a comma separated list of vipers. In case the values come from a hypothetical language choice in a form, we can set it like so:

<se:cache maxage="12:00:00" keydependencies="{request.form(language,default='EN')}">
<!-- cachable code here -->
</se>


In case of more form values, we can combine them comma-separated, like so:
<se:cache maxage="12:00:00" keydependencies="{request.form(language,default='EN')},{request.form(month,default=5)}">
<!-- cachable code here -->
</se>


We can even combine these values with query parameters, for instance to allow for paging:
<se:cache maxage="12:00:00" keydependencies="{request.form(language,default='EN')},{request.form(month,default=5)},{request.query(page,default=1)}">
<!-- cachable code here -->
</se>


Mind you, Smartsite needs to cache each dependency variation separately.

Happy caching!
 
 
Current Mood: creativecreative
 
 
Chances are, that your Smartsite iXperion Manager generates error dialogs about missing manager actions and resource files, when you change and save the default channel item.

With credit to the Smartsite Helpdesk, add a headermapping key, old style, to the Smartsite Site Registry, with a name formatted as follows:
<domain>/cms/

in which you need to replace <domain> with 1 of 3 possibilities:

  1. The machine name, in case of a testing / development environment

  2. The text "localhost", in case the only person to access that machine is you, via the machine itself

  3. The fully qualified domain name to the web site (like smartsite.livejournal.com), in case of a production environment.



Within that key, add a string value named 'ch' with a value of 'MGR'.

Happy channeling!
 
 
Current Mood: chipperchipper
 
 
 
Suppose your client has their site split up into several channels (which, by the way, is the preferred method). Suppose each channel is asigned its own sub tree. Suppose each sub tree has its own collection of helper items. Then how can we tell an article entry template in the iXperion manager to present the correct collection, based on the channel in which thee article resides?

(Or: if you're in channel A, the article should present a collection from channel A, but if you're in channel B, the article should present a collection from channel B.)

Why would this be a question at all? Because the Smartsite Manager, including the one used in iXperion, does not recognise the channel separation while entering new or editing existing articles.

The solution relies on scripting of the contenttype fieldproperties.

For instance, a contenttype field of type ItemInput may have a property StartNode. Within that property, we can use a script viper. (Remember to use Smartsite 5 vipers when coding for the Manager, even when running iXperion!) (By the way: normally entering a script text into the StartNode property proves cumbersome. Make it easier by forcing all properties to use a text editor. Check your personal manager options.)

Here's an example:

{script:
Dim channelvalue
If Len(data("parent"))=0 Then 'Editor is creating a new article
 channelvalue=ado.executescalar( _
   "select subtree from contents where nr=?", data("key"))
Else 'Editor is editing an existing article
 channelvalue=ado.executescalar( _ 
   "select subtree from contents where nr=?", data("nr"))
End If
Select Case channelvalue
Case 2 'Default home of the default channel
 returnValue = 500 'some arbitrary folder in the default channel
Case 1000 'Home of a different channel
 returnValue = 1500 'some arbitrary folder in the other channel
Case Else 'More?
 returnValue = 1 'Site Root
End Select
}


Couple of remarks about this script:

  1. Yes, it does start with "{script:". This is the start of the script viper. Yes, a new line after that opening is required.

  2. It uses the VBScript language by default. Remember to quote your strings.

  3. Avoid using vipers inside the script block: they used to work in Smartsite 5.x, but fail under iXperion.

  4. Subtree management is required for the sampled queries. Older versions of Smartsite might not support subtrees. Vary the queries accordingly.

  5. Setting the magic returnValue variable returns the desired data out of the script block and into the field property.


Of course you can employ similar scripts to switch entire SQL queries, to change entry template colours, or to hide fields.

Happy dividing!
 
 
Current Mood: accomplishedaccomplished
 
 
You know the body text of an article is too long to display in an overview listing. You want to provide a shorter excerpt, so the visitor will still get the general idea.

Can you simply change the SQL-instruction to retrieve the first say 256 characters of the field? Sure. However... that field contains HTML tags. What tags are still opened when you cut off the text at 256? Maybe you even cut the text right down the middle of a tag? While this method may prove fine for some articles, others can seriously break your mark-up, often resulting in seriously malformed pages. So no, can't simply do an SQL-based cut-off.

The next option is to let Smartsite remove the HTML tags for us. A simple xlinks macro can retrieve the text. Then we can have Smartsite remove the HTML tags using the plaintext encoding like so:
format="%plaintext:body%"


Then we need a cut-off. Hmm... can't use a custom translation in an xlinks format. Can use a vbscript viper, but that doesn't work. Tried postponing macro execution, also without success.

OK, different method. Let's still have Smartsite remove the HTML tags for us, and perform the cut-off, but this time we write the whole thing in VBScript. And while we're at it, we throw in a routine to help us cutting off the text in-between words or HTML entities. Lo and behold, it works.

<se type="script" language="vbscript" error="{error}" header="<ul>" footer="</ul>">

Dim strSQL, rst, strShorter, arrWords, i, intCutOff
strSQL = "select nr, body from vwActive where parent = 1"
Set rst = AdoHelper.Execute(strSQL)
strShorter = ""
intCutOff = 256

Do While Not rst.EOF

strShorter = Left(RemoveAllTags(rst("body")),intCutOff)
arrWords = Split(strShorter, " ")
strShorter = ""
i = 0

Do While Len(strShorter) < intCutOff And i < UBound(arrWords)

strShorter = strShorter & " " & arrWords(i)
i = i + 1

Loop

Context.Response.Buffer.AddLine("<li>" & strShorter & "...</li>")
rst.MoveNext

Loop

Set rst = Nothing
</se>


Plenty left to improve:

  • Vary how much text should be returned

  • Vary the query to retrieve data

  • Add a hyperlink to the Smartsite item as well

  • etc



Needless to say, this kind of thing is much easier in Smartsite iXperion...

Happy coding!
 
 
Current Mood: creativecreative
 
 
01 October 2009 @ 16:19
Imagine wanting to export several CMS items, that are spread out over various logical places in the CMS hierarchy. And you don't want to open all the CMS folders and hunt down those items, and selecting them 1 by 1, while holding the ctrl-key, because 1 wrong click will deselect all items and force you to restart selecting. Then maybe this pearl of experience may help you.


This is a general tree in Smartsite 5.
Average Smartsite Configuration Tree


We can open all these folders and hunt down our items... but if we already know the item numbers we can also select the desired items using an SQL Query.
Querying the Smartsite database (Dutch version)


Then we can select the results of that query and hit the 'Load' / 'Laad' button... that creates a temporary tree view of the desired items.
Loading the query results into a Smartsite Tree View (Dutch version)


From this view, manipulating the items together becomes trivial.
 
 
Current Mood: creativecreative
 
 
21 September 2009 @ 14:45
We know we can echo the number, code, and content type of any of the CMS articles into its output. This property we can use to apply page-specific styling. That helps us style menu options, background images, and more.

Suppose our site has a dynamic drop-down menu, rendered using javascript. Surely we wouldn't want to regenerate that menu for every page, just to flick a switch to change the colour of the currently active page, its menu option, and its menu? That takes too much rendering. It's faster to render the menu once, and change the colouring using page-specific styling.

For instance, we could design a bit of CSS, and add it to the page's render template:

<style type="text/css">/* <![CDATA[ */
/* parent menu that holds the menu option of the currently active page */
#page123 .dropmenu122 { background-color: #99f; color: #000; border: 2px inset #339; }
/* menu option for the currently active page */
#page123 .dropmenu122 .menuoption123 { background-color: #000; color: #ccf; border: 2px solid #000; }
/* ]]> */</style>


Then we identify the current page in its body element with the id of 'page123', we render our menu to include the numbers of the pages to which each option links, and we're set.

One way to include a page-specific background-image:

<style type="text/css">/* <![CDATA[ */
/* change background picture for current page banner */
#page123 .topbanner { background-image: url(/img/pages/123/topbanner.png) }
/* ]]> */</style>


The above example assumes each page has its own directory of predefined images. Of course we can have the page editor define what banner needs to be shown, by reading their choice using Smartsite's page property macros.

Why put these CSS blurbs in a render template, instead of writing it up for every page and collecting it into one big CSS file? Because the latter requires more work.
 
 
Current Mood: creativecreative
 
 
Sometimes, a Smartsite Duallist needs replacing with something users like better. In those cases, a Checkbox List comes in handy. Smartsite offers its own Checkbox List component, but what if we wish to use it in our own forms? We build our own.

Check box list example

There's 2 problems to solve:
1. How do we combine the data for the previously chosen options with the data for the not yet chosen options into 1 list, and how do we tell the HTML form which options are chosen?
2. How do we make the list look nice when the option texts run long enough to wrap to the next line?


1. Getting the data


We know that we can tell a checkbox input it's chosen by setting its "checked" attribute. The fastest way to set this, is by having the database query return the correct setting. Luckily, SQL queries can return literal strings. So all we need are two batches of data: one batch of the chosen options, and one batch of the remaining options. Then we combine them into one, using a UNION statement, which we execute using an se:sqlquery macro.

Like so:

select Nr, Description, 'checked="checked" ' as strChecked from tblPossibleOptions
inner join (select NrOption from tblUserChosenOptions where NrUser=?:userid2) as tblUserOptions
on tblPossibleOptions.Nr = tblUserOptions.NrOption

UNION

select Nr, Description, ' ' as strChecked from tblPossibleOptions
left join (select NrOption from tblUserChosenOptions where NrUser=?:userid1) as tblUserOptions
on tblPossibleOptions.Nr = tblUserOptions.NrOption
where tblUserOptions.NrOption is null

order by Description Asc


See how we recognised that options weren't chosen: we created a left join between the possible options and the ones chosen by the user, and explicitely excluded those combinations that were chosen. This is the fastest way known to us today.

Also see how we generated a literal string named 'strChecked', containing the setting we need for our checkbox input.

Did you see the ORDER-BY clause? This sorts the rows in alphabetical order of the option's description, instead of the order in which the data was retrieved.

Also see that we employ a new feature in Smartsite iXperion: we can identify the query parameters by appending a colon and an identifyer to the question mark, like so: "?:identifyer". Those identifyers have to be unique, which is why we chose to number them. Be sure to put the same user identification into each parameter!

The rowformat for the se:sqlquery macro will take this data and create a format per data row. In our case, we wish to enter the literal strChecked from our data into the format. To get iXperion to allow mixing strings and attributes, we need to encase a bit of the format in CDATA blocks, like so:


<![CDATA[<label
for="chkUserOption_]]>{this.field(nr)}<![CDATA["
><input type="checkbox" name="chkUserOption"
id="chkUserOption_]]>{this.field(nr)}<![CDATA["]]>
{this.field(strChecked)}
<![CDATA[value="]]>{this.field(nr)}<![CDATA["
/>]]>{this.field(Description)}<![CDATA[</label>]]>


(Think that looks jumbled? Try writing it in HTML with entities for < and >!)


Making the long wrapped lines look nice


We envelope the checkbox and its description in a single label element. That way, when someone clicks the description, the checkbox will be checked or unchecked. This proves easy to use.

However, you will recognise that some option descriptions run so wide, that they wrap lines, and the description text will end up immediately below the checkbox. That doesn't look very nice. It'd be nicer if all next lines for the same description start at the same indentation as the first line.

And no, we did not choose to employ a table for this. Instead, we used a bit of CSS. We indent the text a bit, and then outdent the first line. Like so:


.checkboxlist label { padding-left: 2em; text-indent: -2em; }


Seems easy, yes? Everything seems easy once you know how.

Happy coding!
 
 
Current Mood: nerdynerdy