November 22
Last modified by sbrickey on 11/19/2013 10:59 AM
Back in Kooboo v3, one of the types of fields that could be added to a content type was a MultiFile field... It's effectively the same as a File in that it's just a place to store binary data, except that the MultiFile allowed you to store groups of like files. I (generally) love the MultiFile field, and use it regularly... in fact, I have an 'Images' field that is MultiFile, which I use to store any images that will be included in the post. The reason I prefer the MultiFile, is that the files are actually stored with the specific post. Storing the contents in this manor makes management of the post and related files much simpler.

Anyway.

Kooboo decided to deprecate MultiFile in v4 (by default), recommending that separate lists be used in their stead. But the functionality is only minimally removed, and can be restored by adding back one simple file. Koobooo has in fact made the file available, to assist those that are upgrading from v3.

Path of the file: ~/CMS_Data/ContentType_Templates/Controls/MultiFiles.cshtml

Contents of the file:

@model IColumn

@{

    var containerId = Model.Name + "_Container";

    var valueGetter = new HtmlString("@(Model." + Model.Name + ")");

}

<tr>

    <th>@(string.IsNullOrEmpty(Model.Label) ? Model.Name : Model.Label)</th>

    <td>

        <div id="@containerId" class="mediafile">

            <input id="@Model.Name" name="@Model.Name" type="hidden" value='@valueGetter' data-bind="value:fieldValue"/>

            <ul class="clearfix" data-bind="foreach:data">

                <!-- ko if: isImage -->

                <li class="img">

                    <span class="preview"></span>

                    <a class="action" data-bind="click: $parent.removeItem">@@Html.IconImage("minus small")</a>

                    <img data-bind="attr:{src:Url}" height="100" width="100">

                </li>

                <!-- /ko -->

                <!-- ko ifnot: isImage -->

                <li>

                    <span class="left" data-bind="{text:FileName}"></span>

                    <a class="action right" data-bind="click: $parent.removeItem">@@Html.IconImage("minus small")</a>

                </li>

                <!-- /ko -->

            </ul>

            <ul class="clearfix" data-bind="foreach:files">

                <li>

                    <input type="file" name="@Model.Name" />

                    <a class="action right" data-bind="click: $parent.removeFile">@@Html.IconImage("minus small")</a>

                </li>

            </ul>

            <a class="action add" data-bind="click:$data.addFile">@@Html.IconImage("plus small")</a>

            @Html.Raw(FormHelper.Tooltip(Model.Tooltip))

        </div>

        <script>

            $(function () {

                function MultiFile_ViewModel(initValue) {

                    var self = this;

                    function getFileName(url) {

                        return _.last(url.split('/'));

                    }

                    function isImage(url) {

                        return /.jpg$|.png$|.gif$|.bmp$|.jpeg$/i.test(url);

                    }

                    var items = [];

                    _.each(initValue.split('|'), function (item) {

                        if (item != '') {

                            items.push({ Url: item, FileName: getFileName(item), isImage: isImage(item) });

                        }

                    });

                    self.data = ko.observableArray(items);

                    self.data.sort();

                    self.removeItem = function () {

                        self.data.remove(this);

                    };

 

                    self.fieldValue = ko.computed(function () {

                        var urls = [];

                        _.each(this.data(), function (item) {

                            urls.push(item.Url);

                        });

                        return urls.join('|');

                    }, this);

 

                    self.files = ko.observableArray([]);

                    self.addFile = function () {

                        self.files.push({});

                    };

                    self.removeFile = function () {

                        self.files.remove(this);

                    };

                }

                var multiFileViewModel = new MultiFile_ViewModel('@valueGetter');

                var multiFileTemplate = $('#@containerId');

                ko.applyBindings(multiFileViewModel, multiFileTemplate[0]);            

            });

        </script>

    </td>

 

</tr>


The file was supposed to be included in the 4.02 release, but I haven't noticed it myself... easier to just add the file real quick.

Reference(s):
http://kooboo.codeplex.com/SourceControl/changeset/view/86e7692c1f15#Kooboo.CMS/Kooboo.CMS.Web/Cms_Data/ContentType_Templates/Controls/MultiFiles.cshtml
http://forum.kooboo.com/yaf_postst2033_Release-of-Kooboo-CMS-4-02.aspx

Disqus: disqus comments
June 10
Last modified by sbrickey on 6/5/2013 10:41 AM
My website doesn't recieve many structural changes. Generally speaking, I'm content with most of what I've got.
Recently though, I realized that I could improve the usability of my blog administration. (so the changes I'll cover will never be seen by anyone else)

The Before
One of the things that really motivated me to choose Kooboo as my new blog platform (formerly SharePoint) is its support for on the fly content editing. Generally speaking though, this applies more to existing content than the new content (the new versions of Kooboo have made improvements in this area).

When I'd designed the blog (content structure, URLs and page layouts, etc)... I knew that I'd want to be able to add content on-the-fly as I'd done with my previous blog platform, without needing to go through the Kooboo admin menu (which requires several navigational clicks, and sometimes had formatting issues).

So the initial Kooboo design includes admin links that let me add content straight from the page (assuming I'm logged in). But the Blog page does not show future posts.

Generally speaking, this led me to two approaches.
- First, I set up an admin page that let me view unpublished and future posts. I could add my future post, and edit it from here. But I didn't like the extra click, so unless there was reason to use a future post (like a desire to avoid several posts on the same day), I didn't use this very often.
- Secondly, and my preference, was to just create the post and date it for immediate release. Since I am a big fan of "save often", I'd often create it with no content (usually just a filler like "pending")... then I would just load the page and edit the content. Depending on timing, it would be easy to watch as I write the entire post just by refreshing the page (hopefully the WayBack machine never picked up on this :)).

The After
I realized that my usage pattern was predicated on creating a post and editing it with as few clicks as possible. Click to create the post, click into the post, edit the content. The only reason I wasn't using future posting (and also revealing the entire writing process, as well as revealing incomplete information) was because future posts were only accessible via a second page (click). And the only reason they were on the other page was because they shouldn't be public.

But page contents are dynamic. And Kooboo gives me a great development experience. Surely something can be done.

I ended up copying the blog listing view (analogous to a web part) into a "future blog posts" view... wrapped the entire thing in a security check (which I'd already created for other uses)... and adjusted the CSS so the future posts were somewhat transparent (makes it look like it's got a translucent sheet over it). Then I add it to the blog page above the published posts.

So now, I can create the posts... they show up on the same blog page that I created it from (just a quick postback to refresh the page with the new content)... click into the post (future posts are available to anyone... I never felt a need to "secure" them... the URL is simply unknown until it hits the publish date)... then edit the post.

Once done, I can go back to the blog page, and see the future posts just as they'll be shown when they are eventually published (in fact, they reuse most of the CSS classes)
Disqus: disqus comments
February 5
Last modified by sbrickey on 2/5/2012 9:10 AM
So I just finished spending about 3 hours moving content from my "following" page into a structured list. I did this primarily because I wanted to make an RSS Aggregator.
Initially, the page was simply content... I could edit it, move things around (change order), or do generally whatever I wanted, and usually pretty quickly (cut/paste, etc).
But I'm aware that most blogs (not mine... i'll probably get around at some point) provide RSS links, and it'd be nice if I could aggregate them on my site, so I could see what's new all from one place. SharePoint Server has a web part ("RSS Aggregator") to do the same thing, I wanted it here in Kooboo.

So I set about. The work consisted of the following tasks...
1. Create a content type and list for the blogs I want to include. Includes a category (individual or team blogs), sort order (within each category), description, link to the blog, and link to the RSS feed.
2. Recreate the basic list of blogs (aka the blogroll). This was real quick since it was pretty much copy/paste from my tag cloud. Adjust the CSS a little.
3. Create a view ("RSS Reader") which takes the URL of an RSS Feed and path to an XSL file. Transform, and output to the page. Turned out to be a lot easier than I'd expected, thanks to the overloads of the XslCompiledTransform.
4. Create a view ("RSS Aggregator") which takes the RSS feed links from the blogroll list, and pipes the data to the RSS Reader.
5. Create the XSL file for the RSS Schema. Thankfully, google found me a good starting point, and Wikipedia has a good sample (well formatted, etc).
After that, just fix the CSS a little to keep readability good.

Now, I say 3 simple steps... that's because I don't count the first two... I should've started them as a list to begin with, but I wasn't sure how they were going to end up. I just wanted to keep track of the blogs that mattered to me.

I was disappointed that I was bumping into file lock issues a little more often than I'd like (which slowed down my ability to make changes as fast as I'd like)... I assume it's something to do with the code (since I ran into the same error trying to use FTP instead of the Kooboo management interface), though I'm not sure why... hopefully the feature request I subimtted for Kooboo will help that.

That said, I was EXTREMELY impressed and surprised at the performance. I expected my page (which loads from about 10 different sites) to take a few seconds to load, which would've been perfectly acceptable to me. Instead, the page loads almost immediately. I'm not even doing anything to optimize it (like using the parallelization framework to run the requests concurrently across multiple cores, or running it async with javascript), and unless performance changes, I doubt I'll change it.

So all said and done, a small amount of time resulting in effective and reusable code. Well worth it :)
Disqus: disqus comments
January 22
Last modified by sbrickey on 1/22/2012 5:05 AM

One of the reasons for choosing Kooboo was its (default) use of XML files to store content. While I understand the benefits of an RDBMS backend, for the size of my website I prefered a simple storage option. As such, I focused my migration effort on generating an XML data file using Kooboo's schema. I chose this because I wanted to use PowerShell for the migration (lightweight, available on any workstation I'd be using, etc), and the support for XML files within .Net is pretty good.

Let's start with pulling the data. SharePoint makes this REALLY easy. I opted for the SharePoint Client Object Model, even though I could've just as easily chosen the Server Object Model (since I ended up executing this on the server anyway, since it was the only place that the Client Runtime assembly was installed). I didn't want to use the Web Services approach, and the Data Services (ODATA) approach was quickly becoming overly complicated. So basically, the code just queries the list for all list items.
[script]

To save the data, I opt for LINQ2XML and the XDocument, since it provides some improved support for handling namespace declarations. I did find that an option I'd have appreciated is System.Xml.Linq.SaveOptions.OmitDuplicateNamespaces, but this is only available in .Net 4, and getting .Net4 into PowerShell would've taken more time than I felt it needed. After opening the file, I include a few of koboo's header schema... then I get into the actual data.
[script]

After getting the header out of the way, I focus on the list items. The first step is to extract the field values from SharePoint. Some data massaging was necessary for the publishing status and the keywords (which used SharePoint's Managed Metadata). I found a quick line of code which I could use to generate Kooboo's UUID (16 random characters). I also found that Kooboo generally uses the UserKey field as a URL key, and for good reason; often a title field will include characters which are invalid for a URL; instead of trying to encode the URL, I created a function to format the title into the UserKey that I wanted.
[script]

I then create an array to store the field names and values, as they will be populated in Kooboo. This becomes basically a field mapping array.
[script]

Finally, I actually generate the XML. Basically, Kooboo has an element for each record of data. Within each record is an element for each field, which includes the key (field name) and its value. The data type is also stored with the value.
[script]

Finally, flush the stream to disk, and close the file. I then validate the schema against the existing data file (which has at least one record, preferably two) to make sure it looks approximately correct, save a backup of the existing data file, and drop the new one into place.

Disqus: disqus comments
January 1
Last modified by sbrickey on 1/1/2012 6:29 PM
So this post will focus on how I customized Kooboo for my liking.

Content
- Out of the box, Kooboo's SampleSite includes some Articles... I am not writing this as a paper, so I can toss it all.
- Next, I need to add my own content types... blog post, code block, etc.
- I also found that Content Folders in Kooboo can be nested, and each nesting can redefine the content type. This means my content folders can match the UI hierarchy (yay).

Layouts (aka masterpages)
By default, Kooboo includes a two-column layout. With little-to-no effort, I can copy/paste and adjust this into a one-column layout. I may at some point add some extra two-column (right side vs left) or three-column layouts, but I'm content at the moment with just these two options.
At this point, the layouts have a bit of redundancy that I want to clean up.
  - First up, the sign-in/out... too much code here... I want a simple one-liner that I can include anywhere, and handles the logic of whether I'm logged in or not, what it shows, etc... I also ended up splitting out the logic code (am I signed in or not, what gets shown, etc) from the Login form (username/password), though this is less significant.
  - Second, the search box. Same as the sign-in/out, I want the search to be a simple one-liner.
  - Now that the header's components are simple one-liners, I can refactor the entire header... this includes the site's name and logo, search box, menu, etc... by default, the sign-in is also at the top.
  - Similarly, let's address the footer, including copyright, engine link (since I want to promote Kooboo), and sign-in button.

Views
So in the layouts, I've already described some of the views. I choose to use folders/namespaces to logically group some of the views. Here are the views so far:
  - Login.Status - Log in / sign out buttons, logic to distinguish between them
  - Login.Form - When logging in, this is the form which is displayed
  - Common.Search - the input box, button, and URL code
  - Common.Header - Site logo, name, top nav menu, and added later: the breadcrumb trail
  - Common.Footer - copyright, link to Kooboo, and login status
The old site has an iframe with a link to Artimis (which shows local traffic conditions)... so I'll create a view for that as well.
I spent a bit of time figuring out what I wanted as far as navigation goes... For a while, I was strongly considering a Windows Explorer-like menu (which combined the look and feel of a breadcrumb trail, with a list of peer and child nodes), since I thought it would provide quick navigation to deeply linked content. I finally decided (at least for now) that a breadcrumb trail is sufficient, and I will provide the nodes on pages, and try to minimize on the depth. But this also means I need a breadcrumb trail. I also found that I need a bit of custom logic to ensure that the trail showed links or labels appropriately, given that I use pages and URL routing differently on some of the pages.
Finally, I have views for content... things like a single blog post, a list of the recent blog posts, tag cloud, and archives.

Data Migration
Finally, the data... I'm still using the basic XML data provider, so I just need to write an XML file for the content.
I used a PowerShell script, which queried SharePoint using the Client Object Model, then created an XDocument to generate the output.
Since the titles have illegal URL characters, the URL routing uses the UserKey... I started with a simple function to replace illegal characters with underscores, but found that I wanted a little more control... I'll post the whole script soon enough.

I'll post actual code snippets soon enough.
Disqus: disqus comments
December 31
Last modified by sbrickey on 2/1/2012 11:46 AM

So you may have seen signs... i've been (very slowly) moving and consolidating websites to this new URL and site engine.

After about a month looking and testing, I've decided and settled on Kooboo as my site engine. My first websites were plain HTML and Classic ASP (some even had an access DB... ooh, ahh). For the past few years though I've been using SharePoint Services/Foundation, and it's been good.

So what changed?

I'm looking at external hosting, since I'm a little tired of the maintenance involved with my 2 Virtual Servers, File Server, a dozen VMs, and custom firewall rules. I've liked the idea of hosting myself because I always kept backup copies, but the web hosting sites these days provide a lot of nice options as well.

But let me tell you, SharePoint hosting is expensive (for a personal site).

My web hosting (via Arvixe) is less than $10/mo... no bandwidth concerns, no disk space concerns.

SharePoint would've cost me at least $30/mo... for maybe 5gb of disk space. I looked into a bunch of options, including cloud servers... but since SharePoint wants at LEAST 4gb of memory, VM hosting gets expensive too. Throw in some VPN routing (I was hoping to configure a WAN between two VM hosts and here, split my AD tree, etc) and the costs went up even higher... suddenly, the cost of running things at home was looking good again.

Further, with my home install, I am a SharePoint farm admin... full access to central admin, install anything i want, admin. With hosted options (Office 365 or others), I'm a site collection admin. At best I can play around in the sandbox API.

So I looked around at some other CMS options.
- Orchard - very popular, it's still in its infancy as far as I'm concerned, but the community seems to really like it and support it.
- Composite C1 - I liked this one, and it has some features that I've requested of Kooboo (metadata for content pages, for example)... but when using the admin console, I watched the browser's RAM usage hit 200+mb... wow...
- Umbraco - I've heard it's good, but I had trouble getting it going in a local VM, nevermind uploading it to a hosting company's FTP site.

Why Kooboo?
- Like others, it supports (and uses by default) an XML data provider... backups can't get much easier than copying a file... plus, my content migration from SharePoint just dumped the (transformed) contents into an XML file.
- Like Orchard, it makes heavy use of MVC (Razor), which makes coding quick and easy. It has a few helpers which seem odd at times to work with, but are relatively easy to get going in the end.
- You heard me, code... I get to log into the admin console, and adjust the code however I like... want to render the tag cloud differently, no problem. Perhaps you noticed that the blog theme copies SharePoint? Took me only a few hours.
- in-page editing.. yes, I can edit content without being in the admin console... arguably, I have to write the code to do that, but if you know where to look, you can reuse the code from the Admin console without too much effort :)
- Responsive forums... While the user community isn't exactly booming, I've always gotten a (good) response in the forums within a day or so. I have noticed, that in the past few weeks (while getting this all going and figured out), I've been one of the more popular posters :) 

 

So there you have it.

I plan to write more, including the (PowerShell) scripts I used to migrate the content, and the code I use in my data views... but for now, at least the blog is no longer in post limbo.

Disqus: disqus comments
Login