<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Alex Boia - Livedesign</title>
	<atom:link href="http://alex.livedesign.ro/feed" rel="self" type="application/rss+xml" />
	<link>http://alex.livedesign.ro</link>
	<description>Just another WordPress weblog</description>
	<lastBuildDate>Sat, 28 Jan 2012 21:49:30 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Despre modificarea conținutului paginilor de către furnizorii de internet mobil</title>
		<link>http://alex.livedesign.ro/despre-modificarea-continutului-paginilor-de-catre-isp-urile-de-internet-mobil</link>
		<comments>http://alex.livedesign.ro/despre-modificarea-continutului-paginilor-de-catre-isp-urile-de-internet-mobil#comments</comments>
		<pubDate>Sat, 28 Jan 2012 16:45:51 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[cache-control]]></category>
		<category><![CDATA[internet mobil]]></category>
		<category><![CDATA[no-transform]]></category>
		<category><![CDATA[transformare continut]]></category>

		<guid isPermaLink="false">http://alex.livedesign.ro/?p=106</guid>
		<description><![CDATA[Ce m-a îndemnat să scriu acest articol au fost sesizările primite de către unul dintre clienții noștri, conform cărora utilizatorii aplicațiilor web dezvoltate pentru ei nu le puteau folosi accesându-le prin intermediul conexiunilor de internet mobil (cel pentru laptop și PC). Observațiile primite nu erau legate doar de un operator anume, ci de toți trei [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_107" class="wp-caption alignleft" style="width: 310px"><a href="http://alex.livedesign.ro/wp-content/uploads/2012/01/ct-what.png"><img class="size-medium wp-image-107" title="ct-what" src="http://alex.livedesign.ro/wp-content/uploads/2012/01/ct-what-300x119.png" alt="" width="300" height="119" /></a><p class="wp-caption-text">Sursa: W3C</p></div>
<p>Ce m-a îndemnat să scriu acest articol au fost sesizările primite de către unul dintre clienții noștri, conform cărora utilizatorii aplicațiilor web dezvoltate pentru ei nu le puteau folosi accesându-le prin intermediul conexiunilor de internet mobil (cel pentru laptop și PC). Observațiile primite nu erau legate doar de un operator anume, ci de toți trei prezenți în România &#8211; Orange, Vodafone și Cosmote.</p>
<p><strong>Ce se întâmpla de fapt</strong></p>
<p>Odată investigată situația, problema s-a dovedit în realitate ceva mai gravă decât era raportată de către utilizatori. Pe scurt, iată ce am descoperit:</p>
<p>- imaginile erau servite la o calitate inferioară celei originale, datorită compresiei agresive aplicate de către ISP, de până la 50% din dimensiunea originală;</p>
<p>- fișierele JavaScript erau modificate de către ISP, eliminându-se comentariile și nu numai, probabil cu scopul de a micșora traficul generat de descărcarea acestora și de a optimiza încărcarea, însă această transformare era prost implementată, funcționarea scripturilor fiind grav afectată (spre exemplu, apăreau erori la rularea acestora în browser);</p>
<p>- conținutul HTML al paginii era modificat de către ISP (spre exemplu comentariile și newline-urile erau eliminate, însă uneori link-urile către resursele JavaScript erau compromise din cauza unor modificări greșite);</p>
<p><span id="more-106"></span>- conținutul fișierelor CSS externe era copiat  de către ISP inline în pagina HTML, iar referințele către fișierele CSS originale erau șterse din document.</p>
<p><strong>O practică destul de des întâlnită</strong></p>
<p>Evident, toate aceste modificări nu puteau fi făcute decât de un proxy configurat de ISP, iar <a href="http://stuartroebuck.blogspot.com/2010/07/compiled-list-of-web-references-to.html" target="_blank">aici</a> am găsit confirmarea acestui fapt. Se pare că, într-adevăr, instalarea și configurarea unui proxy care să cache-uiască și să transforme conținutul web în vederea optimizării traficului pe rețea (cel puțin la nivel declarativ) este o practică des întâlnită printre operatorii de internet mobil, <a href="http://www.bytemobile.com/" target="_blank">ByteMobile</a> fiind platforma preferată de mulți dintre aceștia (inclusiv de cei de la noi din România).</p>
<p><strong>Ce alte probleme pot fi întâlnite și cum puteți voi, ca dezvoltatori să preveniți acest lucru</strong></p>
<p>Transformările descrise mai sus nu sunt însă singurele pe care un astfel de proxy le poate efectua asupra conținutului unei pagini. Au mai fost raportate modificari precum:</p>
<p>- <a href="http://stuartroebuck.blogspot.com/2010/07/mobile-proxy-cache-content-modification.html" target="_blank">inserarea inline a conținutului fișierelor JavaScript externe folosite de către documentul HTML</a>;</p>
<p>- <a href="http://stackoverflow.com/questions/3282373/web-site-exhibits-javascript-error-on-ipad-iphone-under-3g-but-not-under-wifi/" target="_blank">inserarea inline a unor secvente de cod JavaScript proprii platformei de optimizare folosite</a>.</p>
<p>Soluția la toate aceste dureri de cap produse de transformări inoportune a conținutului este trimiterea de către aplicație a unei valori speciale a <a href="http://stuartroebuck.blogspot.com/2010/08/official-way-to-bypassing-data.html" target="_blank">header-ului Cache-Control</a>:</p>
<pre class="brush: text; gutter: true; first-line: 1">Cache-Control: no-transform</pre>
<p>Directiva no-transform este specificată și în <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5" target="_blank">RFC2616</a> și are caracter obligatoriu. Cu alte cuvinte, proxy-urile sunt obligate să transmită nemodificat conținutul paginilor ce includ în antentul lor această directivă. În ceea ce privește cazul nostru, vă pot confirma că această abordare funcționează perfect.</p>
<p><strong>De ce sunt totuși inoportune aceste ”optimizări”</strong></p>
<p>Primul și cel mai important argument este, evident, faptul că în majoritatea cazurilor sunt prost implementate și pot strica funcționalitatea site-ului, producând neplăceri atât utilizatorului, cât și proprietarilor aplicației, cu atât mai mult cu cât aceste modificări sunt făcute uneori fără înștiințarea utilizatorului și fără a-i acorda posibilitatea să renunțe la acest ”feature”;</p>
<p>Apoi, la o analiză atentă se poate observa că, de fapt, unele măsuri de optimizare au fix efectul opus. Să luam ca exemplu inserarea inline a conținutului fișierelor CSS și JavScript. În mod obișnuit, navigatorul descarcă fișierele CSS și JavaScript externe și le salvează pe disc, în cache-ul local pentru un timp oarecare. Când utilizatorul accesează succesiv pagini care fac referire către aceleasi resurse externe, navigatorul le servește pe acestea din cache, evitând astfel descărcarea lor, generând practic zero trafic pentru a le servi. Dacă în schimb conținutul resurselor este inserat inline în paginile HTML, ele vor fi transferate cu fiecare accesare a paginilor respective. Cum de obicei paginile sunt dinamice și nu sunt cache-uite rezultă că de fiecare dată când sunt accesate se transferă integral si conținutul acestor resurse, generând un trafic inutil.</p>
]]></content:encoded>
			<wfw:commentRss>http://alex.livedesign.ro/despre-modificarea-continutului-paginilor-de-catre-isp-urile-de-internet-mobil/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Noutăți în iOS SDK 5.0 &#8211; Totul despre UIPageViewController</title>
		<link>http://alex.livedesign.ro/noutati-in-ios5-totul-despre-uipageviewcontroller</link>
		<comments>http://alex.livedesign.ro/noutati-in-ios5-totul-despre-uipageviewcontroller#comments</comments>
		<pubDate>Tue, 08 Nov 2011 09:15:33 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[ios]]></category>
		<category><![CDATA[monotouch]]></category>
		<category><![CDATA[uipageviewcontroller]]></category>

		<guid isPermaLink="false">http://alex.livedesign.ro/?p=102</guid>
		<description><![CDATA[Dintre noutățile aduse de iOS SDK5.0, una care mi-a atras în mod deosebit atenția este prezența noii clase UIPageViewController, care permite crearea unei cărți digitale simulând experiența oferită de o carte reală.
UIPageViewController este de fapt un container al cărui rol principal este cel de a gestiona o colecție de view-controllere (care reprezintă paginile cărții digitale), [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://alex.livedesign.ro/wp-content/uploads/2011/11/UPageViewControllerSample.png" target="_blank"><img class="alignleft size-medium wp-image-103" title="UPageViewControllerSample" src="http://alex.livedesign.ro/wp-content/uploads/2011/11/UPageViewControllerSample-155x300.png" alt="" width="155" height="300" /></a>Dintre noutățile aduse de iOS SDK5.0, una care mi-a atras în mod deosebit atenția este prezența noii clase <code>UIPageViewController</code>, care permite crearea unei cărți digitale simulând experiența oferită de o carte reală.</p>
<p><code>UIPageViewController</code> este de fapt un container al cărui rol principal este cel de a gestiona o colecție de view-controllere (care reprezintă paginile cărții digitale), facilitând navigarea între ele.</p>
<p><strong>Opțiuni de afișare</strong></p>
<p><span style="text-decoration: underline;">Tranziții</span></p>
<p>O tranziție reprezintă efectul folosit pentru animarea navigării de la o pagină (adică un view-controller) la alta. Momentan nu este disponibilă decât o singură opțiune &#8211; cea care produce un efect asemănător cu cel din iBooks.</p>
<p><span style="text-decoration: underline;">Orientarea navigării</span></p>
<p>Orientarea pe care se face trecerea între &#8220;pagini&#8221;. Aici există două opțiuni:</p>
<p>- pe orizontală (<code>UIPageViewControllerNavigationOrientation.Horizontal</code>) &#8211; trecerea se face de la stânga la dreapta, sau invers;</p>
<p>- pe verticală (<code>UIPageViewControllerNavigationOrientation.Vertical</code>) &#8211; trecerea se face de sus în jos, sau invers.</p>
<p><span style="text-decoration: underline;"><span id="more-102"></span></span></p>
<p><span style="text-decoration: underline;">Pivotul folosit pentru animație (Spine)</span></p>
<p>Pivotul reprezintă axa în jurul căreia se animează trecerea de la o pagină la alta. Există patru opțiuni:</p>
<p>- <code>UIPageViewControllerSpineLocation.None</code> &#8211; Fără pivot &#8211; Această opțiune nu este însă validă cu tranziția standard;</p>
<p>- <code>UIPageViewControllerSpineLocation.Min</code> &#8211; Pivotul se află în partea stângă a paginii (atunci când trecerea se face pe orizontală), sau în partea de sus a paginii (atunci când trecerea se face pe verticală);</p>
<p>- <code>UIPageViewControllerSpineLocation.Max</code> &#8211; Pivotul se află în partea dreaptă a paginii (atunci când trecerea se face pe orizontal), sau în partea de jos a paginii (atunci când trecerea se face pe verticală);</p>
<p>- <code>UIPageViewControllerSpineLocation.Mid</code> &#8211; Pivotul se află la mijloc, permițând astfel afișarea a două pagini pe ecran. Acest mod se folosește de obicei atunci când device-ul este orientat landscape.</p>
<p><span style="text-decoration: underline;">DoubleSided</span></p>
<p>Această proprietate a <code>UIPageViewController</code> stabiliește câte pagini sunt vizibile la un moment dat: una dacă are valoarea <code>false</code>, două în caz contrar. Atunci când pivotul se află la mijlocul ecranului, <code>DoubleSided</code> trebuie neapărat să fie <code>true</code>.</p>
<p>Dacă are valoarea <code>true</code> și pivotul nu se află la mijlocul ecranului, framework-ul va plasa view-controller-ele noastre și pe spatele paginilor astfel încât vizibile efectiv nu vor fi decât jumătate din ele. Ca atare, <code>DoubleSided</code> nu este folosită în general decât atunci când pivotul este plasat la mijloc.</p>
<p><strong>Un exemplu de implementare</strong></p>
<p>Pentru a exemplifica API-ul concentrat în jurul <code>UIPageViewController</code> am trasat următoarele cerințe:</p>
<p>- vor fi suportate toate orientările posibile ale interfeței dispozitivului;</p>
<p>- pentru orientările de tip landscape vor fi afișate câte două pagini simultan, iar pentru orientările de tip portrait va fi afișată o singură pagină.</p>
<p><span style="text-decoration: underline;">Crearea view-controller-ului cu rol de pagină<br />
 </span></p>
<p>Pentru a prezenta paginile, va fi nevoie de un view-controller care să gestioneze conținutul unei pagini. Acesta va fi responsabil pentru actualizarea conținutului în funcție de numărul paginii pe care o reprezintă. În plus, va ști să trateze cazul special al unei pagini placeholder, marcată cu numărul 0.</p>
<p>În exemplul nostru acest view-controller nu face decât să afișeze într-un <code>UILabel</code> numărul paginii curente, neavând practic nimic deosebit în construcția sa.</p>
<p>Constructorul clasei primește drept parametru numărul paginii pentru care va fi afișat conținutul. Acest număr poate fi consultat ulterior prin proprietatea read-only <code>CurrentPage</code>.</p>
<pre class="brush: csharp; gutter: true; first-line: 1">public PageContentViewController (int page) : base()
{
     mCurrentPage = page;
}</pre>
<pre class="brush: csharp; gutter: true; first-line: 1">public int CurrentPage
{
	get {
		return mCurrentPage;
	}
}</pre>
<p>În plus, trebui să instruim view-controller-ul să suporte toate orientările posibile:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">public override bool ShouldAutorotateToInterfaceOrientation
    (UIInterfaceOrientation toInterfaceOrientation)
{
	return true;
}</pre>
<p><span style="text-decoration: underline;">Crearea DataSource-ului</span></p>
<p>DataSource-ul este probabil cea mai importantă componentă care trebuie scrisă. Astfel, aceasta este creată extinzând <code>UIPageViewControllerDataSource</code>, care expune două metode ce pot fi suprascrise și implementate:</p>
<p>- <code>GetNextViewController (UIPageViewController pageViewController, UIViewController refViewController) </code>- trebuie să returneze view-controller-ul care-l succede pe cel curent, dat prin al doilea parametru. Primul parametru reprezintă instanța de <code>UIPageViewController</code> care apelează metoda atunci când utilizatorul face &#8220;swipe&#8221; pentru a naviga la pagina următoare. Dacă nu mai sunt pagini de afișat (adica dacă ne aflăm deja la ultima pagină) atunci vom returna <code>null</code>.</p>
<pre class="brush: csharp; gutter: true; first-line: 1">public override UIViewController GetNextViewController
	(UIPageViewController pageViewCtrl, UIViewController refViewCtrl)
{
	int page = ((PageContentViewController)refViewCtrl).CurrentPage;

	if (page == mTotalPages &amp;&amp; pageViewCtrl.SpineLocation ==
		UIPageViewControllerSpineLocation.Mid) {
		return new PageContentViewController(0);
	}
	if (page &gt; 0 &amp;&amp; page + 1 &lt;= mTotalPages) {
		return new PageContentViewController(page + 1);
	} else {
		return null;
	}
}</pre>
<p>Implementarea prezentată aici este destul de simplă și nu voi insista decât pe liniile 6-9. Atunci când pivotul este situat la mijlocul ecranului (fiind deci afișate câte două pagini simultan), la fiecare gest de navigare făcut de utilizator <code>UIPageViewController</code>-ul apelează de două ori succesiv această metodă pentru a obține paginile ce vor fi afișate, iar dacă vreunul din apeluri returnează <code>null</code>, atunci navigarea nu va mai avea loc. Acest caz este întâlnit atunci când avem un număr impar de pagini. Ca atare, am ales ca ultima pagină să fie o pagină placeholder, marcată cu numărul 0.</p>
<p>- <code>GetPreviousViewController (UIPageViewController pageViewController, UIViewController refViewController)</code> &#8211; trebuie să returneze view-controller-ul care-l precedă pe cel curent, sau <code>null</code> dacă pagina curentă este prima pagină. Implementarea este asemănătoare cu cea a <code>GetNextViewController</code>:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">public override UIViewController GetPreviousViewController
	(UIPageViewController pageViewCtrl, UIViewController refViewCtrl)
{
	int page = ((PageContentViewController)refViewCtrl).CurrentPage;

	if (page &gt; 0 &amp;&amp; page - 1 &gt;= 1) {
		return new PageContentViewController(page - 1);
	} else {
		return null;
	}
}</pre>
<p>În plus, pentru conveniență, am adăugat o nouă metodă, <code>GetViewControllers</code>, care primește doi parametri:</p>
<p>- poziționarea pivotului;</p>
<p>- numărul paginii de referință.</p>
<p>Rezultatul execuției este un vector care conține:</p>
<p>- două view-controllere (primul corespunde paginii de referință, iar cel de-al doilea paginii următoare, dacă aceasta există, sau unei pagini placeholder în caz contrar) , dacă pivotul este poziționat la mijlog</p>
<p>- un singur view-controller, corespunzător paginii de referință, în celelalte situații.</p>
<p>Metoda este apelată atât la selectarea view-controller-elor afișate inițial, cât și la schimbarea orientării dispozitivului, pentru a determina care sunt paginile ce trebuie afișate.</p>
<pre class="brush: csharp; gutter: true; first-line: 1">public PageContentViewController[] GetViewControllers
    (UIPageViewControllerSpineLocation spine, int fromPage)
{
	List&lt;PageContentViewController&gt; viewControllers =
		new List&lt;PageContentViewController&gt;(2);

	if (mTotalPages == 0) {
		return new PageContentViewController[] {};
	}
	if (fromPage &gt; mTotalPages) {
		fromPage = mTotalPages;
	}

	viewControllers.Add(new PageContentViewController(fromPage));
	if (spine == UIPageViewControllerSpineLocation.Mid) {
		if (fromPage + 1 &gt; mTotalPages) {
			fromPage = 0;
		} else {
			fromPage ++;
		}
		viewControllers.Add(new PageContentViewController(fromPage));
	}

	return viewControllers.ToArray();
}</pre>
<p><span style="text-decoration: underline;">Crearea Delegate-ului</span></p>
<p>Implementarea Delegate-ului este în principiu opțională, dar absolut necesară în cazul nostru, deoarece ne dorim să variem poziția pivotului (și implicit numărul de pagini afișate) în funcție de orientarea interfeței dispozitivului.</p>
<p>Pentru a obține acest lucru trebuie să suprascriem metoda <code>GetSpineLocation</code> care primește doi parametri:</p>
<p>- <code>UIPageViewController</code>-ul care o apelează;</p>
<p>- orientarea interfeței dispozitivului.</p>
<pre class="brush: csharp; gutter: true; first-line: 1">public override UIPageViewControllerSpineLocation GetSpineLocation
	(UIPageViewController pageViewCtrl, UIInterfaceOrientation o)
{
	BookDataSource ds = (BookDataSource)pageViewCtrl.DataSource;
	UIPageViewControllerSpineLocation spine;
	PageContentViewController page =
		(PageContentViewController)pageViewCtrl.ViewControllers[0];
	int startPage = page.CurrentPage;

	if (o == UIInterfaceOrientation.LandscapeLeft ||
		o == UIInterfaceOrientation.LandscapeRight) {
		spine = UIPageViewControllerSpineLocation.Mid;
		pageViewCtrl.DoubleSided = true;
		if (page.CurrentPage % 2 == 0) {
			startPage = page.CurrentPage - 1;
		}
	} else {
		spine = UIPageViewControllerSpineLocation.Min;
		pageViewCtrl.DoubleSided = false;
	}

	pageViewCtrl.SetViewControllers(
		ds.GetViewControllers(spine, startPage),
		UIPageViewControllerNavigationDirection.Forward,
		true, delegate(bool finished) {
			return;
	});
	return spine;
}</pre>
<p>În exemplul de față sunt parcurși următorii pași:</p>
<p>- este determinată noua poziție a pivotului;</p>
<p>- instruim <code>UIPageViewController</code>-ul să afișeze una sau două pagini (via proprietatea <code>DoubleSided</code>, care trebuie să fie obligatoriu setată la valoarea true atunci când pivotul este situat la mijloc);</p>
<p>- setăm noile view-controllere care vor fi afișate (spre exemplu, dacă interfața este orientată portrait, afișând o singură pagină &#8211; pagina 7, și utilizatorul schimbă orientarea în landscape, vor fi afișate view-controller-ele corespunzătoare paginilor 7 și 8).</p>
<p><span style="text-decoration: underline;">Crearea container-ului care va coordona activitatea</span></p>
<p>Pentru a folosi un <code>UIPageViewController</code> vom avea nevoie de un container pentru acesta. În exemplul nostru, rolurile acestui container sunt după cum urmează:</p>
<p>- creează o instanță de <code>UIPageViewController</code>, pe care o configurează cu DataSource-ul și Delegate-ul corespunzătoare;</p>
<p>- setează view-controller-ele afișate inițial, folosind metoda <code>UIPageViewController.SetViewControllers</code>;</p>
<p>- implementează metoda <code>ShouldAutorotateToInterfaceOrientation</code>, pentru a suporta toate orientările posibile ale unui dispozitiv.</p>
<pre class="brush: csharp; gutter: true; first-line: 1">public override void ViewDidLoad ()
{
	UIViewController[] viewControllers = null;
	base.ViewDidLoad ();

	mDs = new BookDataSource();
	mDlg = new BookDelegate();

	mBook = new UIPageViewController(
		UIPageViewControllerTransitionStyle.PageCurl,
		UIPageViewControllerNavigationOrientation.Horizontal,
		UIPageViewControllerSpineLocation.Min
	);
	viewControllers = mDs.GetViewControllers(mBook.SpineLocation, 1);

	mBook.DataSource = mDs;
	mBook.Delegate = mDlg;
	mBook.View.Frame = View.Bounds;

	mBook.SetViewControllers(
		viewControllers,
		UIPageViewControllerNavigationDirection.Forward,
		false, delegate(bool finished) {
			return;
	});

	View.AddSubview(mBook.View);
	AddChildViewController(mBook);
}</pre>
<p>Metoda <code>UIPageViewController.SetViewControllers</code> primește următorii parametri:</p>
<p>- Un vector care conține view-controller-ele ce vor fi afișate. Ca regulă, acest vector conține două view-controllere atunci când pivotul este <code>UIPageViewControllerSpineLocation.Mid</code> și unul singur în celelalte cazuri;</p>
<p>- Direcția de navigare către noile view-controllere: <code>UIPageViewControllerNavigationDirection.Forward</code> (navigare înainte) sau <code>UIPageViewControllerNavigationDirection.Reverse</code> (navigare înapoi). De reținut faptul că aceast parametru nu afectează decât modul în care este animată tranziția curentă, neavând efect asupra tranzițiilor următoare;</p>
<p>- Dacă tranziția este animată sau nu;</p>
<p>- Un callback ce va fi apelat atunci când tranziția este finalizată. Acest callback trebuie să primească un parametru de tip boolean care este true dacă animația a fost finalizată, respectiv false dacă animația nu a fost executată.</p>
<p>În plus, trebuie menționat că fără a adăuga explicită <code>UIPageViewController</code>-ul drept copil al container-ului nostru, metoda <code>GetSpineLocation</code> a Delegate-ului nu va fi apelată, de unde utilitatea liniei 28.</p>
<p><span style="text-decoration: underline;">Folosirea acestor clase</span></p>
<p>Odată create aceste clase, utilizarea lor este simplă și se reduce la a adăuga container-ul nostru în ierarhia de view-uri a aplicației:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
	private UIWindow window;

	private BookViewController mBook = null;

	public override bool FinishedLaunching
            (UIApplication app, NSDictionary opts)
	{
		mBook = new BookViewController();

		window = new UIWindow (UIScreen.MainScreen.Bounds);
		window.RootViewController = mBook;
		window.MakeKeyAndVisible ();

		return true;
	}
}</pre>
<p><a href="http://alex.livedesign.ro/wp-content/uploads/2011/11/UIPageViewControllerSample.zip" target="_blank">Click aici pentru a descărca proiectul care exemplifică toate aceste concepte</a>.<br class="spacer_" /></p>
]]></content:encoded>
			<wfw:commentRss>http://alex.livedesign.ro/noutati-in-ios5-totul-despre-uipageviewcontroller/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Un exemplu de utilizare al Substitution API în ASP.NET</title>
		<link>http://alex.livedesign.ro/exemplu-de-utilizare-al-substitution-api-in-asp-net</link>
		<comments>http://alex.livedesign.ro/exemplu-de-utilizare-al-substitution-api-in-asp-net#comments</comments>
		<pubDate>Tue, 25 Oct 2011 19:12:32 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[caching]]></category>

		<guid isPermaLink="false">http://alex.livedesign.ro/?p=97</guid>
		<description><![CDATA[Substitution API este de fapt un nume mai pompos pentru a descrie funcționalitatea oferită de metoda HttpResponse.WriteSubstitution(callback) care, în loc să scrie un șir de caractere în stream-ul răspunsului, ”scrie” un placeholder (înlocuitor) ce constă din funcția dată ca parametru care, atunci când răspunsul este trimis către client, este executată iar poziția ei în răspuns [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft size-thumbnail wp-image-99" title="donut_1305012889" src="http://alex.livedesign.ro/wp-content/uploads/2011/10/donut_1305012889-150x150.jpg" alt="" width="150" height="150" />Substitution API este de fapt un nume mai pompos pentru a descrie funcționalitatea oferită de metoda <code>HttpResponse.WriteSubstitution(callback)</code> care, în loc să scrie un șir de caractere în stream-ul răspunsului, ”scrie” un placeholder (înlocuitor) ce constă din funcția dată ca parametru care, atunci când răspunsul este trimis către client, este executată iar poziția ei în răspuns este înlocuită cu rezultatul execuției. Astfel, acest procedeu răspunde nevoii de actualiza dinamic porțiuni dintr-o pagină (sau dintr-un UserControl &#8211; .ascx) cache-uită folosind API-ul de output caching.</p>
<p><strong>Datele problemei</strong></p>
<p>Avem următoarea sarcină de lucru: trebuie să prezentăm o pagină al cărei conținut nu se schimbă foarte des și este același, indiferent de utilizatorul autentificat, fiind astfel un candidat foarte bun pentru output caching. Totuși, există o mică zonă din pagină care poate varia: link-ul de log-in (care apare dacă utilizatorul nu e autentificat), sau de log-out (care apare dacă utilizatorul este autentificat). Problema este că, odată salvat răspunsul în cache, acel link va rămâne același din momentul primei accesări a paginii, indiferent utilizatorul se autentifică sau nu ulterior.</p>
<p><span id="more-97"></span><strong></strong></p>
<p><strong>Prima soluție</strong></p>
<p>Prima soluție, cea mai simplă, implică folosirea controlului <code>asp:Substitution</code> cu o metodă callback care produce întreg HTML-ul necesar afișării link-ului. Adică ceva de genul:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">&lt;asp:Substitution ID="LinkSubstitution" runat="server" MethodName="GenerateLink" /&gt;</pre>
<p>unde <code>GenerateLink</code> este definită în code-behind astfel:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">public static string GenerateLink(HttpContext context)
{
    bool auth = false;
    if (context.Request["IsAuthenticated"] == "true")
        auth = true;
   if (auth)
       return "&lt;a href=\"Logout.aspx\"&gt;Log-out&lt;/a&gt;";
   else
       return "&lt;a href=\"Login.aspx\"&gt;Log-in&lt;/a&gt;";
}</pre>
<p><span style="text-decoration: underline;">Observație: De reținut e faptul că metoda callback nu poate aparține unui instanțe de obiect de tip <code>Page</code> sau <code>Control</code>. Trebuie să fie ori statică, ori să aparțină unei instanțe de obiect de tip diferit.</span></p>
<p><strong>O rezolvare mai bună</strong></p>
<p>Soluția de  mai sus, deși ușor de implementat, nu este deloc elegantă, pretându-se mai degrabă ca artificiu temporar și nu ca o soluție de durată.</p>
<p>Ideal ar fi să avem o componentă care să ne ofere posibilitatea de varia codul HTML generat în funcție de rezultatul callback-ului dat de noi. În cazul nostru, acest rezultat ar fi format din: text-ul link-ului, adresa lui (atributul <code>href</code>) și, opțional, atributul <code>title</code> al acestuia. Aceste informații le vom grupa într-o mică clasă, după cum urmează:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">public class HtmlDynamicAnchorData
{
     public string Href;
     public string Text;
     public string Title;

     public HtmlDynamicAnchorData(string href, string txt)
     {
         Href = href;
         Text = txt;
     }

     public HtmlDynamicAnchorData(string href, string txt, string t)
         : this(href, txt)
     {
         Title = t;
     }
}</pre>
<p>Pentru a crea o componentă reutilizabilă și ușor de folosit, am ales varianta dezvoltării ei ca un server control, pe care l-am denumit <code>HtmlDynamicAnchor</code>. Clasa de bază pentru acesta este <code>System.Web.UI.Control</code>, întrucât nu avem nevoie decât de funcționalități minime, cum ar fi folosirea view-state-ului și renderizarea.</p>
<p>Controlul expune două proprietăți:</p>
<p>- <code>CssClass</code> &#8211; folosită pentru a stabili clasa CSS a link-ului &#8211; opțional;</p>
<p>- <code>MethodName</code> &#8211; folosită pentru a stabili numele metodei ce va returna datele necesare: titlu, url și text &#8211; obligatoriu. Această metodă trebuie să primească drept parametru un obiect de tip HttpContext și să returneze un obiect de tip <code>HtmlDynamicAnchorData</code>.</p>
<p>În plus, am suprascris metoda <code>Control.RenderControl</code>, astfel încât, în loc de a scrie output-ul propriu-zis, scrie un placeholder pentru a marca faptul că va fi actualizat dinamic chiar și atunci când pagina sau control-ul din care face parte va fi cache-uit:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">public override void RenderControl(HtmlTextWriter output)
{
    TemplateControl tc = TemplateControl;
    Type dlgType = typeof(HtmlDynamicAnchorDataDelegate);
    HtmlDynamicAnchorDataDelegate dataDlg = null;
    Dictionary&lt;string, string&gt; attribs = null;
    HtmlDynamicAnchorSubstitution subst = null;
    string attrib = null;

    try
    {
        dataDlg = (HtmlDynamicAnchorDataDelegate)
           Delegate.CreateDelegate(dlgType, tc.GetType(), MethodName);
    }
    catch (Exception)
    {
        dataDlg = null;
    }

    if (dataDlg == null)
        ThrowBadMethodName(MethodName);

    attribs = new Dictionary&lt;string, string&gt;();
    attrib = CssClass;
    if (!string.IsNullOrEmpty(attrib))
        attribs.Add("class", attrib);

    subst = new HtmlDynamicAnchorSubstitution(dataDlg, attribs);
    subst.RegisterCallback(HttpContext.Current);
}</pre>
<p>unde <code>HtmlDynamicAnchorDataDelegate</code> este definită astfel:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">public delegate HtmlDynamicAnchorData
        HtmlDynamicAnchorDataDelegate(HttpContext context);</pre>
<p>Majoritatea operațiilor efectuate de această metodă ar trebui sa fie clare, punctul de interes constituindu-l ultimele două linii. Astfel, <code>HtmlDynamicAnchorSubstitution</code> reprezintă o clasă ajutătoare care:</p>
<p>- prin intermediul metodei <code>RegisterCallback</code> apelează <code>HttpResponse.WriteSubstitution</code> având ca parametru metoda privată <code>Render(context)</code>;</p>
<pre class="brush: csharp; gutter: true; first-line: 1">public void RegisterCallback(HttpContext context)
{
    HttpResponseSubstitutionCallback callback = null;
    HttpResponse response = context.Response;

    callback = new HttpResponseSubstitutionCallback(Render);
    response.WriteSubstitution(callback);
}</pre>
<p>- ține o referință către delegate-ul al cărui nume l-am transmis  controlului nostru, astfel încât, atunci cand metoda privată <code>Render</code> este  apelată, delegate-ul respectiv va fi folosit pentru a obține datele  necesare redării link-ului propriu-zis.</p>
<pre class="brush: csharp; gutter: true; first-line: 1">private string Render(HttpContext context)
{
    StringBuilder sb = null;
    HtmlDynamicAnchorData data = null;

    if (mDataDlg != null)
        data = mDataDlg(context);
    data = mDataDlg(context);
    if (data == null)
        return string.Empty;

    sb = new StringBuilder();
    sb.Append("&lt;a");

    foreach (string attribute in mAttributes.Keys)
    {
        if (attribute == "href" || attribute == "title")
            continue;
        AppendAttribute(sb, attribute, mAttributes[attribute]);
    }

    if (!string.IsNullOrEmpty(data.Href))
        AppendAttribute(sb, "href", data.Href);
    if (!string.IsNullOrEmpty(data.Title))
        AppendAttribute(sb, "title", data.Title);

    sb.Append("&gt;");
    if (mDataDlg != null)
        sb.Append(HttpUtility.HtmlEncode(data.InnerText));
    sb.Append("&lt;/a&gt;");

    return sb.ToString();
}</pre>
<p>Pentru motivul care ne impune folosirea unei clase ajutătoare, vezi obsevația de mai sus.</p>
<p><strong>Folosirea controlului astfel creat</strong></p>
<p>În primul rând, trebuie definită (spre exemplu, în code-behind) metoda care va genera datele pentru redarea controlului:</p>
<pre class="brush: actionscript3; gutter: true; first-line: 1">public static HtmlDynamicAnchorData GetLinkData(HttpContext context)
{
    bool auth = false;
    if (context.Request["IsAuthenticated"] == "true")
        auth = true;
   if (!auth)
       return new HtmlDynamicAnchorData("Login.aspx", "Log-in");
   else
       return new HtmlDynamicAnchorData("Logout.aspx", "Log-out");
}</pre>
<p>Folosirea este apoi cât se poate de simplă:</p>
<p>- mai întâi înregistrăm controlul:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">&lt;%@ Register Assembly="AssemblyName"
    Namespace="ContorlNamespace" TagPrefix="ui" %&gt;</pre>
<p>- apoi îl plasăm unde dorim în pagină:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">&lt;ui:HtmlDynamicAnchor ID="DynamicAnchor"
    runat="server" MethodName="GetLinkData" /&gt;</pre>
<p>În sfârșit, atașez acestui articol un mic proiect demonstrativ, care include întreg codul sursă pentru acest control plus un exemplu de utilizare. <a href="http://alex.livedesign.ro/wp-content/uploads/2011/10/SubstitutionApiSample.zip" target="_blank">Click aici pentru descărcare</a>.</p>
<p>Și în caz că vă întrebați, ce legătură există între articol și imagine, aflați că procedeul descris aici se mai numeste și ”donut caching” <img src='http://alex.livedesign.ro/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
]]></content:encoded>
			<wfw:commentRss>http://alex.livedesign.ro/exemplu-de-utilizare-al-substitution-api-in-asp-net/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Cum facem debugging în PHP</title>
		<link>http://alex.livedesign.ro/cum-facem-debugging-in-php</link>
		<comments>http://alex.livedesign.ro/cum-facem-debugging-in-php#comments</comments>
		<pubDate>Fri, 21 Oct 2011 21:45:02 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[debugging]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[profiling]]></category>

		<guid isPermaLink="false">http://alex.livedesign.ro/?p=78</guid>
		<description><![CDATA[În ceea ce privește debugging-ul și profiling-ul, PHP oferă un suport standard nesemnificativ, golul fiind umplut însă de o serie de extensii și instrumente precum:
- XDebug &#8211; o extensie PHP care oferă facilități precum profiling, code coverage, remote debugging, afișarea stack trace-urilor, afișarea îmbunătățită a rezultatelor produse de var_dump etc.;
- APD &#8211; Advanced PHP Debugger [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://alex.livedesign.ro/wp-content/uploads/2011/10/php-debug.jpg"><img class="alignleft size-full wp-image-93" title="php-debug" src="http://alex.livedesign.ro/wp-content/uploads/2011/10/php-debug.jpg" alt="" width="200" height="117" /></a>În ceea ce privește debugging-ul și profiling-ul, PHP oferă un suport standard nesemnificativ, golul fiind umplut însă de o serie de extensii și instrumente precum:</p>
<p>- <a href="http://xdebug.org/index.php" target="_blank">XDebug</a> &#8211; o extensie PHP care oferă facilități precum profiling, code coverage, remote debugging, afișarea stack trace-urilor, afișarea îmbunătățită a rezultatelor produse de var_dump etc.;</p>
<p>- <a href="http://php.net/apd" target="_blank">APD</a> &#8211; Advanced PHP Debugger &#8211; este tot o extensie de PHP care oferă un API pentru debugging;</p>
<p>- <a href="http://www.php-debugger.com/dbg/" target="_blank">DBG</a> &#8211; software disponibil atât gratuit, cât și cu plată;</p>
<p>- <a href="http://www.firephp.org/" target="_blank">FirePHP</a> &#8211; permite tipărirea mesajelor în consola Firebug, folosind apeluri de funcții executate în scripturile PHP;</p>
<p>- Zend IDE, care vine cu propriul său debugger etc.</p>
<p><strong>Primii pași</strong></p>
<p>Atunci când începem un proiect este bine să avem un mediu de dezvoltare configurat corespunzător, astfel încât să putem fi notificați din timp de eventualele probleme apărute, oricât de nesemnificative ar putea fi. Astfel, când dezvoltăm o aplicație în PHP trebuie să ne asigurăm ca nivelul de raportare a erorilor este cel mai ridicat posibil și că, evident, PHP-ul este configurat să afișeze erorile. Setările necesare ar putea fi făcute dinamic la începutul unui script, după cum urmează:</p>
<pre class="brush: php; gutter: true; first-line: 1">ini_set('display_errors', 'On');
error_reporting(E_ALL);</pre>
<p>Merită menționat faptul că, începând cu PHP5, este disponibilă constanta E_STRICT, care, printre altele, activează emiterea avertizărilor referitoare la metodele deprecated folosite în programul vostru. Astfel, linia 2 ar deveni:</p>
<pre class="brush: php; gutter: true; first-line: 1">error_reporting(E_ALL | E_STRICT)</pre>
<p><span id="more-78"></span></p>
<p><strong>Suportul standard existent în PHP &#8211; limitat</strong></p>
<p>După cum am spus și mai devreme, suportul standard pentru debugging în PHP este insuficient, fiindu-ne puse la dispoziție doar câteva funcții, cum ar fi: var_dump, print_r, sau <a href="http://php.net/manual/en/function.debug-backtrace.php" target="_blank">debug_backtrace</a>. Dintre ele, probabil cea mai utilă (probabil după var_dump <img src='http://alex.livedesign.ro/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> ) ar fi <a href="http://php.net/manual/en/function.debug-backtrace.php" target="_blank">debug_backtrace</a> care, după cum afirmă și numele ei, generează o listă detaliată cu toate apelurile ce au condus la execuția funcției sau liniei unde a fost utilizată, returnând această listă ca un array. Iată și un exemplu:</p>
<pre class="brush: php; gutter: true; first-line: 1">function error_handler($errNo, $errStr) {
    print '&lt;pre&gt;';
    var_dump(debug_backtrace());
    print '&lt;/pre&gt;';
}

set_error_handler('error_handler');
trigger_error('An error occured', E_USER_ERROR);</pre>
<p>Am ilustrat aici un posibil scenariu de utilizare: obținerea backtrace-ului într-un handler global de erori, pentru a investiga condițiile producerii erorii respective. O puteți folosi însă în orice funcție în care doriți să aflați care este contextul în care aceasta este executată.</p>
<p><strong>Componente externe pe care le putem folosi</strong></p>
<p>Pe lângă capabilitățile standard, avem la dispoziție o suită întreagă de extensii și de tool-uri externe de debugging, o parte din ele amintite ceva mai sus. În continuare voi vorbi însă numai de una dintre ele: XDebug.</p>
<p><strong>XDebug</strong></p>
<p>XDebug este o extensie de PHP care facilitează depanarea scripturilor oferind funcționalități precum:</p>
<p>- stacktrace-uri cu un nivel configurabil de detaliere, furnizate automat la apariția unei erori;</p>
<p>- afișarea îmbunătățită a conținutului variabilelor;</p>
<p>- code coverage &#8211; evidențierea liniilor și secvențelor de cod ce au fost efectiv rulate într-un script;</p>
<p>- profiling &#8211; măsurarea performanțelor înregistrate de script ca ajutor în determinarea punctelor lente conținute de acesta;</p>
<p>- remote debugging &#8211; XDebug furnizează o interfață prin care programele externe de debugging pot interacționa cu scripturile PHP.</p>
<p><span style="text-decoration: underline;">Stacktrace-urile și configurarea detaliilor oferite de acestea</span></p>
<p>Atunci când în script va fi declanșată o eroare, fie ea de către interpretor, fie de către programator (folosind trigger_error), XDebug este configurat să afișeze implicit un stacktrace asociat acelei erori.</p>
<pre class="brush: php; gutter: true; first-line: 1">function trigger_user_error() {
    trigger_error('An error occured', E_USER_ERROR);
}
trigger_user_error();
</pre>
<p>Această secvență de cod va produce următorul stacktrace:</p>
<p><a href="http://alex.livedesign.ro/wp-content/uploads/2011/10/Xdebug.StackTrace.Default.png"><img class="aligncenter size-full wp-image-87" title="Xdebug.StackTrace.Default" src="http://alex.livedesign.ro/wp-content/uploads/2011/10/Xdebug.StackTrace.Default.png" alt="" width="595" height="138" /></a></p>
<p>Implicit, sunt prezentate următoarele informații:</p>
<p>- Momentul de timp la care a început execuția funcției;</p>
<p>- Câtă memorie era folosită în momentul respectiv;</p>
<p>- Numele funcției executate;</p>
<p>- Locul (fișierul și linia) în care a fost executată funcția.</p>
<p>Dar, uneori avem nevoie de ceva mai multe informații decât atât, cum ar fi informații despre variabilele transmise ca parametru funcțiilor afișate în stacktrace. Pentru a obține aceste informații, trebuie să activăm colectarea parametrilor în XDebug, atribuind valoarea dorită directivei de configurare <a href="http://xdebug.org/docs/all_settings#collect_params" target="_blank">xdebug.collect_params</a>. Spre exemplu, atribuirea valorii 4 are drept rezultat afișarea numelui variabilei și a conținutului acesteia:</p>
<pre class="brush: php; gutter: true; first-line: 1">ini_set('xdebug.collect_params', '4');</pre>
<p><a href="http://alex.livedesign.ro/wp-content/uploads/2011/10/Xdebug.StackTrace.CollectVariables.4.png"><img class="aligncenter size-full wp-image-88" title="Xdebug.StackTrace.CollectVariables.4" src="http://alex.livedesign.ro/wp-content/uploads/2011/10/Xdebug.StackTrace.CollectVariables.4.png" alt="" width="595" height="142" /></a></p>
<p>Pe lângă această facilitate, XDebug mai permite și afișarea conținutului variabilelor superglobale, sau a celor locale. Pentru mai multe detalii, <a href="http://xdebug.org/docs/stack_trace" target="_blank">accesați pagina de documentație referitoare la stacktrace-uri</a>.</p>
<p>Totuși, dacă dintr-un oarecare motiv doriți să dezactivați temporar afișarea stacktrace-urilor, puteți folosi funcția <a href="http://xdebug.org/docs/basic#xdebug_disable" target="_blank">xdebug_disable()</a> și, pentru a reactiva această funcționalitate, <a href="http://xdebug.org/docs/basic#xdebug_enable" target="_blank">xdebug_enable()</a>.</p>
<p><span style="text-decoration: underline;">Afișarea îmbunătățită a conținutului variabilelor</span></p>
<p>XDebug înlocuiește funcția var_dump existentă în PHP, varianta oferită în schimb producând un output preformatat (nu mai este deci nevoie să folosim tag-ul &lt;pre&gt; pentru a putea citi rezultatul funcției), în care tipurile diferite de date sunt colorate diferit, permițând totodată configurarea nivelului maxim de imbricare, precum și a porțiunii afișabile dintr-un șir de caractere.</p>
<pre class="brush: php; gutter: true; first-line: 1">ini_set('xdebug.var_display_max_children', 5);
ini_set('xdebug.var_display_max_data', 5);
ini_set('xdebug.var_display_max_depth', 1);

$object = new stdClass;
$object-&gt;self = $object;

$array = array(
	'first' =&gt; 1,
	'second' =&gt; 2,
	'third' =&gt; 3,
	'fourth' =&gt; 'Lorem ipsum dolor sit amet (...)',
	'fifth' =&gt; $object,
	'sixth' =&gt; 'Not shown'
);
var_dump($array);</pre>
<p>Secvența de cod de mai sus produce următorul rezultat:</p>
<p style="text-align: left;"><a href="http://alex.livedesign.ro/wp-content/uploads/2011/10/XDebug.VarDump.png"><img class="size-full wp-image-89 aligncenter" title="XDebug.VarDump" src="http://alex.livedesign.ro/wp-content/uploads/2011/10/XDebug.VarDump.png" alt="" width="343" height="148" /></a>Astfel:</p>
<p>- numărul de elemente afișate dintr-un array poate fi configurat cu ajutorul directivei<a href="http://xdebug.org/docs/all_settings#var_display_max_children" target="_blank"> xdebug.var_display_max_children</a>;</p>
<p>- numărul de caractere afișate dintr-un string poate fi configurat cu ajutorul directivei <a href="http://xdebug.org/docs/all_settings#var_display_max_data" target="_blank">xdebug.var_display_max_data</a>;</p>
<p>- nivelul maxim de niveluri imbricate afișate (în cazul array-urilor și al obiectelor) poate fi configurat cu ajutorul directivei <a href="http://xdebug.org/docs/all_settings#var_display_max_depth" target="_blank">xdebug.var_display_max_depth</a>.</p>
<p><span style="text-decoration: underline;">Code coverage</span></p>
<p>Activarea code coverage-ului permite detectarea liniilor dintr-un script, precum și fișierele corespunzătoare, care sunt efectiv executate la rularea unui program. Acest lucru este util atunci când doriți să aflați cât de eficiente sunt testele pe care le-ați scris, întrucât vă arată practic ce zone de cod au rămas neacoperite de test case-uri.</p>
<pre class="brush: php; gutter: true; first-line: 1">ini_set('xdebug.coverage_enable', '1');

xdebug_start_code_coverage();

function execute($value) {
	if ($value % 2 == 0) {
		return $value + 3;
	} else {
		return $value + 2;
	}
}

$values = array( 2, 4, 6, 8, 10 );
foreach ($values as $value) {
	$value = execute($value);
}

$coverage = xdebug_get_code_coverage();

print '&lt;pre&gt;';
foreach ($coverage as $file =&gt; $execLines) {
	$fileContents = file($file);
	$lines = array_keys($execLines);
	print '&lt;b&gt;' . $file . '&lt;/b&gt;' . PHP_EOL;
	foreach ($fileContents as $idx =&gt; $line) {
		if (in_array($idx + 1, $lines)) {
			print '&lt;span style="background-color:red; color:white;"&gt;'
				. htmlentities($line) .
					'&lt;/span&gt;';
		} else {
			print htmlentities($line);
		}
	}
}
print '&lt;/pre&gt;';</pre>
<p>Exemplul de față ilustrează următoarele operații:</p>
<p>- activarea code coverage-ului (acesta este dezactivat implicit, din motive ce țin de performanță);</p>
<p>- începerea colectării informațiilor legate de code-coverage, apelând <a href="http://xdebug.org/docs/code_coverage#xdebug_start_code_coverage" target="_blank">xdebug_start_code_coverage()</a>;</p>
<p>- citirea acestor informații într-un array, folosind <a href="http://xdebug.org/docs/code_coverage#xdebug_get_code_coverage" target="_blank">xdebug_get_code_coverage()</a>;</p>
<p>- folosirea datelor obținute pentru a evidenția liniile executate (liniile 20 &#8211; 35).</p>
<p><span style="text-decoration: underline;">Remote debugging</span></p>
<p>XDebug oferă o interfață prin intermediul căreia clienții de debugging pot interacționa cu scripturile PHP. Pentru a realiza acest lucru, XDebug pune la dispoziție două protocoale:</p>
<p>- vechiul protocol GDB, folosit din linia de comandă;</p>
<p>- <a href="http://xdebug.org/docs-dbgp.php" target="_blank">DBGp</a>, suportat începând cu XDebug 2, folosit și în exemplele de mai jos.</p>
<p>În ceea ce privește clienții de debugging, <a href="http://xdebug.org/docs/remote#clients" target="_blank">oferta este foarte variată</a>, din care am ales plug-in-ul de <a href="http://notepad-plus-plus.org/" target="_blank">Notepad++</a>, ce poate fi descărcat gratuit de <a href="http://sourceforge.net/projects/npp-plugins/files/DBGP%20Plugin/DBGP%20Plugin%20v0.12%20beta/" target="_blank">aici</a>. Pentru a-l instala, pur și simplu copiați fișierul dbgpPlugin.dll în folderul plugins din locul unde ați instalat Notepad++, după care reporniți editorul.</p>
<p>Pentru a putea iniția o sesiune de debugging trebuie mai întâi să configurați XDebug:</p>
<p>- activarea suportului pentru remote debugging se face atribuind valoarea 1 directivei <a href="http://xdebug.org/docs/all_settings#remote_enable" target="_blank">xdebug.remote_enable</a>;</p>
<p>- adresa unde rulează clientul de debugging și portul pe care ascultă acesta sunt specificate ca valori ale directivelor <a href="http://xdebug.org/docs/all_settings#remote_host" target="_blank">xdebug.remote_host</a>, respectiv <a href="http://xdebug.org/docs/all_settings#remote_port" target="_blank">xdebug.remote_port</a>;</p>
<p>- modul de debugging este stabilit folosind directiva <a href="http://xdebug.org/docs/all_settings#remote_mode" target="_blank">xdebug.remote_mode</a>; valorile posibile sunt ”req” (implicit, sesiunea de debugging este inițiată odată cu pornirea scriptului) sau ”jit” (sesiunea de debugging este inițiată la apariția unei erori).</p>
<p>Aceste configurări vor fi salvate în php.ini, sub grupul XDebug:</p>
<pre class="brush: php; gutter: true; first-line: 1">[XDebug]
xdebug.remote_enable = 1
xdebug.remote_host = 127.0.0.1
xdebug.remote_port = 9000
xdebug.remote_mode = req</pre>
<p>Pentru ca aceste modificări să aibă efect, trebuie ca server-ul web să fie repornit. Odată configurat XDebug, puteți porni consola de debugging din Notepad++:</p>
<p><a href="http://alex.livedesign.ro/wp-content/uploads/2011/10/DBGP.OpenConsole.png"><img class="aligncenter size-full wp-image-90" title="DBGP.OpenConsole" src="http://alex.livedesign.ro/wp-content/uploads/2011/10/DBGP.OpenConsole.png" alt="" width="406" height="87" /></a></p>
<p>Interfața de debugging oferă aproape toate controalele obișnuite pentru o astfel de consolă: stabilirea breakpoint-urilor, step-in, step-out, step-over, watch etc. După ce ați stabilit breakpoint-urile nu vă ramâne decât să accesați din browser script-ul pe care doriți să îl depanați adăugand la URL parametrul <code>XDEBUG_SESSION_START=nume_sesiune</code>, pentru a activa debugger-ul.</p>
<p>Pentru a putea vedea contextele local (variabilele aflate în scriptul / metoda / funcția curentă) și global (variabilele superglobale<code>$_SERVER</code>, <code>$_ENV</code> etc.) actualizate la fiecare step, trebuie să accesați interfața de configurare a plugin-ului: Plugins / DBGp / Config. Aici va trebui să bifați următoarele opțiuni:</p>
<p>- Refresh local context on every step &#8211; pentru actualizarea contextului local;</p>
<p>- Refresh global context on every step &#8211; pentru actualizarea contextului global.</p>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 332px; width: 1px; height: 1px; overflow: hidden;"><span class="methodname"><strong>debug_backtrace</strong></span></div>
]]></content:encoded>
			<wfw:commentRss>http://alex.livedesign.ro/cum-facem-debugging-in-php/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SFTP în C#/.NET folosind SSH.NET</title>
		<link>http://alex.livedesign.ro/ssh-si-sftp-in-c-net-folosind-ssh-net</link>
		<comments>http://alex.livedesign.ro/ssh-si-sftp-in-c-net-folosind-ssh-net#comments</comments>
		<pubDate>Fri, 30 Sep 2011 18:06:38 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[.net]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[SFTP]]></category>
		<category><![CDATA[SSH]]></category>
		<category><![CDATA[SSH.NET]]></category>

		<guid isPermaLink="false">http://alex.livedesign.ro/?p=77</guid>
		<description><![CDATA[Una din sarcinile pe care le-am avut de îndeplinit recent a presupus, printre altele, și gestiunea unor fișiere aflate pe alte mașini (ex: listarea directoarelor, descărcarea fișierelor necesare, procesarea lor, încărcarea lor pe o altă masină etc.). Pentru ce aveam nevoie, o soluție bazată pe protocolul SFTP era deci cea mai indicată, iar singura soluție [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://alex.livedesign.ro/wp-content/uploads/2011/09/winscpau4.png"><img class="alignleft size-thumbnail wp-image-84" title="winscpau4" src="http://alex.livedesign.ro/wp-content/uploads/2011/09/winscpau4-150x150.png" alt="" width="150" height="150" /></a>Una din sarcinile pe care le-am avut de îndeplinit recent a presupus, printre altele, și gestiunea unor fișiere aflate pe alte mașini (ex: listarea directoarelor, descărcarea fișierelor necesare, procesarea lor, încărcarea lor pe o altă masină etc.). Pentru ce aveam nevoie, o soluție bazată pe protocolul <a href="http://en.wikipedia.org/wiki/SSH_File_Transfer_Protocol" target="_blank">SFTP</a> era deci cea mai indicată, iar singura soluție open-source pentru platforma .NET pe care am putut-o găsi a fost <a href="http://sshnet.codeplex.com/" target="_blank">SSH.NET</a> (<a href="http://www.tamirgal.com/blog/page/SharpSSH.aspx" target="_blank">SharpSSH</a> nu funcționa corespunzător și nici nu mai este dezvoltată de ceva vreme, iar o soluție platită nu își justifica în nici un fel costurile în cazul de față).</p>
<p>SSH.NET este un proiect complet nou, foarte bine scris care, din câte observ, primește din ce în ce mai multă atenție (paradoxal însă, am dat destul de greu de el). Fiind însă destul de slab documentat (pe undeva e și normal să fie așa, focusul fiind momentan pe dezvoltare) m-am gândit că ar fi bine sa ilustrez într-o serie de două articole modalitatea de utilizare a acestei librării pentru a lucra cu SFTP, respectiv <a href="http://en.wikipedia.org/wiki/Secure_Shell" target="_blank">SSH</a> în C#/.NET.</p>
<p><strong>Conectarea</strong></p>
<p>Întâi de toate, trebuie să aflați ce modalități de conectare suportă server-ul la care vă conectați. Odată ce aflați aceste detalii, veți configura o instanță a uneia dintre clasele:</p>
<p>- <code>PrivateKeyConnectionInfo</code> &#8211; pentru autentificarea folosind o pereche de chei;</p>
<p>- <code>PasswordConnectionInfo</code> &#8211; pentru autentificarea folosind un nume de utilizator și o parolă;</p>
<p>- <code>KeyboardInteractiveConnectionInfo</code> &#8211; pentru a folosi modalitatea de autentificare interactivă (care, în particular, poate funcționa asemănător modalității de autentificare cu parolă).</p>
<p>pentru a vă putea autentifica.</p>
<p><span id="more-77"></span>În exemplul de mai jos am folosit modalitatea de autentificare interactivă:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">private void ConnectToServer()
{
    _kbConnectionInfo = new KeyboardInteractiveConnectionInfo("127.0.0.1", 22, "root");
    _kbConnectionInfo.AuthenticationBanner += new EventHandler(OnAuthenticationBanner);
    _kbConnectionInfo.AuthenticationPrompt += new EventHandler(OnAuthenticationPrompt);
    _sftpClient = new SftpClient(_kbConnectionInfo);
    _sftpClient.Connect();
}

private void OnAuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)
{
    foreach (AuthenticationPrompt p in e.Prompts)
        p.Response = "root";
}

private void OnAuthenticationBanner(object sender, AuthenticationBannerEventArgs e)
{
    Console.WriteLine(e.BannerMessage);
}</pre>
<p>Se observă că, pe lângă simpla furnizare a adresei server-ului, a portului si a numelui de utilizator, atașăm handlere pentru două evenimente:</p>
<p>- <code>AuthenticationBanner</code> &#8211; care este declanșat atunci când (și dacă) banner-ul de autentificare este trimis dinspre server. Acest eveniment este disponibil indiferent de ce modalitate de autentificare veți folosi și nu este neapărat necesar să înregistrați un handler pentru el;</p>
<p>- <code>AuthenticationPrompt</code> &#8211; este declanșat atunci când server-ul trimite prompt-urile de autentificare. Acest eveniment este specific modalității de autentificare interactivă și este obligatoriu să înregistrați un handler pentru el, în vederea finalizării cu succes a autentificării. În cazul de față, este vorba de un server care pur și simplu se așteptă să îi trimitem o parolă validă, așă că nu avem altceva de făcut decât să scriem parola în răspunsul prompt-ului conținut în colecția <code>e.Prompts</code>. Pentru scenarii mai avansate însă, puteți inspecta și proprietatea <code>AuthenticationPrompt.Request</code> pentru a lua deciziile potrivite.</p>
<p>Odată creat și configurat obiectul ce reprezintă modalitatea de autentificare, acesta va fi dat ca parametru la crearea unei noi instanțe de <code>SftpClient</code>, clasa care oferă metodele de lucru cu fișierele si directoarele server-ului prin intermediul SFTP.</p>
<p><strong>Lucrul cu fișierele și directoarele</strong></p>
<p>Cum scopul principal al unui client SFTP îl reprezintă manipularea fișierelor și directoarelor, să vedem ce ne oferă SSH.NET în acest sens.</p>
<p>Cea mai simplă operație posibilă este aflarea directorului curent:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">Console.WriteLine(_sftpClient.WorkingDirectory);</pre>
<p>Pentru a lista conținutul unui director vom folosi metoda <code>SftpClient.ListDirectory</code>, ce returnează o colecție de obiecte de tip <code>SftpFile</code>:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">_entries = _sftpClient.ListDirectory("/path/or/dir");
foreach (SftpFile _entry in _entries)
    Console.WriteLine(_entry.Name);</pre>
<p>Schimbarea directorului curent se realizează foarte simplu, folosind metoda <code>SftpClient.ChangeDirectory</code>:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">_sftpClient.ChangeDirectory("/path/to/dir");</pre>
<p>Pentru a copia un fișier de pe server se folosește metoda <code>SftpClient.DownloadFile</code>, care ia ca parametri calea fișierului ce va fi descărcat de pe server și un <code>System.IO.Stream</code> în care va fi scris fișierul:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">_fs = new FileStream("/to", FileMode.OpenOrCreate, FileAccess.Write);
_sftpClient.DownloadFile("/from", _fs);</pre>
<p>Pentru a încărca un fișier pe server vom proceda asemănător, folsind metoda <code>SftpClient.UploadFile</code>, care ia ca parametri un <code>System.IO.Stream</code> din care va fi citit conținutul fișierului și calea de pe server unde va fi stocat:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">_fs = new FileStream("/from", FileMode.Open, FileAccess.Read);
_sftpClient.UploadFile(_fs, "/to");</pre>
<p>De remarcat că aceste două metode, <code>UploadFile</code> și <code>DownloadFile</code> nu limitează scenariile de utilizare la încărcarea dintr-un fișier, respectiv descărcarea într-un fișier, programatorul putând opta pentru folosirea și altor tipuri de stream-uri, cum ar fi un <code>System.IO.MemoryStream</code>.</p>
<p>Dar dacă dorim să ștergem un fișier sau un director? În acest caz vom folosi metodele <code>SftpClient.DeleteFile</code>, respectiv <code>SftpClient.DeleteDirectory</code>:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">_sftpClient.DeleteFile("/path/to/delete");</pre>
<p>O metodă la fel de utilă este și <code>SftpClient.Exists</code> care returnează true în cazul existenței căii către fișierul sau directorul dat ca parametru.</p>
<p><strong>Tratarea erorilor</strong></p>
<p>Cum lucrurile mai merg și prost uneori, aceste metode pot genera excepții, în funcție de cazurile limită întâlnite. Astfel:</p>
<p>- o excepție de tip <code>SftpPermissionDeniedException</code> este generată atunci când operația nu este permisă pentru utilizatorul curent;</p>
<p>- o excepție de tip <code>SftpPathNotFoundException</code> este generată atunci când calea asupra căreia se efectuează operația nu a fost găsită pe server;</p>
<p>- o excepție de tip <code>SshException</code> când orice altceva merge prost în comunicația dintre server și client.</p>
<p>Astfel, o operație efectuată pe server nu se reduce la un simplu apel al metodei corespunzătoare, fiind necesară și o tratare a erorilor. Spre exemplu, operația de ștergere a unui fișier ar putea arăta așa:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">try
{
    _sftpClient.DeleteFile("/at/path");
}
catch (SftpPathNotFoundException)
{
    Console.WriteLine("Path not found");
}
catch (SftpPermissionDeniedException)
{
    Console.WriteLine("Permission denied");
}
catch (Exception)
{
    Console.WriteLine("File could not be deleted");
}</pre>
<p><strong>Operații asincrone</strong></p>
<p>Toate operațiile descrise până acum au fost sincrone, fapt ce ne poate pune în dificultate dacă avem de-a face cu operații care necesită mult timp pentru a fi terminate (ex: transferul unui fișier mare, latența mare a rețelei etc.). Soluțiile la această problemă sunt fie desfășurarea operațiilor explicit într-un thread separat de thread-ul în care rulează interfața, fie utilizarea facilităților de <a href="http://www.codeproject.com/KB/cs/AsyncMethodInvocation.aspx" target="_blank">operații asincrone</a> oferite de clasa <code>SftpClient</code>. Pentru exemplificare am ales operația de încărcare a unui fișier:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">_fs = new FileStream("/from", FileMode.Open, FileAccess.Read);
_sftpClient.BeginUploadFile(_fs, "/to", AsyncCopyCallback, _fs);</pre>
<p>unde <code>AsyncCopyCallback</code> este definită astfel:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">private void AsyncCopyCallback(IAsyncResult result)
{
    FileStream _uploadedStream = null;

    try
    {
        _sftpClient.EndUploadFile(result);
    }
    catch (SftpPathNotFoundException)
    {
        Console.WriteLine("Path not found");
    }
    catch (SftpPermissionDeniedException)
    {
        Console.WriteLine("Permission denied");
    }
    catch (Exception)
    {
        Console.WriteLine("Could not copy file");
    }
    finally
    {
        _uploadedStream = result.AsyncState as FileStream;
        if (_uploadedStream != null)
            _uploadedStream.Dispose();
    }
}</pre>
<p>Vă sunt dator cu câteva explicații:</p>
<p>- orice excepție apărută în timp ce fișierul este încărcat, va fi generată odată cu apelul metodei EndUploadFile (acest lucru este de fapt general valabil pentru orice metodă invocată asincron în .NET, nu numai în cazul particular al librăriei SSH.NET care <em>reproduce</em> acest comportament);</p>
<p>- toate operațiile primesc drept parametri suplimentari un callback ce va fi executat când se finalizează operația (putem seta acest parametru la valoarea null daca nu suntem interesați de a primi această notificare) și un obiect ce poate conține informații suplimentare și care se regăsește în <code>IAsyncResult.AsyncState</code> (și acesta poate fi, și de cele mai multe ori chiar este, setat la valoarea null). În cazul de față, cel din urmă parametru este folosit pentru a transmit stream-ul din care se citește, pentru a-l putea închide la finalul operației (evident, puteam să ținem o referință către stream într-o variabilă privată, însă poate nu ne dorim întotdeauna acest lucru).</p>
<p><a href="http://alex.livedesign.ro/wp-content/uploads/2011/10/SSHDotNetSample.zip" target="_blank">La final, atașez un mic proiect care exemplifică utilizarea clasei SftpClient</a>. Proiectul conține o aplicație ce rulează în consolă și oferă câteva funcționalități de bază pentru interacționarea cu un server SFTP, a cărui adresă o puteți configura în app.config. Numele de utilizator și parola folosite la autentificare pot fi deasemenea configurate în app.config.</p>
]]></content:encoded>
			<wfw:commentRss>http://alex.livedesign.ro/ssh-si-sftp-in-c-net-folosind-ssh-net/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>NorcImoDesktop &#8211; Aplicație desktop pentru gestiunea anunțurilor din NORC Imobiliare</title>
		<link>http://alex.livedesign.ro/norcimodesktop-aplicatie-desktop-pentru-gestiunea-anunturilor-din-norc-imobiliare</link>
		<comments>http://alex.livedesign.ro/norcimodesktop-aplicatie-desktop-pentru-gestiunea-anunturilor-din-norc-imobiliare#comments</comments>
		<pubDate>Mon, 26 Sep 2011 12:09:02 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Portofoliu]]></category>
		<category><![CDATA[ActionScript]]></category>
		<category><![CDATA[Adobe Air]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[c#]]></category>

		<guid isPermaLink="false">http://alex.livedesign.ro/?p=79</guid>
		<description><![CDATA[Tehnologii folosite
- ActionScript și Adobe Air pentru dezvoltarea aplicației desktop;
- C# și ASP.NET pentru serviciile web ce rulează pe serverele NORC și furnizează date pentru aplicație.
Responsabilitățile avute în cadrul proiectului
- dezvoltarea integrală a aplicației desktop;
- am contribuit la dezvoltarea serviciului web ce furnizează date pentru aplicație.
Scurtă descriere a aplicației
Aplicația NorcImoDesktop propune o modalitate simplă pentru [...]]]></description>
			<content:encoded><![CDATA[<p><strong><a href="http://alex.livedesign.ro/wp-content/uploads/2011/09/NorcImoDesktopLogo.png"><img class="alignleft size-full wp-image-80" title="NorcImoDesktopLogo" src="http://alex.livedesign.ro/wp-content/uploads/2011/09/NorcImoDesktopLogo.png" alt="" width="254" height="186" /></a>Tehnologii folosite</strong></p>
<p>- <a href="http://alex.livedesign.ro/tag/actionscript" target="_blank">ActionScript</a> și <a href="http://alex.livedesign.ro/tag/adobe-air" target="_blank">Adobe Air</a> pentru dezvoltarea aplicației desktop;</p>
<p>- <a href="http://alex.livedesign.ro/tag/c" target="_blank">C#</a> și <a href="http://alex.livedesign.ro/tag/asp-net" target="_blank">ASP.NET</a> pentru serviciile web ce rulează pe serverele NORC și furnizează date pentru aplicație.</p>
<p><strong>Responsabilitățile avute în cadrul proiectului</strong></p>
<p>- dezvoltarea integrală a aplicației desktop;</p>
<p>- am contribuit la dezvoltarea serviciului web ce furnizează date pentru aplicație.</p>
<p><strong>Scurtă descriere a aplicației</strong></p>
<p><a href="http://www.norc-imobiliare.ro/desktop/" target="_blank">Aplicația NorcImoDesktop</a> propune o modalitate simplă pentru gestionarea anunțurilor existente pe NORC Imobiliare, precum și pentru adăugarea de noi anunțuri.</p>
<p>Facilitățile oferite includ:</p>
<p>- o interfata simpla si bine structurata pentru introducerea de anunturi noi pe        site;</p>
<p>- modificarea textului anuntului, asistata de sugestii de descriere precum si de generarea automata a unui text ce include punctele de interes din jur;</p>
<p>- modificarea/stabilirea amplasamentului proprietatii folosind harta digitala;</p>
<p>- promovarea anunțurilor;</p>
<p>- vizualizarea performanțelor contului.</p>
<p><span id="more-79"></span>
<div class="ngg-galleryoverview" id="ngg-gallery-25-79">


	
	<!-- Thumbnails -->
		
	<div id="ngg-image-228" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/adaugaanunt.png" title=" " class="thickbox" rel="set_25" >
								<img title="adaugaanunt" alt="adaugaanunt" src="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/thumbs/thumbs_adaugaanunt.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-229" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/anunturidraft.png" title=" " class="thickbox" rel="set_25" >
								<img title="anunturidraft" alt="anunturidraft" src="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/thumbs/thumbs_anunturidraft.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-230" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/anunturipublice.png" title=" " class="thickbox" rel="set_25" >
								<img title="anunturipublice" alt="anunturipublice" src="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/thumbs/thumbs_anunturipublice.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-231" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/consumcredite.png" title=" " class="thickbox" rel="set_25" >
								<img title="consumcredite" alt="consumcredite" src="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/thumbs/thumbs_consumcredite.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-232" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/dashboard_1.png" title=" " class="thickbox" rel="set_25" >
								<img title="dashboard_1" alt="dashboard_1" src="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/thumbs/thumbs_dashboard_1.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-233" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/dashboard_2.png" title=" " class="thickbox" rel="set_25" >
								<img title="dashboard_2" alt="dashboard_2" src="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/thumbs/thumbs_dashboard_2.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-234" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/despre.png" title=" " class="thickbox" rel="set_25" >
								<img title="despre" alt="despre" src="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/thumbs/thumbs_despre.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-235" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/editadresa.png" title=" " class="thickbox" rel="set_25" >
								<img title="editadresa" alt="editadresa" src="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/thumbs/thumbs_editadresa.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-236" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/editdescriere.png" title=" " class="thickbox" rel="set_25" >
								<img title="editdescriere" alt="editdescriere" src="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/thumbs/thumbs_editdescriere.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-237" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/editdetaliigenerale.png" title=" " class="thickbox" rel="set_25" >
								<img title="editdetaliigenerale" alt="editdetaliigenerale" src="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/thumbs/thumbs_editdetaliigenerale.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-238" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/editdetaliisuplimentare.png" title=" " class="thickbox" rel="set_25" >
								<img title="editdetaliisuplimentare" alt="editdetaliisuplimentare" src="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/thumbs/thumbs_editdetaliisuplimentare.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-239" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/editgaleriemedia.png" title=" " class="thickbox" rel="set_25" >
								<img title="editgaleriemedia" alt="editgaleriemedia" src="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/thumbs/thumbs_editgaleriemedia.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-240" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/editstreetview.png" title=" " class="thickbox" rel="set_25" >
								<img title="editstreetview" alt="editstreetview" src="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/thumbs/thumbs_editstreetview.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 		
	<div id="ngg-image-241" class="ngg-gallery-thumbnail-box"  >
		<div class="ngg-gallery-thumbnail" >
			<a href="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/login.png" title=" " class="thickbox" rel="set_25" >
								<img title="login" alt="login" src="http://alex.livedesign.ro/wp-content/gallery/norcimodesktop/thumbs/thumbs_login.png" width="100" height="75" />
							</a>
		</div>
	</div>
	
		
 	 	
	<!-- Pagination -->
 	<div class='ngg-clear'></div>
 	
</div>

</p>
]]></content:encoded>
			<wfw:commentRss>http://alex.livedesign.ro/norcimodesktop-aplicatie-desktop-pentru-gestiunea-anunturilor-din-norc-imobiliare/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Procesarea documentelor HTML în PHP folosind SimpleHtmlDom</title>
		<link>http://alex.livedesign.ro/procesarea-documentelor-html-php-folosind-simplehtmldom</link>
		<comments>http://alex.livedesign.ro/procesarea-documentelor-html-php-folosind-simplehtmldom#comments</comments>
		<pubDate>Mon, 12 Sep 2011 21:19:38 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[dom]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[scraping]]></category>
		<category><![CDATA[simplehtmldom]]></category>

		<guid isPermaLink="false">http://alex.livedesign.ro/?p=68</guid>
		<description><![CDATA[Un subiect interesant pentru orice programator, indiferent de limbajul de programare pe care il folosește, îl reprezintă procesarea documentelor HTML, iar expresiile regulate, deși sunt o abordare destul de întâlnită, nu reprezintă întotdeauna o soluție potrivită.  De multe ori (mai ales atunci când avem nevoie să efectuăm operații complexe sau sensibile, cum ar fi validările) [...]]]></description>
			<content:encoded><![CDATA[<p><a style="padding-right: 5px; padding-bottom: 5px;" href="http://alex.livedesign.ro/wp-content/uploads/2011/09/cool-html-codes.jpg"><img class="alignleft size-medium wp-image-75" title="cool-html-codes" src="http://alex.livedesign.ro/wp-content/uploads/2011/09/cool-html-codes-300x225.jpg" alt="" width="216" height="162" /></a>Un subiect interesant pentru orice programator, indiferent de limbajul de programare pe care il folosește, îl reprezintă procesarea documentelor HTML, iar expresiile regulate, deși sunt o abordare destul de întâlnită, <a href="http://www.codinghorror.com/blog/2009/11/parsing-html-the-cthulhu-way.html" target="_blank">nu reprezintă întotdeauna o soluție potrivită</a>.  De multe ori (mai ales atunci când avem nevoie să efectuăm operații complexe sau sensibile, cum ar fi validările) se impune folosirea unui procesor HTML pentru a citi, modifica sau produce un document, unul din aceste procesoare fiind <a href="http://simplehtmldom.sourceforge.net/" target="_blank">SimpleHtmlDom</a>, despre care am ales să scriu în continuare.</p>
<p>Pentru a nu da acestei expuneri un aer plictisitor de manual școlar, am ales să exemplific conceptele folosind un site aflat în producție, alegând în acest scop <a href="http://www.realitatea.net/" target="_blank">www.realitatea.net</a>. Voi extrage din pagina principală a acestui site următoarele elemente:</p>
<p>- titlul paginii HTML;</p>
<p>- toate foile de still incluse;</p>
<p>- toate scripturile externe incluse;</p>
<p>- articolul principal: titlu, fotografie și conținut;</p>
<p>- cele mai noi știri: titlu si link.</p>
<p><strong><span id="more-68"></span>Pregătirile</strong></p>
<p>Pentru început, va trebui să <a href="http://simplehtmldom.sourceforge.net/" target="_blank">descărcați SimpleHtmlDom de aici</a>. Pachetul include mai multe fișiere și directoare, dar veți avea nevoie doar de simple_html_dom.php, pe care îl veți include ca de obicei:</p>
<pre class="brush: php; gutter: true; first-line: 1">require_once 'simple_html_dom.php';</pre>
<p><strong>Analiza paginii</strong></p>
<p>Pasul următor presupune puțină muncă de documentare: dacă modalitatea de obținere a script-urilor, foilor de stil și a titlului este identică pentru orice pagină html, pentru a citi celelalte elemente va trebui să analizăm structura paginii în cauză. Acest lucru poate fi făcut vizualizând direct sursa sau, mai ușor, folosind <a href="http://getfirebug.com/" target="_blank">Firebug</a>. Iată ce obținem:</p>
<div id="attachment_70" class="wp-caption aligncenter" style="width: 466px"><a href="http://alex.livedesign.ro/wp-content/uploads/2011/09/RealitateaRaw1.png"><img class="size-full wp-image-70 " title="RealitateaRaw" src="http://alex.livedesign.ro/wp-content/uploads/2011/09/RealitateaRaw1.png" alt="" width="456" height="270" /></a><p class="wp-caption-text">Codul HTML pentru articolul principal</p></div>
<div id="attachment_73" class="wp-caption aligncenter" style="width: 468px"><a href="http://alex.livedesign.ro/wp-content/uploads/2011/09/RealitateaRawLatestNews.png"><img class="size-full wp-image-73" title="RealitateaRawLatestNews" src="http://alex.livedesign.ro/wp-content/uploads/2011/09/RealitateaRawLatestNews.png" alt="" width="458" height="215" /></a><p class="wp-caption-text">Codul HTML pentru lista de cele mai noi articole</p></div>
<p><strong>Să trecem la treabă</strong></p>
<p>Înarmați cu aceste informații, putem  începe lucrul efectiv. Mai întâi, încărcăm pagina și construim arborele DOM:</p>
<pre class="brush: php; gutter: true; first-line: 1">$html = file_get_html('http://www.realitatea.net/');</pre>
<p>Pentru obținerea titlului paginii, nu avem decât să  căutam primul element  și să îi citim conținutul:</p>
<pre class="brush: php; gutter: true; first-line: 1">$title = $html-&gt;find('title', 0);
if ($title) {
    $title = $title-&gt;plaintext;
}</pre>
<p>Metoda <code>find</code> este metoda pe care o veți folosi cel mai mult. Exemplul de față este probabil cel mai simplu caz de utilizare: găsește elementele având tag-ul dat ca prim parametru (<code>title</code>) și returnează numai elementul cu indexul dat ca al doilea parametru (0, adică primul element). În plus, mai merită menționat că putem obține conținutul text al unui element (din care au fost scoase tag-urile HTML) folosind proprietatea plaintext (ex: <code>$title-&gt;plaintext</code>).</p>
<p>Să citim acum lista de script-uri și lista de foi de stil:</p>
<pre class="brush: php; gutter: true; first-line: 1">$styles = array();
$domStyles = $html-&gt;find('link[rel=stylesheet]');
foreach ($domStyles as $ds) {
    $styles[] = $ds-&gt;href;
}</pre>
<pre class="brush: php; gutter: true; first-line: 1">$scripts = array();
$domJs = $html-&gt;find('script[type=text/javascript]');
foreach ($domJs as $dj) {
    if (!$dj-&gt;src) {
        continue;
    }
    $scripts[] = $dj-&gt;src;
}</pre>
<p>Aceste două exemple ilustrează o altă modalitate de selecție a elementelor: toate elementele HTML cu numele de tag dat,  filtrând după valoarea unui anumit atribut (ex: <code>style[rel=stylesheet]</code>), precum și faptul că putem accesa atributele html ca proprietăți ale obiectului ce reprezintă tag-ul respectiv (<code>$ds-&gt;href</code>).</p>
<p>Urmează extragerea articolului principal:</p>
<pre class="brush: php; gutter: true; first-line: 1">$mainNews = array();
$mainNewsDom = $html-&gt;getElementById('mainNews');
if ($mainNewsDom) {
	//title
	$mainNewsTitleDom = $mainNewsDom-&gt;find('div.title h2', 0);
	if ($mainNewsTitleDom) {
		$mainNews['title'] = $mainNewsTitleDom-&gt;plaintext;
	}

	//content
	$mainNewsContentDom = $mainNewsDom-&gt;find('div.leadIn p');
	if ($mainNewsContentDom) {
		foreach ($mainNewsContentDom as $content) {
			$content = trim($content-&gt;plaintext);
			if (empty($content)) {
				continue;
			}
			$mainNews['content'] = $content;
		}
	}

	//picture
	$mainPicDom = $html-&gt;getElementById('main_gallery');
	if ($mainPicDom &amp;&amp; ($mainPicDom = $mainPicDom-&gt;find('img', 0))) {
		$mainNews['pic'] = $mainPicDom-&gt;src;
	}
}
</pre>
<p>Ce putem observa în această secvență de cod:</p>
<p>- că putem identifica un element direct dupa identificatorul său (valoarea atributului id): <code>$html-&gt;getElementById('mainNews');</code></p>
<p>- că fiecare nod suportă metoda find;</p>
<p>- că putem interoga arborele după o anumită ierarhie, în exemplul de față cerând toate elementele h2 care sunt descendente ai tag-urilor div cu clasa leadIn: <code>$mainNewsDom-&gt;find('div.title h2', 0)</code>.</p>
<p>În sfârșit, să citim cele mai noi articole:</p>
<pre class="brush: php; gutter: true; first-line: 1">$latestNewsDom = $html-&gt;find('div.newsList ul div');
if ($latestNewsDom) {
	foreach ($latestNewsDom as $lnd) {
		$lnd = $lnd-&gt;first_child()-&gt;first_child();
		$latestNews[] = array(
			'url' =&gt; $lnd-&gt;getAttribute('href'),
			'title' =&gt; $lnd-&gt;plaintext
		);
	}
}
</pre>
<p>În acest fel aflăm și cum putem accesa primul copil al unui nod: <code>$nod-&gt;first_child()</code>. Pe lângă <code>first_child()</code> mai există următoarele metode care permit aflarea nodurilor învecinate:</p>
<p>- <code>$nod-&gt;parent()</code> &#8211; accesarea nodului părinte;</p>
<p>- <code>$nod-&gt;last_child()</code> &#8211; accesarea ultimului copil al unui nod;</p>
<p>- <code>$nod-&gt;next_sibling()</code> &#8211; accesarea următorului nod aflat pe același nivel al ierarhiei cu nodul $nod;</p>
<p>- <code>$nod-&gt;prev_sibling()</code> &#8211; accesarea precendentului nod aflat pe același nivel al ierarhiei cu nodul $nod.</p>
<p>Evident, API-ul oferit de către SimpleHtmlDom suportă mai multe modalități de procesare a conținutului unui document HTML, modalități pe care le puteți afla citind documentația ce înșoțește pachetul oferit de autor. În final, atașez codul sursă ce reunește toate aceste exemple într-un script demonstrativ.</p>
<p><a href="http://alex.livedesign.ro/wp-content/uploads/2011/09/SimpleHtmlDom.zip">Click aici pentru a descărca exemplul.</a><br class="spacer_" /></p>
]]></content:encoded>
			<wfw:commentRss>http://alex.livedesign.ro/procesarea-documentelor-html-php-folosind-simplehtmldom/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Google Maps API V3 și problema lui MapMoveEnd</title>
		<link>http://alex.livedesign.ro/google-maps-api-v3-si-problema-lui-mapmoveend</link>
		<comments>http://alex.livedesign.ro/google-maps-api-v3-si-problema-lui-mapmoveend#comments</comments>
		<pubDate>Sat, 10 Sep 2011 08:36:59 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[google maps api v3]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[moveend]]></category>

		<guid isPermaLink="false">http://alex.livedesign.ro/?p=65</guid>
		<description><![CDATA[Sau problema lipsei lui MapMoveEnd, ca să fiu mai exact. Mai întâi, însă, permiteți-mi să trasez, în linii mari, contextul acestei probleme. În versiunea 2 a API-ului Google Maps exista un eveniment declanșat de hartă, denumit sugestiv moveend. Acest eveniment semnifica finalizarea modificărilor suferite de suprafața afișată de hartă, modificări produse de oricare din următoarele [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://alex.livedesign.ro/wp-content/uploads/2011/09/Google-Maps-Logo.jpg"><img class="alignleft size-full wp-image-66" title="Google-Maps-Logo" src="http://alex.livedesign.ro/wp-content/uploads/2011/09/Google-Maps-Logo.jpg" alt="" width="200" height="200" /></a>Sau problema lipsei lui MapMoveEnd, ca să fiu mai exact. Mai întâi, însă, permiteți-mi să trasez, în linii mari, contextul acestei probleme. În versiunea 2 a API-ului Google Maps exista un eveniment declanșat de hartă, denumit sugestiv <code>moveend</code>. Acest eveniment semnifica finalizarea modificărilor suferite de suprafața afișată de hartă, modificări produse de oricare din următoarele operații:</p>
<p>- pan de hartă (drag &amp; drop, prin intermediul controlului de pan, sau programatic cu ajutorul metodei <code>GMap2.setCenter</code>);</p>
<p>- zoom de hartă (folosind rotița de la mouse, prin dublu clic, folosind controlul de zoom, sau programatic cu ajutorul metodei <code>GMap2.setZoom</code>).</p>
<p>Nu are rost să menționez cât de util poate fi , și chiar este, un astfel de eveniment. Problema este că acesta a dispărut în versiunea 3, apărând în schimb o serie de alte evenimente:</p>
<p>- <code>center_changed</code> &#8211; declanșat de fiecare dată când centrul hărții se schimbă;</p>
<p>- <code>bounds_changed</code> &#8211; declanșat atunci când box-ul hărții se schimbă;</p>
<p>- <code>idle</code> &#8211; atunci când harta nu mai primește evenimente, după o secvență de pan sau zoom.</p>
<p><span id="more-65"></span>Evenimentele <code>bounds_changed</code> și <code>center_changed</code> nu pot fi folosite ca surogat din cauză că în cazul unui pan prin drag &amp; drop sunt declanșate pentru fiecare poziție intermediară între punctul final și inițial.</p>
<p>Evenimentul <code>idle</code>, totuși, seamănă cel mai mult în funcționare cu ceea ce vrem să obținem. Diferența este că atunci când utilizatorul începe să facă drag și se oprește temporar, fără a ridica însă degetul de pe mouse, harta declanșează evenimentul <code>idle</code>, iar după ce utilizatorul ridică degetul de pe mouse fără a mai mișca harta, evenimentul <code>idle</code> nu mai este semnalat. Ori eu îmi doream ca acest eveniment să fie declanșat doar după ce utilizatorul a ridicat degetul de pe mouse. Pentru a simula acest comportament a trebuit să folosesc două evenimente suplimentare: <code>dragstart </code>și <code>dragend</code>, astfel:</p>
<p>1. Atunci când este declanșat evenimentul <code>dragstart</code>, setează indicatorul <code>bIsDragging = true;</code></p>
<p>2. Atunci când este declanșat evenimentul <code>dragend</code>, setează indicatorul <code>bIsDragging = false.</code> Verifica daca indicatorul <code>bTriggeredIdle</code> este false. Dacă da, atunci declanșează manual evenimentul <code>idle</code>.</p>
<p>3. Atunci când este declanșat evenimentul <code>idle</code>, verifică dacă indicatorul <code>bIsDragging</code> are valoarea <code>true</code>. Dacă da, atunci oprește execuția, setand indicatorul <code>bTriggeredIdle = false</code>. Altfel, continuă procesarea evenimentului si seteaza indicatorul<code> bTriggeredIdle = true</code>.</p>
<p>Iată și o secvență de cod care exemplifică cele prezentate mai sus:</p>
<pre class="brush: javascript; gutter: true; first-line: 1">var bDragging = false;
var bTriggeredIdle = false;
var opts = { /*...map options...*/ }
var canvas = document.getElementById('map_canvas');
var map = new google.maps.Map(canvas, opts);
google.maps.event.addListener(map, 'idle', function() {
    if (bDragging) {
        bTriggeredIdle = false;
        return;
    }
    bTriggeredIdle = true;
    /*...do processing here...*/
 });
google.maps.event.addListener(map, 'dragstart', function() {
    bDragging = true;
});
google.maps.event.addListener(map, 'dragend', function() {
    bDragging = false;  
    if (bTriggeredIdle == false) {
        google.maps.event.trigger(map, 'idle');
    }
});</pre>
]]></content:encoded>
			<wfw:commentRss>http://alex.livedesign.ro/google-maps-api-v3-si-problema-lui-mapmoveend/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Cum afișăm reclame AdMob în aplicațiile iOS scrise cu MonoTouch</title>
		<link>http://alex.livedesign.ro/cum-afisam-reclame-admob-in-aplicatiile-ios-scrise-cu-monotouch</link>
		<comments>http://alex.livedesign.ro/cum-afisam-reclame-admob-in-aplicatiile-ios-scrise-cu-monotouch#comments</comments>
		<pubDate>Sat, 03 Sep 2011 14:11:13 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[admob]]></category>
		<category><![CDATA[bindings]]></category>
		<category><![CDATA[ios]]></category>
		<category><![CDATA[ipad]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[monotouch]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://alex.livedesign.ro/?p=59</guid>
		<description><![CDATA[Înainte să abordez subiectul central al acestui articol, aș dori să vorbesc un pic despre ceea ce stă la baza funcționării tehnologiei MonoTouch. Ei bine, MonoTouch expune o interfață C# prin intermediul căreia putem folosi librăriile din CocoaTouch, oferind în același timp acces la un subset al API-urilor din .NET. Cu alte cuvinte, la baza [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://alex.livedesign.ro/wp-content/uploads/2011/09/logo-articol.png"><img class="alignleft size-full wp-image-63" title="logo-articol" src="http://alex.livedesign.ro/wp-content/uploads/2011/09/logo-articol.png" alt="" width="280" height="121" /></a>Înainte să abordez subiectul central al acestui articol, aș dori să vorbesc un pic despre ceea ce stă la baza funcționării tehnologiei <a href="http://ios.xamarin.com/" target="_blank">MonoTouch</a>. Ei bine, MonoTouch expune o interfață C# prin intermediul căreia putem folosi librăriile din CocoaTouch, oferind în același timp acces la un subset al API-urilor din .NET. Cu alte cuvinte, la baza funcționării acestui framework stă un engine de interoperabilitate (asemănător mecanismului <a href="http://en.wikipedia.org/wiki/Platform_Invocation_Services" target="_blank">P/Invoke</a>) care asigură comunicarea între codul C# scris de dezvoltator si API-urile scrise în C și Objective-C, API-uri ce compun SDK-ul oferit de Apple pentru platformele iOS. Întregul sistem este, evident, ceva mai complex decât atât, mai multe informații putând fi găsite <a href="http://ios.xamarin.com/Documentation/API_Design" target="_blank">aici</a>.</p>
<p>Foarte important de reținut este faptul că putem folosi aceste mecanisme pentru a consuma orice librărie scrisă în Objective-C, care nu este încă expusă prin intermediul framework-ului MonoTouch. Acesta este și cazul librăriei <a href="http://www.admob.com/" target="_blank">AdMob</a>, pe care Google o livrează compilată doar ca binar pentru Objective-C, impunându-se deci nevoia creării unui binding.</p>
<p>Nu voi prezenta toți pașii necesari scrierii unui astfel de binding, aceștia fiind descriși <a href="http://ios.xamarin.com/Documentation/Binding_New_Objective-C_Types" target="_blank">aici</a> cu lux de amănunte. Voi atașa în schimb sursele necesare, prezentând, evident, instrucțiunile și exemplele de rigoare.</p>
<p><strong><span id="more-59"></span>Pasul 1.</strong> În primul rând, având sursele binding-urilor, veți folosi executabilul <code>btouch</code> pentru a general DLL-ul ce realizează intermedierea cu librăria AdMob:</p>
<pre class="brush: bash; gutter: true; first-line: 1">/Developer/MonoTouch/usr/bin/btouch AdMob.cs -s:Enums.cs </pre>
<p>Trebuie notat că, deși pachetul oferit spre descărcare conține deja acest DLL, el nu este compatibil cu toate versiunile de MonoTouch datorită faptului ca de la o versiune la alta mecanismele interne necesare realizării interoperabilității pot diferi. Este, deci, recomandat să generați DLL-ul folosind utilitarul corespunzând versiunii pe care o aveți instalată.</p>
<p><strong>Pasul 2.</strong> Adăugați librăria AdMob în proiect, referențiați DLL-ul proaspăt generat și configurați compilatorul pentru folosi această librărie. Pentru a configura compilatorul va trebui să accesați <code>Project -&gt; [NumeProiect] Options -&gt; iPhone Build</code> în MonoDevelop. În fereastra deschisă, completați câmpul <code>"Additional mtouch arguments"</code> astfel:</p>
<pre class="brush: bash; gutter: true; first-line: 1">-gcc_flags "-L${ProjectDir} -lGoogleAdMobAds -force_load ${ProjectDir}/libGoogleAdMobAds.a"</pre>
<p>Acest câmp trebuie completat pentru fiecare configurație definită în proiectul vostru.</p>
<p><strong>Pasul 3</strong>. Folosirea efectivă a librăriei. Cel mai frecvent model de utilizare este reprezentat de servirea unei reclame standard, folosind una din dimensiunile suportate de AdMob:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">mBanner = new GADBannerView();
mBanner.AdUnitId = "[your-ad-unit-id]";
mBanner.Delegate = new TestBannerDelegate();
mBanner.RootViewController = this;
mBanner.Frame = new RectangleF(0, 0, 320, 50);
mBanner.LoadRequest(GADRequest.Request);</pre>
<p>Un alt exemplu este reprezentat de servirea reclamelor interstițiale, afișate pe tot ecranul. Servirea acestor reclame se face, de obicei în două etape: lansarea cererii pentru reclama și, odată cu finalizarea acesteia cu succes, afișarea reclamei. Vom avea, deci, nevoie de un delegate:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">public class TestInterstitialDelegate : GADInterstitialDelegate
{
    private SomeViewController mCtrl = null;

    public TestInterstitialDelegate (SomeViewController ctrl)
    {
         mCtrl = ctrl;
    }

    public override void DidReceiveAd (GADInterstitial ad)
    {
         ad.PresentFromViewController(mCtrl);
    }
}</pre>
<p>Lansarea cererii se face asemănător:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">mInterstitial = new GADInterstitial();
mInterstitial.AdUnitId = "[your-ad-unit-id]";
mInterstitial.Delegate = new TestInterstitialDelegate(this);
mInterstitial.LoadRequest(GADRequest.Request);</pre>
<p>Când rulați aplicația de pe simulator este bine să efectuați cererile de reclamă în modul test, astfel:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">request = GADRequest();
if (Runtime.Arch == Arch.Simulator) {
    request.TestDevices = new string[] {
        "Simulator"
    };
}
mBanner.LoadRequest(request);</pre>
<p>Iata, în sfârșit, și pachetul promis la începutul articolului. Acesta conține atât sursele pentru binding-uri, cât și un proiect demonstrativ.</p>
<p><a href="http://alex.livedesign.ro/wp-content/uploads/2011/09/AdMobBindings.zip">Click aici pentru a decărca pachetul.</a></p>
]]></content:encoded>
			<wfw:commentRss>http://alex.livedesign.ro/cum-afisam-reclame-admob-in-aplicatiile-ios-scrise-cu-monotouch/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Cum se creează target-uri pentru NLog</title>
		<link>http://alex.livedesign.ro/cum-se-creeaza-target-uri-pentru-nlog</link>
		<comments>http://alex.livedesign.ro/cum-se-creeaza-target-uri-pentru-nlog#comments</comments>
		<pubDate>Sun, 28 Aug 2011 22:22:29 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[custom]]></category>
		<category><![CDATA[nlog]]></category>
		<category><![CDATA[target]]></category>

		<guid isPermaLink="false">http://alex.livedesign.ro/?p=48</guid>
		<description><![CDATA[Ce este NLog
NLog este o platformă gratuită de jurnalizare pentru .NET și Mono care oferă facilităţi avansate precum:
- procesarea mesajelor primite, îmbunătățirea lor cu diverse informații contextuale (precum momentul de timp al înregistrării, informații despre firul de execuție, informații despre proces etc.) și formatarea acestora conform regulilor dorite;
- suportă diverse modalități de jurnalizare: în fișiere [...]]]></description>
			<content:encoded><![CDATA[<p><strong><a style="padding-right: 5px; padding-bottom: 5px;" href="http://alex.livedesign.ro/wp-content/uploads/2011/08/NLog-e1314570437599.png"><img class="alignleft size-full wp-image-58" title="NLog" src="http://alex.livedesign.ro/wp-content/uploads/2011/08/NLog-e1314570437599.png" alt="" width="167" height="78" /></a>Ce este NLog</strong></p>
<p><a href="http://nlog-project.org/" target="_blank">NLog</a> este o platformă gratuită de jurnalizare pentru .NET și Mono care oferă facilităţi avansate precum:</p>
<p>- procesarea mesajelor primite, îmbunătățirea lor cu diverse informații contextuale (precum momentul de timp al înregistrării, informații despre firul de execuție, informații despre proces etc.) și formatarea acestora conform regulilor dorite;</p>
<p>- suportă diverse modalități de jurnalizare: în fișiere pe disc, într-o bază de date, în EventLog-ul sistemului de operare, pe rețea, pe e-mail etc.</p>
<p>- opțiuni de rutare a mesajelor, incluzând: buffering, jurnalizare asincronă, load balancing, failover etc.</p>
<p>- ușor de configurat, incluzând și o extensie de Visual Studio ce face mai comodă crearea fişierelor de configurare.</p>
<p>Este disponibil sub licență BSD, iar sursa poate fi descărcata de pe <a href="http://github.com/jkowalski/NLog/" target="_blank">GitHub</a>.</p>
<p><strong>Ce este un target pentru NLog și cum scriem un astfel de target<br />
 </strong></p>
<p>Pe scurt, un target pentru NLog reprezintă o destinaţie a mesajelor trimise de aplicaţie, un target putând fie să stocheze mesajele, fie să le afişeze, sau să le trimită mai departe. Puteti găsi <a href="http://nlog-project.org/wiki/Targets" target="_blank">aici</a> o listă de target-uri suportate de NLog în momentul de față.</p>
<p>Totuși, necesitățile unui proiect nu pot fi acoperite mereu de componentele standard, tocmai acesta fiind motivul pentru care NLog ne oferă posibilitatea de a ne crea propriile target-uri. În continuare voi descrie, deci, pașii necesari în crearea unui astfel de target, alegând pentru exemplificare dezvoltarea unui target care salvează în baza de date mesaje personalizate ce descriu operațiunile efectuate într-un sistem.</p>
<p><span id="more-48"></span>Întâi de toate, să creăm tabela care va ține înregistrările noastre:</p>
<pre class="brush: sql; gutter: true; first-line: 1">CREATE TABLE samplenlog_target_t
(
  log_id serial NOT NULL,
  log_time bigint,
  log_operation character varying(250),
  log_username character varying(250),
  log_accesslevel character varying(250),
  log_description character varying(250)
)</pre>
<p><strong>Pasul 1 </strong>- Extidenți clasa de baza NLog.Targets.Target si implementați metoda Write(LogEventInfo logEvent):</p>
<pre class="brush: csharp; gutter: true; first-line: 1">using System;
using NLog;
using NLog.Targets;

namespace Ro.LiveDesign.Samples.SampleNLogTargets
{
    /// &lt;summary&gt;
    /// Step 1 - Extend the base class Nlog.Targets.Target
    /// &lt;/summary&gt;
    [Target("CustomDbNLog")]
    public class CustomDbNLogTarget : Target
    {
        protected override void Write(LogEventInfo logEvent)
        {
            base.Write(logEvent);
        }
    }
}</pre>
<p><strong>Pasul 2</strong> &#8211; Definiți structura mesajului personalizat extinzând clasa NLog.LogEventInfo și definind proprietățile ce compun mesajul personalizat. În exemplul nostru avem patru astfel de proprietăți:</p>
<p>- momentul de timp al înregistrării mesajului;</p>
<p>- tipul operațiunii efectuate;</p>
<p>- numele de utilizator al celui care a efectuat operațiunea;</p>
<p>- nivelul de acces al utilizatorului;</p>
<p>- o descriere suplimentară (opțională).</p>
<ul>
</ul>
<pre class="brush: csharp; gutter: true; first-line: 1">using System;
using NLog;

namespace Ro.LiveDesign.Samples.SampleNLogTargets
{
    /// &lt;summary&gt;
    /// Step 2 - Define custom event info
    /// &lt;/summary&gt;
    public class CustomDbNLogEventInfo : LogEventInfo
    {
        public long Time { get; set;}

        public string Operation { get; set; }

        public string UserName { get; set; }

        public string AccessLevel { get; set; }

        public string Description { get; set; }
    }
}
</pre>
<p><strong>Pasul 3 </strong>- Pentru a putea folosi noul target trebuie sa configurați framework-ul NLog, dându-i informațiile necesare despre target-ul dumneavoastră. Puteți face acest lucru în două moduri: programatic sau folosind fișierul de configurare.</p>
<p>Configurarea programatică se realizează în felul următor:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">CustomDbNLogTarget target = null;
LoggingConfiguration config = null;
LoggingRule rule = null;

//create and configure target
target = new CustomDbNLogTarget();
target.ConnectionStringName = "MyConnection";
target.Name = "CustomDbNLog";

//create logging rule
rule = new LoggingRule(LOGGER_NAME, LogLevel.Info, target);

//create logging configuration
config = LogManager.Configuration;
if (config == null)
    config = new LoggingConfiguration();
config.AddTarget("CustomDbNLog", target);
config.LoggingRules.Add(rule);

//load the new logging configuration
LogManager.Configuration = config;</pre>
<p>Alternativ, puteți folosi fișierul de configurare pentru a specifica informațiile necesare:</p>
<pre class="brush: xml; gutter: true; first-line: 1">&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
    &lt;extensions&gt;
        &lt;add assembly="Ro.LiveDesign.Samples.SampleNLogTargets" /&gt;
    &lt;/extensions&gt;
    &lt;targets&gt;
        &lt;target xsi:type="CustomDbNLog" name="CustomDbNLog" connectionStringName="MyConnection" /&gt;
    &lt;/targets&gt;
    &lt;rules&gt;
        &lt;logger name="Ro.LiveDesign.Samples.CustomDbNLog" minlevel="Info" writeTo="CustomDbNLog"&gt;&lt;/logger&gt;
    &lt;/rules&gt;
&lt;/nlog&gt;</pre>
<p><strong>Pasul 4</strong> &#8211; Odată ajunși aici nu trebuie decât să trimitem mesajele noastre logger-ului corespunzător:</p>
<pre class="brush: csharp; gutter: true; first-line: 1">Logger logger = null;
CustomDbNLogEventInfo eventInfo = null;

//register the target
//RegisterTarget();

//create a sample event info
eventInfo = new CustomDbNLogEventInfo();
eventInfo.Time = DateTime.Now.Ticks;
eventInfo.Operation = "op.login";
eventInfo.AccessLevel = "administrator";
eventInfo.UserName = "admin";
eventInfo.Description = "User admin logged in";
eventInfo.Level = LogLevel.Info;

//fetch logger and log event
logger = LogManager.GetLogger(LOGGER_NAME);
logger.Log(eventInfo);</pre>
<p>Și pentru că orice explicație teoretică trebuie însoțită de exemple, la sfârșitul articolului atașez și proiectul VS 2010 care conține implementarea acestui tutorial.</p>
<p>Pentru a-l rula aveți nevoie de:</p>
<p>- Visual Studio 2010;</p>
<p>- .NET Framework 3.5 (minim);</p>
<p>- PostgreSQL.</p>
<p>Puteți, evident, adapta exemplul pentru a salva informațiile folosind alt SGBD, cum ar fi MySQL sau SQL Server.</p>
<p><a href="http://alex.livedesign.ro/wp-content/uploads/2011/08/SampleNLogTargets.zip" target="_blank">Click aici pentru a descărca proiectul demonstrativ</a>.<br class="spacer_" /></p>
]]></content:encoded>
			<wfw:commentRss>http://alex.livedesign.ro/cum-se-creeaza-target-uri-pentru-nlog/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

