Text editing

Let's write a text app so we can add text elements to our pages. It shall support rich text formatting so we will store it in raw HTML format.

The big idea is the same: use a central app and individual instances. As in case of the menu, we will implement the .Inline method.

We will store the HTML content of the text file within the body of the text file itself, as shown green on the image above. That's exactly how the html-1 app does it.

Here is an example of how our text app could be stored on the filesystem, including a text instance:

PATH                                :BUCKET  [TYPE]
//example.com/apps/text             :0       [app-1]
//example.com/apps/text/.Inline     :0       [sjs-4e]
//example.com/apps/text/text.css    :0       [css-2]
//example.com/text-instance         :2       [/apps/text]

The .Inline file shall contain:

<?
    var css = response.addCssModule(source.select('../text.css'));
?>
<div class="<?= css.div ?>"><?== f.body() ?></div>

It should be all familiar.

We are using the wrapping div to help namespace our CSS. Create the /apps/text/text.css file, set its type to css-2 and add the following code:

.div b {
    color: red;
}

Notice how the style is only applied to <b> tags within the text element because of the .div selector. To avoid class name conflicts, it is highly recommended to only use namespaced CSS.

Set the /text-instance file's body to Hello <b>world</b>. The following will be injected into the page when we render it with a text.inline() call. You can verify it by visiting your website's root page and looking at its source code.

<div class="o1-file" data-o1-path="//example.com/text-instance"></div>
    <div class="N93B1F7D0-div">Hello <b>world</b></div>
</div></div>

It will be wrapped in a HTML structure identifying the file on the filesystem, so you will automatically have drag&drop support and access to the contextmenu.

Editing

To add text-editing support, we must add some markup to our inner div:

  • class="o1-contenteditable" to mark it as editable,

  • data-o1-contenteditable="body" to route the data to the file's body upon saving, and

  • data-o1-contenteditable-format="html" to allow rich text formatting.

<?
    var css = response.addCssModule(source.select('../text.css'));
?>
<div
    class="o1-contenteditable <?= css.div ?>"
    data-o1-contenteditable="body"
    data-o1-contenteditable-format="html"
><?== f.body() ?></div>

Warning: make sure to not add whitespace around <?== f.body() ?>. It would be added to the text every time upon loading the page, making it impossible for you to remove it.

Test drive

  • Visit your website's root page to see the text instance.

  • Enter Text mode by clicking the T icon on the Boomla Toolbar (or by pressing F4 on your keyboard).

  • Make some changes. Rich text formatting should be allowed. (Note that the editor will normalize the HTML structure of the text upon clicking into it, so it may be modified slightly on the first click.)

  • Save by clicking the Save icon in the toolbar, by clicking Ctrl+S or by exiting Text mode.

So how does Boomla find where to route the contents of the editable div?

  • It looks for the closest DOM element with the class o1-file.

  • That DOM element should have an attribute named data-o1-path holding the file's path.

  • As specified by data-o1-contenteditable="body", Boomla will save the edited value in the body in that file.

Editable menu

Now that we are at it, let's also make our menu entries editable:

  • add the o1-file class to menu entries,

  • define the data-o1-path attribute on entries,

  • add the o1-contenteditable class to make them editable,

  • route the values back to the title property of page files by setting data-o1-contenteditable="title".

Note that we did not allow rich text formatting, this is intentional. Let's keep the menu's style managed centrally.

var css = response.addCssModule(source.select('../menu.css'));
 
function renderMenu(page) {
    var s = '';
 
    var classNames = css.a + ' o1-file o1-contenteditable';
    if (context.id() === page.id()) {
        classNames += ' ' + css.active;
    }

    s += '<a class="' + classNames + '" data-o1-path="' + page.path() + '" data-o1-contenteditable="title" href="' + page.path() + '">';
        s += util.htmlEncode(page.title());
    s += '</a>';
 
    s += '<div class="' + css.subpages + '">';
        page.query(':1').each(function(subpage) {
            s += renderMenu(subpage);
        });
    s += '</div>';

    return s;
}
 
var p = renderMenu(f.root());
response.body(p);

A couple things to note here:

  • When changing the file title of a page, its file name will be automatically regenerated for you in a search engine friendly manner. For example  A **crazy** title! :) will become a-crazy-title.

  • After renaming a page, Boomla will automatically redirect visitors from the old path of the page to the latest path. It is done using the built-in version history. Each file has a file ID which is used to track the location of files across versions. In case you are into this, a SEO friendly 301 redirection is performed. Read about this later if you are interested.

See how to do it (silent video)

Drag & drop

Let's add drag & drop capabilities in the next part.

Subscribe to our newsletter!