Advanced topics

Captive Portal HTML/CCS code example

Overview

Captive Portal templates in GATE can be customized in two ways:

This page demonstrates the HTML/CSS implementation of the Hotel Template for captive portals.

Hotel Template Preview

Live Template: Captive Portal Hotel Template

Hotel Template Screenshot

hotel1-little.jpg

hotel1-little.jpg

hotel1-little.jpg

Complete HTML/CSS Code

Below is the complete HTML template code for the Hotel captive portal:

{% extends "page/base.html" %}

{% load admin_static media i18n %}

{% block extrastyle %}
	{% if page.variables.favicon %}
	<link rel="shortcut icon" type="image/ico" href="{% portal_files '' %}{{ page.variables.favicon }}" />
    {% endif %}

	<style>
        .cc-selector input{
            margin:0;padding:0;
            -webkit-appearance:none;
            -moz-appearance:none;
            appearance:none;
        }

        .cc-selector-2 input{
            position:absolute;
            z-index:999;
        }

        .hombre{background-image:url({% portal_files 'templates/img/hombre.svg' %});}
        .mujer{background-image:url({% portal_files 'templates/img/mujer.svg' %});}

        .cc-selector-2 input:active +.drinkcard-cc, .cc-selector input:active +.drinkcard-cc{opacity: .9;}
        .cc-selector-2 input:checked +.drinkcard-cc, .cc-selector input:checked +.drinkcard-cc{
            -webkit-filter: none;
            -moz-filter: none;
            filter: none;
        }
        .drinkcard-cc{
            cursor:pointer;
            background-size:contain;
            background-repeat:no-repeat;
            display:inline-block;
            width:60px; height: 60px;
            -webkit-transition: all 100ms ease-in;
            -moz-transition: all 100ms ease-in;
            transition: all 100ms ease-in;
            -webkit-filter: brightness(1.8) grayscale(1) opacity(.5);
            -moz-filter: brightness(1.8) grayscale(1) opacity(.5);
            filter: brightness(1.8) grayscale(1) opacity(.5);
        }
        .drinkcard-cc:hover{
            -webkit-filter: brightness(1.2) grayscale(.5) opacity(1);
            -moz-filter: brightness(1.2) grayscale(.5) opacity(1);
            filter: brightness(1.2) grayscale(.5) opacity(1);
        }
	</style>

{% endblock %}

{% block body_tag %}
    <body class="blank {% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}"
          {% if page.variables.background %}style="background: url('{% portal_files '' %}{{ page.variables.background }}') no-repeat center center fixed; -webkit-background-size: cover; -moz-background-size: cover; -o-background-size: cover; background-size: cover;"{% endif %}
          {% if page.variables.background_color %}style="background-color: {{ page.variables.background_color }};"{% endif %}>
{% endblock %}

{% block main %}
    {% include "includes/social_detect.html" %}
	
	<div class="row">
        <div class="col-md-2 col-md-offset-{{ page.variables.offset|default:'2' }}" style="background-color: {{ page.variables.login_form_color|default:'rgba( 34, 34, 34, .7)' }}; min-height: 100vh; min-width: 320px; box-shadow: 0 0 10px 0px black;">
            <div class="row text-{{ page.variables.font_color|default:'white' }}">
                <div class="col-md-12">
                    <div class="hpanel m-l-sm m-r-sm" style="margin-top: 8%">
                        <div class="hidden-xs hidden-sm" style="min-height: 100px; background: unset; border: 0px;">
                            &nbsp;
                        </div>
                        <div class="text-center m-b-sm" style="background: unset; border: 0px;">
                            <img class="m-b-xs" style="max-width: {{ page.variables.logo_max_width|default:'200' }}px;" 
                                 src="{% if page.variables.logo %}{% portal_files page.variables.logo %}{% else %}{% portal_files 'templates/img/logo1-white.png' %}{% endif %}">
                            <h3>
                                <strong>{{ page.variables.title|default:'Lorem ipsum dolor'|safe }}</strong><br>
                            	<small>{{ page.variables.sub_title|default:'Lorem ipsum dolor'|safe }}</small>    
                            </h3>
                        </div>
                        <div style="background: unset; border: 0px; padding-top: 0px;">
                            <form method="post" id="register" action="#" class="m-t-lg">
                                {% csrf_token %}
                                {% if next %}
                                <input type="hidden" name="next" value="{{ next }}" />
                                {% endif %}
                                <input type="hidden" id="tzoffset" name="tzoffset" value="" />
                                {% if page.variables.register_form_template %}
                            	{% include page.variables.register_form_template %}
                            	{% else %}
                                <div class="form-group text-center">
                                    <h4 class="m-b font-extra-bold">Selecciona tu género</h4>
                                    <div class="cc-selector text-center">
                                        <input id="hombre" type="radio" name="gender" value="m"/>
                                        <label class="drinkcard-cc hombre m-r" for="hombre"></label>
                                        <input id="mujer" type="radio" name="gender" value="f"/>
                                        <label class="drinkcard-cc mujer m-l"for="mujer"></label>
                                    </div>
                                    <h4 class="m-b font-extra-bold">Agrega tu email y nombre completo</h4>
                                    <input type="text" id="email" name="email" placeholder="Correo electrónico" class="form-control m-b" value="" autocorrect="off" autocapitalize="none">
                                    <input type="text" id="full_name" name="full_name" placeholder="Nombre y apellido" class="form-control m-b">
                                    <button type="submit" class="btn btn-block btn-{{ page.variables.btncolor|default:'info' }} registerButton"><strong>{% trans 'login'|capfirst %}</strong></button>
                                </div>
                                {% endif %}
                                {% if social_auth %}
                                <hr/>
                                <div class="text-center">
                                    <h4 class="m-b">O usa tus redes sociales</h4>
                                    {% if facebook == True %}
                                    <a href="#" class="btn btn-social btn-facebook loginFacebook m-b-xs">
                                        <span class="fa fa-facebook"></span>Facebook
                                    </a>
                                    {% endif %}
                                    {% if google == True %}
                                    <a href="#" class="btn btn-social btn-google loginGoogle m-b-xs">
                                        <span class="fa fa-google"></span>Google
                                    </a>
                                    {% endif %}
                                    {% if twitter == True %}
                                    <a href="#" class="btn btn-social btn-twitter loginTwitter m-b-xs">
                                        <span class="fa fa-twitter"></span>Twitter
                                    </a>
                                    {% endif %}
                                    {% if linkedin == True %}
                                    <a href="#" class="btn btn-social btn-linkedin loginLinkedin m-b-xs">
                                        <span class="fa fa-linkedin"></span>LinkedIn
                                    </a>
                                    {% endif %}
                                    {% if instagram == True %}
                                    <a href="#" class="btn btn-social btn-instagram loginInstagram m-b-xs">
                                        <span class="fa fa-instagram"></span>Instagram
                                    </a>
                                    {% endif %}
                                    {% if yahoo == True %}
                                    <a href="#" class="btn btn-social btn-yahoo loginYahoo m-b-xs">
                                        <span class="fa fa-yahoo"></span>Yahoo
                                    </a>
                                    {% endif %}
                                    <div class="checkbox checkbox-info">
                                        <input type="checkbox" class="socialAcceptCheck" name="accept">
                                        <label>Acepto las condiciones de conexión </label><br>
                                        <small>(ver abajo)</small>
                                    </div>
                                    <div class="hpanel collapsed" style="margin-bottom: 0px; margin-top: 10px;">
                                        <div class="panel-heading hbuilt text-left" style="padding: 5px 10px;">
                                            <a class="showhide">
                                                <div class="panel-tools">
                                                    <i class="fa fa-chevron-up"></i>
                                                </div>
                                                <h6><i class="pe-7s-info text-info"></i> Condiciones de social media</h6>
                                            </a>
                                        </div>
                                        <div class="panel-body text-left" style="color: black">
                                            <small>
                                                Al hacer login con tu cuenta de redes sociales aceptas compartir la información pública de tu perfil con Zequenze y sus socios de conectividad
                                            </small>
                                        </div>
                                    </div>
                                </div>
                                {% endif %}
                            </form>
                        </div>
                        {% if page.variables.footer_text %}
                        <div class="m-l-sm m-t-xxl">
                            {% if page.variables.footer_logo %}
                            <img class="m-b-xs" style="max-width: {{ page.variables.footer_logo_max_width|default:'50' }}px;" src="{% if page.variables.logo %}{% portal_files '' %}{{ page.variables.footer_logo }}{% else %}{% portal_files 'templates/img/logo1-white.png' %}{% endif %}">
                            {% endif %}
                            <p>{{ page.variables.footer_text|default:'Lorem ipsum dolor sit amet, consectetur adipiscing elit sed do eiusmod tempor.'|safe }}</p>
                        </div>
                        {% endif %}
                    </div>
                    <div class="hpanel" style="margin-top: 10%"></div>
                </div>
            </div>
        </div>
	</div>
        
    {{ page.content|safe }}

{% endblock %}
        
{% block bottom_scripts %}
    {% if page.variables.welcome_url %}
    <script>
        {% if page.variables.welcome_url and request.path|slice:":6" == '/page/' %}
        facebook_querystring = '?{{ request.query_string }}&next={{ page.variables.welcome_url | urlencode }}%3F{{ request.query_string | urlencode }}';
        {% else %}
        facebook_querystring = '?{{ request.query_string }}&next={{ page.variables.welcome_url | urlencode }}';
        {% endif %}
        twitter_querystring = facebook_querystring;
        google_querystring = facebook_querystring;
        twitter_querysstring = facebook_querystring;
        linkedin_querystring = facebook_querystring;
        instagram_querystring = facebook_querystring;
        yahoo_querystring = facebook_querystring;
    </script>
    {% endif %}

    {% if social_auth %}{% include "includes/social_auth.html" %}{% endif %}
        
    {% if uam_api %}
        {% include "includes/uam_default.html" %}
    {% endif %}

    <script>
			$(function() {
                {% get_current_language  as LANGUAGE_CODE %}
                $('#datepicker').datepicker({
                    format: 'yyyy-mm-dd',
                    autoclose: true,
                    clearBtn: true,
                    disableTouchKeyboard: true,
                    startView: 2,
                    maxViewMode: 2,
                    language: '{{ LANGUAGE_CODE }}',
                    templates: {
                        leftArrow: '<b style="font-size: 22px;">&laquo;</b>',
                        rightArrow: '<b style="font-size: 22px;">&raquo;</b>'
                    }
                });
                $('.loginButton').click(function() {
                    $('.splash').addClass('dim').css('display', 'block');
                    $('#tzoffset').val((new Date()).getTimezoneOffset());
                    {% if uam_api and uam_api.address %}
                    var username = document.getElementById('username').value;
                    var password = document.getElementById('password').value;
                    uam_login(username, password);
                    {% else %}
                    $("#register").submit();
                    {% endif %}
                }); 
                 
                $('.registerButton').click(function() {
                    $('.splash').addClass('dim').css('display', 'block');
                    $.ajax({
                        url : "{{ request.main_path }}/register/?{{ request.query_string }}",
                        type : "POST",
                        data: $('#' + this.id).serialize(),
                        context: $('#' + this.id),
                        error: function (xhr, textStatus, errorThrown) {
                            $('.splash').css('display', 'none');
                            title = xhr.status + " - " + errorThrown;
                            msg = xhr.status + " - " + errorThrown + " - error received from the backend. Please try again later.";
                            swal(title, msg, "error");
                        },
                        success : function(json) {
                            $('.splash').css('display', 'none');
                            if(json.status) {
                                swal({
                                    title: "",
                                    {% if page.variables.welcome_logo and page.variables.welcome_logo != '' %}
                                    text: "<img class='m-b

Dynamic variables for integration services

Overview

Dynamic variable substitution allows you to insert contextual values into integration service configurations. Variables are referenced using double curly braces syntax: {{ variable_name }}.

Available Variables

The following variables are available for use in integration service configurations:

Variable Name Description
username Username entered or assigned to the user
password Password entered or assigned to the user
email Email address entered or configured for the user
user__first_name First name of the user. If the user has both a first and middle name, both will be included
user__last_name Last name of the user. If the user has two last names, both will be included
user__first_first_name First name of the user only
user__second_first_name Middle name of the user
user__first_last_name First last name of the user
user__second_last_name Second last name of the user
user__external_id External ID field for the user
user__date_joined User's join date
user__aaa_profile Name of the first AAA profile configured for the user
org_short_name Short name of the portal page organization
location Name of the current location of the user
now_formatted Current date/time in ISO format: YYYY-MM-DDTHH:MM:SSZ

Usage Examples

To use a variable in your integration service configuration, enclose the variable name in double curly braces:

User: {{ username }}
Email: {{ email }}
Location: {{ location }}
Timestamp: {{ now_formatted }}

Dynamic Variables in Redirection URLs

TBC

Mobile WiFi Offload

Overview

Mobile WiFi Offload enables UE devices (smartphones) to utilize WiFi-based connectivity instead of cellular mobile connections. This technology serves as a strategic tool for Mobile Network Operators (MNOs) and Mobile Virtual Network Operators (MVNOs) to optimize their network operations and service offerings.

Key Benefits

Lower Cost-per-Bit

Particularly advantageous for MVNOs, this technology dramatically reduces variable costs associated with subscriber mobile data usage by routing traffic through proprietary WiFi networks. This approach is especially effective in low-mobility locations where most data consumption occurs, such as offices and homes.

Enhanced Indoor Coverage

WiFi Offload provides MNOs and MVNOs with a cost-effective solution to improve indoor coverage without the complexity and expense of small-cell deployments.

Service Differentiation

Operators worldwide leverage WiFi Offload to distinguish their service offerings from traditional MNO services by providing innovative features when users connect via WiFi, including:

Architecture Options

There are several ways to implement Mobile WiFi Offload, depending on the level of integration desired with the mobile network infrastructure.

SIM-based WiFi Offload

This architecture leverages SIM card credentials for WiFi network authentication. The UE authenticates using quintuplets stored on the SIM card, which are validated against the mobile network's HSS database.

byEG7sk4YW5CUlcE-tmpqh4bmzwn.png
GVhfidIBFSNkKDJi-tmpvt1vsa7z.png
BfUS5hxVztzAJVlA-tmprozte5ge.png
bECGqKiNlbXMccwj-tmp95ko5dpb.png

Note: The diagram above presents a simplified architecture (excluding WAG and WAG integration to mobile network PGW) for clarity.

Implementation Challenges

Advantages

Once properly configured on the UE, WiFi offload operates seamlessly without requiring additional subscriber intervention.

EAP-based Authentication (UserID/Password)

This alternative approach proves particularly attractive for MVNO light deployments, implementing WiFi Offload through traditional UserID/Password authentication mechanisms.

fqzzdFYitJGIBxQS-tmp99ce2-bw.png
DoO484rmGhxssKNy-tmp8h71bqr1.png
TxGifek6KCAwTG2R-tmpf89scghr.png
PHUXziTVe4FqrFp8-tmpn4aetp-4.png

This architecture replaces SIM-based authentication with conventional UserID/Password validation against MVNO databases (CRM, AAA systems, etc.).

Implementation Challenges

Advantages

Testing FCM Push Notification service

Overview

To test Firebase Cloud Messaging (FCM) push notifications in GATE, you'll need two essential components:

This guide walks you through the complete testing process within GATE's interface.

Prerequisites

Before beginning, ensure you have:

Step-by-Step Testing Process

Step 1: Access Push Notification Service

  1. Navigate to the Settings main menu
  2. Select the Services sub-menu

service-submenu.png

service-submenu.png

service-submenu.png

service-submenu.png

  1. Search for push in the services list
  2. Click on Push notification

push-notification.png

push-notification.png

push-notification.png

push-notification.png

Step 2: Configure API Key

  1. On the Push Notification Service screen, enter your Firebase API key
  2. Click the Save and continue editing button to save your configuration

api-key.png

api-key.png

api-key.png

api-key.png

Step 3: Access Service Test Interface

Click on the Service test tab at the top of the Push Notification Service screen to access the testing interface.

Screenshot-from-2020-04-03-15-02-17.png

Screenshot-from-2020-04-03-15-02-17.png

Screenshot-from-2020-04-03-15-02-17.png

Screenshot-from-2020-04-03-15-02-17.png

Step 4: Configure Test Notification

Fill in the following required fields:

Once all fields are completed, click the Proceed button to send the test notification.

Screenshot-from-2020-04-03-14-58-15.png

Screenshot-from-2020-04-03-14-58-15.png

Screenshot-from-2020-04-03-14-58-15.png

Screenshot-from-2020-04-03-14-58-15.png

Step 5: Verify Results

If everything went ok and the test is successful:

success.png

success.png

success.png

success.png

Troubleshooting

If notifications fail to deliver, verify:

User Scripts

Overview

User Scripts are Python-based automation tools that execute operations on a predetermined list of users. Users can be selected either through custom filters configured by the administrator or by importing rows from a CSV file. These scripts enable you to:

Note: User Scripts must be executed manually by the administrator.

Accessing User Scripts

To access the User Scripts configuration screen:

  1. Navigate to the Users main menu
  2. Select Scripts from the sub-menu

The Users scripts configuration screen can be found under the Scripts sub-menu on the Users main menu:

Screenshot 2024-05-06 at 12.04.55.png

Screenshot 2024-05-06 at 12.04.55.png

Screenshot 2024-05-06 at 12.04.55.png

User Object Reference

Overview

The script execution context provides access to a special User object, referenced as either user or obj. This object allows you to:

Attributes

The user object exposes the following attributes:

Name Type Description
obj_id integer Numeric unique identification of the current user
username string Used to read or change the username field of the current user (read/write)
username_length integer Used to read or change the username_length field of the current user (read/write)
first_name string Used to read or change the first_name field of the current user (read/write)
last_name string Used to read or change the last_name field of the current user (read/write)
email string Used to read or change the email field of the current user (read/write)
external_id string Used to read or change the external_id field of the current user (read/write)
klass string Used to read or change the klass field of the current user (read/write)
is_active boolean Used to activate or deactivate the current user (read/write)
date_joined datetime Date/time of user joined (read-only)
first_login datetime Date/time of user first login (read-only)
last_login datetime Date/time of user last login (read-only)
expiration datetime Date/time of user expiration (read/write)
last_update datetime Date/time of user last update (read/write)
organization_id integer Numeric identification of the user organization
avatar_url string Used to read or change the avatar_url field of the current user (read/write)
description string Used to read or change the description field of the current user (read/write)

Methods

The user object provides the following methods for managing profiles and attributes:

profile_add()

Adds a specific profile to the user. You can add using the id or the short name of the profile.

Parameters:

Note: Use either id OR short_name, not both.

Returns:

Examples:

# Add profile by ID
updated, message = user.profile_add(id=12)

# Add profile by short name
updated, message = user.profile_add(short_name="aaa-01")

profile_remove()

Removes a specific profile from the user. You can remove using the id or the short name of the profile.

Parameters:

Note: Use either id OR short_name, not both.

Returns:

Examples:

# Remove profile by ID
updated, message = user.profile_remove(id=12)

# Remove profile by short name
updated, message = user.profile_remove(short_name="aaa-01")

attribute_add()

Adds a specific attribute to the user. You must provide the attribute name, value, and optionally an operation.

Parameters:

Returns:

Example:

updated, message = user.attribute_add(
    attr='Username',
    value="zequenze",
    operation='+='
)

attribute_remove()

Removes a specific attribute from the user. You must provide the attribute name, value, and optionally an operation.

Parameters:

Returns:

Example:

updated, message = user.attribute_remove(
    attr='Username',
    value="zequenze",
    operation='+='
)

check_attribute_add()

Adds a specific check attribute to the user. You must provide the attribute name, value, and optionally an operation.

Parameters:

Returns:

Example:

updated, message = user.check_attribute_add(
    attr='Max-Daily-Session',
    value=4,
    operation='=='
)

check_attribute_remove()

Removes a specific check attribute from the user. You must provide the attribute name, value, and optionally an operation.

Parameters:

Returns:

Example:

updated, message = user.check_attribute_remove(
    attr='Max-Daily-Session',
    value=4,
    operation='=='
)