Similar Topics in New Topic Creation

Go down

Tutorial Similar Topics in New Topic Creation

Post by Luffy on April 6th 2018, 3:47 pm

Similar Topics in New Topic Creation


This code allows the users to see a list of similar topics while they are writing the title. It is ideal for support forums in which almost all answers are already solved. Here's an example of how it looks like:


Everyone can adapt the code according to his/her forum. It can also be possible to adapt the code to any other version, although this would be up to the user, and this adaptation is not supported. By default the code works for phpBB3

Installation


ACP(Admin Control Panel) > Modules >  HTML & JAVASCRIPT > Javascript codes management > Javascript codes management
Position: All pages

Code:
   /* The following code is DOM-dependent. It may not work if you modify the posting structure in the forum templates */
 
    var FLRX = FLRX || {};
 
    FLRX.similarTopics = (function () {
 
        'use strict';
 
        let settings = { // default settings
            forums : [],
            searchIn : false,
            maxTopics : 5,
            wordMinLength: 4,
 
 
            autocomplete: false, // disable browser autocomplete from subject input
 
            /* Advanced settings */
            excludedCharacters : new RegExp(/[.,\/#!$%\^&\*¿?!¡;:{}\\=\-_`~"«“‘’”»()\[\]]/, 'g'), // The ignored characters from the topic title.
            dom : { // default settings (should work if left like this with unmodified templates)
 
                /* Search page */
                topicscontainer : '.forabg',
                topic          : 'dd.dterm',
                infocontainer  : '.span-tab',
                titlelink      : '.topictitle',
                userlink        : 'a[href^="/u"]',
                forumlink      : 'a[href^="/f"]',
                topicicon      : 'dl.icon',
 
                /* structure */
                visible        : 'visible',
 
                /* posting page */
                titleinput      : '#postingbox input[name="subject"]',
                inputcontainer  : 'dl',
 
                /* created elements */
                maincontainer  : $('<div />', { id : 'similarTopics' }),
                similartopiccont : $('<div />', { class : 'topic-container' }),
                loadingelm      : $('<div />', { class : 'spinner' })
                                    .append($('<div/>', { class:'double-bounce1' }))
                                    .append($('<div/>', { class:'double-bounce2' })),
                topicelmcont    : $('<div />', { class: 'topic' }),
                topicelmtitle  : $('<div />', { class: 'topic-title' }),
                topicdatacont  : $('<div />', { class: 'topic-data' }),
                topicflags      : $('<div />', { class: 'topic-flags' }),
                topiciconcont  : $('<div />', { class: 'topic-icon' }),
                topicstatus    : $('<div />', { class: 'topic-status' }),
                topicelminfo    : $('<div />', { class: 'topic-info' }),
                topicauthor    : $('<span />', { class: 'topic-author', text: 'by ' }),
                topicforum      : $('<span />', { class: 'topic-forum', text: ' in ' }),
                similarstitle  : $('<h4 />', { class: 'similarTopics-title', text: 'You may be interested in checking this topics before opening a new one' }),
 
            },
        },
 
        structure = {},
 
        request,
 
        debounce = function(cb, delay) {
            let timeout;
            return function(...a) {
                clearTimeout(timeout);
                timeout = setTimeout( _ => { timeout = null;  cb.call(this, ...a);  }, delay);
            };
        },
 
        /* transforms a UTF8-encoded URI into Windows-1252 */
        sanitizeURI = function(uri) {
 
            /* For some reason Forumotion uses Windows-1252 encoding in search URIs.
            This workaround will only fix issues with Spanish characters */
 
            return uri.replace(/%C3%91/g, '%D1') // Ñ
                .replace(/%C3%B1/g, '%F1') // ñ
                .replace(/%C3%81/g, '%C1') // Á
                .replace(/%C3%89/g, '%C9') // É
                .replace(/%C3%8D/g, '%CD') // Í
                .replace(/%C3%93/g, '%D3') // Ó
                .replace(/%C3%9A/g, '%DA') // Ú
                .replace(/%C3%9C/g, '%DC') // Ü
                .replace(/%C3%A1/g, '%E1') // á
                .replace(/%C3%A9/g, '%E9') // é
                .replace(/%C3%AD/g, '%ED') // í
                .replace(/%C3%B3/g, '%F3') // ó
                .replace(/%C3%BA/g, '%FA') // ú
                .replace(/%C3%BC/g, '%FC'); // ü
        },
 
        /* returns an object array (representation of topics) from a search URL synchronously */
        searchTopics = function(url, cb) {
            $.ajax({
                url : url,
            }).done(function(data) {
                let relatedTopics = [],
                    $forabg = $(settings.dom.topicscontainer, data);
                if($forabg.length) {
                    $forabg.find(settings.dom.topic).slice(0, settings.maxTopics).each(function() {
                      
                        let $this = $(this),
                        $topictitle = $this.find(settings.dom.titlelink),
                        $spantab = $this.find(settings.dom.infocontainer),
                        $forumlink = $spantab.find(settings.dom.forumlink),
                        $userlink = $spantab.find(settings.dom.userlink),
                        $topicicon = $this.closest(settings.dom.topicicon);
 
                        relatedTopics.push({
                            title  : $topictitle.text().trim(),
                            url    : $topictitle.attr('href'),
                            icon  : $this.css('background-image').slice(4, -1),
                            status : $topicicon.css('background-image').length
                                        ? $topicicon.css('background-image').slice(4, -1)
                                        : false,
                            forum  : {
                                name : $forumlink.text(),
                                url  : $forumlink.attr('href'),
                            },
                            user  : {
                                name : $userlink.text(),
                                url  : $userlink.attr('href'),
                            },
                        });
                    });
                }
 
                cb.call(this, relatedTopics);
            }).fail(_ => {let up; throw up || false});
        },
 
        /* returns an array with the words of a string that fulfil conditions of settings.excludedCharacters */
        getWords = function(str) {
            return str.trim().replace(settings.excludedCharacters, '').split(' ').filter(elm => elm.length >= settings.wordMinLength);
        },
 
        /* updates the similar topics DOM structure with the ones in the input array */
        updateDOM = function(arr) {
 
            structure.topiccontainer.empty();
 
            if(arr.length) {
                let docfrag = document.createDocumentFragment();
                $.each(arr, function(index, topic) {
 
                    let $topicTitle = settings.dom.topicelmtitle.clone(),
                    $topicContainer = settings.dom.topicelmcont.clone(),
                    $topicInfo      = settings.dom.topicelminfo.clone(),
                    $topicauthor    = settings.dom.topicauthor.clone(),
                    $topicstatus    = settings.dom.topicstatus.clone(),
                    $topicforum    = settings.dom.topicforum.clone(),
                    $topicflags    = settings.dom.topicflags.clone(),
                    $topicdata      = settings.dom.topicdatacont.clone(),
                    $topicicon      = settings.dom.topiciconcont.clone(),
 
                    /* link creation */
                    $topicLink  = $('<a />', { href: topic.url, text: topic.title }),
                    $forumlink  = $('<a />', { href: topic.forum.url, text: topic.forum.name }),
                    $authorlink = $('<a />', { href: topic.user.url, text: topic.user.name });
 
                    $topicicon.css('background-image', `url('${ topic.icon }')`);
                    topic.status && $topicstatus.css('background-image', `url('${ topic.status }')`);
 
                    $topicauthor.append($authorlink);
                    $topicforum.append($forumlink);
                    $topicTitle.append($topicLink);
                    $topicInfo.append($topicauthor, $topicforum);
                    $topicdata.append($topicTitle, $topicInfo);
                    $topicflags.append($topicstatus, $topicicon);
                    $topicContainer.append($topicflags, $topicdata);
 
                    docfrag.append($topicContainer[0]);
 
                });
                structure.topiccontainer[0].appendChild(docfrag);
              
            } else
                structure.maincontainer.removeClass(settings.dom.visible);
              
        },
 
        setLoadingStatus = function() {
            structure.loadingcontainer.addClass(settings.dom.visible);
            structure.recentstitle.removeClass(settings.dom.visible);
            structure.topiccontainer.removeClass(settings.dom.visible);
        },
        topicsRetrieved = function (){
            structure.loadingcontainer.removeClass(settings.dom.visible);
            structure.recentstitle.addClass(settings.dom.visible);
            structure.topiccontainer.addClass(settings.dom.visible);
        },
 
        searchAlgorithm = function(words, cb) {
            let params = {
                search_where    : settings.searchIn || `f${/\?f=(\d+)/.exec(location.search)[1]}`,
                show_results    : 'topics',
                sort_by        : 0,
                sort_dir        : 'DESC',
                search_terms    : 'all',
                search_keywords : words.join(' '),
            };
 
            searchTopics(`/search?${sanitizeURI($.param(params))}`, function(arr) {
                let relatedTopics = arr;
 
                if(relatedTopics.length < settings.maxTopics) {
                    params.search_terms = 'any';
                    searchTopics(`/search?${sanitizeURI($.param(params))}`, function(arr) {
                        let searchAnyWord = arr,
                        neededElms = settings.maxTopics - relatedTopics.length;
                        searchAnyWord = searchAnyWord.filter(elm => relatedTopics.find(e => e.url == elm.url) === undefined); // Ignore duplicates
                        relatedTopics = [...relatedTopics, ...searchAnyWord.slice(0, neededElms)];
 
                        cb.call(this, relatedTopics);
 
                    });
                }
              
                cb.call(this, relatedTopics);
 
            });
 
        },
 
        /* main function */
        searchSimilarTopics = function($title) {
            let words = getWords($title.val());
          
 
            if(words.length == 0)
                return;
 
            // for the first time, if it was hidden
            structure.maincontainer.addClass(settings.dom.visible);
 
            setLoadingStatus();
 
            searchAlgorithm(words, function(arr) {
                updateDOM(arr);
                topicsRetrieved();
            });
 
        },
        generateStructure = function($title) {
            let $similarTopics = settings.dom.maincontainer.clone(),
            $spinner = settings.dom.loadingelm.clone(),
            $topicsContainer = settings.dom.similartopiccont.clone(),
            $recentsTitle = settings.dom.similarstitle.clone();
          
            structure = {
                maincontainer    : $similarTopics,
                loadingcontainer : $spinner,
                topiccontainer  : $topicsContainer,
                recentstitle    : $recentsTitle,
            };
 
            $similarTopics.append($spinner, $recentsTitle, $topicsContainer);
 
            $title.closest(settings.dom.inputcontainer).after($similarTopics);
        },
        init = function(options) {
            $.extend(true, settings, options);
 
            let timeout,
            $title = $(settings.dom.titleinput);
 
            if(!settings.autocomplete)
                $title.attr('autocomplete', 'off');
 
            // append the basic dom structure (should be hidden by default with CSS)
            generateStructure($title);
 
            $title.on('keypress', debounce(function(e) {
                if(e.which !== 0)
                    searchSimilarTopics($title);
            }, 500));
 
        };
 
 
        /* API :-) */
        return {
            init : init,
        };
 
    })();
 
 
    !function() {
 
        const settings = {
            forums : [1,2,3,4,5,6,7], // Forum IDs (separated by comma) where the "Similar Topics" feature will be enabled. Set to true to enable the feature everywhere (not recommended).
            searchIn : '-1', // Where the searches will take place. Use -1 to search everywhere. If not set, it will search the forum where the topic is being created
            maxTopics : 5, // Maximum amount of topics shown
        };
 
 
        location.pathname == '/post' &&
        location.search.indexOf('&mode=newtopic') > -1 &&
        (settings.forums === true || settings.forums.some(id => location.search.indexOf(`?f=${id}`) > -1)) &&
        $(function() {
            FLRX.similarTopics.init(settings);
        });
 
    }();

Now add this CSS code to your forum

Code:
#similarTopics{width:500px;background:#E1EBF2;padding:5px 10px;border-radius:5px;margin:5px 0 0 10em}
#similarTopics,#similarTopics .spinner,#similarTopics .topic-container,#similarTopics .similarTopics-title{display:none}
#similarTopics.visible,#similarTopics .spinner.visible,#similarTopics .similarTopics-title.visible,#similarTopics .topic-container.visible{display:block}
#similarTopics .topic{display:flex;border-bottom:1px solid #fff;padding:5px 0;margin:5px 0}
#similarTopics .topic:last-child{border-bottom:none}
#similarTopics .topic-data{flex:1}
#similarTopics .topic-flags{align-items:center;margin-right:10px;position:relative}
#similarTopics .topic-icon{position:absolute;top:0;left:0;bottom:0;right:0;background:transparent 50% 50% no-repeat}
#similarTopics .similarTopics-title{border-bottom:1px solid #0076b1;color:#0076b1;font-size:.9em;margin:.5em 0;text-transform:uppercase}
#similarTopics .topic-status{width:27px;height:27px;background:transparent 0 0 no-repeat}

At the beginning of the code there are several lines between brackets in the const settings definition. This is where you will be able to add several configuration lines (the lines that are already in the code are ones by default I have added). Next I will add a bit of a documentation to know what settings can you add.

Documentation



Here are all the settings you can add to the script. Please note all the options have the same format:

Code:
name: value,

That means if you wanna add several options, the beginning of the script should look (more or less) like this:

Code:
const settings = {
    nameOption1: value,
    nameOption2: value,
    nameOption3: value,
};

WARNING: To add an option with its default value it's the same as not to add it.

The options are the following:


  • forums



    When creating a topic in the forums the functionality will appear. A forum will be represented by its ID (the URL of the forum). The IDs will be between brackets separated by commas. For example:

    Code:
    forums: [1, 2, 3, 4, 5],

    Special cases:

    • []: Not turning on the feature in any forum (useless and self-defeating).
      Code:
      forums: [],


    Default value:
    Code:
    forums: [],

  • searchIn



    The forum or category where the search will be done. To identify which forum or category will be done, an ID will be used which is added after a  "c" for categories or a "f" for forums (between quote marks). For example, to search in the category with id 3, it would be:

    Code:
    searchIn: 'c3',

    There are special values:


    • false: Search in the same forum where the topic is being created.
    • '-1': Seach in all forum

    Value by default:
    Code:
    searchIn: false,

  • maxTopics



    Number of topics what will show. The minimum value is 3:

    Example:

    Code:
    maxTopics : 10,

    Value by default:
    Code:
    maxTopics : 5,


  • autocomplete



    Default value:

    Turn on or off the autocomplete of the field of the title (the suggestions of the browser). That is, to put the attribute autocomplete of the element input to on or off

    You can use the value "true" to be on, or "false" to not be.

    Example:

    Code:
    autocomplete : true,

    Default value:
    Code:
    autocomplete : false,

  • wordMinLength



    A minimum number of characters of a word to be taken into account in the search. Please note that because of the Forumotions limitations, it isn't possible to put less than 3.

    The value is a full number that can be between 4 to the maximum numbers of characters allowed by Forumotion.

    Example:

    Code:
    wordMinLength : 5,

    Default value:
    Code:
    wordMinLength : 4,



There also exists options for advanced users. To use them it is neccesary to have JS and HTML knowledge.

Advanced options:


  • excludedCharacters



    Regular expression object that matches the characters to be ignored in titles.

    Default value:
    Code:
    excludedCharacters : new RegExp(/[.,\/#!$%\^&\*¿?!¡;:{}\\=\-_`~"«“‘’”»()\[\]]/, 'g'),

  • dom



    Custom object that allows you to modify the way in which the DOM of the forum is interpreted. Useful if you want to modify the HTML structure of the plugin or if you have modified parts of the templates which the plugin depends on.

    You can see all the options in the default value, which would correspond on how a phpBB3 forum structure is interpreted. Adapting it to any other version should be easy. In order to have an easier adaption to any type of template modifications, the relationships between the elements are all strictly parental (i.e.., there is no possibility that any of the DOM identifiers of this object are the same) and the way they are related parentally is of the deep type (i.e. they don't need to be children or direct parents between them - the plugin implementation uses the find and closest methods to select the elements).

    It is not necessary to put all the object properties that appear in the default value, only those that are different from those of the object in the default value.

    Default value:
    Code:
    dom : {
     
                /* Search page */
                topicscontainer : '.forabg',
                topic           : 'dd.dterm',
                infocontainer   : '.span-tab',
                titlelink       : '.topictitle',
                userlink        : 'a[href^="/u"]',
                forumlink       : 'a[href^="/f"]',
                topicicon       : 'dl.icon',
     
                /* structure */
                visible         : 'visible',
     
                /* posting page */
                titleinput      : '#postingbox input[name="subject"]',
                inputcontainer  : 'dl',
     
                /* created elements */
                maincontainer   : $('<div />', { id : 'similarTopics' }),
                similartopiccont : $('<div />', { class : 'topic-container' }),
                loadingelm      : $('<div />', { class : 'spinner' })
                                    .append($('<div/>', { class:'double-bounce1' }))
                                    .append($('<div/>', { class:'double-bounce2' })),
                topicelmcont    : $('<div />', { class: 'topic' }),
                topicelmtitle   : $('<div />', { class: 'topic-title' }),
                topicdatacont   : $('<div />', { class: 'topic-data' }),
                topicflags      : $('<div />', { class: 'topic-flags' }),
                topiciconcont   : $('<div />', { class: 'topic-icon' }),
                topicstatus     : $('<div />', { class: 'topic-status' }),
                topicelminfo    : $('<div />', { class: 'topic-info' }),
                topicauthor     : $('<span />', { class: 'topic-author', text: 'por ' }),
                topicforum      : $('<span />', { class: 'topic-forum', text: ' en ' }),
                similarstitle   : $('<h4 />', { class: 'similarTopics-title', text: 'Quizás te interese...' }),
     
            },



avatar
Luffy
Manager
Manager

Male Posts : 5787
Reputation : 648
Language : Greek, English
Location : Cyprus, Greece

https://www.crossroadz.net

Back to top Go down

Tutorial Re: Similar Topics in New Topic Creation

Post by SarkZKalie on September 20th 2018, 1:23 pm

The tutorial has been updated, included some new feature below :

Added
  • PhpBB2, PhpBB3, PunBB, Invision and ModernBB as supported versions.
  • Latest version 1.1.2 by Flerex
  • Search topics in the whole forum
  • Max topics increased up to 50, from 5
  • Vertical scroll bar

Note:
It may not work on Microsoft Edge.
Apply only for default search_results_topics template.

Installation

1) Add this to your CSS

Code:
#similarTopics{height:150px;width:600px;background:#E1EBF2;padding:5px 10px;border-radius:2px;margin:5px auto;overflow-x:hidden}
#similarTopics,#similarTopics .spinner,#similarTopics .topic-container,#similarTopics .similarTopics-title{display:none}
#similarTopics.visible,#similarTopics .spinner.visible,#similarTopics .similarTopics-title.visible,#similarTopics .topic-container.visible{display:block}
#similarTopics .topic{display:flex;border-bottom:1px solid #fff;padding:5px 0;margin:5px 0}
#similarTopics .topic:last-child{border-bottom:none}
#similarTopics .topic-data{flex:1}
#similarTopics .topic-flags{align-items:center;margin-right:10px;position:relative}
#similarTopics .topic-icon{position:absolute;top:0;left:0;bottom:0;right:0;background:transparent 50% 50% no-repeat}
#similarTopics .similarTopics-title{border-bottom:1px solid #0076b1;color:#0076b1;font-size:.9em;margin:.5em 0;text-transform:uppercase;display:block}
#similarTopics .topic-status{width:27px;height:27px;background:transparent top center no-repeat}
#similarTopics .topic-title{display: inline-block;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;width:500px}

2) Host yourself a new one and you should let it works on the all page OR put this script in Admin Control Panel -> Display -> Templates -> Post & PMs -> posting_body template, before {SCEDITOR}.

Code:
<!-- Similar topics for Forumotion version PhpBB2 -->
<script src="http://xenomorph.forumotion.com/17239.js" type="text/javascript"></script>
Code:
<!-- Similar topics for Forumotion version PhpBB3 -->
<script src="http://xenomorph.forumotion.com/16475.js" type="text/javascript"></script>
Code:
<!-- Similar topics for Forumotion version PunBB -->
<script src="http://xenomorph.forumotion.com/12450.js" type="text/javascript"></script>
Code:
<!-- Similar topics for Forumotion version Invision -->
<script src="http://xenomorph.forumotion.com/14113.js" type="text/javascript"></script>
Code:
<!-- Similar topics for Forumotion version ModernBB -->
<script src="http://xenomorph.forumotion.com/13795.js" type="text/javascript"></script>
3) Don't forget to publish your new template.

Enjoy

This tutorial was created by Flerex
Special thank to Sr.Smith from the Spanish Support Forum - who helped to convert this great script to PhpBB2 and Invision.


avatar
SarkZKalie
Support Moderator
Support Moderator

Male Posts : 968
Reputation : 159
Language : English

http://rotavn.forumotion.com

Back to top Go down

Back to top


 
Permissions in this forum:
You cannot reply to topics in this forum