Archive

Archive for the ‘PHP’ Category

WordPress 烦人的 revision 和 auto-draft

July 6th, 2010 Fwolf 1 comment

revision 是早就有了,auto-draft 是最近才发现的,个人非常不喜欢这2个功能,偏偏 WordPress 还没有在后台中增加显式的关闭功能,所以更显得烦人。

revision 是你每保存一次 post 的时候,都把修改前的内容存成一个 revision,这样你就不用担心以前的版本找不到了。问题是,写 blog 又不是写代码,用得着这把牛刀么?就是写代码,也有不想保存的版本,基本上扔到 scm 里面就不会再看了呀。

禁用 revision 的方法,对目前的 WordPress 3.0 有效,在 wp-config.php 中添加:

define('WP_POST_REVISIONS', false);
define('AUTOSAVE_INTERVAL', 60000);

同时也禁止了自动保存,多手工保存吧,或者本地写好了再 post 。

auto-draft 是这样出现的,当你 new 一个 post 的时候,以前是第一次保存的时候生成 id,现在则是打开 new 页面的时候就生成了,体现在数据库中 wp_posts.post_statusauto-draft。这种没有内容先保存的方法一般是用来避免多人同时保存时的写入数据冲突,可一般的 blog 会频繁产生这种情况么?更糟糕的是,auto-draft 类型的post 无法在 Posts 管理中进行编辑,也就是说如果你打开了 new post 页面,输入了一些内容,然后没有保存或者发布就离开了这个页面,那么数据库中就多了一条 auto-draft “僵尸记录”,你再也找不到它了。

auto-draft 目前好像没有方法关闭,但可以从数据库中把他们更改为 draft,以后当草稿修改成新文章就是了:

mysql> SELECT DISTINCT post_status, count(1) FROM wp_posts GROUP BY post_status;
mysql> UPDATE wp_posts SET post_status='draft' WHERE post_status='auto-draft';

最后,贡献一个 php 脚本,自动把 revision 和 auto-draft 都修改成草稿 draft,并且找出数据库中不连续的 post id,把他们也都存成草稿,这样可以保持 url 中 id 的连续性,似乎更加美观和整洁。未经严格测试,请参考使用:

Related posts

Categories: Blog, PHP Tags: , , ,

可恶,被 PHP-Mcrypt 的官方 Example 误导了

October 22nd, 2009 Fwolf 6 comments

在看 php 的 mcrypt 加密,想使用对称算法,解决小块内容(比如 url、post)网上传输的安全性。即加密、解密用同一个密码。官方文档有个非常完整的演示功能的例子,大概顺序是:

  • 打开 module
  • 生成 IV
  • 得到 key/密钥/密码
  • 初始化(引擎?)
  • 进行加密操作
  • 关闭(引擎?)
  • 重新初始化(引擎?)
  • 进行解密操作
  • 关闭(引擎?)
  • 关闭 module

加密、解密放在了一个代码片段中,大概是想说,加、解密就那一句代码不同而已。

按照这个理解,为了使用方便,我把加、解密分解成了2个函数,内容都和例子差不多,不会有错。但一运行,不管用哪种加密算法,都会出现奇怪的解密后与原文不一致的错误。还不是完全不一致,后面大半段内容都是正确的,比如原文是包含 a-z 26个字母的字符串,运行结果如下:

$ ./mcrypt.php 
Encrypt:
M~<5¶¤Jw^TÝ×. ÃV¯
Decrypt:
Âò¹ÁIijklmnopqrstuvwxyz

好一通找原因,最后在支持算法列表页面中找到这么一句:The IV must be unique and must be the same when decrypting/encrypting.加、解密时所使用的 IV 必须相同。

昏,例子代码中 IV 是使用随机数生成的,分成2个函数之后,加、解密操作生成的 IV 肯定不一样,这就是解密失败的原因。mcrypt_create_iv() 函数文档页面的 user notes 中有位 Chris 还对 IV 纠正了一些错误观点。

综上,正确解密需要将 IV 与密文一同存储、传递。而我的需求比较简单,就没有必要这么作,反正 IV 也不需要保密,所以直接用 key 的 sha1 值的片断,比如前8位(与 git 版本号简写类似)作为 IV,对安全性影响不大,应该是可以接受的。

问题解决,收工,有和我一样吃过亏的同学么?

Related posts

Categories: PHP Tags: , , , , ,

Curl奇怪的403错误

July 1st, 2009 Fwolf 4 comments

自己用的小PHP应用,使用curl抓网页下来处理,为了穿墙方便,使用Privoxy作为代理,便于选择哪些网站使用proxy、哪些不用。但今天却遇到了奇怪的问题,访问google baidu这些网站居然都返回403错误,而访问其他的一些网站没事,如果设置为不使用proxy则都能正常访问。

难道google baidu就不让用proxy连接么?显然不可能,所以打开curl的信息输出(curl_setopt($this->mSh, CURLOPT_VERBOSE, 1);)看看,得到以下结果:

*   Trying 127.0.0.1... * connected
* Connected to 127.0.0.1 (127.0.0.1) port 8118 (#0)
* Establish HTTP proxy tunnel to www.baidu.com:80
> CONNECT www.baidu.com:80 HTTP/1.0
Host: www.baidu.com:80
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Proxy-Connection: Keep-Alive

< HTTP/1.0 403 Connection not allowable
< X-Hint: If you read this message interactively, then you know why this happens ,-)
< 
* The requested URL returned error: 403
* Received HTTP code 403 from proxy after CONNECT
* Closing connection #0
... Failed.

可以看到proxy服务器工作正常,的确是baidu返回了403错误,但原因肯定还在我这边。终于,从网上(1of2, 2of2)得到了点启发──我使用的是proxytunnel而非proxy。

在代码中,有这么一句:

	curl_setopt($this->mSh, CURLOPT_HTTPPROXYTUNNEL, true);
	curl_setopt($this->mSh, CURLOPT_PROXY, $phost);

php文档中没有详细说明,不过man curl中有详细解释,两者都是代理,proxytunnel(-p参数)允许其他协议通过http代理传输,而proxy(-x参数)则只能走http协议。所以我猜测,google baidu的服务器和curl的proxytunnel不和,所以返回403。

禁用掉上面2行代码的第一句后,curl访问恢复正常。

比较奇怪的是,几种操作系统下还不一样,一台MAC OSX就要显式的禁用proxytunnel才可以,curl版本:

$ curl --version
curl 7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Protocols: tftp ftp telnet dict ldap http file https ftps 
Features: GSS-Negotiate IPv6 Largefile NTLM SSL libz 

而另外一台ubuntu则完全不受影响,怎么都能用,curl版本:

$ curl --version
curl 7.18.2 (i486-pc-linux-gnu) libcurl/7.18.2 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.10
Protocols: tftp ftp telnet dict ldap ldaps http file https ftps 
Features: GSS-Negotiate IDN IPv6 Largefile NTLM SSL libz 

MT主机上的centos也没事,curl版本:

$ curl --version
curl 7.15.5 (i686-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5
Protocols: tftp ftp telnet dict ldap http file https ftps 
Features: GSS-Negotiate IDN IPv6 Largefile NTLM SSL libz 

看来不完全是curl版本问题,MAC OSX的确与众不同啊。

还有一个原因也会导致curl返回403错误,如果设置了:

	curl_setopt($ch, CURLOPT_NOBODY, true);

则需要紧跟着设置:

	curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');

不然会因为http服务器不允许 HEAD 命令而返回403错误。参考:Trouble with a cURL request in PHP。MAC OSX上curl之所以特殊,也不排除是这种原因吧。

Related posts

Categories: Develop, Internet, PHP Tags: , , , , , ,

在GLPI中输出中文PDF文件

April 2nd, 2009 Fwolf 2 comments

PDF,好东西,就是麻烦,以前鼓捣ThingkingRock的时候打过交道,感觉挺复杂,现在捣鼓GLPI时又遇到了,GLPI用的是ezpdf。

先说点闲话,有些比较偏门的东西是真难找啊,比如今天要说的,算不上是学术问题,也没有企业级的支持(如果你愿意打电话咨询GLPI官方又懂法语我没话说),只能在网上大海捞针的找,一方面这是很辛苦的,查阅各类资料几十篇(前提还是你得能找到),写下来也就寥寥百余字,所以我一般愿意把我翻到的资料列在后面作为参考,或许能为别人省点力气;另一方面就是搜索引擎的功劳了,记得还没有google和百度的时候,只有一个yahoo分类目录,要搜问题就得到几个大型论坛比如CSDN里去翻,信息量就窄多了,另外搜索引擎的质量在这里也起到了很大的作用,信息重复率高不高、能否最快速度找到原创内容、信息关联度、是否有用等因素都关系到用户花费时间的多少,这也是我很少用百度的原因之一,还有就是感觉百度的英文资料差太多了。────谨以此纪念我几乎24小时的连续工作以及疲惫的眼睛和脖子。

EZPDF

EZPDF一般是不支持unicode多字节编码的,不过还是作了一些尝试,毕竟是GLPI内置的,搞定了用着方便。首先EZPDF使用afm字体,要得到afm字体,需要用到ttf2tex,在pdfTeX包里:

	$ aptitude install ttf2tex

转换字体,这次我不用宋体了,用于打印的,还是仿宋看着舒服,用了个方正仿宋简体:

	$ ttf2afm -e T1-WGL4.enc -o fzfangsong.afm fzfangsong.TTF

试了试不能用,然后用afm2font处理:

	$ php5 afm2font.php fzfangsong

得到php_fzfangsong.font等文件,但这些文件无论怎么套上ezpdf都是不行,什么文字都没了,pdf文件中倒是显示了正确的字体名:

fzfangsong
Type1
Not embedded

再在网上翻资料,简直就是钻到TeX用户堆儿里去了,忽然发现windows字体应该是TrueType字体,而ezpdf使用的难道是Type1字体,两者之间还需要转换?终于查到基于texlive2008的中文绿色免安装tex系统中有打包的字体,这里面有给TeX用的Type1字体,就是大了点,“dottexlive2008.tar.bz2 仅包含全部字体包和相关宏包”一共是685M,拉下来,好在网速还算很快。把里面的afm字体单独解压出来:

	$ tar xjvf /big1/dottexlive2008.tar.bz2 .texlive2008/texmf-var/fonts/afm/
	$ cd .texlive2008/texmf-var/fonts/afm/cjk/gbkfs
	$ cat gbkfs??.afm > gbkfs.afm

拿这个gbkfs.afm配置到ezpdf里,能打出字来了,但中文还是问号,估计是因为数据是utf-8编码,而字体是gbk编码的?修改class.ezpdf.php,在function ezProcessText的第一行加上(参考:ezpdf打印德文的处理):

	$text = mb_convert_encoding($text, 'gbk', 'utf-8');

还是问号,难道字体要嵌入pdf才行?

结果又查到一篇Choosing a PHP PDF generation library for Dokeos,说ezpdf根本、确实、100%就不支持utf-8编码,合着白折腾了。

ezpdf官网确认一下,最近的新闻是17 June 2006的,比上面那篇文章还晚,看来ezpdf确实是没法用了。用tcpdf吧,不过在glpi的roadmap里,换上tcpdf是要在0.80实现的,前面还有100+ ticket没完成,也是遥遥无期啊,只好自己动手,丰衣足食了。

TCPDF

从官网下载tcpdf放到php的include路径下,对照字体设置方法来准备字体:

	$ cd tcpdf/fonts
	# Simsun字体好像还不是truetype(嵌入了点阵字体),转换有些问题,先拿方正字体来用,正好方正仿宋简体打印用更好看
	#$ ln -s /big2/fonts/xpfonts/simsun.ttf
	# 注意文件名转为小写
	$ ln -s /big2/fonts/fzfont/fzfangsong.TTF fzfangsong.ttf
	$ utils/ttf2ufm -a -F fzfangsong.ttf 
	# 生成了3个新文件
	$ ls
	fzfangsong.ufm fzfangsong.t1a fzfangsong.afm	
	# 我选的这个方正字体应该是TrueTypeUnicode字体,用TrueType转的不显字
	# 文中有说明,在ttf ufm后面还可以跟参数,这里的false代表不将字体嵌入PDF文档,
	# 那样会使pdf文件大小增大为自身+字体文件大小,很恐怖的
	$ php5 -q utils/makefont.php fzfangsong.ttf fzfangsong.ufm false
	# 生成可用的文件了
	$ls
	fzfangsong.z fzfangsong.php

现在弄个简单的例子,能够用tcpdf输出pdf文件了,里面要有这么一句:

	$pdf->SetFont('fzfangsong', '', 16);

现在文件尺寸还是太大,因为字体依然是全嵌入的(如果是embedded subset更好,不过TCPDF现在还做不到)。按照那篇文章修改fzfangsong.php文件:

	//$type='TrueTypeUnicode';
	$type='cidfont0';
	...
	//$dw=600;
	$dw=1000;
	...
	//$enc='';
	$diff='';
	//$file='fzfangsong.z';
	//$ctg='fzfangsong.ctg.z';
	$originalsize=3548232;
 
	// Chinese Simplified
	$enc='UniGB-UTF16-H';
	$cidinfo=array('Registry'=>'Adobe', 'Ordering'=>'GB1','Supplement'=>2);
	include(dirname(__FILE__).'/uni2cid_ag15.php');

现在生成的pdf文件尺寸倒是很小了,但用eivnce看文字全部是空白,这是在Linux下看,跑到windows下看正常,而且完美,gb和big5编码都能显示。又用其它一些字体试了试,都是windows正常但linux下看不了,应该是刚才作的步骤正确,但与平台配合起来还欠点什么。

仔细对比了上面生成的两种pdf文件,以及另外一个用openoffice.org生成的pdf文件(这个应该比较标准吧),发现还是openoffice.org生成的文件又小、效果又好,用的是“已嵌入子集/embedded subset”方式,

最终查到,我基本上已经作好了,问题出在evince身上,安装poppler-data包(evince用这个来处理中文)后,完美解决,字体为非嵌入not embedded方式。生成的pdf文件非常小,用windows和evince浏览也都正常。混合gb2312和big5编码的内容也没问题:evince下中文出西欧字符不出,windows下Adobe Reader只出gb2312的字,Foxit Reader全部中文和西欧字符完美。而在全内嵌字体的情况下,用evince查看big5字符都是小黑块。

hack GLPI

接下来是大工程,把ezpdf换成tcpdf。修改inc/export.function.phpfunction displaySearchFooter函数,把case PDF_OUTPUT_LANDSCAPEcase PDF_OUTPUT_PORTRAIT两部分的内容都换成TCPDF的处理,源数据还借用原来的$PDF_HEADER,$PDF_ARRAY。除了纸张参数不一样,两部分的处理是相同的,下面是一个简单的例子:

	// --------
	require_once('tcpdf/tcpdf.php');
	global $PDF_HEADER,$PDF_ARRAY;
 
	// create new PDF document
	$pdf = new TCPDF('L', PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false); 
 
	// set document information
	$pdf->SetCreator(PDF_CREATOR);
	$pdf->SetAuthor('Fwolf');
	$pdf->SetTitle($title);
	$pdf->SetSubject('GLPI Export');
	$pdf->SetKeywords('TCPDF, PDF, GLPI, export, fwolf, work, asset');
 
	// set header and footer fonts
	$pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN));
	$pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA));
	// remove default header/footer
	$pdf->setPrintHeader(false);
	$pdf->setPrintFooter(true); 
 
	// set default monospaced font
	$pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
 
	//set margins
	$pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP - 10, PDF_MARGIN_RIGHT);
	$pdf->SetHeaderMargin(PDF_MARGIN_HEADER);
	$pdf->SetFooterMargin(PDF_MARGIN_FOOTER);
 
	//set auto page breaks
	$pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);
 
	//set some language-dependent strings
	$pdf->setLanguageArray($l); 
 
	// ---------------------------------------------------------
 
	// add a page
	$pdf->AddPage();
 
	// Column width, by conteng max length
	$w_max = $pdf->getPageWidth() - PDF_MARGIN_LEFT - PDF_MARGIN_RIGHT;
	$w = array();
	foreach($PDF_ARRAY as $row) {
		foreach ($row as $i => $cell) {
			// Chinese utf8 2 words length=6, should be 4 here
			$cell = trim($cell);
			$l = mb_strwidth($cell, 'utf-8');
			// FZFangSong is not fixed font, so add some to en chars
			// eg: 哈哈abc
			// strlen = 6 + 3 = 9
			// mbstrlen = 5
			// mbstrwidth = 7
			$l += ($l + mb_strlen($cell, 'utf-8') - strlen($cell))
				* 0.3;
 
			if (!isset($w[$i])) {
				$w[$i] = $l;
			}
			elseif ($w[$i] < $l) {
				$w[$i] = $l;
			}
		}
	}
	// Compare with max width, maybe below 0
	$i = $w_max - array_sum($w);
	// Add back spaces left to each width
	$i = $i / count($w);
	foreach ($w as $k => $v) {
		$w[$k] = $v + $i;
	}
 
 
	// Table title
	$pdf->SetFont('fzfangsong', '', 14);
	$pdf->Write(0, '设备一览表', 0, 0, 'C', 1);
 
	// set font
	$pdf->SetFont('fzfangsong', '', 10);
 
	// Table header
	$pdf->SetFillColor(208, 220, 255);
	$pdf->SetTextColor(0);
	foreach ($PDF_HEADER as $i => $head) {
		$pdf->Cell($w[$i], 7, $head, 1, 0, 'C', 1);
	}
	$pdf->Ln();
 
	// Rows
	$pdf->SetFillColor(224, 235, 255);
	$pdf->SetTextColor(0);
	$fill = 0;
	foreach($PDF_ARRAY as $row) {
		foreach ($row as $i => $cell) {
			$pdf->Cell($w[$i], 6, $cell, 'LR', 0, 'L', $fill);
		}
		$pdf->Ln();
		$fill=!$fill;
	}
	$pdf->Cell(array_sum($w), 0, '', 'T'); 
 
	// ---------------------------------------------------------
 
	//Close and output PDF document
	$pdf->Output('glpi.pdf', 'I'); 
	return;
	break;

总体上这个例子是照着官网Colored Tables例子来的。

最后,把这个文件中所有的utf8_decode处理都去掉,完成。

参考

Update @ 2009-11-06

Print to pdf 的时候,Historical 中的 Comments 如何显示?默认的不好,可以考虑换成 diff 信息,如果又不愿用 pear 里的 xdiff,可以考虑它的前身──用 php 直接实现的 PHP inline diff

Related posts

Categories: PHP Tags: , , , , , , , , ,

teamol=WebCollab

March 1st, 2009 Fwolf 4 comments

Team Online 项目管理系统

如果你曾经在国内某个php源码下载网站下载过一个叫teamol的团队任务分配软件,不知你有没有注意到,它和国外开源项目WebCollab之间似乎有着亲密的联系。

本来我也没注意,可这个teamol下载回来以后安装颇不顺利,文档TeamOL setup.doc和安装界面有不一样的地方,虽然国内开源项目用doc写文档不算奇怪,但这个文档内容也太少了。代码注释中有个“官方网站”http://www.inodea.cn是打不开的,代码中很多地方标的版本号是0.1,可下载时我记得说的出3.0或者3.2版本。

开始安装以后有个地方卡壳了,还没安装数据库,就要从数据库里信息来验证用户是否有权限安装。hack安装之后,path配置也有问题,模板中还有一些错误,比如明明这段函数内没有$title这个变量却多次使用,要知道即使是global $title也是没有值的。

反正歪歪扭扭总算把程序配置得能运转了,开始试试,功能倒还有点让我满意的意思,除了日期选择的弹出窗口我极其不喜欢。然后我就发现了一件让我大跌眼镜的事情,这么一个错误百出或者说有点像半成品的“国产开源”作品,居然不仅有英、中文支持,还有一个看似排版比较成熟的英文帮助页面:

webcollab-help

好奇的我挑了帮助页面中的一句话上G一搜,便找到了WebCollab。仔细比较下来,两者的目录结构也几乎一样:

dir structure of teamol and webcollab

结果应该不用我说了,也或许teamol的作者原想只是以webcollab为蓝本边学习边制作一个全新的系统吧,从注释中的版本0.1和修改过使用frame的页面框架中能够看出作者还是付出了些劳动的。

回到主题,用于小组内工作任务分配跟踪,倒是可以试试WebCollab,界面是简陋了点,基本的内容算是都有了。但这类软件,至今没找到一个特别好用的,包括很多在线的GTD或者项目管理系统。

WebCollab login

Related posts

Categories: Internet, PHP Tags: , , ,