incubator-sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache Sling Website > 46 Line Blog
Date Tue, 16 Feb 2010 13:09:00 GMT
<html>
<head>
    <base href="http://cwiki.apache.org/confluence">
            <link rel="stylesheet" href="/confluence/s/1519/1/1/_/styles/combined.css?spaceKey=SLINGxSITE&amp;forWysiwyg=true"
type="text/css">
    </head>
<body style="background-color: white" bgcolor="white">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
     <h2><a href="http://cwiki.apache.org/confluence/display/SLINGxSITE/46+Line+Blog">46
Line Blog</a></h2>
     <h4>Page <b>edited</b> by             <a href="http://cwiki.apache.org/confluence/display/~bdelacretaz">Bertrand
Delacretaz</a>
    </h4>
     Add missing display-footnotes macro
          <div id="versionComment" class="noteMacro" style="display:none; padding: 5px;">
     Add missing display-footnotes macro<br />
     </div>
          <br/>
     <div class="notificationGreySide">
         <p>This tutorial is based on the first <em>Sling Gems</em> on dev.day.com:
The <a href="http://dev.day.com/microsling/content/blogs/main/sling-46-lines-blog.html"
rel="nofollow">Sling gems: a blog in 46 lines of code</a>. It has slightly been adapted
to fit here.</p>

<p>In this tutorial, the SlingPostServlet and the sling.js library are brought together
using 46 (no kidding: <em>fourty-six</em>) lines of code to create a simple blog
(or let's say <em>bloggish</em>) application.</p>

<p>I used this example in my <a href="/confluence/pages/createpage.action?spaceKey=SLINGxSITE&amp;title=Rapid+JCR+applications+development+with+Sling&amp;linkCreation=true&amp;fromPageId=5963888"
class="createlink">http://us.apachecon.com/c/acus2009/sessions/284</a> presentation
at ApacheCon US 09 in Oakland (slides will be available soon), and I think it's a good testimony
to the power and simplicity of Sling.</p>


<h2><a name="46LineBlog-Step1%3ACreatingcontent"></a>Step 1: Creating content</h2>

<p>The easiest way to create content in Sling is to use an HTTP POST request, let's
use a simple HTML form to do that:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-html">
<span class="code-tag">&lt;html&gt;</span>
  <span class="code-tag">&lt;body&gt;</span>
    <span class="code-tag">&lt;h1&gt;</span>Sling microblog<span class="code-tag">&lt;/h1&gt;</span>
  
    <span class="code-tag">&lt;div&gt;</span>
      <span class="code-tag">&lt;form method=<span class="code-quote">"POST"</span>&gt;</span>
        Title:<span class="code-tag">&lt;br/&gt;</span>
        <span class="code-tag">&lt;input type=<span class="code-quote">"text"</span>
name=<span class="code-quote">"title"</span> style=<span class="code-quote">"width:100%"</span>/&gt;</span>
        
        <span class="code-tag">&lt;br/&gt;</span>Text:<span class="code-tag">&lt;br/&gt;</span>
        <span class="code-tag">&lt;textarea style=<span class="code-quote">"width:100%"</span>
name=<span class="code-quote">"text"</span>&gt;</span><span class="code-tag">&lt;/textarea&gt;</span>
        
        <span class="code-tag">&lt;br/&gt;</span>
        <span class="code-tag">&lt;input type=<span class="code-quote">"submit"</span>
value=<span class="code-quote">"save"</span>/&gt;</span>
        <span class="code-tag">&lt;input type=<span class="code-quote">"hidden"</span>
name=<span class="code-quote">":redirect"</span> value=<span class="code-quote">"*.html"</span>/&gt;</span>
      <span class="code-tag">&lt;/form&gt;</span>
    <span class="code-tag">&lt;/div&gt;</span>
  
    <span class="code-tag"><span class="code-comment">&lt;!-- code of step
2 comes here --&gt;</span></span>
  <span class="code-tag">&lt;/body&gt;</span>
<span class="code-tag">&lt;/html&gt;</span>
</pre>
</div></div>

<p>That's two input fields, a submit button and a hidden field that tells Sling what
to do after the POST (in this case: redirect to the html view of the node that was just created).</p>

<p>To test the form, start Sling and save the above script as <tt>/apps/blog/blog.esp</tt>
<style type='text/css'>
.FootnoteMarker, .FootnoteNum a {
  background: transparent url(/confluence/download/resources/com.adaptavist.confluence.footnoteMacros:footnote/gfx/footnote.png)
no-repeat top right;
  padding: 1px 2px 0px 1px;
  border-left: 1px solid #8898B8;
  border-bottom: 1px solid #6B7C9B;
  margin: 1px;
  text-decoration: none;
}
.FootnoteNum a {
  margin-top: 2px;
  margin-right: 0px;
}
.FootnoteNum {
  font-size: x-small;
  text-align: right;
  padding-bottom: 4px;
}
.footnote-th1 {
  text-align: right;
}
.Footnote {
  padding-left: 7px;
  margin-bottom: 4px;
  border: 1px none #DDDDDD;
  writingMode: tb-rl;
}
.accessibility {
     display: none;
     visibility: hidden;
}
@media aural,braille,embossed {
        .FootnoteMarker, .FootnoteNum a {
         border: 1px solid #000000;
         background: #ffffff none;
    }
    .accessibility {
         display: run-in;
         visibility: visible;
    }
}
</style>
<script type='text/javascript' language='JavaScript'>
//<!--\n
var effectInProgress = {};
var despamEffect = function (id,effectType,duration) {
  if ((effectInProgress[id]) || (typeof(Effect)=="undefined") || (typeof(Effect[effectType])=="undefined"))
return;
  new Effect[effectType](id);
  effectInProgress[id]=true;
  setTimeout('effectInProgress[\"'+id+'\"]=false;',duration*1000);
};
var oldFootnoteId = '';
var footnoteHighlight = function(id,pulsateNum) {
  if (oldFootnoteId!='') document.getElementById('Footnote'+oldFootnoteId).style['borderStyle']
= 'none';
  oldFootnoteId = id;
  document.getElementById('Footnote'+id).style['borderStyle'] = 'solid';
  despamEffect('Footnote'+id,'Highlight',1)
  if (pulsateNum) despamEffect('FootnoteNum'+id,'Pulsate',3)
}
var footnoteMarkerHighlight = function(id) {
  if (oldFootnoteId!='') document.getElementById('Footnote'+oldFootnoteId).style['borderStyle']
= 'none';
  oldFootnoteId = '';
  despamEffect('FootnoteMarker'+id,'Pulsate',3)
}
//-->
</script>

<sup id='FootnoteMarker1'>
    <a name='FootnoteMarker1'
        href='#Footnote1'
        onClick='footnoteHighlight("1",true);'
        alt='Footnote: Click here to display the footnote'
        title='Footnote: Click here to display the footnote'
        class='FootnoteMarker'>
            1
    </a>
</sup>
 in the Sling repository - a WebDAV mount is the easiest way to do that. Browsing to <tt><a
href="http://localhost:8888/content/blog/*.html" rel="nofollow">http://localhost:8888/content/blog/*.html</a></tt>

<sup id='FootnoteMarker2'>
    <a name='FootnoteMarker2'
        href='#Footnote2'
        onClick='footnoteHighlight("2",true);'
        alt='Footnote: Click here to display the footnote'
        title='Footnote: Click here to display the footnote'
        class='FootnoteMarker'>
            2
    </a>
</sup>
 should display the above form 
<sup id='FootnoteMarker3'>
    <a name='FootnoteMarker3'
        href='#Footnote3'
        onClick='footnoteHighlight("3",true);'
        alt='Footnote: Click here to display the footnote'
        title='Footnote: Click here to display the footnote'
        class='FootnoteMarker'>
            3
    </a>
</sup>
</p>

<p>Input some data (using "foo" for the title, for the sake of our examples below),
save the form, and Sling<br/>
should display the form again, using the URL of the node that was just created.</p>

<p>At this point you're probably looking at an empty form with an URL ending in <em>foo</em>,
if you used that for the title. Or <em>foo_0</em> or <em>foo_1</em>
if other _foo_s already existed. Don't worry about not seeing your content, we'll fix that
right away.</p>


<h2><a name="46LineBlog-Step2%3AWhere%27smycontent%3F"></a>Step 2: Where's
my content?</h2>

<p>To verify that our content has been created, we can have a look at the JSON data
at <tt><a href="http://localhost:8888/content/blog/foo.tidy.json" rel="nofollow">http://localhost:8888/content/blog/foo.tidy.json</a></tt>,
which should display our new node's values:</p>


<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-javascript">
{
  "jcr:primaryType": "nt:unstructured",
  "text": "This is the foo text",
  "title": "foo"
}
</pre>
</div></div>

<p>That's reassuring, but what we really want is for these values to be displayed on
the editing form for our post.</p>

<p>Thanks to the <em>sling.js</em> client library, we just need to add a
<tt>Sling.wizard()</tt> call to our form to display those values. Let's first
add a <tt>&lt;head&gt;</tt> element to our form to load the <em>sling.js</em>
library, before the existing <tt>&lt;body&gt;</tt> of course:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-html">
<span class="code-tag">&lt;head&gt;</span>
  <span class="code-tag">&lt;script src=<span class="code-quote">"/system/sling.js"</span>&gt;</span><span
class="code-tag">&lt;/script&gt;</span>
<span class="code-tag">&lt;/head&gt;</span>
</pre>
</div></div>

<p>And add the <tt>Sling.wizard()</tt> after the form, where we had the
<em>code of step 2 comes here</em> comment:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-html">
<span class="code-tag"><span class="code-comment">&lt;!-- code of step 2 comes
here --&gt;</span></span>
<span class="code-tag">&lt;script&gt;</span>Sling.wizard();<span class="code-tag">&lt;/script&gt;</span>
</pre>
</div></div>

<p>Reloading the form at <tt><a href="http://localhost:8888/content/blog/*.html"
rel="nofollow">http://localhost:8888/content/blog/*.html</a></tt> and creating
a new post should now redirect to an editable version of the post, with the form fields correctly
initialized.</p>

<p>We can now create and edit posts; let's add some navigation, using more of the <em>sling.js</em>
functionality. </p>

<h2><a name="46LineBlog-Step3%3ANavigation"></a>Step 3: Navigation</h2>

<p>The <em>sling.js</em> library provides utilities to access and manipulate
content. For our blog, we'll use the <tt>getContent(path)</tt> method to list
the siblings of the current node.</p>

<p>Add the following code to your script, after the <tt>Sling.wizard()</tt>
call that was added in step 2:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-html">
<span class="code-tag">&lt;h3&gt;</span>Navigation<span class="code-tag">&lt;/h3&gt;</span>
<span class="code-tag">&lt;ul&gt;</span>
    <span class="code-tag">&lt;li&gt;</span><span class="code-tag">&lt;em&gt;</span><span
class="code-tag">&lt;a href=<span class="code-quote">"/content/blog/*.html"</span>&gt;</span>[Create
new post]<span class="code-tag">&lt;/a&gt;</span><span class="code-tag">&lt;/em&gt;</span><span
class="code-tag">&lt;/li&gt;</span>
    <span class="code-tag">&lt;script&gt;</span>
      var posts = Sling.getContent(<span class="code-quote">"/content/blog"</span>,
2);
      for(var i in posts) {
        document.write(<span class="code-quote">"&amp;lt;li&gt;"</span>
          + <span class="code-quote">"&amp;lt;a href='/content/blog/"</span>
+ i + <span class="code-quote">".html'&gt;"</span> 
          + posts[i].title 
          + <span class="code-quote">"&amp;lt;/a&gt;&amp;lt;/li&gt;"</span>);
      }
    <span class="code-tag">&lt;/script&gt;</span>
<span class="code-tag">&lt;/ul&gt;</span>
</pre>
</div></div>

<p>The first link to <tt>/content/blog/*</tt> brings us back to our content
creating form, which is nothing else than the editing form reading empty values and posting
to the "magic star" URL. </p>

<p>The rest of the javascript runs client-side, as it is not embedded in <tt>&lt;%
%&gt;</tt> code markers, calls the <tt>sling.getContent</tt> method
to get two levels of node data below <tt>/content/blog</tt>, and displays links
to nodes that it finds.</p>

<p>That's a basic navigation, of course, in a real blog we'd need some paging and contextualization
to cope with large numbers of posts.</p>

<p>Nevertheless, with this addition our ESP script allows us to create, edit and navigate
blog posts - not bad for 46 lines of code, including comments, whitespace and output formatting.</p>


<h2><a name="46LineBlog-Step4%3ADatafirst%2Cstructurelater"></a>Step 4:
Data first, structure later</h2>

<p>You might have heard this mantra, which we apply in many areas of Sling.</p>

<p>In this case, adding a new field to our blog posts could not be easier: just add
an input field to the form, and Sling will do the rest.</p>

<p>Adding this inside our script's <tt>&lt;form&gt;</tt> element,
for example:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-html">
<span class="code-tag">&lt;br/&gt;</span>Author:<span class="code-tag">&lt;br/&gt;</span>
<span class="code-tag">&lt;input type=<span class="code-quote">"author"</span>
name=<span class="code-quote">"author"</span> style=<span class="code-quote">"width:100%"</span>/&gt;</span>
</pre>
</div></div>

<p>Allows us to add an author name to our blog posts. No need to define anything at
the repository level, as Sling is using it in unstructured mode in this case, and no need
to migrate existing data, the author field of existing posts will simply be empty.</p>


<h2><a name="46LineBlog-IwantmyESP%21"></a>I want my ESP!</h2>

<p>Now wait...we said we were going to create an ESP script, but our "application" is
just static HTML and some client javascript at this point.</p>

<p>That's correct - as we are using only Sling client-facing features at this point
(HTTP POST and <tt>sling.js</tt>), we do not necessarily need to use ESP code.</p>

<p>To keep things simple, we'll refrain from adding ESP-based features at this point,
and keep that for a future Sling Gem.</p>


<h2><a name="46LineBlog-That%27sthepowerofSling"></a>That's the power of
Sling</h2>

<p>The 46-line blog is a good example of the power of Sling. It leverages the <tt>SlingPostServlet</tt>

<sup id='FootnoteMarker4'>
    <a name='FootnoteMarker4'
        href='#Footnote4'
        onClick='footnoteHighlight("4",true);'
        alt='Footnote: Click here to display the footnote'
        title='Footnote: Click here to display the footnote'
        class='FootnoteMarker'>
            4
    </a>
</sup>
, which handles POST requests in a form-friendly way, and the <tt><a href="http://svn.apache.org/repos/asf/sling/trunk/bundles/servlets/post/src/main/resources/system/sling.js"
rel="nofollow">sling.js</a></tt> client library, which provides high-level
functionality on the client side.</p>

<p>A slightly fancier version of the <em><a href="/confluence/download/attachments/5963888/blog.esp?version=1">blog.esp</a></em>
script is attached to this post, the code is the same but some CSS makes it look a bit nicer.</p>

<p><table class='Footnotes' style='width: 100%; border:none;' cellspacing='0' cellpadding='0'
summary='This table contains one or more notes for references made elsewhere on the page.'>
  <caption class='accessibility'>Footnotes</caption>
  <thead class='accessibility'>
    <tr class='accessibility'>
      <th class='accessibility' id='footnote-th1'>Reference</th>
      <th class='accessibility' id='footnote-th2'>Notes</th>
    </tr>
  </thead>
  <tbody>
    <tr name='Footnote1'>
      <td valign='top' class='FootnoteNum' headings='footnote-th1'>
        <a href='#FootnoteMarker1'
          onClick='footnoteMarkerHighlight("1");'
          onMouseOver='footnoteHighlight("1",false);'
          alt='Footnote: Click to return to reference in text'
          title='Footnote: Click to return to reference in text'
          id='FootnoteNum1'>
            1
        </a>
      </td>
      <td id='Footnote1'
        valign='top'
        width='100%'
        class='Footnote'
        headings='footnote-th2'>
          ESP is Sling's server-side javascript language
      </td>
    </tr>
    <tr name='Footnote2'>
      <td valign='top' class='FootnoteNum' headings='footnote-th1'>
        <a href='#FootnoteMarker2'
          onClick='footnoteMarkerHighlight("2");'
          onMouseOver='footnoteHighlight("2",false);'
          alt='Footnote: Click to return to reference in text'
          title='Footnote: Click to return to reference in text'
          id='FootnoteNum2'>
            2
        </a>
      </td>
      <td id='Footnote2'
        valign='top'
        width='100%'
        class='Footnote'
        headings='footnote-th2'>
           This assumes your instance of Sling is running on port 8888. If that's not the
case, adjust the example URLs accordingly. 
      </td>
    </tr>
    <tr name='Footnote3'>
      <td valign='top' class='FootnoteNum' headings='footnote-th1'>
        <a href='#FootnoteMarker3'
          onClick='footnoteMarkerHighlight("3");'
          onMouseOver='footnoteHighlight("3",false);'
          alt='Footnote: Click to return to reference in text'
          title='Footnote: Click to return to reference in text'
          id='FootnoteNum3'>
            3
        </a>
      </td>
      <td id='Footnote3'
        valign='top'
        width='100%'
        class='Footnote'
        headings='footnote-th2'>
          Note that this requires disabling anonymous access to Sling pages: uncheck the <em>Allow
Anonymous Access</em> setting on the <em>Sling Request Authenticator</em>
configuration page at <tt><a href="http://localhost:8888/system/console/config" rel="nofollow">http://localhost:8888/system/console/config</a></tt>,
and save that configuration.
      </td>
    </tr>
    <tr name='Footnote4'>
      <td valign='top' class='FootnoteNum' headings='footnote-th1'>
        <a href='#FootnoteMarker4'
          onClick='footnoteMarkerHighlight("4");'
          onMouseOver='footnoteHighlight("4",false);'
          alt='Footnote: Click to return to reference in text'
          title='Footnote: Click to return to reference in text'
          id='FootnoteNum4'>
            4
        </a>
      </td>
      <td id='Footnote4'
        valign='top'
        width='100%'
        class='Footnote'
        headings='footnote-th2'>
          See <a href="http://sling.apache.org/site/manipulating-content-the-slingpostservlet.html"
rel="nofollow">Manipulating Content: The SlingPostServlet </a> for a more complete
description of the Sling POST servlet
      </td>
    </tr>
  </tbody>
</table></p>

     </div>
     <div id="commentsSection" class="wiki-content pageSection">
       <div style="float: right;">
            <a href="http://cwiki.apache.org/confluence/users/viewnotifications.action"
class="grey">Change Notification Preferences</a>
       </div>

       <a href="http://cwiki.apache.org/confluence/display/SLINGxSITE/46+Line+Blog">View
Online</a>
       |
       <a href="http://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=5963888&revisedVersion=4&originalVersion=3">View
Change</a>
              |
       <a href="http://cwiki.apache.org/confluence/display/SLINGxSITE/46+Line+Blog?showComments=true&amp;showCommentArea=true#addcomment">Add
Comment</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message