标签归档:插件

jQuery插件 – zTree

jQuery插件zTree, 项目地址:http://www.ztree.me/v3/main.php#_zTreeInfo, 国人开发,中文文档。使用上相对容易。累世这类插件的使用,往往会有很多配置信息,所以需要花一些时间理清楚配置参数的作用,然后学习基本的API。通过例子学习是一个捷径。

以下是一个例子,在这个例子中,我实现了根据勾选的叶子节点,获取整个路径的信息,因为我实际中需要保存这个路径:

<?php 
/*
[
  {
     "id":"1",
     "parentid":"0",
     "name":"服装及配饰"
  },
  {}
]
*/
include 'inc.php';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script src="jquery.min.js"></script>
<script src="js/jquery.ztree.all-3.5.min.js"></script>
<link rel="stylesheet" href="css/zTreeStyle/zTreeStyle.css" type="text/css" media="all" />

<script>
treeJson = <?php echo $tree?>;

var setting = {
	view:{  
		// 双击展开
		dblClickExpand: true,
		// 是否显示对齐线 
		showLine: false,  
		// 是否显示图标
		showIcon:false
	},  
	data: {  
		simpleData: {
			// 是否启用简单数据模式(这个方式只要把每个目录按照如下格式读出放入数组即可)
			enable: true,
			// 目录ID的标识符
			idKey: "id",
			// 符目录ID标识符
			pIdKey: "parentid",
			// 根目录ID值
			rootPId: 0
		}  
	},
	check: {
		// 是否启用选框
		enable: true,
		// 设置自动关联勾选时是否触发 beforeCheck / onCheck 事件回调函数
		autoCheckTrigger: true,
		chkStyle: "checkbox"
	}
};


$(function(){
	// 第一个参数是一个容器(jQuery包装),第二参数是配置信息,第三参数是Json数据
	// 注意:容器需要应用一个叫ztree的CSS类,也可以修改,不过对应CSS样式要替换(一般不需要改动)
	zTree = $.fn.zTree.init($("#erpTree"), setting, treeJson);	
	
	
	$("#trigger").click(function(){
		var set = $(this);
		if(set.attr('clear') == 'false'){
			set.attr('clear','true');
			// 清楚所有勾选
			zTree.checkAllNodes(false);
			return;
		}else if(set.attr('clear') == 'true'){
			set.attr('clear','false');
		}
		
		// 节点ID,不需要区分子节点 和 父节点
		var nodes = [82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,403,404,405,406,445,476,510,511];
		for(i in nodes){
			// 获取节点对象
			var node = zTree.getNodeByParam('id',nodes[i]);
			// 选中节点 第一参数必须是zTree内部节点对象,第二参数表示是否选中,第三参数表示是否联动操作(子节点选中,父节点也选上)
			zTree.checkNode(node, true, true);
			// 更新节点信息
			//zTree.updateNode(node);
		}
	});
	
	$("#getNodes").click(function(){
		// 获取所有选中的节点对象
		var checkedNodes = zTree.getCheckedNodes(true);
		// 选中节点ID与对象对照表
		var cates = {};
		for(var ii in checkedNodes){
			cates[checkedNodes[ii]['id']] =  checkedNodes[ii];
		}
			
		// 子节点路径对应
		var catePath = {};
		for(i in checkedNodes){
			// 子节点 回溯 获取路径
			if(!checkedNodes[i]['isParent']){
				var id = checkedNodes[i]['id'], name = checkedNodes[i]['name'];
				var ids = [], names = [];
				ids.push(id); names.push(name);
				pid = checkedNodes[i]['parentid'];
				for(var j=0; j<30; j++){
					if(pid != 0){
						node = cates[pid];
						ids.push(node['id']);
						names.push(node['name']);
						pid = node['parentid'];
					}else{
						break;
					}
				}
				catePath[id] = [ids.reverse().join('-'),names.reverse().join('-')];
			}
		}
		
		var outStr = '';
		$.each(catePath,function(){
			//console.log(this.join('|'));
			outStr += this.join('|')+"<br />";
		});
		
		$("#log").html(outStr);
	});
});

</script>
</head>
<body>
<table>
	<tr>
    	<td style="vertical-align:top;width:350px;">
        <div id="erpTree" class="ztree"></div>
        <button id="trigger" clear="true">设置节点</button> -- <button id="getNodes">获取节点</button>
        </td>
        <td style="vertical-align:top">
        <div id="log"></div>
        </td>
    </tr>
</table>
</body>
</html>

Demo地址:http://blog.ifeeline.com/jquery/zTree_v3/index.php

jQuery UI 插件收藏 – Selectmenu

jQuery UI插件Selectmenu

效果参看: jQuery UI Selectnumu

这个插件实际是模拟一个Select,把原来的进行隐藏,不过它并不影响原来的Select表单特性。这个插件依赖jQuery UI库,从归类上应该归入Widgets组,跟Widgets组的Slider Tabs Tooltip是一个层次的。可以通过修改样式获取不同的效果。

永久链接:http://blog.ifeeline.com/880.html

jQuery插件收藏 – JscrollPane

Jscrollpane效果
参考:JscrollPane效果图

插件官方网页:http://jscrollpane.kelvinluck.com/

官网提供了很多例子的,以下提供一张对比图,对于要自定义外观可以快速参考:
Jscrollpane插件快速参考

它把原来容器的内容用了jspPane容器包含,然后外层再加上一个jspContainer容器,这个容器内有一个叫jspHorizontalBar容器,用来显示滚动条,最外层是原来的容器。对于滚动条,提供了非常细致的控制,能完美模拟需要的滚动条。

原创文件,转载务必保留出处。
永久链接:http://blog.ifeeline.com/873.html

Magento插件Layoutviewer详解

http://blog.ifeeline.com/369.html中,原文作者介绍了一个查看包布局 和 页面布局 和 句柄的插件:configviewer。虽然实现比较简单,但是比较有研究价值。

首先这个插件通过Magento中的事件触发而工作的。如果不理解这个,请参考http://blog.ifeeline.com/465.html

先看这个插件的配置文件

<?xml version="1.0"?>
<config>	
	<modules>
	<Vfeelit_Layoutviewer>
		<version>0.1.0</version>
	</Vfeelit_Layoutviewer></modules>

	<global>
		<models>
			<vfeelit_layoutviewer>
				<class>Vfeelit_Layoutviewer_Model</class>
			</vfeelit_layoutviewer>
			
			<core>
				<rewrite>
					<layout_update>Vfeelit_Layoutviewer_Model_Layout_Update</layout_update>
				</rewrite>
			</core>
			
		</models>
	
		<events>
			<controller_action_postdispatch>
				<observers>
					<vfeelit_layoutviewer_model_observer>
						<type>singleton</type>						
						<class>Vfeelit_Layoutviewer_Model_Observer</class>
						<method>checkForLayoutDisplayRequest</method>
					</vfeelit_layoutviewer_model_observer>
				</observers>
			</controller_action_postdispatch>
		</events>
	</global>	
</config>

首先是core里面的rewrite标签,这里提供了一个覆盖核心类的方法。比如,Mage:getModel(‘core/layout_update’),获取的是Mage_Core_Model_Layout_Update,通过rewrite,那么就会得到里面对应值(Vfeelit_Layoutviewer_Model_Layout_Update)。

相当于是使用Vfeelit_Layoutviewer_Model_Layout_Update替换了系统默认的Mage_Core_Model_Layout_Update。那么看看自定义的类:

	class Vfeelit_Layoutviewer_Model_Layout_Update extends Mage_Core_Model_Layout_Update {		
		public function getPackageLayout() {
			$this->fetchFileLayoutUpdates();
			return $this->_packageLayout;
		}		
	}

这个就是自定义的类的全部内容。注意,它是继承Mage_Core_Model_Layout_Update类的(必须),然后提供了一个自定义的方法:getPackageLayout()。当然,也可以覆盖继承过来的方法。这个就是Magento替换核心类的方案。

这里先跳到events标签,里面的controller_action_postdispatch是事件名,看起来是在控制器的分发之后触发的:

// Mage_Core_Controller_Varien_Action
    public function postDispatch()
    {
		//……
        Mage::dispatchEvent('controller_action_postdispatch', array('controller_action'=>$this));
    }

正如我们所见,的确如此。 控制器的分发过程可以参考http://blog.ifeeline.com/481.html。这个事件触发执行的代码基本可以认为是临近最后要执行的代码了,就等着把输出缓冲送回客户端了(执行完代码就输出),这个时候要执行Vfeelit_Layoutviewer_Model_Observer类对象的checkForLayoutDisplayRequest方法:

//Vfeelit_Layoutviewer_Model_Observer
		private function init() {
$this->setLayout(Mage::app()->getFrontController()->getAction()->getLayout());
			$this->setUpdate($this->getLayout()->getUpdate());
		}
		
		//entry point
		public function checkForLayoutDisplayRequest($observer) {			
			$this->init();
			$is_set = array_key_exists(self::FLAG_SHOW_LAYOUT, $_GET);
			if(		$is_set && 'package' == $_GET[self::FLAG_SHOW_LAYOUT]) {
				$this->outputPackageLayout();
			}
			else if($is_set && 'page'    == $_GET[self::FLAG_SHOW_LAYOUT]) {
				$this->outputPageLayout();			
			}
			else if($is_set && 'handles' == $_GET[self::FLAG_SHOW_LAYOUT]) {
				$this->outputHandles();
			}
		}
 

首先执行的是init方法,它从控制器中获取布局对象,这个就是Mage_Core_Model_Layout。然后这个对象保存在_data[‘layout’]中。接下来从布局对象中调用getUpdate()方法获取layout_update对象,注意,layout_update已经通过rewrite指令修改为了Vfeelit_Layoutviewer_Model_Layout_Update类型,所以这个方法就会获取一个这个类型的对象引用保存到_data[‘update’]中。 然后根据条件调用outputPackageLayout 和 outputPageLayout 和 outputHandles方法:

接下来继续看具体方法:

	class Vfeelit_Layoutviewer_Model_Observer extends Varien_Object{
		const FLAG_SHOW_LAYOUT 			= 'showLayout';
		const FLAG_SHOW_LAYOUT_FORMAT 	= 'showLayoutFormat';		
		const HTTP_HEADER_TEXT			= 'Content-Type: text/plain';
		const HTTP_HEADER_HTML			= 'Content-Type: text/html';
		const HTTP_HEADER_XML			= 'Content-Type: text/xml';
		
		private $request;
		
		private function init() {
$this->setLayout(Mage::app()->getFrontController()->getAction()->getLayout());
			$this->setUpdate($this->getLayout()->getUpdate());
		}
		
		//entry point
		public function checkForLayoutDisplayRequest($observer) {			
			$this->init();
			$is_set = array_key_exists(self::FLAG_SHOW_LAYOUT, $_GET);
			if(		$is_set && 'package' == $_GET[self::FLAG_SHOW_LAYOUT]) {
				$this->outputPackageLayout();
			}
			else if($is_set && 'page'    == $_GET[self::FLAG_SHOW_LAYOUT]) {
				$this->outputPageLayout();			
			}
			else if($is_set && 'handles' == $_GET[self::FLAG_SHOW_LAYOUT]) {
				$this->outputHandles();
			}
		}

		private function outputHandles() {
			$update = $this->getUpdate();
			$handles = $update->getHandles();
			echo '<h1>','Handles For This Request','</h1>'."\n";
			echo '<ol>' . "\n";
			foreach($handles as $handle) {
				echo '<li>',$handle,'</li>';
			}
			echo '</ol>' . "\n";			
			die();
		}
		
		private function outputHeaders() {
			$is_set = array_key_exists(self::FLAG_SHOW_LAYOUT_FORMAT,$_GET);			
			$header		= self::HTTP_HEADER_XML;
			if($is_set && 'text' == $_GET[self::FLAG_SHOW_LAYOUT_FORMAT]) {
				$header = self::HTTP_HEADER_TEXT;
			}
			header($header);
		}
		
		private function outputPageLayout() {
			$layout = $this->getLayout();		//获取layout对象
			$this->outputHeaders();		
			die($layout->getNode()->asXML());	//直接从layout获取页面布局
		}
		
		private function outputPackageLayout() {
			$update = $this->getUpdate();
			$this->outputHeaders();
			die($update->getPackageLayout()->asXML()); //从layout_update对象中获取包布局
		}
	}

看outputPageLayout方法,调用update的getPackageLayout()方法,上面已经说过,系统的Layout_Update对象已经换成了Vfeelit_Layoutviewer_Model_Layout_Update,其实它仅仅是添加了getPackageLayout方法而已。它里面调用了继承过来的fetchFileLayoutUpdates()方法,它把所有的布局XML读取进来,然后放入到_packageLayout中。细节可以参考:http://blog.ifeeline.com/488.html

考虑到事件的触发是在postdispatch时,看起来,这个layout_update已经读取了相关的文件了,但是那必须是在控制器方法调用了loadLayout()为假设的,如果没有调用,那意味着Layout_update还没有读取布局xml文件。当要读取包布局时就是空白了。为了无论如何至少能把包布局呈现出来了,这里重写了Layout_Update类并实现了getPackageLayout方法,不管如何都读取包布局(如果没有执行loadLayout方法,句柄和页面布局是无法呈现的),但是这里的考虑还是有一点欠缺,因为layout_update的内容可能还有来自数据库的配置(目录产品CMS都可能应用自定义的布局配置,写入数据库中),方法中直接读取文件配置,没有考虑这个。改进方法是,如果有Layout_Update对象,就使用它,如果没有才强制手动读取:

	class Vfeelit_Layoutviewer_Model_Layout_Update extends Mage_Core_Model_Layout_Update {		
		public function getPackageLayout() {
			if(empty($this->_packageLayout)){
				$this->fetchFileLayoutUpdates();
			}
			return $this->_packageLayout;
		}		
}

这个工具是在做页面布局时可以使得我们可以查看包布局和页面布局和句柄,非常实用。同时,通过研究这个工具的代码,可以知道如果重写系统的核心类和系统的事件机制,还有控制器分发过程和包布局和页面布局与句柄的关系(使用句柄从包布局中剥离页面布局)。

永久链接:http://blog.ifeeline.com/512.html
原创文章,转载务必保留出处。

Zen-cart 插件 Limint Access 根据国家和语言限制用户访问

根据国家和语言限制用户访问,本人编写这个插件就依据这个需求而写。你经常可能遇到这样的情况,去访问某个网址时,得到的结果是页面不存在,或定位到一个登录页面,输入密码后才能访问:
重定位到登录页
或者:
重定位到错误页面

要实现这个限制,需要依靠$_SERVER[‘HTTP_USER_AGENT’]变量的支持,这个变量获取浏览器支持的语言,一般按照如下格式传递:

zh-cn,en-us;q=0.7,en;q=0.3

它使用逗号分隔,得到的段中再以冒号分隔,这样一般就会得到两段,第一段是语言和国家代码,第二段是权值。先说第一段,它用横杠分出语言代码 和 国家代码,比如en-us表示en语言,就是英文,然后是国家代码us,就是美国,这个就代表了美国英语。第二段是一个权值,值越大表明优先值越大,如果没有指定值,那么它默认就是1,一般表示它首先接受的语言或国家代码,这里说的是一般,是因为有些浏览器并不按照这个规则操作,比如Chrome。

从这个分析来看,如果要限制中文,只要这个串含有zh,就可以认为它支持中文,那么它就是限制的对象,如果不想限制中文访问,但是要限制中国大陆访问,那么这个串中含有cn就是限制对象,这样香港中文和台湾中文就都可以访问了。

另外,对于某些爬虫是可以直接禁止的,比如LWP::Simple,BBBike,wget,scrapbot,这其中有的是知了名的采集程序,这个依赖$_SERVER[‘HTTP_USER_AGENT’]。

附上一段关键代码:

if(defined('LIMIT_ACCESS_ENABLE') && (LIMIT_ACCESS_ENABLE == 'true')){
			
	$spiders = array();
	if(defined('LIMIT_ACCESS_SPIDERS') && (LIMIT_ACCESS_SPIDERS != '')){
			$spiders = explode(',',LIMIT_ACCESS_SPIDERS);
	}
	if(count($spiders) <= 0){
		$spiders = array("LWP::Simple","BBBike","wget","scrapbot");
	}
		
	if(trim($_SERVER['HTTP_USER_AGENT']) == ""){
		header("Status: 404 Not Found");
		exit();
	}else{
		foreach($spiders as $ua){
			if( strpos(strtolower(trim($_SERVER['HTTP_USER_AGENT'])),strtolower(trim($ua))) !== false ){
				header("Status: 404 Not Found");
				break;
				exit();
			}
		}
	}

	$pass = true;
	if(defined('LIMIT_ACCESS_PASSWORD') && (LIMIT_ACCESS_PASSWORD != '')){
		if(isset($_SESSION['force_login']) && trim($_SESSION['force_login']) != ''){
			$pass = false;
		}
	}

	if($pass){
		$languages = array(); //array("zh");
		$contries = array();  //array("cn","jp","ko");
				
		if(defined('LIMIT_ACCESS_LANGUAGES') && (LIMIT_ACCESS_LANGUAGES != '')){
			$languages = explode(',',LIMIT_ACCESS_LANGUAGES);
		}
				
		if(defined('LIMIT_ACCESS_CONTRIES') && (LIMIT_ACCESS_CONTRIES != '')){
			$contries = explode(',',LIMIT_ACCESS_CONTRIES);
		}
				
		$break = false;
		foreach($languages as $lan){
			if( strpos(strtolower(trim($_SERVER['HTTP_ACCEPT_LANGUAGE'])),strtolower(trim($lan))) !== false ){
				$break = true;
				break;
			}
		}
				
		foreach($contries as $cou){
			if( strpos(strtolower(trim($_SERVER['HTTP_ACCEPT_LANGUAGE'])),strtolower(trim($cou))) !== false ){
				$break = true;
				break;
			}
		}
					
		if($break){
			if(defined('LIMIT_ACCESS_PASSWORD') && (LIMIT_ACCESS_PASSWORD != '')){
				if(strpos(strtolower($_SERVER['REQUEST_URI']),"login.php") === false){
					zen_redirect("/login.php");
					exit();
				}
			}else{
				header("Status: 404 Not Found");
				exit;
			}
		}
	}
		
}

另外,有些朋友希望根据浏览器所在的操作系统的语言来限制访问,比如如果操作系统语言是中文,则限制访问,但是在我测试的浏览器中,只有IE内核的浏览器可以获得操作系统语言。换句话说就是,如果你用IE来访问,而且用的是中文操作系统,而我现在要限制中文访问,那么你的IE就无法访问我的站点了,或者输入密码后才能进入。

原创文章,转载务必保留本文链接。
永久链接:http://blog.ifeeline.com/276.html

Zen-cart 插件 Admin New Customer 安装使用与改进

Zen-cart 插件 Admin New Customer下载地址:http://www.zen-cart.com/downloads.php?do=file&id=1501
我下载的是admin_new_customer_1.1版本,它只针对Zer-cart 1.5.0 +版本。
此插件有修改zen-cart原文件,使用Winmerge比对可以知道:

1 admin/customers.php文件的1179行后添加(增加一个连接):
<?php
    if(empty($action)) { ?>
              <tr>
                <td align="right" colspan="10" class="smallText"><?php echo '<a href="' . zen_href_link(FILENAME_NEW_CUSTOMER) . '">' . zen_image_button('button_insert.gif', IMAGE_INSERT) . '</a>'; ?></td>
              </tr><?php
    } ?>
2 admin/includes/functions/html_output.php
172行后添加:
      $output_string .= '    hideStateField(' . $form . ');' . "\n" ;
175行后添加:
                      '    showStateField(' . $form . ');' . "\n" .
179行后添加:
  ////
  // javascript to dynamically update the states/provinces list when the country is changed
  // TABLES: zones
  function zen_js_billing_zone_list($country, $form, $field) {
  	global $db;
  	$countries = $db->Execute("select distinct zone_country_id
  			from " . TABLE_ZONES . "
  			order by zone_country_id");

  	$num_country = 1;
  	$output_string = '';
  	while (!$countries->EOF) {
  		if ($num_country == 1) {
  			$output_string .= '  if (' . $country . ' == "' . $countries->fields['zone_country_id'] . '") {' . "\n";
  		} else {
  			$output_string .= '  } else if (' . $country . ' == "' . $countries->fields['zone_country_id'] . '") {' . "\n";
  		}

  		$states = $db->Execute("select zone_name, zone_id
  				from " . TABLE_ZONES . "
  				where zone_country_id = '" . $countries->fields['zone_country_id'] . "'
  				order by zone_name");


  		$num_state = 1;
  		while (!$states->EOF) {
  			if ($num_state == '1') $output_string .= '    ' . $form . '.' . $field . '.options[0] = new Option("' . PLEASE_SELECT . '", "");' . "\n";
  			$output_string .= '    ' . $form . '.' . $field . '.options[' . $num_state . '] = new Option("' . $states->fields['zone_name'] . '", "' . $states->fields['zone_id'] . '");' . "\n";
  			$num_state++;
  			$states->MoveNext();
  		}
  		$num_country++;
  		$countries->MoveNext();
  		$output_string .= '    hideBillingStateField(' . $form . ');' . "\n" ;
  	}
  	$output_string .= '  } else {' . "\n" .
  			'    ' . $form . '.' . $field . '.options[0] = new Option("' . TYPE_BELOW . '", "");' . "\n" .
  			'    showBillingStateField(' . $form . ');' . "\n" .
  			'  }' . "\n";

  	return $output_string;
  }

3 admin/includes/languages/english.php 文件中修改了一些常量定义值。

4 includes/functions/password_funcs.php文件的118行[$entropy = sha1_file('/includes/configure.php');]替换成(实际是换成绝对路径):
$entropy = sha1_file(DIR_FS_CATALOG . DIR_WS_INCLUDES . 'configure.php');

一般只要直接覆盖即可,除非对这些文件有修改。

看第四点,这里修正了Zen-cart1.5.1的一个Bug,原版居然直接使用’/includes/configure.php’子串。
覆盖安装,进入后台->Customers->Customers,点击Insert,显示警告:

WARNING: An Error occurred, please refresh the page and try again.

//查看日志发现
[19-Dec-2012 05:39:18 UTC] PHP Fatal error:  1366:Incorrect integer value: '' for column 'entry_zone_id' at row 1 :: insert into address_book (customers_id, entry_company, entry_firstname, entry_lastname, entry_street_address, entry_suburb, entry_postcode, entry_city, entry_state, entry_zone_id, entry_country_id) values ('1', '', 'jerry', 'saa', 'guangzhou, guangdong', '', '6666', 'guangzhou', '', '', '223') in D:\www\web\z151.com\public_html\includes\classes\db\mysql\query_factory.php on line 120

日志提示说不正确的整数值,应该是Mysql配置成了严格模式,修改为:

#sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
sql-mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"

重启Mysql后,警告消除。

在zent-cart1.5.0以前的版本,在处理国家的省份时存在Bug,大体是如果国家没有设置省份,那么应该让客户自己输入,但是如果自己输入就永远无法通过验证,这个Bug在zent-cart1.5.x中已经被修复,可惜这个插件的代码还是使用存在Bug的那个代码段,所以需要做一些修改才行:
admin_new_customer_state
对应代码修改为:

$entry_state_has_zones = ($check->fields['total'] > 0);
if ($entry_state_has_zones == true) {
    //......
}else{
    if (strlen($cInfo->entry_state) < ENTRY_STATE_MIN_LENGTH) {
	$messageStack->add(ENTRY_STATE_ERROR);
	break;
    }
}
//在处理Bill地址时也一样逻辑

具体修改需要比对admin/new_customer.php文件,修正程序包下载:admin_new_customer_1.1_fix_by_blog.ifeeline.com

原创文件,转载务必保留出处。
永久连接: http://blog.ifeeline.com/269.html

Zen-cart 插件 “Fast and Easy AJAX Checkout for Zen Cart” bug??

Fast and Easy AJAX Checkout for Zen Cart购买地址:
https://www.numinix.com/zen-cart-modules/checkout/fast-and-easy-ajax-checkout-for-zen-cart-dl-797
现在是150刀,以前是75刀,价格翻了一翻,真是贱的很。不过有一个对应的免费的版本叫:Fast and Easy Checkout for Zen Cart(没有Ajax),这个版本也很贱,总让你不舒服,然后让你想去花钱买那个付费版本,花钱买了付费版本还不算,这个付费版本可能还有一些预留的Bug,然后让你购买它们提供的安装服务(35刀),这个鬼佬贱到家了。

我下载了付费的版本2.24.1的插件,安装完毕后在日志中就产生如下警告:

[10-Dec-2012 02:05:08 UTC] PHP Warning:  Missing argument 1 for ot_coupon::get_order_total(), called in D:\www\web\z151.com\public_html\includes\modules\order_total\ot_coupon.php on line 510 and defined in D:\www\web\z151.com\public_html\includes\modules\order_total\ot_coupon.php on line 595

打开ot_coupon.php检查510行 和 595行:

...
$orderTotal = $this->get_order_total();
…
function get_order_total($couponCode){

发现,get_order_total函数是要带参数的,而510行调用的时候并没有带参数,所以导致警告。ot_coupon.php这个文件是内核文件,但是它被插件提供的文件覆盖了,查看了下原来的文件发现get_order_total函数并没有被修改,所以调用get_order_total不带参数应该是覆盖的文件导致的警告,修改为:

$this->get_order_total((int)$_SESSION['cc_id']);

当把代码放到生成环境时,出现如下错误:
[10-Dec-2012 02:46:17 UTC] PHP Parse error: syntax error, unexpected ‘}’ in /web/www.ifeeline.com/public_html/includes/templates/cot/templates/quick_checkout/tpl_modules_qc_shipping.php on line 119
检查tpl_modules_qc_shipping.php文件并没有发现大括号不对应问题,而且在本地可以正常运行,到了生产服务器就出现这样的错误,后发现如下代码(文件87行附近):

<?          
for ($j = 0; $j < $quotes[$i]['email_no']; $j++) {
?>

PHP的界定符没有使用标准做法(少写php),而其它的都是标准写法,这才知道这是短标签问题,本地PHP环境配置是:

short_open_tag = On

而服务器配置是 Off。

把代码的短标签修改为标准写法,代码运行正常。

这里基本可以肯定的说,这个短标签问题是人为预留,因为其它都是标准写法,唯独有一个是短标签,对于关闭了短标签配置的服务器,这个错误很难被发现。最后你可能要去购买它的安装服务了,35刀安装一次,跟抢一样。

原创文章,转载务必保留出处。
永久链接: http://blog.ifeeline.com/260.html

Zen-cart 插件 Image Handler4 for v1.5.x 安装使用

Zen-cart 插件 Image Handler4 for v1.5.x 下载链接:
http://www.zen-cart.com/downloads.php?do=file&id=1380

插件主要涉及如下文件修改:

/YOUR_ADMIN/includes/modules/category_product_listing_IH4.php -> /YOUR_ADMIN/includes/modules/category_product_listing.php
/includes/modules/pages/popup_image/header_php_IH4.php -> /includes/modules/pages/popup_image/header_php.php
/includes/modules/pages/popup_image_additional/header_php_IH4.php -> /includes/modules/pages/popup_image_additional/header_php.php
/includes/modules/IH_INSTALL/additional_images.php -> /includes/modules/YOUR_TEMPLATE/additional_images.php
/includes/modules/IH_INSTALL/main_product_image.php -> /includes/modules/YOUR_TEMPLATE/main_product_image.php
/includes/templates/IH_INSTALL/popup_image_additional/tpl_main_page.php -> /includes/templates/YOUR_TEMPLATE/popup_image_additional/tpl_main_page.php

/YOUR_ADMIN/includes/modules/category_product_listing.php
/includes/modules/pages/popup_image/header_php.php
/includes/modules/pages/popup_image_additional/header_php.php
此插件实际修改了此三个核心文件,另外三个其中两个是模板覆盖,一个是模板文件。

参考:

//安装时,覆盖的核心文件
$core_files = array(
	array(DIR_FS_ADMIN.'includes/modules/category_product_listing.php',DIR_FS_ADMIN.'includes/modules/category_product_listing_IH4.php'),
       	array(DIR_FS_CATALOG.'includes/modules/pages/popup_image/header_php.php',DIR_FS_CATALOG.'includes/modules/pages/popup_image/header_php_IH4.php'),
      	array(DIR_FS_CATALOG.'includes/modules/pages/popup_image_additional/header_php.php',DIR_FS_CATALOG.'includes/modules/pages/popup_image_additional/header_php_IH4.php'),
);

//卸载时,还原文件
$rollback_files = array(
    	array(DIR_FS_ADMIN.'includes/modules/category_product_listing.php',DIR_FS_ADMIN.'includes/modules/category_product_listing.DEFAULT.php.bak'),
      	array(DIR_FS_CATALOG.'includes/modules/pages/popup_image/header_php.php',DIR_FS_CATALOG.'includes/modules/pages/popup_image/header_php.DEFAULT.php.bak'),
     	array(DIR_FS_CATALOG.'includes/modules/pages/popup_image_additional/header_php.php',DIR_FS_CATALOG.'includes/modules/pages/popup_image_additional/header_php.DEFAULT.php.bak'),
);

IH4这个版本是针对Zen-cart1.5.0以上的版本的,在安装和卸载方面做了一些自动化的处理。基本上,把文件拷贝进入对应目录,然后打开后台就会自动安装,对于涉及到的文件覆盖,比如category_product_listing_IH4.php到category_product_listing.php,首先是把原来的category_product_listing.php命名为类似category_product_listing.phpIH_1354760956.bak,然后把category_product_listing_IH4.php改为category_product_listing.php,对于卸载,把当前的category_product_listing.php重命名为类似category_product_listing.phpIH_1354760956.bak,然后把category_product_listing.DEFAULT.php.bak拷贝为category_product_listing.php,之后再把category_product_listing.DEFAULT.php.bak删除。

理顺一下:
插件拷贝进入的文件有category_product_listing_IH4.php 和 category_product_listing.DEFAULT.php.bak,安装时category_product_listing_IH4.php被重命名了,如果卸载了那么category_product_listing.DEFAULT.php.bak被删除了,可见category_product_listing_IH4.php是被修改过的文件,category_product_listing.DEFAULT.php.bak是原来的系统没有修改过的文件,安装时使用修改过的文件,卸载时把没有修改过的对应回去,安装 和 卸载过程都产生了category_product_listing.phpIH_xxxxxx.bak这样的备份文件,实在是有点不干净。

修改一下:
修改对应文件,让其直接对应到最终文件,包含覆盖文件,让其拷贝覆盖,然后打开后台就完成安装。自动安装了之后如果卸载,修改其只删除配置项(不删除文件,如果要对应回来只要手动修改3个被覆盖的核心文件即可,这样就不存在很多垃圾文件了)。

附上修改的版本(仅供参考):
Image_HandlerV4修改版

IH4基本的工作原理:
在includes/functions/html_output.php中的zen_image()函数:

  function zen_image($src, $alt = '', $width = '', $height = '', $parameters = '') {
    global $template_dir, $zco_notifier;
    // ....
    if (function_exists('handle_image')) {
      $newimg = handle_image($src, $alt, $width, $height, $parameters);
      list($src, $alt, $width, $height, $parameters) = $newimg;
      $zco_notifier->notify('NOTIFY_HANDLE_IMAGE', array($newimg));
    }
    //.....

如果存在handle_image函数,则使用这个函数对图片进行处理。IH4正是使用这个预留“钩子”实现缓存图片的功能。handle_image函数在includes/functions/extra_functions/functions_bmz_image_handler.php中定义,这个文件中读入配置参数,然后调用includes/classes/bmz_image_handler.php中定义的ih_image类实例来处理图片缓存。

原创文章,转载务必保留出处。
永久链接:http://blog.ifeeline.com/254.html

Zen-cart插件 css_js_loader 人为预留错误?

Zen-cart插件 css_js_loader 下载页:
https://www.numinix.com/zen-cart-modules/other-modules/css-js-loader

这个插件是Numinix提供的免费插件,用来压缩CSS和JS,针对页面控制CSS或JS脚本的加载,是一个实用插件,不过2.0.0和2.1.0版本或以后版本都存在一个人为的预留错误。

按照说明安装完毕之后,后台启用CSS和JS压缩:

然后访问前台,发现样式没有起作用,查看源码:

<link rel="stylesheet" type="text/css" href="min/?f=/includes/templates/cmm/css/stylesheet.css&1354635372" />

就在源码中点击这个链接,在chrome中得到:

意思就是链接不存在,查看一下PHP产生的日志:

[04-Dec-2012 15:36:12 UTC] PHP Warning:  fopen(D:/www/web/in.data/min/cache/minify_stylesheet,stylesheet_css_buttons.css_2f5c85de52b3d1be4c806900d7346742) [function.fopen]: failed to open stream: No such file or directory in D:\www\web\in.data\min\lib\Minify\Cache\File.php on line 100
[04-Dec-2012 15:36:12 UTC] PHP Warning:  flock() expects parameter 1 to be resource, boolean given in D:\www\web\in.data\min\lib\Minify\Cache\File.php on line 101
[04-Dec-2012 15:36:12 UTC] PHP Warning:  stream_get_contents() expects parameter 1 to be resource, boolean given in D:\www\web\in.data\min\lib\Minify\Cache\File.php on line 102
[04-Dec-2012 15:36:12 UTC] PHP Warning:  flock() expects parameter 1 to be resource, boolean given in D:\www\web\in.data\min\lib\Minify\Cache\File.php on line 103
[04-Dec-2012 15:36:12 UTC] PHP Warning:  fclose() expects parameter 1 to be resource, boolean given in D:\www\web\in.data\min\lib\Minify\Cache\File.php on line 104

第一条日志就说明文件不存在,无法打开,产生了警告错误日志。

那么去min目录下查看文件,发现根本没有cache目录,所以压缩文件当然不存在了。建立cache目录,之后,缓存生成,样式正常了。

如何认定这是一个人为的预留错误呢? CSS/JS Loader是一个由Numinix免费提供的Zen-cart插件,虽然是免费的,但是如果要请它安装就要收费35美金,这个插件安装只是拷贝文件如此而已,Numinix在2.0.0以后删除了cache目录,人为添加了一个错误,如果没有错误当然不会有人请它安装了,它的目的是不言而喻,都说天下没有免费的午餐,此一例。恶心一下死鬼佬。

经验总结:发现问题,首先应该查看日志。

原创文章,转载务必保留出处。
永久链接:http://blog.ifeeline.com/246.html