Homeflow api

View project onGitHub

The Homeflow API

Hi there and welcome to the Homeflow wiki.

Homeflow provides developers with everything they need to build exceptional property based websites. Within this Wiki we hope you will find all the information you need to get your agency or portal theme up and running.

You'll use the IDE you're familiar with plus all the developer tools you know and love such as: HTML, CSS, JavaScript and jQuery. You will make your own decisions on the plugins you or your customer requires and have complete design control over your new website from the word go.

As alluded to above, we're making the assumption that you're familiar with HTML, CSS etc. You will also need a basic working knowledge of Git and Liquid, though both are relatively simple to pick up as you go along.

Before we get started, there are three areas of the Homeflow API to be aware of as they will be mentioned throughout this documentation:

1) The Agency/Portal Admin
2) Hestia
3) Ctesius

You can consider The Agency and Portal Admin as the repository of all the information related to an agency or portal (essentially a CMS) - think properties, pages, content, images and more. Hestia (The Greek Goddess of the Heart) allows the querying of this information at lightning fast speeds. Finally Ctesius (The Greek God of Property) is our frontend application that is powered by Hestia. It includes an event management system, supports advanced property searching, user profiling and will support and augment the theme you will build.

All themes are developed using mostly HTML, CSS and JavaScript, but they also make use of the Liquid templating system, made popular by Shopify. Liquid sits in places of your code where dynamic content is required, such as property, agency, staff or user information.

We will explore all of the technologies mentioned above in much more detail later in the Wiki. In the meantime you can head straight over to the section you require using the menu on the right or move on to Getting started.

Getting started

Firstly we will need to initialise a Git repository for your theme on our platform and provide it with an API key so it can query Hestia and get the information it needs. You don't need to do anything here and it's a stage we will automate in the future.

Once we have done this we will create you an account on Gitlab (our internal Git platform) and you will receive an automatic email containing your login information. Before you can get to work on your theme, your will need to supply Gitlab with your Public SSH key. This is so you can clone and push/pull to and from your theme's repository. For information on how to fetch or generate your SSH key, visit this link.

This link will take you to the SSH section on Homeflow where you can paste in your key: http://hades.homeflow.co.uk/keys.

To clone your repository, open up a command prompt and cd to directory of your choice (but one that you won't forget and is easy to get to!) and run the following:

git clone <link>

Your Git clone URL will look something like: git@hades.homeflow.co.uk:fdesign/pantani.git.

By this point we should have provided you with a staging link so you/your team/your customer can view the development progress of the site. Once you have added your public key and you have successfully cloned your repository, we can now start to put your theme's folder structure together.

Theme folder structure

Ctesius themes use the standard Rails conventions for folder layouts. Don't worry if you're not familiar with Rails or this structure as an example theme folder structure can be seen below or found on the demo Stabilisers theme.

  • theme_name
    • agencies
      • index.liquid
      • show.liquid
    • articles
      • index.liquid
      • show.liquid
    • assets
      • images
      • javascripts
      • stylesheets
        • theme_styles.lcss - our LCSS file holds our styles just as a CSS file would
    • branches
      • _branches_list.ljson - a JSON view to list an array of branches
      • index.liquid
      • show.liquid
    • home
      • home.liquid - the main homepage
    • layouts
      • application.liquid - the main layout file which renders the other pages
    • pages
      • show.liquid
    • properties
      • _properties_list.ljson - a JSON view to list an array of properties
      • index.liquid
      • show.liquid
    • staff
      • index.liquid
      • show.liquid
    • user
      • new.liquid

If your repository doesn't have these files and folders yet, add them in. Once done, make your first commit and push to your repository. We would recommend a Git GUI such as Tower for OSx or one of the freebies available for Linux such as Git Cola. If you're using Git via the command line, bring up a terminal, cd to you theme's root directory and run:

git commit -a -m 'Theme folders and files'

Then run:

git push origin master

This will add all of your modified files, commit them as well as push them to the remote repository. We can't view the site online just yet as there's nothing to render. Time to add some structure and content to our application.liquid file.

The layout File

Every page render starts at the layout, which is always found in /layouts/application.liquid. This file will contain the html, head, and body tags and will define the overall 'makeup' of your site.

Take a look at the source in the Stabilisers theme and you will see there's a few things to note. Firstly we link to the default styles and JavaScripts using our first bit of Liquid syntax:

   {{ "application" | javascript_include_tag }}
   {{ "application" | stylesheet_link_tag }}

This will bring in all the default styles and some JavaScript libraries which form the basis of every Ctesius theme. As of the latest revision to this Wiki, they are:

  • Bootstrap
  • jQuery - v1.9
  • jQuery Cycle - 3.0.3 (11-JUL-2013)
  • jQuery Nivo Slider
  • Backbone.js - 1.0.0
  • Underscore.js - 1.4.3
  • leaflet.js
  • Font Awesome
  • the Homeflow search and profile system

You'll also notice that we make another call:

{{ "stabilisers" | theme_stylesheet_link_tag }}

There is a subtle different between the two Liquid directives in that the latter has a theme_ directive and the other does not. When you add a theme stylesheet or theme JavaScript directive, the theme will expect the quoted LCSS file to reside in /assets/stylesheets (/assets/javascripts for JavaScript files). LCSS files are pretty much CSS files run through a Liquid filter. This enables us to customise the CSS easily for different sites. The directive without the theme pointer will load something from the core app.

Continuing with the layout file the next command you'll see is:

{% include 'layouts/js_templates' %}

This is the first include that we've seen. A bit like PHP includes, Liquid includes allow you include a partial template. This is excellent for including blocks that occur on several pages, or, as we'll see later, in Liquid for loops. Important point: partial names must be preceeded by an underscore _. When you call the partial however, no underscore is required though you must put the path in quotes.

The directive here pulls in the default JavaScript templates for the dynamically drawn user sections of the site. They are all overridable so you can design them as you need to. We'll expand more on these templates later.

Perhaps the most important section for the layout is the helper content_for_layout. This outputs the rendered results of the rest of the template.

{{ content_for_layout }}

In other words, all other template parts are like partials that get evaluated and loaded into this section.

Finally we need to boot the Ctesius system to handle searches, user profiles and so on. To do that we add the following function:

 <script type="text/javascript">
      $(document).ready(function(){ 
        Ctesius.init()
      });
 </script>

This needs to be used after any Ctesius events or config options are set. Some theme developers choose to add this function at the foot of application.liquid whereas others will add it to a theme JavaScripts file, include the directive at the bottom of it, then include the partial.

Building up the layout

Other than the Liquid directives in the head section, the content_for_layout we saw earlier and the Ctesius boot, the application.liquid file is constructed just the same as a regular web page - add your classes and IDs, divs and CSS in the normal way and style them up using your theme CSS. Be sure to to put it in the stylesheets directive and make sure the names match up:

{{ "your_theme_css" | theme_stylesheet_link_tag }}

We talk more about images in the Working with images and assets section, but for now add your agency's/portal's logo into the /assets/images folder and reference it in your application markup as follows:

<img src="{{'your_image.png' | theme_image_url : "70x70" }}" />

You will notice that we have added a Liquid directive straight into the source of the image. Liquid will expect there to be an image of the name and extension quoted in your theme images folder (/assets/images). Also note that we've added an arguement on the end to ask Liquid to resize the image on the server, then send it to the browser - neat.

Running your theme

Now that you have hopefully added your HTML, some CSS and some images, it's time to see the results. Commit and push your changes and observe the staging URL we sent to you. It will look something like:

Agencies: http://agency_homeflow_domain.agent.staging.homeflow.co.uk
Portals: http://portal_homeflow_domain.search.staging.homeflow.co.uk

Hopefully you will see the result in the browser.

Building up the application and home page

At this point we can start to think about the elements that will be consistent to every page and those that will just reside on the home page. Those that are consistent to every page need to reside in the application.liquid file and would probably include:

  • The header
  • The navigation menu
  • The footer

The header with an agency or portal logo

A header would probably consist of the agency or portal logo, so let's take a first foray into the Agency or Portal admin, add the logo, then pull it out and plug it into the source using Liquid. Note that if you'd rather just add or keep your logo in /assets/images that's absolutely fine.

To get to your admin, you use a link similar to the two links outlined before:

Agencies: http://agency_homeflow_domain.homeflow.co.uk/admin
Portals: http://portal_homeflow_domain.portal.homeflow.co.uk/admin

We'll assume you have been provided with your login.

In the agency admin, go to Website/Appearance/Logos and upload your logo
In the portal admin, go to Configure/Appearance/Logos and upload your portal logo.

Now in your source, within your header tags or header div tags, add:

Agency:
<img src="{{ agency.logo | url_for_agency_logo }}" alt="{{agency.name}} logo" />

Portal:
<img src="{{ portal.website_logo | url_for_portal_logo }}" alt="{{portal.name}}" />

There's a couple of new things here - the first is we need a key when referencing an agency or portal logo (and many other things as we'll see) and we're using the agency and portal Liquid name tag to fill in the alt attribute of the image. As you become more experienced with your template(s) you will find there's some useful and sometimes innovative ways you can use tags.

The navigation menu

The navigation is another area where you have complete freedom of development - if you wanted to hardcode your menu and add/remove new or old items as they come along, that's absolutely fine. If however, you'd prefer to add the navigation tree to the agency admin and extract them using Liquid, that's fine too. Generally speaking portals add their menu to their source whereas theme designers or agency sites will tend to code the menu using Liquid, as their theme can then be deployed for multiple agencies.

Let's assume for now you would like to query the agency admin and therefore Hestia for the menu and, if it has been added, its sub-menu counterpart.

To start head over to your agency admin. Reminder: http://agency_homeflow_domain.homeflow.co.uk/admin

Then go to Website/Navigation. From here you can drag in menu items for existing pages or create custom links, e.g. to the /branches page for example. Once you've got your primary menu items, you can then edit each one and add sub-menu items.

Here's a code construct that would retrieve the primary menu items:

{% assign primary_menu = 'primary' | site_menu %}
<ul class="top-level">
    {% for menu_item in primary_menu.items %}
        <li class="top-level-li">
            <a href="{{ menu_item.url }}">{{ menu_item.name }}</a>
        </li>
    {% endfor %}
</ul>

Here we can see two new things: an assign statement and a Liquid for loop.

The assign statement allows you to assign something to a Liquid variable. This can be as simple as a string or result of some evaluation. Also, as seen above, you can assign Liquid objects to a variable for looping and other purposes.

And the good old for loop. You'll probably be familiar with the for loop from other programming languages and Liquid's variant is very simple to use - loop through an object and you can write out its attributes as required.

So what about a nested menu to support dropdrowns? Here's a code construct that will satisfy this requirement:

<ul class="top-level">
    {% assign primary_menu = 'primary' | site_menu %}
    {% for menu_item in primary_menu.items %}
        <li class="top-level-li">
            <a class="navigation" href="{{ menu_item.url }}">{{ menu_item.name }}</a>
            {% if menu_item.items != empty %}
                <ul class="subnav">
                    {% for sub_menu_item in menu_item.items %}
                        <li>
                            <a href="{{sub_menu_item.url}}">{{sub_menu_item.name }}</a>
                        </li>
                    {% endfor %}
                </ul>
            {% endif %}
        </li>
    {% endfor %}
</ul>

There's nothing new here but let's step through what's going on. Firstly we do our assign then we loop through the primary menu items and output an li and anchor for each. Next we check whether the menu item we're looping over has any items within it by doing an if not empty test - empty is a handy Liquid check to see if a variable has been initialised. If so, we output another unordered list and then we loop through the sub menu items of that menu item. We then output an li and anchor for each.

Finally we close off the if and for loop using the appropriate end Liquid statements.

The carousel

Many agency sites and indeed portals have what we refer to as a carousel on their home page. This is a gallery of images with text overlays designed to give the user an attractive initial presentation and to give your site the 'wow' factor. At the time of writing, many theme developers are using full width images within their carousel as well as responsive images to cater for different viewports.

Your first port of call is to head to the carousel section within the agency or portal admin. You can find that under Website/carousel. Don't worry about the settings for now but instead go straight to Add item. Keep the standard option selected and upload your background image, add a title, a standfirst (the chunk of accompanying text) and a link if required. The minimum you'll want is an image and standfirst.

Now return to you theme and the home/home.liquid file. This is one place, and a logical one, where we can add our carousel code:

<ul>
    {% for item in agency.carousel_items %}
        <li style="background-image:url({{item.image | url_for_generic_image}})">
            <div class="slide-content">
                <h2>
                    {% if item.link_url != empty %}
                        <a href="{{ item.link_url }}" class="link_url" title="{{item.title}}">
                    {% endif %}
                            {{item.title}}
                    {% if item.link_url != empty %}
                        </a>
                    {% endif %} 
                </h2>
                <p>
                    {{item.standfirst | truncate: 200}}
                </p>
            </div>
        </li>
    {% endfor %}
</ul>

There are numerous ways you can use the carousel - some themes have a small photo box whereas others go full width with the photos. In the example above, we are going full width with our carousel image set as a background against the li for each carousel item using a for loop. As you can see, we check if the link_url is empty and if not, we wrap out title with the link URL. We then output our standfirst and in this case we truncate it to two hundred characters (more on truncate in a bit). Note that we could run a check on standfirst but here we're assuming a standfirst has been entered for each carousel item.

Whilst we're covering the carousel and home.liquid it's worth pointing out that your carousel doesn't have to reside in home.liquid. Many front pages are split into different sections and sometimes it doesn't make sense for everything that is featured on the home page to be in home (by the way the home.liquid file is automatically included into the {{content_for_layout}} yield when on the site's root). On occasions where we want to include content on the home page but you'd rather add it to application.liquid, you can use the following statement:

{% if page.controller_name == 'home' %}
    *content*
{% endif %}

And for a handy reference as to what controller is being used, you can add the controller and action as a class to the body tag then inspect the source:

<body class="{{page.controller_name}} {{page.action_name}}">

The footer

Again, the footer is an area where you can hardcode in your design and content, or you can manage the content using the Agent/Portal admin and the content chunks we have made available. Content chunks are well-known content areas that get used on all sites - e.g. a header, footer, sidebar and so on. If we add some content to the footer chunk we can then render it out.

Agencies: Website/Content
Portals: Configure/Content/Pages

Both content chunk sections are then on the left. Checkout the Site footer chunk, add some filler content for now then save it.

Now, where you want to render the chunk in your source (if it's the footer then most likely the application.liquid page), use the following code:

{% content_block 'site_footer' %}
    {% if content_block %}
        {{content_block}}
    {% endif %}
{% endcontent_block %}

This will render your chunk. Note that you can add HTML syntax into the chunk using the MCE editor.

Working with property searches

This part of the Wiki will explore how we construct the forms and display search results - arguably the most important aspect of a property search website.

Constructing a property search form

Somewhere in your application layout file you're likely to have a property search form. There are a myriad of filter options you can show and a couple of methods of searching which we'll explore later, but for now is a basic form is below. Note we have stripped any classes or styling to keep it as simple as possible:

<form action='/search' id='search' method='get' />
    <input type="radio" name="channel" value="sales" id="buy"> <label>For Sale</label>
    <input type="radio" name="channel" value="lettings" id="let"> <label>To Let</label>       
    <button onclick="Ctesius.Actions.submitSearchForm(); return false;" type="submit">Search</button>
</form>

When the Ctesius.Actions.submitSearchForm() is called the system will evaluate every form on the page looking for elements with specified IDs and use them to construct a search. Here the only fields the user can select are whether it's a sales or lettings search. The results that will be shown with this type of form will be an index of an agency or portal's properties - this is where our /properties/index.liquid file comes in. More on that in the Displaying search results section.

Expanding on our first example, we can now add two addition fields that allow the user to type in a location. If it's recognised in our database, we'll load a number of suggested locations for the user to select. The hidden place ID field is a lookup reference for our system and is required in the source. With just two extra fields your property search will be completely supported by our geo location database:

<input type='hidden' id ='place_id' name='place_id' value='{{place_id}}' />   
<input placeholder='Town or Postcode' type="text" id='location' name="location" value="{{location_field}}"/>

Now let's add some beds and price bracket options:

<select id="min_beds" name='min_beds'>
    <option value="" title="Min beds">Beds min</option>
    <option value="1" title="1 Bed">1 bed</option>
    <option value="2" title="2 Beds">2 beds</option>
</select>
<select id="max_beds" name='max_beds'>
    <option value="" title="Max beds">Beds max</option>
    <option value="1" title="1 Bed">1 bed</option>
    <option value="2" title="2 Beds">2 beds</option>
</select>
<select id="min_price" name='min_price'>
    <option value="" title="Min price">Price min</option>
</select> 
<select id="max_price" name='max_price'>
    <option value="" title="Max price">Price max</option>
</select>

This is probably all self-explanatory. One thing to note is that the IDs and names are important so we can process the form.

Finally let's add a property type selector:

<select id="type" name='type'>
    <option value="" title="Property type">Property type</option>
    {{ agency | tag_dropdown_list }}
</select>

You will note a snippet of Liquid here. This code takes some pre-selected types in the Agency or Portal Admin and outputs them as options.

If it were a portal, you would use the portal tag instead:

{{ portal | tag_dropdown_list }}

Another option is to simply specify the types - the names here must match what we use as type references:

<option value="cottage" title="cottage">Cottage</option>
<option value="farmhouse" title="farmhouse">Farmhouse</option>
<option value="townhouse" title="townhouse">Townhouse</option>

Again you can style the form and position it in anyway you choose. Let's move on and see how we deal with property search results.

Displaying search results

After the form has been submitted, Ctesius and Hestia will go off an fetch the results - normally ten per page though this is configurable. A JSON response is returned and Liquid loops through and displays the results.

The first file we will want to get up and running is the properties/index.liquid file, which is located in the properties folder. The index displays every property belonging to an agency or portal. All searches are channel based so an index could show all sales or all lettings. On the whole the index will check whether the properties JSON is empty and if not, it will then include a _results.liquid partial, which is the main body of code that we'll be working with.

So the code could look something like this:

{% if properties == empty %}    
    <h4>Sorry, we couldn't find any properties for this search.</h4>
{% else %}
    {% include 'search/results' %}
{% endif %}

Note that in our example above we've put our results partial in a search sub-folder. You don't have to do this and your results partial could reside in the properties folder if you wish.

Our results partial will probably include top and bottom pagination, top and/or bottom search statistics, alternative view types (list, map, grid, etc) and of course the Liquid loop that displays the properties. Note that one, more or indeed all of the above can be partials so we can re-use them if necessary.

The properties loop

Since Liquid for loops can include a partial for each property it makes sense to include a tried, tested and mananageble partial of code for each property. We refer to this partial as _property_small.liquid.

Here's what our for loop looks like:

{% for property in properties %}
    {% include "properties/property_small" %}
{% endfor %}

As you can see we loop through the properties array and include the _property_small partial for each one. Let's now take a look at what a sample _property_small.liquid could look like:

<div id="property_{{property.property_id}}" class="property-small">
    {{ property | photo_overlay }}
    <a href="{{property | url_for_property}}">
        <img src="{{ property.photos.first | url_for_property_asset: "176x133" }}">
    </a>
    <a href="{{property | url_for_property}}">
        {{property.display_address | truncate : 60}}
    </a>
    <p>{{property.price}}</p>
    <p>{{property.short_description | truncate : 185}}</p>
    <p><a href="{{property | url_for_property}}">Full details</a></p>
</div>

Let's step through this. As you can see, we need to wrap each set of results in an ID or class container and we'll need to style up the elements so it renders nicely on a row. We'll assume you are okay with that and explain the Liquid.

Firstly, the ID of the div is property_{{property.property_id}}. As you can imagine, every property in our system gets assigned a numerical ID, and it's this ID that's used in URL and for other functions on the theme and in the backend. For now, we've outputted the ID so we can use our Save to shortlist function - more on that later.

The next tag {{ property | photo_overlay }} outputs the property status in the form of a sash image banner, or a custom banner of your choice. You can get to this custom images section via your agency or portal admin with the following link: /configure/website/appearance/custom_images.

The next piece of Liquid is: {{ property.photos.first | url_for_property_asset: "176x133" }}. As you might imagine, this looks at the photos array and gets the link of the first one for use in the image source. We then use the url_for_property_asset key and we pass in a size in pixels. As mentioned before, Liquid will resize the image on the server and send it to the browser.

A tag that's used numerous times in this code, as well as on many of the property related pages; {{property | url_for_property}} outputs the relative address to the property in question.

The next tag is {{property.display_address | truncate : 60}}. This tag outputs the display address of the property, which is the address as it comes in from the agency or portal feeds. A new arguement seen here is truncate. This is a very useful Liquid function, which is particularly useful when curtailing long addresses, descriptions and so on - just pass in the number of characters you want to keep. Another useful truncate function is truncatewords: 20. This does what it says on the tin.

Handling locations and postcodes

So far we have a basic properties index page and hopefully a set of results using the properties loop. How then, do we deal with locations like counties and towns as well as postcodes? This is where our include partial search/_results.liquid comes into its own. All we need to do is add three subfolders in the root directory: locations, counties and postcodes. Within these folders we simply need a show.liquid file and inside this file we add {% include 'search/results' %}. This will use our search results partial for the location searches - great. W now need to do now is make sure our page headings reflect the search that has been carried out. We may as well look at pagination whilst we're at it, too...

Headings and pagination

Headings need to reflect the search the user has carried out or it should output a very general statement if it's an index search. There's other considerations here; for instance if it is a county or postcode seach and so on. Some developers choose to add pagination statistics and pagination navigation together with search and location information, whereas others choose to seperate the two - you are free to find whatever suits you/your client. Note that the code below would be in search/_results.liquid. Here's an example heading:

<h1>
    {{search.dictionary.general_collective | capitalize}} 
    {{search.dictionary.preposition}} 
    {% if location %}in {{location.name}}{% endif %}
</h1>

This could output something like:

Houses for sale in Walton on Thames

The general_collective is the property type, the preposition is the channel (sales or lettings) and finally we do a location test and output the in and {{location.name}} if it comes back as true. Let's look at another example:

{{pagination.from_record}} to {{pagination.to_record}} of {{pagination.total_count}} 
Properties found 
{% if location %} 
    in {{location.name}}
{% endif %}
{% if pagination.has_prev_page %}
    | <a href="{{pagination.previous_page_link}}" class="pagination_prev">
        Previous {{pagination.page_size}}
      </a>
{% endif %}
{% if pagination.has_next_page %}
    | <a href="{{pagination.next_page_link}}" class="pagination_next">
        Next {{pagination.page_size}}
      </a>
{% endif %}

This might output something like:

1 to 12 of 31 Properties found in Walton on Thames | Next 12

This example starts with the from_record - if your page size is ten (which is the default) then this will go 1, 11, 21 etc. We then output the to_record, which again, if we're going up in tens, this would be 10, 20, 30 etc. We then output {{pagination.total_count}}, which outputs the item count.

Our next segment of code is two if statements that will output next and previous buttons or links if they are required. As you can see, we can get the next and previous links using the appropriate Liquid syntax as well as a handle on the page size, which is most likely going to be ten, but could be twelve, fourteen or whatever you or your client requires.

More on pagination

Ctesius ships with a for pagination function. If you've ever used Google before, and let's face it, who hasn't, then you'll be familiar with the pages before and after pagination they utilise at the foot of the search results.

This function takes a couple of arguements: previous and after.

These arguements allow you to define how many trailing and remaining pages to show. Here's the code:

 {% unless pagination.page_count == 1 %}
    <ul>
        {% if pagination.has_prev_page %}
            {% if pagination.page_count > 2 %}
               <li><a href='{{pagination.first_page_link}}'>&lt; First</a></li>
            {% endif %}
        {% endif %}
        {% for_pagination pagination previous: 4 after : 4 %}
            <li>
                <a href="{% if pagination_item.page_number == pagination.current_page %}#
                {% else %}{{pagination_item.link}}{% endif %}" 
                {% if pagination_item.page_number == pagination.current_page %} class='s'{% endif %}>
                {{pagination_item.page_number}}</a>
            </li>
        {% endfor_pagination %}
        {% if pagination.has_next_page and pagination.current_page != pagination.page_count %}
            {% if pagination.page_count > 2 %}
                <li>
                    <a href='{{pagination.last_page_link}}'>Last &gt;</a>
                </li>
            {% endif %}
        {% endif %}
    </ul>    
{% endunless %}

We start with the first unless statement we've seen. The unless statement will continue to output the code between the tags unless a certain condition is met. Here if page count is one, then we don't want to try and output the whole for block (incidentally this could be changed to an if/else and we could output the first page number to signify there's only one page). Next we use a couple of statments we've already seen and a Liquid tag we've not - {{pagination.first_page_link}} outputs the first page link.

Next we get into our for loop. First we give it a reference and then previous: 4 after : 4, meaning we want four trailing pages and four forthcoming pages (if they're available). The body of the for loop outputs the appropriate li anchor code for each. The final block in this code does a couple of checks to make sure there's pages remaining before outputting a helper last page link. We then close off our if statements and unless statements in the usual way.

Togglable areas

The core application javascript include comes with a JavaScript toggle system that can show and hide different tabs on a click. This is useful when working with different views and, as we will see later, it is also very useful on the property show page.

There are at least three well defined view types for the results page. They are list, grid and map. Here's what the code might look like:

<div id='togglable_list' class='togglable_area'>
    {% include 'properties/properties_list' %}
</div>
<div id="properties_grid_toggle_view" class="hidden togglable_area">
    {% include 'properties/properties_grid' %}
</div>
<div id='togglable_map' class='togglable_area hidden'>
    {% include 'properties/pagination_for_map' %}
    <div id='draggable_map_view'></div>
</div>

Then to link to the views, we simply use the hash reference in the anchor like so:

<li><a href="#home" class="active">LIST</a></li>
<li><a href="#grid">GRID</a></li>
<li><a href="#map">MAP</a></li>

Note that #home correlates to the default list view.

Working with the map view

Ctesius comes loaded with maps for property results, individual properties, branches and so on. A draggable map is at your disposal for property results. The draggable map view can be placed on the map tab outlined above and can also update the property results on the fly based on the user dragging or zooming. To get the map up and running, all you need to do is add the draggable_map_view div to your tab and supply it with a height and a width in your CSS:

<div id='draggable_map_view'></div>

Given that we're allowing the user to zoom and drag, it would be useful to have a dialogue of the current properties that are on display. It would also be useful to update this when the user interacts with the map. With Ctesius's built in event system, we can do just that.

Above the draggable_map_view div, you will see we have an include for map pagination. This is where we can add any map related dialogue we have. In our example below it's actually very little:

<span id='map_info'></span>
{% include 'properties/pagination_links' %}

Here we have an empty div ready to be populated with our dynamic map information and we have a partial to include the list, grid and map links that are common to all of the views. All we need to add to get the map information is two Ctesius events:

<script>
  Ctesius.registerEvent('before_draggable_map_updated',function(){
     $('#map_info').html('Updating map...')
  });

  Ctesius.registerEvent('draggable_map_updated',function(res){
     $('#map_info').html('Showing ' + res.properties.length + ' of ' + res.pagination.total_count + ' properties. Zoom in or drag the map to see more.' )
  });
</script>

Ctesius kicks events throughout the execution of a theme that we can conveniently get a handle on and perform some kind of action. In our example above, we register a callback function to execute when our draggable map events are kicked. By doing this we can add some nice feedback to the user. You can add these functions anywhere you like though many developers add them to the js_event_registers partial, which resides in the layouts folder.

Working with grid views

At the time of writing many agencies are integrating grid views into their property results. Grid views can sometimes produce a better visual offering than traditional list views and of course offers the user a degree of presentation flexibility. The easiest way to get up and running with a grid view is to have a properties_grid partial. Within this partial there is a second properties loop:

% for property in properties %}
    {% include 'properties/property_grid' %}
{% endfor %}

The partial property_grid then contains your individual grid view syntax. Since we're running another properties loop you might be forgiven to thinking we're fetching the properties again. Actually what we're doing is processing the same JSON that was returned from list view, so there is little or no overhead.

Saving properties to a shortlist

A common feature on property websites is saving properties to a semi or permanent storage with the appropriate widgets to link to or add/remove properties as the user sees fit. This kind of function is extremely useful on portals, but is also a nice feature for agencies to have.

Our first port of call is to use our first override partial so far. An override is simply a partial well-known to Ctesius that's in the core app ready for use. Sometimes core files can be used without needing to override them but in this instance we'll assume you want to modify the layout or at least have a degree of control over the partial.

In your js_templates folder, add the partial _saved_properties.liquid. In it we need a couple of constructs, the first is:

{% raw %}
    <script id="saved_properties_template" type="text/liquid">
        <div class="content box">
          <h3>My property shortlist</h3>
          <div class="shortlist_inner">
              <div id="property_shortlist" class="content-block"> 
                  <div id='favourite_property_list' class="content"></div>
              </div>
          </div>
        </div>
    </script>

Essentially this is our widget container. Here's the next construct:

    <script id="saved_property_template" type="text/liquid">
        <div id="mini_property_{{property.property_id}}" class="mini_property mini_property_{{property.property_id}}">
            <div style="position:relative;" class="clearfix">
                <img src="/liquid_assets/images/delete.png" style="display: none;" id="remove_icon_{{property.property_id}}"
                    class="remove_icon" onclick="javascript:Ctesius.Actions.removeSavedProperty({{property.property_id}})">
                <a href="{{property.property_url}}"><img src="{{ property.small_photo }}" class="shortlist_img"></a>
                <div class="featured_property_data">     
                    <a href="{{property.property_url}}">{{ property.bedrooms }} bedrooms</a><br />
                    <a href="{{property.property_url}}">{{ property.price }}</a><br />
                    <a href="{{property.property_url}}">{{ property.road_name | truncate : 20 }}</a>
                </div>
            </div>
        </div>
    </script>
{% endraw %}

This construct is the actual saved property record within the widget.

There's quite a lot here to digest but most of it we have touched on previously in the Wiki. The raw tag however, is one we haven't seen so far. Anything within the raw tag is ignored by the Liquid serverside parser. Instead it's sent back to the browser for (in our case) the application JavaScript to process. As you can see we output the property's ID in various places so we can get a handle on it.

Next we need to allow our widget to show up somewhere in your theme - typically a sidebar. Add the following code whereever you would like it to show:

<div id="saved_properties_view" class="hidden"></div>.

This will render the results of the script code seen above within the saved_properties_view div tags.

Next we need to output a save button or link on each property. To do that we add the following code to our _property_small.liquid that we worked with earlier in the Wiki (and the grid view if you have one):

<a class="shortlist_link_{{ property.property_id }}" 
onclick="Ctesius.Actions.addSavedProperty({{property.property_id}}); return false;">
    Add to Shortlist
</a>

The new code here is the Ctesius addSavedProperty action - essentially this saves the JSON representation of property's information to the browser's local storage for retrieval as and when required. It is now close to working but there's some events we need to add to js_event_registers to stitch it all together. The first is in an event to do some stuff when the view is rendered (i.e. when the user clicks the add button):

Ctesius.registerEvent('saved_property_view_rendered', function(saved_property){
    $('#saved_properties_view').removeClass("hidden");
    $(".shortlist_link_"+saved_property.id).html("Remove from Shortlist");
    $(".shortlist_link_"+saved_property.id).attr("onclick", 'Ctesius.Actions.removeSavedProperty('+saved_property.id+'); return false;');
    $('#mini_property_'+saved_property.id).mouseenter(function(){$('#remove_icon_'+saved_property.id).show()});
    $('#mini_property_'+saved_property.id).mouseleave(function(){$('#remove_icon_'+saved_property.id).hide()});
});

Firstly we remove the hidden class from our sidebar widget so it shows. We then target the link, change the HTML to Remove from Shortlist then we add a click event to remove the very same property. Our last two lines are jQuery callback functions that show or hide the visual aid signifiying the user can remove the property.

Saving searches

Saving searches is a tad quicker and easier to set up than the saved properties and they save in much the same way as the saved properties. They also make use of the events system to give the user some feedback. Here's the first bit of code you will need to add where you would like the link/button:

{% if location %}
    <li><a onclick="Ctesius.Actions.saveCurrentSearch();" id="save_search">SAVE SEARCH</a></li>
{% endif %}

Here you can see we have wrapped the link in a location if statement. You don't have to do this, though a saved search and the resultant email alerts to the user might not be very useful if the properties are not where they want to live or the location where they searched. Much like the saved properties function, here we call saveCurrentSearch(). We also give the link an ID so we can grab it in context using our events.

Speaking of events, here's the two we'll need to provide the user with some feedback:

Ctesius.registerEvent('saved_search_added', function(search, collection){
    if (search.equalTo(Ctesius.getSearch())){
        $('#save_search').html('REMOVE SEARCH');
        $("#save_search").attr("onClick", 'Ctesius.Actions.removeSavedSearch("'+search.search_id()+'");')
    }

    $('#saved_searches_link').effect('highlight',{color: '{{theme_preferences.accent_colour}}'}, 3000);
});

Ctesius.registerEvent('saved_search_removed', function(search, collection){
    if (search.equalTo(Ctesius.getSearch())){
        $("#save_search").html("SAVE SEARCH")
        $("#save_search").attr("onclick", 'Ctesius.Actions.saveCurrentSearch();')
    }
});

Again we're calling some Ctesius functions that will add or remove the saved search accordingly. We then set the display of the link and add the onclick events as needed. There's a couple of new statements used in this code that are worth a look:

$('#saved_searches_link').effect('highlight',{color: '{{theme_preferences.accent_colour}}'}, 3000);

The first is more a concept than anything else - the events that we can get a handle on are not only good for adding and removing properties, searches etc, but can also be used to provide visual aids to the user. Here the saved_searches_link in the user profile header (more on this later) is highlighted and then fades out to show the user that the search was saved and can be viewed/amended in their control panel.

The other new Liquid tag is {{theme_preferences.accent_colour}}.

Earlier we mentioned that LCSS files are run through a Liquid filter. If you're building a theme that will be rolled out to multiple agencies, or if you want to standardise the colour palette and manage it in one place, you can use the agency/portal admin to set the colours, then pull them out in Liquid using tags similar to the one above.

Draw a Map

Coming soon

The property show page

Outputting basic information

Many of the concepts we have used on the results partial, property small and elsewhere in the Wiki get used on the property show page as well. The goal of the show page is to show all of the property's photos, videos, its full description, floor plans, brochures, Energy Performance Certificates (EPC) and its location on a map (including a Street view), then channel all of this into a telephone or email lead to the agency.

Your first requirement is likely to be an output of the property's address, or at least part of it, and its price in a title:

<h1>{{property.display_address}}</h1>
<h2>{{property.price}}</h2>

Next you will probably want to output the photos of the property. Many developers use jQuery type photo sliders for displaying and navigating through the photos. Thankfully, by using the photos loop, this is relatively easy to achieve:

{% for photo in property.photos %}
    <img src="{{ photo | url_for_property_photo : "410x308"}}" height="308" width="410" />
{% endfor %}

And if you just wanted the property's main photo:

<img src="{{property.main_photo | url_for_property_photo : "410x308"}}" height="308" width="410" />

Next we might want to output the short description for the property:

{{property.short_description | truncate: 900}}

Then, to get the property's status (Sold, Let, etc), we can use:

<h4>{{property.status}}</h4>

Other property drops

{{property.available_on}}

If supplied, the date the property is available.

{% for feature in property.features %}
    {{ feature }}
{% endfor %}

A list of property features that can be outputted to bullet points.

{{property.description}}

The full description.

{{property.road_name}}

The property's road name.

{{ property.vox_number }}

Homeflow is able to generate recorded telephone numbers for properties. Doing a check on this drop can see whether one is available.

{{property.bedrooms}}

A numerical figure that sometimes comes in as zero or empty so needs to be checked.

{{property.bathrooms}}

As above.

{{property.reception_rooms}}

As per bedrooms.

{{property.property_ref}}

The agent's supplied property reference.

{% for floorplan in property.floorplans %}
    {% if forloop.first %}
        <a href="http://mr0.homeflow.co.uk/{{ floorplan.image }}" title="Floor plan">
            {% if property.floorplans.size == 1 %}
                View floor plan
            {% else %}
                View floor plans
            {% endif %}
        </a>
    {% else %}
        <a href="http://mr0.homeflow.co.uk/{{ floorplan.image }}" style="display:none;" title="Floor plan"></a>
    {% endif %}
{% endfor %}

The collection of floor plans. This loop could output the floor plans to a light box e.g Fancybox, Colorbox, etc.

{% for brochure in property.brochures %}
   <li><a href="{{ brochure | url_for_property_asset }}">Download brochure</a></li>
{% endfor %}

This for loop would output all the brochures supplied.

{% for epc_chart in property.epc_charts %}
    {% if forloop.first %}
        <a href="{{ epc_chart | url_for_property_asset }}">
            {% if property.epc_charts.size == 1 %}
                View EPC chart
            {% else %}
                View EPC charts
            {% endif %}
        </a>
    {% else %}
        <a href="{{ epc_chart | url_for_property_asset }}" style="display:none"></a>
    {% endif %}
{% endfor %}

As per above and floor plans.

Working with maps

Many developers employ the tabbing system for property maps so the user can cycle through the different types: road, streetview and satellite. The first requirement is some events:

<script>
    Ctesius.addConfig('small_map_element', 'contact_map')
    Ctesius.registerEvent('render_tab', function(tab_name){
        switch(tab_name){
        case 'streetview':
            {% gmap_for property as streetview in streetview %}
        break;
        case 'satellite':
            {% gmap_for property as satellite in satellite %}
        break;
        }
    });
</script>

The first line here adds our map to the contact_map ID or class (seen below). The render_tab event registers a callback function that renders the appropriate Google map depending on the tab clicked. We then have our anchors and togglable areas:

<a class="selected" id="tab_map" href="#tabs/map">MAP</a>
<a id="tab_streetview" href="#tabs/streetview">STREETVIEW</a>
<a id="tab_satellite" href="#tabs/satellite">SATELLITE</a>
<div id="togglable_map" class="togglable_area">
    <div id="contact_map"></div>
</div>
<div id="togglable_streetview" class="togglable_area hidden">
    <div id='streetview' class="google_map_container"></div>
</div>
<div id="togglable_satellite" class="togglable_area hidden">
    <div id='satellite' class="google_map_container"></div>
</div>

Submitting a lead

Email type leads are submitted to Homeflow and then on to the agent by ubiquitous web forms. Here's a simplified version:

 <form action="/properties/{{property.property_id}}/{{property.primary_channel}}/leads" method="post">
    <div id='form_error'></div>
    <label class="text" for="firstname">First name</label>
        <input id="firstname" name="lead_client[first_name]" type="text">
    <label class="text" for="surname">Last name</label>
        <input id="surname" name="lead_client[last_name]" type="text">
    <label class="text" for="email">Email</label>
        <input id="email" name="lead_client[email]" type="email">
    <label class="text" for="telephone">Telephone</label>
        <input id="telephone" name="lead_client[tel_home]" type="text">
    <label class="text" for="message">Message</label>
        <textarea id="message" name="lead[message]" rows="3"></textarea>
    <button type="submit">Send enquiry</button>
</form>

If you are familiar with forms, which we're sure you are, there will be nothing new here to you. The only requirements are the names of the fields as well as the form action. The form error div near the top is reserved for any lead sending errors. Once a lead is sent, a flash message can be displayed. We have a whole array of flash notices that are used depending on the response back from the server and to display the notices, all you need to do is and the alert location div to your application.liquid or on the subject page itself:

<div id='alert_location'></div>

Working with agencies

More commonly found on portals, the agency page acts as the top level holdall for the agency's branches, staff, contact details and so on. The Ctesius app comes with a URL pattern that can describe the agency, the branch and the staff, should you need it to. Let's look at what a typical agency page might contain.

The agency show page

Anything agency related will reside in the agencies folder in your Rails directory structure and your show page will follow the normal format of show.liquid. Let's start to build the page using the agency name, agency description and the agency's portal logo with a fallback to their default logo:

<h1>{{agency.name}}</h1>
{% if agency.portal_logo %}
    <img src="{{agency.portal_logo | url_for_agency_logo : "200x_"}}" />
{% elsif agency.logo %}
    <img src="{{agency.logo | url_for_agency_logo : "200x_"}}" />
{% endif %}
{{agency.description}}

Next, let's add a Google style map to our show page - note that this will show the branches belonging to an agency:

<div id='agency_map'></div>
<script>
    {% gmap_for agency.branches as roadmap in agency_map %}
</script>

Don't forget to give your map div a height and a width setting in your CSS.

If you would prefer to use a Leaflet style map:

<div id='branches_map'></div>
<script type="text/javascript">
  Ctesius.addConfig('branch_map_element', 'branches_map');
  Ctesius.addConfig('branches', {% include_as_json branches/branches_list %});
</script>

Now let's extract the branches of an agency, including their branch pic or logo, description etc:

{% for branch in agency.branches_ordered_alphanumerically %}
    {% include "branches/branch_small" %}
{% endfor %}

Note that we're ordering the branches alphabetically using a helper function and we're reusing our branch_small. Not only is this excellent reuse, but it means if your branch_small layout is sorted, it should work out of the box on you agencies pages. In addition, when you need to make a change to it, it will be reflected on all pages that use it.

Finally, let's see how we can extract some social media links from the CRM:

{% if agency.has_social_links %}
    <div class="agency_social">
        {% if agency.facebook_uri %}
            <a href="{{agency.facebook_uri }}" target="_blank"><img src="{{'facebook' | theme_image_url}}" width="32" height="32"></a>
        {% endif %}
        {% if agency.twitter_uri %}
            <a href="{{agency.twitter_uri }}" target="_blank"><img src="{{'twitter' | theme_image_url}}" width="32" height="32"></a>
        {% endif %}
        {% if agency.linkedin_uri %}
            <a href="{{agency.linkedin_uri }}" target="_blank"><img src="{{'linkedin' | theme_image_url}}" width="32" height="32"></a>
        {% endif %}
        {% if agency.googleplus_uri %}
            <a href="{{agency.googleplus_uri }}" target="_blank"><img src="{{'google-plus' | theme_image_url}}" width="32" height="32"></a>
        {% endif %}
    </div>
{% endif %}

This block of code first executes a has_social_links function - this checks whether any one of the standard social media slots has a value. If at least one does, it will execute the block. We then use individual agency drop checks to see if the URI is available and output a theme image linking to the URI if so.

Working with branches

Way back at beginning of the Wiki we outlined the Rails folder structure we must use in order to retrieve property, agency, branch information and so on. To retrieve everything related to a branch, to perform branch searches and generally build the template up, we need to work in our branches folder. Let's start with the index file.

The branches index

As we saw with the properties index, our index pages are called when no seach criteria is given and we just want a flat list of all properties, branches, etc. A branches index route would simply be: http://www.agency_domain.com/branches. On our index page we can add the following code:

{% if branches != empty %}
    {% for branch in branches %}
        {% include "branches/branch_small" %}
    {% endfor %}
{% endif %}

This construct is exactly the same as a properties loop seen before except we are referencing our branches collection as well as including a branch_small partial for each returned branch. A branch_small might look something like:

<div class="branch">
    {% if branch.agency.portal_logo %}
        <img src="{{ branch.agency.portal_logo | url_for_agency_logo : "150!" }}" />
    {% else %}
        <img src="{{ 'awaiting-image.png' | theme_image_url }}" />
    {% endif %} 
    <div class="details">
        <h3>
            <a href="{{ branch | url_for_branch}}">{{branch.name}} branch</a>
        </h3>
        <p class="content">
            {{branch.description | strip_tags | truncate : 250}}
        </p>
        <p>
            <a href="{{ branch | url_for_branch}}">More Information</a>
        </p>
    </div>
</div>

Here we are checking whether the agency's portal logo has been set and if it has, we output it in the source. Note you could use {{ branch.photo | url_for_generic_image: "150!" }} instead, which would retrieve the branch photo as set in the branches area of the agency or portal admin. This photo is normally a shop front image or something similar. As another alternative, you could use the overall agency logo: {{ agency.logo | url_for_agency_logo : '150!'}}. Note that we've got a backup coded in to output a standard holding image if no portal logo is available. This is a theme_image_url - you may remember theme images reside in /assets/images.

One new element here is the bang (explanation mark) after the width dimension. We've built in some nifty Liquid filters that give you some flexiblity when requesting images from the server. In this instance we're saying: get us the agency portal logo and resize it so the width and height are no more than 150 pixels. Other options here are to specify the dimensions exactly or specify a height or a width, then get Liquid to automatically set the other dimension whilst retaining the aspect ratio - we do this by using an underscore where the dimension you want to calculate would normally be, e.g: "150x_".

Next we're outputting the branch name and wrapping it in the URL for the branch. We then go on to output the branch description. Note that in some instances you might be able to guarantee that the description has been added, if you can't then you can wrap the output in an {% if branch.description %}. Note the truncate filter that we've seen before. There's also a new filter here called strip_tags. Along with strip_html and strip_links it strips the description of any unwanted HTML tags and links.

Not every theme will call for a branch_small partial to be used - some might just have the name of the branch and a link, whilst others might have the name, link and a map of the branches.

Working with branch maps

Homeflow supports a variety of map types which you can extend with custom pins, overlays and so on. The first and easiest map to set-up is the Leafleft.js map type. To get this up and running on the branches index page, start by adding:

<script>
    Ctesius.addConfig('branch_map_element', 'branch_map');
    Ctesius.addConfig('branches', {% include_as_json branches/branches_list %});
</script>

This is the first time we've seen a built-in Ctesius addConfig function - the first addConfig assigns our branch map to the element branch_map whilst the second outputs a dump of JSON branch information for use on the front end. Next we need to declare our div to apply the map to:

<div id="branch_map"></div>

Don't forget to give your map ID or class and height and/or width to get it to show up. This should be enough to get your Leaflet branch map showing with a pin for each branch. The map bubbles have a fairly basic presentation that's fine for most, but if you would like to customise your pin, you can override _branch_map_pin.liquid in your js_templates folder and customise the layout and styles as necessary. Note that you'll need the basic layout code in the override, if you don't have this, pop us a line or grab it from another theme if we've given you access.

If you would prefer a Google style map you still need the addConfig branches code seen before, but this time, instead of your addConfig branch_map, we need:

{% gmap_for agency.branches as roadmap in branch_map %}

This should then populate your branch_map div with a Google style map. Note that we're using the agency.branches drop to access the branches belonging to an agency.

Branch searching

If you're building a portal or larger agency theme, you will pleased to know that users can search for your branches using our geo location database. This comes complete with a handy AJAX style auto suggest location based on the town or city the user enters. To get up and running, first we need to add our form:

<form action="/branches" id="search" method="get"> 
    <input type="text" name="location" id="location" class="autocomplete_location">
    <input type='hidden' name='place_id' value='{{place_id}}' class='autocomplete_location_result' />
    <button value="Search" type="submit"></button>    
</form>

Next where you branch results need to be, we have:

{% if branches != empty %}
    {% for branch in branches %}
        {% include 'branches/branch_small' %}
    {% endfor %}
{% else %}
    <h1>Sorry, no branches were found.</h1>
{% endif %}

The output here will be an index of branches, but if we submit our form with a location, our branches controller will go off, fetch the results and display them in place of the index results.

Branch titles and pagination

Much like we saw earlier in teh Wiki with property results, it's useful to output some titles and especially useful to output pagination if the branch results are on several pages. There's a number of ways that you can deal with titles - here's one such example:

{% if location %}
    {% if location.name != ''%}Branches in {{ location.name }}{% if county %}, {{county.name}}{% endif %}{% endif %}
{% elsif county %}
    Branches in {{ county.name }}
{% elsif postcode %}
    Branches in {{ postcode.postcode }}
{% else %}
    Branches Index
{% endif %}

As you can see, the IF statement checks for the location or location type and outputs the appropriate heading between a heading tag. Remember that if you use the construct multiple times, you can add it to a partial if you wish.

The branches index results comes loaded with the same pagination figures and options we saw in the property results earlier. Here's an example construct:

Page {{pagination.current_page}} of 
{% if pagination.has_next_page %}
    {{pagination.total_count}}
{% else %}
    {{pagination.current_page}}
{% endif %}<br>     
{% if pagination.has_prev_page %}
    <a href="{{pagination.previous_page_link}}">&laquo; Previous page</a>
{% endif %}
{% if pagination.has_prev_page and pagination.has_next_page %} 
    - 
{% endif %}
{% if pagination.has_next_page %}
    <a href="{{pagination.next_page_link}}">Next page &raquo;</a>
{% endif %}

This might output something like:

        Page 2 of 178
« Previous page - Next page »

By running various IF checks, we can essentially figure out whether there's a next and/or previous page (for example) and format the output accordingly.

The branch show

Wherever we have a Liquid tag that looks like {{ branch | url_for_branch}} or if the branch URL is called, the branch show page will get called into action. As with all of the subjects, the branch show page is simply titled show.liquid and lives in the branches folder.

The branch show page supports many of the things we saw on the property show page - for instance, you could have a JavaScript based carousel showing branch photos, list the staff members belonging to a branch, output the branch description and so on. What information you fetch and how you display it will of course depend on your design, but let's run through the basics:

To get the branch name we use the branch drop and name: {{branch.name}}. Moving on, we can then fetch the branch description:

{% if branch.description_with_agency_fallback %}
    <p>
        {{branch.description_with_agency_fallback | strip_html}}
    </p>
{% endif %}

This nifty bit of code will try and get the branch description, but if it's empty, we'll get the agency description. Note that it is unlikely an agent will not have a description for their branch AND their agency, but it's always good practice to check. Notice also we're stripping the HTML as sometimes some links and other formatting gets added to the description which can wreak havoc on our layout.

Staff profiles are a whole subject unto themselves which we will look at later in the Wiki but for now, if you wanted to display some mini profiles with links to full profiles, or just give you branch pages the human touch, you could use something like:

{% unless branch.staff_profiles.size == 0 %}
    <div class="sm_staff_gallery">
        {% for staff_member in branch.staff_profiles limit:6 %}
            {% if staff_member.avatar %}
                <a href="{{ staff_member | url_for_staff_member}}" title="{{staff_member.name}}">
                    <img src="{{ staff_member.avatar | url_for_staff_profile_avatar : "40x40" }}" title="{{staff_member.name}}">
                </a>
            {% else %}
                <a href="{{ staff_member | url_for_staff_member}}" title="{{staff_member.name}}">
                    <img src="/liquid_assets/images/sp.jpg" height="40" width="40" title="{{staff_member.name}}">
                </a>
            {% endif %}
        {% endfor %}
    </div>
{% endunless %}

Everything here should be reasonably familiar to you, though note we've added a limit to our for loop so that a maximum of six profiles are extracted. Also note that we're referencing a fallback image directly. Whilst this is a quirk of this code copied from a theme, sometimes you might need to reference an image using its relative path. On the whole though, you can just use Liquid and theme_image_url.

Displaying branch properties

The branch properties page is one where we can thankfully reuse our property loops and pagination figures as seen on the property results. Alternatively we can make use of the tabbing facility and include a function that will return some recent sales or recent lettings. If you would like to get the full results and make use of pagination, we'll need to use the former of the two options as the properties method in the branches controller needs to run to return the results and pagination.

Typically branch pages will make use of our built-in tabbing facility as seen earlier for property results. To get the tabs up and running, you will need the following code:

<ul class="nav-menu nav-tabs">
   <li><a href="#tabs/summary" id="tab_summary" class="selected">Summary</a></li>
   <li><a href="#tabs/team" id="tab_team">Meet our team</a></li>
   <li><a href="#tabs/sales" id="tab_sales">Our sales</a></li>
   <li><a href="#tabs/lettings" id="tab_lettings">Our lettings</a></li>
</ul>

NB: the Ctesius app will take care of adding selected active classes to your tabs but you will need to style them.

Recent sales and lettings

Now we have our tabs, we can add the area that will be toggled on a click. Here's what a togglable sales tab and recent branch sales function looks like:

{% if branch.recent_sales_properties != empty %}
    <script>
      {% assign properties = branch.recent_sales_properties %}
      sales_property_for_config = {% include_as_json properties/properties_list %}.properties
    </script>
    <div id="togglable_sales" class="togglable_area clearfix hidden">
        <h2>Latest sale listings for {{branch.name}}</h2>   
        {% for property in branch.recent_sales_properties limit: 20 %}
          {% include "properties/property_small" %}
        {% endfor %}
    </div>
{% endif %}

Here we're making us of one of Ctesius's many built-in functions - this one gets recent properties that the branch has added or we have received via a feed. To get this up and running we literally call the function which returns the JSON we need. We then use the good ol' properties loop to output the results, whilst reusing our property_small. Note that we've wrapped this whole block in an IF statement that checks if the function returns empty. It would be good practice to hide the link to the tab if the function has no results too.

To get this up and running for lettings, just substitute any reference to sales for lettings.

All branch properties

If you would like to display all properties that belong to a branch with pagination, we'll need a properties.liquid file within the branches folder. Once you have this you can start to build up the layout. This will most likely be a hybrid of your properties result page and your branch show page so use partials where possible and reuse what you've already written.

To start with, you might want to add a title:

<h1>{{agency.name}} - {{branch.name}} branch - All {{current_channel}}</h1>

Not much new here but it's worth knowing that {{current_channel}} will return either a 'Sales' or 'Lettings' string. Next you might want some tabs that will enable the user to navigate around:

<ul class="nav-menu nav-tabs">
    <li><a href="{{branch | url_for_branch : branch.agency}}#tabs/summary" id="tab_summary">Summary</a></li>
    <li><a href="#tabs/team" id="tab_team">Meet our team</a></li>
    {% if branch.sales_branch_properties_count > 0 %}
        <li><a href="{{branch | url_for_branch : branch.agency}}/sales" id="sales">Our sales</a></li>
    {% endif %}
    {% if branch.lettings_branch_properties_count > 0 %}
        <li><a href="{{branch | url_for_branch : branch.agency}}/lettings" id="lets">Our lettings</a></li>
    {% endif %}
</ul>

The first two tabs here would be hidden tabs on the page that get selected and displayed on a click. The branch properties tabs however, need to go through the branch properties controller to get the results. All we need do here is specify the appropriate branch URL with /sales or /lettings. If you're developing a portal with agencies that have multiple branches, you will need to use {{branch | url_for_branch : branch.agency}} in your URL, else you can just use {{branch | url_for_branch}}.

Next we need to output the actual results that are returned from our request. Thankfully this is just the same as outputting our property results seen earlier so you will have your pagination header, property loop, property small output and footer pagination (if required).