Applications


Installing Oracle SQL*Loader on CentOS without full server installation

Assuming you do not want to install the full Oracle suite on the current machine, the following guide will show you how we installed Oracle SQL*Loader on CentOS release 6.7 (Final) using specific packages only. For this tutorial to be successful though, you need to have somewhere the full installation somewhere else as we need to to copy two files from there (the sqlldr executable and the binary file ulus.msb).

On the machine that has the full Oracle suite installation

Execute locate sqlldr to find the location of sqlldr binary file. If it does not return a result use the following:

find / -name "sqlldr" 2>/dev/null

The above command will search for sqlldr in the whole filesystem and it will redirect all errors to /dev/null so that you will not get spammed by access right issues etc.

After you found the file, copy it to the machine you want to use it from, the exact location will be given below.

Then, you need to find ulus.msb using the following command

find / -name "ulus.msb" 2>/dev/null

Copy that file to the machine you want to use sqlldr from as well, the exact location will be given below.

On the machine you want to execute sqlldr from and does not have the full Oracle suite installed

After we get the above files, we need to download the appropriate files from http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html.

To access the files you need to click the Accept License Agreement radio button which means you agree to the ‘OTN Development and Distribution License Agreement for Instant Client‘ and finally you need to be logged in.

To login visit this page https://login.oracle.com/mysso/signon.jsp. If you are not registered yet, you need to create an account with a valid email from here https://profile.oracle.com/myprofile/account/create-account.jspx.

Once you are done with these steps, you need to download two rpm files for the client:

  • oracle-instantclientRELEASE-basic-VERSION.x86_64.rpm
  • oracle-instantclientRELEASE-sqlplus-VERSION.x86_64.rpm
  • oracle-instantclientRELEASE-jdbc-VERSION.x86_64.rpm
  • oracle-instantclientRELEASE-devel-VERSION.x86_64.rpm

The latest version at the time this document was written was version 12.1.0.2.0 and so we downloaded the following files:

Oracle-SQLLOADER

The files were then moved to the server that was to be installed and using an account with administration rights, they were installed using the the rpm -ivh  command:

rpm -ivh oracle-instantclient12.1-basic-12.1.0.2.0-1.x86_64.rpm
rpm -ivh oracle-instantclient12.1-sqlplus-12.1.0.2.0-1.x86_64.rpm
rpm -ivh oracle-instantclient12.1-jdbc-12.1.0.2.0-1.x86_64.rpm
rpm -ivh oracle-instantclient12.1-devel-12.1.0.1.0-1.x86_64.rpm

In case you placed the two rpm files in a folder by themselves you can install both at the same time using the following command which is version independent:

rpm -ivh oracle-instantclient*

The packages will be installed in /usr/lib/oracle/RELEASE. Use ls /usr/lib/oracle/ to find the RELEASE number of your installation (in case you are not sure) and copy the path to client64 folder. In our case it was /usr/lib/oracle/12.1/client64. We will use this path as the ORACLE_HOME variable later on.

Following, you need to setup the environment variables for bash.
If you want to set the environment variables in your account only, add the following in your ~/.bash_profile:

ORACLE_HOME=/usr/lib/oracle/12.1/client64
PATH=$ORACLE_HOME/bin:$PATH
LD_LIBRARY_PATH=$ORACLE_HOME/lib
export ORACLE_HOME
export LD_LIBRARY_PATH
export PATH

And execute source ~/.bash_profile  to reload your .bash_profile (alternatively, logout and login again).

If you want to set the environment variables for all users, create a .sh file in /etc/profile.d/ (e.g. oracle.sh) and add the content above in that file.

Installing SQL*Loader

Copy the sqlldr executable we got from the other machine to $ORACLE_HOME/bin/ (/usr/lib/oracle/RELEASE/client64/bin/). In our case it was /usr/lib/oracle/12.1/client64/bin/.

Then, copy the ulus.msb binary file to $ORACLE_HOME/rdbms/mesg/ (/usr/lib/oracle/RELEASE/client64/rdbms/mesg/). In our case it was /usr/lib/oracle/12.1/client64/rdbms/mesg/. If the folder does not exist, create it.

Finally, create a text file named tnsnames.ora in $ORACLE_HOME/network/admin/ (/usr/lib/oracle/RELEASE/client64/network/admin/). In our case it was /usr/lib/oracle/12.1/client64/network/admin/. If the folder does not exist, create it.

In tnsnames.ora we need to add the connection information for our databases and name them. The reason we need to do this, is because sqlldr does not accept the connection string from the command line like sqlplus as seen at the end of this tutorial here http://bytefreaks.net/applications/oracle/installing-oracle-sqlplus-on-centos. The structure of the connection string though, is exactly the same as the one used in sqlplus. Following the example from the other guide and the naming convention for the tnsnames.ora file, the format of the string would be:

connectionname=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=HOSTNAME_OR_IP)(PORT=1521))(CONNECT_DATA=(SID=MY_SID)))

SQL*Loader will be available for use using the sqlldr command.

The following example will connect to server with

  • IP 192.168.10.1,
  • on the SID ASID
  • using the user myuser
  • the password pass
  • and we will use the control file profitLoader.ctl  to configure what the program will do once it starts (profitLoader.ctl is a file we wrote that describes the structure of the input file and the table to which the data should be added, code below for reference)

In tnsnames.ora file will add the following line:

myconnection=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.10.1)(PORT=1521))(CONNECT_DATA=(SID=ASID)))"

and we will use the connection information as follows:

sqlldr "username[alias]/password@connectionname" control=profitLoader.ctl;

In our example we do not have an alias, so we will remove the [] part and use the rest. the command will be formed as follows:

sqlldr "myuser/pass@myconnection" control=profitLoader.ctl;

The content of profitLoader.ctl is as follows:

LOAD DATA
INFILE '/dev/shm/profitLoader.dat'
APPEND INTO TABLE PROFIT
FIELDS TERMINATED BY ','
(USERID, TRANSACTIONID, PROFIT)

In our scenario, we have a csv file that contains 3 columns and we want to load all the data to a table called PROFIT.


Installing Oracle SQL*Plus on CentOS 4

The following guide will show you how we installed Oracle SQL*Plus on CentOS release 6.7 (Final).

First of all we need to download the appropriate files from http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html.

To access the files you need to click the Accept License Agreement radio button which means you agree to the ‘OTN Development and Distribution License Agreement for Instant Client‘ and finally you need to be logged in.

To login visit this page https://login.oracle.com/mysso/signon.jsp. If you are not registered yet, you need to create an account with a valid email from here https://profile.oracle.com/myprofile/account/create-account.jspx.

Once you are done with these steps, you need to download two rpm files for the client:

  • oracle-instantclientRELEASE-basic-VERSION.x86_64.rpm
  • oracle-instantclientRELEASE-sqlplus-VERSION.x86_64.rpm

The latest version at the time this document was written was version 12.1.0.2.0 and so we downloaded the following files:

Oracle-SQLPLUS Download page

The files were then moved to the server that was to be installed and using an account with administration rights, they were installed using the the rpm -ivh  command:

rpm -ivh oracle-instantclient12.1-basic-12.1.0.2.0-1.x86_64.rpm
rpm -ivh oracle-instantclient12.1-sqlplus-12.1.0.2.0-1.x86_64.rpm

In case you placed the two rpm files in a folder by themselves you can install both at the same time using the following command which is version independent:

rpm -ivh oracle-instantclient*.rpm

The packages will be installed in /usr/lib/oracle/RELEASE. Use ls /usr/lib/oracle/ to find the RELEASE number of your installation (in case you are not sure) and copy the path to client64 folder. In our case it was /usr/lib/oracle/12.1/client64. We will use this path as the ORACLE_HOME variable later on.

Following, you need to setup the environment variables for bash.
If you want to set the environment variables in your account only, add the following in your ~/.bash_profile:

ORACLE_HOME=/usr/lib/oracle/12.1/client64
PATH=$ORACLE_HOME/bin:$PATH
LD_LIBRARY_PATH=$ORACLE_HOME/lib
export ORACLE_HOME
export LD_LIBRARY_PATH
export PATH

And execute source ~/.bash_profile  to reload your .bash_profile (alternatively, logout and login again).

If you want to set the environment variables for all users, create a .sh file in /etc/profile.d/ (e.g. oracle.sh) and add the content above in that file.

SQL*Plus will be available for use using the sqlplus command.

sqlplus "username/password@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=HOSTNAME_OR_IP)(PORT=1521))(CONNECT_DATA=(SID=MY_SID)))"

The following example will connect to server with

  • IP 192.168.10.1,
  • on the SID ASID
  • using the user myuser
  • and the password pass
sqlplus "myuser/pass@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.10.1)(PORT=1521))(CONNECT_DATA=(SID=ASID)))"

Installing CMS (Contest Management System) on multiple servers

For the needs of the 24th BOI (Balkan Olympiad in Informatics) that will be held in Cyprus in June 2016 we setup CMS (Contest Management System)  on multiple servers.

We used Ubuntu 14.04 LTS (Trusty Tahr) for the OS of the servers, the installations were made with encrypted LVM.

We used 3 servers in total, the first (alpha) would serve as a master that holds all services for the competition except for the workers that test the submissions. The other two (beta and gamma) were setup to hold the workers.

On Alpha:

We installed various packages that are needed for the installation of the system

sudo apt-get install build-essential fpc postgresql postgresql-client gettext python2.7 python-setuptools python-tornado python-psycopg2 python-sqlalchemy python-psutil python-netifaces python-crypto python-tz python-six iso-codes shared-mime-info stl-manual python-beautifulsoup python-mechanize python-coverage python-mock cgroup-lite python-requests python-werkzeug python-gevent patool;

We downloaded the CMS and created the basic configuration.

#Download stable version from GitHub
wget https://github.com/cms-dev/cms/releases/download/v1.2.0/v1.2.0.tar.gz
#Extract the archive
tar -xf v1.2.0.tar.gz 
cd cms
#Copy the sample configuration files to be modified
cp config/cms.conf.sample config/cms.conf
cp config/cms.ranking.conf.sample config/cms.ranking.conf

cms.conf

Using a text editor we modified file config/cms.conf. The changes we did are the following:

  • We replaced "database": "postgresql+psycopg2://cmsuser:password@localhost/database"
    With the username, the password and database name we will use for our database configuration in a while. e.g.
    "postgresql+psycopg2://myuser:myPassword@alpha/mydatabase". Note: we chose a username and database name with no capital letters.
  • We replaced "secret_key": "8e045a51e4b102ea803c06f92841a1fb" with another random 32 character string containing a-z and 0-9 (hex characters)
  • We replaced "rankings": ["http://usern4me:passw0rd@alpha:8890/"] to some other username password configuration. e.g. "rankings": ["http://myUsern4me:myPassw0rd@localhost:8890/"]
  • We changed the "_section": "AsyncLibrary", to hold the configuration of the services as follows
"_section": "AsyncLibrary",

"core_services":
{
    "LogService": [["alpha", 29000]],
    "ResourceService": [["alpha", 28000], ["beta", 28000], ["gamma", 28000]],
    "ScoringService": [["alpha", 28500]],
    "Checker": [["alpha", 22000]],
    "EvaluationService": [["alpha", 25000]],
    "Worker": [["beta", 26000], ["beta", 26001], ["beta", 26002], ["beta", 26003], ["gamma", 26000], ["gamma", 26001], ["gamma", 26002], ["gamma", 26003]],
    "ContestWebServer": [["alpha", 21000]],
    "AdminWebServer": [["alpha", 21100]],
    "ProxyService": [["alpha", 28600]],
    "PrintingService": [["alpha", 25123]]
},

"other_services":
{
    "TestFileCacher": [["alpha", 27501]]
},

We replaced all instances of localhost with alpha . We did not put any workers on alpha for fairness.

cms.ranking.conf

Using a text editor we modified file config/cms.ranking.conf. The changes we did were to set

    "username":   "usern4me",
    "password":   "passw0rd",

to the values we set in the file cms.conf at the section rankings. In our example the values would be myUsern4me and myPassw0rd.

Setting up the system

After we were done with the configuration changes, we executed the following ./setup.py build so that the system would perform the basic checks and setup. Then, we executed sudo ./setup.py install to make the installation.
Following, we added the user we will use to start the competition system to the user group of the cms sudo usermod -a -G cmsuser george.

And we switched to the DB user to take the necessary actions.

Database Configuration

sudo su - postgres to switch to the DB user.
We created the DB user we defined in the cms.conf file before (myuser) with the password we defined using the command createuser myuser -P, the password in our case is myPassword.
After that, we created our database (mydatabase) and we assigned our user to be the owner with full access rights of that database using the following commands: createdb -O myuser mydatabase then psql mydatabase -c 'ALTER SCHEMA public OWNER TO myuser' finally psql mydatabase -c 'GRANT SELECT ON pg_largeobject TO myuser'.

Allowing connections from other machines

We needed to find the hba file for psql to allow incoming connections from other machines. We got the location of the file using psql -t -P format=unaligned -c 'show hba_file';. In our case the location was /etc/postgresql/9.3/main/pg_hba.conf. Using a text editor we added at the end of the file the following line:

host  mydatabase  myuser  0.0.0.0/0  md5

The above line instructs the system to allow our user to connect from any IP to our database.
For better security, you can use the following lines instead:

host  mydatabase  myuser  beta  md5
host  mydatabase  myuser  gamma  md5

Then we used psql -t -P format=unaligned -c 'show config_file'; which gave use the file /etc/postgresql/9.3/main/postgresql.conf, using a text editor we changed #listen_addresses = 'localhost' to listen_addresses = '*' to enable listening on all network devices.

On the terminal, we exited the console of the database user, we re-installed the cms using sudo ./setup.py install to create all database tables and we rebooted the machine using sudo reboot to apply group changes to the user as well.

Once the system reboots, execute cmsInitDB to create the tables for our database.

Create a file named updateHosts.sh and copy the following code in it. We will use it to update the host file of the machine so that it will identify the external IP of the machine first.

#!/bin/bash

FILE="/etc/hosts";
BACKUP="$FILE.original";

if [ -f "$BACKUP" ]; then
	echo "--- --- --- BackUp exists --- --- ---";
else
	cp "$FILE" "$BACKUP";
	if [ $? -ne 0 ]; then
		echo -e "--- --- --- Could not create backUp. Terminating! --- --- ---";
		exit -1;
	fi
	echo "--- --- --- BackUp Created --- --- ---";
fi

echo "--- --- --- Original '$FILE' --- --- ---";
cat "$BACKUP";

DEFAULT_DEVICE="p7p1";
DEVICE=${1:-$DEFAULT_DEVICE};
IP=`ifconfig "$DEVICE" | grep "inet addr" | cut -d ':' -f 2 | cut -d ' ' -f 1`;
HOST=`hostname`;

> "$FILE";
while read LINE; do
	if [[ "$LINE" == *"$HOST"* ]]; then
		echo -ne "$IP\t" >> "$FILE";
		echo "$LINE" | cut -f2- >> "$FILE";
	else
		echo "$LINE" >> "$FILE";
	fi
done < "$BACKUP";

echo "--- --- --- Updated '$FILE' --- --- ---";
cat "$FILE";

exit 0;

Execute chmod +x updateHosts.sh to make the script into an executable one.

The script needs to know the name of the network device it is supposed to tamper with. Type ifconfig in your terminal to find the available device names. The one you most likely are interested in would have an IP similar to the one of your PC or smartphone e.g. inet addr:192.168.10.5. If the device is an ethernet device, then you will find the following next to the device name Link encap:Ethernet. The name you need to copy could be eth0 or p7p1 or something similar to these.

Once you get the name execute sudo ./updateHosts.sh DEVICE_NAME and replace DEVICE_NAME with the name you copied in the previous step.

Starting the system on Alpha:

In a terminal execute cmsAdminWebServer 0 to start the administration server and use it to create a new contest from http://alpha:8889/.

Once you created you competition from http://alpha:8889/, open up 3 terminals to alpha.

On the first one execute cmsLogService 0 to start the logging service.

On the second execute cmsRankingWebServer to start the Ranking Server that can be reached at http://alpha:8890/.

And on the third one cmsResourceService 0 -a 1 to start the competition we just created along with all services that are needed for the competition from alpha. The competition can be reached at http://alpha:8888/.

Setting up the live replication of the database:

Switch to the postgres user sudo su - postgres and create an ssh key ssh-keygen.
Copy on both servers the key you just created using ssh-copy-id contest@beta and ssh-copy-id contest@gamma.

Next we will create a user called replicator that can be used solely for the replication process and set a random password of it:

psql -c "CREATE USER replicator REPLICATION LOGIN CONNECTION LIMIT -1 ENCRYPTED PASSWORD 'randomPassword';"

Then edit the file /etc/postgresql/9.3/main/pg_hba.conf (we found the location of pg_hba.conf using the command psql -t -P format=unaligned -c 'show hba_file';) and add the following lines:

host    replication     replicator     beta   md5
host    replication     replicator     gamma   md5

This will allow user replicator to connect from the machines beta and gamma.
You could give the following line, instead of the two above:

host    replication     replicator     0.0.0.0/0   md5

This will allow user replicator to connect from ANY IP.
Also, if you know from beforehand the IPs that beta and gamma are going to have, it is best to replace the above with two lines containing the IPs of the two machines as it is more secure.

host    replication     replicator     IP_address_of_beta/32   md5
host    replication     replicator     IP_address_of_gamma/32   md5

Following, using psql -t -P format=unaligned -c 'show config_file'; we got the path to the file postgresql.conf (/etc/postgresql/9.3/main/postgresql.conf) which we edited using a text editor. The following changes were made to the file:

  • We uncommented and changed #wal_level = minimal to wal_level = hot_standby
  • We uncommented and changed #archive_mode = off to archive_mode = on
  • We uncommented and changed #archive_command = '' to archive_command = 'cd .'
  • We uncommented and changed #max_wal_senders = 0 to max_wal_senders = 10
  • We uncommented and changed #hot_standby = off to hot_standby = on

Save the file and execute service postgresql restart to apply the changes.

Execute psql -c "ALTER USER replicator WITH CONNECTION LIMIT -1"; to remove the limit of connections for our replication user.
You can verify the settings using psql -c "SELECT rolname, rolconnlimit FROM pg_roles";, this command will show you the connection limits per user. If the value is -1 then it means that there is no restriction.

On Beta and Gamma:

We installed various packages that are needed for the installation of the system, we intended in using these machines both as the workers for the competition but also as live backups in case alpha goes down. Later on, we will enable live copying of the database in alpha to these machines.

sudo apt-get install build-essential fpc postgresql postgresql-client gettext python2.7 python-setuptools python-tornado python-psycopg2 python-sqlalchemy python-psutil python-netifaces python-crypto python-tz python-six iso-codes shared-mime-info stl-manual python-beautifulsoup python-mechanize python-coverage python-mock cgroup-lite python-requests python-werkzeug python-gevent patool;

We switched to the postgres user sudo su - postgres and we stopped the postgres service service postgresql stop.

Then edit the file /etc/postgresql/9.3/main/pg_hba.conf (we found the location of pg_hba.conf using the command psql -t -P format=unaligned -c 'show hba_file';) and add the following line:

host    replication     replicator     alpha  md5

Following, using psql -t -P format=unaligned -c 'show config_file'; we got the path to the file postgresql.conf (/etc/postgresql/9.3/main/postgresql.conf) which we edited using a text editor. The following changes were made to the file:

  • We uncommented and changed #listen_addresses = 'localhost' to listen_addresses = 'localhost,beta' for beta and to listen_addresses = 'localhost,gamma' for gamma
  • We uncommented and changed #wal_level = minimal to wal_level = hot_standby
  • We uncommented and changed #archive_mode = off to archive_mode = on
  • We uncommented and changed #archive_command = '' to archive_command = 'cd .'
  • We uncommented and changed #max_wal_senders = 0 to max_wal_senders = 1
  • We uncommented and changed #hot_standby = off to hot_standby = on

Execute pg_basebackup -h alpha -D /var/lib/postgresql/9.3/main/ -U replicator -v -P --xlog-method=stream to copy the database from Alpha. If you get the error pg_basebackup: directory "/var/lib/postgresql/9.3/main/" exists but is not empty then delete the contents of the folder with the command rm -rf /var/lib/postgresql/9.3/main/* and try again. You will be prompted for a password, use the random password we assigned the user replicator before. In this scenario it will be randomPassword.

Afterwards, create the file /var/lib/postgresql/9.3/main/recovery.conf and add the following content:

standby_mode = 'on'
primary_conninfo = 'host=alpha port=5432 user=replicator password=randomPassword sslmode=require'
trigger_file = '/tmp/postgresql.trigger'

Finally, start the service using service postgresql start and exit to stop using the postgres user.

Starting the workers on beta and gamma:

First, we downloaded the CMS:

#Download stable version from GitHub
wget https://github.com/cms-dev/cms/releases/download/v1.2.0/v1.2.0.tar.gz
#Extract the archive
tar -xf v1.2.0.tar.gz

Then, we copied the configuration files from alpha. Specifically, we copied the configuration files from alpha to 'cms/config/cms.conf' and 'cms/config/cms.ranking.conf' using the following commands:

scp contest@alpha:~/cms/config/cms.conf ~/cms/config/cms.conf
scp contest@alpha:~/cms/config/cms.ranking.conf ~/cms/config/cms.ranking.conf

Afterwards, we entered the CMS folder cd cms, build it ./setup.py build and installed it sudo ./setup.py install.
Following, we added the user we will use to start the competition system to the user group of the cms sudo usermod -a -G cmsuser george. We rebooted the machine using sudo reboot to apply all changes including the group change to the user.

Then we copied the updateHosts.sh file from alpha using scp contest@alpha:~/updateHosts.sh ~/updateHosts.sh, executed chmod +x updateHosts.sh to make the script into an executable one. And, we executed sudo ./updateHosts.sh DEVICE_NAME. Like before you need to replace DEVICE_NAME with the network device name that will be used for the communication.

To start the workers, execute cmsResourceService 1 -a 1 on beta and cmsResourceService 2 -a 1 on gamma.

Next steps

In case you have DNS problems, modify the file /etc/hosts and add the following entries to it:

10.1.10.3       alpha
10.1.10.4       beta
10.1.10.5       gamma

In the end it should look something similar to this:

127.0.0.1    localhost
10.1.10.3    alpha
10.1.10.4    beta
10.1.10.5    gamma

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

WPML: How to add the Languages’s list in a WordPress menu

The code in this article can be used to create a WPML language switcher that will be attached at the bottom of a specific WordPress menu. Each Language will be a sub-menu item of a new menu item named Languages.

Menu Languages Options

You should add this code to your theme’s functions.php file. It is highly recommended to add this change to the functions.php of child theme.

Choosing which languages to show

By default, the following code will be showing ALL available languages, including the currently active one. We did that as we only have two languages in our website and it would weird to: (a) have only one sub-menu item in the Languages menu item or (b) remove the Languages menu item and place the inactive language item directly on the menu.

To show only the inactive languages, change the line that contains $showActive = true; to $showActive = false;.

Choosing which menu to show the languages on

To choose the menu on which you want the languages to appear in, you need to set the value of $themeLocation = 'main'; to the theme location that the menu has in your current theme.

For the default WordPress themes (Twenty Sixteen, Twenty Fifteen, etc.) setting the value to primary as follows $themeLocation = 'primary'; will add the languages to the main menu.

For the customizr theme (this also works for the pro version),  setting the value to main as follows $themeLocation = 'main'; will add the languages to the main menu.

For other themes, you need to find the name of the menu you want to append the languages to. To do so change the line $showMenuName = false; to $showMenuName = true;. This will print at the beginning of your menu the location of the menu inside the theme using a <pre> tag. Copy that value to the $themeLocation variable, change the $showMenuName back to $showMenuName = false; and you are done.

Menu Location

/* Add languages menu to main menu */
add_filter('wp_nav_menu_items', 'new_nav_menu_items', 10, 2);
function new_nav_menu_items($items, $args) {
    
    $showActive = true;
    $themeLocation = 'main';
    $showMenuName = false;

    if ($showMenuName) {
        echo "<pre>"; print_r(get_object_vars($args)['theme_location']); echo "</pre>";
    }

    // get languages
    $languages = apply_filters( 'wpml_active_languages', NULL, 'skip_missing=0' );

    if ( $languages && $args->theme_location == $themeLocation) {

        if(!empty($languages)){

            $items = $items . '<br/><li class="menu-item">Languages';
            $items = $items . '<ul class="dropdown-menu">';
            foreach($languages as $l){

                if(!$l['active'] || $showActive){
                    $items = $items . '<li class="menu-item"><a href="' . $l['url'] . '"><img src="' . $l['country_flag_url'] . '" height="12" alt="' . $l['language_code'] . '" width="18" /> ' . $l['native_name'] . '</a></li>';
                }
            }
            $items = $items . '</li></ul>';
        }
    }

    return $items;
}

The above code was generated using the documentation examples of WPML.