<?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>Advanced Programming Stories</title>
	<atom:link href="http://developstories.gr/feed/" rel="self" type="application/rss+xml" />
	<link>http://developstories.gr</link>
	<description>advanced web development techniques &#38; reverse engineering stories</description>
	<lastBuildDate>Fri, 16 Nov 2012 07:53:56 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.7.3</generator>
	<item>
		<title>Windows task controll with php</title>
		<link>http://developstories.gr/php/windows-task-controll/</link>
		<comments>http://developstories.gr/php/windows-task-controll/#respond</comments>
		<pubDate>Fri, 16 Nov 2012 07:53:56 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://developstories.gr/?p=237</guid>
		<description><![CDATA[Πέρσι τέτοια εποχή, ένα φίλος που έχει γυμναστήριο μου ζήτησε να του κάνω ένα πρόγραμμα για την διαχείριση των συνδρομών του, και να το συνδέσω με smart card για να ενημερώνονται αυτόματα οι επισκέψεις των μελών του. Εγώ &#8220;κολλημένος&#8221; με php, δεν πέρασε καν από το μυαλό μου να γίνει κάτι σε desktop, άλλα σε &#8230; <a href="http://developstories.gr/php/windows-task-controll/" class="more-link">Continue reading<span class="screen-reader-text"> "Windows task controll with php"</span></a>]]></description>
				<content:encoded><![CDATA[<p>Πέρσι τέτοια εποχή, ένα φίλος που έχει γυμναστήριο μου ζήτησε να του κάνω ένα <a title="Διαχείριση Μελών και Συνδρομών - Δωρεάν Δοκιμή" href="http://clubmanager.gr" target="_blank">πρόγραμμα για την διαχείριση των συνδρομών</a> του, και να το συνδέσω με <a href="http://en.wikipedia.org/wiki/Smart_card" target="_blank">smart card</a> για να ενημερώνονται αυτόματα οι επισκέψεις των μελών του.</p>
<p>Εγώ &#8220;κολλημένος&#8221; με php, δεν πέρασε καν από το μυαλό μου να γίνει κάτι σε desktop, άλλα σε web που θα έπαιζε τοπικά σε browser. Έπρεπε όμως να βρω ένα τρόπο για να συνδέσω το smart card με την εφαρμογή. Η εύκολη λύση αφού θα έπαιζε σε windows ήταν να χρησιμοποιήσω <a href="http://php.net/manual/en/function.shell-exec.php" target="_blank">shell exec</a> και να τρέχω το exe αρχείο που είχε ο driver για να διαβάζει την κάρτα.</p>
<p>Έκανα λοιπόν ένα php αρχείο πού έτρεχε με while(true) και sleep 3 δευτερόλεπτα και έλεγχε συνέχεια το smart card reader αν μπήκε ή βγήκε η κάρτα. Το αποτελέσμα το έγραφε σε ένα αρχείο, όπου η web εφαρμογή με ajax έβλεπε αυτό το αρχείο και ανάλογα έδειχνε ότι η κάρτα είναι κενή ή άνοιγε απ&#8217; ευθείας την καρτέλα του μέλους και περνούσε αυτόματα την επίσκεψη.</p>
<p>Μέχρι εδώ όλα καλά, πως όμως θα ξεκινούσε αυτό το script; Θα το έβαζα στο άνοιγμα του υπολογιστή; Δεν μου άρεσε καθόλου αυτή η ιδέα. Θα προτιμούσα να ξεκινάει μόλις ο χρήστης κάνει login στην εφαρμογή και να σταματάει όταν κάνει logout. Αγούγετε καλύτερά&#8230;</p>
<p>Για να το πετύχω αυτό χρησιμοποίησα την <a href="http://www.php.net/manual/en/book.com.php" target="_blank">com php class</a> με <a href="http://msdn.microsoft.com/en-us/library/d5fk67ky%28v=vs.84%29.aspx" target="_blank">WScript.Shell και Run</a>. Για να ελέγχω όμως αν παίζει ήδη το script και να το σταματάω χρησιμοποίησα πάλι την shell_exec σε συνδυασμό με την <a href="http://technet.microsoft.com/en-us/library/bb491010.aspx" target="_blank">tasklist</a> και <a href="http://technet.microsoft.com/en-us/library/bb491009.aspx" target="_blank">taskkill</a>. Παρακάτω μπορείτε να δείτε τον κώδικα αναλυτικά:</p>
<pre class="brush: php; title: ; notranslate">
define ('IS_WIN',(PHP_OS=='WINNT' || PHP_OS=='WIN32' || PHP_OS=='Windows') ? true : false);

function smartcard_running() {
    if (IS_WIN) {
        $ar=explode(&quot;\n&quot;,shell_exec('tasklist /FI &quot;imagename eq php.exe&quot;'));
        foreach($ar as $v) {
            while (strstr($v,&quot;  &quot;)) $v=str_replace(&quot;  &quot;,&quot; &quot;,$v);
            $v=explode(&quot; &quot;,$v);
            if ($v[0]=='php.exe') {
                return true;
            }
        }
    }
    return false;
}

function smartcard_start() {
    if (IS_WIN) {
        $WshShell = new COM('WScript.Shell');
        $cmd=&quot;C:\\wamp\\bin\\php\\php5.3.5\\php.exe &quot;.dirname(__FILE__).&quot;\\smartcard_server.php&quot;;
        $WshShell-&gt;Run($cmd, 1, false);
    }
}

function smartcard_stop() {
    if (IS_WIN) {
        $ar=explode(&quot;\n&quot;,shell_exec('tasklist /FI &quot;imagename eq php.exe&quot;'));
        foreach($ar as $v) {
            while (strstr($v,&quot;  &quot;)) $v=str_replace(&quot;  &quot;,&quot; &quot;,$v);
            $v=explode(&quot; &quot;,$v);
            if ($v[0]=='php.exe') {
                shell_exec('taskkill /pid '.$v[1].' /F');
            }
        }
    }
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://developstories.gr/php/windows-task-controll/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Firefox work offline vs wireless usb</title>
		<link>http://developstories.gr/firefox/work-offline-vs-wireless-usb/</link>
		<comments>http://developstories.gr/firefox/work-offline-vs-wireless-usb/#respond</comments>
		<pubDate>Sun, 29 Apr 2012 21:40:24 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[firefox]]></category>

		<guid isPermaLink="false">http://developstories.gr/?p=212</guid>
		<description><![CDATA[Πριν λίγες μέρες κάποιος πάνω σε μια κουβέντα που είχαμε μου είπε το εξής: &#8220;Έχω βάλει στο κατάστημα μου ένα pc με windows (εδώ χαλάστηκα λίγο άλλα δεν γ&#8230;..) που συνδέεται στο Internet με wireless usb, ξεκινάει με ένα Firefox σε fullscreen mode και δείχνει κάποιες πληροφορίες από μια σελίδα. Ένα πρόβλημα που έχω είναι &#8230; <a href="http://developstories.gr/firefox/work-offline-vs-wireless-usb/" class="more-link">Continue reading<span class="screen-reader-text"> "Firefox work offline vs wireless usb"</span></a>]]></description>
				<content:encoded><![CDATA[<p>Πριν λίγες μέρες κάποιος πάνω σε μια κουβέντα που είχαμε μου είπε το εξής:</p>
<p><em>&#8220;Έχω βάλει στο κατάστημα μου ένα pc με windows (εδώ χαλάστηκα λίγο άλλα δεν γ&#8230;..) που συνδέεται στο Internet με <a href="http://en.wikipedia.org/wiki/Wireless_USB" target="_blank">wireless usb</a>, ξεκινάει με ένα Firefox σε fullscreen mode και δείχνει κάποιες πληροφορίες από μια σελίδα. Ένα πρόβλημα που έχω είναι ότι όταν ξεκινάει ο υπολογιστής μέχρι να συνδεθεί το usb έχει μια καθυστέρηση, και ο Firefox δεν βλέπει την σελίδα βγάζει error page, και μπαίνει σε κατάσταση work offline. Όταν τελικά πραγματοποιηθεί η σύνδεση, πρέπει να πατήσω refresh και στην συνεχεία να απενεργοποιήσω το work online, χακεράς έγινα για να τα βρω αυτά, για να εμφανιστεί η σελίδα. Υπάρχει ένα ακόμα πρόβλημα ότι ο χρήστης μπορεί να πάρει έλεγχο, απενεργοποιώντας το fullscreen πολύ εύκολα, και να μπει σε άλλη σελίδα. Έτσι χάνετε η υπηρεσία που προσφέρω, εκτός και αν ελέγχω όλη την ώρα τι γίνεται στον υπολογιστή.&#8221;</em></p>
<p>Σε κάτι τέτοια προβλήματα, μου αρέσει να βρίσκω λύσεις&#8230;</p>
<p>Ξεκινώντας από ένα search στο google, βρήκα το <a href="https://addons.mozilla.org/el/firefox/addon/r-kiosk/" target="_blank">r-kiosk</a> ένα plugin που βάζει τον Firefox μόνιμα σε fullscreen και δεν επιτρέπει τον χρήστη να βγει, πάρα μόνο να κλείσει τον browser. Ένας <a href="http://teamtutorials.com/windows-tutorials/configuring-your-own-kiosk-machine" target="_blank">νέος user</a> στον υπολογιστή με ελάχιστα δικαιώματα και <a href="http://www.knowledgesutra.com/discuss/ttomsl-change-windows.html" target="_blank">shell</a> το Firefox, είναι ότι πρέπει για να γίνει το pc <a href="http://en.wikipedia.org/wiki/Interactive_kiosk" target="_blank">interactive kiosk</a>.</p>
<p>Τι γίνεται όμως με το πρόβλημα του wireless usb σε σχέση με το work offline και την πρόσβαση σε άλλες σελίδες; Εκεί λύση δεν βρέθηκε, και έτσι αποφάσισα να γράψω ένα <a href="https://developer.mozilla.org/en/Building_an_Extension" target="_blank">Firefox extension</a> που θα κάνει τα παραπάνω.</p>
<p>Για τον έλεγχο αν υπάρχει σύνδεση στο Internet χρησιμοποίησα ένα request προς την σελίδα, ανά δευτερόλεπτο μέχρι να είναι επιτυχής το request.</p>
<pre class="brush: jscript; title: ; notranslate">
CheckConnection : function () {
    var d=new Date();
    var req = new XMLHttpRequest();
    req.mozBackgroundRequest = true;
    req.open(&quot;GET&quot;,lockurlApp.Url,false);
    try {
        req.send(null);
        if (req.status == 200) {
            openUILinkIn( lockurlApp.Url, 'current' );
            return;
        }
    }
    catch(err) {}
    setTimeout('lockurlApp.CheckConnection()',1000);
},
</pre>
<p>Όταν όμως γίνει request και δεν υπάρχει σύνδεση Internet, ο Firefox μπαίνει σε work offline με αποτέλεσμα να μην καταλαβαίνει πότε έχει έρθει η σύνδεση. Έψαξα λοιπόν για το πως θα <a href="http://lifehacker.com/5714560/stop-firefox-from-automatically-entering-work-offline-mode" target="_blank">απενεργοποιήσω το work offline</a>, άλλα όποια λύση και να βρήκα, δεν έπαιζε. Το μόνο που βρήκα ήταν ένα <a href="https://addons.mozilla.org/el/firefox/addon/work-offline/" target="_blank">plugin</a> που έβγαζε ένα button για να αλλάζεις την κατάσταση του work offline. Με λίγο reverse στο plugin, και η λύση, ήταν μπροστά μου.</p>
<pre class="brush: jscript; title: ; notranslate">
var ioService = Components.classes[&quot;@mozilla.org/network/io-service;1&quot;].getService(Components.interfaces.nsIIOService2);
if (ioService.offline) BrowserOffline.toggleOfflineStatus();
</pre>
<p>Τώρα έπρεπε να επιτρέψω την πρόσβαση μόνο στην συγκεκριμένη σελίδα. Βρήκα από το manual του mozilla developer network το <a href="https://developer.mozilla.org/en/Code_snippets/Progress_Listeners" target="_blank">event όταν αλλάζει το location στο address bar</a>, πολύ απλό τελικά, ένας έλεγχος στο uri και είμαστε έτοιμοι.</p>
<pre class="brush: jscript; title: ; notranslate">
onLocationChange: function(aProgress, aRequest, aURI) {

    if ( /www\.allow1\.com\/*/.test(aURI.spec) ) {
        return;
    }
    if ( /www\.allow2\.com\/*/.test(aURI.spec) ) {
        return;
    }
    //redirect if not allowed
    openUILinkIn( 'www.allow1.com', 'current' );
}
</pre>
<p>Οποίος θέλει να χρησιμοποιήσει το plugin ή μέρος του κώδικα, μπορεί να τον <a href="http://developstories.gr/wp-content/uploads/lockURL.zip">κατεβάσει από εδώ</a>. (Μια αναφορά βέβαια δεν θα με χάλαγε καθόλου)</p>
]]></content:encoded>
			<wfw:commentRss>http://developstories.gr/firefox/work-offline-vs-wireless-usb/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Asynchronous multiple file upload με το swfupload</title>
		<link>http://developstories.gr/php/asynchronous-multiple-file-upload-%ce%bc%ce%b5-%cf%84%ce%bf-swfupload/</link>
		<comments>http://developstories.gr/php/asynchronous-multiple-file-upload-%ce%bc%ce%b5-%cf%84%ce%bf-swfupload/#respond</comments>
		<pubDate>Mon, 31 Oct 2011 10:24:06 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[jQuery]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[upload]]></category>

		<guid isPermaLink="false">http://developstories.gr/?p=151</guid>
		<description><![CDATA[Μέχρι σήμερα έχω δοκιμάσει πολλούς τρόπους για ajax file upload και με κανένα δεν ήμουν ποτέ ευχαριστημένος. Ένας από αυτούς τους τρόπους που δοκίμασα, χρειάστηκε ακόμα και η εγκατάσταση του php extension uploadprogress. Σε συνδιασμό με css replace στο input file controll είχα ένα ικανοποιητικό αποτέλεσμα για asynchronous file upload. Τελικά κατέληξα να δουλεύω με &#8230; <a href="http://developstories.gr/php/asynchronous-multiple-file-upload-%ce%bc%ce%b5-%cf%84%ce%bf-swfupload/" class="more-link">Continue reading<span class="screen-reader-text"> "Asynchronous multiple file upload με το swfupload"</span></a>]]></description>
				<content:encoded><![CDATA[<p>Μέχρι σήμερα έχω δοκιμάσει πολλούς τρόπους για <a title="10+Ajax Upload File Sample With PHP" href="http://www.freshdesignweb.com/10example-ajax-upload-file-with-php.html" target="_blank">ajax file upload</a> και με κανένα δεν ήμουν ποτέ ευχαριστημένος. Ένας από αυτούς τους τρόπους που δοκίμασα, χρειάστηκε ακόμα και η εγκατάσταση του php extension <a title="Php extension uploadprogress" href="http://t.wits.sg/2008/06/25/howto-php-and-jquery-upload-progress-bar/" target="_blank">uploadprogress</a>. Σε συνδιασμό με <a title="Query Custom File Upload Input" href="http://www.filamentgroup.com/lab/jquery_custom_file_input_book_designing_with_progressive_enhancement/" target="_blank">css replace στο input file controll</a> είχα ένα ικανοποιητικό αποτέλεσμα για asynchronous file upload. Τελικά κατέληξα να δουλεύω με το <a title="Uplodify jQuery plugin" href="http://www.uploadify.com" target="_blank">uplodify</a> ένα jquery plugin χωρίς να μου αρέσει πολύ.</p>
<p>Τον τελευταίο καιρό ασχολήθηκα ξανά με το θέμα και βρήκα ένα πολύ ενδιαφέρων project το <a title="Upload multiple files" href="http://swfupload.org/" target="_blank">swfupload</a>, το οποίο το μόνο του μειονέκτημα είναι ότι δεν είναι jquery plugin. Δεν πειράζει όμως γιατί βρήκα <a title="jQuery plugin that makes working with SWFUpload even easier" href="https://github.com/ifunk/swfupload-jquery-plugin" target="_blank">plugin σε jquery</a> που το κάνει port. To swfupload είναι πολύ κάλο γιατί έχει υποστήριξη για multiple files, filter για τύπο αρχείων (πχ *.pdf), button image replace (hover,disable,&#8230;), και πολύ καλή διαχείριση των events. Η υλοποίηση του είναι σε flash, έτσι υπάρχει πληροφορία κατά το upload ποσά bytes έχουν ανεβεί. Το σημαντικότερο που με ενθουσίασε είναι ότι μπορείς να ελέγξεις πότε θα ξεκινήσει το upload, δηλαδή διαχωρίζει το select file, από το upload file event.</p>
<p>Όλα τα υπόλοιπα που έχω δοκιμάσει, μόλις επιλέξεις ένα αρχείο, ανεβαίνει κατευθείαν στον server, με αποτέλεσμα να πρέπει να αποθήκευσεις το αρχείο κάπου temporarily ώστε μετά την ολοκλήρωση της φόρμας να μπορείς να το χρησιμοποιήσεις. Αυτό χρειάζεται μέθοδο για file garbage collection σε περίπτωση που δεν ολοκληρωθεί η αποστολή της φόρμας για να σβήνει τα άχρηστα αρχεία.</p>
<p>Σε συνδυασμό με το <a title="jQuery Progress Bar plugin" href="http://t.wits.sg/jquery-progress-bar/" target="_blank">jQuery progress bar</a> το swfupload γίνεται το απόλυτο tool για upload files. Ας το δούμε λίγο στην πράξη. Θα φτιάξουμε μια φόρμα, με ένα πεδίο, και με επιλογή για 4 text αρχεία (.txt).</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
session_start();
?&gt;
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
&lt;head&gt;
&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;
&lt;title&gt;Upload files&lt;/title&gt;
&lt;link href=&quot;main.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; /&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;js/jquery.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;js/jquery.progressbar.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;js/swfupload.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;js/jquery.swfupload.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;var SID=&quot;&lt;?php echo session_id();?&gt;&quot;;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;js/main.js&quot;&gt;&lt;/script&gt;
&lt;/head&gt;

&lt;body&gt;

&lt;form action=&quot;&quot; id=&quot;form&quot; method=&quot;post&quot;&gt;
&lt;fieldset&gt;
&lt;label for=&quot;email&quot;&gt;EMAIL&lt;/label&gt;
&lt;input type=&quot;text&quot; name=&quot;email&quot; id=&quot;email&quot; value=&quot;&quot; /&gt;
&lt;div id=&quot;upload_form&quot;&gt;
Files (max 4):
&lt;div id=&quot;custom-queue&quot;&gt;&lt;/div&gt;
&lt;input id=&quot;btn_file&quot; type=&quot;button&quot; /&gt;
&lt;/div&gt;
&lt;br/&gt;
&lt;div id=&quot;bar_file&quot; style=&quot;display:none&quot;&gt;&lt;/div&gt;
&lt;div id=&quot;ajax_loader&quot; style=&quot;display:none&quot;&gt;&lt;img src=&quot;img/ajax-loader.gif&quot; alt=&quot;loader&quot; /&gt;&lt;/div&gt;
&lt;input id=&quot;submit_btn&quot; type=&quot;submit&quot; value=&quot;Submit&quot;/&gt;
&lt;/fieldset&gt;
&lt;/form&gt;

&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Στο παραπάνω παράδειγμα χρειάζεται στο php.ini η μεταβλητή session.auto_start off γιατί στην συνέχεια θα χρειαστούμε να περάσουμε στο upload script το session_id, ώστε να έχουμε μια σύνδεση του αποθηκευμένου αρχείου με το submit form script. Η σελίδα μας φορτώνει όλα τα απαραίτητα javascript libs  καθώς και το main.js, όπου εκεί βρίσκετε ο κύριος κώδικας. Η φόρμα μας έχει το &#8220;custom-queue&#8221; div, οπού εκεί θα υποδεχθεί τα επιλεγμένα αρχεία,  το &#8220;btn_file&#8221; button όπου εκεί θα φορτωθεί ο κώδικας του swfupload, και 2 hidden div όπου το 1ο έχει το progress bar για το upload των αρχείων και το 2o έχει ένα <a title="Ajax loading gif animator" href="http://www.ajaxload.info/" target="_blank">ajax loader image</a> το οποίο θα εμφανιστεί κατά την αποστολή της φόρμας.</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
session_id($_GET['SID']);
session_start();

$dest='upload_files/'.$_GET['SID'].'/';
if (!file_exists($dest)) mkdir($dest);
$dest.=$_FILES['Filedata']['name'];
move_uploaded_file($_FILES['Filedata']['tmp_name'],$dest);
die($_FILES['Filedata']['size']);
?&gt;
</pre>
<p>Αυτό είναι ένα απλό upload script που η μόνη του ιδιαιτερότητα είναι ότι χρησιμοποιεί την μεταβλητή $_GET[&#8216;SID&#8217;] για να ξεκινήσει το ίδιο session και για να αποθυκεύσει το αρχείο σε folder ώστε στην συνέχεια να είναι προσπελάσιμο από το submit form script. Στο παράδειγμά μας κάνει create ένα folder με όνομα το $_GET[&#8216;SID&#8217;], και εκεί μέσα βάζει τα αρχεία από το upload. Όσα αρχεία επιλέγουμε τότε φορές θα εκτελεστεί το upload script. Στο τέλος τυπώνουμε το filezize, ώστε αν κάτι έχει γίνει λάθος το filesize θα είναι 0 και το swfupload θα προκαλέσει error event. Περισσότερα σχετικά με το error handling μπορείτε να δείτε στο <a title="Swfupload documentation" href="http://demo.swfupload.org/Documentation/" target="_blank">manual</a> του swfupload.</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
session_start();

$ret=array('success'=&gt;true);

// TODO:

header('Cache-Control: no-cache, must-revalidate');
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Content-type: application/json');
echo json_encode($ret);
?&gt;
</pre>
<p>Το submit post form είναι ένα απλό ajax script όπου μπορεί να γυρίσει μεταβλητές σε json, όπως success κτλ. Εδώ χρειάζεται κάποιος επιπλέον κώδικας για να σβήνει τα αρχεία αν δεν ολοκληρωθεί η διαδικασία (είχα αναφέρει παραπάνω για file garbage collection), εδώ όμως όπως βλέπετε είναι ποιο απλή η διαδικασία. Τα αρχεία τώρα μπορούμε να τα βρούμε πολύ εύκολα γιατί όπως είπαμε και παραπάνω τα έχουμε αποθυκεύσει με βάση το session_id.</p>
<pre class="brush: jscript; title: ; notranslate">
$(document).ready(function(){

$(&quot;#upload_form&quot;).swfupload({

upload_url : &quot;upload?SID=&quot;+SID,
flash_url : &quot;swfupload.swf&quot;,

file_size_limit : &quot;10 MB&quot;,
file_upload_limit : &quot;0&quot;,
file_queue_limit: &quot;4&quot;,
file_types: &quot;*.txt&quot;,
file_types_description: &quot;Text files&quot;,

button_image_url : &quot;img/upload.png&quot;,
button_width : 88,
button_height : 22,
button_placeholder : $(&quot;#btn_file&quot;)[0],
button_window_mode : SWFUpload.WINDOW_MODE.TRANSPARENT

})
.bind(&quot;fileQueued&quot;, function(event, file){
$(&quot;#custom-queue&quot;).append('&lt;div id=&quot;file_'+file.id+'&quot; title=&quot;'+file.name+
'&quot;&gt;'+file.name+'&lt;span&gt; (&lt;a title=&quot;remove file&quot; onclick=&quot;return removeFile(\''+file.id+'\')&quot; href=&quot;#remove&quot;&gt;x&lt;/a&gt;)&lt;/span&gt;&lt;/div&gt;');
})
.bind('uploadComplete', function(event, file){
// upload has completed, lets try the next one in the queue
$(&quot;#file_&quot;+file.id).removeClass('file-progress').addClass('file-done');
if ($('.file-queue').length==$('.file-done').length) {
$(&quot;#bar_file&quot;).hide();
$(&quot;#ajax_loader&quot;).show();

$.ajax({
type: &quot;POST&quot;,
url: &quot;submit.php&quot;,
data: $(&quot;#form&quot;).serialize(),
dataType: &quot;json&quot;,
success: function(data) {
$(&quot;#ajax_loader&quot;).fadeOut();
$(&quot;#bar_file&quot;).html('&lt;strong&gt;Submit completed&lt;/strong&gt;').fadeIn();
}
});
}
else {
$(this).swfupload('startUpload');
}
})
.bind(&quot;uploadStart&quot;, function(event, file){
$(&quot;#file_&quot;+file.id).addClass('file-progress');
$(&quot;#bar_file&quot;).progressBar(0);
})
.bind(&quot;uploadProgress&quot;, function(event, file, bytesLoaded, bytesTotal){
var percentage = Math.floor(100 * parseInt(bytesLoaded) / parseInt(bytesTotal));
$(&quot;#bar_file&quot;).progressBar(percentage);
})
.bind(&quot;uploadError&quot;, function(event, file, errorCode, message){
if (errorCode === SWFUpload.UPLOAD_ERROR.FILE_CANCELLED) {
$(&quot;#file_&quot;+file.id).fadeOut('normal',function() { $(&quot;#file_&quot;+file.id).remove(); });
}
});

$(&quot;#bar_file&quot;).progressBar({
boxImage : 'img/progressbar.gif',
barImage : 'img/progressbg_orange.gif'
});

$(&quot;#form&quot;).submit(function() {
if ($('.file-queue').length==0) {
alert('You must select a file!');
}
else {
$(&quot;#bar_file&quot;).show();
$(&quot;#submit_btn&quot;).hide();
$(&quot;#SWFUpload_0&quot;).width(0);
$(&quot;#upload_form&quot;).swfupload('startUpload');
}
return false;
});
});

function removeFile(id) {
$(&quot;#upload_form&quot;).swfupload('cancelUpload',id,true);
return false;
}
</pre>
<p>Και πάμε στο σημαντικότερο μέρος του project που είναι javascript. Εδώ έχουμε όλα τα bind events για το swfupload, από το load, το select files μέχρι και το submit της φόρμας. Ας τα πάρουμε από την αρχή για να τα δούμε αναλυτικά.</p>
<pre class="brush: jscript; title: ; notranslate">
$(&quot;#upload_form&quot;).swfupload({

upload_url : &quot;upload?SID=&quot;+SID,
flash_url : &quot;swfupload.swf&quot;,

file_size_limit : &quot;10 MB&quot;,
file_upload_limit : &quot;0&quot;,
file_queue_limit: &quot;4&quot;,
file_types: &quot;*.txt&quot;,
file_types_description: &quot;Text files&quot;,

button_image_url : &quot;img/upload.png&quot;,
button_width : 88,
button_height : 22,
button_placeholder : $(&quot;#btn_file&quot;)[0],
button_window_mode : SWFUpload.WINDOW_MODE.TRANSPARENT

})
</pre>
<p>Εδώ φορτώνουμε το swfupload βάζοντας το url από το upload.php (περνάμε και το SID που το έχουμε βάλει σε μια javascript μεταβλητή και περιέχει το session_id, το url που βρίσκεται το swfupload.swf, το μέχρι πόσα αρχεία θέλουμε, τι μέγεθος να έχει το καθένα, και τι τύπο (πχ μπορούμε να βάλουμε &#8220;*.pdf,*.odt&#8221;).  Τέλος το image αρχείο, με το μέγεθος του και σε ποιο element να γίνει bind.</p>
<pre class="brush: jscript; title: ; notranslate">
.bind(&quot;fileQueued&quot;, function(event, file){
$(&quot;#custom-queue&quot;).append('&lt;div id=&quot;file_'+file.id+'&quot; title=&quot;'+file.name+
'&quot;&gt;'+file.name+'&lt;span&gt; (&lt;a title=&quot;remove file&quot; onclick=&quot;return removeFile(\''+file.id+'\')&quot; href=&quot;#remove&quot;&gt;x&lt;/a&gt;)&lt;/span&gt;&lt;/div&gt;');
})
</pre>
<p>Και κάπου εδώ αρχίζουν τα events, 1ο event το &#8220;fileQueued&#8221;, το οποίο εκτελείτε κατά την επιλογή αρχείων. Όσα αρχεία επιλεγούν τόσες φορές θα εκτελεστεί. Εδώ το μόνο που θέλουμε να κάνουμε είναι να εμφανίσουμε τα επιλεγμένα αρχεία, στο div &#8220;custom-queue&#8221;.  Τέλος βάζουμε και ένα (x) για αφαίρεσή αρχείου από την λίστα.  Για την αφαίρεσή χρειαζόμαστε το file.id.  Στο file.name έχουμε το όνομα του αρχείου.</p>
<pre class="brush: jscript; title: ; notranslate">
.bind(&quot;uploadStart&quot;, function(event, file){
$(&quot;#file_&quot;+file.id).addClass('file-progress');
$(&quot;#bar_file&quot;).progressBar(0);
})
.bind(&quot;uploadProgress&quot;, function(event, file, bytesLoaded, bytesTotal){
var percentage = Math.floor(100 * parseInt(bytesLoaded) / parseInt(bytesTotal));
$(&quot;#bar_file&quot;).progressBar(percentage);
})
.bind(&quot;uploadError&quot;, function(event, file, errorCode, message){
if (errorCode === SWFUpload.UPLOAD_ERROR.FILE_CANCELLED) {
$(&quot;#file_&quot;+file.id).fadeOut('normal',function() { $(&quot;#file_&quot;+file.id).remove(); });
}
});
</pre>
<p>Τα επόμενα 3 event έχουν να κάνουν με το upload. Στο &#8220;uploadStart&#8221; αλάζουμε το class στο αρχείο που είναι να ανέβει ώστε να φαίνεται ποιο είναι, και μηδενίζουμε το progress bar. Και αυτό το event εκτελείτε τόσες φορές όσα τα αρχεία που έχουμε επιλέξει. To &#8220;uploadProgress&#8221; μας δείχνει το progress του upload. Τέλος το &#8220;uploadError&#8221; εκτελείτε σε περίπτωση σφάλματος. Εδώ το χρησιμοποιούμε και για την αφαίρεση ενός αρχείου από την λίστα.</p>
<pre class="brush: jscript; title: ; notranslate">
.bind('uploadComplete', function(event, file){
// upload has completed, lets try the next one in the queue
$(&quot;#file_&quot;+file.id).removeClass('file-progress').addClass('file-done');
if ($('.file-queue').length==$('.file-done').length) {
$(&quot;#bar_file&quot;).hide();
$(&quot;#ajax_loader&quot;).show();

$.ajax({
type: &quot;POST&quot;,
url: &quot;submit.php&quot;,
data: $(&quot;#form&quot;).serialize(),
dataType: &quot;json&quot;,
success: function(data) {
$(&quot;#ajax_loader&quot;).fadeOut();
$(&quot;#bar_file&quot;).html('&lt;strong&gt;Submit completed&lt;/strong&gt;').fadeIn();
}
});
}
else {
$(this).swfupload('startUpload');
}
})
</pre>
<p>Το &#8220;uploadComplete&#8221; είναι το event που εκτελείτε μετά την ολοκλήρωση του upload κάθε αρχείου.  Εδώ ελέγχουμε και αν έχει άλλο δίνουμε εντολή για το επόμενο, κ.ο.κ. Αν δεν έχει άλλο κάνουμε submit την φόρμα μας. Για τον έλεγχο αν υπάρχει άλλο αρχείο στην λίστα, χρησιμοποιούμε την class  &#8220;file-done&#8221; την οποία την προσθέτουμε στο div &#8220;file-queue&#8221; μετά το τέλος του upload. Οπότε με ένα απλό έλεγχο στο length του &#8220;file-queue&#8221; και του &#8220;file-done&#8221;, αν είναι ίσα δηλαδή, δεν έχουμε άλλα αρχεία.</p>
<pre class="brush: jscript; title: ; notranslate">
$(&quot;#form&quot;).submit(function() {
if ($('.file-queue').length==0) {
alert('You must select a file!');
}
else {
$(&quot;#bar_file&quot;).show();
$(&quot;#submit_btn&quot;).hide();
$(&quot;#SWFUpload_0&quot;).width(0);
$(&quot;#upload_form&quot;).swfupload('startUpload');
}
return false;
});
</pre>
<p>Τέλος αλλάζουμε το submit event της φόρμας ώστε να γίνεται πρώτα το upload. Δίνουμε στο swfupload object width 0, για να εξαφανιστεί το κουμπί upload, κάνουμε hide το submit button, εμφανίζουμε το progress bar και ξεκινάμε το 1o upload, δίνοντάς εντολή στο flash.</p>
<p>Αυτό ήταν ένα απλό παράδειγμα σε σχέση με τις δυνατότητες που παρέχει το swfupload, το οποίο σε συνδυασμό και με άλλα jquery plugins, το αποτέλεσμα μπορεί να είναι καλύτερο.</p>
<p>Όλος ο παραπάνω κώδικας βρίσκεται <a title="Download swfupload.zip" href="http://developstories.gr/wp-content/uploads/swfupload.zip">εδώ και μπορείτε να το κατεβάσατε ελεύθερα</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://developstories.gr/php/asynchronous-multiple-file-upload-%ce%bc%ce%b5-%cf%84%ce%bf-swfupload/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Reverse WordPress admin module</title>
		<link>http://developstories.gr/php/reverse-wordpress-admin-module/</link>
		<comments>http://developstories.gr/php/reverse-wordpress-admin-module/#respond</comments>
		<pubDate>Mon, 14 Feb 2011 00:59:51 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[php]]></category>
		<category><![CDATA[Reverse Engineering]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://developstories.gr/?p=147</guid>
		<description><![CDATA[Αυτό που με εξιτάρει περισσότερο στο τομέα του development είναι όταν έχω ένα πρόβλημα και η λύση είναι στο reverse 😉 Ας υποθέσουμε ότι χρειαζόμαστε να συνδέσουμε μια υπάρχουσα εφαρμογή με το wordpress χωρίς όμως να ξαναφτιάξουμε την εφαρμογή χρησιμοποιώντας τις βιβλιοθήκες του. Χρειαζόμαστε ένα plugin που να εμφανίζει ένα iframe με την εφαρμογή μας. &#8230; <a href="http://developstories.gr/php/reverse-wordpress-admin-module/" class="more-link">Continue reading<span class="screen-reader-text"> "Reverse WordPress admin module"</span></a>]]></description>
				<content:encoded><![CDATA[<p>Αυτό που με εξιτάρει περισσότερο στο τομέα του development είναι όταν έχω ένα πρόβλημα και η λύση είναι στο reverse <img src="https://s.w.org/images/core/emoji/2.2.1/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>Ας υποθέσουμε ότι χρειαζόμαστε να συνδέσουμε μια υπάρχουσα εφαρμογή με το <a href="http://wordpress.org/">wordpress</a> χωρίς όμως να ξαναφτιάξουμε την εφαρμογή χρησιμοποιώντας τις βιβλιοθήκες του. Χρειαζόμαστε ένα <a href="http://codex.wordpress.org/Plugin_Resources">plugin</a> που να εμφανίζει ένα iframe με την εφαρμογή μας. Πολύ εύκολο για κάποιον που έχει ασχοληθεί με plugins στο wordpress. Το δύσκολο είναι πως θα καταλάβει η εφαρμογή, ότι ο χρήστης είναι συνδεμένος, ώστε να μην μπορεί κάποιος να την χρησιμοποιήσει χωρίς login. </p>
<p>Και κάπου εδώ ξεκινάει το reverse. Στα php application (open source), σε αντίθεση με τα executable application το reverse είναι πολύ απλή υπόθεση, γιατί το μόνο που χρειάζεται είναι υπομονή και πολλά echo ή file_put_contents.</p>
<p>Ξεκινώντας από την index.php στο wp-admin έχουμε ένα include στην admin.php όπου στην συνέχεια έχει μια κλήση στην function auth_redirect που βρίσκεται στην  wp-includes/ pluggable.php. Στην function auth_redirect έχει μια κλήση στην wp_validate_auth_cookie όπου εκεί βρίσκεται ο έλεγχος για την εγκυρότητα του logged in. Ο έλεγχος βασίζεται σε ένα cookie όπου περιέχει 3 στοιχεία, το username το expires, και τέλος ένα hash value. Με κάποιο αλγόριθμο χρησιμοποιώντας το username κάποια salt και keys, και το encrypted password που έχει o user αποθηκευμένο στην db (mysql), παράγεται ένα hash value το οποίο στην συνέχεια συγκρίνεται με το hast value από το cookie.</p>
<p>Άρα χρειαζόμαστε 2 τιμές για να μπορέσουμε να κάνουμε την σύγκριση και στον δικό μας κώδικα. Η μία είναι το cookie και η δεύτερη η πρόσβαση στην db για να διαβάσουμε το encrypted password. Το cookie μπορούμε να το περάσουμε σαν argument στο iframe και το password μπορούμε να το διαβάσουμε αν γνωρίζουμε τα login/pass της mysql (connection string) και το username του χρήστη. </p>
<p>Φτιάχνουμε λοιπόν ένα plugin που το μόνο που έχει, είναι την εμφάνιση του iframe στην επιλογή ενός item menu. </p>
<pre class="brush: plain; title: ; notranslate">&lt;?php
/**
 * @package MyAdmin
 */
/*
Plugin Name: MyAdmin
Plugin URI: http://developstories.gr
Description:
Version: 1.0.0
Author: Thek
Author URI: http://socialwhale.com/thek27
License: GPLv2
*/
add_action('admin_menu', 'my_plugin_menu');

function my_plugin_menu() {
	add_menu_page('MyAdmin', MyAdmin', 'read', 'my_menu','my_view');
}

function my_view() {
	echo '&lt;iframe src=&quot;/my-admin?auth_cookie='.$_COOKIE[AUTH_COOKIE].'&amp;auth_key='.urlencode(base64_encode(AUTH_KEY.AUTH_SALT)).'&quot; width=&quot;100%&quot; height=&quot;500&quot;&gt;&lt;/iframe&gt;';
}
?&gt;
</pre>
<p>Αν δούμε καλύτερα στο src του iframe περνάμε 2 μεταβλητές και όχι μόνο το cookie άλλα το AUTH_KEY και το AUTH_SALT όπου τα χρησιμοποιεί ο αλγόριθμος για την παραγωγή του hash value. Αυτά βρίσκονται στο wp-config.php. Για μεγαλύτερη ασφάλεια μπορούμε να τα κάνουμε copy/paste στον δικό μας αλγόριθμο χωρίς να τα περνάμε στο url. </p>
<p>Τώρα δεν μένει παρά να αντιγράψουμε τον κώδικα που παράγει το hast value, και στην συνεχεία να κάνουμε την σύγκριση.<br />
Ο αλγόριθμος είναι βασισμένος σε 2 συνεχόμενα md5 hash values. </p>
<pre class="brush: plain; title: ; notranslate">&lt;?
list($username, $expiration, $hmac) = explode('|', $_GET['auth_cookie']);

$db-&gt;query('SELECT `user_pass` FROM `wp_users` WHERE `user_login`=&quot;'.addslashes($username).'&quot; LIMIT 1');
if ($db-&gt;next()) {
	$pass_frag = substr($db-&gt;Record['user_pass'], 8, 4);
	$key = hash_hmac('md5', $username . $pass_frag . '|' . $expiration, base64_decode($_GET['auth_key']));
	$hash = hash_hmac('md5', $username . '|' . $expiration, $key);

	//$hash==$hmac
}
?&gt;</pre>
]]></content:encoded>
			<wfw:commentRss>http://developstories.gr/php/reverse-wordpress-admin-module/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Συνδέοντας το git με τον apache σε ubuntu server</title>
		<link>http://developstories.gr/server/git-apache-ubuntu/</link>
		<comments>http://developstories.gr/server/git-apache-ubuntu/#comments</comments>
		<pubDate>Tue, 11 May 2010 10:43:20 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[server]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[repository]]></category>
		<category><![CDATA[version control]]></category>

		<guid isPermaLink="false">http://developstories.gr/?p=110</guid>
		<description><![CDATA[Καθώς μεγαλώνει η ομάδα του social whale, και ταυτόχρονα οι απαιτήσεις (βλ. διαγωνισμός vodafone), αρχίζει η ανάγκη για version control system. Έτσι αποφασίσαμε να πειραματιστούμε με το git σε έναν Ubuntu server. Έτσι, ξεκινώντας από το 0 και με σύμμαχο το google, αρχίσαμε τα tests. Όποιο tutorial και να βρίσκαμε δεν είχε ακριβώς αυτό που &#8230; <a href="http://developstories.gr/server/git-apache-ubuntu/" class="more-link">Continue reading<span class="screen-reader-text"> "Συνδέοντας το git με τον apache σε ubuntu server"</span></a>]]></description>
				<content:encoded><![CDATA[<p>Καθώς μεγαλώνει η ομάδα του <a href="http://socialwhale.com">social whale</a>, και ταυτόχρονα οι απαιτήσεις (βλ. <a href="http://pea.to/SocialWhale">διαγωνισμός vodafone</a>), αρχίζει η ανάγκη για version control system. Έτσι αποφασίσαμε να πειραματιστούμε με το <a title="Version Control System" href="http://git-scm.com/">git</a> σε έναν Ubuntu server.</p>
<p>Έτσι, ξεκινώντας από το 0 και με σύμμαχο το google, αρχίσαμε τα tests. Όποιο tutorial και να βρίσκαμε δεν είχε ακριβώς αυτό που θέλαμε να πετύχουμε. Πιο συγκεκριμένα θέλαμε, όταν κάνει μια αλλαγή κάποιος developer/designer και την ανεβάζει στο repository, αυτόματα να μπορεί να βλέπει την αλλαγή σε ένα dev url. Η λύση που σκεφτήκαμε ήταν να φτιάξουμε ένα client που έκανε αυτόματα sync από το repository και ταυτόχρονα να είναι το virtual host που βλέπει ο apache. Αυτό το πετύχαμε με ένα hook στο repository, ώστε σε κάθε update που δέχεται να κάνει sync τον client του apache. Ας το δούμε στην πράξη:</p>
<p><strong>1. Εγκατάσταση του git στον server (ip: 192.168.0.2)</strong><br />
<code>user@ubuntu_server: sudo apt-get install git-core</code></p>
<p><strong>2. Δημιουργία ενός git share repository στον server</strong><br />
<code>user@ubuntu_server: sudo mkdir /myapp.git<br />
user@ubuntu_server: sudo chown user:group /myapp.git<br />
user@ubuntu_server: cd /myapp.git<br />
user@ubuntu_server: git --bare init</code></p>
<p><strong>3. Δημιουργία ενός κλώνου του repository για την σύνδεση με τον apache</strong><br />
<code>user@ubuntu_server: cd /var/www<br />
user@ubuntu_server: git clone /myapp.git</code></p>
<p>Σε μερικές εκδόσεις του git (πχ. 1.4, 1.5) δεν επιτρέπει το clone σε empty repository οπότε πρέπει να δημιουργήσουμε ένα empty και απλά να το συνδέσουμε με το master repository.<br />
<code>user@ubuntu_server: cd /var/www<br />
user@ubuntu_server: mkdir myapp<br />
user@ubuntu_server: cd myapp<br />
user@ubuntu_server: git init<br />
user@ubuntu_server: git remote add origin /myapp.git<br />
user@ubuntu_server: git config branch.master.remote origin<br />
user@ubuntu_server: git config branch.master.merge refs/heads/master<br />
</code></p>
<p><strong>4. Σύνδεση του <a title="Apache Virtual Host documentation" href="http://httpd.apache.org/docs/1.3/vhosts/">VirtualHost</a> με το clone folder στο DocumentRoot</strong><br />
<code>user@ubuntu_server: sudo vi /etc/apache2/site-enabled/myapp<br />
&lt;VirtualHost *:80&gt;<br />
  ServerName myapp.com<br />
  DocumentRoot /var/www/myapp<br />
&lt;/VirtualHost&gt;</p>
<p>user@ubuntu_server: cd /var/www/myapp<br />
user@ubuntu_server: echo 'MyApp (ver 1.00)' &gt; index.html<br />
user@ubuntu_server: git add index.html<br />
user@ubuntu_server: git commit -a -m 'Add index.html'<br />
user@ubuntu_server: git push origin master</code></p>
<p>Με την <a href="http://www.kernel.org/pub/software/scm/git/docs/git-clone.html">git clone</a> φτιάξαμε ένα κλώνο από το myapp git repository, με την <a href="http://www.kernel.org/pub/software/scm/git/docs/git-add.html">git add</a> προσθέσαμε ένα αρχείο, με την <a href="http://www.kernel.org/pub/software/scm/git/docs/git-commit.html">git commit</a> στείλαμε την αλλαγή στο τοπικό μας repository (κάθε clone έχει και δικό του repository), και τέλος με την <a href="http://www.kernel.org/pub/software/scm/git/docs/git-push.html">git push</a> στείλαμε την αλλαγή και στο κεντρικό μας repository. Τώρα αν ανοίξουμε ένα browser και δώσουμε το url του VirtualHost θα πρέπει να εμφανιστεί το &#8220;MyApp (ver 1.00)&#8221;.</p>
<p><strong>5. Στείνουμε σε ένα ubuntu desktop client το git και κατεβάζουμε (clone) το repository από τον server (desktop ip:192.168.0.3)</strong><br />
<code>user@ubuntu_desktop: sudo apt-get install git-core<br />
user@ubuntu_desktop: git clone ssh://user@192.168.0.2/myapp.git</code></p>
<p>Ετσι δημιουργήθηκε ένα folder myapp που μέσα έχει την index.html</p>
<p><strong>6. Αλλάζουμε την index.html και την ανεβάζουμε στο repository στο server</strong><br />
<code>user@ubuntu_desktop: echo 'MyApp (ver 2.00)' &gt; index.html<br />
user@ubuntu_desktop: git commit -a -m 'Modify index.html'<br />
user@ubuntu_desktop: git push</code></p>
<p>Τώρα αν κάνουμε refresh τον browser, θα διαπιστώσουμε ότι δεν άλλαξε τίποτα. Καλά δεν θα ήταν να μας έδειχνε αυτόματα την διορθωμένη index.html; Για να γίνει αυτό θα πρέπει να πάμε πάλι πίσω στον server και συγκεκριμένα στο clone repository που βλέπει ο apache και να δώσουμε:<br />
user@ubuntu_server: <a href="http://www.kernel.org/pub/software/scm/git/docs/git-pull.html">git pull</a></p>
<p>Τώρα στο refresh θα δούμε το αλλαγμένο ver την σελίδα μας.</p>
<p><strong>7. Δημιουργία script για αυτόματο sync του apache από το κεντρικό repository</strong><br />
To git έχει <a href="http://www.kernel.org/pub/software/scm/git/docs/githooks.html">hook scripts</a> που εκτελούνται μετά η πριν από συγκεκριμένες διαδικασίες. Αυτό που θέλουμε εδώ είναι κάνουμε hook το after post.</p>
<p><code>user@ubuntu_server: cd /myapp.git/hooks<br />
user@ubuntu_server: vi post-update<br />
#!/bin/sh<br />
cd /var/www/myapp<br />
env -i git pull</p>
<p>user@ubuntu_server: chmod +x post-update</code></p>
<p>Τώρα πάμε πίσω στον client να το δοκιμάσουμε, επαναλαμβάνουμε το βήμα 6 απλά αντί για ver 2.0 βάζουμε 3.0. Στο refresh, η σελίδα μας έχει αλλάξει, χωρίς να δώσουμε git pull στον server, αυτό γίνεται αυτόματα με το hook.</p>
<p><strong>8. Δημιουργία script για αυτόματο push με από κάθε commit</strong><br />
Δεν μας έφτασε αυτό, θέλαμε να αποφύγουμε και το git push στους clients γιατί επειδή είναι με ssh και μας ζητάει password συνέχεια, και όταν υπάρχουν συνεχόμενες αλλαγές (πχ σε ένα css element) είναι πολύ χρονοβόρα διαδικασία. Σκεφτήκαμε ότι θα μπορούσαμε να βάλουμε ένα hook όπου μετά από ένα commit να γίνεται αυτόματα το push. Όλα ωραία στην θεωρία άλλα όταν πας στην πράξη αρχίζουν τα προβλήματα. Το πρόβλημα είναι κάπως πρέπει να δωσεις το password. Η λύση εδώ είναι το <a href="http://en.wikipedia.org/wiki/Expect">expect command line script</a>.</p>
<p><code>user@ubuntu_desktop: sudo apt-get install expect<br />
user@ubuntu_desktop: cd .git/hooks<br />
user@ubuntu_desktop: vi post-commit<br />
#!/usr/bin/expect<br />
spawn git push<br />
expect "password:"<br />
send "mypass\r"<br />
interact</code></p>
<p>Δεν πρέπει να ξεχνάμε ότι πριν από ένα commit, push θα πρέπει να έχει προηγηθεί ένα pull.</p>
<p><strong>9. Τελευταίo test</strong><br />
<code>user@ubuntu_desktop: cd ../..<br />
user@ubuntu_desktop: echo 'MyApp (ver 4.00)' &gt; index.html<br />
user@ubuntu_desktop: git commit -a -m 'Modify index.html'</code></p>
<p>Τώρα αν κάνουμε refresh τον browser, βλέπουμε το ver 4.00, και το έχουμε πετύχει με μόνο μια εντολή την commit.</p>
<p>Νομίζω ότι ένα τέτοιο σενάριο είναι πολύ καλό για δημιουργία projects με πολλούς developers/designers και μάλιστα χωρίς να είναι στον ίδιο χώρο αν ανοίξουμε το http και το ssh στον server.</p>
]]></content:encoded>
			<wfw:commentRss>http://developstories.gr/server/git-apache-ubuntu/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Google Maps: Markers σε ομάδες με τον MarkerClusterer</title>
		<link>http://developstories.gr/jquery/google-maps-markers-%cf%83%ce%b5-%ce%bf%ce%bc%ce%ac%ce%b4%ce%b5%cf%82-%ce%bc%ce%b5-%cf%84%ce%bf%ce%bd-markerclusterer/</link>
		<comments>http://developstories.gr/jquery/google-maps-markers-%cf%83%ce%b5-%ce%bf%ce%bc%ce%ac%ce%b4%ce%b5%cf%82-%ce%bc%ce%b5-%cf%84%ce%bf%ce%bd-markerclusterer/#comments</comments>
		<pubDate>Mon, 08 Mar 2010 10:48:53 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[google maps]]></category>

		<guid isPermaLink="false">http://developstories.gr/?p=105</guid>
		<description><![CDATA[Το παρών άρθρο είναι ένα guest post απο τον Αλέξη Μπρέσσα. Στο άρθρο προϋποθέτουμε ότι ο αναγνώστης γνωρίζει javascript, και θα βοηθούσε να έχει ασχοληθεί με το Google Maps API στο παρελθόν. Το Google Maps API έδωσε τη δυνατότητα στους web developers να προσθέτουν χάρτες σε ιστοσελίδες, και να &#8220;σημαδεύουν&#8221; σημεία με markers. Έτσι, έχει &#8230; <a href="http://developstories.gr/jquery/google-maps-markers-%cf%83%ce%b5-%ce%bf%ce%bc%ce%ac%ce%b4%ce%b5%cf%82-%ce%bc%ce%b5-%cf%84%ce%bf%ce%bd-markerclusterer/" class="more-link">Continue reading<span class="screen-reader-text"> "Google Maps: Markers σε ομάδες με τον MarkerClusterer"</span></a>]]></description>
				<content:encoded><![CDATA[<p>Το παρών άρθρο είναι ένα guest post απο τον <a href="http://www.facebook.com/abresas">Αλέξη Μπρέσσα</a>. Στο άρθρο προϋποθέτουμε ότι ο αναγνώστης γνωρίζει javascript, και θα βοηθούσε να έχει ασχοληθεί με το Google Maps API στο παρελθόν.</p>
<p>Το <a href="http://code.google.com/apis/maps/">Google Maps API</a> έδωσε τη δυνατότητα στους web developers να προσθέτουν χάρτες σε ιστοσελίδες, και να &#8220;σημαδεύουν&#8221; σημεία με markers.<br />
Έτσι, έχει γίνει standard για πολλές κατηγορίες ιστοσελίδων, όπως μεσητικά, <a href="http://www.businesscatalogue.gr/">κατάλογοι επιχειρήσεων</a> κ.α. ή για τη σελίδα επικοινωνίας εταιρειών.<br />
Εάν όμως οι καταχωρήσεις μιας ιστοσελίδας αυξηθούν αρκετά, τότε τα πολλά markers μειώνουν τη χρηστικότητα ενός χάρτη, ενώ μπορεί κανείς να φτάσει και σε τραγική<br />
κατάληξη όπως αυτή:</p>
<p><a href="http://imagehoster.eu/"><img class="alignnone" title="tragic" src="http://imagehoster.eu/images/ku825hfsv1zctasahl36.jpg" alt="" width="500" height="400" /></a></p>
<p>Προς μεγάλη ευκολία όποιου αντιμετωπίσει αυτό το πρόβλημα, έχει αναπτυχθεί ένα εργαλείο το οποίο φαίνεται να μην είναι ευρέως γνωστό: το MarkerClusterer.<br />
To MarkerClusterer λύνει το πρόβλημα με το να φτιάχνει ομάδες από markers, όταν αυτά είναι το ένα πολύ κοντά στο άλλο. Το παραπάνω τραγικό παράδειγμα,<br />
τώρα γίνεται πιο συμμαζεμένο, όμορφο, μέχρι και εντυπωσιακό:</p>
<p><a href="http://imagehoster.eu/"><img class="alignnone" title="impressive" src="http://imagehoster.eu/images/kg27l80fqp80tcy70epy.jpg" alt="" width="501" height="400" /></a></p>
<p>Για να δούμε πώς μπορεί κανείς να χρησιμοποιήσει το MarkerClusterer:</p>
<pre class="brush: jscript; title: ; notranslate">
function map_init( mapElement ) {
var greece = new GLatLng( 39.1130, 24.0313 );
var thessaloniki = new GLatLng( 40.5408, 23.0480 );

var gmap = new GMap2( mapElement );
gmap.setUIToDefault();
gmap.setCenter( greece, 7 );

// σε αυτό τον πίνακα θα προσθέσεις τα markers που θέλεις
var markers = [];

// εδώ προσθέτουμε markers σε 20 τυχαία σημεία κοντά στη Θεσσαλονίκη
for ( var i = 0; i &lt; 20; ++i ) {
var latoffset = Math.random() - 0.5;
var lngoffset = Math.random() - 0.5;
var latlng = new GLatLng( thessaloniki.lat() + latoffset, thessaloniki.lng() + lngoffset );
var marker = new GMarker( latlng );

// προσθήκη του marker στον πίνακα
markers.push( marker );
}

// δημιουργία του clusterer και προσθήκη των markers στο χάρτη
var clusterer = new MarkerClusterer( gmap, markers );
}
</pre>
<p>Αρκεί λοιπόν τώρα κανείς να καλέσει τη συνάρτηση map_init με παράμετρο ένα &lt;div&gt; που θα χρησιμοποιηθεί<br />
για το χάρτη. Συνήθως θα καλείται όταν φορτώσει η ιστοσελίδα. Με <a href="http://developstories.gr/cat/jquery/">jquery</a>, αν το div έχει id=&#8221;map_canvas&#8221;:</p>
<pre class="brush: jscript; title: ; notranslate">
$( document ).ready( function() {
map_init( $( 'map_canvas' )[ 0 ] );
} );
</pre>
<p>Θα πρέπει επίσης να προσθέσετε το javascript αρχείο του Google Maps (με το απαραίτητο key) και του MarkerClusterer<br />
(και βέβαια το jquery αν χρησιμοποιηθεί αυτό) στον HTML κώδικά σας:</p>
<pre class="brush: jscript; title: ; notranslate">
&lt;script type=&quot;text/javascript&quot; src=&quot;http://maps.google.com/maps?file=api&amp;amp;v=2&amp;amp;key=GOOGLE_MAPS_KEY&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/1.0/src/markerclusterer_packed.js&quot; /&gt;
</pre>
<p>Μπορείτε επίσης να δείτε τον MarkerClusterer εν δράσει στο <a href="http://www.businesscatalogue.gr">business catalogue</a>.</p>
<p>Ο MarkerClusterer είναι μέρος μιας βιβλιοθήκης πολλών εργαλείων για Google Maps που ονομάζεται &#8220;GMaps Utility Library&#8221; και μπορεί κανείς να τη <a href="http://code.google.com/p/gmaps-utility-library-dev">βρει εδώ</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://developstories.gr/jquery/google-maps-markers-%cf%83%ce%b5-%ce%bf%ce%bc%ce%ac%ce%b4%ce%b5%cf%82-%ce%bc%ce%b5-%cf%84%ce%bf%ce%bd-markerclusterer/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Social Whale Groups jQuery plugin</title>
		<link>http://developstories.gr/jquery/socialwhale-widget-plugin/</link>
		<comments>http://developstories.gr/jquery/socialwhale-widget-plugin/#respond</comments>
		<pubDate>Thu, 04 Feb 2010 13:32:14 +0000</pubDate>
		<dc:creator><![CDATA[Kostas Theodorou]]></dc:creator>
				<category><![CDATA[jQuery]]></category>
		<category><![CDATA[social whale]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://developstories.gr/?p=93</guid>
		<description><![CDATA[Όπως θα έχετε διαπιστώσει το SocialWhale είναι το project που αφιερώνω τον περισσότερο χρόνο μου στο development. Αυτό μου δίνει το πλεονέκτημα να ασχολούμαι με πολύ ενδιαφέροντα αντικείμενα (ff extensions, betavine, jquery plugins, api&#8217;s κτλ). Έτσι έχω εμπλουτίσει τις γνώσεις μου πάνω στην javascript και ποιο συγκεκριμένα στο jquery framework. Η συνεισφορά της ομάδας του &#8230; <a href="http://developstories.gr/jquery/socialwhale-widget-plugin/" class="more-link">Continue reading<span class="screen-reader-text"> "Social Whale Groups jQuery plugin"</span></a>]]></description>
				<content:encoded><![CDATA[<p>Όπως θα έχετε διαπιστώσει το <a title="Social Whale" href="http://socialwhale.com">SocialWhale</a> είναι το project που αφιερώνω τον περισσότερο χρόνο μου στο development. Αυτό μου δίνει το πλεονέκτημα να ασχολούμαι με πολύ ενδιαφέροντα αντικείμενα (<a href="https://addons.mozilla.org/en-US/firefox/addon/11718/">ff extensions</a>, <a href="http://widget.vodafone.com/dev/widgets/social_whale_mini_1457">betavine</a>, <a href="http://plugins.jquery.com">jquery plugins</a>, <a href="http://en.wikipedia.org/wiki/Application_programming_interface">api&#8217;s</a> κτλ). Έτσι έχω εμπλουτίσει τις γνώσεις μου πάνω στην <a href="http://en.wikipedia.org/wiki/JavaScript">javascript</a> και ποιο συγκεκριμένα στο <a href="http://jquery.com">jquery framework</a>.</p>
<p>Η συνεισφορά της <a href="http://twitter.com/socialwhale/team">ομάδας του SocialWhale</a> για το <a href="http://wordcamp.gr">wordcampGR</a> event ήταν και η αφορμή για την δημιουργία ενός <a href="http://plugins.jquery.com/project/jwhale">jquery plugin</a> (<a href="http://twitter.com/thek27">thek27</a>) σε συνδυασμό με ένα wordpress plugin (<a href="http://twitter.com/humanworks">humanworks</a>) το οποίο παρουσιάζει ένα <a href="http://groups.socialwhale.com/1">SocialWhale Group</a> σε ένα <a href="http://wordcamp.gr/socialwhale">widget</a>. Για τους registered χρήστες υπάρχει και η δυνατότητα για vote και post στο group.</p>
<p>Αν και έχω ασχοληθεί παρά πολύ με τα plugins του jquery (ui, autocomplete, bt, fancybox, jGrowl, κ.α.) δεν είχα κάνει ποτέ κάποιο δικό μου όποτε ξεκίνησα από την αρχή, με reverse σε υπάρχοντα plugins και search στο google για tutorials και παραδείγματα.</p>
<p>Αφού βρήκα το <a href="http://denis.tumblr.com/post/51601242/a-simple-jquery-plugin-skeleton">skeleton</a> για το plugin η συνέχεια ήταν εύκολη υπόθεση, copy/paste από κώδικα socialwhale, εκτός από 2 σημεία που έπρεπε να βρούμε μια έξυπνη λύση. Το πρώτο ήταν το <a href="http://oauth.net">oauth</a> login που έπρεπε να γίνει από το site του socialwhale και στην συνέχεια να γίνεται redirect πίσω, ώστε να εμφανιστεί η φόρμα για το post και το action για το vote. Το δεύτερο ήταν και το ποιο ενδιαφέρον, γιατί ήθελα να βρω μια λύση με iframe για να αποφύγω το <a href="http://en.wikipedia.org/wiki/Cross-site_scripting">cross site scripting</a> που δεν παίζει με <a href="http://groups.google.com/group/jquery-en/browse_thread/thread/8bd0e249e2041b3b">ajax post</a>.</p>
<p>Ας τα πάρουμε από την αρχή, το socialwhale είναι έτσι φτιαγμένο που έχει δικό του api (extended πάνω στο twitter), έτσι για να καταλάβουμε αν ένας χρήστης είναι logged on ελέγχουμε το attribute voted από το json result (πχ <a href="http://groups.socialwhale.com/groups_api/timeline/both/1.json">groups_api/timeline/both/1.json</a>), και αν αυτό είναι null, τότε δεν είναι, διαφορετικά σημαίνει αν έχει κάνει vote το συγκεκριμένο update. Τώρα για το login απλά φτιάξαμε ένα button στο http://socialwhale.com/oauth όπου το φορτώνουμε με iframe, έτσι όλος ο υπόλοιπος κώδικας βρίσκεται στον server, ώστε να μπορούμε να ελέγχουμε όλα τα <a href="http://widgetmotion.com/">widget</a> από ένα oauth login, αυτό του socialwhale. Έτσι αν ένας χρήστης είναι login στο socialwhale, τότε είναι ταυτόχρονα login σε όλα τα widgets  ανεξαρτήτου group.</p>
<p>Τώρα όσο αφορά το submit της φόρμας, σκέφτηκα την λογική του iframe, αλλά με δυναμικό τρόπο, ώστε να έχω και τον έλεγχο του <a href="http://old.nabble.com/Trigger-ajax-event-on-iframe-load-td20294154s27240.html">onload event</a>. Ποιο συγκεκριμένα να δημιουργείτε στο runtime ένα <a href="http://stackoverflow.com/questions/205087/jquery-ready-in-a-dynamically-inserted-iframe">iframe με src=&#8221;about:blank&#8221;</a> και να γεμίζει με την φόρμα που στην συνέχεια γίνεται submit στο socialwhale. Μόλις επιστρέψει το post εμφανίζει στο body ένα div που έχει μέσα το json object και γίνεται trigger το load event του iframe. Έτσι στην συνέχεια γίνεται refresh το timeline και εμφανίζονται τα νέα posts.</p>
<p>Όπως και μάθε plugin έτσι και αυτό έχει options για πόσα rows, αν θα έχει auto refresh κτλ.<br />
Παράδείγματα μπορείτε να δείτε στο <a href="http://wordcamp.gr/socialwhale/">wordcamp.gr</a> και στο <a href="http://blog.socialwhale.com/wpwhale-demo">blog.socialwhale.com</a>.</p>
<p>Το plugin είναι κάτω από <a href="http://www.gnu.org/licenses/gpl.html">GPL licence</a> το <a href="http://blog.socialwhale.com/2010/01/30/social-whale-group-plugins/">Copyright</a> ανήκει στο SocialWhale και βρίσκεται στο  <a href="http://code.google.com/p/jwhale">google code</a> με την ονομασία jwhale.</p>
]]></content:encoded>
			<wfw:commentRss>http://developstories.gr/jquery/socialwhale-widget-plugin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ζητούνται Web developers</title>
		<link>http://developstories.gr/uncategorized/%ce%b6%ce%b7%cf%84%ce%bf%cf%8d%ce%bd%cf%84%ce%b1%ce%b9-web-developers/</link>
		<comments>http://developstories.gr/uncategorized/%ce%b6%ce%b7%cf%84%ce%bf%cf%8d%ce%bd%cf%84%ce%b1%ce%b9-web-developers/#respond</comments>
		<pubDate>Fri, 25 Sep 2009 17:32:27 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[php]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[web developers]]></category>
		<category><![CDATA[εργασία]]></category>

		<guid isPermaLink="false">http://developstories.gr/?p=91</guid>
		<description><![CDATA[Αν είστε subscriber στο blog μου σίγουρα θα σας ενδιαφέρει η παρακάτω αγγελία. Η αγγελία αφορά το Social Whale, το project με το οποίο ασχολούμαι τους τελευταίους μήνες.]]></description>
				<content:encoded><![CDATA[<p>Αν είστε subscriber στο blog μου σίγουρα <a href="http://humanworks.gr/humanworks/%CE%B6%CE%B7%CF%84%CE%BF%CF%8D%CE%BD%CF%84%CE%B1%CE%B9-web-developers/">θα σας ενδιαφέρει η παρακάτω αγγελία</a>. Η αγγελία αφορά το Social Whale, το project με το οποίο ασχολούμαι τους τελευταίους μήνες.</p>
]]></content:encoded>
			<wfw:commentRss>http://developstories.gr/uncategorized/%ce%b6%ce%b7%cf%84%ce%bf%cf%8d%ce%bd%cf%84%ce%b1%ce%b9-web-developers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ένα firefox extension για twitterholics</title>
		<link>http://developstories.gr/twitter/%ce%ad%ce%bd%ce%b1-firefox-extension-%ce%b3%ce%b9%ce%b1-twitterholics/</link>
		<comments>http://developstories.gr/twitter/%ce%ad%ce%bd%ce%b1-firefox-extension-%ce%b3%ce%b9%ce%b1-twitterholics/#comments</comments>
		<pubDate>Wed, 06 May 2009 05:41:42 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[twitter]]></category>
		<category><![CDATA[firefox]]></category>
		<category><![CDATA[social whale]]></category>

		<guid isPermaLink="false">http://developstories.gr/?p=86</guid>
		<description><![CDATA[Τελευταία και αφού μπήκα στο development team του social whale, ασχολούμαι αρκετά με development γύρω απο το twitter. Μετά το winamp plugin για το audiotwit, σκεφτήκαμε να κάνουμε κάτι πιό χρήσιμο για την κοινότητα του twitter. The social whale firefox extension To social whale firefox extension είναι ένα απλό addon το οποίο μπορείτε να χρησιμοποιήτε &#8230; <a href="http://developstories.gr/twitter/%ce%ad%ce%bd%ce%b1-firefox-extension-%ce%b3%ce%b9%ce%b1-twitterholics/" class="more-link">Continue reading<span class="screen-reader-text"> "Ένα firefox extension για twitterholics"</span></a>]]></description>
				<content:encoded><![CDATA[<p>Τελευταία και αφού μπήκα στο development team του <a href="http://socialwhale.com/">social whale</a>, ασχολούμαι αρκετά με development γύρω απο το twitter. Μετά το <a href="http://developstories.gr/twitter/winamp-plugin-%CE%B3%CE%B9%CE%B1-%CF%84%CE%BF-audiotwit/">winamp plugin</a> για το <a href="http://audiotwit.com/">audiotwit</a>, σκεφτήκαμε να κάνουμε κάτι πιό χρήσιμο για την κοινότητα του twitter.</p>
<h3>The social whale firefox extension</h3>
<p>To <a href="https://addons.mozilla.org/en-US/firefox/addon/11718/">social whale firefox extension</a> είναι ένα απλό addon το οποίο μπορείτε να χρησιμοποιήτε για να δημιουργήσετε short urls μέσω του <a href="http://twt.gs/">twt.gs</a> και για να κάνετε upload φωτογραφίες μέσω του <a href="http://twitpic.com/">twitpic.com</a>.</p>
<p>Για οδηγίες ανατρέξτε <a href="http://blog.socialwhale.com/2009/04/30/social-whale-firefox-extension/">εδώ</a>, ενώ άν έχετε οποιοδήποτε σχόλιο ή παρατήρηση αφήστε ένα σχόλιο.</p>
]]></content:encoded>
			<wfw:commentRss>http://developstories.gr/twitter/%ce%ad%ce%bd%ce%b1-firefox-extension-%ce%b3%ce%b9%ce%b1-twitterholics/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>php και sudo</title>
		<link>http://developstories.gr/php/php-%ce%ba%ce%b1%ce%b9-sudo/</link>
		<comments>http://developstories.gr/php/php-%ce%ba%ce%b1%ce%b9-sudo/#comments</comments>
		<pubDate>Fri, 06 Feb 2009 11:34:19 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[php]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[sudo]]></category>

		<guid isPermaLink="false">http://developstories.gr/?p=80</guid>
		<description><![CDATA[Ένα απο τα ζητήματα που προκύπτουν στο advanced web development είναι η αλληλεπίδραση με το λειτουργικό σύστημα. Για παράδειγμα θέλουμε να τρέξουμε μία εντολή και να δούμε το αποτέλεσμά της. Αυτό γίνεται εύκολα με την shell_exec() ή την exec() ωστόσο απο μόνη της δέν λύνει όλα τα προβλήματα. Ο apache αλλα και κάθε web server &#8230; <a href="http://developstories.gr/php/php-%ce%ba%ce%b1%ce%b9-sudo/" class="more-link">Continue reading<span class="screen-reader-text"> "php και sudo"</span></a>]]></description>
				<content:encoded><![CDATA[<p>Ένα απο τα ζητήματα που προκύπτουν στο advanced web development είναι η αλληλεπίδραση με το λειτουργικό σύστημα. Για παράδειγμα θέλουμε να τρέξουμε μία εντολή και να δούμε το αποτέλεσμά της. Αυτό γίνεται εύκολα με την shell_exec() ή την exec() ωστόσο απο μόνη της δέν λύνει όλα τα προβλήματα.</p>
<p>Ο apache αλλα και κάθε web server τρέχει με ελάχιστα δικαιώματα που συνήθως του επιτρέπουν να κάνει μόνο βασικά πράγματα. Απο άποψη ασφάλειας αυτό είναι θεμιτό και σε καμία περίπτωση δέν πρέπει να τρέχει με δικαιώματα root γιατί εκεί ένα απλό bug ασφαλείας μπορεί να οδηγήσει σε πλήρη καταστροφή. Την λύση σ&#8217;αυτό το πρόβλημα την δίνει το εργαλείο sudo.</p>
<h3>Τί είναι το sudo</h3>
<p>Το <a href="http://www.courtesan.com/sudo/">sudo</a> είναι ένα εργαλείο για *nix συστήματα (unix,linux,κτλ) το οποίο επιτρέπει να τρέξουμε κάποια εντολή με δικαιώματα root. Έτσι αντί να κάνουμε vurnerable όλο τον web server μπορούμε όποτε χρειάζεται να τρέχουμε μέσω sudo τις εντολές που θέλουμε.</p>
<h3>Sudo configuration</h3>
<p>Καταρχήν πρέπει να κάνουμε ένα απλό configuration στο sudo ωστε να δέχεται εντολές απο τον web server. Για την ακρίβεια πρέπει να δώσουμε δικαιώματα στον χρήστη που τρέχει τον apache που συνήθως λέγεται apache. Για σιγουριά δείτε στο httpd.conf το directive User.</p>
<p>Για να κάνουμε το sudo να δέχεται εντολές απο τον web server πάμε στο αρχείο /etc/sudoers και προσθέτουμε την παρακάτω γραμμή:</p>
<blockquote><p><code><strong>apache ALL=(ALL) ALL</strong></code></p></blockquote>
<p>Αν ο χρήστης δέν ονομάζεται apache αντικαταστήστε το όνομα apache στο παραπάνω. Για να μήν υπάρχουν προβλήματα ασφαλείας θέτουμε και ένα password στον χρήστη apache. Αυτό γίνεται απο κονσόλα με την παρακάτω εντολή:</p>
<blockquote><p><strong><code>passwd apache</code></strong></p></blockquote>
<p>Αν δέν ακολουθήσετε το τελευταίο βήμα, κάθε php script στον server θα μπορεί να τρέξει εντολές με δικαιώματα root και αυτό μπορεί να οδηγήσει σε σοβαρά προβλήματα ασφαλείας.</p>
<h3>Τρέχοντας sudo με php</h3>
<p>Αφού τελειώσουμε με τα configuration το υπόλοιπο είναι πολύ εύκολο αρκεί να χρησιμοποιήσετε την παρακάτω <a href="http://developstories.gr/cat/php/">php</a> function που δημιούργησα για αυτό τον σκοπό:</p>
<pre class="brush: php; title: ; notranslate">

define(&quot;SUDO&quot;,&quot;/usr/bin/sudo&quot;);
define(&quot;SUDO_PASS&quot;,&quot;pass&quot;);

// SUDO START
exec('echo '.SUDO_PASS.'|'.SUDO.' -S -v 2&gt;&amp;1');

// SUDO EXECUTE
echo exec(SUDO . &quot; ls /root 2&gt;&amp;1&quot;);

// SUDO STOP
exec(SUDO.&quot; -k 2&gt;&amp;1&quot;);

</pre>
<p>Προσέξτε τα δύο define στην αρχή. Το πρώτο είναι το path για την sudo και λογικά δέν χρειάζεται να το πειράξετε. Το δεύτερο όρισμα (SUDO_PASS) πρέπει να περιέχει το password του apache.</p>
]]></content:encoded>
			<wfw:commentRss>http://developstories.gr/php/php-%ce%ba%ce%b1%ce%b9-sudo/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
