Kubernetes Administration

Wanneer je gebruik maakt van kubectl om je Kubernetes cluster te beheren, en je wilt bijvoorbeeld een selectie maken van pods die een andere status hebben dan running dan kun je gebruik maken van de field selector. Dit argument maakt het mogelijk om snel meerdere bijvoorbeeld gecrashte pods te killen:

kubectl get pods –field-selector=phase.status!=Running

kubectl delete pods –field-selector=phase.status!=Running

Google PageSpeed Apache module

Het wordt weer eens tijd voor een post, afgelopen dagen wat aan hannesen geweest met google pagespeed optimalisatie en de webservers meteen voorzien van pagespeed_mod. De installatie ging vrij soepel (op Debian, dpkg -i pagespeed.mod.dpkg). De configuratie ging ook bijna vanzelf wat filters hier en daar aanzetten, en klaar 🙂

Maar wat doet deze apache module nu precies? De naam zegt het al het verbeterd de snelheid van het laden van website, hoe doen ze dit dan?

  • Afbeeldingen worden opnieuw gecomprimeerd of omgezet in een andere formaat;
  • Comments overbodige tekens etc worden uit de html, css en javascripts gehaald;
  • Systeem activeert een cache systeem wat zorgdraagt dat de boel snel blijft laden;

Kortom leuke module en zeker zeker zeer handig! Hieronder mijn pagespeed na een uurtje tweaken:

Mijn apache module configuratie:

<IfModule pagespeed_module>

ModPagespeed on
ModPagespeedInheritVHostConfig on
AddOutputFilterByType MOD_PAGESPEED_OUTPUT_FILTER text/html
ModPagespeedFileCachePath            “/var/cache/mod_pagespeed/”
ModPagespeedLogDir “/var/log/pagespeed”
ModPagespeedSslCertDirectory “/etc/ssl/certs”

ModPagespeedRewriteLevel CoreFilters
ModPagespeedEnableFilters prioritize_critical_css
ModPagespeedEnableFilters defer_javascript
ModPagespeedEnableFilters sprite_images
ModPagespeedEnableFilters convert_png_to_webp,convert_jpeg_to_webp
ModPagespeedEnableFilters collapse_whitespace,remove_comments

ModPagespeedFileCacheInodeLimit        500000
ModPagespeedImageRecompressionQuality 60
ModPagespeedJpegRecompressionQuality -1
ModPagespeedWebpRecompressionQuality -1

<Location /pagespeed_admin>

Order allow,deny
Allow from localhost
Allow from 127.0.0.1
SetHandler pagespeed_admin

</Location>

<Location /pagespeed_global_admin>

Order allow,deny
Allow from localhost
Allow from 127.0.0.1
SetHandler pagespeed_global_admin

</Location>

ModPagespeedEnableCachePurge on
ModPagespeedStatisticsLogging on
ModPagespeedMessageBufferSize 100000

</IfModule>

Meer informatie over de module zelf:

https://developers.google.com/speed/pagespeed/module/

Samsung Galaxy S7 Edge

samsung-galaxy-s7-edge_1

Nou sinds begin deze week in het bezit van de Samsung Galaxy S7 Edge (Mijn Samsung Edge S6+ was op de grond gevallen en scherm kapot, moeilijk leverbaard etc..) Anyhow ik moet zeggen dat ik aangenaam verrast bent en vooral door de accuduur! Kan makkelijk hele dag mee zonder te laden, en ik gebruik hem vrij veel. Camera is ook top, nu niet meer laten vallen. 🙂

Edit: Na een kleine 2 weken gebruiken van de s7 edge begin ik steeds blijer te worden van de accu, niet maar laden tussendoor wat een verademing!

Xamarin.iOS controle en toegang camera

move-to-ios-icon

Wanneer je een app ontwikkeld en je wilt hierin bijvoorbeeld gebruik maken van een bepaalde privacy gevoelige data bronnen (camera, microfoon, gallery, contacts etc.) Dan moet je hier de gebruiker om toestemming voor vragen, in dit stukje laat ik zien hoe je de camera toegang kunt controleren van de app, en hoe je bij de eerste keer opstarten van de app de gebruiker om toestemming kunt vragen om de camera te gebruiken. Let op wanneer er gebruik wordt gemaakt van asynchrone code, dat je wel wacht op de gebruikers interactie op de vragen. Bijvoorbeeld: await cameraAccess();

        public async Task cameraAccess()
        {
            // Ophalen camera toegangs status
            AVAuthorizationStatus authStatus = AVCaptureDevice.GetAuthorizationStatus(AVMediaType.Video);

            if (authStatus == AVAuthorizationStatus.Authorized)
            {
                // Ja we hebben toegang tot de camera
                return true;
            }
            else if (authStatus == AVAuthorizationStatus.Denied)
            {
                // Nee camera toegang stat uit
                return false;
            }
            else if (authStatus == AVAuthorizationStatus.Restricted)
            {
                // restricted, gebeurt normaal gesproken niet
                return false;
            }
            else if (authStatus == AVAuthorizationStatus.NotDetermined)
            {
                // Niet bekend, waarschijnlijk eerste keer dat de app gestart wordt
                bool CamresultData = await AVCaptureDevice.RequestAccessForMediaTypeAsync(AVMediaType.Video);
                return CamresultData;
            }
            else
            {
                return false;
            }
        }

Xamarin cross platform forms, uitvoeren native code (iOs/Android) via interfaces

xamarin-logo

Xamarin Forms is een cross platform framework, met als targets: Android, iOS, UWP. Heel mooi maar soms is het noodzakelijk om bepaalde functionaliteit aan te roepen die alleen native op het doelplatform beschikbaar is. Denk hierbij bijvoorbeeld aan het uitlezen van het serienummer van het apparaat, hoe ga je deze informatie verwerken in je applicatie? Hier bestaat een hele mooie oplossing voor: interfaces

Een interface bestaat uit:

  • één klasse(class) die we gebruiken in deel van de gedeelde code;
  • en één klasse(class) voor elke native platform.

In de klasse(class) die we in de shared code definiëren maken we de interface klasse(class), deze klasse(class) bevat alle functies die we ook in de native code class moeten programmeren, zie code hieronder:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExampleApp
{
    public interface getHWId
    {
        string DUID();
    }
}

Android native code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using System.Threading.Tasks;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Text;
using ExampleApp.Droid;
using Android.Graphics;
using Android.Content.PM;
using Android.Hardware;
[assembly: Xamarin.Forms.Dependency(typeof(IgetHWId))]

namespace ExampleApp.Droid
{
    class IgetHWId : getHWId
    {
        public string DUID()
        {
            string deviceId;
            deviceId = Android.OS.Build.Serial;
            return (deviceId);
        }
    }
}

iOs native code:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using ExampleApp.iOS;
using UIKit;
using Xamarin.Forms.Platform.iOS;
using WebKit;
using CoreGraphics;
using AVKit;
using AVFoundation;

[assembly: Xamarin.Forms.Dependency(typeof(IgetHWId))]

namespace ExampleApp.iOS
{
    class IgetHWId : getHWId
    {
        public string DUID()
        {
            string deviceId;
            deviceId = UIKit.UIDevice.CurrentDevice.IdentifierForVendor.AsString();
            return (deviceId);
        }
    }
}

Nu nog het stukje code om de DUID() functie aan te roepen vanuit de platform shared code:

    private getHWId nativeInterface;
    nativeInterface = new DependencyService.Get();
    Debug.WriteLine("Uniek hardware id = " + nativeInterface->DUID());

Uitleg: Linked Lists in PHP

main-qimg-9c7723e302afb4d0063023eba3055e9e

Linked lists zijn een van de meeste basale dingen die een beetje developer moet weten, een linked list is een lijst met objecten die verwijzen naar het volgende object door middel van pointers (geheugen verwijzingen).

Wat kan een reden zijn om Linked Lists te gebruiken en geen Array:

In een linked list is het makkelijker om nodes toe te voegen tussen bijvoorbeeld 2 nodes, omdat je gewoon een nieuwe node kunt maken en de pointers naar elkaar kunt laten verwijzen. Als jij bijvoorbeeld een array hebt met element 0 tot 9 en je wil na element 4 een nieuwe element op plaats 5 toevoegen moet je de boel opnieuw sorteren en herstructureren (want 5 wordt 6, 6 wordt 7 etc.), dit kost veel meer cpu en geheugen dan met een linked list.

Een andere reden kan zijn, de goede toepasbaarheid in multi threaded applications. Dit omdat je gewoon andere nodes in de lijst kunt verwijderen en tegelijk ook bewerkingen in een object kunt doen zonder dat je een fatale uitzondering krijgt. Als je een array zou gaan her-sorteren en de pointers opnieuw zou toewijzen zou het geheugen van waar de thread bezig is niet meer overeenkomen met waar de array data zich bevindt.

Voorbeeld van een real-life situatie:

Goed bruikbaar in bijvoorbeeld games, of dingen waar veel informatie in een object zit zodat het allemaal een beetje snel blijf 😉 Je kan bijvoorbeeld denken aan Command & Conquer, als je alle objecten moet tekenen op het scherm moet je x,y coordinaten bij houden, doel coordinaten, status voortuig, kleur, type, plaatje of de bitmaps voor het texture mappen etc. Om alle dingen op het scherm te tekenen loop je door de lijst, als het object binnen het kader van het scherm valt teken je hem anders sla je hem over en is die kapot geschoten, unlink je hem gewoon en is die weg 🙂

Hieronder een voorbeeld in php, om, een indruk te krijgen hoe een linked list nu eigenlijk precies werkt.

Output:

LinkedList nodes:
Naam: Victor Link, Leeftijd: 24
Naam: Henk Test, Leeftijd: 70
Naam: Piet Voorbeeld, Leeftijd: 55
Naam: Jan Klaas, Leeftijd: 18
Naam: Sander Aerts, Leeftijd: 34
Naam: Paoter Gustaaf, Leeftijd: 39

Piet Voorbeeld is 55 jaar oud

LinkedList nodes:
Naam: Henk Test, Leeftijd: 70
Naam: Piet Voorbeeld, Leeftijd: 55
Naam: Jan Klaas, Leeftijd: 18
Naam: Sander Aerts, Leeftijd: 34
Naam: Paoter Gustaaf, Leeftijd: 39

LinkedList nodes:
Naam: Henk Test, Leeftijd: 70
Naam: Piet Voorbeeld, Leeftijd: 55
Naam: Jan Klaas, Leeftijd: 18
Naam: Sander Aerts, Leeftijd: 34
/**
	 * **************************************************************************
	 * Linked Lists voorbeeld in php
	 * **************************************************************************
	 * Author			: Sander Aerts (sander@aerts-it.nl)
	 * Created			: 17 - 11 - 2016
	 * Last modified	: 17 - 11 - 2016
	 * **************************************************************************
	*/
	
	/**
	 *  Node object
	 */
	class listItem {
		public $name;
		public $age;
		public $head;
		public $next;
	}
	
	/**
	 *  Eerste node uit de lijst verwijderen
	 */
	function removeFirstItem(&$list)
	{
		/**
		 *  Controle of we niet de enige node zijn in de lijst
		 */
		if ( $list !=null && $list->next != null ) {
			$cNode = $list->next;
			$list  = $cNode;
		} elseif ( $list != null && $list->next == null ) {
			/**
			 *  Enige node, verwijder deze
			 */
			unset($list);
		}
	}
	
	/**
	 *  Laatste node uit de lijst verwijderen
	 */
	function removeLastItem(&$list)
	{
		if ( $list != null ) {
			/**
			 *  Ga naar de laatste node in de lijst
			 */
			$cNode = $list;
			while ( $cNode->next->next != null ) $cNode = $cNode->next;

			/**
			 * Laatste node verwijderen
			 */
			$cNode->next = null;
		}
	}
	
	/**
	 *  Node toevoegen aan begin van de lijst
	 */
	function addItemHead(&$list, $name, $age)
	{
		if ( $list == null ) {
			/* Nog geen nodes in de lijst, maak de eerste node aan  */
			$list 		= new listItem;
			$list->head = true;
			$list->next = null;
			$list->name = $name;
			$list->age  = $age;
		} else {
			/* Nieuwe node toevoegen aan begin van de lijst */
			$newItem 			= new listItem;
			$newItem->head 		= true;
			$newItem->next 		= $list;
			$newItem->name 		= $name;
			$newItem->age  		= $age;
			$list				= $newItem;
			$list->next->head	= false;
		}
	}
	
	/**
	 *  Node toevoegen aan het einde van de lijst
	 */
	function addItemTail(&$list, $name, $age)
	{
		if ( $list == null ) {
			addItemHead($list, $name, $age);
		} else {
			/**
			 *  Ga naar de laatste node in de lijst
			 */
			$cNode = $list;
			while ( $cNode->next != null ) $cNode = $cNode->next;

			/**
			 *  Nieuwe node toevoegen aan begin van de lijst
			 */
			$newItem 			= new listItem;
			$newItem->head 		= false;
			$newItem->next 		= null;
			$newItem->name 		= $name;
			$newItem->age  		= $age;
			$cNode->next		= $newItem;
		}
	}
	
	/**
	 *  Zoek iets in de lijst en resulteer dit resultaat als pointer uit de lijst
	 */
	function searchName(&$list, $name)
	{
		if ( $list != null ) {
			$cNode = $list;
			if ( $cNode->name == $name ) return($cNode);
			while ( $cNode->next != null ) {
				$cNode = $cNode->next;
				if ( $cNode->name == $name ) return($cNode);
			}
		} else {
			return(null);
		}
	}
	
	/**
	 *  Lijst weergeven
	 */
	function showList(&$list)
	{
		echo "\r\nLinkedList nodes:\r\n";
		if ( $list != null ) {
			$cNode = $list;
			echo "Naam: " . $cNode->name . ", Leeftijd: " . $cNode->age . "\r\n";
			while ( $cNode->next != null ) {
				$cNode = $cNode->next;
				echo "Naam: " . $cNode->name . ", Leeftijd: " . $cNode->age . "\r\n";
			}
		} else {
			die("Lijst bevat geen nodes!");
		}
	}
	
	header("Content-type: text/plain");
	
	/**
	 *  Nodes toevoegen aan begin van de lijst, de eerste node wordt
	 *  dus automatisch de 2de node, wat met printen van de lijst dus
	 *  tegenovergestelde volgorde weergeeft zoals hieronder toegevoegd
	 */
	addItemHead($myList, "Sander Aerts", "34");
	addItemHead($myList, "Jan Klaas", "18");
	addItemHead($myList, "Piet Voorbeeld", "55");
	addItemHead($myList, "Henk Test", "70");
	addItemHead($myList, "Victor Link", "24");
	
	/**
	 *  Node toevoegen aan het einde van de lijst, deze komt dus achter de
	 *  node "Sander Aerts"
	 */
	addItemTail($myList, "Paoter Gustaaf", "39");
	
	/**
	 *  Lijst printen
	 */
	showList($myList);
	
	/**
	 *  Zoek naar "Piet Voorbeeld" in de lijst
	 */
	$node = searchName($myList, "Piet Voorbeeld");
	echo "\r\n" . $node->name . " is " . $node->age . " jaar oud\r\n";
	
	/**
	 *  Eerst node verwijderen en lijst printen
	 */
	removeFirstItem($myList);
	showList($myList);
	
	/**
	 *  Laatste node verwijderen en lijst printen
	 */
	removeLastItem($myList);
	showList($myList);

RDW Kenteken registratie sidecode integratie

logo

RDW maakt heeft kenteken sidecodes van 1 t/m 11, hierbij wordt de volgende registratie methode gebruikt:

Sidecode 1:

Eerst 2 letters gevolgd door 2 x 2 cijfers: [XX-99-99], bij de eerste sidecode reeks kun je aan de tweede letter in het kenteken opmaken om wat voor voertuig het ging.

Sidecode 2:

Eerst 2 x 2 cijfers en daarna 2 letters: [99-99-XX], gestart in 1965.

Sidecode 3:

Eerst 2 cijfers, daarna 2 letters en daarna weer 2 cijfers: [99-XX-99], gestart in 1973.

Sidecode 4:

Eerst 2 letters, daarna 2 cijfers en daarna weer 2 letters: XX-99-XX, gestart midden 1978, bepaalde combinaties zijn niet gebruikt zoals bijvoorbeeld: SS, SD.

Sidecode 5:

Eerst 2 x 2 letters, daarna 2 cijfers: [XX-XX-99], gestart in 1991.

Sidecode 6:

Eerst 2 cijfers, daarna 2 x 2 letters: [99-XX-XX]m gestart in 1999.

Sidecode 7:

Eerst 2 cijfers, daarna 3 letters, en daarna 1 cijfer: [99-XXX-9], gestart in oktober 2004. Letter combinaties die eindigen op SD of SS worden niet gebruikt, ook worden de volgende letters niet gebruikt: GVD, KKK, NSB, PKK, PSV, PVV en TBS. Vorder worden lettercombinaties die op schutting taal lijken geweerd zoals: GEK, LYK, K** L** ;).

Sidecode 8:

Eerst 1 cijfer, daarna 3 letters, en daarna 2 cijfers: [9-XXX-99], gestart in maart 2013.

Sidecode 9:

Eerst 2 letters, daarna 3 cijfers en dan 1 letter: [XX-999-X], gestart in maart 2015 en in gebruik voor lichte bedrijfswagens.

Sidecode 10:

Eerst 1 letter, daarna 3 cijfers, en daarna 2 letters: [D-999-XX], in gebruikt voor bromfietsen.

Sidecode 11:

Eerst 3 letters, daarna 2 cijfers en daarna 1 letter: [XXX-99-X], in gebruik voor bromfietsen.

Hieronder een javascript, om automatisch de streepjes op de juiste positie bij de juiste sidecode te plaatsen.

function sidecodeSeperator(licensePlate)
	{
		var newLicenseplate = licensePlate.replace(/-/gi, '');
		if ( newLicenseplate.length == 6 ) {
			/*
				Detect sidecode type, and rewrite licenseplate
			*/
			newLicenseplate = newLicenseplate.replace(/([a-z]{2})([0-9]{2})([0-9]{2})/i, '$1-$2-$3');	// Sidecode: 1
			newLicenseplate = newLicenseplate.replace(/([0-9]{2})([0-9]{2})([a-z]{2})/i, '$1-$2-$3');	// Sidecode: 2
			newLicenseplate = newLicenseplate.replace(/([0-9]{2})([a-z]{2})([0-9]{2})/i, '$1-$2-$3');	// Sidecode: 3
			newLicenseplate = newLicenseplate.replace(/([a-z]{2})([0-9]{2})([a-z]{2})/i, '$1-$2-$3');	// Sidecode: 4
			newLicenseplate = newLicenseplate.replace(/([0-9]{2})([a-z]{2})([a-z]{2})/i, '$1-$2-$3');	// Sidecode: 5
			newLicenseplate = newLicenseplate.replace(/([0-9]{2})([a-z]{2})([a-z]{2})/i, '$1-$2-$3');	// Sidecode: 6
			newLicenseplate = newLicenseplate.replace(/([0-9]{2})([a-z]{3})([0-9]{1})/i, '$1-$2-$3');	// Sidecode: 7
			newLicenseplate = newLicenseplate.replace(/([0-9]{1})([a-z]{3})([0-9]{2})/i, '$1-$2-$3');	// Sidecode: 8
			newLicenseplate = newLicenseplate.replace(/([a-z]{2})([0-9]{3})([a-z]{1})/i, '$1-$2-$3');	// Sidecode: 9
			newLicenseplate = newLicenseplate.replace(/([a-z]{1})([0-9]{3})([a-z]{2})/i, '$1-$2-$3');	// Sidecode: 10
			newLicenseplate = newLicenseplate.replace(/([a-z]{3})([0-9]{2})([a-z]{1})/i, '$1-$2-$3');	// Sidecode: 11
			
			return(newLicenseplate);
		} else {
			return(licensePlate);
		}	
	}

SOAP/REST combinatie API php

Combinatie API zowel RESTful als SOAP, in php met een WSDL

/**
	 *  AOZ Soap Class
	 */
	class soapServiceFunctions { 
		public $authorized 	= false;
		public $testmode	= false;

		/**
		 *  Check auhtorization
		 */
		function authorization($header) {
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			global $_SERVER;
			if ( isset($header->username) && isset($header->hash) ) 
			{ 
				if ( $header->username == "testuser" && $header->hash == "7f3be433c9bab15315e23fbb4664d33c6074211b" ) {
					$this->authorized 			= true;
					$this->testmode				= true;
					$this->userData["userid"] 	= 1;
				} else {
					$this->userData	= $this->auth->checkWSDLAccess($header->username, $header->hash, $_SERVER["REMOTE_ADDR"]);
					if ( $this->userData ) $this->authorized 	= true;
					$this->userData["userid"] 	= $this->userData["id"];
				}
			} 
		} 

		/**
		 *  Exception handler
		 */
		function serviceAPIException($errNr, $msg, $call)
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			switch ( $this->serviceType ) {
				case "SOAP":
					$this->soap->fault($errNr, $msg, $call, "Contact the AOZ support department on 076 - 74 10 100.");
				break;;
				default:
					throw new Exception($errNr . "::" . $msg . "::" . $call . "]->" . "Contact the AOZ support department on 076 - 74 10 100.");
				break;;
			}
		}
		
		/**
		 *  Request for bag (basisregistratie adressen information ...
		 */
		function someFunction(
					$firstName,
					$lastName,
		) {	
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			
			return(
				array(
					"processData"				=> $this->serviceConversion($this->procData),
					"aozBagRequest"				=> $this->serviceConversion($rs),
					"dataResources"				=> null	
				)
			);
		}

		/**
		 *  Convert to API 
		 */
		function serviceConversion($dataSet)
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			switch ( $this->serviceType ) {
				case "SOAP":
					return(new SoapVar($dataSet, SOAP_ENC_OBJECT));
				break;;
				default:
					return($dataSet);
				break;;
			}
		}
		
		/**
		 *  API Constructor ...
		 */
		function __construct($soapService, $sql, $auth, $crypt, $conversionTbl, $serviceType = "SOAP")
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			global $logData;
			$this->logData			= &$logData;
			$this->soap 			= $soapService;
			$this->sql 				= $sql;
			$this->auth 			= $auth;
			$this->crypt			= $crypt;
			$this->conversionTbl	= $conversionTbl;
			$this->serviceType		= $serviceType;
			$this->logData["API"]	= $serviceType;
			$this->locationImages	= true;
			$this->imageHashes		= array();
			$this->procData			= array(
											"calls"		=> 0,
											"resources"	=> 0,
											"runtime"	=> microtime(true),
											"date"		=> time(),
											"errors"	=> 0,
											"warnings"	=> 0,
											"images"	=> 0,
											"timezone"	=> date_default_timezone_get()
										);
		}
	} 
	
	/**
	 *  RESTful API class
	 */
	abstract class RESTFullAPI extends soapServiceFunctions { 
		/**
		 *  Request vars
		 */
		protected $method		= "";
		protected $endpoint		= "";
		protected $verb			= "";
		protected $args			= Array();
		protected $file			= null;
		protected $username		= "";
		protected $hash			= "";
		
		/**
		 *  API Entrypoint
		 */
		
		/**
		 *  Response function
		 */
		function __response($data, $statusCode = 200)
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			header("HTTP/1.1 " . $statusCode . " " . $this->__requestStatus($statusCode));
			return json_encode($data);
		}
		
		/**
		 *  Cleanup input data
		 */
		function __cleanInput($data)
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			$clean_input = Array();
			if (is_array($data)) {
				foreach ($data as $k => $v) {
					$clean_input[$k] = $this->__cleanInput($v);
				}
			} else {
				$clean_input = trim(strip_tags($data));
			}
			return $clean_input;
		}
		
		/**
		 *  Check request status
		 */
		function __requestStatus($statusCode)
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			$statusCodes = array(
								200 => "OK",
								404 => "Not Found",
								405 => "Method Not Allowed",
								500 => "Internal Server Error"
							);
			return($statusCodes[$statusCode] ? $statusCodes[$statusCode] : $statusCodes[500]);
		}
		
		/**
		 *  RESTful API constructor
		 */
		function __construct($requestInput)
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			global $server, $sql, $auth, $crypt, $conversionTbl, $apiClass;
			$apiClass = $this;
			
			/**
			 * Response headers 
			 */
			header("Access-Control-Allow-Orgin: *");
			header("Access-Control-Allow-Methods: *");
			header("Content-Type: application/json");
			
			/**
			 *  Parse arguments
			 */
			$this->args 	= explode("/", rtrim($requestInput));
			$this->endpoint	= array_shift($this->args);
			$this->method	= $_SERVER["REQUEST_METHOD"];
			
			/**
			 *  Check request method
			 */
			if ( $this->method == "POST" && array_key_exists("HTTP_X_HTTP_METHOD", $_SERVER) )
			{
				switch ( $_SERVER["HTTP_X_HTTP_METHOD"] ) {
					case "DELETE":
					case "PUT":
						$this->method = $_SERVER["HTTP_X_HTTP_METHOD"];
					break;;
					default:
						$this->serviceAPIException("400", "Unexpected headers found ...", __CLASS__ . "::" . __FUNCTION__);
					break;;
				}
			}
			
			/**
			 *  Parse data for correct REQUEST method
			 */
			switch ( $this->method ) {
				case "DELETE":
				case "POST":
					$this->request 	= $this->__cleanInput($_POST);
				break;;
				case "GET":
					$this->request 	= $this->__cleanInput($_GET);
				break;;
				case "PUT":
					$this->request 	= $this->__cleanInput($_GET);
					$this->file		= file_get_contents("php://input");
				break;;
				default:
					$this->__response("Invalid method", 405);
				break;;
			}
			
			parent::__construct($server, $sql, $auth, $crypt, $conversionTbl, "RESTFUL");
		}
		
		/**
		 *  Get Arguments from class
		 */
		function getArgs()
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			return($this->args);
		}
		
		function processAPI() {
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			if (method_exists($this, $this->endpoint)) {
				$cc 		= 0;
				$arguments 	= "";
				foreach ($this->args as $var ) {
					$varList[$cc] = $var;
					$arguments .= "\$varList[$cc], ";
					$cc++;
				}
				
				$reflection = new ReflectionMethod('soapServiceClass', $this->endpoint);
				if ( $reflection->getNumberOfRequiredParameters() > $cc ) {
					$this->serviceAPIException("428", "Required arguments missing ...", __CLASS__ . "::" . __FUNCTION__);
				} else {
					$arguments  = substr($arguments, 0, strlen($arguments) - 2);
					$call 		= "\$result = \$this->{\$this->endpoint}(" . $arguments . ");";
					eval($call);
					return $this->__response($result);
				}
			}
			return $this->__response("No Endpoint: $this->endpoint", 404);
		}
		
		/**
		 *  RESTful API deconstructor
		 */
		function __deconstruct()
		{
			
		}
	}
	
	/**
	 *  This RESTful API entrypoint
	 */
	class RESTApi extends RESTFullAPI {		
		/**
		 *  API Constructor
		 */
		function __construct($request, $origin, $start)
		{
			logSysCall(( __CLASS__ == "" ? __FUNCTION__ : __METHOD__), __LINE__, __FILE__);
			$request = strstr($request, $start);
			parent::__construct($request);
			
			if ( isset($this->request["username"]) && isset($this->request["hash"]) ) 
			{ 
				/**
				 *  Are we a test user?
				 */
				if ( $this->request["username"] == "testuser" && $this->request["hash"] == "7f3be433c9bab15315e23fbb4664d33c6074211b" ) {
					$this->authorized 			= true;
					$this->testmode				= true;
					$this->userData["userid"] 	= 1;
				} else {
					/**
					 *  Check user WSDL access
					 */
					$this->userData	= $this->auth->checkWSDLAccess($this->request["username"], $this->request["hash"], $_SERVER["REMOTE_ADDR"]);
					if ( $this->userData ) $this->authorized 	= true;
					$this->userData["userid"] 	= $this->userData["id"];
				}
			} else {
				$this->serviceAPIException("400", "Missing authorization parameters ...", __CLASS__ . "::" . __FUNCTION__);
			}

		}
	}
	
	/**
	 *  Check API request type, RESTful / SOAP
	 */
	$requestHeaders 		= getallheaders();
	$logData["headers"]		= json_encode($requestHeaders);
	$logData["url"]			= $_SERVER["REQUEST_URI"];
	$logData["rawRequest"]	= file_get_contents("php://input");
	$logData["ip"]			= $_SERVER["REMOTE_ADDR"];
	
	/**
	 *  API Processing
	 */
	if ( array_key_exists("SOAPAction", $requestHeaders) || isset($_GET["wsdl"]) ) {	
		/**
		 *  SOAP/API Service Entrypoint
		 */
		if ( isset($_GET["wsdl"]) ) {
			header("Content-type: text/xml");
			/**
			 *  AOZ_WSE_ENDPOINT_PEOPLE_SEARCH Set service endpoint
			 */
			echo str_replace("%%AOZ_WSE_ENDPOINT_PEOPLE_SEARCH%%", AOZ_WSE_ENDPOINT_PEOPLE_SEARCH, file_get_contents("aozWse.wsdl"));
		} else {
			$soapServiceOptions = array(
									"soap_version"	=> SOAP_1_2,
									"cache_wsdl"	=> WSDL_CACHE_NONE,
									"send_errors"	=> true
									);
			$server = new SoapServer("aozWse.wsdl", $soapServiceOptions);
			$server->setClass("soapServiceClass", $server, $sql, $auth, $crypt, $conversionTbl);
			$server->handle();
		}
		$logData["vars"]		= "";
	} else {
		/**
		 *  RESTful API Service Entrypoint
		 */
		if ( array_key_exists("request", $_REQUEST) ) {
			if (!array_key_exists('HTTP_ORIGIN', $_SERVER)) {
				$_SERVER['HTTP_ORIGIN'] = $_SERVER['SERVER_NAME'];
			}

			try {
				$API = new RESTApi($_REQUEST['request'], $_SERVER['HTTP_ORIGIN'], $_REQUEST['start']);
				echo $API->processAPI();
				$logData["vars"] = json_encode($API->getArgs());
			} catch (Exception $e) {
				echo json_encode(Array('error' => $e->getMessage()));
			}
		} else {
			echo "RESTful API Endpoint";
		}
	}

Xamarin Forms / iOS / Appsize

xamarin-logo

Apple hanteert in de app store een limiet van 100 megabyte voor apps zodat ze downloadbaar blijven via lte/3g (mobiele netwerken) . Ook wordt de app size groter wanneer deze geüpload/ingediend wordt in de app store, bijvoorbeeld een app binary van 53 megabyte kan bijvoorbeeld 110 megabyte worden. Dit komt omdat de binary’s aangepast worden voor specifieke platformen. Maar door middel van wijzigingen in de linker en builder kun je de app size reduceren zodat hij na upload in de app store ook onder de 100 megabyte blijft. Mijn app ging van 53 megabyte naar 20 megabyte.

Aanpassingen die ik hiervoor gedaan heb zijn:

  • iOS build generally, General, linker behavior op: link SDK assemblies only
  • iOS build generally, Advanced, generic value type sharing: ja

Optioneel kun je nog builden voor een specifieke platform architectuur, dit raad echter af omdat dit app compatibiliteit niet ten goede komt.

postvak-in-debugachtergrondonderzoek-nl-outlook_2postvak-in-debugachtergrondonderzoek-nl-outlook

 

Simpele asterisk IVR

asterisk_logo-svg

Vandaag wederom een simpele IVR opgezet in Asterisk RT (RealTime), met de volgende functionaliteit:

  • Boodschap met 4 keuzes;
  • Afwijkende MOH (music on hold/wachtmuziek);
  • Elke keuze gaat naar een specifiek intern SIP account;
  • Timeout na 30 seconden, daarna komt hij in de backup queue;
  • Timeout na 30 seconden, daarna wordt hij extern doorgeschakeld;

Configuratie:

schermafbeelding-2016-11-14-om-21-48-57

schermafbeelding-2016-11-14-om-21-49-18

Geschreven versie volg nog, ivm klantgegevens bepaalde delen geblurd.