in education

Blog toolbox tutorial, part one


Blog toolbox Tutorial: Post search, Visit count and User profile

Welcome to first tutorial of our Blog toolbox plugin. Throughout our tutorials we will try to show you why is Blog toolbox great addition to your plugin set if you are using RainLab.Blog plugin on OctoberCMS. In this tutorial we will focus on Post search, Visit count and User profile components.

Post search component


If you ever wanted to add AJAX search on your website to allow users easy search and navigation of your posts, then this component will help you with that. Post search component is using OctoberCMS AJAX event handlers and in combination with partials you get instant list updates as user fills search input.

Component setup

In order to setup this component, create CMS page and inject the component. After that create a partial named "search-post-list" for example. Naming can be different but don't forget to adjust names from this tutorial.

Partial code example:

<ul>
    {% for post in post_search_result %}
    <li>
        {% if post.hasPostType('youtube_link') %}
            <a href="{{ post.getCustomPostType('youtube_link').value }}" target="_blank">
                <div class="res-post">
                    {% if post.featured_images.count %}
                        {% for image in post.featured_images|slice(0, 1) %}
                            <img class="card-img-top" src="{{ image.path }}" alt="{{image.description }}">
                        {% endfor %}
                    {% endif %}

                    <div class="res-post-desc">
                        <h5>{{ post.title }}</h5>
                        <p>{{ post.published_at | strftime('%d.%m.%Y')}}</p>
                    </div>
                </div>
            </a>                   
        {% else %}
            <a href="{{ 'post'|page({category: post.categories[0].name | lowercase, slug: post.slug}) }}" target="_blank">
                <div class="res-post">
                    {% if post.featured_images.count %}
                        {% for image in post.featured_images|slice(0, 1) %}
                            <img class="card-img-top" src="{{ image.path }}" alt="{{image.description }}">
                        {% endfor %}
                    {% endif %}

                    <div class="res-post-desc">
                        <h5>{{ post.title }}</h5>
                        <p>{{ post.published_at | strftime('%d.%m.%Y')}}</p>
                    </div>
                </div>
            </a>
        {% endif %} 
        </li>
    {% endfor %}
</ul>

As you can see from above it's simple list that displays posts returned by component. What is great about partials and AJAX event handlers is that this partial will automatically update list items when component returns data.

Now let's get back to CMS page where you injected this component. There are three things you need to add in order for this component to work. You need JS code to listen search input changes, this can be done with JQuery, element with text input and html container where you inject your partial.

CMS page with JQuery input listener, AJAX event handler and partial injection example:

<script>
    $(document).ready(function(){

        $("#search-field").on("keyup",function(){

            var search_term = $(this).val();
            $.request('onSearch', {
                data: {
                    search_term: search_term,
                },
                update:{ "search-post-list": "#search-list-holder" },
                complete: function(response) {

                }
            });
        });
    });

    <div class="modal fade" id="exampleModalPreview" tabindex="-1" role="dialog" aria-labelledby="exampleModalPreviewLabel" aria-hidden="true">
        <div class="modal-dialog" role="document">
            <div class="modal-content ">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                        <i class="far fa-times-circle"></i>
                    </button>
                </div>
                <div class="modal-body">
                    <div class="container">
                        <div class="modal-row row">
                            <div class="modal-col-1 col-lg-5">
                                <div class="form-group search-form">
                                    <!-- Search input -->
                                    <input type="text" class="form-control search-input" id="search-field" placeholder="Search">
                                </div>
                            </div>

                            <div class="modal-col-2 col-lg-6 hide-for-search">

                                <!-- Partial container -->
                                <ul id="search-list-holder">
                                    {% partial 'search-post-list' %}
                                </ul>

                            </div>                                                
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

</script>

So from this example you can see how everything is connected.
If you changed names for partial and maybe ID of the input, don't forget to change them if you copy this snippet.

Visit count component


This component will keep track of user visits on your posts. Inside backend you will have nice preview of visits in form of visual graph for last seven days and total number of visits per each blog post.


Component setup

This component will work out of the box when you inject it on your single post page. But if you need to customize anything let's see how it works and then you will know if you need to change anything for your website. This component also uses AJAX event handler to send data back to component.

CMS page code example for visit count:

<script>
    $(document).ready(function(){
        var visited_post_id = '{{post_id}}'; // post_id is comming from component, which is the ID of visited blog post
        var is_revisit = true;
        var existingCookie = document.cookie.match("postVisit"+visited_post_id);
        if(existingCookie == null){
            setCookie("postVisit"+visited_post_id,visited_post_id,2);
            is_revisit = false;
        }

        var visit_type;

        if(is_revisit)
            visit_type = "revisit";
        else
            visit_type = "visit";

        $.request('onVisit', {
            data: {
                visitType: visit_type,
                post_id: visited_post_id
            },
            success: function(response) {

            }
        });

        function setCookie(name, value, expiration_days) {
            var d = new Date();
            d.setTime(d.getTime() + (expiration_days*24*60*60*1000));
            var expires = "expires="+ d.toUTCString();
            document.cookie = name + "=" + value + ";" + expires + ";path=/";
        }
    });

</script>

This is all you need to put inside your CMS page as for code part and of course you need to inject component on the page.

As you can see this component uses Cookies to track visits and revisits from users. If you want to change cookie name or duration go ahead and change it as you see fit. If you are from EU keep an eye on GDPR because you might need to notify users about this cookie.

User profile component


Inside backend you will see additional fields on User accounts when you install Blog toolbox. This component will give you all that data to be displayed on your frontend. Important thing to note for this component is that it uses User slug as get parameter in CMS page slug.

Component setup

First create CMS page, name it something like User bio. Slug for this CMS page has to contain /:user-slug by default. In our example CMS page has slug: "/user-bio/:user-slug". You can change name of the get parameter inside component configuration if you wish. Also don't forget to inject component to user profile CMS page.

CMS page code example:

<section id="bio-intro">

    <div class="bio-row row">
        <div class="user-col col-xl-5 col-11">
            <div class="user-img">
                <img src="{{ userProfile.user.avatar.path }}">
            </div>

            <div class="user-intro">
                <h4>{{userProfile.user.getFullNameAttribute()}}</h4>
                <p>
                    {{userProfile.user.ideaverum_blogtoolbox_business_position}}
                </p>
            </div>

            <div class="user-bio">
                <p>
                    {{userProfile.user.ideaverum_blogtoolbox_bio}}
                </p>
            </div>
        </div>

        <div class="user-post-col col-xl-6 col-lg-10 col-md-11 col-sm-11 col-10">
            <div class="user-post-row row">
                {% set all_posts = userProfile.user.posts %}
                {% for post in all_posts|sort|reverse %}

                    <div class="post-list col-xl-4 col-lg-4 col-md-4 col-sm-6">
                        <div class="card">
                            {% if post.featured_images.count %}
                                {% for image in post.featured_images|slice(0, 1) %}
                                    {% if post.categories.count > 0 %}
                                        <a href="{{ 'post'|page({category: post.categories[0].name | lowercase, slug: post.slug}) }}" target="_blank"/>
                                    {% endif %}
                                        <img class="card-img-top" src="{{ image.path }}" alt="{{image.description }}">
                                    {% if post.categories.count > 0 %}
                                        </a>
                                    {% endif %}
                                {% endfor %}
                            {% endif %}

                            <div class="card-body">
                                {% if post.categories.count > 0 %}
                                    <a href="{{ 'category'|page({slug: post.categories[0].name | lowercase}) }}" target="_blank" style="display: inline-block; text-decoration: none;">
                                        <p class="bl-category">{{ post.categories[0].name }}</p>
                                    </a>
                                {% endif %}

                                <div class="post-tag">
                                    {% for tag in post.getTagList %}
                                        {% if tag != "" %}
                                            <a href="{{ 'tag'|page({tag: tag}) }}" target="_blank">
                                                #{{tag}}
                                            </a>
                                        {% endif %}
                                    {% endfor %}
                                </div>

                                <h3 class="bl-heading">
                                    {% if post.hasPostType('youtube_link') %}
                                        <a href="{{post.getCustomPostType('youtube_link').value}}" target="_blank">
                                            {{ post.title }}
                                        </a>
                                    {% else %}
                                        <a  href="{{ 'post'|page({category: post.categories[0].name | lowercase, slug: post.slug}) }}" target="_blank">
                                            {{ post.title }}
                                        </a>
                                    {% endif %}
                                </h3>

                                <div class="bl-social">
                                    <a href="https://www.facebook.com/sharer/sharer.php?u={{ 'post'|page({category: post.categories[0].name | lowercase, slug: post.slug}) }}" target="_blank">
                                        <i class="fab fa-facebook-f bl-facebook grow"></i>
                                    </a>

                                    <a href="https://twitter.com/intent/tweet?text={{ post.title }}%0D%0A{{ 'post'|page({category: post.categories[0].name | lowercase, slug: post.slug}) }}%0D%0A&hashtags={{ post.getTagList|join(',') }}" target="_blank">
                                        <i class="fab fa-twitter bl-twitter grow"></i>
                                    </a>

                                    <button class="copy-btn" data-clipboard-text="{{ 'post'|page({category: post.categories[0].name | lowercase, slug: post.slug}) }}">
                                        <i class="fas fa-share bl-share grow"></i>
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                {% endfor %}
            </div>
        </div>
    </div>
</section>

Oh, this one is a bit bigger than previous code snippets. This is because User profile component will give you all data saved on User profile and also give you list of blog posts created by that user. So we decided to put list of posts inside this example as well.

Inside this example you can also see some methods for custom post types which are available in Blog toolbox. We will talk more about custom post type feature in next tutorials.

Buy blog toolbox plugin - 13 USD

Next: Blog toolbox tutorial, part two