Обновляем корейский BlackVue DR400G-HD и меняем язык.

Приобрел себе недавно этот девайс на Ebay. Приехал он с корейской прошивкой версии 1.038 соответственно голосовые подсказки на корейском языке.
На офф сайте на данный момент лежит последняя 1.040 – прошивка с англоязычными подсказками. Прошив ее я обнаружил что регистратор не хочет включаться заявив что версия прошивки и программного обеспечения не совпадает. “version information is different” .
Погуглив оказалось что с версии 1.038 регистраторы предназначенные для Кореи не прошиваются на отличные от корейского языки. Continue reading Обновляем корейский BlackVue DR400G-HD и меняем язык.

Yii ConsoleApplication and Gearman Workers PSD to JPEG conversation

Hi. I am going to show small example of how I use gearman with my Yii applications.
If someone doesn’t know what is gearman here is quick explanation from the official website http://gearman.org

A Gearman powered application consists of three parts: a client, a worker, and a job server. The client is responsible for creating a job to be run and sending it to a job server. The job server will find a suitable worker that can run the job and forwards the job on. The worker performs the work requested by the client and sends a response to the client through the job server. Gearman provides client and worker APIs that your applications call to talk with the Gearman job server (also known as gearmand) so you don’t need to deal with networking or mapping of jobs. Internally, the gearman client and worker APIs communicate with the job server using TCP sockets.

Our example application converts psd to jpeg. We have to install gearman job server, gearman pecl extension and ImageMagick on the server.
Continue reading Yii ConsoleApplication and Gearman Workers PSD to JPEG conversation

Bitcoin биржа Mt.Gox – взломана

Японская биржа торгующая bitcoin была взломана. Как пишет администрация злоумышленники получили доступ ко всей базе в том числе к зашифрованным паролям. Всем пользователям рекомендовано сменить пароли (в случае если они используют одинаковые) в других сервисах.

https://support.mtgox.com/entries/20208066-huge-bitcoin-sell-off-due-to-a-compromised-account-rollback

Fully ajax website with Yii – Part 2

In previous post I described how to create simple full ajax web application without changing basically logic of the application. All changes was made to the layout, view and to the base classes.
But as you maybe already noticed not everything works well. For example if you tried to sign-in -menu won’t be updated. In this short article I will show how to fix such kind of problems.
All data which ouputs for ajax request collecting with help of flash messages (built-in Yii functionality);
So basically if you wand update something on your page you have to set flash message with name updatedata wich contains an array with key value elements

Example   protected/controllers/SiteController.php ;  actionLoginMethod

 
                ...
                // collect user input data
		if(isset($_POST['LoginForm']))
		{
			$model->attributes=$_POST['LoginForm'];
			// validate user input and redirect to the previous page if valid
                        if($model->validate() && $model->login()) {
                               Yii::app()->user->setFlash('updatedata', array(
 						'#mainmenu'=>$this->renderPartial('/includes/menu',array(),true),));                   
                               $this->redirect(Yii::app()->homeUrl);
                        }
                ...

As you can see I put all mainmenu to separate file. Here is it’s content

protected/views/includes/menu.php

	widget('zii.widgets.CMenu',array(
			'items'=>array(
				array('label'=>'Home', 'url'=>array('/site/index')),
				array('label'=>'About', 'url'=>array('/site/page', 'view'=>'about')),
				array('label'=>'Contact', 'url'=>array('/site/contact')),
				array('label'=>'Login', 'url'=>array('/site/login'), 'visible'=>Yii::app()->user->isGuest),
				array('label'=>'Logout ('.Yii::app()->user->name.')', 'url'=>array('/site/logout'), 'visible'=>!Yii::app()->user->isGuest)
			),
		)); ?>

For avoiding dublicating data I cut this code from main layout and put there renderPartial instead.

protected/views/layout/main.php

...

...

So all blocks that might be updated via ajax better to move to includes folder. Now after sign -in main menu is changing.

Now we have to add the same code to sign-out method

protected/controllers/SiteController.php

...
public function actionLogout()
	{
		Yii::app()->user->logout();
		Yii::app()->user->setFlash('updatedata', array(
 						'#mainmenu'=>$this->renderPartial('/includes/menu',array(),true),));
		
		$this->redirect(Yii::app()->homeUrl);
	}
...

In that way you can update everyrhing you want on the webpage. Good luck!
P.S. Suggestions are welcome!

Full ajax Yii webapp demo

Fully ajax website with Yii – Part 1

This article will be suitable for who made fully ajax site like  Gmail with best PHP Framework – Yii.
Main requirements are:

  1. All pages should be loaded via ajax.
  2. All forms should be submitted via ajax.
  3. Pages should be reachable by search engines.
  4. History support.
  5. Window title support
  6. Minimal additional code.

Here  is my solution.
I get for example simple web application which you can made with help of yiic utility.

cd /home/www/mywebsite
wget http://yii.googlecode.com/files/yii-1.1.7.r3135.tar.gz
gunzip yii-1.1.7.r3135.tar.gz
tar -xvf yii-1.1.7.r3135.tar
mv yii-1.1.7.r3135/framework/ ./
php framework/yiic.php webapp ./

That is it your application has been created!

Add this code to the protected/views/layout/main.php file before  </head> tag.


getClientScript();
  		$cs->registerCoreScript('jquery');
  		$cs->registerCoreScript('bbq');
?>
  	
  	registerScript('applychanges', $script, CClientScript::POS_HEAD);
?>	

  	registerScript('loading-indicator', $script, CClientScript::POS_READY);
?>



registerScript('ajaxlinks-and-forms', $script, CClientScript::POS_READY);
?>

Next code should be added after <body> tag

We’ve jus added javascript code which will handle all client side job
Then we extend Base controller class “Controller”.

protected/componens/Controller.php; Add new methods below:

public function ajaxRender($file, $data=array()) {
		
		$data['title'] = CHtml::encode($this->pageTitle);			
		header('Content-type: text/x-json');
		echo CJSON::encode($data);
		Yii::app()->end();

	}
		
	public function render($file, $params = array(), $data=array()) {
		
		if(Yii::app()->request->isAjaxRequest){

			if(Yii::app()->user->hasFlash('updatedata')) {
				$flashdata = Yii::app()->user->getFlash('updatedata');		
				$data = $data + $flashdata;	
			}
			
			
			$data['#content'] = parent::renderPartial($file, $params, true);
			$data['title'] = CHtml::encode($this->pageTitle);
			
			header('Content-type: text/x-json');
			echo CJSON::encode($data);
			Yii::app()->end();
			
		} else {			
			echo parent::render($file, $params, true);	
		}	
	
	}
	
	public function redirect($url) {
		
		if(is_array($url) && isset($url[0]))
			$url = $url[0];
	
		if(Yii::app()->request->isAjaxRequest){
			
			$data = array();
			
			if(Yii::app()->user->hasFlash('updatedata')) {
				$flashdata = Yii::app()->user->getFlash('updatedata');		
				$data = $data + $flashdata;	
			}
			
			$data['#content']= '';
			
			
			
			header('Content-type: text/x-json');
			echo CJSON::encode($data);
			Yii::app()->end();
				
		} else {
			parent::redirect($url);
		}
	}
	
	public function refresh() {
		if(Yii::app()->request->isAjaxRequest){

			$data = array();
			
			if(Yii::app()->user->hasFlash('updatedata')) {
				$flashdata = Yii::app()->user->getFlash('updatedata');		
				$data = $data + $flashdata;	
			}
			
			
			$data['#content']= '';
			
			header('Content-type: text/x-json');
			echo CJSON::encode($data);
			Yii::app()->end();
			
		} else {
			parent::refresh();
		}
	}

Then you have to override some Request methods to support CWebUser redirects:

Put content below to new file with name protected/components/EHttpRequest.php

request->isAjaxRequest){

			$data = array();
			
			if(Yii::app()->user->hasFlash('updatedata')) {
				$flashdata = Yii::app()->user->getFlash('updatedata');		
				$data = $data + $flashdata;	
			}
			
			$data['#content']= '';
			
			header('Content-type: text/x-json');
			echo CJSON::encode($data);
			
			Yii::app()->end();	
		} else {
			parent::redirect($url);
		}
	}
	
	public function refresh() {
		if(Yii::app()->request->isAjaxRequest){

			$data = array();
			
			if(Yii::app()->user->hasFlash('updatedata')) {
				$flashdata = Yii::app()->user->getFlash('updatedata');		
				$data = $data + $flashdata;	
			}
			
			
			$data['#content']= '';
			
			header('Content-type: text/x-json');
			echo CJSON::encode($data);
			
			
			Yii::app()->end();	
		} else {
			parent::refresh();
		}
	}
}

Set new class name for request component in your main config protected/config/main.php

It should looks like this

  .... 
  'components'=>array(
		'user'=>array(
			// enable cookie-based authentication
			'allowAutoLogin'=>true,
		),
		
		'request'=>array(
                     'class'=>'EHttpRequest',
               ),

   ....

That’s all. Now you have full ajax simple web application.
Offcourse it has some limitations and issues, further I’ll extend this article.

P.S. I have forgotten to say about styles.
Just add these lines to your css file.

#loading {
	position:fixed;
	padding:3px;
	background:#80B646;
	color:#fff;
	display:none;
	z-index:999;
	right:0;
	top:0;
}
#loading.activity {
	display:block;
}

Эмуляция статических страниц в WordPress

Когда для сайта не совсем подходит WordPress CMS, а очень хочется – разработчики идут на всякие хитрости, благо WordPress их предоставляет с головой. Continue reading Эмуляция статических страниц в WordPress

Привет Solr, Sphinx – пока, пока!

Недавно для одно из своих проектов понадобился полноценный поиск. В первую очередь я обратился к Sphinx но вскоре был вынужден от него отказаться в пользу поискового движка Solr от Apache Software Foundation

Основные преимущества (для меня)

  • Solr поставляется с более либеральной лицензией. (чтобы изменить функционал или использовать Sphinx как часть коммерческого приложения нужно покупать коммерческую лицензию)
  • Solr не нуждается в предоставлении уникального идентификатора для документа. Для Sphinx нужен числовое уникальное значение.
  • В Solr тоже есть посдветка искомых слов.
  • В Solr также есть поддержка русской морфологии, подсветка искомого запроса в результатах поиска.
  • Для Solr не проблема удалить единичный или набор документов которые просто подходят определенному критерию поиска. Например можно все результаты поиска по определенному слову. В случае со Sphinx для этого нужно проводить переиндексацию. Также в единичном порядке можно добавить документы в индекс.
  • В Solr встроена поддержка проверки орфографии и утилита позволяющая не использовать дубликаты документов в поиске.
  • Solr может индексировать документы такие как Microsoft Word, PDF и другие
  • Solr работает везде где есть Java
  • Для Solr есть отличное PHP extension под PHP http://www.php.net/manual/en/book.solr.php Позволяющее проводить поиск, добавлять, удалять документы

Думаю преимуществ достаточно чтобы выбрать Solr как поисковый движок для Вашего сайта.

Кратко о установке и использовании. На примере Freebsd.

Обновляем порты  (portsnap fetch, portsnap extract, portsnap update) или любым другим удобным для Вас способом

cd /usr/ports/textproc/apache-solr
make install clean

В директории порта,  появится папка work в которой будет  готовый к работе дистрибутив. Переместим его в более удобное для работы место.

Далее.

cd apache-solr-1.4.1/example

java -jar start.jar

Все, Solr запускается.

Gearman воркер для скачивания файлов по URl

Gearman, сервер организации и распределения задач, более подробно можно узнать на оффициальном сайте http://gearman.org/

Данную статью я пишу для тех кто уже знает для чего он нужен и как его устанавливать.  В одном из моих проектов gearman воркеры скачивают файлы по ссылке. Также они во время работы отсылают статусы о прогрессе. Вы можете запустить нужно количество воркеров в зависимости от доступного интернет канала и конфигурации серверов.

Собственно сам класс воркера

download_size = 0;
		$this->downloaded = 0;
		$this->username = '';
		$this->pswd = '';
		$this->referrer = '';
		
	}
	private function closefp() {
		@fclose($this->tmp_file_pointer);
	}
	
	private	function remote_filesize()
	{	
		ob_start();
		$ch = curl_init($this->url);
		curl_setopt($ch, CURLOPT_HEADER, 1);
		curl_setopt($ch, CURLOPT_NOBODY, 1);
	    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
	    curl_setopt($ch, CURLOPT_REFERER, $this->referrer);
	    
		if(!empty($this->username) && !empty($this->pswd)) {
			curl_setopt($ch, CURLOPT_USERPWD, $this->username.':'.$this->pswd);
		}
	
		$ok = curl_exec($ch);
		
		curl_close($ch);
		$head = ob_get_contents();
		ob_end_clean();
	
		$regex = '/Content-Length:\s([0-9].+?)\s/';
		$count = preg_match($regex, $head, $matches);
	
		return isset($matches[1]) ? $matches[1] : 0;
	}
	
	
	function fetchfile($job) {
		$this->resetvalues();
		
		$this->job = $job;
		$workload= $this->job->workload();
 	
		echo "Received job: " . $this->job->handle() . "\n";
		
		$args = json_decode($workload);
		
		if(!isset($args->url) || !isset($args->tmp_file)) {
			print "Url and tmp_file are required";
			return;
		}
		
		$this->url = $args->url;
		$this->pswd = (isset($args->password))? $args->password : '';
		$this->username = (isset($args->username))? $args->username : '';
		$this->referrer = (isset($args->referrer)) ? $args->referrer : $this->url;
		
		$this->download_size = $this->remote_filesize();
		
		if($this->download_size==0) {
			$this->resetvalues();
			print "Can't get size header of the remote file";
			return;
		}
		
		$this->tmp_file_name = $args->tmp_file;
		$this->tmp_file_pointer = fopen($this->tmp_file_name, "wb");
		
		if($this->tmp_file_pointer)
			$this->fetchurl();
		else {
				$this->resetvalues();
				print("Couldn't open tmp file");
				return;
			 }
	}
	
	function percent($num_amount, $num_total) {
		$count1 = $num_amount / $num_total;
		$count2 = $count1 * 100;
		$count = ceil($count2);
		return $count;
	}

	function fetchurl() {
	
		$ch = curl_init($this->url);	
		curl_setopt($ch, CURLOPT_NOPROGRESS, true);
		curl_setopt($ch, CURLOPT_WRITEFUNCTION, array($this, 'fetchcallback'));
		curl_setopt($ch, CURLOPT_REFERER, $this->referrer);
		
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
		
		if(!empty($this->username) && !empty($this->pswd)) {
			curl_setopt($ch, CURLOPT_USERPWD, $this->username.':'.$this->pswd);
		}
		
		curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, 'readHeader'));
		curl_exec($ch);
		curl_close($ch);
	}
	
	function readHeader($ch, $headers_text) {
		return strlen($headers_text);
	}
	
	function fetchcallback($ch, $str) {
			$length =  strlen($str);
			
			$this->downloaded +=$length;
			if(!fwrite($this->tmp_file_pointer,$str)) {
				$this->resetvalues();
				print "Can't write to tmp file\n";
				return;
			} 
			
			if($this->downloaded >= $this->download_size) {
				 $this->resetvalues();
				 $this->job->sendStatus(100,100);
				 $this->job->sendComplete();
				  
				 $this->closefp();			
				 print "100% Done\n";	
				 return;
			}		
			
			if($this->last_part_time > time()- 2) // 2 seconds
			return $length;
			
			$this->last_part_time = time();
			$this->job->sendStatus($this->percent($this->downloaded, $this->download_size) ,100);

			print $this->percent($this->downloaded, $this->download_size)."%...";
			return $length;	
	}
	
}

Как видите для скачивания используется curl.
Далее прилагаю код демона, который поднимает вышеуказанный класс. У меня это реализовано как консольная комманда Yii фреймворка, но вы можете реализовать демон как угодно.

Версия с использованием Yii

addServer(); 
		$worker->addFunction("fetchfile", array($worker, "fetchfile")); 
		
		
		while (1)
		{
		  print "Waiting for job...\n";
		 
		  $ret= $worker->work();
		  if ($worker->returnCode() != GEARMAN_SUCCESS)
		    break;
		}

	}

}

Standalone версия


addServer(); 
		$worker->addFunction("fetchfile", array($worker, "fetchfile")); 
		
		
		while (1)
		{
		  print "Waiting for job...\n";
		 
		  $ret= $worker->work();
		  if ($worker->returnCode() != GEARMAN_SUCCESS)
		    break;
		}

Запуск standalone версии


php worker.php

Запус версии с использованием Yii

php dispatch.php Fetch

Ну и наконец чтобы статья была исчерпывающей я приведу скрипт (точку входа) dispatch.php для Yii. В принципе он очень похож на обычный index.php для Yii application

require_once(dirname(__FILE__).'/framework/yii.php');
$config=dirname(__FILE__).'/protected/config/console.php';

// remove the following line when in production mode
defined('YII_DEBUG') or define('YII_DEBUG',true);

Yii::createConsoleApplication($config)->run();

Нужно только создать отдельный конфиг для консоли. Как минимум скопируйте содержимое main config
Все, будем считать что наши демоны 🙂 запущены и ждут камманды от gearman jоb сервера чтобы начать работу.

Даем комманду job серверу

addServer();

if(isset($_GET['handle'])){
	$handle = $_GET['handle'];
	$status = $client->jobStatus($handle);
	print_r($status);
	die;
}

$params = array(
		'referrer'=>'',
		'tmp_file'=>dirname(__FILE__).'/test.file',
		'url'=>'http://dl1.overload.in.ua/files/2050/SOD3z9jF3LiXdxO/file.zip',
		);

$params = json_encode($params);
$handle = $client->doBackground("fetchfile", $params);
header('Location: index.php?handle='.urlencode($handle));

В примере идет подключение к job серверу, дается фоновая команда с параметрами. Далее работа скрипта заканчивается и идет редирект с параметром именем задачи job-handle
Далее по этому параметру скрипт может следить за прогрессом выполнения именно этого таска.

Конец.