Articles

Responsive ModX Commenting with Quip

Mike Harvey

Article Header

Details on the recent refactoring some key elements of ModX Quip Commenting AddOn for responsive design and simpler user experience

Background

I offered to document the changes I have made to ModX Quip which is a nice commenting addon for ModX CMS, but is lacking in some useability features compared to Wordpress and others. My goals were to make it responsive (using bootstrap 3.0) and quicker with more inline functions and fewer postbacks to the server. I am still working on integrating reCaptchav2, but to do it in an unobtrusive way without hacking the existing quip recaptcha functionality. I also tested some markdown functionality of the Comments textarea using TinyMCE, and Markdown-It, but decided against it for several reasons and scrapped the markdown idea.

Included here are all the custom chunks, scripts, and styles I used in the process with as much explanation I think necessary for a reasonably experienced ModX developer. Much thanks to the awesome ModX Community for their input and support not only for this work, but for the past several years.

Platform and Versions

As of the writing of this, all the relevant versions are the latest stable versions available:

  • Platform: ModxCLoud
  • Version: Revo 2.3.5pl
  • Jquery: 2.0.2
  • Bootstrap: v3.3.5 with all components
  • Articles: 1.7.11-pl
  • Quip: 2.3.3-pl
  • getResources: 1.6.1-pl

Without further ado, see the tabs below for each component of the solution.

Templates and Chunks

All of the formatting for the following chunks is using Bootstrap 3.0 css and components to enable a responsive layout. The addtional css required will be shown in the next section.
  1. CommentsContainerTPL chunk

    Chunk to include at the bottom of your article template to show the comments for that article:
    [[!Quip@CMHQuip? &thread=`article-[[*id]]`]]
    
    <hr />
    
    <a id="post-a-comment" class="post-comment replyTo main-comment-form" data-pid="0" data-toggle="collapse" href="#comment-form" aria-expanded="false" aria-controls="comment-form"><h2 style="margin-bottom:20px">Add a Comment</h2></a><br /><br />
       <div class="row">
               <div class="col-sm-12">
                       <div id="comment-form" class="post-comment-body collapse">
                              [[!QuipReply@CMHReply? &thread=`article-[[*id]]`]]
                       </div>
                </div>
        </div>
                    
  2. CommentsWrapper chunk

    Set this as your Comments Wrapper Chunk in the Articles>Comments>Display tab:
    <div class="quip">
        <h3> [[%quip.comments]] ( [[+total]])</h3>
        
        <div id="quip-topofcomments- [[+idprefix]]"></div>
    
        [[+comments:notempty=`
        [[+comments]]
        `]]
    
        [[+pagination]]
    </div>
                    
  3. MediaCommentTpl chunk

    Chunk for each comment. This is formatted as a Bootstrap media compenent which handles nesting very nicely for threaded comments. Set this as your Comment Chunk in the Articles>Comments>Display tab:
    <div class="media" id="[[+idprefix]][[+id]]">
        <div class="author-container pull-left">
        [[+gravatarUrl:notempty=`<img src="[[+gravatarUrl]]" class="media-object" />`]]
        <span class="desc">
            <span class="author_name">[[+authorName]]</span>
        </span>
        </div>
        <div class="media-body">
            <span class="small pull-right"><i>Posted On: [[+createdon]]</i></span><br />
            <p>[[+body]]</p>
            [[+replyUrl:notempty=`<a role="button" href="[[+replyUrl]]" data-pid="[[+id]]" class="replyTo btn btn-warning btn-sm"><span class="glyphicon glyphicon-share-alt"></span>Reply</a>`]][[+approved:if=`[[+approved]]`:is=`1`:then=``:else=`- <em>[[%quip.unapproved? &namespace=`quip` &topic=`default`]]</em>`]]<span class="quip-comment-options">[[+report]][[+options]]</span>
            [[+children:notempty=`[[+children]]`]]
        </div>
    </div>
                        
  4. addCommentTpl chunk

    Set this your Reply Form Chunk in the Articles>Comments>Display tab:
    <div id="preview-div" style="display:none;">
        <div class="media">
            <div class="pull-left">
                  <img id="gravpreview" src="" class="media-object" />
                  <span class="desc">
                      <span class="author_name"><span id="authorName"></span></span>
                  </span>
            </div>
            <div class="media-body">
                <p><span id="comment"></span></p>
                <br class="clear" />
            </div>
        </div>
    </div>
    <div class="alert alert-success" role="alert" style="display:none;">
        <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">x</span></button>
         [[+successMsg]]
    </div>
     
    <form id="quip-add-comment- [[+idprefix]]" action=" [[+url]]#quip-comment-preview-box- [[+idprefix]]" method="post" role="form" class="comment-form">
    <div class="row">
        <input id="idprefix" type="hidden" name="idprefix" value=" [[+idprefix]]" />
        <input type="hidden" name="nospam" value="" />
        <input type="hidden" name="thread" value=" [[+thread]]" />
        <input type="hidden" name="parent" value=" [[+parent]]" />
        <input type="hidden" name="auth_nonce" value=" [[+auth_nonce]]" />
        <input type="hidden" name="preview_mode" value=" [[+preview_mode]]" />
     
         <div class="form-group col-sm-12">
            <input class="form-control" type="text" name="name" placeholder="Name" id="quip-comment-name- [[+idprefix]]" value=" [[+name]]" />
            <span class="help-block" style="display: none;">Please enter a valid name.</span><span class="error"> [[+error.name]]</span>
        </div>
        <div class="form-group col-md-12">
            <input class="form-control" type="text" name="email" placeholder="Email" id="quip-comment-email- [[+idprefix]]" value=" [[+email]]" />
            <span class="help-block" style="display: none;">Please enter a valid e-mail address.</span><span class="error"> [[+error.email]]</span>
        </div>
        <div class="form-group col-md-12">
            <input class="form-control" type="text" name="website" placeholder="Website" id="quip-comment-website- [[+idprefix]]" value=" [[+website]]" />
            <span class="help-block" style="display: none;">Please enter a valid website address.</span><span class="error"> [[+error.website]]</span>
        </div>
     </div>
    <div class="row">
        <div class="col-md-12 form-group ">
            <div class="checkbox col-lg-4 col-md-4 col-sm-12">
                <label for="quip-comment-notify- [[+idprefix]]">
                     <input type="checkbox" value="1" name="notify" id="quip-comment-notify- [[+idprefix]]"  [[+notify:if=` [[+notify]]`:eq=`1`:then=`checked="checked"`]] />
                      [[%quip.notify_me]]                
                </label>
                <span class="error"> [[+error.notify]]</span>
            </div>
            
            <div class="col-lg-8 col-md-8 col-sm-12 recaptcha pull-right">
                 [[+quip.recaptcha_html]]
                <span class="error"> [[+error.recaptcha]]</span>
            </div>
        </div>
    </div>
    <div class="row">
        <div class="col-md-4 col-md-offset-8 help-block"><span class="float-right small"> [[%quip.allowed_tags? &tags=` [[++quip.allowed_tags:htmlent]]`]]</span></div>
    </div>
    <div class="row">
        <div class="form-group col-md-12">
            <textarea name="comment" class="form-control" id="quip-comment-box- [[+idprefix]]" rows="5" placeholder=" [[%quip.comment_add_new]]" > [[+comment]]</textarea>
            <span class="help-block" style="display: none;">Please see allowable tabs above.</span><span class="error"> [[+error.comment]]</span>
        </div>
    </div>
    <div class="row">
         <div class="form-group col-md-12">
            <button id="preview-btn" type="submit" name=" [[+preview_action]]" value="1" class="btn btn-warning btn-sm" style="display: inline-block; margin-top: 10px;"> [[%quip.preview]]</button>
            [[+can_post:is=`1`:then=`<button type="submit" name=" [[+post_action]]" value="1" class="btn btn-success btn-sm" style="display: inline-block; block; margin-top: 10px;"> [[%quip.post]]</button>`]]
            <button type="button" class="reply-cancel btn btn-danger btn-sm pull-right" style="display: inline-block; margin-top: 10px;">Cancel</button>
         </div>
    </div>
                        
  5. CommentOptionsTpl chunk

    Set this as your Comments Options Chunk in the Articles>Comments>Display tab:
    [[+allowRemove:notempty=` <a href="[[+removeUrl]]" id="remove-btn" class="btn btn-warning btn-xs pull-right" style="display: inline-block; margin-right: 5px; font-size:10px;"><span class="glyphicon glyphicon-remove-sign"></span>&nbsp;[[%quip.remove]]</a>`]]
                        
  6. ReportasSpamTpl chunk

    Set this as either an option &tplReport on your QuipQ snippet call, or (what I did) create a new Property set to hold all my settings:
    <span class="pull-right">
        [[+reported:empty=`<a href="[[+reportUrl]]" id="report-btn" class="btn btn-danger btn-xs pull-right" style="display: inline-block; padding-left:5px; font-size:10px;"><i class="fa fa-comment"></i>&nbsp;Report Spam</a>`]]
        [[+reported:notempty=`<span class="bg-danger small text-danger" style="padding:2px;">[[%quip.reported_as_spam]]</span>`]]
        </span>
                        
  7. SideBar chunk

    Used on article template pages:
    <div class="well">
        <h4>Latest Posts</h4>
        <div class="row">
            <div class="col-lg-12">
                <ul class="list-unstyled icon-list article">
                  [[getResources?
                    &parents=`110`
                    &depth=`2`
                    &limit=`5`
                    &sortby=`{"createdon":"DESC"}`
                    &tpl=`latestArticlesTpl`
                  ]]
                </ul>
            </div>
        </div>
    </div>
    <div class="well">
        <h4>Latest Comments</h4>
        <ul class="list-unstyled icon-list comment">
            [[!QuipLatestComments? &tpl=`latestCommentsTpl` &limit=`5`]]
        </ul>
    </div>
                        
  8. latestArticles chunk

    Set this as the &tpl option on the getResources snippet call. See (Sidebar chunk above):
    <!-- blog entry [[+idx]] -->
        [[!getAuthorInfo? &userId=`[[+createdby]]` &prefix=`user.`]]
        <li class="blog-list br-gray"><a href="[[~[[+id]]]]">[[+pagetitle]]</a>
            <span class="small">-<a class="sm-blog-author" href="mailto:[[+user.email]]">[[+user.fullname]]</a><br />
            <span class="small glyphicon glyphicon-time"></span> Posted on [[+publishedon:strtotime:date=`%a %b %e, %Y`]]</span>
    
        </li>
    <!-- end blog item -->
                        
  9. latestComments chunk

    Set this as the &tpl option on the QuipLatestComments snippet call. See (Sidebar chunk above):
    <!-- comment entry [[+idx]] -->
        <li class="comment-list br-gray"><span class="glyphicons glyphicons-comments"></span>&nbsp;<a href="[[+url]]">[[+body:ellipsis=`20`]]</a><br />
        <span class="small glyphicon glyphicon-time"></span> Posted on [[+ago:strtotime:date=`%a %b %e, %Y`]]</span>
        </li>
        <!-- end comment -->
                        

Snippets and Code Changes

Custom Snippets and code changes are detailed below:
  1. getAuthor snippet

    Custom snippet used to get author details for sidebar (see call in Sidebar chunk above):
    
        $userId = $modx->getOption('userId',$scriptProperties,false);
        if (empty($userId)) return '';
         
        /* get user and profile by user id */
        $user = $modx->getObject('modUser',$userId);
        if (!$user) return '';
        $profile = $user->getOne('Profile');
        if (!$profile) return '';
         
        $userArray = array_merge($user->toArray(),$profile->toArray());
         
        $modx->toPlaceholders($userArray,'user');
        return '';
                        
  2. recaptcha.class.php

    Customization to make recaptcha widget responsive. I did this in lieu of the recaptchv2 integration though I may get to that sooner or later. Changes to the getHtml function are described below. The file is located at core/components/quip/model/recaptcha/recaptcha.class.php. Before changes:
    
        public function getHtml($theme = 'clean',$error = null) {
            if (empty($this->config[reCaptcha::OPT_PUBLIC_KEY])) {
                return $this->error($this->modx->lexicon('recaptcha.no_api_key'));
            }
    
            /* use ssl or not */
            $server = !empty($this->config[reCaptcha::OPT_USE_SSL]) ? $this->config[reCaptcha::OPT_API_SECURE_SERVER] : $this->config[reCaptcha::OPT_API_SERVER];
    
            $errorpart = '';
            if ($error) {
               $errorpart = "&error=" . $error;
            }
            $opt = array(
            // changes will start here
                'theme' => $theme,
                'lang' => $this->modx->getOption('cultureKey',null,'en'),
            );
            return '
            //changes will end here
     <noscript>
            <iframe src="'. $server . 'noscript?k=' . $this->config[reCaptcha::OPT_PUBLIC_KEY] . $errorpart . '" height="300" width="500" frameborder="0"></iframe><br/>
            <textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
            <input type="hidden" name="recaptcha_response_field" value="manual_challenge"/>
    </noscript>';
    }
                        
    After changes:
    
        public function getHtml($theme = 'clean',$error = null) {
            if (empty($this->config[reCaptcha::OPT_PUBLIC_KEY])) {
                return $this->error($this->modx->lexicon('recaptcha.no_api_key'));
            }
    
            /* use ssl or not */
            $server = !empty($this->config[reCaptcha::OPT_USE_SSL]) ? $this->config[reCaptcha::OPT_API_SECURE_SERVER] : $this->config[reCaptcha::OPT_API_SERVER];
    
            $errorpart = '';
            if ($error) {
               $errorpart = "&error=" . $error;
            }
            $opt = array(
            // start of changes to theme
                'theme' => 'custom',
                'custom_theme_widget' => 'recaptcha_widget',
                'lang' => $this->modx->getOption('cultureKey',null,'en'),
            );
            return '<script type="text/javascript">var RecaptchaOptions = '.$this->modx->toJSON($opt).';</script>
             <div id="recaptcha_widget" style="display:none">
                <div class="control-group">
                    <div class="controls">
                        <a id="recaptcha_image" href="#" class="thumbnail"></a>
                        <div class="recaptcha_only_if_incorrect_sol" style="color:red">Incorrect please try again</div>
                    </div>
                </div>
             
                <div class="control-group">
                    <label class="recaptcha_only_if_image control-label">Enter the words above:</label>
                    <label class="recaptcha_only_if_audio control-label">Enter the numbers you hear:</label>
             
                    <div class="input-group">
                        <input type="text" id="recaptcha_response_field" class="input-recaptcha form-control" name="recaptcha_response_field" />
                        <span class="input-group-btn">
                            <a class="btn btn-warning" href="javascript:Recaptcha.reload()"><span title="Refresh Image" class="glyphicon glyphicon-refresh"></span></a>
                        </span>
                        <span class="input-group-btn">
                            <a class="btn btn-warning recaptcha_only_if_image" href="javascript:Recaptcha.switch_type(\'audio\')"><span title="Get an audio CAPTCHA"class="glyphicon glyphicon-headphones"></span></a>
                        </span>
                        <span class="input-group-btn">
                            <a class="btn btn-warning recaptcha_only_if_audio" href="javascript:Recaptcha.switch_type(\'image\')"><span title="Get an image CAPTCHA" class="glyphicon glyphicon-picture"></span></a>
                        </span>
                        <span class="input-group-btn">  
                        <a class="btn btn-warning" href="javascript:Recaptcha.showhelp()"><span class="glyphicon glyphicon-question-sign"></span></a>
                        </span>
                    </div>
                </div>
            </div>
            // end of changes to theme
            <script type="text/javascript" src="'. $server . 'challenge?k=' . $this->config[reCaptcha::OPT_PUBLIC_KEY] . $errorpart . '"></script>
            
            <noscript>
                    <iframe src="'. $server . 'noscript?k=' . $this->config[reCaptcha::OPT_PUBLIC_KEY] . $errorpart . '" height="300" width="500" frameborder="0"></iframe><br/>
                    <textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
                    <input type="hidden" name="recaptcha_response_field" value="manual_challenge"/>
            </noscript>';
        }
    
                        

Javascript Functions

As one of the primary goals was to make fewer posts to the server, I decided to inline both the reply to comment and preview functions. The Reply function leverages a single form which is moved within the page as requested by the user. The original inline reply inspiration came from DesignCouch's site design, and was further improved by the LEague OF Extraordinary Catholics.

All of the functions below should be placed in the document ready function to be invoked after the page is fully rendered.

Requirements: Bootstrap Collapse and Popovers are implemented jQuery MD5 Plugin 1.2.1 used for gravatar preview fetching.
  1. Toggle Main Comment Form Click Event

    This function toggles the main comment form which is originally positioned at the bottom of the page under the Add a Comment heading:
    
        $("#post-a-comment").click(function(event){
            event.preventDefault();
            $("#comment-form").collapse('toggle');
        });
                
  2. Reply to Comment Form Click Event

    This is the magic function that moves the single comment form and preview section inline within the threaded comments.
    
        $('.replyTo').on('click', function(event){
            event.preventDefault();
            var idPrefix = $('#idprefix').val();
            var commentParent = $(this).attr('data-pid');
            $('#preview-div').hide();
            $('.replyTo').removeClass('hide');
            replyurl = $(this).attr('href');
            $("#quip-add-comment-qcom").parent().insertAfter(this).collapse("show");
            $("#quip-add-comment-qcom input[name=parent]").val(commentParent);
            $('#quip-comment-box-' + idPrefix ).val('');
            $("#remove-btn, #report-btn").hide();
            $("#preview-div").popover('hide');
            if (!$(this).hasClass('main-comment-form')) {$(this).addClass('hide');}
            return false;
        });
                
  3. Reply Cancel Form Click Event

    This function remove the reply form on cancel and return it to the default position at the bottom of the page. It also hides any currently visible preview and clears the comment textarea.
    $('.reply-cancel').on('click', function(event){
             event.preventDefault();
             var idPrefix = $('#idprefix').val();
             $('#preview-div').hide();
             $('#quip-add-comment-qcom').parent().insertBefore('.main-comment-form').collapse('hide');
             $('.replyTo').removeClass('hide');
             $('#remove-btn, #report-btn').show();
             $("#preview-div").popover('hide');
             $('#quip-comment-box-' + idPrefix ).val('');
             return false;
        });
                
  4. Preview Form Click Event

    This function takes current form values and populates them in a preview div that emulates the comment tpl without having to postback to the server.
    $('#preview-btn').on('click', function(event){
         event.preventDefault();
         var idPrefix = $('#idprefix').val();
         var emailID = #quip-comment-email-' + idPrefix;
         var emailAddy = $(emailID).val();
         $('#gravpreview').attr('src', 'http://www.gravatar.com/avatar/' + $.md5(emailAddy));
         var author = $('#quip-comment-name-' + idPrefix ).val();
         $('#authorName').html(author);
         var comment = $('#quip-comment-box-' + idPrefix ).val();
         $('#comment').html(comment);
         $('#preview-div').fadeIn(2200);
         $("#preview-div").popover('show');
    });
                
  5. Popover Functions for Preview

    The below function are optional but clearly show the user where their preview is in the tree. It is impemented with javascript only (no data hooks on html elements) in case you want to omit it entirely. These also go in document ready function.
    
        $('#preview-div').popover({
            placement: 'right',
            animation: true,
            html: true,
            title : ' Here\'s your preview!',
            content: 'To change something just modify the form and click the preview button again.',
            viewport: { selector: '.container', padding: 0 },
            delay: '3000'
        });
    
        $('#preview-btn').on('click', function(event){
             event.preventDefault();
             console.log("Firing preview");
        
             var idPrefix = $('#idprefix').val();
             var emailID = "#quip-comment-email-" + idPrefix;
             var emailAddy = $(emailID).val();
             $('#gravpreview').attr('src', 'http://www.gravatar.com/avatar/' + $.md5(emailAddy));
             var author = $('#quip-comment-name-' + idPrefix ).val();
             $('#authorName').html(author);
             var comment = $('#quip-comment-box-' + idPrefix ).val();
             $('#comment').html(comment);
             $('#preview-div').fadeIn(2200);
             $("#preview-div").popover('show');
             return false;
        });
                

Styling

All of the formatting is using Bootstrap 3.0 css and components to enable a responsive layout. The additional css for the layout also bears responsiveness in mind.
  1. Required Additional CSS

    Load these styles after your Bootstrap CSS and any plugin CSS required. They really should be the last stylesheet loaded:
    
        .media-body p{
          min-height: 60px;
          background-color: #f0f0f0;
          padding: 5px 10px;
        }
        
        span.desc{
          background-color: #cecece;
          bottom: -5px;
          color: #fff;
          left: 0;
          position: relative;
          width: 80px;
          overflow: visible;
          text-align: center;
          height: 44px;
          display: table-cell;
          vertical-align: middle;
          -webkit-border-bottom-left-radius: 10px;
          -moz-border-radius-bottomleft: 10px;
          border-bottom-left-radius: 10px;
        }
        
        span.author_name{
          color: #fff;
          font-size: .8em;
        }
        
        #post-a-comment {
          margin-bottom:20px;
        }
                    
  2. Optional Additional CSS

    These styles are optional and for the buttons in the comment section as well as the list styles for the Preview Popver, Latest Articles and Latest Comments listed in the Sidebar. I included them only for completeness.
    
        /* -------------- POPOVER STYLES ----------------- */
        .popover {
          width:100%;
        }
        .popover-title {
          font-weight: 700;
          color: #fff;
          background-color: #000;
        }
        
        .popover-content {
          font-size: .9em
        }
    
        /* -------------- OPTIONAL BUTTONS ----------------- */
        .btn {
            -webkit-transition: all 0.4s;
            transition: all 0.4s;
            border-radius: 0;
            font-family: 'open_sanslight', sans-serif;
        }
        
        .btn-outline-inverse {
          color: #000;
          background-color: transparent;
          border-color: #000 !important;
          text-decoration:none !important;
        }
        .btn-outline-inverse:hover,
        .btn-outline-inverse:focus,
        .btn-outline-inverse:active {
          color: #fff;
          text-shadow: none;
          background-color: #000;
          border-color: #000;
          text-decoration:none;
        }
        .btn-outline-inverse:hover a,
        .btn-outline-inverse:focus a,
        .btn-outline-inverse:active a {
           text-decoration:none !important;
        }
        
        .btn-outline {
          color: #000;
          background-color: transparent;
          border-color: #000 !important;
          text-decoration:none !important;
        }
        .btn-outline:hover,
        .btn-outline:focus,
        .btn-outline:active {
          color: #fff;
          text-shadow: none;
          background-color: #000;
          border-color: #000;
          text-decoration:none;
        }
        .btn-outline:hover a,
        .btn-outline:focus a,
        .btn-outline:active a {
           text-decoration:none !important;
        }
        
        /* -------------- OPTIONAL LAYOUT FOR SIDEBAR ----------------- */
        .icon-list li {
          padding: 0 0 15px 30px;
          display: block;
          position: relative;
        }
        .icon-list li:before {
          font-family: 'Glyphicons Halflings';
          position: absolute;
          left: 0px;
          top: -2px;
          font-size: 120%;
        }
        .comment li:before {
         content: '\e111';
        }
        
        .article li:before {
         content: '\e055';
        }
                    

Comments (14)

Posted On: Dec 11, 2019 at 03:39 PM

Paper Writing Service - EssayErudite.com

Academic assignments often force students to learn fast how to prepare excellent papers and submit them on time.
The up-to-date paper writing service are key for students’ success, especially when they want to get excellent papers fast.
A lot of students prefer services of academic paper professional writers, which help them to submit great papers promptly.

paper writing service

Paper Writing Service - https://essayerudite.com/paper-writing-service/

thesis writing service uk
argumentative research paper
help me write my research paper
accounting assignment help
best custom essay writing service

Reply
Posted On: Dec 14, 2019 at 12:05 AM

Интересует испания поиск туров и отелей ? Если планируете долгожданное путешествие, то https://luxortour.kz готов прийти на помощь в подборе тура. Квалифицированные, опытные сотрудники забронируют отель, помогут оплатить тур в режиме онлайн, возьмут на себя все заботы по подбору тура. Сотрудничество происходит с большим количеством операторов - они предлагают самые разные варианты отдыха, которые подойдут как самым взыскательным клиентам, так и туристам с ограниченным бюджетом.

Хотите вы организовать пляжный отдых или отправиться на горнолыжный курорт, полюбоваться экзотическими странами или рассмотреть редкие достопримечательности, специалисты luxortour.kz помогут приобрести оптимальное решение по выгодной стоимости. Сотрудники ознакомят вас с отелями, особенностями курорта, ответят на любые вопросы по телефону. На поиск тура не уйдет много времени, а база постоянно обновляется, поэтому в ней появляется все больше актуальных вариантов.

Клиентами турагентства являются люди, которые доверяют организацию отдыха luxortour.kz, но при этом не желают переплачивать. Вы получаете все необходимые гарантии того, что сможете рассчитывать на высокий уровень обслуживания и комфортные условия приобретения. Это подтверждают положительные отзывы клиентов. В стоимость путевки включено питание, проживание, медицинская страховка, а также топливный сбор. Оплата производится комфортным способом.

Если и вы не согласны на компромиссы, то доверьте организацию путешествия туроператору, который знает о нем все!

Reply
Posted On: Feb 02, 2020 at 11:00 PM

Виртуальные казино предлагают отправиться в мир захватывающих развлечений, ощутить неподдельный азарт и испытать непередаваемые эмоции, играя в автоматы, рулетку, покер и другие азартные игры в онлайн-режиме. Среди виртуальных игорных заведений Джойказино пользуется особой популярностью у пользователей. Игровой портал создает комфортные и безопасные условия как для новичков, так и для опытных игроков, удовлетворяя требования даже самых бывалых гемблеров. Здесь каждый найдет развлечение по душе и сможет провести свой досуг не только увлекательно, но и выгодно с финансовой точки зрения. Заработать деньги играя в автоматы вполне реально, вы можете убедиться в этом лично, перейдя по ссылке https://joycasino-vip.net на официальный сайт клуба.

Играть в слоты в Джойказино можно абсолютно бесплатно, выбрав демонстрационный режим любой заинтересовавшей вас модели игрового аппарата. Погрузиться в мир настоящего азарта, окунуться в атмосферу популярный эмуляторов, разработанных лучшими компаниями Novomatic, Igrosoft, Mega Jack, может любой желающий – как зарегистрированный пользователь, так и гость сайта. Деморежим позволит протестировать новинки слотов, получить полезный опыт, ознакомиться с геймплеем и разработать собственную стратегию. Как следует потренировавшись, вы сможете добиться успеха в игре с реальными ставками и заработать деньги.

Игроки Джойказино получают массу выгодных преимуществ, с которыми игра становится еще более прибыльной и захватывающей. Новичкам предоставляются стартовые бонусы в виде фриспинов и дополнительных средств на депозит. Активные игроки могут вернуть часть потраченных на ставки средств по условиям игорного заведения. Кроме того, играя в автоматы вы сможете накапливать баллы и повышать свой ранг, получая еще больше привилегий.

Reply
Posted On: Feb 03, 2020 at 12:11 AM

Советская эпоха давно закончилась, оставив после себя приятные воспоминания и...нежнейшие пончики. Самые вкусные пончики, вкус которых знаком нам с детства, теперь можно заказать с доставкой на дом или в офис в любом количестве в пределах Москвы. Насладиться горячими, нежнейшими пончиками с сахарной пудрой, шоколадом, джемом и другими добавками предлагает знаменитая пончиковая «РусПыш». Мы придерживаемся традиционной рецептуры, поэтому наши пончики всегда получаются удивительно вкусными, умеренно сладкими и необыкновено аппетитными.

Заказать оригинальные советские пончики с тем самым неповторимым вкусом можно на сайте https://ruspish.ru/ по ссылке: где купить пышные пончики недорого Мы предлагаем несколько разновидностей пончиков, среди которых вы обязательно найдете свои любимые:

- несладкие – пончики, которые станут прекрасным дополнением к первым и вторым блюдам, подавать их можно с любыми соусами, овощами и соленьями;
- пончики с сахарной пудрой – классическая комбинация, которая никого не оставит равнодушным;
- пончики с ягодами – клубникой, смородиной, облепихой, черникой, клюквой – полезная сладость с целым набором витаминов;
- со сгущенкой и шоколадом – для настоящих сладкоежек.

Русские пончики всегда вызывают небывалый ажиотаж, поэтому если вы ждете гостей, заказывать угощение лучше с запасом. Не стоит беспокоиться, что в дороге пончики остынут, ведь мы тщательно упакуем их в коробочку из плотного картона, что позволит сохранить их первоначальный вид. Свежие, ароматные, хрустящие пончики, как будто только из печи мы доставим в любую точку Москвы. Лучшие пончики на ВДНХ вы можете отведать в наших кафе, где мы готовим их на глазах у покупателей. Мы используем исключительно натуральные ингредиенты, поэтому наши пончики получаются настолько вкусными, как те самые лакомства из детства.

Reply
Posted On: Feb 03, 2020 at 03:20 AM

В нашей жизни случаются ситуации, когда могут понадобиться услуги адвоката. Переоформление недвижимости, вступление в наследство, раздел имущества, заключение договоров – все эти операции требуют вмешательства специалиста с богатым опытом в юридической сфере. Если работодатель ущемляет права своих работников, например, не выплачивает ранее оговоренную зарплату, то помощь адвокатов поможет раз и навсегда решить эту проблему.

Ищете грамотного адвоката в Ногинске? Обращайтесь к специалистам филиала «Фемида» Московской областной Коллегии адвокатов. У нас работают юристы с многолетним опытом, которые оказывают профессиональную юридическую помощь и с успехом справляются с делами любой сложности. Наша компания предоставляет следующие услуги:

- юридические консультации;
- составление исков, заявлений, жалоб, договоров;
- подготовка и анализ документов;
- участие в досудебных разбирательствах и судебных процессах;
- ведение уголовных, гражданских, административных дел;
- переоформление малого и среднего бизнеса, закрытие и открытие предпринимательской деятельности.

Наши адвокаты отслеживают все актуальные изменения в законодательстве Российской Федерации и располагают необходимой информацией, которая может понадобиться для защиты физических и юридических лиц в суде. Нашими услугами могут воспользоваться не только жители Москвы и Московской области, но и других регионов России и СНГ.

Если вам понадобилась профессиональная юридическая помощь и защита квалифицированного адвоката, оставляйте свою заявку на сайте https://www.femida-law.com/ по ссылке: адвокаты Черноголовка, указав свои контактные данные. Также вы можете связаться с нами по номеру телефона, который указан на сайте. Мы проанализируем вашу проблему и быстро найдем решение.

Reply
Posted On: Feb 04, 2020 at 02:57 AM

http://mewkid.net/who-is-xandra/ - Dosage For Amoxicillin 500mg Amoxicillin 500 Mg rgd.zmni.cmhworks.com.elv.kk http://mewkid.net/who-is-xandra/

Reply
Posted On: Feb 04, 2020 at 03:26 AM

http://mewkid.net/who-is-xandra/ - Amoxicillin Buy Amoxicillin Online Without Prescription ama.ajzx.cmhworks.com.swj.zi http://mewkid.net/who-is-xandra/

Reply
Posted On: Feb 06, 2020 at 05:50 AM

Когда компании необходимо собрать информацию о своей целевой аудитории очень удобно воспользоваться услугами сайта https://lpg.tf где помогут быстро и качественно создать лендинг пейдж. Удобство пользования сервисом достигается благодаря редактору, который при помощи быстрого создания шаблонов и текста оформит страницу в необходимом для вас виде. Страница будет оснащена высокой конверсией и станет привлекательной для большинства пользователей. А поддержка специалистов даже после завершения сделки позволит не переживать о работоспособности страницы.

Reply
Posted On: Feb 06, 2020 at 10:06 AM

Для приобретения действительно качественных комплектующих для лодок, катеров и яхт, обратите собственное внимание на предложения, располагающиеся на странице https://priliv-new.com.ua/g22159942-komplektuyuschie-zapchasti-dlya ведь именно на этой странице ассортимент раскрывается. При необходимости, вы всегда сможете выбрать системы управления для лодок, якоря, швартовые веревки, кранцы и не только. Приобрести по максимально доступной цене можно даже масло для лодочных моторов.

Reply
Posted On: Feb 06, 2020 at 01:58 PM

Много красивых девушек на сайте https://prostitutki-moskvy.msk.ru/ предлагают свои услуги для того, чтобы мужчины, женщины и даже пары могли познать новое для себя. Пользователь сайта может просматривать множество различных фотографий до заказа, чтобы разврат был поистине неудержимым. Индивидуалки Москвы могут поразить любого человека, продемонстрировав наглядно и чувственно, что такое настоящее удовольствие. На сайте указывается возраст девушек, параметры груди, стоимость выезда, время работы и не только.

Reply
Posted On: Feb 06, 2020 at 05:32 PM

Для новичков и профессионалов все секреты персонального компьютера открывает сайт https://it-tehnik.ru ведь именно на нем размещается достаточно много статей, которые будут полезны для читателей. В правой колонке сайта располагается колонка прямого эфира, в которой можно задать свои вопросы и получить исчерпывающие ответы. У сайта также есть канал в Telegram, Youtube и даже в Яндекс. Дзен. В левой части сайта располагается перечень категорий. На главной странице уже содержится перечень различных статей, в которых наглядно описываются пошаговые курсы, советы и не только.

Reply
Posted On: Feb 06, 2020 at 09:11 PM

В Москве массаж на дому прямо сейчас необходимо заказать на сайте https://mistermassage.ru ведь здоровье, как говорится, бесценно! Массаж может быть классическим, лимфодренажным, медовым, антицеллюлитным и не только. Постоянными спутниками нашей жизни сейчас являются стресс, усталость, переработка и не только. От всего этого необходимо избавиться, иначе это все будет приводить к пагубным результатам, потере сил и энергии. Чтобы болезни вас не беспокоили, обращайтесь к услугам практикующего достаточно давно специалиста.

Reply
Posted On: Feb 07, 2020 at 01:06 AM

Узнать больше о сортах и географии кофе можно после посещения сайта https://coffeetee.ru (там, кстати, и рецепты предлагаются). Пользователь сайта может принять участие в опросах, увидеть тонкости выпускаемого в разных странах кофе, сумеет понять, каким образом можно получить возможность наслаждаться новыми вкусами и оттенками. Итальянский кофе, польский, аргентинский и не только - о всех их видах можно прочесть максимально детальные статьи. Готовить вкусный кофе можно и без дорогой кофемашины!

Reply
Posted On: Feb 20, 2020 at 03:04 PM

Для тех, кто увлечен духовными практиками, нумерологией, астрологией, Рэйки, целительством на основе многовекового опыта, встает вопрос – кого выбрать своим Учителем. Кому доверить свою личность, чтобы достичь высот в духовном мире, жизни. Своевременную помощь оказывает в этом Духовный Наставник, познакомиться с которой можно на сайте https://www.marinareiki.ru Здесь Мастер коротко рассказывает о себе, о пути к духовному миру. Отвечает на вопросы, подсказывает в проблемах, указывает на правильные поступки.

Тем, кто в поиске, полезно будет знать несколько фактов из жизни Мастера: обучалась искусствам минералогии, астрологии, нумерологии в школе известного астролога, с 2013 года из работы в социуме полностью перешла к духовной деятельности.

Сейчас астролог и целитель полностью отдает себя излечению людей и передаче накопленных знаний тем, кто остро нуждается в этом, кому нужна рука помощи и совет. На сайте есть вся информация, которая поможет определиться с кандидатурой своего будущего Учителя. Опыт – огромная тайна и помощь всем, кто готов довериться и научиться.

Reply



Allowed tags: <b><i><br>