Grandstream Live Chat Snippet in WordPress

How to successfully integrate Live Chat Snippet in WordPress using Grandstream UCM. This live chat uses livechatiframe to create a iFrame embed into website using the JavaScript snippet.

The backstory

With been integrating a live chat on our company website. I quickly realized that the live chat code based on an iFrame did not work out of the box. The live chat is available as a JavaScript snippet provided by Grandstream. In the background using a UCM6301 IP-PBX and the website is on WordPress with a standard theme. I guess it is Twenty Eleven.

After I wrote to helpdesk, they give me a slightly modified JavaScript. The default code snippet can be found in the UCM WebUI under Messaging – Live Chat. It is available as a link and as code snippet.

Install WPCode Plugin to WordPress

In order to add this Live Chat snippet in your WordPress as described in this tutorial, you need to install the well-known and useful WPCode sippet plugin. Do this with sign in to WP admin and go to plugins page. Click Add New Plugin and type in “WPCode” in to the search field. Install and activate the WPCode plugin on your WordPress.

Live Chat Snippet integration in WordPress

First you create a new snippet by clicking on the Add New button and select Add Your Custome Code (New Snippet) bottom in the first selection. Select HTML-Snippet in the top right. Then type in a title and insert the following code into the editor.

<style>
  #chat-container {
    position: fixed;
    bottom: 20px;
    right: 20px;
    z-index: 100000;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
  }
</style>
<div id="chat-container"></div>
<script>
window.addEventListener("load", (event) => {
  var openSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" class="design-iconfont">\n' +
      '  <path d="M27.8832966,16.887873 C27.8094879,16.8666413 27.7342507,16.8550603 27.660442,16.8492698 C27.6742514,16.6663873 27.6537754,16.4864 27.6023474,16.3204063 C27.639966,15.9363048 27.6666324,15.5497905 27.6666324,15.1555556 C27.6666324,8.62631111 22.4433584,3.33333333 16.0000982,3.33333333 C9.55636184,3.33333333 4.33356399,8.62631111 4.33356399,15.1555556 C4.33356399,15.5497905 4.35975417,15.9363048 4.39784898,16.3204063 C4.34594481,16.4864 4.32546885,16.6663873 4.33927822,16.8492698 C4.26546953,16.8550603 4.19070847,16.8666413 4.11689979,16.887873 L3.54452533,17.0519365 C2.91310393,17.2333714 2.5378701,17.9287111 2.70739198,18.6052317 L3.93356854,23.5044571 C4.10261424,24.1804952 4.75213067,24.5819683 5.38355207,24.4010159 L5.95545034,24.2374349 C5.98925948,24.2273016 6.01973533,24.2104127 6.05211591,24.1978667 L6.08925835,24.3561397 C6.20497132,24.8468825 6.56068156,25.1885206 6.97448639,25.2681397 C9.50064818,28.5822222 13.7929804,28.6932063 14.8982059,28.6628063 C14.9120153,28.6632889 14.9248723,28.6666667 14.9386817,28.6666667 L17.0962762,28.6666667 C17.8910291,28.6666667 18.5353075,28.0996825 18.5353075,27.4 C18.5353075,26.7008 17.8910291,26.1333333 17.0962762,26.1333333 L14.9386817,26.1333333 C14.1439288,26.1333333 13.5001266,26.7008 13.5001266,27.4 C13.5001266,27.5119492 13.5220311,27.6181079 13.5525069,27.7213714 C10.7211105,27.3300317 8.98255883,25.9687873 8.10447356,25.0447238 L8.51304035,24.9207111 C9.10303365,24.7407238 9.45350586,24.0506921 9.29541242,23.3794794 L7.57733669,16.0887873 C7.44210013,15.5136 6.9773435,15.142527 6.47639681,15.1594159 L6.47639681,15.1555556 C6.47639681,9.82542222 10.7401579,5.5047619 16.0000982,5.5047619 C21.2600385,5.5047619 25.5237996,9.82542222 25.5237996,15.1555556 L25.5237996,15.1594159 C25.0223767,15.142527 24.5576201,15.5136 24.4223835,16.0887873 L22.704784,23.3794794 C22.5466905,24.0506921 22.8966866,24.7407238 23.4866799,24.9207111 L24.5557153,25.2454603 C25.1457086,25.4259302 25.7523684,25.0273524 25.9104619,24.3561397 L25.9476043,24.1978667 C25.9804611,24.2104127 26.0104607,24.2273016 26.044746,24.2374349 L26.6166443,24.4010159 C27.2480657,24.5819683 27.897106,24.1804952 28.0666279,23.5044571 L29.2928044,18.6052317 C29.4618501,17.9287111 29.0866163,17.2333714 28.4551949,17.0519365 L27.8832966,16.887873 Z" fill="#FFF" fill-rule="evenodd"/>\n' +
      '</svg>'
  var closeSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" class="design-iconfont">\n' +
      '  <path d="M6.95955989,6.95955989 C7.35008418,6.56903559 7.98324916,6.56903559 8.37377345,6.95955989 L15.9996667,14.5846667 L23.6262266,6.95955989 C24.0167508,6.56903559 24.6499158,6.56903559 25.0404401,6.95955989 C25.4309644,7.35008418 25.4309644,7.98324916 25.0404401,8.37377345 L17.4136667,15.9996667 L25.0404401,23.6262266 C25.4009241,23.9867105 25.4286536,24.5539416 25.1236287,24.9462328 L25.0404401,25.0404401 C24.6499158,25.4309644 24.0167508,25.4309644 23.6262266,25.0404401 L15.9996667,17.4136667 L8.37377345,25.0404401 C7.98324916,25.4309644 7.35008418,25.4309644 6.95955989,25.0404401 C6.56903559,24.6499158 6.56903559,24.0167508 6.95955989,23.6262266 L14.5846667,15.9996667 L6.95955989,8.37377345 C6.59907592,8.01328949 6.57134639,7.44605843 6.87637128,7.05376722 Z" fill="#FFF" fill-rule="evenodd"/>\n' +
      '</svg>'
  var config = {
    "parentClass": "",
    "btnSize": 50,
    "btnLeft": "",
    "btnRight": 16,
    "btnTop": "",
    "btnBottom": 30,
    "liveChatUrl": 'https://PASTE HERE YOUR GDMS LIVE CHAT LINK',
    "liveChatWidth": 350,
    "liveChatHeight": 550,
    "expandDire": ""
  }
  initLiveChat(config)

  function initLiveChat (config) {
    var bodyDom = document.body
    if (config.parentClass) {
      bodyDom = document.querySelector('.'+config.parentClass)
    }
    if (!bodyDom.style.position) {
      bodyDom.style.position = 'relative'
    }
    var chatContainer = document.getElementById('chat-container')
    var entryBtn = document.createElement('div')
    entryBtn.className = 'live-chat-entry close'
    btnInitStyle()
    btnCloseStyle()
    chatContainer.appendChild(entryBtn)
    entryBtn.onclick = function () {
      var liveChatIframe = document.getElementById('liveChatIframe')
      if (this.classList.contains('close')) {
        this.classList.remove('close')
        this.classList.add('open')
        btnOpenStyle()
        if (liveChatIframe) {
          iframeOpenStyle(liveChatIframe)
        } else {
          iframeInit()
        }
      } else {
        this.classList.remove('open')
        this.classList.add('close')
        btnCloseStyle()
        if (liveChatIframe) {
          iframeCloseStyle(liveChatIframe)
        }
      }
    }
    entryBtn.addEventListener('mouseover', () => {
      entryBtn.style.backgroundColor = '#4299FC';
    });

    entryBtn.addEventListener('mouseout', () => {
      entryBtn.style.backgroundColor = '#3F8EF0';
    });
    function btnInitStyle () {
      entryBtn.style.position = 'relative'
      entryBtn.style.width = config.btnSize >= 0 ? config.btnSize +'px' : '50px'
      entryBtn.style.height = config.btnSize >= 0 ? config.btnSize +'px' : '50px'
      entryBtn.style.borderRadius = config.btnSize >= 0 ? config.btnSize/2 +'px' : '25px'
      entryBtn.style.backgroundColor = '#3F8EF0'
      entryBtn.style.padding = '12px'
      entryBtn.style.cursor = 'pointer'
      entryBtn.style.userSelect = 'none'
      entryBtn.style.boxSizing = 'border-box'
      entryBtn.style.zIndex = 100000
      entryBtn.innerHTML = openSvg + closeSvg
    }
    function btnCloseStyle () {
      entryBtn.querySelectorAll('svg')[0].style.display = 'block'
      entryBtn.querySelectorAll('svg')[1].style.display = 'none'
    }
    function btnOpenStyle () {
      entryBtn.querySelectorAll('svg')[0].style.display = 'none'
      entryBtn.querySelectorAll('svg')[1].style.display = 'block'
    }
    function iframeInit () {
      var iframeBox = document.createElement('iframe')
      var iframeWidth = typeof config.liveChatWidth === 'number' && config.liveChatWidth >= 0 ? config.liveChatWidth : ''
      var iframeHeight = typeof config.liveChatHeight === 'number' && config.liveChatHeight >= 0 ? config.liveChatHeight : ''
      var btnSize = config.btnSize > 0 ?  config.btnSize : 50
      iframeBox.id = 'liveChatIframe'
      iframeBox.src = config.liveChatUrl;
      iframeBox.width = iframeWidth ? iframeWidth + 'px' : config.liveChatWidth
      iframeBox.height = iframeHeight ? iframeHeight + 'px' : config.liveChatHeight
      iframeBox.sandbox= 'allow-same-origin allow-scripts allow-forms allow-popups allow-downloads'
      iframeBox.allow= 'camera; microphone; autoplay; speaker; speaker-selection'
      iframeBox.style.maxWidth = '100%';
      iframeBox.style.maxHeight = 'calc(100vh - 120px)';
      iframeBox.style.border = '1px solid #ccc';
      iframeBox.style.borderRadius = '4px';
      iframeBox.style.position = 'relative';
      iframeBox.style.overflow = 'hidden';

      var closeButton = document.createElement('div');
      closeButton.innerHTML = closeSvg;
      closeButton.style.position = 'absolute';
      closeButton.style.top = '8px';
      closeButton.style.right = '8px';
      closeButton.style.width = '24px';
      closeButton.style.height = '24px';
      closeButton.style.cursor = 'pointer';
      closeButton.style.zIndex = '1';
      closeButton.onclick = function () {
        entryBtn.click(); // Call the click event handler of the entry button to toggle the chat window
      };
      iframeBox.appendChild(closeButton);

      chatContainer.appendChild(iframeBox);
    }
    function iframeCloseStyle (liveChatIframe) {
      liveChatIframe.style.display = 'none'
    }
    function iframeOpenStyle (liveChatIframe) {
      liveChatIframe.style.display = 'block';
    var iframeHeight = typeof config.liveChatHeight === 'number' && config.liveChatHeight >= 0 ? config.liveChatHeight : '';
    liveChatIframe.height = iframeHeight ? iframeHeight + 'px' : config.liveChatHeight;

    // Set the iframe width based on a condition or a new value
    var expandedWidth = 350; // Example width, adjust to your preference
    liveChatIframe.style.width = expandedWidth + 'px';
    }
  }
})
</script>

quote replace the placeholder in the config section at “liveChatUrl” with your live chat link generated in UCM under Messaging – Live Chat.

Live Chat Snippet in the WPCode editor

The JavaScript snippet looks something like this, as an HTML-Snippet in the WPcode snippet editor.

Grandstream Live Chat Snippet in WordPress

Finaly activate the Live Chat snippet with move the slider at top right and show the Live Chat on your page preview.

Note: Don’t forget to adjust the live chat settings according to your own preferences. Do this in the UCM WebUI under Messaging by clicking on the edit symbol under options.

XScreenSaver sonar must be setuid to ping

When I invoke the XScreenSaver Sonar or lock the screen, the Sonar XScreenSaver displays “sonar must be setuid to ping”

XScreenSaver sonar must be setuid to ping

Last weekend was a rainy Sunday, so I thought it would be a good time to upgrade the Linux Mint. My Thinkpad is running Linux Mint they I would upgarde from Linux Mint 21 (Vanessa) to Linux Mint 22 (Wilma). For those who are not familiar with the beautiful Linux Mint desktop. Linux Mint is based on the Ubuntu distribution, so Debian is the underlying system. Debian is the exemplary open source operating system what I prefer and mainly use for the servers.

One of the features I like on the Linux desktop is the Sonar XScreenSaver. Sonar shows the active hosts in the local network which are recognized by using ping as a screen saver. When I try to enable the XScreenSaver Sonar in the control center, it appers “sonar must be setuid to ping”.

cause

The Sonar program executed out from XScreenSaver must be installed as setuid root in order to ping hosts. This is because root privileges are needed to create an ICMP RAW socket.

Solution

First we find Sonar, which is not located under the same path on all distributions. You can easily find the location of Sonar with using the locate command.

$ locate sonar | head -n 1
/usr/libexec/xscreensaver/sonar

With head we see the first line with path where sonar is.

Now run the command as follows in the terminal shell to set permission.

$ sudo chmod +s /usr/libexec/xscreensaver/sonar

The s permissions instead of x in the owner permissions means that the sticky bit (suid) is enabled. So this file will be executed with root permissions by all users.

XScreenSaver setuid sonar ping

Note. some distributions Sonar is be under a different path.

$ sudo chmod +s /usr/lib/xscreensaver/sonar

Finaly check whether the permission has been set.

$ ls -ld /usr/libexec/xscreensaver/sonar
-rwsr-sr-x 1 root root 127448 Mar 31 19:32 /usr/libexec/xscreensaver/sonar

Conclusion

On most Unix systems, the Sonar program must be installed as setuid root in order to ping hosts. This is because root privileges are needed to create an ICMP RAW socket. Privileges are disavowed shortly after startup (just after connecting to the X server) so this is believed to be safe:

$ sudo chown root:root sonar
$ sudo chmod u+s sonar

It is not necessary to make it setuid on MacOS systems. Because on MacOS, unprivileged programs can ping by using ICMP DGRAM sockets instead of ICMP RAW.

In ping-mode, the display is a logarithmic scale, calibrated so that the three rings represent ping times of approximately 2.5, 70 and 2,000 milliseconds respectively.

This means that if any the hosts you are pinging take longer than 2 seconds to respond. They won’t show up; and if you are pinging several hosts with very fast response times. They will all appear close to the center of the screen (making their names hard to read.)